pi-subagents 0.40.0 → 0.41.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 (119) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +246 -525
  3. package/agents/oracle.md +1 -0
  4. package/package.json +12 -4
  5. package/prompts/parallel-context-build.md +1 -1
  6. package/prompts/parallel-handoff-plan.md +1 -1
  7. package/prompts/review-loop.md +1 -1
  8. package/skills/pi-subagents/SKILL.md +6 -6
  9. package/skills/pi-subagents/references/constraints-and-recipes.md +19 -26
  10. package/skills/pi-subagents/references/execution-controls.md +98 -97
  11. package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  12. package/skills/pi-subagents/references/prompting-and-roles.md +18 -27
  13. package/src/agents/agent-management.ts +155 -65
  14. package/src/agents/agent-serializer.ts +19 -0
  15. package/src/agents/agents.ts +154 -71
  16. package/src/agents/chain-serializer.ts +10 -7
  17. package/src/agents/frontmatter.ts +5 -3
  18. package/src/agents/identity.ts +1 -1
  19. package/src/agents/proactive-skills.ts +13 -10
  20. package/src/agents/skills.ts +23 -6
  21. package/src/api/control-channel.ts +4 -0
  22. package/src/api/delegation.ts +26 -194
  23. package/src/api/external-runs.ts +129 -0
  24. package/src/api/intercom-bridge.ts +3 -0
  25. package/src/api/pi-args.ts +5 -0
  26. package/src/api/preflight.ts +3 -3
  27. package/src/api/shared-types.ts +19 -0
  28. package/src/extension/config.ts +10 -0
  29. package/src/extension/control-notices.ts +5 -39
  30. package/src/extension/doctor.ts +10 -9
  31. package/src/extension/fanout-child.ts +7 -4
  32. package/src/extension/index.ts +232 -68
  33. package/src/extension/rpc.ts +18 -12
  34. package/src/extension/schemas.ts +48 -37
  35. package/src/extension/tool-description.ts +36 -86
  36. package/src/inspectors/herdr/actions.ts +229 -0
  37. package/src/inspectors/herdr/client.ts +130 -0
  38. package/src/inspectors/herdr/inspector-runner.ts +141 -0
  39. package/src/inspectors/herdr/project-panes.ts +154 -0
  40. package/src/integrations/herdr-status.ts +330 -0
  41. package/src/intercom/intercom-bridge.ts +3 -2
  42. package/src/intercom/result-intercom.ts +5 -1
  43. package/src/missions/actions.ts +372 -0
  44. package/src/missions/lifecycle.ts +314 -0
  45. package/src/missions/store.ts +442 -0
  46. package/src/missions/types.ts +135 -0
  47. package/src/policy/authority.ts +46 -0
  48. package/src/profiles/profiles.ts +29 -3
  49. package/src/runs/background/async-execution.ts +98 -49
  50. package/src/runs/background/async-job-tracker.ts +10 -2
  51. package/src/runs/background/async-resume.ts +6 -6
  52. package/src/runs/background/async-status.ts +29 -1
  53. package/src/runs/background/auto-drain.ts +3 -3
  54. package/src/runs/background/chain-append.ts +3 -2
  55. package/src/runs/background/control-channel.ts +9 -7
  56. package/src/runs/background/fleet-view.ts +3 -4
  57. package/src/runs/background/notify.ts +2 -1
  58. package/src/runs/background/process-terminal.ts +5 -5
  59. package/src/runs/background/result-watcher.ts +13 -5
  60. package/src/runs/background/run-id-resolver.ts +3 -3
  61. package/src/runs/background/run-status.ts +35 -8
  62. package/src/runs/background/scheduled-runs.ts +602 -375
  63. package/src/runs/background/stale-run-reconciler.ts +3 -3
  64. package/src/runs/background/subagent-runner.ts +608 -445
  65. package/src/runs/background/subagent-wait.ts +50 -9
  66. package/src/runs/background/wait-subscriptions.ts +253 -0
  67. package/src/runs/background/wait-tool.ts +12 -4
  68. package/src/runs/foreground/async-steering-action.ts +3 -3
  69. package/src/runs/foreground/chain-clarify.ts +8 -4
  70. package/src/runs/foreground/chain-execution.ts +56 -30
  71. package/src/runs/foreground/execution.ts +15 -2
  72. package/src/runs/foreground/subagent-executor.ts +1023 -273
  73. package/src/runs/shared/acceptance.ts +28 -6
  74. package/src/runs/shared/child-protocol.ts +302 -22
  75. package/src/runs/shared/dynamic-fanout.ts +1 -1
  76. package/src/runs/shared/external-cli-runner.ts +130 -0
  77. package/src/runs/shared/long-running-guard.ts +42 -1
  78. package/src/runs/shared/nested-events.ts +59 -5
  79. package/src/runs/shared/nested-render.ts +9 -4
  80. package/src/runs/shared/parallel-handoff.ts +86 -2
  81. package/src/runs/shared/parallel-utils.ts +11 -2
  82. package/src/runs/shared/permissions.ts +95 -0
  83. package/src/runs/shared/pi-args.ts +11 -1
  84. package/src/runs/shared/pi-spawn.ts +11 -1
  85. package/src/runs/shared/run-history.ts +1 -1
  86. package/src/runs/shared/subagent-prompt-runtime.ts +36 -5
  87. package/src/runs/shared/subagent-startup-retry.ts +5 -2
  88. package/src/runs/shared/turn-budget.ts +6 -6
  89. package/src/runs/shared/worktree.ts +122 -12
  90. package/src/shared/accessible-dir.ts +29 -7
  91. package/src/shared/artifacts.ts +18 -1
  92. package/src/shared/fork-context.ts +3 -2
  93. package/src/shared/launch-contract.ts +1 -0
  94. package/src/shared/settings.ts +10 -0
  95. package/src/shared/types.ts +156 -14
  96. package/src/shared/utils.ts +8 -6
  97. package/src/slash/delegation-adapters.ts +32 -194
  98. package/src/slash/delegation-request.ts +43 -126
  99. package/src/slash/prompt-template-bridge.ts +158 -205
  100. package/src/slash/prompt-workflows.ts +21 -57
  101. package/src/slash/slash-bridge.ts +14 -0
  102. package/src/slash/slash-commands.ts +31 -632
  103. package/src/slash/subagents-admin.ts +18 -14
  104. package/src/tui/fleet-status.ts +156 -21
  105. package/src/tui/fleet-transcript.ts +110 -5
  106. package/src/tui/fleet.ts +56 -24
  107. package/src/tui/render.ts +291 -109
  108. package/src/types/pi-runtime-compat.d.ts +14 -0
  109. package/src/watchdog/lsp-diagnostics.ts +12 -7
  110. package/src/watchdog/model-selection.ts +2 -2
  111. package/src/watchdog/permission-arbiter.ts +145 -0
  112. package/src/watchdog/register-child.ts +1 -1
  113. package/src/watchdog/register-main.ts +1 -1
  114. package/src/watchdog/review.ts +4 -1
  115. package/src/watchdog/runtime.ts +3 -2
  116. package/src/workflows/chat-progress.ts +140 -0
  117. package/src/workflows/scripted-workflow.ts +415 -0
  118. package/agents/advisor.md +0 -73
  119. package/src/extension/chain-validation.ts +0 -181
