pi-subagents 0.40.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +54 -1879
  3. package/agents/oracle.md +1 -0
  4. package/inspector-runner.mjs +11 -0
  5. package/package.json +13 -5
  6. package/prompts/parallel-context-build.md +1 -1
  7. package/prompts/parallel-handoff-plan.md +1 -1
  8. package/prompts/review-loop.md +1 -1
  9. package/skills/pi-subagents/SKILL.md +9 -7
  10. package/skills/pi-subagents/references/constraints-and-recipes.md +24 -32
  11. package/skills/pi-subagents/references/execution-controls.md +99 -97
  12. package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  13. package/skills/pi-subagents/references/prompting-and-roles.md +23 -30
  14. package/src/agents/agent-management.ts +155 -65
  15. package/src/agents/agent-serializer.ts +19 -0
  16. package/src/agents/agents.ts +154 -71
  17. package/src/agents/chain-serializer.ts +10 -7
  18. package/src/agents/frontmatter.ts +5 -3
  19. package/src/agents/identity.ts +1 -1
  20. package/src/agents/proactive-skills.ts +13 -10
  21. package/src/agents/skills.ts +23 -6
  22. package/src/api/control-channel.ts +4 -0
  23. package/src/api/delegation.ts +26 -194
  24. package/src/api/external-runs.ts +129 -0
  25. package/src/api/intercom-bridge.ts +3 -0
  26. package/src/api/pi-args.ts +5 -0
  27. package/src/api/preflight.ts +3 -3
  28. package/src/api/shared-types.ts +19 -0
  29. package/src/extension/config.ts +10 -0
  30. package/src/extension/control-notices.ts +5 -39
  31. package/src/extension/doctor.ts +10 -9
  32. package/src/extension/fanout-child.ts +7 -4
  33. package/src/extension/index.ts +232 -68
  34. package/src/extension/rpc.ts +18 -12
  35. package/src/extension/schemas.ts +48 -37
  36. package/src/extension/tool-description.ts +36 -86
  37. package/src/inspectors/herdr/actions.ts +229 -0
  38. package/src/inspectors/herdr/client.ts +130 -0
  39. package/src/inspectors/herdr/inspector-runner.ts +141 -0
  40. package/src/inspectors/herdr/project-panes.ts +154 -0
  41. package/src/integrations/herdr-status.ts +330 -0
  42. package/src/intercom/intercom-bridge.ts +3 -2
  43. package/src/intercom/result-intercom.ts +5 -1
  44. package/src/missions/actions.ts +374 -0
  45. package/src/missions/lifecycle.ts +314 -0
  46. package/src/missions/store.ts +442 -0
  47. package/src/missions/types.ts +135 -0
  48. package/src/policy/authority.ts +46 -0
  49. package/src/profiles/profiles.ts +29 -3
  50. package/src/runs/background/async-execution.ts +110 -50
  51. package/src/runs/background/async-job-tracker.ts +27 -4
  52. package/src/runs/background/async-resume.ts +6 -6
  53. package/src/runs/background/async-status.ts +29 -1
  54. package/src/runs/background/auto-drain.ts +3 -3
  55. package/src/runs/background/chain-append.ts +3 -2
  56. package/src/runs/background/control-channel.ts +9 -7
  57. package/src/runs/background/fleet-view.ts +3 -4
  58. package/src/runs/background/notify.ts +2 -1
  59. package/src/runs/background/process-terminal.ts +5 -5
  60. package/src/runs/background/result-watcher.ts +13 -5
  61. package/src/runs/background/run-id-resolver.ts +3 -3
  62. package/src/runs/background/run-status.ts +35 -8
  63. package/src/runs/background/scheduled-runs.ts +602 -375
  64. package/src/runs/background/stale-run-reconciler.ts +3 -3
  65. package/src/runs/background/subagent-runner.ts +608 -445
  66. package/src/runs/background/subagent-wait.ts +50 -9
  67. package/src/runs/background/wait-subscriptions.ts +253 -0
  68. package/src/runs/background/wait-tool.ts +12 -4
  69. package/src/runs/foreground/async-steering-action.ts +3 -3
  70. package/src/runs/foreground/chain-clarify.ts +8 -4
  71. package/src/runs/foreground/chain-execution.ts +56 -30
  72. package/src/runs/foreground/execution.ts +34 -9
  73. package/src/runs/foreground/subagent-executor.ts +1036 -274
  74. package/src/runs/shared/acceptance.ts +28 -6
  75. package/src/runs/shared/child-protocol.ts +302 -22
  76. package/src/runs/shared/dynamic-fanout.ts +1 -1
  77. package/src/runs/shared/external-cli-runner.ts +130 -0
  78. package/src/runs/shared/long-running-guard.ts +42 -1
  79. package/src/runs/shared/mcp-direct-tool-allowlist.ts +2 -0
  80. package/src/runs/shared/nested-events.ts +59 -5
  81. package/src/runs/shared/nested-render.ts +9 -4
  82. package/src/runs/shared/parallel-handoff.ts +86 -2
  83. package/src/runs/shared/parallel-utils.ts +11 -2
  84. package/src/runs/shared/permissions.ts +95 -0
  85. package/src/runs/shared/pi-args.ts +468 -109
  86. package/src/runs/shared/pi-spawn.ts +11 -1
  87. package/src/runs/shared/run-history.ts +1 -1
  88. package/src/runs/shared/subagent-prompt-runtime.ts +36 -5
  89. package/src/runs/shared/subagent-startup-retry.ts +5 -2
  90. package/src/runs/shared/turn-budget.ts +6 -6
  91. package/src/runs/shared/worktree.ts +122 -12
  92. package/src/shared/accessible-dir.ts +29 -7
  93. package/src/shared/artifacts.ts +143 -1
  94. package/src/shared/fork-context.ts +3 -2
  95. package/src/shared/launch-contract.ts +1 -0
  96. package/src/shared/settings.ts +10 -0
  97. package/src/shared/types.ts +156 -14
  98. package/src/shared/utils.ts +8 -6
  99. package/src/slash/delegation-adapters.ts +32 -194
  100. package/src/slash/delegation-request.ts +43 -126
  101. package/src/slash/prompt-template-bridge.ts +158 -205
  102. package/src/slash/prompt-workflows.ts +21 -57
  103. package/src/slash/slash-bridge.ts +14 -0
  104. package/src/slash/slash-commands.ts +31 -632
  105. package/src/slash/subagents-admin.ts +18 -14
  106. package/src/tui/fleet-status.ts +156 -21
  107. package/src/tui/fleet-transcript.ts +110 -5
  108. package/src/tui/fleet.ts +57 -25
  109. package/src/tui/render.ts +368 -152
  110. package/src/types/pi-runtime-compat.d.ts +8 -0
  111. package/src/watchdog/lsp-diagnostics.ts +12 -7
  112. package/src/watchdog/model-selection.ts +2 -2
  113. package/src/watchdog/permission-arbiter.ts +145 -0
  114. package/src/watchdog/register-child.ts +1 -1
  115. package/src/watchdog/register-main.ts +1 -1
  116. package/src/watchdog/review.ts +4 -1
  117. package/src/watchdog/runtime.ts +3 -2
  118. package/src/workflows/chat-progress.ts +140 -0
  119. package/src/workflows/scripted-workflow.ts +415 -0
  120. package/agents/advisor.md +0 -73
  121. package/src/extension/chain-validation.ts +0 -181
