pi-subagents 0.81.0 → 0.82.1

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 (152) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +1 -1
  3. package/bundle/agents.js +3 -3
  4. package/bundle/child-tool-plan.js +2 -2
  5. package/bundle/{chunk-L5ZHE4L4.js → chunk-3JCDWF4V.js} +4 -4
  6. package/bundle/{chunk-LSRUTHT7.js → chunk-43SCHIMD.js} +1 -1
  7. package/bundle/{chunk-VJHOELQP.js → chunk-46XKCPJR.js} +193 -81
  8. package/bundle/{chunk-G5DKFLLI.js → chunk-4SDU2BZX.js} +15 -10
  9. package/bundle/{chunk-2ZSNH26H.js → chunk-4Y2ROR7L.js} +7 -7
  10. package/bundle/{chunk-V2JWDB5U.js → chunk-52UBALJL.js} +2 -2
  11. package/bundle/{chunk-3DC5KBVP.js → chunk-5TYMSZX3.js} +1 -1
  12. package/bundle/{chunk-NNLASTQE.js → chunk-6XJB7PIX.js} +20 -6
  13. package/bundle/{chunk-ZBC44JJ3.js → chunk-CIZQXJ5Q.js} +22 -9
  14. package/bundle/{chunk-KSWQVGDW.js → chunk-FXUXCX2K.js} +225 -18
  15. package/bundle/{chunk-563BHAZ5.js → chunk-G4GZD3OJ.js} +35 -26
  16. package/bundle/{chunk-ARRKBB2B.js → chunk-GJECHSAR.js} +2 -2
  17. package/bundle/{chunk-V3BIUPLU.js → chunk-IVAE5Z6Q.js} +256 -52
  18. package/bundle/{chunk-V6MR5TOQ.js → chunk-JYUKW2SY.js} +25 -19
  19. package/bundle/{chunk-BJN5LUQY.js → chunk-OYF6AFDY.js} +1 -1
  20. package/bundle/{chunk-CEKS3TFO.js → chunk-PBK2I7WY.js} +6 -6
  21. package/bundle/{chunk-TUWLXTAM.js → chunk-PDZWJCHD.js} +681 -320
  22. package/bundle/{chunk-EYNJ6VL6.js → chunk-PLCCBLPY.js} +2 -2
  23. package/bundle/{chunk-XMFLCKMQ.js → chunk-QE4AGIMG.js} +339 -132
  24. package/bundle/{chunk-74RT5YQE.js → chunk-RMQFMMAT.js} +123 -33
  25. package/bundle/{chunk-WSBNENFS.js → chunk-UCPP4GNU.js} +14 -4
  26. package/bundle/{chunk-2XYWN7QJ.js → chunk-UTT254JE.js} +2 -2
  27. package/bundle/{chunk-BPS2AGBZ.js → chunk-VSUSLDNW.js} +93 -52
  28. package/bundle/{chunk-ZP3HODFH.js → chunk-XSWOTIA3.js} +1 -1
  29. package/bundle/{chunk-3IF2PGW7.js → chunk-YKEWIB4M.js} +1 -1
  30. package/bundle/{chunk-26C3WLYQ.js → chunk-ZIV4J4HG.js} +498 -274
  31. package/bundle/control-channel.js +2 -2
  32. package/bundle/index.js +1 -1
  33. package/bundle/intercom-bridge.js +2 -2
  34. package/bundle/preflight.js +8 -8
  35. package/bundle/project-panes.js +3 -3
  36. package/docs/agents.md +37 -7
  37. package/docs/configuration.md +9 -1
  38. package/docs/tool-reference.md +3 -1
  39. package/docs/workflows.md +16 -0
  40. package/package.json +2 -2
  41. package/skills/council-mode/SKILL.md +1 -4
  42. package/skills/pi-subagents/SKILL.md +130 -107
  43. package/skills/pi-subagents/references/orchestration.md +124 -0
  44. package/skills/pi-subagents/references/prompting-and-roles.md +96 -295
  45. package/src/agents/agent-management.js +3 -3
  46. package/src/agents/agent-memory.js +3 -1
  47. package/src/agents/agents.js +6 -6
  48. package/src/agents/frontmatter.d.ts +2 -0
  49. package/src/agents/frontmatter.js +15 -1
  50. package/src/agents/skills.js +2 -1
  51. package/src/extension/config.js +3 -0
  52. package/src/extension/herdr-pi-bridge.js +17 -10
  53. package/src/extension/index.js +82 -2
  54. package/src/extension/reply-workflow-script.js +5 -2
  55. package/src/extension/schemas.js +3 -3
  56. package/src/extension/subagent-guide.js +1 -2
  57. package/src/intercom/native-supervisor-channel.js +20 -0
  58. package/src/missions/lifecycle.js +7 -1
  59. package/src/runs/background/active-async-capacity.d.ts +1 -0
  60. package/src/runs/background/active-async-capacity.js +9 -3
  61. package/src/runs/background/async-execution.js +20 -10
  62. package/src/runs/background/async-job-tracker.d.ts +6 -1
  63. package/src/runs/background/async-job-tracker.js +9 -4
  64. package/src/runs/background/async-status.d.ts +4 -0
  65. package/src/runs/background/async-status.js +22 -3
  66. package/src/runs/background/await-async-run.js +3 -7
  67. package/src/runs/background/boot-id.d.ts +12 -0
  68. package/src/runs/background/boot-id.js +47 -0
  69. package/src/runs/background/interrupted-elsewhere.d.ts +22 -0
  70. package/src/runs/background/interrupted-elsewhere.js +68 -0
  71. package/src/runs/background/notify.d.ts +2 -0
  72. package/src/runs/background/notify.js +13 -0
  73. package/src/runs/background/result-delivery-ownership.d.ts +6 -0
  74. package/src/runs/background/result-delivery-ownership.js +19 -1
  75. package/src/runs/background/result-watcher.d.ts +3 -1
  76. package/src/runs/background/result-watcher.js +24 -8
  77. package/src/runs/background/run-child-session.d.ts +1 -6
  78. package/src/runs/background/run-child-session.js +1 -1
  79. package/src/runs/background/run-status.js +2 -1
  80. package/src/runs/background/stale-run-reconciler.d.ts +25 -0
  81. package/src/runs/background/stale-run-reconciler.js +52 -13
  82. package/src/runs/background/subagent-runner-bootstrap.js +3 -0
  83. package/src/runs/background/subagent-runner.d.ts +4 -0
  84. package/src/runs/background/subagent-runner.js +13 -5
  85. package/src/runs/background/subagent-wait.d.ts +1 -1
  86. package/src/runs/background/subagent-wait.js +6 -1
  87. package/src/runs/background/terminal-run-index.d.ts +2 -0
  88. package/src/runs/background/terminal-run-index.js +44 -3
  89. package/src/runs/foreground/async-stop-action.js +3 -1
  90. package/src/runs/foreground/execution.js +6 -3
  91. package/src/runs/foreground/foreground-history.d.ts +16 -2
  92. package/src/runs/foreground/foreground-history.js +133 -31
  93. package/src/runs/foreground/subagent-executor.d.ts +11 -0
  94. package/src/runs/foreground/subagent-executor.js +277 -74
  95. package/src/runs/shared/child-launch.d.ts +2 -2
  96. package/src/runs/shared/child-session.d.ts +3 -2
  97. package/src/runs/shared/child-session.js +51 -8
  98. package/src/runs/shared/child-tool-plan.d.ts +7 -0
  99. package/src/runs/shared/child-tool-plan.js +21 -6
  100. package/src/runs/shared/herdr-connection.d.ts +17 -8
  101. package/src/runs/shared/herdr-connection.js +19 -7
  102. package/src/runs/shared/herdr-external-adapters.d.ts +3 -2
  103. package/src/runs/shared/herdr-external-adapters.js +1 -1
  104. package/src/runs/shared/herdr-machine.d.ts +12 -2
  105. package/src/runs/shared/herdr-machine.js +55 -9
  106. package/src/runs/shared/herdr-pane-host.d.ts +88 -0
  107. package/src/runs/shared/herdr-pane-host.js +155 -0
  108. package/src/runs/shared/herdr-placed-run.d.ts +27 -150
  109. package/src/runs/shared/herdr-placed-run.js +48 -395
  110. package/src/runs/shared/local-machine-access.d.ts +5 -0
  111. package/src/runs/shared/local-machine-access.js +28 -0
  112. package/src/runs/shared/orca-progress-tabs.d.ts +0 -1
  113. package/src/runs/shared/orca-progress-tabs.js +1 -24
  114. package/src/runs/shared/parallel-utils.d.ts +1 -1
  115. package/src/runs/shared/placed-run-owner.d.ts +49 -0
  116. package/src/runs/shared/placed-run-owner.js +93 -0
  117. package/src/runs/shared/placement-host.d.ts +99 -0
  118. package/src/runs/shared/placement-host.js +157 -0
  119. package/src/runs/shared/rex-machine.d.ts +25 -0
  120. package/src/runs/shared/rex-machine.js +83 -0
  121. package/src/runs/shared/rex-pane-host.d.ts +62 -0
  122. package/src/runs/shared/rex-pane-host.js +327 -0
  123. package/src/shared/completion-owner.d.ts +5 -0
  124. package/src/shared/completion-owner.js +5 -0
  125. package/src/shared/session-identity.d.ts +2 -0
  126. package/src/shared/session-identity.js +26 -0
  127. package/src/shared/types.d.ts +45 -6
  128. package/src/tui/fleet.d.ts +1 -0
  129. package/src/tui/fleet.js +25 -15
  130. package/src/tui/render.d.ts +6 -0
  131. package/src/tui/render.js +3 -3
  132. package/src/tui/subagent-messages.js +1 -0
  133. package/src/workflows/scripted-workflow.d.ts +1 -1
  134. package/src/workflows/workflow-auto-resume.d.ts +32 -0
  135. package/src/workflows/workflow-auto-resume.js +102 -0
  136. package/src/workflows/workflow-launch-record.d.ts +27 -0
  137. package/src/workflows/workflow-launch-record.js +48 -0
  138. package/src/workflows/workflow-resume-claim.d.ts +82 -0
  139. package/src/workflows/workflow-resume-claim.js +210 -0
  140. package/src/workflows/workflow-resume.d.ts +56 -0
  141. package/src/workflows/workflow-resume.js +106 -0
  142. package/src/workflows/workflow-reuse.d.ts +15 -9
  143. package/src/workflows/workflow-reuse.js +77 -14
  144. package/src/workflows/workflow-revival.d.ts +2 -0
  145. package/src/workflows/workflow-revival.js +2 -1
  146. package/src/workflows/workflow-stop-cause.d.ts +6 -0
  147. package/src/workflows/workflow-stop-cause.js +8 -0
  148. package/skills/pi-subagents/references/constraints-and-recipes.md +0 -74
  149. package/skills/pi-subagents/references/execution-controls.md +0 -563
  150. package/skills/pi-subagents/references/management-authoring-rpc.md +0 -165
  151. package/skills/pi-subagents/references/multi-lane-orchestration.md +0 -61
  152. package/skills/pi-subagents/references/review-and-validation.md +0 -73
