Skip to content

agentHost/claude: Phase 5 + Phase 6 — IAgent provider, sendMessage (re-land)#314533

Open
TylerLeonhardt wants to merge 7 commits intomainfrom
tyler/claude-ahp-phase6
Open

agentHost/claude: Phase 5 + Phase 6 — IAgent provider, sendMessage (re-land)#314533
TylerLeonhardt wants to merge 7 commits intomainfrom
tyler/claude-ahp-phase6

Conversation

@TylerLeonhardt
Copy link
Copy Markdown
Member

Summary

Re-submits the Claude IAgent provider (Phase 5 + Phase 6) for the Agent Host. The original Phase 6 PR (#314216) was reverted in #314358; this PR re-lands that work plus three follow-ups: PR-review fixes, a stale-dep cleanup, and the SDK bump from 0.2.112 → 0.2.128 (which now ships native binaries as platform-specific packages).

Implements phase5-plan.md and phase6-plan.md of the Claude-in-Agent-Host roadmap. Tools remain denied (canUseTool: 'deny') — Phase 7 owns tool wiring.

Commits in this stack

  1. agentHost/claude: post-Phase-4 cleanup
  2. agentHost/claude: lock Phase 5 implementation plan
  3. agentHost/claude: Phase 5 — IAgent provider skeleton
  4. agentHost/claude: Phase 6 — sendMessage, single-turn, no tools
  5. agentHost/claude: address PR review (listSessions resilience, dispose abort)new since agentHost/claude: Phase 6 — sendMessage, single-turn, no tools #314216
  6. Drop stale @anthropic-ai/sandbox-runtime dep from merge resolutionnew
  7. Bump @anthropic-ai/claude-agent-sdk 0.2.112 → 0.2.128new

Phase 6 highlights

  • Materialization race safety. ClaudeAgent.sendMessage routes through _sessionSequencer so concurrent first sends collapse into one materialize + N ordered sends — no double-fork of the SDK subprocess.
  • Two abort gates in _materializeProvisional. Post-sdk.startup() and post-_writeCustomizationDirectory() checks ensure that a disposeSession landing mid-materialize disposes the produced WarmQuery instead of leaking a subprocess into _sessions.
  • Session ↔ mapper split. ClaudeAgentSession owns the prompt iterator + per-turn deferreds; mapSDKMessageToAgentSignals is a pure reducer with state owned by the session — independently testable, no shared mutable state.
  • IClaudeAgentSdkService.startup() added alongside listSessions() so the agent can be tested with a fake SDK without spawning a real subprocess.
  • Reducer ordering invariant. SessionResponsePart is allocated on content_block_start before any delta arrives, so the protocol's "part-must-precede-delta" invariant holds by construction.

Post-revert follow-ups

PR-review fixes (3a4bcadb6cd):

  • listSessions wraps _sdkService.listSessions() in try/catch. AgentService.listSessions fans out across providers via Promise.all; an SDK dynamic-import failure would otherwise nuke every other provider's session list. Now logs and returns [].
  • dispose aborts _provisionalSessions AbortControllers before super.dispose(). Previously a racing first sendMessage parked inside _writeCustomizationDirectory could pass the materialize abort gates and call _sessions.set on a disposed DisposableMap, orphaning the WarmQuery. Aborting first triggers the existing post-customization-write abort gate, which asyncDisposes the WarmQuery.

SDK bump 0.2.112 → 0.2.128 (ed182229c7f):

  • New SDK ships a ~200MB claude executable per platform via 8 optional platform-specific packages (@anthropic-ai/claude-agent-sdk-{darwin,win32,linux}-{x64,arm64}{,-musl}), mirroring the @github/copilot pattern.
  • build/lib/claudeAgentSdk.ts mirrors build/lib/copilot.ts to strip off-target packages from the build output.
  • gulpfile.vscode.ts asar-unpacks @anthropic-ai/claude-agent-sdk-* so the executable keeps exec permissions; gulpfile.reh.ts applies the filter for the REH build.
  • alpine-{arch} maps to linux-{arch}-musl (claude is statically linked against libc and must match the host).
  • 8 new cglicenses.json entries mirroring the parent SDK's © Anthropic PBC. All rights reserved. text.
  • New Query.readFile method stubbed in FakeQuery / RoundTripQuery test doubles.

Tests

  • 45 unit tests in claudeAgent.test.ts covering materialize lifecycle (provisional/abort/double-materialize), prompt iterator pumping, dispose ↔ shutdown ↔ send race matrix, the C1 dispose-during-customization-write regression, mapper output for text + thinking + result, the SDK loader's cache-and-log-once contract, plus the two new regressions added in 3a4bcadb6cd.
  • 2 proxy-backed integration tests in claudeAgent.integrationTest.ts: real ClaudeProxyService (loopback HTTP server) + stubbed Copilot CAPI streaming canned MessageStreamEvents — exercises the full agent → proxy → CAPI → SSE → agent pipeline including the ANTHROPIC_AUTH_TOKEN nonce contract.

Out of scope (deferred per roadmap)

  • Tools beyond canUseTool: 'deny' (Phase 7)
  • Fork via config.fork (Phase 6.5 — needs message-UUID lookup)
  • Mode dropdown / branch UI (Phase 7+)
  • Transcript reconstruction in getSessionMessages (Phase 13)
  • Subagent + permission-request signals (Phase 7)

TylerLeonhardt and others added 7 commits May 5, 2026 10:47
- roadmap.md: mark Phase 4 as DONE, link merged PR #313780.
- phase4-plan.md: record live-system smoke completion in §7.8;
  disabled-gate run skipped (covered by unit tests + env-var guard).
- claudeAgent.test.ts: drop gratuitous 'as unknown as' cast in the
  CCAModel fixture (literal already matches CCAModelBilling exactly;
  plan §7.4 forbids unsafe casts in tests).
Handoff plan for Phase 5 (replace 7 throwing stubs in claudeAgent.ts).
Locked against post-PR-#313841 reality (provisional sessions,
onDidMaterializeSession, 30s empty-session GC) and the IAgent contract
on origin/main.

