pi-subagents 0.35.1 → 0.37.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 +71 -0
- package/README.md +224 -31
- package/agents/advisor.md +73 -0
- package/agents/delegate.md +2 -0
- package/agents/worker.md +2 -0
- package/package.json +11 -13
- package/skills/pi-subagents/SKILL.md +42 -13
- package/src/agents/agent-management.ts +7 -1
- package/src/agents/agents.ts +121 -17
- package/src/api/capability-ceiling.ts +17 -0
- package/src/api/delegation.ts +127 -0
- package/src/api/preflight.ts +399 -0
- package/src/extension/config.ts +7 -1
- package/src/extension/index.ts +52 -38
- package/src/extension/rpc.ts +31 -2
- package/src/extension/schemas.ts +29 -3
- package/src/extension/tool-description.ts +4 -2
- package/src/intercom/intercom-bridge.ts +1 -1
- package/src/intercom/native-supervisor-channel.ts +45 -6
- package/src/intercom/result-intercom.ts +7 -0
- package/src/runs/background/async-execution.ts +158 -17
- package/src/runs/background/async-job-tracker.ts +4 -0
- package/src/runs/background/async-resume.ts +47 -8
- package/src/runs/background/async-status.ts +94 -3
- package/src/runs/background/chain-append.ts +2 -0
- package/src/runs/background/completion-batcher.ts +6 -4
- package/src/runs/background/completion-dedupe.ts +2 -11
- package/src/runs/background/fleet-view.ts +9 -4
- package/src/runs/background/notify.ts +132 -120
- package/src/runs/background/process-terminal.ts +280 -0
- package/src/runs/background/result-watcher.ts +138 -78
- package/src/runs/background/run-status.ts +10 -2
- package/src/runs/background/scheduled-runs.ts +6 -1
- package/src/runs/background/stale-run-reconciler.ts +6 -0
- package/src/runs/background/subagent-runner.ts +406 -54
- package/src/runs/background/subagent-wait.ts +130 -4
- package/src/runs/background/wait-tool.ts +2 -2
- package/src/runs/foreground/chain-execution.ts +181 -111
- package/src/runs/foreground/execution.ts +147 -47
- package/src/runs/foreground/foreground-control.ts +90 -0
- package/src/runs/foreground/subagent-executor.ts +426 -165
- package/src/runs/shared/acceptance.ts +94 -41
- package/src/runs/shared/agent-contract.ts +38 -0
- package/src/runs/shared/capability-ceiling.ts +177 -0
- package/src/runs/shared/child-protocol.ts +1 -1
- package/src/runs/shared/completion-guard.ts +36 -5
- package/src/runs/shared/context-mode.ts +44 -0
- package/src/runs/shared/dynamic-fanout.ts +5 -5
- package/src/runs/shared/long-running-guard.ts +4 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
- package/src/runs/shared/nested-events.ts +35 -3
- package/src/runs/shared/parallel-handoff.ts +154 -0
- package/src/runs/shared/parallel-utils.ts +11 -0
- package/src/runs/shared/pi-args.ts +148 -56
- package/src/runs/shared/run-history.ts +90 -5
- package/src/runs/shared/session-lease.ts +25 -5
- package/src/runs/shared/structured-output.ts +112 -7
- package/src/runs/shared/subagent-control.ts +4 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +31 -19
- package/src/runs/shared/task-intent.ts +10 -5
- package/src/runs/shared/tool-availability.ts +21 -3
- package/src/runs/shared/tool-budget.ts +11 -5
- package/src/runs/shared/turn-budget.ts +2 -1
- package/src/runs/shared/worktree.ts +63 -14
- package/src/shared/accessible-dir.ts +25 -0
- package/src/shared/artifacts.ts +37 -7
- package/src/shared/atomic-json.ts +14 -42
- package/src/shared/child-transcript.ts +52 -0
- package/src/shared/file-system-retry.ts +47 -0
- package/src/shared/launch-contract.ts +123 -0
- package/src/shared/settings.ts +9 -1
- package/src/shared/types.ts +314 -28
- package/src/shared/utils.ts +17 -42
- package/src/slash/delegation-adapters.ts +153 -6
- package/src/slash/delegation-json.ts +108 -0
- package/src/slash/delegation-request.ts +182 -36
- package/src/slash/prompt-template-bridge.ts +222 -37
- package/src/slash/selector.ts +147 -0
- package/src/slash/slash-commands.ts +15 -6
- package/src/slash/slash-live-state.ts +2 -2
- package/src/slash/subagents-admin.ts +42 -42
- package/src/tui/fleet-status.ts +362 -0
- package/src/tui/fleet-transcript.ts +472 -0
- package/src/tui/fleet.ts +318 -59
- package/src/tui/render.ts +17 -15
- package/src/watchdog/change-signature.ts +105 -12
- package/src/watchdog/review.ts +7 -2
- package/src/watchdog/runtime.ts +5 -3
- package/src/slash/subagents-editor.ts +0 -86
|
@@ -3,11 +3,12 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
5
5
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import {
|
|
6
|
+
import type { AgentConfig, AgentScope } from "../../agents/agents.ts";
|
|
7
7
|
import { getArtifactsDir, getProjectChainRunsDir } from "../../shared/artifacts.ts";
|
|
8
8
|
import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
|
|
9
9
|
import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
10
10
|
import { executeChain } from "./chain-execution.ts";
|
|
11
|
+
import { beginForegroundChild, finishForegroundChild, updateForegroundChild } from "./foreground-control.ts";
|
|
11
12
|
import { resolveExecutionAgentScope } from "../../agents/agent-scope.ts";
|
|
12
13
|
import { handleManagementAction } from "../../agents/agent-management.ts";
|
|
13
14
|
import { buildDoctorReport } from "../../extension/doctor.ts";
|
|
@@ -46,9 +47,14 @@ import { formatControlIntercomMessage, formatControlNoticeMessage, resolveContro
|
|
|
46
47
|
import { resolveTurnBudgetConfig } from "../shared/turn-budget.ts";
|
|
47
48
|
import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.ts";
|
|
48
49
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
50
|
+
import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
51
|
+
import { isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
49
52
|
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
53
|
+
import { cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
50
54
|
import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage } from "../../shared/utils.ts";
|
|
51
55
|
import { DEFAULT_GLOBAL_CONCURRENCY_LIMIT, Semaphore } from "../shared/parallel-utils.ts";
|
|
56
|
+
import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup } from "../shared/parallel-handoff.ts";
|
|
57
|
+
import { summarizeContextModes, type ContextMode, type ContextSummary } from "../shared/context-mode.ts";
|
|
52
58
|
import {
|
|
53
59
|
attachNestedChildrenToResultChildren,
|
|
54
60
|
buildSubagentResultIntercomPayload,
|
|
@@ -81,13 +87,16 @@ import {
|
|
|
81
87
|
type AgentProgress,
|
|
82
88
|
type AsyncStatus,
|
|
83
89
|
type AcceptanceInput,
|
|
90
|
+
type AgentContract,
|
|
84
91
|
type ArtifactConfig,
|
|
85
92
|
type ArtifactPaths,
|
|
86
93
|
type ControlConfig,
|
|
87
94
|
type ControlEvent,
|
|
88
95
|
type Details,
|
|
89
96
|
type ExtensionConfig,
|
|
97
|
+
type ForegroundRunControl,
|
|
90
98
|
type IntercomEventBus,
|
|
99
|
+
type JsonSchemaObject,
|
|
91
100
|
type MaxOutputConfig,
|
|
92
101
|
type NestedRouteInfo,
|
|
93
102
|
type NestedRunSummary,
|
|
@@ -127,7 +136,9 @@ interface TaskParam {
|
|
|
127
136
|
progress?: boolean;
|
|
128
137
|
model?: string;
|
|
129
138
|
skill?: string | string[] | boolean;
|
|
139
|
+
outputSchema?: JsonSchemaObject;
|
|
130
140
|
acceptance?: AcceptanceInput;
|
|
141
|
+
agentContract?: AgentContract;
|
|
131
142
|
toolBudget?: ToolBudgetConfig;
|
|
132
143
|
}
|
|
133
144
|
|
|
@@ -142,6 +153,7 @@ export interface SubagentParamsLike {
|
|
|
142
153
|
agent?: string;
|
|
143
154
|
task?: string;
|
|
144
155
|
message?: string;
|
|
156
|
+
steeringRecovery?: boolean;
|
|
145
157
|
chain?: ChainStep[];
|
|
146
158
|
tasks?: TaskParam[];
|
|
147
159
|
concurrency?: number;
|
|
@@ -152,6 +164,8 @@ export interface SubagentParamsLike {
|
|
|
152
164
|
timeoutMs?: number;
|
|
153
165
|
maxRuntimeMs?: number;
|
|
154
166
|
turnBudget?: TurnBudgetConfig;
|
|
167
|
+
/** Internal-only strict turn-boundary enforcement for versioned foreground delegation. */
|
|
168
|
+
enforceHardTurnLimit?: boolean;
|
|
155
169
|
toolBudget?: ToolBudgetConfig;
|
|
156
170
|
clarify?: boolean;
|
|
157
171
|
share?: boolean;
|
|
@@ -168,9 +182,11 @@ export interface SubagentParamsLike {
|
|
|
168
182
|
skill?: string | string[] | boolean;
|
|
169
183
|
output?: string | boolean;
|
|
170
184
|
outputMode?: "inline" | "file-only";
|
|
185
|
+
outputSchema?: JsonSchemaObject;
|
|
171
186
|
agentScope?: unknown;
|
|
172
187
|
chainDir?: string;
|
|
173
188
|
acceptance?: AcceptanceInput;
|
|
189
|
+
agentContract?: AgentContract;
|
|
174
190
|
schedule?: string;
|
|
175
191
|
scheduleName?: string;
|
|
176
192
|
additional?: number;
|
|
@@ -220,10 +236,12 @@ interface ExecutionContextData {
|
|
|
220
236
|
deadlineAt?: number;
|
|
221
237
|
turnBudget?: ResolvedTurnBudget;
|
|
222
238
|
toolBudget?: ResolvedToolBudget;
|
|
239
|
+
allowZeroToolBudget?: boolean;
|
|
223
240
|
configToolBudget?: ResolvedToolBudget;
|
|
224
241
|
contextPolicy: AgentDefaultContextPolicy;
|
|
225
242
|
modelScope?: ModelScopeConfig;
|
|
226
243
|
parentSessionId: string | null;
|
|
244
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
227
245
|
}
|
|
228
246
|
|
|
229
247
|
function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefined): string {
|
|
@@ -351,6 +369,19 @@ function trimRememberedForegroundRuns(state: SubagentState): void {
|
|
|
351
369
|
}
|
|
352
370
|
}
|
|
353
371
|
|
|
372
|
+
function foregroundChildActivityFromProgress(progress: SingleResult["progress"] | undefined) {
|
|
373
|
+
return {
|
|
374
|
+
...(progress?.activityState ? { activityState: progress.activityState } : {}),
|
|
375
|
+
...(progress?.lastActivityAt !== undefined ? { lastActivityAt: progress.lastActivityAt } : {}),
|
|
376
|
+
...(progress?.currentTool ? { currentTool: progress.currentTool } : {}),
|
|
377
|
+
...(progress?.currentToolStartedAt !== undefined ? { currentToolStartedAt: progress.currentToolStartedAt } : {}),
|
|
378
|
+
...(progress?.currentPath ? { currentPath: progress.currentPath } : {}),
|
|
379
|
+
...(progress?.turnCount !== undefined ? { turnCount: progress.turnCount } : {}),
|
|
380
|
+
...(progress?.tokens !== undefined ? { tokens: progress.tokens } : {}),
|
|
381
|
+
...(progress?.toolCount !== undefined ? { toolCount: progress.toolCount } : {}),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
354
385
|
function rememberForegroundRun(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[] }): void {
|
|
355
386
|
state.foregroundRuns ??= new Map();
|
|
356
387
|
const previous = state.foregroundRuns.get(input.runId);
|
|
@@ -365,7 +396,9 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
365
396
|
const child = {
|
|
366
397
|
agent: result.agent,
|
|
367
398
|
index,
|
|
399
|
+
...(result.context ? { context: result.context } : {}),
|
|
368
400
|
status: resolveSubagentResultStatus({ exitCode: result.exitCode, interrupted: result.interrupted, detached: result.detached }),
|
|
401
|
+
...foregroundChildActivityFromProgress(result.progress),
|
|
369
402
|
updatedAt,
|
|
370
403
|
...(result.exitCode !== undefined ? { exitCode: result.exitCode } : {}),
|
|
371
404
|
...(result.error ? { error: result.error } : {}),
|
|
@@ -379,6 +412,8 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
379
412
|
...(result.transcriptError ? { transcriptError: result.transcriptError } : {}),
|
|
380
413
|
...(result.detachedReason ? { detachedReason: result.detachedReason } : {}),
|
|
381
414
|
...(result.acceptance ? { acceptance: result.acceptance } : {}),
|
|
415
|
+
...(result.capabilityCeiling ? { capabilityCeiling: result.capabilityCeiling } : {}),
|
|
416
|
+
...(result.capabilityAudit ? { capabilityAudit: result.capabilityAudit } : {}),
|
|
382
417
|
};
|
|
383
418
|
const recovered = previous?.children[index];
|
|
384
419
|
return child.status === "detached" && recovered && recovered.status !== "detached" ? recovered : child;
|
|
@@ -387,6 +422,29 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
387
422
|
trimRememberedForegroundRuns(state);
|
|
388
423
|
}
|
|
389
424
|
|
|
425
|
+
function applyControlEventToRememberedForegroundRun(state: SubagentState, event: ControlEvent): void {
|
|
426
|
+
const run = state.foregroundRuns?.get(event.runId);
|
|
427
|
+
if (!run) return;
|
|
428
|
+
const index = event.index ?? (run.children.length === 1 ? run.children[0]?.index : undefined);
|
|
429
|
+
if (index === undefined) return;
|
|
430
|
+
const child = run.children[index];
|
|
431
|
+
if (!child || child.status !== "detached") return;
|
|
432
|
+
const updatedAt = event.ts;
|
|
433
|
+
run.updatedAt = updatedAt;
|
|
434
|
+
run.children[index] = {
|
|
435
|
+
...child,
|
|
436
|
+
activityState: event.to,
|
|
437
|
+
updatedAt,
|
|
438
|
+
...(event.elapsedMs !== undefined ? { lastActivityAt: event.ts - event.elapsedMs } : {}),
|
|
439
|
+
...(event.currentTool ? { currentTool: event.currentTool } : {}),
|
|
440
|
+
...(event.currentToolDurationMs !== undefined ? { currentToolStartedAt: event.ts - event.currentToolDurationMs } : {}),
|
|
441
|
+
...(event.currentPath ? { currentPath: event.currentPath } : {}),
|
|
442
|
+
...(event.turns !== undefined ? { turnCount: event.turns } : {}),
|
|
443
|
+
...(event.tokens !== undefined ? { tokens: event.tokens } : {}),
|
|
444
|
+
...(event.toolCount !== undefined ? { toolCount: event.toolCount } : {}),
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
390
448
|
function updateRememberedForegroundChild(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; index: number; result: SingleResult; events: IntercomEventBus }): void {
|
|
391
449
|
state.foregroundRuns ??= new Map();
|
|
392
450
|
const updatedAt = Date.now();
|
|
@@ -401,9 +459,11 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
401
459
|
...child,
|
|
402
460
|
agent: input.result.agent,
|
|
403
461
|
index: input.index,
|
|
462
|
+
...(input.result.context ? { context: input.result.context } : {}),
|
|
404
463
|
status: input.result.acceptance?.status === "rejected"
|
|
405
464
|
? "failed"
|
|
406
465
|
: resolveSubagentResultStatus({ exitCode: input.result.exitCode, interrupted: input.result.interrupted, detached: false }),
|
|
466
|
+
...foregroundChildActivityFromProgress(input.result.progress),
|
|
407
467
|
updatedAt,
|
|
408
468
|
...(input.result.exitCode !== undefined ? { exitCode: input.result.exitCode } : {}),
|
|
409
469
|
...(input.result.error ? { error: input.result.error } : {}),
|
|
@@ -417,6 +477,8 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
417
477
|
...(input.result.transcriptError ? { transcriptError: input.result.transcriptError } : {}),
|
|
418
478
|
...(input.result.detachedReason ? { detachedReason: input.result.detachedReason } : {}),
|
|
419
479
|
...(input.result.acceptance ? { acceptance: input.result.acceptance } : {}),
|
|
480
|
+
...(input.result.capabilityCeiling ? { capabilityCeiling: input.result.capabilityCeiling } : {}),
|
|
481
|
+
...(input.result.capabilityAudit ? { capabilityAudit: input.result.capabilityAudit } : {}),
|
|
420
482
|
};
|
|
421
483
|
trimRememberedForegroundRuns(state);
|
|
422
484
|
const output = getSingleResultOutput(input.result).trim();
|
|
@@ -424,6 +486,9 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
424
486
|
const summary = !success && input.result.error
|
|
425
487
|
? `${input.result.error}${output ? `\n\nOutput:\n${output}` : ""}`
|
|
426
488
|
: output || input.result.error || "Detached child exited without final output.";
|
|
489
|
+
// A detached callback may outlive its extension runtime. Stale sessions are
|
|
490
|
+
// intentionally dropped rather than routed through a replacement runtime.
|
|
491
|
+
if (!input.sessionId || input.sessionId !== state.currentSessionId) return;
|
|
427
492
|
input.events.emit(SUBAGENT_FOREGROUND_COMPLETE_EVENT, {
|
|
428
493
|
id: `${input.runId}:${input.index}`,
|
|
429
494
|
runId: input.runId,
|
|
@@ -442,7 +507,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
442
507
|
});
|
|
443
508
|
}
|
|
444
509
|
|
|
445
|
-
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string } | undefined {
|
|
510
|
+
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
|
|
446
511
|
const requested = (params.id ?? params.runId)?.trim();
|
|
447
512
|
if (!requested || !state.foregroundRuns?.size || !state.currentSessionId) return undefined;
|
|
448
513
|
const sessionRuns = [...state.foregroundRuns.values()].filter((run) => run.sessionId === state.currentSessionId);
|
|
@@ -461,7 +526,16 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
|
|
|
461
526
|
if (path.extname(child.sessionFile) !== ".jsonl") throw new Error(`Foreground run '${run.runId}' child ${index} session file must be a .jsonl file: ${child.sessionFile}`);
|
|
462
527
|
const sessionFile = path.resolve(child.sessionFile);
|
|
463
528
|
if (!fs.existsSync(sessionFile)) throw new Error(`Foreground run '${run.runId}' child ${index} session file does not exist: ${child.sessionFile}`);
|
|
464
|
-
return {
|
|
529
|
+
return {
|
|
530
|
+
runId: run.runId,
|
|
531
|
+
mode: run.mode,
|
|
532
|
+
state: "complete",
|
|
533
|
+
agent: child.agent,
|
|
534
|
+
index,
|
|
535
|
+
cwd: run.cwd,
|
|
536
|
+
sessionFile,
|
|
537
|
+
...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
|
|
538
|
+
};
|
|
465
539
|
}
|
|
466
540
|
|
|
467
541
|
type AsyncResumeSourceTarget = ReturnType<typeof resolveAsyncResumeTarget> & { source: "async" };
|
|
@@ -475,6 +549,7 @@ type NestedResumeSourceTarget = {
|
|
|
475
549
|
index: number;
|
|
476
550
|
cwd?: string;
|
|
477
551
|
sessionFile: string;
|
|
552
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
478
553
|
};
|
|
479
554
|
type ResumeSourceTarget = AsyncResumeSourceTarget | ForegroundResumeSourceTarget | NestedResumeSourceTarget;
|
|
480
555
|
|
|
@@ -824,8 +899,10 @@ function appendStepToAsyncChain(input: {
|
|
|
824
899
|
dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
|
|
825
900
|
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
826
901
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
902
|
+
contextForAgent: contextPolicy.contextForAgent,
|
|
827
903
|
asyncDir: resolved.location.asyncDir,
|
|
828
904
|
validateOutputBindings: false,
|
|
905
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings(status.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(asyncCtx.currentSessionId)),
|
|
829
906
|
});
|
|
830
907
|
if ("error" in built) {
|
|
831
908
|
return {
|
|
@@ -930,6 +1007,7 @@ function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "neste
|
|
|
930
1007
|
index: 0,
|
|
931
1008
|
cwd: asyncDir ? path.dirname(asyncDir) : undefined,
|
|
932
1009
|
sessionFile: validateNestedSessionFile(run, trustedSessionRoots),
|
|
1010
|
+
...(run.capabilityCeiling ? { capabilityCeiling: run.capabilityCeiling } : {}),
|
|
933
1011
|
};
|
|
934
1012
|
}
|
|
935
1013
|
|
|
@@ -1174,7 +1252,7 @@ async function resumeAsyncRun(input: {
|
|
|
1174
1252
|
};
|
|
1175
1253
|
}
|
|
1176
1254
|
const runId = randomUUID().slice(0, 8);
|
|
1177
|
-
const artifactConfig: ArtifactConfig = { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false };
|
|
1255
|
+
const artifactConfig: ArtifactConfig = { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir };
|
|
1178
1256
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1179
1257
|
const contextPolicy = resolveExplicitContextPolicy(input.params);
|
|
1180
1258
|
const workflowTask = (input.params.task ?? followUp) || undefined;
|
|
@@ -1207,11 +1285,12 @@ async function resumeAsyncRun(input: {
|
|
|
1207
1285
|
availableModels,
|
|
1208
1286
|
cwd: effectiveCwd,
|
|
1209
1287
|
maxOutput: input.params.maxOutput,
|
|
1210
|
-
artifactsDir: getArtifactsDir(parentSessionFile, effectiveCwd),
|
|
1288
|
+
artifactsDir: getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir),
|
|
1211
1289
|
artifactConfig,
|
|
1212
1290
|
shareEnabled: input.params.share === true,
|
|
1213
1291
|
sessionRoot: input.deps.getSubagentSessionRoot(parentSessionFile),
|
|
1214
1292
|
chainSkills: normalized === false ? [] : (normalized ?? []),
|
|
1293
|
+
agentContract: input.params.agentContract,
|
|
1215
1294
|
dynamicFanoutMaxItems: input.deps.config.chain?.dynamicFanout?.maxItems,
|
|
1216
1295
|
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1217
1296
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
@@ -1222,6 +1301,7 @@ async function resumeAsyncRun(input: {
|
|
|
1222
1301
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
1223
1302
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
1224
1303
|
globalConcurrencyLimit: input.deps.config.globalConcurrencyLimit,
|
|
1304
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
|
|
1225
1305
|
});
|
|
1226
1306
|
if (result.isError) return result;
|
|
1227
1307
|
const attachedId = result.details.asyncId ?? runId;
|
|
@@ -1237,8 +1317,8 @@ async function resumeAsyncRun(input: {
|
|
|
1237
1317
|
|
|
1238
1318
|
const runId = randomUUID().slice(0, 8);
|
|
1239
1319
|
const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(agentConfig, recoveryDescriptor) : agentConfig;
|
|
1240
|
-
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false };
|
|
1241
|
-
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd);
|
|
1320
|
+
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir };
|
|
1321
|
+
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
1242
1322
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1243
1323
|
const result = executeAsyncSingle(runId, {
|
|
1244
1324
|
agent: target.agent,
|
|
@@ -1283,12 +1363,15 @@ async function resumeAsyncRun(input: {
|
|
|
1283
1363
|
availableModels,
|
|
1284
1364
|
output: recoveryDescriptor?.outputPath,
|
|
1285
1365
|
outputMode: recoveryDescriptor?.outputMode,
|
|
1366
|
+
...(recoveryDescriptor?.agentContract ? { agentContract: recoveryDescriptor.agentContract } : {}),
|
|
1367
|
+
...(recoveryDescriptor?.structuredOutputSchema ? { structuredOutputSchema: recoveryDescriptor.structuredOutputSchema } : {}),
|
|
1286
1368
|
...(recoveryDescriptor?.skills ? { skills: [...recoveryDescriptor.skills] } : {}),
|
|
1287
1369
|
...(recoveryDescriptor?.acceptance !== undefined && input.params.acceptance === undefined ? { acceptance: recoveryDescriptor.acceptance } : {}),
|
|
1288
1370
|
...(input.params.timeoutMs !== undefined ? { timeoutMs: input.params.timeoutMs } : {}),
|
|
1289
1371
|
...(input.absoluteDeadlineAt !== undefined ? { absoluteDeadlineAt: input.absoluteDeadlineAt } : {}),
|
|
1290
1372
|
...(input.params.turnBudget !== undefined ? { turnBudget: input.params.turnBudget } : {}),
|
|
1291
1373
|
...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
|
|
1374
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, recoveryDescriptor?.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
|
|
1292
1375
|
});
|
|
1293
1376
|
if (result.isError) return result;
|
|
1294
1377
|
|
|
@@ -1322,19 +1405,22 @@ function formatFailedSingleRunOutput(result: SingleResult, displayOutput: string
|
|
|
1322
1405
|
if (output && output !== error.trim()) {
|
|
1323
1406
|
lines.push("", "Output:", output);
|
|
1324
1407
|
}
|
|
1325
|
-
if (result.artifactPaths?.outputPath) {
|
|
1408
|
+
if (result.artifactPaths?.outputPath && fs.existsSync(result.artifactPaths.outputPath)) {
|
|
1326
1409
|
lines.push("", `Output artifact: ${result.artifactPaths.outputPath}`);
|
|
1327
1410
|
}
|
|
1328
1411
|
return lines.join("\n");
|
|
1329
1412
|
}
|
|
1330
1413
|
|
|
1331
|
-
function createForegroundControlNotifier(data: Pick<ExecutionContextData, "controlConfig" | "intercomBridge">, deps: Pick<ExecutorDeps, "pi">): (event: ControlEvent) => void {
|
|
1332
|
-
return (event) =>
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1414
|
+
function createForegroundControlNotifier(data: Pick<ExecutionContextData, "controlConfig" | "intercomBridge">, deps: Pick<ExecutorDeps, "pi" | "state">): (event: ControlEvent) => void {
|
|
1415
|
+
return (event) => {
|
|
1416
|
+
applyControlEventToRememberedForegroundRun(deps.state, event);
|
|
1417
|
+
emitControlNotification({
|
|
1418
|
+
pi: deps.pi,
|
|
1419
|
+
controlConfig: data.controlConfig,
|
|
1420
|
+
intercomBridge: data.intercomBridge,
|
|
1421
|
+
event,
|
|
1422
|
+
});
|
|
1423
|
+
};
|
|
1338
1424
|
}
|
|
1339
1425
|
|
|
1340
1426
|
async function emitForegroundResultIntercom(input: {
|
|
@@ -1345,6 +1431,7 @@ async function emitForegroundResultIntercom(input: {
|
|
|
1345
1431
|
results: SingleResult[];
|
|
1346
1432
|
chainSteps?: number;
|
|
1347
1433
|
nestedChildren?: NestedRunSummary[];
|
|
1434
|
+
parallelHandoff?: Details["parallelHandoff"];
|
|
1348
1435
|
}): Promise<ReturnType<typeof buildSubagentResultIntercomPayload> | null> {
|
|
1349
1436
|
if (!input.intercomBridge.active || !input.intercomBridge.orchestratorTarget) return null;
|
|
1350
1437
|
const children = input.results.flatMap((result, index) => result.detached ? [] : [{
|
|
@@ -1368,6 +1455,7 @@ async function emitForegroundResultIntercom(input: {
|
|
|
1368
1455
|
source: "foreground",
|
|
1369
1456
|
children: attachNestedChildrenToResultChildren(input.runId, children, input.nestedChildren),
|
|
1370
1457
|
...(typeof input.chainSteps === "number" ? { chainSteps: input.chainSteps } : {}),
|
|
1458
|
+
...(input.parallelHandoff ? { parallelHandoff: input.parallelHandoff } : {}),
|
|
1371
1459
|
});
|
|
1372
1460
|
const delivered = await deliverSubagentResultIntercomEvent(input.pi.events, payload);
|
|
1373
1461
|
if (!delivered) return null;
|
|
@@ -1390,6 +1478,7 @@ async function maybeBuildForegroundIntercomReceipt(input: {
|
|
|
1390
1478
|
results: input.details.results,
|
|
1391
1479
|
...(typeof input.details.totalSteps === "number" ? { chainSteps: input.details.totalSteps } : {}),
|
|
1392
1480
|
...(input.nestedChildren?.length ? { nestedChildren: input.nestedChildren } : {}),
|
|
1481
|
+
...(input.details.parallelHandoff ? { parallelHandoff: input.details.parallelHandoff } : {}),
|
|
1393
1482
|
});
|
|
1394
1483
|
if (!payload) return null;
|
|
1395
1484
|
return {
|
|
@@ -1531,7 +1620,8 @@ function getRequestedModeLabel(params: SubagentParamsLike): Details["mode"] {
|
|
|
1531
1620
|
|
|
1532
1621
|
interface AgentDefaultContextPolicy {
|
|
1533
1622
|
params: SubagentParamsLike;
|
|
1534
|
-
contextForAgent(agentName: string):
|
|
1623
|
+
contextForAgent(agentName: string): ContextMode;
|
|
1624
|
+
contextSummary?: ContextSummary;
|
|
1535
1625
|
usesFork: boolean;
|
|
1536
1626
|
}
|
|
1537
1627
|
|
|
@@ -1540,12 +1630,15 @@ function resolveAgentDefaultContextPolicy(params: SubagentParamsLike, agents: Ag
|
|
|
1540
1630
|
return resolveExplicitContextPolicy(params);
|
|
1541
1631
|
}
|
|
1542
1632
|
const byName = new Map(agents.map((agent) => [agent.name, agent]));
|
|
1543
|
-
const contextForAgent = (agentName: string):
|
|
1633
|
+
const contextForAgent = (agentName: string): ContextMode =>
|
|
1544
1634
|
byName.get(agentName)?.defaultContext === "fork" ? "fork" : "fresh";
|
|
1545
|
-
const
|
|
1635
|
+
const requestedAgentNames = collectRequestedAgentNames(params);
|
|
1636
|
+
const contextSummary = summarizeContextModes(requestedAgentNames.map((name) => contextForAgent(name)));
|
|
1637
|
+
const usesFork = contextSummary === "fork" || contextSummary === "mixed";
|
|
1546
1638
|
return {
|
|
1547
|
-
params
|
|
1639
|
+
params,
|
|
1548
1640
|
contextForAgent,
|
|
1641
|
+
contextSummary,
|
|
1549
1642
|
usesFork,
|
|
1550
1643
|
};
|
|
1551
1644
|
}
|
|
@@ -1555,6 +1648,7 @@ function resolveExplicitContextPolicy(params: SubagentParamsLike): AgentDefaultC
|
|
|
1555
1648
|
return {
|
|
1556
1649
|
params,
|
|
1557
1650
|
contextForAgent: () => context,
|
|
1651
|
+
contextSummary: context,
|
|
1558
1652
|
usesFork: context === "fork",
|
|
1559
1653
|
};
|
|
1560
1654
|
}
|
|
@@ -1571,8 +1665,12 @@ function shouldForkAgent(contextPolicy: AgentDefaultContextPolicy, agentName: st
|
|
|
1571
1665
|
return contextPolicy.contextForAgent(agentName) === "fork";
|
|
1572
1666
|
}
|
|
1573
1667
|
|
|
1668
|
+
function summarizeResultContext(details: Details, fallback: ContextSummary | undefined): ContextSummary | undefined {
|
|
1669
|
+
return summarizeContextModes(details.results.map((result) => result.context)) ?? fallback;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1574
1672
|
function buildRequestedModeError(params: SubagentParamsLike, message: string): AgentToolResult<Details> {
|
|
1575
|
-
return
|
|
1673
|
+
return withResolvedContext(
|
|
1576
1674
|
{
|
|
1577
1675
|
content: [{ type: "text", text: message }],
|
|
1578
1676
|
isError: true,
|
|
@@ -1617,8 +1715,12 @@ function resolveForegroundTimeout(params: SubagentParamsLike): { timeoutMs?: num
|
|
|
1617
1715
|
return { timeoutMs: rawTimeout ?? rawMaxRuntime };
|
|
1618
1716
|
}
|
|
1619
1717
|
|
|
1620
|
-
function resolveToolBudget(
|
|
1621
|
-
|
|
1718
|
+
function resolveToolBudget(
|
|
1719
|
+
raw: unknown,
|
|
1720
|
+
label = "toolBudget",
|
|
1721
|
+
options?: { minimumHard?: 0 | 1 },
|
|
1722
|
+
): { toolBudget?: ResolvedToolBudget; error?: string } {
|
|
1723
|
+
const resolved = validateToolBudgetConfig(raw, label, options);
|
|
1622
1724
|
return { toolBudget: resolved.budget, error: resolved.error };
|
|
1623
1725
|
}
|
|
1624
1726
|
|
|
@@ -1688,16 +1790,18 @@ function normalizeRepeatedParallelCounts(params: SubagentParamsLike): { params?:
|
|
|
1688
1790
|
return { params };
|
|
1689
1791
|
}
|
|
1690
1792
|
|
|
1691
|
-
function
|
|
1793
|
+
function withResolvedContext(
|
|
1692
1794
|
result: AgentToolResult<Details>,
|
|
1693
|
-
|
|
1795
|
+
fallback: ContextSummary | undefined,
|
|
1694
1796
|
): AgentToolResult<Details> {
|
|
1695
|
-
if (
|
|
1797
|
+
if (!result.details) return result;
|
|
1798
|
+
const context = summarizeResultContext(result.details, fallback);
|
|
1799
|
+
if (!context) return result;
|
|
1696
1800
|
return {
|
|
1697
1801
|
...result,
|
|
1698
1802
|
details: {
|
|
1699
1803
|
...result.details,
|
|
1700
|
-
context
|
|
1804
|
+
context,
|
|
1701
1805
|
},
|
|
1702
1806
|
};
|
|
1703
1807
|
}
|
|
@@ -1715,15 +1819,15 @@ function withForkThinkingNotes(
|
|
|
1715
1819
|
return { ...result, content: [...result.content, { type: "text", text: note }] };
|
|
1716
1820
|
}
|
|
1717
1821
|
|
|
1718
|
-
function toExecutionErrorResult(params: SubagentParamsLike, error: unknown): AgentToolResult<Details> {
|
|
1822
|
+
function toExecutionErrorResult(params: SubagentParamsLike, error: unknown, contextSummary?: ContextSummary): AgentToolResult<Details> {
|
|
1719
1823
|
const message = error instanceof Error ? error.message : String(error);
|
|
1720
|
-
return
|
|
1824
|
+
return withResolvedContext(
|
|
1721
1825
|
{
|
|
1722
1826
|
content: [{ type: "text", text: message }],
|
|
1723
1827
|
isError: true,
|
|
1724
1828
|
details: { mode: getRequestedModeLabel(params), results: [] },
|
|
1725
1829
|
},
|
|
1726
|
-
|
|
1830
|
+
contextSummary,
|
|
1727
1831
|
);
|
|
1728
1832
|
}
|
|
1729
1833
|
|
|
@@ -1969,6 +2073,8 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1969
2073
|
...(task.reads !== undefined && task.reads !== true ? { reads: task.reads } : {}),
|
|
1970
2074
|
...(task.progress !== undefined ? { progress: task.progress } : {}),
|
|
1971
2075
|
...(task.toolBudget !== undefined ? { toolBudget: task.toolBudget } : {}),
|
|
2076
|
+
...(task.outputSchema !== undefined ? { outputSchema: task.outputSchema } : {}),
|
|
2077
|
+
...(task.agentContract !== undefined ? { agentContract: task.agentContract } : {}),
|
|
1972
2078
|
...(task.acceptance !== undefined ? { acceptance: task.acceptance } : {}),
|
|
1973
2079
|
}));
|
|
1974
2080
|
return executeAsyncChain(id, {
|
|
@@ -1991,12 +2097,14 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1991
2097
|
chainSkills: [],
|
|
1992
2098
|
sessionFilesByFlatIndex: params.tasks.map((task, index) => sessionFileForTask(task.agent, index, task.model)),
|
|
1993
2099
|
thinkingOverridesByFlatIndex: params.tasks.map((task, index) => thinkingOverrideForTask(task.agent, index, task.model)),
|
|
2100
|
+
contextForAgent: contextPolicy.contextForAgent,
|
|
1994
2101
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
1995
2102
|
waitToolEnabled: deps.waitToolEnabled,
|
|
1996
2103
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
1997
2104
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
1998
2105
|
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
1999
2106
|
controlConfig,
|
|
2107
|
+
agentContract: params.agentContract,
|
|
2000
2108
|
controlIntercomTarget,
|
|
2001
2109
|
childIntercomTarget,
|
|
2002
2110
|
nestedRoute,
|
|
@@ -2004,6 +2112,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2004
2112
|
turnBudget: data.turnBudget,
|
|
2005
2113
|
toolBudget: data.toolBudget,
|
|
2006
2114
|
configToolBudget: data.configToolBudget,
|
|
2115
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2007
2116
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2008
2117
|
});
|
|
2009
2118
|
}
|
|
@@ -2029,6 +2138,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2029
2138
|
chainSkills,
|
|
2030
2139
|
sessionFilesByFlatIndex: collectChainSessionFiles(chain, sessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
2031
2140
|
thinkingOverridesByFlatIndex: collectChainThinkingOverrides(chain, thinkingOverrideForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
2141
|
+
contextForAgent: contextPolicy.contextForAgent,
|
|
2032
2142
|
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
2033
2143
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
2034
2144
|
waitToolEnabled: deps.waitToolEnabled,
|
|
@@ -2036,6 +2146,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2036
2146
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
2037
2147
|
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
2038
2148
|
controlConfig,
|
|
2149
|
+
agentContract: params.agentContract,
|
|
2039
2150
|
controlIntercomTarget,
|
|
2040
2151
|
childIntercomTarget,
|
|
2041
2152
|
nestedRoute,
|
|
@@ -2043,6 +2154,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2043
2154
|
turnBudget: data.turnBudget,
|
|
2044
2155
|
toolBudget: data.toolBudget,
|
|
2045
2156
|
configToolBudget: data.configToolBudget,
|
|
2157
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2046
2158
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2047
2159
|
});
|
|
2048
2160
|
}
|
|
@@ -2077,6 +2189,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2077
2189
|
shareEnabled,
|
|
2078
2190
|
sessionRoot,
|
|
2079
2191
|
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride),
|
|
2192
|
+
context: contextPolicy.contextForAgent(params.agent!),
|
|
2080
2193
|
skills,
|
|
2081
2194
|
output: effectiveOutput,
|
|
2082
2195
|
outputMode: effectiveOutputMode,
|
|
@@ -2092,11 +2205,14 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2092
2205
|
controlIntercomTarget,
|
|
2093
2206
|
childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(agent, index) : undefined,
|
|
2094
2207
|
nestedRoute,
|
|
2208
|
+
agentContract: params.agentContract,
|
|
2209
|
+
structuredOutputSchema: params.outputSchema,
|
|
2095
2210
|
acceptance: params.acceptance,
|
|
2096
2211
|
timeoutMs: data.timeoutMs,
|
|
2097
2212
|
turnBudget: data.turnBudget,
|
|
2098
2213
|
toolBudget: data.toolBudget,
|
|
2099
2214
|
configToolBudget: data.configToolBudget,
|
|
2215
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2100
2216
|
});
|
|
2101
2217
|
}
|
|
2102
2218
|
|
|
@@ -2145,6 +2261,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2145
2261
|
sessionFileForIndex,
|
|
2146
2262
|
sessionFileForTask,
|
|
2147
2263
|
thinkingOverrideForTask,
|
|
2264
|
+
contextForAgent: contextPolicy.contextForAgent,
|
|
2148
2265
|
artifactsDir,
|
|
2149
2266
|
artifactConfig,
|
|
2150
2267
|
includeProgress: params.includeProgress,
|
|
@@ -2152,6 +2269,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2152
2269
|
onUpdate,
|
|
2153
2270
|
onControlEvent,
|
|
2154
2271
|
controlConfig,
|
|
2272
|
+
agentContract: params.agentContract,
|
|
2155
2273
|
childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(runId, agent, index) : undefined,
|
|
2156
2274
|
orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
2157
2275
|
foregroundControl,
|
|
@@ -2170,6 +2288,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2170
2288
|
toolBudget: data.toolBudget,
|
|
2171
2289
|
configToolBudget: data.configToolBudget,
|
|
2172
2290
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2291
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2173
2292
|
});
|
|
2174
2293
|
|
|
2175
2294
|
if (chainResult.requestedAsync) {
|
|
@@ -2210,6 +2329,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2210
2329
|
chainSkills: chainResult.requestedAsync.chainSkills,
|
|
2211
2330
|
sessionFilesByFlatIndex: collectChainSessionFiles(asyncChain, sessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
2212
2331
|
thinkingOverridesByFlatIndex: collectChainThinkingOverrides(asyncChain, thinkingOverrideForTask, deps.config.chain?.dynamicFanout?.maxItems),
|
|
2332
|
+
contextForAgent: contextPolicy.contextForAgent,
|
|
2213
2333
|
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
2214
2334
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
2215
2335
|
waitToolEnabled: deps.waitToolEnabled,
|
|
@@ -2217,6 +2337,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2217
2337
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
2218
2338
|
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
2219
2339
|
controlConfig,
|
|
2340
|
+
agentContract: params.agentContract,
|
|
2220
2341
|
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
2221
2342
|
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
2222
2343
|
nestedRoute: data.nestedRoute,
|
|
@@ -2224,6 +2345,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2224
2345
|
turnBudget: data.turnBudget,
|
|
2225
2346
|
toolBudget: data.toolBudget,
|
|
2226
2347
|
configToolBudget: data.configToolBudget,
|
|
2348
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2227
2349
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2228
2350
|
});
|
|
2229
2351
|
}
|
|
@@ -2260,11 +2382,13 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2260
2382
|
interface ForegroundParallelRunInput {
|
|
2261
2383
|
tasks: TaskParam[];
|
|
2262
2384
|
taskTexts: string[];
|
|
2385
|
+
taskDescriptions: string[];
|
|
2263
2386
|
agents: AgentConfig[];
|
|
2264
2387
|
ctx: ExtensionContext;
|
|
2265
2388
|
state: SubagentState;
|
|
2266
2389
|
intercomEvents: IntercomEventBus;
|
|
2267
2390
|
parentSessionId: string | null;
|
|
2391
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
2268
2392
|
signal: AbortSignal;
|
|
2269
2393
|
runId: string;
|
|
2270
2394
|
sessionDirForIndex: (idx?: number) => string | undefined;
|
|
@@ -2286,6 +2410,7 @@ interface ForegroundParallelRunInput {
|
|
|
2286
2410
|
behaviors: Array<ReturnType<typeof resolveStepBehavior>>;
|
|
2287
2411
|
firstProgressIndex: number;
|
|
2288
2412
|
controlConfig: ResolvedControlConfig;
|
|
2413
|
+
contextPolicy: AgentDefaultContextPolicy;
|
|
2289
2414
|
onControlEvent?: (event: ControlEvent) => void;
|
|
2290
2415
|
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
2291
2416
|
orchestratorIntercomTarget?: string;
|
|
@@ -2300,8 +2425,8 @@ interface ForegroundParallelRunInput {
|
|
|
2300
2425
|
deadlineAt?: number;
|
|
2301
2426
|
turnBudget?: ResolvedTurnBudget;
|
|
2302
2427
|
toolBudgets: (ResolvedToolBudget | undefined)[];
|
|
2428
|
+
agentContract?: AgentContract;
|
|
2303
2429
|
}
|
|
2304
|
-
|
|
2305
2430
|
function buildParallelModeError(message: string): AgentToolResult<Details> {
|
|
2306
2431
|
return {
|
|
2307
2432
|
content: [{ type: "text", text: message }],
|
|
@@ -2374,15 +2499,52 @@ function resolveParallelTaskCwd(
|
|
|
2374
2499
|
return resolveChildCwd(paramsCwd, task.cwd);
|
|
2375
2500
|
}
|
|
2376
2501
|
|
|
2377
|
-
function
|
|
2378
|
-
worktreeSetup: WorktreeSetup
|
|
2379
|
-
artifactsDir: string
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2502
|
+
function finalizeParallelWorktreeHandoff(input: {
|
|
2503
|
+
worktreeSetup: WorktreeSetup;
|
|
2504
|
+
artifactsDir: string;
|
|
2505
|
+
runId: string;
|
|
2506
|
+
cwd: string;
|
|
2507
|
+
tasks: TaskParam[];
|
|
2508
|
+
results: SingleResult[];
|
|
2509
|
+
}): { suffix: string; reference?: NonNullable<Details["parallelHandoff"]> } {
|
|
2510
|
+
const diffsDir = path.join(input.artifactsDir, "worktree-diffs", input.runId);
|
|
2511
|
+
const diffs = diffWorktrees(input.worktreeSetup, input.tasks.map((task) => task.agent), diffsDir);
|
|
2512
|
+
const cleanup = cleanupWorktrees(input.worktreeSetup);
|
|
2513
|
+
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
2514
|
+
try {
|
|
2515
|
+
const reference = writeParallelHandoffGroup({
|
|
2516
|
+
manifestPath: parallelHandoffPath(input.artifactsDir, input.runId),
|
|
2517
|
+
runId: input.runId,
|
|
2518
|
+
mode: "parallel",
|
|
2519
|
+
source: "foreground",
|
|
2520
|
+
cwd: input.cwd,
|
|
2521
|
+
stepIndex: 0,
|
|
2522
|
+
flatStartIndex: 0,
|
|
2523
|
+
setup: input.worktreeSetup,
|
|
2524
|
+
diffs,
|
|
2525
|
+
cleanup,
|
|
2526
|
+
results: input.results.map((result) => ({
|
|
2527
|
+
agent: result.agent,
|
|
2528
|
+
status: resolveSubagentResultStatus({
|
|
2529
|
+
exitCode: result.exitCode,
|
|
2530
|
+
interrupted: result.interrupted,
|
|
2531
|
+
detached: result.detached,
|
|
2532
|
+
state: result.stopped ? "stopped" : undefined,
|
|
2533
|
+
}),
|
|
2534
|
+
summary: resultSummaryForIntercom(result),
|
|
2535
|
+
...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
|
|
2536
|
+
...(result.structuredOutput !== undefined ? { structuredOutput: result.structuredOutput } : {}),
|
|
2537
|
+
...(result.structuredOutputPath ? { structuredOutputPath: result.structuredOutputPath } : {}),
|
|
2538
|
+
...(result.sessionFile ? { sessionPath: result.sessionFile } : {}),
|
|
2539
|
+
})),
|
|
2540
|
+
});
|
|
2541
|
+
return {
|
|
2542
|
+
suffix: [diffSummary, formatParallelHandoffReference(reference)].filter(Boolean).join("\n\n"),
|
|
2543
|
+
reference,
|
|
2544
|
+
};
|
|
2545
|
+
} catch (error) {
|
|
2546
|
+
return { suffix: [diffSummary, formatParallelHandoffError(error)].filter(Boolean).join("\n\n") };
|
|
2547
|
+
}
|
|
2386
2548
|
}
|
|
2387
2549
|
|
|
2388
2550
|
function findDuplicateParallelOutputPath(input: {
|
|
@@ -2435,20 +2597,23 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2435
2597
|
);
|
|
2436
2598
|
const interruptController = new AbortController();
|
|
2437
2599
|
if (input.foregroundControl) {
|
|
2438
|
-
input.foregroundControl
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
};
|
|
2600
|
+
beginForegroundChild(input.foregroundControl, {
|
|
2601
|
+
index,
|
|
2602
|
+
agent: task.agent,
|
|
2603
|
+
description: input.taskDescriptions[index],
|
|
2604
|
+
interrupt: () => {
|
|
2605
|
+
if (interruptController.signal.aborted) return false;
|
|
2606
|
+
interruptController.abort();
|
|
2607
|
+
return true;
|
|
2608
|
+
},
|
|
2609
|
+
});
|
|
2449
2610
|
}
|
|
2611
|
+
const structuredRuntime = task.outputSchema
|
|
2612
|
+
? createStructuredOutputRuntime(task.outputSchema, path.join(input.artifactsDir, "structured-output", input.runId))
|
|
2613
|
+
: undefined;
|
|
2450
2614
|
return runSync(input.ctx.cwd, input.agents, task.agent, taskText, {
|
|
2451
2615
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
2616
|
+
context: input.contextPolicy.contextForAgent(task.agent),
|
|
2452
2617
|
cwd: taskCwd,
|
|
2453
2618
|
signal: input.signal,
|
|
2454
2619
|
interruptSignal: interruptController.signal,
|
|
@@ -2466,6 +2631,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2466
2631
|
outputMode: behavior?.outputMode,
|
|
2467
2632
|
maxSubagentDepth: input.maxSubagentDepths[index],
|
|
2468
2633
|
waitToolEnabled: input.waitToolEnabled,
|
|
2634
|
+
capabilityCeiling: input.capabilityCeiling,
|
|
2469
2635
|
controlConfig: input.controlConfig,
|
|
2470
2636
|
onControlEvent: input.onControlEvent,
|
|
2471
2637
|
onDetachedExit: (result) => updateRememberedForegroundChild(input.state, { runId: input.runId, mode: "parallel", cwd: taskCwd, sessionId: input.parentSessionId, index, result, events: input.intercomEvents }),
|
|
@@ -2478,6 +2644,8 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2478
2644
|
preferredModelProvider: input.ctx.model?.provider,
|
|
2479
2645
|
modelScope: input.modelScope,
|
|
2480
2646
|
skills: effectiveSkills === false ? [] : effectiveSkills,
|
|
2647
|
+
structuredOutput: structuredRuntime,
|
|
2648
|
+
agentContract: task.agentContract ?? input.agentContract,
|
|
2481
2649
|
acceptance: task.acceptance,
|
|
2482
2650
|
acceptanceContext: { mode: "parallel" },
|
|
2483
2651
|
timeoutMs: input.timeoutMs,
|
|
@@ -2489,18 +2657,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2489
2657
|
const stepResults = progressUpdate.details?.results || [];
|
|
2490
2658
|
const stepProgress = progressUpdate.details?.progress || [];
|
|
2491
2659
|
if (input.foregroundControl && stepProgress.length > 0) {
|
|
2492
|
-
|
|
2493
|
-
input.foregroundControl.currentAgent = task.agent;
|
|
2494
|
-
input.foregroundControl.currentIndex = index;
|
|
2495
|
-
input.foregroundControl.currentActivityState = current?.activityState;
|
|
2496
|
-
input.foregroundControl.lastActivityAt = current?.lastActivityAt;
|
|
2497
|
-
input.foregroundControl.currentTool = current?.currentTool;
|
|
2498
|
-
input.foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
|
|
2499
|
-
input.foregroundControl.currentPath = current?.currentPath;
|
|
2500
|
-
input.foregroundControl.turnCount = current?.turnCount;
|
|
2501
|
-
input.foregroundControl.tokens = current?.tokens;
|
|
2502
|
-
input.foregroundControl.toolCount = current?.toolCount;
|
|
2503
|
-
input.foregroundControl.updatedAt = Date.now();
|
|
2660
|
+
updateForegroundChild(input.foregroundControl, index, stepProgress[0]);
|
|
2504
2661
|
}
|
|
2505
2662
|
if (stepResults.length > 0) input.liveResults[index] = stepResults[0];
|
|
2506
2663
|
if (stepProgress.length > 0) input.liveProgress[index] = stepProgress[0];
|
|
@@ -2519,10 +2676,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2519
2676
|
}
|
|
2520
2677
|
: undefined,
|
|
2521
2678
|
}).finally(() => {
|
|
2522
|
-
if (input.foregroundControl
|
|
2523
|
-
input.foregroundControl.interrupt = undefined;
|
|
2524
|
-
input.foregroundControl.updatedAt = Date.now();
|
|
2525
|
-
}
|
|
2679
|
+
if (input.foregroundControl) finishForegroundChild(input.foregroundControl, index);
|
|
2526
2680
|
});
|
|
2527
2681
|
}, input.globalSemaphore);
|
|
2528
2682
|
}
|
|
@@ -2687,7 +2841,9 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2687
2841
|
...(behaviorOverrides[i]?.reads !== undefined ? { reads: behaviorOverrides[i]!.reads } : {}),
|
|
2688
2842
|
...(progress !== undefined ? { progress } : {}),
|
|
2689
2843
|
...(t.toolBudget !== undefined ? { toolBudget: t.toolBudget } : {}),
|
|
2844
|
+
...(t.outputSchema !== undefined ? { outputSchema: t.outputSchema } : {}),
|
|
2690
2845
|
...(t.acceptance !== undefined ? { acceptance: t.acceptance } : {}),
|
|
2846
|
+
...(t.agentContract !== undefined ? { agentContract: t.agentContract } : {}),
|
|
2691
2847
|
};
|
|
2692
2848
|
});
|
|
2693
2849
|
return executeAsyncChain(id, {
|
|
@@ -2706,22 +2862,33 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2706
2862
|
chainSkills: [],
|
|
2707
2863
|
sessionFilesByFlatIndex: tasks.map((task, index) => sessionFileForTask(task.agent, index, modelOverrides[index])),
|
|
2708
2864
|
thinkingOverridesByFlatIndex: tasks.map((task, index) => thinkingOverrideForTask(task.agent, index, modelOverrides[index])),
|
|
2865
|
+
contextForAgent: contextPolicy.contextForAgent,
|
|
2709
2866
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
2710
2867
|
waitToolEnabled: deps.waitToolEnabled,
|
|
2711
2868
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
2712
2869
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
2713
2870
|
worktreeBaseDir: deps.config.worktreeBaseDir,
|
|
2714
2871
|
controlConfig,
|
|
2872
|
+
agentContract: params.agentContract,
|
|
2715
2873
|
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
2716
2874
|
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
2875
|
+
nestedRoute: data.nestedRoute,
|
|
2717
2876
|
timeoutMs: data.timeoutMs,
|
|
2718
2877
|
turnBudget: data.turnBudget,
|
|
2878
|
+
toolBudget: data.toolBudget,
|
|
2879
|
+
configToolBudget: data.configToolBudget,
|
|
2719
2880
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2720
2881
|
});
|
|
2721
2882
|
}
|
|
2722
2883
|
}
|
|
2723
2884
|
|
|
2724
|
-
const behaviors =
|
|
2885
|
+
const behaviors = tasks.map((task, index) => {
|
|
2886
|
+
let behavior = suppressProgressForReadOnlyTask(resolveStepBehavior(agentConfigs[index]!, behaviorOverrides[index]!), taskTexts[index]);
|
|
2887
|
+
if (behaviorOverrides[index]?.output === undefined && typeof behavior.output === "string" && !path.isAbsolute(behavior.output)) {
|
|
2888
|
+
behavior = { ...behavior, output: path.join("parallel-0", `${index}-${task.agent}`, behavior.output) };
|
|
2889
|
+
}
|
|
2890
|
+
return behavior;
|
|
2891
|
+
});
|
|
2725
2892
|
const firstProgressIndex = behaviors.findIndex((behavior) => behavior.progress);
|
|
2726
2893
|
const liveResults: (SingleResult | undefined)[] = new Array(tasks.length).fill(undefined);
|
|
2727
2894
|
const liveProgress: (AgentProgress | undefined)[] = new Array(tasks.length).fill(undefined);
|
|
@@ -2737,6 +2904,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2737
2904
|
);
|
|
2738
2905
|
if (errorResult) return errorResult;
|
|
2739
2906
|
|
|
2907
|
+
let worktreeFinalized = false;
|
|
2740
2908
|
try {
|
|
2741
2909
|
const outputBaseDir = path.join(artifactsDir, "outputs", runId);
|
|
2742
2910
|
const duplicateOutputError = findDuplicateParallelOutputPath({
|
|
@@ -2759,6 +2927,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2759
2927
|
const parallelProgressDir = path.join(artifactsDir, "progress", runId);
|
|
2760
2928
|
if (parallelProgressPrecreated) writeInitialProgressFile(parallelProgressDir);
|
|
2761
2929
|
|
|
2930
|
+
const taskDescriptions = taskTexts.map((taskText) => taskText.trim());
|
|
2762
2931
|
for (let i = 0; i < taskTexts.length; i++) {
|
|
2763
2932
|
if (shouldForkAgent(contextPolicy, tasks[i]!.agent)) taskTexts[i] = wrapForkTask(taskTexts[i]!);
|
|
2764
2933
|
}
|
|
@@ -2767,11 +2936,13 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2767
2936
|
const results = await runForegroundParallelTasks({
|
|
2768
2937
|
tasks,
|
|
2769
2938
|
taskTexts,
|
|
2939
|
+
taskDescriptions,
|
|
2770
2940
|
agents,
|
|
2771
2941
|
ctx,
|
|
2772
2942
|
state: deps.state,
|
|
2773
2943
|
intercomEvents: deps.pi.events,
|
|
2774
2944
|
parentSessionId: data.parentSessionId,
|
|
2945
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2775
2946
|
signal,
|
|
2776
2947
|
runId,
|
|
2777
2948
|
sessionDirForIndex,
|
|
@@ -2791,6 +2962,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2791
2962
|
behaviors,
|
|
2792
2963
|
firstProgressIndex: parallelProgressPrecreated ? -1 : firstProgressIndex,
|
|
2793
2964
|
controlConfig,
|
|
2965
|
+
contextPolicy,
|
|
2794
2966
|
onControlEvent,
|
|
2795
2967
|
childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(runId, agent, index) : undefined,
|
|
2796
2968
|
orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
@@ -2807,6 +2979,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2807
2979
|
deadlineAt,
|
|
2808
2980
|
turnBudget: data.turnBudget,
|
|
2809
2981
|
toolBudgets,
|
|
2982
|
+
agentContract: params.agentContract,
|
|
2810
2983
|
});
|
|
2811
2984
|
for (let i = 0; i < results.length; i++) {
|
|
2812
2985
|
const run = results[i]!;
|
|
@@ -2822,6 +2995,11 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2822
2995
|
updateForegroundNestedProjection(foregroundControl);
|
|
2823
2996
|
attachRootChildrenToSteps(runId, results, foregroundControl.nestedChildren);
|
|
2824
2997
|
}
|
|
2998
|
+
let handoff: ReturnType<typeof finalizeParallelWorktreeHandoff> | undefined;
|
|
2999
|
+
if (worktreeSetup) {
|
|
3000
|
+
worktreeFinalized = true;
|
|
3001
|
+
handoff = finalizeParallelWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: effectiveCwd, tasks, results });
|
|
3002
|
+
}
|
|
2825
3003
|
const interrupted = results.find((result) => result.interrupted);
|
|
2826
3004
|
const details = compactForegroundDetails({
|
|
2827
3005
|
mode: "parallel",
|
|
@@ -2831,6 +3009,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2831
3009
|
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
2832
3010
|
totalChildUsage: sumResultsUsage(results),
|
|
2833
3011
|
totalCost: sumResultsCost(results),
|
|
3012
|
+
...(handoff?.reference ? { parallelHandoff: handoff.reference } : {}),
|
|
2834
3013
|
});
|
|
2835
3014
|
rememberForegroundRun(deps.state, { runId, mode: "parallel", cwd: effectiveCwd, sessionId: data.parentSessionId, results: details.results });
|
|
2836
3015
|
if (interrupted) {
|
|
@@ -2864,7 +3043,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2864
3043
|
};
|
|
2865
3044
|
}
|
|
2866
3045
|
|
|
2867
|
-
const worktreeSuffix =
|
|
3046
|
+
const worktreeSuffix = handoff?.suffix ?? "";
|
|
2868
3047
|
const ok = results.filter((result) => result.exitCode === 0).length;
|
|
2869
3048
|
const downgradeNote = backgroundRequestedWhileClarifying ? " (background requested, but clarify kept this run foreground)" : "";
|
|
2870
3049
|
const aggregatedOutput = aggregateParallelOutputs(
|
|
@@ -2888,7 +3067,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2888
3067
|
details,
|
|
2889
3068
|
};
|
|
2890
3069
|
} finally {
|
|
2891
|
-
if (worktreeSetup) cleanupWorktrees(worktreeSetup);
|
|
3070
|
+
if (worktreeSetup && !worktreeFinalized) cleanupWorktrees(worktreeSetup);
|
|
2892
3071
|
}
|
|
2893
3072
|
}
|
|
2894
3073
|
|
|
@@ -2924,7 +3103,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2924
3103
|
};
|
|
2925
3104
|
}
|
|
2926
3105
|
const effectiveToolBudget = resolveEffectiveToolBudget({ runBudget: data.toolBudget, agentBudget: agentConfig.toolBudget, configBudget: data.configToolBudget });
|
|
2927
|
-
if (effectiveToolBudget.error) return toExecutionErrorResult(params, new Error(effectiveToolBudget.error));
|
|
3106
|
+
if (effectiveToolBudget.error) return toExecutionErrorResult(params, new Error(effectiveToolBudget.error), data.contextPolicy.contextSummary);
|
|
2928
3107
|
|
|
2929
3108
|
const currentProvider = ctx.model?.provider;
|
|
2930
3109
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
@@ -3009,6 +3188,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3009
3188
|
shareEnabled,
|
|
3010
3189
|
sessionRoot,
|
|
3011
3190
|
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride),
|
|
3191
|
+
context: contextPolicy.contextForAgent(params.agent!),
|
|
3012
3192
|
skills: skillOverride === false ? [] : skillOverride,
|
|
3013
3193
|
output: effectiveOutput,
|
|
3014
3194
|
outputMode: effectiveOutputMode,
|
|
@@ -3023,9 +3203,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3023
3203
|
controlConfig,
|
|
3024
3204
|
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
3025
3205
|
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
3206
|
+
nestedRoute: data.nestedRoute,
|
|
3207
|
+
agentContract: params.agentContract,
|
|
3208
|
+
structuredOutputSchema: params.outputSchema,
|
|
3209
|
+
acceptance: params.acceptance,
|
|
3026
3210
|
timeoutMs: data.timeoutMs,
|
|
3027
3211
|
turnBudget: data.turnBudget,
|
|
3028
3212
|
toolBudget: effectiveToolBudget.toolBudget,
|
|
3213
|
+
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
3029
3214
|
});
|
|
3030
3215
|
}
|
|
3031
3216
|
}
|
|
@@ -3039,6 +3224,9 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3039
3224
|
if (validationError) {
|
|
3040
3225
|
return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [] } };
|
|
3041
3226
|
}
|
|
3227
|
+
const structuredRuntime = params.outputSchema
|
|
3228
|
+
? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path.join(artifactsDir, "structured-output", runId) : undefined)
|
|
3229
|
+
: undefined;
|
|
3042
3230
|
task = injectSingleOutputInstruction(task, outputPath, agentConfig);
|
|
3043
3231
|
|
|
3044
3232
|
let effectiveSkills: string[] | undefined;
|
|
@@ -3050,91 +3238,77 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3050
3238
|
const interruptController = new AbortController();
|
|
3051
3239
|
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
3052
3240
|
if (foregroundControl) {
|
|
3053
|
-
foregroundControl
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
};
|
|
3241
|
+
beginForegroundChild(foregroundControl, {
|
|
3242
|
+
index: 0,
|
|
3243
|
+
agent: params.agent!,
|
|
3244
|
+
description: foregroundControl.description,
|
|
3245
|
+
interrupt: () => {
|
|
3246
|
+
if (interruptController.signal.aborted) return false;
|
|
3247
|
+
interruptController.abort();
|
|
3248
|
+
return true;
|
|
3249
|
+
},
|
|
3250
|
+
});
|
|
3064
3251
|
}
|
|
3065
3252
|
|
|
3066
3253
|
const forwardSingleUpdate = onUpdate
|
|
3067
3254
|
? (update: AgentToolResult<Details>) => {
|
|
3068
|
-
if (foregroundControl)
|
|
3069
|
-
const firstProgress = update.details?.progress?.[0];
|
|
3070
|
-
foregroundControl.currentAgent = params.agent;
|
|
3071
|
-
foregroundControl.currentIndex = firstProgress?.index ?? 0;
|
|
3072
|
-
foregroundControl.currentActivityState = firstProgress?.activityState;
|
|
3073
|
-
foregroundControl.lastActivityAt = firstProgress?.lastActivityAt;
|
|
3074
|
-
foregroundControl.currentTool = firstProgress?.currentTool;
|
|
3075
|
-
foregroundControl.currentToolStartedAt = firstProgress?.currentToolStartedAt;
|
|
3076
|
-
foregroundControl.currentPath = firstProgress?.currentPath;
|
|
3077
|
-
foregroundControl.turnCount = firstProgress?.turnCount;
|
|
3078
|
-
foregroundControl.tokens = firstProgress?.tokens;
|
|
3079
|
-
foregroundControl.toolCount = firstProgress?.toolCount;
|
|
3080
|
-
foregroundControl.updatedAt = Date.now();
|
|
3081
|
-
}
|
|
3255
|
+
if (foregroundControl) updateForegroundChild(foregroundControl, 0, update.details?.progress?.[0]);
|
|
3082
3256
|
onUpdate(update);
|
|
3083
3257
|
}
|
|
3084
3258
|
: undefined;
|
|
3085
3259
|
|
|
3086
3260
|
const deadlineAt = data.deadlineAt ?? (data.timeoutMs !== undefined ? Date.now() + data.timeoutMs : undefined);
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
cwd
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
foregroundControl.updatedAt = Date.now();
|
|
3261
|
+
let r: Awaited<ReturnType<typeof runSync>>;
|
|
3262
|
+
try {
|
|
3263
|
+
r = await runSync(ctx.cwd, agents, params.agent!, task, {
|
|
3264
|
+
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
3265
|
+
context: data.contextPolicy.contextForAgent(params.agent!),
|
|
3266
|
+
cwd: effectiveCwd,
|
|
3267
|
+
signal,
|
|
3268
|
+
interruptSignal: interruptController.signal,
|
|
3269
|
+
allowIntercomDetach: agentConfig.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
|
|
3270
|
+
intercomEvents: deps.pi.events,
|
|
3271
|
+
runId,
|
|
3272
|
+
sessionDir: sessionDirForIndex(0),
|
|
3273
|
+
sessionFile: sessionFileForTask(params.agent!, 0, modelOverride),
|
|
3274
|
+
share: shareEnabled,
|
|
3275
|
+
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
3276
|
+
artifactConfig,
|
|
3277
|
+
maxOutput: params.maxOutput,
|
|
3278
|
+
outputPath,
|
|
3279
|
+
outputMode: effectiveOutputMode,
|
|
3280
|
+
maxSubagentDepth,
|
|
3281
|
+
waitToolEnabled: deps.waitToolEnabled,
|
|
3282
|
+
onUpdate: forwardSingleUpdate,
|
|
3283
|
+
controlConfig,
|
|
3284
|
+
onControlEvent,
|
|
3285
|
+
intercomSessionName: childIntercomTarget,
|
|
3286
|
+
orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
3287
|
+
nestedRoute: foregroundControl?.nestedRoute,
|
|
3288
|
+
index: 0,
|
|
3289
|
+
modelOverride,
|
|
3290
|
+
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
3291
|
+
availableModels,
|
|
3292
|
+
preferredModelProvider: currentProvider,
|
|
3293
|
+
modelScope: data.modelScope,
|
|
3294
|
+
skills: effectiveSkills,
|
|
3295
|
+
structuredOutput: structuredRuntime,
|
|
3296
|
+
agentContract: params.agentContract,
|
|
3297
|
+
acceptance: params.acceptance,
|
|
3298
|
+
acceptanceContext: { mode: "single" },
|
|
3299
|
+
onDetachedExit: (result) => updateRememberedForegroundChild(deps.state, { runId, mode: "single", cwd: effectiveCwd, sessionId: data.parentSessionId, index: 0, result, events: deps.pi.events }),
|
|
3300
|
+
timeoutMs: data.timeoutMs,
|
|
3301
|
+
deadlineAt,
|
|
3302
|
+
turnBudget: data.turnBudget,
|
|
3303
|
+
enforceHardTurnLimit: params.enforceHardTurnLimit,
|
|
3304
|
+
toolBudget: effectiveToolBudget.toolBudget,
|
|
3305
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
3306
|
+
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
3307
|
+
});
|
|
3308
|
+
} finally {
|
|
3309
|
+
if (!artifactConfig.enabled) cleanupStructuredOutputRuntime(structuredRuntime);
|
|
3137
3310
|
}
|
|
3311
|
+
if (foregroundControl) finishForegroundChild(foregroundControl, 0);
|
|
3138
3312
|
recordRun(params.agent!, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
|
|
3139
3313
|
|
|
3140
3314
|
if (r.progress) allProgress.push(r.progress);
|
|
@@ -3254,7 +3428,21 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3254
3428
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
3255
3429
|
ctx: ExtensionContext,
|
|
3256
3430
|
) => Promise<AgentToolResult<Details>>;
|
|
3431
|
+
/**
|
|
3432
|
+
* Correlated extension-to-extension delegation owns its request IDs and
|
|
3433
|
+
* cancellation controllers, so independent requests may execute concurrently.
|
|
3434
|
+
* The ordinary model-facing tool keeps the one-foreground-call-per-turn guard.
|
|
3435
|
+
*/
|
|
3436
|
+
executeDelegated: (
|
|
3437
|
+
id: string,
|
|
3438
|
+
params: SubagentParamsLike,
|
|
3439
|
+
signal: AbortSignal,
|
|
3440
|
+
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
3441
|
+
ctx: ExtensionContext,
|
|
3442
|
+
) => Promise<AgentToolResult<Details>>;
|
|
3257
3443
|
} {
|
|
3444
|
+
const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
|
|
3445
|
+
const delegatedZeroToolBudgets = new WeakSet<object>();
|
|
3258
3446
|
const execute = async (
|
|
3259
3447
|
_id: string,
|
|
3260
3448
|
params: SubagentParamsLike,
|
|
@@ -3262,6 +3450,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3262
3450
|
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
3263
3451
|
ctx: ExtensionContext,
|
|
3264
3452
|
): Promise<AgentToolResult<Details>> => {
|
|
3453
|
+
const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
|
|
3454
|
+
const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
|
|
3265
3455
|
deps.state.baseCwd = ctx.cwd;
|
|
3266
3456
|
deps.state.foregroundRuns ??= new Map();
|
|
3267
3457
|
deps.state.foregroundControls ??= new Map();
|
|
@@ -3438,7 +3628,22 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3438
3628
|
try {
|
|
3439
3629
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, ASYNC_DIR, RESULTS_DIR);
|
|
3440
3630
|
const runId = location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
|
|
3441
|
-
return steerAsyncRun({
|
|
3631
|
+
return steerAsyncRun({
|
|
3632
|
+
state: deps.state,
|
|
3633
|
+
runId,
|
|
3634
|
+
message,
|
|
3635
|
+
index: paramsWithResolvedCwd.index,
|
|
3636
|
+
kill: deps.kill,
|
|
3637
|
+
location,
|
|
3638
|
+
signal,
|
|
3639
|
+
...(paramsWithResolvedCwd.steeringRecovery === false
|
|
3640
|
+
? {}
|
|
3641
|
+
: {
|
|
3642
|
+
recover: ({ absoluteDeadlineAt, ...limits }) =>
|
|
3643
|
+
resumeAsyncRun({ params: { ...limits, action: "resume", id: runId, message }, requestCwd, ctx, deps, absoluteDeadlineAt }),
|
|
3644
|
+
}
|
|
3645
|
+
),
|
|
3646
|
+
});
|
|
3442
3647
|
} catch (error) {
|
|
3443
3648
|
const text = error instanceof Error ? error.message : String(error);
|
|
3444
3649
|
return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
|
|
@@ -3455,7 +3660,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3455
3660
|
if (resolved?.kind === "nested") return steerNestedRun({ target: resolved, message, index: paramsWithResolvedCwd.index, signal });
|
|
3456
3661
|
if (resolved?.kind === "foreground") return { content: [{ type: "text", text: "action='steer' currently supports live async Pi child sessions only; use action='interrupt' or action='resume' for foreground runs." }], isError: true, details: { mode: "management", results: [] } };
|
|
3457
3662
|
if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
3458
|
-
return steerAsyncRun({
|
|
3663
|
+
return steerAsyncRun({
|
|
3664
|
+
state: deps.state,
|
|
3665
|
+
runId: resolved.id,
|
|
3666
|
+
message,
|
|
3667
|
+
index: paramsWithResolvedCwd.index,
|
|
3668
|
+
kill: deps.kill,
|
|
3669
|
+
location: resolved.location,
|
|
3670
|
+
signal,
|
|
3671
|
+
...(paramsWithResolvedCwd.steeringRecovery === false
|
|
3672
|
+
? {}
|
|
3673
|
+
: {
|
|
3674
|
+
recover: ({ absoluteDeadlineAt, ...limits }) =>
|
|
3675
|
+
resumeAsyncRun({
|
|
3676
|
+
params: { ...limits, action: "resume", id: resolved!.id, message },
|
|
3677
|
+
requestCwd,
|
|
3678
|
+
ctx,
|
|
3679
|
+
deps,
|
|
3680
|
+
absoluteDeadlineAt,
|
|
3681
|
+
}),
|
|
3682
|
+
}
|
|
3683
|
+
),
|
|
3684
|
+
});
|
|
3459
3685
|
}
|
|
3460
3686
|
if (action === "append-step") {
|
|
3461
3687
|
return appendStepToAsyncChain({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
|
|
@@ -3593,7 +3819,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3593
3819
|
depth,
|
|
3594
3820
|
deps.config.forceTopLevelAsync === true,
|
|
3595
3821
|
);
|
|
3596
|
-
const runToolBudget = resolveToolBudget(
|
|
3822
|
+
const runToolBudget = resolveToolBudget(
|
|
3823
|
+
effectiveParams.toolBudget,
|
|
3824
|
+
"toolBudget",
|
|
3825
|
+
allowZeroToolBudget ? { minimumHard: 0 } : undefined,
|
|
3826
|
+
);
|
|
3597
3827
|
if (runToolBudget.error) return buildRequestedModeError(effectiveParams, runToolBudget.error);
|
|
3598
3828
|
const configToolBudget = resolveToolBudget(deps.config.toolBudget, "config.toolBudget");
|
|
3599
3829
|
if (configToolBudget.error) return buildRequestedModeError(effectiveParams, configToolBudget.error);
|
|
@@ -3615,7 +3845,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3615
3845
|
const sessionName = resolveIntercomSessionTarget(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
|
|
3616
3846
|
const intercomBridge = resolveIntercomBridge({
|
|
3617
3847
|
config: deps.config.intercomBridge,
|
|
3618
|
-
context: effectiveParams.context,
|
|
3848
|
+
context: effectiveParams.context ?? (contextPolicy.usesFork ? "fork" : undefined),
|
|
3619
3849
|
orchestratorTarget: sessionName,
|
|
3620
3850
|
});
|
|
3621
3851
|
const agents = intercomBridge.active
|
|
@@ -3688,7 +3918,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3688
3918
|
forkSessionFileForIndex = forkContextResolver.sessionFileForIndex;
|
|
3689
3919
|
forkThinkingOverrideForIndex = forkContextResolver.thinkingOverrideForIndex;
|
|
3690
3920
|
} catch (error) {
|
|
3691
|
-
return toExecutionErrorResult(effectiveParams, error);
|
|
3921
|
+
return toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
|
|
3692
3922
|
}
|
|
3693
3923
|
const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
|
|
3694
3924
|
const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
|
|
@@ -3698,8 +3928,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3698
3928
|
const artifactConfig: ArtifactConfig = {
|
|
3699
3929
|
...DEFAULT_ARTIFACT_CONFIG,
|
|
3700
3930
|
enabled: effectiveParams.artifacts !== false,
|
|
3931
|
+
dir: deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
|
|
3701
3932
|
};
|
|
3702
|
-
const artifactsDir = getArtifactsDir(parentSessionFile, effectiveCwd);
|
|
3933
|
+
const artifactsDir = getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
3703
3934
|
|
|
3704
3935
|
let sessionRoot: string;
|
|
3705
3936
|
if (effectiveParams.sessionDir) {
|
|
@@ -3717,6 +3948,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3717
3948
|
return toExecutionErrorResult(
|
|
3718
3949
|
effectiveParams,
|
|
3719
3950
|
new Error(`Failed to create session directory '${sessionRoot}': ${message}`),
|
|
3951
|
+
contextPolicy.contextSummary,
|
|
3720
3952
|
);
|
|
3721
3953
|
}
|
|
3722
3954
|
const sessionDirForIndex = (idx?: number) =>
|
|
@@ -3727,11 +3959,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3727
3959
|
return forkSessionFileForIndex(idx);
|
|
3728
3960
|
};
|
|
3729
3961
|
const forkThinkingOverrideForTask: ForkThinkingOverrideForTask = (agentName, idx = 0, modelOverride) => {
|
|
3730
|
-
if (!shouldForkAgent(contextPolicy, agentName)) return
|
|
3962
|
+
if (!shouldForkAgent(contextPolicy, agentName)) return delegatedThinkingOverride;
|
|
3731
3963
|
prepareForkThinking(agentName, idx, modelOverride);
|
|
3732
3964
|
const override = forkThinkingOverrideForIndex(idx);
|
|
3733
3965
|
if (override === "off") forkThinkingDowngrades.set(idx, agentName);
|
|
3734
|
-
return override;
|
|
3966
|
+
return override ?? delegatedThinkingOverride;
|
|
3735
3967
|
};
|
|
3736
3968
|
const childSessionFileForTask: ForkSessionFileForTask = (agentName, idx, modelOverride) =>
|
|
3737
3969
|
forkSessionFileForTask(agentName, idx, modelOverride) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
@@ -3742,13 +3974,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3742
3974
|
preflightForkSessionsForStaticTasks(effectiveParams, contextPolicy, forkSessionFileForTask, deps.config.chain?.dynamicFanout?.maxItems);
|
|
3743
3975
|
}
|
|
3744
3976
|
} catch (error) {
|
|
3745
|
-
return toExecutionErrorResult(effectiveParams, error);
|
|
3977
|
+
return toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
|
|
3746
3978
|
}
|
|
3747
3979
|
const chainBindingsError = validateExecutionChainBindings(effectiveParams, deps.config.chain?.dynamicFanout?.maxItems);
|
|
3748
|
-
if (chainBindingsError) return chainBindingsError;
|
|
3980
|
+
if (chainBindingsError) return withResolvedContext(chainBindingsError, contextPolicy.contextSummary);
|
|
3749
3981
|
|
|
3750
3982
|
const onUpdateWithContext = onUpdate
|
|
3751
|
-
? (r: AgentToolResult<Details>) => onUpdate(
|
|
3983
|
+
? (r: AgentToolResult<Details>) => onUpdate(withResolvedContext(r, contextPolicy.contextSummary))
|
|
3752
3984
|
: undefined;
|
|
3753
3985
|
|
|
3754
3986
|
const reservation = reserveSpawnBudget(
|
|
@@ -3783,22 +4015,30 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3783
4015
|
timeoutMs: foregroundTimeout.timeoutMs,
|
|
3784
4016
|
turnBudget: turnBudget.turnBudget,
|
|
3785
4017
|
toolBudget: runToolBudget.toolBudget,
|
|
4018
|
+
allowZeroToolBudget,
|
|
3786
4019
|
configToolBudget: configToolBudget.toolBudget,
|
|
3787
4020
|
contextPolicy,
|
|
3788
4021
|
modelScope,
|
|
3789
4022
|
parentSessionId: deps.state.currentSessionId,
|
|
4023
|
+
capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(deps.state.currentSessionId ?? undefined),
|
|
3790
4024
|
};
|
|
3791
4025
|
|
|
3792
|
-
const
|
|
4026
|
+
const foregroundDescription = effectiveParams.task?.trim()
|
|
4027
|
+
|| effectiveParams.tasks?.[0]?.task?.trim()
|
|
4028
|
+
|| (effectiveParams.chain ? firstRawChainTask(effectiveParams.chain)?.trim() : undefined);
|
|
4029
|
+
const foregroundControl: ForegroundRunControl | undefined = effectiveAsync
|
|
3793
4030
|
? undefined
|
|
3794
4031
|
: {
|
|
3795
4032
|
runId,
|
|
3796
4033
|
mode: foregroundMode,
|
|
3797
4034
|
startedAt: Date.now(),
|
|
3798
4035
|
updatedAt: Date.now(),
|
|
4036
|
+
cwd: effectiveCwd,
|
|
3799
4037
|
currentAgent: undefined,
|
|
3800
4038
|
currentIndex: undefined,
|
|
4039
|
+
description: foregroundDescription,
|
|
3801
4040
|
currentActivityState: undefined,
|
|
4041
|
+
activeChildren: new Map(),
|
|
3802
4042
|
nestedRoute,
|
|
3803
4043
|
interrupt: undefined,
|
|
3804
4044
|
};
|
|
@@ -3870,7 +4110,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3870
4110
|
let nestedForegroundStarted = false;
|
|
3871
4111
|
try {
|
|
3872
4112
|
const asyncResult = runAsyncPath(execData, deps);
|
|
3873
|
-
if (asyncResult) return
|
|
4113
|
+
if (asyncResult) return withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary);
|
|
3874
4114
|
if (foregroundControl) {
|
|
3875
4115
|
writeNestedForegroundEvent("subagent.nested.started");
|
|
3876
4116
|
nestedForegroundStarted = true;
|
|
@@ -3878,20 +4118,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3878
4118
|
if (hasChain && effectiveParams.chain) {
|
|
3879
4119
|
const result = await runChainPath(execData, deps);
|
|
3880
4120
|
writeNestedForegroundEvent("subagent.nested.completed", result);
|
|
3881
|
-
return
|
|
4121
|
+
return withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary);
|
|
3882
4122
|
}
|
|
3883
4123
|
if (hasTasks && effectiveParams.tasks) {
|
|
3884
4124
|
const result = await runParallelPath(execData, deps);
|
|
3885
4125
|
writeNestedForegroundEvent("subagent.nested.completed", result);
|
|
3886
|
-
return
|
|
4126
|
+
return withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary);
|
|
3887
4127
|
}
|
|
3888
4128
|
if (hasSingle) {
|
|
3889
4129
|
const result = await runSinglePath(execData, deps);
|
|
3890
4130
|
writeNestedForegroundEvent("subagent.nested.completed", result);
|
|
3891
|
-
return
|
|
4131
|
+
return withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary);
|
|
3892
4132
|
}
|
|
3893
4133
|
} catch (error) {
|
|
3894
|
-
const errorResult = withForkThinkingNotes(toExecutionErrorResult(effectiveParams, error), forkThinkingDowngrades);
|
|
4134
|
+
const errorResult = withForkThinkingNotes(toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary), forkThinkingDowngrades);
|
|
3895
4135
|
if (nestedForegroundStarted) writeNestedForegroundEvent("subagent.nested.completed", errorResult);
|
|
3896
4136
|
return errorResult;
|
|
3897
4137
|
} finally {
|
|
@@ -3904,11 +4144,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3904
4144
|
}
|
|
3905
4145
|
}
|
|
3906
4146
|
|
|
3907
|
-
return
|
|
4147
|
+
return withResolvedContext({
|
|
3908
4148
|
content: [{ type: "text", text: "Invalid params" }],
|
|
3909
4149
|
isError: true,
|
|
3910
4150
|
details: { mode: "single" as const, results: [] },
|
|
3911
|
-
},
|
|
4151
|
+
}, contextPolicy.contextSummary);
|
|
3912
4152
|
};
|
|
3913
4153
|
|
|
3914
4154
|
const executeWithSingleDispatchGuard = async (
|
|
@@ -3933,5 +4173,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3933
4173
|
}
|
|
3934
4174
|
};
|
|
3935
4175
|
|
|
3936
|
-
|
|
4176
|
+
const executeDelegated = async (
|
|
4177
|
+
id: string,
|
|
4178
|
+
params: SubagentParamsLike,
|
|
4179
|
+
signal: AbortSignal,
|
|
4180
|
+
onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
|
|
4181
|
+
ctx: ExtensionContext,
|
|
4182
|
+
): Promise<AgentToolResult<Details>> => {
|
|
4183
|
+
const delegatedParams = { ...params };
|
|
4184
|
+
const privateParams = delegatedParams as SubagentParamsLike & {
|
|
4185
|
+
delegatedThinkingOverride?: AgentConfig["thinking"];
|
|
4186
|
+
delegatedAllowZeroToolBudget?: true;
|
|
4187
|
+
};
|
|
4188
|
+
const thinkingOverride = privateParams.delegatedThinkingOverride;
|
|
4189
|
+
const allowZeroToolBudget = privateParams.delegatedAllowZeroToolBudget === true;
|
|
4190
|
+
delete privateParams.delegatedThinkingOverride;
|
|
4191
|
+
delete privateParams.delegatedAllowZeroToolBudget;
|
|
4192
|
+
if (thinkingOverride !== undefined) delegatedThinkingOverrides.set(delegatedParams, thinkingOverride);
|
|
4193
|
+
if (allowZeroToolBudget) delegatedZeroToolBudgets.add(delegatedParams);
|
|
4194
|
+
return execute(id, delegatedParams, signal, onUpdate, ctx);
|
|
4195
|
+
};
|
|
4196
|
+
|
|
4197
|
+
return { execute: executeWithSingleDispatchGuard, executeDelegated };
|
|
3937
4198
|
}
|