@@ -7,7 +7,7 @@ import {
7
7
  getAgentDir,
8
8
  getConfigDirName,
9
9
  getProjectConfigDir
10
- } from "./chunk-ZBC44JJ3.js";
10
+ } from "./chunk-CIZQXJ5Q.js";
11
11
  import {
12
12
  TEMP_ROOT_DIR
13
13
  } from "./chunk-5LFLVSTM.js";
@@ -935,6 +935,15 @@ function formatRuntimeSnapshotMcpServersError(agentName, serverNames) {
935
935
  const subject = agentName ? `Agent '${agentName}'` : "Subagent";
936
936
  return `${subject} selects MCP tools from servers that exist only in pi-mcp-adapter's runtime snapshot (${serverNames.join(", ")}). MCP servers from the runtime snapshot cannot be provided to in-process children; MCP tools must come from an ambient adapter extension in a background child (\`async: true\`), so add the server to the adapter's configuration file instead.`;
937
937
  }
938
+ function isToolExtensionPath(entry) {
939
+ return entry.includes("/") || entry.endsWith(".ts") || entry.endsWith(".js");
940
+ }
941
+ function assertNoToolModifierEntries(agentName, tools) {
942
+ const entries = tools.filter((entry) => /^[+-]/u.test(entry) && !isToolExtensionPath(entry));
943
+ if (entries.length === 0) return;
944
+ const subject = agentName ? `Agent '${agentName}'` : "Subagent";
945
+ throw new Error(`${subject} lists Pi tool modifier ${entries.length === 1 ? "entry" : "entries"} in \`tools\`: ${entries.join(", ")}. A child's \`tools\` list is the complete set of tools it gets, so +name and -name entries are not supported; name every tool the child needs, or omit \`tools\` and use \`excludeTools\` to drop one.`);
946
+ }
938
947
  function projectLaunchResolvedChildExtensions(toolPlan) {
939
948
  const runtime = boundedExtensionIdentifiers(toolPlan.runtimeExtensions);
940
949
  const configured = boundedExtensionIdentifiers(toolPlan.configuredExtensions);
@@ -1014,9 +1023,8 @@ function resolvePiLaunchToolPlan(input) {
1014
1023
  throw new Error(`Capability ceiling from ${capabilityCeiling.sources.join(", ") || "unknown source"} denies extensions but this host requires: ${requiredExtensions.map(({ id }) => id).join(", ")}.`);
1015
1024
  }
1016
1025
  const allowedToolSet = capabilityCeiling?.allowedTools === void 0 ? void 0 : new Set(capabilityCeiling.allowedTools);
1017
- const requestedBuiltinTools = input.tools?.filter(
1018
- (tool) => !(tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js"))
1019
- ) ?? [];
1026
+ const requestedBuiltinTools = input.tools?.filter((tool) => !isToolExtensionPath(tool)) ?? [];
1027
+ assertNoToolModifierEntries(input.agentName, input.tools ?? []);
1020
1028
  if (input.requireReadTool && allowedToolSet && !allowedToolSet.has("read")) {
1021
1029
  throw new Error(
1022
1030
  `Capability ceiling from ${capabilityCeiling?.sources.join(", ") || "unknown source"} excludes required tool 'read' for lazy skill loading.`
@@ -1031,9 +1039,7 @@ function resolvePiLaunchToolPlan(input) {
1031
1039
  if (effectiveDeclaredBuiltinTools.includes("subagent_supervisor") && !fanoutAuthorized) {
1032
1040
  throw new Error("Tool 'subagent_supervisor' requires fanout authorization: include 'subagent' in the effective tools allowlist or enable allowNestedSubagents.");
1033
1041
  }
1034
- const toolExtensionPaths = capabilityCeiling?.denyExtensions ? [] : (input.tools ?? []).filter(
1035
- (tool) => !requestedBuiltinTools.includes(tool) && (tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js"))
1036
- );
1042
+ const toolExtensionPaths = capabilityCeiling?.denyExtensions ? [] : (input.tools ?? []).filter(isToolExtensionPath);
1037
1043
  const mcpResolution = capabilityCeiling?.denyExtensions ? { selections: [], unresolvedSelectors: [] } : input.builtinMcpTools ? { selections: input.builtinMcpTools, unresolvedSelectors: [], builtin: true } : resolveMcpDirectToolResolution(input.mcpDirectTools, input.cwd, input.runtimeSnapshotHost);
1038
1044
  if (mcpResolution.runtimeServerNames?.length) {
1039
1045
  throw new Error(formatRuntimeSnapshotMcpServersError(input.agentName, mcpResolution.runtimeServerNames));
@@ -1114,9 +1120,7 @@ function resolvePiLaunchToolPlan(input) {
1114
1120
  ) ?? [],
1115
1121
  internalTools,
1116
1122
  extensionsDenied: capabilityCeiling.denyExtensions,
1117
- removedExtensionCount: capabilityCeiling.denyExtensions ? (input.extensions?.length ?? 0) + (input.subagentOnlyExtensions?.length ?? 0) + (input.tools ?? []).filter(
1118
- (tool) => tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")
1119
- ).length : 0,
1123
+ removedExtensionCount: capabilityCeiling.denyExtensions ? (input.extensions?.length ?? 0) + (input.subagentOnlyExtensions?.length ?? 0) + (input.tools ?? []).filter(isToolExtensionPath).length : 0,
1120
1124
  requestedMcpToolCount: input.mcpDirectTools?.length ?? 0,
1121
1125
  effectiveMcpTools,
1122
1126
  agentAllowed: input.agentName === void 0 ? true : isAgentAllowedByCapabilityCeiling(
@@ -1180,6 +1184,7 @@ export {
1180
1184
  deriveForkPromptCacheKey,
1181
1185
  supervisorChannelDir,
1182
1186
  applyThinkingSuffix,
1187
+ assertNoToolModifierEntries,
1183
1188
  projectLaunchResolvedChildExtensions,
1184
1189
  resolvePiLaunchToolPlan
1185
1190
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  buildWorkflowChatProgressRows
3
- } from "./chunk-KSWQVGDW.js";
3
+ } from "./chunk-FXUXCX2K.js";
4
4
  import {
5
5
  aggregateStepStatus,
6
6
  contextModeBadge,
@@ -25,13 +25,13 @@ import {
25
25
  projectWorkflowChecklist,
26
26
  withCachedUiContext,
27
27
  workflowGraphStageNodes
28
- } from "./chunk-NNLASTQE.js";
28
+ } from "./chunk-6XJB7PIX.js";
29
29
  import {
30
30
  flatToLogicalStepIndex,
31
31
  formatWorkflowJsonPreview,
32
32
  hostStepReportName,
33
33
  hostStepVerdictLabel
34
- } from "./chunk-VJHOELQP.js";
34
+ } from "./chunk-46XKCPJR.js";
35
35
  import {
36
36
  snapshotExternalRuns
37
37
  } from "./chunk-UKG4V45O.js";
@@ -49,7 +49,7 @@ import {
49
49
  getSingleResultOutput,
50
50
  shortenPath,
51
51
  unresolvedChildWatchdogBlockers
52
- } from "./chunk-ZBC44JJ3.js";
52
+ } from "./chunk-CIZQXJ5Q.js";
53
53
  import {
54
54
  MAX_WIDGET_JOBS,
55
55
  WIDGET_ANIMATION_INTERVAL_MS,
@@ -1029,12 +1029,11 @@ function shouldCollapseSingleChildDetails(job, step) {
1029
1029
  if (job.status === "complete") return isCompletedWidgetStepStatus(step.status);
1030
1030
  return false;
1031
1031
  }
1032
- function singleChildTask(job, step) {
1032
+ function stepTaskSummary(step, agentName) {
1033
1033
  const task = compactTaskText(step.description, step.label);
1034
1034
  if (task) return task;
1035
1035
  const sessionName = step.sessionName?.trim();
1036
1036
  if (!sessionName) return void 0;
1037
- const agentName = singleChildAgentName(job, step);
1038
1037
  const sessionTask = stripRepeatedAgentPrefix(sessionName, agentName);
1039
1038
  return sessionTask === agentName ? void 0 : compactTaskText(sessionTask);
1040
1039
  }
@@ -1952,7 +1951,7 @@ function foregroundStyleWidgetStepLines(job, theme, step, itemTitle, index, tota
1952
1951
  const lines = [`${rowIndent}${rowMarker}${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index - 1), frame, thinking)} ${themeBold(theme, rowLabel)}${contextModeBadge(theme, step.context)} ${theme.fg("dim", "\xB7")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`];
1953
1952
  const lane = projectAsyncLane(job, step);
1954
1953
  if (lane) lines.push(...formatLaneProjectionLines(lane, theme, detailIndent));
1955
- const task = collapseDetails ? singleChildTask(job, step) : compactTaskText(step.description, step.label);
1954
+ const task = collapseDetails ? stepTaskSummary(step, displayName) : compactTaskText(step.description, step.label);
1956
1955
  if (task) lines.push(`${detailIndent}${theme.fg("dim", `task: ${task}`)}`);
1957
1956
  const activity = widgetStepActivityLine(step, width, expanded, job.updatedAt);
1958
1957
  const currentPath = options?.includeCurrentPath && step.currentPath ? shortenPath(step.currentPath) : void 0;
@@ -4153,6 +4152,7 @@ export {
4153
4152
  buildAsyncStatusSnapshotForState,
4154
4153
  clearLegacyResultAnimationTimer,
4155
4154
  widgetRenderKey,
4155
+ stepTaskSummary,
4156
4156
  setInlineWorkflowCoverage,
4157
4157
  renderWidget,
4158
4158
  renderSubagentSummary,
@@ -3,11 +3,11 @@ import {
3
3
  } from "./chunk-7J4ZXMY7.js";
4
4
  import {
5
5
  getProjectSubagentsDir
6
- } from "./chunk-3DC5KBVP.js";
6
+ } from "./chunk-5TYMSZX3.js";
7
7
  import {
8
8
  getPiSpawnCommand,
9
9
  writeAtomicJson
10
- } from "./chunk-ZBC44JJ3.js";
10
+ } from "./chunk-CIZQXJ5Q.js";
11
11
 
12
12
  // src/inspectors/herdr/project-panes.ts
13
13
  import * as fs from "node:fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getAgentDir
3
- } from "./chunk-ZBC44JJ3.js";
3
+ } from "./chunk-CIZQXJ5Q.js";
4
4
  import {
5
5
  TEMP_ARTIFACTS_DIR
6
6
  } from "./chunk-5LFLVSTM.js";
@@ -39,10 +39,10 @@ import {
39
39
  validateModelResponseAliases,
40
40
  withWorkflowChildRunModel,
41
41
  withWorktreeTransaction
42
- } from "./chunk-VJHOELQP.js";
42
+ } from "./chunk-46XKCPJR.js";
43
43
  import {
44
44
  validateIntercomBridgeConfig
45
- } from "./chunk-ZP3HODFH.js";
45
+ } from "./chunk-XSWOTIA3.js";
46
46
  import {
47
47
  stableJsonDigest
48
48
  } from "./chunk-7F6RKT6W.js";
@@ -73,7 +73,7 @@ import {
73
73
  validateToolBudgetConfig,
74
74
  waitForFileSystemRetry,
75
75
  writeAtomicJson
76
- } from "./chunk-ZBC44JJ3.js";
76
+ } from "./chunk-CIZQXJ5Q.js";
77
77
  import {
78
78
  DIRS,
79
79
  RUNNER_LAUNCHER_NAME_PATTERN,
@@ -2133,6 +2133,7 @@ function isAsyncStatusIsolationError(asyncDir, error) {
2133
2133
  const message = getErrorMessage(error);
2134
2134
  return /^(?:Failed to (?:inspect|read|parse|validate) async status file|Invalid async status file) '/.test(message) || message.startsWith(`Invalid async status '${statusPath}'`) || message.startsWith(`Invalid host step '${statusPath}`) || /^(workflowChildren|Invalid workflowChildren)/.test(message);
2135
2135
  }
2136
+ var reportedCorruptStatus = /* @__PURE__ */ new Map();
2136
2137
  function isolateCorruptActiveRun(asyncDir, runId, error, now) {
2137
2138
  const statusPath = path3.join(asyncDir, "status.json");
2138
2139
  const processTerminal = readProcessTerminal(asyncDir, { runId });
@@ -2152,7 +2153,18 @@ function isolateCorruptActiveRun(asyncDir, runId, error, now) {
2152
2153
  markerAction = `failed to release active marker: ${getErrorMessage(releaseError)}`;
2153
2154
  }
2154
2155
  }
2155
- console.error(`[pi-subagents] Skipping corrupt active async run '${runId}' at '${statusPath}': ${getErrorMessage(error)}; ${markerAction}.`);
2156
+ const line = `[pi-subagents] Skipping corrupt active async run '${runId}' at '${statusPath}': ${getErrorMessage(error)}; ${markerAction}.`;
2157
+ let fileSignature = "";
2158
+ try {
2159
+ const stat = fs3.statSync(statusPath);
2160
+ fileSignature = `${stat.size}:${stat.mtimeMs}`;
2161
+ } catch {
2162
+ }
2163
+ const signature = `${fileSignature}
2164
+ ${line}`;
2165
+ if (reportedCorruptStatus.get(statusPath) === signature) return;
2166
+ reportedCorruptStatus.set(statusPath, signature);
2167
+ console.error(line);
2156
2168
  }
2157
2169
  function isNotFoundError(error) {
2158
2170
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
@@ -2442,7 +2454,7 @@ function listAsyncRuns(asyncDirRoot, options = {}, observeStatus) {
2442
2454
  }
2443
2455
  if (wantsTerminal) {
2444
2456
  for (const sessionId of owners ?? [void 0]) {
2445
- for (const entry of readRecentTerminalRunIndex(asyncDirRoot, { sessionId, ...options.entryLimit !== void 0 ? { limit: options.entryLimit } : {} })) indexed.add(entry);
2457
+ for (const entry of readRecentTerminalRunIndex(asyncDirRoot, { sessionId, onCorruptStatus: options.onCorruptStatus, ...options.entryLimit !== void 0 ? { limit: options.entryLimit } : {} })) indexed.add(entry);
2446
2458
  }
2447
2459
  }
2448
2460
  entries = [...indexed];
@@ -2467,14 +2479,16 @@ function listAsyncRuns(asyncDirRoot, options = {}, observeStatus) {
2467
2479
  const asyncDir = path3.join(asyncDirRoot, entry);
2468
2480
  let status;
2469
2481
  try {
2482
+ let owned = false;
2470
2483
  if (owners) {
2471
2484
  const stored = readStatus(asyncDir);
2472
2485
  if (stored && !owners.has(stored.sessionId)) {
2473
2486
  observeStatus?.(stored);
2474
2487
  continue;
2475
2488
  }
2489
+ owned = stored !== null;
2476
2490
  }
2477
- const reconciliation = options.reconcile === false ? void 0 : reconcileAsyncRun(asyncDir, { resultsDir: options.resultsDir, kill: options.kill, now: options.now }, observeStatus);
2491
+ const reconciliation = options.reconcile === false ? void 0 : reconcileAsyncRun(asyncDir, { resultsDir: options.resultsDir, kill: options.kill, now: options.now, adoptCompletionOwnerId: owned ? options.adoptCompletionOwnerId : void 0 }, observeStatus);
2478
2492
  status = reconciliation?.status ?? readStatus(asyncDir);
2479
2493
  if (options.reconcile === false) observeStatus?.(status);
2480
2494
  } catch (error) {
@@ -307,6 +307,15 @@ function parseFrontmatterList(raw) {
307
307
  return item.split(",");
308
308
  }).map((value) => value.trim()).filter(Boolean);
309
309
  }
310
+ function unquoteName(value) {
311
+ if (value.length >= 2 && (value.startsWith("'") && value.endsWith("'") || value.startsWith('"') && value.endsWith('"'))) {
312
+ return value.slice(1, -1);
313
+ }
314
+ return value;
315
+ }
316
+ function parseUnquotedList(raw) {
317
+ return parseFrontmatterList(raw)?.map(unquoteName);
318
+ }
310
319
  function parseFrontmatter(content) {
311
320
  const frontmatter = {};
312
321
  const normalized = content.replace(/\r\n/g, "\n");
@@ -350,7 +359,10 @@ function parseFrontmatter(content) {
350
359
  const [key, rawValueValue] = match.slice(1);
351
360
  if (key === void 0 || rawValueValue === void 0) continue;
352
361
  const rawValue = rawValueValue.trim();
353
- const isQuoted = rawValue.startsWith('"') && rawValue.endsWith('"') || rawValue.startsWith("'") && rawValue.endsWith("'");
362
+ const quote = rawValue[0];
363
+ const nameList = key === "tools" || key === "excludeTools" || key === "mutationTools" || key === "extensions" || key === "subagentOnlyExtensions";
364
+ const quotedList = nameList && (quote === '"' || quote === "'") && (rawValue.includes(`${quote},`) || rawValue.includes(`, ${quote}`));
365
+ const isQuoted = !quotedList && (quote === '"' || quote === "'") && rawValue.endsWith(quote);
354
366
  const value = isQuoted ? rawValue.slice(1, -1) : rawValue;
355
367
  const isFolded = !isQuoted && (rawValue === ">" || rawValue === ">-");
356
368
  const isLiteral = !isQuoted && (rawValue === "|" || rawValue === "|-");
@@ -4045,7 +4057,8 @@ function readMemoryFile(memoryDir) {
4045
4057
  let fd;
4046
4058
  try {
4047
4059
  const noFollow = typeof fs9.constants.O_NOFOLLOW === "number" ? fs9.constants.O_NOFOLLOW : 0;
4048
- fd = fs9.openSync(file, fs9.constants.O_RDONLY | noFollow);
4060
+ const nonBlock = typeof fs9.constants.O_NONBLOCK === "number" ? fs9.constants.O_NONBLOCK : 0;
4061
+ fd = fs9.openSync(file, fs9.constants.O_RDONLY | noFollow | nonBlock);
4049
4062
  } catch (error) {
4050
4063
  const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
4051
4064
  return code === "ELOOP" ? "unsafe" : null;
@@ -5691,11 +5704,11 @@ function loadAgentsFromDefinitionFiles(files, source, discoveryPriority, package
5691
5704
  else if (frontmatter.advertise === "false") advertise = false;
5692
5705
  else throw new Error(`Agent '${localName}' has invalid advertise frontmatter; expected true or false.`);
5693
5706
  }
5694
- const rawTools = parseFrontmatterList(frontmatter.tools);
5707
+ const rawTools = parseUnquotedList(frontmatter.tools);
5695
5708
  const parsedTools = splitToolList(rawTools);
5696
5709
  const tools = parsedTools.tools ?? [];
5697
5710
  const mcpDirectTools = parsedTools.mcpDirectTools ?? [];
5698
- const excludeTools = parseFrontmatterList(frontmatter.excludeTools);
5711
+ const excludeTools = parseUnquotedList(frontmatter.excludeTools);
5699
5712
  const parsedAllowedAgents = parseFrontmatterList(frontmatter.allowedAgents);
5700
5713
  const allowedAgents = parsedAllowedAgents === void 0 ? void 0 : normalizeCapabilityCeilingAllowedAgents(parsedAllowedAgents);
5701
5714
  const defaultReads = parseFrontmatterList(frontmatter.defaultReads);
@@ -5744,9 +5757,9 @@ function loadAgentsFromDefinitionFiles(files, source, discoveryPriority, package
5744
5757
  if (frontmatter.acceptanceRole === "read-only" || frontmatter.acceptanceRole === "writer") acceptanceRole = frontmatter.acceptanceRole;
5745
5758
  else throw new Error(`Agent '${localName}' has invalid acceptanceRole frontmatter; expected 'read-only' or 'writer'.`);
5746
5759
  }
5747
- const extensions = resolveAgentRelativeExtensionPaths(parseFrontmatterList(frontmatter.extensions), filePath);
5748
- const subagentOnlyExtensions = resolveAgentRelativeExtensionPaths(parseFrontmatterList(frontmatter.subagentOnlyExtensions), filePath);
5749
- const mutationTools = parseFrontmatterList(frontmatter.mutationTools);
5760
+ const extensions = resolveAgentRelativeExtensionPaths(parseUnquotedList(frontmatter.extensions), filePath);
5761
+ const subagentOnlyExtensions = resolveAgentRelativeExtensionPaths(parseUnquotedList(frontmatter.subagentOnlyExtensions), filePath);
5762
+ const mutationTools = parseUnquotedList(frontmatter.mutationTools);
5750
5763
  let fast;
5751
5764
  if (frontmatter.fast !== void 0) {
5752
5765
  if (frontmatter.fast === "true") fast = true;
@@ -7824,7 +7837,7 @@ function readOptionalJsonFile(filePath, label) {
7824
7837
  return JSON.parse(fs15.readFileSync(filePath, "utf-8"));
7825
7838
  } catch (error) {
7826
7839
  const code = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
7827
- if (code === "ENOENT") return null;
7840
+ if (code === "ENOENT" || code === "ENOTDIR") return null;
7828
7841
  const message = error instanceof Error ? error.message : String(error);
7829
7842
  throw new Error(`Failed to read ${label} '${filePath}': ${message}`, {
7830
7843
  cause: error instanceof Error ? error : void 0
@@ -8393,7 +8406,7 @@ export {
8393
8406
  resolveExternalCliRunnerStatus,
8394
8407
  normalizeExternalCliRunnerStatus,
8395
8408
  externalCliReceiptMetadata,
8396
- parseFrontmatterList,
8409
+ parseUnquotedList,
8397
8410
  parseFrontmatter,
8398
8411
  resolveSkills,
8399
8412
  resolveSkillsWithFallback,
@@ -11,11 +11,15 @@ import {
11
11
  resolveAsyncStatusChild,
12
12
  stopStoppableAsyncStatusChildren,
13
13
  summarizeContextModes,
14
+ utf8Tail,
14
15
  workflowPreflightLaneForRuntimeKey
15
- } from "./chunk-NNLASTQE.js";
16
+ } from "./chunk-6XJB7PIX.js";
16
17
  import {
17
18
  assertSafeNestedId,
19
+ checkRecordedPidLiveness,
20
+ encodeIndexSegment,
18
21
  findNestedRunMatchesById,
22
+ isRecordedPid,
19
23
  readActiveRunToolCallIndex,
20
24
  readProcessTerminal,
21
25
  readTerminalRunToolCallIndex,
@@ -26,12 +30,13 @@ import {
26
30
  resultPayloadPathForIndexedRun,
27
31
  resultPayloadPathForSessionRun,
28
32
  updateActiveRunIndex,
33
+ validateModelResponseAliases,
29
34
  writeAsyncResultFile
30
- } from "./chunk-VJHOELQP.js";
35
+ } from "./chunk-46XKCPJR.js";
31
36
  import {
32
37
  deliverStopRequest,
33
38
  stopInboxClosedPath
34
- } from "./chunk-BJN5LUQY.js";
39
+ } from "./chunk-OYF6AFDY.js";
35
40
  import {
36
41
  SUBAGENT_STOPPED_MESSAGE,
37
42
  formatDuration,
@@ -39,8 +44,10 @@ import {
39
44
  formatTokens,
40
45
  readStatus,
41
46
  shortenPath,
42
- writeAtomicJson
43
- } from "./chunk-ZBC44JJ3.js";
47
+ validateAcceptanceInput,
48
+ writeAtomicJson,
49
+ writePrivateAtomicJson
50
+ } from "./chunk-CIZQXJ5Q.js";
44
51
  import {
45
52
  DIRS
46
53
  } from "./chunk-5LFLVSTM.js";
@@ -49,15 +56,6 @@ import {
49
56
  truncateDisplayText
50
57
  } from "./chunk-YFCK7H2S.js";
51
58
 
52
- // src/shared/completion-owner.ts
53
- import { randomUUID } from "node:crypto";
54
- var COMPLETION_OWNER_KEY = /* @__PURE__ */ Symbol.for("pi-subagents.completion-owner-id");
55
- function currentCompletionOwnerId() {
56
- const runtime = globalThis;
57
- runtime[COMPLETION_OWNER_KEY] ??= randomUUID();
58
- return runtime[COMPLETION_OWNER_KEY];
59
- }
60
-
61
59
  // src/workflows/chat-progress.ts
62
60
  import { spawnSync } from "node:child_process";
63
61
  import * as fs from "node:fs";
@@ -411,8 +409,8 @@ function formatDetachedForegroundFleetLines(runs) {
411
409
  }
412
410
  function externalCliStepFacts(step, now) {
413
411
  if (step.runner?.type !== "external-cli") return void 0;
414
- const process = step.externalProcess;
415
- const elapsed = process ? process.durationMs ?? Math.max(0, (process.endedAt ?? now) - process.startedAt) : void 0;
412
+ const process2 = step.externalProcess;
413
+ const elapsed = process2 ? process2.durationMs ?? Math.max(0, (process2.endedAt ?? now) - process2.startedAt) : void 0;
416
414
  return `external-cli${elapsed !== void 0 ? ` \xB7 ${formatDuration(elapsed)}` : ""}`;
417
415
  }
418
416
  function formatAsyncFleetLines(runs, now = Date.now()) {
@@ -861,6 +859,8 @@ function sealPausedRun(asyncDir, status, resultsDir) {
861
859
  sessionId: status.sessionId,
862
860
  asyncDir,
863
861
  ...status.completionOwnerId ? { completionOwnerId: status.completionOwnerId } : {},
862
+ ...status.completionOwnerProcess ? { completionOwnerProcess: status.completionOwnerProcess } : {},
863
+ ...status.parentWorkflowRunId ? { parentWorkflowRunId: status.parentWorkflowRunId } : {},
864
864
  ...status.toolCallId ? { toolCallId: status.toolCallId } : {},
865
865
  results: (status.steps ?? []).map((step) => ({
866
866
  agent: step.agent,
@@ -1010,8 +1010,209 @@ function stopAsyncRun(state, runId, kill, location, childId) {
1010
1010
  }
1011
1011
  }
1012
1012
 
1013
+ // src/runs/foreground/foreground-history.ts
1014
+ import * as fs6 from "node:fs";
1015
+ import * as path6 from "node:path";
1016
+ var MAX_REMEMBERED_FOREGROUND_RUNS = 50;
1017
+ var FOREGROUND_OWNER_EXITED_STOP_ERROR = "Subagent stopped because the Pi process that ran it exited before the run finished.";
1018
+ var MAX_INLINE_OUTPUT_BYTES = 64 * 1024;
1019
+ var MAX_RESUME_CONTRACT_BYTES = 64 * 1024;
1020
+ function historyDir(resultsDir) {
1021
+ return path6.join(resultsDir, "foreground-history");
1022
+ }
1023
+ function runHistoryPath(resultsDir, runId) {
1024
+ return path6.join(historyDir(resultsDir), `${encodeIndexSegment(runId)}.json`);
1025
+ }
1026
+ function boundedTail(value) {
1027
+ return utf8Tail(value, MAX_INLINE_OUTPUT_BYTES).text;
1028
+ }
1029
+ function compactChild(child) {
1030
+ const outputPath = child.artifactPaths?.outputPath ?? child.savedOutputPath;
1031
+ return {
1032
+ agent: child.agent,
1033
+ index: child.index,
1034
+ ...child.sessionName ? { sessionName: child.sessionName } : {},
1035
+ ...child.context ? { context: child.context } : {},
1036
+ ...child.sessionFile ? { sessionFile: child.sessionFile } : {},
1037
+ ...child.model ? { model: child.model } : {},
1038
+ ...child.thinking ? { thinking: child.thinking } : {},
1039
+ status: child.status,
1040
+ ...child.activityState ? { activityState: child.activityState } : {},
1041
+ ...child.lastActivityAt !== void 0 ? { lastActivityAt: child.lastActivityAt } : {},
1042
+ ...child.currentTool ? { currentTool: child.currentTool } : {},
1043
+ ...child.currentToolStartedAt !== void 0 ? { currentToolStartedAt: child.currentToolStartedAt } : {},
1044
+ ...child.currentPath ? { currentPath: child.currentPath } : {},
1045
+ ...child.turnCount !== void 0 ? { turnCount: child.turnCount } : {},
1046
+ ...child.tokens !== void 0 ? { tokens: child.tokens } : {},
1047
+ ...child.toolCount !== void 0 ? { toolCount: child.toolCount } : {},
1048
+ ...child.exitCode !== void 0 ? { exitCode: child.exitCode } : {},
1049
+ ...child.error ? { error: child.error } : {},
1050
+ ...!outputPath && child.finalOutput ? { finalOutput: boundedTail(child.finalOutput) } : {},
1051
+ ...child.outputState ? { outputState: child.outputState } : {},
1052
+ ...child.outputPartial ? { outputPartial: true } : {},
1053
+ ...child.outputMode ? { outputMode: child.outputMode } : {},
1054
+ ...child.savedOutputPath ? { savedOutputPath: child.savedOutputPath } : {},
1055
+ ...child.outputSaveError ? { outputSaveError: child.outputSaveError } : {},
1056
+ ...child.artifactPaths ? { artifactPaths: child.artifactPaths } : {},
1057
+ ...child.transcriptPath ? { transcriptPath: child.transcriptPath } : {},
1058
+ ...child.transcriptError ? { transcriptError: child.transcriptError } : {},
1059
+ ...child.acceptance ? { acceptance: child.acceptance } : {},
1060
+ ...child.resumeContract ? { resumeContract: child.resumeContract } : {},
1061
+ ...child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {},
1062
+ ...child.extensionBindings ? { extensionBindings: child.extensionBindings } : {},
1063
+ ...child.requiredExtensions ? { requiredExtensions: child.requiredExtensions } : {},
1064
+ ...child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {},
1065
+ ...child.updatedAt !== void 0 ? { updatedAt: child.updatedAt } : {}
1066
+ };
1067
+ }
1068
+ function isRestorableForegroundStatus(status) {
1069
+ return status === "completed" || status === "failed" || status === "paused" || status === "stopped";
1070
+ }
1071
+ function hasRunningChild(run) {
1072
+ return run.children.some((child) => child.status === "running");
1073
+ }
1074
+ function isAdmittedChildStatus(run, status) {
1075
+ return isRestorableForegroundStatus(status) || status === "running" && run.mode === "single" && isRecordedPid(run.owner);
1076
+ }
1077
+ function isRestorableResumeContract(value) {
1078
+ if (value === void 0) return true;
1079
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1080
+ try {
1081
+ if (Buffer.byteLength(JSON.stringify(value), "utf8") > MAX_RESUME_CONTRACT_BYTES) return false;
1082
+ validateModelResponseAliases(value?.modelResponseAliases);
1083
+ } catch {
1084
+ return false;
1085
+ }
1086
+ const contract = value;
1087
+ if (Object.keys(contract).some((key) => !["modelResponseAliases", "outputSchema", "agentContract", "acceptance", "output", "outputMode"].includes(key))) return false;
1088
+ if (contract.outputSchema !== void 0 && contract.outputSchema !== false && (!contract.outputSchema || typeof contract.outputSchema !== "object" || Array.isArray(contract.outputSchema))) return false;
1089
+ if (contract.agentContract !== void 0 && (!contract.agentContract || typeof contract.agentContract !== "object" || Array.isArray(contract.agentContract) || contract.agentContract.version !== 1)) return false;
1090
+ if (validateAcceptanceInput(contract.acceptance).length > 0) return false;
1091
+ if (contract.output !== void 0 && typeof contract.output !== "string" && typeof contract.output !== "boolean") return false;
1092
+ return contract.outputMode === void 0 || contract.outputMode === "inline" || contract.outputMode === "file-only";
1093
+ }
1094
+ function compactRun(run) {
1095
+ if (!run.sessionId) return void 0;
1096
+ if (run.children.length === 0 || !run.children.every((child) => isAdmittedChildStatus(run, child.status) && isRestorableResumeContract(child.resumeContract))) return void 0;
1097
+ const running = hasRunningChild(run);
1098
+ return {
1099
+ runId: run.runId,
1100
+ mode: run.mode,
1101
+ cwd: run.cwd,
1102
+ sessionId: run.sessionId,
1103
+ updatedAt: run.updatedAt,
1104
+ children: run.children.map(compactChild),
1105
+ ...running ? { owner: run.owner } : {},
1106
+ ...run.parentWorkflowRunId ? { parentWorkflowRunId: run.parentWorkflowRunId } : {}
1107
+ };
1108
+ }
1109
+ function readRunFile(filePath) {
1110
+ try {
1111
+ const parsed = JSON.parse(fs6.readFileSync(filePath, "utf-8"));
1112
+ return isRestorableRun(parsed) ? parsed : void 0;
1113
+ } catch {
1114
+ return void 0;
1115
+ }
1116
+ }
1117
+ function historyFiles(dir) {
1118
+ try {
1119
+ return fs6.readdirSync(dir).filter((name) => name.endsWith(".json"));
1120
+ } catch {
1121
+ return [];
1122
+ }
1123
+ }
1124
+ function isRestorableRun(value) {
1125
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1126
+ const run = value;
1127
+ return typeof run.runId === "string" && Boolean(run.runId) && (run.mode === "single" || run.mode === "parallel" || run.mode === "chain") && typeof run.cwd === "string" && Boolean(run.cwd) && typeof run.sessionId === "string" && Boolean(run.sessionId) && typeof run.updatedAt === "number" && Number.isFinite(run.updatedAt) && Array.isArray(run.children) && run.children.length > 0 && (run.owner === void 0 || isRecordedPid(run.owner)) && (run.parentWorkflowRunId === void 0 || typeof run.parentWorkflowRunId === "string") && run.children.every((child) => Boolean(child && typeof child === "object" && !Array.isArray(child) && typeof child.agent === "string" && typeof child.index === "number" && isRestorableResumeContract(child.resumeContract) && isAdmittedChildStatus(run, child.status)));
1128
+ }
1129
+ function pruneHistory(dir, limit) {
1130
+ const files = historyFiles(dir);
1131
+ if (files.length <= limit) return;
1132
+ const byAge = files.flatMap((name) => {
1133
+ try {
1134
+ return [{ name, mtimeMs: fs6.statSync(path6.join(dir, name)).mtimeMs }];
1135
+ } catch {
1136
+ return [];
1137
+ }
1138
+ }).sort((left, right) => left.mtimeMs - right.mtimeMs);
1139
+ for (const { name, mtimeMs } of byAge.slice(0, byAge.length - limit)) {
1140
+ const filePath = path6.join(dir, name);
1141
+ const run = readRunFile(filePath);
1142
+ if (run && hasRunningChild(run) && !ownerConfirmedDead(run)) continue;
1143
+ try {
1144
+ if (fs6.statSync(filePath).mtimeMs !== mtimeMs) continue;
1145
+ } catch {
1146
+ continue;
1147
+ }
1148
+ fs6.rmSync(filePath, { force: true });
1149
+ }
1150
+ }
1151
+ function persistForegroundRun(run, options = {}) {
1152
+ const compact = compactRun(run);
1153
+ if (!compact) return;
1154
+ const resultsDir = options.resultsDir ?? DIRS.results;
1155
+ const dir = historyDir(resultsDir);
1156
+ if (!fs6.existsSync(dir)) {
1157
+ fs6.mkdirSync(dir, { recursive: true });
1158
+ fs6.rmSync(path6.join(resultsDir, "foreground-history.json"), { force: true });
1159
+ }
1160
+ writePrivateAtomicJson(runHistoryPath(resultsDir, compact.runId), compact);
1161
+ pruneHistory(dir, options.limit ?? MAX_REMEMBERED_FOREGROUND_RUNS);
1162
+ }
1163
+ function discardRunningForegroundRun(runId, options = {}) {
1164
+ const filePath = runHistoryPath(options.resultsDir ?? DIRS.results, runId);
1165
+ const run = readRunFile(filePath);
1166
+ if (run && hasRunningChild(run) && run.owner?.pid === process.pid) fs6.rmSync(filePath, { force: true });
1167
+ }
1168
+ function ownerConfirmedDead(run) {
1169
+ return checkRecordedPidLiveness(run.owner) === "dead";
1170
+ }
1171
+ function restoreInterruptedRun(run) {
1172
+ if (!ownerConfirmedDead(run)) return void 0;
1173
+ const { owner: _owner, ...rest } = run;
1174
+ return {
1175
+ ...rest,
1176
+ children: run.children.map((child) => {
1177
+ if (child.status !== "running") return child;
1178
+ const { activityState: _activityState, currentTool: _currentTool, currentToolStartedAt: _currentToolStartedAt, currentPath: _currentPath, sessionFile, ...stopped } = child;
1179
+ return {
1180
+ ...stopped,
1181
+ status: "stopped",
1182
+ exitCode: 1,
1183
+ error: FOREGROUND_OWNER_EXITED_STOP_ERROR,
1184
+ finalOutput: FOREGROUND_OWNER_EXITED_STOP_ERROR,
1185
+ ...sessionFile && fs6.existsSync(sessionFile) ? { sessionFile } : {}
1186
+ };
1187
+ })
1188
+ };
1189
+ }
1190
+ function foregroundChildStateLabel(run, child) {
1191
+ if (child.status !== "stopped" || child.error !== FOREGROUND_OWNER_EXITED_STOP_ERROR) return child.status;
1192
+ return child.sessionFile ? `interrupted when Pi exited \xB7 resume ${run.runId}` : "interrupted when Pi exited";
1193
+ }
1194
+ function restoreForegroundRunHistory(state, options = {}) {
1195
+ const sessionId = options.sessionId ?? state.currentSessionId;
1196
+ if (!sessionId) return 0;
1197
+ const dir = historyDir(options.resultsDir ?? DIRS.results);
1198
+ const saved = historyFiles(dir).flatMap((name) => readRunFile(path6.join(dir, name)) ?? []);
1199
+ if (options.observeForeign) {
1200
+ for (const run of saved) if (run.sessionId !== sessionId && hasRunningChild(run)) options.observeForeign(run);
1201
+ }
1202
+ const runs = saved.filter((run) => run.sessionId === sessionId).sort((left, right) => right.updatedAt - left.updatedAt).slice(0, options.limit ?? MAX_REMEMBERED_FOREGROUND_RUNS);
1203
+ state.foregroundRuns ??= /* @__PURE__ */ new Map();
1204
+ let restored = 0;
1205
+ for (const run of runs) {
1206
+ if (state.foregroundRuns.has(run.runId)) continue;
1207
+ const loaded = hasRunningChild(run) ? restoreInterruptedRun(run) : run;
1208
+ if (!loaded) continue;
1209
+ state.foregroundRuns.set(run.runId, loaded);
1210
+ restored += 1;
1211
+ }
1212
+ return restored;
1213
+ }
1214
+
1013
1215
  export {
1014
- currentCompletionOwnerId,
1015
1216
  resolveGitRepositoryIdentity,
1016
1217
  resolveWorkflowChatProgress,
1017
1218
  buildWorkflowChatProgressRows,
@@ -1023,5 +1224,11 @@ export {
1023
1224
  formatAsyncResultTranscript,
1024
1225
  resolveSubagentRunId,
1025
1226
  deliverAsyncRunStop,
1026
- stopAsyncRun
1227
+ stopAsyncRun,
1228
+ MAX_REMEMBERED_FOREGROUND_RUNS,
1229
+ persistForegroundRun,
1230
+ discardRunningForegroundRun,
1231
+ ownerConfirmedDead,
1232
+ foregroundChildStateLabel,
1233
+ restoreForegroundRunHistory
1027
1234
  };