@@ -1,52 +1,25 @@
1
1
  import {
2
- SUBAGENT_DELEGATION_PROTOCOL_VERSION,
3
- SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
4
- type SubagentDelegationAcceptanceResult,
5
- type SubagentDelegationEffectsResult,
6
- type SubagentDelegationExecutionResult,
7
2
  type SubagentDelegationRequest,
8
3
  type SubagentDelegationResponse,
9
- type SubagentDelegationReviewResult,
10
4
  type SubagentDelegationStatus,
5
+ type SubagentDelegationThinking,
11
6
  type SubagentDelegationUpdate,
12
- type SubagentDelegationV2Request,
13
- type SubagentDelegationV2Response,
14
- type SubagentDelegationV2Thinking,
15
- type SubagentDelegationV2Value,
16
- type SubagentDelegationV2Update,
7
+ type SubagentDelegationValue,
17
8
  } from "../api/delegation.ts";
18
9
  import type { AcceptanceInput, AgentContract, EffectsProjection, ExecutionProjection, JsonSchemaObject, ReviewProjection, ToolBudgetConfig, TurnBudgetConfig, Usage } from "../shared/types.ts";
19
- import { isAgentContractV1 } from "../runs/shared/agent-contract.ts";
20
10
  import { cloneJsonWithinByteLimit } from "./delegation-json.ts";
21
11
 
22
- export interface PromptTemplateDelegationTask {
23
- agent: string;
24
- task: string;
25
- model?: string;
26
- cwd?: string;
27
- }
28
-
29
- export interface PromptTemplateDelegationParallelResult {
30
- agent: string;
31
- messages: unknown[];
32
- isError: boolean;
33
- errorText?: string;
34
- }
35
-
36
12
  export interface PromptTemplateDelegationRequest {
37
13
  requestId: string;
38
14
  agent: string;
39
15
  task: string;
40
- tasks?: PromptTemplateDelegationTask[];
41
16
  context: "fresh" | "fork";
42
17
  model: string;
43
18
  cwd: string;
44
- worktree?: boolean;
45
19
  }
46
20
 
