Skip to content

Commit 780b5a4

Browse files
Aleksey512claude
andcommitted
fix(runtime): bump Claude Agent SDK to 0.3.220, guard the launched binary
The version guard validated the global `claude` on PATH while the Agent SDK launched its own bundled (2.1.90) binary — a false-positive compatibility check (PR #162 review, P1): the guard passed on 2.1.220 while query() ran 2.1.90 and still crashed on the empty attribution strings. Resolve via the reviewer's option 1: bump @anthropic-ai/claude-agent-sdk 0.2.90 -> 0.3.220, which bundles Claude Code 2.1.220 (>= 2.1.191) through per-platform native binaries, and rewrite the guard so it inspects the exact binary query() will launch: - explicit pathToClaudeCodeExecutable configured -> spawn `<path> --version` (buildClaudeQueryOptions forwards the same path to query()); - otherwise -> read the version of the SDK's bundled binary from its manifest.json (readBundledClaudeVersion), resolved via the SDK main entry since the package exports restrict the manifest subpath. No spawn, no PATH lookup, no platform/musl ambiguity. Removes the misleading discoveredExecutablePath (PATH-CLI) fallback that produced the false positive. Also routes model discovery through the same guard (previously bypassed). The empty-attribution suppression contract and unrelated-settings pass-through from prior iterations are unchanged. Verification: runtime 899 passed (1 integration skipped), agent 376, shared 206; typecheck/format/lint/build green; runtime coverage 85.69% (version.ts 88.88%). Bundled CC confirmed at 2.1.220 via the manifest. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e4c61eb commit 780b5a4

14 files changed

Lines changed: 537 additions & 420 deletions

.docker/Dockerfile

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,15 @@ ENV NODE_ENV=production \
6464
HOME=/home/node \
6565
CODEX_CLI_PATH=/app/node_modules/.bin/codex \
6666
PATH=/app/node_modules/.bin:${PATH}
67-
# Claude Code CLI — Agent SDK spawns it as a subprocess. Pinned (not `latest`)
68-
# so the image is reproducible and guaranteed compatible: builds below 2.1.191
69-
# reject the empty attribution strings used to suppress Co-Authored-By trailers
70-
# and exit with code 1. Override with `--build-arg CLAUDE_CODE_VERSION=...`; the
71-
# runtime adapter additionally enforces >= 2.1.191 via the version guard.
67+
# Claude Code CLI. NOTE: the SDK transport does NOT spawn this — `@anthropic-ai/
68+
# claude-agent-sdk` (pinned in package.json, currently bundling Claude Code
69+
# 2.1.220 via its per-platform native binary) launches its own bundled binary,
70+
# and the runtime version guard inspects that exact artifact. This global
71+
# install backs the CLI transport, the connection/diagnostic probes, and any
72+
# explicit `pathToClaudeCodeExecutable` override. Pinned (not `latest`) so the
73+
# image is reproducible; builds below 2.1.191 reject the empty attribution
74+
# strings used to suppress Co-Authored-By trailers and exit with code 1.
75+
# Override with `--build-arg CLAUDE_CODE_VERSION=...`.
7276
ARG CLAUDE_CODE_VERSION=2.1.220
7377
RUN apt-install ca-certificates gosu git \
7478
&& npm i -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}
@@ -117,6 +121,8 @@ ENV NODE_ENV=production \
117121
# backs spec validation. When absent, those gates do not fail — they SKIP, staying
118122
# registered while enforcing nothing, which reads as a pass in any settings audit.
119123
# Diagnosed 2026-07-23 by a parity audit run live inside this image.
124+
# Claude Code CLI: backs the CLI transport + probes only (the SDK transport uses
125+
# the binary bundled with @anthropic-ai/claude-agent-sdk). See the api stage above.
120126
ARG CLAUDE_CODE_VERSION=2.1.220
121127
RUN apt-install ca-certificates gosu git curl jq python3 gh \
122128
&& npm i -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}

docs/getting-started.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
- **Docker** — Docker Desktop or compatible runtime
88
- **Node.js** 20.19+ or 22.12+ and **npm** 10+ — only needed if running without Docker
9-
- **Claude Code CLI**only needed if running without Docker (`npm i -g @anthropic-ai/claude-code`, version `>= 2.1.191`). The Agent SDK spawns Claude Code as a subprocess, so the CLI must be installed globally. Older builds reject the empty attribution strings used to suppress Co-Authored-By trailers and crash at startup; the runtime enforces `>= 2.1.191` and fails fast with `CLAUDE_VERSION_UNSUPPORTED` otherwise
9+
- **Claude Code CLI**optional unless using the **CLI transport** without Docker (`npm i -g @anthropic-ai/claude-code`, version `>= 2.1.191`). The default SDK transport uses the Claude Code binary **bundled with `@anthropic-ai/claude-agent-sdk`** (currently `2.1.220`), so no global install is required for it; the runtime enforces `>= 2.1.191` and fails fast with `CLAUDE_VERSION_UNSUPPORTED` against an incompatible binary (older builds reject the empty attribution strings used to suppress Co-Authored-By trailers and crash at startup)
1010
- **Claude subscription** or Anthropic API key (for agent features)
1111

