From 424307c5af0b35184aec6fd7ceaceed7d6183fdc Mon Sep 17 00:00:00 2001 From: Gabriel Brown Date: Sat, 11 Jul 2026 11:23:52 -0400 Subject: [PATCH] build(agent-job): pin Claude Code CLI; smoke one turn per runtime Add @anthropic-ai/claude-code@2.1.207 to the job image global npm install, extend the smoke script with claude --version plus a documented per-runtime turn block, and reconcile OpenCode/Claude adapter flags against the real CLIs (all assumed flags confirmed). Document the anthropic_oauth_json credential snapshot pattern in the README. --- README.md | 11 ++++++++--- apps/agent-worker/src/runtime/claude-adapter.ts | 6 +++--- apps/agent-worker/src/runtime/opencode-adapter.ts | 5 ++--- docker/agent-job.Dockerfile | 2 +- scripts/smoke-agent-container | 12 ++++++++++++ 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index adbf62a..d2e0df3 100644 --- a/README.md +++ b/README.md @@ -206,8 +206,9 @@ SPOON_AGENT_JOB_IMAGE="git.gbrown.org/gib/spoon-agent-job:latest" ``` The job image includes Node 22, Bun, pnpm and yarn through Corepack, npm, git, -ripgrep, Python, build tools, OpenCode, and the Codex CLI. It is not the forked -project's production runtime; it is the agent execution environment. +ripgrep, Python, build tools, OpenCode, the Codex CLI, and the Claude Code CLI. +It is not the forked project's production runtime; it is the agent execution +environment. Production worker runtime requirements: @@ -255,7 +256,11 @@ Deployment readiness checklist: API-key based AI provider profiles run through OpenCode. Codex ChatGPT login profiles run through the Codex CLI: Spoon writes the encrypted `auth.json` into the isolated job workspace as `CODEX_HOME/.codex/auth.json` before execution. -Treat that saved auth file like a password and only use it on trusted workers. +Anthropic profiles run through the Claude Code CLI: API-key profiles authenticate +via `ANTHROPIC_API_KEY`, while `anthropic_oauth_json` credential-snapshot profiles +have their encrypted OAuth blob written into the isolated job workspace as +`~/.claude/.credentials.json` before execution. Treat those saved auth files like +a password and only use them on trusted workers. diff --git a/apps/agent-worker/src/runtime/claude-adapter.ts b/apps/agent-worker/src/runtime/claude-adapter.ts index 50a5839..eb90b2d 100644 --- a/apps/agent-worker/src/runtime/claude-adapter.ts +++ b/apps/agent-worker/src/runtime/claude-adapter.ts @@ -42,9 +42,9 @@ export const createClaudeAdapter: AdapterFactory = (deps) => { workspace.turnMarker = `spoon-turn-${randomUUID()}`; const marker = workspace.turnMarker; - // NOTE: exact flags are UNVERIFIED against the pinned CLI and are confirmed - // in Task 11's smoke. `--verbose` is required for `stream-json`; - // `--resume ` carries continuity. + // Flags verified against `claude --help` (@anthropic-ai/claude-code@2.1.207): + // `-p`, `--output-format stream-json`, `--verbose`, `--dangerously-skip-permissions`, + // `--model`, and `--resume ` (continuity) all exist. const argv = [ 'claude', '-p', diff --git a/apps/agent-worker/src/runtime/opencode-adapter.ts b/apps/agent-worker/src/runtime/opencode-adapter.ts index 9c7b27d..5bf4fc2 100644 --- a/apps/agent-worker/src/runtime/opencode-adapter.ts +++ b/apps/agent-worker/src/runtime/opencode-adapter.ts @@ -57,9 +57,8 @@ export const createOpenCodeAdapter: AdapterFactory = (deps) => { workspace.turnMarker = `spoon-turn-${randomUUID()}`; const marker = workspace.turnMarker; - // NOTE: `--session` continuity is UNVERIFIED against `opencode run --help` and - // may be dropped in Task 11's smoke; if unsupported, OpenCode turns are stateless - // (acceptable — Codex/Claude carry continuity). + // Flags verified against `opencode run --help` (opencode-ai@1.17.9): + // `--format json`, `--model`, and `--session ` all exist. const argv = [ 'opencode', 'run', diff --git a/docker/agent-job.Dockerfile b/docker/agent-job.Dockerfile index 5c15b23..a8a42e1 100644 --- a/docker/agent-job.Dockerfile +++ b/docker/agent-job.Dockerfile @@ -47,7 +47,7 @@ RUN dnf install -y --setopt=install_weak_deps=False --nodocs \ # Package managers + pinned agent CLIs (kept identical to the prior image). # Fedora's nodejs-npm doesn't ship corepack, so install pnpm/yarn via npm. -RUN npm install -g pnpm yarn bun@1.3.10 opencode-ai@1.17.9 @openai/codex@0.142.0 \ +RUN npm install -g pnpm yarn bun@1.3.10 opencode-ai@1.17.9 @openai/codex@0.142.0 @anthropic-ai/claude-code@2.1.207 \ && npm cache clean --force # oh-my-posh prompt (binary only; we ship our own /etc/spoon/omp.json theme). diff --git a/scripts/smoke-agent-container b/scripts/smoke-agent-container index 809cd0c..343a65a 100755 --- a/scripts/smoke-agent-container +++ b/scripts/smoke-agent-container @@ -28,4 +28,16 @@ fi python3 --version opencode --version codex --version + claude --version ' + +# Manual per-runtime smoke (requires creds in your shell env): +# docker build -f docker/agent-job.Dockerfile -t spoon-agent-job:latest . +# docker run -d --name spoon-box-smoke --init spoon-agent-job:latest sleep infinity +# docker exec -e OPENAI_API_KEY -w /workspace spoon-box-smoke \ +# opencode run --format json --model openai/gpt-5.5 -- 'print hello' +# docker exec -e ANTHROPIC_API_KEY -w /workspace spoon-box-smoke \ +# claude -p 'print hello' --output-format stream-json --verbose --dangerously-skip-permissions +# docker exec -e CODEX_HOME=/root/.codex -w /workspace spoon-box-smoke \ +# codex exec --json --dangerously-bypass-approvals-and-sandbox 'print hello' +# docker rm -f spoon-box-smoke