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.
- package/CHANGELOG.md +76 -0
- package/README.md +246 -525
- package/agents/oracle.md +1 -0
- package/package.json +12 -4
- package/prompts/parallel-context-build.md +1 -1
- package/prompts/parallel-handoff-plan.md +1 -1
- package/prompts/review-loop.md +1 -1
- package/skills/pi-subagents/SKILL.md +6 -6
- package/skills/pi-subagents/references/constraints-and-recipes.md +19 -26
- package/skills/pi-subagents/references/execution-controls.md +98 -97
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +18 -27
- package/src/agents/agent-management.ts +155 -65
- package/src/agents/agent-serializer.ts +19 -0
- package/src/agents/agents.ts +154 -71
- package/src/agents/chain-serializer.ts +10 -7
- package/src/agents/frontmatter.ts +5 -3
- package/src/agents/identity.ts +1 -1
- package/src/agents/proactive-skills.ts +13 -10
- package/src/agents/skills.ts +23 -6
- package/src/api/control-channel.ts +4 -0
- package/src/api/delegation.ts +26 -194
- package/src/api/external-runs.ts +129 -0
- package/src/api/intercom-bridge.ts +3 -0
- package/src/api/pi-args.ts +5 -0
- package/src/api/preflight.ts +3 -3
- package/src/api/shared-types.ts +19 -0
- package/src/extension/config.ts +10 -0
- package/src/extension/control-notices.ts +5 -39
- package/src/extension/doctor.ts +10 -9
- package/src/extension/fanout-child.ts +7 -4
- package/src/extension/index.ts +232 -68
- package/src/extension/rpc.ts +18 -12
- package/src/extension/schemas.ts +48 -37
- package/src/extension/tool-description.ts +36 -86
- package/src/inspectors/herdr/actions.ts +229 -0
- package/src/inspectors/herdr/client.ts +130 -0
- package/src/inspectors/herdr/inspector-runner.ts +141 -0
- package/src/inspectors/herdr/project-panes.ts +154 -0
- package/src/integrations/herdr-status.ts +330 -0
- package/src/intercom/intercom-bridge.ts +3 -2
- package/src/intercom/result-intercom.ts +5 -1
- package/src/missions/actions.ts +372 -0
- package/src/missions/lifecycle.ts +314 -0
- package/src/missions/store.ts +442 -0
- package/src/missions/types.ts +135 -0
- package/src/policy/authority.ts +46 -0
- package/src/profiles/profiles.ts +29 -3
- package/src/runs/background/async-execution.ts +98 -49
- package/src/runs/background/async-job-tracker.ts +10 -2
- package/src/runs/background/async-resume.ts +6 -6
- package/src/runs/background/async-status.ts +29 -1
- package/src/runs/background/auto-drain.ts +3 -3
- package/src/runs/background/chain-append.ts +3 -2
- package/src/runs/background/control-channel.ts +9 -7
- package/src/runs/background/fleet-view.ts +3 -4
- package/src/runs/background/notify.ts +2 -1
- package/src/runs/background/process-terminal.ts +5 -5
- package/src/runs/background/result-watcher.ts +13 -5
- package/src/runs/background/run-id-resolver.ts +3 -3
- package/src/runs/background/run-status.ts +35 -8
- package/src/runs/background/scheduled-runs.ts +602 -375
- package/src/runs/background/stale-run-reconciler.ts +3 -3
- package/src/runs/background/subagent-runner.ts +608 -445
- package/src/runs/background/subagent-wait.ts +50 -9
- package/src/runs/background/wait-subscriptions.ts +253 -0
- package/src/runs/background/wait-tool.ts +12 -4
- package/src/runs/foreground/async-steering-action.ts +3 -3
- package/src/runs/foreground/chain-clarify.ts +8 -4
- package/src/runs/foreground/chain-execution.ts +56 -30
- package/src/runs/foreground/execution.ts +15 -2
- package/src/runs/foreground/subagent-executor.ts +1023 -273
- package/src/runs/shared/acceptance.ts +28 -6
- package/src/runs/shared/child-protocol.ts +302 -22
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/external-cli-runner.ts +130 -0
- package/src/runs/shared/long-running-guard.ts +42 -1
- package/src/runs/shared/nested-events.ts +59 -5
- package/src/runs/shared/nested-render.ts +9 -4
- package/src/runs/shared/parallel-handoff.ts +86 -2
- package/src/runs/shared/parallel-utils.ts +11 -2
- package/src/runs/shared/permissions.ts +95 -0
- package/src/runs/shared/pi-args.ts +11 -1
- package/src/runs/shared/pi-spawn.ts +11 -1
- package/src/runs/shared/run-history.ts +1 -1
- package/src/runs/shared/subagent-prompt-runtime.ts +36 -5
- package/src/runs/shared/subagent-startup-retry.ts +5 -2
- package/src/runs/shared/turn-budget.ts +6 -6
- package/src/runs/shared/worktree.ts +122 -12
- package/src/shared/accessible-dir.ts +29 -7
- package/src/shared/artifacts.ts +18 -1
- package/src/shared/fork-context.ts +3 -2
- package/src/shared/launch-contract.ts +1 -0
- package/src/shared/settings.ts +10 -0
- package/src/shared/types.ts +156 -14
- package/src/shared/utils.ts +8 -6
- package/src/slash/delegation-adapters.ts +32 -194
- package/src/slash/delegation-request.ts +43 -126
- package/src/slash/prompt-template-bridge.ts +158 -205
- package/src/slash/prompt-workflows.ts +21 -57
- package/src/slash/slash-bridge.ts +14 -0
- package/src/slash/slash-commands.ts +31 -632
- package/src/slash/subagents-admin.ts +18 -14
- package/src/tui/fleet-status.ts +156 -21
- package/src/tui/fleet-transcript.ts +110 -5
- package/src/tui/fleet.ts +56 -24
- package/src/tui/render.ts +291 -109
- package/src/types/pi-runtime-compat.d.ts +14 -0
- package/src/watchdog/lsp-diagnostics.ts +12 -7
- package/src/watchdog/model-selection.ts +2 -2
- package/src/watchdog/permission-arbiter.ts +145 -0
- package/src/watchdog/register-child.ts +1 -1
- package/src/watchdog/register-main.ts +1 -1
- package/src/watchdog/review.ts +4 -1
- package/src/watchdog/runtime.ts +3 -2
- package/src/workflows/chat-progress.ts +140 -0
- package/src/workflows/scripted-workflow.ts +415 -0
- package/agents/advisor.md +0 -73
- 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
|
|
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?:
|
|
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
|
|
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?:
|
|
159
|
-
/** Internal-only
|
|
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:
|
|
206
|
-
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(
|
|
414
|
-
|
|
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
|
-
):
|
|
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 (
|
|
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
|
-
|
|
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:
|
|
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") >
|
|
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,
|
|
537
|
-
if (
|
|
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
|
-
...(
|
|
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
|
|
14
|
-
| { ok: false;
|
|
9
|
+
| { ok: true; request: SubagentDelegationRequest }
|
|
10
|
+
| { ok: false; requestId?: string; ownerRunId?: string; nodeId?: string; error: string };
|
|
15
11
|
|
|
16
|
-
const
|
|
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
|
|
30
|
+
const thinkingLevels = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
56
31
|
const MAX_SCHEMA_BYTES = 64 * 1024;
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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") >
|
|
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") >
|
|
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") >
|
|
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 >
|
|
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") >
|
|
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) >
|
|
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" || !
|
|
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 (
|
|
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
|
|
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
|
-
}
|