1212
## Quick Start with Docker
@@ -130,7 +130,7 @@ despite the Docker layer cache.
130130
## Installation without Docker
131131

132132
```bash
133-
npm i -g @anthropic-ai/claude-code@latest # requiredAgent SDK uses Claude Code CLI (>= 2.1.191)
133+
npm i -g @anthropic-ai/claude-code@latest # optionalonly for the CLI transport (>= 2.1.191); the SDK transport uses the binary bundled with @anthropic-ai/claude-agent-sdk
134134
git clone https://github.com/lee-to/aif-handoff.git
135135
cd aif-handoff
136136
npm install

docs/providers.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,11 @@ Optional proxy mode:
197197

198198
> **Attribution suppression:** the adapter forwards `settings.attribution = { commit: "", pr: "" }` — empty strings hide the Co-Authored-By trailers, per Claude Code's documented contract. These empty strings are forwarded verbatim (not normalized away); collapsing them to `{}` would restore Claude Code's default attribution.
199199
200-
**Minimum Claude Code version: `2.1.191`.** Older builds reject empty attribution strings at startup and the spawned `claude` exits with code 1 and no stderr, which surfaces as the opaque `Claude Code process exited with code 1`. Before every SDK/API run the adapter probes the effective executable's version and fails fast with an actionable `CLAUDE_VERSION_UNSUPPORTED` (category `transport`) error when it is below `2.1.191`, instead of failing opaquely inside the Agent SDK. If the version cannot be determined (binary not on PATH, unparseable output), the check is skipped with a warning and the run proceeds — the real failure, if any, is then explained by the runtime diagnostics.
200+
**Minimum Claude Code version: `2.1.191`.** Older builds reject empty attribution strings at startup and the spawned `claude` exits with code 1 and no stderr, which surfaces as the opaque `Claude Code process exited with code 1`. Before every SDK/API run (and before model discovery) the adapter checks the version of the **exact binary `query()` will launch** and fails fast with an actionable `CLAUDE_VERSION_UNSUPPORTED` (category `transport`) error when it is below `2.1.191`, instead of failing opaquely inside the Agent SDK. The check never inspects an unrelated `claude` on `PATH`: when an explicit executable is configured it spawns `<path> --version`; when none is configured it reads the version of the Claude Code binary **bundled with `@anthropic-ai/claude-agent-sdk`** from that package's `manifest.json` (the precise artifact the SDK launches — no spawn, no PATH/musl ambiguity). If that version cannot be determined, the check is skipped with a warning and the run proceeds — the real failure, if any, is then explained by the runtime diagnostics.
201201

202-
**Effective executable selection.** The SDK transport resolves the `claude` binary in this order: an explicit `pathToClaudeCodeExecutable` / `claudeCliPath` (per-profile or via `execution.hooks`), then the auto-discovered global install (`findClaudePath()``/usr/local/bin/claude`, `~/.local/bin/claude`, npm global prefix, `which claude`), and finally the Agent SDK's own resolution. `normalizeSdkExecutablePath()` drops bare Unix wrapper paths so the SDK can do its own lookup. The Docker image pins the global install via the `CLAUDE_CODE_VERSION` build arg (`2.1.220` by default; override with `--build-arg CLAUDE_CODE_VERSION=...`). To bypass the runtime check in a non-Docker setup, set `AIF_CLAUDE_SKIP_VERSION_CHECK=1`.
202+
**Compatible by construction.** The runtime depends on `@anthropic-ai/claude-agent-sdk` `0.3.220`, whose bundled native Claude Code binary is `2.1.220` (declared in its `manifest.json`) — at/above the `2.1.191` minimum — so the default SDK path accepts the empty attribution strings without further setup.
203+
204+
**Effective executable selection.** The SDK transport launches the binary bundled with `@anthropic-ai/claude-agent-sdk` by default (the Agent SDK resolves its own per-platform native binary; Handoff does not point it at a global install). An explicit override — `pathToClaudeCodeExecutable` via `execution.hooks`, or the adapter constructor's `executablePath` — makes the SDK launch and the guard inspect that exact path instead. (Note: the per-profile `claudeCliPath` option is honored by the **CLI transport** and by **model discovery**, not by the main SDK run path.) `normalizeSdkExecutablePath()` drops bare Unix wrapper paths unless they were explicitly configured. The Docker image additionally installs a global `claude` pinned via the `CLAUDE_CODE_VERSION` build arg (`2.1.220` by default; override with `--build-arg CLAUDE_CODE_VERSION=...`) for the CLI transport and connection/diagnostic probes. To bypass the runtime check, set `AIF_CLAUDE_SKIP_VERSION_CHECK=1`.
203205

204206
### Claude (CLI)
205207

0 commit comments

Comments
 (0)