Decisions captured:
- Non-fork createSession is synchronous and in-memory; fork deferred
  to Phase 6 (throws TODO).
- IClaudeAgentSdkService surface mirrors IAgent (no dir parameter on
  listSessions); SDK loader caches resolved module, retries on
  failure, logs once.
- listSessions joins SDK enumeration with workbench session DB
  metadata via ISessionDataService; per-entry try/catch resilience.
- shutdown() routes per-session teardown through the same
  SequencerByKey<string> used by disposeSession() so concurrent
  shutdown/disposeSession cannot double-dispose a wrapper in Phase 6.
- 14 unit tests defined (12 lifecycle + 2 resolved-config), including
  log-once contract and shutdown/disposeSession race guard.
Lands the ClaudeAgent IAgent provider behind the
'chat.agentHost.claudeAgent.enabled' setting (env gate
VSCODE_AGENT_HOST_ENABLE_CLAUDE=1). Pins
@anthropic-ai/claude-agent-sdk@0.2.112 in workspace + remote/.

Implemented in this phase:
* createSession - non-fork, in-memory wrapper only. Honors
  config.session for restore. The fork path and SDK session
  creation are deferred to Phase 6.
* listSessions - SDK is source of truth; per-session DB read
  is a best-effort overlay (failure never excludes an entry).
* disposeSession / shutdown - routed through a per-session
  SequencerByKey to serialize teardown.
* getDescriptor, getProtectedResources, models,
  onDidSessionProgress, setClientCustomizations,
  setClientTools, onClientToolCallComplete,
  setCustomizationEnabled, authenticate, respondTo*Request -
  minimal Phase-5 wiring.

Stubbed for Phase 6 (throw async 'TODO: Phase 6'):
sendMessage, abortSession, changeModel, getSessionMessages,
plus the createSession fork path.

