pi-subagents 0.81.0 → 0.82.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 (141) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/bundle/agents.js +3 -3
  3. package/bundle/child-tool-plan.js +2 -2
  4. package/bundle/{chunk-LSRUTHT7.js → chunk-43SCHIMD.js} +1 -1
  5. package/bundle/{chunk-G5DKFLLI.js → chunk-4SDU2BZX.js} +15 -10
  6. package/bundle/{chunk-V2JWDB5U.js → chunk-52UBALJL.js} +2 -2
  7. package/bundle/{chunk-3DC5KBVP.js → chunk-5TYMSZX3.js} +1 -1
  8. package/bundle/{chunk-CEKS3TFO.js → chunk-7IVSUMQQ.js} +6 -6
  9. package/bundle/{chunk-KSWQVGDW.js → chunk-7PCKZHR5.js} +225 -18
  10. package/bundle/{chunk-563BHAZ5.js → chunk-7YMXOYEU.js} +33 -25
  11. package/bundle/{chunk-XMFLCKMQ.js → chunk-B5EKXDHE.js} +336 -130
  12. package/bundle/{chunk-ZBC44JJ3.js → chunk-CIZQXJ5Q.js} +22 -9
  13. package/bundle/{chunk-L5ZHE4L4.js → chunk-GC4DSPMH.js} +4 -4
  14. package/bundle/{chunk-ARRKBB2B.js → chunk-GJECHSAR.js} +2 -2
  15. package/bundle/{chunk-2ZSNH26H.js → chunk-M2CTFYT3.js} +7 -7
  16. package/bundle/{chunk-TUWLXTAM.js → chunk-MHZ4G23A.js} +681 -320
  17. package/bundle/{chunk-WSBNENFS.js → chunk-NGGAFDL6.js} +14 -4
  18. package/bundle/{chunk-VJHOELQP.js → chunk-NRQ75MGH.js} +160 -78
  19. package/bundle/{chunk-BJN5LUQY.js → chunk-OYF6AFDY.js} +1 -1
  20. package/bundle/{chunk-EYNJ6VL6.js → chunk-PLCCBLPY.js} +2 -2
  21. package/bundle/{chunk-74RT5YQE.js → chunk-RMQFMMAT.js} +123 -33
  22. package/bundle/{chunk-V6MR5TOQ.js → chunk-SIJ4S3EN.js} +25 -19
  23. package/bundle/{chunk-NNLASTQE.js → chunk-VH5OAWVG.js} +19 -5
  24. package/bundle/{chunk-BPS2AGBZ.js → chunk-W2SZ22EM.js} +93 -52
  25. package/bundle/{chunk-V3BIUPLU.js → chunk-WVD4UEL4.js} +256 -52
  26. package/bundle/{chunk-ZP3HODFH.js → chunk-XSWOTIA3.js} +1 -1
  27. package/bundle/{chunk-3IF2PGW7.js → chunk-YKEWIB4M.js} +1 -1
  28. package/bundle/{chunk-26C3WLYQ.js → chunk-ZLINIYTP.js} +497 -272
  29. package/bundle/{chunk-2XYWN7QJ.js → chunk-ZQGSDHL6.js} +2 -2
  30. package/bundle/control-channel.js +2 -2
  31. package/bundle/index.js +1 -1
  32. package/bundle/intercom-bridge.js +2 -2
  33. package/bundle/preflight.js +8 -8
  34. package/bundle/project-panes.js +3 -3
  35. package/docs/agents.md +31 -0
  36. package/docs/configuration.md +9 -1
  37. package/docs/tool-reference.md +3 -1
  38. package/docs/workflows.md +16 -0
  39. package/package.json +2 -2
  40. package/skills/pi-subagents/references/execution-controls.md +5 -4
  41. package/src/agents/agent-management.js +3 -3
  42. package/src/agents/agent-memory.js +3 -1
  43. package/src/agents/agents.js +6 -6
  44. package/src/agents/frontmatter.d.ts +2 -0
  45. package/src/agents/frontmatter.js +15 -1
  46. package/src/agents/skills.js +2 -1
  47. package/src/extension/config.js +3 -0
  48. package/src/extension/herdr-pi-bridge.js +17 -10
  49. package/src/extension/index.js +82 -2
  50. package/src/extension/schemas.js +3 -3
  51. package/src/intercom/native-supervisor-channel.js +20 -0
  52. package/src/missions/lifecycle.js +7 -1
  53. package/src/runs/background/active-async-capacity.d.ts +1 -0
  54. package/src/runs/background/active-async-capacity.js +9 -3
  55. package/src/runs/background/async-execution.js +20 -10
  56. package/src/runs/background/async-job-tracker.d.ts +6 -1
  57. package/src/runs/background/async-job-tracker.js +9 -4
  58. package/src/runs/background/async-status.d.ts +2 -0
  59. package/src/runs/background/async-status.js +21 -2
  60. package/src/runs/background/await-async-run.js +3 -7
  61. package/src/runs/background/boot-id.d.ts +12 -0
  62. package/src/runs/background/boot-id.js +47 -0
  63. package/src/runs/background/interrupted-elsewhere.d.ts +22 -0
  64. package/src/runs/background/interrupted-elsewhere.js +68 -0
  65. package/src/runs/background/notify.d.ts +2 -0
  66. package/src/runs/background/notify.js +13 -0
  67. package/src/runs/background/result-delivery-ownership.d.ts +6 -0
  68. package/src/runs/background/result-delivery-ownership.js +19 -1
  69. package/src/runs/background/result-watcher.d.ts +3 -1
  70. package/src/runs/background/result-watcher.js +24 -8
  71. package/src/runs/background/run-child-session.d.ts +1 -6
  72. package/src/runs/background/run-child-session.js +1 -1
  73. package/src/runs/background/run-status.js +2 -1
  74. package/src/runs/background/stale-run-reconciler.d.ts +25 -0
  75. package/src/runs/background/stale-run-reconciler.js +52 -13
  76. package/src/runs/background/subagent-runner-bootstrap.js +3 -0
  77. package/src/runs/background/subagent-runner.d.ts +4 -0
  78. package/src/runs/background/subagent-runner.js +13 -5
  79. package/src/runs/background/subagent-wait.d.ts +1 -1
  80. package/src/runs/background/subagent-wait.js +6 -1
  81. package/src/runs/background/terminal-run-index.d.ts +1 -0
  82. package/src/runs/background/terminal-run-index.js +4 -2
  83. package/src/runs/foreground/async-stop-action.js +3 -1
  84. package/src/runs/foreground/execution.js +6 -3
  85. package/src/runs/foreground/foreground-history.d.ts +16 -2
  86. package/src/runs/foreground/foreground-history.js +133 -31
  87. package/src/runs/foreground/subagent-executor.d.ts +11 -0
  88. package/src/runs/foreground/subagent-executor.js +277 -74
  89. package/src/runs/shared/child-launch.d.ts +2 -2
  90. package/src/runs/shared/child-session.d.ts +3 -2
  91. package/src/runs/shared/child-session.js +51 -8
  92. package/src/runs/shared/child-tool-plan.d.ts +7 -0
  93. package/src/runs/shared/child-tool-plan.js +21 -6
  94. package/src/runs/shared/herdr-connection.d.ts +17 -8
  95. package/src/runs/shared/herdr-connection.js +19 -7
  96. package/src/runs/shared/herdr-external-adapters.d.ts +3 -2
  97. package/src/runs/shared/herdr-external-adapters.js +1 -1
  98. package/src/runs/shared/herdr-machine.d.ts +12 -2
  99. package/src/runs/shared/herdr-machine.js +55 -9
  100. package/src/runs/shared/herdr-pane-host.d.ts +88 -0
  101. package/src/runs/shared/herdr-pane-host.js +155 -0
  102. package/src/runs/shared/herdr-placed-run.d.ts +27 -150
  103. package/src/runs/shared/herdr-placed-run.js +48 -395
  104. package/src/runs/shared/local-machine-access.d.ts +5 -0
  105. package/src/runs/shared/local-machine-access.js +28 -0
  106. package/src/runs/shared/orca-progress-tabs.d.ts +0 -1
  107. package/src/runs/shared/orca-progress-tabs.js +1 -24
  108. package/src/runs/shared/parallel-utils.d.ts +1 -1
  109. package/src/runs/shared/placed-run-owner.d.ts +49 -0
  110. package/src/runs/shared/placed-run-owner.js +93 -0
  111. package/src/runs/shared/placement-host.d.ts +99 -0
  112. package/src/runs/shared/placement-host.js +157 -0
  113. package/src/runs/shared/rex-machine.d.ts +25 -0
  114. package/src/runs/shared/rex-machine.js +83 -0
  115. package/src/runs/shared/rex-pane-host.d.ts +62 -0
  116. package/src/runs/shared/rex-pane-host.js +327 -0
  117. package/src/shared/completion-owner.d.ts +5 -0
  118. package/src/shared/completion-owner.js +5 -0
  119. package/src/shared/session-identity.d.ts +2 -0
  120. package/src/shared/session-identity.js +26 -0
  121. package/src/shared/types.d.ts +45 -6
  122. package/src/tui/fleet.d.ts +1 -0
  123. package/src/tui/fleet.js +24 -15
  124. package/src/tui/render.d.ts +6 -0
  125. package/src/tui/render.js +3 -3
  126. package/src/tui/subagent-messages.js +1 -0
  127. package/src/workflows/scripted-workflow.d.ts +1 -1
  128. package/src/workflows/workflow-auto-resume.d.ts +32 -0
  129. package/src/workflows/workflow-auto-resume.js +102 -0
  130. package/src/workflows/workflow-launch-record.d.ts +27 -0
  131. package/src/workflows/workflow-launch-record.js +48 -0
  132. package/src/workflows/workflow-resume-claim.d.ts +82 -0
  133. package/src/workflows/workflow-resume-claim.js +210 -0
  134. package/src/workflows/workflow-resume.d.ts +56 -0
  135. package/src/workflows/workflow-resume.js +106 -0
  136. package/src/workflows/workflow-reuse.d.ts +15 -9
  137. package/src/workflows/workflow-reuse.js +77 -14
  138. package/src/workflows/workflow-revival.d.ts +2 -0
  139. package/src/workflows/workflow-revival.js +2 -1
  140. package/src/workflows/workflow-stop-cause.d.ts +6 -0
  141. package/src/workflows/workflow-stop-cause.js +8 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.82.0] - 2026-10-24
