1
>
/*---------------------------------------------------------------------------------------------
claudeSubagentRegistry.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 { Disposable } from '../../../../base/common/lifecycle.js';
7
>
import {
8
>
ResponsePartKind,
9
>
ToolCallStatus,
10
>
ToolResultContentType,
11
>
type ResponsePart,
12
>
type Turn,
13
>
} from '../../common/state/protocol/state.js';
14
>
15
>
/**
16
>
* Tool names whose `tool_use` blocks spawn a subagent. The SDK's
17
>
* `Task` (and legacy `Agent`) tools encode subagent invocations as
18
>
* normal tool_use entries; we observe them here at spawn time and
19
>
* track each one as a {@link SubagentSpawn}.
20
>
*/
21
>
export const SUBAGENT_TOOL_NAMES: ReadonlySet<string> = new Set(['Task', 'Agent']);
22
>
23
>
/**
24
>
* Regex matching the SDK's synthetic per-subagent suffix appended to
25
>
* `Task`/`Agent` `tool_result` text blocks. Empirically observed
26
>
* format: `agentId: <hex> (use SendMessage with to: '<hex>') ...`.
27
>
* Tolerant by design — case-insensitive, lenient whitespace, anchored
28
>
* only by line start — so minor wording drift between SDK versions
29
>
* doesn't silently break correlation.
30
>
*/
31
>
export const SUBAGENT_ID_SUFFIX_REGEX = /^\s*agentId:\s+([a-z0-9]+)\b/im;
32
>
33
>
/**
34
>
* One Task tool_use in the parent session that did (or may have)
35
>
* spawned a subagent. All lifecycle state for *this* spawn lives here:
36
>
*
37
>
* - {@link agentId}: the SDK's identity for the spawned subagent.
38
>
* Set when learned (`canUseTool` `options.agentID`, strategy
39
>
* resolution, or transcript priming).
40
>
* - {@link background}: foreground vs. background mode. Defaults to
41
>
* `false` (foreground is the common case); flipped to `true` when
42
>
* the SDK emits `system.task_started`. Background spawns have
43
>
* deferred completion via `system.task_notification`.
44
>
* - {@link subagentType} / {@link description} / {@link prompt}:
45
>
* metadata from the `tool_use.input` (`subagent_type`,
46
>
* `description` and `prompt` fields). Available once the canonical
47
>
* `assistant` message arrives with the complete input bag (the
48
>
* early `content_block_start` has empty input). Used for UI labels
49
>
* and to seed the subagent's opening request.
50
>
* - {@link markAnnounced} / {@link markCompleted}: idempotency
51
>
* guards for the workbench-facing `subagent_started` /
52
>
* `subagent_completed` signals.
53
>
*/
54
>
export class SubagentSpawn {
55
>
background = false;
56
>
subagentType: string | undefined;
57
>
description: string | undefined;
58
>
prompt: string | undefined;
59
>
60
>
private _agentId: string | undefined;
61
>
private _announced = false;
62
>
private _completed = false;
63
>
64
>
constructor(readonly toolUseId: string) { }
65
>
66
>
get agentId(): string | undefined {
67
return this._agentId;
68
}
70
>
/**
71
>
* Set the SDK's agent id for this spawn. First-writer-wins: once
72
>
* set, subsequent calls are no-ops. Multiple call sites converge on
73
>
* the same value (canUseTool's `options.agentID`, the strategy chain,
74
>
* and transcript priming all surface the SDK's single identity), so
75
>
* the invariant is enforced here rather than at every caller.
76
>
*/
77
>
setAgentId(agentId: string): void {
78
if (this._agentId === undefined) {
79
this._agentId = agentId;
80
}
81
}
83
>
markAnnounced(): boolean {
84
if (this._announced) {
85
return false;