Skip to content

Claude agent — Phase 7: tool permission round-trip + interactive tools#315399

Merged
TylerLeonhardt merged 2 commits intomainfrom
tyler/retired-duck
May 8, 2026
Merged

Claude agent — Phase 7: tool permission round-trip + interactive tools#315399
TylerLeonhardt merged 2 commits intomainfrom
tyler/retired-duck

Conversation

@TylerLeonhardt
Copy link
Copy Markdown
Member

Phase 7: wires the Claude SDK's canUseTool callback through to the host so tool invocations can be permission-gated, and routes the two interactive built-in tools per their actual semantics.

What's in

  • _handleCanUseTool in claudeAgent.ts routes per-tool: most tools auto-approve; ExitPlanMode is a permission gate (session.requestPermissionpending_confirmation); AskUserQuestion is a structured user-input prompt (session.requestUserInputSessionInputRequested).
  • ClaudeMapperState now tracks tool_usetool_result correlation across messages (the SDK delivers them in separate envelopes), with defense-in-depth cleanup on every result envelope so an unmatched tool_use cannot leak across turns.
  • New claudeInteractiveTools.ts and claudeToolDisplay.ts modules for the interactive-tool registry and tool naming/display.

Tests

  • 88/88 mapper + agent unit tests passing
  • 5/5 integration tests passing, including a full proxy-backed Read-tool round-trip (SDK tool_usepending_confirmationrespondToPermissionRequest(true)tool_result → continuation), and an orphan-tool_use cleanup test that proves the mapper's cross-message maps are drained even when no tool_result arrives.

Docs

phase7-plan.md, CONTEXT.md, roadmap.md, smoke.md updated to reflect the per-tool routing split. ExitPlanMode = permission, AskUserQuestion = user input — these were initially conflated in the plan and have been corrected throughout.

Wires the Claude SDK's canUseTool callback through to the host. ExitPlanMode = permission gate; AskUserQuestion = user-input prompt. Adds cross-message tool_use to tool_result correlation in ClaudeMapperState with defense-in-depth cleanup on result.
Copilot AI review requested due to automatic review settings May 8, 2026 23:13
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

This PR advances the Claude agent integration (Phase 7) by wiring the Claude SDK’s tool permission callbacks through the agent host so tool invocations can be permission-gated, and by correctly routing the two interactive built-in tools (ExitPlanMode and AskUserQuestion) according to their semantics (permission vs structured user input). It also extends the Claude SDK message mapper to correlate tool_usetool_result across multiple SDK envelopes and adds new helper modules plus broad unit/integration test coverage.

Changes:

  • Implement host-side canUseTool handling with per-tool routing: standard tools via pending_confirmation, ExitPlanMode via custom confirmation buttons, AskUserQuestion via SessionInputRequested.
  • Extend the Claude mapper with per-session state to emit tool-call lifecycle actions (SessionToolCallStart/Delta/Complete) and correlate tool_use with later tool_result user envelopes.
  • Add new Claude tool display + interactive-tool projection helpers and snapshot/unit/integration tests; update docs and smoke procedures accordingly.
Show a summary per file
File Description
src/vs/platform/agentHost/node/claude/claudeAgent.ts Wires canUseTool/onElicitation, adds interactive-tool handling, and forwards permission-mode changes between turns.
src/vs/platform/agentHost/node/claude/claudeAgentSession.ts Adds pending permission/user-input maps and threads per-session mapper state into the mapper.
src/vs/platform/agentHost/node/claude/claudeMapSessionEvents.ts Adds stateful mapping for streamed tool_use and synthetic tool_result, and part-id disambiguation across SDK messages.
src/vs/platform/agentHost/node/claude/claudeToolDisplay.ts New tool-name→permission/display helpers and confirmation titles.
src/vs/platform/agentHost/node/claude/claudeInteractiveTools.ts New pure projections for ExitPlanMode confirmation UI and AskUserQuestion input/answer mapping.
src/vs/platform/agentHost/node/sessionPermissions.ts Allows agents to supply custom confirmation options, falling back to standard options.
src/vs/platform/agentHost/test/node/claudeAgent.test.ts Adds Phase 7 unit coverage for permission round-trips, interactive tools, and permissionMode propagation.
src/vs/platform/agentHost/test/node/claudeAgent.integrationTest.ts Extends proxy-backed integration tests to validate callback wiring and a Read-tool round-trip.
src/vs/platform/agentHost/test/node/claudeMapSessionEvents.test.ts Adds direct mapper tests for tool-use/result lifecycle, attribution, and cleanup.
src/vs/platform/agentHost/test/node/claudeMapSessionEventsTestUtils.ts Adds test helpers for input_json_delta and synthetic user tool_result envelopes.
src/vs/platform/agentHost/test/node/claudeToolDisplay.test.ts New snapshot tests for tool display/permission mapping and path extraction.
src/vs/platform/agentHost/test/node/claudeInteractiveTools.test.ts New unit tests for interactive-tool projections and answer flattening.
src/vs/platform/agentHost/node/claude/smoke.md Updates smoke guidance and artifacts for Phase 7 tool/permission/user-input flows.
src/vs/platform/agentHost/node/claude/roadmap.md Updates roadmap notes around permission semantics and deferred enhancements.
src/vs/platform/agentHost/node/claude/phase7-plan.md Updates Phase 7 plan with post-implementation corrections and detailed steps/notes.
src/vs/platform/agentHost/node/claude/phase6.1-plan.md Updates Phase 6.1 notes to reflect ExitPlanMode routing correction.
src/vs/platform/agentHost/node/claude/CONTEXT.md Updates canonical routing table and clarifies SDK-side auto-approval vs host UI bridge behavior.

