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
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
-
import { existsSync,
|
|
7
|
-
import * as os from "node:os";
|
|
8
|
-
import * as path from "node:path";
|
|
6
|
+
import { existsSync, unlinkSync } from "node:fs";
|
|
9
7
|
import type { Message } from "@earendil-works/pi-ai";
|
|
10
8
|
import type { AgentConfig } from "../../agents/agents.ts";
|
|
11
9
|
import {
|
|
@@ -15,13 +13,10 @@ import {
|
|
|
15
13
|
writeMetadata,
|
|
16
14
|
} from "../../shared/artifacts.ts";
|
|
17
15
|
import {
|
|
18
|
-
type AcceptanceFinalizationTurn,
|
|
19
|
-
type AcceptanceLedger,
|
|
20
16
|
type AgentProgress,
|
|
21
17
|
type ArtifactPaths,
|
|
22
18
|
type ControlEvent,
|
|
23
19
|
type ModelAttempt,
|
|
24
|
-
type ResolvedAcceptanceConfig,
|
|
25
20
|
type RunSyncOptions,
|
|
26
21
|
type SingleResult,
|
|
27
22
|
type Usage,
|
|
@@ -44,10 +39,9 @@ import {
|
|
|
44
39
|
detectSubagentError,
|
|
45
40
|
extractToolArgsPreview,
|
|
46
41
|
extractTextFromContent,
|
|
47
|
-
formatResourceLimitExceeded,
|
|
48
42
|
} from "../../shared/utils.ts";
|
|
49
43
|
import { buildSkillInjection, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
50
|
-
import { evaluateCompletionMutationGuard
|
|
44
|
+
import { evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
|
|
51
45
|
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
52
46
|
import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
53
47
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
@@ -70,20 +64,7 @@ import {
|
|
|
70
64
|
shouldEscalateMutatingFailures,
|
|
71
65
|
summarizeRecentMutatingFailures,
|
|
72
66
|
} from "../shared/long-running-guard.ts";
|
|
73
|
-
import {
|
|
74
|
-
acceptanceFailureMessage,
|
|
75
|
-
acceptanceSelfReviewConfig,
|
|
76
|
-
attachFinalizationToLedger,
|
|
77
|
-
buildFinalizationProcessFailureLedger,
|
|
78
|
-
createFinalizationProcessFailureTurn,
|
|
79
|
-
createFinalizationTurn,
|
|
80
|
-
evaluateAcceptance,
|
|
81
|
-
formatAcceptanceFinalizationPrompt,
|
|
82
|
-
formatAcceptancePrompt,
|
|
83
|
-
resolveEffectiveAcceptance,
|
|
84
|
-
shouldRunAcceptanceFinalization,
|
|
85
|
-
stripAcceptanceReport,
|
|
86
|
-
} from "../shared/acceptance.ts";
|
|
67
|
+
import { acceptanceFailureMessage, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport } from "../shared/acceptance.ts";
|
|
87
68
|
|
|
88
69
|
const artifactOutputByResult = new WeakMap<SingleResult, string>();
|
|
89
70
|
const acceptanceOutputByResult = new WeakMap<SingleResult, string>();
|
|
@@ -109,43 +90,6 @@ function appendRecentOutput(progress: AgentProgress, lines: string[]): void {
|
|
|
109
90
|
}
|
|
110
91
|
}
|
|
111
92
|
|
|
112
|
-
const FOREGROUND_TIMEOUT_EXIT_CODE = 124;
|
|
113
|
-
|
|
114
|
-
function formatForegroundTimeoutMessage(timeoutMs: number | undefined): string {
|
|
115
|
-
return timeoutMs ? `Timed out after ${timeoutMs}ms.` : "Timed out.";
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function createTimedOutResult(agent: string, task: string, options: RunSyncOptions): SingleResult {
|
|
119
|
-
const message = formatForegroundTimeoutMessage(options.timeoutMs);
|
|
120
|
-
return {
|
|
121
|
-
agent,
|
|
122
|
-
task,
|
|
123
|
-
exitCode: FOREGROUND_TIMEOUT_EXIT_CODE,
|
|
124
|
-
messages: [],
|
|
125
|
-
usage: emptyUsage(),
|
|
126
|
-
error: message,
|
|
127
|
-
finalOutput: message,
|
|
128
|
-
timedOut: true,
|
|
129
|
-
progress: {
|
|
130
|
-
index: options.index ?? 0,
|
|
131
|
-
agent,
|
|
132
|
-
status: "failed",
|
|
133
|
-
task,
|
|
134
|
-
recentTools: [],
|
|
135
|
-
recentOutput: [message],
|
|
136
|
-
toolCount: 0,
|
|
137
|
-
tokens: 0,
|
|
138
|
-
durationMs: 0,
|
|
139
|
-
lastActivityAt: Date.now(),
|
|
140
|
-
},
|
|
141
|
-
progressSummary: {
|
|
142
|
-
toolCount: 0,
|
|
143
|
-
tokens: 0,
|
|
144
|
-
durationMs: 0,
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
93
|
function stripAcceptanceReportsFromMessages(messages: Message[] | undefined): void {
|
|
150
94
|
for (const message of messages ?? []) {
|
|
151
95
|
if (message.role !== "assistant" || !Array.isArray(message.content)) continue;
|
|
@@ -204,11 +148,10 @@ async function runSingleAttempt(
|
|
|
204
148
|
attemptNotes: string[];
|
|
205
149
|
outputSnapshot?: SingleOutputSnapshot;
|
|
206
150
|
originalTask?: string;
|
|
207
|
-
completionPolicy: CompletionPolicy;
|
|
208
151
|
},
|
|
209
152
|
): Promise<SingleResult> {
|
|
210
153
|
const modelArg = applyThinkingSuffix(model, agent.thinking);
|
|
211
|
-
|
|
154
|
+
const { args, env: sharedEnv, tempDir } = buildPiArgs({
|
|
212
155
|
baseArgs: ["--mode", "json", "-p"],
|
|
213
156
|
task,
|
|
214
157
|
sessionEnabled: shared.sessionEnabled,
|
|
@@ -232,10 +175,10 @@ async function runSingleAttempt(
|
|
|
232
175
|
childIndex: options.index ?? 0,
|
|
233
176
|
parentEventSink: options.nestedRoute?.eventSink,
|
|
234
177
|
parentControlInbox: options.nestedRoute?.controlInbox,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
178
|
+
parentRootRunId: options.nestedRoute?.rootRunId,
|
|
179
|
+
parentCapabilityToken: options.nestedRoute?.capabilityToken,
|
|
180
|
+
structuredOutput: options.structuredOutput,
|
|
181
|
+
});
|
|
239
182
|
|
|
240
183
|
const result: SingleResult = {
|
|
241
184
|
agent: agent.name,
|
|
@@ -301,12 +244,6 @@ async function runSingleAttempt(
|
|
|
301
244
|
let detached = false;
|
|
302
245
|
let intercomStarted = false;
|
|
303
246
|
let assistantError: string | undefined;
|
|
304
|
-
let timedOut = false;
|
|
305
|
-
let resourceLimited = false;
|
|
306
|
-
let timeoutTimer: NodeJS.Timeout | undefined;
|
|
307
|
-
let timeoutEscalationTimer: NodeJS.Timeout | undefined;
|
|
308
|
-
let resourceLimitTimer: NodeJS.Timeout | undefined;
|
|
309
|
-
let resourceLimitEscalationTimer: NodeJS.Timeout | undefined;
|
|
310
247
|
let removeAbortListener: (() => void) | undefined;
|
|
311
248
|
let removeInterruptListener: (() => void) | undefined;
|
|
312
249
|
let activityTimer: NodeJS.Timeout | undefined;
|
|
@@ -378,22 +315,6 @@ async function runSingleAttempt(
|
|
|
378
315
|
settled = true;
|
|
379
316
|
clearFinalDrainTimers();
|
|
380
317
|
clearStdioGuard();
|
|
381
|
-
if (timeoutTimer) {
|
|
382
|
-
clearTimeout(timeoutTimer);
|
|
383
|
-
timeoutTimer = undefined;
|
|
384
|
-
}
|
|
385
|
-
if (timeoutEscalationTimer) {
|
|
386
|
-
clearTimeout(timeoutEscalationTimer);
|
|
387
|
-
timeoutEscalationTimer = undefined;
|
|
388
|
-
}
|
|
389
|
-
if (resourceLimitTimer) {
|
|
390
|
-
clearTimeout(resourceLimitTimer);
|
|
391
|
-
resourceLimitTimer = undefined;
|
|
392
|
-
}
|
|
393
|
-
if (resourceLimitEscalationTimer) {
|
|
394
|
-
clearTimeout(resourceLimitEscalationTimer);
|
|
395
|
-
resourceLimitEscalationTimer = undefined;
|
|
396
|
-
}
|
|
397
318
|
if (activityTimer) {
|
|
398
319
|
clearInterval(activityTimer);
|
|
399
320
|
activityTimer = undefined;
|
|
@@ -488,26 +409,6 @@ async function runSingleAttempt(
|
|
|
488
409
|
};
|
|
489
410
|
|
|
490
411
|
|
|
491
|
-
const triggerResourceLimit = (kind: "maxExecutionTimeMs" | "maxTokens", limit: number, observed?: number) => {
|
|
492
|
-
if (processClosed || detached || settled || timedOut || resourceLimited) return;
|
|
493
|
-
resourceLimited = true;
|
|
494
|
-
const message = formatResourceLimitExceeded({ agent: agent.name, kind, limit, observed });
|
|
495
|
-
result.resourceLimitExceeded = { kind, limit, ...(observed !== undefined ? { observed } : {}), message };
|
|
496
|
-
result.error = message;
|
|
497
|
-
result.finalOutput = message;
|
|
498
|
-
progress.status = "failed";
|
|
499
|
-
progress.durationMs = Date.now() - startTime;
|
|
500
|
-
appendRecentOutput(progress, [message]);
|
|
501
|
-
progress.activityState = undefined;
|
|
502
|
-
fireUpdate();
|
|
503
|
-
trySignalChild(proc, "SIGINT");
|
|
504
|
-
resourceLimitEscalationTimer = setTimeout(() => {
|
|
505
|
-
if (settled || processClosed || detached) return;
|
|
506
|
-
trySignalChild(proc, "SIGTERM");
|
|
507
|
-
}, 1000);
|
|
508
|
-
resourceLimitEscalationTimer.unref?.();
|
|
509
|
-
};
|
|
510
|
-
|
|
511
412
|
const emitUpdateSnapshot = (text: string) => {
|
|
512
413
|
if (!options.onUpdate || processClosed) return;
|
|
513
414
|
const progressSnapshot = snapshotProgress(progress);
|
|
@@ -592,9 +493,6 @@ async function runSingleAttempt(
|
|
|
592
493
|
result.usage.cacheWrite += u.cacheWrite || 0;
|
|
593
494
|
result.usage.cost += u.cost?.total || 0;
|
|
594
495
|
progress.tokens = result.usage.input + result.usage.output;
|
|
595
|
-
if (options.maxTokens !== undefined && progress.tokens >= options.maxTokens) {
|
|
596
|
-
triggerResourceLimit("maxTokens", options.maxTokens, progress.tokens);
|
|
597
|
-
}
|
|
598
496
|
}
|
|
599
497
|
if (!result.model && evt.message.model) result.model = evt.message.model;
|
|
600
498
|
if (evt.message.errorMessage) assistantError = evt.message.errorMessage;
|
|
@@ -723,45 +621,9 @@ async function runSingleAttempt(
|
|
|
723
621
|
}
|
|
724
622
|
}
|
|
725
623
|
|
|
726
|
-
if (options.timeoutAt !== undefined) {
|
|
727
|
-
const triggerTimeout = () => {
|
|
728
|
-
if (processClosed || detached || settled || timedOut || resourceLimited) return;
|
|
729
|
-
timedOut = true;
|
|
730
|
-
const message = formatForegroundTimeoutMessage(options.timeoutMs);
|
|
731
|
-
result.timedOut = true;
|
|
732
|
-
result.error = message;
|
|
733
|
-
result.finalOutput = message;
|
|
734
|
-
progress.status = "failed";
|
|
735
|
-
progress.durationMs = Date.now() - startTime;
|
|
736
|
-
appendRecentOutput(progress, [message]);
|
|
737
|
-
progress.activityState = undefined;
|
|
738
|
-
fireUpdate();
|
|
739
|
-
trySignalChild(proc, "SIGINT");
|
|
740
|
-
timeoutEscalationTimer = setTimeout(() => {
|
|
741
|
-
if (settled || processClosed || detached) return;
|
|
742
|
-
trySignalChild(proc, "SIGTERM");
|
|
743
|
-
}, 1000);
|
|
744
|
-
timeoutEscalationTimer.unref?.();
|
|
745
|
-
};
|
|
746
|
-
const delay = options.timeoutAt - Date.now();
|
|
747
|
-
if (delay <= 0) triggerTimeout();
|
|
748
|
-
else {
|
|
749
|
-
timeoutTimer = setTimeout(triggerTimeout, delay);
|
|
750
|
-
timeoutTimer.unref?.();
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
if (options.maxExecutionTimeMs !== undefined) {
|
|
755
|
-
const maxExecutionTimeMs = options.maxExecutionTimeMs;
|
|
756
|
-
resourceLimitTimer = setTimeout(() => {
|
|
757
|
-
triggerResourceLimit("maxExecutionTimeMs", maxExecutionTimeMs);
|
|
758
|
-
}, maxExecutionTimeMs);
|
|
759
|
-
resourceLimitTimer.unref?.();
|
|
760
|
-
}
|
|
761
|
-
|
|
762
624
|
if (options.interruptSignal) {
|
|
763
625
|
const interrupt = () => {
|
|
764
|
-
if (processClosed || detached || settled
|
|
626
|
+
if (processClosed || detached || settled) return;
|
|
765
627
|
interruptedByControl = true;
|
|
766
628
|
progress.status = "running";
|
|
767
629
|
progress.durationMs = Date.now() - startTime;
|
|
@@ -783,40 +645,6 @@ async function runSingleAttempt(
|
|
|
783
645
|
}
|
|
784
646
|
});
|
|
785
647
|
result.exitCode = exitCode;
|
|
786
|
-
if (result.resourceLimitExceeded) {
|
|
787
|
-
result.exitCode = 1;
|
|
788
|
-
result.error = result.error ?? result.resourceLimitExceeded.message;
|
|
789
|
-
result.finalOutput = result.finalOutput || result.error;
|
|
790
|
-
if (result.progress) {
|
|
791
|
-
result.progress.status = "failed";
|
|
792
|
-
result.progress.activityState = undefined;
|
|
793
|
-
result.progress.durationMs = Date.now() - startTime;
|
|
794
|
-
}
|
|
795
|
-
result.progressSummary = {
|
|
796
|
-
toolCount: progress.toolCount,
|
|
797
|
-
tokens: progress.tokens,
|
|
798
|
-
durationMs: result.progress?.durationMs ?? Date.now() - startTime,
|
|
799
|
-
};
|
|
800
|
-
result.controlEvents = allControlEvents.length ? allControlEvents : undefined;
|
|
801
|
-
return result;
|
|
802
|
-
}
|
|
803
|
-
if (result.timedOut) {
|
|
804
|
-
result.exitCode = FOREGROUND_TIMEOUT_EXIT_CODE;
|
|
805
|
-
result.error = result.error ?? formatForegroundTimeoutMessage(options.timeoutMs);
|
|
806
|
-
result.finalOutput = result.finalOutput || result.error;
|
|
807
|
-
if (result.progress) {
|
|
808
|
-
result.progress.status = "failed";
|
|
809
|
-
result.progress.activityState = undefined;
|
|
810
|
-
result.progress.durationMs = Date.now() - startTime;
|
|
811
|
-
}
|
|
812
|
-
result.progressSummary = {
|
|
813
|
-
toolCount: progress.toolCount,
|
|
814
|
-
tokens: progress.tokens,
|
|
815
|
-
durationMs: result.progress?.durationMs ?? Date.now() - startTime,
|
|
816
|
-
};
|
|
817
|
-
result.controlEvents = allControlEvents.length ? allControlEvents : undefined;
|
|
818
|
-
return result;
|
|
819
|
-
}
|
|
820
648
|
if (interruptedByControl) {
|
|
821
649
|
result.exitCode = 0;
|
|
822
650
|
result.interrupted = true;
|
|
@@ -881,9 +709,9 @@ async function runSingleAttempt(
|
|
|
881
709
|
durationMs: progress.durationMs,
|
|
882
710
|
};
|
|
883
711
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
const completionGuard = result.exitCode === 0 && !result.error &&
|
|
712
|
+
const acceptanceOutput = getFinalOutput(result.messages);
|
|
713
|
+
let fullOutput = stripAcceptanceReport(acceptanceOutput);
|
|
714
|
+
const completionGuard = result.exitCode === 0 && !result.error && agent.completionGuard !== false
|
|
887
715
|
? evaluateCompletionMutationGuard({
|
|
888
716
|
agent: agent.name,
|
|
889
717
|
task: shared.originalTask ?? task,
|
|
@@ -892,8 +720,7 @@ async function runSingleAttempt(
|
|
|
892
720
|
mcpDirectTools: agent.mcpDirectTools,
|
|
893
721
|
})
|
|
894
722
|
: undefined;
|
|
895
|
-
|
|
896
|
-
if (completionGuardTriggered) {
|
|
723
|
+
if (completionGuard?.triggered && !observedMutationAttempt) {
|
|
897
724
|
result.exitCode = 1;
|
|
898
725
|
result.error = "Subagent completed without making edits for an implementation task.\nIt appears to have returned planning or scratchpad output instead of applying changes.";
|
|
899
726
|
progress.status = "failed";
|
|
@@ -909,17 +736,17 @@ async function runSingleAttempt(
|
|
|
909
736
|
reason: "completion_guard",
|
|
910
737
|
}));
|
|
911
738
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
739
|
+
if (options.outputPath && result.exitCode === 0) {
|
|
740
|
+
const resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot);
|
|
741
|
+
fullOutput = stripAcceptanceReport(resolvedOutput.fullOutput);
|
|
742
|
+
result.savedOutputPath = resolvedOutput.savedPath;
|
|
743
|
+
result.outputSaveError = resolvedOutput.saveError;
|
|
744
|
+
if (resolvedOutput.savedPath) {
|
|
745
|
+
result.outputReference = formatSavedOutputReference(resolvedOutput.savedPath, fullOutput);
|
|
746
|
+
}
|
|
920
747
|
}
|
|
921
|
-
|
|
922
|
-
|
|
748
|
+
artifactOutputByResult.set(result, fullOutput);
|
|
749
|
+
acceptanceOutputByResult.set(result, acceptanceOutput);
|
|
923
750
|
result.outputMode = options.outputMode ?? "inline";
|
|
924
751
|
result.finalOutput = options.outputMode === "file-only" && result.savedOutputPath && result.outputReference
|
|
925
752
|
? result.outputReference.message
|
|
@@ -942,99 +769,6 @@ async function runSingleAttempt(
|
|
|
942
769
|
return result;
|
|
943
770
|
}
|
|
944
771
|
|
|
945
|
-
async function runAcceptanceFinalizationLoop(input: {
|
|
946
|
-
runtimeCwd: string;
|
|
947
|
-
agent: AgentConfig;
|
|
948
|
-
result: SingleResult;
|
|
949
|
-
initialLedger: AcceptanceLedger;
|
|
950
|
-
initialOutput: string;
|
|
951
|
-
acceptance: ResolvedAcceptanceConfig;
|
|
952
|
-
options: RunSyncOptions;
|
|
953
|
-
systemPrompt: string;
|
|
954
|
-
resolvedSkillNames?: string[];
|
|
955
|
-
skillsWarning?: string;
|
|
956
|
-
}): Promise<AcceptanceLedger> {
|
|
957
|
-
const sessionFile = input.result.sessionFile ?? input.options.sessionFile;
|
|
958
|
-
const maxTurns = input.acceptance.finalization.maxTurns;
|
|
959
|
-
const turns: AcceptanceFinalizationTurn[] = [];
|
|
960
|
-
if (!sessionFile) {
|
|
961
|
-
const message = "Acceptance finalization requires a session file for same-session continuation.";
|
|
962
|
-
turns.push(createFinalizationProcessFailureTurn({ turn: 1, prompt: "", message }));
|
|
963
|
-
return buildFinalizationProcessFailureLedger({ initialLedger: input.initialLedger, turns, maxTurns, message });
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
const selfReviewAcceptance = acceptanceSelfReviewConfig(input.acceptance);
|
|
967
|
-
let previousFailure = acceptanceFailureMessage(input.initialLedger);
|
|
968
|
-
let authoritativeLedger = input.initialLedger;
|
|
969
|
-
for (let turn = 1; turn <= maxTurns; turn++) {
|
|
970
|
-
const prompt = formatAcceptanceFinalizationPrompt({
|
|
971
|
-
acceptance: input.acceptance,
|
|
972
|
-
initialOutput: input.initialOutput,
|
|
973
|
-
initialLedger: input.initialLedger,
|
|
974
|
-
turn,
|
|
975
|
-
maxTurns,
|
|
976
|
-
...(previousFailure ? { previousFailure } : {}),
|
|
977
|
-
});
|
|
978
|
-
const finalizationOptions: RunSyncOptions = { ...input.options, sessionFile, outputMode: "inline" };
|
|
979
|
-
delete finalizationOptions.sessionDir;
|
|
980
|
-
delete finalizationOptions.outputPath;
|
|
981
|
-
delete finalizationOptions.structuredOutput;
|
|
982
|
-
delete finalizationOptions.onUpdate;
|
|
983
|
-
finalizationOptions.allowIntercomDetach = false;
|
|
984
|
-
const finalizationResult = await runSingleAttempt(
|
|
985
|
-
input.runtimeCwd,
|
|
986
|
-
input.agent,
|
|
987
|
-
prompt,
|
|
988
|
-
input.result.model,
|
|
989
|
-
finalizationOptions,
|
|
990
|
-
{
|
|
991
|
-
sessionEnabled: true,
|
|
992
|
-
systemPrompt: input.systemPrompt,
|
|
993
|
-
resolvedSkillNames: input.resolvedSkillNames,
|
|
994
|
-
skillsWarning: input.skillsWarning,
|
|
995
|
-
attemptNotes: [],
|
|
996
|
-
originalTask: prompt,
|
|
997
|
-
completionPolicy: "acceptance-contract",
|
|
998
|
-
},
|
|
999
|
-
);
|
|
1000
|
-
sumUsage(input.result.usage, finalizationResult.usage);
|
|
1001
|
-
input.result.progressSummary = {
|
|
1002
|
-
toolCount: (input.result.progressSummary?.toolCount ?? 0) + (finalizationResult.progressSummary?.toolCount ?? 0),
|
|
1003
|
-
tokens: input.result.usage.input + input.result.usage.output,
|
|
1004
|
-
durationMs: (input.result.progressSummary?.durationMs ?? 0) + (finalizationResult.progressSummary?.durationMs ?? 0),
|
|
1005
|
-
};
|
|
1006
|
-
if (finalizationResult.controlEvents?.length) {
|
|
1007
|
-
input.result.controlEvents = [...(input.result.controlEvents ?? []), ...finalizationResult.controlEvents];
|
|
1008
|
-
}
|
|
1009
|
-
const rawOutput = acceptanceOutputByResult.get(finalizationResult) ?? getFinalOutput(finalizationResult.messages) ?? finalizationResult.finalOutput ?? "";
|
|
1010
|
-
if (finalizationResult.exitCode !== 0 || finalizationResult.error || finalizationResult.detached || finalizationResult.interrupted) {
|
|
1011
|
-
const message = finalizationResult.error ?? "Acceptance finalization turn did not complete successfully.";
|
|
1012
|
-
turns.push(createFinalizationProcessFailureTurn({ turn, prompt, rawOutput, message }));
|
|
1013
|
-
return buildFinalizationProcessFailureLedger({ initialLedger: input.initialLedger, turns, maxTurns, message });
|
|
1014
|
-
}
|
|
1015
|
-
const selfReviewLedger = await evaluateAcceptance({
|
|
1016
|
-
acceptance: selfReviewAcceptance,
|
|
1017
|
-
output: rawOutput,
|
|
1018
|
-
cwd: input.options.cwd ?? input.runtimeCwd,
|
|
1019
|
-
});
|
|
1020
|
-
authoritativeLedger = selfReviewLedger;
|
|
1021
|
-
turns.push(createFinalizationTurn({ turn, prompt, rawOutput, ledger: selfReviewLedger }));
|
|
1022
|
-
const failure = acceptanceFailureMessage(selfReviewLedger);
|
|
1023
|
-
if (!failure) {
|
|
1024
|
-
authoritativeLedger = input.acceptance === selfReviewAcceptance
|
|
1025
|
-
? selfReviewLedger
|
|
1026
|
-
: await evaluateAcceptance({
|
|
1027
|
-
acceptance: input.acceptance,
|
|
1028
|
-
output: rawOutput,
|
|
1029
|
-
cwd: input.options.cwd ?? input.runtimeCwd,
|
|
1030
|
-
});
|
|
1031
|
-
return attachFinalizationToLedger({ initialLedger: input.initialLedger, authoritativeLedger, turns, status: "completed", maxTurns });
|
|
1032
|
-
}
|
|
1033
|
-
previousFailure = failure;
|
|
1034
|
-
}
|
|
1035
|
-
return attachFinalizationToLedger({ initialLedger: input.initialLedger, authoritativeLedger, turns, status: "failed", maxTurns });
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
772
|
/**
|
|
1039
773
|
* Run a subagent synchronously (blocking until complete)
|
|
1040
774
|
*/
|
|
@@ -1068,16 +802,8 @@ export async function runSync(
|
|
|
1068
802
|
error: outputModeValidationError,
|
|
1069
803
|
};
|
|
1070
804
|
}
|
|
1071
|
-
if (options.timeoutAt !== undefined && Date.now() >= options.timeoutAt) {
|
|
1072
|
-
return createTimedOutResult(agentName, task, options);
|
|
1073
|
-
}
|
|
1074
|
-
const effectiveOptions: RunSyncOptions = {
|
|
1075
|
-
...options,
|
|
1076
|
-
maxExecutionTimeMs: options.maxExecutionTimeMs ?? agent.maxExecutionTimeMs,
|
|
1077
|
-
maxTokens: options.maxTokens ?? agent.maxTokens,
|
|
1078
|
-
};
|
|
1079
805
|
|
|
1080
|
-
const shareEnabled =
|
|
806
|
+
const shareEnabled = options.share === true;
|
|
1081
807
|
const effectiveAcceptance = resolveEffectiveAcceptance({
|
|
1082
808
|
explicit: options.acceptance,
|
|
1083
809
|
agentName,
|
|
@@ -1087,10 +813,6 @@ export async function runSync(
|
|
|
1087
813
|
dynamic: options.acceptanceContext?.dynamic,
|
|
1088
814
|
dynamicGroup: options.acceptanceContext?.dynamicGroup,
|
|
1089
815
|
});
|
|
1090
|
-
if (shouldRunAcceptanceFinalization(effectiveAcceptance) && !options.sessionFile) {
|
|
1091
|
-
const sessionDir = options.sessionDir ?? mkdtempSync(path.join(os.tmpdir(), "pi-subagent-finalization-"));
|
|
1092
|
-
options.sessionFile = path.join(sessionDir, "session.jsonl");
|
|
1093
|
-
}
|
|
1094
816
|
const acceptancePrompt = formatAcceptancePrompt(effectiveAcceptance);
|
|
1095
817
|
const taskWithAcceptance = acceptancePrompt ? `${task}\n${acceptancePrompt}` : task;
|
|
1096
818
|
const sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;
|
|
@@ -1128,13 +850,13 @@ export async function runSync(
|
|
|
1128
850
|
|
|
1129
851
|
let artifactPathsResult: ArtifactPaths | undefined;
|
|
1130
852
|
let jsonlPath: string | undefined;
|
|
1131
|
-
if (
|
|
1132
|
-
artifactPathsResult = getArtifactPaths(
|
|
1133
|
-
ensureArtifactsDir(
|
|
1134
|
-
if (
|
|
853
|
+
if (options.artifactsDir && options.artifactConfig?.enabled !== false) {
|
|
854
|
+
artifactPathsResult = getArtifactPaths(options.artifactsDir, options.runId, agentName, options.index);
|
|
855
|
+
ensureArtifactsDir(options.artifactsDir);
|
|
856
|
+
if (options.artifactConfig?.includeInput !== false) {
|
|
1135
857
|
writeArtifact(artifactPathsResult.inputPath, `# Task for ${agentName}\n\n${taskWithAcceptance}`);
|
|
1136
858
|
}
|
|
1137
|
-
if (
|
|
859
|
+
if (options.artifactConfig?.includeJsonl !== false) {
|
|
1138
860
|
jsonlPath = artifactPathsResult.jsonlPath;
|
|
1139
861
|
}
|
|
1140
862
|
}
|
|
@@ -1144,8 +866,8 @@ export async function runSync(
|
|
|
1144
866
|
for (let i = 0; i < modelsToTry.length; i++) {
|
|
1145
867
|
const candidate = modelsToTry[i];
|
|
1146
868
|
if (candidate) attemptedModels.push(candidate);
|
|
1147
|
-
const outputSnapshot = captureSingleOutputSnapshot(
|
|
1148
|
-
const result = await runSingleAttempt(runtimeCwd, agent, taskWithAcceptance, candidate,
|
|
869
|
+
const outputSnapshot = captureSingleOutputSnapshot(options.outputPath);
|
|
870
|
+
const result = await runSingleAttempt(runtimeCwd, agent, taskWithAcceptance, candidate, options, {
|
|
1149
871
|
sessionEnabled,
|
|
1150
872
|
systemPrompt,
|
|
1151
873
|
resolvedSkillNames: resolvedSkills.length > 0 ? resolvedSkills.map((skill) => skill.name) : undefined,
|
|
@@ -1155,14 +877,6 @@ export async function runSync(
|
|
|
1155
877
|
attemptNotes,
|
|
1156
878
|
outputSnapshot,
|
|
1157
879
|
originalTask: task,
|
|
1158
|
-
completionPolicy: resolveCompletionPolicy({
|
|
1159
|
-
agent: agent.name,
|
|
1160
|
-
task,
|
|
1161
|
-
completionGuardEnabled: agent.completionGuard !== false,
|
|
1162
|
-
usesAcceptanceContract: effectiveAcceptance.explicit,
|
|
1163
|
-
tools: agent.tools,
|
|
1164
|
-
mcpDirectTools: agent.mcpDirectTools,
|
|
1165
|
-
}),
|
|
1166
880
|
});
|
|
1167
881
|
lastResult = result;
|
|
1168
882
|
sumUsage(aggregateUsage, result.usage);
|
|
@@ -1180,7 +894,7 @@ export async function runSync(
|
|
|
1180
894
|
if (attemptSucceeded) {
|
|
1181
895
|
break;
|
|
1182
896
|
}
|
|
1183
|
-
if (
|
|
897
|
+
if (!isRetryableModelFailure(result.error) || i === modelsToTry.length - 1) {
|
|
1184
898
|
break;
|
|
1185
899
|
}
|
|
1186
900
|
attemptNotes.push(formatModelAttemptNote(attempt, modelsToTry[i + 1]));
|
|
@@ -1252,33 +966,14 @@ export async function runSync(
|
|
|
1252
966
|
if (sessionFile) result.sessionFile = sessionFile;
|
|
1253
967
|
}
|
|
1254
968
|
|
|
1255
|
-
|
|
1256
|
-
const acceptanceForInitialReport = shouldRunAcceptanceFinalization(effectiveAcceptance)
|
|
1257
|
-
? acceptanceSelfReviewConfig(effectiveAcceptance)
|
|
1258
|
-
: effectiveAcceptance;
|
|
1259
|
-
const initialAcceptance = await evaluateAcceptance({
|
|
1260
|
-
acceptance: acceptanceForInitialReport,
|
|
1261
|
-
output: initialAcceptanceOutput,
|
|
1262
|
-
cwd: options.cwd ?? runtimeCwd,
|
|
1263
|
-
});
|
|
1264
|
-
result.acceptance = initialAcceptance;
|
|
1265
|
-
if (shouldRunAcceptanceFinalization(effectiveAcceptance) && result.exitCode === 0 && !result.detached && !result.interrupted) {
|
|
1266
|
-
result.acceptance = await runAcceptanceFinalizationLoop({
|
|
1267
|
-
runtimeCwd,
|
|
1268
|
-
agent,
|
|
1269
|
-
result,
|
|
1270
|
-
initialLedger: initialAcceptance,
|
|
1271
|
-
initialOutput: initialAcceptanceOutput,
|
|
969
|
+
result.acceptance = await evaluateAcceptance({
|
|
1272
970
|
acceptance: effectiveAcceptance,
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
resolvedSkillNames: resolvedSkills.length > 0 ? resolvedSkills.map((skill) => skill.name) : undefined,
|
|
1276
|
-
...(missingSkills.length > 0 ? { skillsWarning: `Skills not found: ${missingSkills.join(", ")}` } : {}),
|
|
971
|
+
output: acceptanceOutputByResult.get(result) ?? result.finalOutput ?? "",
|
|
972
|
+
cwd: options.cwd ?? runtimeCwd,
|
|
1277
973
|
});
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
if (acceptanceFailure && result.acceptance.explicit && result.exitCode === 0 && !result.detached && !result.interrupted) {
|
|
974
|
+
const acceptanceFailure = acceptanceFailureMessage(result.acceptance);
|
|
975
|
+
stripAcceptanceReportsFromMessages(result.messages);
|
|
976
|
+
if (acceptanceFailure && result.acceptance.explicit && result.exitCode === 0 && !result.detached && !result.interrupted) {
|
|
1282
977
|
result.exitCode = 1;
|
|
1283
978
|
result.error = result.error ? `${result.error}\n${acceptanceFailure}` : acceptanceFailure;
|
|
1284
979
|
if (result.progress) {
|