@selesai/code 0.13.0 → 0.13.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 (461) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/core/agent-session.d.ts +1 -1
  3. package/dist/core/agent-session.js +1 -1
  4. package/dist/core/sdk.d.ts +2 -2
  5. package/dist/core/sdk.js +1 -1
  6. package/dist/core/system-prompt.d.ts +1 -1
  7. package/dist/core/system-prompt.js +1 -1
  8. package/dist/extensions/ascii.txt +9 -0
  9. package/dist/extensions/package.json +1 -2
  10. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
  11. package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
  12. package/dist/extensions/pi-hermes-memory/README.md +643 -0
  13. package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
  14. package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
  15. package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
  16. package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
  17. package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
  18. package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
  19. package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
  20. package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
  21. package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
  22. package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
  23. package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
  24. package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
  25. package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
  26. package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
  27. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
  28. package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
  29. package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
  30. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
  31. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
  32. package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
  33. package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
  34. package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
  35. package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
  36. package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
  37. package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
  38. package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
  39. package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
  40. package/dist/extensions/pi-hermes-memory/package.json +66 -0
  41. package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
  42. package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
  43. package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
  44. package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
  45. package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
  46. package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
  47. package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
  48. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
  49. package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
  50. package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
  51. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
  52. package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
  53. package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
  54. package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
  55. package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
  56. package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
  57. package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
  58. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
  59. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
  60. package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
  61. package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
  62. package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
  63. package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
  64. package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
  65. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
  66. package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
  67. package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
  68. package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
  69. package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
  70. package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
  71. package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
  72. package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
  73. package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
  74. package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
  75. package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
  76. package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
  77. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
  78. package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
  79. package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
  80. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
  82. package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
  83. package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
  84. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
  85. package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
  86. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
  87. package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
  88. package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
  89. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
  90. package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
  91. package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
  92. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
  93. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
  94. package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
  95. package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
  96. package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
  97. package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
  98. package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
  99. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
  100. package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
  101. package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
  102. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
  103. package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
  104. package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
  105. package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
  106. package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
  107. package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
  108. package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
  109. package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
  110. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
  111. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
  112. package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
  113. package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
  114. package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
  115. package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
  116. package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
  117. package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
  118. package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
  119. package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
  120. package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
  121. package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
  122. package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
  123. package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
  124. package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
  125. package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
  126. package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
  127. package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
  128. package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
  129. package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
  130. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
  131. package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
  132. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
  133. package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
  134. package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
  135. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
  136. package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
  137. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
  138. package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
  139. package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
  140. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
  141. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
  142. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
  143. package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
  144. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
  145. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
  146. package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
  147. package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
  148. package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
  149. package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
  150. package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
  151. package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
  152. package/dist/extensions/pi-subagents/AGENTS.md +5 -0
  153. package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
  154. package/dist/extensions/pi-subagents/README.md +12 -6
  155. package/dist/extensions/pi-subagents/VISION.md +81 -0
  156. package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
  157. package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
  158. package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
  159. package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
  160. package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
  161. package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
  162. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
  163. package/dist/extensions/pi-subagents/docs/agents.md +488 -0
  164. package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
  165. package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
  166. package/dist/extensions/pi-subagents/docs/models.md +27 -4
  167. package/dist/extensions/pi-subagents/docs/observability.md +10 -9
  168. package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
  169. package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
  170. package/dist/extensions/pi-subagents/package-lock.json +389 -2
  171. package/dist/extensions/pi-subagents/package.json +3 -1
  172. package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
  173. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
  174. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
  175. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
  176. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  177. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
  178. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
  179. package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
  180. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
  181. package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
  182. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
  183. package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
  184. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
  185. package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
  186. package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
  187. package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
  188. package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
  189. package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
  190. package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
  191. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
  192. package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
  193. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
  194. package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
  195. package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
  196. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
  197. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
  198. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
  199. package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
  200. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -3
  201. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
  202. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
  203. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
  204. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
  205. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
  206. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
  207. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
  208. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
  209. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
  210. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
  211. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
  212. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
  213. package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
  214. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
  215. package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
  216. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
  217. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
  218. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
  219. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
  220. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
  221. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
  222. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
  223. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
  224. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
  225. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
  226. package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
  227. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
  228. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
  229. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
  230. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
  231. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
  232. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
  233. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
  234. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
  235. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
  236. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
  237. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
  238. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
  239. package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
  240. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
  241. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
  242. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
  243. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
  244. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
  245. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
  246. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
  247. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
  248. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
  249. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
  250. package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
  251. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
  252. package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
  253. package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
  254. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
  255. package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
  256. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
  257. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
  258. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
  259. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
  260. package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
  261. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
  262. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
  263. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
  264. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
  265. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
  266. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
  267. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
  268. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
  269. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
  270. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
  271. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
  272. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
  273. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
  274. package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
  275. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
  276. package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
  277. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
  278. package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
  279. package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
  280. package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
  281. package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
  282. package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
  283. package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
  284. package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
  285. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
  286. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
  287. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
  288. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
  289. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
  290. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
  291. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
  292. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
  293. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
  294. package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
  295. package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
  296. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
  297. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
  298. package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
  299. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
  300. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
  301. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
  302. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
  303. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
  304. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  305. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
  306. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
  307. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
  308. package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
  309. package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
  310. package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
  311. package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
  312. package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
  313. package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
  314. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
  315. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  316. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
  317. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
  318. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
  319. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
  320. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
  321. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
  322. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
  323. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
  324. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
  325. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
  326. package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
  327. package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
  328. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
  329. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
  330. package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
  331. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
  332. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
  333. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
  334. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
  335. package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
  336. package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
  337. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
  338. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
  339. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
  340. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
  341. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
  342. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
  343. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
  344. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
  345. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
  346. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
  347. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
  348. package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
  349. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
  350. package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
  351. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
  352. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
  353. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
  354. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
  355. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
  356. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
  357. package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
  358. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
  359. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
  360. package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
  361. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
  362. package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
  363. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
  364. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
  365. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
  366. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
  367. package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
  368. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
  369. package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
  370. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
  371. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +132 -0
  372. package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
  373. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
  374. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
  375. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
  376. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
  377. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
  378. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  379. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
  380. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
  381. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
  382. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
  383. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
  384. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
  385. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
  386. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
  387. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
  388. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
  389. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
  390. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
  391. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
  392. package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
  393. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
  394. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
  395. package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
  396. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
  397. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
  398. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
  399. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
  400. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
  401. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
  402. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
  403. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
  404. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
  405. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
  406. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  407. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
  408. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
  409. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
  410. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
  411. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
  412. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
  413. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
  414. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
  415. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
  416. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
  417. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
  418. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
  419. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
  420. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
  421. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
  422. package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
  423. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
  424. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
  425. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
  426. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
  427. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
  428. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
  429. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
  430. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
  431. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
  432. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
  433. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
  434. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
  435. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
  436. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
  437. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
  438. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
  439. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
  440. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
  441. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
  442. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
  443. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
  444. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
  445. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
  446. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
  447. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
  448. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
  449. package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
  450. package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
  451. package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
  452. package/dist/modes/interactive/interactive-mode.js +7 -7
  453. package/dist/skills/workflow/SKILL.md +65 -50
  454. package/package.json +112 -110
  455. package/dist/extensions/enable-readonly-tools.test.ts +0 -63
  456. package/dist/extensions/enable-readonly-tools.ts +0 -24
  457. package/dist/extensions/model-prompt-injector/config.json +0 -16
  458. package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
  459. package/dist/extensions/model-prompt-injector/index.ts +0 -148
  460. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
  461. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