Tests: 29 unit tests in claudeAgent.test.ts cover the
createSession restore-id path, listSessions overlay resilience,
dispose serialization, and stub surfaces.

Note: provisional / onDidMaterializeSession is intentionally
omitted in Phase 5 (see plan section 3.3.1) - the workbench needs
an immediate sessionAdded until the agent has real materialization
work, which arrives in Phase 6 alongside SDK query() startup.
Implements the Phase 6 plan: provisional sessions materialize on first sendMessage, route a single-turn prompt through the Anthropic Claude Agent SDK's WarmQuery, and stream SDKMessages back as protocol AgentSignals via a pure mapSDKMessageToAgentSignals reducer.

Tools remain denied (canUseTool: 'deny'); fork moves to Phase 6.5; Plan Mode UI moves to Phase 7.

Highlights:

- ClaudeAgent.sendMessage routes through _sessionSequencer to collapse concurrent first sends into one materialize + N ordered sends.

- _materializeProvisional has two abort gates (post-startup + post-customizationDirectory write) so disposeSession landing mid-materialize cannot leak a WarmQuery subprocess.

- ClaudeAgentSession owns the prompt iterator + per-turn deferreds; mapSDKMessageToAgentSignals is a pure reducer with state owned by the session.

- IClaudeAgentSdkService gains startup() alongside listSessions().

Tests: 43 unit + 2 proxy-backed integration. Council-review fixes (C1 dispose race, C2 missing integration test, S1 cwd-less ratification) included.
… abort)

Two Copilot-reviewer comments on #314216:

1. listSessions: wrap _sdkService.listSessions() in try/catch. AgentService.listSessions fans out across providers via Promise.all; an SDK dynamic-import failure would otherwise nuke every other provider's session list. Now logs and returns [].

2. dispose: abort _provisionalSessions AbortControllers before super.dispose(). Previously a racing first sendMessage parked inside _writeCustomizationDirectory could pass the materialize abort gates and call _sessions.set on a disposed DisposableMap, orphaning the WarmQuery. Aborting first triggers the existing post-customization-write abort gate, which asyncDisposes the WarmQuery.

Tests: 2 new regressions (listSessions empty on SDK throw; agent.dispose() during racing materialize disposes the WarmQuery). 45/45 unit + 2/2 integration pass.
The new SDK no longer vendors native binaries inside the main package.
It now ships a ~200MB `claude` executable per platform via 8 optional
platform-specific packages, mirroring the @github/copilot pattern:

  @anthropic-ai/claude-agent-sdk-{darwin,win32}-{x64,arm64}
  @anthropic-ai/claude-agent-sdk-linux-{x64,arm64}{,-musl}

The SDK loader picks the right package at runtime via process.platform
/process.arch (and tries -musl first on linux).

To strip off-target packages from the build output:
- build/lib/claudeAgentSdk.ts mirrors build/lib/copilot.ts
- gulpfile.vscode.ts and gulpfile.reh.ts apply the filter alongside
  the existing copilot one
- gulpfile.vscode.ts asar-unpacks @anthropic-ai/claude-agent-sdk-* so
  the executable stays on disk (asar would break exec permissions)
- alpine-{arch} maps to linux-{arch}-musl (claude is statically linked
  against libc and must match the host)

cglicenses.json gets 8 new entries mirroring the parent SDK's
"© Anthropic PBC. All rights reserved." text.

The new SDK Query interface adds a `readFile` method; FakeQuery and
RoundTripQuery test doubles get matching stubs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 5, 2026 20:10
@microsoft microsoft deleted a comment from github-actions Bot May 5, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Re-lands the Claude agent-host provider’s Phase 5/6 work: provisional Claude sessions now materialize on first send, route prompts through the Claude SDK, and stream session signals back through the agent-host protocol. It also wires the new SDK dependency into agent-host packaging/build flows and updates the phase/smoke documentation around the Claude rollout.