6
+
7
+ ### Highlights
8
+
9
+ - Background workflows cut off by Pi crashing, quitting or reloading resume where they stopped when you reopen their session, without running finished steps again.
10
+ - Background runs that finished while Pi was closed report their results when you reopen their session, and a foreground run cut off by a crash can be resumed by id.
11
+ - Pi children can run in their own Rex terminal tabs, on this Mac or on another Mac over SSH.
12
+ - After a machine restart, runs that died with it no longer show as running or hold a capacity slot.
13
+ - `/subagents-fleet` shows each child's task next to its agent name, so children of one run are easy to tell apart.
14
+
15
+ ### Added
16
+
17
+ - Background workflows cut off by Pi exiting, crashing or reloading now resume on their own when you reopen or reload their session within a day, and steps that already finished are not run again. You can also resume one yourself with `subagent({ action: "resume", id })`, or set `autoResume: false` to resume them only by hand. A stopped workflow is resumed only once, and its notice says how it resumed. If you start a new Pi in the same folder instead, it tells you which session to reopen. See [docs/workflows.md](docs/workflows.md).
18
+ - `machine: "local"` runs a native Pi child in a new background tab of your own Rex session, with the same controls as a Herdr-placed Pi child: prompts, steering, follow-ups, abort, `contact_supervisor` and results. The launch fails if the parent Pi is not running inside Rex, and Claude Code, Codex, Cursor and generic `external-cli` agents are rejected. A finished child's tab stays open until you close it; an aborted or stopped child's tab is closed. See [docs/agents.md](docs/agents.md#running-a-pi-child-in-a-rex-pane-on-this-machine).
19
+ - A native Pi child can also run in a Rex tab on another Mac over SSH. Name the Mac in your user settings with `subagents.machines.<name>.rex.ssh`, then pass `machine: "<name>"`. The child opens in that Mac's `subagents` Rex session, which is created the first time it is needed. These machines do not need Herdr, and project settings cannot name a Rex host. See [docs/agents.md](docs/agents.md#running-a-pi-child-in-a-rex-pane-on-another-mac).
20
+
21
+ ### Changed
22
+
23
+ - Each background child's row in `/subagents-fleet` now shows its task after the agent name, for example `worker · Fix two Cycode Python SAST findings…`, so you can tell children of one run apart without opening each one. The selected child's header still shows the run id. Thanks to [@MattDavisRV](https://github.com/MattDavisRV) for #3020.
24
+
25
+ ### Fixed
26
+
27
+ - A background run that was still working when Pi exited or crashed never reported its result, even after you reopened its session. Its result now shows once in the reopened session: right away if the run already finished, or as soon as it finishes.
28
+ - A foreground single run cut off by a Pi crash can now be resumed by id. It used to be recorded only when it ended, so nothing pointed to its transcript. It is now recorded when it starts, shows as `interrupted when Pi exited · resume <id>` when its session reopens, and `subagent({ action: "resume", id, message })` continues it from its transcript. Foreground runs remembered by earlier versions are not carried over and can no longer be resumed by id.
29
+ - After Pi crashed or was killed while an async workflow ran, relaunching the same workflow ran every child again. The workflow is now marked `stopCause: "owner-exited"` when its session reopens, or when `options.reuseFrom` names it from another session, and a relaunch reuses the children that already finished, the same as after `/reload`. Its failed result says the Pi process exited. See [docs/workflows.md](docs/workflows.md).
30
+ - A workflow relaunched more than once could start a second copy of a child that was still running, or run a finished child again. A relaunch only looked at the run it reused, and that run had no record of children still waiting for a concurrency slot when it stopped. A relaunch now looks at every earlier run in its relaunch chain and takes each child from the newest run that recorded it. Thanks to [@wireknight](https://github.com/wireknight) for the report on #3021.
31
+ - After a machine restart, a background run or workflow that died with the machine could show as running, and hold its capacity slot, for up to a day, because its old process id could now belong to another process. On Linux and macOS, Pi now saves a boot id next to each process id and treats a process id from an earlier boot as dead. Runs started through a `launcher` are unchanged.
32
+ - A background child awaited by an `async: false` workflow now gets an immediate `contact_supervisor` tool error when it asks a question, because its blocked parent cannot answer. The error tells it to report partial results or use an async workflow. Before, the question could wait until the run deadline and reach the parent after the child had timed out. Async workflows and children that are not awaited still get normal supervisor replies. Thanks to [@XLi-hub](https://github.com/XLi-hub) for #3028 and [@isjiajia01](https://github.com/isjiajia01) for the report in #3018.
33
+ - Agents called `bg_wait` in a loop when it returned early because a run that was still working needed attention, such as a long `bash` command or a quiet stretch. The result now says the run is still running and that `stopOnAttention: false` waits through this, and the option's description says it still stops for supervisor requests. Thanks to [@jettoblack](https://github.com/jettoblack) for #3019.
34
+ - A quoted name in an agent's `tools`, `excludeTools`, `mutationTools`, `extensions` or `subagentOnlyExtensions` list, such as `tools: "read", "bash"` or a `- "read"` list item, is no longer read with its quote marks. A quoted tool name made the child fail to start, and a quoted extension path pointed at a file that doesn't exist. Thanks to [@HerbertGao](https://github.com/HerbertGao) for #3017.
35
+ - An agent whose `tools` list has a Pi `+name` or `-name` entry, such as `tools: +codemode`, now fails before launch with an error that names the agent and the entry. Before, the entry was kept as a literal tool name, so the child required a tool named `+codemode` that no child has, and adding `read` for skills, MCP tools or `structured_output` produced a list that Pi rejects. A Herdr- or Rex-placed child whose agent definition on that machine has such an entry fails the same way before its first prompt. `tools` is a complete list: name every tool the child needs, or omit `tools` and use `excludeTools` to drop one. See [docs/agents.md](docs/agents.md#tool-and-extension-selection). Thanks to [@Nyarlathoteppppp](https://github.com/Nyarlathoteppppp) for #3029.
36
+ - A host-required child extension (`registerRequiredChildExtensions`) is now recognized when Pi loads it through another spelling of its path, such as a symlink, a different letter case, or a path with a Unicode space, so its load, provider-registration and startup failures still stop the launch. A required extension that Pi did not load at all now stops the launch instead of letting the child start without it. Thanks to [@pandalaohe](https://github.com/pandalaohe) for #3012.
37
+ - A child agent's `MEMORY.md` that is a named pipe (FIFO) no longer stalls its launch while the prompt is built; it is skipped like any other file that is not a regular file. Thanks to [@XLi-hub](https://github.com/XLi-hub) for #3027.
38
+ - Skill discovery no longer fails with `ENOTDIR` when a `settings.json` `packages` entry names a single extension file. Thanks to [@tara-marchand](https://github.com/tara-marchand) for #3011.
39
+ - Herdr-placed native Pi children no longer fail before their first prompt. The child's pane bridge looked for its active tools where Pi does not provide them, found none, and rejected every launch.
40
+ - A corrupt saved run status or mission file no longer prints the same warning on every status refresh or retry. The warning prints once, and again only when the file or its outcome changes. The run is still skipped or retried as before, and the file is left as it is. Thanks to [@ozand](https://github.com/ozand) for #3037 and the report in #3036.
41
+ - Updated `undici` from 8.10.2 to 8.12.3, outside the range of [GHSA-xjj6-f33q-929c](https://github.com/advisories/GHSA-xjj6-f33q-929c), so projects that install pi-subagents no longer fail `npm audit` because of it. Thanks to [@setanta00](https://github.com/setanta00) for #3022.
42
+
5
43
  ## [0.81.0] - 2026-10-22
6
44
 
7
45
  ### Highlights
package/bundle/agents.js CHANGED
@@ -2,11 +2,11 @@ import {
2
2
  RUNTIME_AGENT_REGISTER_EVENT,
3
3
  RUNTIME_AGENT_REGISTER_VERSION,
4
4
  registerAgentViaEvents
5
- } from "./chunk-LSRUTHT7.js";
5
+ } from "./chunk-43SCHIMD.js";
6
6
  import {
7
7
  registerRuntimeAgent
8
- } from "./chunk-3IF2PGW7.js";
9
- import "./chunk-ZBC44JJ3.js";
8
+ } from "./chunk-YKEWIB4M.js";
9
+ import "./chunk-CIZQXJ5Q.js";
10
10
  import "./chunk-5LFLVSTM.js";
11
11
  import "./chunk-YFCK7H2S.js";
12
12
  import "./chunk-Z7KVHQHL.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  resolvePiLaunchToolPlan
3
- } from "./chunk-G5DKFLLI.js";
3
+ } from "./chunk-4SDU2BZX.js";
4
4
  import "./chunk-CTPAE36Y.js";
5
- import "./chunk-ZBC44JJ3.js";
5
+ import "./chunk-CIZQXJ5Q.js";
6
6
  import "./chunk-5LFLVSTM.js";
7
7
  import "./chunk-YFCK7H2S.js";
8
8
  import "./chunk-Z7KVHQHL.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  registerRuntimeAgent
3
- } from "./chunk-3IF2PGW7.js";
3
+ } from "./chunk-YKEWIB4M.js";
4
4
 
5
5
  // src/agents/runtime-agent-events.ts
6
6
  var RUNTIME_AGENT_REGISTER_EVENT = "pi-subagents:runtime-agent-register:v1";
@@ -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
  };
@@ -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";
@@ -1,27 +1,27 @@
1
1
  import {
2
- currentCompletionOwnerId,
3
2
  isTrustedRecordedSessionFile,
4
3
  resolveSubagentRunId
5
- } from "./chunk-KSWQVGDW.js";
4
+ } from "./chunk-7PCKZHR5.js";
6
5
  import {
7
6
  agentStreamOptions,
8
7
  opencodeSessionHeaders,
9
8
  ownsRun,
10
9
  resolveAsyncResumeTarget
11
- } from "./chunk-NNLASTQE.js";
10
+ } from "./chunk-VH5OAWVG.js";
12
11
  import {
12
+ currentCompletionOwnerId,
13
13
  listMissions,
14
14
  missionRecordPath,
15
15
  readMissionBinding,
16
16
  reconcileAsyncRun,
17
17
  resolveAuthorityDecision,
18
18
  resolveMissionStoreLocation
19
- } from "./chunk-VJHOELQP.js";
19
+ } from "./chunk-NRQ75MGH.js";
20
20
  import {
21
21
  deliverInterruptRequest,
22
22
  queueRevivalBrief,
23
23
  requestAsyncSteer
24
- } from "./chunk-BJN5LUQY.js";
24
+ } from "./chunk-OYF6AFDY.js";
25
25
  import {
26
26
  formatShellCommand
27
27
  } from "./chunk-7J4ZXMY7.js";
