pi-subagents 0.53.0 → 0.55.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.
Files changed (76) hide show
  1. package/CHANGELOG.md +66 -5
  2. package/README.md +1 -1
  3. package/agents/reviewer.md +12 -2
  4. package/docs/agents.md +4 -4
  5. package/docs/configuration.md +6 -6
  6. package/docs/extension-api.md +14 -4
  7. package/docs/models.md +41 -6
  8. package/docs/observability.md +14 -3
  9. package/docs/tool-reference.md +21 -6
  10. package/docs/workflows.md +7 -1
  11. package/index.ts +10 -1
  12. package/package.json +1 -1
  13. package/prompts/council.md +19 -7
  14. package/prompts/parallel-review.md +5 -1
  15. package/prompts/review-loop.md +9 -5
  16. package/skills/council-mode/SKILL.md +50 -26
  17. package/skills/pi-subagents/SKILL.md +5 -1
  18. package/skills/pi-subagents/references/constraints-and-recipes.md +16 -1
  19. package/skills/pi-subagents/references/execution-controls.md +13 -4
  20. package/skills/pi-subagents/references/multi-lane-orchestration.md +3 -3
  21. package/skills/pi-subagents/references/prompting-and-roles.md +14 -7
  22. package/src/agents/agent-management.ts +115 -14
  23. package/src/agents/agent-serializer.ts +2 -2
  24. package/src/agents/agents.ts +195 -47
  25. package/src/api/external-job-provider.ts +10 -1
  26. package/src/api/preflight.ts +33 -19
  27. package/src/api/project-panes.ts +2 -0
  28. package/src/extension/doctor.ts +10 -0
  29. package/src/extension/fanout-child.ts +3 -2
  30. package/src/extension/index.ts +24 -4
  31. package/src/extension/public-execution.ts +12 -9
  32. package/src/extension/rpc.ts +77 -3
  33. package/src/extension/schemas.ts +2 -1
  34. package/src/extension/tool-description.ts +9 -6
  35. package/src/extension/tool-result.ts +19 -0
  36. package/src/inspectors/herdr/client.ts +3 -3
  37. package/src/inspectors/herdr/focus.ts +55 -0
  38. package/src/inspectors/herdr/project-panes.ts +228 -44
  39. package/src/integrations/herdr-status.ts +26 -4
  40. package/src/runs/background/async-execution.ts +112 -13
  41. package/src/runs/background/async-job-tracker.ts +33 -14
  42. package/src/runs/background/async-resume.ts +20 -2
  43. package/src/runs/background/async-retention.ts +1 -1
  44. package/src/runs/background/async-status.ts +10 -0
  45. package/src/runs/background/chain-root-attachment.ts +5 -0
  46. package/src/runs/background/control-channel.ts +98 -10
  47. package/src/runs/background/notify.ts +62 -1
  48. package/src/runs/background/result-watcher.ts +4 -3
  49. package/src/runs/background/run-status.ts +15 -1
  50. package/src/runs/background/stale-run-reconciler.ts +3 -0
  51. package/src/runs/background/subagent-runner.ts +251 -38
  52. package/src/runs/background/wait-completions.ts +2 -0
  53. package/src/runs/background/wait-tool.ts +4 -3
  54. package/src/runs/foreground/async-stop-action.ts +23 -2
  55. package/src/runs/foreground/execution.ts +110 -8
  56. package/src/runs/foreground/subagent-executor.ts +361 -45
  57. package/src/runs/foreground/workflow-detach-reconcile.ts +3 -0
  58. package/src/runs/shared/acceptance.ts +1 -0
  59. package/src/runs/shared/child-identity.ts +36 -0
  60. package/src/runs/shared/completion-guard.ts +50 -1
  61. package/src/runs/shared/external-job-bridge.ts +53 -37
  62. package/src/runs/shared/external-job-runner.ts +126 -23
  63. package/src/runs/shared/model-fallback.ts +46 -15
  64. package/src/runs/shared/model-scope.ts +106 -39
  65. package/src/runs/shared/orca-progress-tabs.ts +71 -9
  66. package/src/runs/shared/parallel-utils.ts +12 -0
  67. package/src/runs/shared/pi-args.ts +31 -1
  68. package/src/runs/shared/subagent-prompt-runtime.ts +39 -10
  69. package/src/runs/shared/tool-availability.ts +1 -3
  70. package/src/shared/launch-contract.ts +6 -5
  71. package/src/shared/thinking-ceiling.ts +52 -0
  72. package/src/shared/types.ts +72 -5
  73. package/src/tui/fleet-status.ts +66 -13
  74. package/src/tui/render.ts +5 -4
  75. package/src/watchdog/permission-arbiter.ts +59 -51
  76. package/src/workflows/scripted-workflow.ts +107 -10
@@ -11,7 +11,7 @@ import { createCapacityResilientJsonWriter } from "../../shared/capacity-resilie
11
11
  import { isStorageCapacityError } from "../../shared/file-system-retry.ts";
12
12
  import { updateActiveRunIndex } from "./active-run-index.ts";
13
13
  import { createChildTranscriptWriter, type ChildTranscriptWriter } from "../../shared/child-transcript.ts";
14
- import { closeSteerInbox, consumeInterruptRequest, consumeSteerRequests, deliverInterruptRequest, deliverStopRequest, deliverTimeoutRequest, enqueueStepSteer, steerAcksDir, steerCapabilityPath, stepSteerInboxDir, watchAsyncControlInbox, type SteerAck, type SteerCapability, type SteerRequest } from "./control-channel.ts";
14
+ import { closeSteerInbox, consumeInterruptRequest, consumeSteerRequests, deliverInterruptRequest, deliverStopRequest, deliverTimeoutRequest, enqueueStepSteer, steerAcksDir, steerCapabilityPath, stepSteerInboxDir, watchAsyncControlInbox, type SteerAck, type SteerCapability, type SteerRequest, type StopRequest } from "./control-channel.ts";
15
15
  import { appendJsonl as appendRawJsonl, formatOutputArtifactContent, getArtifactPaths, writeArtifact, writeMetadata } from "../../shared/artifacts.ts";
16
16
  import { PI_CODING_AGENT_PACKAGE, getPiSpawnCommand, resolveInstalledPiPackageRoot } from "../shared/pi-spawn.ts";
17
17
  import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, injectOutputPathSystemPrompt, injectSingleOutputInstruction, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
