- 95d63a4: Apply
:zSELinux label by default on Docker bind mounts, matching the existing Podman behavior. AddsselinuxLabeloption toDockerOptions("z"|"Z"|false, default"z"). Extracts sharedformatVolumeMountfrom Podman provider intosrc/mountUtils.tsso both providers use the same volume-mount formatter. - 9bf43df: Auto-create parent directories for file-target bind mounts under
/home/agent. When a user mount targets a single file whose sandbox-side parent directory may not exist in the image (e.g./home/agent/.codex/auth.json), both Docker and Podman providers now runmkdir -p+chownon the parent at container start. File mounts whose parent is outside/home/agentfail at config time with a clear error and remediation guidance. - adbb3cc: Add
variantoption to theopencodeagent provider for controlling reasoning effort via opencode's--variantCLI flag.
-
1b742cb: Replace hardcoded "GitHub issues" language in simple-loop and sequential-reviewer templates with backlog-agnostic wording so scaffolded projects read correctly regardless of the chosen backlog manager.
-
a85d6c0: Add Docker UID alignment via build-arg and pre-flight diagnostic. Dockerfile templates now accept
AGENT_UID/AGENT_GIDbuild-args (default 1000) andsandcastle docker build-imagedefaults them to the host UID/GID. The Docker provider gainscontainerUid/containerGidoptions and a pre-flightdocker image inspectcheck that catches UID mismatches before container start. See ADR-0014. -
856e6b7: fix: unescape
\n,\r,\t, and\\in double-quoted.envvalues to match standard dotenv semantics -
77590d0: fix: sequential-reviewer template uses createSandbox so implementer and reviewer share a branch
The sequential-reviewer template previously used
merge-to-headfor the implementer, which merged the temp branch into HEAD and deleted it. The reviewer then tried to create a worktree for the host branch (e.g.main), which was already checked out — causing a git worktree conflict.Restructured to use
createSandbox()with an explicit named branch, so both the implementer and reviewer run in the same sandbox on the same branch. This matches the pattern used by the parallel-planner-with-review template. -
c9f8348: Fix Docker mount failures on Windows hosts by switching from
-v host:sandboxto--mount type=bind,source=...,target=...format (avoiding colon ambiguity with drive letters), and adding missingpatchGitMountsForWindowscalls increateSandboxandcreateSandboxFromWorktreecode paths. -
0fd2e74: Add structured output support:
Output.object({ tag, schema })andOutput.string({ tag })extract typed, validated payloads from agent stdout. Addsoutputoption toRunOptionswith overloaded return type,StructuredOutputErrorfor extraction failures, and entry-time validation formaxIterations === 1and tag-in-prompt checks.
- 7400ead: Add a short hint to the
parallel-plannerandparallel-planner-with-reviewplan prompts noting that the issues list is already filtered, so the planner agent is less likely to requery and pick up issues outside the configured filter. - 21b6442: Fix Windows hosts emitting backslash separators for in-container paths during session capture/resume and
copyPaths.sandboxSessionStore,defaultSessionPathsLayer, andstartSandbox'scopyPathsnow use POSIX joins for paths that target the Linux container, sodocker cp/podman cpno longer reject them on Windows.
- 904ad82: Fix
PromptError: Prompt argument "{{TASK_ID}}" has no matching value in promptArgsthrown on every iteration of thesimple-loop,sequential-reviewer, andparallel-planner*merge flows aftersandcastle init. TheVIEW_TASK_COMMANDandCLOSE_TASK_COMMANDregistry values used to embed{{TASK_ID}}, which got baked into prompts whose runtime promptArgs do not includeTASK_ID. They now use a plain<ID>placeholder for the agent to fill in from surrounding context.
- 54b5111: Add
timeouts.copyToWorktreeMsoption to override the host-to-worktree copy timeout (default: 60 000 ms). - d8484ca: Surface fallback
cp -Rfailures fromcopyToWorktreeas a typedCopyToWorktreeErrorinstead of silently swallowing them - b6cc84f: Fix
WorktreeManager.pruneStaledeleting active worktrees when.sandcastle(or any ancestor of the repo directory) is a symlink.git worktree listreturns canonicalized paths, so the un-canonicalized prefix never matched the active set and parallelcreateSandbox()calls would wipe each other's worktrees mid-run, surfacing asspawn /bin/sh ENOENT. - 26920ca: Fix
branchStrategy.baseBranchbeing silently dropped when callingsandcastle.run()with a worktree-based sandbox. New branches now correctly fork from the requestedbaseBranchinstead of the host's HEAD. - bbb0f39: Fix
encodeProjectPathto handle Windows paths by replacing backslashes with hyphens and stripping drive-letter colons, producing a valid single directory-name component on Windows. - b2123e4: Add optional
timeoutMsfield to hook objects, allowing per-hook timeout overrides with fallback to the default 60s - a658fcc: Update Quick Start install command to recommend
--save-devand note that Sandcastle is a dev/CI tool - 425b77e: Use APFS clonefile (
cp -cR) on macOS for copy-to-worktree instead of GNU--reflink=auto, giving Mac users instant copy-on-write on APFS volumes
- e868d2d: Fix
createWorktreefailing with "already exists" when reusing a preserved mid-rebase worktree. Collision detection now also matches by target path, covering the detached-HEAD state during an in-progress rebase.
-
9c8516d: Surface agent error details in
AgentErrorwhen stderr is empty. Error events emitted to stdout by Codex and Pi, plus OpenCode's result text, are now parsed and included in the error message instead of being dropped. -
b2cc893: Show context window size per iteration in the run summary. Each iteration with usage data emits a
Context window: NNNkline (tokens rounded up to the nearest 1000) in both terminal and log-to-file mode. -
2843c1b: Support
baseBranchwhen creating sandboxes, so new branches can be forked from a specified ref. Available both oncreateSandboxand in the named branch strategy. -
d860e84: Fix Beads Dockerfile build failure on arm64 hosts (e.g. Apple Silicon). The image now builds on both amd64 and arm64.
-
fdd9b9e: Fix built-in review prompt templates so they respect the configured source branch instead of always diffing against
main. -
cfbeb67: Fix parallel-planner-with-review template to capture reviewer result and merge commits from both implementer and reviewer runs
-
eb03260: Fix transient worktree creation failure when
branch.autoSetupMergeorpush.autoSetupRemoteis enabled globally -
4032e64: Inline prompts (
prompt: "...") are now passed to the agent literally — no{{KEY}}substitution, no!`command`expansion, no built-in{{SOURCE_BRANCH}}/{{TARGET_BRANCH}}injection. Fixes #453: callers that build inline prompts from arbitrary content (issue bodies, PR descriptions) no longer fail when that content happens to contain{{...}}. PassingpromptArgsalongside an inline prompt is now an error; usepromptFileto opt into template behavior. -
6bc4d74: Fix
PromptPreprocessorexecuting!`...`patterns that arrive viapromptArgssubstitution. Argument values are now treated as inert data: only shell blocks written in the raw template are executed. Previously, any caller passing text throughpromptArgs(issue titles, bodies, docs excerpts, etc.) could hit spurious command execution — or, with untrusted inputs, remote shell execution — because the preprocessor scanned the fully-assembled prompt after substitution. -
359907e: Add
onAgentStreamEventoption tologgingin log-to-file mode. The callback receives eachtextchunk andtoolCallemitted by the agent, with the iteration number and a timestamp, so callers can forward the agent's output stream to an external observability system. Errors thrown by the callback are swallowed so a broken forwarder cannot kill the run. -
ce1bf1b: Support tilde expansion in
sandboxPathfor Docker and Podman mount configs.Users can now write
sandboxPath: "~/.npm"and it expands to/home/agent/.npminside the sandbox. The expansion uses the provider's declaredsandboxHomedir("/home/agent"for Docker and Podman). Using~insandboxPathwith a provider that has nosandboxHomedirthrows a descriptive error at mount resolution time.
- 2e7147b: Show commit-aware sync logs only for isolated sandboxes. Displays "Syncing N commit(s) to host" when commits exist or "No commits to sync out" when there are none, instead of the generic "Syncing changes to host" message. Bind-mount providers no longer show sync logs since sync-out only applies to isolated sandboxes.
- b0d5400: Fix git worktree mounts broken on Windows hosts (issue #410). On Windows, the parent
.gitdirectory is now mounted at a deterministic POSIX path inside the sandbox, and the worktree's.gitfile is patched with a correctedgitdir:path that resolves inside the Linux container.
- 1c71374: Add AbortSignal support for cancelling runs and interactive sessions. Pass
signaltorun(),interactive(),Sandbox.run(),Sandbox.interactive(), or any Worktree equivalent. Aborting kills the in-flight agent subprocess; handles remain usable for subsequent calls. Lifecycle hooks (host.onWorktreeReady,host.onSandboxReady,sandbox.onSandboxReady) are also cancelled when the signal fires. - 148905b: Expose per-iteration token usage on
IterationResultvia a newusage?: IterationUsagefield. Returns raw token counts (inputTokens,cacheCreationInputTokens,cacheReadInputTokens,outputTokens) for Claude Code runs. Non-Claude agent providers returnundefined. - 95ef2bd: Fix Codex agent provider not logging output during runs.
- 6ca70c1: Fix session resume failing with
docker cp (in) failed/podman cp (in) failedwhen the sandbox's project directory didn't yet exist. - 8d4e8ef: Fix Windows paths breaking Docker/Podman volume mounts. Backslashes in host paths and Windows-style sandbox paths are now normalized before reaching the container runtime.
- a971e1e: Faster sandbox startup — remove the recursive
chownthat ran on every Docker and Podman container start. AddcontainerUid/containerGidoptions to the Podman provider for controlling in-container ownership. - 49c461e: Fix duplicate command entries appearing in the task log. Each command now appears once (with its token count).
- a2dff20: Remove
throwOnDuplicateWorktreeoption; worktrees are now always reused — clean worktrees log a message, dirty worktrees log a warning. - 51d668c: Fix runs failing when prompts exceed 128 KB on Linux. Prompts are now delivered via stdin instead of command-line arguments, avoiding the
execve(2)argument size limit. - 308a1f6:
Worktree.run()now acceptsresumeSessionto resume a prior Claude Code session by ID, matching the existing support on top-levelrun().
- ba6121e: Add a
cwdoption tocreateSandbox(),createWorktree(),run(), andinteractive(). When provided,cwdreplacesprocess.cwd()as the host repo directory used for worktrees,.sandcastle/.env, logs, patches, and git operations, letting you drive Sandcastle from outside the target repo. Relative paths resolve againstprocess.cwd(); absolute paths pass through. ACwdErroris raised when the path does not exist or is not a directory. - f872268: Fix session capture, which always failed with "Could not find the file". Sandcastle was looking for session JSONLs under a
sessions/subdirectory that Claude Code does not actually use.
- 800e743: Restructure hooks API to group by execution location (
hostvssandbox). The old flathooks: { onSandboxReady }shape is replaced withhooks: { host?: { onWorktreeReady?, onSandboxReady? }, sandbox?: { onSandboxReady? } }. Host hooks run on the developer's machine; sandbox hooks run inside the container. Breaking change (pre-1.0).
- 4515aa9: Add
copyFileInandcopyFileOutmethods toBindMountSandboxHandlefor moving individual files between the host and the sandbox. Docker usesdocker cp, Podman usespodman cp, and the newtestBindMount()provider uses a plain filesystem copy. - 3aa9d9a: Fix Podman sandbox failing on macOS when host UID differs from 1000 by chowning /home/agent to the host UID:GID after container start, matching Docker provider behavior.
- 0a84413: Breaking: Replace
RunResult.iterationsRunwithRunResult.iterations: IterationResult[]. EachIterationResultcarries an optionalsessionIdextracted from Claude Code's stream-json init line. Consumers needing the iteration count should readiterations.length. Non-Claude agent providers producesessionId: undefined. The same change applies toOrchestrateResult,SandboxRunResult, andWorktreeRunResult. - 85eb071: Add session capture and resume for Claude Code:
- Capture: after each iteration, the agent's session is saved to the host at
~/.claude/projects/<encoded>/sessions/<id>.jsonlso it can be replayed or inspected locally with Claude Code's usual tooling. AddscaptureSessionsoption toclaudeCode()(defaulttrue) andsessionFilePathtoIterationResult. - Resume: adds
resumeSessionoption torun()for continuing a prior Claude Code conversation in a new sandbox run. Incompatible withmaxIterations > 1. - Exposes the underlying
SessionStoreinterface andtransferSessionhelper for users who want to move sessions between the host and a sandbox directly.
- Capture: after each iteration, the agent's session is saved to the host at
- c8cfcc6: Add timeout to the isolated provider
copyPathsloop instartSandbox. The entire copy loop is now wrapped withwithTimeout(120s), producing aCopyToWorktreeTimeoutErroron expiry, consistent with the per-step timeout pattern used elsewhere in the sandbox lifecycle. - bab11e9: Add
networkoption to Docker and Podman sandbox providers for custom container networking - a2c580f: Make Dockerfile generation aware of the selected backlog manager. When "beads" is chosen, the Dockerfile installs beads CLI tools instead of GitHub CLI.
- a2fd5ad: Generate
.env.exampledynamically duringsandcastle initbased on selected agent and backlog manager instead of copying a static file from the template directory. - 20741fe: Fix parallel-planner templates to use {{CLOSE_TASK_COMMAND}} placeholder instead of hardcoded "close the issue" language, and replace "GitHub issue" with backlog-agnostic wording
- b7880ec: Make
prompt/promptFileoptional ininteractive()— when neither is provided, the agent TUI launches with no initial prompt (the full prompt pipeline is skipped). - aea1131: Add per-step timeouts across the sandbox lifecycle. Every lifecycle step is now wrapped with
Effect.timeoutFailvia awithTimeoututility, producing a step-specific tagged error on expiry. Breaking:TimeoutErrorrenamed toAgentIdleTimeoutErrorwithtimeoutMsfield replacingidleTimeoutSeconds. - c261079: Support relative paths in MountConfig for bind-mount sandbox providers.
hostPathrelative paths resolve fromprocess.cwd(), andsandboxPathrelative paths resolve from the sandbox repo directory. - d13acc3: Remove unnecessary
copyToWorktreeandbranchStrategyfrom planner and merger agents in parallel planner templates. These lightweight agents (maxIterations: 1) now default to head mode, avoiding the overhead of copying node_modules into worktrees. - 0f8a99a: Remove semaphore concurrency limiter from parallel-planner-with-review template. Issue pipelines now run concurrently via Promise.allSettled without a concurrency cap, matching the parallel-planner template.
- bf23e83: Rename workspace terminology back to worktree across the codebase. All public API types and functions renamed from
Workspace*toWorktree*(e.g.createWorktree(),Worktree,WorktreeBranchStrategy).copyToWorkspacerenamed tocopyToWorktree.sandboxWorkspacePathrenamed tosandboxRepoPathandSANDBOX_WORKSPACE_DIRtoSANDBOX_REPO_DIRfor sandbox-internal paths. Source files renamed accordingly (WorktreeManager.ts,CopyToWorktree.ts,createWorktree.ts).
- 6d0c1fb: Make
sandboxoptional inInteractiveOptions, defaulting tonoSandbox()
- fdeccd4: Change agent provider
buildPrintCommandandbuildInteractiveArgsto accept an options object{ prompt, dangerouslySkipPermissions }instead of a bare prompt string. TheclaudeCode()factory now conditionally includes--dangerously-skip-permissionsbased on the boolean. - f413493: Add backlog manager selection to
sandcastle init(GitHub Issues or Beads). All templates use placeholders ({{LIST_TASKS_COMMAND}},{{VIEW_TASK_COMMAND}},{{CLOSE_TASK_COMMAND}}) replaced at scaffold time with the correct commands for the chosen manager. Parallel-planner uses{ id: string }instead of{ number: number }in plan JSON,TASK_IDinstead ofISSUE_NUMBERin prompt args, and raw IDs in log output. Selecting Beads skips the "Create Sandcastle label" step. - 0e2e5fe: Fix
sandcastle initto strip--label Sandcastlefrom scaffolded prompt files when user declines label creation - f413493: Add
interactive()API for launching interactive agent sessions inside sandboxes, replacing the oldinteractiveCLI command. Includes thesandbox.interactive()method oncreateSandbox(), full prompt preprocessing (promptFile, shell expressions, argument substitution), all three branch strategies,onSandboxReadyhooks,copyToWorkspacefor worktree providers, env resolution, andinteractiveExecon Docker and Podman providers. ClackDisplay now shows intro/summary and progress (creating worktree, copying files, starting sandbox, syncing, merging, commit collection) for interactive sessions. - 29d224d: Add interactive arg collection for missing prompt arguments. When
interactive()encounters{{KEY}}placeholders with no matching prompt argument, it prompts the user at the terminal via@clack/promptstext input. Built-in args (SOURCE_BRANCH,TARGET_BRANCH) are excluded from prompting.run()behavior is unchanged. - 83a86f6: Add no-sandbox provider for interactive mode.
noSandbox()runs the agent directly on the host with no container isolation — only accepted byinteractive(), notrun()orcreateSandbox(). The agent does not receive--dangerously-skip-permissions, so the user manages permissions themselves. Import from@ai-hero/sandcastle/sandboxes/no-sandbox. - f413493: Fix Podman integration: rootless mode support with
--userns=keep-idflag (configurable viausernsoption), pre-flight image existence check, Podman Machine detection on macOS/Windows, 5s timeout on signal handler cleanup, correct:ro,zsyntax for SELinux-labeled readonly bind mounts, andinteractiveExecfor interactive agent sessions viapodman exec -it. - 0cde1a2: Add PodmanLifecycle module and
sandcastle podman build-image/sandcastle podman remove-imageCLI commands, mirroring the existing Docker CLI commands for Podman users. - 530a8af: Fix Podman container crashes: rename base image's
nodeuser (UID 1000) toagentinstead of creating a new user, so--userns=keep-idmaps to the correct home directory owner. Override entrypoint inpodman runto avoid double-sleep when the image already definesENTRYPOINT ["sleep", "infinity"]. - 8bcb78e: Add post-agent logging to withSandboxLifecycle for syncing, merging, and commit collection phases
- 1844288: Rename
copyToSandboxoption tocopyToWorkspaceacross the public API (run(),interactive(),createSandbox()) and rename internal moduleCopyToSandbox.tstoCopyToWorkspace.ts. This aligns with the formalized distinction between "sandbox" (isolation boundary) and "workspace" (directory where the agent runs). No behavior changes. - 35feb6f: Add sandbox provider selection (Docker / Podman) to
sandcastle init. Selecting Podman writesContainerfileinstead ofDockerfileand uses Podman-specific build commands. - c54e389: Show per-command estimated token counts in the "Expanding shell expressions" taskLog after shell expressions resolve
- e84ffe3: Add a Codex
effortoption that forwardsmodel_reasoning_effortto Codex for exec and interactive runs.
- 98d22da: Add
applyToHostlifecycle callback toSandboxInfoso isolated providers can sync changes to the host worktree before host-side git operations. FixbaseHeadrecording to use the host worktree instead of the sandbox, ensuring correct commit collection aftersyncOutcreates new SHAs viaformat-patch/am. - be40c63:
createSandbox()now uses the sharedstartSandboxhelper, adding support for isolated sandbox providers (e.g. Vercel, Daytona). Eachrun()call syncs commits back to the host worktree viaapplyToHost. - 0d393c9: Write SandboxError messages to the log file when run() fails in file-logging mode
- c0a4db3: Isolated sandbox providers now create worktrees, matching the bind-mount lifecycle. This enables proper branch strategy support (merge-to-head and named branches) and failure-mode worktree preservation for isolated providers.
- 973ed21: Run onSandboxReady hooks and shell expressions in parallel for faster environment setup
- 4f99506: Allow optional whitespace inside prompt argument placeholders so that both
{{ARG}}and{{ ARG }}resolve identically
- e3fd351: Add
sudooption to hook commands andexec()interface for running commands with elevated privileges inside sandboxes - a30acb3: Strip matching surrounding quotes from .env file values so that
KEY="value"andKEY='value'are parsed asvalueinstead of including literal quote characters - f1fdd4f: Log files now append between runs instead of overwriting. Each run writes a
--- Run started: <ISO timestamp> ---delimiter header, preserving logs from previous runs of the same branch+agent combination.
- cd2a219: Fix templates crashing with "copyToSandbox is not supported with head branch strategy" by adding explicit
branchStrategy: { type: "merge-to-head" }to all templaterun()calls that usecopyToSandbox. - 2cafddd: Use sandbox provider's
workspacePathinstead of hardcoded/home/agent/workspacefor sandbox-side commands, fixing Vercel sandbox support where the workspace is at/vercel/sandbox/workspace.
-
0bb95e2: Add CODING_STANDARDS.md to reviewer-based templates (sequential-reviewer, parallel-planner-with-review) so the reviewer agent has concrete standards to enforce during code review.
-
bb444af: Add optional
mountsconfig todocker()andpodman()providers for mounting host directories (e.g. package manager caches) into sandbox containers. Each mount supportshostPath(with~expansion),sandboxPath, and optionalreadonlyflag. Throws a clear error if a host path does not exist. -
16315da: Add Daytona isolated sandbox provider (
@ai-hero/sandcastle/sandboxes/daytona) -
a8e7d72: Add OpenCode as a built-in agent provider. The
opencode()factory returns anAgentProviderthat invokesopencode runwith raw stdout passthrough (no JSON stream parsing). Includes CLI registry entry, init scaffold with Dockerfile template, and documentation. -
9d6dfba: Add
parallel-planner-with-reviewtemplate that combines parallel execution with per-branch code review usingcreateSandbox. Also fixmaxIterationsdefaults: sequential-reviewer reviewer 10→1, parallel-planner merger 10→1. -
859f2f5: Add Podman sandbox provider (
sandcastle/sandboxes/podman) as a bind-mount provider mirroring Docker's behavior with SELinux label support -
d917d69: Allow sandbox providers and agent providers to accept
env: Record<string, string>at construction time. Provider env is merged with the.sandcastle/.envresolver output at launch, with provider values taking precedence. Agent and sandbox provider env must not have overlapping keys. -
6192024: Add
throwOnDuplicateWorktreeoption toRunOptionsandCreateSandboxOptions. When set tofalse, a worktree collision reuses the existing worktree instead of failing. Defaults totrue(current behavior). -
22ec222: Add Vercel isolated sandbox provider (
sandcastle/sandboxes/vercel) using@vercel/sandboxSDK -
0d08a33: Buffer Pi provider text deltas before display to prevent one-word-per-line terminal output in stdout mode
-
448c9da: Support directories in
copyInfor isolated sandbox providers and renamecopyOuttocopyFileOut -
c30f690: Derive CLI version from package.json instead of hardcoding it.
-
6e7738d: Fix sequential-reviewer template: replace broken prompt argument placeholders with self-contained issue selection and closure logic matching the simple-loop pattern
-
a43cfe4: Merge
execandexecStreaminginto a singleexecmethod with an optionalonLinecallback in options.Breaking change (pre-1.0): The
execStreamingmethod has been removed fromBindMountSandboxHandle,IsolatedSandboxHandle, andSandboxService. Useexec(command, { onLine: (line) => ... })instead.Migration: Replace
handle.execStreaming(cmd, onLine, { cwd })withhandle.exec(cmd, { onLine, cwd }). -
d1b75e4: Move
branchStrategyfrom sandbox provider config torun()options. Branch strategy is now specified as an optional field onRunOptionsinstead of on provider factory functions likedocker(). When omitted, defaults to{ type: "head" }for bind-mount providers and{ type: "merge-to-head" }for isolated providers. Using{ type: "head" }with an isolated provider now throws a clear runtime error. -
8265b88: Remove Docker-specific language from JSDoc comments on provider-agnostic APIs
-
90c017d: Reset idle timer on any stdout line from the sandbox, not just parsed structured events. This prevents false idle timeouts for providers that emit non-JSON output (e.g. TUI-based agents).
- 40a756f: Replace
worktreeconfig withbranchStrategyon the sandbox provider. DefineBranchStrategytypes (head,merge-to-head,branch) and wire them into bind-mount and isolated providers.IsolatedSandboxProviderexposesbranchStrategy(defaulting to{ type: "merge-to-head" }),testIsolated()accepts abranchStrategyoption, and TypeScript prevents{ type: "head" }on isolated providers at compile time. The deprecatedworktreefield onRunOptionsand theWorktreeModetype have been removed. README documentation, code examples, the "How it works" section, and option tables have been updated to usebranchStrategyterminology throughout.
- 6a16d69: Make chownInContainer non-fatal so sandbox startup doesn't crash when chown -R fails on macOS VirtioFS read-only bind mounts
- 105f1ef: Fix pi parser to handle current pi-mono JSON stream format
- 7bf0961: Remove TokenUsage feature from all providers and orchestrator. The TokenUsage interface, extractUsage helper, formatUsageRows function, and usage summary display have been deleted. ParsedStreamEvent's result variant no longer carries a usage field.
- c8df3a1: Point users to #191 for using Claude subscription instead of an API key in .env.example, README, and init CLI output
-
5b04e73: ### Breaking changes
sandboxis now a required option onrun()andcreateSandbox()imageNameremoved from top-levelRunOptionsandCreateSandboxOptions— image configuration now lives inside the sandbox provider (e.g.docker({ imageName }))docker()factory is exported exclusively from@ai-hero/sandcastle/sandboxes/dockersandcastle build-imageandsandcastle remove-imageare nowsandcastle docker build-imageandsandcastle docker remove-image
- Pluggable sandbox provider abstraction with bind-mount and isolated provider types
createBindMountSandboxProviderandcreateIsolatedSandboxProviderfactories- Filesystem-based test isolated provider
- Git bundle sync-in for isolated providers
copyToSandboxsupport for isolated providers viacopyInafter sync-in- Git format-patch/am sync-out for committed changes
- Git diff/apply sync-out for uncommitted changes
- Untracked file extraction via
copyOutback to the host - Artifact persistence and recovery for failed sync-out (patches saved to
.sandcastle/patches/<timestamp>/)
- 4d79ab9: Add optional
effortparameter toclaudeCode()for controlling Claude Code's reasoning effort level (low,medium,high,max)
- 01846be: Fix Docker sandbox failing when run from a git worktree. When
.gitis a worktree file (not a directory), also mount the parent repository's.gitdirectory so git can resolve the repository inside the container.
- 008e539: Use
.mtsextension for scaffolded main file to fix ESM resolution in projects without"type": "module"in package.json. When the project's package.json has"type": "module", the file is scaffolded asmain.tsinstead.
- fc62054: Fixed npm global install permission error in PI and Codex agent Dockerfiles by running
npm install -gas root before switching to theagentuser.
- 674e426: Add
{ mode: 'none' }worktree variant that bind-mounts the host working directory directly into the sandbox container. No worktree is created, pruned, or cleaned up, and no merge step runs after iterations complete. Commits go directly onto the host's checked-out branch.copyToSandboxthrows a runtime error withmode: 'none'. BothSOURCE_BRANCHandTARGET_BRANCHbuilt-in prompt arguments resolve to the host's current branch.
- 77765bb: Add codex agent provider:
codex(model)factory, stream parser for Codex CLI's--jsonJSONL output, Dockerfile template, init scaffolding, and CLI support - 1f2134d: Add pi as a supported agent provider.
pi(model)factory function is exported from@ai-hero/sandcastle. Pi's--mode jsonJSONL output is parsed correctly (message_update, tool_execution_start, agent_end events).sandcastle init --agent piscaffolds a working setup with pi's Dockerfile and correctmain.ts.sandcastle interactive --agent pilaunches an interactive pi session. - 3aff5f5: Refactor AgentProvider to runtime-only factory pattern.
run()now requiresagent: claudeCode("model")instead ofmodel: "...". TheclaudeCodefactory andAgentProvidertype are now exported from the package. Removed:getAgentProvider,parseStreamJsonLine,formatToolCall,DEFAULT_MODELfrom public API. - 75b4400: Bump default idle timeout from 5 minutes to 10 minutes to reduce spurious TimeoutError failures during long agent operations
- c62b429: Wire CLI interactive command for multi-agent support. The
interactivecommand now accepts--agentand--modelflags, uses the provider'sbuildInteractiveArgs()for docker exec, and displays the provider name in status messages. - b1dd427: Add
createSandbox()programmatic API for reusable sandboxes across multiplerun()calls - 54e76e0: Decouple init scaffolding from runtime providers.
envManifestanddockerfileTemplateremoved fromAgentProviderinterface.sandcastle initnow has--agentand--modelflags with interactive agent selection. Dockerfile templates owned by init's internal registry. Each template carries a static.env.examplefile copied as-is during scaffold. Scaffoldedmain.tsis rewritten with the selected agent factory and model. - f35fa48: Log periodic idle warnings every minute of agent inactivity
- fabf0f7: Use run name instead of agent name in worktree and branch naming. When a
nameis provided torun(), worktree directories and temp branches now include the run name (e.g.sandcastle/<name>/<timestamp>) instead of the agent provider name. RenamedsanitizeAgentNametosanitizeName. - cce183a: Replace top-level
branchoption onRunOptionswith aworktreediscriminated union that explicitly models two workspace modes:{ mode: 'temp-branch' }(default) and{ mode: 'branch', branch: string }. This is a breaking change — the oldbranchfield is removed.
-
783b4cd: Base worktree cleanup on uncommitted changes rather than run success/failure.
Previously, worktrees were always preserved on failure and always removed on success. Now the decision is based on whether the worktree has uncommitted changes (unstaged modifications, staged changes, or untracked files):
- Success + clean worktree: remove silently (same as before)
- Success + dirty worktree: preserve and print "uncommitted changes" message
- Failure + clean worktree: remove and print "no uncommitted changes" message
- Failure + dirty worktree: preserve with current preservation message
RunResultnow includes an optionalpreservedWorktreePathfield set when a successful run leaves a worktree behind due to uncommitted changes.TimeoutError.preservedWorktreePathandAgentError.preservedWorktreePathare only set when the worktree is actually preserved (dirty), not on every failure.
-
5eef716: Inject
{{SOURCE_BRANCH}}and{{TARGET_BRANCH}}as built-in prompt arguments. These are available in any prompt without passing them viapromptArgs. Passing either key inpromptArgsnow fails with an error. -
78ef034: Fix sandbox crash on macOS by setting
HOME=/home/agentin the container environment. Previously, Docker's--userflag causedHOMEto default to/, makinggit config --globalfail with a permission error on//.gitconfig. -
fed9a66: Replace wall-clock timeout with idle-based timeout that resets on each agent output event.
- Rename
timeoutSeconds→idleTimeoutSecondsinRunOptionsandOrchestrateOptions - Change default from 1200s (20 min) to 300s (5 min)
- Timeout now tracks from last received message (text or tool call), not run start
- Error message updated to: "Agent idle for N seconds — no output received. Consider increasing the idle timeout with --idle-timeout."
- Rename
-
b16e0e0: Support multiple completion signals via
completionSignal: string | string[]. The result fieldwasCompletionSignalDetected: booleanis replaced bycompletionSignal?: string— the matched signal string, orundefinedif none fired. -
0f48ef8: Preserve worktree on failure (timeout, agent error, SIGINT, SIGTERM)
When a run session ends in failure, the sandbox (Docker container) is removed but the worktree is now preserved on the host. A message is printed with the worktree path and manual cleanup instructions. On successful completion, both the sandbox and worktree are removed as before.
TimeoutErrorandAgentErrornow carry an optionalpreservedWorktreePathfield so programmatic callers can inspect or build on the preserved worktree.
- 1cd8bdb: Remove single-branch shortcut in parallel-planner template; always use the merge agent
- 1cd8bdb: Close GitHub issue when single-branch merge is performed directly in parallel-planner template
- 8e08f7e: Document custom completion signal in the Early termination README section
- 6f9d3be: Fix CLI option tables to show correct default
--image-nameassandcastle:<repo-dir-name>instead ofsandcastle:local - 4c94c5f: Fix README incorrectly describing
.sandcastle/prompt.mdas a default forpromptFile. NeitherpromptnorpromptFilehas a default — omitting both causes an error. The.sandcastle/prompt.mdpath is a convention scaffolded bysandcastle init, not an automatic fallback. - 0d93587: Include run name in log filename to prevent overwrites in multi-agent workflows. When
nameis passed torun(), it is appended to the log filename (e.g.main-implementer.loginstead ofmain.log). - 26683b5: Lead the API section with a simple run() example before the full options reference.
- 3e32b7b: Remove
sandcastle interactiveCLI command documentation from README - 762642e: Remove stale
patches/entry from scaffolded.sandcastle/.gitignore. Nothing in Sandcastle creates a.sandcastle/patches/directory — the worktree-based architecture eliminated patch-based sync.
- 8b43a04: Remove pnpm/corepack from default sandbox Dockerfile template. The base Node.js image already includes npm, so the
corepack enablestep is unnecessary overhead. All init templates now usenpm installandnpm runinstead of pnpm equivalents. - 925506d: Replace pnpm with npm in README documentation
- 74b3f3b: Replace pnpm with npm in scaffold templates. All generated prompt files and main.ts hooks now use
npm installandnpm runinstead of pnpm, consistent with the project's migration to npm.
- 3ece5cb: Removed unused
mkdir -p /home/agent/reposfrom Dockerfile template. The workspace is bind-mounted at/home/agent/workspace, so this directory was never used.
- 0f61f59: Filter issue lists by
Sandcastlelabel in all templates.sandcastle initnow offers to create the label on the repo.
- a5cff39: Hide
agentoption from public API. Theagentfield has been removed fromRunOptionsand the--agentCLI flag has been removed frominitandinteractivecommands. Agent selection is now hardcoded toclaude-codeinternally. The agent provider system remains as an internal implementation detail.
-
f11fd90: Add JSDoc comments to all public-facing type properties:
RunResult,LoggingOption, andPromptArgs. -
1fc5e32: Add kitchen-sink
run()example to README with inline JSDoc-style comments on every option. Also updates theRunOptionstable to remove the hiddenagentfield, fix themaxIterationsdefault (1, not 5), fix thetimeoutSecondsdefault (1200, not 900), update theimageNamedefault, and add the missingnameandcopyToSandboxfields. Removes the removed--agentflag from thesandcastle initandsandcastle interactiveCLI tables. -
b713226: Migrate from npm to pnpm across the project (issue #168).
- Added
packageManager: "pnpm@10.7.0"topackage.json - Generated
pnpm-lock.yaml(replacespackage-lock.json) - Updated CI and release workflows to use
pnpm/action-setupandpnpmcommands - Updated all template
main.tsfiles to usepnpm installinonSandboxReadyhooks - Updated all prompt files (
.sandcastle/andsrc/templates/) to referencepnpm run typecheckandpnpm run test - Updated
README.mddevelopment and hooks examples to use pnpm - Updated
InitService.tsnext-steps text to reference pnpm
- Added
-
cd429c0: Replace --ff-only with regular merge for worktree merge-back (issue #162)
When the agent finishes, Sandcastle now uses
git mergeinstead ofgit merge --ff-onlyto integrate the temp branch back into the host branch. This allows users to make commits on the host branch while Sandcastle is running without causing merge-back failures. Fast-forward still happens naturally when the host branch hasn't moved; only the requirement that it must fast-forward is removed. -
db3adec: Show run name instead of provider name in log-to-file summary (issue #160).
When
nameis passed torun(), it now appears as theAgentvalue in the run summary instead of the internal provider name (claude-code). When no name is provided the provider name is used as before. -
df9fe6c: Surface tool calls in run logs (issues #163, #164, #165, #166).
parseStreamJsonLinenow returns an array of events per line. Assistant messages may producetextand/ortool_callitems. Tool calls are filtered to an allowlist (Bash, WebSearch, WebFetch, Agent) with per-tool arg extraction, and displayed interleaved with agent text output. The Display service gains atoolCall(name, formattedArgs)method rendered as a dim-styled step in terminal mode and a plain log line in log-to-file mode. -
dbe5989: Update 'How it works' section in README to describe the worktree-based architecture, replacing the outdated sync-in/sync-out description. Also fix related references to sync-in/sync-out throughout the README.