@@ -32,7 +32,7 @@ import {
32
32
  tryLease,
33
33
  writeAtomicJson,
34
34
  writePrivateAtomicJson
35
- } from "./chunk-ZBC44JJ3.js";
35
+ } from "./chunk-CIZQXJ5Q.js";
36
36
  import {
37
37
  DIRS
38
38
  } from "./chunk-5LFLVSTM.js";
@@ -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-VH5OAWVG.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-NRQ75MGH.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
  };
@@ -6,33 +6,35 @@ import {
6
6
  resolveWorkflowForegroundSteeringTarget,
7
7
  steerAsyncRun,
8
8
  steerWorkflowForegroundTarget
9
- } from "./chunk-CEKS3TFO.js";
9
+ } from "./chunk-7IVSUMQQ.js";
10
10
  import {
11
11
  FLEET_STATUS_WIDGET_KEY,
12
12
  reportFleetWarning,
13
- runningTone
14
- } from "./chunk-2ZSNH26H.js";
13
+ runningTone,
14
+ stepTaskSummary
15
+ } from "./chunk-M2CTFYT3.js";
15
16
  import {
17
+ foregroundChildStateLabel,
16
18
  formatAsyncRunTranscript,
17
19
  stopAsyncRun
18
- } from "./chunk-KSWQVGDW.js";
20
+ } from "./chunk-7PCKZHR5.js";
19
21
  import {
20
22
  contextModeBadge,
21
23
  contextModeLabel,
22
24
  decodeUtf8Tail,
23
25
  listAsyncRuns
24
- } from "./chunk-NNLASTQE.js";
25
- import "./chunk-VJHOELQP.js";
26
- import "./chunk-BJN5LUQY.js";
26
+ } from "./chunk-VH5OAWVG.js";
27
+ import "./chunk-NRQ75MGH.js";
28
+ import "./chunk-OYF6AFDY.js";
27
29
  import "./chunk-NIXWDZZV.js";
