@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
@@ -4,13 +4,13 @@ import * as os from "node:os";
4
4
  import * as path from "node:path";
5
5
  import { describe, it } from "node:test";
6
6
  import { acquireActiveAsyncCapacity } from "../../src/runs/background/active-async-capacity.ts";
7
- import { consumeSteerRequests, consumeSteerRequestsFromDir, consumeStopRequestPayload, stepSteerInboxDir, writeSteerAck } from "../../src/runs/background/control-channel.ts";
7
+ import { consumeSteerRequests, consumeStopRequestPayload } from "../../src/runs/background/control-channel.ts";
8
8
  import { listAsyncRuns } from "../../src/runs/background/async-status.ts";
9
9
  import { inspectSubagentStatus } from "../../src/runs/background/run-status.ts";
10
10
  import { createSubagentExecutor, steerWorkflowChildByKey } from "../../src/runs/foreground/subagent-executor.ts";
11
11
  import { resolveExternalCliRunnerStatus } from "../../src/runs/shared/external-cli-contract.ts";
12
- import { steerWorkflowForegroundTarget, workflowForegroundSteeringDir } from "../../src/runs/foreground/workflow-foreground-steering.ts";
13
- import { ASYNC_DIR, RESULTS_DIR, type SubagentState } from "../../src/shared/types.ts";
12
+ import { steerWorkflowForegroundTarget } from "../../src/runs/foreground/workflow-foreground-steering.ts";
13
+ import { ASYNC_DIR, RESULTS_DIR, type ForegroundChildControl, type ForegroundSteerInput, type SubagentState } from "../../src/shared/types.ts";
14
14
 
