@selesai/code 0.13.0 → 0.13.2

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 (463) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/core/agent-session.d.ts +1 -1
  3. package/dist/core/agent-session.js +1 -1
  4. package/dist/core/sdk.d.ts +2 -2
  5. package/dist/core/sdk.js +1 -1
  6. package/dist/core/system-prompt.d.ts +1 -1
  7. package/dist/core/system-prompt.js +1 -1
  8. package/dist/extensions/package.json +1 -2
  9. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
  10. package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
  11. package/dist/extensions/pi-hermes-memory/README.md +643 -0
  12. package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
  13. package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
  14. package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
  15. package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
  16. package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
  17. package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
  18. package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
  19. package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
  20. package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
  21. package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
  22. package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
  23. package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
  24. package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
  25. package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
  26. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
  27. package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
  28. package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
  29. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
  30. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
  31. package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
  32. package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
  33. package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
  34. package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
  35. package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
  36. package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
  37. package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
  38. package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
  39. package/dist/extensions/pi-hermes-memory/package.json +66 -0
  40. package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
  41. package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
  42. package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
  43. package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
  44. package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
  45. package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
  46. package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
  47. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
  48. package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
  49. package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
  50. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
  51. package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
  52. package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
  53. package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
  54. package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
  55. package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
  56. package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
  57. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
  58. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
  59. package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
  60. package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
  61. package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
  62. package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
  63. package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
  64. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
  65. package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
  66. package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
  67. package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
  68. package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
  69. package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
  70. package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
  71. package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
  72. package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
  73. package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
  74. package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
  75. package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
  76. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
  77. package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
  78. package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
  79. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
  80. package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
  82. package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
  83. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
  84. package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
  85. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
  86. package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
  87. package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
  88. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
  89. package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
  90. package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
  91. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
  92. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
  93. package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
  94. package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
  95. package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
  96. package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
  97. package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
  98. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
  99. package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
  100. package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
  101. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
  102. package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
  103. package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
  104. package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
  105. package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
  106. package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
  107. package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
  108. package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
  109. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
  110. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
  111. package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
  112. package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
  113. package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
  114. package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
  115. package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
  116. package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
  117. package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
  118. package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
  119. package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
  120. package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
  121. package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
  122. package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
  123. package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
  124. package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
  125. package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
  126. package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
  127. package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
  128. package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
  129. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
  130. package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
  131. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
  132. package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
  133. package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
  134. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
  135. package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
  136. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
  137. package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
  138. package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
  139. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
  140. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
  141. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
  142. package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
  143. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
  144. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
  145. package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
  146. package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
  147. package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
  148. package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
  149. package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
  150. package/dist/extensions/pi-rewind-hook/README.md +4 -1
  151. package/dist/extensions/pi-rewind-hook/index.ts +151 -21
  152. package/dist/extensions/pi-rewind-hook/package.json +5 -3
  153. package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
  154. package/dist/extensions/pi-subagents/AGENTS.md +5 -0
  155. package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
  156. package/dist/extensions/pi-subagents/README.md +12 -6
  157. package/dist/extensions/pi-subagents/VISION.md +81 -0
  158. package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
  159. package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
  160. package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
  161. package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
  162. package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
  163. package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
  164. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
  165. package/dist/extensions/pi-subagents/docs/agents.md +488 -0
  166. package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
  167. package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
  168. package/dist/extensions/pi-subagents/docs/models.md +27 -4
  169. package/dist/extensions/pi-subagents/docs/observability.md +10 -9
  170. package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
  171. package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
  172. package/dist/extensions/pi-subagents/package-lock.json +389 -2
  173. package/dist/extensions/pi-subagents/package.json +3 -1
  174. package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
  175. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
  176. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
  177. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
  178. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  179. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
  180. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
  181. package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
  182. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
  183. package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
  184. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
  185. package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
  186. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
  187. package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
  188. package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
  189. package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
  190. package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
  191. package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
  192. package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
  193. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
  194. package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
  195. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
  196. package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
  197. package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
  198. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
  199. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
  200. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
  201. package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
  202. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -7
  203. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
  204. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
  205. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
  206. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
  207. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
  208. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
  209. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
  210. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
  211. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
  212. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
  213. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
  214. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
  215. package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
  216. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
  217. package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
  218. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
  219. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
  220. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
  221. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
  222. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
  223. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
  224. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
  225. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
  226. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
  227. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
  228. package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
  229. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
  230. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
  231. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
  232. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
  233. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
  234. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
  235. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
  236. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
  237. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
  238. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
  239. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
  240. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
  241. package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
  242. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
  243. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
  244. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
  245. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
  246. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
  247. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
  248. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
  249. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
  250. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
  251. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
  252. package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
  253. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
  254. package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
  255. package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
  256. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
  257. package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
  258. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
  259. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
  260. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
  261. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
  262. package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
  263. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
  264. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
  265. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
  266. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
  267. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
  268. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
  269. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
  270. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
  271. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
  272. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
  273. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
  274. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
  275. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
  276. package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
  277. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
  278. package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
  279. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
  280. package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
  281. package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
  282. package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
  283. package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
  284. package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
  285. package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
  286. package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
  287. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
  288. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
  289. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
  290. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
  291. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
  292. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
  293. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
  294. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
  295. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
  296. package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
  297. package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
  298. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
  299. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
  300. package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
  301. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
  302. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
  303. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
  304. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
  305. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
  306. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  307. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
  308. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
  309. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
  310. package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
  311. package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
  312. package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
  313. package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
  314. package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
  315. package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
  316. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
  317. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  318. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
  319. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
  320. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
  321. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
  322. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
  323. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
  324. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
  325. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
  326. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
  327. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
  328. package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
  329. package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
  330. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
  331. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
  332. package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
  333. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
  334. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
  335. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
  336. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
  337. package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
  338. package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
  339. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
  340. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
  341. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
  342. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
  343. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
  344. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
  345. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
  346. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
  347. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
  348. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
  349. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
  350. package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
  351. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
  352. package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
  353. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
  354. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
  355. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
  356. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
  357. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
  358. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
  359. package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
  360. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
  361. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
  362. package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
  363. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
  364. package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
  365. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
  366. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
  367. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
  368. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
  369. package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
  370. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
  371. package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
  372. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
  373. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +136 -6
  374. package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
  375. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
  376. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
  377. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
  378. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
  379. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
  380. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  381. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
  382. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
  383. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
  384. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
  385. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
  386. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
  387. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
  388. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
  389. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
  390. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
  391. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
  392. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
  393. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
  394. package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
  395. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
  396. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
  397. package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
  398. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
  399. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
  400. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
  401. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
  402. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
  403. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
  404. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
  405. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
  406. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
  407. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
  408. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  409. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
  410. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
  411. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
  412. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
  413. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
  414. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
  415. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
  416. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
  417. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
  418. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
  419. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
  420. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
  421. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
  422. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
  423. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
  424. package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
  425. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
  426. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
  427. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
  428. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
  429. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
  430. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
  431. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
  432. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
  433. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
  434. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
  435. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
  436. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
  437. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
  438. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
  439. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
  440. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
  441. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
  442. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
  443. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
  444. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
  445. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
  446. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
  447. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
  448. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
  449. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
  450. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
  451. package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
  452. package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
  453. package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
  454. package/dist/modes/interactive/interactive-mode.js +7 -7
  455. package/dist/skills/workflow/SKILL.md +65 -50
  456. package/package.json +112 -110
  457. package/dist/extensions/enable-readonly-tools.test.ts +0 -63
  458. package/dist/extensions/enable-readonly-tools.ts +0 -24
  459. package/dist/extensions/model-prompt-injector/config.json +0 -16
  460. package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
  461. package/dist/extensions/model-prompt-injector/index.ts +0 -148
  462. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
  463. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