28
- import "./chunk-ZP3HODFH.js";
30
+ import "./chunk-XSWOTIA3.js";
29
31
  import "./chunk-7F6RKT6W.js";
30
32
  import "./chunk-CTPAE36Y.js";
31
33
  import "./chunk-7J4ZXMY7.js";
32
34
  import {
33
35
  getArtifactPaths,
34
36
  getArtifactsDir
35
- } from "./chunk-3DC5KBVP.js";
37
+ } from "./chunk-5TYMSZX3.js";
36
38
  import {
37
39
  snapshotExternalRuns
38
40
  } from "./chunk-UKG4V45O.js";
@@ -44,7 +46,7 @@ import {
44
46
  formatTokens,
45
47
  readStatus,
46
48
  shortenPath
47
- } from "./chunk-ZBC44JJ3.js";
49
+ } from "./chunk-CIZQXJ5Q.js";
48
50
  import {
49
51
  DIRS
50
52
  } from "./chunk-5LFLVSTM.js";
@@ -203,18 +205,22 @@ function asyncItems(run, description) {
203
205
  if (run.steps.length === 0 || run.mode === "workflow") {
204
206
  return [{ key: `async:${run.id}`, kind: "async", runId: run.id, agent: run.mode, state: run.state, updatedAt, run, ...description ? { description } : {} }];
205
207
  }
206
- return run.steps.map((step) => ({
207
- key: `async:${run.id}:${step.index}`,
208
- kind: "async",
209
- runId: run.id,
210
- index: step.index,
211
- agent: step.label ? `${step.label} (${step.agent})` : step.agent,
212
- state: step.status,
213
- updatedAt: step.lastActivityAt ?? updatedAt,
214
- run,
215
- step,
216
- ...description ? { description } : {}
217
- }));
208
+ return run.steps.map((step) => {
209
+ const task = stepTaskSummary(step, step.agent);
210
+ return {
211
+ key: `async:${run.id}:${step.index}`,
212
+ kind: "async",
213
+ runId: run.id,
214
+ index: step.index,
215
+ agent: step.label ? `${step.label} (${step.agent})` : step.agent,
216
+ state: step.status,
217
+ updatedAt: step.lastActivityAt ?? updatedAt,
218
+ run,
219
+ step,
220
+ ...task ? { task } : {},
221
+ ...description ? { description } : {}
222
+ };
223
+ });
218
224
  }
