pi-subagents 0.32.0 → 0.33.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 +30 -3
- package/README.md +147 -58
- package/install.mjs +2 -1
- package/package.json +3 -2
- package/skills/pi-subagents/SKILL.md +70 -14
- package/src/agents/agent-management.ts +177 -5
- package/src/agents/agent-memory.ts +254 -0
- package/src/agents/agent-serializer.ts +11 -0
- package/src/agents/agents.ts +142 -12
- package/src/agents/chain-serializer.ts +27 -2
- package/src/extension/doctor.ts +1 -9
- package/src/extension/fanout-child.ts +2 -2
- package/src/extension/index.ts +65 -90
- package/src/extension/rpc.ts +369 -0
- package/src/extension/schemas.ts +52 -8
- package/src/extension/tool-description.ts +200 -0
- package/src/intercom/intercom-bridge.ts +21 -253
- package/src/intercom/native-supervisor-channel.ts +510 -0
- package/src/runs/background/async-execution.ts +51 -7
- package/src/runs/background/async-job-tracker.ts +12 -2
- package/src/runs/background/async-status.ts +27 -2
- package/src/runs/background/completion-batcher.ts +166 -0
- package/src/runs/background/control-channel.ts +106 -1
- package/src/runs/background/fleet-view.ts +515 -0
- package/src/runs/background/notify.ts +161 -44
- package/src/runs/background/result-watcher.ts +1 -2
- package/src/runs/background/run-id-resolver.ts +3 -2
- package/src/runs/background/run-status.ts +166 -6
- package/src/runs/background/scheduled-runs.ts +514 -0
- package/src/runs/background/subagent-runner.ts +409 -35
- package/src/runs/background/wait.ts +353 -0
- package/src/runs/foreground/chain-execution.ts +95 -21
- package/src/runs/foreground/execution.ts +150 -21
- package/src/runs/foreground/subagent-executor.ts +378 -64
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/model-fallback.ts +167 -20
- package/src/runs/shared/model-scope.ts +128 -0
- package/src/runs/shared/nested-events.ts +31 -0
- package/src/runs/shared/parallel-utils.ts +1 -0
- package/src/runs/shared/pi-args.ts +30 -1
- package/src/runs/shared/subagent-prompt-runtime.ts +108 -2
- package/src/runs/shared/tool-budget.ts +74 -0
- package/src/runs/shared/turn-budget.ts +52 -0
- package/src/shared/artifacts.ts +1 -0
- package/src/shared/atomic-json.ts +15 -2
- package/src/shared/child-transcript.ts +212 -0
- package/src/shared/settings.ts +3 -1
- package/src/shared/types.ts +134 -19
- package/src/slash/prompt-workflows.ts +330 -0
- package/src/slash/slash-commands.ts +16 -2
- package/src/tui/render.ts +16 -8
- package/src/extension/companion-suggestions.ts +0 -359
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
writeArtifact,
|
|
13
13
|
writeMetadata,
|
|
14
14
|
} from "../../shared/artifacts.ts";
|
|
15
|
+
import { createChildTranscriptWriter, type ChildTranscriptWriter } from "../../shared/child-transcript.ts";
|
|
15
16
|
import {
|
|
16
17
|
type AgentProgress,
|
|
17
18
|
type ArtifactPaths,
|
|
@@ -43,6 +44,7 @@ import {
|
|
|
43
44
|
extractTextFromContent,
|
|
44
45
|
} from "../../shared/utils.ts";
|
|
45
46
|
import { buildSkillInjection, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
47
|
+
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
46
48
|
import { evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
|
|
47
49
|
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
48
50
|
import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
@@ -67,6 +69,8 @@ import {
|
|
|
67
69
|
summarizeRecentMutatingFailures,
|
|
68
70
|
} from "../shared/long-running-guard.ts";
|
|
69
71
|
import { acceptanceFailureMessage, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport } from "../shared/acceptance.ts";
|
|
72
|
+
import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, shouldAbortForTurnBudget, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
|
|
73
|
+
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
|
|
70
74
|
|
|
71
75
|
const artifactOutputByResult = new WeakMap<SingleResult, string>();
|
|
72
76
|
const acceptanceOutputByResult = new WeakMap<SingleResult, string>();
|
|
@@ -98,7 +102,7 @@ function resolveAttemptTimeout(options: RunSyncOptions): { timeoutMs: number; re
|
|
|
98
102
|
};
|
|
99
103
|
}
|
|
100
104
|
|
|
101
|
-
function
|
|
105
|
+
function buildSkippedAcceptanceLedger(acceptance: ResolvedAcceptanceConfig, input: { id: string; message: string }): AcceptanceLedger {
|
|
102
106
|
return {
|
|
103
107
|
status: acceptance.level === "none" ? "not-required" : "rejected",
|
|
104
108
|
explicit: acceptance.explicit,
|
|
@@ -107,7 +111,7 @@ function buildTimedOutAcceptanceLedger(acceptance: ResolvedAcceptanceConfig): Ac
|
|
|
107
111
|
criteria: acceptance.criteria,
|
|
108
112
|
runtimeChecks: acceptance.level === "none"
|
|
109
113
|
? []
|
|
110
|
-
: [{ id:
|
|
114
|
+
: [{ id: input.id, status: "failed", message: input.message }],
|
|
111
115
|
verifyRuns: [],
|
|
112
116
|
};
|
|
113
117
|
}
|
|
@@ -175,6 +179,7 @@ async function runSingleAttempt(
|
|
|
175
179
|
skillsWarning?: string;
|
|
176
180
|
jsonlPath?: string;
|
|
177
181
|
artifactPaths?: ArtifactPaths;
|
|
182
|
+
transcriptWriter?: ChildTranscriptWriter;
|
|
178
183
|
attemptNotes: string[];
|
|
179
184
|
outputSnapshot?: SingleOutputSnapshot;
|
|
180
185
|
originalTask?: string;
|
|
@@ -197,7 +202,7 @@ async function runSingleAttempt(
|
|
|
197
202
|
tools: agent.tools,
|
|
198
203
|
extensions: agent.extensions,
|
|
199
204
|
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
200
|
-
systemPrompt: shared.systemPrompt,
|
|
205
|
+
systemPrompt: appendTurnBudgetSystemPrompt(shared.systemPrompt, options.turnBudget),
|
|
201
206
|
mcpDirectTools: agent.mcpDirectTools,
|
|
202
207
|
cwd: options.cwd ?? runtimeCwd,
|
|
203
208
|
promptFileStem: agent.name,
|
|
@@ -208,11 +213,12 @@ async function runSingleAttempt(
|
|
|
208
213
|
childIndex: options.index ?? 0,
|
|
209
214
|
parentEventSink: options.nestedRoute?.eventSink,
|
|
210
215
|
parentControlInbox: options.nestedRoute?.controlInbox,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
+
parentRootRunId: options.nestedRoute?.rootRunId,
|
|
217
|
+
parentCapabilityToken: options.nestedRoute?.capabilityToken,
|
|
218
|
+
parentSessionId: options.parentSessionId,
|
|
219
|
+
structuredOutput: options.structuredOutput,
|
|
220
|
+
toolBudget: options.toolBudget,
|
|
221
|
+
});
|
|
216
222
|
|
|
217
223
|
const result: SingleResult = {
|
|
218
224
|
agent: agent.name,
|
|
@@ -222,8 +228,11 @@ async function runSingleAttempt(
|
|
|
222
228
|
usage: emptyUsage(),
|
|
223
229
|
model: modelArg,
|
|
224
230
|
artifactPaths: shared.artifactPaths,
|
|
231
|
+
transcriptPath: shared.transcriptWriter ? shared.artifactPaths?.transcriptPath : undefined,
|
|
225
232
|
skills: shared.resolvedSkillNames,
|
|
226
233
|
skillsWarning: shared.skillsWarning,
|
|
234
|
+
...(options.turnBudget ? { turnBudget: initialTurnBudgetState(options.turnBudget) } : {}),
|
|
235
|
+
...(options.toolBudget ? { toolBudget: initialToolBudgetState(options.toolBudget) } : {}),
|
|
227
236
|
};
|
|
228
237
|
const startTime = Date.now();
|
|
229
238
|
if (options.structuredOutput) {
|
|
@@ -299,6 +308,19 @@ async function runSingleAttempt(
|
|
|
299
308
|
let timeoutTimer: NodeJS.Timeout | undefined;
|
|
300
309
|
let timeoutTerminationTimer: NodeJS.Timeout | undefined;
|
|
301
310
|
let timeoutHardKillTimer: NodeJS.Timeout | undefined;
|
|
311
|
+
let turnBudgetSoftReached = false;
|
|
312
|
+
let turnBudgetTerminationTimer: NodeJS.Timeout | undefined;
|
|
313
|
+
let turnBudgetHardKillTimer: NodeJS.Timeout | undefined;
|
|
314
|
+
const clearTurnBudgetTimers = () => {
|
|
315
|
+
if (turnBudgetTerminationTimer) {
|
|
316
|
+
clearTimeout(turnBudgetTerminationTimer);
|
|
317
|
+
turnBudgetTerminationTimer = undefined;
|
|
318
|
+
}
|
|
319
|
+
if (turnBudgetHardKillTimer) {
|
|
320
|
+
clearTimeout(turnBudgetHardKillTimer);
|
|
321
|
+
turnBudgetHardKillTimer = undefined;
|
|
322
|
+
}
|
|
323
|
+
};
|
|
302
324
|
const clearTimeoutTimers = () => {
|
|
303
325
|
if (timeoutTimer) {
|
|
304
326
|
clearTimeout(timeoutTimer);
|
|
@@ -382,6 +404,7 @@ async function runSingleAttempt(
|
|
|
382
404
|
clearFinalDrainTimers();
|
|
383
405
|
clearStdioGuard();
|
|
384
406
|
clearTimeoutTimers();
|
|
407
|
+
clearTurnBudgetTimers();
|
|
385
408
|
if (activityTimer) {
|
|
386
409
|
clearInterval(activityTimer);
|
|
387
410
|
activityTimer = undefined;
|
|
@@ -455,6 +478,50 @@ async function runSingleAttempt(
|
|
|
455
478
|
}));
|
|
456
479
|
return true;
|
|
457
480
|
};
|
|
481
|
+
const requestTurnBudgetAbort = (turnCount: number) => {
|
|
482
|
+
const budget = options.turnBudget;
|
|
483
|
+
if (!budget || result.timedOut || result.turnBudgetExceeded || interruptedByControl || processClosed || settled || detached) return;
|
|
484
|
+
const message = turnBudgetExceededMessage(budget, turnCount);
|
|
485
|
+
result.turnBudgetExceeded = true;
|
|
486
|
+
result.wrapUpRequested = true;
|
|
487
|
+
result.turnBudget = turnBudgetState(budget, turnCount, true);
|
|
488
|
+
result.error = message;
|
|
489
|
+
result.finalOutput = message;
|
|
490
|
+
progress.status = "failed";
|
|
491
|
+
progress.error = message;
|
|
492
|
+
progress.durationMs = Date.now() - startTime;
|
|
493
|
+
fireUpdate();
|
|
494
|
+
trySignalChild(proc, "SIGINT");
|
|
495
|
+
turnBudgetTerminationTimer = setTimeout(() => {
|
|
496
|
+
if (processClosed || settled || detached || result.timedOut) return;
|
|
497
|
+
trySignalChild(proc, "SIGTERM");
|
|
498
|
+
}, 1000);
|
|
499
|
+
turnBudgetTerminationTimer.unref?.();
|
|
500
|
+
turnBudgetHardKillTimer = setTimeout(() => {
|
|
501
|
+
if (processClosed || settled || detached || result.timedOut) return;
|
|
502
|
+
trySignalChild(proc, "SIGKILL");
|
|
503
|
+
}, 4000);
|
|
504
|
+
turnBudgetHardKillTimer.unref?.();
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
const updateTurnBudget = (turnCount: number, terminalAssistantStop: boolean) => {
|
|
508
|
+
const budget = options.turnBudget;
|
|
509
|
+
if (!budget || result.timedOut || result.turnBudgetExceeded) return;
|
|
510
|
+
if (turnCount < budget.maxTurns) {
|
|
511
|
+
result.turnBudget = { ...budget, outcome: "within-budget", turnCount };
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (!turnBudgetSoftReached) {
|
|
515
|
+
turnBudgetSoftReached = true;
|
|
516
|
+
result.wrapUpRequested = true;
|
|
517
|
+
appendRecentOutput(progress, [turnBudgetSoftNote(budget, turnCount)]);
|
|
518
|
+
}
|
|
519
|
+
result.turnBudget = turnBudgetState(budget, turnCount, false);
|
|
520
|
+
if (shouldAbortForTurnBudget(budget, turnCount, terminalAssistantStop)) {
|
|
521
|
+
requestTurnBudgetAbort(turnCount);
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
458
525
|
const updateActivityState = (now: number): boolean => {
|
|
459
526
|
if (!controlConfig.enabled) return false;
|
|
460
527
|
const idleState = deriveActivityState({
|
|
@@ -495,7 +562,7 @@ async function runSingleAttempt(
|
|
|
495
562
|
const fireUpdate = () => {
|
|
496
563
|
if (!options.onUpdate || processClosed) return;
|
|
497
564
|
progress.durationMs = Date.now() - startTime;
|
|
498
|
-
const output = result.timedOut && result.finalOutput ? result.finalOutput : getFinalOutput(result.messages);
|
|
565
|
+
const output = (result.timedOut || result.turnBudgetExceeded) && result.finalOutput ? result.finalOutput : getFinalOutput(result.messages);
|
|
499
566
|
emitUpdateSnapshot(output || "(running...)");
|
|
500
567
|
};
|
|
501
568
|
|
|
@@ -506,9 +573,11 @@ async function runSingleAttempt(
|
|
|
506
573
|
try {
|
|
507
574
|
evt = JSON.parse(line) as { type?: string; message?: Message; toolName?: string; args?: unknown };
|
|
508
575
|
} catch {
|
|
576
|
+
shared.transcriptWriter?.writeStdoutLine(line);
|
|
509
577
|
// Non-JSON stdout lines are expected; only structured events are parsed.
|
|
510
578
|
return;
|
|
511
579
|
}
|
|
580
|
+
shared.transcriptWriter?.writeChildEvent(evt);
|
|
512
581
|
|
|
513
582
|
const now = Date.now();
|
|
514
583
|
progress.durationMs = now - startTime;
|
|
@@ -526,6 +595,9 @@ async function runSingleAttempt(
|
|
|
526
595
|
|| (evt.toolName === "contact_supervisor" && (toolArgs.reason === "need_decision" || toolArgs.reason === "interview_request"));
|
|
527
596
|
}
|
|
528
597
|
progress.toolCount++;
|
|
598
|
+
if (options.toolBudget) {
|
|
599
|
+
result.toolBudget = toolBudgetState(options.toolBudget, progress.toolCount);
|
|
600
|
+
}
|
|
529
601
|
progress.currentTool = evt.toolName;
|
|
530
602
|
progress.currentToolArgs = extractToolArgsPreview(toolArgs);
|
|
531
603
|
progress.currentToolStartedAt = now;
|
|
@@ -559,6 +631,11 @@ async function runSingleAttempt(
|
|
|
559
631
|
if (evt.message.role === "assistant") {
|
|
560
632
|
result.usage.turns++;
|
|
561
633
|
progress.turnCount = result.usage.turns;
|
|
634
|
+
const stopReason = (evt.message as { stopReason?: string }).stopReason;
|
|
635
|
+
const hasToolCall = Array.isArray(evt.message.content)
|
|
636
|
+
&& evt.message.content.some((part) => (part as { type?: string }).type === "toolCall");
|
|
637
|
+
const terminalAssistantStop = stopReason === "stop" && !hasToolCall;
|
|
638
|
+
updateTurnBudget(result.usage.turns, terminalAssistantStop);
|
|
562
639
|
const u = evt.message.usage;
|
|
563
640
|
if (u) {
|
|
564
641
|
result.usage.input += u.input || 0;
|
|
@@ -573,10 +650,7 @@ async function runSingleAttempt(
|
|
|
573
650
|
const assistantText = extractTextFromContent(evt.message.content);
|
|
574
651
|
appendRecentOutput(progress, assistantText.split("\n").slice(-10));
|
|
575
652
|
// Final assistant message: start the exit drain window.
|
|
576
|
-
|
|
577
|
-
const hasToolCall = Array.isArray(evt.message.content)
|
|
578
|
-
&& evt.message.content.some((part) => (part as { type?: string }).type === "toolCall");
|
|
579
|
-
if (stopReason === "stop" && !hasToolCall) {
|
|
653
|
+
if (terminalAssistantStop) {
|
|
580
654
|
if (!evt.message.errorMessage && assistantText.trim()) assistantError = undefined;
|
|
581
655
|
cleanTerminalAssistantStopReceived ||= !evt.message.errorMessage;
|
|
582
656
|
startFinalDrain();
|
|
@@ -589,6 +663,10 @@ async function runSingleAttempt(
|
|
|
589
663
|
if (evt.type === "tool_result_end" && evt.message) {
|
|
590
664
|
result.messages.push(evt.message);
|
|
591
665
|
const resultText = extractTextFromContent(evt.message.content);
|
|
666
|
+
if (options.toolBudget && pendingToolResult && resultText.includes("Tool budget hard limit reached")) {
|
|
667
|
+
result.toolBudgetBlocked = true;
|
|
668
|
+
result.toolBudget = toolBudgetState(options.toolBudget, progress.toolCount, pendingToolResult.tool);
|
|
669
|
+
}
|
|
592
670
|
appendRecentOutput(progress, resultText.split("\n").slice(-10));
|
|
593
671
|
const toolSnapshot = pendingToolResult;
|
|
594
672
|
pendingToolResult = undefined;
|
|
@@ -676,18 +754,38 @@ async function runSingleAttempt(
|
|
|
676
754
|
// JSONL artifact flush is best effort.
|
|
677
755
|
});
|
|
678
756
|
cleanupTempDir(tempDir);
|
|
679
|
-
if (detached) {
|
|
680
|
-
finish(-2);
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
|
-
processClosed = true;
|
|
684
757
|
if (buf.trim()) processLine(buf);
|
|
758
|
+
if (stderrBuf.trim()) shared.transcriptWriter?.writeStderrText(stderrBuf);
|
|
685
759
|
if (!result.error && assistantError) result.error = assistantError;
|
|
686
760
|
const forcedDrainAfterFinalSuccess = forcedTerminationSignal && cleanTerminalAssistantStopReceived && !result.error;
|
|
687
761
|
if (code !== 0 && stderrBuf.trim() && !result.error && !forcedDrainAfterFinalSuccess) {
|
|
688
762
|
result.error = stderrBuf.trim();
|
|
689
763
|
}
|
|
690
764
|
const finalCode = forcedDrainAfterFinalSuccess ? 0 : forcedTerminationSignal || signal ? (code ?? 1) : (code ?? 0);
|
|
765
|
+
if (detached) {
|
|
766
|
+
result.exitCode = result.error && finalCode === 0 ? 1 : finalCode;
|
|
767
|
+
progress.status = result.exitCode === 0 ? "completed" : "failed";
|
|
768
|
+
progress.durationMs = Date.now() - startTime;
|
|
769
|
+
if (result.error) progress.error = result.error;
|
|
770
|
+
result.progressSummary = {
|
|
771
|
+
toolCount: progress.toolCount,
|
|
772
|
+
tokens: progress.tokens,
|
|
773
|
+
durationMs: progress.durationMs,
|
|
774
|
+
};
|
|
775
|
+
const finalOutput = getFinalOutput(result.messages);
|
|
776
|
+
result.finalOutput = finalOutput.trim() || result.error || result.finalOutput || "Detached child exited without final output.";
|
|
777
|
+
if (result.artifactPaths && options.artifactConfig?.enabled !== false && options.artifactConfig?.includeOutput !== false) {
|
|
778
|
+
try {
|
|
779
|
+
writeArtifact(result.artifactPaths.outputPath, result.finalOutput);
|
|
780
|
+
} catch {
|
|
781
|
+
// Detached children may outlive test/temp cleanup; recovered status is best-effort.
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
options.onDetachedExit?.(snapshotResult(result, snapshotProgress(progress)));
|
|
785
|
+
finish(-2);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
processClosed = true;
|
|
691
789
|
finish(finalCode);
|
|
692
790
|
});
|
|
693
791
|
proc.on("error", (error) => {
|
|
@@ -697,6 +795,7 @@ async function runSingleAttempt(
|
|
|
697
795
|
// JSONL artifact flush is best effort.
|
|
698
796
|
});
|
|
699
797
|
cleanupTempDir(tempDir);
|
|
798
|
+
if (stderrBuf.trim()) shared.transcriptWriter?.writeStderrText(stderrBuf);
|
|
700
799
|
if (!result.error) {
|
|
701
800
|
result.error = error instanceof Error ? error.message : String(error);
|
|
702
801
|
}
|
|
@@ -827,6 +926,11 @@ async function runSingleAttempt(
|
|
|
827
926
|
fullOutput = fullOutput.trim()
|
|
828
927
|
? `${timeoutMessage}\n\nPartial output before timeout:\n${fullOutput}`
|
|
829
928
|
: timeoutMessage;
|
|
929
|
+
} else if (result.turnBudgetExceeded && result.turnBudget) {
|
|
930
|
+
fullOutput = formatTurnBudgetOutput(turnBudgetExceededMessage(result.turnBudget, result.turnBudget.turnCount), fullOutput);
|
|
931
|
+
} else if (result.wrapUpRequested && result.turnBudget?.outcome === "wrap-up-requested") {
|
|
932
|
+
const note = turnBudgetSoftNote(result.turnBudget, result.turnBudget.wrapUpRequestedAtTurn ?? result.turnBudget.turnCount);
|
|
933
|
+
fullOutput = fullOutput.trim() ? `${note}\n\n${fullOutput}` : note;
|
|
830
934
|
}
|
|
831
935
|
const completionGuard = result.exitCode === 0 && !result.error && agent.completionGuard !== false
|
|
832
936
|
? evaluateCompletionMutationGuard({
|
|
@@ -951,6 +1055,10 @@ export async function runSync(
|
|
|
951
1055
|
const skillInjection = buildSkillInjection(resolvedSkills);
|
|
952
1056
|
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${skillInjection}` : skillInjection;
|
|
953
1057
|
}
|
|
1058
|
+
const memoryInjection = buildAgentMemoryInjection(agent, skillCwd);
|
|
1059
|
+
if (memoryInjection) {
|
|
1060
|
+
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${memoryInjection}` : memoryInjection;
|
|
1061
|
+
}
|
|
954
1062
|
systemPrompt = injectOutputPathSystemPrompt(systemPrompt, options.outputPath);
|
|
955
1063
|
|
|
956
1064
|
const candidates = buildModelCandidates(
|
|
@@ -958,6 +1066,7 @@ export async function runSync(
|
|
|
958
1066
|
agent.fallbackModels,
|
|
959
1067
|
options.availableModels,
|
|
960
1068
|
options.preferredModelProvider,
|
|
1069
|
+
{ scope: options.modelScope },
|
|
961
1070
|
);
|
|
962
1071
|
const attemptedModels: string[] = [];
|
|
963
1072
|
const modelAttempts: ModelAttempt[] = [];
|
|
@@ -968,6 +1077,7 @@ export async function runSync(
|
|
|
968
1077
|
|
|
969
1078
|
let artifactPathsResult: ArtifactPaths | undefined;
|
|
970
1079
|
let jsonlPath: string | undefined;
|
|
1080
|
+
let transcriptWriter: ChildTranscriptWriter | undefined;
|
|
971
1081
|
if (options.artifactsDir && options.artifactConfig?.enabled !== false) {
|
|
972
1082
|
artifactPathsResult = getArtifactPaths(options.artifactsDir, options.runId, agentName, options.index);
|
|
973
1083
|
ensureArtifactsDir(options.artifactsDir);
|
|
@@ -977,6 +1087,17 @@ export async function runSync(
|
|
|
977
1087
|
if (options.artifactConfig?.includeJsonl !== false) {
|
|
978
1088
|
jsonlPath = artifactPathsResult.jsonlPath;
|
|
979
1089
|
}
|
|
1090
|
+
if (options.artifactConfig?.includeTranscript !== false) {
|
|
1091
|
+
transcriptWriter = createChildTranscriptWriter({
|
|
1092
|
+
transcriptPath: artifactPathsResult.transcriptPath,
|
|
1093
|
+
source: "foreground",
|
|
1094
|
+
runId: options.runId,
|
|
1095
|
+
agent: agentName,
|
|
1096
|
+
childIndex: options.index,
|
|
1097
|
+
cwd: options.cwd ?? runtimeCwd,
|
|
1098
|
+
});
|
|
1099
|
+
transcriptWriter.writeInitialUserMessage(taskWithAcceptance);
|
|
1100
|
+
}
|
|
980
1101
|
}
|
|
981
1102
|
|
|
982
1103
|
let lastResult: SingleResult | undefined;
|
|
@@ -991,6 +1112,7 @@ export async function runSync(
|
|
|
991
1112
|
skillsWarning: missingSkills.length > 0 ? `Skills not found: ${missingSkills.join(", ")}` : undefined,
|
|
992
1113
|
jsonlPath,
|
|
993
1114
|
artifactPaths: artifactPathsResult,
|
|
1115
|
+
transcriptWriter,
|
|
994
1116
|
attemptNotes,
|
|
995
1117
|
outputSnapshot,
|
|
996
1118
|
originalTask: task,
|
|
@@ -1010,7 +1132,7 @@ export async function runSync(
|
|
|
1010
1132
|
usage: { ...result.usage },
|
|
1011
1133
|
};
|
|
1012
1134
|
modelAttempts.push(attempt);
|
|
1013
|
-
if (result.timedOut) {
|
|
1135
|
+
if (result.timedOut || result.turnBudgetExceeded) {
|
|
1014
1136
|
break;
|
|
1015
1137
|
}
|
|
1016
1138
|
if (attemptSucceeded) {
|
|
@@ -1046,6 +1168,9 @@ export async function runSync(
|
|
|
1046
1168
|
}
|
|
1047
1169
|
}
|
|
1048
1170
|
|
|
1171
|
+
if (transcriptWriter) result.transcriptPath = artifactPathsResult?.transcriptPath;
|
|
1172
|
+
if (transcriptWriter?.getError()) result.transcriptError = transcriptWriter.getError();
|
|
1173
|
+
|
|
1049
1174
|
if (artifactPathsResult && options.artifactConfig?.enabled !== false) {
|
|
1050
1175
|
result.artifactPaths = artifactPathsResult;
|
|
1051
1176
|
if (options.artifactConfig?.includeOutput !== false) {
|
|
@@ -1064,6 +1189,8 @@ export async function runSync(
|
|
|
1064
1189
|
durationMs: result.progressSummary?.durationMs,
|
|
1065
1190
|
toolCount: result.progressSummary?.toolCount,
|
|
1066
1191
|
error: result.error,
|
|
1192
|
+
...(transcriptWriter ? { transcriptPath: artifactPathsResult.transcriptPath } : {}),
|
|
1193
|
+
transcriptError: result.transcriptError,
|
|
1067
1194
|
skills: result.skills,
|
|
1068
1195
|
skillsWarning: result.skillsWarning,
|
|
1069
1196
|
timestamp: Date.now(),
|
|
@@ -1089,8 +1216,10 @@ export async function runSync(
|
|
|
1089
1216
|
}
|
|
1090
1217
|
|
|
1091
1218
|
result.acceptance = result.timedOut
|
|
1092
|
-
?
|
|
1093
|
-
:
|
|
1219
|
+
? buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "timeout", message: "Acceptance was not evaluated because the subagent timed out." })
|
|
1220
|
+
: result.turnBudgetExceeded
|
|
1221
|
+
? buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "turn-budget", message: "Acceptance was not evaluated because the subagent exceeded its turn budget." })
|
|
1222
|
+
: await evaluateAcceptance({
|
|
1094
1223
|
acceptance: effectiveAcceptance,
|
|
1095
1224
|
output: acceptanceOutputByResult.get(result) ?? result.finalOutput ?? "",
|
|
1096
1225
|
cwd: options.cwd ?? runtimeCwd,
|