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
|
@@ -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";
|
|
@@ -6,7 +7,7 @@ import type { Message } from "@earendil-works/pi-ai";
|
|
|
6
7
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
7
8
|
import { createChildTranscriptWriter, type ChildTranscriptWriter } from "../../shared/child-transcript.ts";
|
|
8
9
|
import { closeSteerInbox, consumeInterruptRequest, consumeSteerRequests, deliverInterruptRequest, deliverStopRequest, deliverTimeoutRequest, enqueueStepSteer, steerAcksDir, steerCapabilityPath, stepSteerInboxDir, watchAsyncControlInbox, type SteerAck, type SteerCapability, type SteerRequest } from "./control-channel.ts";
|
|
9
|
-
import { appendJsonl as appendRawJsonl, getArtifactPaths } from "../../shared/artifacts.ts";
|
|
10
|
+
import { appendJsonl as appendRawJsonl, formatOutputArtifactContent, getArtifactPaths } from "../../shared/artifacts.ts";
|
|
10
11
|
import { PI_CODING_AGENT_PACKAGE, getPiSpawnCommand, resolveInstalledPiPackageRoot } from "../shared/pi-spawn.ts";
|
|
11
12
|
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, injectOutputPathSystemPrompt, injectSingleOutputInstruction, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
12
13
|
import {
|
|
@@ -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,16 +93,20 @@ 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";
|
|
100
|
+
import { attachContractProjections, isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
97
101
|
import { waitForImportedAsyncRoot } from "./chain-root-attachment.ts";
|
|
98
102
|
import { appendRunnerStepsToStatus, consumeChainAppendRequests, countPendingChainAppendRequests } from "./chain-append.ts";
|
|
99
103
|
import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, turnBudgetDecision, turnBudgetDeferredNote, turnBudgetDeferredState, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
|
|
100
104
|
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
|
|
105
|
+
import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup } from "../shared/parallel-handoff.ts";
|
|
101
106
|
import { resolveWatchdogConfig } from "../../watchdog/settings.ts";
|
|
102
107
|
import { createBoundedByteTail, createBoundedLineReader, formatProtocolOutputLimit, MAX_CHILD_STDERR_BYTES, projectChildLifecycle, type ChildLifecycleAction, type ProtocolOutputLimit } from "../shared/child-protocol.ts";
|
|
103
108
|
import { acquireSessionLease, type SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
109
|
+
import { decodeSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
104
110
|
import {
|
|
105
111
|
CHILD_WATCHDOG_CONFIG_ENV,
|
|
106
112
|
acceptChildWatchdogEvent,
|
|
@@ -144,12 +150,19 @@ interface SubagentRunConfig {
|
|
|
144
150
|
turnBudget?: ResolvedTurnBudget;
|
|
145
151
|
toolBudget?: ResolvedToolBudget;
|
|
146
152
|
revivalLease?: SessionLeaseRequest;
|
|
153
|
+
revivalLeaseToken?: string;
|
|
147
154
|
/** Global cap on simultaneously-running subagent tasks within this run. */
|
|
148
155
|
globalConcurrencyLimit?: number;
|
|
156
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
157
|
+
launchContractDigest?: string;
|
|
158
|
+
runnerProcessInstanceId?: string;
|
|
149
159
|
}
|
|
150
160
|
|
|
151
161
|
interface StepResult {
|
|
152
162
|
agent: string;
|
|
163
|
+
context?: "fresh" | "fork";
|
|
164
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
165
|
+
capabilityAudit?: import("../shared/capability-ceiling.ts").SubagentCapabilityAudit;
|
|
153
166
|
output: string;
|
|
154
167
|
error?: string;
|
|
155
168
|
protocolError?: ProtocolOutputLimit;
|
|
@@ -174,11 +187,18 @@ interface StepResult {
|
|
|
174
187
|
truncated?: boolean;
|
|
175
188
|
transcriptPath?: string;
|
|
176
189
|
transcriptError?: string;
|
|
190
|
+
agentContract?: import("../../shared/types.ts").AgentContract;
|
|
191
|
+
launchContractDigest?: string;
|
|
192
|
+
execution?: import("../../shared/types.ts").ExecutionProjection;
|
|
193
|
+
review?: import("../../shared/types.ts").ReviewProjection;
|
|
194
|
+
effects?: import("../../shared/types.ts").EffectsProjection;
|
|
177
195
|
structuredOutput?: unknown;
|
|
178
196
|
structuredOutputPath?: string;
|
|
179
197
|
structuredOutputSchemaPath?: string;
|
|
180
198
|
acceptance?: import("../../shared/types.ts").AcceptanceLedger;
|
|
181
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;
|
|
182
202
|
}
|
|
183
203
|
|
|
184
204
|
const ASYNC_INTERRUPT_SIGNAL: NodeJS.Signals = process.platform === "win32" ? "SIGBREAK" : "SIGUSR2";
|
|
@@ -256,6 +276,15 @@ function shouldPersistChildEvent(event: Record<string, unknown>): boolean {
|
|
|
256
276
|
return event.type !== "message_update";
|
|
257
277
|
}
|
|
258
278
|
|
|
279
|
+
function isBlockingSupervisorTool(toolName: string | undefined, args: unknown): boolean {
|
|
280
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) return false;
|
|
281
|
+
if (toolName === "contact_supervisor") {
|
|
282
|
+
const reason = (args as Record<string, unknown>).reason;
|
|
283
|
+
return reason === "need_decision" || reason === "interview_request";
|
|
284
|
+
}
|
|
285
|
+
return toolName === "intercom" && (args as Record<string, unknown>).action === "ask";
|
|
286
|
+
}
|
|
287
|
+
|
|
259
288
|
function findLatestSessionFile(sessionDir: string): string | null {
|
|
260
289
|
try {
|
|
261
290
|
const files = fs
|
|
@@ -380,6 +409,9 @@ interface RunPiStreamingResult {
|
|
|
380
409
|
toolBudgetBlocked?: boolean;
|
|
381
410
|
observedMutationAttempt?: boolean;
|
|
382
411
|
watchdog?: ChildWatchdogStateSnapshot;
|
|
412
|
+
processInstanceId: string;
|
|
413
|
+
processCloseObservedAt?: number;
|
|
414
|
+
processSignal?: string | null;
|
|
383
415
|
}
|
|
384
416
|
|
|
385
417
|
function runPiStreaming(
|
|
@@ -402,6 +434,7 @@ function runPiStreaming(
|
|
|
402
434
|
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void,
|
|
403
435
|
): Promise<RunPiStreamingResult> {
|
|
404
436
|
return new Promise((resolve) => {
|
|
437
|
+
const processInstanceId = randomUUID();
|
|
405
438
|
onWriterProcess?.({ state: "spawning" });
|
|
406
439
|
const outputStream = fs.createWriteStream(outputFile, { flags: "w" });
|
|
407
440
|
const spawnEnv = { ...process.env, ...(env ?? {}), ...getSubagentDepthEnv(maxSubagentDepth) };
|
|
@@ -739,6 +772,7 @@ function runPiStreaming(
|
|
|
739
772
|
});
|
|
740
773
|
child.on("close", (exitCode, signal) => {
|
|
741
774
|
settled = true;
|
|
775
|
+
const processCloseObservedAt = Date.now();
|
|
742
776
|
try {
|
|
743
777
|
onWriterProcess?.({ state: "none" });
|
|
744
778
|
} catch {
|
|
@@ -774,6 +808,9 @@ function runPiStreaming(
|
|
|
774
808
|
wrapUpRequested: turnBudget?.outcome === "wrap-up-requested" || turnBudget?.outcome === "termination-deferred" || turnBudgetExceeded || undefined,
|
|
775
809
|
observedMutationAttempt,
|
|
776
810
|
watchdog: childWatchdogState,
|
|
811
|
+
processInstanceId,
|
|
812
|
+
processCloseObservedAt,
|
|
813
|
+
processSignal: signal,
|
|
777
814
|
});
|
|
778
815
|
});
|
|
779
816
|
|
|
@@ -796,7 +833,7 @@ function runPiStreaming(
|
|
|
796
833
|
const stderr = stderrTail.text();
|
|
797
834
|
const finalOutput = getFinalOutput(messages) || rawStdoutTail.text().trim();
|
|
798
835
|
const spawnErrorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
|
799
|
-
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 });
|
|
800
837
|
});
|
|
801
838
|
});
|
|
802
839
|
}
|
|
@@ -939,6 +976,7 @@ interface SingleStepContext {
|
|
|
939
976
|
childIntercomTarget?: string;
|
|
940
977
|
orchestratorIntercomTarget?: string;
|
|
941
978
|
nestedRoute?: NestedRouteInfo;
|
|
979
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
942
980
|
onAttemptStart?: (attempt: { model?: string; thinking?: string }) => void;
|
|
943
981
|
onChildEvent?: (event: ChildEvent) => void;
|
|
944
982
|
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void;
|
|
@@ -951,6 +989,9 @@ async function runSingleStep(
|
|
|
951
989
|
ctx: SingleStepContext,
|
|
952
990
|
): Promise<{
|
|
953
991
|
agent: string;
|
|
992
|
+
context?: "fresh" | "fork";
|
|
993
|
+
agentContract?: import("../../shared/types.ts").AgentContract;
|
|
994
|
+
launchContractDigest?: string;
|
|
954
995
|
output: string;
|
|
955
996
|
exitCode: number | null;
|
|
956
997
|
error?: string;
|
|
@@ -971,10 +1012,14 @@ async function runSingleStep(
|
|
|
971
1012
|
sessionFile?: string;
|
|
972
1013
|
intercomTarget?: string;
|
|
973
1014
|
completionGuardTriggered?: boolean;
|
|
1015
|
+
effects?: import("../../shared/types.ts").EffectsProjection;
|
|
1016
|
+
execution?: import("../../shared/types.ts").ExecutionProjection;
|
|
1017
|
+
review?: import("../../shared/types.ts").ReviewProjection;
|
|
974
1018
|
structuredOutput?: unknown;
|
|
975
1019
|
structuredOutputPath?: string;
|
|
976
1020
|
structuredOutputSchemaPath?: string;
|
|
977
1021
|
acceptance?: import("../../shared/types.ts").AcceptanceLedger;
|
|
1022
|
+
writerAttemptCount?: number;
|
|
978
1023
|
}> {
|
|
979
1024
|
if (step.importAsyncRoot) {
|
|
980
1025
|
let importTimedOut = false;
|
|
@@ -1052,7 +1097,7 @@ async function runSingleStep(
|
|
|
1052
1097
|
if (ctx.outputs) task = resolveOutputReferences(task, ctx.outputs);
|
|
1053
1098
|
const taskForCompletionGuard = task;
|
|
1054
1099
|
if (step.effectiveAcceptance) {
|
|
1055
|
-
const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance);
|
|
1100
|
+
const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance, { reportOptional: isAgentContractV1(step.agentContract) });
|
|
1056
1101
|
if (acceptancePrompt) task = `${task}\n${acceptancePrompt}`;
|
|
1057
1102
|
}
|
|
1058
1103
|
const sessionEnabled = Boolean(step.sessionFile) || ctx.sessionEnabled;
|
|
@@ -1086,7 +1131,10 @@ async function runSingleStep(
|
|
|
1086
1131
|
? [step.model]
|
|
1087
1132
|
: [undefined];
|
|
1088
1133
|
const attemptedModels: string[] = [];
|
|
1134
|
+
let capabilityAudit: import("../shared/capability-ceiling.ts").SubagentCapabilityAudit | undefined;
|
|
1089
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;
|
|
1090
1138
|
const attemptNotes: string[] = [];
|
|
1091
1139
|
const eventsPath = path.join(path.dirname(ctx.outputFile), "events.jsonl");
|
|
1092
1140
|
let finalResult: RunPiStreamingResult | undefined;
|
|
@@ -1095,6 +1143,7 @@ async function runSingleStep(
|
|
|
1095
1143
|
let turnBudget = ctx.turnBudget ? initialTurnBudgetState(ctx.turnBudget) : undefined;
|
|
1096
1144
|
let toolBudget = step.toolBudget ? initialToolBudgetState(step.toolBudget) : undefined;
|
|
1097
1145
|
let toolBudgetBlocked = false;
|
|
1146
|
+
let actualLaunchContractDigest = step.launchContractDigest;
|
|
1098
1147
|
|
|
1099
1148
|
for (let index = 0; index < candidates.length; index++) {
|
|
1100
1149
|
if (ctx.timeoutSignal?.aborted || ctx.skipAcceptance?.()) break;
|
|
@@ -1117,7 +1166,7 @@ async function runSingleStep(
|
|
|
1117
1166
|
childIndex: ctx.flatIndex,
|
|
1118
1167
|
})
|
|
1119
1168
|
: undefined;
|
|
1120
|
-
const { args, env, tempDir, toolDiagnosticPath } = buildPiArgs({
|
|
1169
|
+
const { args, env, tempDir, toolDiagnosticPath, capabilityAudit: attemptCapabilityAudit } = buildPiArgs({
|
|
1121
1170
|
parentSessionId: step.parentSessionId,
|
|
1122
1171
|
baseArgs: ["--mode", "json", "-p"],
|
|
1123
1172
|
task,
|
|
@@ -1134,6 +1183,7 @@ async function runSingleStep(
|
|
|
1134
1183
|
systemPrompt: appendTurnBudgetSystemPrompt(step.systemPrompt ?? "", ctx.turnBudget),
|
|
1135
1184
|
systemPromptMode: step.systemPromptMode,
|
|
1136
1185
|
mcpDirectTools: step.mcpDirectTools,
|
|
1186
|
+
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
1137
1187
|
cwd: step.cwd ?? ctx.cwd,
|
|
1138
1188
|
promptFileStem: step.agent,
|
|
1139
1189
|
intercomSessionName: ctx.childIntercomTarget,
|
|
@@ -1153,6 +1203,39 @@ async function runSingleStep(
|
|
|
1153
1203
|
childWatchdog,
|
|
1154
1204
|
waitToolEnabled: step.waitToolEnabled,
|
|
1155
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;
|
|
1156
1239
|
const run = await runPiStreaming(
|
|
1157
1240
|
args,
|
|
1158
1241
|
step.cwd ?? ctx.cwd,
|
|
@@ -1172,6 +1255,16 @@ async function runSingleStep(
|
|
|
1172
1255
|
ctx.registerTurnBudgetAbort,
|
|
1173
1256
|
ctx.onWriterProcess,
|
|
1174
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
|
+
}
|
|
1175
1268
|
if (run.turnBudget) turnBudget = run.turnBudget;
|
|
1176
1269
|
else if (ctx.turnBudget) {
|
|
1177
1270
|
const assistantMessages = run.messages.filter((message) => message.role === "assistant");
|
|
@@ -1200,13 +1293,18 @@ async function runSingleStep(
|
|
|
1200
1293
|
const missingStructuredOutput = effectiveStructuredOutput
|
|
1201
1294
|
? !fs.existsSync(effectiveStructuredOutput.outputPath)
|
|
1202
1295
|
: false;
|
|
1203
|
-
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))
|
|
1204
1302
|
? "Subagent produced no output (possible model cold-start or empty response)."
|
|
1205
1303
|
: undefined;
|
|
1206
1304
|
let structuredOutput: unknown;
|
|
1207
1305
|
let structuredError: string | undefined;
|
|
1208
1306
|
if (effectiveStructuredOutput && run.exitCode === 0 && !run.error && !toolAvailabilityError && !hiddenError?.hasError && !emptyOutputError) {
|
|
1209
|
-
const structured = readStructuredOutput({
|
|
1307
|
+
const structured = await readStructuredOutput({
|
|
1210
1308
|
schema: effectiveStructuredOutput.schema,
|
|
1211
1309
|
schemaPath: effectiveStructuredOutput.schemaPath,
|
|
1212
1310
|
outputPath: effectiveStructuredOutput.outputPath,
|
|
@@ -1214,7 +1312,8 @@ async function runSingleStep(
|
|
|
1214
1312
|
if (structured.error) structuredError = structured.error;
|
|
1215
1313
|
else structuredOutput = structured.value;
|
|
1216
1314
|
}
|
|
1217
|
-
const
|
|
1315
|
+
const completionGuardEnabled = isAgentContractV1(step.agentContract) ? step.completionGuard === true : step.completionGuard !== false;
|
|
1316
|
+
const completionGuard = run.exitCode === 0 && !run.error && !toolAvailabilityError && !hiddenError?.hasError && !emptyOutputError && completionGuardEnabled
|
|
1218
1317
|
? evaluateCompletionMutationGuard({
|
|
1219
1318
|
agent: step.agent,
|
|
1220
1319
|
task: taskForCompletionGuard,
|
|
@@ -1224,26 +1323,33 @@ async function runSingleStep(
|
|
|
1224
1323
|
})
|
|
1225
1324
|
: undefined;
|
|
1226
1325
|
const completionGuardTriggered = completionGuard?.triggered === true && !run.observedMutationAttempt;
|
|
1227
|
-
const
|
|
1326
|
+
const fileMutationEffect = completionGuard
|
|
1327
|
+
? {
|
|
1328
|
+
status: completionGuard.expectedMutation ? completionGuardTriggered ? "missing" as const : "observed" as const : "not-applicable" as const,
|
|
1329
|
+
expected: completionGuard.expectedMutation,
|
|
1330
|
+
attempted: completionGuard.attemptedMutation || run.observedMutationAttempt === true,
|
|
1331
|
+
...(completionGuardTriggered ? { message: "Subagent completed without making edits for an implementation task." } : {}),
|
|
1332
|
+
}
|
|
1333
|
+
: undefined;
|
|
1334
|
+
const completionGuardError = completionGuardTriggered && !isAgentContractV1(step.agentContract)
|
|
1228
1335
|
? "Subagent completed without making edits for an implementation task.\nIt appears to have returned planning or scratchpad output instead of applying changes."
|
|
1229
1336
|
: undefined;
|
|
1230
|
-
const effectiveExitCode = toolAvailabilityError || completionGuardTriggered || structuredError
|
|
1337
|
+
const effectiveExitCode = toolAvailabilityError || (completionGuardTriggered && !isAgentContractV1(step.agentContract)) || structuredError || emptyOutputError
|
|
1231
1338
|
? 1
|
|
1232
1339
|
: hiddenError?.hasError
|
|
1233
1340
|
? (hiddenError.exitCode ?? 1)
|
|
1234
|
-
:
|
|
1341
|
+
: run.error && run.exitCode === 0
|
|
1235
1342
|
? 1
|
|
1236
|
-
: run.
|
|
1237
|
-
? 1
|
|
1238
|
-
: run.exitCode;
|
|
1343
|
+
: run.exitCode;
|
|
1239
1344
|
const error = toolAvailabilityError
|
|
1240
1345
|
?? completionGuardError
|
|
1241
1346
|
?? structuredError
|
|
1347
|
+
?? emptyOutputError
|
|
1242
1348
|
?? (hiddenError?.hasError
|
|
1243
1349
|
? hiddenError.details
|
|
1244
1350
|
? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}`
|
|
1245
1351
|
: `${hiddenError.errorType} failed with exit code ${effectiveExitCode}`
|
|
1246
|
-
:
|
|
1352
|
+
: run.error || (run.exitCode !== 0 && run.stderr.trim() ? run.stderr.trim() : undefined));
|
|
1247
1353
|
const attempt: ModelAttempt = {
|
|
1248
1354
|
model: candidate ?? run.model ?? step.model ?? "default",
|
|
1249
1355
|
success: effectiveExitCode === 0 && !error,
|
|
@@ -1261,7 +1367,7 @@ async function runSingleStep(
|
|
|
1261
1367
|
toolBudgetBlocked = Boolean(blockedMessage);
|
|
1262
1368
|
toolBudget = toolBudgetState(step.toolBudget, toolMessages.length, blockedMessage ? (blockedMessage as { toolName?: string }).toolName : undefined);
|
|
1263
1369
|
}
|
|
1264
|
-
finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput } as RunPiStreamingResult & { structuredOutput?: unknown };
|
|
1370
|
+
finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput, ...(step.agentContract ? { agentContract: step.agentContract } : {}), ...(fileMutationEffect ? { effects: { fileMutation: fileMutationEffect } } : {}) } as RunPiStreamingResult & { structuredOutput?: unknown; agentContract?: import("../../shared/types.ts").AgentContract; effects?: import("../../shared/types.ts").EffectsProjection };
|
|
1265
1371
|
if (run.turnBudgetExceeded) break;
|
|
1266
1372
|
if (run.stopped || run.timedOut || ctx.timeoutSignal?.aborted || ctx.stopSignal?.aborted || ctx.skipAcceptance?.()) break;
|
|
1267
1373
|
if (attempt.success || completionGuardTriggered) break;
|
|
@@ -1315,6 +1421,7 @@ async function runSingleStep(
|
|
|
1315
1421
|
cwd: step.cwd ?? ctx.cwd,
|
|
1316
1422
|
signal: combinedAbortSignal([ctx.timeoutSignal, ctx.stopSignal]),
|
|
1317
1423
|
abortMessage: ctx.stopSignal?.aborted ? ctx.stopMessage ?? "Subagent stopped by user." : ctx.timeoutMessage ?? "Subagent timed out.",
|
|
1424
|
+
reportOptional: isAgentContractV1(step.agentContract),
|
|
1318
1425
|
})
|
|
1319
1426
|
: undefined;
|
|
1320
1427
|
const stoppedAfterAcceptance = finalResult?.stopped === true || ctx.stopSignal?.aborted === true;
|
|
@@ -1330,7 +1437,7 @@ async function runSingleStep(
|
|
|
1330
1437
|
: acceptance
|
|
1331
1438
|
: undefined;
|
|
1332
1439
|
const acceptanceFailure = effectiveAcceptance ? acceptanceFailureMessage(effectiveAcceptance) : undefined;
|
|
1333
|
-
const acceptanceCanFailRun = acceptanceFailure && effectiveAcceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted && !timedOutAfterAcceptance && !stoppedAfterAcceptance && !turnBudgetExceeded;
|
|
1440
|
+
const acceptanceCanFailRun = acceptanceFailure && effectiveAcceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted && !timedOutAfterAcceptance && !stoppedAfterAcceptance && !turnBudgetExceeded && !isAgentContractV1(step.agentContract);
|
|
1334
1441
|
const effectiveFinalExitCode = timedOutAfterAcceptance || stoppedAfterAcceptance || turnBudgetExceeded ? 1 : acceptanceCanFailRun ? 1 : finalResult?.exitCode ?? 1;
|
|
1335
1442
|
const effectiveFinalError = stoppedAfterAcceptance
|
|
1336
1443
|
? ctx.stopMessage ?? "Subagent stopped by user."
|
|
@@ -1344,7 +1451,12 @@ async function runSingleStep(
|
|
|
1344
1451
|
|
|
1345
1452
|
if (artifactPaths && ctx.artifactConfig?.enabled !== false) {
|
|
1346
1453
|
if (ctx.artifactConfig?.includeOutput !== false) {
|
|
1347
|
-
fs.writeFileSync(artifactPaths.outputPath,
|
|
1454
|
+
fs.writeFileSync(artifactPaths.outputPath, formatOutputArtifactContent({
|
|
1455
|
+
output,
|
|
1456
|
+
error: effectiveFinalError,
|
|
1457
|
+
transcriptPath: transcriptWriter ? artifactPaths.transcriptPath : undefined,
|
|
1458
|
+
metadataPath: ctx.artifactConfig?.includeMetadata === false ? undefined : artifactPaths.metadataPath,
|
|
1459
|
+
}), "utf-8");
|
|
1348
1460
|
}
|
|
1349
1461
|
if (ctx.artifactConfig?.includeMetadata !== false) {
|
|
1350
1462
|
fs.writeFileSync(
|
|
@@ -1359,6 +1471,8 @@ async function runSingleStep(
|
|
|
1359
1471
|
modelAttempts,
|
|
1360
1472
|
error: effectiveFinalError,
|
|
1361
1473
|
acceptance: effectiveAcceptance,
|
|
1474
|
+
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
1475
|
+
launchContractDigest: actualLaunchContractDigest,
|
|
1362
1476
|
...(transcriptWriter ? { transcriptPath: artifactPaths.transcriptPath } : {}),
|
|
1363
1477
|
transcriptError: transcriptWriter?.getError(),
|
|
1364
1478
|
skills: step.skills,
|
|
@@ -1369,8 +1483,11 @@ async function runSingleStep(
|
|
|
1369
1483
|
}
|
|
1370
1484
|
}
|
|
1371
1485
|
|
|
1372
|
-
|
|
1486
|
+
const result = {
|
|
1373
1487
|
agent: step.agent,
|
|
1488
|
+
context: step.context,
|
|
1489
|
+
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
1490
|
+
launchContractDigest: actualLaunchContractDigest,
|
|
1374
1491
|
output: outputForSummary,
|
|
1375
1492
|
exitCode: effectiveFinalExitCode,
|
|
1376
1493
|
error: effectiveFinalError,
|
|
@@ -1393,18 +1510,36 @@ async function runSingleStep(
|
|
|
1393
1510
|
toolBudget,
|
|
1394
1511
|
toolBudgetBlocked: toolBudgetBlocked || undefined,
|
|
1395
1512
|
completionGuardTriggered: completionGuardTriggeredFinal,
|
|
1513
|
+
...((finalResult as (RunPiStreamingResult & { effects?: import("../../shared/types.ts").EffectsProjection }) | undefined)?.effects ? { effects: (finalResult as RunPiStreamingResult & { effects?: import("../../shared/types.ts").EffectsProjection }).effects } : {}),
|
|
1396
1514
|
structuredOutput: timedOutAfterAcceptance || stoppedAfterAcceptance || turnBudgetExceeded ? undefined : (finalResult as (RunPiStreamingResult & { structuredOutput?: unknown }) | undefined)?.structuredOutput,
|
|
1397
1515
|
structuredOutputPath: timedOutAfterAcceptance || stoppedAfterAcceptance || turnBudgetExceeded ? undefined : effectiveStructuredOutput?.outputPath,
|
|
1398
1516
|
structuredOutputSchemaPath: timedOutAfterAcceptance || stoppedAfterAcceptance || turnBudgetExceeded ? undefined : effectiveStructuredOutput?.schemaPath,
|
|
1399
1517
|
acceptance: effectiveAcceptance,
|
|
1400
1518
|
watchdog: finalResult?.watchdog,
|
|
1519
|
+
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
1520
|
+
writerProcesses,
|
|
1521
|
+
writerAttemptCount,
|
|
1401
1522
|
};
|
|
1523
|
+
return isAgentContractV1(step.agentContract) ? attachContractProjections(result as unknown as import("../../shared/types.ts").SingleResult) as unknown as typeof result : result;
|
|
1402
1524
|
}
|
|
1403
1525
|
|
|
1404
1526
|
type RunnerStatusStep = NonNullable<AsyncStatus["steps"]>[number] & {
|
|
1405
1527
|
exitCode?: number | null;
|
|
1406
1528
|
};
|
|
1407
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
|
+
|
|
1408
1543
|
type RunnerStatusPayload = Omit<AsyncStatus, "steps" | "parallelGroups" | "pid" | "cwd" | "currentStep" | "chainStepCount" | "lastUpdate"> & {
|
|
1409
1544
|
pid: number;
|
|
1410
1545
|
cwd: string;
|
|
@@ -1440,7 +1575,7 @@ function markParallelGroupSetupFailure(input: {
|
|
|
1440
1575
|
input.statusPayload.steps[flatTaskIndex].endedAt = input.failedAt;
|
|
1441
1576
|
input.statusPayload.steps[flatTaskIndex].durationMs = 0;
|
|
1442
1577
|
input.statusPayload.steps[flatTaskIndex].exitCode = 1;
|
|
1443
|
-
input.results.push({ agent: input.group.parallel[taskIndex].agent, output: input.setupError, success: false, exitCode: 1, sessionFile: input.group.parallel[taskIndex].sessionFile });
|
|
1578
|
+
input.results.push({ agent: input.group.parallel[taskIndex].agent, context: input.group.parallel[taskIndex].context, output: input.setupError, success: false, exitCode: 1, sessionFile: input.group.parallel[taskIndex].sessionFile });
|
|
1444
1579
|
}
|
|
1445
1580
|
input.statusPayload.currentStep = input.groupStartFlatIndex;
|
|
1446
1581
|
input.statusPayload.lastUpdate = input.failedAt;
|
|
@@ -1505,19 +1640,15 @@ function prepareParallelTaskRun(
|
|
|
1505
1640
|
};
|
|
1506
1641
|
}
|
|
1507
1642
|
|
|
1508
|
-
function
|
|
1509
|
-
|
|
1510
|
-
worktreeSetup: WorktreeSetup | undefined,
|
|
1643
|
+
function captureParallelWorktreeDiffs(
|
|
1644
|
+
worktreeSetup: WorktreeSetup,
|
|
1511
1645
|
asyncDir: string,
|
|
1512
1646
|
stepIndex: number,
|
|
1513
1647
|
group: Extract<RunnerStep, { parallel: SubagentStep[] }>,
|
|
1514
|
-
): string {
|
|
1515
|
-
if (!worktreeSetup) return previousOutput;
|
|
1648
|
+
): { diffs: ReturnType<typeof diffWorktrees>; summary: string } {
|
|
1516
1649
|
const diffsDir = path.join(asyncDir, "worktree-diffs", `step-${stepIndex}`);
|
|
1517
1650
|
const diffs = diffWorktrees(worktreeSetup, group.parallel.map((task) => task.agent), diffsDir);
|
|
1518
|
-
|
|
1519
|
-
if (!diffSummary) return previousOutput;
|
|
1520
|
-
return `${previousOutput}\n\n${diffSummary}`;
|
|
1651
|
+
return { diffs, summary: formatWorktreeDiffSummary(diffs) };
|
|
1521
1652
|
}
|
|
1522
1653
|
|
|
1523
1654
|
function ensureParallelProgressFile(cwd: string, group: Extract<RunnerStep, { parallel: SubagentStep[] }>): void {
|
|
@@ -1612,10 +1743,14 @@ async function runSubagent(
|
|
|
1612
1743
|
const transcriptPath = resolveAsyncStepTranscriptPath({ artifactsDir, artifactConfig, runId: id, agent: task.agent, flatIndex: taskFlatIndex, flatStepCount: initialFlatStepCount });
|
|
1613
1744
|
initialStatusSteps.push({
|
|
1614
1745
|
agent: task.agent,
|
|
1746
|
+
...(task.context ? { context: task.context } : {}),
|
|
1615
1747
|
phase: task.phase,
|
|
1616
1748
|
label: task.label,
|
|
1617
1749
|
outputName: task.outputName,
|
|
1618
1750
|
structured: task.structured,
|
|
1751
|
+
...(task.agentContract ? { agentContract: task.agentContract } : {}),
|
|
1752
|
+
...(task.launchContractDigest ? { launchContractDigest: task.launchContractDigest } : {}),
|
|
1753
|
+
...(task.capabilityCeiling ? { capabilityCeiling: task.capabilityCeiling } : {}),
|
|
1619
1754
|
status: "pending",
|
|
1620
1755
|
...(task.toolBudget ? { toolBudget: initialToolBudgetState(task.toolBudget) } : {}),
|
|
1621
1756
|
...(task.sessionFile ? { sessionFile: task.sessionFile } : {}),
|
|
@@ -1633,10 +1768,13 @@ async function runSubagent(
|
|
|
1633
1768
|
parallelGroups.push({ start: flatStepCount, count: 1, stepIndex });
|
|
1634
1769
|
initialStatusSteps.push({
|
|
1635
1770
|
agent: `expand:${step.parallel.agent}`,
|
|
1771
|
+
...(step.parallel.context ? { context: step.parallel.context } : {}),
|
|
1636
1772
|
phase: step.phase ?? step.parallel.phase,
|
|
1637
1773
|
label: step.label ?? step.parallel.label ?? `Dynamic fanout (${step.collect.as})`,
|
|
1638
1774
|
outputName: step.collect.as,
|
|
1639
1775
|
structured: Boolean(step.collect.outputSchema),
|
|
1776
|
+
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
1777
|
+
...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
|
|
1640
1778
|
status: "pending",
|
|
1641
1779
|
...(step.parallel.toolBudget ? { toolBudget: initialToolBudgetState(step.parallel.toolBudget) } : {}),
|
|
1642
1780
|
recentTools: [],
|
|
@@ -1648,10 +1786,14 @@ async function runSubagent(
|
|
|
1648
1786
|
const transcriptPath = resolveAsyncStepTranscriptPath({ artifactsDir, artifactConfig, runId: id, agent: step.agent, flatIndex: stepFlatIndex, flatStepCount: initialFlatStepCount });
|
|
1649
1787
|
initialStatusSteps.push({
|
|
1650
1788
|
agent: step.agent,
|
|
1789
|
+
...(step.context ? { context: step.context } : {}),
|
|
1651
1790
|
phase: step.phase,
|
|
1652
1791
|
label: step.label,
|
|
1653
1792
|
outputName: step.outputName,
|
|
1654
1793
|
structured: step.structured,
|
|
1794
|
+
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
1795
|
+
...(step.launchContractDigest ? { launchContractDigest: step.launchContractDigest } : {}),
|
|
1796
|
+
...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
|
|
1655
1797
|
status: "pending",
|
|
1656
1798
|
...(step.toolBudget ? { toolBudget: initialToolBudgetState(step.toolBudget) } : {}),
|
|
1657
1799
|
...(step.sessionFile ? { sessionFile: step.sessionFile } : {}),
|
|
@@ -1669,6 +1811,11 @@ async function runSubagent(
|
|
|
1669
1811
|
const sessionEnabled = Boolean(config.sessionDir)
|
|
1670
1812
|
|| shareEnabled
|
|
1671
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
|
+
}
|
|
1672
1819
|
const statusPayload: RunnerStatusPayload = {
|
|
1673
1820
|
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
1674
1821
|
runId: id,
|
|
@@ -1690,6 +1837,9 @@ async function runSubagent(
|
|
|
1690
1837
|
chainStepCount: steps.length,
|
|
1691
1838
|
parallelGroups,
|
|
1692
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 } } : {}),
|
|
1693
1843
|
steps: initialStatusSteps,
|
|
1694
1844
|
artifactsDir,
|
|
1695
1845
|
sessionDir: config.sessionDir,
|
|
@@ -1896,21 +2046,24 @@ async function runSubagent(
|
|
|
1896
2046
|
}
|
|
1897
2047
|
}
|
|
1898
2048
|
};
|
|
1899
|
-
const pausedStepResult = (agent: string): SingleStepResult => ({
|
|
2049
|
+
const pausedStepResult = (agent: string, context?: "fresh" | "fork"): SingleStepResult => ({
|
|
1900
2050
|
agent,
|
|
2051
|
+
context,
|
|
1901
2052
|
output: "Paused after interrupt. Waiting for explicit next action.",
|
|
1902
2053
|
exitCode: 0,
|
|
1903
2054
|
interrupted: true,
|
|
1904
2055
|
});
|
|
1905
|
-
const timedOutStepResult = (agent: string): SingleStepResult => ({
|
|
2056
|
+
const timedOutStepResult = (agent: string, context?: "fresh" | "fork"): SingleStepResult => ({
|
|
1906
2057
|
agent,
|
|
2058
|
+
context,
|
|
1907
2059
|
output: timeoutMessage ?? "Subagent timed out.",
|
|
1908
2060
|
error: timeoutMessage ?? "Subagent timed out.",
|
|
1909
2061
|
exitCode: 1,
|
|
1910
2062
|
timedOut: true,
|
|
1911
2063
|
});
|
|
1912
|
-
const stoppedStepResult = (agent: string): SingleStepResult => ({
|
|
2064
|
+
const stoppedStepResult = (agent: string, context?: "fresh" | "fork"): SingleStepResult => ({
|
|
1913
2065
|
agent,
|
|
2066
|
+
context,
|
|
1914
2067
|
output: stopMessage,
|
|
1915
2068
|
error: stopMessage,
|
|
1916
2069
|
exitCode: 1,
|
|
@@ -1980,6 +2133,7 @@ async function runSubagent(
|
|
|
1980
2133
|
const activeLongRunningSteps = new Set<number>();
|
|
1981
2134
|
const mutatingFailureStates = initialStatusSteps.map(() => createMutatingFailureState());
|
|
1982
2135
|
const pendingToolResults: Array<{ tool: string; path?: string; mutates: boolean; startedAt?: number } | undefined> = initialStatusSteps.map(() => undefined);
|
|
2136
|
+
const supervisorAttentionSteps = new Map<number, ActivityState | undefined>();
|
|
1983
2137
|
const mutatingFailureWindowMs = 5 * 60_000;
|
|
1984
2138
|
const appendControlEvent = (event: ReturnType<typeof buildControlEvent>) => {
|
|
1985
2139
|
if (!controlConfig.enabled) return;
|
|
@@ -2010,6 +2164,15 @@ async function runSubagent(
|
|
|
2010
2164
|
statusPayload.currentToolStartedAt = activeStep?.currentToolStartedAt;
|
|
2011
2165
|
statusPayload.currentPath = activeStep?.currentPath;
|
|
2012
2166
|
};
|
|
2167
|
+
const syncAggregateActivityState = (): void => {
|
|
2168
|
+
const nextRunState = statusPayload.steps.some((step) => step.activityState === "needs_attention")
|
|
2169
|
+
? "needs_attention"
|
|
2170
|
+
: statusPayload.steps.some((step) => step.activityState === "active_long_running")
|
|
2171
|
+
? "active_long_running"
|
|
2172
|
+
: undefined;
|
|
2173
|
+
currentActivityState = nextRunState;
|
|
2174
|
+
statusPayload.activityState = nextRunState;
|
|
2175
|
+
};
|
|
2013
2176
|
const maybeEmitActiveLongRunning = (flatIndex: number, now: number): boolean => {
|
|
2014
2177
|
if (!controlConfig.enabled || activeLongRunningSteps.has(flatIndex)) return false;
|
|
2015
2178
|
const step = statusPayload.steps[flatIndex];
|
|
@@ -2280,15 +2443,45 @@ async function runSubagent(
|
|
|
2280
2443
|
pendingToolResults[flatIndex] = { tool: event.toolName, path: currentPath, mutates, startedAt: now };
|
|
2281
2444
|
statusPayload.toolCount = (statusPayload.toolCount ?? 0) + 1;
|
|
2282
2445
|
syncTopLevelCurrentTool();
|
|
2446
|
+
if (controlConfig.enabled && isBlockingSupervisorTool(event.toolName, event.args) && step.activityState !== "needs_attention") {
|
|
2447
|
+
const previous = step.activityState;
|
|
2448
|
+
step.activityState = "needs_attention";
|
|
2449
|
+
supervisorAttentionSteps.set(flatIndex, previous);
|
|
2450
|
+
currentActivityState = "needs_attention";
|
|
2451
|
+
statusPayload.activityState = "needs_attention";
|
|
2452
|
+
appendControlEvent(buildControlEvent({
|
|
2453
|
+
type: "needs_attention",
|
|
2454
|
+
from: previous,
|
|
2455
|
+
to: "needs_attention",
|
|
2456
|
+
runId: id,
|
|
2457
|
+
agent: step.agent,
|
|
2458
|
+
index: flatIndex,
|
|
2459
|
+
ts: now,
|
|
2460
|
+
message: `${step.agent} is waiting for a supervisor reply`,
|
|
2461
|
+
reason: "supervisor_request",
|
|
2462
|
+
turns: step.turnCount,
|
|
2463
|
+
tokens: step.tokens?.total,
|
|
2464
|
+
toolCount: step.toolCount,
|
|
2465
|
+
currentTool: step.currentTool,
|
|
2466
|
+
currentToolDurationMs: 0,
|
|
2467
|
+
currentPath: step.currentPath,
|
|
2468
|
+
}));
|
|
2469
|
+
}
|
|
2283
2470
|
} else if (event.type === "tool_execution_end") {
|
|
2284
2471
|
if (step.currentTool) {
|
|
2285
2472
|
step.recentTools ??= [];
|
|
2286
2473
|
step.recentTools.push({ tool: step.currentTool, args: step.currentToolArgs || "", endMs: now });
|
|
2287
2474
|
}
|
|
2475
|
+
const supervisorPreviousActivity = supervisorAttentionSteps.get(flatIndex);
|
|
2476
|
+
const clearedSupervisorAttention = supervisorAttentionSteps.delete(flatIndex);
|
|
2288
2477
|
step.currentTool = undefined;
|
|
2289
2478
|
step.currentToolArgs = undefined;
|
|
2290
2479
|
step.currentToolStartedAt = undefined;
|
|
2291
2480
|
step.currentPath = undefined;
|
|
2481
|
+
if (clearedSupervisorAttention && step.activityState === "needs_attention") {
|
|
2482
|
+
step.activityState = supervisorPreviousActivity;
|
|
2483
|
+
syncAggregateActivityState();
|
|
2484
|
+
}
|
|
2292
2485
|
syncTopLevelCurrentTool();
|
|
2293
2486
|
} else if (event.type === "tool_result_end" && event.message) {
|
|
2294
2487
|
const toolSnapshot = pendingToolResults[flatIndex];
|
|
@@ -2602,7 +2795,7 @@ async function runSubagent(
|
|
|
2602
2795
|
if (materialized.parallel.length > 1 && step.parallel.outputPath && !step.parallel.namespaceOutputPath) {
|
|
2603
2796
|
throw new DynamicFanoutError(`Dynamic chain step ${stepIndex + 1} materialized ${materialized.parallel.length} items that resolve output to the same path: ${step.parallel.outputPath}. Remove the explicit output path or use an inherited relative agent output so each item can be isolated.`);
|
|
2604
2797
|
}
|
|
2605
|
-
if (materialized.collectedOnEmpty) validateDynamicCollection(step.collect.outputSchema, materialized.collectedOnEmpty);
|
|
2798
|
+
if (materialized.collectedOnEmpty) await validateDynamicCollection(step.collect.outputSchema, materialized.collectedOnEmpty);
|
|
2606
2799
|
} catch (error) {
|
|
2607
2800
|
const now = Date.now();
|
|
2608
2801
|
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
@@ -2621,7 +2814,7 @@ async function runSubagent(
|
|
|
2621
2814
|
statusPayload.lastUpdate = now;
|
|
2622
2815
|
markDynamicGraphGroup(stepIndex, "failed", message);
|
|
2623
2816
|
writeStatusPayload();
|
|
2624
|
-
results.push({ agent: step.parallel.agent, output: message, error: message, success: false, exitCode: 1 });
|
|
2817
|
+
results.push({ agent: step.parallel.agent, context: step.parallel.context, output: message, error: message, success: false, exitCode: 1 });
|
|
2625
2818
|
break;
|
|
2626
2819
|
}
|
|
2627
2820
|
|
|
@@ -2633,6 +2826,7 @@ async function runSubagent(
|
|
|
2633
2826
|
mode: config.mode,
|
|
2634
2827
|
async: true,
|
|
2635
2828
|
dynamicGroup: true,
|
|
2829
|
+
agentContract: step.agentContract,
|
|
2636
2830
|
});
|
|
2637
2831
|
|
|
2638
2832
|
if (materialized.parallel.length === 0) {
|
|
@@ -2664,13 +2858,14 @@ async function runSubagent(
|
|
|
2664
2858
|
cwd,
|
|
2665
2859
|
signal: combinedAbortSignal([timeoutAbortController.signal, stopAbortController.signal]),
|
|
2666
2860
|
abortMessage: stopAbortController.signal.aborted ? stopMessage : timeoutMessage ?? "Subagent timed out.",
|
|
2861
|
+
reportOptional: isAgentContractV1(step.agentContract),
|
|
2667
2862
|
})
|
|
2668
2863
|
: undefined;
|
|
2669
2864
|
const groupStopped = stopped || stopAbortController.signal.aborted;
|
|
2670
2865
|
const groupTimedOut = !groupStopped && (timedOut || timeoutAbortController.signal.aborted);
|
|
2671
2866
|
const effectiveGroupAcceptance = groupTimedOut || groupStopped ? undefined : groupAcceptance;
|
|
2672
2867
|
if (placeholder && effectiveGroupAcceptance) placeholder.acceptance = effectiveGroupAcceptance;
|
|
2673
|
-
const groupAcceptanceFailure = effectiveGroupAcceptance ? acceptanceFailureMessage(effectiveGroupAcceptance) : undefined;
|
|
2868
|
+
const groupAcceptanceFailure = effectiveGroupAcceptance && (!isAgentContractV1(step.agentContract) || step.gateOn === "acceptance") ? acceptanceFailureMessage(effectiveGroupAcceptance) : undefined;
|
|
2674
2869
|
if (groupTimedOut || groupStopped || groupAcceptanceFailure) {
|
|
2675
2870
|
const errorMessage = groupStopped ? stopMessage : groupTimedOut ? timeoutMessage ?? "Subagent timed out." : groupAcceptanceFailure!;
|
|
2676
2871
|
statusPayload.state = groupStopped ? "stopped" : "failed";
|
|
@@ -2686,7 +2881,7 @@ async function runSubagent(
|
|
|
2686
2881
|
markDynamicGraphGroup(stepIndex, groupStopped ? "stopped" : "failed", errorMessage, effectiveGroupAcceptance);
|
|
2687
2882
|
statusPayload.lastUpdate = Date.now();
|
|
2688
2883
|
writeStatusPayload();
|
|
2689
|
-
results.push({ agent: step.parallel.agent, output: errorMessage, error: errorMessage, success: false, exitCode: 1, timedOut: groupTimedOut ? true : undefined, stopped: groupStopped ? true : undefined, acceptance: effectiveGroupAcceptance });
|
|
2884
|
+
results.push({ agent: step.parallel.agent, context: step.parallel.context, output: errorMessage, error: errorMessage, success: false, exitCode: 1, timedOut: groupTimedOut ? true : undefined, stopped: groupStopped ? true : undefined, acceptance: effectiveGroupAcceptance });
|
|
2690
2885
|
break;
|
|
2691
2886
|
}
|
|
2692
2887
|
flatIndex++;
|
|
@@ -2716,6 +2911,7 @@ async function runSubagent(
|
|
|
2716
2911
|
mode: config.mode,
|
|
2717
2912
|
async: true,
|
|
2718
2913
|
dynamic: step.parallel.acceptanceInput === undefined,
|
|
2914
|
+
agentContract: step.parallel.agentContract ?? step.agentContract,
|
|
2719
2915
|
}),
|
|
2720
2916
|
systemPrompt: step.parallel.namespaceOutputPath ? injectOutputPathSystemPrompt(step.parallel.systemPrompt ?? "", outputPath, step.parallel) : step.parallel.systemPrompt,
|
|
2721
2917
|
outputPath,
|
|
@@ -2735,10 +2931,13 @@ async function runSubagent(
|
|
|
2735
2931
|
const transcriptPath = resolveAsyncStepTranscriptPath({ artifactsDir, artifactConfig, runId: id, agent: task.agent, flatIndex: groupStartFlatIndex + itemIndex, flatStepCount: dynamicFlatStepCount });
|
|
2736
2932
|
return {
|
|
2737
2933
|
agent: task.agent,
|
|
2934
|
+
...(task.context ? { context: task.context } : {}),
|
|
2738
2935
|
phase: task.phase ?? step.phase,
|
|
2739
2936
|
label: task.label,
|
|
2740
2937
|
outputName: undefined,
|
|
2741
2938
|
structured: Boolean(task.structuredOutputSchema),
|
|
2939
|
+
...(task.agentContract ? { agentContract: task.agentContract } : {}),
|
|
2940
|
+
...(task.capabilityCeiling ? { capabilityCeiling: task.capabilityCeiling } : {}),
|
|
2742
2941
|
status: "pending",
|
|
2743
2942
|
...(task.sessionFile ? { sessionFile: task.sessionFile } : {}),
|
|
2744
2943
|
...(transcriptPath ? { transcriptPath } : {}),
|
|
@@ -2798,9 +2997,9 @@ async function runSubagent(
|
|
|
2798
2997
|
let aborted = false;
|
|
2799
2998
|
const parallelResults = await mapConcurrent(dynamicSteps, concurrency, async (task, taskIdx) => {
|
|
2800
2999
|
const fi = groupStartFlatIndex + taskIdx;
|
|
2801
|
-
if (timedOut) return timedOutStepResult(task.agent);
|
|
2802
|
-
if (stopped) return stoppedStepResult(task.agent);
|
|
2803
|
-
if (interrupted) return pausedStepResult(task.agent);
|
|
3000
|
+
if (timedOut) return timedOutStepResult(task.agent, task.context);
|
|
3001
|
+
if (stopped) return stoppedStepResult(task.agent, task.context);
|
|
3002
|
+
if (interrupted) return pausedStepResult(task.agent, task.context);
|
|
2804
3003
|
if (aborted && failFast) {
|
|
2805
3004
|
const skippedAt = Date.now();
|
|
2806
3005
|
statusPayload.steps[fi].status = "failed";
|
|
@@ -2811,7 +3010,7 @@ async function runSubagent(
|
|
|
2811
3010
|
statusPayload.steps[fi].exitCode = -1;
|
|
2812
3011
|
statusPayload.lastUpdate = skippedAt;
|
|
2813
3012
|
writeStatusPayload();
|
|
2814
|
-
return { agent: task.agent, output: "(skipped — fail-fast)", exitCode: -1 as number | null, skipped: true };
|
|
3013
|
+
return { agent: task.agent, context: task.context, output: "(skipped — fail-fast)", exitCode: -1 as number | null, skipped: true };
|
|
2815
3014
|
}
|
|
2816
3015
|
const taskStartTime = Date.now();
|
|
2817
3016
|
statusPayload.currentStep = fi;
|
|
@@ -2842,6 +3041,7 @@ async function runSubagent(
|
|
|
2842
3041
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
2843
3042
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
2844
3043
|
nestedRoute: config.nestedRoute,
|
|
3044
|
+
capabilityCeiling: config.capabilityCeiling,
|
|
2845
3045
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
2846
3046
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
2847
3047
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
@@ -2883,13 +3083,23 @@ async function runSubagent(
|
|
|
2883
3083
|
statusPayload.steps[fi].error = stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.error;
|
|
2884
3084
|
statusPayload.steps[fi].transcriptPath = singleResult.transcriptPath ?? statusPayload.steps[fi].transcriptPath;
|
|
2885
3085
|
statusPayload.steps[fi].transcriptError = singleResult.transcriptError;
|
|
3086
|
+
statusPayload.steps[fi].agentContract = singleResult.agentContract;
|
|
3087
|
+
statusPayload.steps[fi].launchContractDigest = singleResult.launchContractDigest;
|
|
3088
|
+
statusPayload.steps[fi].effects = singleResult.effects;
|
|
3089
|
+
statusPayload.steps[fi].execution = singleResult.execution;
|
|
3090
|
+
statusPayload.steps[fi].review = singleResult.review;
|
|
2886
3091
|
statusPayload.steps[fi].structuredOutput = singleResult.structuredOutput;
|
|
2887
3092
|
statusPayload.steps[fi].structuredOutputPath = singleResult.structuredOutputPath;
|
|
2888
3093
|
statusPayload.steps[fi].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
2889
3094
|
statusPayload.steps[fi].acceptance = singleResult.acceptance;
|
|
2890
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;
|
|
2891
3100
|
statusPayload.lastUpdate = taskEndTime;
|
|
2892
3101
|
writeStatusPayload();
|
|
3102
|
+
appendCapabilityCeilingAppliedEvent(eventsPath, id, fi, task.agent, singleResult);
|
|
2893
3103
|
appendJsonl(eventsPath, JSON.stringify({
|
|
2894
3104
|
type: stopped || childStopped ? "subagent.step.stopped" : timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
2895
3105
|
ts: taskEndTime, runId: id, stepIndex: fi, agent: task.agent,
|
|
@@ -2903,6 +3113,9 @@ async function runSubagent(
|
|
|
2903
3113
|
for (const pr of parallelResults) {
|
|
2904
3114
|
results.push({
|
|
2905
3115
|
agent: pr.agent,
|
|
3116
|
+
context: pr.context,
|
|
3117
|
+
agentContract: pr.agentContract,
|
|
3118
|
+
launchContractDigest: pr.launchContractDigest,
|
|
2906
3119
|
output: pr.output,
|
|
2907
3120
|
error: pr.error,
|
|
2908
3121
|
protocolError: pr.protocolError,
|
|
@@ -2926,18 +3139,34 @@ async function runSubagent(
|
|
|
2926
3139
|
artifactPaths: pr.artifactPaths,
|
|
2927
3140
|
transcriptPath: pr.transcriptPath,
|
|
2928
3141
|
transcriptError: pr.transcriptError,
|
|
3142
|
+
effects: pr.effects,
|
|
3143
|
+
execution: pr.execution,
|
|
3144
|
+
review: pr.review,
|
|
2929
3145
|
structuredOutput: pr.structuredOutput,
|
|
2930
3146
|
structuredOutputPath: pr.structuredOutputPath,
|
|
2931
3147
|
structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
|
|
2932
3148
|
acceptance: pr.acceptance,
|
|
2933
3149
|
watchdog: pr.watchdog,
|
|
3150
|
+
capabilityCeiling: pr.capabilityCeiling,
|
|
3151
|
+
capabilityAudit: pr.capabilityAudit,
|
|
2934
3152
|
});
|
|
2935
3153
|
}
|
|
2936
3154
|
const collection = collectDynamicResults(step as Parameters<typeof collectDynamicResults>[0], materialized.items, parallelResults);
|
|
2937
3155
|
const failures = parallelResults.filter((result) => result.exitCode !== 0 && result.exitCode !== -1);
|
|
2938
|
-
|
|
3156
|
+
const acceptanceFailures = parallelResults
|
|
3157
|
+
.map((result, originalIndex) => ({ result, originalIndex, task: dynamicSteps[originalIndex] }))
|
|
3158
|
+
.filter(({ result, task }) => isAgentContractV1(task?.agentContract ?? step.agentContract) && task?.gateOn === "acceptance" && result.acceptance?.status === "rejected");
|
|
3159
|
+
if (acceptanceFailures.length > 0) {
|
|
3160
|
+
const message = acceptanceFailures
|
|
3161
|
+
.map(({ result, originalIndex }) => `Dynamic item ${originalIndex + 1} (${result.agent}, key ${materialized.items[originalIndex]?.key ?? originalIndex}) acceptance rejected: ${acceptanceFailureMessage(result.acceptance) ?? "acceptance rejected"}`)
|
|
3162
|
+
.join("\n");
|
|
3163
|
+
results.push({ agent: step.parallel.agent, context: step.parallel.context, output: message, error: message, success: false, exitCode: 1, structuredOutput: collection });
|
|
3164
|
+
statusPayload.error = message;
|
|
3165
|
+
markDynamicGraphGroup(stepIndex, "failed", message);
|
|
3166
|
+
}
|
|
3167
|
+
if (failures.length === 0 && acceptanceFailures.length === 0) {
|
|
2939
3168
|
try {
|
|
2940
|
-
validateDynamicCollection(step.collect.outputSchema, collection);
|
|
3169
|
+
await validateDynamicCollection(step.collect.outputSchema, collection);
|
|
2941
3170
|
outputs[step.collect.as] = {
|
|
2942
3171
|
text: JSON.stringify(collection),
|
|
2943
3172
|
structured: collection,
|
|
@@ -2956,12 +3185,13 @@ async function runSubagent(
|
|
|
2956
3185
|
cwd,
|
|
2957
3186
|
signal: combinedAbortSignal([timeoutAbortController.signal, stopAbortController.signal]),
|
|
2958
3187
|
abortMessage: stopAbortController.signal.aborted ? stopMessage : timeoutMessage ?? "Subagent timed out.",
|
|
3188
|
+
reportOptional: isAgentContractV1(step.agentContract),
|
|
2959
3189
|
})
|
|
2960
3190
|
: undefined;
|
|
2961
3191
|
const groupStopped = stopped || stopAbortController.signal.aborted;
|
|
2962
3192
|
const groupTimedOut = !groupStopped && (timedOut || timeoutAbortController.signal.aborted);
|
|
2963
3193
|
const effectiveGroupAcceptance = groupTimedOut || groupStopped ? undefined : groupAcceptance;
|
|
2964
|
-
const groupAcceptanceFailure = effectiveDynamicGroupAcceptance.explicit && effectiveGroupAcceptance ? acceptanceFailureMessage(effectiveGroupAcceptance) : undefined;
|
|
3194
|
+
const groupAcceptanceFailure = effectiveDynamicGroupAcceptance.explicit && effectiveGroupAcceptance && (!isAgentContractV1(step.agentContract) || step.gateOn === "acceptance") ? acceptanceFailureMessage(effectiveGroupAcceptance) : undefined;
|
|
2965
3195
|
const groupError = groupStopped ? stopMessage : groupTimedOut ? timeoutMessage ?? "Subagent timed out." : groupAcceptanceFailure;
|
|
2966
3196
|
markDynamicGraphGroup(stepIndex, groupError ? groupStopped ? "stopped" : "failed" : "completed", groupError, effectiveGroupAcceptance);
|
|
2967
3197
|
if (groupError) {
|
|
@@ -2981,7 +3211,7 @@ async function runSubagent(
|
|
|
2981
3211
|
}
|
|
2982
3212
|
} catch (error) {
|
|
2983
3213
|
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
2984
|
-
results.push({ agent: step.parallel.agent, output: message, error: message, success: false, exitCode: 1, structuredOutput: collection });
|
|
3214
|
+
results.push({ agent: step.parallel.agent, context: step.parallel.context, output: message, error: message, success: false, exitCode: 1, structuredOutput: collection });
|
|
2985
3215
|
statusPayload.error = message;
|
|
2986
3216
|
markDynamicGraphGroup(stepIndex, "failed", message);
|
|
2987
3217
|
}
|
|
@@ -3001,7 +3231,7 @@ async function runSubagent(
|
|
|
3001
3231
|
ts: Date.now(),
|
|
3002
3232
|
runId: id,
|
|
3003
3233
|
stepIndex,
|
|
3004
|
-
success: failures.length === 0,
|
|
3234
|
+
success: failures.length === 0 && acceptanceFailures.length === 0,
|
|
3005
3235
|
}));
|
|
3006
3236
|
if (failures.length > 0) markDynamicGraphGroup(stepIndex, "failed", failures[0]?.error ?? "Dynamic fanout child failed.");
|
|
3007
3237
|
statusPayload.lastUpdate = Date.now();
|
|
@@ -3017,6 +3247,7 @@ async function runSubagent(
|
|
|
3017
3247
|
const groupStartFlatIndex = flatIndex;
|
|
3018
3248
|
let aborted = false;
|
|
3019
3249
|
let worktreeSetup: WorktreeSetup | undefined;
|
|
3250
|
+
let worktreeFinalized = false;
|
|
3020
3251
|
if (group.worktree) {
|
|
3021
3252
|
const worktreeTaskCwdConflict = findWorktreeTaskCwdConflict(group.parallel, cwd);
|
|
3022
3253
|
if (worktreeTaskCwdConflict) {
|
|
@@ -3085,9 +3316,9 @@ async function runSubagent(
|
|
|
3085
3316
|
concurrency,
|
|
3086
3317
|
async (task, taskIdx) => {
|
|
3087
3318
|
const fi = groupStartFlatIndex + taskIdx;
|
|
3088
|
-
if (timedOut) return timedOutStepResult(task.agent);
|
|
3089
|
-
if (stopped) return stoppedStepResult(task.agent);
|
|
3090
|
-
if (interrupted) return pausedStepResult(task.agent);
|
|
3319
|
+
if (timedOut) return timedOutStepResult(task.agent, task.context);
|
|
3320
|
+
if (stopped) return stoppedStepResult(task.agent, task.context);
|
|
3321
|
+
if (interrupted) return pausedStepResult(task.agent, task.context);
|
|
3091
3322
|
if (aborted && failFast) {
|
|
3092
3323
|
const skippedAt = Date.now();
|
|
3093
3324
|
statusPayload.steps[fi].status = "failed";
|
|
@@ -3102,7 +3333,7 @@ async function runSubagent(
|
|
|
3102
3333
|
appendJsonl(eventsPath, JSON.stringify({
|
|
3103
3334
|
type: "subagent.step.failed", ts: skippedAt, runId: id, stepIndex: fi, agent: task.agent, exitCode: -1, durationMs: 0,
|
|
3104
3335
|
}));
|
|
3105
|
-
return { agent: task.agent, output: "(skipped — fail-fast)", exitCode: -1 as number | null, skipped: true };
|
|
3336
|
+
return { agent: task.agent, context: task.context, output: "(skipped — fail-fast)", exitCode: -1 as number | null, skipped: true };
|
|
3106
3337
|
}
|
|
3107
3338
|
|
|
3108
3339
|
const taskStartTime = Date.now();
|
|
@@ -3145,6 +3376,7 @@ async function runSubagent(
|
|
|
3145
3376
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
3146
3377
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
3147
3378
|
nestedRoute: config.nestedRoute,
|
|
3379
|
+
capabilityCeiling: config.capabilityCeiling,
|
|
3148
3380
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
3149
3381
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
3150
3382
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
@@ -3192,13 +3424,22 @@ async function runSubagent(
|
|
|
3192
3424
|
statusPayload.steps[fi].error = stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.error;
|
|
3193
3425
|
statusPayload.steps[fi].transcriptPath = singleResult.transcriptPath ?? statusPayload.steps[fi].transcriptPath;
|
|
3194
3426
|
statusPayload.steps[fi].transcriptError = singleResult.transcriptError;
|
|
3427
|
+
statusPayload.steps[fi].agentContract = singleResult.agentContract;
|
|
3428
|
+
statusPayload.steps[fi].effects = singleResult.effects;
|
|
3429
|
+
statusPayload.steps[fi].execution = singleResult.execution;
|
|
3430
|
+
statusPayload.steps[fi].review = singleResult.review;
|
|
3195
3431
|
statusPayload.steps[fi].structuredOutput = singleResult.structuredOutput;
|
|
3196
3432
|
statusPayload.steps[fi].structuredOutputPath = singleResult.structuredOutputPath;
|
|
3197
3433
|
statusPayload.steps[fi].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
3198
3434
|
statusPayload.steps[fi].acceptance = singleResult.acceptance;
|
|
3199
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;
|
|
3200
3440
|
statusPayload.lastUpdate = taskEndTime;
|
|
3201
3441
|
writeStatusPayload();
|
|
3442
|
+
appendCapabilityCeilingAppliedEvent(eventsPath, id, fi, task.agent, singleResult);
|
|
3202
3443
|
|
|
3203
3444
|
appendJsonl(eventsPath, JSON.stringify({
|
|
3204
3445
|
type: stopped || childStopped ? "subagent.step.stopped" : timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
@@ -3248,6 +3489,9 @@ async function runSubagent(
|
|
|
3248
3489
|
for (const pr of parallelResults) {
|
|
3249
3490
|
results.push({
|
|
3250
3491
|
agent: pr.agent,
|
|
3492
|
+
context: pr.context,
|
|
3493
|
+
agentContract: pr.agentContract,
|
|
3494
|
+
launchContractDigest: pr.launchContractDigest,
|
|
3251
3495
|
output: pr.output,
|
|
3252
3496
|
error: pr.error,
|
|
3253
3497
|
protocolError: pr.protocolError,
|
|
@@ -3271,6 +3515,9 @@ async function runSubagent(
|
|
|
3271
3515
|
artifactPaths: pr.artifactPaths,
|
|
3272
3516
|
transcriptPath: pr.transcriptPath,
|
|
3273
3517
|
transcriptError: pr.transcriptError,
|
|
3518
|
+
effects: pr.effects,
|
|
3519
|
+
execution: pr.execution,
|
|
3520
|
+
review: pr.review,
|
|
3274
3521
|
structuredOutput: pr.structuredOutput,
|
|
3275
3522
|
structuredOutputPath: pr.structuredOutputPath,
|
|
3276
3523
|
structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
|
|
@@ -3298,21 +3545,62 @@ async function runSubagent(
|
|
|
3298
3545
|
attemptedModels: r.attemptedModels,
|
|
3299
3546
|
})),
|
|
3300
3547
|
);
|
|
3301
|
-
|
|
3548
|
+
if (worktreeSetup) {
|
|
3549
|
+
const captured = captureParallelWorktreeDiffs(worktreeSetup, asyncDir, stepIndex, group);
|
|
3550
|
+
if (captured.summary) previousOutput = `${previousOutput}\n\n${captured.summary}`;
|
|
3551
|
+
worktreeFinalized = true;
|
|
3552
|
+
const cleanup = cleanupWorktrees(worktreeSetup);
|
|
3553
|
+
try {
|
|
3554
|
+
statusPayload.parallelHandoff = writeParallelHandoffGroup({
|
|
3555
|
+
manifestPath: parallelHandoffPath(asyncDir),
|
|
3556
|
+
runId: id,
|
|
3557
|
+
mode: (config.resultMode ?? statusPayload.mode) === "parallel" ? "parallel" : "chain",
|
|
3558
|
+
source: "async",
|
|
3559
|
+
cwd,
|
|
3560
|
+
stepIndex,
|
|
3561
|
+
flatStartIndex: groupStartFlatIndex,
|
|
3562
|
+
setup: worktreeSetup,
|
|
3563
|
+
diffs: captured.diffs,
|
|
3564
|
+
cleanup,
|
|
3565
|
+
results: parallelResults.map((result) => ({
|
|
3566
|
+
agent: result.agent,
|
|
3567
|
+
status: result.stopped ? "stopped" : result.interrupted ? "paused" : result.exitCode === 0 ? "completed" : "failed",
|
|
3568
|
+
summary: result.output || result.error || "(no output)",
|
|
3569
|
+
...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
|
|
3570
|
+
...(result.structuredOutput !== undefined ? { structuredOutput: result.structuredOutput } : {}),
|
|
3571
|
+
...(result.structuredOutputPath ? { structuredOutputPath: result.structuredOutputPath } : {}),
|
|
3572
|
+
...(result.sessionFile ? { sessionPath: result.sessionFile } : {}),
|
|
3573
|
+
})),
|
|
3574
|
+
});
|
|
3575
|
+
previousOutput = `${previousOutput}\n\n${formatParallelHandoffReference(statusPayload.parallelHandoff)}`;
|
|
3576
|
+
} catch (error) {
|
|
3577
|
+
previousOutput = `${previousOutput}\n\n${formatParallelHandoffError(error)}`;
|
|
3578
|
+
}
|
|
3579
|
+
writeStatusPayload();
|
|
3580
|
+
}
|
|
3302
3581
|
|
|
3303
3582
|
appendJsonl(eventsPath, JSON.stringify({
|
|
3304
3583
|
type: "subagent.parallel.completed",
|
|
3305
3584
|
ts: Date.now(),
|
|
3306
3585
|
runId: id,
|
|
3307
3586
|
stepIndex,
|
|
3308
|
-
success: parallelResults.every((r) => r.exitCode === 0 || r.exitCode === -1)
|
|
3587
|
+
success: parallelResults.every((r) => r.exitCode === 0 || r.exitCode === -1)
|
|
3588
|
+
&& parallelResults.every((result, index) => !(isAgentContractV1(group.parallel[index]?.agentContract) && group.parallel[index]?.gateOn === "acceptance" && result.acceptance?.status === "rejected")),
|
|
3309
3589
|
}));
|
|
3310
3590
|
|
|
3591
|
+
const acceptanceGateFailure = parallelResults
|
|
3592
|
+
.map((result, index) => ({ result, index, task: group.parallel[index] }))
|
|
3593
|
+
.find(({ result, task }) => isAgentContractV1(task?.agentContract) && task?.gateOn === "acceptance" && result.acceptance?.status === "rejected");
|
|
3594
|
+
if (acceptanceGateFailure) {
|
|
3595
|
+
statusPayload.error = acceptanceFailureMessage(acceptanceGateFailure.result.acceptance) ?? "Parallel acceptance gate rejected the step.";
|
|
3596
|
+
writeStatusPayload();
|
|
3597
|
+
break;
|
|
3598
|
+
}
|
|
3311
3599
|
if (parallelResults.some((r) => r.exitCode !== 0 && r.exitCode !== -1)) {
|
|
3312
3600
|
break;
|
|
3313
3601
|
}
|
|
3314
3602
|
} finally {
|
|
3315
|
-
if (worktreeSetup) cleanupWorktrees(worktreeSetup);
|
|
3603
|
+
if (worktreeSetup && !worktreeFinalized) cleanupWorktrees(worktreeSetup);
|
|
3316
3604
|
}
|
|
3317
3605
|
} else {
|
|
3318
3606
|
const seqStep = step as SubagentStep;
|
|
@@ -3354,6 +3642,7 @@ async function runSubagent(
|
|
|
3354
3642
|
childIntercomTarget: config.childIntercomTargets?.[flatIndex],
|
|
3355
3643
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
3356
3644
|
nestedRoute: config.nestedRoute,
|
|
3645
|
+
capabilityCeiling: config.capabilityCeiling,
|
|
3357
3646
|
registerInterrupt: (interrupt) => registerStepInterrupt(flatIndex, interrupt),
|
|
3358
3647
|
registerTimeout: (interrupt) => registerStepTimeout(flatIndex, interrupt),
|
|
3359
3648
|
registerStop: (stop) => registerStepStop(flatIndex, stop),
|
|
@@ -3376,6 +3665,9 @@ async function runSubagent(
|
|
|
3376
3665
|
const childStopped = singleResult.stopped === true;
|
|
3377
3666
|
results.push({
|
|
3378
3667
|
agent: singleResult.agent,
|
|
3668
|
+
context: singleResult.context,
|
|
3669
|
+
agentContract: singleResult.agentContract,
|
|
3670
|
+
launchContractDigest: singleResult.launchContractDigest,
|
|
3379
3671
|
output: stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.output,
|
|
3380
3672
|
error: stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.error,
|
|
3381
3673
|
protocolError: singleResult.protocolError,
|
|
@@ -3390,11 +3682,16 @@ async function runSubagent(
|
|
|
3390
3682
|
artifactPaths: singleResult.artifactPaths,
|
|
3391
3683
|
transcriptPath: singleResult.transcriptPath,
|
|
3392
3684
|
transcriptError: singleResult.transcriptError,
|
|
3685
|
+
effects: singleResult.effects,
|
|
3686
|
+
execution: singleResult.execution,
|
|
3687
|
+
review: singleResult.review,
|
|
3393
3688
|
structuredOutput: singleResult.structuredOutput,
|
|
3394
3689
|
structuredOutputPath: singleResult.structuredOutputPath,
|
|
3395
3690
|
structuredOutputSchemaPath: singleResult.structuredOutputSchemaPath,
|
|
3396
3691
|
acceptance: singleResult.acceptance,
|
|
3397
3692
|
watchdog: singleResult.watchdog,
|
|
3693
|
+
capabilityCeiling: singleResult.capabilityCeiling,
|
|
3694
|
+
capabilityAudit: singleResult.capabilityAudit,
|
|
3398
3695
|
interrupted: singleResult.interrupted,
|
|
3399
3696
|
timedOut: timedOut || singleResult.timedOut ? true : undefined,
|
|
3400
3697
|
stopped: stopped || childStopped ? true : undefined,
|
|
@@ -3460,17 +3757,26 @@ async function runSubagent(
|
|
|
3460
3757
|
statusPayload.steps[flatIndex].error = stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.error;
|
|
3461
3758
|
statusPayload.steps[flatIndex].transcriptPath = singleResult.transcriptPath ?? statusPayload.steps[flatIndex].transcriptPath;
|
|
3462
3759
|
statusPayload.steps[flatIndex].transcriptError = singleResult.transcriptError;
|
|
3760
|
+
statusPayload.steps[flatIndex].agentContract = singleResult.agentContract;
|
|
3761
|
+
statusPayload.steps[flatIndex].effects = singleResult.effects;
|
|
3762
|
+
statusPayload.steps[flatIndex].execution = singleResult.execution;
|
|
3763
|
+
statusPayload.steps[flatIndex].review = singleResult.review;
|
|
3463
3764
|
statusPayload.steps[flatIndex].structuredOutput = singleResult.structuredOutput;
|
|
3464
3765
|
statusPayload.steps[flatIndex].structuredOutputPath = singleResult.structuredOutputPath;
|
|
3465
3766
|
statusPayload.steps[flatIndex].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
3466
3767
|
statusPayload.steps[flatIndex].acceptance = singleResult.acceptance;
|
|
3467
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;
|
|
3468
3773
|
if (stepTokens) {
|
|
3469
3774
|
statusPayload.steps[flatIndex].tokens = stepTokens;
|
|
3470
3775
|
statusPayload.totalTokens = { ...previousCumulativeTokens };
|
|
3471
3776
|
}
|
|
3472
3777
|
statusPayload.lastUpdate = stepEndTime;
|
|
3473
3778
|
writeStatusPayload();
|
|
3779
|
+
appendCapabilityCeilingAppliedEvent(eventsPath, id, flatIndex, seqStep.agent, singleResult);
|
|
3474
3780
|
|
|
3475
3781
|
appendJsonl(eventsPath, JSON.stringify({
|
|
3476
3782
|
type: stopped || childStopped ? "subagent.step.stopped" : timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
@@ -3497,6 +3803,11 @@ async function runSubagent(
|
|
|
3497
3803
|
}
|
|
3498
3804
|
|
|
3499
3805
|
flatIndex++;
|
|
3806
|
+
if (isAgentContractV1(seqStep.agentContract) && seqStep.gateOn === "acceptance" && singleResult.acceptance?.status === "rejected") {
|
|
3807
|
+
statusPayload.error = acceptanceFailureMessage(singleResult.acceptance) ?? "Chain acceptance gate rejected the step.";
|
|
3808
|
+
writeStatusPayload();
|
|
3809
|
+
break;
|
|
3810
|
+
}
|
|
3500
3811
|
if (singleResult.exitCode !== 0) {
|
|
3501
3812
|
break;
|
|
3502
3813
|
}
|
|
@@ -3567,7 +3878,7 @@ async function runSubagent(
|
|
|
3567
3878
|
clearTimeout(timeoutTimer);
|
|
3568
3879
|
timeoutTimer = undefined;
|
|
3569
3880
|
}
|
|
3570
|
-
statusPayload.state = stopped ? "stopped" : timedOut || turnBudgetExceeded ? "failed" : interrupted ? "paused" : results.every((r) => r.success) ? "complete" : "failed";
|
|
3881
|
+
statusPayload.state = stopped ? "stopped" : timedOut || turnBudgetExceeded || statusPayload.error ? "failed" : interrupted ? "paused" : results.every((r) => r.success) ? "complete" : "failed";
|
|
3571
3882
|
closeSteerInbox(asyncDir, statusPayload.state);
|
|
3572
3883
|
disposeControlInbox();
|
|
3573
3884
|
for (const request of consumeSteerRequests(asyncDir)) deliverSteerRequest(request);
|
|
@@ -3663,6 +3974,7 @@ async function runSubagent(
|
|
|
3663
3974
|
...(stopped ? { stopped: true, error: stopMessage } : timedOut ? { timedOut: true, error: timeoutMessage ?? "Subagent timed out." } : turnBudgetExceeded ? { error: statusPayload.error ?? "Subagent exceeded turn budget." } : {}),
|
|
3664
3975
|
results: results.map((r) => ({
|
|
3665
3976
|
agent: r.agent,
|
|
3977
|
+
context: r.context,
|
|
3666
3978
|
output: r.output,
|
|
3667
3979
|
error: r.error,
|
|
3668
3980
|
protocolError: r.protocolError,
|
|
@@ -3686,14 +3998,24 @@ async function runSubagent(
|
|
|
3686
3998
|
truncated: r.truncated,
|
|
3687
3999
|
transcriptPath: r.transcriptPath,
|
|
3688
4000
|
transcriptError: r.transcriptError,
|
|
4001
|
+
agentContract: r.agentContract,
|
|
4002
|
+
launchContractDigest: r.launchContractDigest,
|
|
4003
|
+
execution: r.execution,
|
|
4004
|
+
review: r.review,
|
|
4005
|
+
effects: r.effects,
|
|
3689
4006
|
structuredOutput: r.structuredOutput,
|
|
3690
4007
|
structuredOutputPath: r.structuredOutputPath,
|
|
3691
4008
|
structuredOutputSchemaPath: r.structuredOutputSchemaPath,
|
|
3692
4009
|
acceptance: r.acceptance,
|
|
3693
4010
|
watchdog: r.watchdog,
|
|
4011
|
+
capabilityCeiling: r.capabilityCeiling,
|
|
4012
|
+
capabilityAudit: r.capabilityAudit,
|
|
3694
4013
|
})),
|
|
3695
4014
|
outputs,
|
|
3696
4015
|
workflowGraph: statusPayload.workflowGraph,
|
|
4016
|
+
parallelHandoff: statusPayload.parallelHandoff,
|
|
4017
|
+
capabilityCeiling: statusPayload.capabilityCeiling,
|
|
4018
|
+
capabilityAudit: statusPayload.capabilityAudit,
|
|
3697
4019
|
exitCode: stopped || timedOut || turnBudgetExceeded ? 1 : interrupted || results.every((r) => r.success) ? 0 : 1,
|
|
3698
4020
|
timestamp: runEndedAt,
|
|
3699
4021
|
durationMs: runEndedAt - overallStartTime,
|
|
@@ -3703,6 +4025,7 @@ async function runSubagent(
|
|
|
3703
4025
|
artifactsDir,
|
|
3704
4026
|
cwd,
|
|
3705
4027
|
asyncDir,
|
|
4028
|
+
launchContractDigest: config.launchContractDigest,
|
|
3706
4029
|
sessionId: config.sessionId,
|
|
3707
4030
|
sessionFile: effectiveSessionFile,
|
|
3708
4031
|
intercomTarget: config.controlIntercomTarget,
|
|
@@ -3715,6 +4038,28 @@ async function runSubagent(
|
|
|
3715
4038
|
} catch (err) {
|
|
3716
4039
|
console.error(`Failed to write result file ${resultPath}:`, err);
|
|
3717
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
|
+
}
|
|
3718
4063
|
}
|
|
3719
4064
|
|
|
3720
4065
|
async function waitForStartupControl(
|
|
@@ -3758,6 +4103,7 @@ async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
3758
4103
|
try {
|
|
3759
4104
|
if (config.revivalLease) {
|
|
3760
4105
|
lease = acquireSessionLease(config.revivalLease);
|
|
4106
|
+
config.revivalLeaseToken = lease.owner.token;
|
|
3761
4107
|
writeAtomicJson(startupPath, { state: "ready", token: lease.owner.token, pid: process.pid, owner: lease.owner });
|
|
3762
4108
|
await waitForStartupControl(startupAckPath, lease.owner.token, "ack");
|
|
3763
4109
|
writeAtomicJson(startupPath, { state: "acknowledged", token: lease.owner.token, pid: process.pid });
|
|
@@ -3784,11 +4130,17 @@ async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
3784
4130
|
} finally {
|
|
3785
4131
|
process.off("exit", releaseOnExit);
|
|
3786
4132
|
if (lease) {
|
|
4133
|
+
let acknowledged = false;
|
|
3787
4134
|
try {
|
|
3788
|
-
lease.release();
|
|
4135
|
+
acknowledged = lease.release();
|
|
3789
4136
|
} catch (error) {
|
|
3790
4137
|
console.error("Failed to release session revival lease:", error);
|
|
3791
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
|
+
}
|
|
3792
4144
|
}
|
|
3793
4145
|
}
|
|
3794
4146
|
}
|