1
>
/*---------------------------------------------------------------------------------------------
claudeSubagentResolver.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 { CancellationToken } from '../../../../base/common/cancellation.js';
7
>
import { URI } from '../../../../base/common/uri.js';
8
>
import { vObjAny, vString as vStringValidator } from '../../../../base/common/validation.js';
9
>
import { ILogService } from '../../../log/common/log.js';
10
>
import { AgentSession } from '../../common/agentService.js';
11
>
import { parseSubagentSessionUri } from '../../common/state/sessionState.js';
12
>
import {
13
>
ResponsePartKind,
14
>
ToolCallStatus,
15
>
type Turn,
16
>
} from '../../common/state/protocol/state.js';
17
>
import { IClaudeAgentSdkService } from './claudeAgentSdkService.js';
18
>
import { mapSessionMessagesToTurns } from './claudeReplayMapper.js';
19
>
import { scanTranscriptForAgentIds, SUBAGENT_TOOL_NAMES, type SubagentRegistry } from './claudeSubagentRegistry.js';
20
>
21
>
/**
22
>
* One link in the resolver chain. Each strategy consults a different
23
>
* SDK primitive to map a `toolCallId` back to an `agentId`. Strategies
24
>
* are ordered cheapest-first inside {@link getSubagentTranscript}
25
>
* (TextSuffix → PromptMatch → Native).
26
>
*/
27
>
export interface ISubagentLookupStrategy {
28
>
/** Short label, used as a category in any future telemetry counter. */
29
>
readonly name: string;
30
>
/** Returns the agentId or `undefined` if this strategy cannot resolve the input. */
31
>
lookup(toolCallId: string, ctx: ISubagentLookupContext): Promise<string | undefined>;
32
>
}
33
>
34
>
export interface ISubagentLookupContext {
35
>
readonly parentUri: URI;
36
>
/** SDK session id (`AgentSession.id(parentUri)`). Precomputed so strategies don't repeat the work. */
37
>
readonly parentSessionId: string;
38
>
/** Pre-fetched parent transcript when available; otherwise the strategy must fetch its own. */
39
>
readonly parentTranscript?: readonly Turn[];
40
>
readonly token: CancellationToken;
41
>
}
42
>
43
>
/**
44
>
* Strategy 1 — scan the parent transcript for the synthetic agentId
45
>
* suffix. Cheapest path: the transcript is usually already in memory
46
>
* from the live session or the parent's own replay fetch.
47
>
*/
48
>
export class TextSuffixStrategy implements ISubagentLookupStrategy {
49
>
readonly name = 'text_suffix';
50
>
51
>
constructor(
52
private readonly _sdk: IClaudeAgentSdkService,
53
private readonly _logService: ILogService,
54
) { }
56
>
async lookup(toolCallId: string, ctx: ISubagentLookupContext): Promise<string | undefined> {
57
const transcript = await fetchParentTurns(this._sdk, this._logService, ctx, 'TextSuffix');
58
if (!transcript) {