pi-subagents 0.36.0 → 0.37.1
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 +38 -0
- package/README.md +111 -16
- package/agents/delegate.md +2 -0
- package/agents/planner.md +2 -1
- package/agents/worker.md +2 -0
- package/package.json +6 -4
- package/skills/pi-subagents/SKILL.md +18 -973
- package/skills/pi-subagents/references/constraints-and-recipes.md +256 -0
- package/skills/pi-subagents/references/execution-controls.md +411 -0
- package/skills/pi-subagents/references/management-authoring-rpc.md +140 -0
- package/skills/pi-subagents/references/prompting-and-roles.md +268 -0
- package/src/agents/agent-management.ts +7 -1
- package/src/agents/agents.ts +99 -12
- package/src/api/capability-ceiling.ts +17 -0
- package/src/api/delegation.ts +6 -1
- package/src/api/preflight.ts +399 -0
- package/src/extension/index.ts +16 -7
- package/src/extension/rpc.ts +29 -2
- package/src/extension/schemas.ts +10 -4
- package/src/extension/tool-description.ts +4 -2
- package/src/intercom/intercom-bridge.ts +5 -2
- package/src/runs/background/async-execution.ts +125 -14
- package/src/runs/background/async-job-tracker.ts +19 -12
- package/src/runs/background/async-resume.ts +27 -9
- package/src/runs/background/async-status.ts +18 -0
- package/src/runs/background/notify.ts +3 -0
- package/src/runs/background/process-terminal.ts +280 -0
- package/src/runs/background/result-watcher.ts +9 -3
- package/src/runs/background/run-status.ts +7 -1
- 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 +182 -12
- package/src/runs/foreground/chain-execution.ts +5 -0
- package/src/runs/foreground/execution.ts +59 -13
- package/src/runs/foreground/subagent-executor.ts +142 -47
- package/src/runs/shared/acceptance.ts +41 -30
- package/src/runs/shared/capability-ceiling.ts +177 -0
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +56 -17
- package/src/runs/shared/model-fallback.ts +8 -0
- package/src/runs/shared/nested-events.ts +8 -1
- package/src/runs/shared/parallel-utils.ts +5 -0
- package/src/runs/shared/pi-args.ts +141 -58
- package/src/runs/shared/session-lease.ts +25 -5
- package/src/runs/shared/subagent-prompt-runtime.ts +14 -1
- package/src/runs/shared/task-intent.ts +1 -1
- package/src/runs/shared/tool-availability.ts +18 -2
- package/src/shared/launch-contract.ts +123 -0
- package/src/shared/types.ts +118 -8
- package/src/shared/utils.ts +17 -42
- package/src/slash/delegation-adapters.ts +6 -1
- package/src/slash/slash-commands.ts +1 -1
- package/src/tui/fleet-status.ts +62 -16
- package/src/tui/fleet.ts +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
import * as fs from "node:fs";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
import { pathToFileURL } from "node:url";
|
|
@@ -57,16 +58,17 @@ import {
|
|
|
57
58
|
DEFAULT_GLOBAL_CONCURRENCY_LIMIT,
|
|
58
59
|
Semaphore,
|
|
59
60
|
} from "../shared/parallel-utils.ts";
|
|
60
|
-
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir } from "../shared/pi-args.ts";
|
|
61
|
+
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, resolvePiLaunchToolPlan } from "../shared/pi-args.ts";
|
|
61
62
|
import { outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.ts";
|
|
62
63
|
import { createStructuredOutputRuntime, readStructuredOutput } from "../shared/structured-output.ts";
|
|
63
64
|
import { readChildToolDiagnosticError } from "../shared/tool-availability.ts";
|
|
64
65
|
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection } from "../shared/dynamic-fanout.ts";
|
|
65
66
|
import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent } from "../shared/nested-events.ts";
|
|
66
67
|
import { formatModelAttemptNote, isRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
68
|
+
import { markProcessTerminalCandidateLeaseRelease, writeProcessTerminalCandidate, type ProcessTerminalCandidate } from "./process-terminal.ts";
|
|
67
69
|
import { createSteeringStatus, recordSteeringRequest, steeringStatus, terminalSteeringNoticeState, updateSteeringTarget } from "./steering.ts";
|
|
68
70
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
69
|
-
import { detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput, readStatus } from "../../shared/utils.ts";
|
|
71
|
+
import { detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput, hasEmptyTerminalAssistantResponse, readStatus } from "../../shared/utils.ts";
|
|
70
72
|
import { evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
|
|
71
73
|
import {
|
|
72
74
|
createMutatingFailureState,
|
|
@@ -91,6 +93,7 @@ import {
|
|
|
91
93
|
type WorktreeSetup,
|
|
92
94
|
} from "../shared/worktree.ts";
|
|
93
95
|
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
96
|
+
import { launchBindingDigest } from "../../shared/launch-contract.ts";
|
|
94
97
|
import { writeInitialProgressFile } from "../../shared/settings.ts";
|
|
95
98
|
import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
|
|
96
99
|
import { acceptanceFailureMessage, aggregateAcceptanceReport, buildSkippedAcceptanceLedger, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport } from "../shared/acceptance.ts";
|
|
@@ -103,6 +106,7 @@ import { formatParallelHandoffError, formatParallelHandoffReference, parallelHan
|
|
|
103
106
|
import { resolveWatchdogConfig } from "../../watchdog/settings.ts";
|
|
104
107
|
import { createBoundedByteTail, createBoundedLineReader, formatProtocolOutputLimit, MAX_CHILD_STDERR_BYTES, projectChildLifecycle, type ChildLifecycleAction, type ProtocolOutputLimit } from "../shared/child-protocol.ts";
|
|
105
108
|
import { acquireSessionLease, type SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
109
|
+
import { decodeSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
106
110
|
import {
|
|
107
111
|
CHILD_WATCHDOG_CONFIG_ENV,
|
|
108
112
|
acceptChildWatchdogEvent,
|
|
@@ -146,13 +150,19 @@ interface SubagentRunConfig {
|
|
|
146
150
|
turnBudget?: ResolvedTurnBudget;
|
|
147
151
|
toolBudget?: ResolvedToolBudget;
|
|
148
152
|
revivalLease?: SessionLeaseRequest;
|
|
153
|
+
revivalLeaseToken?: string;
|
|
149
154
|
/** Global cap on simultaneously-running subagent tasks within this run. */
|
|
150
155
|
globalConcurrencyLimit?: number;
|
|
156
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
157
|
+
launchContractDigest?: string;
|
|
158
|
+
runnerProcessInstanceId?: string;
|
|
151
159
|
}
|
|
152
160
|
|
|
153
161
|
interface StepResult {
|
|
154
162
|
agent: string;
|
|
155
163
|
context?: "fresh" | "fork";
|
|
164
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
165
|
+
capabilityAudit?: import("../shared/capability-ceiling.ts").SubagentCapabilityAudit;
|
|
156
166
|
output: string;
|
|
157
167
|
error?: string;
|
|
158
168
|
protocolError?: ProtocolOutputLimit;
|
|
@@ -178,6 +188,7 @@ interface StepResult {
|
|
|
178
188
|
transcriptPath?: string;
|
|
179
189
|
transcriptError?: string;
|
|
180
190
|
agentContract?: import("../../shared/types.ts").AgentContract;
|
|
191
|
+
launchContractDigest?: string;
|
|
181
192
|
execution?: import("../../shared/types.ts").ExecutionProjection;
|
|
182
193
|
review?: import("../../shared/types.ts").ReviewProjection;
|
|
183
194
|
effects?: import("../../shared/types.ts").EffectsProjection;
|
|
@@ -186,6 +197,8 @@ interface StepResult {
|
|
|
186
197
|
structuredOutputSchemaPath?: string;
|
|
187
198
|
acceptance?: import("../../shared/types.ts").AcceptanceLedger;
|
|
188
199
|
watchdog?: import("../../shared/types.ts").ChildWatchdogProgress;
|
|
200
|
+
writerProcesses?: Array<{ processInstanceId: string; kind: "pi-writer"; attempt: number; closeObservedAt: number; exitCode: number | null; signal: string | null }>;
|
|
201
|
+
writerAttemptCount?: number;
|
|
189
202
|
}
|
|
190
203
|
|
|
191
204
|
const ASYNC_INTERRUPT_SIGNAL: NodeJS.Signals = process.platform === "win32" ? "SIGBREAK" : "SIGUSR2";
|
|
@@ -396,6 +409,9 @@ interface RunPiStreamingResult {
|
|
|
396
409
|
toolBudgetBlocked?: boolean;
|
|
397
410
|
observedMutationAttempt?: boolean;
|
|
398
411
|
watchdog?: ChildWatchdogStateSnapshot;
|
|
412
|
+
processInstanceId: string;
|
|
413
|
+
processCloseObservedAt?: number;
|
|
414
|
+
processSignal?: string | null;
|
|
399
415
|
}
|
|
400
416
|
|
|
401
417
|
function runPiStreaming(
|
|
@@ -418,6 +434,7 @@ function runPiStreaming(
|
|
|
418
434
|
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void,
|
|
419
435
|
): Promise<RunPiStreamingResult> {
|
|
420
436
|
return new Promise((resolve) => {
|
|
437
|
+
const processInstanceId = randomUUID();
|
|
421
438
|
onWriterProcess?.({ state: "spawning" });
|
|
422
439
|
const outputStream = fs.createWriteStream(outputFile, { flags: "w" });
|
|
423
440
|
const spawnEnv = { ...process.env, ...(env ?? {}), ...getSubagentDepthEnv(maxSubagentDepth) };
|
|
@@ -755,6 +772,7 @@ function runPiStreaming(
|
|
|
755
772
|
});
|
|
756
773
|
child.on("close", (exitCode, signal) => {
|
|
757
774
|
settled = true;
|
|
775
|
+
const processCloseObservedAt = Date.now();
|
|
758
776
|
try {
|
|
759
777
|
onWriterProcess?.({ state: "none" });
|
|
760
778
|
} catch {
|
|
@@ -790,6 +808,9 @@ function runPiStreaming(
|
|
|
790
808
|
wrapUpRequested: turnBudget?.outcome === "wrap-up-requested" || turnBudget?.outcome === "termination-deferred" || turnBudgetExceeded || undefined,
|
|
791
809
|
observedMutationAttempt,
|
|
792
810
|
watchdog: childWatchdogState,
|
|
811
|
+
processInstanceId,
|
|
812
|
+
processCloseObservedAt,
|
|
813
|
+
processSignal: signal,
|
|
793
814
|
});
|
|
794
815
|
});
|
|
795
816
|
|
|
@@ -812,7 +833,7 @@ function runPiStreaming(
|
|
|
812
833
|
const stderr = stderrTail.text();
|
|
813
834
|
const finalOutput = getFinalOutput(messages) || rawStdoutTail.text().trim();
|
|
814
835
|
const spawnErrorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
|
815
|
-
resolve({ stderr, exitCode: 1, messages, usage, model, error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : turnBudgetExceeded ? turnBudgetMessage : error ?? assistantError ?? spawnErrorMessage, protocolError, finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput, timedOut, stopped, turnBudget, turnBudgetExceeded, wrapUpRequested: turnBudget?.outcome === "wrap-up-requested" || turnBudget?.outcome === "termination-deferred" || turnBudgetExceeded || undefined, observedMutationAttempt, watchdog: childWatchdogState });
|
|
836
|
+
resolve({ stderr, exitCode: 1, messages, usage, model, error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : turnBudgetExceeded ? turnBudgetMessage : error ?? assistantError ?? spawnErrorMessage, protocolError, finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput, timedOut, stopped, turnBudget, turnBudgetExceeded, wrapUpRequested: turnBudget?.outcome === "wrap-up-requested" || turnBudget?.outcome === "termination-deferred" || turnBudgetExceeded || undefined, observedMutationAttempt, watchdog: childWatchdogState, processInstanceId });
|
|
816
837
|
});
|
|
817
838
|
});
|
|
818
839
|
}
|
|
@@ -955,6 +976,7 @@ interface SingleStepContext {
|
|
|
955
976
|
childIntercomTarget?: string;
|
|
956
977
|
orchestratorIntercomTarget?: string;
|
|
957
978
|
nestedRoute?: NestedRouteInfo;
|
|
979
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
958
980
|
onAttemptStart?: (attempt: { model?: string; thinking?: string }) => void;
|
|
959
981
|
onChildEvent?: (event: ChildEvent) => void;
|
|
960
982
|
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void;
|
|
@@ -969,6 +991,7 @@ async function runSingleStep(
|
|
|
969
991
|
agent: string;
|
|
970
992
|
context?: "fresh" | "fork";
|
|
971
993
|
agentContract?: import("../../shared/types.ts").AgentContract;
|
|
994
|
+
launchContractDigest?: string;
|
|
972
995
|
output: string;
|
|
973
996
|
exitCode: number | null;
|
|
974
997
|
error?: string;
|
|
@@ -996,6 +1019,7 @@ async function runSingleStep(
|
|
|
996
1019
|
structuredOutputPath?: string;
|
|
997
1020
|
structuredOutputSchemaPath?: string;
|
|
998
1021
|
acceptance?: import("../../shared/types.ts").AcceptanceLedger;
|
|
1022
|
+
writerAttemptCount?: number;
|
|
999
1023
|
}> {
|
|
1000
1024
|
if (step.importAsyncRoot) {
|
|
1001
1025
|
let importTimedOut = false;
|
|
@@ -1107,7 +1131,10 @@ async function runSingleStep(
|
|
|
1107
1131
|
? [step.model]
|
|
1108
1132
|
: [undefined];
|
|
1109
1133
|
const attemptedModels: string[] = [];
|
|
1134
|
+
let capabilityAudit: import("../shared/capability-ceiling.ts").SubagentCapabilityAudit | undefined;
|
|
1110
1135
|
const modelAttempts: ModelAttempt[] = [];
|
|
1136
|
+
const writerProcesses: Array<{ processInstanceId: string; kind: "pi-writer"; attempt: number; closeObservedAt: number; exitCode: number | null; signal: string | null }> = [];
|
|
1137
|
+
let writerAttemptCount = 0;
|
|
1111
1138
|
const attemptNotes: string[] = [];
|
|
1112
1139
|
const eventsPath = path.join(path.dirname(ctx.outputFile), "events.jsonl");
|
|
1113
1140
|
let finalResult: RunPiStreamingResult | undefined;
|
|
@@ -1116,6 +1143,7 @@ async function runSingleStep(
|
|
|
1116
1143
|
let turnBudget = ctx.turnBudget ? initialTurnBudgetState(ctx.turnBudget) : undefined;
|
|
1117
1144
|
let toolBudget = step.toolBudget ? initialToolBudgetState(step.toolBudget) : undefined;
|
|
1118
1145
|
let toolBudgetBlocked = false;
|
|
1146
|
+
let actualLaunchContractDigest = step.launchContractDigest;
|
|
1119
1147
|
|
|
1120
1148
|
for (let index = 0; index < candidates.length; index++) {
|
|
1121
1149
|
if (ctx.timeoutSignal?.aborted || ctx.skipAcceptance?.()) break;
|
|
@@ -1138,7 +1166,7 @@ async function runSingleStep(
|
|
|
1138
1166
|
childIndex: ctx.flatIndex,
|
|
1139
1167
|
})
|
|
1140
1168
|
: undefined;
|
|
1141
|
-
const { args, env, tempDir, toolDiagnosticPath } = buildPiArgs({
|
|
1169
|
+
const { args, env, tempDir, toolDiagnosticPath, capabilityAudit: attemptCapabilityAudit } = buildPiArgs({
|
|
1142
1170
|
parentSessionId: step.parentSessionId,
|
|
1143
1171
|
baseArgs: ["--mode", "json", "-p"],
|
|
1144
1172
|
task,
|
|
@@ -1155,6 +1183,7 @@ async function runSingleStep(
|
|
|
1155
1183
|
systemPrompt: appendTurnBudgetSystemPrompt(step.systemPrompt ?? "", ctx.turnBudget),
|
|
1156
1184
|
systemPromptMode: step.systemPromptMode,
|
|
1157
1185
|
mcpDirectTools: step.mcpDirectTools,
|
|
1186
|
+
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
1158
1187
|
cwd: step.cwd ?? ctx.cwd,
|
|
1159
1188
|
promptFileStem: step.agent,
|
|
1160
1189
|
intercomSessionName: ctx.childIntercomTarget,
|
|
@@ -1174,6 +1203,39 @@ async function runSingleStep(
|
|
|
1174
1203
|
childWatchdog,
|
|
1175
1204
|
waitToolEnabled: step.waitToolEnabled,
|
|
1176
1205
|
});
|
|
1206
|
+
if (step.definitionDigest) {
|
|
1207
|
+
const toolPlan = resolvePiLaunchToolPlan({
|
|
1208
|
+
tools: step.tools,
|
|
1209
|
+
extensions: step.extensions,
|
|
1210
|
+
subagentOnlyExtensions: step.subagentOnlyExtensions,
|
|
1211
|
+
mcpDirectTools: step.mcpDirectTools,
|
|
1212
|
+
cwd: step.cwd ?? ctx.cwd,
|
|
1213
|
+
requireReadTool: Boolean(step.skills?.length),
|
|
1214
|
+
structuredOutput: Boolean(effectiveStructuredOutput),
|
|
1215
|
+
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
1216
|
+
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
|
|
1217
|
+
});
|
|
1218
|
+
actualLaunchContractDigest = launchBindingDigest({
|
|
1219
|
+
definitionDigest: step.definitionDigest,
|
|
1220
|
+
task: step.launchBindingTask ?? task,
|
|
1221
|
+
...(candidate ? { model: candidate } : {}),
|
|
1222
|
+
modelCandidates: candidates,
|
|
1223
|
+
...(resolveEffectiveThinking(candidate, step.thinking) ? { thinking: resolveEffectiveThinking(candidate, step.thinking) } : {}),
|
|
1224
|
+
systemPrompt: appendTurnBudgetSystemPrompt(step.systemPrompt ?? "", ctx.turnBudget),
|
|
1225
|
+
systemPromptMode: step.systemPromptMode,
|
|
1226
|
+
inheritProjectContext: step.inheritProjectContext,
|
|
1227
|
+
inheritSkills: step.inheritSkills,
|
|
1228
|
+
skills: step.skills,
|
|
1229
|
+
tools: toolPlan.effectiveToolAllowlist,
|
|
1230
|
+
extensions: toolPlan.extensionArgs,
|
|
1231
|
+
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
1232
|
+
...(step.outputPath ? { outputPath: step.outputPath } : {}),
|
|
1233
|
+
...(step.outputMode ? { outputMode: step.outputMode } : {}),
|
|
1234
|
+
...(step.structuredOutputSchema ? { structuredOutputSchema: step.structuredOutputSchema } : {}),
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
capabilityAudit = attemptCapabilityAudit;
|
|
1238
|
+
writerAttemptCount += 1;
|
|
1177
1239
|
const run = await runPiStreaming(
|
|
1178
1240
|
args,
|
|
1179
1241
|
step.cwd ?? ctx.cwd,
|
|
@@ -1193,6 +1255,16 @@ async function runSingleStep(
|
|
|
1193
1255
|
ctx.registerTurnBudgetAbort,
|
|
1194
1256
|
ctx.onWriterProcess,
|
|
1195
1257
|
);
|
|
1258
|
+
if (run.processCloseObservedAt !== undefined) {
|
|
1259
|
+
writerProcesses.push({
|
|
1260
|
+
processInstanceId: run.processInstanceId,
|
|
1261
|
+
kind: "pi-writer",
|
|
1262
|
+
attempt: index,
|
|
1263
|
+
closeObservedAt: run.processCloseObservedAt,
|
|
1264
|
+
exitCode: run.exitCode,
|
|
1265
|
+
signal: run.processSignal ?? null,
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1196
1268
|
if (run.turnBudget) turnBudget = run.turnBudget;
|
|
1197
1269
|
else if (ctx.turnBudget) {
|
|
1198
1270
|
const assistantMessages = run.messages.filter((message) => message.role === "assistant");
|
|
@@ -1221,7 +1293,12 @@ async function runSingleStep(
|
|
|
1221
1293
|
const missingStructuredOutput = effectiveStructuredOutput
|
|
1222
1294
|
? !fs.existsSync(effectiveStructuredOutput.outputPath)
|
|
1223
1295
|
: false;
|
|
1224
|
-
const emptyOutputError = run.exitCode === 0
|
|
1296
|
+
const emptyOutputError = run.exitCode === 0
|
|
1297
|
+
&& !run.error
|
|
1298
|
+
&& !toolAvailabilityError
|
|
1299
|
+
&& !run.finalOutput.trim()
|
|
1300
|
+
&& (!effectiveStructuredOutput || missingStructuredOutput)
|
|
1301
|
+
&& (!hiddenError?.hasError || hasEmptyTerminalAssistantResponse(run.messages))
|
|
1225
1302
|
? "Subagent produced no output (possible model cold-start or empty response)."
|
|
1226
1303
|
: undefined;
|
|
1227
1304
|
let structuredOutput: unknown;
|
|
@@ -1257,23 +1334,22 @@ async function runSingleStep(
|
|
|
1257
1334
|
const completionGuardError = completionGuardTriggered && !isAgentContractV1(step.agentContract)
|
|
1258
1335
|
? "Subagent completed without making edits for an implementation task.\nIt appears to have returned planning or scratchpad output instead of applying changes."
|
|
1259
1336
|
: undefined;
|
|
1260
|
-
const effectiveExitCode = toolAvailabilityError || (completionGuardTriggered && !isAgentContractV1(step.agentContract)) || structuredError
|
|
1337
|
+
const effectiveExitCode = toolAvailabilityError || (completionGuardTriggered && !isAgentContractV1(step.agentContract)) || structuredError || emptyOutputError
|
|
1261
1338
|
? 1
|
|
1262
1339
|
: hiddenError?.hasError
|
|
1263
1340
|
? (hiddenError.exitCode ?? 1)
|
|
1264
|
-
:
|
|
1341
|
+
: run.error && run.exitCode === 0
|
|
1265
1342
|
? 1
|
|
1266
|
-
: run.
|
|
1267
|
-
? 1
|
|
1268
|
-
: run.exitCode;
|
|
1343
|
+
: run.exitCode;
|
|
1269
1344
|
const error = toolAvailabilityError
|
|
1270
1345
|
?? completionGuardError
|
|
1271
1346
|
?? structuredError
|
|
1347
|
+
?? emptyOutputError
|
|
1272
1348
|
?? (hiddenError?.hasError
|
|
1273
1349
|
? hiddenError.details
|
|
1274
1350
|
? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}`
|
|
1275
1351
|
: `${hiddenError.errorType} failed with exit code ${effectiveExitCode}`
|
|
1276
|
-
:
|
|
1352
|
+
: run.error || (run.exitCode !== 0 && run.stderr.trim() ? run.stderr.trim() : undefined));
|
|
1277
1353
|
const attempt: ModelAttempt = {
|
|
1278
1354
|
model: candidate ?? run.model ?? step.model ?? "default",
|
|
1279
1355
|
success: effectiveExitCode === 0 && !error,
|
|
@@ -1395,6 +1471,8 @@ async function runSingleStep(
|
|
|
1395
1471
|
modelAttempts,
|
|
1396
1472
|
error: effectiveFinalError,
|
|
1397
1473
|
acceptance: effectiveAcceptance,
|
|
1474
|
+
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
1475
|
+
launchContractDigest: actualLaunchContractDigest,
|
|
1398
1476
|
...(transcriptWriter ? { transcriptPath: artifactPaths.transcriptPath } : {}),
|
|
1399
1477
|
transcriptError: transcriptWriter?.getError(),
|
|
1400
1478
|
skills: step.skills,
|
|
@@ -1409,6 +1487,7 @@ async function runSingleStep(
|
|
|
1409
1487
|
agent: step.agent,
|
|
1410
1488
|
context: step.context,
|
|
1411
1489
|
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
1490
|
+
launchContractDigest: actualLaunchContractDigest,
|
|
1412
1491
|
output: outputForSummary,
|
|
1413
1492
|
exitCode: effectiveFinalExitCode,
|
|
1414
1493
|
error: effectiveFinalError,
|
|
@@ -1437,6 +1516,9 @@ async function runSingleStep(
|
|
|
1437
1516
|
structuredOutputSchemaPath: timedOutAfterAcceptance || stoppedAfterAcceptance || turnBudgetExceeded ? undefined : effectiveStructuredOutput?.schemaPath,
|
|
1438
1517
|
acceptance: effectiveAcceptance,
|
|
1439
1518
|
watchdog: finalResult?.watchdog,
|
|
1519
|
+
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
1520
|
+
writerProcesses,
|
|
1521
|
+
writerAttemptCount,
|
|
1440
1522
|
};
|
|
1441
1523
|
return isAgentContractV1(step.agentContract) ? attachContractProjections(result as unknown as import("../../shared/types.ts").SingleResult) as unknown as typeof result : result;
|
|
1442
1524
|
}
|
|
@@ -1445,6 +1527,19 @@ type RunnerStatusStep = NonNullable<AsyncStatus["steps"]>[number] & {
|
|
|
1445
1527
|
exitCode?: number | null;
|
|
1446
1528
|
};
|
|
1447
1529
|
|
|
1530
|
+
function appendCapabilityCeilingAppliedEvent(eventsPath: string, runId: string, stepIndex: number, agent: string, result: StepResult): void {
|
|
1531
|
+
if (!result.capabilityCeiling) return;
|
|
1532
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1533
|
+
type: "subagent.capability-ceiling.applied",
|
|
1534
|
+
ts: Date.now(),
|
|
1535
|
+
runId,
|
|
1536
|
+
stepIndex,
|
|
1537
|
+
agent,
|
|
1538
|
+
capabilityCeiling: result.capabilityCeiling,
|
|
1539
|
+
...(result.capabilityAudit ? { capabilityAudit: result.capabilityAudit } : {}),
|
|
1540
|
+
}));
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1448
1543
|
type RunnerStatusPayload = Omit<AsyncStatus, "steps" | "parallelGroups" | "pid" | "cwd" | "currentStep" | "chainStepCount" | "lastUpdate"> & {
|
|
1449
1544
|
pid: number;
|
|
1450
1545
|
cwd: string;
|
|
@@ -1654,6 +1749,8 @@ async function runSubagent(
|
|
|
1654
1749
|
outputName: task.outputName,
|
|
1655
1750
|
structured: task.structured,
|
|
1656
1751
|
...(task.agentContract ? { agentContract: task.agentContract } : {}),
|
|
1752
|
+
...(task.launchContractDigest ? { launchContractDigest: task.launchContractDigest } : {}),
|
|
1753
|
+
...(task.capabilityCeiling ? { capabilityCeiling: task.capabilityCeiling } : {}),
|
|
1657
1754
|
status: "pending",
|
|
1658
1755
|
...(task.toolBudget ? { toolBudget: initialToolBudgetState(task.toolBudget) } : {}),
|
|
1659
1756
|
...(task.sessionFile ? { sessionFile: task.sessionFile } : {}),
|
|
@@ -1677,6 +1774,7 @@ async function runSubagent(
|
|
|
1677
1774
|
outputName: step.collect.as,
|
|
1678
1775
|
structured: Boolean(step.collect.outputSchema),
|
|
1679
1776
|
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
1777
|
+
...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
|
|
1680
1778
|
status: "pending",
|
|
1681
1779
|
...(step.parallel.toolBudget ? { toolBudget: initialToolBudgetState(step.parallel.toolBudget) } : {}),
|
|
1682
1780
|
recentTools: [],
|
|
@@ -1694,6 +1792,8 @@ async function runSubagent(
|
|
|
1694
1792
|
outputName: step.outputName,
|
|
1695
1793
|
structured: step.structured,
|
|
1696
1794
|
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
1795
|
+
...(step.launchContractDigest ? { launchContractDigest: step.launchContractDigest } : {}),
|
|
1796
|
+
...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
|
|
1697
1797
|
status: "pending",
|
|
1698
1798
|
...(step.toolBudget ? { toolBudget: initialToolBudgetState(step.toolBudget) } : {}),
|
|
1699
1799
|
...(step.sessionFile ? { sessionFile: step.sessionFile } : {}),
|
|
@@ -1711,6 +1811,11 @@ async function runSubagent(
|
|
|
1711
1811
|
const sessionEnabled = Boolean(config.sessionDir)
|
|
1712
1812
|
|| shareEnabled
|
|
1713
1813
|
|| flatSteps.some((step) => Boolean(step.sessionFile));
|
|
1814
|
+
if (config.runnerProcessInstanceId) {
|
|
1815
|
+
for (const step of initialStatusSteps) {
|
|
1816
|
+
step.processTerminal = { version: 1, state: "pending", runId: id, runnerProcessInstanceId: config.runnerProcessInstanceId };
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1714
1819
|
const statusPayload: RunnerStatusPayload = {
|
|
1715
1820
|
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
1716
1821
|
runId: id,
|
|
@@ -1732,6 +1837,9 @@ async function runSubagent(
|
|
|
1732
1837
|
chainStepCount: steps.length,
|
|
1733
1838
|
parallelGroups,
|
|
1734
1839
|
workflowGraph: config.workflowGraph,
|
|
1840
|
+
...(config.launchContractDigest ? { launchContractDigest: config.launchContractDigest } : {}),
|
|
1841
|
+
...(config.capabilityCeiling ? { capabilityCeiling: config.capabilityCeiling } : {}),
|
|
1842
|
+
...(config.runnerProcessInstanceId ? { processTerminal: { version: 1 as const, state: "pending" as const, runId: id, runnerProcessInstanceId: config.runnerProcessInstanceId } } : {}),
|
|
1735
1843
|
steps: initialStatusSteps,
|
|
1736
1844
|
artifactsDir,
|
|
1737
1845
|
sessionDir: config.sessionDir,
|
|
@@ -2829,6 +2937,7 @@ async function runSubagent(
|
|
|
2829
2937
|
outputName: undefined,
|
|
2830
2938
|
structured: Boolean(task.structuredOutputSchema),
|
|
2831
2939
|
...(task.agentContract ? { agentContract: task.agentContract } : {}),
|
|
2940
|
+
...(task.capabilityCeiling ? { capabilityCeiling: task.capabilityCeiling } : {}),
|
|
2832
2941
|
status: "pending",
|
|
2833
2942
|
...(task.sessionFile ? { sessionFile: task.sessionFile } : {}),
|
|
2834
2943
|
...(transcriptPath ? { transcriptPath } : {}),
|
|
@@ -2932,6 +3041,7 @@ async function runSubagent(
|
|
|
2932
3041
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
2933
3042
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
2934
3043
|
nestedRoute: config.nestedRoute,
|
|
3044
|
+
capabilityCeiling: config.capabilityCeiling,
|
|
2935
3045
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
2936
3046
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
2937
3047
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
@@ -2974,6 +3084,7 @@ async function runSubagent(
|
|
|
2974
3084
|
statusPayload.steps[fi].transcriptPath = singleResult.transcriptPath ?? statusPayload.steps[fi].transcriptPath;
|
|
2975
3085
|
statusPayload.steps[fi].transcriptError = singleResult.transcriptError;
|
|
2976
3086
|
statusPayload.steps[fi].agentContract = singleResult.agentContract;
|
|
3087
|
+
statusPayload.steps[fi].launchContractDigest = singleResult.launchContractDigest;
|
|
2977
3088
|
statusPayload.steps[fi].effects = singleResult.effects;
|
|
2978
3089
|
statusPayload.steps[fi].execution = singleResult.execution;
|
|
2979
3090
|
statusPayload.steps[fi].review = singleResult.review;
|
|
@@ -2982,8 +3093,13 @@ async function runSubagent(
|
|
|
2982
3093
|
statusPayload.steps[fi].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
2983
3094
|
statusPayload.steps[fi].acceptance = singleResult.acceptance;
|
|
2984
3095
|
statusPayload.steps[fi].watchdog = singleResult.watchdog;
|
|
3096
|
+
statusPayload.steps[fi].capabilityCeiling = singleResult.capabilityCeiling;
|
|
3097
|
+
statusPayload.steps[fi].capabilityAudit = singleResult.capabilityAudit;
|
|
3098
|
+
if (singleResult.capabilityCeiling) statusPayload.capabilityCeiling = singleResult.capabilityCeiling;
|
|
3099
|
+
if (singleResult.capabilityAudit) statusPayload.capabilityAudit = singleResult.capabilityAudit;
|
|
2985
3100
|
statusPayload.lastUpdate = taskEndTime;
|
|
2986
3101
|
writeStatusPayload();
|
|
3102
|
+
appendCapabilityCeilingAppliedEvent(eventsPath, id, fi, task.agent, singleResult);
|
|
2987
3103
|
appendJsonl(eventsPath, JSON.stringify({
|
|
2988
3104
|
type: stopped || childStopped ? "subagent.step.stopped" : timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
2989
3105
|
ts: taskEndTime, runId: id, stepIndex: fi, agent: task.agent,
|
|
@@ -2999,6 +3115,7 @@ async function runSubagent(
|
|
|
2999
3115
|
agent: pr.agent,
|
|
3000
3116
|
context: pr.context,
|
|
3001
3117
|
agentContract: pr.agentContract,
|
|
3118
|
+
launchContractDigest: pr.launchContractDigest,
|
|
3002
3119
|
output: pr.output,
|
|
3003
3120
|
error: pr.error,
|
|
3004
3121
|
protocolError: pr.protocolError,
|
|
@@ -3030,6 +3147,8 @@ async function runSubagent(
|
|
|
3030
3147
|
structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
|
|
3031
3148
|
acceptance: pr.acceptance,
|
|
3032
3149
|
watchdog: pr.watchdog,
|
|
3150
|
+
capabilityCeiling: pr.capabilityCeiling,
|
|
3151
|
+
capabilityAudit: pr.capabilityAudit,
|
|
3033
3152
|
});
|
|
3034
3153
|
}
|
|
3035
3154
|
const collection = collectDynamicResults(step as Parameters<typeof collectDynamicResults>[0], materialized.items, parallelResults);
|
|
@@ -3257,6 +3376,7 @@ async function runSubagent(
|
|
|
3257
3376
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
3258
3377
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
3259
3378
|
nestedRoute: config.nestedRoute,
|
|
3379
|
+
capabilityCeiling: config.capabilityCeiling,
|
|
3260
3380
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
3261
3381
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
3262
3382
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
@@ -3313,8 +3433,13 @@ async function runSubagent(
|
|
|
3313
3433
|
statusPayload.steps[fi].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
3314
3434
|
statusPayload.steps[fi].acceptance = singleResult.acceptance;
|
|
3315
3435
|
statusPayload.steps[fi].watchdog = singleResult.watchdog;
|
|
3436
|
+
statusPayload.steps[fi].capabilityCeiling = singleResult.capabilityCeiling;
|
|
3437
|
+
statusPayload.steps[fi].capabilityAudit = singleResult.capabilityAudit;
|
|
3438
|
+
if (singleResult.capabilityCeiling) statusPayload.capabilityCeiling = singleResult.capabilityCeiling;
|
|
3439
|
+
if (singleResult.capabilityAudit) statusPayload.capabilityAudit = singleResult.capabilityAudit;
|
|
3316
3440
|
statusPayload.lastUpdate = taskEndTime;
|
|
3317
3441
|
writeStatusPayload();
|
|
3442
|
+
appendCapabilityCeilingAppliedEvent(eventsPath, id, fi, task.agent, singleResult);
|
|
3318
3443
|
|
|
3319
3444
|
appendJsonl(eventsPath, JSON.stringify({
|
|
3320
3445
|
type: stopped || childStopped ? "subagent.step.stopped" : timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
@@ -3366,6 +3491,7 @@ async function runSubagent(
|
|
|
3366
3491
|
agent: pr.agent,
|
|
3367
3492
|
context: pr.context,
|
|
3368
3493
|
agentContract: pr.agentContract,
|
|
3494
|
+
launchContractDigest: pr.launchContractDigest,
|
|
3369
3495
|
output: pr.output,
|
|
3370
3496
|
error: pr.error,
|
|
3371
3497
|
protocolError: pr.protocolError,
|
|
@@ -3516,6 +3642,7 @@ async function runSubagent(
|
|
|
3516
3642
|
childIntercomTarget: config.childIntercomTargets?.[flatIndex],
|
|
3517
3643
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
3518
3644
|
nestedRoute: config.nestedRoute,
|
|
3645
|
+
capabilityCeiling: config.capabilityCeiling,
|
|
3519
3646
|
registerInterrupt: (interrupt) => registerStepInterrupt(flatIndex, interrupt),
|
|
3520
3647
|
registerTimeout: (interrupt) => registerStepTimeout(flatIndex, interrupt),
|
|
3521
3648
|
registerStop: (stop) => registerStepStop(flatIndex, stop),
|
|
@@ -3540,6 +3667,7 @@ async function runSubagent(
|
|
|
3540
3667
|
agent: singleResult.agent,
|
|
3541
3668
|
context: singleResult.context,
|
|
3542
3669
|
agentContract: singleResult.agentContract,
|
|
3670
|
+
launchContractDigest: singleResult.launchContractDigest,
|
|
3543
3671
|
output: stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.output,
|
|
3544
3672
|
error: stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.error,
|
|
3545
3673
|
protocolError: singleResult.protocolError,
|
|
@@ -3562,6 +3690,8 @@ async function runSubagent(
|
|
|
3562
3690
|
structuredOutputSchemaPath: singleResult.structuredOutputSchemaPath,
|
|
3563
3691
|
acceptance: singleResult.acceptance,
|
|
3564
3692
|
watchdog: singleResult.watchdog,
|
|
3693
|
+
capabilityCeiling: singleResult.capabilityCeiling,
|
|
3694
|
+
capabilityAudit: singleResult.capabilityAudit,
|
|
3565
3695
|
interrupted: singleResult.interrupted,
|
|
3566
3696
|
timedOut: timedOut || singleResult.timedOut ? true : undefined,
|
|
3567
3697
|
stopped: stopped || childStopped ? true : undefined,
|
|
@@ -3636,12 +3766,17 @@ async function runSubagent(
|
|
|
3636
3766
|
statusPayload.steps[flatIndex].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
3637
3767
|
statusPayload.steps[flatIndex].acceptance = singleResult.acceptance;
|
|
3638
3768
|
statusPayload.steps[flatIndex].watchdog = singleResult.watchdog;
|
|
3769
|
+
statusPayload.steps[flatIndex].capabilityCeiling = singleResult.capabilityCeiling;
|
|
3770
|
+
statusPayload.steps[flatIndex].capabilityAudit = singleResult.capabilityAudit;
|
|
3771
|
+
if (singleResult.capabilityCeiling) statusPayload.capabilityCeiling = singleResult.capabilityCeiling;
|
|
3772
|
+
if (singleResult.capabilityAudit) statusPayload.capabilityAudit = singleResult.capabilityAudit;
|
|
3639
3773
|
if (stepTokens) {
|
|
3640
3774
|
statusPayload.steps[flatIndex].tokens = stepTokens;
|
|
3641
3775
|
statusPayload.totalTokens = { ...previousCumulativeTokens };
|
|
3642
3776
|
}
|
|
3643
3777
|
statusPayload.lastUpdate = stepEndTime;
|
|
3644
3778
|
writeStatusPayload();
|
|
3779
|
+
appendCapabilityCeilingAppliedEvent(eventsPath, id, flatIndex, seqStep.agent, singleResult);
|
|
3645
3780
|
|
|
3646
3781
|
appendJsonl(eventsPath, JSON.stringify({
|
|
3647
3782
|
type: stopped || childStopped ? "subagent.step.stopped" : timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
@@ -3864,6 +3999,7 @@ async function runSubagent(
|
|
|
3864
3999
|
transcriptPath: r.transcriptPath,
|
|
3865
4000
|
transcriptError: r.transcriptError,
|
|
3866
4001
|
agentContract: r.agentContract,
|
|
4002
|
+
launchContractDigest: r.launchContractDigest,
|
|
3867
4003
|
execution: r.execution,
|
|
3868
4004
|
review: r.review,
|
|
3869
4005
|
effects: r.effects,
|
|
@@ -3872,10 +4008,14 @@ async function runSubagent(
|
|
|
3872
4008
|
structuredOutputSchemaPath: r.structuredOutputSchemaPath,
|
|
3873
4009
|
acceptance: r.acceptance,
|
|
3874
4010
|
watchdog: r.watchdog,
|
|
4011
|
+
capabilityCeiling: r.capabilityCeiling,
|
|
4012
|
+
capabilityAudit: r.capabilityAudit,
|
|
3875
4013
|
})),
|
|
3876
4014
|
outputs,
|
|
3877
4015
|
workflowGraph: statusPayload.workflowGraph,
|
|
3878
4016
|
parallelHandoff: statusPayload.parallelHandoff,
|
|
4017
|
+
capabilityCeiling: statusPayload.capabilityCeiling,
|
|
4018
|
+
capabilityAudit: statusPayload.capabilityAudit,
|
|
3879
4019
|
exitCode: stopped || timedOut || turnBudgetExceeded ? 1 : interrupted || results.every((r) => r.success) ? 0 : 1,
|
|
3880
4020
|
timestamp: runEndedAt,
|
|
3881
4021
|
durationMs: runEndedAt - overallStartTime,
|
|
@@ -3885,6 +4025,7 @@ async function runSubagent(
|
|
|
3885
4025
|
artifactsDir,
|
|
3886
4026
|
cwd,
|
|
3887
4027
|
asyncDir,
|
|
4028
|
+
launchContractDigest: config.launchContractDigest,
|
|
3888
4029
|
sessionId: config.sessionId,
|
|
3889
4030
|
sessionFile: effectiveSessionFile,
|
|
3890
4031
|
intercomTarget: config.controlIntercomTarget,
|
|
@@ -3897,6 +4038,28 @@ async function runSubagent(
|
|
|
3897
4038
|
} catch (err) {
|
|
3898
4039
|
console.error(`Failed to write result file ${resultPath}:`, err);
|
|
3899
4040
|
}
|
|
4041
|
+
if (config.runnerProcessInstanceId) {
|
|
4042
|
+
const writers: Record<string, Array<{ processInstanceId: string; kind: "pi-writer"; attempt: number; closeObservedAt: number; exitCode: number | null; signal: string | null }>> = {};
|
|
4043
|
+
const expectedWriters: Record<string, number> = {};
|
|
4044
|
+
for (const [index, result] of results.entries()) {
|
|
4045
|
+
writers[String(index)] = result.writerProcesses ?? [];
|
|
4046
|
+
expectedWriters[String(index)] = result.writerAttemptCount ?? 0;
|
|
4047
|
+
}
|
|
4048
|
+
const candidate: ProcessTerminalCandidate = {
|
|
4049
|
+
version: 1,
|
|
4050
|
+
runId: id,
|
|
4051
|
+
runnerProcessInstanceId: config.runnerProcessInstanceId,
|
|
4052
|
+
writers,
|
|
4053
|
+
expectedWriters,
|
|
4054
|
+
...(config.revivalLease?.sessionFile ? { sessionFile: config.revivalLease.sessionFile } : {}),
|
|
4055
|
+
...(config.revivalLeaseToken ? { revivalLeaseToken: config.revivalLeaseToken } : {}),
|
|
4056
|
+
};
|
|
4057
|
+
try {
|
|
4058
|
+
writeProcessTerminalCandidate(asyncDir, candidate);
|
|
4059
|
+
} catch (error) {
|
|
4060
|
+
console.error(`Failed to write process-terminal candidate for '${id}':`, error);
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
3900
4063
|
}
|
|
3901
4064
|
|
|
3902
4065
|
async function waitForStartupControl(
|
|
@@ -3940,6 +4103,7 @@ async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
3940
4103
|
try {
|
|
3941
4104
|
if (config.revivalLease) {
|
|
3942
4105
|
lease = acquireSessionLease(config.revivalLease);
|
|
4106
|
+
config.revivalLeaseToken = lease.owner.token;
|
|
3943
4107
|
writeAtomicJson(startupPath, { state: "ready", token: lease.owner.token, pid: process.pid, owner: lease.owner });
|
|
3944
4108
|
await waitForStartupControl(startupAckPath, lease.owner.token, "ack");
|
|
3945
4109
|
writeAtomicJson(startupPath, { state: "acknowledged", token: lease.owner.token, pid: process.pid });
|
|
@@ -3966,11 +4130,17 @@ async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
3966
4130
|
} finally {
|
|
3967
4131
|
process.off("exit", releaseOnExit);
|
|
3968
4132
|
if (lease) {
|
|
4133
|
+
let acknowledged = false;
|
|
3969
4134
|
try {
|
|
3970
|
-
lease.release();
|
|
4135
|
+
acknowledged = lease.release();
|
|
3971
4136
|
} catch (error) {
|
|
3972
4137
|
console.error("Failed to release session revival lease:", error);
|
|
3973
4138
|
}
|
|
4139
|
+
try {
|
|
4140
|
+
markProcessTerminalCandidateLeaseRelease(config.asyncDir, lease.owner.token, acknowledged);
|
|
4141
|
+
} catch (error) {
|
|
4142
|
+
console.error("Failed to record session revival lease release:", error);
|
|
4143
|
+
}
|
|
3974
4144
|
}
|
|
3975
4145
|
}
|
|
3976
4146
|
}
|
|
@@ -79,6 +79,7 @@ import { isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
|
79
79
|
import type { ChainOutputMap } from "../../shared/types.ts";
|
|
80
80
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
81
81
|
import type { ContextMode } from "../shared/context-mode.ts";
|
|
82
|
+
import type { ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
82
83
|
|
|
83
84
|
interface ChainExecutionDetailsInput {
|
|
84
85
|
results: SingleResult[];
|
|
@@ -148,6 +149,7 @@ interface ParallelChainRunInput {
|
|
|
148
149
|
toolBudget?: ResolvedToolBudget;
|
|
149
150
|
configToolBudget?: ToolBudgetConfig;
|
|
150
151
|
globalSemaphore?: Semaphore;
|
|
152
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
151
153
|
dynamic?: boolean;
|
|
152
154
|
}
|
|
153
155
|
|
|
@@ -342,6 +344,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
342
344
|
const agentContract = task.agentContract ?? input.step.agentContract ?? input.agentContract;
|
|
343
345
|
const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskStr, {
|
|
344
346
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
347
|
+
capabilityCeiling: input.capabilityCeiling,
|
|
345
348
|
context: input.contextForAgent?.(task.agent),
|
|
346
349
|
cwd: taskCwd,
|
|
347
350
|
signal: input.signal,
|
|
@@ -471,6 +474,7 @@ interface ChainExecutionParams {
|
|
|
471
474
|
configToolBudget?: ToolBudgetConfig;
|
|
472
475
|
/** Global cap on simultaneously-running tasks within this chain. Defaults to DEFAULT_GLOBAL_CONCURRENCY_LIMIT. */
|
|
473
476
|
globalConcurrencyLimit?: number;
|
|
477
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
474
478
|
}
|
|
475
479
|
|
|
476
480
|
interface ChainExecutionResult {
|
|
@@ -1248,6 +1252,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1248
1252
|
});
|
|
1249
1253
|
const r = await runSync(ctx.cwd, agents, seqStep.agent, stepTask, {
|
|
1250
1254
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
1255
|
+
capabilityCeiling: params.capabilityCeiling,
|
|
1251
1256
|
context: params.contextForAgent?.(seqStep.agent),
|
|
1252
1257
|
cwd: resolveChildCwd(cwd ?? ctx.cwd, seqStep.cwd),
|
|
1253
1258
|
signal,
|