15
15
  function createState(): SubagentState {
16
16
  return {
@@ -68,24 +68,38 @@ function cleanup(runId: string, asyncDir: string): void {
68
68
  fs.rmSync(path.join(RESULTS_DIR, `${runId}.json`), { force: true });
69
69
  }
70
70
 
71
- function createWorkflowForegroundControl(state: SubagentState, workflowRunId: string, childRunId: string): string {
72
- const routeDir = workflowForegroundSteeringDir(ASYNC_DIR, workflowRunId, childRunId);
71
+ interface RecordedSteer {
72
+ message: string;
73
+ mode?: string;
74
+ }
75
+
76
+ function createWorkflowForegroundControl(
77
+ state: SubagentState,
78
+ workflowRunId: string,
79
+ childRunId: string,
80
+ options: { steer?: ForegroundChildControl["steer"] | null } = {},
81
+ ): RecordedSteer[] {
82
+ const steers: RecordedSteer[] = [];
83
+ const steer = options.steer === null
84
+ ? undefined
85
+ : options.steer ?? (async (input: ForegroundSteerInput) => {
86
+ steers.push({ message: input.message, ...(input.mode ? { mode: input.mode } : {}) });
87
+ return { state: input.mode === "follow_up" ? "queued" as const : "delivered" as const };
88
+ });
73
89
  state.workflowControllers ??= new Map();
74
90
  state.workflowControllers.set(workflowRunId, new AbortController());
75
91
  state.foregroundControls.set(childRunId, {
76
92
  runId: childRunId,
77
93
  parentWorkflowRunId: workflowRunId,
78
94
  workflowKey: childRunId,
79
- workflowSteeringDir: routeDir,
80
95
  sessionId: "session",
81
96
  mode: "single",
82
97
  startedAt: 100,
83
98
  updatedAt: 100,
84
- activeChildren: new Map([[0, { index: 0, agent: "worker", startedAt: 100, updatedAt: 100 }]]),
99
+ activeChildren: new Map([[0, { index: 0, agent: "worker", startedAt: 100, updatedAt: 100, ...(steer ? { steer } : {}) }]]),
85
100
  schedulingOwners: 1,
86
101
  });
87
- fs.mkdirSync(stepSteerInboxDir(routeDir, 0), { recursive: true });
88
- return routeDir;
102
+ return steers;
89
103
  }
90
104
 
91
105
  async function waitUntil<T>(read: () => T | undefined, timeoutMs = 5_000): Promise<T> {
@@ -200,22 +214,15 @@ describe("async interrupt action", () => {
200
214
  const workflowRunId = `workflow-child-${Date.now().toString(36)}`;
201
215
  const childRunId = `${workflowRunId}-child`;
202
216
  const asyncDir = createRunningAsync(state, workflowRunId, { track: false, mode: "workflow" });
203
- const routeDir = createWorkflowForegroundControl(state, workflowRunId, childRunId);
217
+ const steers = createWorkflowForegroundControl(state, workflowRunId, childRunId);
204
218
  try {
205
- const action = executorWithKill(state, () => true)
219
+ const result = await executorWithKill(state, () => true)
206
220
  .execute("steer", { action: "steer", id: childRunId, message: "Focus on the failing test." }, new AbortController().signal, undefined, ctx());
207
- const request = await waitUntil(() => {
208
- const inbox = stepSteerInboxDir(routeDir, 0);
209
- const entry = fs.existsSync(inbox) ? fs.readdirSync(inbox).find((name) => name.endsWith(".json")) : undefined;
210
- return entry ? JSON.parse(fs.readFileSync(path.join(inbox, entry), "utf-8")) as { id: string; message: string } : undefined;
211
- });
212
- writeSteerAck(routeDir, { requestId: request.id, index: 0, ts: Date.now(), state: "delivered", message: "accepted" });
213
- const result = await action;
214
221
 
215
222
  assert.equal(result.isError, undefined);
216
223
  assert.equal(result.details.steering?.state, "delivered");
217
224
  assert.equal(result.details.steering?.sourceRunId, childRunId);
218
- assert.equal(request.message, "Focus on the failing test.");
225
+ assert.deepEqual(steers, [{ message: "Focus on the failing test." }]);
219
226
  assert.match(text(result), /Message sent:\n```text\nFocus on the failing test\.\n```/);
220
227
  } finally {
221
228
  cleanup(workflowRunId, asyncDir);
@@ -227,16 +234,27 @@ describe("async interrupt action", () => {
227
234
  const workflowRunId = `workflow-key-foreground-${Date.now().toString(36)}`;
228
235
  const childRunId = `${workflowRunId}-writer`;
229
236
  const asyncDir = createRunningAsync(state, workflowRunId, { track: false, mode: "workflow" });
230
- const routeDir = createWorkflowForegroundControl(state, workflowRunId, childRunId);
237
+ const steers = createWorkflowForegroundControl(state, workflowRunId, childRunId);
231
238
  try {
232
- const action = steerWorkflowChildByKey({ state, workflowRunId, key: childRunId, message: "Focus on the contract.", options: { ackTimeoutMs: 500 } });
233
- const request = await waitUntil(() => {
234
- const inbox = stepSteerInboxDir(routeDir, 0);
235
- const entry = fs.readdirSync(inbox).find((name) => name.endsWith(".json"));
236
- return entry ? JSON.parse(fs.readFileSync(path.join(inbox, entry), "utf-8")) as { id: string } : undefined;
237
- });
238
- writeSteerAck(routeDir, { requestId: request.id, index: 0, ts: Date.now(), state: "delivered", message: "accepted" });
239
- assert.equal((await action).state, "delivered");
239
+ const action = await steerWorkflowChildByKey({ state, workflowRunId, key: childRunId, message: "Focus on the contract.", options: { ackTimeoutMs: 500 } });
240
+ assert.equal(action.state, "delivered");
241
+ assert.deepEqual(steers, [{ message: "Focus on the contract." }]);
242
+ } finally {
243
+ cleanup(workflowRunId, asyncDir);
244
+ }
245
+ });
246
+
247
+ it("retries a foreground workflow-key steer until the child session is running", async () => {
248
+ const state = createState();
249
+ const workflowRunId = `workflow-key-early-${Date.now().toString(36)}`;
250
+ const childRunId = `${workflowRunId}-writer`;
251
+ const asyncDir = createRunningAsync(state, workflowRunId, { track: false, mode: "workflow" });
252
+ let attempts = 0;
253
+ createWorkflowForegroundControl(state, workflowRunId, childRunId, { steer: async () => ++attempts < 3 ? { state: "failed" as const, reason: "Child session is not running yet." } : { state: "delivered" as const } });
254
+ try {
255
+ const action = await steerWorkflowChildByKey({ state, workflowRunId, key: childRunId, message: "Early.", options: { ackTimeoutMs: 500 } });
256
+ assert.equal(action.state, "delivered");
257
+ assert.equal(attempts, 3);
240
258
  } finally {
241
259
  cleanup(workflowRunId, asyncDir);
242
260
  }
@@ -342,74 +360,65 @@ describe("async interrupt action", () => {
342
360
  }
343
361
  });
344
362
 
345
- it("rejects a steer request when its workflow foreground route is already removed", async () => {
363
+ it("reports a failed steer when the child session rejects it", async () => {
346
364
  const state = createState();
347
- const workflowRunId = `workflow-missing-route-${Date.now().toString(36)}`;
365
+ const workflowRunId = `workflow-steer-failed-${Date.now().toString(36)}`;
348
366
  const childRunId = `${workflowRunId}-child`;
349
367
  const asyncDir = createRunningAsync(state, workflowRunId, { track: false, mode: "workflow" });
350
- const routeDir = createWorkflowForegroundControl(state, workflowRunId, childRunId);
368
+ createWorkflowForegroundControl(state, workflowRunId, childRunId, { steer: async () => ({ state: "failed", reason: "session is settling" }) });
351
369
  try {
352
370
  const control = state.foregroundControls.get(childRunId)!;
353
- fs.rmSync(routeDir, { recursive: true, force: true });
354
371
  const result = await steerWorkflowForegroundTarget({
355
372
  target: { control, workflowRunId, sourceRunId: childRunId },
356
373
  message: "Focus on the failing test.",
357
- ackTimeoutMs: 40,
358
374
  });
359
375
 
360
376
  assert.equal(result.isError, true);
361
- assert.match(text(result), /no live workflow steering route/);
362
- assert.equal(result.details.steering, undefined);
363
- assert.equal(fs.existsSync(routeDir), false);
377
+ assert.match(text(result), /Steering failed .*session is settling/);
378
+ assert.equal(result.details.steering?.state, "failed");
364
379
  } finally {
365
380
  cleanup(workflowRunId, asyncDir);
366
381
  }
367
382
  });
368
383
 
369
- it("rejects a steer request when its workflow foreground child inbox is missing", async () => {
384
+ it("rejects a steer request when the child session is not steerable", async () => {
370
385
  const state = createState();
371
- const workflowRunId = `workflow-missing-inbox-${Date.now().toString(36)}`;
386
+ const workflowRunId = `workflow-no-steer-${Date.now().toString(36)}`;
372
387
  const childRunId = `${workflowRunId}-child`;
373
388
  const asyncDir = createRunningAsync(state, workflowRunId, { track: false, mode: "workflow" });
374
- const routeDir = createWorkflowForegroundControl(state, workflowRunId, childRunId);
389
+ createWorkflowForegroundControl(state, workflowRunId, childRunId, { steer: null });
375
390
  try {
376
391
  const control = state.foregroundControls.get(childRunId)!;
377
- fs.rmSync(stepSteerInboxDir(routeDir, 0), { recursive: true, force: true });
378
392
  const result = await steerWorkflowForegroundTarget({
379
393
  target: { control, workflowRunId, sourceRunId: childRunId },
380
394
  message: "Focus on the failing test.",
381
- ackTimeoutMs: 40,
382
395
  });
383
396
 
384
397
  assert.equal(result.isError, true);
385
- assert.match(text(result), /no live workflow steering route/);
398
+ assert.match(text(result), /does not support steering/);
386
399
  assert.equal(result.details.steering, undefined);
387
- assert.equal(fs.existsSync(stepSteerInboxDir(routeDir, 0)), false);
388
400
  } finally {
389
401
  cleanup(workflowRunId, asyncDir);
390
402
  }
391
403
  });
392
404
 
393
- it("rejects a steer request when its workflow foreground route is removed during the final acknowledgment wait", async () => {
405
+ it("queues follow-up steering through the child session", async () => {
394
406
  const state = createState();
395
- const workflowRunId = `workflow-removed-route-${Date.now().toString(36)}`;
407
+ const workflowRunId = `workflow-follow-up-${Date.now().toString(36)}`;
396
408
  const childRunId = `${workflowRunId}-child`;
397
409
  const asyncDir = createRunningAsync(state, workflowRunId, { track: false, mode: "workflow" });
398
- const routeDir = createWorkflowForegroundControl(state, workflowRunId, childRunId);
410
+ const steers = createWorkflowForegroundControl(state, workflowRunId, childRunId);
399
411
  try {
400
412
  const control = state.foregroundControls.get(childRunId)!;
401
- const action = steerWorkflowForegroundTarget({
413
+ const result = await steerWorkflowForegroundTarget({
402
414
  target: { control, workflowRunId, sourceRunId: childRunId },
403
- message: "Focus on the failing test.",
404
- ackTimeoutMs: 40,
415
+ message: "After this, update the docs.",
416
+ mode: "follow_up",
405
417
  });
406
- await waitUntil(() => fs.existsSync(stepSteerInboxDir(routeDir, 0)) ? true : undefined);
407
- fs.rmSync(routeDir, { recursive: true, force: true });
408
- const result = await action;
409
418
 
410
- assert.equal(result.isError, true);
411
- assert.match(text(result), /no live child session/);
412
- assert.equal(result.details.steering, undefined);
419
+ assert.equal(result.isError, undefined);
420
+ assert.equal(result.details.steering?.deliveryStatus, "queued");
421
+ assert.deepEqual(steers, [{ message: "After this, update the docs.", mode: "follow_up" }]);
413
422
  } finally {
414
423
  cleanup(workflowRunId, asyncDir);
415
424
  }
@@ -421,19 +430,17 @@ describe("async interrupt action", () => {
421
430
  const workflowRunId = `workflow-${target}-${Date.now().toString(36)}`;
422
431
  const childRunId = `${workflowRunId}-child`;
423
432
  const asyncDir = createRunningAsync(state, workflowRunId, { track: false, mode: "workflow" });
424
- const routeDir = createWorkflowForegroundControl(state, workflowRunId, childRunId);
433
+ const steers = createWorkflowForegroundControl(state, workflowRunId, childRunId);
425
434
  try {
426
- const controller = new AbortController();
427
- setTimeout(() => controller.abort(), 10);
428
435
  const params = target === "id"
429
436
  ? { action: "steer", id: workflowRunId, message: "Review the contract." }
430
437
  : { action: "steer", dir: asyncDir, message: "Review the contract." };
431
438
  const result = await executorWithKill(state, () => true)
432
- .execute("steer", params, controller.signal, undefined, ctx());
439
+ .execute("steer", params, new AbortController().signal, undefined, ctx());
433
440
 
434
441
  assert.equal(result.isError, undefined);
435
442
  assert.equal(result.details.steering?.sourceRunId, workflowRunId);
436
- assert.equal(consumeSteerRequestsFromDir(stepSteerInboxDir(routeDir, 0))[0]?.message, "Review the contract.");
443
+ assert.deepEqual(steers, [{ message: "Review the contract." }]);
437
444
  } finally {
438
445
  cleanup(workflowRunId, asyncDir);
439
446
  }
@@ -444,16 +451,16 @@ describe("async interrupt action", () => {
444
451
  const state = createState();
445
452
  const workflowRunId = `workflow-ambiguous-${Date.now().toString(36)}`;
446
453
  const asyncDir = createRunningAsync(state, workflowRunId, { track: false, mode: "workflow" });
447
- const firstRoute = createWorkflowForegroundControl(state, workflowRunId, `${workflowRunId}-one`);
448
- const secondRoute = createWorkflowForegroundControl(state, workflowRunId, `${workflowRunId}-two`);
454
+ const firstSteers = createWorkflowForegroundControl(state, workflowRunId, `${workflowRunId}-one`);
455
+ const secondSteers = createWorkflowForegroundControl(state, workflowRunId, `${workflowRunId}-two`);
449
456
  try {
450
457
  const result = await executorWithKill(state, () => true)
451
458
  .execute("steer", { action: "steer", id: workflowRunId, message: "Do not guess." }, new AbortController().signal, undefined, ctx());
452
459
 
453
460
  assert.equal(result.isError, true);
454
461
  assert.match(text(result), /2 live foreground children/);
455
- assert.equal(consumeSteerRequestsFromDir(stepSteerInboxDir(firstRoute, 0)).length, 0);
456
- assert.equal(consumeSteerRequestsFromDir(stepSteerInboxDir(secondRoute, 0)).length, 0);
462
+ assert.equal(firstSteers.length, 0);
463
+ assert.equal(secondSteers.length, 0);
457
464
  } finally {
458
465
  cleanup(workflowRunId, asyncDir);
459
466
  }
@@ -475,7 +482,7 @@ describe("async interrupt action", () => {
475
482
  .execute("steer", { action: "steer", dir: asyncDir, message: "Too late." }, new AbortController().signal, undefined, ctx());
476
483
 
477
484
  assert.equal(result.isError, true);
478
- assert.match(text(result), /no live foreground child/);
485
+ assert.match(text(result), /not running or queued/);
479
486
  assert.equal(fs.existsSync(path.join(asyncDir, "control", "steer-requests")), false);
480
487
  } finally {
481
488
  cleanup(workflowRunId, asyncDir);
@@ -1019,4 +1026,4 @@ describe("async interrupt action", () => {
1019
1026
  cleanup(runId, asyncDir);
1020
1027
  }
1021
1028
  });
1022
- });
1029
+ });
@@ -49,6 +49,56 @@ describe("async recovery descriptor", () => {
49
49
  }
50
50
  });
51
51
 
52
+ it("accepts a safe baseRef in persisted recovery descriptors", () => {
53
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-async-recovery-base-ref-"));
54
+ try {
55
+ fs.writeFileSync(path.join(root, "recovery-descriptor.json"), JSON.stringify({
56
+ version: 1,
57
+ baseRef: "@/foo",
58
+ runFanoutBudget: runFanoutBudget("run-base-ref"),
59
+ sourceRunId: "run-base-ref",
60
+ agent: "worker",
61
+ cwd: root,
62
+ systemPromptMode: "replace",
63
+ inheritGlobalContext: false,
64
+ inheritProjectContext: false,
65
+ inheritSkills: false,
66
+ outputMode: "inline",
67
+ maxSubagentDepth: 2,
68
+ share: false,
69
+ }), "utf-8");
70
+ assert.equal(readAsyncRecoveryDescriptor(root)?.baseRef, "@/foo");
71
+ } finally {
72
+ fs.rmSync(root, { recursive: true, force: true });
73
+ }
74
+ });
75
+
76
+ it("rejects unsafe baseRef values in persisted recovery descriptors", () => {
77
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-async-recovery-bad-base-ref-"));
78
+ try {
79
+ for (const baseRef of ["refs/heads/unsafe..ref", "a".repeat(40), "a".repeat(64)] as const) {
80
+ fs.writeFileSync(path.join(root, "recovery-descriptor.json"), JSON.stringify({
81
+ version: 1,
82
+ baseRef,
83
+ runFanoutBudget: runFanoutBudget("run-bad-base-ref"),
84
+ sourceRunId: "run-bad-base-ref",
85
+ agent: "worker",
86
+ cwd: root,
87
+ systemPromptMode: "replace",
88
+ inheritGlobalContext: false,
89
+ inheritProjectContext: false,
90
+ inheritSkills: false,
91
+ outputMode: "inline",
92
+ maxSubagentDepth: 2,
93
+ share: false,
94
+ }), "utf-8");
95
+ assert.throws(() => readAsyncRecoveryDescriptor(root), /baseRef must be a valid Git ref/);
96
+ }
97
+ } finally {
98
+ fs.rmSync(root, { recursive: true, force: true });
99
+ }
100
+ });
101
+
52
102
  it("defaults inheritGlobalContext from inheritProjectContext for descriptors from older versions", () => {
53
103
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-async-recovery-legacy-global-context-"));
54
104
  try {
@@ -185,4 +235,4 @@ describe("async recovery descriptor", () => {
185
235
  fs.rmSync(root, { recursive: true, force: true });
186
236
  }
187
237
  });
188
- });
238
+ });
@@ -287,6 +287,54 @@ describe("async retention cleanup", () => {
287
287
  }
288
288
  });
289
289
 
290
+ it("does not reclaim a live replacement owner after a reused pid identity changes", async () => {
291
+ const roots = makeRoots();
292
+ try {
293
+ const reusedPid = process.pid + 100_000;
294
+ const lockDir = path.join(roots.root, ".async-retention.lock");
295
+ const ownerPath = path.join(lockDir, "owner.json");
296
+ const replacementOwner = {
297
+ version: 1,
298
+ token: "replacement-token",
299
+ pid: reusedPid,
300
+ hostname: "test-host",
301
+ processStartIdentity: "replacement-process",
302
+ startedAt: NOW,
303
+ };
304
+ fs.mkdirSync(lockDir);
305
+ fs.writeFileSync(ownerPath, JSON.stringify({
306
+ version: 1,
307
+ token: "old-token",
308
+ pid: reusedPid,
309
+ hostname: "test-host",
310
+ processStartIdentity: "old-process",
311
+ startedAt: NOW,
312
+ }));
313
+
314
+ let currentIdentity = "old-process";
315
+ const options = {
316
+ ...cleanupOptions(roots),
317
+ hostname: "test-host",
318
+ processStartIdentity: "cleaner-process",
319
+ isProcessAlive: (pid: number) => pid === reusedPid || pid === process.pid,
320
+ getProcessStartIdentity: (pid: number) => pid === reusedPid ? currentIdentity : "cleaner-process",
321
+ };
322
+ const first = await cleanupAsyncRetention(options);
323
+ assert.equal(first.acquired, false);
324
+ assert.equal(first.skipped["lock-busy"], 1);
325
+
326
+ fs.writeFileSync(ownerPath, JSON.stringify(replacementOwner));
327
+ currentIdentity = "replacement-process";
328
+ const second = await cleanupAsyncRetention(options);
329
+
330
+ assert.equal(second.acquired, false);
331
+ assert.equal(second.skipped["lock-busy"], 1);
332
+ assert.deepEqual(JSON.parse(fs.readFileSync(ownerPath, "utf-8")), replacementOwner);
333
+ } finally {
334
+ fs.rmSync(roots.root, { recursive: true, force: true });
335
+ }
336
+ });
337
+
290
338
  it("limits result candidates to the cleanup batch", async () => {
291
339
  const roots = makeRoots();
292
340
  try {
@@ -632,4 +680,4 @@ describe("async retention cleanup", () => {
632
680
  fs.rmSync(roots.root, { recursive: true, force: true });
633
681
  }
634
682
  });
635
- });
683
+ });
@@ -0,0 +1,103 @@
1
+ import assert from "node:assert/strict";
2
+ import childProcess from "node:child_process";
3
+ import * as fs from "node:fs";
4
+ import { syncBuiltinESMExports } from "node:module";
5
+ import * as os from "node:os";
6
+ import * as path from "node:path";
7
+ import test from "node:test";
8
+ import { fileURLToPath } from "node:url";
9
+ import { SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV } from "../../src/shared/utils.ts";
10
+
11
+ test("executeAsyncSingle preloads supplemental server aliases before jiti, but not for complete hosts", async (t) => {
12
+ const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), "async-spawn-preload-")));
13
+ const host = path.join(root, "host");
14
+ const server = "@earendil-works/pi-server";
15
+ const expectedAliases: Record<string, string> = {};
16
+ const hostExports: Record<string, string[]> = {
17
+ "@selesai/code": ["."],
18
+ "@earendil-works/pi-agent-core": [".", "./node"],
19
+ "@earendil-works/chord": [".", "./context"],
20
+ "@earendil-works/pi-tui": ["."],
21
+ "@earendil-works/pi-ai": ["./compat", "./oauth", "./providers/all"],
22
+ "typebox": [".", "./compile", "./value"],
23
+ [server]: [".", "./unix"],
24
+ "@earendil-works/pi-client": ["./unix"],
25
+ };
26
+ // Use real package manifests/targets, as in host-peer-runtime-imports.test.ts.
27
+ function writeHostPackage(pkg: string) {
28
+ const dir = pkg === "@selesai/code" ? host : path.join(host, "node_modules", pkg);
29
+ fs.mkdirSync(dir, { recursive: true });
30
+ const exports = Object.fromEntries(hostExports[pkg]!.map(subpath => {
31
+ const target = `./${subpath === "." ? "index" : subpath.slice(2).replaceAll("/", "-")}.mjs`;
32
+ fs.writeFileSync(path.join(dir, target), "export {};\n");
33
+ expectedAliases[subpath === "." ? pkg : `${pkg}/${subpath.slice(2)}`] = path.join(dir, target);
34
+ if (pkg === "@earendil-works/pi-ai" && subpath === "./compat") expectedAliases[pkg] = path.join(dir, target);
35
+ return [subpath, target];
36
+ }));
37
+ fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ name: pkg, version: "0.85.0", exports }));
38
+ }
39
+ const originalArgv1 = process.argv[1];
40
+ try {
41
+ for (const pkg of Object.keys(hostExports)) {
42
+ if (pkg !== server) writeHostPackage(pkg);
43
+ }
44
+ for (const specifier of [server, `${server}/unix`]) {
45
+ expectedAliases[specifier] = fileURLToPath(import.meta.resolve(specifier));
46
+ }
47
+ // Production discovers the host from the Pi entrypoint at module load.
48
+ process.argv[1] = expectedAliases["@selesai/code"];
49
+ // helpers -> mock-pi -> child-session -> readonly evidence -> child hooks
50
+ // also loads async-execution. Set the host before importing that graph.
51
+ const { makeAgent } = await import("../support/helpers.ts");
52
+ const { executeAsyncSingle } = await import("../../src/runs/background/async-execution.ts");
53
+ const spawn = t.mock.method(childProcess, "spawn", () => {
54
+ // Stop at the only external I/O seam: no fake pid or detached lifecycle.
55
+ throw new Error("spawn boundary captured");
56
+ });
57
+ syncBuiltinESMExports();
58
+ for (const scenario of ["supplemental", "complete", "stable", "missing-stable"]) {
59
+ if (scenario === "complete") writeHostPackage(server);
60
+ if (scenario === "stable") {
61
+ fs.writeFileSync(path.join(host, "package.json"), JSON.stringify({ name: "@selesai/code", version: "0.85.1", exports: { ".": "./index.mjs" } }));
62
+ for (const pkg of [server, "@earendil-works/pi-client"]) fs.rmSync(path.join(host, "node_modules", pkg), { recursive: true });
63
+ for (const specifier of [server, `${server}/unix`, "@earendil-works/pi-client/unix"]) delete expectedAliases[specifier];
64
+ }
65
+ if (scenario === "missing-stable") fs.unlinkSync(expectedAliases["@earendil-works/pi-agent-core/node"]!);
66
+ const result = executeAsyncSingle(`spawn-preload-${scenario}`, {
67
+ agent: "worker", task: "Inspect launch wiring", agentConfig: makeAgent("worker"),
68
+ ctx: { pi: { events: { emit() {} } }, cwd: root, currentSessionId: "spawn-preload-session" },
69
+ artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
70
+ shareEnabled: false, sessionRoot: path.join(root, "sessions"), maxSubagentDepth: 1, acceptance: false,
71
+ });
72
+ assert.equal(result.isError, true);
73
+ if (scenario === "missing-stable") {
74
+ assert.match(result.content[0]!.text, /@earendil-works\/pi-agent-core\/node/);
75
+ assert.equal(spawn.mock.callCount(), 3);
76
+ continue;
77
+ }
78
+ assert.match(result.content[0]!.text, /spawn boundary captured/);
79
+ assert.equal(spawn.mock.callCount(), scenario === "supplemental" ? 1 : scenario === "complete" ? 2 : 3);
80
+ const [command, args, options] = spawn.mock.calls.at(-1)!.arguments;
81
+ assert.ok(path.isAbsolute(command));
82
+ assert.equal(options.env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV], host);
83
+ const actualAliases = JSON.parse(options.env.JITI_ALIAS) as Record<string, string>;
84
+ assert.deepEqual(Object.fromEntries(Object.entries(actualAliases).map(([key, target]) => [key, fs.realpathSync(target)])), expectedAliases);
85
+ const jitiIndex = scenario === "supplemental" ? 2 : 0;
86
+ if (scenario !== "supplemental") assert.ok(!args.includes("--import"));
87
+ else {
88
+ assert.equal(args[0], "--import");
89
+ assert.equal(args[1], new URL("../../runner-server-preload.mjs", import.meta.url).href);
90
+ assert.ok(fs.existsSync(fileURLToPath(args[1])));
91
+ }
92
+ assert.match(args[jitiIndex], /[/\\]jiti-cli\.mjs$/);
93
+ assert.match(args[jitiIndex + 1], /[/\\]subagent-runner\.ts$/);
94
+ assert.equal(args.length, jitiIndex + 3);
95
+ }
96
+ } finally {
97
+ t.mock.restoreAll();
98
+ syncBuiltinESMExports();
99
+ if (originalArgv1 === undefined) delete process.argv[1];
100
+ else process.argv[1] = originalArgv1;
101
+ fs.rmSync(root, { recursive: true, force: true });
102
+ }
103
+ });
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
- import type { AsyncJobState, HostStepNodeV1, WorkflowGraphSnapshot, WorkflowNodeStatus } from "../../src/shared/types.ts";
3
+ import type { AsyncJobState, HostStepNode, WorkflowGraphSnapshot, WorkflowNodeStatus } from "../../src/shared/types.ts";
4
4
  import {
5
5
  ASYNC_STATUS_SNAPSHOT_KIND,
6
6
  ASYNC_STATUS_SNAPSHOT_VERSION,
@@ -15,7 +15,7 @@ function job(input: Partial<AsyncJobState> & Pick<AsyncJobState, "asyncId" | "st
15
15
  } as AsyncJobState;
16
16
  }
17
17
 
18
- function hostStep(overrides: Partial<HostStepNodeV1> = {}): HostStepNodeV1 {
18
+ function hostStep(overrides: Partial<HostStepNode> = {}): HostStepNode {
19
19
  return {
20
20
  version: 1,
21
21
  kind: "host-step",
@@ -257,4 +257,4 @@ describe("async status projection", () => {
257
257
  { name: "writer · Second (worker)", state: "running" },
258
258
  ]);
259
259
  });
260
- });
260
+ });
@@ -11,7 +11,7 @@ import {
11
11
  registerSubagentCapabilityCeiling,
12
12
  } from "../../src/api/capability-ceiling.ts";
13
13
  import { runSync } from "../../src/runs/foreground/execution.ts";
14
- import { buildPiArgs } from "../../src/runs/shared/pi-args.ts";
14
+ import { buildInProcessChildLaunch } from "../../src/runs/shared/child-launch.ts";
15
15
 
16
16
  function agent(name: string): AgentConfig {
17
17
  return {
@@ -84,19 +84,20 @@ describe("capability ceiling agent allowlist", () => {
84
84
  assert.deepEqual(result.capabilityCeiling?.allowedAgents, ["reviewer"]);
85
85
  });
86
86
 
87
- it("includes allowedAgents in propagated launch env and audit metadata", () => {
88
- const { env, capabilityAudit } = buildPiArgs({
89
- baseArgs: [],
90
- task: "Review",
87
+ it("includes allowedAgents in the child runtime config and audit metadata", () => {
88
+ const { config, capabilityAudit } = buildInProcessChildLaunch({
89
+ host: "parent",
90
+ cwd: process.cwd(),
91
91
  sessionEnabled: false,
92
92
  inheritProjectContext: false,
93
+ inheritGlobalContext: false,
93
94
  inheritSkills: false,
94
95
  childAgentName: "reviewer",
96
+ childIndex: 0,
95
97
  capabilityCeiling: { version: 1, allowedAgents: ["reviewer"], allowedTools: ["read"], denyExtensions: true, sources: ["plan-mode"] },
96
98
  });
97
99
  assert.equal(capabilityAudit?.agentAllowed, true);
98
100
  assert.deepEqual(capabilityAudit?.agentRestrictionSources, ["plan-mode"]);
99
- assert.ok(env.SELESAI_SUBAGENT_CAPABILITY_CEILING_V1);
100
- assert.deepEqual(decodeSubagentCapabilityCeiling(env.SELESAI_SUBAGENT_CAPABILITY_CEILING_V1)?.allowedAgents, ["reviewer"]);
101
+ assert.deepEqual(config.capabilityCeiling?.allowedAgents, ["reviewer"]);
101
102
  });
102
- });
103
+ });
@@ -47,22 +47,25 @@ describe("subagent capability ceiling", () => {
47
47
  assert.throws(() => parseSubagentCapabilityCeiling({ version: 2, allowedTools: ["read"], denyExtensions: true, sources: ["plan"] }), /version/);
48
48
  });
49
49
 
50
- it("combines inherited process policy with exact-session registrations", () => {
51
- const previous = process.env.SELESAI_SUBAGENT_CAPABILITY_CEILING_V1;
50
+ it("combines the inherited child runtime ceiling with exact-session registrations", () => {
52
51
  const sessionId = `current-${Date.now()}-${Math.random()}`;
53
52
  const handle = registerSubagentCapabilityCeiling({ sessionId, source: "local", ceiling: { allowedTools: ["grep", "read"] } });
54
53
  try {
55
- process.env.SELESAI_SUBAGENT_CAPABILITY_CEILING_V1 = encodeSubagentCapabilityCeiling({ version: 1, allowedTools: ["read", "write"], denyExtensions: true, sources: ["ancestor"] });
56
- assert.deepEqual(resolveCurrentSubagentCapabilityCeiling(sessionId), {
54
+ const inherited = decodeSubagentCapabilityCeiling(encodeSubagentCapabilityCeiling({ version: 1, allowedTools: ["read", "write"], denyExtensions: true, sources: ["ancestor"] }));
55
+ assert.deepEqual(resolveSubagentCapabilityCeiling(sessionId, inherited), {
57
56
  version: 1,
58
57
  allowedTools: ["read"],
59
58
  denyExtensions: true,
60
59
  sources: ["ancestor", "local"],
61
60
  });
61
+ assert.deepEqual(resolveCurrentSubagentCapabilityCeiling(sessionId), {
62
+ version: 1,
63
+ allowedTools: ["grep", "read"],
64
+ denyExtensions: false,
65
+ sources: ["local"],
66
+ });
62
67
  } finally {
63
68
  handle.dispose();
64
- if (previous === undefined) delete process.env.SELESAI_SUBAGENT_CAPABILITY_CEILING_V1;
65
- else process.env.SELESAI_SUBAGENT_CAPABILITY_CEILING_V1 = previous;
66
69
  }
67
70
  });
68
- });
71
+ });