Changes:

  • Add the Claude SDK service, session wrapper, prompt resolver, event mapper, and sendMessage/materialization flow in the agent-host Claude provider.
  • Register the Claude SDK service in both agent-host entrypoints and package the platform-specific Claude SDK binaries in desktop/REH builds.
  • Add proxy-backed integration coverage and refresh the Phase 4/5/6 roadmap, plan, and smoke-test documentation.
Show a summary per file
File Description
src/vs/platform/agentHost/test/node/claudeAgent.integrationTest.ts Adds proxy-backed integration tests for Claude agent materialization and proxy auth.
src/vs/platform/agentHost/node/claude/smoke.md Expands the manual smoke plan for Phase 6 sendMessage/materialization behavior.
src/vs/platform/agentHost/node/claude/roadmap.md Marks Phase 4 as completed in the Claude roadmap.
src/vs/platform/agentHost/node/claude/phase6-plan.md Adds the detailed Phase 6 implementation plan.
src/vs/platform/agentHost/node/claude/phase5-plan.md Adds the detailed Phase 5 implementation plan.
src/vs/platform/agentHost/node/claude/phase4-plan.md Records the completed Phase 4 smoke evidence.
src/vs/platform/agentHost/node/claude/claudePromptResolver.ts Introduces prompt/attachment-to-Claude-content-block conversion.
src/vs/platform/agentHost/node/claude/claudeMapSessionEvents.ts Maps Claude SDK stream/result messages into agent-host session actions.
src/vs/platform/agentHost/node/claude/claudeAgentSession.ts Adds the per-session query owner, prompt queue, and SDK message pump.
src/vs/platform/agentHost/node/claude/claudeAgentSdkService.ts Adds the lazy Claude SDK wrapper with listSessions() and startup().
src/vs/platform/agentHost/node/claude/claudeAgent.ts Implements provisional session creation, materialization, config schema, session listing, and real sendMessage.
src/vs/platform/agentHost/node/agentHostServerMain.ts Registers the Claude SDK service in the standalone agent-host server entrypoint.
src/vs/platform/agentHost/node/agentHostMain.ts Registers the Claude SDK service in the regular agent-host entrypoint.
src/vs/platform/agentHost/common/claudeSessionConfigKeys.ts Adds Claude-specific session config keys/types.
remote/package.json Adds the Claude SDK dependency to the REH package manifest.
package.json Adds the Claude SDK dependency to the root product manifest.
package-lock.json Locks the Claude SDK and its platform-specific optional packages.
eslint.config.js Allows importing the Claude SDK from the agent-host code.
cglicenses.json Adds license overrides for the new Claude SDK platform packages.
build/lib/claudeAgentSdk.ts Adds build helpers for keeping only the target Claude SDK platform package.
build/gulpfile.vscode.ts Applies Claude SDK filtering/unpacking in desktop packaging.
build/gulpfile.reh.ts Applies Claude SDK filtering in REH packaging.

Copilot's findings

Files not reviewed (1)
  • remote/package-lock.json: Language not supported
  • Files reviewed: 22/24 changed files
  • Comments generated: 4

Comment on lines +161 to +162
void this._processMessages().catch(err =>
this._logService.error(`[ClaudeAgentSession] _processMessages crashed: ${err}`));
Comment thread cglicenses.json
Comment on lines +840 to +844
{
"name": "@anthropic-ai/claude-agent-sdk-darwin-arm64",
"licenseDetail": [
"© Anthropic PBC. All rights reserved. Use is subject to Anthropic's Commercial Terms of Service."
]
Comment on lines +44 to +49
if (arch === 'armhf') {
// VS Code build uses 'armhf'; Node reports process.arch === 'arm'
nodeArch = 'arm';
}

return `${nodePlatform}-${nodeArch}${muslSuffix}`;
ANTHROPIC_AUTH_TOKEN: `${proxyHandle.nonce}.${sessionId}`,
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
USE_BUILTIN_RIPGREP: '0',
PATH: `${dirname(rgPath)}${delimiter}${process.env.PATH ?? ''}`,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants