pi-subagents 0.28.0 → 0.29.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 +14 -0
- package/README.md +18 -61
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +4 -35
- package/src/agents/agent-management.ts +10 -20
- package/src/agents/agent-selection.ts +2 -0
- package/src/agents/agent-serializer.ts +0 -10
- package/src/agents/agents.ts +304 -47
- package/src/agents/chain-serializer.ts +4 -9
- package/src/extension/doctor.ts +4 -3
- package/src/extension/fanout-child.ts +0 -2
- package/src/extension/index.ts +3 -8
- package/src/extension/schemas.ts +32 -22
- package/src/intercom/intercom-bridge.ts +11 -1
- package/src/intercom/result-intercom.ts +0 -5
- package/src/runs/background/async-execution.ts +20 -11
- package/src/runs/background/run-status.ts +1 -7
- package/src/runs/background/subagent-runner.ts +81 -211
- package/src/runs/foreground/chain-execution.ts +62 -58
- package/src/runs/foreground/execution.ts +38 -343
- package/src/runs/foreground/subagent-executor.ts +28 -99
- package/src/runs/shared/acceptance.ts +605 -22
- package/src/runs/shared/completion-guard.ts +3 -26
- package/src/runs/shared/model-fallback.ts +38 -0
- package/src/runs/shared/parallel-utils.ts +6 -10
- package/src/runs/shared/subagent-prompt-runtime.ts +3 -2
- package/src/runs/shared/workflow-graph.ts +2 -6
- package/src/shared/atomic-json.ts +68 -11
- package/src/shared/settings.ts +1 -0
- package/src/shared/types.ts +10 -48
- package/src/shared/utils.ts +2 -8
- package/src/tui/render.ts +14 -29
- package/src/runs/shared/acceptance-contract.ts +0 -318
- package/src/runs/shared/acceptance-evaluation.ts +0 -221
- package/src/runs/shared/acceptance-finalization.ts +0 -173
- package/src/runs/shared/acceptance-reports.ts +0 -127
|
@@ -8,8 +8,6 @@ import { appendJsonl, getArtifactPaths } from "../../shared/artifacts.ts";
|
|
|
8
8
|
import { PI_CODING_AGENT_PACKAGE, getPiSpawnCommand, resolveInstalledPiPackageRoot } from "../shared/pi-spawn.ts";
|
|
9
9
|
import { captureSingleOutputSnapshot, finalizeSingleOutput, formatSavedOutputReference, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
10
10
|
import {
|
|
11
|
-
type AcceptanceFinalizationTurn,
|
|
12
|
-
type AcceptanceLedger,
|
|
13
11
|
type ActivityState,
|
|
14
12
|
type ArtifactConfig,
|
|
15
13
|
type ArtifactPaths,
|
|
@@ -19,9 +17,7 @@ import {
|
|
|
19
17
|
type ModelAttempt,
|
|
20
18
|
type NestedRouteInfo,
|
|
21
19
|
type ResolvedControlConfig,
|
|
22
|
-
type ResourceLimitExceeded,
|
|
23
20
|
type SubagentRunMode,
|
|
24
|
-
type TokenUsage,
|
|
25
21
|
type Usage,
|
|
26
22
|
type WorkflowGraphSnapshot,
|
|
27
23
|
DEFAULT_MAX_OUTPUT,
|
|
@@ -54,8 +50,8 @@ import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelSt
|
|
|
54
50
|
import { nestedSummaryFromAsyncStatus, writeNestedEvent } from "../shared/nested-events.ts";
|
|
55
51
|
import { formatModelAttemptNote, isRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
56
52
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
57
|
-
import { detectSubagentError, extractTextFromContent, extractToolArgsPreview,
|
|
58
|
-
import { evaluateCompletionMutationGuard
|
|
53
|
+
import { detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput } from "../../shared/utils.ts";
|
|
54
|
+
import { evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
|
|
59
55
|
import {
|
|
60
56
|
createMutatingFailureState,
|
|
61
57
|
didMutatingToolFail,
|
|
@@ -68,6 +64,7 @@ import {
|
|
|
68
64
|
summarizeRecentMutatingFailures,
|
|
69
65
|
} from "../shared/long-running-guard.ts";
|
|
70
66
|
import { parseSessionTokens } from "../../shared/session-tokens.ts";
|
|
67
|
+
import type { TokenUsage } from "../../shared/types.ts";
|
|
71
68
|
import {
|
|
72
69
|
cleanupWorktrees,
|
|
73
70
|
createWorktrees,
|
|
@@ -80,19 +77,7 @@ import {
|
|
|
80
77
|
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
81
78
|
import { writeInitialProgressFile } from "../../shared/settings.ts";
|
|
82
79
|
import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
|
|
83
|
-
import {
|
|
84
|
-
acceptanceFailureMessage,
|
|
85
|
-
acceptanceSelfReviewConfig,
|
|
86
|
-
attachFinalizationToLedger,
|
|
87
|
-
buildFinalizationProcessFailureLedger,
|
|
88
|
-
createFinalizationProcessFailureTurn,
|
|
89
|
-
createFinalizationTurn,
|
|
90
|
-
evaluateAcceptance,
|
|
91
|
-
formatAcceptanceFinalizationPrompt,
|
|
92
|
-
formatAcceptancePrompt,
|
|
93
|
-
shouldRunAcceptanceFinalization,
|
|
94
|
-
stripAcceptanceReport,
|
|
95
|
-
} from "../shared/acceptance.ts";
|
|
80
|
+
import { acceptanceFailureMessage, aggregateAcceptanceReport, evaluateAcceptance, formatAcceptancePrompt, stripAcceptanceReport } from "../shared/acceptance.ts";
|
|
96
81
|
|
|
97
82
|
interface SubagentRunConfig {
|
|
98
83
|
id: string;
|
|
@@ -140,8 +125,7 @@ interface StepResult {
|
|
|
140
125
|
structuredOutput?: unknown;
|
|
141
126
|
structuredOutputPath?: string;
|
|
142
127
|
structuredOutputSchemaPath?: string;
|
|
143
|
-
acceptance?: AcceptanceLedger;
|
|
144
|
-
resourceLimitExceeded?: ResourceLimitExceeded;
|
|
128
|
+
acceptance?: import("../../shared/types.ts").AcceptanceLedger;
|
|
145
129
|
}
|
|
146
130
|
|
|
147
131
|
const ASYNC_INTERRUPT_SIGNAL: NodeJS.Signals = process.platform === "win32" ? "SIGBREAK" : "SIGUSR2";
|
|
@@ -236,7 +220,6 @@ interface RunPiStreamingResult {
|
|
|
236
220
|
finalOutput: string;
|
|
237
221
|
interrupted?: boolean;
|
|
238
222
|
observedMutationAttempt?: boolean;
|
|
239
|
-
resourceLimitExceeded?: ResourceLimitExceeded;
|
|
240
223
|
}
|
|
241
224
|
|
|
242
225
|
function runPiStreaming(
|
|
@@ -250,8 +233,6 @@ function runPiStreaming(
|
|
|
250
233
|
childEventContext?: ChildEventContext,
|
|
251
234
|
registerInterrupt?: (interrupt: (() => void) | undefined) => void,
|
|
252
235
|
onChildEvent?: (event: ChildEvent) => void,
|
|
253
|
-
maxExecutionTimeMs?: number,
|
|
254
|
-
maxTokens?: number,
|
|
255
236
|
): Promise<RunPiStreamingResult> {
|
|
256
237
|
return new Promise((resolve) => {
|
|
257
238
|
const outputStream = fs.createWriteStream(outputFile, { flags: "w" });
|
|
@@ -275,10 +256,7 @@ function runPiStreaming(
|
|
|
275
256
|
let error: string | undefined;
|
|
276
257
|
let assistantError: string | undefined;
|
|
277
258
|
let interrupted = false;
|
|
278
|
-
let resourceLimitExceeded: ResourceLimitExceeded | undefined;
|
|
279
259
|
let observedMutationAttempt = false;
|
|
280
|
-
let resourceLimitTimer: NodeJS.Timeout | undefined;
|
|
281
|
-
let resourceLimitEscalationTimer: NodeJS.Timeout | undefined;
|
|
282
260
|
const rawStdoutLines: string[] = [];
|
|
283
261
|
|
|
284
262
|
const writeOutputLine = (line: string) => {
|
|
@@ -292,19 +270,6 @@ function runPiStreaming(
|
|
|
292
270
|
}
|
|
293
271
|
};
|
|
294
272
|
|
|
295
|
-
const triggerResourceLimit = (kind: ResourceLimitExceeded["kind"], limit: number, observed?: number) => {
|
|
296
|
-
if (settled || resourceLimitExceeded) return;
|
|
297
|
-
const message = formatResourceLimitExceeded({ agent: childEventContext?.agent ?? "subagent", kind, limit, observed });
|
|
298
|
-
resourceLimitExceeded = { kind, limit, ...(observed !== undefined ? { observed } : {}), message };
|
|
299
|
-
error = message;
|
|
300
|
-
writeOutputLine(message);
|
|
301
|
-
trySignalChild(child, "SIGINT");
|
|
302
|
-
resourceLimitEscalationTimer = setTimeout(() => {
|
|
303
|
-
if (!settled) trySignalChild(child, "SIGTERM");
|
|
304
|
-
}, 1000);
|
|
305
|
-
resourceLimitEscalationTimer.unref?.();
|
|
306
|
-
};
|
|
307
|
-
|
|
308
273
|
const appendChildEvent = (event: Record<string, unknown>) => {
|
|
309
274
|
if (!childEventContext) return;
|
|
310
275
|
appendJsonl(childEventContext.eventsPath, JSON.stringify({
|
|
@@ -359,10 +324,6 @@ function runPiStreaming(
|
|
|
359
324
|
usage.cacheRead += eventUsage.cacheRead ?? 0;
|
|
360
325
|
usage.cacheWrite += eventUsage.cacheWrite ?? 0;
|
|
361
326
|
usage.cost += eventUsage.cost?.total ?? 0;
|
|
362
|
-
const observedTokens = usage.input + usage.output;
|
|
363
|
-
if (maxTokens !== undefined && observedTokens >= maxTokens) {
|
|
364
|
-
triggerResourceLimit("maxTokens", maxTokens, observedTokens);
|
|
365
|
-
}
|
|
366
327
|
}
|
|
367
328
|
const stopReason = (event.message as { stopReason?: string }).stopReason;
|
|
368
329
|
const hasToolCall = Array.isArray(event.message.content)
|
|
@@ -398,12 +359,6 @@ function runPiStreaming(
|
|
|
398
359
|
let finalDrainTimer: NodeJS.Timeout | undefined;
|
|
399
360
|
let finalHardKillTimer: NodeJS.Timeout | undefined;
|
|
400
361
|
let settled = false;
|
|
401
|
-
if (maxExecutionTimeMs !== undefined) {
|
|
402
|
-
resourceLimitTimer = setTimeout(() => {
|
|
403
|
-
triggerResourceLimit("maxExecutionTimeMs", maxExecutionTimeMs);
|
|
404
|
-
}, maxExecutionTimeMs);
|
|
405
|
-
resourceLimitTimer.unref?.();
|
|
406
|
-
}
|
|
407
362
|
const clearStdioGuard = attachPostExitStdioGuard(child, { idleMs: 2000, hardMs: 8000 });
|
|
408
363
|
child.stdout.on("data", (chunk: Buffer) => {
|
|
409
364
|
const text = chunk.toString();
|
|
@@ -417,7 +372,7 @@ function runPiStreaming(
|
|
|
417
372
|
processStderrText(chunk.toString());
|
|
418
373
|
});
|
|
419
374
|
registerInterrupt?.(() => {
|
|
420
|
-
if (settled
|
|
375
|
+
if (settled) return;
|
|
421
376
|
interrupted = true;
|
|
422
377
|
if (!error) error = "Interrupted. Waiting for explicit next action.";
|
|
423
378
|
trySignalChild(child, "SIGINT");
|
|
@@ -434,14 +389,6 @@ function runPiStreaming(
|
|
|
434
389
|
clearTimeout(finalHardKillTimer);
|
|
435
390
|
finalHardKillTimer = undefined;
|
|
436
391
|
}
|
|
437
|
-
if (resourceLimitTimer) {
|
|
438
|
-
clearTimeout(resourceLimitTimer);
|
|
439
|
-
resourceLimitTimer = undefined;
|
|
440
|
-
}
|
|
441
|
-
if (resourceLimitEscalationTimer) {
|
|
442
|
-
clearTimeout(resourceLimitEscalationTimer);
|
|
443
|
-
resourceLimitEscalationTimer = undefined;
|
|
444
|
-
}
|
|
445
392
|
};
|
|
446
393
|
function startFinalDrain(): void {
|
|
447
394
|
if (childExited || finalDrainTimer || settled) return;
|
|
@@ -473,12 +420,12 @@ function runPiStreaming(
|
|
|
473
420
|
if (stdoutBuf.trim()) processStdoutLine(stdoutBuf);
|
|
474
421
|
if (stderrBuf.trim()) appendChildLine("subagent.child.stderr", stderrBuf);
|
|
475
422
|
outputStream.end();
|
|
476
|
-
const finalOutput =
|
|
477
|
-
const finalError =
|
|
423
|
+
const finalOutput = getFinalOutput(messages) || rawStdoutLines.join("\n").trim();
|
|
424
|
+
const finalError = error ?? assistantError;
|
|
478
425
|
const forcedDrainAfterFinalSuccess = forcedTerminationSignal && cleanTerminalAssistantStopReceived && !finalError;
|
|
479
426
|
resolve({
|
|
480
427
|
stderr,
|
|
481
|
-
exitCode:
|
|
428
|
+
exitCode: interrupted || forcedDrainAfterFinalSuccess ? 0 : forcedTerminationSignal || signal ? (exitCode ?? 1) : exitCode,
|
|
482
429
|
messages,
|
|
483
430
|
usage,
|
|
484
431
|
model,
|
|
@@ -486,7 +433,6 @@ function runPiStreaming(
|
|
|
486
433
|
finalOutput,
|
|
487
434
|
interrupted,
|
|
488
435
|
observedMutationAttempt,
|
|
489
|
-
resourceLimitExceeded,
|
|
490
436
|
});
|
|
491
437
|
});
|
|
492
438
|
|
|
@@ -496,9 +442,9 @@ function runPiStreaming(
|
|
|
496
442
|
clearDrainTimers();
|
|
497
443
|
clearStdioGuard();
|
|
498
444
|
outputStream.end();
|
|
499
|
-
const finalOutput =
|
|
445
|
+
const finalOutput = getFinalOutput(messages) || rawStdoutLines.join("\n").trim();
|
|
500
446
|
const spawnErrorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
|
501
|
-
resolve({ stderr, exitCode: 1, messages, usage, model, error:
|
|
447
|
+
resolve({ stderr, exitCode: 1, messages, usage, model, error: error ?? assistantError ?? spawnErrorMessage, finalOutput, observedMutationAttempt });
|
|
502
448
|
});
|
|
503
449
|
});
|
|
504
450
|
}
|
|
@@ -653,8 +599,7 @@ async function runSingleStep(
|
|
|
653
599
|
structuredOutput?: unknown;
|
|
654
600
|
structuredOutputPath?: string;
|
|
655
601
|
structuredOutputSchemaPath?: string;
|
|
656
|
-
acceptance?: AcceptanceLedger;
|
|
657
|
-
resourceLimitExceeded?: ResourceLimitExceeded;
|
|
602
|
+
acceptance?: import("../../shared/types.ts").AcceptanceLedger;
|
|
658
603
|
}> {
|
|
659
604
|
const effectiveStructuredOutput = step.structuredOutput ?? (step.structuredOutputSchema
|
|
660
605
|
? createStructuredOutputRuntime(step.structuredOutputSchema, path.join(path.dirname(ctx.outputFile), "structured-output"))
|
|
@@ -742,8 +687,6 @@ async function runSingleStep(
|
|
|
742
687
|
{ eventsPath, runId: ctx.id, stepIndex: ctx.flatIndex, agent: step.agent },
|
|
743
688
|
ctx.registerInterrupt,
|
|
744
689
|
ctx.onChildEvent,
|
|
745
|
-
step.maxExecutionTimeMs,
|
|
746
|
-
step.maxTokens,
|
|
747
690
|
);
|
|
748
691
|
cleanupTempDir(tempDir);
|
|
749
692
|
|
|
@@ -759,15 +702,7 @@ async function runSingleStep(
|
|
|
759
702
|
if (structured.error) structuredError = structured.error;
|
|
760
703
|
else structuredOutput = structured.value;
|
|
761
704
|
}
|
|
762
|
-
const
|
|
763
|
-
agent: step.agent,
|
|
764
|
-
task: taskForCompletionGuard,
|
|
765
|
-
completionGuardEnabled: step.completionGuard !== false,
|
|
766
|
-
usesAcceptanceContract: step.effectiveAcceptance?.explicit === true,
|
|
767
|
-
tools: step.tools,
|
|
768
|
-
mcpDirectTools: step.mcpDirectTools,
|
|
769
|
-
});
|
|
770
|
-
const completionGuard = run.exitCode === 0 && !run.error && !hiddenError?.hasError && completionPolicy === "mutation-guard"
|
|
705
|
+
const completionGuard = run.exitCode === 0 && !run.error && !hiddenError?.hasError && step.completionGuard !== false
|
|
771
706
|
? evaluateCompletionMutationGuard({
|
|
772
707
|
agent: step.agent,
|
|
773
708
|
task: taskForCompletionGuard,
|
|
@@ -780,7 +715,7 @@ async function runSingleStep(
|
|
|
780
715
|
const completionGuardError = completionGuardTriggered
|
|
781
716
|
? "Subagent completed without making edits for an implementation task.\nIt appears to have returned planning or scratchpad output instead of applying changes."
|
|
782
717
|
: undefined;
|
|
783
|
-
const effectiveExitCode =
|
|
718
|
+
const effectiveExitCode = completionGuardTriggered
|
|
784
719
|
? 1
|
|
785
720
|
: structuredError
|
|
786
721
|
? 1
|
|
@@ -808,8 +743,8 @@ async function runSingleStep(
|
|
|
808
743
|
completionGuardTriggeredFinal = completionGuardTriggered;
|
|
809
744
|
finalOutputSnapshot = outputSnapshot;
|
|
810
745
|
finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput } as RunPiStreamingResult & { structuredOutput?: unknown };
|
|
811
|
-
if (attempt.success ||
|
|
812
|
-
if (
|
|
746
|
+
if (attempt.success || completionGuardTriggered) break;
|
|
747
|
+
if (!isRetryableModelFailure(error) || index === candidates.length - 1) break;
|
|
813
748
|
attemptNotes.push(formatModelAttemptNote(attempt, candidates[index + 1]));
|
|
814
749
|
}
|
|
815
750
|
|
|
@@ -821,12 +756,12 @@ async function runSingleStep(
|
|
|
821
756
|
const output = resolvedOutput.fullOutput;
|
|
822
757
|
const outputReference = resolvedOutput.savedPath ? formatSavedOutputReference(resolvedOutput.savedPath, output) : undefined;
|
|
823
758
|
let outputForSummary = output;
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
759
|
+
if (attemptNotes.length > 0) {
|
|
760
|
+
outputForSummary = `${attemptNotes.join("\n")}\n\n${outputForSummary}`.trim();
|
|
761
|
+
}
|
|
827
762
|
const outputForAcceptance = rawOutput;
|
|
828
|
-
|
|
829
|
-
|
|
763
|
+
const finalizedOutput = finalizeSingleOutput({
|
|
764
|
+
fullOutput: outputForSummary,
|
|
830
765
|
outputPath: step.outputPath,
|
|
831
766
|
outputMode: step.outputMode,
|
|
832
767
|
exitCode: finalResult?.exitCode ?? 1,
|
|
@@ -835,117 +770,13 @@ async function runSingleStep(
|
|
|
835
770
|
saveError: resolvedOutput.saveError,
|
|
836
771
|
});
|
|
837
772
|
outputForSummary = finalizedOutput.displayOutput;
|
|
838
|
-
const
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
output: outputForAcceptance,
|
|
845
|
-
cwd: step.cwd ?? ctx.cwd,
|
|
846
|
-
})
|
|
773
|
+
const acceptance = step.effectiveAcceptance
|
|
774
|
+
? await evaluateAcceptance({
|
|
775
|
+
acceptance: step.effectiveAcceptance,
|
|
776
|
+
output: outputForAcceptance,
|
|
777
|
+
cwd: step.cwd ?? ctx.cwd,
|
|
778
|
+
})
|
|
847
779
|
: undefined;
|
|
848
|
-
if (acceptance && step.effectiveAcceptance && shouldRunAcceptanceFinalization(step.effectiveAcceptance) && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted) {
|
|
849
|
-
const sessionFile = step.sessionFile ?? (sessionDir ? findLatestSessionFile(sessionDir) ?? undefined : undefined);
|
|
850
|
-
const maxTurns = step.effectiveAcceptance.finalization.maxTurns;
|
|
851
|
-
const turns: AcceptanceFinalizationTurn[] = [];
|
|
852
|
-
if (!sessionFile) {
|
|
853
|
-
const message = "Acceptance finalization requires a session file for same-session continuation.";
|
|
854
|
-
turns.push(createFinalizationProcessFailureTurn({ turn: 1, prompt: "", message }));
|
|
855
|
-
acceptance = buildFinalizationProcessFailureLedger({ initialLedger: acceptance, turns, maxTurns, message });
|
|
856
|
-
} else {
|
|
857
|
-
const selfReviewAcceptance = acceptanceSelfReviewConfig(step.effectiveAcceptance);
|
|
858
|
-
let previousFailure = acceptanceFailureMessage(acceptance);
|
|
859
|
-
let authoritativeLedger = acceptance;
|
|
860
|
-
for (let turn = 1; turn <= maxTurns; turn++) {
|
|
861
|
-
const prompt = formatAcceptanceFinalizationPrompt({
|
|
862
|
-
acceptance: step.effectiveAcceptance,
|
|
863
|
-
initialOutput: outputForAcceptance,
|
|
864
|
-
initialLedger: acceptance,
|
|
865
|
-
turn,
|
|
866
|
-
maxTurns,
|
|
867
|
-
...(previousFailure ? { previousFailure } : {}),
|
|
868
|
-
});
|
|
869
|
-
const { args, env, tempDir } = buildPiArgs({
|
|
870
|
-
baseArgs: ["--mode", "json", "-p"],
|
|
871
|
-
task: prompt,
|
|
872
|
-
sessionEnabled: true,
|
|
873
|
-
sessionFile,
|
|
874
|
-
model: finalResult?.model ?? step.model,
|
|
875
|
-
thinking: step.thinking,
|
|
876
|
-
inheritProjectContext: step.inheritProjectContext,
|
|
877
|
-
inheritSkills: step.inheritSkills,
|
|
878
|
-
tools: step.tools,
|
|
879
|
-
extensions: step.extensions,
|
|
880
|
-
systemPrompt: step.systemPrompt,
|
|
881
|
-
systemPromptMode: step.systemPromptMode,
|
|
882
|
-
mcpDirectTools: step.mcpDirectTools,
|
|
883
|
-
cwd: step.cwd ?? ctx.cwd,
|
|
884
|
-
promptFileStem: `${step.agent}-acceptance-finalization`,
|
|
885
|
-
intercomSessionName: ctx.childIntercomTarget,
|
|
886
|
-
orchestratorIntercomTarget: ctx.orchestratorIntercomTarget,
|
|
887
|
-
runId: ctx.id,
|
|
888
|
-
childAgentName: step.agent,
|
|
889
|
-
childIndex: ctx.flatIndex,
|
|
890
|
-
parentEventSink: ctx.nestedRoute?.eventSink,
|
|
891
|
-
parentControlInbox: ctx.nestedRoute?.controlInbox,
|
|
892
|
-
parentRootRunId: ctx.nestedRoute?.rootRunId,
|
|
893
|
-
parentCapabilityToken: ctx.nestedRoute?.capabilityToken,
|
|
894
|
-
});
|
|
895
|
-
ctx.onAttemptStart?.({ model: finalResult?.model ?? step.model, thinking: resolveEffectiveThinking(finalResult?.model ?? step.model, step.thinking) });
|
|
896
|
-
const finalizationRun = await runPiStreaming(
|
|
897
|
-
args,
|
|
898
|
-
step.cwd ?? ctx.cwd,
|
|
899
|
-
`${ctx.outputFile}.finalization-${turn}.log`,
|
|
900
|
-
env,
|
|
901
|
-
ctx.piPackageRoot,
|
|
902
|
-
ctx.piArgv1,
|
|
903
|
-
step.maxSubagentDepth,
|
|
904
|
-
{ eventsPath, runId: ctx.id, stepIndex: ctx.flatIndex, agent: step.agent },
|
|
905
|
-
ctx.registerInterrupt,
|
|
906
|
-
ctx.onChildEvent,
|
|
907
|
-
step.maxExecutionTimeMs,
|
|
908
|
-
step.maxTokens,
|
|
909
|
-
);
|
|
910
|
-
cleanupTempDir(tempDir);
|
|
911
|
-
modelAttempts.push({
|
|
912
|
-
model: finalResult?.model ?? finalizationRun.model ?? step.model ?? "default",
|
|
913
|
-
success: finalizationRun.exitCode === 0 && !finalizationRun.error,
|
|
914
|
-
exitCode: finalizationRun.exitCode,
|
|
915
|
-
error: finalizationRun.error,
|
|
916
|
-
usage: finalizationRun.usage,
|
|
917
|
-
});
|
|
918
|
-
const finalizationOutput = finalizationRun.finalOutput;
|
|
919
|
-
if (finalizationRun.exitCode !== 0 || finalizationRun.error || finalizationRun.interrupted) {
|
|
920
|
-
const message = finalizationRun.error ?? "Acceptance finalization turn did not complete successfully.";
|
|
921
|
-
turns.push(createFinalizationProcessFailureTurn({ turn, prompt, rawOutput: finalizationOutput, message }));
|
|
922
|
-
acceptance = buildFinalizationProcessFailureLedger({ initialLedger: acceptance, turns, maxTurns, message });
|
|
923
|
-
break;
|
|
924
|
-
}
|
|
925
|
-
const selfReviewLedger = await evaluateAcceptance({
|
|
926
|
-
acceptance: selfReviewAcceptance,
|
|
927
|
-
output: finalizationOutput,
|
|
928
|
-
cwd: step.cwd ?? ctx.cwd,
|
|
929
|
-
});
|
|
930
|
-
authoritativeLedger = selfReviewLedger;
|
|
931
|
-
turns.push(createFinalizationTurn({ turn, prompt, rawOutput: finalizationOutput, ledger: selfReviewLedger }));
|
|
932
|
-
const failure = acceptanceFailureMessage(selfReviewLedger);
|
|
933
|
-
if (!failure) {
|
|
934
|
-
authoritativeLedger = step.effectiveAcceptance === selfReviewAcceptance
|
|
935
|
-
? selfReviewLedger
|
|
936
|
-
: await evaluateAcceptance({
|
|
937
|
-
acceptance: step.effectiveAcceptance,
|
|
938
|
-
output: finalizationOutput,
|
|
939
|
-
cwd: step.cwd ?? ctx.cwd,
|
|
940
|
-
});
|
|
941
|
-
acceptance = attachFinalizationToLedger({ initialLedger: acceptance, authoritativeLedger, turns, status: "completed", maxTurns });
|
|
942
|
-
break;
|
|
943
|
-
}
|
|
944
|
-
previousFailure = failure;
|
|
945
|
-
if (turn === maxTurns) acceptance = attachFinalizationToLedger({ initialLedger: acceptance, authoritativeLedger, turns, status: "failed", maxTurns });
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
780
|
const acceptanceFailure = acceptance ? acceptanceFailureMessage(acceptance) : undefined;
|
|
950
781
|
const acceptanceCanFailRun = acceptanceFailure && acceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted;
|
|
951
782
|
const effectiveFinalExitCode = acceptanceCanFailRun ? 1 : finalResult?.exitCode ?? 1;
|
|
@@ -968,7 +799,6 @@ async function runSingleStep(
|
|
|
968
799
|
model: finalResult?.model,
|
|
969
800
|
attemptedModels: attemptedModels.length > 0 ? attemptedModels : undefined,
|
|
970
801
|
modelAttempts,
|
|
971
|
-
resourceLimitExceeded: finalResult?.resourceLimitExceeded,
|
|
972
802
|
skills: step.skills,
|
|
973
803
|
timestamp: Date.now(),
|
|
974
804
|
}, null, 2),
|
|
@@ -994,7 +824,6 @@ async function runSingleStep(
|
|
|
994
824
|
structuredOutputPath: effectiveStructuredOutput?.outputPath,
|
|
995
825
|
structuredOutputSchemaPath: effectiveStructuredOutput?.schemaPath,
|
|
996
826
|
acceptance,
|
|
997
|
-
resourceLimitExceeded: finalResult?.resourceLimitExceeded,
|
|
998
827
|
};
|
|
999
828
|
}
|
|
1000
829
|
|
|
@@ -1283,7 +1112,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1283
1112
|
writeAtomicJson(statusPath, statusPayload);
|
|
1284
1113
|
emitNestedSelfEvent(statusPayload.state === "running" || statusPayload.state === "queued" ? "subagent.nested.updated" : "subagent.nested.completed");
|
|
1285
1114
|
};
|
|
1286
|
-
const markDynamicGraphGroup = (stepIndex: number, status: "completed" | "failed" | "running", error?: string, acceptance?: AcceptanceLedger): void => {
|
|
1115
|
+
const markDynamicGraphGroup = (stepIndex: number, status: "completed" | "failed" | "running", error?: string, acceptance?: import("../../shared/types.ts").AcceptanceLedger): void => {
|
|
1287
1116
|
const groupNode = statusPayload.workflowGraph?.nodes.find((node) => node.id === `step-${stepIndex}`);
|
|
1288
1117
|
if (!groupNode) return;
|
|
1289
1118
|
groupNode.status = status;
|
|
@@ -1625,9 +1454,36 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1625
1454
|
placeholder.durationMs = 0;
|
|
1626
1455
|
}
|
|
1627
1456
|
previousOutput = "Dynamic fanout produced 0 results.";
|
|
1457
|
+
const groupAcceptance = step.effectiveAcceptance?.explicit
|
|
1458
|
+
? await evaluateAcceptance({
|
|
1459
|
+
acceptance: step.effectiveAcceptance,
|
|
1460
|
+
output: "",
|
|
1461
|
+
report: aggregateAcceptanceReport({
|
|
1462
|
+
results: [],
|
|
1463
|
+
notes: "Dynamic fanout produced 0 results.",
|
|
1464
|
+
}),
|
|
1465
|
+
cwd,
|
|
1466
|
+
})
|
|
1467
|
+
: undefined;
|
|
1468
|
+
if (placeholder && groupAcceptance) placeholder.acceptance = groupAcceptance;
|
|
1469
|
+
const groupAcceptanceFailure = groupAcceptance ? acceptanceFailureMessage(groupAcceptance) : undefined;
|
|
1470
|
+
if (groupAcceptanceFailure) {
|
|
1471
|
+
statusPayload.state = "failed";
|
|
1472
|
+
statusPayload.error = groupAcceptanceFailure;
|
|
1473
|
+
if (placeholder) {
|
|
1474
|
+
placeholder.status = "failed";
|
|
1475
|
+
placeholder.error = groupAcceptanceFailure;
|
|
1476
|
+
placeholder.exitCode = 1;
|
|
1477
|
+
}
|
|
1478
|
+
markDynamicGraphGroup(stepIndex, "failed", groupAcceptanceFailure, groupAcceptance);
|
|
1479
|
+
statusPayload.lastUpdate = now;
|
|
1480
|
+
writeStatusPayload();
|
|
1481
|
+
results.push({ agent: step.parallel.agent, output: groupAcceptanceFailure, error: groupAcceptanceFailure, success: false, exitCode: 1, acceptance: groupAcceptance });
|
|
1482
|
+
break;
|
|
1483
|
+
}
|
|
1628
1484
|
flatIndex++;
|
|
1629
1485
|
statusPayload.lastUpdate = now;
|
|
1630
|
-
markDynamicGraphGroup(stepIndex, "completed");
|
|
1486
|
+
markDynamicGraphGroup(stepIndex, "completed", undefined, groupAcceptance);
|
|
1631
1487
|
writeStatusPayload();
|
|
1632
1488
|
continue;
|
|
1633
1489
|
}
|
|
@@ -1759,14 +1615,12 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1759
1615
|
statusPayload.steps[fi].structuredOutputPath = singleResult.structuredOutputPath;
|
|
1760
1616
|
statusPayload.steps[fi].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
1761
1617
|
statusPayload.steps[fi].acceptance = singleResult.acceptance;
|
|
1762
|
-
statusPayload.steps[fi].resourceLimitExceeded = singleResult.resourceLimitExceeded;
|
|
1763
1618
|
statusPayload.lastUpdate = taskEndTime;
|
|
1764
1619
|
writeStatusPayload();
|
|
1765
1620
|
appendJsonl(eventsPath, JSON.stringify({
|
|
1766
1621
|
type: singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
1767
1622
|
ts: taskEndTime, runId: id, stepIndex: fi, agent: task.agent,
|
|
1768
1623
|
exitCode: singleResult.exitCode, durationMs: taskEndTime - taskStartTime,
|
|
1769
|
-
resourceLimitExceeded: singleResult.resourceLimitExceeded,
|
|
1770
1624
|
}));
|
|
1771
1625
|
if (singleResult.exitCode !== 0 && failFast) aborted = true;
|
|
1772
1626
|
return { ...singleResult, skipped: false };
|
|
@@ -1791,7 +1645,6 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1791
1645
|
structuredOutputPath: pr.structuredOutputPath,
|
|
1792
1646
|
structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
|
|
1793
1647
|
acceptance: pr.acceptance,
|
|
1794
|
-
resourceLimitExceeded: pr.resourceLimitExceeded,
|
|
1795
1648
|
});
|
|
1796
1649
|
}
|
|
1797
1650
|
const collection = collectDynamicResults(step as Parameters<typeof collectDynamicResults>[0], materialized.items, parallelResults);
|
|
@@ -1806,7 +1659,31 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1806
1659
|
stepIndex,
|
|
1807
1660
|
};
|
|
1808
1661
|
statusPayload.outputs = outputs;
|
|
1809
|
-
|
|
1662
|
+
const groupAcceptance = step.effectiveAcceptance
|
|
1663
|
+
? await evaluateAcceptance({
|
|
1664
|
+
acceptance: step.effectiveAcceptance,
|
|
1665
|
+
output: "",
|
|
1666
|
+
report: aggregateAcceptanceReport({
|
|
1667
|
+
results: parallelResults,
|
|
1668
|
+
notes: `Dynamic fanout collected ${collection.length} result(s) into ${step.collect.as}.`,
|
|
1669
|
+
}),
|
|
1670
|
+
cwd,
|
|
1671
|
+
})
|
|
1672
|
+
: undefined;
|
|
1673
|
+
const groupAcceptanceFailure = groupAcceptance ? acceptanceFailureMessage(groupAcceptance) : undefined;
|
|
1674
|
+
markDynamicGraphGroup(stepIndex, groupAcceptanceFailure ? "failed" : "completed", groupAcceptanceFailure, groupAcceptance);
|
|
1675
|
+
if (groupAcceptanceFailure) {
|
|
1676
|
+
results.push({
|
|
1677
|
+
agent: step.parallel.agent,
|
|
1678
|
+
output: groupAcceptanceFailure,
|
|
1679
|
+
error: groupAcceptanceFailure,
|
|
1680
|
+
success: false,
|
|
1681
|
+
exitCode: 1,
|
|
1682
|
+
structuredOutput: collection,
|
|
1683
|
+
acceptance: groupAcceptance,
|
|
1684
|
+
});
|
|
1685
|
+
statusPayload.error = groupAcceptanceFailure;
|
|
1686
|
+
}
|
|
1810
1687
|
} catch (error) {
|
|
1811
1688
|
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
1812
1689
|
results.push({ agent: step.parallel.agent, output: message, error: message, success: false, exitCode: 1, structuredOutput: collection });
|
|
@@ -1991,7 +1868,6 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1991
1868
|
statusPayload.steps[fi].structuredOutputPath = singleResult.structuredOutputPath;
|
|
1992
1869
|
statusPayload.steps[fi].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
1993
1870
|
statusPayload.steps[fi].acceptance = singleResult.acceptance;
|
|
1994
|
-
statusPayload.steps[fi].resourceLimitExceeded = singleResult.resourceLimitExceeded;
|
|
1995
1871
|
statusPayload.lastUpdate = taskEndTime;
|
|
1996
1872
|
writeStatusPayload();
|
|
1997
1873
|
|
|
@@ -1999,7 +1875,6 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1999
1875
|
type: singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
2000
1876
|
ts: taskEndTime, runId: id, stepIndex: fi, agent: task.agent,
|
|
2001
1877
|
exitCode: singleResult.exitCode, durationMs: taskDuration,
|
|
2002
|
-
resourceLimitExceeded: singleResult.resourceLimitExceeded,
|
|
2003
1878
|
}));
|
|
2004
1879
|
if (singleResult.completionGuardTriggered) {
|
|
2005
1880
|
const event = buildControlEvent({
|
|
@@ -2058,7 +1933,6 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2058
1933
|
structuredOutputPath: pr.structuredOutputPath,
|
|
2059
1934
|
structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
|
|
2060
1935
|
acceptance: pr.acceptance,
|
|
2061
|
-
resourceLimitExceeded: pr.resourceLimitExceeded,
|
|
2062
1936
|
});
|
|
2063
1937
|
}
|
|
2064
1938
|
for (let t = 0; t < group.parallel.length; t++) {
|
|
@@ -2160,7 +2034,6 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2160
2034
|
structuredOutputPath: singleResult.structuredOutputPath,
|
|
2161
2035
|
structuredOutputSchemaPath: singleResult.structuredOutputSchemaPath,
|
|
2162
2036
|
acceptance: singleResult.acceptance,
|
|
2163
|
-
resourceLimitExceeded: singleResult.resourceLimitExceeded,
|
|
2164
2037
|
});
|
|
2165
2038
|
if (seqStep.outputName) {
|
|
2166
2039
|
outputs[seqStep.outputName] = outputEntryFromAsyncResult({
|
|
@@ -2206,7 +2079,6 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2206
2079
|
statusPayload.steps[flatIndex].structuredOutputPath = singleResult.structuredOutputPath;
|
|
2207
2080
|
statusPayload.steps[flatIndex].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
2208
2081
|
statusPayload.steps[flatIndex].acceptance = singleResult.acceptance;
|
|
2209
|
-
statusPayload.steps[flatIndex].resourceLimitExceeded = singleResult.resourceLimitExceeded;
|
|
2210
2082
|
if (stepTokens) {
|
|
2211
2083
|
statusPayload.steps[flatIndex].tokens = stepTokens;
|
|
2212
2084
|
statusPayload.totalTokens = { ...previousCumulativeTokens };
|
|
@@ -2223,7 +2095,6 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2223
2095
|
exitCode: singleResult.exitCode,
|
|
2224
2096
|
durationMs: stepEndTime - stepStartTime,
|
|
2225
2097
|
tokens: stepTokens,
|
|
2226
|
-
resourceLimitExceeded: singleResult.resourceLimitExceeded,
|
|
2227
2098
|
}));
|
|
2228
2099
|
if (singleResult.completionGuardTriggered) {
|
|
2229
2100
|
const event = buildControlEvent({
|
|
@@ -2370,7 +2241,6 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2370
2241
|
structuredOutputPath: r.structuredOutputPath,
|
|
2371
2242
|
structuredOutputSchemaPath: r.structuredOutputSchemaPath,
|
|
2372
2243
|
acceptance: r.acceptance,
|
|
2373
|
-
resourceLimitExceeded: r.resourceLimitExceeded,
|
|
2374
2244
|
})),
|
|
2375
2245
|
outputs,
|
|
2376
2246
|
workflowGraph: statusPayload.workflowGraph,
|