Sandcastle

@ai-hero/sandcastle v0.9 — agent orchestration / 6 agent providers / sandbox isolation / branch strategy / session resume

1 — 概要

Sandcastle は、AI コーディングエージェントを隔離された sandbox で動かし、変更を branch / worktree 経由で回収する TypeScript CLI / SDK。単発実行なら run()、長く使う環境なら createSandbox()、sandbox なしの探索から始めるなら createWorktree() が中心になる。

1run() で一発起動
6agent provider
5sandbox provider
Niteration loop
2 — 実行フロー

Sandcastle は「prompt を渡す」だけの薄いラッパーではない。sandbox を作る前に host 側で branch / worktree / env / prompt を解決し、sandbox 内で agent を起動し、最後に commit と session を host 側へ戻す。

01
resolve
cwd、branch strategy、prompt source、env、mount を host 側で確定。
02
worktree
必要なら git worktree を作成。named branch は lock で並行利用を防ぐ。
03
sandbox
provider が sandbox handle を作り、hooks と setup を実行。
04
agent
agent provider が command を作り、stream を parse。idle timeout も監視。
05
recover
commit、stdout、session、usage を result に集約。必要なら target branch へ merge。
認知負荷の核: sandbox は実行境界、worktree は変更境界、branch strategy は merge 境界。3 つを分けて見ると設計が読みやすい。
3 — 公開 API
run(options)

単発の AFK agent 実行。sandbox lifecycle を自動管理し、iteration、completion signal、commit、log path を返す。

createSandbox(options)

同じ sandbox と同じ branch を複数回使う。実装 → レビューのような multi-run pipeline 向き。

createWorktree(options)

worktree を first-class に扱う。先に探索し、後で sandboxed AFK run に渡せる。

interactive(options)

agent を対話モードで起動。noSandbox() を指定すれば host 上で直接実行できる。worktree の wt.interactive() は sandbox 未指定時に no-sandbox が既定。

import { run, claudeCode } from "@ai-hero/sandcastle";
import { docker } from "@ai-hero/sandcastle/sandboxes/docker";

const result = await run({
  agent: claudeCode("claude-opus-4-7"),
  sandbox: docker(),
  promptFile: ".sandcastle/prompt.md",
  maxIterations: 3,
});

console.log(result.commits);
4 — Agent Provider

AgentProvider は sandbox 内で起動するコマンド組み立てと output parse を担う。factory 関数に model 文字列を渡して run() / createSandbox() / interactive() へ注入する。

Factory例session capture
claudeCode(model, opts?)claudeCode("claude-opus-4-7", { effort: "high", permissionMode: "auto" })既定 ON。v0.8+ で permissionMode により AFK run の tool approval を bypass 以外に切替可能。
codex(model, opts?)codex("gpt-5.4", { approvalsReviewer: "auto_review" })既定 ON。v0.8+ で approvalsReviewer: "auto_review" が reviewer agent 経由の approval を有効化。
pi(model, opts?)pi("claude-sonnet-4-6", { thinking: "high" })既定 ON
cursor(model)cursor("composer-2")—
opencode(model)opencode("opencode/big-pickle")—
copilot(model)copilot("claude-sonnet-4.5")—
session resume / fork: Claude Code・Codex・Pi は iteration 後に session ファイルを host へ取り込む。v0.9 では Claude Code の subagent / workflow transcript(<session-id>/subagents/agent-*.jsonl)も main session と一緒に capture する(個別失敗は warning、main session 失敗は run 失敗)。RunResult.resume() は同一 session を継続、RunResult.fork() は新 session ID で分岐。fork は session のみを分離し branch / worktree は分離しない(並行 fork には明示 branch が必要)。
5 — Sandbox Provider

SandboxProvider は「どこで agent を動かすか」を差し替える小さな契約。built-in provider は実行場所だけでなく、使える branch strategy と同期方式も変える。

Docker
bind-mount

host の worktree を container へ mount。ローカルで最も一般的。

Podman
bind-mount

rootless 寄りのローカル provider。Docker と同じ型の境界で扱える。

Vercel
isolated

cloud sandbox。code in / commits out の sync が必要な境界。

No-sandbox
none

container を作らず host で agent を直接実行。run() / createSandbox() / interactive() すべてで指定可能。

Daytona
isolated

@daytona/sdk 経由の cloud isolated sandbox。Vercel と同様に code in / commits out の sync が必要。

Provider 型実装者が満たす契約設計上の意味
bind-mountexec(), interactiveExec(), file copy, close()host filesystem へ mount 経由で直接書く。sync が不要。
isolatedexec(), copyIn(), copyFileOut(), close()sandbox 側 filesystem が独立。commit extraction が必要。
no-sandboxhost command execution隔離を省略したい run / interactive。worktree 探索の既定にも使われる。
6 — Prompt と Iteration

Sandcastle の prompt system は薄く、強い workflow 意見を押し付けない。prompt は literal、promptFile は template として扱われる。この差が重要。

入力処理使いどころ
promptそのまま agent へ渡す。promptArgs と併用不可。JavaScript 側で文字列を組む短い指示。
promptFile{{KEY}} substitution と shell expression expansion を実行。promptArgs の null / undefined は v0.9 で PromptError として fail-fast。issue / branch / command output を混ぜる再利用 prompt。
completionSignalagent output に含まれたら iteration loop を早期停止。既定は <promise>COMPLETE</promise>。タスク完了時に無駄な iteration を走らせない。
completionTimeoutSecondscompletion signal 検出後、process が exit しない「hanging process」に対する猶予(既定 60 秒)。signal 後の MCP / 子 process 残留でも commit を失わず成功扱いにできる。
output: Output.object()prompt 内の XML tag から schema 検証済み JSON を抽出。maxIterations === 1 必須。型付き結果を run の戻り値として受け取る。
resumeSessionhost 上の session ファイルを sandbox へ移送し iteration 1 で resume。前回会話を sandbox 内で継続。
7 — 運用ガード

branch strategy

head は host 作業ツリーへ直接、merge-to-head は一時 branch、branch は明示 branch。変更境界が用途で変わる。

worktree lock

named branch の worktree 共有を lock file で fail-fast。待機や retry ではなく「同じ branch を同時に使う caller error」と扱う。

idle / completion timeout

idleTimeoutSeconds(既定 600)は output 停止中の agent を fail-fast。completionTimeoutSeconds は signal 検出後の hanging process 用で、expire 時は warning 付き成功。

  • README.md — Quick start、API、provider、session capture / resume / fork
  • CHANGELOG.md — v0.9.0 subagent transcript capture、init / promptArgs 改善
  • CONTEXT.md — 用語(completion timeout、hanging process、structured output)
  • src/index.ts — 公開 export(6 agent / 5 sandbox factory)
  • src/run.ts / src/createSandbox.ts / src/createWorktree.ts — 実行モデル
  • docs/adr/0019-completion-timeout-for-hanging-process.md — completion timeout
  • docs/adr/0018-fork-is-session-only.md — fork の branch 非分離