update-claude-deps
Review and upgrade Claude Code CLI, Claude Agent SDK, and Anthropic SDK versions used in the project
SKILL.md.claude/skills/update-claude-deps/SKILL.md
GitHubUpdate Claude Dependencies
Review the current versions of Claude-related packages and create an upgrade plan.
Steps
Find current versions by reading these files:
agent-container/package.json— find@anthropic-ai/claude-agent-sdkand@anthropic-ai/sdkversions. The agent SDK version IS the CLI version (the CLI ships inside it as native-binary optional deps; SDK 0.3.x ↔ CLI 2.1.x by patch number)package.json(root) — find@anthropic-ai/sdkversion
Check latest versions by searching the web for:
- Latest
@anthropic-ai/claude-codeversion on npm / GitHub releases - Latest
@anthropic-ai/claude-agent-sdkversion on npm - Latest
@anthropic-ai/sdkversion on npm - Changelogs / release notes for each
- Latest
Analyze the diff between current and latest:
- List new features added between current and latest versions
- Identify any breaking changes or deprecations
- Note bug fixes relevant to our usage (especially memory leaks, stability)
- Check if the npm install method is still supported or if native binary is required
Assess risk based on our actual SDK usage in the codebase:
agent-container/src/claude-code.tsusesquery(),tool(),createSdkMcpServer(), message types- Root project uses
new Anthropic()andclient.messages.create() - Check if any APIs we use have changed
Present findings as a table showing:
- Package name, current version, latest version, delta
- Risk level (low/medium/high) with justification
- Key new features and bug fixes worth upgrading for
- Any breaking changes that would require code modifications
Ask the user if they want to proceed with the upgrade before making changes.
Key files to update when upgrading
agent-container/package.json—@anthropic-ai/claude-agent-sdk(which carries the CLI binary — no separate Dockerfile pin) and@anthropic-ai/sdkpackage.json(root) —@anthropic-ai/sdksrc/api/llm-sdk-bundle.ts— Regenerate after upgrading@anthropic-ai/sdk. This is a pre-built browser bundle of the SDK served to dashboard iframes. Regenerate with:
Thenpx esbuild node_modules/@anthropic-ai/sdk/index.mjs --bundle --minify --format=iife --global-name=__AnthropicSDK_ns --platform=browser --external:'node:*' > /tmp/sdk-bundle.js echo 'window.__AnthropicSDK = __AnthropicSDK_ns.default || __AnthropicSDK_ns.Anthropic;' >> /tmp/sdk-bundle.js--external:'node:*'flag is required since SDK 0.116+: the credential-chain code lazilyimport()snode:fs/node:pathbehind runtime guards that never fire in a browser, and esbuild errors on them without the external marker. Then replace the template literal insrc/api/llm-sdk-bundle.tswith the contents of/tmp/sdk-bundle.js(escape backslashes first, then backticks and${), and verify the escaped literal evaluates back to the exact raw bundle
Validation after upgrading
Re-record the protocol fixtures, then run the container unit suite. Frame ORDER (idle vs result,
background_tasks_changedvs thetask_*bookends) changes between CLI releases without a changelog line — 2.1.296 moved a finishing task's snapshot removal from before its terminal signals to after them — and only a live CLI shows it. Afternpm ciinagent-container/:ANTHROPIC_API_KEY=... node .claude/skills/update-claude-deps/capture-fixtures.mjs # ~1.5 min, a few cents on haikuIt records each scenario (
--list: background Bash, background subagent, Stop with only a background subagent live, a subagent's own background Bash, SendMessage resume) intosrc/shared/lib/container/__fixtures__/sdk<patch>-<name>/in the host capture wrapper ({t, message: {content: <frame>}}), with a privateCLAUDE_CONFIG_DIRso nothing from your~/.claudeleaks in. Diff each new timeline against the previoussdk<patch>-*capture of the same scenario before anything else — that diff is the protocol change. Then add the new directories toFIXTURE_EXPECTATIONSinagent-container/src/session-settlement.test.ts(the discovery test fails until you do) and to theSDK<patch>_FIXTURESreplay block insrc/shared/lib/container/sdk206-fixtures-replay.test.ts, and runcd agent-container && npx vitest runplus the host replay files (npx vitest run src/shared/lib/container). Keep the older captures: they are the regression history for runtimes still on older CLIs.Gated session-GC E2E suites (MANDATORY on any
@anthropic-ai/claude-agent-sdk/ CLI bump — CI never runs these, and they guard CLI-behavior assumptions the idle-eviction reaper depends on):cd agent-container RUN_SESSION_GC_E2E=1 ANTHROPIC_API_KEY=... npx vitest run src/session-gc.e2e.test.ts RUN_SESSION_GC_E2E=1 ANTHROPIC_API_KEY=... npx vitest run src/session-gc-durability.e2e.test.ts RUN_SESSION_GC_E2E=1 ANTHROPIC_API_KEY=... npx vitest run src/session-gc-background-stop.e2e.test.ts- Run the three files separately, never in one vitest invocation: the pgrep zero-process assertions in each see the other worker's CLI subprocesses.
- Costs real API tokens (~$0.08, ~2.5 min total on haiku).
- What they hold in place, and what breaks silently if an SDK/CLI change violates it:
- the CLI does NOT emit
session_state_changed:idlebetween a finished turn and a queued follow-up (violation → the reaper kills queued messages); - an interrupt of a live turn yields a
result+idleso the session settles (violation → every user Stop pins a ~250MB parked subprocess forever); - the CLI exits cleanly on stdin EOF, flushing its transcript (violation → eviction silently loses the latest turns /
shouldQuery:falseappends on the next--resume); --resumeresumes in-place with the same session id, with prior context intact;- (background-stop file) while a background SUBAGENT is live the CLI emits NO idle after the lead turn's result (CLI 2.1.269+; backgrounded Bash still gets the premature idle), the reaper leaves the session alone, a Stop (
scope: 'turn') then is a fast no-op that keeps the process and the subagent (claude-code.ts foregroundTurnEnded/pendingSendsdecide "no foreground turn" — an interrupt sent in that state gets a receipt but never a result, and the fallback restart would kill the subagent), and the completion wake still runs to a result + idle.
- the CLI does NOT emit
Task-tools opt-in (MANDATORY re-verify on any CLI bump):
claude-code.ts buildQueryOptions()lists TaskCreate/TaskGet/TaskList/TaskUpdate inallowedToolsbecause CLI 2.1.233+ registers them by default only on older models (Claude 3.x, Opus 4.0–4.7, Sonnet 4.0–4.6, Haiku 4.5). Since 0.3.268 this is the documented path (SDK changelog: "elsewhere list them intools/allowedTools"); theCLAUDE_CODE_ENABLE_TODO_TOOLS=1env var is the documented CLI-side equivalent and works too (verified live on claude-opus-4-8 with 0.3.272: either path registers all four; TodoWrite is never registered alongside them). The in-chat task-list UI (derive-task-list.ts) and existing agent workflows depend on those tools, and the failure is silent (tools just stop appearing; nothing errors). To re-verify: run theclaude-prompt-driftskill's capture after the bump and confirm the superagent-axistools.mdstill lists all four Task tools. If they've vanished, try the env var as a fallback; if that is gone too, the CLI's only other re-enable path is a server-side feature flag that ourCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1blocks — so escalate to a product decision (patch the gate, or retire the task-list UI's wire dependency) rather than shipping silently.If either E2E file fails, do not ship the bump — check the settlement tracker (
agent-container/src/session-settlement.ts) and graceful-stop path (claude-code.ts stop({graceful:true})) against the new CLI's stream behavior.Vendored bundled skill/workflow drift (MANDATORY on any
@anthropic-ai/claude-agent-sdk/ CLI bump):claude-code.ts buildQueryOptions()setsdisableBundledSkills: true, which strips every skill and workflow that ships inside the CLI, and the image-baked Gamut plugin (agent-container/plugin/, see its README) carries copies of the ones we keep — thedeep-researchworkflow and theworkflow-authoringskill (the Workflow tool's own prompt tells the model to load it; without it the model writes workflow scripts blind). Those copies no longer track upstream, so afternpm ciinagent-container/runnode .claude/skills/update-claude-deps/extract-bundled.mjs- exit 0 → no drift, nothing to do;
- exit 1 → it prints a unified diff per drifted entry against what is embedded in the new binary. Read the diff (prompt wording, vote thresholds, fetch budgets, schema changes, new script-API guidance), then adopt it with
--writeunless a change is clearly wrong for us — each file must stay identical to upstream apart from the entry'spatchesin the extractor'sVENDOREDtable (mechanical one-line substitutions such as thegamut:workflow prefix), so any bigger local edit means forking under a new name instead; - a drift hunk that REMOVES guidance deserves suspicion before
--write: the prompt text is a template whose conditions call gates and env getters, which the extractor stubs as unset (() => undefined). A condition that tests a getter some other way can flip into the wrong branch — grep the body chunk for the hunk's text to see whether it sits inside a${…?…:…}and pin that identifier underconstantsif so; - exit 2 → the extractor could not find or parse a chunk, could not resolve an identifier the prompt text interpolates (pin it under that entry's
constants), or a patch no longer matches upstream. Fix the extractor (its header comment explains the expected shapes) rather than shipping blind. While there, list what the new CLI bundles (grep -a -o 'var i="[a-z-]*",e=i,' <binary>for workflows; theskillsarray in a headless run'ssystem:initmessage for skills) and decide whether anything new is worth vendoring the same way —disableBundledSkillshides all of it, so a useful addition is invisible until someone looks. The reverse leak happens too: a built-in skill is not "bundled" and survives the flag (2.1.296'splugin-authoring), so check the superagent-axismessages.mdskill list from theclaude-prompt-driftcapture for anything not ours (gamut:*or agent-created) and switch it off inclaude-code.tssettings.skillOverrides.
Important notes
- The Claude Code CLI ships inside
@anthropic-ai/claude-agent-sdkas per-platform native-binary optional dependencies (claude-agent-sdk-<os>-<arch>); the Dockerfile symlinks the bundled binary onto PATH. There is no separate install.sh/npm CLI install to pin — bumping the SDK version bumps the CLI. - SDK and CLI versions track in lockstep by patch number (SDK 0.3.x ↔ CLI 2.1.x, e.g. 0.3.238 ↔ 2.1.238)
DISABLE_AUTOUPDATER=1is set in the Dockerfile to prevent runtime updates- Bundled CLI skills/workflows are disabled in agent sessions (
disableBundledSkills);deep-researchandworkflow-authoringare vendored underagent-container/plugin/and checked for drift byextract-bundled.mjsin this skill's directory (validation step 5) - Do NOT run
npm build— use typecheck + lint to verify changes