pi-subagents 0.28.0 → 0.30.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 +31 -0
- package/README.md +26 -62
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +29 -35
- package/src/agents/agent-management.ts +29 -22
- package/src/agents/agent-selection.ts +2 -0
- package/src/agents/agent-serializer.ts +5 -10
- package/src/agents/agents.ts +339 -47
- package/src/agents/chain-serializer.ts +4 -9
- package/src/agents/proactive-skills.ts +191 -0
- package/src/extension/doctor.ts +4 -3
- package/src/extension/fanout-child.ts +1 -3
- package/src/extension/index.ts +6 -9
- package/src/extension/schemas.ts +63 -26
- package/src/intercom/intercom-bridge.ts +11 -1
- package/src/intercom/result-intercom.ts +0 -5
- package/src/runs/background/async-execution.ts +186 -74
- package/src/runs/background/async-resume.ts +53 -5
- package/src/runs/background/async-status.ts +4 -1
- package/src/runs/background/chain-append.ts +282 -0
- package/src/runs/background/chain-root-attachment.ts +161 -0
- package/src/runs/background/run-status.ts +2 -7
- package/src/runs/background/subagent-runner.ts +160 -219
- package/src/runs/foreground/chain-execution.ts +62 -58
- package/src/runs/foreground/execution.ts +39 -343
- package/src/runs/foreground/subagent-executor.ts +316 -111
- package/src/runs/shared/acceptance.ts +605 -22
- package/src/runs/shared/chain-outputs.ts +23 -8
- package/src/runs/shared/completion-guard.ts +3 -26
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/model-fallback.ts +38 -0
- package/src/runs/shared/parallel-utils.ts +13 -10
- package/src/runs/shared/pi-args.ts +3 -2
- package/src/runs/shared/subagent-control.ts +8 -11
- 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 +20 -49
- package/src/shared/utils.ts +2 -8
- package/src/slash/slash-bridge.ts +3 -1
- package/src/slash/slash-commands.ts +1 -1
- 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,
|
|
@@ -221,6 +164,7 @@ async function runSingleAttempt(
|
|
|
221
164
|
inheritSkills: agent.inheritSkills,
|
|
222
165
|
tools: agent.tools,
|
|
223
166
|
extensions: agent.extensions,
|
|
167
|
+
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
224
168
|
systemPrompt: shared.systemPrompt,
|
|
225
169
|
mcpDirectTools: agent.mcpDirectTools,
|
|
226
170
|
cwd: options.cwd ?? runtimeCwd,
|
|
@@ -232,10 +176,10 @@ async function runSingleAttempt(
|
|
|
232
176
|
childIndex: options.index ?? 0,
|
|
233
177
|
parentEventSink: options.nestedRoute?.eventSink,
|
|
234
178
|
parentControlInbox: options.nestedRoute?.controlInbox,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
179
|
+
parentRootRunId: options.nestedRoute?.rootRunId,
|
|
180
|
+
parentCapabilityToken: options.nestedRoute?.capabilityToken,
|
|
181
|
+
structuredOutput: options.structuredOutput,
|
|
182
|
+
});
|
|
239
183
|
|
|
240
184
|
const result: SingleResult = {
|
|
241
185
|
agent: agent.name,
|
|
@@ -301,12 +245,6 @@ async function runSingleAttempt(
|
|
|
301
245
|
let detached = false;
|
|
302
246
|
let intercomStarted = false;
|
|
303
247
|
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
248
|
let removeAbortListener: (() => void) | undefined;
|
|
311
249
|
let removeInterruptListener: (() => void) | undefined;
|
|
312
250
|
let activityTimer: NodeJS.Timeout | undefined;
|
|
@@ -378,22 +316,6 @@ async function runSingleAttempt(
|
|
|
378
316
|
settled = true;
|
|
379
317
|
clearFinalDrainTimers();
|
|
380
318
|
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
319
|
if (activityTimer) {
|
|
398
320
|
clearInterval(activityTimer);
|
|
399
321
|
activityTimer = undefined;
|
|
@@ -488,26 +410,6 @@ async function runSingleAttempt(
|
|
|
488
410
|
};
|
|
489
411
|
|
|
490
412
|
|
|
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
413
|
const emitUpdateSnapshot = (text: string) => {
|
|
512
414
|
if (!options.onUpdate || processClosed) return;
|
|
513
415
|
const progressSnapshot = snapshotProgress(progress);
|
|
@@ -592,9 +494,6 @@ async function runSingleAttempt(
|
|
|
592
494
|
result.usage.cacheWrite += u.cacheWrite || 0;
|
|
593
495
|
result.usage.cost += u.cost?.total || 0;
|
|
594
496
|
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
497
|
}
|
|
599
498
|
if (!result.model && evt.message.model) result.model = evt.message.model;
|
|
600
499
|
if (evt.message.errorMessage) assistantError = evt.message.errorMessage;
|
|
@@ -723,45 +622,9 @@ async function runSingleAttempt(
|
|
|
723
622
|
}
|
|
724
623
|
}
|
|
725
624
|
|
|
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
625
|
if (options.interruptSignal) {
|
|
763
626
|
const interrupt = () => {
|
|
764
|
-
if (processClosed || detached || settled
|
|
627
|
+
if (processClosed || detached || settled) return;
|
|
765
628
|
interruptedByControl = true;
|
|
766
629
|
progress.status = "running";
|
|
767
630
|
progress.durationMs = Date.now() - startTime;
|
|
@@ -783,40 +646,6 @@ async function runSingleAttempt(
|
|
|
783
646
|
}
|
|
784
647
|
});
|
|
785
648
|
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
649
|
if (interruptedByControl) {
|
|
821
650
|
result.exitCode = 0;
|
|
822
651
|
result.interrupted = true;
|
|
@@ -881,9 +710,9 @@ async function runSingleAttempt(
|
|
|
881
710
|
durationMs: progress.durationMs,
|
|
882
711
|
};
|
|
883
712
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
const completionGuard = result.exitCode === 0 && !result.error &&
|
|
713
|
+
const acceptanceOutput = getFinalOutput(result.messages);
|
|
714
|
+
let fullOutput = stripAcceptanceReport(acceptanceOutput);
|
|
715
|
+
const completionGuard = result.exitCode === 0 && !result.error && agent.completionGuard !== false
|
|
887
716
|
? evaluateCompletionMutationGuard({
|
|
888
717
|
agent: agent.name,
|
|
889
718
|
task: shared.originalTask ?? task,
|
|
@@ -892,8 +721,7 @@ async function runSingleAttempt(
|
|
|
892
721
|
mcpDirectTools: agent.mcpDirectTools,
|
|
893
722
|
})
|
|
894
723
|
: undefined;
|
|
895
|
-
|
|
896
|
-
if (completionGuardTriggered) {
|
|
724
|
+
if (completionGuard?.triggered && !observedMutationAttempt) {
|
|
897
725
|
result.exitCode = 1;
|
|
898
726
|
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
727
|
progress.status = "failed";
|
|
@@ -909,17 +737,17 @@ async function runSingleAttempt(
|
|
|
909
737
|
reason: "completion_guard",
|
|
910
738
|
}));
|
|
911
739
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
740
|
+
if (options.outputPath && result.exitCode === 0) {
|
|
741
|
+
const resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot);
|
|
742
|
+
fullOutput = stripAcceptanceReport(resolvedOutput.fullOutput);
|
|
743
|
+
result.savedOutputPath = resolvedOutput.savedPath;
|
|
744
|
+
result.outputSaveError = resolvedOutput.saveError;
|
|
745
|
+
if (resolvedOutput.savedPath) {
|
|
746
|
+
result.outputReference = formatSavedOutputReference(resolvedOutput.savedPath, fullOutput);
|
|
747
|
+
}
|
|
920
748
|
}
|
|
921
|
-
|
|
922
|
-
|
|
749
|
+
artifactOutputByResult.set(result, fullOutput);
|
|
750
|
+
acceptanceOutputByResult.set(result, acceptanceOutput);
|
|
923
751
|
result.outputMode = options.outputMode ?? "inline";
|
|
924
752
|
result.finalOutput = options.outputMode === "file-only" && result.savedOutputPath && result.outputReference
|
|
925
753
|
? result.outputReference.message
|
|
@@ -942,99 +770,6 @@ async function runSingleAttempt(
|
|
|
942
770
|
return result;
|
|
943
771
|
}
|
|
944
772
|
|
|
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
773
|
/**
|
|
1039
774
|
* Run a subagent synchronously (blocking until complete)
|
|
1040
775
|
*/
|
|
@@ -1068,16 +803,8 @@ export async function runSync(
|
|
|
1068
803
|
error: outputModeValidationError,
|
|
1069
804
|
};
|
|
1070
805
|
}
|
|
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
806
|
|
|
1080
|
-
const shareEnabled =
|
|
807
|
+
const shareEnabled = options.share === true;
|
|
1081
808
|
const effectiveAcceptance = resolveEffectiveAcceptance({
|
|
1082
809
|
explicit: options.acceptance,
|
|
1083
810
|
agentName,
|
|
@@ -1087,10 +814,6 @@ export async function runSync(
|
|
|
1087
814
|
dynamic: options.acceptanceContext?.dynamic,
|
|
1088
815
|
dynamicGroup: options.acceptanceContext?.dynamicGroup,
|
|
1089
816
|
});
|
|
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
817
|
const acceptancePrompt = formatAcceptancePrompt(effectiveAcceptance);
|
|
1095
818
|
const taskWithAcceptance = acceptancePrompt ? `${task}\n${acceptancePrompt}` : task;
|
|
1096
819
|
const sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;
|
|
@@ -1128,13 +851,13 @@ export async function runSync(
|
|
|
1128
851
|
|
|
1129
852
|
let artifactPathsResult: ArtifactPaths | undefined;
|
|
1130
853
|
let jsonlPath: string | undefined;
|
|
1131
|
-
if (
|
|
1132
|
-
artifactPathsResult = getArtifactPaths(
|
|
1133
|
-
ensureArtifactsDir(
|
|
1134
|
-
if (
|
|
854
|
+
if (options.artifactsDir && options.artifactConfig?.enabled !== false) {
|
|
855
|
+
artifactPathsResult = getArtifactPaths(options.artifactsDir, options.runId, agentName, options.index);
|
|
856
|
+
ensureArtifactsDir(options.artifactsDir);
|
|
857
|
+
if (options.artifactConfig?.includeInput !== false) {
|
|
1135
858
|
writeArtifact(artifactPathsResult.inputPath, `# Task for ${agentName}\n\n${taskWithAcceptance}`);
|
|
1136
859
|
}
|
|
1137
|
-
if (
|
|
860
|
+
if (options.artifactConfig?.includeJsonl !== false) {
|
|
1138
861
|
jsonlPath = artifactPathsResult.jsonlPath;
|
|
1139
862
|
}
|
|
1140
863
|
}
|
|
@@ -1144,8 +867,8 @@ export async function runSync(
|
|
|
1144
867
|
for (let i = 0; i < modelsToTry.length; i++) {
|
|
1145
868
|
const candidate = modelsToTry[i];
|
|
1146
869
|
if (candidate) attemptedModels.push(candidate);
|
|
1147
|
-
const outputSnapshot = captureSingleOutputSnapshot(
|
|
1148
|
-
const result = await runSingleAttempt(runtimeCwd, agent, taskWithAcceptance, candidate,
|
|
870
|
+
const outputSnapshot = captureSingleOutputSnapshot(options.outputPath);
|
|
871
|
+
const result = await runSingleAttempt(runtimeCwd, agent, taskWithAcceptance, candidate, options, {
|
|
1149
872
|
sessionEnabled,
|
|
1150
873
|
systemPrompt,
|
|
1151
874
|
resolvedSkillNames: resolvedSkills.length > 0 ? resolvedSkills.map((skill) => skill.name) : undefined,
|
|
@@ -1155,14 +878,6 @@ export async function runSync(
|
|
|
1155
878
|
attemptNotes,
|
|
1156
879
|
outputSnapshot,
|
|
1157
880
|
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
881
|
});
|
|
1167
882
|
lastResult = result;
|
|
1168
883
|
sumUsage(aggregateUsage, result.usage);
|
|
@@ -1180,7 +895,7 @@ export async function runSync(
|
|
|
1180
895
|
if (attemptSucceeded) {
|
|
1181
896
|
break;
|
|
1182
897
|
}
|
|
1183
|
-
if (
|
|
898
|
+
if (!isRetryableModelFailure(result.error) || i === modelsToTry.length - 1) {
|
|
1184
899
|
break;
|
|
1185
900
|
}
|
|
1186
901
|
attemptNotes.push(formatModelAttemptNote(attempt, modelsToTry[i + 1]));
|
|
@@ -1252,33 +967,14 @@ export async function runSync(
|
|
|
1252
967
|
if (sessionFile) result.sessionFile = sessionFile;
|
|
1253
968
|
}
|
|
1254
969
|
|
|
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,
|
|
970
|
+
result.acceptance = await evaluateAcceptance({
|
|
1272
971
|
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(", ")}` } : {}),
|
|
972
|
+
output: acceptanceOutputByResult.get(result) ?? result.finalOutput ?? "",
|
|
973
|
+
cwd: options.cwd ?? runtimeCwd,
|
|
1277
974
|
});
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
if (acceptanceFailure && result.acceptance.explicit && result.exitCode === 0 && !result.detached && !result.interrupted) {
|
|
975
|
+
const acceptanceFailure = acceptanceFailureMessage(result.acceptance);
|
|
976
|
+
stripAcceptanceReportsFromMessages(result.messages);
|
|
977
|
+
if (acceptanceFailure && result.acceptance.explicit && result.exitCode === 0 && !result.detached && !result.interrupted) {
|
|
1282
978
|
result.exitCode = 1;
|
|
1283
979
|
result.error = result.error ? `${result.error}\n${acceptanceFailure}` : acceptanceFailure;
|
|
1284
980
|
if (result.progress) {
|