@@ -48,6 +48,7 @@ import {
48
48
  type SteeringStatus,
49
49
  type SteeringTargetState,
50
50
  type SteeringTargetStatus,
51
+ type SubagentChildStatusEvent,
51
52
  DEFAULT_MAX_OUTPUT,
52
53
  type MaxOutputConfig,
53
54
  SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
@@ -85,7 +86,7 @@ import { readChildToolDiagnosticError } from "../shared/tool-availability.ts";
85
86
  import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection } from "../shared/dynamic-fanout.ts";
86
87
  import { claimRunFanoutBatch, getRunFanoutBudgetSnapshot } from "../shared/run-fanout-budget.ts";
87
88
  import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent } from "../shared/nested-events.ts";
88
- import { formatModelAttemptNote, isRetryableModelFailure, recordRetryableModelFailure } from "../shared/model-fallback.ts";
89
+ import { formatModelAttemptNote, formatSubagentModelVerificationError, isContextOverflow, isRetryableModelFailure, recordRetryableModelFailure } from "../shared/model-fallback.ts";
89
90
  import {
90
91
  SUBAGENT_STARTUP_RETRY_DELAYS_MS,
91
92
  formatSubagentExtensionConflictError,
@@ -99,7 +100,7 @@ import { createOwnedProcessTreeController, type OwnedProcessTreeController } fro
99
100
  import { createSteeringStatus, recordSteeringRequest, steeringStatus, terminalSteeringNoticeState, updateSteeringTarget } from "./steering.ts";
100
101
  import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
101
102
  import { PROMPT_REDACTED, detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput, hasEmptyTerminalAssistantResponse, readStatus } from "../../shared/utils.ts";
102
- import { evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
103
+ import { evaluateCompletionMutationGuard, validateImplementationToolContract } from "../shared/completion-guard.ts";
103
104
  import {
104
105
  createMutatingFailureState,
105
106
  didMutatingToolFail,
@@ -123,6 +124,7 @@ import {
123
124
  type WorktreeSetup,
124
125
  } from "../shared/worktree.ts";
125
126
  import { resolveEffectiveThinking } from "../../shared/model-info.ts";
127
+ import { assertThinkingWithinCeiling, decodeThinkingCeiling, SUBAGENT_THINKING_CEILING_ENV } from "../../shared/thinking-ceiling.ts";
126
128
  import { launchBindingDigest } from "../../shared/launch-contract.ts";
127
129
  import { writeInitialProgressFile } from "../../shared/settings.ts";
128
130
  import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
@@ -130,6 +132,7 @@ import { acceptanceFailureMessage, aggregateAcceptanceReport, buildSkippedAccept
130
132
  import { attachContractProjections, isAgentContractV1 } from "../shared/agent-contract.ts";
131
133
  import { waitForImportedAsyncRoot } from "./chain-root-attachment.ts";
132
134
  import { appendRunnerStepsToStatus, consumeChainAppendRequests, countPendingChainAppendRequests, statusStepDescription } from "./chain-append.ts";
135
+ import { asyncStatusChildIdentity } from "../shared/child-identity.ts";
133
136
  import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, turnBudgetDecision, turnBudgetDeferredNote, turnBudgetDeferredState, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
134
137
  import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
135
138
  import { effectiveToolTimeoutMs, formatToolTimeoutMessage, toolTimeoutCallKey } from "../shared/tool-timeout.ts";
@@ -236,6 +239,8 @@ interface StepResult {
236
239
  model?: string;
237
240
  attemptedModels?: string[];
238
241
  modelAttempts?: ModelAttempt[];
242
+ /** True when the dispatch failed because the input exceeded the model's context window. */
243
+ contextOverflow?: boolean;
239
244
  totalCost?: CostSummary;
240
245
  artifactPaths?: ArtifactPaths;
241
246
  outputSaveError?: string;
@@ -558,6 +563,8 @@ function runPiStreaming(
558
563
  toolTimeoutMs?: number,
559
564
  runDeadlineAt?: number,
560
565
  orcaProgressTab?: OrcaProgressTab,
566
+ expectedModelForVerification?: string,
567
+ modelVerificationRegistry?: Array<{ provider: string; id: string; fullId: string }>,
561
568
  ): Promise<RunPiStreamingResult> {
562
569
  return new Promise((resolve) => {
563
570
  const startedAt = Date.now();
@@ -716,7 +723,14 @@ function runPiStreaming(
716
723
  if (text) writeOutputText(text);
717
724
 
718
725
  if (event.type !== "message_end" || event.message.role !== "assistant") return;
719
- if (event.message.model) model = event.message.model;
726
+ const hasToolCall = assistantStartsToolCall(event.message);
727
+ if (event.message.model) {
728
+ model = event.message.model;
729
+ if (expectedModelForVerification && !hasToolCall) {
730
+ const modelVerificationError = formatSubagentModelVerificationError(expectedModelForVerification, event.message.model, modelVerificationRegistry);
731
+ if (modelVerificationError && !error) error = modelVerificationError;
732
+ }
733
+ }
720
734
  if (event.message.errorMessage) assistantError = event.message.errorMessage;
721
735
  const eventUsage = event.message.usage;
722
736
  if (eventUsage) {
@@ -1257,6 +1271,7 @@ async function runSingleStepInner(
1257
1271
  model: imported.model,
1258
1272
  attemptedModels: imported.attemptedModels,
1259
1273
  modelAttempts: imported.modelAttempts,
1274
+ contextOverflow: imported.contextOverflow,
1260
1275
  totalCost: imported.totalCost,
1261
1276
  structuredOutput: timedOut || stopped ? undefined : imported.structuredOutput,
1262
1277
  structuredOutputPath: timedOut || stopped ? undefined : imported.structuredOutputPath,
@@ -1276,6 +1291,43 @@ async function runSingleStepInner(
1276
1291
  let task = step.task.replace(placeholderRegex, () => ctx.previousOutput);
1277
1292
  if (ctx.outputs) task = resolveOutputReferences(task, ctx.outputs);
1278
1293
  const taskForCompletionGuard = task;
1294
+ let resolvedTaskToolPlan: ReturnType<typeof resolvePiLaunchToolPlan> | undefined;
1295
+ if (!step.runner) {
1296
+ resolvedTaskToolPlan = resolvePiLaunchToolPlan(omitUndefinedProperties({
1297
+ tools: step.tools,
1298
+ extensions: step.extensions,
1299
+ subagentOnlyExtensions: step.subagentOnlyExtensions,
1300
+ mcpDirectTools: step.mcpDirectTools,
1301
+ cwd: step.cwd ?? ctx.cwd,
1302
+ requireReadTool: Boolean(step.skills?.length),
1303
+ structuredOutput: Boolean(effectiveStructuredOutput),
1304
+ capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
1305
+ inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
1306
+ permissionRules: step.permissionRules,
1307
+ }));
1308
+ const contractTools = resolvedTaskToolPlan.explicitToolAllowlist ? resolvedTaskToolPlan.effectiveToolAllowlist : undefined;
1309
+ const contractError = validateImplementationToolContract({
1310
+ agent: step.agent,
1311
+ task: taskForCompletionGuard,
1312
+ tools: contractTools,
1313
+ mcpDirectTools: resolvedTaskToolPlan.effectiveMcpTools,
1314
+ configuredExtensions: resolvedTaskToolPlan.configuredExtensions,
1315
+ requestedTools: resolvedTaskToolPlan.requestedBuiltinTools,
1316
+ acceptanceRole: step.acceptanceRole,
1317
+ completionGuard: step.completionGuard,
1318
+ });
1319
+ if (contractError) {
1320
+ return omitUndefinedProperties({
1321
+ agent: step.agent,
1322
+ context: step.context,
1323
+ output: contractError,
1324
+ error: contractError,
1325
+ exitCode: 1,
1326
+ capabilityCeiling: resolvedTaskToolPlan.capabilityCeiling,
1327
+ capabilityAudit: resolvedTaskToolPlan.capabilityAudit,
1328
+ });
1329
+ }
1330
+ }
1279
1331
  if (step.effectiveAcceptance) {
1280
1332
  const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance, { reportOptional: isAgentContractV1(step.agentContract) });
1281
1333
  if (acceptancePrompt) task = `${task}\n${acceptancePrompt}`;
@@ -1392,6 +1444,7 @@ async function runSingleStepInner(
1392
1444
  timeoutMessage: ctx.timeoutMessage,
1393
1445
  stopMessage: ctx.stopMessage,
1394
1446
  onExternalJob: ctx.onExternalJob,
1447
+ followUp: step.externalJobFollowUp,
1395
1448
  }));
1396
1449
  try { fs.writeFileSync(ctx.outputFile, external.output, "utf-8"); } catch { /* Observability output is best-effort. */ }
1397
1450
  const resolvedOutput = step.outputPath && external.exitCode === 0
@@ -1458,9 +1511,18 @@ async function runSingleStepInner(
1458
1511
  // Escalated to "file" after an unexplained zero-activity startup failure so
1459
1512
  // retries keep the task text out of argv (endpoint pre-exec scans may deny it).
1460
1513
  let taskDeliveryOverride: SubagentTaskDelivery | undefined;
1514
+ let contextOverflow = false;
1515
+ let launchWarningsEmitted = false;
1461
1516
  modelAttemptsLoop: while (modelIndex < candidates.length) {
1462
1517
  if (ctx.timeoutSignal?.aborted || ctx.stopSignal?.aborted || ctx.skipAcceptance?.()) break;
1463
1518
  const candidate = candidates[modelIndex];
1519
+ const expectedModelForVerification = candidate && !(step.skipPrimaryModelVerification && modelIndex === 0) ? candidate : undefined;
1520
+ try {
1521
+ assertThinkingWithinCeiling({ model: candidate, configThinking: step.thinking, ceiling: step.thinkingCeiling ?? decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]), agent: step.agent, runId: ctx.id });
1522
+ } catch (error) {
1523
+ const message = error instanceof Error ? error.message : String(error);
1524
+ return omitUndefinedProperties({ agent: step.agent, output: message, error: message, exitCode: 1, context: step.context, thinkingCeiling: step.thinkingCeiling });
1525
+ }
1464
1526
  ctx.onAttemptStart?.(omitUndefinedProperties({ model: candidate, thinking: resolveEffectiveThinking(candidate, step.thinking) }));
1465
1527
  const outputSnapshot = captureSingleOutputSnapshot(step.outputPath);
1466
1528
  if (effectiveStructuredOutput) {
@@ -1479,7 +1541,7 @@ async function runSingleStepInner(
1479
1541
  childIndex: ctx.flatIndex,
1480
1542
  })
1481
1543
  : undefined;
1482
- const { args, env, tempDir, toolDiagnosticPath, runtimeAcknowledgedExtensionsPath, capabilityAudit: attemptCapabilityAudit } = buildPiArgs(omitUndefinedProperties({
1544
+ const { args, env, tempDir, toolDiagnosticPath, runtimeAcknowledgedExtensionsPath, capabilityAudit: attemptCapabilityAudit, warnings } = buildPiArgs(omitUndefinedProperties({
1483
1545
  parentSessionId: step.parentSessionId,
1484
1546
  baseArgs: ["--mode", "json", "-p"],
1485
1547
  task,
@@ -1524,9 +1586,14 @@ async function runSingleStepInner(
1524
1586
  : undefined,
1525
1587
  childWatchdog,
1526
1588
  waitToolEnabled: step.waitToolEnabled,
1589
+ thinkingCeiling: step.thinkingCeiling,
1527
1590
  }));
1591
+ if (!launchWarningsEmitted && warnings.length > 0) {
1592
+ for (const warning of warnings) console.warn(`[pi-subagents] ${warning}`);
1593
+ launchWarningsEmitted = true;
1594
+ }
1528
1595
  if (step.definitionDigest) {
1529
- const toolPlan = resolvePiLaunchToolPlan(omitUndefinedProperties({
1596
+ const toolPlan = resolvedTaskToolPlan ?? resolvePiLaunchToolPlan(omitUndefinedProperties({
1530
1597
  tools: step.tools,
1531
1598
  extensions: step.extensions,
1532
1599
  subagentOnlyExtensions: step.subagentOnlyExtensions,
@@ -1536,6 +1603,7 @@ async function runSingleStepInner(
1536
1603
  structuredOutput: Boolean(effectiveStructuredOutput),
1537
1604
  capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
1538
1605
  inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
1606
+ permissionRules: step.permissionRules,
1539
1607
  }));
1540
1608
  launchResolvedExtensions = projectLaunchResolvedChildExtensions(toolPlan);
1541
1609
  actualLaunchContractDigest = launchBindingDigest(omitUndefinedProperties({
@@ -1544,6 +1612,7 @@ async function runSingleStepInner(
1544
1612
  ...(candidate ? { model: candidate } : {}),
1545
1613
  modelCandidates: candidates as string[],
1546
1614
  ...(resolveEffectiveThinking(candidate, step.thinking) ? { thinking: resolveEffectiveThinking(candidate, step.thinking) } : {}),
1615
+ ...(step.thinkingCeiling ? { thinkingCeiling: step.thinkingCeiling } : {}),
1547
1616
  systemPrompt: appendTurnBudgetSystemPrompt(step.systemPrompt ?? "", ctx.turnBudget),
1548
1617
  systemPromptMode: step.systemPromptMode,
1549
1618
  inheritProjectContext: step.inheritProjectContext,
@@ -1580,6 +1649,8 @@ async function runSingleStepInner(
1580
1649
  ctx.toolTimeoutMs,
1581
1650
  ctx.deadlineAt,
1582
1651
  ctx.orcaProgressTab,
1652
+ expectedModelForVerification,
1653
+ step.modelVerificationRegistry,
1583
1654
  );
1584
1655
  if (run.processCloseObservedAt !== undefined) {
1585
1656
  writerProcesses.push({
@@ -1652,21 +1723,25 @@ async function runSingleStepInner(
1652
1723
  ? "Subagent produced no output (possible model cold-start or empty response)."
1653
1724
  : undefined;
1654
1725
  const completionGuardEnabled = isAgentContractV1(step.agentContract) ? step.completionGuard === true : step.completionGuard !== false;
1655
- const completionGuard = run.exitCode === 0 && !run.error && !toolAvailabilityError && !structuredError && !hiddenError?.hasError && !emptyOutputError && completionGuardEnabled
1726
+ const completionToolPlan = resolvedTaskToolPlan;
1727
+ const completionGuard = run.exitCode === 0 && !run.error && !structuredError && !hiddenError?.hasError && !emptyOutputError && completionGuardEnabled
1656
1728
  ? evaluateCompletionMutationGuard(omitUndefinedProperties({
1657
1729
  agent: step.agent,
1658
1730
  task: taskForCompletionGuard,
1659
1731
  messages: run.messages,
1660
- tools: step.tools,
1661
- mcpDirectTools: step.mcpDirectTools,
1732
+ tools: completionToolPlan ? (completionToolPlan.explicitToolAllowlist ? completionToolPlan.effectiveToolAllowlist : undefined) : step.tools,
1733
+ mcpDirectTools: completionToolPlan?.effectiveMcpTools ?? step.mcpDirectTools,
1734
+ toolAvailabilityError,
1662
1735
  }))
1663
1736
  : undefined;
1664
1737
  const completionGuardTriggered = completionGuard?.triggered === true && !run.observedMutationAttempt;
1738
+ const completionGuardBlocked = completionGuard?.blocked === true;
1665
1739
  const fileMutationEffect = completionGuard
1666
1740
  ? {
1667
- status: completionGuard.expectedMutation ? completionGuardTriggered ? "missing" as const : "observed" as const : "not-applicable" as const,
1741
+ status: completionGuardBlocked ? "blocked" as const : completionGuard.expectedMutation ? completionGuardTriggered ? "missing" as const : "observed" as const : "not-applicable" as const,
1668
1742
  expected: completionGuard.expectedMutation,
1669
- attempted: completionGuard.attemptedMutation || run.observedMutationAttempt === true,
1743
+ attempted: completionGuardBlocked ? false : completionGuard.attemptedMutation || run.observedMutationAttempt === true,
1744
+ ...(completionGuardBlocked && completionGuard.message ? { message: completionGuard.message } : {}),
1670
1745
  ...(completionGuardTriggered ? { message: "Subagent completed without making edits for an implementation task." } : {}),
1671
1746
  }
1672
1747
  : undefined;
@@ -1771,6 +1846,11 @@ async function runSingleStepInner(
1771
1846
  }
1772
1847
  const retryableModelFailure = isRetryableModelFailure(error);
1773
1848
  if (retryableModelFailure) recordRetryableModelFailure(candidate ?? run.model ?? step.model, error);
1849
+ if (isContextOverflow(error)) {
1850
+ contextOverflow = true;
1851
+ attemptNotes.push(`[fallback] ${attempt.model} failed: context overflow — the input exceeds this model's context window. Reduce the task input or use a model with a larger context window.`);
1852
+ break modelAttemptsLoop;
1853
+ }
1774
1854
  if (!retryableModelFailure || modelIndex === candidates.length - 1) break modelAttemptsLoop;
1775
1855
  attemptNotes.push(formatModelAttemptNote(attempt, candidates[modelIndex + 1]));
1776
1856
  modelIndex += 1;
@@ -1908,6 +1988,7 @@ async function runSingleStepInner(
1908
1988
  model: finalResult?.model,
1909
1989
  attemptedModels: attemptedModels.length > 0 ? attemptedModels : undefined,
1910
1990
  modelAttempts,
1991
+ contextOverflow: contextOverflow || undefined,
1911
1992
  totalCost: costSummaryFromAttempts(modelAttempts),
1912
1993
  artifactPaths,
1913
1994
  outputSaveError: artifactErrors.outputSaveError,
@@ -1943,21 +2024,8 @@ async function runSingleStep(
1943
2024
  step: SubagentStep,
1944
2025
  ctx: SingleStepContext,
1945
2026
  ): Promise<StepResult & { completionGuardTriggered?: boolean }> {
1946
- if (step.importAsyncRoot) return runSingleStepInner(step, ctx);
1947
- const orcaProgressTab = createOrcaProgressTab({
1948
- cwd: step.cwd ?? ctx.cwd,
1949
- runId: ctx.id,
1950
- agent: step.agent,
1951
- index: ctx.flatIndex,
1952
- });
1953
- try {
1954
- const result = await runSingleStepInner(step, { ...ctx, orcaProgressTab });
1955
- orcaProgressTab?.finish(result.stopped ? "stopped" : result.exitCode === 0 && !result.error ? "completed" : "failed", result.sessionFile);
1956
- return result;
1957
- } catch (error) {
1958
- orcaProgressTab?.finish("failed");
1959
- throw error;
1960
- }
2027
+ if (!step.importAsyncRoot) ctx.orcaProgressTab?.section({ agent: step.agent, index: ctx.flatIndex, count: ctx.flatStepCount });
2028
+ return runSingleStepInner(step, ctx);
1961
2029
  }
1962
2030
 
1963
2031
  type RunnerStatusStep = NonNullable<AsyncStatus["steps"]>[number] & {
@@ -2226,6 +2294,7 @@ async function runSubagent(
2226
2294
  const activeChildInterrupts = new Map<number, () => void>();
2227
2295
  const activeChildTimeouts = new Map<number, () => void>();
2228
2296
  const activeChildStops = new Map<number, () => void>();
2297
+ const childStopRequests = new Map<number, { childId: string; requestedAt: number }>();
2229
2298
  const activeChildTurnBudgetAborts = new Map<number, (message: string, state?: TurnBudgetState) => void>();
2230
2299
  const pendingStepSteers: SteerRequest[] = [];
2231
2300
  const steeringCapabilities = new Map<number, SteerCapability>();
@@ -2269,6 +2338,7 @@ async function runSubagent(
2269
2338
  ...(task.launchContractDigest ? { launchContractDigest: task.launchContractDigest } : {}),
2270
2339
  ...(task.launchResolvedExtensions ? { launchResolvedExtensions: task.launchResolvedExtensions } : {}),
2271
2340
  ...(task.capabilityCeiling ? { capabilityCeiling: task.capabilityCeiling } : {}),
2341
+ ...(task.thinkingCeiling ? { thinkingCeiling: task.thinkingCeiling } : {}),
2272
2342
  status: "pending",
2273
2343
  ...(task.toolBudget ? { toolBudget: initialToolBudgetState(task.toolBudget) } : {}),
2274
2344
  ...(task.sessionFile ? { sessionFile: task.sessionFile } : {}),
@@ -2294,6 +2364,7 @@ async function runSubagent(
2294
2364
  structured: Boolean(step.collect.outputSchema),
2295
2365
  ...(step.agentContract ? { agentContract: step.agentContract } : {}),
2296
2366
  ...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
2367
+ ...(step.thinkingCeiling ? { thinkingCeiling: step.thinkingCeiling } : {}),
2297
2368
  status: "pending",
2298
2369
  ...(step.parallel.toolBudget ? { toolBudget: initialToolBudgetState(step.parallel.toolBudget) } : {}),
2299
2370
  recentTools: [],
@@ -2316,6 +2387,7 @@ async function runSubagent(
2316
2387
  ...(step.launchContractDigest ? { launchContractDigest: step.launchContractDigest } : {}),
2317
2388
  ...(step.launchResolvedExtensions ? { launchResolvedExtensions: step.launchResolvedExtensions } : {}),
2318
2389
  ...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
2390
+ ...(step.thinkingCeiling ? { thinkingCeiling: step.thinkingCeiling } : {}),
2319
2391
  status: "pending",
2320
2392
  ...(step.toolBudget ? { toolBudget: initialToolBudgetState(step.toolBudget) } : {}),
2321
2393
  ...(step.sessionFile ? { sessionFile: step.sessionFile } : {}),
@@ -2338,6 +2410,19 @@ async function runSubagent(
2338
2410
  step.processTerminal = { version: 1, state: "pending", runId: id, runnerProcessInstanceId: config.runnerProcessInstanceId };
2339
2411
  }
2340
2412
  }
2413
+ const initialAgentLabel = initialStatusSteps.length === 1
2414
+ ? initialStatusSteps[0]!.agent
2415
+ : (config.resultMode ?? (flatSteps.length > 1 ? "chain" : "single")) === "parallel"
2416
+ ? `parallel:${initialStatusSteps.map((step) => step.agent).join("+")}`
2417
+ : `chain:${initialStatusSteps.map((step) => step.agent).join("->")}`;
2418
+ const orcaProgressTab = flatSteps.every((step) => step.importAsyncRoot) ? undefined : createOrcaProgressTab({
2419
+ cwd,
2420
+ runId: id,
2421
+ agent: initialAgentLabel,
2422
+ index: 0,
2423
+ stepCount: Math.max(initialStatusSteps.length, 1),
2424
+ });
2425
+
2341
2426
  const statusPayload: RunnerStatusPayload = omitUndefinedProperties({
2342
2427
  lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
2343
2428
  runId: id,
@@ -2520,6 +2605,7 @@ async function runSubagent(
2520
2605
  model: step.model,
2521
2606
  attemptedModels: step.attemptedModels,
2522
2607
  modelAttempts: step.modelAttempts,
2608
+ contextOverflow: step.contextOverflow,
2523
2609
  })),
2524
2610
  exitCode: state === "complete" || state === "paused" ? 0 : 1,
2525
2611
  timestamp: now,
@@ -2555,6 +2641,83 @@ async function runSubagent(
2555
2641
  statusPayload.lastUpdate = Date.now();
2556
2642
  writeStatusPayload();
2557
2643
  };
2644
+ const childStopTargetId = (index: number): string => asyncStatusChildIdentity(requiredStatusStep(statusPayload, index), index);
2645
+ const appendChildStatusEvent = (index: number, childId: string, status: "stopping" | "stopped", now = Date.now()): void => {
2646
+ const step = requiredStatusStep(statusPayload, index);
2647
+ appendJsonl(eventsPath, JSON.stringify({
2648
+ type: "subagent.child-status",
2649
+ version: 1,
2650
+ ts: now,
2651
+ runId: id,
2652
+ childId,
2653
+ status,
2654
+ reason: "user",
2655
+ source: "async",
2656
+ stepIndex: index,
2657
+ agent: step.agent,
2658
+ ...(step.runId ? { childRunId: step.runId } : {}),
2659
+ ...(step.workflowKey ? { workflowKey: step.workflowKey } : {}),
2660
+ ...(step.phase ? { phase: step.phase } : {}),
2661
+ ...(step.label ? { label: step.label } : {}),
2662
+ } satisfies SubagentChildStatusEvent));
2663
+ };
2664
+ const appendTerminalChildStatusEvent = (index: number, now = Date.now()): void => {
2665
+ const request = childStopRequests.get(index);
2666
+ if (request) appendChildStatusEvent(index, request.childId, "stopped", now);
2667
+ };
2668
+ const markChildStopRequested = (index: number, childId: string, now = Date.now()): boolean => {
2669
+ const step = statusPayload.steps[index];
2670
+ if (!step || (step.status !== "pending" && step.status !== "running")) return false;
2671
+ childStopRequests.set(index, { childId, requestedAt: now });
2672
+ step.stopRequested = true;
2673
+ step.stopRequestedAt = now;
2674
+ delete step.activityState;
2675
+ statusPayload.lastUpdate = now;
2676
+ writeStatusPayload();
2677
+ appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.stop_requested", ts: now, runId: id, stepIndex: index, childId, agent: step.agent }));
2678
+ appendChildStatusEvent(index, childId, "stopping", now);
2679
+ return true;
2680
+ };
2681
+ const markChildStopped = (index: number, now = Date.now()): void => {
2682
+ const step = requiredStatusStep(statusPayload, index);
2683
+ if (step.status === "stopped") return;
2684
+ step.status = "stopped";
2685
+ step.error = stopMessage;
2686
+ step.exitCode = 1;
2687
+ step.stopped = true;
2688
+ step.stopRequested = true;
2689
+ step.stopRequestedAt = childStopRequests.get(index)?.requestedAt ?? step.stopRequestedAt ?? now;
2690
+ delete step.activityState;
2691
+ step.endedAt = now;
2692
+ step.durationMs = step.startedAt ? now - step.startedAt : 0;
2693
+ step.lastActivityAt = now;
2694
+ statusPayload.lastUpdate = now;
2695
+ writeStatusPayload();
2696
+ const childId = childStopRequests.get(index)?.childId ?? childStopTargetId(index);
2697
+ appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.stopped", ts: now, runId: id, stepIndex: index, childId, agent: step.agent, exitCode: 1, durationMs: step.durationMs }));
2698
+ appendChildStatusEvent(index, childId, "stopped", now);
2699
+ };
2700
+ const childStopResult = (index: number, agent: string, context?: "fresh" | "fork"): SingleStepResult => {
2701
+ markChildStopped(index);
2702
+ return stoppedStepResult(agent, context);
2703
+ };
2704
+ const stopChildStep = (request: StopRequest): void => {
2705
+ if (request.targetIndex === undefined) {
2706
+ stopRunner();
2707
+ return;
2708
+ }
2709
+ const childId = request.childId ?? childStopTargetId(request.targetIndex);
2710
+ const now = Date.now();
2711
+ if (!markChildStopRequested(request.targetIndex, childId, now)) {
2712
+ appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.stop_failed", ts: now, runId: id, stepIndex: request.targetIndex, childId, message: "Child is not pending or running." }));
2713
+ return;
2714
+ }
2715
+ const stop = activeChildStops.get(request.targetIndex);
2716
+ if (stop) stop();
2717
+ else if (requiredStatusStep(statusPayload, request.targetIndex).status === "pending") {
2718
+ appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.stop_queued", ts: now, runId: id, stepIndex: request.targetIndex, childId }));
2719
+ }
2720
+ };
2558
2721
  const registerStepInterrupt = (flatIndex: number, interrupt: (() => void) | undefined): void => {
2559
2722
  if (!interrupt) {
2560
2723
  activeChildInterrupts.delete(flatIndex);
@@ -2577,7 +2740,7 @@ async function runSubagent(
2577
2740
  return;
2578
2741
  }
2579
2742
  activeChildStops.set(flatIndex, stop);
2580
- if (stopped) stop();
2743
+ if (stopped || childStopRequests.has(flatIndex)) stop();
2581
2744
  };
2582
2745
  const registerStepTurnBudgetAbort = (flatIndex: number, abort: ((message: string, state?: TurnBudgetState) => void) | undefined): void => {
2583
2746
  if (!abort) {
@@ -3482,7 +3645,7 @@ async function runSubagent(
3482
3645
  const disposeControlInbox = watchAsyncControlInbox(asyncDir, {
3483
3646
  onInterrupt: interruptRunner,
3484
3647
  onTimeout: timeoutRunner,
3485
- onStop: stopRunner,
3648
+ onStop: stopChildStep,
3486
3649
  onSteer: (request) => {
3487
3650
  const targetStep = request.targetIndex !== undefined ? statusPayload.steps[request.targetIndex] : undefined;
3488
3651
  if (targetStep?.status === "pending") {
@@ -3558,6 +3721,19 @@ async function runSubagent(
3558
3721
  if (materialized.parallel.length > 1 && step.parallel.outputPath && !step.parallel.namespaceOutputPath) {
3559
3722
  throw new DynamicFanoutError(`Dynamic chain step ${stepIndex + 1} materialized ${materialized.parallel.length} items that resolve output to the same path: ${step.parallel.outputPath}. Remove the explicit output path or use an inherited relative agent output so each item can be isolated.`);
3560
3723
  }
3724
+ for (const [itemIndex] of materialized.parallel.entries()) {
3725
+ const thinkingOverride = step.thinkingOverrides?.[itemIndex];
3726
+ const model = thinkingOverride ? applyThinkingSuffix(step.parallel.model, thinkingOverride, true) : step.parallel.model;
3727
+ const configThinking = thinkingOverride ? thinkingOverride : step.parallel.thinking;
3728
+ const candidates = step.parallel.modelCandidates !== undefined
3729
+ ? step.parallel.modelCandidates.length > 0
3730
+ ? step.parallel.modelCandidates.map((candidate) => thinkingOverride ? applyThinkingSuffix(candidate, thinkingOverride, true) ?? candidate : candidate)
3731
+ : [undefined]
3732
+ : model ? [model] : [undefined];
3733
+ for (const candidate of candidates) {
3734
+ assertThinkingWithinCeiling({ model: candidate, configThinking, ceiling: step.parallel.thinkingCeiling ?? decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]), agent: step.parallel.agent, runId: id });
3735
+ }
3736
+ }
3561
3737
  if (materialized.collectedOnEmpty) await validateDynamicCollection(step.collect.outputSchema, materialized.collectedOnEmpty);
3562
3738
  if (!config.runFanoutBudget) throw new Error("Async runner is missing its run fan-out budget identity.");
3563
3739
  const runFanoutBudget = claimRunFanoutBatch(config.runFanoutBudget, materialized.parallel.map((_, itemIndex) => `chain[${stepIndex}].expand[${itemIndex}]`));
@@ -3714,6 +3890,7 @@ async function runSubagent(
3714
3890
  ...(task.skills ? { skills: task.skills } : {}),
3715
3891
  ...(task.model ? { model: task.model } : {}),
3716
3892
  ...(task.thinking ? { thinking: task.thinking } : {}),
3893
+ ...(task.thinkingCeiling ? { thinkingCeiling: task.thinkingCeiling } : {}),
3717
3894
  ...(task.modelCandidates && task.modelCandidates.length > 0 ? { attemptedModels: task.modelCandidates } : task.model ? { attemptedModels: [task.model] } : {}),
3718
3895
  recentTools: [],
3719
3896
  recentOutput: [],
@@ -3785,6 +3962,7 @@ async function runSubagent(
3785
3962
  }
3786
3963
  if (timedOut) return timedOutStepResult(task.agent, task.context);
3787
3964
  if (stopped) return stoppedStepResult(task.agent, task.context);
3965
+ if (childStopRequests.has(fi)) return childStopResult(fi, task.agent, task.context);
3788
3966
  if (interrupted) return pausedStepResult(task.agent, task.context);
3789
3967
  if (aborted && failFast) {
3790
3968
  const skippedAt = Date.now();
@@ -3844,7 +4022,8 @@ async function runSubagent(
3844
4022
  onWriterProcess,
3845
4023
  onExternalProcess: (process) => updateExternalProcess(fi, process),
3846
4024
  onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
3847
- skipAcceptance: () => timedOut || stopped,
4025
+ skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
4026
+ orcaProgressTab,
3848
4027
  }), config.deadlineAt);
3849
4028
  const taskEndTime = Date.now();
3850
4029
  const childInterrupted = singleResult.interrupted === true;
@@ -3869,6 +4048,7 @@ async function runSubagent(
3869
4048
  setOptionalProperty(requiredStatusStep(statusPayload, fi), "thinking", resolveEffectiveThinking(singleResult.model, requiredStatusStep(statusPayload, fi).thinking));
3870
4049
  setOptionalProperty(requiredStatusStep(statusPayload, fi), "attemptedModels", singleResult.attemptedModels);
3871
4050
  setOptionalProperty(requiredStatusStep(statusPayload, fi), "modelAttempts", singleResult.modelAttempts);
4051
+ setOptionalProperty(requiredStatusStep(statusPayload, fi), "contextOverflow", singleResult.contextOverflow);
3872
4052
  setOptionalProperty(requiredStatusStep(statusPayload, fi), "totalCost", singleResult.totalCost);
3873
4053
  if (singleResult.totalCost) {
3874
4054
  pendingParallelUsageCost = {
@@ -3900,12 +4080,13 @@ async function runSubagent(
3900
4080
  statusPayload.lastUpdate = taskEndTime;
3901
4081
  writeStatusPayload();
3902
4082
  appendCapabilityCeilingAppliedEvent(eventsPath, id, fi, task.agent, singleResult);
3903
- appendJsonl(eventsPath, JSON.stringify({
3904
- type: stopped || childStopped ? "subagent.step.stopped" : timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
3905
- ts: taskEndTime, runId: id, stepIndex: fi, agent: task.agent,
3906
- exitCode: stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode, durationMs: taskEndTime - taskStartTime,
3907
- }));
3908
- if (singleResult.exitCode !== 0 && failFast) aborted = true;
4083
+ appendJsonl(eventsPath, JSON.stringify({
4084
+ type: stopped || childStopped ? "subagent.step.stopped" : timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
4085
+ ts: taskEndTime, runId: id, stepIndex: fi, agent: task.agent,
4086
+ exitCode: stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode, durationMs: taskEndTime - taskStartTime,
4087
+ }));
4088
+ if (stopped || childStopped) appendTerminalChildStatusEvent(fi, taskEndTime);
4089
+ if (singleResult.exitCode !== 0 && failFast && !childStopped) aborted = true;
3909
4090
  return stopped || childStopped ? { ...singleResult, output: stopMessage, error: stopMessage, exitCode: 1, interrupted: false, timedOut: false, stopped: true, skipped: false } : timedOut ? { ...singleResult, output: timeoutMessage ?? "Subagent timed out.", error: timeoutMessage ?? "Subagent timed out.", exitCode: 1, interrupted: false, timedOut: true, skipped: false } : { ...singleResult, skipped: false };
3910
4091
  }, globalSemaphore);
3911
4092
 
@@ -3938,6 +4119,7 @@ async function runSubagent(
3938
4119
  model: pr.model,
3939
4120
  attemptedModels: pr.attemptedModels,
3940
4121
  modelAttempts: pr.modelAttempts,
4122
+ contextOverflow: pr.contextOverflow,
3941
4123
  totalCost: pr.totalCost,
3942
4124
  artifactPaths: pr.artifactPaths,
3943
4125
  transcriptPath: pr.transcriptPath,
@@ -4157,6 +4339,7 @@ async function runSubagent(
4157
4339
  }
4158
4340
  if (timedOut) return timedOutStepResult(task.agent, task.context);
4159
4341
  if (stopped) return stoppedStepResult(task.agent, task.context);
4342
+ if (childStopRequests.has(fi)) return childStopResult(fi, task.agent, task.context);
4160
4343
  if (interrupted) return pausedStepResult(task.agent, task.context);
4161
4344
  if (aborted && failFast) {
4162
4345
  const skippedAt = Date.now();
@@ -4232,7 +4415,8 @@ async function runSubagent(
4232
4415
  onWriterProcess,
4233
4416
  onExternalProcess: (process) => updateExternalProcess(fi, process),
4234
4417
  onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
4235
- skipAcceptance: () => timedOut || stopped,
4418
+ skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
4419
+ orcaProgressTab,
4236
4420
  }), config.deadlineAt);
4237
4421
  if (task.sessionFile) {
4238
4422
  latestSessionFile = task.sessionFile;
@@ -4263,6 +4447,7 @@ async function runSubagent(
4263
4447
  setOptionalProperty(requiredStatusStep(statusPayload, fi), "thinking", resolveEffectiveThinking(singleResult.model, requiredStatusStep(statusPayload, fi).thinking));
4264
4448
  setOptionalProperty(requiredStatusStep(statusPayload, fi), "attemptedModels", singleResult.attemptedModels);
4265
4449
  setOptionalProperty(requiredStatusStep(statusPayload, fi), "modelAttempts", singleResult.modelAttempts);
4450
+ setOptionalProperty(requiredStatusStep(statusPayload, fi), "contextOverflow", singleResult.contextOverflow);
4266
4451
  setOptionalProperty(requiredStatusStep(statusPayload, fi), "totalCost", singleResult.totalCost);
4267
4452
  if (singleResult.totalCost) {
4268
4453
  pendingParallelUsageCost = {
@@ -4299,6 +4484,7 @@ async function runSubagent(
4299
4484
  ts: taskEndTime, runId: id, stepIndex: fi, agent: task.agent,
4300
4485
  exitCode: stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode, durationMs: taskDuration,
4301
4486
  }));
4487
+ if (stopped || childStopped) appendTerminalChildStatusEvent(fi, taskEndTime);
4302
4488
  if (singleResult.completionGuardTriggered) {
4303
4489
  const event = buildControlEvent(omitUndefinedProperties({
4304
4490
  from: requiredStatusStep(statusPayload, fi).activityState,
@@ -4313,7 +4499,7 @@ async function runSubagent(
4313
4499
  appendControlEvent(event);
4314
4500
  }
4315
4501
 
4316
- if (singleResult.exitCode !== 0 && failFast) aborted = true;
4502
+ if (singleResult.exitCode !== 0 && failFast && !childStopped) aborted = true;
4317
4503
  return stopped || childStopped ? { ...singleResult, output: stopMessage, error: stopMessage, exitCode: 1, interrupted: false, timedOut: false, stopped: true, skipped: false } : timedOut ? { ...singleResult, output: timeoutMessage ?? "Subagent timed out.", error: timeoutMessage ?? "Subagent timed out.", exitCode: 1, interrupted: false, timedOut: true, skipped: false } : { ...singleResult, skipped: false };
4318
4504
  },
4319
4505
  globalSemaphore,
@@ -4366,6 +4552,7 @@ async function runSubagent(
4366
4552
  model: pr.model,
4367
4553
  attemptedModels: pr.attemptedModels,
4368
4554
  modelAttempts: pr.modelAttempts,
4555
+ contextOverflow: pr.contextOverflow,
4369
4556
  totalCost: pr.totalCost,
4370
4557
  artifactPaths: pr.artifactPaths,
4371
4558
  transcriptPath: pr.transcriptPath,
@@ -4469,6 +4656,26 @@ async function runSubagent(
4469
4656
  }
4470
4657
  } else {
4471
4658
  const seqStep = step as SubagentStep;
4659
+ if (timedOut) {
4660
+ results.push(timedOutStepResult(seqStep.agent, seqStep.context));
4661
+ flatIndex++;
4662
+ continue;
4663
+ }
4664
+ if (stopped) {
4665
+ results.push(stoppedStepResult(seqStep.agent, seqStep.context));
4666
+ flatIndex++;
4667
+ continue;
4668
+ }
4669
+ if (childStopRequests.has(flatIndex)) {
4670
+ results.push(childStopResult(flatIndex, seqStep.agent, seqStep.context));
4671
+ flatIndex++;
4672
+ continue;
4673
+ }
4674
+ if (interrupted) {
4675
+ results.push(pausedStepResult(seqStep.agent, seqStep.context));
4676
+ flatIndex++;
4677
+ continue;
4678
+ }
4472
4679
  let singleWorktreeSetup: WorktreeSetup | undefined;
4473
4680
  if (seqStep.worktree) {
4474
4681
  try {
@@ -4553,7 +4760,8 @@ async function runSubagent(
4553
4760
  onWriterProcess,
4554
4761
  onExternalProcess: (process) => updateExternalProcess(flatIndex, process),
4555
4762
  onExternalJob: (externalJob) => updateExternalJob(flatIndex, externalJob),
4556
- skipAcceptance: () => timedOut || stopped,
4763
+ skipAcceptance: () => timedOut || stopped || childStopRequests.has(flatIndex),
4764
+ orcaProgressTab,
4557
4765
  }), config.deadlineAt);
4558
4766
  } catch (error) {
4559
4767
  if (singleWorktreeSetup) cleanupWorktrees(singleWorktreeSetup);
@@ -4583,6 +4791,7 @@ async function runSubagent(
4583
4791
  model: singleResult.model,
4584
4792
  attemptedModels: singleResult.attemptedModels,
4585
4793
  modelAttempts: singleResult.modelAttempts,
4794
+ contextOverflow: singleResult.contextOverflow,
4586
4795
  totalCost: singleResult.totalCost,
4587
4796
  artifactPaths: singleResult.artifactPaths,
4588
4797
  transcriptPath: singleResult.transcriptPath,
@@ -4661,6 +4870,7 @@ async function runSubagent(
4661
4870
  setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "thinking", resolveEffectiveThinking(singleResult.model, requiredStatusStep(statusPayload, flatIndex).thinking));
4662
4871
  setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "attemptedModels", singleResult.attemptedModels);
4663
4872
  setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "modelAttempts", singleResult.modelAttempts);
4873
+ setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "contextOverflow", singleResult.contextOverflow);
4664
4874
  setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "totalCost", singleResult.totalCost);
4665
4875
  setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "error", stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.error);
4666
4876
  setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "transcriptPath", singleResult.transcriptPath ?? requiredStatusStep(statusPayload, flatIndex).transcriptPath);
@@ -4698,6 +4908,7 @@ async function runSubagent(
4698
4908
  durationMs: stepEndTime - stepStartTime,
4699
4909
  tokens: stepTokens,
4700
4910
  }));
4911
+ if (stopped || childStopped) appendTerminalChildStatusEvent(flatIndex, stepEndTime);
4701
4912
  if (singleWorktreeSetup && !singleResult.detached) {
4702
4913
  const diffs = diffWorktrees(singleWorktreeSetup, [seqStep.agent], path.join(asyncDir, "worktree-diffs", `step-${stepIndex}`));
4703
4914
  const diffSummary = formatWorktreeDiffSummary(diffs);
@@ -4935,6 +5146,7 @@ async function runSubagent(
4935
5146
  model: r.model,
4936
5147
  attemptedModels: r.attemptedModels,
4937
5148
  modelAttempts: r.modelAttempts,
5149
+ contextOverflow: r.contextOverflow,
4938
5150
  totalCost: r.totalCost,
4939
5151
  artifactPaths: r.artifactPaths,
4940
5152
  outputSaveError: r.outputSaveError,
@@ -4999,6 +5211,7 @@ async function runSubagent(
4999
5211
  statusPayload.lastUpdate = Date.now();
5000
5212
  }
5001
5213
  writeStatusPayload();
5214
+ orcaProgressTab?.finish(statusPayload.state === "complete" ? "completed" : statusPayload.state === "stopped" ? "stopped" : "failed", effectiveSessionFile);
5002
5215
  appendJsonl(
5003
5216
  eventsPath,
5004
5217
  JSON.stringify({
@@ -44,6 +44,7 @@ export function toWaitCompletion(data: Record<string, unknown>, runId: string):
44
44
  const childRunId = asNonEmptyString(child.runId);
45
45
  const error = asNonEmptyString(child.error);
46
46
  const model = asNonEmptyString(child.model);
47
+ const contextOverflow = child.contextOverflow === true;
47
48
  return [{
48
49
  ...(agent ? { agent } : {}),
49
50
  ...(childRunId ? { runId: childRunId } : {}),
@@ -51,6 +52,7 @@ export function toWaitCompletion(data: Record<string, unknown>, runId: string):
51
52
  ...(outputState ? { outputState } : {}),
52
53
  ...(error ? { error } : {}),
53
54
  ...(model ? { model } : {}),
55
+ ...(contextOverflow ? { contextOverflow: true } : {}),
54
56
  ...(artifactPaths ? { artifactPaths } : {}),
55
57
  }];
56
58
  })