@@ -1,16 +1,12 @@
1
1
  import {
2
2
  SUBAGENT_DELEGATION_CANCEL_EVENT,
3
- SUBAGENT_DELEGATION_PROTOCOL_VERSION,
4
- SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
5
3
  SUBAGENT_DELEGATION_REQUEST_EVENT,
6
4
  SUBAGENT_DELEGATION_RESPONSE_EVENT,
7
5
  SUBAGENT_DELEGATION_STARTED_EVENT,
8
6
  SUBAGENT_DELEGATION_UPDATE_EVENT,
7
+ type SubagentDelegationInvalidResponse,
9
8
  type SubagentDelegationRequest,
10
9
  type SubagentDelegationResponse,
11
- type SubagentDelegationV2InvalidResponse,
12
- type SubagentDelegationV2Request,
13
- type SubagentDelegationV2Response,
14
10
  } from "../api/delegation.ts";
15
11
  import { parseSubagentDelegationRequest } from "./delegation-request.ts";
16
12
  import {
@@ -21,9 +17,6 @@ import {
21
17
  toSubagentDelegationExecutionParams,
22
18
  toSubagentDelegationResponse,
23
19
  toSubagentDelegationUpdate,
24
- toSubagentDelegationV2ExecutionParams,
25
- toSubagentDelegationV2Response,
26
- toSubagentDelegationV2Update,
27
20
  type DelegatedSubagentExecutionParams,
28
21
  type PromptTemplateBridgeResult,
29
22
  type PromptTemplateDelegationRequest,
@@ -51,11 +44,8 @@ interface PromptTemplateBridgeOptions<Ctx extends { cwd?: string }> {
51
44
  ctx: Ctx,
52
45
  onUpdate: (result: PromptTemplateBridgeResult) => void,
53
46
  ) => Promise<PromptTemplateBridgeResult>;
54
- /**
55
- * Concurrent-safe executor for strict versioned delegation requests.
56
- * Non-versioned prompt-template requests retain the ordinary single-dispatch guard.
57
- */
58
- executeVersioned?: (
47
+ /** Concurrent-safe executor for structured delegation requests. */
48
+ executeStructured?: (
59
49
  requestId: string,
60
50
  params: DelegatedSubagentExecutionParams,
61
51
  signal: AbortSignal,
@@ -64,32 +54,43 @@ interface PromptTemplateBridgeOptions<Ctx extends { cwd?: string }> {
64
54
  ) => Promise<PromptTemplateBridgeResult>;
65
55
  }
66
56
 
57
+ function hasStructuredDelegationMarker(data: unknown): boolean {
58
+ if (!data || typeof data !== "object" || Array.isArray(data)) return false;
59
+ const value = data as Record<string, unknown>;
60
+ return Object.hasOwn(value, "ownerRunId")
61
+ || Object.hasOwn(value, "nodeId")
62
+ || Object.hasOwn(value, "result")
63
+ || Object.hasOwn(value, "version");
64
+ }
65
+
66
+ function validId(value: unknown): value is string {
67
+ return typeof value === "string" && value.trim().length > 0 && value.length <= 256 && !/[\r\n]/.test(value);
68
+ }
69
+
67
70
  export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: string }>(
68
71
  options: PromptTemplateBridgeOptions<Ctx>,
69
72
  ): {
70
73
  cancelAll: () => void;
71
74
  dispose: () => void;
72
75
  } {
73
- // Legacy and V1 retain requestId-only correlation. V2 attempts are isolated by
74
- // their complete wire identity, while logical-node ownership is tracked separately.
75
- const controllers = new Map<string, AbortController>();
76
- const pendingCancels = new Map<string, true>();
77
- const v2Controllers = new Map<string, AbortController>();
78
- const pendingV2Cancels = new Map<string, true>();
79
- const activeV2Nodes = new Map<string, { attemptKey: string; controller: AbortController }>();
80
- const settledV2Attempts = new Map<string, true>();
76
+ const legacyControllers = new Map<string, AbortController>();
77
+ const pendingLegacyCancels = new Map<string, true>();
78
+ const attemptControllers = new Map<string, AbortController>();
79
+ const pendingAttemptCancels = new Map<string, true>();
80
+ const activeOwnedNodes = new Map<string, { attemptKey: string; controller: AbortController }>();
81
+ const settledAttempts = new Map<string, true>();
81
82
  const subscriptions: Array<() => void> = [];
82
83
  let disposed = false;
83
- let v2IdentitySaturated = false;
84
+ let identitySaturated = false;
84
85
 
85
86
  const subscribe = (event: string, handler: (data: unknown) => void): void => {
86
87
  const unsubscribe = options.events.on(event, handler);
87
88
  if (typeof unsubscribe === "function") subscriptions.push(unsubscribe);
88
89
  };
89
- const ownsRequest = (requestId: string, controller: AbortController): boolean =>
90
- !disposed && controllers.get(requestId) === controller;
91
- const ownsV2Attempt = (attemptKey: string, controller: AbortController): boolean =>
92
- !disposed && v2Controllers.get(attemptKey) === controller;
90
+ const ownsLegacyRequest = (requestId: string, controller: AbortController): boolean =>
91
+ !disposed && legacyControllers.get(requestId) === controller;
92
+ const ownsAttempt = (attemptKey: string, controller: AbortController): boolean =>
93
+ !disposed && attemptControllers.get(attemptKey) === controller;
93
94
  const boundedRemember = (map: Map<string, true>, key: string): void => {
94
95
  map.delete(key);
95
96
  map.set(key, true);
@@ -99,28 +100,27 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
99
100
  map.delete(oldest);
100
101
  }
101
102
  };
102
- const rememberV2Identity = (map: Map<string, true>, key: string): void => {
103
- if (map.has(key) || v2IdentitySaturated) return;
103
+ const rememberIdentity = (map: Map<string, true>, key: string): void => {
104
+ if (map.has(key) || identitySaturated) return;
104
105
  if (map.size >= 8_192) {
105
- v2IdentitySaturated = true;
106
+ identitySaturated = true;
106
107
  return;
107
108
  }
108
109
  map.set(key, true);
109
110
  if (map.size === 8_192) {
110
- // V2 identity facts are security state, not an LRU cache. Saturate as
111
- // soon as the bounded history fills so no later untracked attempt can
112
- // start, rather than evicting a cancellation or settled-attempt fact.
113
- v2IdentitySaturated = true;
111
+ // Exact cancellation and terminal-attempt facts are security state, not an
112
+ // LRU cache. Once full, fail closed rather than evicting identity facts.
113
+ identitySaturated = true;
114
114
  }
115
115
  };
116
- const v2NodeKey = (ownerRunId: string, nodeId: string): string => JSON.stringify([ownerRunId, nodeId]);
117
- const v2AttemptKey = (requestId: string, ownerRunId: string, nodeId: string): string => JSON.stringify([requestId, ownerRunId, nodeId]);
118
- const rememberPendingCancel = (requestId: string): void => {
119
- boundedRemember(pendingCancels, requestId);
116
+ const nodeKey = (ownerRunId: string, nodeId: string): string => JSON.stringify([ownerRunId, nodeId]);
117
+ const attemptKey = (requestId: string, ownerRunId: string, nodeId: string): string => JSON.stringify([requestId, ownerRunId, nodeId]);
118
+ const rememberPendingLegacyCancel = (requestId: string): void => {
119
+ boundedRemember(pendingLegacyCancels, requestId);
120
120
  };
121
- const emitV2Terminal = (attemptKey: string, payload: SubagentDelegationV2Response): void => {
122
- if (disposed || settledV2Attempts.has(attemptKey)) return;
123
- rememberV2Identity(settledV2Attempts, attemptKey);
121
+ const emitTerminal = (key: string, payload: SubagentDelegationResponse): void => {
122
+ if (disposed || settledAttempts.has(key)) return;
123
+ rememberIdentity(settledAttempts, key);
124
124
  options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, payload);
125
125
  };
126
126
 
@@ -128,119 +128,107 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
128
128
  if (!data || typeof data !== "object" || Array.isArray(data)) return;
129
129
  const value = data as Record<string, unknown>;
130
130
  const requestId = value.requestId;
131
- if (typeof requestId !== "string" || !requestId.trim() || requestId.length > 256 || /[\r\n]/.test(requestId)) return;
132
- if (Object.hasOwn(value, "version")) {
133
- if (value.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION) {
134
- if (Object.keys(value).some((key) => key !== "version" && key !== "requestId" && key !== "ownerRunId" && key !== "nodeId")) return;
135
- const ownerRunId = value.ownerRunId;
136
- const nodeId = value.nodeId;
137
- if (typeof ownerRunId !== "string" || !ownerRunId.trim() || ownerRunId.length > 256 || /[\r\n]/.test(ownerRunId)) return;
138
- if (typeof nodeId !== "string" || !nodeId.trim() || nodeId.length > 256 || /[\r\n]/.test(nodeId)) return;
139
- const attemptKey = v2AttemptKey(requestId, ownerRunId, nodeId);
140
- const controller = v2Controllers.get(attemptKey);
141
- if (controller) controller.abort();
142
- else rememberV2Identity(pendingV2Cancels, attemptKey);
143
- return;
144
- }
145
- if (value.version !== SUBAGENT_DELEGATION_PROTOCOL_VERSION) return;
146
- if (Object.keys(value).some((key) => key !== "version" && key !== "requestId")) return;
131
+ if (!validId(requestId)) return;
132
+ if (hasStructuredDelegationMarker(data)) {
133
+ if (Object.keys(value).some((key) => key !== "requestId" && key !== "ownerRunId" && key !== "nodeId")) return;
134
+ const ownerRunId = value.ownerRunId;
135
+ const nodeId = value.nodeId;
136
+ if (!validId(ownerRunId) || !validId(nodeId)) return;
137
+ const key = attemptKey(requestId, ownerRunId, nodeId);
138
+ const controller = attemptControllers.get(key);
139
+ if (controller) controller.abort();
140
+ else rememberIdentity(pendingAttemptCancels, key);
141
+ return;
147
142
  }
148
- const controller = controllers.get(requestId);
143
+ const controller = legacyControllers.get(requestId);
149
144
  if (controller) {
150
145
  controller.abort();
151
146
  return;
152
147
  }
153
- rememberPendingCancel(requestId);
148
+ rememberPendingLegacyCancel(requestId);
154
149
  });
155
150
 
156
151
  subscribe(PROMPT_TEMPLATE_SUBAGENT_REQUEST_EVENT, async (data) => {
157
- const isVersioned = !!data && typeof data === "object" && Object.hasOwn(data, "version");
152
+ const structuredPayload = hasStructuredDelegationMarker(data);
158
153
  let requestId: string;
159
154
  let params: DelegatedSubagentExecutionParams;
160
- let versionedRequest: SubagentDelegationRequest | SubagentDelegationV2Request | undefined;
161
- let v2Request: SubagentDelegationV2Request | undefined;
162
- let v2Key: string | undefined;
155
+ let structuredRequest: SubagentDelegationRequest | undefined;
156
+ let key: string | undefined;
163
157
  let legacyRequest: PromptTemplateDelegationRequest | undefined;
164
158
 
165
- if (isVersioned) {
159
+ if (structuredPayload) {
166
160
  const parsed = parseSubagentDelegationRequest(data);
167
161
  if (parsed.ok === false) {
168
162
  if (!disposed && parsed.requestId) {
169
- if (parsed.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION) {
170
- const payload = {
171
- version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
172
- requestId: parsed.requestId,
173
- ...(parsed.ownerRunId ? { ownerRunId: parsed.ownerRunId } : {}),
174
- ...(parsed.nodeId ? { nodeId: parsed.nodeId } : {}),
175
- status: "invalid_request",
176
- error: parsed.error,
177
- } satisfies SubagentDelegationV2InvalidResponse;
178
- if (parsed.ownerRunId && parsed.nodeId) {
179
- const attemptKey = v2AttemptKey(parsed.requestId, parsed.ownerRunId, parsed.nodeId);
180
- if (!v2Controllers.has(attemptKey)) emitV2Terminal(attemptKey, payload);
181
- } else {
182
- options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, payload);
183
- }
184
- } else if (!controllers.has(parsed.requestId)) {
185
- options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, {
186
- version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
187
- requestId: parsed.requestId,
188
- status: "invalid_request",
189
- error: parsed.error,
190
- } satisfies SubagentDelegationResponse);
163
+ const payload = {
164
+ requestId: parsed.requestId,
165
+ ...(parsed.ownerRunId ? { ownerRunId: parsed.ownerRunId } : {}),
166
+ ...(parsed.nodeId ? { nodeId: parsed.nodeId } : {}),
167
+ status: "invalid_request",
168
+ error: parsed.error,
169
+ } satisfies SubagentDelegationInvalidResponse;
170
+ if (parsed.ownerRunId && parsed.nodeId) {
171
+ const attemptedKey = attemptKey(parsed.requestId, parsed.ownerRunId, parsed.nodeId);
172
+ if (!attemptControllers.has(attemptedKey)) emitTerminal(attemptedKey, payload);
173
+ } else {
174
+ options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, payload);
191
175
  }
192
176
  }
193
177
  return;
194
178
  }
195
- versionedRequest = parsed.request;
179
+ structuredRequest = parsed.request;
196
180
  requestId = parsed.request.requestId;
197
- if (parsed.request.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION) {
198
- v2Request = parsed.request;
199
- v2Key = v2AttemptKey(requestId, parsed.request.ownerRunId, parsed.request.nodeId);
200
- params = toSubagentDelegationV2ExecutionParams(parsed.request);
201
- } else {
202
- params = toSubagentDelegationExecutionParams(parsed.request);
203
- }
181
+ key = attemptKey(requestId, parsed.request.ownerRunId, parsed.request.nodeId);
182
+ params = toSubagentDelegationExecutionParams(parsed.request);
204
183
  } else {
184
+ if (data && typeof data === "object" && !Array.isArray(data)) {
185
+ const legacy = data as Record<string, unknown>;
186
+ if (legacy.tasks !== undefined || legacy.worktree !== undefined) {
187
+ if (typeof legacy.requestId === "string" && legacy.requestId) {
188
+ options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, {
189
+ requestId: legacy.requestId,
190
+ messages: [],
191
+ isError: true,
192
+ errorText: "Legacy prompt-template tasks/worktree orchestration was removed; use workflowScript.",
193
+ });
194
+ }
195
+ return;
196
+ }
197
+ }
205
198
  legacyRequest = parsePromptTemplateRequest(data);
206
199
  if (!legacyRequest) return;
207
200
  requestId = legacyRequest.requestId;
208
201
  params = toLegacyExecutionParams(legacyRequest);
209
202
  }
210
203
 
211
- // Legacy and V1 keep requestId-only ownership. V2 retransmission and terminal
212
- // suppression use the full tuple, independently from logical-node ownership.
213
- if (!v2Request && controllers.has(requestId)) return;
214
- if (v2Request && v2Key) {
215
- if (v2Controllers.has(v2Key) || settledV2Attempts.has(v2Key)) return;
216
- if (pendingV2Cancels.delete(v2Key)) {
217
- emitV2Terminal(v2Key, {
218
- version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
204
+ if (!structuredRequest && legacyControllers.has(requestId)) return;
205
+ if (structuredRequest && key) {
206
+ if (attemptControllers.has(key) || settledAttempts.has(key)) return;
207
+ if (pendingAttemptCancels.delete(key)) {
208
+ emitTerminal(key, {
219
209
  requestId,
220
- ownerRunId: v2Request.ownerRunId,
221
- nodeId: v2Request.nodeId,
210
+ ownerRunId: structuredRequest.ownerRunId,
211
+ nodeId: structuredRequest.nodeId,
222
212
  status: "cancelled",
223
213
  });
224
214
  return;
225
215
  }
226
- if (v2IdentitySaturated) {
216
+ if (identitySaturated) {
227
217
  options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, {
228
- version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
229
218
  requestId,
230
- ownerRunId: v2Request.ownerRunId,
231
- nodeId: v2Request.nodeId,
219
+ ownerRunId: structuredRequest.ownerRunId,
220
+ nodeId: structuredRequest.nodeId,
232
221
  status: "unavailable_context",
233
- error: "Delegation v2 identity capacity is exhausted for this extension context.",
234
- } satisfies SubagentDelegationV2Response);
222
+ error: "Delegation identity capacity is exhausted for this extension context.",
223
+ } satisfies SubagentDelegationResponse);
235
224
  return;
236
225
  }
237
- const active = activeV2Nodes.get(v2NodeKey(v2Request.ownerRunId, v2Request.nodeId));
226
+ const active = activeOwnedNodes.get(nodeKey(structuredRequest.ownerRunId, structuredRequest.nodeId));
238
227
  if (active) {
239
- emitV2Terminal(v2Key, {
240
- version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
228
+ emitTerminal(key, {
241
229
  requestId,
242
- ownerRunId: v2Request.ownerRunId,
243
- nodeId: v2Request.nodeId,
230
+ ownerRunId: structuredRequest.ownerRunId,
231
+ nodeId: structuredRequest.nodeId,
244
232
  status: "duplicate_node",
245
233
  });
246
234
  return;
@@ -248,22 +236,14 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
248
236
  }
249
237
  const ctx = options.getContext();
250
238
  if (!ctx) {
251
- if (v2Request && v2Key) {
252
- emitV2Terminal(v2Key, {
253
- version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
239
+ if (structuredRequest && key) {
240
+ emitTerminal(key, {
254
241
  requestId,
255
- ownerRunId: v2Request.ownerRunId,
256
- nodeId: v2Request.nodeId,
242
+ ownerRunId: structuredRequest.ownerRunId,
243
+ nodeId: structuredRequest.nodeId,
257
244
  status: "unavailable_context",
258
245
  error: "No active extension context for delegated subagent execution.",
259
246
  });
260
- } else if (versionedRequest) {
261
- options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, {
262
- version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
263
- requestId,
264
- status: "unavailable_context",
265
- error: "No active extension context for delegated subagent execution.",
266
- } satisfies SubagentDelegationResponse);
267
247
  } else if (legacyRequest) {
268
248
  options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, {
269
249
  ...legacyRequest,
@@ -276,29 +256,22 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
276
256
  }
277
257
 
278
258
  const controller = new AbortController();
279
- if (v2Request && v2Key) {
280
- v2Controllers.set(v2Key, controller);
281
- activeV2Nodes.set(v2NodeKey(v2Request.ownerRunId, v2Request.nodeId), { attemptKey: v2Key, controller });
259
+ if (structuredRequest && key) {
260
+ attemptControllers.set(key, controller);
261
+ activeOwnedNodes.set(nodeKey(structuredRequest.ownerRunId, structuredRequest.nodeId), { attemptKey: key, controller });
282
262
  } else {
283
- controllers.set(requestId, controller);
284
- if (pendingCancels.delete(requestId)) controller.abort();
263
+ legacyControllers.set(requestId, controller);
264
+ if (pendingLegacyCancels.delete(requestId)) controller.abort();
285
265
  }
286
266
  if (controller.signal.aborted) {
287
- if (v2Request && v2Key) {
288
- emitV2Terminal(v2Key, {
289
- version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
267
+ if (structuredRequest && key) {
268
+ emitTerminal(key, {
290
269
  requestId,
291
- ownerRunId: v2Request.ownerRunId,
292
- nodeId: v2Request.nodeId,
270
+ ownerRunId: structuredRequest.ownerRunId,
271
+ nodeId: structuredRequest.nodeId,
293
272
  status: "cancelled",
294
273
  });
295
- activeV2Nodes.delete(v2NodeKey(v2Request.ownerRunId, v2Request.nodeId));
296
- } else if (versionedRequest) {
297
- options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, {
298
- version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
299
- requestId,
300
- status: "cancelled",
301
- } satisfies SubagentDelegationResponse);
274
+ activeOwnedNodes.delete(nodeKey(structuredRequest.ownerRunId, structuredRequest.nodeId));
302
275
  } else if (legacyRequest) {
303
276
  options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, {
304
277
  ...legacyRequest,
@@ -307,23 +280,21 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
307
280
  errorText: "Delegated prompt cancelled.",
308
281
  } satisfies PromptTemplateDelegationResponse);
309
282
  }
310
- if (v2Key) v2Controllers.delete(v2Key);
311
- else controllers.delete(requestId);
283
+ if (key) attemptControllers.delete(key);
284
+ else legacyControllers.delete(requestId);
312
285
  return;
313
286
  }
314
287
 
315
288
  options.events.emit(
316
- versionedRequest ? SUBAGENT_DELEGATION_STARTED_EVENT : PROMPT_TEMPLATE_SUBAGENT_STARTED_EVENT,
317
- v2Request
318
- ? { version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION, requestId, ownerRunId: v2Request.ownerRunId, nodeId: v2Request.nodeId }
319
- : versionedRequest
320
- ? { version: SUBAGENT_DELEGATION_PROTOCOL_VERSION, requestId }
321
- : { requestId },
289
+ structuredRequest ? SUBAGENT_DELEGATION_STARTED_EVENT : PROMPT_TEMPLATE_SUBAGENT_STARTED_EVENT,
290
+ structuredRequest
291
+ ? { requestId, ownerRunId: structuredRequest.ownerRunId, nodeId: structuredRequest.nodeId }
292
+ : { requestId },
322
293
  );
323
294
 
324
295
  try {
325
- const executeRequest = versionedRequest && options.executeVersioned
326
- ? options.executeVersioned
296
+ const executeRequest = structuredRequest && options.executeStructured
297
+ ? options.executeStructured
327
298
  : options.execute;
328
299
  const result = await executeRequest(
329
300
  requestId,
@@ -331,14 +302,9 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
331
302
  controller.signal,
332
303
  ctx,
333
304
  (update) => {
334
- if (v2Key ? !ownsV2Attempt(v2Key, controller) : !ownsRequest(requestId, controller)) return;
335
- if (v2Request) {
336
- const payload = toSubagentDelegationV2Update(v2Request, update);
337
- if (payload) options.events.emit(SUBAGENT_DELEGATION_UPDATE_EVENT, payload);
338
- return;
339
- }
340
- if (versionedRequest) {
341
- const payload = toSubagentDelegationUpdate(requestId, update);
305
+ if (key ? !ownsAttempt(key, controller) : !ownsLegacyRequest(requestId, controller)) return;
306
+ if (structuredRequest) {
307
+ const payload = toSubagentDelegationUpdate(structuredRequest, update);
342
308
  if (payload) options.events.emit(SUBAGENT_DELEGATION_UPDATE_EVENT, payload);
343
309
  return;
344
310
  }
@@ -346,14 +312,9 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
346
312
  if (payload) options.events.emit(PROMPT_TEMPLATE_SUBAGENT_UPDATE_EVENT, payload);
347
313
  },
348
314
  );
349
- if (v2Key ? !ownsV2Attempt(v2Key, controller) : !ownsRequest(requestId, controller)) return;
350
- if (v2Request && v2Key) {
351
- emitV2Terminal(v2Key, toSubagentDelegationV2Response(v2Request, result, controller.signal.aborted));
352
- } else if (versionedRequest) {
353
- options.events.emit(
354
- SUBAGENT_DELEGATION_RESPONSE_EVENT,
355
- toSubagentDelegationResponse(requestId, result, controller.signal.aborted),
356
- );
315
+ if (key ? !ownsAttempt(key, controller) : !ownsLegacyRequest(requestId, controller)) return;
316
+ if (structuredRequest && key) {
317
+ emitTerminal(key, toSubagentDelegationResponse(structuredRequest, result, controller.signal.aborted));
357
318
  } else if (legacyRequest) {
358
319
  options.events.emit(
359
320
  PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT,
@@ -363,23 +324,15 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
363
324
  );
364
325
  }
365
326
  } catch (error) {
366
- if (v2Key ? !ownsV2Attempt(v2Key, controller) : !ownsRequest(requestId, controller)) return;
367
- if (v2Request && v2Key) {
368
- emitV2Terminal(v2Key, {
369
- version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
327
+ if (key ? !ownsAttempt(key, controller) : !ownsLegacyRequest(requestId, controller)) return;
328
+ if (structuredRequest && key) {
329
+ emitTerminal(key, {
370
330
  requestId,
371
- ownerRunId: v2Request.ownerRunId,
372
- nodeId: v2Request.nodeId,
331
+ ownerRunId: structuredRequest.ownerRunId,
332
+ nodeId: structuredRequest.nodeId,
373
333
  status: controller.signal.aborted ? "cancelled" : "failed",
374
334
  ...(controller.signal.aborted ? {} : { error: error instanceof Error ? error.message : String(error) }),
375
335
  });
376
- } else if (versionedRequest) {
377
- options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, {
378
- version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
379
- requestId,
380
- status: controller.signal.aborted ? "cancelled" : "failed",
381
- ...(controller.signal.aborted ? {} : { error: error instanceof Error ? error.message : String(error) }),
382
- } satisfies SubagentDelegationResponse);
383
336
  } else if (legacyRequest) {
384
337
  options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, {
385
338
  ...legacyRequest,
@@ -389,40 +342,40 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
389
342
  } satisfies PromptTemplateDelegationResponse);
390
343
  }
391
344
  } finally {
392
- if (v2Key) {
393
- if (v2Controllers.get(v2Key) === controller) v2Controllers.delete(v2Key);
394
- } else if (controllers.get(requestId) === controller) controllers.delete(requestId);
395
- if (v2Request) {
396
- const key = v2NodeKey(v2Request.ownerRunId, v2Request.nodeId);
397
- if (activeV2Nodes.get(key)?.controller === controller) activeV2Nodes.delete(key);
345
+ if (key) {
346
+ if (attemptControllers.get(key) === controller) attemptControllers.delete(key);
347
+ } else if (legacyControllers.get(requestId) === controller) legacyControllers.delete(requestId);
348
+ if (structuredRequest) {
349
+ const ownedNodeKey = nodeKey(structuredRequest.ownerRunId, structuredRequest.nodeId);
350
+ if (activeOwnedNodes.get(ownedNodeKey)?.controller === controller) activeOwnedNodes.delete(ownedNodeKey);
398
351
  }
399
352
  }
400
353
  });
401
354
 
402
355
  return {
403
356
  cancelAll: () => {
404
- for (const controller of controllers.values()) controller.abort();
405
- for (const controller of v2Controllers.values()) controller.abort();
406
- controllers.clear();
407
- v2Controllers.clear();
408
- pendingCancels.clear();
409
- pendingV2Cancels.clear();
410
- activeV2Nodes.clear();
411
- settledV2Attempts.clear();
412
- v2IdentitySaturated = false;
357
+ for (const controller of legacyControllers.values()) controller.abort();
358
+ for (const controller of attemptControllers.values()) controller.abort();
359
+ legacyControllers.clear();
360
+ attemptControllers.clear();
361
+ pendingLegacyCancels.clear();
362
+ pendingAttemptCancels.clear();
363
+ activeOwnedNodes.clear();
364
+ settledAttempts.clear();
365
+ identitySaturated = false;
413
366
  },
414
367
  dispose: () => {
415
368
  disposed = true;
416
- for (const controller of controllers.values()) controller.abort();
417
- for (const controller of v2Controllers.values()) controller.abort();
418
- controllers.clear();
419
- v2Controllers.clear();
369
+ for (const controller of legacyControllers.values()) controller.abort();
370
+ for (const controller of attemptControllers.values()) controller.abort();
371
+ legacyControllers.clear();
372
+ attemptControllers.clear();
420
373
  for (const unsubscribe of subscriptions) unsubscribe();
421
374
  subscriptions.length = 0;
422
- pendingCancels.clear();
423
- pendingV2Cancels.clear();
424
- activeV2Nodes.clear();
425
- settledV2Attempts.clear();
375
+ pendingLegacyCancels.clear();
376
+ pendingAttemptCancels.clear();
377
+ activeOwnedNodes.clear();
378
+ settledAttempts.clear();
426
379
  },
427
380
  };
428
381
  }