219
225
  function orderFleetAsyncRuns(runs, terminalLimit, trackedJobs) {
220
226
  const updatedAt = (run) => run.lastUpdate ?? run.endedAt ?? run.startedAt;
@@ -503,7 +509,7 @@ function foregroundRecentDetail(item, state) {
503
509
  const lines = [
504
510
  `Run: ${item.runId}`,
505
511
  "Source: foreground",
506
- `State: ${child.status}`,
512
+ `State: ${foregroundChildStateLabel(run, child)}`,
507
513
  `Mode: ${run.mode}`,
508
514
  `Child: ${child.index} (${child.agent})${contextModeLabel(child.context) ? ` ${contextModeLabel(child.context)}` : ""}`,
509
515
  modelThinking ? `Model: ${modelThinking}` : void 0,
@@ -1179,8 +1185,10 @@ var SubagentFleetComponent = class {
1179
1185
  const item = this.snapshot.items[index];
1180
1186
  const marker = index === this.selected ? this.theme.fg("accent", "\u203A") : " ";
1181
1187
  const context = item.kind === "async" ? contextModeBadge(this.theme, item.step?.context ?? item.run.context) : item.kind === "foreground-recent" ? contextModeBadge(this.theme, item.child.context) : "";
1182
- const agent = index === this.selected ? this.theme.bold(item.agent) : item.agent;
1183
- const identity = item.runId.slice(0, 8);
1188
+ const task = item.kind === "async" ? item.task : void 0;
1189
+ const name = task && item.kind === "async" && item.step ? item.step.agent : item.agent;
1190
+ const agent = index === this.selected ? this.theme.bold(name) : name;
1191
+ const identity = task ?? item.runId.slice(0, 8);
1184
1192
  const left = `${marker} ${statusGlyph(item, this.theme)} ${agent}${context} ${this.theme.fg("dim", `\xB7 ${identity}`)}`;
1185
1193
  return rightAligned(left, this.theme.fg("dim", item.state), layout.roster.width);
1186
1194
  });