@selesai/code 0.13.15 → 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 (473) hide show
  1. package/CHANGELOG.md +37 -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/capability-gateway/index.ts +12 -1
  65. package/dist/extensions/capability-gateway/integration.test.ts +23 -0
  66. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +18 -0
  67. package/dist/extensions/pi-hermes-memory/README.md +16 -12
  68. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +1 -1
  69. package/dist/extensions/pi-hermes-memory/package-lock.json +587 -62
  70. package/dist/extensions/pi-hermes-memory/package.json +4 -4
  71. package/dist/extensions/pi-hermes-memory/src/config.ts +32 -0
  72. package/dist/extensions/pi-hermes-memory/src/constants.ts +12 -2
  73. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +109 -51
  74. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +9 -3
  75. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +257 -98
  76. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +9 -2
  77. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +5 -0
  78. package/dist/extensions/pi-hermes-memory/src/index.ts +22 -2
  79. package/dist/extensions/pi-hermes-memory/src/paths.ts +1 -1
  80. package/dist/extensions/pi-hermes-memory/src/store/db.ts +6 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +58 -1
  82. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +60 -8
  83. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +169 -7
  84. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +7 -21
  85. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +134 -15
  86. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +4 -2
  87. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +24 -9
  88. package/dist/extensions/pi-hermes-memory/src/types.ts +11 -0
  89. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +42 -0
  90. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +159 -3
  91. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +452 -0
  92. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +39 -0
  93. package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +91 -0
  94. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +29 -0
  95. package/dist/extensions/pi-hermes-memory/tests/store/fts-query.test.ts +116 -0
  96. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +163 -0
  97. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +233 -0
  98. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +12 -0
  99. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +175 -0
  100. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +2 -2
  101. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +22 -0
  102. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +37 -1
  103. package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
  104. package/dist/extensions/pi-intercom/README.md +16 -2
  105. package/dist/extensions/pi-intercom/broker/spawn.test.ts +19 -2
  106. package/dist/extensions/pi-intercom/broker/spawn.ts +4 -4
  107. package/dist/extensions/pi-intercom/index.ts +1 -1
  108. package/dist/extensions/pi-intercom/intercom.integration.test.ts +121 -2
  109. package/dist/extensions/pi-intercom/package.json +1 -1
  110. package/dist/extensions/pi-intercom/project-agent.test.ts +1 -1
  111. package/dist/extensions/pi-intercom/project-agent.ts +1 -6
  112. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +7 -6
  113. package/dist/extensions/pi-rewind-hook/index.test.ts +7 -0
  114. package/dist/extensions/pi-subagents/.github/workflows/test.yml +36 -4
  115. package/dist/extensions/pi-subagents/CHANGELOG.md +120 -0
  116. package/dist/extensions/pi-subagents/README.md +3 -3
  117. package/dist/extensions/pi-subagents/agents/researcher.md +23 -13
  118. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -1
  119. package/dist/extensions/pi-subagents/agents/scout.md +1 -1
  120. package/dist/extensions/pi-subagents/docs/agents.md +32 -16
  121. package/dist/extensions/pi-subagents/docs/configuration.md +41 -15
  122. package/dist/extensions/pi-subagents/docs/extension-api.md +109 -6
  123. package/dist/extensions/pi-subagents/docs/missions.md +2 -0
  124. package/dist/extensions/pi-subagents/docs/models.md +58 -1
  125. package/dist/extensions/pi-subagents/docs/observability.md +50 -12
  126. package/dist/extensions/pi-subagents/docs/tool-reference.md +17 -10
  127. package/dist/extensions/pi-subagents/docs/watchdog.md +1 -1
  128. package/dist/extensions/pi-subagents/docs/workflows.md +26 -9
  129. package/dist/extensions/pi-subagents/package-lock.json +747 -177
  130. package/dist/extensions/pi-subagents/package.json +6 -4
  131. package/dist/extensions/pi-subagents/runner-server-preload.mjs +13 -0
  132. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +2 -1
  133. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  134. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +21 -4
  135. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
  136. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
  137. package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +63 -0
  138. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +61 -16
  139. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
  140. package/dist/extensions/pi-subagents/src/agents/agents.ts +8 -0
  141. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +0 -1
  142. package/dist/extensions/pi-subagents/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
  143. package/dist/extensions/pi-subagents/src/api/preflight.ts +7 -4
  144. package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -2
  145. package/dist/extensions/pi-subagents/src/api/workflow-resources.ts +6 -0
  146. package/dist/extensions/pi-subagents/src/extension/config.ts +4 -2
  147. package/dist/extensions/pi-subagents/src/extension/doctor.ts +2 -10
  148. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +9 -11
  149. package/dist/extensions/pi-subagents/src/extension/index.ts +99 -10
  150. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +13 -0
  151. package/dist/extensions/pi-subagents/src/extension/rpc.ts +7 -23
  152. package/dist/extensions/pi-subagents/src/extension/schemas.ts +11 -8
  153. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +25 -7
  154. package/dist/extensions/pi-subagents/src/integrations/pi-web-session-liveness.ts +73 -0
  155. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +228 -136
  156. package/dist/extensions/pi-subagents/src/intercom/supervisor-ui.ts +244 -0
  157. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +37 -16
  158. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +18 -18
  159. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +51 -25
  160. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +46 -3
  161. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -3
  162. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +9 -0
  163. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +10 -12
  164. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -15
  165. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +40 -29
  166. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +8 -0
  167. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +79 -247
  168. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +88 -12
  169. package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +6 -6
  170. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +24 -24
  171. package/dist/extensions/pi-subagents/src/runs/background/retained-nested-route-tracker.ts +96 -0
  172. package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +642 -0
  173. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +75 -6
  174. package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +163 -0
  175. package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +86 -0
  176. package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +31 -0
  177. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +18 -4
  178. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -1
  179. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +597 -1092
  180. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +3 -0
  181. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +4 -0
  182. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +20 -17
  183. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +296 -394
  184. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +4 -0
  185. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +3 -1
  186. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +9 -5
  187. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +630 -283
  188. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +8 -5
  189. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +78 -98
  190. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +3 -3
  191. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +16 -3
  192. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
  193. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +47 -47
  194. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -2
  195. package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +174 -0
  196. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +14 -3
  197. package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +319 -0
  198. package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +25 -0
  199. package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +126 -0
  200. package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +373 -0
  201. package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +530 -0
  202. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +2 -2
  203. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +2 -1
  204. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +16 -0
  205. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +11 -11
  206. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +30 -20
  207. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
  208. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +84 -15
  209. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +88 -12
  210. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +37 -53
  211. package/dist/extensions/pi-subagents/src/runs/shared/nested-path.ts +1 -1
  212. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +18 -7
  213. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +57 -12
  214. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +3 -4
  215. package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +0 -11
  216. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +4 -1
  217. package/dist/extensions/pi-subagents/src/runs/shared/readonly-drain-observation.ts +42 -0
  218. package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +69 -0
  219. package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +307 -0
  220. package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +8 -21
  221. package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +3 -30
  222. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +17 -4
  223. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +2 -0
  224. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +99 -386
  225. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +18 -62
  226. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +0 -14
  227. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +31 -9
  228. package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +190 -0
  229. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +506 -226
  230. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +1 -1
  231. package/dist/extensions/pi-subagents/src/shared/jsonl-writer.ts +11 -0
  232. package/dist/extensions/pi-subagents/src/shared/model-response-aliases.ts +13 -0
  233. package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +0 -6
  234. package/dist/extensions/pi-subagents/src/shared/types.ts +145 -89
  235. package/dist/extensions/pi-subagents/src/shared/utils.ts +13 -6
  236. package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +2 -0
  237. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +18 -13
  238. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +0 -6
  239. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +1 -1
  240. package/dist/extensions/pi-subagents/src/tui/fleet.ts +11 -6
  241. package/dist/extensions/pi-subagents/src/tui/render.ts +277 -46
  242. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +0 -1
  243. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +12 -12
  244. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
  245. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +116 -15
  246. package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +15 -18
  247. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +57 -8
  248. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +19 -19
  249. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +3 -3
  250. package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +97 -22
  251. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +3 -0
  252. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +336 -21
  253. package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +1181 -0
  254. package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +2240 -0
  255. package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +2268 -0
  256. package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +1512 -0
  257. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +231 -16
  258. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +4 -18
  259. package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +95 -0
  260. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +9 -15
  261. package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +374 -0
  262. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +20 -10
  263. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +18 -6
  264. package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +151 -0
  265. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +42 -1
  266. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +150 -25
  267. package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +354 -0
  268. package/dist/extensions/pi-subagents/test/integration/shared-cwd-terminal-evidence.test.ts +117 -0
  269. package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +3678 -0
  270. package/dist/extensions/pi-subagents/test/integration/{single-execution.test.ts → single-execution.part-2.test.ts} +500 -4207
  271. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -2
  272. package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +127 -0
  273. package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +365 -0
  274. package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +31 -0
  275. package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +74 -0
  276. package/dist/extensions/pi-subagents/test/smoke/pi085-extension.ts +16 -0
  277. package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +699 -0
  278. package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +418 -0
  279. package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +13 -0
  280. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +30 -76
  281. package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +84 -0
  282. package/dist/extensions/pi-subagents/test/support/result-publication-capacity-preload.mjs +70 -0
  283. package/dist/extensions/pi-subagents/test/support/runner-child-session-factory.ts +17 -0
  284. package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +390 -0
  285. package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +120 -0
  286. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +24 -3
  287. package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +87 -0
  288. package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +163 -0
  289. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +70 -31
  290. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +78 -3
  291. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +2 -8
  292. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +1 -1
  293. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +73 -66
  294. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +51 -1
  295. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +49 -1
  296. package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +103 -0
  297. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +3 -3
  298. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +9 -8
  299. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +10 -7
  300. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +48 -6
  301. package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +90 -0
  302. package/dist/extensions/pi-subagents/test/unit/{pi-args-permission-system.test.ts → child-tool-plan-permission-system.test.ts} +48 -86
  303. package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +33 -0
  304. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +18 -18
  305. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +9 -9
  306. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +8 -7
  307. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +225 -65
  308. package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +17 -22
  309. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +11 -1
  310. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +47 -3
  311. package/dist/extensions/pi-subagents/test/unit/foreign-workflow-steering.test.ts +74 -0
  312. package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +23 -29
  313. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +96 -4
  314. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +4 -4
  315. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +116 -55
  316. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +58 -1
  317. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +104 -3
  318. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
  319. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +31 -1
  320. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +102 -51
  321. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +196 -6
  322. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +181 -38
  323. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +62 -70
  324. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +58 -53
  325. package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +42 -0
  326. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +84 -0
  327. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +13 -5
  328. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +21 -5
  329. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +5 -4
  330. package/dist/extensions/pi-subagents/test/unit/permissions.test.ts +2 -6
  331. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +28 -2
  332. package/dist/extensions/pi-subagents/test/unit/pi-web-session-liveness.test.ts +209 -0
  333. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +38 -1
  334. package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +43 -0
  335. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +24 -2
  336. package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +174 -0
  337. package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +94 -0
  338. package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +1329 -0
  339. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +29 -80
  340. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +11 -8
  341. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +2 -2
  342. package/dist/extensions/pi-subagents/test/unit/retained-nested-route-tracker.test.ts +175 -0
  343. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +38 -12
  344. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +2 -5
  345. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +89 -1
  346. package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +2 -16
  347. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +27 -3
  348. package/dist/extensions/pi-subagents/test/unit/scheduled-supervisor-demand.test.ts +215 -0
  349. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +15 -11
  350. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +237 -1
  351. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -5
  352. package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +2 -2
  353. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +14 -1
  354. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +130 -739
  355. package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +682 -0
  356. package/dist/extensions/pi-subagents/test/unit/supervisor-ui.test.ts +137 -0
  357. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +1 -11
  358. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +23 -3
  359. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +8 -1
  360. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +19 -1
  361. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +72 -4
  362. package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +17 -3
  363. package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +34 -0
  364. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +12 -2
  365. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +46 -6
  366. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +3 -3
  367. package/dist/extensions/pi-subagents/test/unit/workflow-resources.test.ts +133 -1
  368. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +53 -2
  369. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +222 -14
  370. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +526 -85
  371. package/dist/extensions/pi-web-agent/src/backends/config.ts +1 -0
  372. package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +5 -3
  373. package/dist/extensions/pi-web-agent/src/search/tokenin.ts +7 -3
  374. package/dist/extensions/pi-web-agent/src/types.ts +1 -1
  375. package/dist/extensions/pi-zentui/README.md +17 -0
  376. package/dist/extensions/pi-zentui/assets/screenshots/footer-status-hyperlink.png +0 -0
  377. package/dist/extensions/pi-zentui/docs/configuration.md +71 -2
  378. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +11 -0
  379. package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +14 -0
  380. package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +29 -13
  381. package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +5 -4
  382. package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +11 -0
  383. package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +11 -10
  384. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +77 -3
  385. package/dist/extensions/pi-zentui/extensions/zentui/presets.ts +77 -0
  386. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +103 -2
  387. package/dist/extensions/pi-zentui/extensions/zentui/working-line-extension-segments.ts +115 -0
  388. package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +87 -5
  389. package/dist/extensions/pi-zentui/package-lock.json +277 -517
  390. package/dist/extensions/pi-zentui/package.json +7 -4
  391. package/dist/extensions/pi-zentui/test/config-presets.test.ts +238 -0
  392. package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +24 -1
  393. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +570 -2
  394. package/dist/extensions/pi-zentui/test/extension-status.test.ts +90 -0
  395. package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +243 -0
  396. package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
  397. package/dist/extensions/pi-zentui/test/presets.test.ts +121 -0
  398. package/dist/extensions/pi-zentui/test/settings-command.test.ts +205 -1
  399. package/dist/extensions/pi-zentui/test/working-line-extension-segments.test.ts +139 -0
  400. package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +237 -3
  401. package/dist/extensions/pi-zentui/test/working-line.test.ts +94 -2
  402. package/dist/index.d.ts +1 -1
  403. package/dist/main.js +4 -1
  404. package/dist/modes/interactive/chat-viewport.d.ts +19 -0
  405. package/dist/modes/interactive/chat-viewport.js +27 -0
  406. package/dist/modes/interactive/components/assistant-message.d.ts +6 -2
  407. package/dist/modes/interactive/components/assistant-message.js +30 -13
  408. package/dist/modes/interactive/components/custom-editor.d.ts +10 -1
  409. package/dist/modes/interactive/components/custom-editor.js +39 -1
  410. package/dist/modes/interactive/components/first-time-setup.js +1 -1
  411. package/dist/modes/interactive/components/index.d.ts +1 -1
  412. package/dist/modes/interactive/components/markdown-transform.d.ts +2 -0
  413. package/dist/modes/interactive/components/markdown-transform.js +18 -0
  414. package/dist/modes/interactive/components/model-selector.js +19 -17
  415. package/dist/modes/interactive/components/scoped-models-selector.js +21 -23
  416. package/dist/modes/interactive/components/settings-selector.d.ts +7 -1
  417. package/dist/modes/interactive/components/settings-selector.js +128 -20
  418. package/dist/modes/interactive/components/settings-submenu.d.ts +71 -0
  419. package/dist/modes/interactive/components/settings-submenu.js +164 -0
  420. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  421. package/dist/modes/interactive/components/status-indicator.js +10 -3
  422. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  423. package/dist/modes/interactive/components/thinking-selector.js +75 -18
  424. package/dist/modes/interactive/components/tool-execution.d.ts +21 -4
  425. package/dist/modes/interactive/components/tool-execution.js +35 -33
  426. package/dist/modes/interactive/components/trust-selector.js +2 -2
  427. package/dist/modes/interactive/interactive-mode.d.ts +13 -15
  428. package/dist/modes/interactive/interactive-mode.js +144 -80
  429. package/dist/modes/interactive/session-share.d.ts +15 -0
  430. package/dist/modes/interactive/session-share.js +183 -0
  431. package/dist/modes/interactive/theme/dark.json +2 -1
  432. package/dist/modes/interactive/theme/light.json +2 -1
  433. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
  434. package/dist/modes/interactive/theme/theme-controller.js +5 -0
  435. package/dist/modes/interactive/theme/theme-json.d.ts +83 -0
  436. package/dist/modes/interactive/theme/theme-json.js +129 -0
  437. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  438. package/dist/modes/interactive/theme/theme.d.ts +14 -5
  439. package/dist/modes/interactive/theme/theme.js +18 -119
  440. package/dist/modes/interactive/tui-renderer.d.ts +20 -0
  441. package/dist/modes/interactive/tui-renderer.js +65 -0
  442. package/dist/utils/exif-orientation.js +2 -3
  443. package/dist/utils/management-http.d.ts +24 -0
  444. package/dist/utils/management-http.js +53 -0
  445. package/dist/utils/text.d.ts +7 -0
  446. package/dist/utils/text.js +8 -0
  447. package/dist/utils/tools-manager.d.ts +1 -0
  448. package/dist/utils/tools-manager.js +43 -21
  449. package/docs/docs.json +0 -4
  450. package/package.json +4 -4
  451. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +0 -415
  452. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +0 -1059
  453. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +0 -116
  454. package/dist/extensions/pi-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
  455. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +0 -224
  456. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -6986
  457. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +0 -447
  458. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +0 -185
  459. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +0 -311
  460. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +0 -84
  461. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +0 -202
  462. package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +0 -101
  463. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +0 -2347
  464. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +0 -104
  465. package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +0 -49
  466. package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -1465
  467. package/dist/skills/imagegen-frontend-web/SKILL.md +0 -987
  468. package/dist/skills/implanger/SKILL.md +0 -70
  469. package/dist/skills/output/SKILL.md +0 -49
  470. package/dist/skills/planger/SKILL.md +0 -167
  471. package/dist/skills/workflow/SKILL.md +0 -95
  472. package/docs/workflows.md +0 -18
  473. /package/dist/bun/{register-bedrock.d.ts → runtime-setup.d.ts} +0 -0
