@selesai/code 0.13.16 → 0.13.18

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 (467) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +2 -14
  3. package/dist/bun/cli.d.ts +3 -1
  4. package/dist/bun/cli.js +3 -9
  5. package/dist/bun/{register-bedrock.js → runtime-setup.js} +5 -0
  6. package/dist/bun/sandbox-env-setup.d.ts +1 -0
  7. package/dist/bun/sandbox-env-setup.js +3 -0
  8. package/dist/cli/auth-command.d.ts +23 -0
  9. package/dist/cli/auth-command.js +102 -0
  10. package/dist/cli/config-selector.js +2 -1
  11. package/dist/cli/setup.d.ts +1 -0
  12. package/dist/cli/setup.js +11 -0
  13. package/dist/cli.js +2 -14
  14. package/dist/config.d.ts +3 -2
  15. package/dist/config.js +9 -4
  16. package/dist/core/agent-session-runtime.js +15 -7
  17. package/dist/core/agent-session.d.ts +2 -6
  18. package/dist/core/agent-session.js +15 -4
  19. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  20. package/dist/core/compaction/branch-summarization.js +2 -1
  21. package/dist/core/extensions/loader.js +3 -0
  22. package/dist/core/extensions/types.d.ts +16 -2
  23. package/dist/core/http-dispatcher.js +2 -0
  24. package/dist/core/keybindings.d.ts +5 -0
  25. package/dist/core/keybindings.js +4 -0
  26. package/dist/core/messages.d.ts +1 -1
  27. package/dist/core/model-config.d.ts +15 -0
  28. package/dist/core/model-config.js +3 -0
  29. package/dist/core/model-runtime.d.ts +1 -0
  30. package/dist/core/model-runtime.js +5 -2
  31. package/dist/core/provider-composer.js +8 -2
  32. package/dist/core/session-export.d.ts +3 -0
  33. package/dist/core/session-export.js +31 -0
  34. package/dist/core/session-manager.d.ts +3 -2
  35. package/dist/core/session-manager.js +40 -13
  36. package/dist/core/skills.d.ts +1 -1
  37. package/dist/core/skills.js +4 -2
  38. package/dist/core/system-prompt.js +10 -10
  39. package/dist/core/tools/bash.js +3 -117
  40. package/dist/core/tools/edit.d.ts +1 -12
  41. package/dist/core/tools/edit.js +5 -163
  42. package/dist/core/tools/find.js +4 -55
  43. package/dist/core/tools/grep.js +4 -60
  44. package/dist/core/tools/ls.js +4 -49
  45. package/dist/core/tools/read.d.ts +4 -0
  46. package/dist/core/tools/read.js +10 -122
  47. package/dist/core/tools/renderers/bash.d.ts +11 -0
  48. package/dist/core/tools/renderers/bash.js +129 -0
  49. package/dist/core/tools/renderers/edit.d.ts +22 -0
  50. package/dist/core/tools/renderers/edit.js +166 -0
  51. package/dist/core/tools/renderers/find.d.ts +9 -0
  52. package/dist/core/tools/renderers/find.js +63 -0
  53. package/dist/core/tools/renderers/grep.d.ts +9 -0
  54. package/dist/core/tools/renderers/grep.js +68 -0
  55. package/dist/core/tools/renderers/index.d.ts +27 -0
  56. package/dist/core/tools/renderers/index.js +46 -0
  57. package/dist/core/tools/renderers/ls.d.ts +9 -0
  58. package/dist/core/tools/renderers/ls.js +57 -0
  59. package/dist/core/tools/renderers/read.d.ts +10 -0
  60. package/dist/core/tools/renderers/read.js +128 -0
  61. package/dist/core/tools/renderers/write.d.ts +9 -0
  62. package/dist/core/tools/renderers/write.js +152 -0
  63. package/dist/core/tools/write.js +5 -146
  64. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +18 -0
  65. package/dist/extensions/pi-hermes-memory/README.md +16 -12
  66. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +1 -1
  67. package/dist/extensions/pi-hermes-memory/package-lock.json +587 -62
  68. package/dist/extensions/pi-hermes-memory/package.json +4 -4
  69. package/dist/extensions/pi-hermes-memory/src/config.ts +32 -0
  70. package/dist/extensions/pi-hermes-memory/src/constants.ts +12 -2
  71. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +109 -51
  72. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +9 -3
  73. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +257 -98
  74. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +9 -2
  75. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +5 -0
  76. package/dist/extensions/pi-hermes-memory/src/index.ts +22 -2
  77. package/dist/extensions/pi-hermes-memory/src/paths.ts +1 -1
  78. package/dist/extensions/pi-hermes-memory/src/store/db.ts +6 -0
  79. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +58 -1
  80. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +60 -8
  81. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +169 -7
  82. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +7 -21
  83. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +134 -15
  84. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +4 -2
  85. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +24 -9
  86. package/dist/extensions/pi-hermes-memory/src/types.ts +11 -0
  87. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +42 -0
  88. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +159 -3
  89. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +452 -0
  90. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +39 -0
  91. package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +91 -0
  92. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +29 -0
  93. package/dist/extensions/pi-hermes-memory/tests/store/fts-query.test.ts +116 -0
  94. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +163 -0
  95. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +233 -0
  96. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +12 -0
  97. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +175 -0
  98. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +2 -2
  99. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +22 -0
  100. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +37 -1
  101. package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
  102. package/dist/extensions/pi-intercom/README.md +16 -2
  103. package/dist/extensions/pi-intercom/broker/spawn.test.ts +19 -2
  104. package/dist/extensions/pi-intercom/broker/spawn.ts +4 -4
  105. package/dist/extensions/pi-intercom/index.ts +1 -1
  106. package/dist/extensions/pi-intercom/intercom.integration.test.ts +121 -2
  107. package/dist/extensions/pi-intercom/package.json +1 -1
  108. package/dist/extensions/pi-intercom/project-agent.test.ts +1 -1
  109. package/dist/extensions/pi-intercom/project-agent.ts +1 -6
  110. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +7 -6
  111. package/dist/extensions/pi-rewind-hook/index.test.ts +7 -0
  112. package/dist/extensions/pi-subagents/.github/workflows/test.yml +36 -4
  113. package/dist/extensions/pi-subagents/CHANGELOG.md +120 -0
  114. package/dist/extensions/pi-subagents/README.md +3 -3
  115. package/dist/extensions/pi-subagents/agents/researcher.md +23 -13
  116. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -1
  117. package/dist/extensions/pi-subagents/agents/scout.md +1 -1
  118. package/dist/extensions/pi-subagents/docs/agents.md +32 -16
  119. package/dist/extensions/pi-subagents/docs/configuration.md +41 -15
  120. package/dist/extensions/pi-subagents/docs/extension-api.md +109 -6
  121. package/dist/extensions/pi-subagents/docs/missions.md +2 -0
  122. package/dist/extensions/pi-subagents/docs/models.md +58 -1
  123. package/dist/extensions/pi-subagents/docs/observability.md +50 -12
  124. package/dist/extensions/pi-subagents/docs/tool-reference.md +17 -10
  125. package/dist/extensions/pi-subagents/docs/watchdog.md +1 -1
  126. package/dist/extensions/pi-subagents/docs/workflows.md +26 -9
  127. package/dist/extensions/pi-subagents/package-lock.json +747 -177
  128. package/dist/extensions/pi-subagents/package.json +6 -4
  129. package/dist/extensions/pi-subagents/runner-server-preload.mjs +13 -0
  130. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +2 -1
  131. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  132. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +21 -4
  133. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
  134. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
  135. package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +63 -0
  136. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +61 -16
  137. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
  138. package/dist/extensions/pi-subagents/src/agents/agents.ts +8 -0
  139. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +0 -1
  140. package/dist/extensions/pi-subagents/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
  141. package/dist/extensions/pi-subagents/src/api/preflight.ts +7 -4
  142. package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -2
  143. package/dist/extensions/pi-subagents/src/api/workflow-resources.ts +6 -0
  144. package/dist/extensions/pi-subagents/src/extension/config.ts +4 -2
  145. package/dist/extensions/pi-subagents/src/extension/doctor.ts +2 -10
  146. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +9 -11
  147. package/dist/extensions/pi-subagents/src/extension/index.ts +99 -10
  148. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +13 -0
  149. package/dist/extensions/pi-subagents/src/extension/rpc.ts +7 -23
  150. package/dist/extensions/pi-subagents/src/extension/schemas.ts +11 -8
  151. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +25 -7
  152. package/dist/extensions/pi-subagents/src/integrations/pi-web-session-liveness.ts +73 -0
  153. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +228 -136
  154. package/dist/extensions/pi-subagents/src/intercom/supervisor-ui.ts +244 -0
  155. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +37 -16
  156. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +18 -18
  157. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +51 -25
  158. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +46 -3
  159. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -3
  160. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +9 -0
  161. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +10 -12
  162. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -15
  163. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +40 -29
  164. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +8 -0
  165. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +79 -247
  166. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +88 -12
  167. package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +6 -6
  168. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +24 -24
  169. package/dist/extensions/pi-subagents/src/runs/background/retained-nested-route-tracker.ts +96 -0
  170. package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +642 -0
  171. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +75 -6
  172. package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +163 -0
  173. package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +86 -0
  174. package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +31 -0
  175. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +18 -4
  176. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -1
  177. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +597 -1092
  178. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +3 -0
  179. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +4 -0
  180. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +20 -17
  181. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +296 -394
  182. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +4 -0
  183. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +3 -1
  184. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +9 -5
  185. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +630 -283
  186. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +8 -5
  187. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +78 -98
  188. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +3 -3
  189. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +16 -3
  190. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
  191. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +47 -47
  192. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -2
  193. package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +174 -0
  194. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +14 -3
  195. package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +319 -0
  196. package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +25 -0
  197. package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +126 -0
  198. package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +373 -0
  199. package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +530 -0
  200. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +2 -2
  201. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +2 -1
  202. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +16 -0
  203. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +11 -11
  204. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +30 -20
  205. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
  206. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +84 -15
  207. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +88 -12
  208. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +37 -53
  209. package/dist/extensions/pi-subagents/src/runs/shared/nested-path.ts +1 -1
  210. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +18 -7
  211. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +57 -12
  212. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +3 -4
  213. package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +0 -11
  214. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +4 -1
  215. package/dist/extensions/pi-subagents/src/runs/shared/readonly-drain-observation.ts +42 -0
  216. package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +69 -0
  217. package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +307 -0
  218. package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +8 -21
  219. package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +3 -30
  220. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +17 -4
  221. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +2 -0
  222. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +99 -386
  223. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +18 -62
  224. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +0 -14
  225. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +31 -9
  226. package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +190 -0
  227. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +506 -226
  228. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +1 -1
  229. package/dist/extensions/pi-subagents/src/shared/jsonl-writer.ts +11 -0
  230. package/dist/extensions/pi-subagents/src/shared/model-response-aliases.ts +13 -0
  231. package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +0 -6
  232. package/dist/extensions/pi-subagents/src/shared/types.ts +145 -89
  233. package/dist/extensions/pi-subagents/src/shared/utils.ts +13 -6
  234. package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +2 -0
  235. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +18 -13
  236. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +0 -6
  237. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +1 -1
  238. package/dist/extensions/pi-subagents/src/tui/fleet.ts +11 -6
  239. package/dist/extensions/pi-subagents/src/tui/render.ts +277 -46
  240. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +0 -1
  241. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +12 -12
  242. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
  243. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +116 -15
  244. package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +15 -18
  245. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +57 -8
  246. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +19 -19
  247. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +3 -3
  248. package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +97 -22
  249. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +3 -0
  250. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +336 -21
  251. package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +1181 -0
  252. package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +2240 -0
  253. package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +2268 -0
  254. package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +1512 -0
  255. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +231 -16
  256. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +4 -18
  257. package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +95 -0
  258. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +9 -15
  259. package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +374 -0
  260. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +20 -10
  261. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +18 -6
  262. package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +151 -0
  263. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +42 -1
  264. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +150 -25
  265. package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +354 -0
  266. package/dist/extensions/pi-subagents/test/integration/shared-cwd-terminal-evidence.test.ts +117 -0
  267. package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +3678 -0
  268. package/dist/extensions/pi-subagents/test/integration/{single-execution.test.ts → single-execution.part-2.test.ts} +500 -4207
  269. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -2
  270. package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +127 -0
  271. package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +365 -0
  272. package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +31 -0
  273. package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +74 -0
  274. package/dist/extensions/pi-subagents/test/smoke/pi085-extension.ts +16 -0
  275. package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +699 -0
  276. package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +418 -0
  277. package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +13 -0
  278. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +30 -76
  279. package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +84 -0
  280. package/dist/extensions/pi-subagents/test/support/result-publication-capacity-preload.mjs +70 -0
  281. package/dist/extensions/pi-subagents/test/support/runner-child-session-factory.ts +17 -0
  282. package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +390 -0
  283. package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +120 -0
  284. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +24 -3
  285. package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +87 -0
  286. package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +163 -0
  287. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +70 -31
  288. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +78 -3
  289. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +2 -8
  290. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +1 -1
  291. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +73 -66
  292. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +51 -1
  293. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +49 -1
  294. package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +103 -0
  295. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +3 -3
  296. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +9 -8
  297. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +10 -7
  298. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +48 -6
  299. package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +90 -0
  300. package/dist/extensions/pi-subagents/test/unit/{pi-args-permission-system.test.ts → child-tool-plan-permission-system.test.ts} +48 -86
  301. package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +33 -0
  302. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +18 -18
  303. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +9 -9
  304. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +8 -7
  305. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +225 -65
  306. package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +17 -22
  307. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +11 -1
  308. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +47 -3
  309. package/dist/extensions/pi-subagents/test/unit/foreign-workflow-steering.test.ts +74 -0
  310. package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +23 -29
  311. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +96 -4
  312. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +4 -4
  313. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +116 -55
  314. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +58 -1
  315. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +104 -3
  316. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
  317. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +31 -1
  318. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +102 -51
  319. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +196 -6
  320. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +181 -38
  321. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +62 -70
  322. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +58 -53
  323. package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +42 -0
  324. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +84 -0
  325. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +13 -5
  326. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +21 -5
  327. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +5 -4
  328. package/dist/extensions/pi-subagents/test/unit/permissions.test.ts +2 -6
  329. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +28 -2
  330. package/dist/extensions/pi-subagents/test/unit/pi-web-session-liveness.test.ts +209 -0
  331. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +38 -1
  332. package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +43 -0
  333. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +24 -2
  334. package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +174 -0
  335. package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +94 -0
  336. package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +1329 -0
  337. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +29 -80
  338. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +11 -8
  339. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +2 -2
  340. package/dist/extensions/pi-subagents/test/unit/retained-nested-route-tracker.test.ts +175 -0
  341. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +38 -12
  342. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +2 -5
  343. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +89 -1
  344. package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +2 -16
  345. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +27 -3
  346. package/dist/extensions/pi-subagents/test/unit/scheduled-supervisor-demand.test.ts +215 -0
  347. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +15 -11
  348. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +237 -1
  349. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -5
  350. package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +2 -2
  351. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +14 -1
  352. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +130 -739
  353. package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +682 -0
  354. package/dist/extensions/pi-subagents/test/unit/supervisor-ui.test.ts +137 -0
  355. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +1 -11
  356. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +23 -3
  357. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +8 -1
  358. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +19 -1
  359. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +72 -4
  360. package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +17 -3
  361. package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +34 -0
  362. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +12 -2
  363. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +46 -6
  364. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +3 -3
  365. package/dist/extensions/pi-subagents/test/unit/workflow-resources.test.ts +133 -1
  366. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +53 -2
  367. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +222 -14
  368. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +526 -85
  369. package/dist/extensions/pi-zentui/README.md +17 -0
  370. package/dist/extensions/pi-zentui/assets/screenshots/footer-status-hyperlink.png +0 -0
  371. package/dist/extensions/pi-zentui/docs/configuration.md +71 -2
  372. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +11 -0
  373. package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +14 -0
  374. package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +29 -13
  375. package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +5 -4
  376. package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +11 -0
  377. package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +11 -10
  378. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +77 -3
  379. package/dist/extensions/pi-zentui/extensions/zentui/presets.ts +77 -0
  380. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +103 -2
  381. package/dist/extensions/pi-zentui/extensions/zentui/working-line-extension-segments.ts +115 -0
  382. package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +87 -5
  383. package/dist/extensions/pi-zentui/package-lock.json +277 -517
  384. package/dist/extensions/pi-zentui/package.json +7 -4
  385. package/dist/extensions/pi-zentui/test/config-presets.test.ts +238 -0
  386. package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +24 -1
  387. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +570 -2
  388. package/dist/extensions/pi-zentui/test/extension-status.test.ts +90 -0
  389. package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +243 -0
  390. package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
  391. package/dist/extensions/pi-zentui/test/presets.test.ts +121 -0
  392. package/dist/extensions/pi-zentui/test/settings-command.test.ts +205 -1
  393. package/dist/extensions/pi-zentui/test/working-line-extension-segments.test.ts +139 -0
  394. package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +237 -3
  395. package/dist/extensions/pi-zentui/test/working-line.test.ts +94 -2
  396. package/dist/index.d.ts +1 -1
  397. package/dist/main.js +4 -1
  398. package/dist/modes/interactive/chat-viewport.d.ts +19 -0
  399. package/dist/modes/interactive/chat-viewport.js +27 -0
  400. package/dist/modes/interactive/components/assistant-message.d.ts +6 -2
  401. package/dist/modes/interactive/components/assistant-message.js +30 -13
  402. package/dist/modes/interactive/components/custom-editor.d.ts +10 -1
  403. package/dist/modes/interactive/components/custom-editor.js +39 -1
  404. package/dist/modes/interactive/components/first-time-setup.js +1 -1
  405. package/dist/modes/interactive/components/index.d.ts +1 -1
  406. package/dist/modes/interactive/components/markdown-transform.d.ts +2 -0
  407. package/dist/modes/interactive/components/markdown-transform.js +18 -0
  408. package/dist/modes/interactive/components/model-selector.js +19 -17
  409. package/dist/modes/interactive/components/scoped-models-selector.js +21 -23
  410. package/dist/modes/interactive/components/settings-selector.d.ts +7 -1
  411. package/dist/modes/interactive/components/settings-selector.js +128 -20
  412. package/dist/modes/interactive/components/settings-submenu.d.ts +71 -0
  413. package/dist/modes/interactive/components/settings-submenu.js +164 -0
  414. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  415. package/dist/modes/interactive/components/status-indicator.js +10 -3
  416. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  417. package/dist/modes/interactive/components/thinking-selector.js +75 -18
  418. package/dist/modes/interactive/components/tool-execution.d.ts +21 -4
  419. package/dist/modes/interactive/components/tool-execution.js +35 -33
  420. package/dist/modes/interactive/components/trust-selector.js +2 -2
  421. package/dist/modes/interactive/interactive-mode.d.ts +13 -15
  422. package/dist/modes/interactive/interactive-mode.js +144 -80
  423. package/dist/modes/interactive/session-share.d.ts +15 -0
  424. package/dist/modes/interactive/session-share.js +183 -0
  425. package/dist/modes/interactive/theme/dark.json +2 -1
  426. package/dist/modes/interactive/theme/light.json +2 -1
  427. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
  428. package/dist/modes/interactive/theme/theme-controller.js +5 -0
  429. package/dist/modes/interactive/theme/theme-json.d.ts +83 -0
  430. package/dist/modes/interactive/theme/theme-json.js +129 -0
  431. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  432. package/dist/modes/interactive/theme/theme.d.ts +14 -5
  433. package/dist/modes/interactive/theme/theme.js +18 -119
  434. package/dist/modes/interactive/tui-renderer.d.ts +20 -0
  435. package/dist/modes/interactive/tui-renderer.js +65 -0
  436. package/dist/utils/exif-orientation.js +2 -3
  437. package/dist/utils/management-http.d.ts +24 -0
  438. package/dist/utils/management-http.js +53 -0
  439. package/dist/utils/text.d.ts +7 -0
  440. package/dist/utils/text.js +8 -0
  441. package/dist/utils/tools-manager.d.ts +1 -0
  442. package/dist/utils/tools-manager.js +43 -21
  443. package/docs/docs.json +0 -4
  444. package/package.json +4 -4
  445. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +0 -415
  446. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +0 -1059
  447. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +0 -116
  448. package/dist/extensions/pi-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
  449. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +0 -224
  450. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -6986
  451. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +0 -447
  452. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +0 -185
  453. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +0 -311
  454. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +0 -84
  455. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +0 -202
  456. package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +0 -101
  457. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +0 -2347
  458. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +0 -104
  459. package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +0 -49
  460. package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -1465
  461. package/dist/skills/imagegen-frontend-web/SKILL.md +0 -987
  462. package/dist/skills/implanger/SKILL.md +0 -70
  463. package/dist/skills/output/SKILL.md +0 -49
  464. package/dist/skills/planger/SKILL.md +0 -167
  465. package/dist/skills/workflow/SKILL.md +0 -95
  466. package/docs/workflows.md +0 -18
  467. /package/dist/bun/{register-bedrock.d.ts → runtime-setup.d.ts} +0 -0
