pi-subagents 0.57.0 → 0.59.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 +104 -0
- package/docs/agents.md +18 -9
- package/docs/configuration.md +4 -4
- package/docs/extension-api.md +40 -1
- package/docs/models.md +27 -4
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +92 -6
- package/docs/workflows.md +115 -0
- package/package.json +3 -1
- package/prompts/review-loop.md +2 -2
- package/skills/council-mode/SKILL.md +1 -1
- package/skills/pi-subagents/SKILL.md +3 -1
- package/skills/pi-subagents/references/execution-controls.md +6 -2
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +26 -5
- package/src/agents/agent-management.ts +28 -17
- package/src/agents/agent-serializer.ts +9 -2
- package/src/agents/agents.ts +103 -27
- package/src/agents/runtime-agent-events.ts +70 -0
- package/src/agents/runtime-agent-registry.ts +19 -18
- package/src/api/agents.ts +10 -5
- package/src/api/background-work.ts +5 -1
- package/src/api/delegation.ts +0 -7
- package/src/api/preflight.ts +32 -10
- package/src/extension/fanout-child.ts +5 -3
- package/src/extension/index.ts +66 -27
- package/src/extension/public-execution.ts +15 -2
- package/src/extension/rpc.ts +35 -14
- package/src/extension/schemas.ts +37 -12
- package/src/extension/tool-description.ts +25 -6
- package/src/integrations/herdr-status.ts +5 -0
- package/src/intercom/result-intercom.ts +2 -0
- package/src/profiles/profiles.ts +5 -6
- package/src/runs/background/active-async-capacity.ts +2 -2
- package/src/runs/background/async-execution.ts +78 -46
- package/src/runs/background/async-job-tracker.ts +14 -12
- package/src/runs/background/async-resume.ts +29 -27
- package/src/runs/background/async-status-snapshot.ts +23 -261
- package/src/runs/background/async-status.ts +68 -7
- package/src/runs/background/chain-append.ts +8 -3
- package/src/runs/background/chain-root-attachment.ts +60 -8
- package/src/runs/background/fleet-view.ts +21 -11
- package/src/runs/background/notify.ts +184 -9
- package/src/runs/background/result-delivery-ownership.ts +45 -0
- package/src/runs/background/result-files.ts +2 -1
- package/src/runs/background/result-watcher.ts +29 -10
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/retained-children.ts +1 -1
- package/src/runs/background/run-status.ts +18 -8
- package/src/runs/background/scheduled-runs.ts +86 -7
- package/src/runs/background/stale-run-reconciler.ts +10 -4
- package/src/runs/background/steering.ts +4 -14
- package/src/runs/background/subagent-runner.ts +389 -350
- package/src/runs/background/subagent-wait.ts +58 -10
- package/src/runs/background/terminal-run-index.ts +1 -1
- package/src/runs/background/wait-completions.ts +22 -1
- package/src/runs/background/wait-config.ts +23 -9
- package/src/runs/background/wait-tool.ts +9 -2
- package/src/runs/foreground/async-steering-action.ts +2 -2
- package/src/runs/foreground/execution.ts +134 -121
- package/src/runs/foreground/foreground-control.ts +3 -0
- package/src/runs/foreground/foreground-history.ts +1 -0
- package/src/runs/foreground/subagent-executor.ts +527 -257
- package/src/runs/foreground/workflow-detach-reconcile.ts +130 -120
- package/src/runs/shared/abort-recovery.ts +119 -0
- package/src/runs/shared/async-status-projection.ts +463 -0
- package/src/runs/shared/child-identity.ts +19 -4
- package/src/runs/shared/child-launch-plan.ts +151 -0
- package/src/runs/shared/completion-evidence.ts +89 -0
- package/src/runs/shared/completion-guard.ts +5 -4
- package/src/runs/shared/dynamic-fanout.ts +3 -3
- package/src/runs/shared/fast-mode-extension.ts +5 -5
- package/src/runs/shared/host-step-status.ts +230 -0
- package/src/runs/shared/lane-metadata.ts +105 -0
- package/src/runs/shared/launch-cwd.ts +16 -0
- package/src/runs/shared/long-running-guard.ts +2 -1
- package/src/runs/shared/mcp-config-sources.ts +422 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
- package/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
- package/src/runs/shared/model-exclusions.ts +12 -0
- package/src/runs/shared/model-fallback.ts +25 -6
- package/src/runs/shared/nested-events.ts +6 -2
- package/src/runs/shared/nested-render.ts +7 -3
- package/src/runs/shared/parallel-handoff.ts +419 -7
- package/src/runs/shared/parallel-utils.ts +15 -1
- package/src/runs/shared/pi-args.ts +81 -8
- package/src/runs/shared/single-output.ts +44 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +98 -13
- package/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/src/runs/shared/worktree.ts +18 -0
- package/src/shared/child-session-name.ts +46 -0
- package/src/shared/extension-context.ts +24 -0
- package/src/shared/formatters.ts +11 -2
- package/src/shared/launch-contract.ts +5 -1
- package/src/shared/settings.ts +9 -103
- package/src/shared/types.ts +240 -30
- package/src/shared/utils.ts +41 -84
- package/src/slash/delegation-adapters.ts +1 -8
- package/src/slash/delegation-request.ts +0 -4
- package/src/slash/slash-bridge.ts +1 -2
- package/src/slash/slash-commands.ts +369 -90
- package/src/slash/slash-live-state.ts +22 -11
- package/src/slash/subagents-admin.ts +3 -0
- package/src/tui/fleet-status.ts +125 -74
- package/src/tui/fleet.ts +11 -5
- package/src/tui/render.ts +330 -33
- package/src/watchdog/turn-delta.ts +1 -1
- package/src/workflows/chat-progress.ts +6 -3
- package/src/workflows/host-command.ts +230 -0
- package/src/workflows/scripted-workflow.ts +496 -35
- package/src/workflows/workflow-child-summary.ts +9 -5
- package/src/workflows/workflow-preflight.ts +270 -0
- package/src/workflows/workflow-receipt.ts +79 -5
- package/src/workflows/workflow-settlement.ts +246 -0
- package/src/runs/shared/turn-budget.ts +0 -98
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
findLatestSessionFile,
|
|
47
47
|
detectSubagentError,
|
|
48
48
|
hasEmptyTerminalAssistantResponse,
|
|
49
|
+
formatEmptyTerminalAssistantResponseError,
|
|
49
50
|
extractToolArgsPreview,
|
|
50
51
|
extractTextFromContent,
|
|
51
52
|
boundStreamedRecentTools,
|
|
@@ -55,14 +56,18 @@ import {
|
|
|
55
56
|
import { buildSkillInjection, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
56
57
|
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
57
58
|
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, resolveToolTimeoutMs, toolTimeoutCallKey, toolTimeoutFromEnv } from "../shared/tool-timeout.ts";
|
|
58
|
-
import { evaluateCompletionMutationGuard, validateImplementationToolContract } from "../shared/completion-guard.ts";
|
|
59
|
+
import { evaluateCompletionMutationGuard, expectsImplementationMutation, hasMutationToolCapability, validateImplementationToolContract } from "../shared/completion-guard.ts";
|
|
60
|
+
import { planCompletionEvidence } from "../shared/completion-evidence.ts";
|
|
61
|
+
import { planAbortRecovery } from "../shared/abort-recovery.ts";
|
|
59
62
|
import { arbitrateCompletionGuardRescue } from "../shared/llm-intent-arbiter.ts";
|
|
60
63
|
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
64
|
+
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
61
65
|
import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
62
66
|
import { createOrcaProgressTab, type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
63
67
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
64
68
|
import { resolvePermissionRules } from "../shared/permissions.ts";
|
|
65
69
|
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
70
|
+
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
66
71
|
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
|
|
67
72
|
import { assertAgentAllowedByCapabilityCeiling, decodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV } from "../shared/capability-ceiling.ts";
|
|
68
73
|
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
@@ -102,7 +107,6 @@ import {
|
|
|
102
107
|
import { acceptanceFailureMessage, buildSkippedAcceptanceLedger, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport, validateAcceptanceInput } from "../shared/acceptance.ts";
|
|
103
108
|
import { PROMPT_REDACTED } from "../../shared/utils.ts";
|
|
104
109
|
import { attachContractProjections, isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
105
|
-
import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, turnBudgetDecision, turnBudgetDeferredNote, turnBudgetDeferredState, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
|
|
106
110
|
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
|
|
107
111
|
import { resolveWatchdogConfig } from "../../watchdog/settings.ts";
|
|
108
112
|
import { agentDefinitionDigest, launchBindingDigest } from "../../shared/launch-contract.ts";
|
|
@@ -293,6 +297,9 @@ function snapshotStreamResult(result: SingleResult, progress: AgentProgress): Si
|
|
|
293
297
|
return snapshot;
|
|
294
298
|
}
|
|
295
299
|
|
|
300
|
+
const AFTER_COMPACTION_SETTLEMENT = Symbol("afterCompactionSettlement");
|
|
301
|
+
type AbortRecoverySingleResult = SingleResult & { [AFTER_COMPACTION_SETTLEMENT]?: true };
|
|
302
|
+
|
|
296
303
|
async function runSingleAttempt(
|
|
297
304
|
runtimeCwd: string,
|
|
298
305
|
agent: AgentConfig,
|
|
@@ -322,6 +329,10 @@ async function runSingleAttempt(
|
|
|
322
329
|
assertThinkingWithinCeiling({ model: modelArg, configThinking: effectiveThinking, ceiling: options.thinkingCeiling, agent: agent.name, runId: options.runId });
|
|
323
330
|
const expectedModelForVerification = shared.verifyModel ? modelArg : undefined;
|
|
324
331
|
const resolvedThinking = resolveEffectiveThinking(modelArg, effectiveThinking);
|
|
332
|
+
// Display name for the child session: applied inside the child via
|
|
333
|
+
// PI_SUBAGENT_SESSION_NAME and echoed back on the result payload so hosts
|
|
334
|
+
// can label this run without reading the child's session file.
|
|
335
|
+
const childSessionName = deriveChildSessionName({ agent: agent.name, task: shared.originalTask ?? task });
|
|
325
336
|
const watchdogConfig = resolveWatchdogConfig(options.cwd ?? runtimeCwd);
|
|
326
337
|
const childWatchdog = watchdogConfig.ok
|
|
327
338
|
? resolveChildWatchdogConfig({
|
|
@@ -346,16 +357,19 @@ async function runSingleAttempt(
|
|
|
346
357
|
thinking: effectiveThinking,
|
|
347
358
|
systemPromptMode: agent.systemPromptMode,
|
|
348
359
|
inheritProjectContext: agent.inheritProjectContext,
|
|
360
|
+
inheritGlobalContext: agent.inheritGlobalContext,
|
|
349
361
|
inheritSkills: agent.inheritSkills,
|
|
350
362
|
requireReadTool: Boolean(shared.resolvedSkillNames?.length),
|
|
351
363
|
tools: agent.tools,
|
|
364
|
+
allowNestedSubagents: agent.allowNestedSubagents,
|
|
352
365
|
extensions: agent.extensions,
|
|
353
366
|
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
354
|
-
systemPrompt:
|
|
367
|
+
systemPrompt: shared.systemPrompt,
|
|
355
368
|
mcpDirectTools: agent.mcpDirectTools,
|
|
356
369
|
cwd: options.cwd ?? runtimeCwd,
|
|
357
370
|
promptFileStem: agent.name,
|
|
358
371
|
intercomSessionName: options.intercomSessionName,
|
|
372
|
+
sessionName: childSessionName,
|
|
359
373
|
orchestratorIntercomTarget: options.orchestratorIntercomTarget,
|
|
360
374
|
runId: options.runId,
|
|
361
375
|
childAgentName: agent.name,
|
|
@@ -378,18 +392,21 @@ async function runSingleAttempt(
|
|
|
378
392
|
permissionAuditPath,
|
|
379
393
|
childWatchdog,
|
|
380
394
|
waitToolEnabled: options.waitToolEnabled,
|
|
395
|
+
waitToolDefaultTimeoutMs: options.waitToolDefaultTimeoutMs,
|
|
381
396
|
capabilityCeiling: options.capabilityCeiling,
|
|
382
397
|
thinkingCeiling: options.thinkingCeiling,
|
|
383
398
|
extensionBindings: options.extensionBindings,
|
|
399
|
+
runtimeSnapshotHost: options.runtimeSnapshotHost,
|
|
384
400
|
});
|
|
385
401
|
if (!shared.launchWarnings.emitted && warnings.length > 0) {
|
|
386
402
|
for (const warning of warnings) console.warn(`[pi-subagents] ${warning}`);
|
|
387
403
|
shared.launchWarnings.emitted = true;
|
|
388
404
|
}
|
|
389
405
|
|
|
390
|
-
const effectiveSystemPrompt =
|
|
406
|
+
const effectiveSystemPrompt = shared.systemPrompt;
|
|
391
407
|
const toolPlan = resolvePiLaunchToolPlan({
|
|
392
408
|
tools: agent.tools,
|
|
409
|
+
allowNestedSubagents: agent.allowNestedSubagents,
|
|
393
410
|
extensions: agent.extensions,
|
|
394
411
|
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
395
412
|
mcpDirectTools: agent.mcpDirectTools,
|
|
@@ -403,6 +420,7 @@ async function runSingleAttempt(
|
|
|
403
420
|
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
|
|
404
421
|
agentName: agent.name,
|
|
405
422
|
permissionRules,
|
|
423
|
+
runtimeSnapshotHost: options.runtimeSnapshotHost,
|
|
406
424
|
});
|
|
407
425
|
const contractTools = toolPlan.explicitToolAllowlist ? toolPlan.effectiveToolAllowlist : undefined;
|
|
408
426
|
const contractError = validateImplementationToolContract({
|
|
@@ -421,6 +439,7 @@ async function runSingleAttempt(
|
|
|
421
439
|
index: options.index ?? 0,
|
|
422
440
|
agent: agent.name,
|
|
423
441
|
task,
|
|
442
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
424
443
|
messages: [],
|
|
425
444
|
finalOutput: "",
|
|
426
445
|
exitCode: 1,
|
|
@@ -446,6 +465,7 @@ async function runSingleAttempt(
|
|
|
446
465
|
systemPrompt: effectiveSystemPrompt,
|
|
447
466
|
systemPromptMode: agent.systemPromptMode,
|
|
448
467
|
inheritProjectContext: agent.inheritProjectContext,
|
|
468
|
+
inheritGlobalContext: agent.inheritGlobalContext,
|
|
449
469
|
inheritSkills: agent.inheritSkills,
|
|
450
470
|
skills: shared.resolvedSkillNames ?? [],
|
|
451
471
|
tools: toolPlan.effectiveToolAllowlist,
|
|
@@ -460,6 +480,7 @@ async function runSingleAttempt(
|
|
|
460
480
|
index: options.index ?? 0,
|
|
461
481
|
agent: agent.name,
|
|
462
482
|
task: shared.originalTask ?? task,
|
|
483
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
463
484
|
...(options.agentContract ? { agentContract: options.agentContract } : {}),
|
|
464
485
|
launchContractDigest,
|
|
465
486
|
launchResolvedExtensions,
|
|
@@ -473,7 +494,6 @@ async function runSingleAttempt(
|
|
|
473
494
|
transcriptPath: shared.transcriptWriter ? shared.artifactPaths?.transcriptPath : undefined,
|
|
474
495
|
skills: shared.resolvedSkillNames,
|
|
475
496
|
skillsWarning: shared.skillsWarning,
|
|
476
|
-
...(options.turnBudget ? { turnBudget: initialTurnBudgetState(options.turnBudget) } : {}),
|
|
477
497
|
...(options.toolBudget ? { toolBudget: initialToolBudgetState(options.toolBudget) } : {}),
|
|
478
498
|
...(options.capabilityCeiling ? { capabilityCeiling: options.capabilityCeiling } : {}),
|
|
479
499
|
...(capabilityAudit ? { capabilityAudit } : {}),
|
|
@@ -502,6 +522,7 @@ async function runSingleAttempt(
|
|
|
502
522
|
const progress: AgentProgress = {
|
|
503
523
|
index: options.index ?? 0,
|
|
504
524
|
agent: agent.name,
|
|
525
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
505
526
|
status: "running",
|
|
506
527
|
task,
|
|
507
528
|
skills: shared.resolvedSkillNames,
|
|
@@ -561,6 +582,7 @@ async function runSingleAttempt(
|
|
|
561
582
|
return result;
|
|
562
583
|
}
|
|
563
584
|
}
|
|
585
|
+
let afterCompactionSettlement = false;
|
|
564
586
|
const exitCode = await new Promise<number>((resolve) => {
|
|
565
587
|
const proc = spawn(spawnSpec.command, spawnSpec.args, {
|
|
566
588
|
cwd: options.cwd ?? runtimeCwd,
|
|
@@ -580,20 +602,7 @@ async function runSingleAttempt(
|
|
|
580
602
|
let timeoutTimer: NodeJS.Timeout | undefined;
|
|
581
603
|
let timeoutTerminationTimer: NodeJS.Timeout | undefined;
|
|
582
604
|
let timeoutHardKillTimer: NodeJS.Timeout | undefined;
|
|
583
|
-
let turnBudgetSoftReached = false;
|
|
584
|
-
let turnBudgetTerminationTimer: NodeJS.Timeout | undefined;
|
|
585
|
-
let turnBudgetHardKillTimer: NodeJS.Timeout | undefined;
|
|
586
605
|
let protocolHardKillTimer: NodeJS.Timeout | undefined;
|
|
587
|
-
const clearTurnBudgetTimers = () => {
|
|
588
|
-
if (turnBudgetTerminationTimer) {
|
|
589
|
-
clearTimeout(turnBudgetTerminationTimer);
|
|
590
|
-
turnBudgetTerminationTimer = undefined;
|
|
591
|
-
}
|
|
592
|
-
if (turnBudgetHardKillTimer) {
|
|
593
|
-
clearTimeout(turnBudgetHardKillTimer);
|
|
594
|
-
turnBudgetHardKillTimer = undefined;
|
|
595
|
-
}
|
|
596
|
-
};
|
|
597
606
|
const clearTimeoutTimers = () => {
|
|
598
607
|
if (timeoutTimer) {
|
|
599
608
|
clearTimeout(timeoutTimer);
|
|
@@ -656,6 +665,7 @@ async function runSingleAttempt(
|
|
|
656
665
|
let forcedTerminationSignal = false;
|
|
657
666
|
let cleanTerminalAssistantStopReceived = false;
|
|
658
667
|
let agentSettledReceived = false;
|
|
668
|
+
let compactionStartedReceived = false;
|
|
659
669
|
let finalDrainTimer: NodeJS.Timeout | undefined;
|
|
660
670
|
let finalHardKillTimer: NodeJS.Timeout | undefined;
|
|
661
671
|
let watchdogTailTimer: NodeJS.Timeout | undefined;
|
|
@@ -754,7 +764,6 @@ async function runSingleAttempt(
|
|
|
754
764
|
clearStdioGuard();
|
|
755
765
|
clearTimeoutTimers();
|
|
756
766
|
clearAllToolTimeouts();
|
|
757
|
-
clearTurnBudgetTimers();
|
|
758
767
|
if (protocolHardKillTimer) {
|
|
759
768
|
clearTimeout(protocolHardKillTimer);
|
|
760
769
|
protocolHardKillTimer = undefined;
|
|
@@ -892,57 +901,6 @@ async function runSingleAttempt(
|
|
|
892
901
|
}));
|
|
893
902
|
return true;
|
|
894
903
|
};
|
|
895
|
-
const requestTurnBudgetAbort = (turnCount: number) => {
|
|
896
|
-
const budget = options.turnBudget;
|
|
897
|
-
if (!budget || result.timedOut || result.turnBudgetExceeded || interruptedByControl || processClosed || lifecycleFinished) return;
|
|
898
|
-
const message = turnBudgetExceededMessage(budget, turnCount);
|
|
899
|
-
result.turnBudgetExceeded = true;
|
|
900
|
-
result.wrapUpRequested = true;
|
|
901
|
-
result.turnBudget = turnBudgetState(budget, turnCount, true);
|
|
902
|
-
result.error = message;
|
|
903
|
-
result.finalOutput = message;
|
|
904
|
-
progress.status = "failed";
|
|
905
|
-
progress.error = message;
|
|
906
|
-
progress.durationMs = Date.now() - startTime;
|
|
907
|
-
fireUpdate();
|
|
908
|
-
trySignalChild(proc, "SIGINT");
|
|
909
|
-
turnBudgetTerminationTimer = setTimeout(() => {
|
|
910
|
-
if (processClosed || lifecycleFinished || result.timedOut) return;
|
|
911
|
-
trySignalChild(proc, "SIGTERM");
|
|
912
|
-
}, 1000);
|
|
913
|
-
turnBudgetTerminationTimer.unref?.();
|
|
914
|
-
turnBudgetHardKillTimer = setTimeout(() => {
|
|
915
|
-
if (processClosed || lifecycleFinished || result.timedOut) return;
|
|
916
|
-
trySignalChild(proc, "SIGKILL");
|
|
917
|
-
}, 4000);
|
|
918
|
-
turnBudgetHardKillTimer.unref?.();
|
|
919
|
-
};
|
|
920
|
-
|
|
921
|
-
const updateTurnBudget = (turnCount: number, terminalAssistantStop: boolean, toolWorkActiveOrStarting: boolean) => {
|
|
922
|
-
const budget = options.turnBudget;
|
|
923
|
-
if (!budget || result.timedOut || result.turnBudgetExceeded) return;
|
|
924
|
-
if (turnCount < budget.maxTurns) {
|
|
925
|
-
result.turnBudget = { ...budget, outcome: "within-budget", turnCount };
|
|
926
|
-
return;
|
|
927
|
-
}
|
|
928
|
-
if (!turnBudgetSoftReached) {
|
|
929
|
-
turnBudgetSoftReached = true;
|
|
930
|
-
result.wrapUpRequested = true;
|
|
931
|
-
appendRecentOutput(progress, [turnBudgetSoftNote(budget, turnCount)]);
|
|
932
|
-
}
|
|
933
|
-
const decision = turnBudgetDecision(budget, turnCount, terminalAssistantStop, toolWorkActiveOrStarting, options.enforceHardTurnLimit);
|
|
934
|
-
if (decision === "defer") {
|
|
935
|
-
result.turnBudget = turnBudgetDeferredState(
|
|
936
|
-
budget,
|
|
937
|
-
turnCount,
|
|
938
|
-
result.turnBudget?.terminationDeferredAtTurn,
|
|
939
|
-
);
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
result.turnBudget = turnBudgetState(budget, turnCount, false);
|
|
943
|
-
if (decision === "abort") requestTurnBudgetAbort(turnCount);
|
|
944
|
-
};
|
|
945
|
-
|
|
946
904
|
const updateActivityState = (now: number): boolean => {
|
|
947
905
|
if (!controlConfig.enabled) return false;
|
|
948
906
|
const idleState = deriveActivityState({
|
|
@@ -996,7 +954,7 @@ async function runSingleAttempt(
|
|
|
996
954
|
const fireUpdate = () => {
|
|
997
955
|
if (!options.onUpdate || processClosed) return;
|
|
998
956
|
progress.durationMs = Date.now() - startTime;
|
|
999
|
-
const output =
|
|
957
|
+
const output = result.timedOut && result.finalOutput ? result.finalOutput : getFinalOutput(result.messages ?? []);
|
|
1000
958
|
emitUpdateSnapshot(output || "(running...)");
|
|
1001
959
|
};
|
|
1002
960
|
|
|
@@ -1016,8 +974,20 @@ async function runSingleAttempt(
|
|
|
1016
974
|
}
|
|
1017
975
|
shared.transcriptWriter?.writeChildEvent(evt);
|
|
1018
976
|
shared.orcaProgressTab?.event(evt);
|
|
977
|
+
if (evt.type === "compaction_start") compactionStartedReceived = true;
|
|
978
|
+
if (evt.type === "compaction_end" && evt.willRetry === true) {
|
|
979
|
+
compactionStartedReceived = false;
|
|
980
|
+
afterCompactionSettlement = false;
|
|
981
|
+
}
|
|
982
|
+
if (evt.type === "agent_start" || evt.type === "auto_retry_start") {
|
|
983
|
+
compactionStartedReceived = false;
|
|
984
|
+
afterCompactionSettlement = false;
|
|
985
|
+
}
|
|
1019
986
|
const lifecycleAction = projectChildLifecycle(evt, false, childLifecycleState);
|
|
1020
|
-
if (evt.type === "agent_settled" && lifecycleAction === "start-drain")
|
|
987
|
+
if (evt.type === "agent_settled" && lifecycleAction === "start-drain") {
|
|
988
|
+
agentSettledReceived = true;
|
|
989
|
+
afterCompactionSettlement = compactionStartedReceived;
|
|
990
|
+
}
|
|
1021
991
|
applyChildLifecycle(lifecycleAction);
|
|
1022
992
|
|
|
1023
993
|
if (isChildWatchdogStatusEvent(evt)) {
|
|
@@ -1064,7 +1034,7 @@ async function runSingleAttempt(
|
|
|
1064
1034
|
if (options.toolBudget) {
|
|
1065
1035
|
result.toolBudget = toolBudgetState(options.toolBudget, progress.toolCount);
|
|
1066
1036
|
}
|
|
1067
|
-
const mutates = isMutatingTool(evt.toolName, toolArgs);
|
|
1037
|
+
const mutates = isMutatingTool(evt.toolName, toolArgs, agent.mutationTools);
|
|
1068
1038
|
observedMutationAttempt = observedMutationAttempt || mutates;
|
|
1069
1039
|
pendingToolResult = { tool: evt.toolName ?? "tool", path: activeTool?.path, mutates, startedAt: now };
|
|
1070
1040
|
fireUpdate();
|
|
@@ -1089,16 +1059,11 @@ async function runSingleAttempt(
|
|
|
1089
1059
|
if (evt.message.role === "assistant") {
|
|
1090
1060
|
result.usage.turns++;
|
|
1091
1061
|
progress.turnCount = result.usage.turns;
|
|
1092
|
-
const stopReason = (evt.message as { stopReason?: string }).stopReason;
|
|
1093
1062
|
const toolCalls = Array.isArray(evt.message.content)
|
|
1094
1063
|
? evt.message.content.filter((part) => (part as { type?: string }).type === "toolCall")
|
|
1095
1064
|
: [];
|
|
1096
1065
|
const hasToolCall = toolCalls.length > 0;
|
|
1097
|
-
const terminalAssistantStop = stopReason === "stop" && !hasToolCall;
|
|
1098
|
-
const terminalStructuredOutputCall = Boolean(options.structuredOutput)
|
|
1099
|
-
&& toolCalls.length === 1
|
|
1100
|
-
&& (toolCalls[0] as { name?: string }).name === "structured_output";
|
|
1101
|
-
updateTurnBudget(result.usage.turns, terminalAssistantStop || terminalStructuredOutputCall, hasToolCall || Boolean(progress.currentTool));
|
|
1066
|
+
const terminalAssistantStop = (evt.message as { stopReason?: string }).stopReason === "stop" && !hasToolCall;
|
|
1102
1067
|
const u = evt.message.usage;
|
|
1103
1068
|
if (u) {
|
|
1104
1069
|
const window = (u.input || 0) + (u.cacheRead || 0);
|
|
@@ -1358,14 +1323,17 @@ async function runSingleAttempt(
|
|
|
1358
1323
|
const rawStdout = rawStdoutTail.text();
|
|
1359
1324
|
let closeError = result.error ?? toolDiagnosticError ?? assistantError;
|
|
1360
1325
|
const forcedDrainAfterFinalSuccess = Boolean(forcedTerminationSignal || signal) && (cleanTerminalAssistantStopReceived || agentSettledReceived) && !closeError;
|
|
1326
|
+
const forcedDrainAfterEmptyTerminal = forcedDrainAfterFinalSuccess && hasEmptyTerminalAssistantResponse(result.messages ?? []);
|
|
1361
1327
|
if (signal) result.processSignal = signal;
|
|
1328
|
+
if (!closeError && forcedDrainAfterEmptyTerminal && stderr.trim()) {
|
|
1329
|
+
closeError = stderr.trim();
|
|
1330
|
+
}
|
|
1362
1331
|
if (!closeError && isUnexplainedProcessSignal({
|
|
1363
1332
|
processSignal: signal,
|
|
1364
1333
|
interrupted: result.interrupted,
|
|
1365
1334
|
timedOut: result.timedOut,
|
|
1366
1335
|
stopped: result.stopped,
|
|
1367
|
-
|
|
1368
|
-
forcedDrainAfterFinalSuccess,
|
|
1336
|
+
forcedDrainAfterFinalSuccess: forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal,
|
|
1369
1337
|
})) {
|
|
1370
1338
|
closeError = formatProcessSignalError(signal!);
|
|
1371
1339
|
}
|
|
@@ -1375,7 +1343,7 @@ async function runSingleAttempt(
|
|
|
1375
1343
|
if (code !== 0 && stderr.trim() && !closeError && !forcedDrainAfterFinalSuccess) {
|
|
1376
1344
|
closeError = stderr.trim();
|
|
1377
1345
|
}
|
|
1378
|
-
const finalCode = forcedDrainAfterFinalSuccess ? 0 : forcedTerminationSignal || signal ? (code ?? 1) : (code ?? 0);
|
|
1346
|
+
const finalCode = forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal ? 0 : forcedTerminationSignal || signal ? (code ?? 1) : (code ?? 0);
|
|
1379
1347
|
if (!result.error && closeError) result.error = closeError;
|
|
1380
1348
|
finish(finalCode);
|
|
1381
1349
|
});
|
|
@@ -1447,6 +1415,9 @@ async function runSingleAttempt(
|
|
|
1447
1415
|
}
|
|
1448
1416
|
});
|
|
1449
1417
|
result.exitCode = exitCode;
|
|
1418
|
+
if (afterCompactionSettlement) {
|
|
1419
|
+
(result as AbortRecoverySingleResult)[AFTER_COMPACTION_SETTLEMENT] = true;
|
|
1420
|
+
}
|
|
1450
1421
|
if (interruptedByControl) {
|
|
1451
1422
|
result.exitCode = 0;
|
|
1452
1423
|
result.interrupted = true;
|
|
@@ -1467,7 +1438,6 @@ async function runSingleAttempt(
|
|
|
1467
1438
|
&& !abortedBySignal
|
|
1468
1439
|
&& !result.timedOut
|
|
1469
1440
|
&& !result.stopped
|
|
1470
|
-
&& !result.turnBudgetExceeded
|
|
1471
1441
|
&& !result.protocolError
|
|
1472
1442
|
&& !toolAvailabilityError) {
|
|
1473
1443
|
const processExitCode = result.exitCode;
|
|
@@ -1520,9 +1490,12 @@ async function runSingleAttempt(
|
|
|
1520
1490
|
: messages;
|
|
1521
1491
|
const errInfo = detectSubagentError(errorMessages);
|
|
1522
1492
|
const missingOutput = !finalText?.trim() && !validatedStructuredOutput;
|
|
1523
|
-
|
|
1493
|
+
const terminalEmptyAfterUsefulWork = !validatedStructuredOutput
|
|
1494
|
+
&& hasEmptyTerminalAssistantResponse(messages)
|
|
1495
|
+
&& (progress.toolCount > 0 || Boolean(finalText?.trim()));
|
|
1496
|
+
if ((missingOutput || terminalEmptyAfterUsefulWork) && (!errInfo.hasError || hasEmptyTerminalAssistantResponse(messages))) {
|
|
1524
1497
|
result.exitCode = 1;
|
|
1525
|
-
result.error =
|
|
1498
|
+
result.error = formatEmptyTerminalAssistantResponseError(messages);
|
|
1526
1499
|
} else if (errInfo.hasError) {
|
|
1527
1500
|
result.exitCode = errInfo.exitCode ?? 1;
|
|
1528
1501
|
result.error = errInfo.details
|
|
@@ -1541,6 +1514,7 @@ async function runSingleAttempt(
|
|
|
1541
1514
|
}
|
|
1542
1515
|
|
|
1543
1516
|
result.progressSummary = {
|
|
1517
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
1544
1518
|
toolCount: progress.toolCount,
|
|
1545
1519
|
tokens: progress.tokens,
|
|
1546
1520
|
durationMs: progress.durationMs,
|
|
@@ -1566,14 +1540,6 @@ async function runSingleAttempt(
|
|
|
1566
1540
|
fullOutput = fullOutput.trim()
|
|
1567
1541
|
? `${timeoutMessage}\n\n${result.timeoutRecovery.message}\n\nPartial output before timeout:\n${fullOutput}`
|
|
1568
1542
|
: `${timeoutMessage}\n\n${result.timeoutRecovery.message}`;
|
|
1569
|
-
} else if (result.turnBudgetExceeded && result.turnBudget) {
|
|
1570
|
-
fullOutput = formatTurnBudgetOutput(turnBudgetExceededMessage(result.turnBudget, result.turnBudget.turnCount), fullOutput);
|
|
1571
|
-
} else if (result.turnBudget?.outcome === "termination-deferred") {
|
|
1572
|
-
const note = turnBudgetDeferredNote(result.turnBudget, result.turnBudget.terminationDeferredAtTurn ?? result.turnBudget.turnCount);
|
|
1573
|
-
fullOutput = fullOutput.trim() ? `${note}\n\n${fullOutput}` : note;
|
|
1574
|
-
} else if (result.wrapUpRequested && result.turnBudget?.outcome === "wrap-up-requested") {
|
|
1575
|
-
const note = turnBudgetSoftNote(result.turnBudget, result.turnBudget.wrapUpRequestedAtTurn ?? result.turnBudget.turnCount);
|
|
1576
|
-
fullOutput = fullOutput.trim() ? `${note}\n\n${fullOutput}` : note;
|
|
1577
1543
|
}
|
|
1578
1544
|
const completionGuardEnabled = isAgentContractV1(options.agentContract) ? agent.completionGuard === true : agent.completionGuard !== false;
|
|
1579
1545
|
const completionGuard = ((result.exitCode === 0 && !result.error) || toolAvailabilityError) && completionGuardEnabled
|
|
@@ -1583,13 +1549,13 @@ async function runSingleAttempt(
|
|
|
1583
1549
|
messages: result.messages ?? [],
|
|
1584
1550
|
tools: contractTools,
|
|
1585
1551
|
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
1552
|
+
mutationTools: agent.mutationTools,
|
|
1586
1553
|
toolAvailabilityError,
|
|
1587
1554
|
mutationEvidence,
|
|
1588
1555
|
})
|
|
1589
1556
|
: undefined;
|
|
1590
1557
|
const mutationAttemptObserved = observedMutationAttempt || mutationEvidence.attemptedMutation;
|
|
1591
1558
|
let completionGuardTriggered = completionGuard?.triggered === true && !mutationAttemptObserved;
|
|
1592
|
-
const completionGuardBlocked = completionGuard?.blocked === true;
|
|
1593
1559
|
// The classifier is deliberately narrow, so a read-only review task can
|
|
1594
1560
|
// still be misread as implementation. Arbitrate BEFORE any failure side
|
|
1595
1561
|
// effect is published (effects, exit code, progress, notifications,
|
|
@@ -1606,31 +1572,26 @@ async function runSingleAttempt(
|
|
|
1606
1572
|
completionGuardTriggered = arbitration.triggered;
|
|
1607
1573
|
arbiterRescued = arbitration.rescued;
|
|
1608
1574
|
}
|
|
1609
|
-
|
|
1575
|
+
const completionEvidence = planCompletionEvidence({
|
|
1576
|
+
guard: completionGuard,
|
|
1577
|
+
guardTriggered: completionGuardTriggered,
|
|
1578
|
+
completionGuardEnabled,
|
|
1579
|
+
mutationCapable: hasMutationToolCapability(contractTools, toolPlan.effectiveMcpTools),
|
|
1580
|
+
implementationMutationExpected: expectsImplementationMutation(agent.name, shared.originalTask ?? task),
|
|
1581
|
+
mutationAttemptObserved,
|
|
1582
|
+
mutationEvidence,
|
|
1583
|
+
arbiterRescued,
|
|
1584
|
+
agentContractV1: isAgentContractV1(options.agentContract),
|
|
1585
|
+
});
|
|
1586
|
+
if (completionEvidence.fileMutation) {
|
|
1610
1587
|
result.effects = {
|
|
1611
1588
|
...(result.effects ?? {}),
|
|
1612
|
-
fileMutation:
|
|
1613
|
-
status: completionGuardBlocked
|
|
1614
|
-
? "blocked"
|
|
1615
|
-
: completionGuard.expectedMutation
|
|
1616
|
-
? completionGuardTriggered
|
|
1617
|
-
? "missing"
|
|
1618
|
-
: arbiterRescued
|
|
1619
|
-
? "not-applicable"
|
|
1620
|
-
: "observed"
|
|
1621
|
-
: "not-applicable",
|
|
1622
|
-
expected: completionGuard.expectedMutation,
|
|
1623
|
-
attempted: completionGuardBlocked ? false : completionGuard.attemptedMutation || mutationAttemptObserved,
|
|
1624
|
-
evidence: mutationEvidence,
|
|
1625
|
-
...(completionGuardBlocked && completionGuard.message ? { message: completionGuard.message } : {}),
|
|
1626
|
-
...(completionGuardTriggered ? { message: "Subagent completed without making edits for an implementation task." } : {}),
|
|
1627
|
-
...(arbiterRescued ? { resolvedBy: "llm-intent-arbiter" } : {}),
|
|
1628
|
-
},
|
|
1589
|
+
fileMutation: completionEvidence.fileMutation,
|
|
1629
1590
|
};
|
|
1630
1591
|
}
|
|
1631
|
-
if (
|
|
1592
|
+
if (completionEvidence.legacyFailureError) {
|
|
1632
1593
|
result.exitCode = 1;
|
|
1633
|
-
result.error =
|
|
1594
|
+
result.error = completionEvidence.legacyFailureError;
|
|
1634
1595
|
progress.status = "failed";
|
|
1635
1596
|
progress.error = result.error;
|
|
1636
1597
|
emitControlEvent(buildControlEvent({
|
|
@@ -1645,10 +1606,14 @@ async function runSingleAttempt(
|
|
|
1645
1606
|
}));
|
|
1646
1607
|
}
|
|
1647
1608
|
if (options.outputPath && result.exitCode === 0) {
|
|
1648
|
-
const resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot);
|
|
1609
|
+
const resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot, options.outputClaimPath);
|
|
1649
1610
|
fullOutput = stripAcceptanceReport(resolvedOutput.fullOutput);
|
|
1650
1611
|
result.savedOutputPath = resolvedOutput.savedPath;
|
|
1651
1612
|
result.outputSaveError = resolvedOutput.saveError;
|
|
1613
|
+
if (resolvedOutput.fatalError) {
|
|
1614
|
+
result.exitCode = 1;
|
|
1615
|
+
result.error = result.error ? `${result.error}\n${resolvedOutput.saveError}` : resolvedOutput.saveError;
|
|
1616
|
+
}
|
|
1652
1617
|
if (resolvedOutput.savedPath) {
|
|
1653
1618
|
result.outputReference = formatSavedOutputReference(resolvedOutput.savedPath, fullOutput);
|
|
1654
1619
|
if (result.outputState === "absent") result.outputState = "unknown";
|
|
@@ -1688,10 +1653,24 @@ async function runSyncCompletionInner(
|
|
|
1688
1653
|
task: string,
|
|
1689
1654
|
options: RunSyncOptions,
|
|
1690
1655
|
): Promise<SingleResult> {
|
|
1656
|
+
const effectiveCwd = options.cwd ?? runtimeCwd;
|
|
1657
|
+
const cwdError = preflightLaunchCwd(options.requestedCwd ?? effectiveCwd, effectiveCwd);
|
|
1658
|
+
if (cwdError) {
|
|
1659
|
+
return redactResultPrompt(withRunContext({
|
|
1660
|
+
index: options.index ?? 0,
|
|
1661
|
+
agent: agentName,
|
|
1662
|
+
task,
|
|
1663
|
+
exitCode: 1,
|
|
1664
|
+
messages: [],
|
|
1665
|
+
usage: emptyUsage(),
|
|
1666
|
+
error: cwdError,
|
|
1667
|
+
}, options.context));
|
|
1668
|
+
}
|
|
1691
1669
|
options = {
|
|
1692
1670
|
...options,
|
|
1693
1671
|
capabilityCeiling: intersectSubagentCapabilityCeilings(options.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(options.parentSessionId), decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV])),
|
|
1694
1672
|
};
|
|
1673
|
+
const childSessionName = deriveChildSessionName({ agent: agentName, task });
|
|
1695
1674
|
const agent = agents.find((a) => a.name === agentName);
|
|
1696
1675
|
if (!agent) {
|
|
1697
1676
|
const diagnosticContext = options.unknownAgentDiagnosticContext
|
|
@@ -1934,12 +1913,16 @@ async function runSyncCompletionInner(
|
|
|
1934
1913
|
// Escalated to "file" after an unexplained zero-activity startup failure so
|
|
1935
1914
|
// retries keep the task text out of argv (endpoint pre-exec scans may deny it).
|
|
1936
1915
|
let taskDeliveryOverride: SubagentTaskDelivery | undefined;
|
|
1916
|
+
let abortRecoveryAttempted = false;
|
|
1917
|
+
let nextAttemptTask = taskWithAcceptance;
|
|
1937
1918
|
modelAttemptsLoop: for (let modelIndex = 0; modelIndex < modelsToTry.length; modelIndex++) {
|
|
1938
1919
|
const candidate = modelsToTry[modelIndex];
|
|
1939
1920
|
for (let startupAttemptIndex = 0; ; startupAttemptIndex++) {
|
|
1921
|
+
const recoveringAbort = abortRecoveryAttempted;
|
|
1922
|
+
const attemptTask = nextAttemptTask;
|
|
1940
1923
|
const verifyModel = Boolean(candidate) && !(options.modelOverrideFromParent && modelIndex === 0);
|
|
1941
1924
|
const outputSnapshot = captureSingleOutputSnapshot(options.outputPath);
|
|
1942
|
-
const result = await runSingleAttempt(runtimeCwd, agent,
|
|
1925
|
+
const result = await runSingleAttempt(runtimeCwd, agent, attemptTask, candidate, attemptOptions, {
|
|
1943
1926
|
sessionEnabled,
|
|
1944
1927
|
systemPrompt,
|
|
1945
1928
|
resolvedSkillNames: resolvedSkills.length > 0 ? resolvedSkills.map((skill) => skill.name) : undefined,
|
|
@@ -1959,7 +1942,7 @@ async function runSyncCompletionInner(
|
|
|
1959
1942
|
verifyModel,
|
|
1960
1943
|
});
|
|
1961
1944
|
lastResult = result;
|
|
1962
|
-
if (startupAttemptIndex === 0) {
|
|
1945
|
+
if (!recoveringAbort && startupAttemptIndex === 0) {
|
|
1963
1946
|
if (result.model) attemptedModels.push(result.model);
|
|
1964
1947
|
else if (candidate) attemptedModels.push(candidate);
|
|
1965
1948
|
}
|
|
@@ -1975,11 +1958,42 @@ async function runSyncCompletionInner(
|
|
|
1975
1958
|
usage: { ...result.usage },
|
|
1976
1959
|
};
|
|
1977
1960
|
modelAttempts.push(attempt);
|
|
1961
|
+
if (!attemptSucceeded) {
|
|
1962
|
+
const afterCompactionSettlement = (result as AbortRecoverySingleResult)[AFTER_COMPACTION_SETTLEMENT];
|
|
1963
|
+
const abortRecovery = planAbortRecovery({
|
|
1964
|
+
messages: result.messages ?? [],
|
|
1965
|
+
error: result.error,
|
|
1966
|
+
processSignal: result.processSignal,
|
|
1967
|
+
sessionAvailable: Boolean(options.sessionFile && existsSync(options.sessionFile)),
|
|
1968
|
+
alreadyResumed: abortRecoveryAttempted,
|
|
1969
|
+
stopped: result.stopped || result.detached || options.signal?.aborted,
|
|
1970
|
+
interrupted: result.interrupted || intercomDetached || options.interruptSignal?.aborted,
|
|
1971
|
+
timedOut: result.timedOut,
|
|
1972
|
+
toolBudgetExhausted: result.toolBudgetBlocked,
|
|
1973
|
+
usageBudgetExhausted: false,
|
|
1974
|
+
structuredOutputFailed: result.structuredOutputFailed,
|
|
1975
|
+
acceptanceFailed: false,
|
|
1976
|
+
currentTool: result.progress?.currentTool,
|
|
1977
|
+
afterCompactionSettlement,
|
|
1978
|
+
});
|
|
1979
|
+
if (abortRecovery.action === "resume") {
|
|
1980
|
+
abortRecoveryAttempted = true;
|
|
1981
|
+
nextAttemptTask = abortRecovery.prompt;
|
|
1982
|
+
attemptNotes.push("[abort-recovery] provider/transport abort after useful progress; resuming the retained child session once.");
|
|
1983
|
+
continue;
|
|
1984
|
+
}
|
|
1985
|
+
if (abortRecovery.diagnostic) {
|
|
1986
|
+
result.error = result.error ? `${result.error}\n${abortRecovery.diagnostic}` : abortRecovery.diagnostic;
|
|
1987
|
+
attempt.error = result.error;
|
|
1988
|
+
break modelAttemptsLoop;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
if (recoveringAbort && !attemptSucceeded) break modelAttemptsLoop;
|
|
1978
1992
|
if (options.workflowChildPermitLaunch && !attemptSucceeded) break modelAttemptsLoop;
|
|
1979
1993
|
// Preserve the legacy intercom handoff contract: once this logical run has
|
|
1980
1994
|
// been handed to a supervisor, terminating that attempt must not launch a
|
|
1981
1995
|
// startup retry or model fallback. Explicit user detach retains fallback.
|
|
1982
|
-
if (intercomDetached || result.timedOut
|
|
1996
|
+
if (intercomDetached || result.timedOut) break modelAttemptsLoop;
|
|
1983
1997
|
if (attemptSucceeded) break modelAttemptsLoop;
|
|
1984
1998
|
|
|
1985
1999
|
const startupFailure = isRetryableSubagentStartupFailure({
|
|
@@ -1996,7 +2010,6 @@ async function runSyncCompletionInner(
|
|
|
1996
2010
|
interrupted: result.interrupted,
|
|
1997
2011
|
timedOut: result.timedOut,
|
|
1998
2012
|
stopped: result.stopped,
|
|
1999
|
-
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
2000
2013
|
});
|
|
2001
2014
|
const retryDelayMs = SUBAGENT_STARTUP_RETRY_DELAYS_MS[startupAttemptIndex];
|
|
2002
2015
|
if (startupFailure && retryDelayMs !== undefined) {
|
|
@@ -2070,11 +2083,13 @@ async function runSyncCompletionInner(
|
|
|
2070
2083
|
usage: emptyUsage(),
|
|
2071
2084
|
error: "Subagent did not produce a result.",
|
|
2072
2085
|
} satisfies SingleResult, options.context);
|
|
2086
|
+
result.task = task;
|
|
2073
2087
|
|
|
2074
2088
|
result.usage = aggregateUsage;
|
|
2075
2089
|
result.attemptedModels = attemptedModels.length > 0 ? attemptedModels : undefined;
|
|
2076
2090
|
result.modelAttempts = modelAttempts.length > 0 ? modelAttempts : undefined;
|
|
2077
2091
|
result.progressSummary = {
|
|
2092
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
2078
2093
|
toolCount: totalToolCount,
|
|
2079
2094
|
tokens: aggregateUsage.input + aggregateUsage.output,
|
|
2080
2095
|
durationMs: totalDurationMs,
|
|
@@ -2128,8 +2143,6 @@ async function runSyncCompletionInner(
|
|
|
2128
2143
|
result.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "stopped", message: "Acceptance was not evaluated because the subagent was stopped." });
|
|
2129
2144
|
} else if (result.timedOut) {
|
|
2130
2145
|
result.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "timeout", message: "Acceptance was not evaluated because the subagent timed out." });
|
|
2131
|
-
} else if (result.turnBudgetExceeded) {
|
|
2132
|
-
result.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "turn-budget", message: "Acceptance was not evaluated because the subagent exceeded its turn budget." });
|
|
2133
2146
|
} else {
|
|
2134
2147
|
result.acceptance = await evaluateAcceptance({
|
|
2135
2148
|
acceptance: effectiveAcceptance,
|
|
@@ -18,6 +18,7 @@ interface BeginForegroundChildInput {
|
|
|
18
18
|
|
|
19
19
|
function copyProgress(target: ForegroundChildControl, progress: AgentProgress | undefined): void {
|
|
20
20
|
if (!progress) return;
|
|
21
|
+
target.sessionName = progress.sessionName;
|
|
21
22
|
target.currentActivityState = progress.activityState;
|
|
22
23
|
target.lastActivityAt = progress.lastActivityAt;
|
|
23
24
|
target.currentTool = progress.currentTool;
|
|
@@ -36,6 +37,7 @@ function copyProgress(target: ForegroundChildControl, progress: AgentProgress |
|
|
|
36
37
|
|
|
37
38
|
function syncCurrentChild(control: ForegroundRunControl, child: ForegroundChildControl): void {
|
|
38
39
|
control.currentAgent = child.agent;
|
|
40
|
+
control.sessionName = child.sessionName;
|
|
39
41
|
control.currentIndex = child.index;
|
|
40
42
|
control.description = child.description;
|
|
41
43
|
control.currentActivityState = child.currentActivityState;
|
|
@@ -59,6 +61,7 @@ function syncCurrentChild(control: ForegroundRunControl, child: ForegroundChildC
|
|
|
59
61
|
|
|
60
62
|
function clearCurrentChild(control: ForegroundRunControl): void {
|
|
61
63
|
control.currentAgent = undefined;
|
|
64
|
+
control.sessionName = undefined;
|
|
62
65
|
control.currentIndex = undefined;
|
|
63
66
|
control.currentActivityState = undefined;
|
|
64
67
|
control.lastActivityAt = undefined;
|
|
@@ -29,6 +29,7 @@ function compactChild(child: ForegroundResumeChild): ForegroundResumeChild {
|
|
|
29
29
|
return {
|
|
30
30
|
agent: child.agent,
|
|
31
31
|
index: child.index,
|
|
32
|
+
...(child.sessionName ? { sessionName: child.sessionName } : {}),
|
|
32
33
|
...(child.context ? { context: child.context } : {}),
|
|
33
34
|
...(child.sessionFile ? { sessionFile: child.sessionFile } : {}),
|
|
34
35
|
...(child.model ? { model: child.model } : {}),
|