@@ -342,8 +342,10 @@ describe("builtin agent overrides", () => {
342
342
  systemPromptMode: "replace",
343
343
  inheritProjectContext: true,
344
344
  inheritSkills: true,
345
+ allowNestedSubagents: true,
345
346
  acceptanceRole: "writer",
346
347
  subagentOnlyExtensions: ["./tools/child-review.ts"],
348
+ mutationTools: ["replace", "undo_last_replace"],
347
349
  completionGuard: false,
348
350
  },
349
351
  },
@@ -359,8 +361,10 @@ describe("builtin agent overrides", () => {
359
361
  assert.equal(reviewer.systemPromptMode, "replace");
360
362
  assert.equal(reviewer.inheritProjectContext, true);
361
363
  assert.equal(reviewer.inheritSkills, true);
364
+ assert.equal(reviewer.allowNestedSubagents, true);
362
365
  assert.equal(reviewer.acceptanceRole, "writer");
363
366
  assert.deepEqual(reviewer.subagentOnlyExtensions, ["./tools/child-review.ts"]);
367
+ assert.deepEqual(reviewer.mutationTools, ["replace", "undo_last_replace"]);
364
368
  assert.equal(reviewer.completionGuard, false);
365
369
  assert.equal(reviewer.override?.scope, "user");
366
370
  assert.equal(reviewer.override?.path, path.join(tempHome, ".selesai", "agent", "settings.json"));
@@ -479,6 +483,76 @@ describe("builtin agent overrides", () => {
479
483
  assert.equal(reviewer.override?.path, path.join(tempProject, ".selesai", "settings.json"));
480
484
  });
481
485
 
486
+ it("layers active-provider overrides over default agentOverrides", () => {
487
+ writeJson(path.join(tempHome, ".selesai", "agent", "settings.json"), {
488
+ subagents: {
489
+ agentOverrides: {
490
+ worker: { model: "openai/gpt-5-mini", thinking: "low" },
491
+ },
492
+ agentOverridesByProvider: {
493
+ "github-copilot": {
494
+ worker: { model: "github-copilot/gpt-5-mini", thinking: "high" },
495
+ },
496
+ openrouter: {
497
+ worker: { model: "openrouter/openai/gpt-5-mini" },
498
+ },
499
+ },
500
+ },
501
+ });
502
+
503
+ const defaultWorker = discoverAgents(tempProject, "both").agents.find((agent) => agent.name === "worker");
504
+ const copilotWorker = discoverAgents(tempProject, "both", "github-copilot").agents.find((agent) => agent.name === "worker");
505
+ const openrouterWorker = discoverAgents(tempProject, "both", "openrouter").agents.find((agent) => agent.name === "worker");
506
+ assert.equal(defaultWorker?.model, "openai/gpt-5-mini");
507
+ assert.equal(defaultWorker?.thinking, "low");
508
+ assert.equal(copilotWorker?.model, "github-copilot/gpt-5-mini");
509
+ assert.equal(copilotWorker?.thinking, "high");
510
+ assert.equal(openrouterWorker?.model, "openrouter/openai/gpt-5-mini");
511
+ assert.equal(openrouterWorker?.thinking, "low");
512
+ });
513
+
514
+ it("keeps project precedence when provider-specific overrides are active", () => {
515
+ writeJson(path.join(tempHome, ".selesai", "agent", "settings.json"), {
516
+ subagents: { agentOverridesByProvider: { openrouter: { worker: { model: "openrouter/user-model", thinking: "low" } } } },
517
+ });
518
+ writeJson(path.join(tempProject, ".selesai", "settings.json"), {
519
+ subagents: {
520
+ agentOverrides: {
521
+ worker: { thinking: "medium" },
522
+ },
523
+ agentOverridesByProvider: {
524
+ openrouter: { worker: { model: "openrouter/project-model", thinking: "high" } },
525
+ },
526
+ },
527
+ });
528
+
529
+ const worker = discoverAgents(tempProject, "both", "openrouter").agents.find((agent) => agent.name === "worker");
530
+ assert.equal(worker?.model, "openrouter/project-model");
531
+ assert.equal(worker?.thinking, "high");
532
+ assert.equal(worker?.override?.scope, "project");
533
+ });
534
+
535
+ it("keeps provider overrides unambiguous for agents named like override fields", () => {
536
+ writeJson(path.join(tempHome, ".selesai", "agent", "settings.json"), {
537
+ subagents: {
538
+ agentOverridesByProvider: {
539
+ openrouter: {
540
+ model: { model: "openrouter/special-model" },
541
+ tools: { thinking: "high" },
542
+ },
543
+ },
544
+ },
545
+ });
546
+ fs.mkdirSync(path.join(tempHome, ".selesai", "agent", "agents"), { recursive: true });
547
+ for (const name of ["model", "tools"]) {
548
+ fs.writeFileSync(path.join(tempHome, ".selesai", "agent", "agents", `${name}.md`), `---\nname: ${name}\ndescription: ${name}\n---\n`, "utf-8");
549
+ }
550
+
551
+ const agents = discoverAgents(tempProject, "both", "openrouter").agents;
552
+ assert.equal(agents.find((agent) => agent.name === "model")?.model, "openrouter/special-model");
553
+ assert.equal(agents.find((agent) => agent.name === "tools")?.thinking, "high");
554
+ });
555
+
482
556
  it("layers a project override on top of a user override for a custom agent instead of discarding it", () => {
483
557
  // Regression test: a custom agent (e.g. a reviewer persona shipped as a .md
484
558
  // file with no model/thinking/fallbackModels in frontmatter) that gets its
@@ -1007,6 +1081,7 @@ describe("builtin agent overrides", () => {
1007
1081
  thinking: "high",
1008
1082
  systemPromptMode: "append",
1009
1083
  inheritProjectContext: true,
1084
+ inheritGlobalContext: true,
1010
1085
  inheritSkills: false,
1011
1086
  defaultContext: "fork",
1012
1087
  acceptanceRole: "read-only",
@@ -1026,6 +1101,7 @@ describe("builtin agent overrides", () => {
1026
1101
  thinking: undefined,
1027
1102
  systemPromptMode: "replace",
1028
1103
  inheritProjectContext: false,
1104
+ inheritGlobalContext: false,
1029
1105
  inheritSkills: false,
1030
1106
  defaultContext: undefined,
1031
1107
  acceptanceRole: undefined,
@@ -1047,6 +1123,7 @@ describe("builtin agent overrides", () => {
1047
1123
  thinking: false,
1048
1124
  systemPromptMode: "replace",
1049
1125
  inheritProjectContext: false,
1126
+ inheritGlobalContext: false,
1050
1127
  defaultContext: false,
1051
1128
  acceptanceRole: false,
1052
1129
  skills: false,
@@ -68,6 +68,24 @@ describe("agent refinements", () => {
68
68
  assert.throws(() => getAgentRefinementPath(tempDir, "../worker"), /cannot be used/);
69
69
  });
70
70
 
71
+ it("reports discovery evidence and does not launch or write for a missing refinement target", async () => {
72
+ let launched = false;
73
+ const result = await handleRefinementAction("refine", { agent: "missing" }, {
74
+ cwd: tempDir,
75
+ state: state(),
76
+ signal: new AbortController().signal,
77
+ launchProposalChild: async () => {
78
+ launched = true;
79
+ return { details: { results: [] } };
80
+ },
81
+ });
82
+ assert.equal(result.isError, true);
83
+ assert.equal(launched, false);
84
+ assert.match(firstText(result), /^Unknown agent: missing\nEffective cwd: /);
85
+ assert.match(firstText(result), /Consulted agent-definition directories:[\s\S]*Discovered agents:/);
86
+ assert.equal(fs.existsSync(getAgentRefinementPath(tempDir, "missing")), false);
87
+ });
88
+
71
89
  it("does not launch or write when no bounded evidence exists", async () => {
72
90
  writeProjectAgent();
73
91
  let launched = false;
@@ -0,0 +1,86 @@
1
+ import assert from "node:assert/strict";
2
+ import { spawnSync } from "node:child_process";
3
+ import * as fs from "node:fs";
4
+ import * as os from "node:os";
5
+ import * as path from "node:path";
6
+ import { describe, it } from "node:test";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
10
+ const oxlint = path.join(projectRoot, "node_modules", "oxlint", "bin", "oxlint");
11
+ const config = path.join(projectRoot, ".oxlintrc.json");
12
+
13
+ function lintFixture(source: string): string {
14
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-anti-slop-"));
15
+ try {
16
+ const fixture = path.join(root, "rules.ts");
17
+ fs.writeFileSync(fixture, source, "utf8");
18
+ const result = spawnSync(process.execPath, [oxlint, "--config", config, "--format", "json", fixture], {
19
+ cwd: projectRoot,
20
+ encoding: "utf8",
21
+ });
22
+ assert.equal(result.error, undefined, result.error?.message);
23
+ assert.ok(result.status !== null, result.stderr);
24
+ assert.notEqual(result.status, 0, result.stderr);
25
+ return result.stdout;
26
+ } finally {
27
+ fs.rmSync(root, { recursive: true, force: true });
28
+ }
29
+ }
30
+
31
+ describe("anti-slop Oxlint plugin", () => {
32
+ it("loads every configured rule and reports representative violations without flagging safe counterparts", () => {
33
+ const output = lintFixture(String.raw`
34
+ const chained = ("value" as string) as number;
35
+ const conditional = { ...(true ? { value: 1 } : {}) };
36
+ const widened: unknown = "known";
37
+ vi.mock("module");
38
+ function broad(parameter: object): void { void parameter; }
39
+ Reflect.apply(() => {}, null, []);
40
+ Reflect.get({ key: "value" }, "key");
41
+ function runtime(value: string): boolean { return typeof value === "string"; }
42
+ const shapeSignal = 1;
43
+ function unknownInput(value: unknown): void { void value; }
44
+ function unknownOutput(): unknown { return null; }
45
+ type Hidden = unknown;
46
+ type HiddenUnion = string | unknown;
47
+ export type ConcreteAlias = string | number;
48
+ type Opaque = object;
49
+ type ConditionalFunction = unknown extends infer Opaque ? (conditionalValue: Opaque) => void : never;
50
+ const dictionary: Record<string, unknown> = {};
51
+ const broadValue: object = { id: 1 };
52
+ const narrowedValue = broadValue as { id: number };
53
+ const asserted = "x" as string;
54
+
55
+ export function safeObjectInput(parameter: { id: number }): void { void parameter; }
56
+ export function safeCauseInput(cause: unknown): void { void cause; }
57
+ export const safeDictionary: Record<string, string> = {};
58
+ export const safeSpread = { ...(true ? { value: 1 } : { value: 2 }) };
59
+ export const safeConst = "x" as const;
60
+ `);
61
+ const rules = [
62
+ "no-chained-type-assertions",
63
+ "no-conditional-empty-object-spread",
64
+ "no-known-value-widening",
65
+ "no-module-mocking",
66
+ "no-object-parameters",
67
+ "no-reflect-apply",
68
+ "no-reflect-get",
69
+ "no-runtime-typeof",
70
+ "no-shape-in-symbol-names",
71
+ "no-unknown-parameters",
72
+ "no-unknown-returns",
73
+ "no-unknown-type-aliases",
74
+ "no-unsafe-dictionary-type",
75
+ "no-widen-then-assert",
76
+ "require-safety-comment-for-type-assertion",
77
+ ];
78
+ for (const rule of rules) assert.ok(output.includes(`"code": "anti-slop(${rule})"`), `missing ${rule}`);
79
+ assert.ok(output.includes("Type alias `HiddenUnion` hides `unknown`."), "missing union unknown alias");
80
+ assert.equal(output.includes("Type alias `ConcreteAlias` hides `unknown`."), false);
81
+ assert.equal(output.includes("Parameter `conditionalValue` uses the broad `object` type."), false);
82
+ for (const safeName of ["safeObjectInput", "safeCauseInput", "safeDictionary", "safeSpread", "safeConst"]) {
83
+ assert.equal(output.includes(safeName), false, `unexpected diagnostic for ${safeName}`);
84
+ }
85
+ });
86
+ });
@@ -32,7 +32,7 @@ describe("project-local artifact paths", () => {
32
32
  assert.match(getProjectArtifactPackagingWarning(packageDir({ name: "unsafe" })) ?? "", /\.npmignore/);
33
33
  assert.equal(getProjectArtifactPackagingWarning(packageDir({ name: "gitignored" }, { name: ".gitignore", content: ".pi-subagents/\n" })), undefined);
34
34
  assert.equal(getProjectArtifactPackagingWarning(packageDir({ name: "globignored" }, { name: ".npmignore", content: "**/.pi-subagents/**\n" })), undefined);
35
- assert.equal(getProjectArtifactPackagingWarning(packageDir({ name: "classignored" }, { name: ".npmignore", content: "[.]pi/subagents/**\n" })), undefined);
35
+ assert.equal(getProjectArtifactPackagingWarning(packageDir({ name: "classignored" }, { name: ".npmignore", content: "[.]pi-subagents/**\n" })), undefined);
36
36
  assert.match(getProjectArtifactPackagingWarning(packageDir({ name: "explicit-files-over-ignore", files: [".pi-subagents/**"] }, { name: ".npmignore", content: ".pi-subagents/\n" })) ?? "", /artifactDir/);
37
37
  assert.equal(getProjectArtifactPackagingWarning(packageDir({ name: "restricted", files: ["src/**"] })), undefined);
38
38
  assert.equal(getProjectArtifactPackagingWarning(packageDir({ name: "malformed-pattern", files: ["[z-a]"] })), undefined);
@@ -1,8 +1,10 @@
1
1
  import assert from "node:assert/strict";
2
2
  import * as path from "node:path";
3
3
  import { describe, it } from "node:test";
4
- import { buildAsyncRunnerSteps, DEFAULT_ASYNC_TIMEOUT_MS, formatAsyncStartedMessage, resolveAsyncRunnerLogPaths } from "../../src/runs/background/async-execution.ts";
4
+ import type { ExtensionAPI } from "@selesai/code";
5
+ import { buildAsyncRunnerSteps, DEFAULT_ASYNC_TIMEOUT_MS, emitProcessTerminalEvent, formatAsyncStartedMessage, resolveAsyncRunnerLogPaths } from "../../src/runs/background/async-execution.ts";
5
6
  import type { AgentConfig } from "../../src/agents/agents.ts";
7
+ import { SUBAGENT_PROCESS_TERMINAL_EVENT } from "../../src/shared/types.ts";
6
8
 
7
9
  const agent = (name: string, toolBudget?: AgentConfig["toolBudget"]): AgentConfig => ({
8
10
  name,
@@ -25,6 +27,41 @@ const ctx = {
25
27
  };
26
28
 
27
29
  describe("async runner execution", () => {
30
+ it("uses supplied discovery context for missing async agents", () => {
31
+ const result = buildAsyncRunnerSteps("missing-agent", {
32
+ chain: [{ agent: "missing", task: "Do not launch" }],
33
+ agents: [agent("arbitrary")],
34
+ unknownAgentDiagnosticContext: {
35
+ cwd: path.resolve(ctx.cwd),
36
+ scope: "both",
37
+ directories: [{ source: "project", path: path.join(ctx.cwd, ".selesai", "agents"), state: "empty" }],
38
+ agents: [agent("discovered")],
39
+ },
40
+ ctx,
41
+ maxSubagentDepth: 1,
42
+ asyncDir: path.join(process.cwd(), ".tmp-missing-agent"),
43
+ });
44
+ assert.ok("error" in result);
45
+ assert.match(result.error, /^Unknown agent: missing\nEffective cwd: /);
46
+ assert.match(result.error, /discovered \(project\)/);
47
+ assert.doesNotMatch(result.error, /arbitrary \(project\)/);
48
+ });
49
+
50
+ it("uses the resolved cwd override for no-context missing-agent fallback discovery", () => {
51
+ const override = path.join("diagnostic-cwd-override", "nested");
52
+ const result = buildAsyncRunnerSteps("missing-agent-cwd", {
53
+ chain: [{ agent: "missing", task: "Do not launch" }],
54
+ agents: [agent("arbitrary")],
55
+ ctx,
56
+ cwd: override,
57
+ maxSubagentDepth: 1,
58
+ asyncDir: path.join(process.cwd(), ".tmp-missing-agent-cwd"),
59
+ });
60
+ assert.ok("error" in result);
61
+ assert.ok(result.error.startsWith(`Unknown agent: missing\nEffective cwd: ${path.resolve(ctx.cwd, override)}`));
62
+ assert.doesNotMatch(result.error, /arbitrary \(project\)/);
63
+ });
64
+
28
65
  it("formats interactive yield and headless auto-drain guidance separately", () => {
29
66
  const interactive = formatAsyncStartedMessage("Async: worker [interactive]", true);
30
67
  assert.match(interactive, /interactive session[\s\S]*return control/i);
@@ -71,6 +108,31 @@ describe("async runner execution", () => {
71
108
  assert.equal(result.steps[0]?.waitToolEnabled, false);
72
109
  assert.deepEqual(result.steps[1]?.toolBudget, { hard: 2, block: ["grep"] });
73
110
  });
111
+ it("carries the resolved model context window into async runner steps", () => {
112
+ const result = buildAsyncRunnerSteps("context-limit-run", {
113
+ chain: [{ agent: "worker", task: "inspect context" }],
114
+ agents: [{ ...agent("worker"), model: "mock/context" }],
115
+ availableModels: [{ provider: "mock", id: "context", fullId: "mock/context", contextWindow: 128_000 }],
116
+ ctx,
117
+ asyncDir: path.join(process.cwd(), ".tmp-async-context-limit-test"),
118
+ maxSubagentDepth: 2,
119
+ });
120
+
121
+ assert.ok("steps" in result, "expected successful step build");
122
+ assert.equal(result.steps[0]?.contextLimit, 128_000);
123
+ });
124
+
125
+ it("carries explicit nested fanout authorization into async runner steps", () => {
126
+ const nestedAgent = { ...agent("delegator"), allowNestedSubagents: true };
127
+ const result = buildAsyncRunnerSteps("nested-fanout-run", {
128
+ chain: [{ agent: "delegator", task: "delegate" }],
129
+ agents: [nestedAgent],
130
+ ctx,
131
+ });
132
+
133
+ assert.equal(result.error, undefined);
134
+ assert.equal(result.steps[0]?.allowNestedSubagents, true);
135
+ });
74
136
 
75
137
  it("assigns default and agent-level deadlines to async serial and parallel children", () => {
76
138
  const result = buildAsyncRunnerSteps("timeout-run", {
@@ -148,3 +210,41 @@ describe("async runner execution", () => {
148
210
  assert.deepEqual(result.steps[0]?.toolBudget, { hard: 5, block: ["ls"] });
149
211
  });
150
212
  });
213
+
214
+ describe("async runner process terminal events", () => {
215
+ const proof = { version: 1, runId: "run-terminal", runnerProcessInstanceId: "runner-1", state: "unknown", reason: "writer-close-unverified" };
216
+ const staleMessage = "This extension ctx is stale after session replacement or reload.";
217
+
218
+ const makeCtx = (emit: (name: string, payload?: unknown) => unknown) => ({
219
+ ...ctx,
220
+ pi: { events: { emit } } as unknown as ExtensionAPI,
221
+ });
222
+
223
+ it("emits the process terminal proof on a live event bus", () => {
224
+ const emitted: Array<[string, unknown]> = [];
225
+ emitProcessTerminalEvent(makeCtx((name, payload) => { emitted.push([name, payload]); }), proof);
226
+
227
+ assert.deepEqual(emitted, [[SUBAGENT_PROCESS_TERMINAL_EVENT, proof]]);
228
+ });
229
+
230
+ it("drops stale extension ctx failures without throwing", () => {
231
+ assert.doesNotThrow(() => emitProcessTerminalEvent(makeCtx(() => {
232
+ throw new Error(staleMessage);
233
+ }), proof));
234
+ });
235
+
236
+ it("logs non-stale event bus failures without throwing", () => {
237
+ const originalError = console.error;
238
+ let logged: unknown[] | undefined;
239
+ console.error = (...args: unknown[]) => { logged = args; };
240
+ try {
241
+ assert.doesNotThrow(() => emitProcessTerminalEvent(makeCtx(() => {
242
+ throw new Error("event bus unavailable");
243
+ }), proof));
244
+ } finally {
245
+ console.error = originalError;
246
+ }
247
+
248
+ assert.ok(logged?.some((arg) => arg instanceof Error && arg.message === "event bus unavailable"));
249
+ });
250
+ });
@@ -8,6 +8,7 @@ import { consumeSteerRequests, consumeSteerRequestsFromDir, consumeStopRequestPa
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
+ import { resolveExternalCliRunnerStatus } from "../../src/runs/shared/external-cli-contract.ts";
11
12
  import { steerWorkflowForegroundTarget, workflowForegroundSteeringDir } from "../../src/runs/foreground/workflow-foreground-steering.ts";
12
13
  import { ASYNC_DIR, RESULTS_DIR, type SubagentState } from "../../src/shared/types.ts";
13
14
 
@@ -266,6 +267,30 @@ describe("async interrupt action", () => {
266
267
  }
267
268
  });
268
269
 
270
+ it("rejects workflow-key steering for a one-shot external CLI child", async () => {
271
+ const state = createState();
272
+ const workflowRunId = `workflow-key-external-${Date.now().toString(36)}`;
273
+ const childRunId = `${workflowRunId}-child`;
274
+ const workflowDir = createRunningAsync(state, workflowRunId, { track: false, mode: "workflow" });
275
+ const childDir = createRunningAsync(state, childRunId, { track: false });
276
+ const workflowStatus = JSON.parse(fs.readFileSync(path.join(workflowDir, "status.json"), "utf-8"));
277
+ workflowStatus.steps = [{ agent: "external", status: "running", workflowKey: "external", runId: childRunId, async: true }];
278
+ writeJson(path.join(workflowDir, "status.json"), workflowStatus);
279
+ const childStatus = JSON.parse(fs.readFileSync(path.join(childDir, "status.json"), "utf-8"));
280
+ childStatus.pid = process.pid;
281
+ childStatus.steps[0].runner = resolveExternalCliRunnerStatus({ command: "review-cli" });
282
+ writeJson(path.join(childDir, "status.json"), childStatus);
283
+ try {
284
+ const result = await steerWorkflowChildByKey({ state, workflowRunId, key: "external", message: "Do not deliver.", options: { ackTimeoutMs: 20 } });
285
+ assert.equal(result.state, "failed");
286
+ assert.match(result.error ?? "", /External adapter 'external-cli' does not support runs\.steer/);
287
+ assert.equal(consumeSteerRequests(childDir).length, 0);
288
+ } finally {
289
+ cleanup(workflowRunId, workflowDir);
290
+ cleanup(childRunId, childDir);
291
+ }
292
+ });
293
+
269
294
  it("returns missed when a terminal keyed child cannot accept a retained follow-up", async () => {
270
295
  const state = createState();
271
296
  const workflowRunId = `workflow-key-terminal-${Date.now().toString(36)}`;
@@ -546,6 +571,44 @@ describe("async interrupt action", () => {
546
571
  }
547
572
  });
548
573
 
574
+ it("rejects runs.steer for the one-shot external CLI with its adapter reason", async () => {
575
+ const state = createState();
576
+ const runId = `steer-external-${Date.now().toString(36)}`;
577
+ const asyncDir = createRunningAsync(state, runId, { track: false });
578
+ const statusPath = path.join(asyncDir, "status.json");
579
+ const status = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as { steps: Array<Record<string, unknown>> };
580
+ status.steps[0]!.runner = resolveExternalCliRunnerStatus({ command: "review-cli" });
581
+ writeJson(statusPath, status);
582
+ try {
583
+ const result = await executorWithKill(state, () => true)
584
+ .execute("steer", { action: "steer", id: runId, message: "Do not deliver" }, new AbortController().signal, undefined, ctx());
585
+ assert.equal(result.isError, true);
586
+ assert.match(text(result), /External adapter 'external-cli' does not support runs\.steer: The one-shot stdin adapter closes input/);
587
+ assert.equal(fs.existsSync(path.join(asyncDir, "control", "steer-requests")), false);
588
+ } finally {
589
+ cleanup(runId, asyncDir);
590
+ }
591
+ });
592
+
593
+ it("rejects runs.steer for an old persisted external CLI status shape", async () => {
594
+ const state = createState();
595
+ const runId = `steer-old-external-${Date.now().toString(36)}`;
596
+ const asyncDir = createRunningAsync(state, runId, { track: false });
597
+ const statusPath = path.join(asyncDir, "status.json");
598
+ const status = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as { steps: Array<Record<string, unknown>> };
599
+ status.steps[0]!.runner = { type: "external-cli", command: "review-cli", args: [], promptDelivery: "stdin", capabilities: { stop: true, steer: false, resume: false, structuredOutput: false, toolEvents: false } };
600
+ writeJson(statusPath, status);
601
+ try {
602
+ const result = await executorWithKill(state, () => true)
603
+ .execute("steer", { action: "steer", id: runId, message: "Do not deliver" }, new AbortController().signal, undefined, ctx());
604
+ assert.equal(result.isError, true);
605
+ assert.match(text(result), /External adapter 'external-cli' does not support runs\.steer/);
606
+ assert.equal(fs.existsSync(path.join(asyncDir, "control", "steer-requests")), false);
607
+ } finally {
608
+ cleanup(runId, asyncDir);
609
+ }
610
+ });
611
+
549
612
  it("requests an interrupt without signaling a running async runner", async () => {
550
613
  const state = createState();
551
614
  const runId = `interrupt-disk-${Date.now().toString(36)}`;
@@ -631,6 +694,27 @@ describe("async interrupt action", () => {
631
694
  }
632
695
  });
633
696
 
697
+ it("stops a reload-recovered workflow through the durable control channel", async () => {
698
+ const state = createState();
699
+ state.currentSessionId = "session";
700
+ const runId = `stop-recovered-workflow-${Date.now().toString(36)}`;
701
+ const asyncDir = createRunningAsync(state, runId, { track: false, sessionId: "session", mode: "workflow" });
702
+ try {
703
+ const kills: Array<{ pid: number; signal?: NodeJS.Signals | 0 }> = [];
704
+ const result = await executorWithKill(state, (pid, signal) => {
705
+ kills.push({ pid, signal });
706
+ return true;
707
+ }).execute("stop-recovered-workflow", { action: "stop", id: runId }, new AbortController().signal, undefined, ctx());
708
+
709
+ assert.equal(result.isError, undefined);
710
+ assert.match(text(result), new RegExp(`Stop requested for async run ${runId}`));
711
+ assert.equal(consumeStopRequestPayload(asyncDir)?.type, "stop");
712
+ assert.deepEqual(kills, [{ pid: 12345, signal: 0 }]);
713
+ } finally {
714
+ cleanup(runId, asyncDir);
715
+ }
716
+ });
717
+
634
718
  it("writes child-scoped stop requests for a running async run", async () => {
635
719
  const state = createState();
636
720
  state.currentSessionId = "session";
@@ -662,6 +746,43 @@ describe("async interrupt action", () => {
662
746
  }
663
747
  });
664
748
 
749
+ it("/subagents-stop rejects a nested-only child without writing a parent target request", async () => {
750
+ const state = createState();
751
+ state.currentSessionId = "session";
752
+ const runId = `stop-nested-${Date.now().toString(36)}`;
753
+ const nestedChildId = `${runId}-nested`;
754
+ const asyncDir = createRunningAsync(state, runId, { track: false, sessionId: "session" });
755
+ try {
756
+ const statusPath = path.join(asyncDir, "status.json");
757
+ writeJson(statusPath, {
758
+ ...JSON.parse(fs.readFileSync(statusPath, "utf-8")),
759
+ steps: [{
760
+ agent: "wrapper",
761
+ status: "running",
762
+ startedAt: 100,
763
+ children: [{
764
+ id: nestedChildId,
765
+ parentRunId: runId,
766
+ parentStepIndex: 0,
767
+ depth: 1,
768
+ path: [{ runId, stepIndex: 0 }],
769
+ state: "running",
770
+ }],
771
+ }],
772
+ });
773
+
774
+ const result = await executorWithKill(state, () => {
775
+ throw new Error("nested-only child stop must not signal the parent runner");
776
+ }).execute("stop-nested", { action: "stop", id: runId, childId: nestedChildId }, new AbortController().signal, undefined, ctx());
777
+
778
+ assert.equal(result.isError, true);
779
+ assert.match(text(result), new RegExp(`Child '${nestedChildId}' was not found under async run '${runId}'`));
780
+ assert.equal(consumeStopRequestPayload(asyncDir), undefined);
781
+ } finally {
782
+ cleanup(runId, asyncDir);
783
+ }
784
+ });
785
+
665
786
  it("dismisses only a reload-recovered running workflow without terminating work", async () => {
666
787
  const state = createState();
667
788
  state.currentSessionId = "session";
@@ -31,6 +31,7 @@ describe("async recovery descriptor", () => {
31
31
  agent: "worker",
32
32
  cwd: root,
33
33
  systemPromptMode: "replace",
34
+ inheritGlobalContext: false,
34
35
  inheritProjectContext: false,
35
36
  inheritSkills: false,
36
37
  outputMode: "inline",
@@ -48,6 +49,31 @@ describe("async recovery descriptor", () => {
48
49
  }
49
50
  });
50
51
 
52
+ it("defaults inheritGlobalContext from inheritProjectContext for descriptors from older versions", () => {
53
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-async-recovery-legacy-global-context-"));
54
+ try {
55
+ fs.writeFileSync(path.join(root, "recovery-descriptor.json"), JSON.stringify({
56
+ version: 1,
57
+ runFanoutBudget: runFanoutBudget("run-legacy-global-context"),
58
+ sourceRunId: "run-legacy-global-context",
59
+ agent: "worker",
60
+ cwd: root,
61
+ systemPromptMode: "replace",
62
+ inheritProjectContext: true,
63
+ inheritSkills: false,
64
+ outputMode: "inline",
65
+ maxSubagentDepth: 2,
66
+ share: false,
67
+ }), "utf-8");
68
+
69
+ const descriptor = readAsyncRecoveryDescriptor(root);
70
+
71
+ assert.equal(descriptor?.inheritGlobalContext, true);
72
+ } finally {
73
+ fs.rmSync(root, { recursive: true, force: true });
74
+ }
75
+ });
76
+
51
77
  it("rejects unresolved profile context values", () => {
52
78
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-async-recovery-bad-context-"));
53
79
  try {
@@ -58,6 +84,7 @@ describe("async recovery descriptor", () => {
58
84
  agent: "worker",
59
85
  cwd: root,
60
86
  systemPromptMode: "replace",
87
+ inheritGlobalContext: false,
61
88
  inheritProjectContext: false,
62
89
  inheritSkills: false,
63
90
  outputMode: "inline",
@@ -86,6 +113,7 @@ describe("async recovery descriptor", () => {
86
113
  agent: "worker",
87
114
  cwd: root,
88
115
  systemPromptMode: "replace",
116
+ inheritGlobalContext: false,
89
117
  inheritProjectContext: false,
90
118
  inheritSkills: false,
91
119
  outputMode: "inline",
@@ -277,7 +277,7 @@ describe("async resume lookup", () => {
277
277
  });
278
278
  const descriptor = {
279
279
  version: 1, runFanoutBudget: createRunFanoutBudget("run-descriptor", 64), sourceRunId: "run-descriptor", agent: "worker", cwd: root, systemPromptMode: "replace",
280
- inheritProjectContext: false, inheritSkills: false, outputMode: "inline", maxSubagentDepth: 2, share: false,
280
+ inheritProjectContext: false, inheritGlobalContext: false, inheritSkills: false, outputMode: "inline", maxSubagentDepth: 2, share: false,
281
281
  };
282
282
  writeJson(path.join(asyncDir, "recovery-descriptor.json"), { ...descriptor, token: "must-not-be-accepted" });
283
283
  assert.throws(() => resolveAsyncResumeTarget({ id: "run-descriptor" }, { asyncDirRoot: asyncRoot, resultsDir }), /unknown field 'token'/);
@@ -291,15 +291,20 @@ describe("async resume lookup", () => {
291
291
  writeJson(path.join(asyncDir, "recovery-descriptor.json"), {
292
292
  ...descriptor,
293
293
  launchContractDigest: "launch-contract-digest",
294
+ allowNestedSubagents: true,
294
295
  intercomBridge: { mode: "off" },
295
296
  extensionBindings: { "shepherd.dispatch/1": { role: "coder" } },
296
297
  });
297
298
  const valid = resolveAsyncResumeTarget({ id: "run-descriptor" }, { asyncDirRoot: asyncRoot, resultsDir });
298
299
  assert.equal(valid.launchContractDigest, "launch-contract-digest");
299
300
  assert.equal(valid.recoveryDescriptor?.launchContractDigest, "launch-contract-digest");
301
+ assert.equal(valid.recoveryDescriptor?.allowNestedSubagents, true);
300
302
  assert.deepEqual(valid.recoveryDescriptor?.intercomBridge, { mode: "off" });
301
303
  assert.deepEqual(valid.recoveryDescriptor?.extensionBindings, { "shepherd.dispatch/1": { role: "coder" } });
302
304
 
305
+ writeJson(path.join(asyncDir, "recovery-descriptor.json"), { ...descriptor, allowNestedSubagents: "true" });
306
+ assert.throws(() => resolveAsyncResumeTarget({ id: "run-descriptor" }, { asyncDirRoot: asyncRoot, resultsDir }), /allowNestedSubagents/);
307
+
303
308
  writeJson(path.join(asyncDir, "recovery-descriptor.json"), { ...descriptor, extensionBindings: { invalid: true } });
304
309
  assert.throws(() => resolveAsyncResumeTarget({ id: "run-descriptor" }, { asyncDirRoot: asyncRoot, resultsDir }), /namespace/);
305
310
 
@@ -313,7 +318,7 @@ describe("async resume lookup", () => {
313
318
  }
314
319
  });
315
320
 
316
- it("normalizes persisted turn-budget state without weakening public input validation", () => {
321
+ it("ignores removed turn budgets in persisted recovery descriptors", () => {
317
322
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-async-resume-turn-budget-"));
318
323
  try {
319
324
  const asyncRoot = path.join(root, "runs");
@@ -332,6 +337,7 @@ describe("async resume lookup", () => {
332
337
  agent: "worker",
333
338
  cwd: root,
334
339
  systemPromptMode: "replace",
340
+ inheritGlobalContext: false,
335
341
  inheritProjectContext: false,
336
342
  inheritSkills: false,
337
343
  outputMode: "inline",
@@ -353,16 +359,14 @@ describe("async resume lookup", () => {
353
359
 
354
360
  const target = resolveAsyncResumeTarget({ id: "run-turn-budget" }, { asyncDirRoot: asyncRoot, resultsDir });
355
361
 
356
- assert.deepEqual(target.recoveryDescriptor?.initialTurnBudget, { maxTurns: 8, graceTurns: 2 });
362
+ assert.equal("initialTurnBudget" in (target.recoveryDescriptor ?? {}), false);
357
363
 
358
364
  writeJson(path.join(asyncDir, "recovery-descriptor.json"), {
359
365
  ...descriptor,
360
366
  initialTurnBudget: { maxTurns: 8, graceTurns: 2, unrelated: true },
361
367
  });
362
- assert.throws(
363
- () => resolveAsyncResumeTarget({ id: "run-turn-budget" }, { asyncDirRoot: asyncRoot, resultsDir }),
364
- /recoveryDescriptor\.initialTurnBudget\.unrelated is not supported/,
365
- );
368
+ const malformedLegacy = resolveAsyncResumeTarget({ id: "run-turn-budget" }, { asyncDirRoot: asyncRoot, resultsDir });
369
+ assert.equal("initialTurnBudget" in (malformedLegacy.recoveryDescriptor ?? {}), false);
366
370
  } finally {
367
371
  fs.rmSync(root, { recursive: true, force: true });
368
372
  }
@@ -387,6 +391,7 @@ describe("async resume lookup", () => {
387
391
  agent: "worker",
388
392
  cwd: root,
389
393
  systemPromptMode: "replace",
394
+ inheritGlobalContext: false,
390
395
  inheritProjectContext: false,
391
396
  inheritSkills: false,
392
397
  outputMode: "inline",
@@ -435,6 +440,7 @@ describe("async resume lookup", () => {
435
440
  agent: "worker",
436
441
  cwd: root,
437
442
  systemPromptMode: "replace",
443
+ inheritGlobalContext: false,
438
444
  inheritProjectContext: false,
439
445
  inheritSkills: false,
440
446
  outputMode: "inline",
@@ -478,6 +484,7 @@ describe("async resume lookup", () => {
478
484
  agent: "worker",
479
485
  cwd: root,
480
486
  systemPromptMode: "replace",
487
+ inheritGlobalContext: false,
481
488
  inheritProjectContext: false,
482
489
  inheritSkills: false,
483
490
  outputMode: "inline",