Copilot's findings

Comments suppressed due to low confidence (1)

src/vs/platform/agentHost/node/claude/claudeToolDisplay.ts:106

  • getClaudeToolDisplayName returns the MCP fallback string using a template literal (Run MCP tool ...) without localization. Since this is user-facing, it should be localize(...) (with a {0} placeholder) like the confirmation titles are.
export function getClaudeToolDisplayName(toolName: string): string {
	const entry = TOOL_DISPLAY[toolName];
	if (entry) {
		return entry.displayName;
	}
	if (toolName.startsWith(MCP_TOOL_PREFIX)) {
		return `Run MCP tool ${toolName.slice(MCP_TOOL_PREFIX.length)}`;
	}
	return toolName;
  • Files reviewed: 17/17 changed files
  • Comments generated: 6

Comment thread src/vs/platform/agentHost/node/claude/claudeInteractiveTools.ts Outdated
Comment thread src/vs/platform/agentHost/node/claude/claudeToolDisplay.ts Outdated
Comment thread src/vs/platform/agentHost/node/claude/claudeAgentSession.ts Outdated
Comment thread src/vs/platform/agentHost/node/claude/claudeAgent.ts Outdated
Comment thread src/vs/platform/agentHost/node/claude/claudeMapSessionEvents.ts Outdated
Comment thread src/vs/platform/agentHost/node/claude/claudeAgent.ts
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

blocks-ci screenshots changed

Replace the contents of test/componentFixtures/blocks-ci-screenshots.md with:

Updated blocks-ci-screenshots.md
<!-- auto-generated by CI — do not edit manually -->

#### editor/codeEditor/CodeEditor/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/67bfb687fd2818bd53771a60660541b9ed6f38b80d37da0aac15d267ecaeacec)

#### editor/codeEditor/CodeEditor/Light
![screenshot](https://hediet-screenshots.azurewebsites.net/images/0469dd8d0a587d94a1eaec514c79917b93b9a38694ef2b767bb1892819ae0a55)

#### editor/inlineChatZoneWidget/InlineChatZoneWidget/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/97162fc53c861ee13dc78a18e41fe3a25a42f62dc52a560510ebf084a418e1c3)

#### editor/inlineChatZoneWidget/InlineChatZoneWidget/Light
![screenshot](https://hediet-screenshots.azurewebsites.net/images/3b7e2eb5cc9ba727e2bc1c5113c3e17d8e9a6ce9a37b77519be3716ceb9a9afa)

#### editor/inlineChatZoneWidget/InlineChatZoneWidgetTerminated/Dark
![screenshot](https://hediet-screenshots.azurewebsites.net/images/97162fc53c861ee13dc78a18e41fe3a25a42f62dc52a560510ebf084a418e1c3)

#### editor/inlineChatZoneWidget/InlineChatZoneWidgetTerminated/Light
![screenshot](https://hediet-screenshots.azurewebsites.net/images/3b7e2eb5cc9ba727e2bc1c5113c3e17d8e9a6ce9a37b77519be3716ceb9a9afa)

- Localize TOOL_DISPLAY display names + MCP fallback (claudeToolDisplay) - Symmetric id derivation in flattenAskUserAnswers / buildAskUserSessionInputQuestions for empty-header questions
- Await Query.setPermissionMode in ClaudeAgentSession.setPermissionMode and at the sendMessage callsite so the SDK acks the mode change before the next prompt yields
- Guard JSON.stringify trace serialization behind getLevel() <= LogLevel.Trace
- Observe options.signal in _handleCanUseTool: deny on already-aborted, race the parked permission/user-input prompt with the abort listener
- Add unit tests covering the empty-header round-trip and both abort paths
@TylerLeonhardt TylerLeonhardt enabled auto-merge (squash) May 8, 2026 23:41
@TylerLeonhardt TylerLeonhardt merged commit 798d93b into main May 8, 2026
24 of 25 checks passed
@TylerLeonhardt TylerLeonhardt deleted the tyler/retired-duck branch May 8, 2026 23:53
@vs-code-engineering vs-code-engineering Bot added this to the 1.120.0 milestone May 8, 2026
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