@@ -18,6 +18,7 @@ import { readStatus } from "../../shared/utils.ts";
18
18
  import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
19
19
  import { contextModeLabel, summarizeContextModes } from "../shared/context-mode.ts";
20
20
  import { formatAsyncRunOutputPath, formatAsyncRunProgressLabel, listAsyncRuns, type AsyncRunSummary } from "./async-status.ts";
21
+ import { isTrustedRecordedSessionFile } from "../../shared/session-file-trust.ts";
21
22
 
22
23
  const DEFAULT_TRANSCRIPT_LINES = 80;
23
24
  const MAX_TRANSCRIPT_LINES = 500;
@@ -43,6 +44,8 @@ interface TranscriptOptions {
43
44
  index?: number;
44
45
  lines?: number;
45
46
  sessionRoots?: string[];
47
+ trustedSessionFiles?: string[];
48
+ trustedSessionFileRoot?: string;
46
49
  }
47
50
 
48
51
  interface TextTailResult {
@@ -69,6 +72,14 @@ function uniqueStrings(values: Array<string | undefined>): string[] {
69
72
  return result;
70
73
  }
71
74
 
75
+ function fleetChildDisplayName(child: { agent?: string; sessionName?: string }, fallback = "subagent"): string {
76
+ return child.sessionName?.trim() || child.agent || fallback;
77
+ }
78
+
79
+ function fleetStepDisplayName(step: Pick<AsyncJobStep, "agent" | "sessionName" | "label">): string {
80
+ return step.sessionName?.trim() || (step.label ? `${step.label} (${step.agent})` : step.agent);
81
+ }
82
+
72
83
  function resolveMaybeRelative(asyncDir: string, filePath: string | undefined): string | undefined {
73
84
  if (!filePath) return undefined;
74
85
  return path.resolve(asyncDir, filePath);
@@ -120,10 +131,13 @@ function readTextTail(filePath: string, maxLines: number): TextTailResult {
120
131
  }
121
132
  }
122
133
 
123
- function readContainedTextTail(filePath: string, maxLines: number, trustedRoots: string[], label: string): TextTailResult {
124
- if (trustedRoots.length === 0) return { path: filePath, lines: [], truncated: false, error: `Refusing to read ${label} transcript path without a trusted root: ${filePath}` };
134
+ function readContainedTextTail(filePath: string, maxLines: number, trustedRoots: string[], label: string, trustedFiles: string[] = [], trustedFileRoot?: string): TextTailResult {
135
+ if (trustedRoots.length === 0 && (!trustedFileRoot || trustedFiles.length === 0)) return { path: filePath, lines: [], truncated: false, error: `Refusing to read ${label} transcript path without a trusted root: ${filePath}` };
125
136
  const resolvedPath = path.resolve(filePath);
126
- if (!trustedRoots.some((root) => pathWithin(root, resolvedPath))) {
137
+ const recordedCandidate = trustedFileRoot
138
+ && pathWithin(trustedFileRoot, resolvedPath)
139
+ && trustedFiles.some((file) => path.resolve(file) === resolvedPath);
140
+ if (!trustedRoots.some((root) => pathWithin(root, resolvedPath)) && !recordedCandidate) {
127
141
  return { path: filePath, lines: [], truncated: false, error: `Refusing to read ${label} transcript path outside trusted roots: ${filePath}` };
128
142
  }
129
143
  let lstat: fs.Stats;
@@ -143,7 +157,7 @@ function readContainedTextTail(filePath: string, maxLines: number, trustedRoots:
143
157
  } catch (error) {
144
158
  return { path: filePath, lines: [], truncated: false, error: getErrorMessage(error) };
145
159
  }
146
- if (!realRoots.some((root) => pathWithin(root, realPath))) {
160
+ if (!realRoots.some((root) => pathWithin(root, realPath)) && !isTrustedRecordedSessionFile(realPath, trustedFiles, trustedFileRoot)) {
147
161
  return { path: filePath, lines: [], truncated: false, error: `Refusing to read ${label} transcript path outside trusted roots: ${filePath}` };
148
162
  }
149
163
  return readTextTail(resolvedPath, maxLines);
@@ -213,8 +227,8 @@ function sessionMessageLine(record: unknown): string | undefined {
213
227
 
214
228
  /** Structured session tail: parsed content parts, newest last, bounded by
215
229
  * maxMessages. Same trusted-root containment as the prose transcript tail. */
216
- export function readSessionMessagesTail(sessionFile: string, maxMessages: number, trustedRoots: string[]): { messages: SessionTranscriptMessage[]; warnings: string[]; truncated: boolean } {
217
- const tail = readContainedTextTail(sessionFile, Math.max(maxMessages * 4, maxMessages), trustedRoots, "session");
230
+ export function readSessionMessagesTail(sessionFile: string, maxMessages: number, trustedRoots: string[], trustedFiles: string[] = [], trustedFileRoot?: string): { messages: SessionTranscriptMessage[]; warnings: string[]; truncated: boolean } {
231
+ const tail = readContainedTextTail(sessionFile, Math.max(maxMessages * 4, maxMessages), trustedRoots, "session", trustedFiles, trustedFileRoot);
218
232
  const warnings: string[] = [];
219
233
  if (tail.error) warnings.push(`Session read failed for ${sessionFile}: ${tail.error}`);
220
234
  const parsedMessages: SessionTranscriptMessage[] = [];
@@ -235,8 +249,8 @@ export function readSessionMessagesTail(sessionFile: string, maxMessages: number
235
249
  return { messages, warnings, truncated: tail.truncated || parsedMessages.length > messages.length };
236
250
  }
237
251
 
238
- function readSessionTranscriptTail(sessionFile: string, maxLines: number, trustedRoots: string[]): { lines: string[]; warnings: string[] } {
239
- const tail = readContainedTextTail(sessionFile, Math.max(maxLines * 4, maxLines), trustedRoots, "session");
252
+ function readSessionTranscriptTail(sessionFile: string, maxLines: number, trustedRoots: string[], trustedFiles: string[] = [], trustedFileRoot?: string): { lines: string[]; warnings: string[] } {
253
+ const tail = readContainedTextTail(sessionFile, Math.max(maxLines * 4, maxLines), trustedRoots, "session", trustedFiles, trustedFileRoot);
240
254
  const warnings: string[] = [];
241
255
  if (tail.error) warnings.push(`Session read failed for ${sessionFile}: ${tail.error}`);
242
256
  const lines: string[] = [];
@@ -277,7 +291,8 @@ function formatActivityFacts(input: {
277
291
  }
278
292
 
279
293
  function foregroundModeName(control: ForegroundControl): string {
280
- if (control.mode === "single" && control.currentAgent) return control.currentAgent;
294
+ const currentDisplayName = control.sessionName?.trim() || control.currentAgent;
295
+ if (control.mode === "single" && currentDisplayName) return currentDisplayName;
281
296
  return control.mode;
282
297
  }
283
298
 
@@ -296,7 +311,8 @@ function formatForegroundFleetLines(controls: ForegroundControl[]): string[] {
296
311
  toolCount: control.toolCount,
297
312
  ...(control.tokens !== undefined ? { tokens: { total: control.tokens } } : {}),
298
313
  });
299
- const current = control.currentAgent ? ` | ${control.currentAgent}${control.currentIndex !== undefined ? ` #${control.currentIndex}` : ""}` : "";
314
+ const currentDisplayName = control.sessionName?.trim() || control.currentAgent;
315
+ const current = currentDisplayName ? ` | ${currentDisplayName}${control.currentIndex !== undefined ? ` #${control.currentIndex}` : ""}` : "";
300
316
  lines.push(`- ${control.runId} | running | ${foregroundModeName(control)}${current}${activity ? ` | ${activity}` : ""}`);
301
317
  lines.push(` status: subagent({ action: "status", id: "${control.runId}" })`);
302
318
  lines.push(" transcript: live in the expanded foreground result; persisted session transcript appears after completion when sessions are enabled.");
@@ -311,7 +327,7 @@ function formatDetachedForegroundFleetLines(runs: ForegroundRun[]): string[] {
311
327
  const ordered = [...runs].sort((left, right) => right.updatedAt - left.updatedAt);
312
328
  for (const run of ordered) {
313
329
  const detachedChildren = run.children.filter((child) => child.status === "detached");
314
- const childSummary = detachedChildren.map((child) => `${child.agent} #${child.index}`).join(", ");
330
+ const childSummary = detachedChildren.map((child) => `${fleetChildDisplayName(child)} #${child.index}`).join(", ");
315
331
  lines.push(`- ${run.runId} | detached | ${run.mode}${childSummary ? ` | ${childSummary}` : ""}`);
316
332
  lines.push(` status: subagent({ action: "status", id: "${run.runId}" })`);
317
333
  lines.push(` recovery: reply to the supervisor request first, then wait with subagent_wait({ id: "${run.runId}" }); do not resume or launch a replacement while any child remains detached.`);
@@ -332,7 +348,7 @@ function formatAsyncFleetLines(runs: AsyncRunSummary[]): string[] {
332
348
  lines.push(` status: subagent({ action: "status", id: "${run.id}" })`);
333
349
  lines.push(` transcript: subagent({ action: "status", id: "${run.id}", view: "transcript" })`);
334
350
  for (const step of run.steps) {
335
- const display = step.label ? `${step.label} (${step.agent})` : step.agent;
351
+ const display = fleetStepDisplayName(step);
336
352
  const stepContext = contextModeLabel(step.context);
337
353
  const phase = step.phase ? `[${step.phase}] ` : "";
338
354
  const stepActivity = formatActivityFacts(step);
@@ -429,7 +445,7 @@ function selectTranscriptStep(status: AsyncStatus, options: TranscriptOptions):
429
445
  }
430
446
  const step = selectedIndex !== undefined ? steps[selectedIndex] : undefined;
431
447
  const hint = options.index === undefined && steps.length > 1
432
- ? `Tip: pass index to inspect a specific child transcript (${steps.map((candidate, index) => `${index}=${candidate.agent}`).join(", ")}).`
448
+ ? `Tip: pass index to inspect a specific child transcript (${steps.map((candidate, index) => `${index}=${fleetChildDisplayName(candidate)}`).join(", ")}).`
433
449
  : undefined;
434
450
  return { index: selectedIndex, step, hint };
435
451
  }
@@ -439,7 +455,7 @@ function stepStateLine(mode: SubagentRunMode, index: number | undefined, step: A
439
455
  const modelThinking = formatModelThinking(step.model, step.thinking);
440
456
  const context = contextModeLabel(step.context);
441
457
  const parts = [
442
- `${mode === "parallel" ? "Agent" : "Step"}: ${index} (${step.agent})${context ? ` ${context}` : ""}`,
458
+ `${mode === "parallel" ? "Agent" : "Step"}: ${index} (${fleetChildDisplayName(step)})${context ? ` ${context}` : ""}`,
443
459
  step.status,
444
460
  formatActivityFacts(step),
445
461
  modelThinking,
@@ -523,7 +539,7 @@ export function formatAsyncRunTranscript(status: AsyncStatus, asyncDir: string,
523
539
  transcriptSource = "Recent output from status.json";
524
540
  }
525
541
  if (transcriptLines.length === 0 && sessionFile) {
526
- const sessionTail = readSessionTranscriptTail(sessionFile, lineLimit, options.sessionRoots ?? []);
542
+ const sessionTail = readSessionTranscriptTail(sessionFile, lineLimit, options.sessionRoots ?? [], options.trustedSessionFiles, options.trustedSessionFileRoot);
527
543
  transcriptLines = sessionTail.lines;
528
544
  warnings.push(...sessionTail.warnings);
529
545
  if (transcriptLines.length > 0) transcriptSource = `Session transcript tail from ${sessionFile}`;
@@ -547,14 +563,14 @@ export function formatNestedRunTranscript(run: NestedRunSummary, options: Transc
547
563
  `Nested run: ${run.id}`,
548
564
  `State: ${run.state}`,
549
565
  run.mode ? `Mode: ${run.mode}` : undefined,
550
- run.agent ? `Agent: ${run.agent}` : run.agents?.length ? `Agents: ${run.agents.join(", ")}` : undefined,
566
+ run.sessionName?.trim() ? `Agent: ${run.sessionName.trim()}` : run.agent ? `Agent: ${run.agent}` : run.agents?.length ? `Agents: ${run.agents.join(", ")}` : undefined,
551
567
  ].filter((line): line is string => Boolean(line));
552
568
  appendKnownArtifacts(lines, { outputPaths: [], sessionFile: run.sessionFile });
553
569
  if (!run.sessionFile) {
554
570
  appendTranscriptBody(lines, "Transcript tail", [], false);
555
571
  return safeTranscriptLines(lines);
556
572
  }
557
- const sessionTail = readSessionTranscriptTail(run.sessionFile, lineLimit, options.sessionRoots ?? []);
573
+ const sessionTail = readSessionTranscriptTail(run.sessionFile, lineLimit, options.sessionRoots ?? [], options.trustedSessionFiles, options.trustedSessionFileRoot);
558
574
  if (sessionTail.warnings.length) {
559
575
  lines.push("Warnings:");
560
576
  for (const warning of sessionTail.warnings) lines.push(` ${warning}`);
@@ -573,14 +589,14 @@ export function formatAsyncResultTranscript(data: {
573
589
  sessionFile?: string;
574
590
  agent?: string;
575
591
  exitCode?: number | null;
576
- results?: Array<{ agent?: string; output?: string; summary?: string; sessionFile?: string; state?: string; success?: boolean; exitCode?: number | null }>;
592
+ results?: Array<{ agent?: string; sessionName?: string; output?: string; summary?: string; sessionFile?: string; state?: string; success?: boolean; exitCode?: number | null }>;
577
593
  }, resultPath: string, options: TranscriptOptions = {}): string {
578
594
  const lineLimit = transcriptLineLimit(options.lines);
579
595
  const runId = data.runId ?? data.id ?? path.basename(resultPath, ".json");
580
596
  const children = Array.isArray(data.results)
581
597
  ? data.results
582
598
  : data.agent
583
- ? [{ agent: data.agent, output: data.output, summary: data.summary, sessionFile: data.sessionFile, state: data.state, success: data.success, exitCode: data.exitCode }]
599
+ ? [{ agent: data.agent, sessionName: undefined, output: data.output, summary: data.summary, sessionFile: data.sessionFile, state: data.state, success: data.success, exitCode: data.exitCode }]
584
600
  : [];
585
601
  let index = options.index;
586
602
  if (index !== undefined && !Number.isInteger(index)) throw new Error("Transcript index must be an integer.");
@@ -595,8 +611,8 @@ export function formatAsyncResultTranscript(data: {
595
611
  const lines = [
596
612
  `Run: ${runId}`,
597
613
  `State: ${data.state ?? (data.success ? "complete" : "failed")}`,
598
- index !== undefined && child ? `Child: ${index} (${child.agent ?? "subagent"})` : undefined,
599
- index === undefined && children.length > 1 ? `Tip: pass index to inspect a specific child transcript (${children.map((candidate, childIndex) => `${childIndex}=${candidate.agent ?? "subagent"}`).join(", ")}).` : undefined,
614
+ index !== undefined && child ? `Child: ${index} (${fleetChildDisplayName(child)})` : undefined,
615
+ index === undefined && children.length > 1 ? `Tip: pass index to inspect a specific child transcript (${children.map((candidate, childIndex) => `${childIndex}=${fleetChildDisplayName(candidate)}`).join(", ")}).` : undefined,
600
616
  ].filter((line): line is string => Boolean(line));
601
617
  appendKnownArtifacts(lines, { outputPaths: [], sessionFile, resultPath });
602
618
  appendTranscriptBody(lines, "Result transcript tail", transcriptLines.filter((line) => line.trim()), output.split(/\r?\n/).length > lineLimit);
@@ -218,19 +218,19 @@ function readOutputArtifact(outputPath: string): { output?: string; errorText?:
218
218
  }
219
219
  }
220
220
 
221
- function readSessionBackedOutput(sessionPath: string, trustedRoots: string[]): { output?: string } {
222
- if (trustedRoots.length === 0) return {};
221
+ function readSessionBackedOutput(sessionPath: string, trustedRoots: string[], trustedSessionFileRoot?: string): { output?: string } {
222
+ if (trustedRoots.length === 0 && !trustedSessionFileRoot) return {};
223
223
  // The archive retains the child's session file as its output record. The
224
224
  // final answer is the terminal assistant message's text — all text parts
225
225
  // of that one session record, not just the last part.
226
- const tail = readSessionMessagesTail(sessionPath, MAX_MESSAGE_LINES, trustedRoots);
226
+ const tail = readSessionMessagesTail(sessionPath, MAX_MESSAGE_LINES, trustedRoots, [sessionPath], trustedSessionFileRoot);
227
227
  const last = tail.messages.findLast((message) => message.role === "assistant" && message.kind === "text");
228
228
  if (!last) return {};
229
229
  const parts = tail.messages.filter((message) => message.recordIndex === last.recordIndex && message.kind === "text");
230
230
  return { output: parts.map((part) => part.text).join("\n") };
231
231
  }
232
232
 
233
- function readResultOutput(resultsDir: string, sessionId: string, runId: string, stepIndex: number | undefined, trustedRoots: string[], stepAgent: string | undefined, now: () => number): { output?: string; errorText?: string } {
233
+ function readResultOutput(resultsDir: string, sessionId: string, runId: string, stepIndex: number | undefined, trustedRoots: string[], stepAgent: string | undefined, now: () => number, trustedSessionFileRoot?: string): { output?: string; errorText?: string } {
234
234
  const resultPath = resultPayloadPathForSessionRun(resultsDir, sessionId, runId);
235
235
  if (resultPath) return resultOutput(JSON.parse(fs.readFileSync(resultPath, "utf-8")) as unknown, stepIndex);
236
236
  // Read the raw record first: readCompletionReplay best-effort deletes
@@ -284,7 +284,7 @@ function readResultOutput(resultsDir: string, sessionId: string, runId: string,
284
284
  if (artifactPath) return readOutputArtifact(artifactPath);
285
285
  const sessionEntry = archive?.entries.find((entry) => entry.source === "session" && matches(entry));
286
286
  const sessionPath = sessionEntry?.source === "session" ? sessionEntry.path : undefined;
287
- if (sessionPath) return readSessionBackedOutput(sessionPath, trustedRoots);
287
+ if (sessionPath) return readSessionBackedOutput(sessionPath, trustedRoots, trustedSessionFileRoot);
288
288
  const output = archive?.entries.find((entry) => entry.source === "result-tail" && matches(entry))?.text;
289
289
  return output ? { output } : {};
290
290
  }
@@ -377,8 +377,8 @@ export function buildInspectReply(request: InspectRequest, deps: InspectDeps = {
377
377
 
378
378
  let messages: InspectReplyMessage[] | undefined;
379
379
  let task: string | undefined;
380
- if (sessionFile && trustedRoots.length > 0) {
381
- const tail = readSessionMessagesTail(sessionFile, lineLimit, trustedRoots);
380
+ if (sessionFile && (trustedRoots.length > 0 || deps.state?.trustedSessionFileRoot)) {
381
+ const tail = readSessionMessagesTail(sessionFile, lineLimit, trustedRoots, [sessionFile], deps.state?.trustedSessionFileRoot);
382
382
  if (tail.messages.length > 0) {
383
383
  messages = tail.messages.map(toReplyMessage);
384
384
  // The delegated task is the child session's first user message, but
@@ -393,7 +393,7 @@ export function buildInspectReply(request: InspectRequest, deps: InspectDeps = {
393
393
  }
394
394
  }
395
395
 
396
- const result = readResultOutput(resultsDir, currentSessionId, node.status.runId, node.stepIndex, trustedRoots, step?.agent, deps.now ?? Date.now);
396
+ const result = readResultOutput(resultsDir, currentSessionId, node.status.runId, node.stepIndex, trustedRoots, step?.agent, deps.now ?? Date.now, deps.state?.trustedSessionFileRoot);
397
397
  const failedText = step?.error ?? node.status.error;
398
398
  if (result.output === undefined && result.errorText === undefined && failedText !== undefined) {
399
399
  return errorReply(request, "internal", "Inspection could not read the async run artifacts.");
@@ -15,7 +15,21 @@ import {
15
15
  resolveCompletionBatchConfig,
16
16
  } from "./completion-batcher.ts";
17
17
  import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, type ParallelHandoffReference, type ScheduleOrigin, type SubagentState } from "../../shared/types.ts";
18
+ import { safeTerminalText } from "../../shared/display-text.ts";
19
+ import { resolveSubagentResultStatus } from "../../intercom/result-intercom.ts";
18
20
  import { isUnexplainedProcessSignal } from "../shared/process-signal.ts";
21
+ import type { ResultDeliveryOwnership } from "./result-delivery-ownership.ts";
22
+
23
+ export interface SubagentNotifyChildOutput {
24
+ workflowKey?: string;
25
+ runId?: string;
26
+ agent?: string;
27
+ status: string;
28
+ savedOutputPath?: string;
29
+ preview: string;
30
+ previewTruncated?: boolean;
31
+ previewUnavailableReason?: string;
32
+ }
19
33
 
20
34
  export interface SubagentNotifyDetails {
21
35
  agent: string;
@@ -26,6 +40,7 @@ export interface SubagentNotifyDetails {
26
40
  durationMs?: number;
27
41
  workflowRunId?: string;
28
42
  childRuns?: Array<{ runId: string; workflowKey?: string; agent?: string; status?: string }>;
43
+ childOutputs?: SubagentNotifyChildOutput[];
29
44
  reconciledFromDetachedChild?: string;
30
45
  sessionLabel?: string;
31
46
  sessionValue?: string;
@@ -56,7 +71,14 @@ export interface CompletionNotification {
56
71
  workflowKey?: string;
57
72
  agent?: string;
58
73
  status?: string;
74
+ state?: string;
59
75
  success?: boolean;
76
+ output?: string;
77
+ structuredOutput?: unknown;
78
+ outputState?: "present" | "absent" | "unknown";
79
+ outputReference?: string | { path?: string };
80
+ artifactPaths?: { outputPath?: string };
81
+ detached?: boolean;
60
82
  exitCode?: number | null;
61
83
  processSignal?: string | null;
62
84
  interrupted?: boolean;
@@ -91,6 +113,7 @@ export interface RegisterSubagentNotifyOptions {
91
113
  batchConfig?: CompletionBatchConfig;
92
114
  timers?: NotifyTimerApi;
93
115
  now?: () => number;
116
+ ownership?: Pick<ResultDeliveryOwnership, "owns">;
94
117
  }
95
118
 
96
119
  export interface CompletionNotifier {
@@ -98,6 +121,115 @@ export interface CompletionNotifier {
98
121
  dispose(): void;
99
122
  }
100
123
 
124
+ const CHILD_OUTPUT_PREVIEW_MAX_BYTES = 4 * 1024;
125
+ const CHILD_OUTPUT_PREVIEW_COUNT = 8;
126
+ const PREVIEW_TRUNCATION_MARKER = "...[preview truncated]";
127
+ type CompletionChild = NonNullable<CompletionNotification["results"]>[number];
128
+
129
+ function truncateUtf8Head(value: string, maxBytes: number): { text: string; truncated: boolean } {
130
+ const bytes = Buffer.from(value, "utf8");
131
+ if (bytes.length <= maxBytes) return { text: value, truncated: false };
132
+ const markerBytes = Buffer.byteLength(PREVIEW_TRUNCATION_MARKER, "utf8");
133
+ const contentBytes = Math.max(0, maxBytes - markerBytes);
134
+ let end = contentBytes;
135
+ while (end > 0 && (bytes[end]! & 0xc0) === 0x80) end -= 1;
136
+ return {
137
+ text: `${bytes.subarray(0, end).toString("utf8")}${PREVIEW_TRUNCATION_MARKER}`,
138
+ truncated: true,
139
+ };
140
+ }
141
+
142
+ function boundedSafeText(value: string, maxBytes = 1_024): string {
143
+ return truncateUtf8Head(safeTerminalText(value).replace(/\n/g, "\\n"), maxBytes).text;
144
+ }
145
+
146
+ function outputPathFromReference(value: CompletionChild["outputReference"]): string | undefined {
147
+ if (typeof value === "string" && value.trim()) return value.trim();
148
+ if (value && typeof value === "object" && typeof value.path === "string" && value.path.trim()) return value.path.trim();
149
+ return undefined;
150
+ }
151
+
152
+ function childSavedOutputPath(child: CompletionChild): string | undefined {
153
+ return outputPathFromReference(child.outputReference);
154
+ }
155
+
156
+ function childStatus(child: CompletionChild, workflowState?: string): string {
157
+ const knownStatus = child.status === "complete"
158
+ ? "completed"
159
+ : child.status === "completed" || child.status === "failed" || child.status === "paused" || child.status === "stopped" || child.status === "detached"
160
+ ? child.status
161
+ : undefined;
162
+ if (knownStatus) return knownStatus;
163
+ return resolveSubagentResultStatus({
164
+ success: child.success,
165
+ state: child.state ?? (child.success === undefined ? workflowState : undefined),
166
+ interrupted: child.interrupted,
167
+ detached: child.detached,
168
+ processSignal: child.processSignal,
169
+ timedOut: child.timedOut,
170
+ stopped: child.stopped,
171
+ turnBudgetExceeded: child.turnBudgetExceeded,
172
+ exitCode: typeof child.exitCode === "number" ? child.exitCode : undefined,
173
+ });
174
+ }
175
+
176
+ function structuredOutputText(value: unknown): string | undefined {
177
+ if (value === undefined) return undefined;
178
+ try {
179
+ const serialized = JSON.stringify(value, null, 2);
180
+ return typeof serialized === "string" ? serialized : undefined;
181
+ } catch {
182
+ return undefined;
183
+ }
184
+ }
185
+
186
+ function childInlinePreview(child: CompletionChild): { preview?: string; truncated?: boolean; unavailableReason?: string } {
187
+ const output = typeof child.output === "string" ? child.output : "";
188
+ const outputReference = childSavedOutputPath(child);
189
+ const referenceOnly = Boolean(outputReference && output.trim().startsWith("Output saved to:"));
190
+ const raw = child.outputState === "absent" || referenceOnly ? structuredOutputText(child.structuredOutput) : output || structuredOutputText(child.structuredOutput);
191
+ if (!raw?.trim()) {
192
+ return { unavailableReason: referenceOnly ? "saved output is file-only" : "no safe inline output" };
193
+ }
194
+ const safe = safeTerminalText(raw);
195
+ if (!safe.trim()) return { unavailableReason: "no safe inline output" };
196
+ const bounded = truncateUtf8Head(safe, CHILD_OUTPUT_PREVIEW_MAX_BYTES);
197
+ return { preview: bounded.text, ...(bounded.truncated ? { truncated: true } : {}) };
198
+ }
199
+
200
+ function formatChildOutputBlock(children: SubagentNotifyChildOutput[] | undefined): string | undefined {
201
+ if (!children?.length) return undefined;
202
+ const lines = ["Child outputs:"];
203
+ for (let index = 0; index < children.length; index++) {
204
+ const child = children[index]!;
205
+ const key = child.workflowKey ? boundedSafeText(child.workflowKey) : "unavailable";
206
+ const runId = child.runId ? boundedSafeText(child.runId) : "unavailable";
207
+ const status = boundedSafeText(child.status) || "unavailable";
208
+ lines.push(`- key=${key} run=${runId} status=${status}`);
209
+ lines.push(` Saved output: ${child.savedOutputPath ? boundedSafeText(child.savedOutputPath) : "unavailable"}`);
210
+ if (index >= CHILD_OUTPUT_PREVIEW_COUNT) {
211
+ lines.push(" Preview: unavailable (notice preview budget exceeded)");
212
+ continue;
213
+ }
214
+ if (!child.preview) {
215
+ lines.push(` Preview: unavailable (${child.previewUnavailableReason ?? "no safe inline output"})`);
216
+ continue;
217
+ }
218
+ lines.push(" Preview:");
219
+ for (const line of child.preview.split("\n")) lines.push(` | ${line}`);
220
+ }
221
+ if (children.length > CHILD_OUTPUT_PREVIEW_COUNT) {
222
+ lines.push(`- ${children.length - CHILD_OUTPUT_PREVIEW_COUNT} additional child preview(s) omitted by notice budget; child run metadata is retained below.`);
223
+ }
224
+ return lines.join("\n");
225
+ }
226
+
227
+ function formatResultPreview(details: SubagentNotifyDetails): string {
228
+ const summary = details.resultPreview.trim() ? details.resultPreview : "(no output)";
229
+ const childOutputs = formatChildOutputBlock(details.childOutputs);
230
+ return childOutputs ? `${summary}\n\n${childOutputs}` : summary;
231
+ }
232
+
101
233
  function formatSessionLine(details: SubagentNotifyDetails): string | undefined {
102
234
  if (!details.sessionValue) return undefined;
103
235
  return details.sessionLabel ? `${details.sessionLabel}: ${details.sessionValue}` : details.sessionValue;
@@ -129,7 +261,7 @@ export function formatSingleCompletion(details: SubagentNotifyDetails): string {
129
261
  "",
130
262
  scheduleLine,
131
263
  scheduleLine ? "" : undefined,
132
- details.resultPreview.trim() ? details.resultPreview : "(no output)",
264
+ formatResultPreview(details),
133
265
  details.handoffPath ? "" : undefined,
134
266
  details.handoffPath ? `Parallel handoff: ${details.handoffPath}` : undefined,
135
267
  correlationLines.length && !details.handoffPath ? "" : undefined,
@@ -216,7 +348,7 @@ export function formatGroupedCompletion(details: SubagentNotifyDetails[]): strin
216
348
  if (!detail) continue;
217
349
  const sessionLine = formatSessionLine(detail);
218
350
  blocks.push(`${index + 1}. ${detail.agent}${detail.taskInfo ?? ""}${detail.scheduleOrigin ? ` — scheduled run from ${detail.scheduleOrigin.name ?? detail.scheduleOrigin.id} (schedule ${detail.scheduleOrigin.id})` : ""}`);
219
- blocks.push(detail.resultPreview.trim() ? detail.resultPreview : "(no output)");
351
+ blocks.push(formatResultPreview(detail));
220
352
  if (detail.handoffPath) blocks.push(`Parallel handoff: ${detail.handoffPath}`);
221
353
  blocks.push(...formatCorrelationLines(detail));
222
354
  if (sessionLine) blocks.push(sessionLine);
@@ -228,6 +360,8 @@ export function formatGroupedCompletion(details: SubagentNotifyDetails[]): strin
228
360
  interface PendingCompletion {
229
361
  key: string;
230
362
  details: SubagentNotifyDetails;
363
+ sessionId: string;
364
+ completionOwnerId: unknown;
231
365
  triggerTurn: boolean;
232
366
  resolve(accepted: boolean): void;
233
367
  }
@@ -271,6 +405,7 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
271
405
  const paused = !stopped && !result.success && (
272
406
  result.exitCode === 0
273
407
  || result.state === "paused"
408
+ || result.interrupted === true
274
409
  || summary.startsWith("Paused after interrupt.")
275
410
  );
276
411
  const status = stopped ? "stopped" : paused ? "paused" : result.success ? "completed" : "failed";
@@ -286,14 +421,34 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
286
421
  const rawRunId = typeof result.runId === "string" ? result.runId : typeof result.id === "string" ? result.id : undefined;
287
422
  const workflowRunId = (result.mode === "workflow" || agent === "workflow") && rawRunId ? rawRunId : undefined;
288
423
  const childRuns = result.results?.flatMap((child) => {
289
- if (typeof child.runId !== "string" || !child.runId.trim()) return [];
424
+ const runId = typeof child.runId === "string" && child.runId.trim() ? child.runId.trim() : undefined;
425
+ const workflowKey = typeof child.workflowKey === "string" && child.workflowKey.trim() ? child.workflowKey.trim() : undefined;
426
+ if (!runId && (!workflowRunId || !workflowKey)) return [];
290
427
  return [{
291
- runId: child.runId,
292
- ...(typeof child.workflowKey === "string" ? { workflowKey: child.workflowKey } : {}),
428
+ runId: runId ?? "unavailable",
429
+ ...(workflowKey ? { workflowKey } : {}),
293
430
  ...(typeof child.agent === "string" ? { agent: child.agent } : {}),
294
- ...(typeof child.status === "string" ? { status: child.status } : {}),
431
+ status: childStatus(child, workflowRunId ? result.state : undefined),
295
432
  }];
296
433
  }) ?? [];
434
+ const childOutputs = workflowRunId && result.results?.length
435
+ ? result.results.map((child) => {
436
+ const inline = childInlinePreview(child);
437
+ const workflowKey = typeof child.workflowKey === "string" && child.workflowKey.trim() ? child.workflowKey.trim() : undefined;
438
+ const runId = typeof child.runId === "string" && child.runId.trim() ? child.runId.trim() : undefined;
439
+ const savedOutputPath = childSavedOutputPath(child);
440
+ return {
441
+ ...(workflowKey ? { workflowKey } : {}),
442
+ ...(runId ? { runId } : {}),
443
+ ...(typeof child.agent === "string" ? { agent: child.agent } : {}),
444
+ status: childStatus(child, result.state),
445
+ ...(savedOutputPath ? { savedOutputPath } : {}),
446
+ ...(inline.preview ? { preview: inline.preview } : { preview: "" }),
447
+ ...(inline.truncated ? { previewTruncated: true } : {}),
448
+ ...(inline.unavailableReason ? { previewUnavailableReason: inline.unavailableReason } : {}),
449
+ };
450
+ })
451
+ : undefined;
297
452
  const reconciledFromDetachedChild = typeof result.reconciledFromDetachedChild === "string" ? result.reconciledFromDetachedChild : undefined;
298
453
  const session =
299
454
  result.shareUrl
@@ -318,6 +473,7 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
318
473
  ...(handoffPath ? { handoffPath } : {}),
319
474
  ...(workflowRunId ? { workflowRunId } : {}),
320
475
  ...(childRuns.length ? { childRuns } : {}),
476
+ ...(childOutputs?.length ? { childOutputs } : {}),
321
477
  ...(reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {}),
322
478
  ...(session ? { sessionLabel: session.label, sessionValue: session.value } : {}),
323
479
  };
@@ -335,6 +491,10 @@ export default function registerSubagentNotify(
335
491
  const batchConfig = resolveCompletionBatchConfig(options.batchConfig);
336
492
  const batchers = new Map<string, CompletionBatcher<PendingCompletion>>();
337
493
  let disposed = false;
494
+ const ownsResult = options.ownership?.owns
495
+ ?? ((sessionId: string, completionOwnerId: unknown) => sessionId === state.currentSessionId
496
+ && typeof completionOwnerId === "string"
497
+ && completionOwnerId === state.completionOwnerId);
338
498
 
339
499
  const settle = (items: PendingCompletion[], accepted: boolean) => {
340
500
  for (const item of items) {
@@ -343,7 +503,18 @@ export default function registerSubagentNotify(
343
503
  item.resolve(accepted);
344
504
  }
345
505
  };
346
- const emit = (items: PendingCompletion[]) => settle(items, sendCompletion(pi, items));
506
+ const emit = (items: PendingCompletion[]) => {
507
+ const accepted: PendingCompletion[] = [];
508
+ const rejected: PendingCompletion[] = [];
509
+ for (const item of items) {
510
+ const owned = item.details.source === "foreground"
511
+ ? item.sessionId === state.currentSessionId
512
+ : ownsResult(item.sessionId, item.completionOwnerId);
513
+ (owned ? accepted : rejected).push(item);
514
+ }
515
+ settle(rejected, false);
516
+ settle(accepted, sendCompletion(pi, accepted));
517
+ };
347
518
  const getBatcher = (result: CompletionNotification) => {
348
519
  const key = completionBatchKey(result);
349
520
  let batcher = batchers.get(key);
@@ -360,8 +531,10 @@ export default function registerSubagentNotify(
360
531
  };
361
532
 
362
533
  const deliver = (result: CompletionNotification): Promise<boolean> => {
363
- if (disposed || typeof result.sessionId !== "string" || result.sessionId !== state.currentSessionId) return Promise.resolve(false);
364
- if (result.source !== "foreground" && (!state.completionOwnerId || result.completionOwnerId !== state.completionOwnerId)) return Promise.resolve(false);
534
+ if (disposed || typeof result.sessionId !== "string") return Promise.resolve(false);
535
+ if (result.source === "foreground") {
536
+ if (result.sessionId !== state.currentSessionId) return Promise.resolve(false);
537
+ } else if (!ownsResult(result.sessionId, result.completionOwnerId)) return Promise.resolve(false);
365
538
  if (result.intercomDelivered === true) return Promise.resolve(true);
366
539
  const key = buildCompletionKey(result, "notify");
367
540
  const seenAt = seen.get(key);
@@ -376,6 +549,8 @@ export default function registerSubagentNotify(
376
549
  const item: PendingCompletion = {
377
550
  key,
378
551
  details,
552
+ sessionId: result.sessionId,
553
+ completionOwnerId: result.completionOwnerId,
379
554
  triggerTurn: result.triggerTurn !== false,
380
555
  resolve,
381
556
  };
@@ -0,0 +1,45 @@
1
+ import type { SubagentState } from "../../shared/types.ts";
2
+
3
+ const MAX_CLAIMED_PREDECESSOR_SESSIONS = 8;
4
+
5
+ type ResultDeliveryState = Pick<SubagentState, "currentSessionId" | "completionOwnerId">;
6
+
7
+ export interface ResultDeliveryOwnership {
8
+ owns(sessionId: string, completionOwnerId: unknown): boolean;
9
+ claimedSessionIds(): readonly string[];
10
+ claimPredecessor(previousSessionFile: string | undefined, previousRuntimeSessionId: string | null): boolean;
11
+ clear(): void;
12
+ }
13
+
14
+ export function createResultDeliveryOwnership(state: ResultDeliveryState): ResultDeliveryOwnership {
15
+ const claimed = new Map<string, string>();
16
+
17
+ const currentOwner = (): string | undefined => typeof state.completionOwnerId === "string" && state.completionOwnerId
18
+ ? state.completionOwnerId
19
+ : undefined;
20
+
21
+ return {
22
+ owns(sessionId, completionOwnerId) {
23
+ const owner = currentOwner();
24
+ if (!owner || completionOwnerId !== owner) return false;
25
+ return sessionId === state.currentSessionId || claimed.get(sessionId) === owner;
26
+ },
27
+ claimedSessionIds() {
28
+ const owner = currentOwner();
29
+ if (!owner) return [];
30
+ return [...claimed].flatMap(([sessionId, claimedOwner]) => claimedOwner === owner ? [sessionId] : []);
31
+ },
32
+ claimPredecessor(previousSessionFile, previousRuntimeSessionId) {
33
+ const owner = currentOwner();
34
+ if (!owner || !previousSessionFile || !previousRuntimeSessionId) return false;
35
+ if (previousSessionFile !== previousRuntimeSessionId || state.currentSessionId !== previousRuntimeSessionId) return false;
36
+ claimed.delete(previousRuntimeSessionId);
37
+ claimed.set(previousRuntimeSessionId, owner);
38
+ while (claimed.size > MAX_CLAIMED_PREDECESSOR_SESSIONS) claimed.delete(claimed.keys().next().value!);
39
+ return true;
40
+ },
41
+ clear() {
42
+ claimed.clear();
43
+ },
44
+ };
45
+ }