claudeToolCallRegistry.ts ×10

Frontier kind: Code frontier

unlabeled · c_edde387e6edd

272 tests · 19486 LOC · 71 files · introduces 0 tests · 124 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
10 ranges124 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1556 ranges19486 lines · 71 files · Browse complete extent
All tests (intent)
272 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 124 introduced LOC across 10 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/agentHost/node/claude/claudeToolCallRegistry.ts 124 introduced LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- claudeToolCallRegistry.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type { ILogService } from '../../../log/common/log.js';
7 > import type { StringOrMarkdown } from '../../common/state/protocol/state.js';
8 > import { getClaudeInvocationMessage, getClaudeToolDisplayName, getClaudeToolInputString } from './claudeToolDisplay.js';
9 >
10 > /**
11 > * Phase 8.5 — per-tool-call info computed at `content_block_stop` and
12 > * reused at `tool_result` time. Mirrors Copilot's `IToolStartInfo`
13 > * shape: Copilot stashes it at `tool.execution_start` (where the
14 > * Copilot SDK hands over complete args); Claude stashes it at the
15 > * analogous `content_block_stop` (the first point where the
16 > * `input_json_delta` buffer is complete and parseable).
17 > */
18 > export interface IClaudeToolStartInfo {
19 > readonly toolName: string;
20 > readonly displayName: string;
21 > readonly parsedInput: Record<string, unknown> | undefined;
22 > readonly invocationMessage: StringOrMarkdown;
23 > readonly toolInput: string | undefined;
24 > }
25 >
26 > interface IRegistryEntry {
27 > readonly toolName: string;
28 > readonly turnId: string;
29 > inputBuffer: string;
30 > info: IClaudeToolStartInfo | undefined;
31 > }
32 >
33 > /**
34 > * Phase 8.5 — per-session, cross-message tool-call tracking for the
35 > * live mapper. Owns:
36 > *
37 > * - **Attribution** — `tool_use_id → { toolName, turnId }`. A
38 > * `tool_use` lands in one assistant message; the matching
39 > * `tool_result` arrives in a later synthetic `user` message. The
40 > * registry resolves the originating turn so each Complete action
41 > * lands on the correct turn.
42 > * - **Input accumulation** — `input_json_delta` chunks arrive across
43 > * `content_block_start` → `delta*` → `content_block_stop`. The
44 > * registry concatenates them and parses once at `finalize`.
45 > * - **Computed start-info** — `displayName`, rich `invocationMessage`,
46 > * `toolInput` string, parsed input. Computed once at `finalize`
47 > * and looked up at `tool_result` time so `pastTenseMessage` can
48 > * include the original parameters.
49 > *
50 > * Mirror of Copilot's `pendingTools: Map<toolCallId, IToolStartInfo>`
51 > * pattern in
52 > * [`mapSessionEvents.ts`](../copilot/mapSessionEvents.ts) — only the
53 > * seam differs (Copilot's SDK hands over complete args at
54 > * `tool.execution_start`; Claude's SDK streams them in deltas, so the
55 > * "ready" seam is `content_block_stop`).
56 > *
57 > * Encapsulated as a class with named lifecycle methods so the maps'
58 > * mutators are not part of the public surface — Phase 6.1's lesson.
59 > * One instance lives per `ClaudeAgentSession` and is composed by
60 > * `ClaudeMapperState`; the mapper threads `state` (which exposes the
61 > * registry as `state.toolCalls`) into every invocation.
62 > */
63 > export class ClaudeToolCallRegistry {
64 private readonly _entries = new Map<string, IRegistryEntry>();
66 > /**
67 > * Begin tracking a tool call. Called from `content_block_start`
68 > * for a `tool_use` block. Allocates the delta buffer; the
69 > * computed info bag is filled in by {@link finalize}.
70 > */
71 > begin(toolUseId: string, toolName: string, turnId: string): void {
72 this._entries.set(toolUseId, {
73 toolName,
77 });
78 }
80 > /**
81 > * Append one `input_json_delta.partial_json` chunk. No-op if the
82 > * `tool_use_id` is unknown (the caller already logged a warning
83 > * about the index mismatch).
84 > */
85 > appendInputDelta(toolUseId: string, partialJson: string): void {
86 const entry = this._entries.get(toolUseId);
87 if (!entry) {
90 entry.inputBuffer += partialJson;
91 }
93 > /**
94 > * Parse the accumulated buffer and stash the computed
95 > * {@link IClaudeToolStartInfo}. Called from `content_block_stop`.
96 > * Parse failures fall back to `parsedInput: undefined`; the
97 > * past-tense helper handles that by returning a generic message.
98 > */
99 > finalize(toolUseId: string): void {
100 const entry = this._entries.get(toolUseId);
101 if (!entry) {
121 entry.inputBuffer = '';
122 }
124 > /**
125 > * Seed {@link IClaudeToolStartInfo} directly from a pre-parsed
126 > * input object. Used for inner subagent tool uses, which arrive
127 > * already-parsed on the synthesized `assistant` message rather
128 > * than via streamed `input_json_delta` chunks. Without this the
129 > * registry entry's `info` would stay `undefined` and the live
130 > * `tool_result` handler would emit the generic
131 > * `"{displayName} finished"` past-tense, violating D6 (live/replay
132 > * parity).
133 > */
134 > seedParsedInput(toolUseId: string, parsedInput: unknown): void {
135 const entry = this._entries.get(toolUseId);
136 if (!entry) {
142 this._writeInfo(entry, normalized);
143 }
145 > private _writeInfo(entry: IRegistryEntry, parsedInput: Record<string, unknown> | undefined, rawFallback?: string): void {
146 const displayName = getClaudeToolDisplayName(entry.toolName);
147 entry.info = {
153 };
154 }
156 > /**
157 > * Cross-message lookup. Returns `undefined` if the
158 > * `tool_use_id` is unknown (defense-in-depth against transport
159 > * drift / replay). The `info` field may be `undefined` if the
160 > * tool block never reached `content_block_stop`.
161 > */
162 > lookup(toolUseId: string): { readonly turnId: string; readonly toolName: string; readonly info: IClaudeToolStartInfo | undefined } | undefined {
163 const entry = this._entries.get(toolUseId);
164 if (!entry) {
167 return { turnId: entry.turnId, toolName: entry.toolName, info: entry.info };
168 }
170 > /**
171 > * Drop the entry once the matching `tool_result` has been
172 > * delivered. Bounds the registry's memory across long turns.
173 > */
174 > complete(toolUseId: string): void {
175 this._entries.delete(toolUseId);
176 }
178 > /**
179 > * Drop any tracking still pending at the end of a turn and warn
180 > * once per orphan. A `tool_use` whose `tool_result` never arrives
181 > * — model misbehavior, transport drop, future cancellation —
182 > * would otherwise survive in the maps for the lifetime of the
183 > * session and accumulate across turns. Called from `mapResult`
184 > * on every `result` envelope.
185 > */
186 > clearPending(logService: ILogService): void {
187 if (this._entries.size === 0) {
188 return;