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,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
6
7
|
import * as fs from "node:fs";
|
|
7
8
|
import * as os from "node:os";
|
|
8
9
|
import * as path from "node:path";
|
|
@@ -11,10 +12,11 @@ import { createRequire } from "node:module";
|
|
|
11
12
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
12
13
|
import type { AgentConfig } from "../../agents/agents.ts";
|
|
13
14
|
import { writePrivateAtomicJson } from "../../shared/atomic-json.ts";
|
|
14
|
-
import { applyThinkingSuffix } from "../shared/pi-args.ts";
|
|
15
|
+
import { applyThinkingSuffix, resolvePiLaunchToolPlan } from "../shared/pi-args.ts";
|
|
15
16
|
import { injectOutputPathSystemPrompt, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
16
17
|
import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
17
18
|
import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
19
|
+
import type { ContextMode } from "../shared/context-mode.ts";
|
|
18
20
|
import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
19
21
|
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
20
22
|
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
@@ -29,8 +31,10 @@ import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
|
29
31
|
import { resolveEffectiveAcceptance } from "../shared/acceptance.ts";
|
|
30
32
|
import {
|
|
31
33
|
type AcceptanceInput,
|
|
34
|
+
type AgentContract,
|
|
32
35
|
type ArtifactConfig,
|
|
33
36
|
type Details,
|
|
37
|
+
type JsonSchemaObject,
|
|
34
38
|
type MaxOutputConfig,
|
|
35
39
|
type NestedRouteInfo,
|
|
36
40
|
type ResolvedControlConfig,
|
|
@@ -46,11 +50,15 @@ import {
|
|
|
46
50
|
getAsyncConfigPath,
|
|
47
51
|
resolveChildMaxSubagentDepth,
|
|
48
52
|
} from "../../shared/types.ts";
|
|
49
|
-
import { nestedResultsPath, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
|
|
50
|
-
import { initialTurnBudgetState } from "../shared/turn-budget.ts";
|
|
53
|
+
import { nestedResultsPath, nestedSummaryFromAsyncStatus, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
|
|
54
|
+
import { appendTurnBudgetSystemPrompt, initialTurnBudgetState } from "../shared/turn-budget.ts";
|
|
51
55
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
52
56
|
import type { ImportedAsyncRoot } from "./chain-root-attachment.ts";
|
|
53
57
|
import type { SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
58
|
+
import { finalizeProcessTerminal, readProcessTerminal } from "./process-terminal.ts";
|
|
59
|
+
import { SUBAGENT_PROCESS_TERMINAL_EVENT } from "../../shared/types.ts";
|
|
60
|
+
import { decodeSubagentCapabilityCeiling, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
61
|
+
import { agentDefinitionDigest, launchBindingDigest } from "../../shared/launch-contract.ts";
|
|
54
62
|
|
|
55
63
|
const require = createRequire(import.meta.url);
|
|
56
64
|
const piPackageRoot = resolvePiPackageRoot();
|
|
@@ -131,9 +139,11 @@ interface AsyncChainParams {
|
|
|
131
139
|
artifactConfig: ArtifactConfig;
|
|
132
140
|
shareEnabled: boolean;
|
|
133
141
|
sessionRoot?: string;
|
|
142
|
+
agentContract?: AgentContract;
|
|
134
143
|
chainSkills?: string[];
|
|
135
144
|
sessionFilesByFlatIndex?: (string | undefined)[];
|
|
136
145
|
thinkingOverridesByFlatIndex?: (AgentConfig["thinking"] | undefined)[];
|
|
146
|
+
contextForAgent?: (agentName: string) => ContextMode;
|
|
137
147
|
progressDir?: string;
|
|
138
148
|
dynamicFanoutMaxItems?: number;
|
|
139
149
|
maxSubagentDepth: number;
|
|
@@ -152,6 +162,7 @@ interface AsyncChainParams {
|
|
|
152
162
|
configToolBudget?: ResolvedToolBudget;
|
|
153
163
|
/** Global cap on simultaneously-running subagent tasks within the async run. */
|
|
154
164
|
globalConcurrencyLimit?: number;
|
|
165
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
155
166
|
}
|
|
156
167
|
|
|
157
168
|
interface AsyncSingleParams {
|
|
@@ -170,10 +181,13 @@ interface AsyncSingleParams {
|
|
|
170
181
|
sessionDir?: string;
|
|
171
182
|
sessionFile?: string;
|
|
172
183
|
revivalLease?: SessionLeaseRequest;
|
|
184
|
+
context?: ContextMode;
|
|
173
185
|
skills?: string[];
|
|
174
186
|
output?: string | boolean;
|
|
175
187
|
outputMode?: "inline" | "file-only";
|
|
176
188
|
outputBaseDir?: string;
|
|
189
|
+
agentContract?: AgentContract;
|
|
190
|
+
structuredOutputSchema?: JsonSchemaObject;
|
|
177
191
|
modelOverride?: string;
|
|
178
192
|
thinkingOverride?: AgentConfig["thinking"];
|
|
179
193
|
availableModels?: AvailableModelInfo[];
|
|
@@ -192,6 +206,7 @@ interface AsyncSingleParams {
|
|
|
192
206
|
turnBudget?: ResolvedTurnBudget;
|
|
193
207
|
toolBudget?: ResolvedToolBudget;
|
|
194
208
|
configToolBudget?: ResolvedToolBudget;
|
|
209
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
195
210
|
}
|
|
196
211
|
|
|
197
212
|
interface AsyncExecutionResult {
|
|
@@ -212,7 +227,9 @@ export interface AsyncRunnerStepBuildParams {
|
|
|
212
227
|
chainSkills?: string[];
|
|
213
228
|
sessionFilesByFlatIndex?: (string | undefined)[];
|
|
214
229
|
thinkingOverridesByFlatIndex?: (AgentConfig["thinking"] | undefined)[];
|
|
230
|
+
contextForAgent?: (agentName: string) => ContextMode;
|
|
215
231
|
progressDir?: string;
|
|
232
|
+
agentContract?: AgentContract;
|
|
216
233
|
dynamicFanoutMaxItems?: number;
|
|
217
234
|
maxSubagentDepth: number;
|
|
218
235
|
waitToolEnabled?: boolean;
|
|
@@ -222,6 +239,7 @@ export interface AsyncRunnerStepBuildParams {
|
|
|
222
239
|
validateOutputBindings?: boolean;
|
|
223
240
|
toolBudget?: ResolvedToolBudget;
|
|
224
241
|
configToolBudget?: ResolvedToolBudget;
|
|
242
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
225
243
|
}
|
|
226
244
|
|
|
227
245
|
export type AsyncRunnerStepBuildResult =
|
|
@@ -387,7 +405,7 @@ function terminateRunnerBeforeProceed(pid: number): void {
|
|
|
387
405
|
}
|
|
388
406
|
}
|
|
389
407
|
|
|
390
|
-
function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number; error?: string } {
|
|
408
|
+
function spawnRunner(cfg: object, suffix: string, cwd: string, onProcessTerminal?: (proof: unknown) => void): { pid?: number; error?: string } {
|
|
391
409
|
if (!jitiCliPath) {
|
|
392
410
|
return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
|
|
393
411
|
}
|
|
@@ -403,12 +421,14 @@ function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number;
|
|
|
403
421
|
|
|
404
422
|
fs.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
|
|
405
423
|
const cfgPath = getAsyncConfigPath(suffix);
|
|
406
|
-
|
|
424
|
+
const runnerProcessInstanceId = randomUUID();
|
|
425
|
+
const launchConfig = { ...cfg, runnerProcessInstanceId };
|
|
426
|
+
fs.writeFileSync(cfgPath, JSON.stringify(launchConfig));
|
|
407
427
|
const runner = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-runner.ts");
|
|
408
428
|
const nodeCommand = resolveAsyncRunnerNodeCommand();
|
|
409
|
-
const startupPath = typeof (
|
|
410
|
-
&& typeof (
|
|
411
|
-
? path.join((
|
|
429
|
+
const startupPath = typeof (launchConfig as { revivalLease?: unknown; asyncDir?: unknown }).revivalLease === "object"
|
|
430
|
+
&& typeof (launchConfig as { asyncDir?: unknown }).asyncDir === "string"
|
|
431
|
+
? path.join((launchConfig as { asyncDir: string }).asyncDir, "runner-startup.json")
|
|
412
432
|
: undefined;
|
|
413
433
|
const startupAckPath = startupPath ? path.join(path.dirname(startupPath), "runner-startup-ack.json") : undefined;
|
|
414
434
|
const startupProceedPath = startupPath ? path.join(path.dirname(startupPath), "runner-startup-proceed.json") : undefined;
|
|
@@ -416,7 +436,7 @@ function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number;
|
|
|
416
436
|
if (startupAckPath) fs.rmSync(startupAckPath, { force: true });
|
|
417
437
|
if (startupProceedPath) fs.rmSync(startupProceedPath, { force: true });
|
|
418
438
|
|
|
419
|
-
const logPaths = resolveAsyncRunnerLogPaths(
|
|
439
|
+
const logPaths = resolveAsyncRunnerLogPaths(launchConfig);
|
|
420
440
|
let stdoutFd: number | undefined;
|
|
421
441
|
let stderrFd: number | undefined;
|
|
422
442
|
try {
|
|
@@ -440,6 +460,56 @@ function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number;
|
|
|
440
460
|
proc.on("error", (error) => {
|
|
441
461
|
console.error(`[pi-subagents] async spawn failed: ${error.message}`);
|
|
442
462
|
});
|
|
463
|
+
proc.once("close", (exitCode, signal) => {
|
|
464
|
+
const launch = launchConfig as { asyncDir?: unknown; id?: unknown; nestedRoute?: NestedRouteInfo; nestedSelf?: { parentRunId: string; parentStepIndex?: number; depth: number; path?: Array<{ runId: string; stepIndex?: number; agent?: string }> } };
|
|
465
|
+
const asyncDir = launch.asyncDir;
|
|
466
|
+
const runId = launch.id;
|
|
467
|
+
if (typeof asyncDir !== "string" || typeof runId !== "string") return;
|
|
468
|
+
finalizeProcessTerminal(asyncDir, runId, {
|
|
469
|
+
processInstanceId: runnerProcessInstanceId,
|
|
470
|
+
closeObservedAt: Date.now(),
|
|
471
|
+
exitCode,
|
|
472
|
+
signal,
|
|
473
|
+
});
|
|
474
|
+
const persisted = readProcessTerminal(asyncDir, { runId, runnerProcessInstanceId });
|
|
475
|
+
if (!persisted) return;
|
|
476
|
+
if (launch.nestedRoute && launch.nestedSelf) {
|
|
477
|
+
try {
|
|
478
|
+
let status: import("../../shared/types.ts").AsyncStatus;
|
|
479
|
+
try {
|
|
480
|
+
status = JSON.parse(fs.readFileSync(path.join(asyncDir, "status.json"), "utf-8")) as import("../../shared/types.ts").AsyncStatus;
|
|
481
|
+
status.processTerminal = persisted;
|
|
482
|
+
} catch {
|
|
483
|
+
status = {
|
|
484
|
+
runId,
|
|
485
|
+
mode: "single",
|
|
486
|
+
state: persisted.state === "observed" ? "complete" : "failed",
|
|
487
|
+
startedAt: persisted.observedAt ?? Date.now(),
|
|
488
|
+
lastUpdate: Date.now(),
|
|
489
|
+
processTerminal: persisted,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
writeNestedEvent(launch.nestedRoute, {
|
|
493
|
+
type: "subagent.nested.completed",
|
|
494
|
+
ts: Date.now(),
|
|
495
|
+
parentRunId: launch.nestedSelf.parentRunId,
|
|
496
|
+
parentStepIndex: launch.nestedSelf.parentStepIndex,
|
|
497
|
+
child: nestedSummaryFromAsyncStatus(status, asyncDir, {
|
|
498
|
+
id: runId,
|
|
499
|
+
parentRunId: launch.nestedSelf.parentRunId,
|
|
500
|
+
parentStepIndex: launch.nestedSelf.parentStepIndex,
|
|
501
|
+
depth: launch.nestedSelf.depth,
|
|
502
|
+
path: launch.nestedSelf.path,
|
|
503
|
+
mode: status.mode,
|
|
504
|
+
ts: Date.now(),
|
|
505
|
+
}),
|
|
506
|
+
});
|
|
507
|
+
} catch (error) {
|
|
508
|
+
console.error("Failed to emit final nested process-terminal status:", error);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
onProcessTerminal?.(persisted);
|
|
512
|
+
});
|
|
443
513
|
if (typeof proc.pid !== "number") {
|
|
444
514
|
return { error: `async runner did not produce a pid for cwd: ${cwd}` };
|
|
445
515
|
}
|
|
@@ -624,10 +694,14 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
624
694
|
const thinkingOverride = flatIndex === undefined ? undefined : thinkingOverridesByFlatIndex?.[flatIndex];
|
|
625
695
|
const effectiveThinking = thinkingOverride ?? a.thinking;
|
|
626
696
|
const model = applyThinkingSuffix(primaryModel, effectiveThinking, thinkingOverride !== undefined);
|
|
697
|
+
const agentContract = s.agentContract ?? params.agentContract;
|
|
627
698
|
return {
|
|
628
699
|
parentSessionId: ctx.parentSessionId ?? ctx.currentSessionId,
|
|
700
|
+
...(params.capabilityCeiling ? { capabilityCeiling: params.capabilityCeiling } : {}),
|
|
629
701
|
agent: s.agent,
|
|
630
702
|
task,
|
|
703
|
+
...(params.contextForAgent ? { context: params.contextForAgent(s.agent) } : {}),
|
|
704
|
+
...(agentContract ? { agentContract } : {}),
|
|
631
705
|
phase: s.phase,
|
|
632
706
|
label: s.label,
|
|
633
707
|
outputName: s.as,
|
|
@@ -662,9 +736,11 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
662
736
|
mode: resultMode,
|
|
663
737
|
async: true,
|
|
664
738
|
dynamic: false,
|
|
739
|
+
agentContract,
|
|
665
740
|
}),
|
|
666
741
|
acceptanceInput: s.acceptance,
|
|
667
742
|
acceptanceRole: a.acceptanceRole,
|
|
743
|
+
...(s.gateOn ? { gateOn: s.gateOn } : {}),
|
|
668
744
|
...(s.outputSchema ? { structuredOutputSchema: s.outputSchema } : {}),
|
|
669
745
|
...(s.outputSchema ? { structuredOutput: createStructuredOutputRuntime(s.outputSchema, path.join(asyncDir, "structured-output")) } : {}),
|
|
670
746
|
...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
|
|
@@ -707,7 +783,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
707
783
|
}
|
|
708
784
|
}
|
|
709
785
|
const staticStep = nextFlatStep();
|
|
710
|
-
return buildSeqStep(t, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index, { stepIndex, taskIndex });
|
|
786
|
+
return buildSeqStep({ ...t, agentContract: t.agentContract ?? s.agentContract, gateOn: t.gateOn ?? s.gateOn }, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index, { stepIndex, taskIndex });
|
|
711
787
|
}),
|
|
712
788
|
concurrency: s.concurrency,
|
|
713
789
|
failFast: s.failFast,
|
|
@@ -724,7 +800,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
724
800
|
}
|
|
725
801
|
const maxItems = s.expand.maxItems ?? params.dynamicFanoutMaxItems ?? 0;
|
|
726
802
|
const dynamicFlatSteps = Array.from({ length: maxItems }, () => nextFlatStep());
|
|
727
|
-
const parallel = buildSeqStep(s.parallel as SequentialStep, undefined, undefined, progressPrecreated, behavior, undefined, { stepIndex });
|
|
803
|
+
const parallel = buildSeqStep({ ...(s.parallel as SequentialStep), agentContract: s.parallel.agentContract ?? s.agentContract, gateOn: s.parallel.gateOn ?? s.gateOn }, undefined, undefined, progressPrecreated, behavior, undefined, { stepIndex });
|
|
728
804
|
return {
|
|
729
805
|
expand: s.expand,
|
|
730
806
|
parallel,
|
|
@@ -743,9 +819,12 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
743
819
|
mode: resultMode,
|
|
744
820
|
async: true,
|
|
745
821
|
dynamicGroup: true,
|
|
822
|
+
agentContract: s.agentContract ?? params.agentContract,
|
|
746
823
|
}),
|
|
747
824
|
acceptanceInput: s.acceptance,
|
|
748
825
|
acceptanceRole: agent.acceptanceRole,
|
|
826
|
+
...(s.agentContract ?? params.agentContract ? { agentContract: s.agentContract ?? params.agentContract } : {}),
|
|
827
|
+
...(s.gateOn ? { gateOn: s.gateOn } : {}),
|
|
749
828
|
};
|
|
750
829
|
}
|
|
751
830
|
const staticStep = nextFlatStep();
|
|
@@ -816,6 +895,7 @@ export function executeAsyncChain(
|
|
|
816
895
|
nestedRoute,
|
|
817
896
|
} = params;
|
|
818
897
|
const resultMode = params.resultMode ?? "chain";
|
|
898
|
+
const capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
|
|
819
899
|
const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
|
|
820
900
|
const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
|
|
821
901
|
const asyncDir = inheritedNestedRoute
|
|
@@ -844,7 +924,9 @@ export function executeAsyncChain(
|
|
|
844
924
|
chainSkills: params.chainSkills,
|
|
845
925
|
sessionFilesByFlatIndex,
|
|
846
926
|
thinkingOverridesByFlatIndex,
|
|
927
|
+
contextForAgent: params.contextForAgent,
|
|
847
928
|
progressDir: params.progressDir ?? (artifactsDir ? path.join(artifactsDir, "progress", id) : resultMode === "parallel" ? path.join(asyncDir, "progress") : undefined),
|
|
929
|
+
agentContract: params.agentContract,
|
|
848
930
|
outputBaseDir: artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined,
|
|
849
931
|
dynamicFanoutMaxItems: params.dynamicFanoutMaxItems,
|
|
850
932
|
maxSubagentDepth,
|
|
@@ -853,6 +935,7 @@ export function executeAsyncChain(
|
|
|
853
935
|
asyncDir,
|
|
854
936
|
toolBudget: params.toolBudget,
|
|
855
937
|
configToolBudget: params.configToolBudget,
|
|
938
|
+
capabilityCeiling,
|
|
856
939
|
});
|
|
857
940
|
if ("error" in built) {
|
|
858
941
|
try {
|
|
@@ -897,6 +980,7 @@ export function executeAsyncChain(
|
|
|
897
980
|
sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
|
|
898
981
|
asyncDir,
|
|
899
982
|
sessionId: ctx.currentSessionId,
|
|
983
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
900
984
|
piPackageRoot,
|
|
901
985
|
piArgv1: process.argv[1],
|
|
902
986
|
worktreeSetupHook,
|
|
@@ -923,6 +1007,7 @@ export function executeAsyncChain(
|
|
|
923
1007
|
},
|
|
924
1008
|
id,
|
|
925
1009
|
runnerCwd,
|
|
1010
|
+
(proof) => ctx.pi.events.emit(SUBAGENT_PROCESS_TERMINAL_EVENT, proof),
|
|
926
1011
|
);
|
|
927
1012
|
} catch (error) {
|
|
928
1013
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -994,6 +1079,7 @@ export function executeAsyncChain(
|
|
|
994
1079
|
...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
|
|
995
1080
|
startedAt: now,
|
|
996
1081
|
lastUpdate: now,
|
|
1082
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
997
1083
|
},
|
|
998
1084
|
});
|
|
999
1085
|
} catch (error) {
|
|
@@ -1020,6 +1106,7 @@ export function executeAsyncChain(
|
|
|
1020
1106
|
asyncDir,
|
|
1021
1107
|
...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}),
|
|
1022
1108
|
...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
|
|
1109
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1023
1110
|
nestedRoute,
|
|
1024
1111
|
});
|
|
1025
1112
|
}
|
|
@@ -1032,7 +1119,7 @@ export function executeAsyncChain(
|
|
|
1032
1119
|
|
|
1033
1120
|
return {
|
|
1034
1121
|
content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`, ctx.interactive === true) }],
|
|
1035
|
-
details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph, ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
|
|
1122
|
+
details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph, ...(capabilityCeiling ? { capabilityCeiling } : {}), ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
|
|
1036
1123
|
};
|
|
1037
1124
|
}
|
|
1038
1125
|
|
|
@@ -1064,6 +1151,7 @@ export function executeAsyncSingle(
|
|
|
1064
1151
|
nestedRoute,
|
|
1065
1152
|
} = params;
|
|
1066
1153
|
const task = params.task ?? "";
|
|
1154
|
+
const capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
|
|
1067
1155
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
1068
1156
|
const skillNames = params.skills ?? agentConfig.skills ?? [];
|
|
1069
1157
|
const availableModels = params.availableModels;
|
|
@@ -1126,6 +1214,42 @@ export function executeAsyncSingle(
|
|
|
1126
1214
|
if (timeoutMs !== undefined && timeoutMs <= 0) return formatAsyncStartError("single", "The source run's absolute deadline expired before recovery could launch.");
|
|
1127
1215
|
const initialTurnBudget = params.turnBudget ? initialTurnBudgetState(params.turnBudget) : undefined;
|
|
1128
1216
|
const resolvedSessionDir = params.sessionDir ?? (sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined);
|
|
1217
|
+
const structuredOutput = params.structuredOutputSchema
|
|
1218
|
+
? createStructuredOutputRuntime(params.structuredOutputSchema, path.join(asyncDir, "structured-output"))
|
|
1219
|
+
: undefined;
|
|
1220
|
+
const modelCandidates = buildModelCandidates(primaryModel, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider, { scope: ctx.modelScope }).map((candidate) =>
|
|
1221
|
+
applyThinkingSuffix(candidate, effectiveThinking, params.thinkingOverride !== undefined),
|
|
1222
|
+
);
|
|
1223
|
+
const effectiveSystemPrompt = appendTurnBudgetSystemPrompt(systemPrompt, params.turnBudget);
|
|
1224
|
+
const toolPlan = resolvePiLaunchToolPlan({
|
|
1225
|
+
tools: agentConfig.tools,
|
|
1226
|
+
extensions: agentConfig.extensions,
|
|
1227
|
+
subagentOnlyExtensions: agentConfig.subagentOnlyExtensions,
|
|
1228
|
+
mcpDirectTools: agentConfig.mcpDirectTools,
|
|
1229
|
+
cwd: runnerCwd,
|
|
1230
|
+
requireReadTool: Boolean(resolvedSkills.length),
|
|
1231
|
+
structuredOutput: Boolean(params.structuredOutputSchema),
|
|
1232
|
+
capabilityCeiling,
|
|
1233
|
+
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
|
|
1234
|
+
});
|
|
1235
|
+
const launchContractDigest = launchBindingDigest({
|
|
1236
|
+
definitionDigest: agentDefinitionDigest(agentConfig),
|
|
1237
|
+
task,
|
|
1238
|
+
...(model ? { model } : {}),
|
|
1239
|
+
modelCandidates,
|
|
1240
|
+
...(resolveEffectiveThinking(model, effectiveThinking) ? { thinking: resolveEffectiveThinking(model, effectiveThinking) } : {}),
|
|
1241
|
+
systemPrompt: effectiveSystemPrompt,
|
|
1242
|
+
systemPromptMode: agentConfig.systemPromptMode,
|
|
1243
|
+
inheritProjectContext: agentConfig.inheritProjectContext,
|
|
1244
|
+
inheritSkills: agentConfig.inheritSkills,
|
|
1245
|
+
skills: resolvedSkills.map((skill) => skill.name),
|
|
1246
|
+
tools: toolPlan.effectiveToolAllowlist,
|
|
1247
|
+
extensions: toolPlan.extensionArgs,
|
|
1248
|
+
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
1249
|
+
...(outputPath ? { outputPath } : {}),
|
|
1250
|
+
outputMode,
|
|
1251
|
+
...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),
|
|
1252
|
+
});
|
|
1129
1253
|
const resolvedAcceptance = resolveEffectiveAcceptance({
|
|
1130
1254
|
explicit: params.acceptance,
|
|
1131
1255
|
agentName: agent,
|
|
@@ -1133,10 +1257,13 @@ export function executeAsyncSingle(
|
|
|
1133
1257
|
task,
|
|
1134
1258
|
mode: "single",
|
|
1135
1259
|
async: true,
|
|
1260
|
+
agentContract: params.agentContract,
|
|
1136
1261
|
});
|
|
1137
1262
|
const recoveryDescriptor: SteeringRecoveryDescriptor = {
|
|
1138
1263
|
version: 1,
|
|
1264
|
+
launchContractDigest,
|
|
1139
1265
|
sourceRunId: id,
|
|
1266
|
+
...(params.agentContract ? { agentContract: params.agentContract } : {}),
|
|
1140
1267
|
agent,
|
|
1141
1268
|
...(sessionFile ? { sessionFile } : {}),
|
|
1142
1269
|
cwd: runnerCwd,
|
|
@@ -1158,7 +1285,8 @@ export function executeAsyncSingle(
|
|
|
1158
1285
|
...(agentConfig.memory ? { memory: { ...agentConfig.memory } } : {}),
|
|
1159
1286
|
...(outputPath ? { outputPath } : {}),
|
|
1160
1287
|
outputMode,
|
|
1161
|
-
...(
|
|
1288
|
+
...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),
|
|
1289
|
+
...(params.acceptance !== undefined ? { acceptance: params.acceptance } : {}),
|
|
1162
1290
|
...(controlConfig ? { controlConfig } : {}),
|
|
1163
1291
|
...(deadlineAt !== undefined ? { absoluteDeadlineAt: deadlineAt } : {}),
|
|
1164
1292
|
...(params.turnBudget ? { initialTurnBudget: params.turnBudget } : {}),
|
|
@@ -1169,6 +1297,7 @@ export function executeAsyncSingle(
|
|
|
1169
1297
|
...(resolvedSessionDir ? { sessionDir: resolvedSessionDir } : {}),
|
|
1170
1298
|
...(artifactsDir ? { artifactsDir } : {}),
|
|
1171
1299
|
artifactConfig,
|
|
1300
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1172
1301
|
};
|
|
1173
1302
|
try {
|
|
1174
1303
|
writePrivateAtomicJson(path.join(asyncDir, "recovery-descriptor.json"), recoveryDescriptor);
|
|
@@ -1183,14 +1312,14 @@ export function executeAsyncSingle(
|
|
|
1183
1312
|
steps: [
|
|
1184
1313
|
{
|
|
1185
1314
|
parentSessionId: ctx.parentSessionId ?? ctx.currentSessionId,
|
|
1315
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1186
1316
|
agent,
|
|
1187
1317
|
task: taskWithOutputInstruction,
|
|
1318
|
+
...(params.context ? { context: params.context } : {}),
|
|
1188
1319
|
cwd: runnerCwd,
|
|
1189
1320
|
model,
|
|
1190
1321
|
thinking: resolveEffectiveThinking(model, effectiveThinking),
|
|
1191
|
-
modelCandidates
|
|
1192
|
-
applyThinkingSuffix(candidate, effectiveThinking, params.thinkingOverride !== undefined),
|
|
1193
|
-
),
|
|
1322
|
+
modelCandidates,
|
|
1194
1323
|
tools: agentConfig.tools,
|
|
1195
1324
|
extensions: agentConfig.extensions,
|
|
1196
1325
|
subagentOnlyExtensions: agentConfig.subagentOnlyExtensions,
|
|
@@ -1206,7 +1335,13 @@ export function executeAsyncSingle(
|
|
|
1206
1335
|
sessionFile,
|
|
1207
1336
|
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
|
|
1208
1337
|
waitToolEnabled: params.waitToolEnabled,
|
|
1338
|
+
...(params.agentContract ? { agentContract: params.agentContract } : {}),
|
|
1339
|
+
definitionDigest: agentDefinitionDigest(agentConfig),
|
|
1340
|
+
launchBindingTask: task,
|
|
1341
|
+
launchContractDigest,
|
|
1209
1342
|
effectiveAcceptance: resolvedAcceptance,
|
|
1343
|
+
...(structuredOutput ? { structuredOutput } : {}),
|
|
1344
|
+
...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),
|
|
1210
1345
|
...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
|
|
1211
1346
|
},
|
|
1212
1347
|
],
|
|
@@ -1220,6 +1355,7 @@ export function executeAsyncSingle(
|
|
|
1220
1355
|
sessionDir: resolvedSessionDir,
|
|
1221
1356
|
asyncDir,
|
|
1222
1357
|
sessionId: ctx.currentSessionId,
|
|
1358
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1223
1359
|
piPackageRoot,
|
|
1224
1360
|
piArgv1: process.argv[1],
|
|
1225
1361
|
worktreeSetupHook,
|
|
@@ -1233,6 +1369,7 @@ export function executeAsyncSingle(
|
|
|
1233
1369
|
controlIntercomTarget,
|
|
1234
1370
|
childIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,
|
|
1235
1371
|
resultMode: "single",
|
|
1372
|
+
launchContractDigest,
|
|
1236
1373
|
...(params.revivalLease ? { revivalLease: params.revivalLease } : {}),
|
|
1237
1374
|
nestedRoute: nestedRoute ?? inheritedNestedRoute,
|
|
1238
1375
|
nestedSelf: inheritedNestedRoute && nestedAddress ? {
|
|
@@ -1244,6 +1381,7 @@ export function executeAsyncSingle(
|
|
|
1244
1381
|
},
|
|
1245
1382
|
id,
|
|
1246
1383
|
runnerCwd,
|
|
1384
|
+
(proof) => ctx.pi.events.emit(SUBAGENT_PROCESS_TERMINAL_EVENT, proof),
|
|
1247
1385
|
);
|
|
1248
1386
|
} catch (error) {
|
|
1249
1387
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1284,6 +1422,7 @@ export function executeAsyncSingle(
|
|
|
1284
1422
|
...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
|
|
1285
1423
|
startedAt: now,
|
|
1286
1424
|
lastUpdate: now,
|
|
1425
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1287
1426
|
},
|
|
1288
1427
|
});
|
|
1289
1428
|
} catch (error) {
|
|
@@ -1301,14 +1440,16 @@ export function executeAsyncSingle(
|
|
|
1301
1440
|
goal: (params.goal ?? task).slice(0, 120),
|
|
1302
1441
|
cwd: runnerCwd,
|
|
1303
1442
|
asyncDir,
|
|
1443
|
+
launchContractDigest,
|
|
1304
1444
|
...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}),
|
|
1305
1445
|
...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
|
|
1446
|
+
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1306
1447
|
nestedRoute,
|
|
1307
1448
|
});
|
|
1308
1449
|
}
|
|
1309
1450
|
|
|
1310
1451
|
return {
|
|
1311
1452
|
content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]`, ctx.interactive === true) }],
|
|
1312
|
-
details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir, ...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
|
|
1453
|
+
details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir, launchContractDigest, ...(capabilityCeiling ? { capabilityCeiling } : {}), ...(params.context ? { context: params.context } : {}), ...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
|
|
1313
1454
|
};
|
|
1314
1455
|
}
|
|
@@ -91,6 +91,8 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
91
91
|
toolCount: run.toolCount,
|
|
92
92
|
steering: run.steering,
|
|
93
93
|
mode: run.mode,
|
|
94
|
+
context: run.context,
|
|
95
|
+
cwd: run.cwd,
|
|
94
96
|
agents: visibleSteps.map((step) => step.agent),
|
|
95
97
|
currentStep: run.currentStep,
|
|
96
98
|
chainStepCount: run.chainStepCount,
|
|
@@ -397,10 +399,12 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
397
399
|
state.asyncJobs.set(info.id, {
|
|
398
400
|
asyncId: info.id,
|
|
399
401
|
asyncDir,
|
|
402
|
+
...(typeof info.cwd === "string" ? { cwd: path.resolve(info.cwd) } : {}),
|
|
400
403
|
status: "queued",
|
|
401
404
|
pid: typeof info.pid === "number" ? info.pid : undefined,
|
|
402
405
|
...(typeof info.sessionId === "string" ? { sessionId: info.sessionId } : {}),
|
|
403
406
|
mode: info.mode ?? (info.chain ? "chain" : "single"),
|
|
407
|
+
description: info.goal ?? info.task,
|
|
404
408
|
agents,
|
|
405
409
|
chainStepCount: info.chainStepCount,
|
|
406
410
|
parallelGroups: validParallelGroups,
|