claudeAgentSdkService.ts ×16

Frontier kind: Code frontier

unlabeled · c_36f9e47f7904

217 tests · 22250 LOC · 80 files · introduces 0 tests · 193 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
16 ranges193 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1979 ranges22250 lines · 80 files · Browse complete extent
All tests (intent)
217 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: 193 introduced LOC across 16 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/agentHost/node/claude/claudeAgentSdkService.ts 193 introduced LOC · 16 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- claudeAgentSdkService.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 { AnyZodRawShape, ForkSessionOptions, ForkSessionResult, GetSessionMessagesOptions, GetSubagentMessagesOptions, InferShape, ListSessionsOptions, ListSubagentsOptions, McpSdkServerConfigWithInstance, Options, Query, SDKSessionInfo, SDKUserMessage, SdkMcpToolDefinition, SessionMessage, SessionMutationOptions, WarmQuery } from '@anthropic-ai/claude-agent-sdk';
7 > import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
8 > import { pathToFileURL } from 'url';
9 > import { CancellationToken } from '../../../../base/common/cancellation.js';
10 > import { join } from '../../../../base/common/path.js';
11 > import { createDecorator } from '../../../instantiation/common/instantiation.js';
12 > import { ILogService } from '../../../log/common/log.js';
13 > import { IAgentSdkDownloader, IAgentSdkPackage } from '../agentSdkDownloader.js';
14 > import { AgentHostClaudeSdkRootEnvVar } from '../../common/agentService.js';
15 >
16 > /**
17 > * `@anthropic-ai/claude-agent-sdk` distribution descriptor. Lives in this
18 > * file because it encodes Claude-specific knowledge — the env-var name
19 > * and the fact that Claude ships separate `linux-{x64,arm64}-musl` SKUs
20 > * alongside the default glibc ones. The downloader consumes this through
21 > * `IAgentSdkPackage` and never names Claude directly.
22 > */
23 > export const ClaudeSdkPackage: IAgentSdkPackage = {
24 > id: 'claude',
25 > displayName: 'Claude',
26 > devOverrideEnvVar: AgentHostClaudeSdkRootEnvVar,
27 > hasSeparateMuslLinuxPackage: true,
28 > };
29 >
30 > export const IClaudeAgentSdkService = createDecorator<IClaudeAgentSdkService>('claudeAgentSdkService');
31 >
32 > /**
33 > * Pure per-method passthrough shim over `@anthropic-ai/claude-agent-sdk`.
34 > *
35 > * Every method on this interface corresponds 1:1 to a single SDK export.
36 > * The shim owns lazy module loading and the first-failure log-once
37 > * convention; it does NOT compose, wrap, or add behavior on top of the
38 > * SDK's surface. Higher-level orchestration (e.g. building the in-process
39 > * client-tool MCP server) lives in dedicated modules that depend on this
40 > * interface for the raw bindings.
41 > */
42 > export interface IClaudeAgentSdkService {
43 > readonly _serviceBrand: undefined;
44 >
45 > listSessions(): Promise<readonly SDKSessionInfo[]>;
46 > getSessionInfo(sessionId: string): Promise<SDKSessionInfo | undefined>;
47 > startup(params: { options: Options; initializeTimeoutMs?: number }): Promise<WarmQuery>;
48 > /**
49 > * 1:1 with the SDK's top-level `query` export. Returns a `Query` whose
50 > * subprocess starts lazily; callers drive control requests on it (e.g.
51 > * `supportedModels()` for model enumeration) and `close()` it when done.
52 > * Async only because the SDK module itself is loaded lazily.
53 > */
54 > query(params: { prompt: string | AsyncIterable<SDKUserMessage>; options?: Options }): Promise<Query>;
55 > getSessionMessages(sessionId: string, options?: GetSessionMessagesOptions): Promise<readonly SessionMessage[]>;
56 > listSubagents(sessionId: string, options?: ListSubagentsOptions): Promise<readonly string[]>;
57 > getSubagentMessages(sessionId: string, agentId: string, options?: GetSubagentMessagesOptions): Promise<readonly SessionMessage[]>;
58 >
59 > /**
60 > * True iff the SDK can be loaded WITHOUT a network download — a dev
61 > * override or dev bare-import is available, or a previously-downloaded SDK
62 > * is cached on disk. Eager / background callers (e.g. `listSessions` at
63 > * startup) gate on this so listing sessions never kicks off a multi-second
64 > * cold download before the user has started a session.
65 > */
66 > canLoadWithoutDownload(): Promise<boolean>;
67 >
68 > forkSession(sessionId: string, options?: ForkSessionOptions): Promise<ForkSessionResult>;
69 > deleteSession(sessionId: string, options?: SessionMutationOptions): Promise<void>;
70 > createSdkMcpServer(options: {
71 > name: string;
72 > version?: string;
73 > // SDK signature: `tools?: Array<SdkMcpToolDefinition<any>>`. The `any`
74 > // here is required to match the SDK's own erased generic and to allow
75 > // callers to pass an array of tools whose schemas differ from each other.
76 > // eslint-disable-next-line @typescript-eslint/no-explicit-any
77 > tools?: Array<SdkMcpToolDefinition<any>>;
78 > }): Promise<McpSdkServerConfigWithInstance>;
79 >
80 > tool<Schema extends AnyZodRawShape>(
81 > name: string,
82 > description: string,
83 > inputSchema: Schema,
84 > handler: (args: InferShape<Schema>, extra: unknown) => Promise<CallToolResult>
85 > ): Promise<SdkMcpToolDefinition<Schema>>;
86 > }
87 >
88 > /**
89 > * Narrowed structural slice of `@anthropic-ai/claude-agent-sdk` covering
90 > * exactly the bindings the agent host pulls from the SDK. Production
91 > * `import()` returns the full module which is structurally assignable to
92 > * this interface. Tests usually stub {@link IClaudeAgentSdkService} via
93 > * the DI container, but a few existing suites subclass
94 > * {@link ClaudeAgentSdkService} and override {@link ClaudeAgentSdkService._loadSdk}
95 > * to fault or stub these bindings without having to name every export of
96 > * the SDK module — `_loadSdk` is `protected` for that reason.
97 > */
98 > export interface IClaudeSdkBindings {
99 > listSessions(options?: ListSessionsOptions): Promise<SDKSessionInfo[]>;
100 > getSessionInfo(sessionId: string): Promise<SDKSessionInfo | undefined>;
101 > startup(params: { options: Options; initializeTimeoutMs?: number }): Promise<WarmQuery>;
102 > query(params: { prompt: string | AsyncIterable<SDKUserMessage>; options?: Options }): Query;
103 > getSessionMessages(sessionId: string, options?: GetSessionMessagesOptions): Promise<SessionMessage[]>;
104 > listSubagents(sessionId: string, options?: ListSubagentsOptions): Promise<string[]>;
105 > getSubagentMessages(sessionId: string, agentId: string, options?: GetSubagentMessagesOptions): Promise<SessionMessage[]>;
106 > forkSession(sessionId: string, options?: ForkSessionOptions): Promise<ForkSessionResult>;
107 > deleteSession(sessionId: string, options?: SessionMutationOptions): Promise<void>;
108 > createSdkMcpServer(options: {
109 > name: string;
110 > version?: string;
111 > // eslint-disable-next-line @typescript-eslint/no-explicit-any
112 > tools?: Array<SdkMcpToolDefinition<any>>;
113 > }): McpSdkServerConfigWithInstance;
114 > tool<Schema extends AnyZodRawShape>(
115 > name: string,
116 > description: string,
117 > inputSchema: Schema,
118 > handler: (args: InferShape<Schema>, extra: unknown) => Promise<CallToolResult>
119 > ): SdkMcpToolDefinition<Schema>;
120 > }
121 >
122 > export class ClaudeAgentSdkService implements IClaudeAgentSdkService {
123 > declare readonly _serviceBrand: undefined;
124 >
125 > /**
126 > * Cached resolved bindings. We deliberately cache the *resolved* value,
127 > * not the in-flight promise — if a transient `import()` failure recovers
128 > * (e.g. user fixes a broken `node_modules`), the next call retries.
129 > */
130 > private _sdkModule: IClaudeSdkBindings | undefined;
131 >
132 > /**
133 > * Latched once we've logged a load failure, so a corrupt postinstall
134 > * doesn't flood `error` events on every `listSessions()` call.
135 > */
136 > private _firstLoadFailureLogged = false;
137 >
138 > constructor(
139 @ILogService private readonly _logService: ILogService,
140 @IAgentSdkDownloader private readonly _downloader: IAgentSdkDownloader,
141 ) { }
143 > async listSessions(): Promise<readonly SDKSessionInfo[]> {
144 const sdk = await this._getSdk();
145 return sdk.listSessions(undefined);
146 }
148 > async canLoadWithoutDownload(): Promise<boolean> {
149 // A dev override (explicit SDK root) is always local. So is the dev
150 // bare-import path, which is taken when there is no product config —
156 return this._downloader.isSdkResolvableWithoutDownload(ClaudeSdkPackage);
157 }
159 > async getSessionInfo(sessionId: string): Promise<SDKSessionInfo | undefined> {
160 const sdk = await this._getSdk();
161 return sdk.getSessionInfo(sessionId);
162 }
164 > async startup(params: { options: Options; initializeTimeoutMs?: number }): Promise<WarmQuery> {
165 const sdk = await this._getSdk();
166 return sdk.startup(params);
167 }
169 > async query(params: { prompt: string | AsyncIterable<SDKUserMessage>; options?: Options }): Promise<Query> {
170 const sdk = await this._getSdk();
171 return sdk.query(params);
172 }
174 > async getSessionMessages(sessionId: string, options?: GetSessionMessagesOptions): Promise<readonly SessionMessage[]> {
175 const sdk = await this._getSdk();
176 return sdk.getSessionMessages(sessionId, options);
177 }
179 > async listSubagents(sessionId: string, options?: ListSubagentsOptions): Promise<readonly string[]> {
180 const sdk = await this._getSdk();
181 return sdk.listSubagents(sessionId, options);
182 }
184 > async getSubagentMessages(sessionId: string, agentId: string, options?: GetSubagentMessagesOptions): Promise<readonly SessionMessage[]> {
185 const sdk = await this._getSdk();
186 return sdk.getSubagentMessages(sessionId, agentId, options);
187 }
189 > async forkSession(sessionId: string, options?: ForkSessionOptions): Promise<ForkSessionResult> {
190 const sdk = await this._getSdk();
191 return sdk.forkSession(sessionId, options);
192 }
194 > async deleteSession(sessionId: string, options?: SessionMutationOptions): Promise<void> {
195 const sdk = await this._getSdk();
196 return sdk.deleteSession(sessionId, options);
197 }
199 > async createSdkMcpServer(options: {
200 name: string;
201 version?: string;
206 return sdk.createSdkMcpServer(options);
207 }
209 > async tool<Schema extends AnyZodRawShape>(
210 name: string,
211 description: string,
216 return sdk.tool(name, description, inputSchema, handler);
217 }
219 > private async _getSdk(): Promise<IClaudeSdkBindings> {
220 if (this._sdkModule) {
221 return this._sdkModule;
232 }
233 }
235 > protected async _loadSdk(): Promise<IClaudeSdkBindings> {
236 // 1. Env-var override wins — both for the air-gapped server case
237 // (`--claude-sdk-root` flag) and for developers who want to point
267 return import('@anthropic-ai/claude-agent-sdk');
268 }
270 >
271 > // #region Compile-time SDK drift detection
272 > //
273 > // Enforce that every method on IClaudeSdkBindings names a real export of
274 > // `@anthropic-ai/claude-agent-sdk` and is assignable from that export's
275 > // type. If the SDK renames or changes the signature of any of these
276 > // exports, the `_assertBindingsMatchSdk` assignment below stops type-
277 > // checking and the build fails — flagging that the shim needs updating.
278 >
279 > type SdkModule = typeof import('@anthropic-ai/claude-agent-sdk');
280 >
281 > type AssertBindingsMatchSdk = {
282 > [K in keyof IClaudeSdkBindings]: K extends keyof SdkModule
283 > ? SdkModule[K] extends IClaudeSdkBindings[K]
284 > ? true
285 > : ['SDK export signature drifted from IClaudeSdkBindings', K, SdkModule[K], IClaudeSdkBindings[K]]
286 > : ['Not an export of @anthropic-ai/claude-agent-sdk', K];
287 > };
288 >
289 > // Forces the mapped type above to be eagerly checked: if any entry is not
290 > // `true`, this assignment fails to compile. Module-local so the runtime
291 > // surface stays clean — the only purpose is the type-level assertion.
292 > const _assertBindingsMatchSdk: { [K in keyof IClaudeSdkBindings]: true } = null as unknown as AssertBindingsMatchSdk;
293 > void _assertBindingsMatchSdk;
294 >
295 > // #endregion