@@ -6,14 +6,13 @@ import * as path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { describe, it } from "node:test";
8
8
  import { WAIT_TOOL_ENABLED_ENV } from "../../src/runs/background/subagent-wait.ts";
9
- import { SUBAGENT_CHILD_ENV, SUBAGENT_FANOUT_CHILD_ENV } from "../../src/runs/shared/pi-args.ts";
9
+ import { SUBAGENT_CHILD_ENV } from "../../src/runs/shared/child-runtime-config.ts";
10
10
 
11
11
  const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
12
12
 
13
13
  function parentToolEnv(agentDir?: string): NodeJS.ProcessEnv {
14
14
  const env = { ...process.env };
15
15
  delete env[SUBAGENT_CHILD_ENV];
16
- delete env[SUBAGENT_FANOUT_CHILD_ENV];
17
16
  delete env[WAIT_TOOL_ENABLED_ENV];
18
17
  if (agentDir) env.SELESAI_CODING_AGENT_DIR = agentDir;
19
18
  return env;
@@ -554,7 +553,7 @@ describe("subagent extension child mode", () => {
554
553
  const events = { on(channel, handler) { eventHandlers.set(channel, handler); return () => {}; }, emit() {} };
555
554
  const fakePi = new Proxy({
556
555
  events,
557
- on(channel, handler) { handlers.set(channel, handler); },
556
+ on(channel, handler) { handlers.set(channel, [...(handlers.get(channel) ?? []), handler]); },
558
557
  registerTool() {}, registerCommand() {}, registerShortcut() {}, registerMessageRenderer() {},
559
558
  sendMessage() {}, getSessionName() { return undefined; },
560
559
  }, { get(target, prop) { return prop in target ? target[prop] : () => undefined; } });
@@ -566,13 +565,13 @@ describe("subagent extension child mode", () => {
566
565
  modelRegistry: { getAvailable() { return []; } },
567
566
  };
568
567
  registerSubagentExtension(fakePi);
569
- handlers.get("session_start")({}, ctx);
568
+ for (const handler of handlers.get("session_start")) await handler({}, ctx);
570
569
  widgets.length = 0;
571
570
  eventHandlers.get("subagent:async-started")({ id: "widget-run", pid: 1, sessionId: "session-widget", mode: "single", agent: "worker", asyncDir: "/tmp/widget-run" });
572
- handlers.get("tool_result")({ toolName: "subagent" }, ctx);
571
+ for (const handler of handlers.get("tool_result")) await handler({ toolName: "subagent" }, ctx);
573
572
  const asyncWidgets = widgets.filter((entry) => entry.key === "subagent-async");
574
573
  if (asyncWidgets.length < 2 || asyncWidgets.some((entry) => entry.value !== undefined)) throw new Error("async widget rendered despite disabled config: " + JSON.stringify(asyncWidgets));
575
- handlers.get("session_shutdown")();
574
+ for (const handler of handlers.get("session_shutdown")) await handler();
576
575
  `;
577
576
  const env = parentToolEnv();
578
577
  env.SELESAI_CODING_AGENT_DIR = agentDir;
@@ -595,7 +594,7 @@ describe("subagent extension child mode", () => {
595
594
  const events = { on(channel, handler) { eventHandlers.set(channel, handler); return () => {}; }, emit() {} };
596
595
  const fakePi = new Proxy({
597
596
  events,
598
- on(channel, handler) { handlers.set(channel, handler); },
597
+ on(channel, handler) { handlers.set(channel, [...(handlers.get(channel) ?? []), handler]); },
599
598
  registerTool() {}, registerCommand() {}, registerShortcut() {}, registerMessageRenderer() {},
600
599
  sendMessage() {}, getSessionName() { return undefined; },
601
600
  }, { get(target, prop) { return prop in target ? target[prop] : () => undefined; } });
@@ -607,13 +606,13 @@ describe("subagent extension child mode", () => {
607
606
  modelRegistry: { getAvailable() { return []; } },
608
607
  };
609
608
  registerSubagentExtension(fakePi);
610
- handlers.get("session_start")({}, ctx);
609
+ for (const handler of handlers.get("session_start")) await handler({}, ctx);
611
610
  widgets.length = 0;
612
611
  eventHandlers.get("subagent:async-started")({ id: "widget-run", pid: 1, sessionId: "session-widget", mode: "workflow", agent: "worker", asyncDir: "/tmp/widget-run" });
613
- handlers.get("tool_result")({ toolName: "subagent" }, ctx);
612
+ for (const handler of handlers.get("tool_result")) await handler({ toolName: "subagent" }, ctx);
614
613
  const asyncWidgets = widgets.filter((entry) => entry.key === "subagent-async");
615
614
  if (!asyncWidgets.some((entry) => entry.value !== undefined)) throw new Error("async widget was not rendered with FleetView enabled: " + JSON.stringify(asyncWidgets));
616
- handlers.get("session_shutdown")();
615
+ for (const handler of handlers.get("session_shutdown")) await handler();
617
616
  `;
618
617
  const env = parentToolEnv();
619
618
  env.SELESAI_CODING_AGENT_DIR = agentDir;
@@ -639,7 +638,7 @@ describe("subagent extension child mode", () => {
639
638
  process.env.HERDR_PANE_ID = "w1:p1";
640
639
  const fakePi = new Proxy({
641
640
  events,
642
- on(channel, handler) { handlers.set(channel, handler); },
641
+ on(channel, handler) { handlers.set(channel, [...(handlers.get(channel) ?? []), handler]); },
643
642
  exec(command, args) { herdrCommands.push({ command, args }); return Promise.resolve({ code: 0, stdout: "", stderr: "", killed: false }); },
644
643
  registerTool() {}, registerCommand() {}, registerShortcut() {}, registerMessageRenderer() {},
645
644
  sendMessage() {}, getSessionName() { return undefined; },
@@ -655,7 +654,7 @@ describe("subagent extension child mode", () => {
655
654
  const asyncDir = path.join(DIRS.async, runId);
656
655
  fs.rmSync(asyncDir, { recursive: true, force: true });
657
656
  registerSubagentExtension(fakePi);
658
- handlers.get("session_start")({}, ctx);
657
+ for (const handler of handlers.get("session_start")) await handler({}, ctx);
659
658
  widgets.length = 0;
660
659
  fs.mkdirSync(asyncDir, { recursive: true });
661
660
  fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({
@@ -663,19 +662,81 @@ describe("subagent extension child mode", () => {
663
662
  startedAt: Date.now(), lastUpdate: Date.now(), cwd: process.cwd(), pid: process.pid,
664
663
  }), "utf-8");
665
664
  updateActiveRunIndex(asyncDir, "running");
666
- handlers.get("tool_result")({ toolName: "subagent" }, ctx);
665
+ for (const handler of handlers.get("tool_result")) await handler({ toolName: "subagent" }, ctx);
667
666
  const fleetWidgets = widgets.filter((entry) => entry.key === "subagent-fleet-status");
668
667
  if (!fleetWidgets.some((entry) => typeof entry.value === "function")) throw new Error("management result did not restore active fleet status: " + JSON.stringify(fleetWidgets));
669
668
  if (!herdrCommands.some(({ args }) => args.includes("summary=⏳ 1 subagent"))) throw new Error("management result did not restore Herdr status: " + JSON.stringify(herdrCommands));
670
669
  const herdrCommandCount = herdrCommands.length;
671
- handlers.get("tool_result")({ toolName: "subagent" }, ctx);
670
+ for (const handler of handlers.get("tool_result")) await handler({ toolName: "subagent" }, ctx);
672
671
  if (herdrCommands.length !== herdrCommandCount) throw new Error("unchanged active jobs redundantly refreshed Herdr status: " + JSON.stringify(herdrCommands));
673
- handlers.get("session_shutdown")();
672
+ for (const handler of handlers.get("session_shutdown")) await handler();
674
673
  fs.rmSync(asyncDir, { recursive: true, force: true });
675
674
  `;
676
675
  execFileSync(process.execPath, ["--experimental-strip-types", "--import", "./test/support/register-loader.mjs", "--input-type=module", "--eval", script], { cwd: projectRoot, env: parentToolEnv(), stdio: "pipe" });
677
676
  });
678
677
 
678
+ it("registers pi-web liveness for the current session and releases it on shutdown", () => {
679
+ const script = String.raw`
680
+ import registerSubagentExtension from "./index.ts";
681
+ import { currentCompletionOwnerId } from "./src/shared/completion-owner.ts";
682
+ const handlers = new Map();
683
+ const listeners = new Map();
684
+ const events = {
685
+ on(channel, handler) {
686
+ let set = listeners.get(channel);
687
+ if (!set) listeners.set(channel, set = new Set());
688
+ set.add(handler);
689
+ return () => set.delete(handler);
690
+ },
691
+ emit(channel, payload) {
692
+ for (const handler of [...(listeners.get(channel) ?? [])]) handler(payload);
693
+ },
694
+ };
695
+ const pi = new Proxy({
696
+ events,
697
+ on(channel, handler) { handlers.set(channel, [...(handlers.get(channel) ?? []), handler]); },
698
+ registerTool() {}, registerCommand() {}, registerShortcut() {}, registerMessageRenderer() {},
699
+ sendMessage() {}, getSessionName() { return undefined; },
700
+ }, { get(target, prop) { return prop in target ? target[prop] : () => undefined; } });
701
+ const sessionId = "liveness-" + crypto.randomUUID();
702
+ const sessionFile = "/tmp/" + sessionId + ".jsonl";
703
+ let provider;
704
+ let released = 0;
705
+ const registryKey = Symbol.for("@agegr/pi-web/session-liveness/v1");
706
+ globalThis[registryKey] = {
707
+ version: 1,
708
+ register(value) {
709
+ provider = value;
710
+ return () => { released += 1; };
711
+ },
712
+ };
713
+ const ctx = {
714
+ cwd: process.cwd(), hasUI: false,
715
+ ui: { setWidget() {}, requestRender() {}, theme: { fg(_name, text) { return text; }, bg(_name, text) { return text; }, bold(text) { return text; } } },
716
+ sessionManager: { getSessionId() { return sessionId; }, getSessionFile() { return sessionFile; }, getEntries() { return []; } },
717
+ modelRegistry: { getAvailable() { return []; } },
718
+ };
719
+ registerSubagentExtension(pi);
720
+ for (const handler of handlers.get("session_start")) await handler({ reason: "startup" }, ctx);
721
+ if (provider?.name !== "pi-subagents" || provider?.sessionId !== sessionId || provider?.sessionFile !== sessionFile) {
722
+ throw new Error("liveness provider did not preserve exact session identity: " + JSON.stringify(provider));
723
+ }
724
+ if (provider.isActive()) throw new Error("idle runtime reported live work");
725
+ const completionOwnerId = currentCompletionOwnerId();
726
+ events.emit("subagent:async-started", { id: "run-1", sessionId: sessionFile, completionOwnerId, mode: "single", agent: "worker", asyncDir: "/tmp/" + sessionId + "-run" });
727
+ if (!provider.isActive()) throw new Error("queued async work was not reported live");
728
+ events.emit("subagent:async-complete", { id: "run-1", sessionId: sessionFile, completionOwnerId, success: true, summary: "done" });
729
+ if (!provider.isActive()) throw new Error("pending completion delivery was not reported live");
730
+ const deliveryDeadline = Date.now() + 2000;
731
+ while (provider.isActive() && Date.now() < deliveryDeadline) await new Promise((resolve) => setTimeout(resolve, 10));
732
+ if (provider.isActive()) throw new Error("retained terminal work was reported live after delivery");
733
+ for (const handler of handlers.get("session_shutdown")) await handler({ reason: "quit" });
734
+ if (released !== 1) throw new Error("liveness registration was not released exactly once: " + released);
735
+ delete globalThis[registryKey];
736
+ `;
737
+ execFileSync(process.execPath, ["--experimental-strip-types", "--import", "./test/support/register-loader.mjs", "--input-type=module", "--eval", script], { cwd: projectRoot, env: parentToolEnv(), stdio: "pipe" });
738
+ });
739
+
679
740
  it("keeps independent extension runtimes active in one process", () => {
680
741
  const script = String.raw`
681
742
  import registerSubagentExtension from "./index.ts";
@@ -701,7 +762,7 @@ describe("subagent extension child mode", () => {
701
762
  };
702
763
  const pi = new Proxy({
703
764
  events,
704
- on(channel, handler) { handlers.set(channel, handler); },
765
+ on(channel, handler) { handlers.set(channel, [...(handlers.get(channel) ?? []), handler]); },
705
766
  registerTool() {}, registerCommand() {}, registerShortcut() {}, registerMessageRenderer() {},
706
767
  sendMessage() {}, getSessionName() { return undefined; },
707
768
  }, { get(target, prop) { return prop in target ? target[prop] : () => undefined; } });
@@ -721,7 +782,7 @@ describe("subagent extension child mode", () => {
721
782
 
722
783
  const first = createRuntime("independent-first");
723
784
  registerSubagentExtension(first.pi);
724
- first.handlers.get("session_start")({ reason: "startup" }, first.ctx);
785
+ for (const handler of first.handlers.get("session_start")) await handler({ reason: "startup" }, first.ctx);
725
786
  first.events.emit("subagent:async-complete", {
726
787
  id: "independent-baseline", agent: "worker", success: true, summary: "Baseline",
727
788
  exitCode: 0, timestamp: Date.now(), sessionId: "independent-first", completionOwnerId,
@@ -730,9 +791,9 @@ describe("subagent extension child mode", () => {
730
791
  if (baselineDeliveries === 0) throw new Error("first runtime did not register async-complete listeners");
731
792
  const second = createRuntime("independent-second");
732
793
  registerSubagentExtension(second.pi);
733
- second.handlers.get("session_start")({ reason: "startup" }, second.ctx);
794
+ for (const handler of second.handlers.get("session_start")) await handler({ reason: "startup" }, second.ctx);
734
795
 
735
- await first.handlers.get("agent_end")({}, first.ctx);
796
+ for (const handler of first.handlers.get("agent_end")) await handler({}, first.ctx);
736
797
  first.events.emit("subagent:async-complete", {
737
798
  id: "independent-completion", agent: "worker", success: true, summary: "Done",
738
799
  exitCode: 0, timestamp: Date.now(), sessionId: "independent-first", completionOwnerId,
@@ -741,11 +802,11 @@ describe("subagent extension child mode", () => {
741
802
  throw new Error("first runtime no longer received async-complete after second registration");
742
803
  }
743
804
 
744
- await first.handlers.get("session_shutdown")();
805
+ for (const handler of first.handlers.get("session_shutdown")) await handler();
745
806
  if (process.env.SELESAI_SUBAGENT_PARENT_SESSION !== "independent-second") {
746
807
  throw new Error("independent shutdown cleared another runtime's parent session identity");
747
808
  }
748
- await second.handlers.get("agent_end")({}, second.ctx);
809
+ for (const handler of second.handlers.get("agent_end")) await handler({}, second.ctx);
749
810
  second.events.emit("subagent:async-complete", {
750
811
  id: "independent-second-completion", agent: "reviewer", success: true, summary: "Done",
751
812
  exitCode: 0, timestamp: Date.now(), sessionId: "independent-second", completionOwnerId,
@@ -753,7 +814,7 @@ describe("subagent extension child mode", () => {
753
814
  if ((second.eventDeliveries.get("subagent:async-complete") ?? 0) === 0) {
754
815
  throw new Error("first runtime shutdown removed the second runtime's event subscription");
755
816
  }
756
- await second.handlers.get("session_shutdown")();
817
+ for (const handler of second.handlers.get("session_shutdown")) await handler();
757
818
  if (process.env.SELESAI_SUBAGENT_PARENT_SESSION !== undefined) {
758
819
  throw new Error("owning shutdown left its parent session identity active");
759
820
  }
@@ -775,7 +836,7 @@ describe("subagent extension child mode", () => {
775
836
  const events = { on() { return () => {}; }, emit() {} };
776
837
  const pi = new Proxy({
777
838
  events,
778
- on(channel, handler) { handlers.set(channel, handler); },
839
+ on(channel, handler) { handlers.set(channel, [...(handlers.get(channel) ?? []), handler]); },
779
840
  registerTool() {}, registerCommand() {}, registerShortcut() {}, registerMessageRenderer() {},
780
841
  sendMessage() {}, getSessionName() { return undefined; },
781
842
  }, { get(target, prop) { return prop in target ? target[prop] : () => undefined; } });
@@ -791,18 +852,18 @@ describe("subagent extension child mode", () => {
791
852
  const first = createRuntime("slash-first");
792
853
  const second = createRuntime("slash-second");
793
854
  registerSubagentExtension(first.pi);
794
- first.handlers.get("session_start")({ reason: "startup" }, first.ctx);
855
+ for (const handler of first.handlers.get("session_start")) await handler({ reason: "startup" }, first.ctx);
795
856
  registerSubagentExtension(second.pi);
796
- second.handlers.get("session_start")({ reason: "startup" }, second.ctx);
857
+ for (const handler of second.handlers.get("session_start")) await handler({ reason: "startup" }, second.ctx);
797
858
  const details = buildSlashInitialResult("slash-isolation", { agent: "worker", task: "Keep this snapshot" });
798
859
  const liveVersion = getSlashRenderableSnapshot(details).version;
799
860
  if (liveVersion <= 0) throw new Error("slash snapshot was not populated");
800
861
 
801
- await first.handlers.get("session_shutdown")({ reason: "shutdown" });
862
+ for (const handler of first.handlers.get("session_shutdown")) await handler({ reason: "shutdown" });
802
863
  if (getSlashRenderableSnapshot(details).version !== liveVersion) {
803
864
  throw new Error("one runtime shutdown cleared another active runtime's slash snapshot");
804
865
  }
805
- await second.handlers.get("session_shutdown")({ reason: "shutdown" });
866
+ for (const handler of second.handlers.get("session_shutdown")) await handler({ reason: "shutdown" });
806
867
  if (getSlashRenderableSnapshot(details).version !== 0) {
807
868
  throw new Error("last runtime shutdown did not clear slash snapshots");
808
869
  }
@@ -849,7 +910,7 @@ describe("subagent extension child mode", () => {
849
910
  const sent = [];
850
911
  const fakePi = new Proxy({
851
912
  events,
852
- on(channel, handler) { handlers.set(channel, handler); },
913
+ on(channel, handler) { handlers.set(channel, [...(handlers.get(channel) ?? []), handler]); },
853
914
  registerTool() {}, registerCommand() {}, registerShortcut() {}, registerMessageRenderer() {},
854
915
  sendMessage(message) { sent.push(message); }, getSessionName() { return undefined; },
855
916
  }, { get(target, prop) { return prop in target ? target[prop] : () => undefined; } });
@@ -860,7 +921,7 @@ describe("subagent extension child mode", () => {
860
921
  modelRegistry: { getAvailable() { return []; } },
861
922
  };
862
923
  registerSubagentExtension(fakePi);
863
- handlers.get("session_start")({}, ctx);
924
+ for (const handler of handlers.get("session_start")) await handler({}, ctx);
864
925
  sent.length = 0;
865
926
  events.emit("subagent:async-complete", {
866
927
  id: "shutdown-held-completion", agent: "worker", success: true, summary: "Done",
@@ -869,7 +930,7 @@ describe("subagent extension child mode", () => {
869
930
  if (sent.length !== 0) throw new Error("completion was not queued before shutdown");
870
931
  const heldTimers = [...pendingTimers.values()];
871
932
  if (heldTimers.length === 0) throw new Error("completion did not schedule a timer");
872
- handlers.get("session_shutdown")();
933
+ for (const handler of handlers.get("session_shutdown")) await handler();
873
934
  if (pendingTimers.size !== 0) throw new Error("shutdown left completion timers pending");
874
935
  for (const handler of heldTimers) handler();
875
936
  if (sent.length !== 0) throw new Error("stale completion sent after shutdown");
@@ -930,7 +991,7 @@ describe("subagent extension child mode", () => {
930
991
  const sent = [];
931
992
  const pi = new Proxy({
932
993
  events,
933
- on(channel, handler) { handlers.set(channel, handler); },
994
+ on(channel, handler) { handlers.set(channel, [...(handlers.get(channel) ?? []), handler]); },
934
995
  registerTool() {}, registerCommand() {}, registerShortcut() {}, registerMessageRenderer() {},
935
996
  sendMessage(message) { sent.push(message); }, getSessionName() { return undefined; },
936
997
  }, { get(target, prop) { return prop in target ? target[prop] : () => undefined; } });
@@ -945,7 +1006,7 @@ describe("subagent extension child mode", () => {
945
1006
 
946
1007
  const oldRuntime = createRuntime();
947
1008
  registerSubagentExtension(oldRuntime.pi);
948
- oldRuntime.handlers.get("session_start")({ reason: "startup" }, oldRuntime.ctx);
1009
+ for (const handler of oldRuntime.handlers.get("session_start")) await handler({ reason: "startup" }, oldRuntime.ctx);
949
1010
  const oldListenerCount = eventListeners.get("subagent:async-complete")?.size ?? 0;
950
1011
  if (oldListenerCount === 0) throw new Error("old runtime did not register async-complete listeners");
951
1012
  oldRuntime.sent.length = 0;
@@ -960,13 +1021,13 @@ describe("subagent extension child mode", () => {
960
1021
 
961
1022
  const newRuntime = createRuntime();
962
1023
  registerSubagentExtension(newRuntime.pi);
963
- newRuntime.handlers.get("session_start")({ reason: "reload" }, newRuntime.ctx);
1024
+ for (const handler of newRuntime.handlers.get("session_start")) await handler({ reason: "reload" }, newRuntime.ctx);
964
1025
  if (eventListeners.get("subagent:async-complete")?.size !== oldListenerCount) {
965
1026
  throw new Error("replacement runtime did not restore its event subscriptions");
966
1027
  }
967
1028
  for (const [, handler] of oldCompletionTimers) handler();
968
1029
  if (oldRuntime.sent.length !== 0) throw new Error("stale completion sent after runtime cleanup");
969
- await oldRuntime.handlers.get("session_shutdown")({ reason: "reload" });
1030
+ for (const handler of oldRuntime.handlers.get("session_shutdown")) await handler({ reason: "reload" });
970
1031
  if (eventListeners.get("subagent:async-complete")?.size !== oldListenerCount
971
1032
  || process.env.SELESAI_SUBAGENT_PARENT_SESSION !== "notify-reload-session") {
972
1033
  throw new Error("stale shutdown changed the replacement runtime");
@@ -984,7 +1045,7 @@ describe("subagent extension child mode", () => {
984
1045
  handler();
985
1046
  }
986
1047
  if (newRuntime.sent.length !== 1) throw new Error("new notifier was not active after reload cleanup");
987
- await newRuntime.handlers.get("session_shutdown")({ reason: "shutdown" });
1048
+ for (const handler of newRuntime.handlers.get("session_shutdown")) await handler({ reason: "shutdown" });
988
1049
  globalThis.setTimeout = realSetTimeout;
989
1050
  globalThis.clearTimeout = realClearTimeout;
990
1051
  `;
@@ -1013,7 +1074,7 @@ describe("subagent extension child mode", () => {
1013
1074
  const events = { on() { return () => {}; }, emit() {} };
1014
1075
  const fakePi = new Proxy({
1015
1076
  events,
1016
- on(channel, handler) { handlers.set(channel, handler); },
1077
+ on(channel, handler) { handlers.set(channel, [...(handlers.get(channel) ?? []), handler]); },
1017
1078
  registerTool() {}, registerCommand() {}, registerShortcut() {}, registerMessageRenderer() {},
1018
1079
  sendMessage() {}, getSessionName() { return undefined; },
1019
1080
  }, { get(target, prop) { return prop in target ? target[prop] : () => undefined; } });
@@ -1032,9 +1093,9 @@ describe("subagent extension child mode", () => {
1032
1093
  modelRegistry: { getAvailable() { return []; } },
1033
1094
  };
1034
1095
  registerSubagentExtension(fakePi);
1035
- handlers.get("session_start")({ reason: "startup" }, ctx);
1096
+ for (const handler of handlers.get("session_start")) await handler({ reason: "startup" }, ctx);
1036
1097
  stale = true;
1037
- handlers.get("session_shutdown")({ reason: "reload" });
1098
+ for (const handler of handlers.get("session_shutdown")) await handler({ reason: "reload" });
1038
1099
  `;
1039
1100
 
1040
1101
  try {
@@ -1069,7 +1130,7 @@ describe("subagent extension child mode", () => {
1069
1130
  const sent = [];
1070
1131
  const pi = new Proxy({
1071
1132
  events,
1072
- on(channel, handler) { handlers.set(channel, handler); },
1133
+ on(channel, handler) { handlers.set(channel, [...(handlers.get(channel) ?? []), handler]); },
1073
1134
  registerTool() {}, registerCommand() {}, registerShortcut() {}, registerMessageRenderer() {},
1074
1135
  sendMessage(message) { sent.push(message); }, getSessionName() { return undefined; },
1075
1136
  }, { get(target, prop) { return prop in target ? target[prop] : () => undefined; } });
@@ -1083,14 +1144,14 @@ describe("subagent extension child mode", () => {
1083
1144
  const sessionManager = { getSessionId() { return path.basename(currentSession); }, getSessionFile() { return currentSession; }, getEntries() { return []; } };
1084
1145
  const ctx = { cwd: process.cwd(), hasUI: false, ui: { setWidget() {}, requestRender() {}, theme: { fg(_name, text) { return text; }, bg(_name, text) { return text; }, bold(text) { return text; } } }, sessionManager, modelRegistry: { getAvailable() { return []; } } };
1085
1146
  registerSubagentExtension(pi);
1086
- handlers.get("session_start")({ reason: "startup" }, ctx);
1147
+ for (const handler of handlers.get("session_start")) await handler({ reason: "startup" }, ctx);
1087
1148
  currentSession = newSession;
1088
- handlers.get("session_start")({ reason: "new", previousSessionFile: oldSession }, ctx);
1149
+ for (const handler of handlers.get("session_start")) await handler({ reason: "new", previousSessionFile: oldSession }, ctx);
1089
1150
  const owner = currentCompletionOwnerId();
1090
1151
  events.emit("subagent:async-complete", { id: "old-owned", agent: "worker", success: true, summary: "old", timestamp: 1, sessionId: oldSession, completionOwnerId: owner });
1091
1152
  events.emit("subagent:async-complete", { id: "foreign", agent: "worker", success: true, summary: "foreign", timestamp: 2, sessionId: path.join(sessions, "foreign.jsonl"), completionOwnerId: owner });
1092
1153
  if (sent.length !== 1) throw new Error("expected only the claimed predecessor completion, got " + sent.length);
1093
- await handlers.get("session_shutdown")({ reason: "quit" });
1154
+ for (const handler of handlers.get("session_shutdown")) await handler({ reason: "quit" });
1094
1155
  `;
1095
1156
 
1096
1157
  try {
@@ -1108,12 +1169,14 @@ describe("subagent extension child mode", () => {
1108
1169
  const events = { on() { return () => {}; }, emit() {} };
1109
1170
  const commands = [];
1110
1171
  const renderers = [];
1172
+ const entryRenderers = [];
1111
1173
  const fakePi = new Proxy({
1112
1174
  events,
1113
1175
  registerTool() {},
1114
1176
  registerCommand(name) { commands.push(name); },
1115
1177
  registerShortcut() {},
1116
1178
  registerMessageRenderer(type) { renderers.push(type); },
1179
+ registerEntryRenderer(type) { entryRenderers.push(type); },
1117
1180
  sendMessage() {},
1118
1181
  getSessionName() { return undefined; },
1119
1182
  }, {
@@ -1125,6 +1188,8 @@ describe("subagent extension child mode", () => {
1125
1188
  registerSubagentExtension(fakePi);
1126
1189
  if (!commands.includes("subagents-watchdog")) throw new Error("watchdog command not registered: " + commands.join(", "));
1127
1190
  if (!renderers.includes("subagent_watchdog_warning")) throw new Error("watchdog renderer not registered: " + renderers.join(", "));
1191
+ if (!renderers.includes("subagent_supervisor_request")) throw new Error("supervisor request renderer not registered: " + renderers.join(", "));
1192
+ if (!entryRenderers.includes("subagent_supervisor_reply")) throw new Error("supervisor reply entry renderer not registered: " + entryRenderers.join(", "));
1128
1193
  `;
1129
1194
 
1130
1195
  execFileSync(
@@ -1141,7 +1206,7 @@ describe("subagent extension child mode", () => {
1141
1206
  );
1142
1207
  });
1143
1208
 
1144
- it("returns before registering anything for non-fanout children", () => {
1209
+ it("returns before registering anything in a child-hosting process", () => {
1145
1210
  const script = String.raw`
1146
1211
  import registerSubagentExtension from "./index.ts";
1147
1212
  const calls = [];
@@ -1169,16 +1234,15 @@ describe("subagent extension child mode", () => {
1169
1234
  "--eval",
1170
1235
  script,
1171
1236
  ],
1172
- { cwd: projectRoot, env: { ...parentToolEnv(), [SUBAGENT_CHILD_ENV]: "1", [SUBAGENT_FANOUT_CHILD_ENV]: "0" }, stdio: "pipe" },
1237
+ { cwd: projectRoot, env: { ...parentToolEnv(), [SUBAGENT_CHILD_ENV]: "1" }, stdio: "pipe" },
1173
1238
  );
1174
1239
  });
1175
1240
 
1176
- it("returns before registering anything for fanout children", () => {
1241
+ it("returns before registering anything when the child host flag is set after import", () => {
1177
1242
  const script = String.raw`
1178
1243
  import registerSubagentExtension from "./index.ts";
1179
- import { SUBAGENT_CHILD_ENV, SUBAGENT_FANOUT_CHILD_ENV } from "./src/runs/shared/pi-args.ts";
1244
+ import { SUBAGENT_CHILD_ENV } from "./src/runs/shared/child-runtime-config.ts";
1180
1245
  process.env[SUBAGENT_CHILD_ENV] = "1";
1181
- process.env[SUBAGENT_FANOUT_CHILD_ENV] = "1";
1182
1246
  const calls = [];
1183
1247
  const fakePi = new Proxy({}, {
1184
1248
  get(target, prop) {
@@ -1213,9 +1277,9 @@ describe("subagent extension child mode", () => {
1213
1277
  const script = String.raw`
1214
1278
  import registerSubagentExtension from "./index.ts";
1215
1279
  import registerFanoutChildSubagentExtension from "./src/extension/fanout-child.ts";
1216
- import { SUBAGENT_CHILD_ENV, SUBAGENT_FANOUT_CHILD_ENV } from "./src/runs/shared/pi-args.ts";
1280
+ import { SUBAGENT_CHILD_ENV } from "./src/runs/shared/child-runtime-config.ts";
1217
1281
  process.env[SUBAGENT_CHILD_ENV] = "1";
1218
- process.env[SUBAGENT_FANOUT_CHILD_ENV] = "1";
1282
+ const childRuntime = { fanoutChild: true, depth: 1, waitTool: { enabled: true }, fast: false };
1219
1283
 
1220
1284
  const registeredNames = new Set();
1221
1285
  const registrations = [];
@@ -1234,7 +1298,7 @@ describe("subagent extension child mode", () => {
1234
1298
  }
1235
1299
 
1236
1300
  registerSubagentExtension(makePi("index.ts"));
1237
- registerFanoutChildSubagentExtension(makePi("fanout-child.ts"));
1301
+ registerFanoutChildSubagentExtension(makePi("fanout-child.ts"), childRuntime);
1238
1302
  if (registrations.length !== 1 || registrations[0].name !== "subagent" || registrations[0].source !== "fanout-child.ts") {
1239
1303
  throw new Error("expected only fanout-child.ts to register subagent, got " + JSON.stringify(registrations));
1240
1304
  }
@@ -1258,16 +1322,13 @@ describe("subagent extension child mode", () => {
1258
1322
  const script = String.raw`
1259
1323
  import assert from "node:assert/strict";
1260
1324
  import registerFanoutChildSubagentExtension from "./src/extension/fanout-child.ts";
1261
- import { SUBAGENT_CHILD_ENV, SUBAGENT_FANOUT_CHILD_ENV } from "./src/runs/shared/pi-args.ts";
1262
- process.env[SUBAGENT_CHILD_ENV] = "1";
1263
- process.env[SUBAGENT_FANOUT_CHILD_ENV] = "1";
1264
1325
  let registeredTool;
1265
1326
  const fakePi = {
1266
1327
  events: { on() { return () => {}; }, emit() {} },
1267
1328
  registerTool(tool) { registeredTool = tool; },
1268
1329
  getSessionName() { return undefined; },
1269
1330
  };
1270
- registerFanoutChildSubagentExtension(fakePi);
1331
+ registerFanoutChildSubagentExtension(fakePi, { fanoutChild: true, depth: 1, waitTool: { enabled: true }, fast: false });
1271
1332
  if (!registeredTool) throw new Error("tool not registered");
1272
1333
  const ctx = {
1273
1334
  cwd: process.cwd(),
@@ -1304,4 +1365,4 @@ describe("subagent extension child mode", () => {
1304
1365
  { cwd: projectRoot, stdio: "pipe" },
1305
1366
  );
1306
1367
  });
1307
- });
1368
+ });
@@ -6,6 +6,7 @@ import * as path from "node:path";
6
6
  import { describe, it } from "node:test";
7
7
  import { ACTIVE_RUN_INDEX_DIR, readActiveRunToolCallIndex, releaseActiveRunIndex, updateActiveRunIndex } from "../../src/runs/background/active-run-index.ts";
8
8
  import { encodeIndexSegment, indexSegmentAliases, MAX_INDEX_SEGMENT_BYTES } from "../../src/runs/background/index-segment.ts";
9
+ import { readStatus } from "../../src/shared/utils.ts";
9
10
 
10
11
  describe("bounded index segments", () => {
11
12
  it("preserves legacy encoding for short values and hashes oversized opaque ids", () => {
@@ -93,4 +94,60 @@ describe("bounded index segments", () => {
93
94
  fs.rmSync(root, { recursive: true, force: true });
94
95
  }
95
96
  });
96
- });
97
+
98
+ it("readStatus safely handles oversized directory paths without throwing ENAMETOOLONG", () => {
99
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-read-status-long-path-"));
100
+ try {
101
+ const longSegment = "x".repeat(300);
102
+ const longDir = path.join(root, longSegment);
103
+ assert.doesNotThrow(() => {
104
+ const status = readStatus(longDir);
105
+ assert.equal(status, null);
106
+ });
107
+ } finally {
108
+ fs.rmSync(root, { recursive: true, force: true });
109
+ }
110
+ });
111
+
112
+ it("surfaces unrelated ENAMETOOLONG errors from status inspection", (t) => {
113
+ const error = new Error("unrelated status path too long") as NodeJS.ErrnoException;
114
+ error.code = "ENAMETOOLONG";
115
+ t.mock.method(fsDefault, "statSync", () => {
116
+ throw error;
117
+ });
118
+ syncBuiltinESMExports();
119
+ try {
120
+ assert.throws(() => readStatus(path.join(os.tmpdir(), "short-status-id")), (thrown) =>
121
+ thrown instanceof Error
122
+ && /Failed to inspect async status file/.test(thrown.message)
123
+ && (thrown as Error & { cause?: unknown }).cause === error);
124
+ } finally {
125
+ t.mock.restoreAll();
126
+ syncBuiltinESMExports();
127
+ }
128
+ });
129
+
130
+ it("surfaces ENAMETOOLONG errors from status reads", (t) => {
131
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-read-status-read-error-"));
132
+ const asyncDir = path.join(root, "short-status-id");
133
+ const statusPath = path.join(asyncDir, "status.json");
134
+ const error = new Error("unrelated status read path too long") as NodeJS.ErrnoException;
135
+ error.code = "ENAMETOOLONG";
136
+ try {
137
+ fs.mkdirSync(asyncDir);
138
+ fs.writeFileSync(statusPath, "{}", "utf-8");
139
+ t.mock.method(fsDefault, "readFileSync", () => {
140
+ throw error;
141
+ });
142
+ syncBuiltinESMExports();
143
+ assert.throws(() => readStatus(asyncDir), (thrown) =>
144
+ thrown instanceof Error
145
+ && /Failed to read async status file/.test(thrown.message)
146
+ && (thrown as Error & { cause?: unknown }).cause === error);
147
+ } finally {
148
+ t.mock.restoreAll();
149
+ syncBuiltinESMExports();
150
+ fs.rmSync(root, { recursive: true, force: true });
151
+ }
152
+ });
153
+ });