47
21
  export interface PromptTemplateDelegationResponse extends PromptTemplateDelegationRequest {
48
22
  messages: unknown[];
49
- parallelResults?: PromptTemplateDelegationParallelResult[];
50
23
  contentText?: string;
51
24
  isError: boolean;
52
25
  errorText?: string;
@@ -82,11 +55,17 @@ export interface PromptTemplateDelegationUpdate {
82
55
  taskProgress?: PromptTemplateDelegationTaskProgress[];
83
56
  }
84
57
 
58
+ interface DelegationAcceptanceSnapshot {
59
+ status: string;
60
+ evidenceStatus?: string;
61
+ explicit?: boolean;
62
+ }
63
+
85
64
  export interface PromptTemplateBridgeResult {
86
65
  isError?: boolean;
87
66
  content?: unknown;
88
67
  details?: {
89
- mode?: "single" | "parallel" | "chain" | "management";
68
+ mode?: "single" | "parallel" | "chain" | "workflow" | "management";
90
69
  runId?: string;
91
70
  timedOut?: boolean;
92
71
  stopped?: boolean;
@@ -110,7 +89,7 @@ export interface PromptTemplateBridgeResult {
110
89
  sessionFile?: string;
111
90
  agentContract?: AgentContract;
112
91
  execution?: ExecutionProjection;
113
- acceptance?: SubagentDelegationAcceptanceResult;
92
+ acceptance?: DelegationAcceptanceSnapshot;
114
93
  review?: ReviewProjection;
115
94
  effects?: EffectsProjection;
116
95
  usage?: Usage;
@@ -137,14 +116,12 @@ export interface PromptTemplateBridgeResult {
137
116
  export interface DelegatedSubagentExecutionParams {
138
117
  agent?: string;
139
118
  task?: string;
140
- tasks?: PromptTemplateDelegationTask[];
141
119
  context: "fresh" | "fork";
142
120
  model?: string;
143
121
  cwd: string;
144
- worktree?: boolean;
145
122
  timeoutMs?: number;
146
123
  turnBudget?: TurnBudgetConfig;
147
- /** Internal-only strict turn-boundary enforcement for versioned foreground delegation. */
124
+ /** Internal-only strict turn-boundary enforcement for structured foreground delegation. */
148
125
  enforceHardTurnLimit?: boolean;
149
126
  toolBudget?: ToolBudgetConfig;
150
127
  skill?: string | string[] | boolean;
@@ -155,60 +132,31 @@ export interface DelegatedSubagentExecutionParams {
155
132
  acceptance?: AcceptanceInput;
156
133
  artifacts?: boolean;
157
134
  /** Internal-only thinking override accepted by executeDelegated. */
158
- delegatedThinkingOverride?: SubagentDelegationV2Thinking;
159
- /** Internal-only V2 capability accepted and stripped by executeDelegated. */
135
+ delegatedThinkingOverride?: SubagentDelegationThinking;
136
+ /** Internal-only capability accepted and stripped by executeDelegated. */
160
137
  delegatedAllowZeroToolBudget?: true;
161
138
  async: false;
162
139
  foregroundOnly: true;
163
140
  clarify: false;
164
141
  }
165
142
 
166
- function parseDelegationTasks(tasks: unknown): PromptTemplateDelegationTask[] {
167
- if (!Array.isArray(tasks)) return [];
168
- const parsed: PromptTemplateDelegationTask[] = [];
169
- for (const item of tasks) {
170
- if (!item || typeof item !== "object") return [];
171
- const value = item as Partial<PromptTemplateDelegationTask>;
172
- if (typeof value.agent !== "string" || !value.agent.trim()) return [];
173
- if (typeof value.task !== "string" || !value.task.trim()) return [];
174
- const model = typeof value.model === "string" && value.model.trim().length > 0 ? value.model : undefined;
175
- const cwd = typeof value.cwd === "string" && value.cwd.trim().length > 0 ? value.cwd : undefined;
176
- parsed.push({
177
- agent: value.agent,
178
- task: value.task,
179
- ...(model ? { model } : {}),
180
- ...(cwd ? { cwd } : {}),
181
- });
182
- }
183
- return parsed;
184
- }
185
-
186
143
  export function parsePromptTemplateRequest(data: unknown): PromptTemplateDelegationRequest | undefined {
187
144
  if (!data || typeof data !== "object") return undefined;
188
- const value = data as Partial<PromptTemplateDelegationRequest> & { tasks?: unknown };
145
+ const value = data as Partial<PromptTemplateDelegationRequest> & { tasks?: unknown; worktree?: unknown };
146
+ if (value.tasks !== undefined || value.worktree !== undefined) return undefined;
189
147
  if (typeof value.requestId !== "string" || !value.requestId) return undefined;
148
+ if (typeof value.agent !== "string" || !value.agent) return undefined;
149
+ if (typeof value.task !== "string" || !value.task) return undefined;
190
150
  if (typeof value.model !== "string" || !value.model) return undefined;
191
151
  if (typeof value.cwd !== "string" || !value.cwd) return undefined;
192
152
  if (value.context !== "fresh" && value.context !== "fork") return undefined;
193
- const tasks = parseDelegationTasks(value.tasks);
194
- const worktree = value.worktree === true ? true : undefined;
195
- const hasSingle =
196
- typeof value.agent === "string" &&
197
- value.agent.length > 0 &&
198
- typeof value.task === "string" &&
199
- value.task.length > 0;
200
- if (!hasSingle && tasks.length === 0) return undefined;
201
-
202
- const fallbackTask = tasks[0];
203
153
  return {
204
154
  requestId: value.requestId,
205
- agent: hasSingle ? value.agent! : fallbackTask!.agent,
206
- task: hasSingle ? value.task! : fallbackTask!.task,
207
- ...(tasks.length > 0 ? { tasks } : {}),
155
+ agent: value.agent,
156
+ task: value.task,
208
157
  context: value.context,
209
158
  model: value.model,
210
159
  cwd: value.cwd,
211
- ...(worktree ? { worktree } : {}),
212
160
  };
213
161
  }
214
162
 
@@ -339,17 +287,6 @@ export function toDelegationUpdate(requestId: string, update: PromptTemplateBrid
339
287
  }
340
288
 
341
289
  export function toLegacyExecutionParams(request: PromptTemplateDelegationRequest): DelegatedSubagentExecutionParams {
342
- if (request.tasks && request.tasks.length > 0) {
343
- return {
344
- tasks: request.tasks,
345
- context: request.context,
346
- cwd: request.cwd,
347
- worktree: request.worktree,
348
- async: false,
349
- foregroundOnly: true,
350
- clarify: false,
351
- };
352
- }
353
290
  return {
354
291
  agent: request.agent,
355
292
  task: request.task,
@@ -363,30 +300,6 @@ export function toLegacyExecutionParams(request: PromptTemplateDelegationRequest
363
300
  }
364
301
 
365
302
  export function toSubagentDelegationExecutionParams(request: SubagentDelegationRequest): DelegatedSubagentExecutionParams {
366
- return {
367
- agent: request.agent,
368
- task: request.task,
369
- context: request.context,
370
- cwd: request.cwd,
371
- model: request.model,
372
- timeoutMs: request.timeoutMs,
373
- turnBudget: request.turnBudget,
374
- enforceHardTurnLimit: true,
375
- toolBudget: request.toolBudget,
376
- skill: request.skill,
377
- output: request.output,
378
- outputMode: request.outputMode,
379
- outputSchema: request.outputSchema,
380
- agentContract: request.agentContract,
381
- acceptance: request.acceptance,
382
- artifacts: request.artifacts,
383
- async: false,
384
- foregroundOnly: true,
385
- clarify: false,
386
- };
387
- }
388
-
389
- export function toSubagentDelegationV2ExecutionParams(request: SubagentDelegationV2Request): DelegatedSubagentExecutionParams {
390
303
  return {
391
304
  agent: request.agent,
392
305
  task: request.task,
@@ -410,33 +323,13 @@ export function toSubagentDelegationV2ExecutionParams(request: SubagentDelegatio
410
323
  };
411
324
  }
412
325
 
413
- export function toSubagentDelegationUpdate(requestId: string, result: PromptTemplateBridgeResult): SubagentDelegationUpdate | undefined {
414
- const legacy = toDelegationUpdate(requestId, result);
415
- if (!legacy) return undefined;
416
- return {
417
- version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
418
- requestId,
419
- ...(legacy.runId ? { runId: legacy.runId } : {}),
420
- ...(legacy.currentTool ? { currentTool: legacy.currentTool } : {}),
421
- ...(legacy.currentToolArgs ? { currentToolArgs: legacy.currentToolArgs } : {}),
422
- ...(legacy.recentOutput ? { recentOutput: legacy.recentOutput } : {}),
423
- ...(legacy.recentOutputLines ? { recentOutputLines: legacy.recentOutputLines } : {}),
424
- ...(legacy.recentTools ? { recentTools: legacy.recentTools } : {}),
425
- ...(legacy.model ? { model: legacy.model } : {}),
426
- ...(typeof legacy.toolCount === "number" ? { toolCount: legacy.toolCount } : {}),
427
- ...(typeof legacy.durationMs === "number" ? { durationMs: legacy.durationMs } : {}),
428
- ...(typeof legacy.tokens === "number" ? { tokens: legacy.tokens } : {}),
429
- };
430
- }
431
-
432
- export function toSubagentDelegationV2Update(
433
- request: SubagentDelegationV2Request,
326
+ export function toSubagentDelegationUpdate(
327
+ request: SubagentDelegationRequest,
434
328
  result: PromptTemplateBridgeResult,
435
- ): SubagentDelegationV2Update | undefined {
329
+ ): SubagentDelegationUpdate | undefined {
436
330
  const legacy = toDelegationUpdate(request.requestId, result);
437
331
  if (!legacy) return undefined;
438
332
  return {
439
- version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
440
333
  requestId: request.requestId,
441
334
  ownerRunId: request.ownerRunId,
442
335
  nodeId: request.nodeId,
@@ -464,66 +357,30 @@ function resolveSubagentDelegationStatus(
464
357
  if (child?.structuredOutputFailed) return "structured_output_failed";
465
358
  if (child?.turnBudgetExceeded) return "turn_budget_exhausted";
466
359
  if (child?.toolBudgetBlocked) return "tool_budget_exhausted";
467
- if (!isAgentContractV1(child.agentContract) && child?.acceptance?.status === "rejected" && child.acceptance.explicit) return "acceptance_failed";
360
+ if (child?.acceptance?.status === "rejected" && child.acceptance.explicit) return "acceptance_failed";
468
361
  if (result.details?.stopped || child?.stopped || child?.interrupted) return "interrupted";
469
362
  if (result.isError || child?.error || (typeof child?.exitCode === "number" && child.exitCode !== 0)) return "failed";
470
363
  return "completed";
471
364
  }
472
365
 
366
+ const MAX_RESULT_BYTES = 1024 * 1024;
367
+
473
368
  export function toSubagentDelegationResponse(
474
- requestId: string,
369
+ request: SubagentDelegationRequest,
475
370
  result: PromptTemplateBridgeResult,
476
371
  aborted: boolean,
477
372
  ): SubagentDelegationResponse {
478
- const child = result.details?.results?.[0];
479
- const progress = child?.progressSummary ?? result.details?.progress?.[0];
480
- const warnings = [child?.skillsWarning, child?.outputSaveError, child?.transcriptError]
481
- .filter((warning): warning is string => typeof warning === "string" && warning.length > 0);
482
- const status = resolveSubagentDelegationStatus(result, aborted);
483
- const fallbackError = status === "failed" ? firstTextContent(result.content) : undefined;
484
- return {
485
- version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
486
- requestId,
487
- status,
488
- ...(child?.error || fallbackError ? { error: child?.error ?? fallbackError } : {}),
489
- ...(result.details?.runId ? { runId: result.details.runId } : {}),
490
- ...(child ? { childIndex: 0 } : {}),
491
- ...(child?.agent ? { agent: child.agent } : {}),
492
- ...(child?.model ? { model: child.model } : {}),
493
- ...(typeof child?.exitCode === "number" ? { exitCode: child.exitCode } : {}),
494
- ...(child?.execution ? { execution: child.execution as SubagentDelegationExecutionResult } : {}),
495
- ...(child?.finalOutput ? { output: child.finalOutput } : {}),
496
- ...(child?.savedOutputPath ? { outputPath: child.savedOutputPath } : {}),
497
- ...(child?.sessionFile ? { sessionFile: child.sessionFile } : {}),
498
- ...(child?.acceptance ? { acceptance: { status: child.acceptance.status, evidenceStatus: child.acceptance.evidenceStatus, explicit: child.acceptance.explicit } } : {}),
499
- ...(child?.review ? { review: child.review as SubagentDelegationReviewResult } : {}),
500
- ...(child?.effects ? { effects: child.effects as SubagentDelegationEffectsResult } : {}),
501
- ...(typeof child?.usage?.turns === "number" ? { turns: child.usage.turns } : {}),
502
- ...(typeof progress?.toolCount === "number" ? { toolCount: progress.toolCount } : {}),
503
- ...(typeof progress?.durationMs === "number" ? { durationMs: progress.durationMs } : {}),
504
- ...(typeof progress?.tokens === "number" ? { tokens: progress.tokens } : {}),
505
- ...(warnings.length > 0 ? { warnings } : {}),
506
- };
507
- }
508
-
509
- const MAX_V2_RESULT_BYTES = 1024 * 1024;
510
-
511
- export function toSubagentDelegationV2Response(
512
- request: SubagentDelegationV2Request,
513
- result: PromptTemplateBridgeResult,
514
- aborted: boolean,
515
- ): SubagentDelegationV2Response {
516
373
  const child = result.details?.results?.[0];
517
374
  const progress = child?.progressSummary ?? result.details?.progress?.[0];
518
375
  let status = resolveSubagentDelegationStatus(result, aborted);
519
376
  let error = child?.error ?? (status === "failed" ? firstTextContent(result.content) : undefined);
520
- let projectedResult: SubagentDelegationV2Value | undefined;
377
+ let projectedResult: SubagentDelegationValue | undefined;
521
378
  if (status === "completed") {
522
379
  if (request.result.kind === "text") {
523
380
  if (typeof child?.finalOutput !== "string") {
524
381
  status = "failed";
525
382
  error = "Delegated subagent did not capture a text result.";
526
- } else if (Buffer.byteLength(child.finalOutput, "utf8") > MAX_V2_RESULT_BYTES) {
383
+ } else if (Buffer.byteLength(child.finalOutput, "utf8") > MAX_RESULT_BYTES) {
527
384
  status = "failed";
528
385
  error = "Delegated text result exceeds 1 MiB when UTF-8 encoded.";
529
386
  } else {
@@ -533,8 +390,8 @@ export function toSubagentDelegationV2Response(
533
390
  status = "failed";
534
391
  error = "Delegated subagent did not capture the requested structured result.";
535
392
  } else {
536
- const inspected = cloneJsonWithinByteLimit(child.structuredOutput, MAX_V2_RESULT_BYTES);
537
- if (!inspected.ok) {
393
+ const inspected = cloneJsonWithinByteLimit(child.structuredOutput, MAX_RESULT_BYTES);
394
+ if (inspected.ok === false) {
538
395
  status = "failed";
539
396
  error = inspected.reason === "too_large"
540
397
  ? "Delegated structured result exceeds 1 MiB when encoded."
@@ -545,8 +402,8 @@ export function toSubagentDelegationV2Response(
545
402
  }
546
403
  }
547
404
  const usage = child?.usage;
405
+ const childLaunchContractDigest = (child as { launchContractDigest?: string } | undefined)?.launchContractDigest;
548
406
  return {
549
- version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
550
407
  requestId: request.requestId,
551
408
  ownerRunId: request.ownerRunId,
552
409
  nodeId: request.nodeId,
@@ -557,7 +414,7 @@ export function toSubagentDelegationV2Response(
557
414
  ...(child?.model ? { model: child.model } : {}),
558
415
  ...(child?.thinking ? { thinking: child.thinking } : {}),
559
416
  ...(typeof child?.exitCode === "number" ? { exitCode: child.exitCode } : {}),
560
- ...(child?.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
417
+ ...(childLaunchContractDigest ? { launchContractDigest: childLaunchContractDigest } : {}),
561
418
  ...(projectedResult ? { result: projectedResult } : {}),
562
419
  ...(usage ? {
563
420
  usage: {
@@ -580,28 +437,9 @@ export function toPromptTemplateResponse(
580
437
  ): PromptTemplateDelegationResponse {
581
438
  const contentText = firstTextContent(result.content);
582
439
  const messages = buildDelegationMessages(result.details?.results?.[0] ?? {}, contentText);
583
- const parallelResults = request.tasks?.map<PromptTemplateDelegationParallelResult>((task, index) => {
584
- const step = result.details?.results?.[index];
585
- if (!step) {
586
- return {
587
- agent: task.agent,
588
- messages: [],
589
- isError: true,
590
- errorText: "Missing result for delegated parallel task.",
591
- };
592
- }
593
- const exitCode = typeof step.exitCode === "number" ? step.exitCode : undefined;
594
- return {
595
- agent: step.agent ?? task.agent,
596
- messages: buildDelegationMessages(step),
597
- isError: (exitCode !== undefined && exitCode !== 0) || !!step.error,
598
- errorText: step.error || undefined,
599
- };
600
- });
601
440
  return {
602
441
  ...request,
603
442
  messages,
604
- ...(parallelResults ? { parallelResults } : {}),
605
443
  ...(contentText ? { contentText } : {}),
606
444
  isError: result.isError === true,
607
445
  errorText: result.isError ? contentText : undefined,
@@ -1,40 +1,15 @@
1
1
  import {
2
- SUBAGENT_DELEGATION_PROTOCOL_VERSION,
3
- SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
4
2
  type SubagentDelegationRequest,
5
- type SubagentDelegationV2Request,
6
3
  } from "../api/delegation.ts";
7
- import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
8
4
  import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
9
5
  import { resolveTurnBudgetConfig } from "../runs/shared/turn-budget.ts";
10
6
  import { cloneJsonWithinByteLimit } from "./delegation-json.ts";
11
7
 
12
8
  export type SubagentDelegationParseResult =
13
- | { ok: true; request: SubagentDelegationRequest | SubagentDelegationV2Request }
14
- | { ok: false; version?: 1 | 2; requestId?: string; ownerRunId?: string; nodeId?: string; error: string };
9
+ | { ok: true; request: SubagentDelegationRequest }
10
+ | { ok: false; requestId?: string; ownerRunId?: string; nodeId?: string; error: string };
15
11
 
16
- const v1SupportedFields = new Set([
17
- "version",
18
- "requestId",
19
- "agent",
20
- "task",
21
- "context",
22
- "cwd",
23
- "model",
24
- "timeoutMs",
25
- "turnBudget",
26
- "toolBudget",
27
- "skill",
28
- "output",
29
- "outputMode",
30
- "outputSchema",
31
- "agentContract",
32
- "acceptance",
33
- "artifacts",
34
- ]);
35
-
36
- const v2SupportedFields = new Set([
37
- "version",
12
+ const supportedFields = new Set([
38
13
  "requestId",
39
14
  "ownerRunId",
40
15
  "nodeId",
@@ -52,13 +27,13 @@ const v2SupportedFields = new Set([
52
27
  "result",
53
28
  ]);
54
29
 
55
- const v2ThinkingLevels = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
30
+ const thinkingLevels = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
56
31
  const MAX_SCHEMA_BYTES = 64 * 1024;
57
- const MAX_V2_TASK_BYTES = 1024 * 1024;
58
- const MAX_V2_CWD_BYTES = 32 * 1024;
59
- const MAX_V2_SHORT_TEXT_BYTES = 1024;
60
- const MAX_V2_SKILL_ENTRIES = 256;
61
- const MAX_V2_SKILL_AGGREGATE_BYTES = 64 * 1024;
32
+ const MAX_TASK_BYTES = 1024 * 1024;
33
+ const MAX_CWD_BYTES = 32 * 1024;
34
+ const MAX_SHORT_TEXT_BYTES = 1024;
35
+ const MAX_SKILL_ENTRIES = 256;
36
+ const MAX_SKILL_AGGREGATE_BYTES = 64 * 1024;
62
37
 
63
38
  function nonEmptyString(value: unknown): value is string {
64
39
  return typeof value === "string" && value.trim().length > 0;
@@ -69,10 +44,26 @@ function validateId(value: unknown): string | undefined {
69
44
  return value;
70
45
  }
71
46
 
72
- function validateSharedFields(
73
- value: Record<string, unknown>,
74
- identity: { requestId: string; version?: 1 | 2; ownerRunId?: string; nodeId?: string },
75
- ): SubagentDelegationParseResult | undefined {
47
+ export function parseSubagentDelegationRequest(data: unknown): SubagentDelegationParseResult {
48
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
49
+ return { ok: false, error: "Delegation request must be an object." };
50
+ }
51
+ const value = data as Record<string, unknown>;
52
+ const requestId = validateId(value.requestId);
53
+ if (!requestId) {
54
+ return { ok: false, error: "Delegation requestId must be a non-empty string of at most 256 characters without newlines." };
55
+ }
56
+ const ownerRunId = validateId(value.ownerRunId);
57
+ if (!ownerRunId) {
58
+ return { ok: false, requestId, error: "Delegation ownerRunId must be a non-empty string of at most 256 characters without newlines." };
59
+ }
60
+ const nodeId = validateId(value.nodeId);
61
+ if (!nodeId) {
62
+ return { ok: false, requestId, ownerRunId, error: "Delegation nodeId must be a non-empty string of at most 256 characters without newlines." };
63
+ }
64
+ const identity = { requestId, ownerRunId, nodeId };
65
+ const unsupportedField = Object.keys(value).find((key) => !supportedFields.has(key));
66
+ if (unsupportedField) return { ok: false, ...identity, error: `Unsupported delegation field: ${unsupportedField}.` };
76
67
  if (!nonEmptyString(value.agent)) return { ok: false, ...identity, error: "Delegation agent must be a non-empty string." };
77
68
  if (!nonEmptyString(value.task)) return { ok: false, ...identity, error: "Delegation task must be a non-empty string." };
78
69
  if (value.context !== "fresh" && value.context !== "fork") {
@@ -85,8 +76,9 @@ function validateSharedFields(
85
76
  if (value.timeoutMs !== undefined && (typeof value.timeoutMs !== "number" || !Number.isInteger(value.timeoutMs) || value.timeoutMs < 1)) {
86
77
  return { ok: false, ...identity, error: "timeoutMs must be an integer >= 1." };
87
78
  }
88
- if (identity.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION && value.timeoutMs !== undefined && value.timeoutMs > 2_147_483_647) {
89
- return { ok: false, ...identity, error: "timeoutMs must be <= 2147483647 for delegation v2." };
79
+ const timeoutMs = typeof value.timeoutMs === "number" ? value.timeoutMs : undefined;
80
+ if (timeoutMs !== undefined && timeoutMs > 2_147_483_647) {
81
+ return { ok: false, ...identity, error: "timeoutMs must be <= 2147483647." };
90
82
  }
91
83
  const turnBudget = resolveTurnBudgetConfig(value.turnBudget);
92
84
  if (turnBudget.error) return { ok: false, ...identity, error: turnBudget.error };
@@ -96,11 +88,7 @@ function validateSharedFields(
96
88
  return { ok: false, ...identity, error: `toolBudget.${unsupportedToolBudgetField} is not supported.` };
97
89
  }
98
90
  }
99
- const toolBudget = validateToolBudgetConfig(
100
- value.toolBudget,
101
- "toolBudget",
102
- identity.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION ? { minimumHard: 0 } : undefined,
103
- );
91
+ const toolBudget = validateToolBudgetConfig(value.toolBudget, "toolBudget", { minimumHard: 0 });
104
92
  if (toolBudget.error) return { ok: false, ...identity, error: toolBudget.error };
105
93
  if (value.skill !== undefined) {
106
94
  const validSkill = typeof value.skill === "boolean"
@@ -113,46 +101,29 @@ function validateSharedFields(
113
101
  if (value.artifacts !== undefined && typeof value.artifacts !== "boolean") {
114
102
  return { ok: false, ...identity, error: "artifacts must be a boolean." };
115
103
  }
116
- return undefined;
117
- }
118
-
119
- function parseV2(value: Record<string, unknown>, requestId: string): SubagentDelegationParseResult {
120
- const ownerRunId = validateId(value.ownerRunId);
121
- if (!ownerRunId) {
122
- return { ok: false, version: 2, requestId, error: "Delegation ownerRunId must be a non-empty string of at most 256 characters without newlines." };
123
- }
124
- const nodeId = validateId(value.nodeId);
125
- if (!nodeId) {
126
- return { ok: false, version: 2, requestId, ownerRunId, error: "Delegation nodeId must be a non-empty string of at most 256 characters without newlines." };
127
- }
128
- const identity = { version: 2 as const, requestId, ownerRunId, nodeId };
129
- const unsupportedField = Object.keys(value).find((key) => !v2SupportedFields.has(key));
130
- if (unsupportedField) return { ok: false, ...identity, error: `Unsupported delegation field: ${unsupportedField}.` };
131
- const sharedError = validateSharedFields(value, identity);
132
- if (sharedError) return sharedError;
133
- if (Buffer.byteLength(value.task as string, "utf8") > MAX_V2_TASK_BYTES) {
104
+ if (Buffer.byteLength(value.task as string, "utf8") > MAX_TASK_BYTES) {
134
105
  return { ok: false, ...identity, error: "Delegation task exceeds 1 MiB when UTF-8 encoded." };
135
106
  }
136
- if (Buffer.byteLength(value.cwd as string, "utf8") > MAX_V2_CWD_BYTES) {
107
+ if (Buffer.byteLength(value.cwd as string, "utf8") > MAX_CWD_BYTES) {
137
108
  return { ok: false, ...identity, error: "Delegation cwd exceeds 32 KiB when UTF-8 encoded." };
138
109
  }
139
- if (Buffer.byteLength(value.agent as string, "utf8") > MAX_V2_SHORT_TEXT_BYTES) {
110
+ if (Buffer.byteLength(value.agent as string, "utf8") > MAX_SHORT_TEXT_BYTES) {
140
111
  return { ok: false, ...identity, error: "Delegation agent exceeds 1 KiB when UTF-8 encoded." };
141
112
  }
142
- if (typeof value.model === "string" && Buffer.byteLength(value.model, "utf8") > MAX_V2_SHORT_TEXT_BYTES) {
113
+ if (typeof value.model === "string" && Buffer.byteLength(value.model, "utf8") > MAX_SHORT_TEXT_BYTES) {
143
114
  return { ok: false, ...identity, error: "Delegation model exceeds 1 KiB when UTF-8 encoded." };
144
115
  }
145
116
  const skillEntries = typeof value.skill === "string" ? [value.skill] : Array.isArray(value.skill) ? value.skill as string[] : [];
146
- if (skillEntries.length > MAX_V2_SKILL_ENTRIES) {
117
+ if (skillEntries.length > MAX_SKILL_ENTRIES) {
147
118
  return { ok: false, ...identity, error: "Delegation skill supports at most 256 entries." };
148
119
  }
149
- if (skillEntries.some((entry) => Buffer.byteLength(entry, "utf8") > MAX_V2_SHORT_TEXT_BYTES)) {
120
+ if (skillEntries.some((entry) => Buffer.byteLength(entry, "utf8") > MAX_SHORT_TEXT_BYTES)) {
150
121
  return { ok: false, ...identity, error: "Delegation skill entry exceeds 1 KiB when UTF-8 encoded." };
151
122
  }
152
- if (skillEntries.reduce((total, entry) => total + Buffer.byteLength(entry, "utf8"), 0) > MAX_V2_SKILL_AGGREGATE_BYTES) {
123
+ if (skillEntries.reduce((total, entry) => total + Buffer.byteLength(entry, "utf8"), 0) > MAX_SKILL_AGGREGATE_BYTES) {
153
124
  return { ok: false, ...identity, error: "Delegation skill entries exceed 64 KiB in aggregate when UTF-8 encoded." };
154
125
  }
155
- if (value.thinking !== undefined && (typeof value.thinking !== "string" || !v2ThinkingLevels.has(value.thinking))) {
126
+ if (value.thinking !== undefined && (typeof value.thinking !== "string" || !thinkingLevels.has(value.thinking))) {
156
127
  return { ok: false, ...identity, error: "thinking must be one of off, minimal, low, medium, high, xhigh, or max." };
157
128
  }
158
129
  if (!value.result || typeof value.result !== "object" || Array.isArray(value.result)) {
@@ -170,7 +141,7 @@ function parseV2(value: Record<string, unknown>, requestId: string): SubagentDel
170
141
  return { ok: false, ...identity, error: "result.schema must be a JSON Schema object." };
171
142
  }
172
143
  const inspectedSchema = cloneJsonWithinByteLimit(result.schema, MAX_SCHEMA_BYTES);
173
- if (!inspectedSchema.ok) {
144
+ if (inspectedSchema.ok === false) {
174
145
  return {
175
146
  ok: false,
176
147
  ...identity,
@@ -190,60 +161,6 @@ function parseV2(value: Record<string, unknown>, requestId: string): SubagentDel
190
161
  result: structuredSchema
191
162
  ? { kind: "structured", schema: structuredSchema }
192
163
  : { kind: "text" },
193
- } as unknown as SubagentDelegationV2Request,
164
+ } as unknown as SubagentDelegationRequest,
194
165
  };
195
166
  }
196
-
197
- function parseV1(value: Record<string, unknown>, requestId: string): SubagentDelegationParseResult {
198
- const unsupportedField = Object.keys(value).find((key) => !v1SupportedFields.has(key));
199
- if (unsupportedField) return { ok: false, requestId, error: `Unsupported delegation field: ${unsupportedField}.` };
200
- const sharedError = validateSharedFields(value, { requestId });
201
- if (sharedError) return sharedError;
202
- if (value.output !== undefined && typeof value.output !== "boolean" && !nonEmptyString(value.output)) {
203
- return { ok: false, requestId, error: "output must be a boolean or non-empty string." };
204
- }
205
- if (value.outputMode !== undefined && value.outputMode !== "inline" && value.outputMode !== "file-only") {
206
- return { ok: false, requestId, error: "outputMode must be inline or file-only." };
207
- }
208
- if (value.outputMode === "file-only" && !nonEmptyString(value.output)) {
209
- return { ok: false, requestId, error: 'outputMode "file-only" requires output to be a non-empty path.' };
210
- }
211
- if (value.outputSchema !== undefined && (!value.outputSchema || typeof value.outputSchema !== "object" || Array.isArray(value.outputSchema))) {
212
- return { ok: false, requestId, error: "outputSchema must be a JSON Schema object." };
213
- }
214
- if (value.agentContract !== undefined) {
215
- if (!value.agentContract || typeof value.agentContract !== "object" || Array.isArray(value.agentContract)) return { ok: false, requestId, error: "agentContract must be an object." };
216
- const contract = value.agentContract as Record<string, unknown>;
217
- if (contract.version !== 1 || Object.keys(contract).some((key) => key !== "version")) return { ok: false, requestId, error: "agentContract must be { version: 1 }." };
218
- }
219
- const acceptanceErrors = validateAcceptanceInput(value.acceptance);
220
- if (acceptanceErrors.length > 0) return { ok: false, requestId, error: acceptanceErrors.join(" ") };
221
- return { ok: true, request: value as unknown as SubagentDelegationRequest };
222
- }
223
-
224
- export function parseSubagentDelegationRequest(data: unknown): SubagentDelegationParseResult {
225
- if (!data || typeof data !== "object" || Array.isArray(data)) {
226
- return { ok: false, error: "Delegation request must be an object." };
227
- }
228
- const value = data as Record<string, unknown>;
229
- const requestId = validateId(value.requestId);
230
- if (value.version !== SUBAGENT_DELEGATION_PROTOCOL_VERSION && value.version !== SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION) {
231
- return {
232
- ...(requestId ? { requestId } : {}),
233
- ok: false,
234
- error: `Unsupported delegation protocol version: ${String(value.version)}.`,
235
- };
236
- }
237
- if (!requestId) {
238
- return { ok: false, error: "Delegation requestId must be a non-empty string of at most 256 characters without newlines." };
239
- }
240
- if (value.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION) {
241
- // Preserve the v1 parser's historical diagnostic for a v1-shaped payload
242
- // whose version alone was changed to 2.
243
- if (!Object.hasOwn(value, "ownerRunId") && !Object.hasOwn(value, "nodeId") && !Object.hasOwn(value, "result")) {
244
- return { ok: false, version: 2, requestId, error: "Unsupported delegation protocol version: 2." };
245
- }
246
- return parseV2(value, requestId);
247
- }
248
- return parseV1(value, requestId);
249
- }