@@ -6,6 +6,7 @@
6
6
  * observation channel, not a delivery acknowledgement.
7
7
  */
8
8
 
9
+ import { debuglog } from "node:util";
9
10
  import type { ExtensionAPI } from "@selesai/code";
10
11
  import { buildCompletionKey, markSeenWithTtl } from "./completion-dedupe.ts";
11
12
  import {
@@ -34,6 +35,7 @@ export interface SubagentNotifyChildOutput {
34
35
  export type SubagentNotifyWatchdogBlocker = Pick<ChildWatchdogWarningSummary, "summary" | "addressed" | "stalemate"> & { agent: string };
35
36
 
36
37
  export interface SubagentNotifyDetails {
38
+ workflowReceiptPath?: string;
37
39
  agent: string;
38
40
  status: "completed" | "failed" | "paused" | "stopped";
39
41
  source?: "async" | "foreground";
@@ -123,6 +125,7 @@ export interface RegisterSubagentNotifyOptions {
123
125
 
124
126
  export interface CompletionNotifier {
125
127
  deliver(result: CompletionNotification): Promise<boolean>;
128
+ hasPendingDelivery(): boolean;
126
129
  dispose(): void;
127
130
  }
128
131
 
@@ -188,11 +191,17 @@ function structuredOutputText(value: unknown): string | undefined {
188
191
  }
189
192
  }
190
193
 
194
+ function isDegenerateOutput(text: string): boolean {
195
+ return !text.trim() || text.trim() === "</think>";
196
+ }
197
+
191
198
  function childInlinePreview(child: CompletionChild): { preview?: string; truncated?: boolean; unavailableReason?: string } {
192
199
  const output = typeof child.output === "string" ? child.output : "";
193
200
  const outputReference = childSavedOutputPath(child);
194
201
  const referenceOnly = Boolean(outputReference && output.trim().startsWith("Output saved to:"));
195
- const raw = child.outputState === "absent" || referenceOnly ? structuredOutputText(child.structuredOutput) : output || structuredOutputText(child.structuredOutput);
202
+ const raw = child.outputState === "absent" || referenceOnly
203
+ ? structuredOutputText(child.structuredOutput)
204
+ : isDegenerateOutput(output) ? structuredOutputText(child.structuredOutput) ?? output : output;
196
205
  if (!raw?.trim()) {
197
206
  return { unavailableReason: referenceOnly ? "saved output is file-only" : "no safe inline output" };
198
207
  }
@@ -282,6 +291,7 @@ export function formatSingleCompletion(details: SubagentNotifyDetails): string {
282
291
  : undefined;
283
292
  return [
284
293
  `${taskKind} ${details.status}: **${details.agent}**${details.taskInfo ?? ""}`,
294
+ details.workflowReceiptPath ? `Workflow receipt: ${details.workflowReceiptPath}` : undefined,
285
295
  "",
286
296
  scheduleLine,
287
297
  scheduleLine ? "" : undefined,
@@ -302,7 +312,11 @@ export function parseSubagentNotifyContent(content: string): SubagentNotifyDetai
302
312
  const lines = content.split("\n");
303
313
  const match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused|stopped): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
304
314
  if (!match) return undefined;
305
- let body = lines.slice(2);
315
+ // Only the header slot before the blank separator can carry a receipt.
316
+ // Identical lines anywhere in model output remain preview text.
317
+ const receiptHeader = lines[1]?.startsWith("Workflow receipt: ") && lines[2] === "";
318
+ const workflowReceiptPath = receiptHeader ? lines[1]!.slice("Workflow receipt: ".length) : undefined;
319
+ let body = lines.slice(receiptHeader ? 3 : 2);
306
320
  // Restore the schedule origin so a re-rendered notice keeps its attribution and
307
321
  // does not fold the line into the result preview.
308
322
  const scheduleMatch = (body[0] ?? "").match(/^Scheduled run from \*\*(.+?)\*\* \(schedule (.+?)\)\.$/);
@@ -358,6 +372,7 @@ export function parseSubagentNotifyContent(content: string): SubagentNotifyDetai
358
372
  ...(match[1] === "Detached foreground task" ? { source: "foreground" as const } : {}),
359
373
  ...(match[4] ? { taskInfo: match[4] } : {}),
360
374
  ...(parsedScheduleOrigin ? { scheduleOrigin: parsedScheduleOrigin } : {}),
375
+ ...(workflowReceiptPath ? { workflowReceiptPath } : {}),
361
376
  resultPreview,
362
377
  ...(handoffPath ? { handoffPath } : {}),
363
378
  ...(workflowRunId ? { workflowRunId } : {}),
@@ -376,6 +391,7 @@ export function formatGroupedCompletion(details: SubagentNotifyDetails[]): strin
376
391
  if (!detail) continue;
377
392
  const sessionLine = formatSessionLine(detail);
378
393
  blocks.push(`${index + 1}. ${detail.agent}${detail.taskInfo ?? ""}${detail.scheduleOrigin ? ` — scheduled run from ${detail.scheduleOrigin.name ?? detail.scheduleOrigin.id} (schedule ${detail.scheduleOrigin.id})` : ""}`);
394
+ if (detail.workflowReceiptPath) blocks.push(`Workflow receipt: ${detail.workflowReceiptPath}`);
379
395
  blocks.push(formatResultPreview(detail));
380
396
  blocks.push(...formatWatchdogBlockerLines(detail));
381
397
  if (detail.handoffPath) blocks.push(`Parallel handoff: ${detail.handoffPath}`);
@@ -386,7 +402,31 @@ export function formatGroupedCompletion(details: SubagentNotifyDetails[]): strin
386
402
  return blocks.join("\n").trimEnd();
387
403
  }
388
404
 
405
+ const notificationDebug = debuglog("pi-subagents-notify");
406
+ type TraceIdentity = Pick<CompletionNotification, "id" | "runId" | "source">;
407
+ type NotificationReason = "disposed" | "missing_session" | "foreground_session_mismatch" | "not_owned"
408
+ | "intercom_delivered" | "deduped_ttl" | "deduped_pending" | "batch_deferred"
409
+ | "emit_foreground_session_mismatch" | "emit_not_owned" | "send_accepted" | "send_failed" | "dispose_pending";
410
+
411
+ // Slice before sanitizing: diagnostic work and each identity are bounded even for
412
+ // malformed result metadata. Never include task/output, paths, or error bodies.
413
+ function traceIdentity(result: TraceIdentity): TraceIdentity {
414
+ const identity = (value: unknown) => typeof value === "string"
415
+ ? value.slice(0, 128).replace(/[^a-zA-Z0-9_.:-]/g, "_") : undefined;
416
+ return { id: identity(result.id), runId: identity(result.runId), source: result.source === "foreground" ? "foreground" : "async" };
417
+ }
418
+
419
+ function traceNotification(reason: NotificationReason, result?: TraceIdentity): void {
420
+ if (!notificationDebug.enabled) return;
421
+ try {
422
+ notificationDebug("%s", JSON.stringify({ reason, ...(result ? traceIdentity(result) : {}) }));
423
+ } catch {
424
+ // Diagnostics must never change delivery acknowledgement.
425
+ }
426
+ }
427
+
389
428
  interface PendingCompletion {
429
+ trace?: TraceIdentity;
390
430
  key: string;
391
431
  details: SubagentNotifyDetails;
392
432
  sessionId: string;
@@ -447,6 +487,10 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
447
487
  ? result.parallelHandoff as { path?: unknown }
448
488
  : undefined;
449
489
  const handoffPath = typeof parallelHandoff?.path === "string" ? parallelHandoff.path : undefined;
490
+ const receipt = result.workflowReceipt;
491
+ const receiptPath = receipt && typeof receipt === "object" && !Array.isArray(receipt)
492
+ ? (receipt as Record<string, unknown>).path : undefined;
493
+ const workflowReceiptPath = typeof receiptPath === "string" && receiptPath ? receiptPath : undefined;
450
494
  const rawRunId = typeof result.runId === "string" ? result.runId : typeof result.id === "string" ? result.id : undefined;
451
495
  const workflowRunId = (result.mode === "workflow" || agent === "workflow") && rawRunId ? rawRunId : undefined;
452
496
  const directChild = !workflowRunId && result.results?.length === 1 ? result.results[0]! : undefined;
@@ -455,10 +499,17 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
455
499
  : undefined;
456
500
  const directSummary = summary.trim();
457
501
  const directAgent = typeof directChild?.agent === "string" ? directChild.agent : agent;
502
+ const directSummaryBody = directAgent && summary.trimStart().startsWith(`${directAgent}:\n`)
503
+ ? summary.trimStart().slice(directAgent.length + 2)
504
+ : directSummary;
505
+ const directDegenerateSummary = directChild
506
+ && isDegenerateOutput(typeof directChild.output === "string" ? directChild.output : "")
507
+ && isDegenerateOutput(directSummaryBody)
508
+ && structuredOutputText(directChild.structuredOutput) !== undefined;
458
509
  const directNoOutputSummary = directChild && (!directSummary
459
510
  || directSummary === "(no output)"
460
511
  || (directAgent && directSummary === `${directAgent}:\n(no output)`));
461
- const resultPreview = directStructuredPreview && directNoOutputSummary
512
+ const resultPreview = directStructuredPreview && (directNoOutputSummary || directDegenerateSummary)
462
513
  ? `Structured output:\n${directStructuredPreview}`
463
514
  : summary;
464
515
  const childRuns = result.results?.flatMap((child) => {
@@ -516,6 +567,7 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
516
567
  agent,
517
568
  status,
518
569
  ...(scheduleOrigin ? { scheduleOrigin } : {}),
570
+ ...(workflowReceiptPath ? { workflowReceiptPath } : {}),
519
571
  ...(result.source ? { source: result.source } : {}),
520
572
  ...(taskInfo ? { taskInfo } : {}),
521
573
  resultPreview,
@@ -547,8 +599,9 @@ export default function registerSubagentNotify(
547
599
  && typeof completionOwnerId === "string"
548
600
  && completionOwnerId === state.completionOwnerId);
549
601
 
550
- const settle = (items: PendingCompletion[], accepted: boolean) => {
602
+ const settle = (items: PendingCompletion[], accepted: boolean, reason?: NotificationReason) => {
551
603
  for (const item of items) {
604
+ if (reason) traceNotification(reason, item.trace);
552
605
  pending.delete(item.key);
553
606
  if (accepted) markSeenWithTtl(seen, item.key, now(), ttlMs);
554
607
  item.resolve(accepted);
@@ -561,10 +614,12 @@ export default function registerSubagentNotify(
561
614
  const owned = item.details.source === "foreground"
562
615
  ? item.sessionId === state.currentSessionId
563
616
  : ownsResult(item.sessionId, item.completionOwnerId);
617
+ if (!owned) traceNotification(item.details.source === "foreground" ? "emit_foreground_session_mismatch" : "emit_not_owned", item.trace);
564
618
  (owned ? accepted : rejected).push(item);
565
619
  }
566
620
  settle(rejected, false);
567
- settle(accepted, sendCompletion(pi, accepted));
621
+ const sent = sendCompletion(pi, accepted);
622
+ settle(accepted, sent, sent ? "send_accepted" : "send_failed");
568
623
  };
569
624
  const getBatcher = (result: CompletionNotification) => {
570
625
  const key = completionBatchKey(result);
@@ -582,17 +637,35 @@ export default function registerSubagentNotify(
582
637
  };
583
638
 
584
639
  const deliver = (result: CompletionNotification): Promise<boolean> => {
585
- if (disposed || typeof result.sessionId !== "string") return Promise.resolve(false);
640
+ if (disposed || typeof result.sessionId !== "string") {
641
+ traceNotification(disposed ? "disposed" : "missing_session", result);
642
+ return Promise.resolve(false);
643
+ }
586
644
  if (result.source === "foreground") {
587
- if (result.sessionId !== state.currentSessionId) return Promise.resolve(false);
588
- } else if (!ownsResult(result.sessionId, result.completionOwnerId)) return Promise.resolve(false);
589
- if (result.intercomDelivered === true) return Promise.resolve(true);
645
+ if (result.sessionId !== state.currentSessionId) {
646
+ traceNotification("foreground_session_mismatch", result);
647
+ return Promise.resolve(false);
648
+ }
649
+ } else if (!ownsResult(result.sessionId, result.completionOwnerId)) {
650
+ traceNotification("not_owned", result);
651
+ return Promise.resolve(false);
652
+ }
653
+ if (result.intercomDelivered === true) {
654
+ traceNotification("intercom_delivered", result);
655
+ return Promise.resolve(true);
656
+ }
590
657
  const key = buildCompletionKey(result, "notify");
591
658
  const seenAt = seen.get(key);
592
- if (seenAt !== undefined && now() - seenAt <= ttlMs) return Promise.resolve(true);
659
+ if (seenAt !== undefined && now() - seenAt <= ttlMs) {
660
+ traceNotification("deduped_ttl", result);
661
+ return Promise.resolve(true);
662
+ }
593
663
  if (seenAt !== undefined) seen.delete(key);
594
664
  const inFlight = pending.get(key);
595
- if (inFlight) return inFlight;
665
+ if (inFlight) {
666
+ traceNotification("deduped_pending", result);
667
+ return inFlight;
668
+ }
596
669
  const details = buildCompletionDetails(result);
597
670
  let resolve!: (accepted: boolean) => void;
598
671
  const completion = new Promise<boolean>((settleCompletion) => { resolve = settleCompletion; });
@@ -605,6 +678,7 @@ export default function registerSubagentNotify(
605
678
  triggerTurn: result.triggerTurn !== false,
606
679
  resolve,
607
680
  };
681
+ if (notificationDebug.enabled) item.trace = traceIdentity(result);
608
682
  if (details.source === "foreground") {
609
683
  emit([item]);
610
684
  return completion;
@@ -615,6 +689,7 @@ export default function registerSubagentNotify(
615
689
  emit([item]);
616
690
  return completion;
617
691
  }
692
+ if (batchConfig.enabled) traceNotification("batch_deferred", item.trace);
618
693
  batcher.push(item);
619
694
  return completion;
620
695
  };
@@ -628,10 +703,11 @@ export default function registerSubagentNotify(
628
703
 
629
704
  return {
630
705
  deliver,
706
+ hasPendingDelivery: () => pending.size > 0,
631
707
  dispose() {
632
708
  if (disposed) return;
633
709
  disposed = true;
634
- for (const batcher of batchers.values()) settle(batcher.dispose(), false);
710
+ for (const batcher of batchers.values()) settle(batcher.dispose(), false, "dispose_pending");
635
711
  batchers.clear();
636
712
  for (const unsubscribe of [unsubscribeAsync, unsubscribeForeground]) {
637
713
  try {
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import type { ProcessTreeTerminalV1 } from "../../shared/types.ts";
2
+ import type { ProcessTreeTerminal } from "../../shared/types.ts";
3
3
 
4
4
  const DEFAULT_TERM_GRACE_MS = 3000;
5
5
  const DEFAULT_KILL_VERIFY_MS = 1000;
@@ -52,25 +52,25 @@ async function waitUntilGroupTerminal(
52
52
  }
53
53
  }
54
54
 
55
- function observed(processGroupId: number): ProcessTreeTerminalV1 {
55
+ function observed(processGroupId: number): ProcessTreeTerminal {
56
56
  return { state: "observed", mechanism: "posix-process-group", processGroupId, verifiedAt: Date.now() };
57
57
  }
58
58
 
59
59
  /** Owns one writer process group and arbitrates its cleanup exactly once. */
60
60
  export interface OwnedProcessTreeController {
61
- terminate(): Promise<ProcessTreeTerminalV1>;
62
- finishAfterWriterClose(): Promise<ProcessTreeTerminalV1>;
61
+ terminate(): Promise<ProcessTreeTerminal>;
62
+ finishAfterWriterClose(): Promise<ProcessTreeTerminal>;
63
63
  }
64
64
 
65
65
  export function createOwnedProcessTreeController(
66
66
  pid: number,
67
67
  options: { termGraceMs?: number; killVerifyMs?: number } = {},
68
68
  ): OwnedProcessTreeController {
69
- let termination: Promise<ProcessTreeTerminalV1> | undefined;
69
+ let termination: Promise<ProcessTreeTerminal> | undefined;
70
70
  const posixGroupOwned = process.platform !== "win32";
71
71
  const target = posixGroupOwned ? -pid : pid;
72
72
 
73
- const terminate = (): Promise<ProcessTreeTerminalV1> => {
73
+ const terminate = (): Promise<ProcessTreeTerminal> => {
74
74
  if (termination) return termination;
75
75
  termination = (async () => {
76
76
  if (!posixGroupOwned) {
@@ -4,10 +4,10 @@ import { writeAtomicJson, writePrivateAtomicJson } from "../../shared/atomic-jso
4
4
  import {
5
5
  SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
6
6
  type AsyncStatus,
7
- type CanonicalSessionTerminalV1,
8
- type ProcessInstanceExitV1,
7
+ type CanonicalSessionTerminal,
8
+ type ProcessInstanceExit,
9
9
  type ProcessTerminalReason,
10
- type ProcessTerminalV1,
10
+ type ProcessTerminal,
11
11
  } from "../../shared/types.ts";
12
12
  import { canonicalSessionId, inspectSessionLease } from "../shared/session-lease.ts";
13
13
  import { releaseActiveRunIndex } from "./active-run-index.ts";
@@ -16,7 +16,7 @@ export interface ProcessTerminalCandidate {
16
16
  version: 1;
17
17
  runId: string;
18
18
  runnerProcessInstanceId: string;
19
- writers: Record<string, ProcessInstanceExitV1[]>;
19
+ writers: Record<string, ProcessInstanceExit[]>;
20
20
  expectedWriters?: Record<string, number>;
21
21
  sessionFile?: string;
22
22
  revivalLeaseToken?: string;
@@ -34,7 +34,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
34
34
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
35
35
  }
36
36
 
37
- function validProcessInstance(value: unknown, kind?: "runner" | "pi-writer"): value is ProcessInstanceExitV1 {
37
+ function validProcessInstance(value: unknown, kind?: "runner" | "pi-writer"): value is ProcessInstanceExit {
38
38
  if (!isRecord(value)) return false;
39
39
  if (typeof value.processInstanceId !== "string" || value.processInstanceId.length === 0) return false;
40
40
  if (kind ? value.kind !== kind : (value.kind !== "runner" && value.kind !== "pi-writer")) return false;
@@ -56,7 +56,7 @@ function validProcessInstance(value: unknown, kind?: "runner" | "pi-writer"): va
56
56
  && (value.processTree.diagnostic === undefined || typeof value.processTree.diagnostic === "string");
57
57
  }
58
58
 
59
- function validInstance(value: unknown): value is ProcessInstanceExitV1 {
59
+ function validInstance(value: unknown): value is ProcessInstanceExit {
60
60
  return validProcessInstance(value, "pi-writer");
61
61
  }
62
62
 
@@ -78,7 +78,7 @@ export function readProcessTerminalCandidate(asyncDir: string): ProcessTerminalC
78
78
  if (!isRecord(raw) || raw.version !== 1 || typeof raw.runId !== "string" || typeof raw.runnerProcessInstanceId !== "string" || !isRecord(raw.writers)) {
79
79
  throw new Error(`Invalid process-terminal candidate in '${asyncDir}'.`);
80
80
  }
81
- const writers: Record<string, ProcessInstanceExitV1[]> = {};
81
+ const writers: Record<string, ProcessInstanceExit[]> = {};
82
82
  for (const [index, entries] of Object.entries(raw.writers)) {
83
83
  if (!Array.isArray(entries) || !entries.every(validInstance)) throw new Error(`Invalid writer process records for child '${index}'.`);
84
84
  writers[index] = entries;
@@ -115,7 +115,7 @@ export function writeProcessTerminalCandidate(asyncDir: string, candidate: Proce
115
115
  writePrivateAtomicJson(processTerminalCandidatePath(asyncDir), candidate);
116
116
  }
117
117
 
118
- /** Establish ownership before authorizing a runner to launch any child process. */
118
+ /** Establish ownership before authorizing a runner to start any child session. */
119
119
  export function initializeProcessTerminal(asyncDir: string, runId: string, runnerProcessInstanceId: string): void {
120
120
  writeProcessTerminalCandidate(asyncDir, {
121
121
  version: 1,
@@ -137,7 +137,7 @@ export function markProcessTerminalCandidateLeaseRelease(asyncDir: string, token
137
137
  writeProcessTerminalCandidate(asyncDir, { ...candidate, revivalLeaseReleaseAcknowledged: acknowledged });
138
138
  }
139
139
 
140
- function unknownProof(runId: string, runnerProcessInstanceId: string, reason: ProcessTerminalReason, diagnostic?: string): ProcessTerminalV1 {
140
+ function unknownProof(runId: string, runnerProcessInstanceId: string, reason: ProcessTerminalReason, diagnostic?: string): ProcessTerminal {
141
141
  return { version: 1, state: "unknown", runId, runnerProcessInstanceId, reason, ...(diagnostic ? { diagnostic } : {}) };
142
142
  }
143
143
 
@@ -147,7 +147,7 @@ function resumeDisposition(state: string | undefined, sessionFile: string | unde
147
147
  return sessionFile && fs.existsSync(sessionFile) ? "resumable" : "unavailable";
148
148
  }
149
149
 
150
- function sessionProjection(candidate: ProcessTerminalCandidate, lease: ReturnType<typeof inspectSessionLease>): CanonicalSessionTerminalV1 | undefined {
150
+ function sessionProjection(candidate: ProcessTerminalCandidate, lease: ReturnType<typeof inspectSessionLease>): CanonicalSessionTerminal | undefined {
151
151
  if (!candidate.sessionFile || lease.state !== "free") return undefined;
152
152
  if (candidate.revivalLeaseToken && candidate.revivalLeaseReleaseAcknowledged !== true) return undefined;
153
153
  return {
@@ -158,7 +158,7 @@ function sessionProjection(candidate: ProcessTerminalCandidate, lease: ReturnTyp
158
158
  };
159
159
  }
160
160
 
161
- function validateProof(raw: unknown, asyncDir: string, fallback?: { runId?: string; runnerProcessInstanceId?: string }): raw is ProcessTerminalV1 {
161
+ function validateProof(raw: unknown, asyncDir: string, fallback?: { runId?: string; runnerProcessInstanceId?: string }): raw is ProcessTerminal {
162
162
  if (!isRecord(raw) || raw.version !== 1 || !["pending", "observed", "unknown", "not-started"].includes(String(raw.state)) || typeof raw.runId !== "string" || !raw.runId || typeof raw.runnerProcessInstanceId !== "string" || !raw.runnerProcessInstanceId) {
163
163
  throw new Error(`Invalid process-terminal proof in '${asyncDir}'.`);
164
164
  }
@@ -177,21 +177,21 @@ function validateProof(raw: unknown, asyncDir: string, fallback?: { runId?: stri
177
177
  return true;
178
178
  }
179
179
 
180
- export function sanitizeProcessTerminal(value: unknown, fallback: { runId?: string; runnerProcessInstanceId?: string }, label = "status"): ProcessTerminalV1 | undefined {
180
+ export function sanitizeProcessTerminal(value: unknown, fallback: { runId?: string; runnerProcessInstanceId?: string }, label = "status"): ProcessTerminal | undefined {
181
181
  if (value === undefined) return undefined;
182
182
  try {
183
183
  validateProof(value, label, fallback);
184
- return value as ProcessTerminalV1;
184
+ return value as ProcessTerminal;
185
185
  } catch (error) {
186
186
  return unknownProof(fallback.runId ?? label, fallback.runnerProcessInstanceId ?? "unknown", "proof-write-failed", errorMessage(error));
187
187
  }
188
188
  }
189
189
 
190
- export function readProcessTerminal(asyncDir: string, fallback?: { runId?: string; runnerProcessInstanceId?: string }): ProcessTerminalV1 | undefined {
190
+ export function readProcessTerminal(asyncDir: string, fallback?: { runId?: string; runnerProcessInstanceId?: string }): ProcessTerminal | undefined {
191
191
  try {
192
192
  const raw = JSON.parse(fs.readFileSync(processTerminalPath(asyncDir), "utf-8")) as unknown;
193
193
  validateProof(raw, asyncDir, fallback);
194
- return raw as ProcessTerminalV1;
194
+ return raw as ProcessTerminal;
195
195
  } catch (error) {
196
196
  if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
197
197
  return unknownProof(fallback?.runId ?? path.basename(asyncDir), fallback?.runnerProcessInstanceId ?? "unknown", "proof-write-failed", errorMessage(error));
@@ -199,12 +199,12 @@ export function readProcessTerminal(asyncDir: string, fallback?: { runId?: strin
199
199
  }
200
200
 
201
201
  function stepProcessTerminalProof(
202
- proof: ProcessTerminalV1,
202
+ proof: ProcessTerminal,
203
203
  childIndex: number,
204
- state: ProcessTerminalV1["state"],
205
- records: ProcessInstanceExitV1[],
206
- resumeDispositionValue: ProcessTerminalV1["resumeDisposition"],
207
- ): ProcessTerminalV1 {
204
+ state: ProcessTerminal["state"],
205
+ records: ProcessInstanceExit[],
206
+ resumeDispositionValue: ProcessTerminal["resumeDisposition"],
207
+ ): ProcessTerminal {
208
208
  const base = {
209
209
  version: 1 as const,
210
210
  runId: proof.runId,
@@ -221,7 +221,7 @@ function stepProcessTerminalProof(
221
221
  return { ...base, state };
222
222
  }
223
223
 
224
- function overlayStatus(asyncDir: string, proof: ProcessTerminalV1, candidate?: ProcessTerminalCandidate): void {
224
+ function overlayStatus(asyncDir: string, proof: ProcessTerminal, candidate?: ProcessTerminalCandidate): void {
225
225
  const statusPath = path.join(asyncDir, "status.json");
226
226
  try {
227
227
  const status = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatus;
@@ -244,13 +244,13 @@ export function finalizeProcessTerminal(
244
244
  asyncDir: string,
245
245
  runId: string,
246
246
  runnerClose: RunnerCloseObservation,
247
- ): ProcessTerminalV1 {
247
+ ): ProcessTerminal {
248
248
  const existing = readProcessTerminal(asyncDir, { runId, runnerProcessInstanceId: runnerClose.processInstanceId });
249
249
  if (existing && fs.existsSync(processTerminalPath(asyncDir))) {
250
250
  if (existing.state === "observed" && existing.runId === runId && existing.runnerProcessInstanceId === runnerClose.processInstanceId) return existing;
251
251
  if (existing.state === "unknown") return existing;
252
252
  }
253
- let proof: ProcessTerminalV1;
253
+ let proof: ProcessTerminal;
254
254
  let candidateForOverlay: ProcessTerminalCandidate | undefined;
255
255
  try {
256
256
  const candidate = readProcessTerminalCandidate(asyncDir);
@@ -279,7 +279,7 @@ export function finalizeProcessTerminal(
279
279
  } else if (allWriters.some((writer) => writer.kind === "pi-writer" && writer.processTree.state !== "observed")) {
280
280
  proof = unknownProof(runId, runnerClose.processInstanceId, "process-tree-unverified");
281
281
  } else {
282
- const runner: ProcessInstanceExitV1 = { kind: "runner", ...runnerClose };
282
+ const runner: ProcessInstanceExit = { kind: "runner", ...runnerClose };
283
283
  const canonicalSession = session && sessionProjection(candidate, session);
284
284
  proof = {
285
285
  version: 1,
@@ -0,0 +1,96 @@
1
+ import * as fs from "node:fs";
2
+ import type { SubagentState } from "../../shared/types.ts";
3
+ import { shouldUseNativeFsWatch } from "../../shared/watch-strategy.ts";
4
+ import { hasLiveNestedDescendants, projectNestedEvents } from "../shared/nested-events.ts";
5
+
6
+ interface RetainedNestedRouteTrackerOptions {
7
+ pollIntervalMs?: number;
8
+ platform?: NodeJS.Platform;
9
+ watch?: typeof fs.watch;
10
+ }
11
+
12
+ const DEFAULT_POLL_INTERVAL_MS = 5000;
13
+ const REFRESH_DEBOUNCE_MS = 25;
14
+
15
+ export function createRetainedNestedRouteTracker(
16
+ state: Pick<SubagentState, "retainedForegroundNestedRoutes">,
17
+ options: RetainedNestedRouteTrackerOptions = {},
18
+ ): { track: (rootRunId: string) => void; clear: () => void } {
19
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
20
+ const watch = options.watch ?? fs.watch;
21
+ const watchers = new Map<string, fs.FSWatcher>();
22
+ const refreshTimers = new Map<string, ReturnType<typeof setTimeout>>();
23
+ let poller: ReturnType<typeof setInterval> | undefined;
24
+
25
+ const close = (rootRunId: string): void => {
26
+ watchers.get(rootRunId)?.close();
27
+ watchers.delete(rootRunId);
28
+ const timer = refreshTimers.get(rootRunId);
29
+ if (timer) clearTimeout(timer);
30
+ refreshTimers.delete(rootRunId);
31
+ };
32
+
33
+ const refresh = (rootRunId: string): void => {
34
+ const retained = state.retainedForegroundNestedRoutes?.get(rootRunId);
35
+ if (!retained) {
36
+ close(rootRunId);
37
+ return;
38
+ }
39
+ try {
40
+ if (hasLiveNestedDescendants(projectNestedEvents(retained).children)) return;
41
+ state.retainedForegroundNestedRoutes?.delete(rootRunId);
42
+ close(rootRunId);
43
+ } catch (error) {
44
+ console.error(`Failed to refresh retained nested descendants for foreground run '${rootRunId}':`, error);
45
+ }
46
+ };
47
+
48
+ const scheduleRefresh = (rootRunId: string): void => {
49
+ if (refreshTimers.has(rootRunId)) return;
50
+ const timer = setTimeout(() => {
51
+ refreshTimers.delete(rootRunId);
52
+ refresh(rootRunId);
53
+ }, REFRESH_DEBOUNCE_MS);
54
+ timer.unref?.();
55
+ refreshTimers.set(rootRunId, timer);
56
+ };
57
+
58
+ const ensurePoller = (): void => {
59
+ if (poller || (state.retainedForegroundNestedRoutes?.size ?? 0) === 0) return;
60
+ poller = setInterval(() => {
61
+ for (const rootRunId of state.retainedForegroundNestedRoutes?.keys() ?? []) refresh(rootRunId);
62
+ if ((state.retainedForegroundNestedRoutes?.size ?? 0) > 0) return;
63
+ if (poller) clearInterval(poller);
64
+ poller = undefined;
65
+ }, pollIntervalMs);
66
+ poller.unref?.();
67
+ };
68
+
69
+ const track = (rootRunId: string): void => {
70
+ const retained = state.retainedForegroundNestedRoutes?.get(rootRunId);
71
+ if (!retained) return;
72
+ if (shouldUseNativeFsWatch("retained-nested-route-tracker", options.platform) && !watchers.has(rootRunId)) {
73
+ try {
74
+ const watcher = watch(retained.eventSink, () => scheduleRefresh(rootRunId));
75
+ watcher.on("error", () => close(rootRunId));
76
+ watcher.unref?.();
77
+ watchers.set(rootRunId, watcher);
78
+ } catch {
79
+ // The bounded safety poll below covers unsupported or failed watchers.
80
+ }
81
+ }
82
+ scheduleRefresh(rootRunId);
83
+ ensurePoller();
84
+ };
85
+
86
+ const clear = (): void => {
87
+ if (poller) clearInterval(poller);
88
+ poller = undefined;
89
+ for (const rootRunId of watchers.keys()) close(rootRunId);
90
+ for (const timer of refreshTimers.values()) clearTimeout(timer);
91
+ refreshTimers.clear();
92
+ state.retainedForegroundNestedRoutes?.clear();
93
+ };
94
+
95
+ return { track, clear };
96
+ }