@selesai/code 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (466) hide show
  1. package/CHANGELOG.md +17 -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/core/usage-totals.d.ts +2 -0
  9. package/dist/core/usage-totals.js +10 -0
  10. package/dist/extensions/ascii.txt +9 -0
  11. package/dist/extensions/package.json +1 -2
  12. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
  13. package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
  14. package/dist/extensions/pi-hermes-memory/README.md +643 -0
  15. package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
  16. package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
  17. package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
  18. package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
  19. package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
  20. package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
  21. package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
  22. package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
  23. package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
  24. package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
  25. package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
  26. package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
  27. package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
  28. package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
  29. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
  30. package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
  31. package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
  32. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
  33. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
  34. package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
  35. package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
  36. package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
  37. package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
  38. package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
  39. package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
  40. package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
  41. package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
  42. package/dist/extensions/pi-hermes-memory/package.json +66 -0
  43. package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
  44. package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
  45. package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
  46. package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
  47. package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
  48. package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
  49. package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
  50. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
  51. package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
  52. package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
  53. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
  54. package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
  55. package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
  56. package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
  57. package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
  58. package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
  59. package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
  60. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
  61. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
  62. package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
  63. package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
  64. package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
  65. package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
  66. package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
  67. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
  68. package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
  69. package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
  70. package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
  71. package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
  72. package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
  73. package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
  74. package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
  75. package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
  76. package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
  77. package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
  78. package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
  79. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
  80. package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
  82. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
  83. package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
  84. package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
  85. package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
  86. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
  87. package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
  88. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
  89. package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
  90. package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
  91. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
  92. package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
  93. package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
  94. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
  95. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
  96. package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
  97. package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
  98. package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
  99. package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
  100. package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
  101. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
  102. package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
  103. package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
  104. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
  105. package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
  106. package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
  107. package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
  108. package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
  109. package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
  110. package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
  111. package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
  112. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
  113. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
  114. package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
  115. package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
  116. package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
  117. package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
  118. package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
  119. package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
  120. package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
  121. package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
  122. package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
  123. package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
  124. package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
  125. package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
  126. package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
  127. package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
  128. package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
  129. package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
  130. package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
  131. package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
  132. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
  133. package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
  134. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
  135. package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
  136. package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
  137. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
  138. package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
  139. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
  140. package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
  141. package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
  142. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
  143. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
  144. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
  145. package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
  146. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
  147. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
  148. package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
  149. package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
  150. package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
  151. package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
  152. package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
  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 -3
  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 +132 -0
  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/extensions/pi-zentui/extensions/zentui/format.ts +2 -1
  455. package/dist/extensions/pi-zentui/test/format.test.ts +15 -1
  456. package/dist/modes/interactive/components/footer.js +2 -2
  457. package/dist/modes/interactive/interactive-mode.js +11 -11
  458. package/dist/skills/workflow/SKILL.md +65 -50
  459. package/package.json +112 -110
  460. package/dist/extensions/enable-readonly-tools.test.ts +0 -63
  461. package/dist/extensions/enable-readonly-tools.ts +0 -24
  462. package/dist/extensions/model-prompt-injector/config.json +0 -16
  463. package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
  464. package/dist/extensions/model-prompt-injector/index.ts +0 -148
  465. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
  466. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
@@ -0,0 +1,138 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { planMcpDirectToolGrant } from "../../src/runs/shared/mcp-direct-tool-grant.ts";
4
+
5
+ test("plans server grants from explicit metadata facts and preserves resource filtering", () => {
6
+ const grant = planMcpDirectToolGrant({
7
+ selectors: ["browser-mcp"],
8
+ servers: {
9
+ "browser-mcp": { excludeTools: ["browser_click"] },
10
+ },
11
+ metadata: {
12
+ "browser-mcp": {
13
+ tools: [{ name: "click" }, { name: "navigate" }],
14
+ resources: [{ name: "Console Logs", uri: "resource://console" }],
15
+ },
16
+ },
17
+ toolPrefix: "server",
18
+ });
19
+
20
+ assert.deepEqual(grant, {
21
+ selections: [
22
+ { name: "browser_mcp_navigate", selector: "browser-mcp/navigate" },
23
+ { name: "browser_mcp_get_console_logs", selector: "browser-mcp/get_console_logs" },
24
+ ],
25
+ unresolvedSelectors: [],
26
+ });
27
+ });
28
+
29
+ test("plans tool-specific grants and reports unresolved selectors deterministically", () => {
30
+ const grant = planMcpDirectToolGrant({
31
+ selectors: ["github/search_repositories", "missing"],
32
+ servers: {
33
+ github: {},
34
+ },
35
+ metadata: {
36
+ github: { tools: [{ name: "search_repositories" }, { name: "create_issue" }] },
37
+ },
38
+ toolPrefix: "none",
39
+ });
40
+
41
+ assert.deepEqual(grant, {
42
+ selections: [{ name: "search_repositories", selector: "github/search_repositories" }],
43
+ unresolvedSelectors: ["missing"],
44
+ });
45
+ });
46
+
47
+ test("fails closed for missing or stale source facts without consulting ambient state", () => {
48
+ const grant = planMcpDirectToolGrant({
49
+ selectors: ["github", "github/search_repositories"],
50
+ servers: {
51
+ github: {},
52
+ },
53
+ metadata: {},
54
+ });
55
+
56
+ assert.deepEqual(grant, {
57
+ selections: [],
58
+ unresolvedSelectors: ["github", "github/search_repositories"],
59
+ });
60
+ });
61
+
62
+ test("enforces server includeTools before exclusions for tools and generated resources", () => {
63
+ const grant = planMcpDirectToolGrant({
64
+ selectors: ["demo"],
65
+ servers: {
66
+ demo: {
67
+ includeTools: ["get_*", "demo_list_records"],
68
+ excludeTools: ["get_secret", "demo_get_private*"],
69
+ },
70
+ },
71
+ metadata: {
72
+ demo: {
73
+ tools: [
74
+ { name: "get_public" },
75
+ { name: "get_secret" },
76
+ { name: "list_records" },
77
+ { name: "other" },
78
+ ],
79
+ resources: [
80
+ { name: "Run Book", uri: "resource://run-book" },
81
+ { name: "Private Notes", uri: "resource://private-notes" },
82
+ ],
83
+ },
84
+ },
85
+ toolPrefix: "server",
86
+ });
87
+
88
+ assert.deepEqual(grant.selections, [
89
+ { name: "demo_get_public", selector: "demo/get_public" },
90
+ { name: "demo_list_records", selector: "demo/list_records" },
91
+ { name: "demo_get_run_book", selector: "demo/get_run_book" },
92
+ ]);
93
+ assert.deepEqual(grant.unresolvedSelectors, []);
94
+ });
95
+
96
+ test("matches include and exclude patterns against raw and alternate prefix names", () => {
97
+ for (const [toolPrefix, expectedName] of [
98
+ ["server", "demo_mcp_search-records"],
99
+ ["short", "demo_search-records"],
100
+ ["none", "search-records"],
101
+ ] as const) {
102
+ const allowed = planMcpDirectToolGrant({
103
+ selectors: ["demo-mcp"],
104
+ servers: { "demo-mcp": { includeTools: ["demo_mcp_search_records"] } },
105
+ metadata: { "demo-mcp": { tools: [{ name: "search-records" }] } },
106
+ toolPrefix,
107
+ });
108
+ assert.deepEqual(allowed.selections, [{ name: expectedName, selector: "demo-mcp/search-records" }]);
109
+
110
+ const excluded = planMcpDirectToolGrant({
111
+ selectors: ["demo-mcp"],
112
+ servers: { "demo-mcp": { includeTools: ["demo_mcp_search_records"], excludeTools: ["search_records"] } },
113
+ metadata: { "demo-mcp": { tools: [{ name: "search-records" }] } },
114
+ toolPrefix,
115
+ });
116
+ assert.deepEqual(excluded.selections, []);
117
+ }
118
+ });
119
+
120
+ test("supports adapter-compatible question-mark patterns", () => {
121
+ const grant = planMcpDirectToolGrant({
122
+ selectors: ["demo"],
123
+ servers: { demo: { includeTools: ["get_????"], excludeTools: ["get_?ecret"] } },
124
+ metadata: { demo: { tools: [{ name: "get_wiki" }, { name: "get_secret" }, { name: "get_longer" }] } },
125
+ });
126
+
127
+ assert.deepEqual(grant.selections, [{ name: "demo_get_wiki", selector: "demo/get_wiki" }]);
128
+ });
129
+
130
+ test("matches adapter mcp-prefixed policy names", () => {
131
+ const grant = planMcpDirectToolGrant({
132
+ selectors: ["demo"],
133
+ servers: { demo: { includeTools: ["mcp_search_records"] } },
134
+ metadata: { demo: { tools: [{ name: "search_records" }, { name: "list_records" }] } },
135
+ });
136
+
137
+ assert.deepEqual(grant.selections, [{ name: "demo_search_records", selector: "demo/search_records" }]);
138
+ });
@@ -136,7 +136,7 @@ describe("mission launch lifecycle", () => {
136
136
  config: test.missionConfig,
137
137
  });
138
138
  assert.ok(binding);
139
- const missionDir = path.join(worktreeRoot, ".selesai", "subagents", "missions");
139
+ const missionDir = path.join(worktreeRoot, ".pi-subagents", "missions");
140
140
  assert.throws(() => prepareMissionLaunch({
141
141
  params: { missionId: binding.missionId, task: "Prepare worktree work" },
142
142
  projectRoot: worktreeRoot,
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
3
3
  import { afterEach, beforeEach, describe, it } from "node:test";
4
4
  import {
5
5
  clearExclusions,
6
+ DEFAULT_MODEL_EXCLUSION_TTL_MS,
6
7
  filterFallbackCandidates,
7
8
  flushPersist,
8
9
  getExcludedCount,
@@ -11,13 +12,16 @@ import {
11
12
  parseModelKey,
12
13
  recordModelFailure,
13
14
  reloadFromDisk,
15
+ MAX_MODEL_EXCLUSION_TTL_MS,
14
16
  setDefaultTTL,
17
+ type ModelExclusion,
15
18
  } from "../../src/runs/shared/model-exclusions.ts";
16
19
 
17
20
  // The exclusion store is a process-wide singleton persisted under TEMP_ROOT_DIR
18
21
  // (isolated per test run by test/support/isolated-temp-root.mjs). Clear it
19
22
  // before/after each test so cases don't leak state into each other.
20
23
  beforeEach(() => {
24
+ setDefaultTTL(DEFAULT_MODEL_EXCLUSION_TTL_MS);
21
25
  fs.rmSync(getExclusionsFilePath(), { force: true });
22
26
  clearExclusions();
23
27
  });
@@ -61,6 +65,16 @@ describe("model exclusions — TTL expiry", () => {
61
65
  it("rejects invalid default TTLs", () => {
62
66
  assert.throws(() => setDefaultTTL(0), /finite positive/);
63
67
  assert.throws(() => setDefaultTTL(Number.POSITIVE_INFINITY), /finite positive/);
68
+ assert.throws(() => setDefaultTTL(MAX_MODEL_EXCLUSION_TTL_MS + 1), /no greater than/);
69
+ });
70
+
71
+ it("keeps the maximum configured expiry representable", () => {
72
+ setDefaultTTL(MAX_MODEL_EXCLUSION_TTL_MS);
73
+ recordModelFailure({ modelId: "gpt-4", provider: "openai" });
74
+ reloadFromDisk();
75
+ const entry = JSON.parse(fs.readFileSync(getExclusionsFilePath(), "utf-8")).exclusions[0] as ModelExclusion;
76
+ assert.doesNotThrow(() => new Date(entry.expiresAt).toISOString());
77
+ assert.equal(isExcluded("gpt-4", "openai"), true);
64
78
  });
65
79
 
66
80
  it("drops an exclusion after its TTL elapses", async () => {
@@ -69,6 +83,23 @@ describe("model exclusions — TTL expiry", () => {
69
83
  await new Promise((r) => setTimeout(r, 150));
70
84
  assert.equal(isExcluded("gpt-4", "openai"), false);
71
85
  });
86
+
87
+ it("shortens active exclusions without synchronously persisting or extending expiries", () => {
88
+ recordModelFailure({ modelId: "gpt-4", provider: "openai", reason: "503", ttlMs: 60_000 });
89
+ const before = JSON.parse(fs.readFileSync(getExclusionsFilePath(), "utf-8")).exclusions[0] as ModelExclusion;
90
+ setDefaultTTL(30_000, { shortenExisting: true });
91
+ const beforeFlush = JSON.parse(fs.readFileSync(getExclusionsFilePath(), "utf-8")).exclusions[0] as ModelExclusion;
92
+ assert.equal(beforeFlush.expiresAt, before.expiresAt);
93
+ flushPersist();
94
+ const shortened = JSON.parse(fs.readFileSync(getExclusionsFilePath(), "utf-8")).exclusions[0] as ModelExclusion;
95
+ assert.equal(shortened.expiresAt, shortened.recordedAt + 30_000);
96
+
97
+ setDefaultTTL(120_000, { shortenExisting: true });
98
+ flushPersist();
99
+ const notExtended = JSON.parse(fs.readFileSync(getExclusionsFilePath(), "utf-8")).exclusions[0] as ModelExclusion;
100
+ assert.equal(notExtended.expiresAt, shortened.expiresAt);
101
+ assert.ok(shortened.expiresAt < before.expiresAt);
102
+ });
72
103
  });
73
104
 
74
105
  describe("model exclusions — parseModelKey", () => {
@@ -119,6 +150,18 @@ describe("model exclusions — filtering fallback candidates", () => {
119
150
  const filtered = filterFallbackCandidates(candidates);
120
151
  assert.deepEqual(filtered, ["anthropic/claude-3", "openai/gpt-4"]);
121
152
  });
153
+
154
+ it("reports the cached reason and expiry for skipped candidates", () => {
155
+ const skipped: Array<{ candidate: string; exclusion: Readonly<ModelExclusion> }> = [];
156
+ recordModelFailure({ modelId: "gpt-4", provider: "openai", reason: "503 unavailable" });
157
+ const filtered = filterFallbackCandidates(["openai/gpt-4", "anthropic/claude-3"], {
158
+ onExcluded: (candidate, exclusion) => skipped.push({ candidate, exclusion }),
159
+ });
160
+ assert.deepEqual(filtered, ["anthropic/claude-3"]);
161
+ assert.equal(skipped[0]?.candidate, "openai/gpt-4");
162
+ assert.equal(skipped[0]?.exclusion.reason, "503 unavailable");
163
+ assert.ok((skipped[0]?.exclusion.expiresAt ?? 0) > Date.now());
164
+ });
122
165
  });
123
166
 
124
167
  describe("model exclusions — persistence", () => {
@@ -164,4 +207,52 @@ describe("model exclusions — persistence", () => {
164
207
  assert.equal(errors.length, 1);
165
208
  assert.match(String(errors[0]?.[0]), /Failed to load exclusions/);
166
209
  });
210
+
211
+ it("rejects persisted exclusions with invalid timestamps", () => {
212
+ for (const [field, value] of [
213
+ ["expiresAt", null],
214
+ ["expiresAt", 0],
215
+ ["expiresAt", 9_000_000_000_000_000],
216
+ ["recordedAt", null],
217
+ ["recordedAt", -1],
218
+ ["recordedAt", 9_000_000_000_000_000],
219
+ ] as const) {
220
+ const now = Date.now();
221
+ const entry = { modelId: "gpt-4", provider: "openai", reason: "503", recordedAt: now, expiresAt: now + 60_000, [field]: value };
222
+ fs.writeFileSync(getExclusionsFilePath(), JSON.stringify({ version: 1, exclusions: [entry] }), "utf-8");
223
+ const originalError = console.error;
224
+ const errors: unknown[][] = [];
225
+ console.error = (...args: unknown[]) => errors.push(args);
226
+ try {
227
+ reloadFromDisk();
228
+ } finally {
229
+ console.error = originalError;
230
+ }
231
+ assert.equal(getExcludedCount(), 0);
232
+ assert.equal(errors.length, 1);
233
+ assert.match(String(errors[0]?.[1]), new RegExp(`invalid ${field}`));
234
+ }
235
+ });
236
+
237
+ it("rejects persisted exclusions with non-string reasons", () => {
238
+ for (const reason of [null, 503, { bad: true }, ["503"]]) {
239
+ const now = Date.now();
240
+ fs.writeFileSync(getExclusionsFilePath(), JSON.stringify({
241
+ version: 1,
242
+ exclusions: [{ modelId: "gpt-4", provider: "openai", reason, recordedAt: now, expiresAt: now + 60_000 }],
243
+ }), "utf-8");
244
+ const originalError = console.error;
245
+ const errors: unknown[][] = [];
246
+ console.error = (...args: unknown[]) => errors.push(args);
247
+ try {
248
+ reloadFromDisk();
249
+ } finally {
250
+ console.error = originalError;
251
+ }
252
+ assert.equal(getExcludedCount(), 0);
253
+ assert.equal(errors.length, 1);
254
+ assert.match(String(errors[0]?.[1]), /invalid reason/);
255
+ assert.deepEqual(filterFallbackCandidates(["openai/gpt-4", "anthropic/claude-3"]), ["openai/gpt-4", "anthropic/claude-3"]);
256
+ }
257
+ });
167
258
  });
@@ -6,13 +6,14 @@ import {
6
6
  formatSubagentModelVerificationError,
7
7
  isContextOverflow,
8
8
  isRetryableModelFailure,
9
+ isRetryableModelFailureAttempt,
9
10
  normalizeModelSegment,
10
11
  recordRetryableModelFailure,
11
12
  resolveEffectiveSubagentModel,
12
13
  resolveModelCandidate,
13
14
  resolveSubagentModelOverride,
14
15
  } from "../../src/runs/shared/model-fallback.ts";
15
- import { clearExclusions } from "../../src/runs/shared/model-exclusions.ts";
16
+ import { clearExclusions, recordModelFailure } from "../../src/runs/shared/model-exclusions.ts";
16
17
  import { resolveModelScopesForAgent } from "../../src/runs/shared/model-scope.ts";
17
18
 
18
19
  beforeEach(() => clearExclusions());
@@ -53,6 +54,38 @@ describe("model fallback helpers", () => {
53
54
  );
54
55
  });
55
56
 
57
+ it("accepts a bare leaf reported by an Anthropic gateway driver", () => {
58
+ const gatewayModels = [{
59
+ provider: "bifrost-anthropic",
60
+ id: "vertex/claude-fable-5",
61
+ fullId: "bifrost-anthropic/vertex/claude-fable-5",
62
+ }];
63
+ assert.equal(
64
+ formatSubagentModelVerificationError(
65
+ "bifrost-anthropic/vertex/claude-fable-5:high",
66
+ "claude-fable-5",
67
+ gatewayModels,
68
+ ),
69
+ undefined,
70
+ );
71
+ assert.match(
72
+ formatSubagentModelVerificationError(
73
+ "bifrost-anthropic/vertex/claude-fable-5:high",
74
+ "wrong-provider/claude-fable-5",
75
+ gatewayModels,
76
+ ) ?? "",
77
+ /model_verification_failed/,
78
+ );
79
+ assert.match(
80
+ formatSubagentModelVerificationError(
81
+ "bifrost-anthropic/vertex/claude-fable-5:high",
82
+ "claude-sonnet-4",
83
+ gatewayModels,
84
+ ) ?? "",
85
+ /model_verification_failed/,
86
+ );
87
+ });
88
+
56
89
  it("resolves unique owner/name ids when the owner is not a registered provider", () => {
57
90
  const registry = [
58
91
  ...availableModels,
@@ -125,12 +158,21 @@ describe("model fallback helpers", () => {
125
158
  });
126
159
 
127
160
  it("excludes a candidate after a retryable model failure is recorded", () => {
128
- recordRetryableModelFailure("openai/gpt-5-mini", "rate limit exceeded");
129
-
130
- assert.deepEqual(
131
- buildModelCandidates("gpt-5-mini", ["anthropic/claude-sonnet-4"], availableModels),
132
- ["anthropic/claude-sonnet-4"],
133
- );
161
+ const warnings: string[] = [];
162
+ const originalWarn = console.warn;
163
+ console.warn = (message: unknown) => warnings.push(String(message));
164
+ try {
165
+ recordRetryableModelFailure("openai/gpt-5-mini", "rate limit exceeded for Bearer secret-token-value");
166
+ assert.deepEqual(
167
+ buildModelCandidates("gpt-5-mini", ["anthropic/claude-sonnet-4"], availableModels),
168
+ ["anthropic/claude-sonnet-4"],
169
+ );
170
+ } finally {
171
+ console.warn = originalWarn;
172
+ }
173
+ assert.equal(warnings.length, 1);
174
+ assert.match(warnings[0]!, /Skipping model 'openai\/gpt-5-mini'.*reason: rate limit exceeded for \[redacted\]; expires: \d{4}-\d{2}-\d{2}T/);
175
+ assert.doesNotMatch(warnings[0]!, /secret-token-value/);
134
176
  });
135
177
 
136
178
  it("does not exclude a candidate after a task or tool failure", () => {
@@ -190,7 +232,12 @@ describe("model fallback helpers", () => {
190
232
  assert.equal(isRetryableModelFailure("Subagent produced no output (possible model cold-start or empty response)."), true);
191
233
  assert.equal(isRetryableModelFailure("model load failed"), true);
192
234
  assert.equal(isRetryableModelFailure("Stream ended without finish_reason"), true);
235
+ assert.equal(isRetryableModelFailure("Connection error"), true);
236
+ assert.equal(isRetryableModelFailure("APIConnectionError: Connection closed."), true);
237
+ assert.equal(isRetryableModelFailure("Connection reset by peer"), true);
193
238
  assert.equal(isRetryableModelFailure("Request timed out."), true);
239
+ assert.equal(isRetryableModelFailure("internal server error"), true);
240
+ assert.equal(isRetryableModelFailure("500"), true);
194
241
  });
195
242
 
196
243
  it("does not treat ordinary task/tool failures as retryable model failures", () => {
@@ -208,6 +255,13 @@ describe("model fallback helpers", () => {
208
255
  assert.equal(isRetryableModelFailure("Provider error: bash failed (exit 1): request timed out"), true);
209
256
  assert.equal(isRetryableModelFailure("bash failed (exit unknown): request timed out"), true);
210
257
  });
258
+
259
+ it("does not retry raw process stderr after child activity", () => {
260
+ assert.equal(isRetryableModelFailureAttempt({ error: "APIConnectionError: Connection closed.", messages: [{ role: "assistant" }], toolCount: 0 }), false);
261
+ assert.equal(isRetryableModelFailureAttempt({ error: "APIConnectionError: Connection closed.", messages: [{ role: "assistant", errorMessage: "APIConnectionError: Connection closed." }], toolCount: 0 }), true);
262
+ assert.equal(isRetryableModelFailureAttempt({ error: "APIConnectionError: Connection closed.", messages: [], toolCount: 0 }), true);
263
+ assert.equal(isRetryableModelFailureAttempt({ error: "APIConnectionError: Connection closed.", messages: [{ role: "assistant", errorMessage: "APIConnectionError: Connection closed." }], toolCount: 1 }), false);
264
+ });
211
265
  });
212
266
 
213
267
  describe("resolveSubagentModelOverride (cross-session inherit, issue #266)", () => {
@@ -264,6 +318,17 @@ describe("resolveSubagentModelOverride (cross-session inherit, issue #266)", ()
264
318
  );
265
319
  });
266
320
 
321
+ it("fails visibly when an explicit model is excluded instead of falling back", () => {
322
+ recordModelFailure({ modelId: "gpt-5-mini", provider: "openai", reason: "rate limit" });
323
+ assert.throws(
324
+ () => resolveEffectiveSubagentModel("openai/gpt-5-mini", undefined, parentModel, availableModels),
325
+ (error: unknown) => {
326
+ const message = String(error);
327
+ return message.includes("openai/gpt-5-mini") && message.includes("rate limit") && message.includes("expires:");
328
+ },
329
+ );
330
+ });
331
+
267
332
  it("resolves owner/name frontmatter models against the registry", () => {
268
333
  const models = [
269
334
  ...availableModels,
@@ -88,6 +88,27 @@ describe("tracked mutation evidence", () => {
88
88
  });
89
89
  });
90
90
 
91
+ it("does not invoke repository fsmonitor while collecting mutation evidence", () => {
92
+ withRepo((repo) => {
93
+ const marker = path.join(repo, "fsmonitor-invoked");
94
+ const hook = path.join(repo, "fsmonitor-hook.cjs");
95
+ fs.writeFileSync(hook, `require("node:fs").appendFileSync(${JSON.stringify(marker)}, "invoked\\n");\n`, "utf-8");
96
+ git(repo, ["config", "core.fsmonitor", `${JSON.stringify(process.execPath)} ${JSON.stringify(hook)}`]);
97
+ git(repo, ["status", "--short"]);
98
+ assert.equal(fs.existsSync(marker), true);
99
+ fs.rmSync(marker);
100
+ fs.writeFileSync(path.join(repo, "tracked.txt"), "dirty before child\n", "utf-8");
101
+
102
+ const snapshot = snapshotTrackedMutations(repo);
103
+ fs.writeFileSync(path.join(repo, "tracked.txt"), "dirty after child\n", "utf-8");
104
+ const evidence = collectTrackedMutationEvidence(snapshot, repo);
105
+
106
+ assert.equal(snapshot.unavailable, undefined);
107
+ assert.equal(evidence.attemptedMutation, true);
108
+ assert.equal(fs.existsSync(marker), false);
109
+ });
110
+ });
111
+
91
112
  it("uses tracked evidence as completion guard mutation proof", () => {
92
113
  const guard = evaluateCompletionMutationGuard({
93
114
  agent: "worker",
@@ -390,6 +390,7 @@ describe("nested control routing", () => {
390
390
  agent: "worker",
391
391
  cwd: root,
392
392
  systemPromptMode: "replace",
393
+ inheritGlobalContext: false,
393
394
  inheritProjectContext: false,
394
395
  inheritSkills: false,
395
396
  outputMode: "inline",
@@ -333,7 +333,7 @@ describe("nested event parsing and projection", () => {
333
333
  ts: 100,
334
334
  parentRunId: "root-run",
335
335
  parentStepIndex: 1,
336
- child: { ...child("nested-valid-tokens", "running", 100), totalTokens: { input: 10, output: 15, total: 25 } },
336
+ child: { ...child("nested-valid-tokens", "running", 100), totalTokens: { input: 10, output: 15, total: 25, window: 30, windowPeak: 40 } },
337
337
  });
338
338
  fs.writeFileSync(path.join(route.eventSink, "0000000000200-invalid-tokens.json"), `${JSON.stringify({
339
339
  type: "subagent.nested.updated",
@@ -347,7 +347,7 @@ describe("nested event parsing and projection", () => {
347
347
 
348
348
  const registry = projectNestedEvents(route);
349
349
 
350
- assert.deepEqual(registry.children.find((item) => item.id === "nested-valid-tokens")?.totalTokens, { input: 10, output: 15, total: 25 });
350
+ assert.deepEqual(registry.children.find((item) => item.id === "nested-valid-tokens")?.totalTokens, { input: 10, output: 15, total: 25, window: 30, windowPeak: 40 });
351
351
  assert.equal(registry.children.find((item) => item.id === "nested-invalid-tokens")?.totalTokens, undefined);
352
352
  });
353
353
 
@@ -472,3 +472,21 @@ describe("nested event parsing and projection", () => {
472
472
  assert.equal(secondProjection.children[0]?.state, "complete");
473
473
  });
474
474
  });
475
+
476
+ describe("nested session-name projection", () => {
477
+ it("keeps bounded root and step session names from async status", () => {
478
+ const summary = nestedSummaryFromAsyncStatus({
479
+ runId: "child-run",
480
+ mode: "single",
481
+ state: "running",
482
+ startedAt: 1,
483
+ steps: [{
484
+ agent: "reviewer",
485
+ sessionName: "reviewer: Inspect the changed auth middleware",
486
+ status: "running",
487
+ }],
488
+ }, "/tmp/child-run", { id: "child-run", parentRunId: "root-run", depth: 1, ts: 1 });
489
+ assert.equal(summary.sessionName, "reviewer: Inspect the changed auth middleware");
490
+ assert.equal(summary.steps?.[0]?.sessionName, "reviewer: Inspect the changed auth middleware");
491
+ });
492
+ });
@@ -10,6 +10,7 @@ import registerSubagentNotify, {
10
10
  type SubagentNotifyDetails,
11
11
  } from "../../src/runs/background/notify.ts";
12
12
  import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT } from "../../src/shared/types.ts";
13
+ import { createResultDeliveryOwnership } from "../../src/runs/background/result-delivery-ownership.ts";
13
14
 
14
15
  const COMPLETION_OWNER_ID = "completion-owner-a";
15
16
 
@@ -159,6 +160,47 @@ describe("registerSubagentNotify", () => {
159
160
  assert.equal(sent.length, 0);
160
161
  });
161
162
 
163
+ it("accepts only explicitly claimed predecessor-session completions", async () => {
164
+ const events = createEventBus();
165
+ const sent: unknown[] = [];
166
+ const pi = { events, sendMessage(message: unknown) { sent.push(message); } };
167
+ const state = { currentSessionId: "session-old" as string | null, completionOwnerId: COMPLETION_OWNER_ID };
168
+ const ownership = createResultDeliveryOwnership(state);
169
+ assert.equal(ownership.claimPredecessor("session-old", "session-old"), true);
170
+ state.currentSessionId = "session-new";
171
+ const notifier = registerSubagentNotify(pi as never, state, { batchConfig: { enabled: false }, ownership });
172
+ try {
173
+ assert.equal(await notifier.deliver(completionResult({ id: "predecessor", sessionId: "session-old" })), true);
174
+ assert.equal(await notifier.deliver(completionResult({ id: "foreign", sessionId: "session-foreign" })), false);
175
+ assert.equal(await notifier.deliver(completionResult({ id: "wrong-owner", sessionId: "session-old", completionOwnerId: "other-owner" })), false);
176
+ assert.equal(sent.length, 1);
177
+ } finally {
178
+ notifier.dispose();
179
+ }
180
+ });
181
+
182
+ it("rechecks session ownership before emitting a delayed batch", async () => {
183
+ const clock = createFakeClock();
184
+ const events = createEventBus();
185
+ const sent: unknown[] = [];
186
+ const pi = { events, sendMessage(message: unknown) { sent.push(message); } };
187
+ const state = { currentSessionId: "session-a" as string | null, completionOwnerId: COMPLETION_OWNER_ID };
188
+ const notifier = registerSubagentNotify(pi as never, state, {
189
+ batchConfig: { enabled: true, debounceMs: 150, maxWaitMs: 1000, stragglerDebounceMs: 75, stragglerMaxWaitMs: 400, stragglerWindowMs: 2000 },
190
+ timers: clock.api,
191
+ now: clock.now,
192
+ });
193
+ try {
194
+ const pending = notifier.deliver(completionResult({ id: "delayed-unclaimed" }));
195
+ state.currentSessionId = "session-b";
196
+ clock.advance(150);
197
+ assert.equal(await pending, false);
198
+ assert.equal(sent.length, 0);
199
+ } finally {
200
+ notifier.dispose();
201
+ }
202
+ });
203
+
162
204
  it("does not wake the session when background delivery explicitly disables triggerTurn", async () => {
163
205
  const { notifier, sent } = createPi("session-a");
164
206
  assert.equal(await notifier.deliver(completionResult({ id: "direct-silent", triggerTurn: false })), true);
@@ -320,7 +362,7 @@ describe("registerSubagentNotify", () => {
320
362
  assert.deepEqual(sent[0], {
321
363
  message: {
322
364
  customType: "subagent-notify",
323
- content: "Background task paused: **workflow**\n\nRun 'detaches' detached for intercom coordination.\n\nWorkflow run: workflow-1\nChild runs: detaches=child-1 (paused)",
365
+ content: "Background task paused: **workflow**\n\nRun 'detaches' detached for intercom coordination.\n\nChild outputs:\n- key=detaches run=child-1 status=paused\n Saved output: unavailable\n Preview: unavailable (no safe inline output)\n\nWorkflow run: workflow-1\nChild runs: detaches=child-1 (paused)",
324
366
  display: true,
325
367
  },
326
368
  options: { triggerTurn: true },
@@ -549,6 +591,89 @@ describe("completion formatting helpers", () => {
549
591
  assert.equal(parsed?.reconciledFromDetachedChild, "child-2");
550
592
  });
551
593
 
594
+ it("shows bounded child output paths and previews before workflow correlation metadata", () => {
595
+ const details = buildCompletionDetails({
596
+ id: "workflow-stopped",
597
+ runId: "workflow-stopped",
598
+ mode: "workflow",
599
+ agent: "workflow",
600
+ success: false,
601
+ state: "stopped",
602
+ summary: "Workflow stopped after one child completed.",
603
+ results: [
604
+ {
605
+ workflowKey: "review",
606
+ runId: "child-review",
607
+ agent: "worker",
608
+ success: true,
609
+ outputState: "present",
610
+ outputReference: "/tmp/review.md",
611
+ artifactPaths: { outputPath: "/tmp/legacy-review-path" },
612
+ output: `\u001b[31mReview heading\u001b[0m\n${"x".repeat(5_000)}`,
613
+ },
614
+ {
615
+ workflowKey: "stopped",
616
+ agent: "worker",
617
+ stopped: true,
618
+ outputState: "absent",
619
+ artifactPaths: { outputPath: "/tmp/stopped.md" },
620
+ },
621
+ ],
622
+ });
623
+
624
+ assert.equal(details.status, "stopped");
625
+ assert.deepEqual(details.childOutputs?.map(({ workflowKey, runId, status, savedOutputPath }) => ({ workflowKey, runId, status, savedOutputPath })), [
626
+ { workflowKey: "review", runId: "child-review", status: "completed", savedOutputPath: "/tmp/review.md" },
627
+ { workflowKey: "stopped", runId: undefined, status: "stopped", savedOutputPath: undefined },
628
+ ]);
629
+ assert.ok(Buffer.byteLength(details.childOutputs?.[0]?.preview ?? "", "utf8") <= 4 * 1024);
630
+
631
+ const content = formatSingleCompletion(details);
632
+ assert.match(content, /Child outputs:/);
633
+ assert.match(content, /key=review run=child-review status=completed/);
634
+ assert.match(content, /Saved output: \/tmp\/review\.md/);
635
+ assert.match(content, /Review heading/);
636
+ assert.doesNotMatch(content, /legacy-review-path/);
637
+ assert.match(content, /preview truncated/);
638
+ assert.match(content, /key=stopped run=unavailable status=stopped/);
639
+ assert.match(content, /Saved output: unavailable/);
640
+ assert.match(content, /Preview: unavailable \(no safe inline output\)/);
641
+ assert.doesNotMatch(content, /\u001b/);
642
+ assert.match(content, /Workflow run: workflow-stopped/);
643
+ assert.match(content, /Child runs: review=child-review \(completed\), stopped=unavailable \(stopped\)/);
644
+
645
+ const parsed = parseSubagentNotifyContent(content);
646
+ assert.equal(parsed?.workflowRunId, "workflow-stopped");
647
+ assert.match(parsed?.resultPreview ?? "", /Child outputs:/);
648
+ assert.match(parsed?.resultPreview ?? "", /Review heading/);
649
+ assert.doesNotMatch(parsed?.resultPreview ?? "", /Workflow run:/);
650
+ });
651
+
652
+ it("does not label artifact-only paths as saved workflow output", () => {
653
+ const details = buildCompletionDetails({
654
+ id: "workflow-artifact-only",
655
+ runId: "workflow-artifact-only",
656
+ mode: "workflow",
657
+ agent: "workflow",
658
+ success: true,
659
+ results: [{
660
+ workflowKey: "artifact-only",
661
+ runId: "child-artifact-only",
662
+ agent: "worker",
663
+ success: true,
664
+ outputState: "present",
665
+ output: "inline artifact-only report",
666
+ artifactPaths: { outputPath: "/tmp/pi/async/child-artifact-only" },
667
+ }],
668
+ });
669
+
670
+ const content = formatSingleCompletion(details);
671
+ assert.match(content, /key=artifact-only run=child-artifact-only status=completed/);
672
+ assert.match(content, /Saved output: unavailable/);
673
+ assert.match(content, /inline artifact-only report/);
674
+ assert.doesNotMatch(content, /\/tmp\/pi\/async\/child-artifact-only/);
675
+ });
676
+
552
677
  it("reports false when Pi rejects sendMessage synchronously", async () => {
553
678
  const pi = { events: createEventBus(), sendMessage() { throw new Error("runtime inactive"); } };
554
679
  const notifier = registerSubagentNotify(pi as never, { currentSessionId: "session-a" }, { batchConfig: { enabled: false } });
@@ -558,6 +683,9 @@ describe("completion formatting helpers", () => {
558
683
 
559
684
  it("buildCompletionDetails derives paused and stopped statuses", () => {
560
685
  assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, state: "paused", summary: "Paused after interrupt.", timestamp: 1 }).status, "paused");
686
+ assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, interrupted: true, summary: "interrupted", timestamp: 1 }).status, "paused");
687
+ const pausedWorkflow = buildCompletionDetails({ id: "workflow", agent: "workflow", mode: "workflow", state: "paused", results: [{ workflowKey: "failed", success: false }] });
688
+ assert.equal(pausedWorkflow.childOutputs?.[0]?.status, "failed");
561
689
  assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, summary: "boom", exitCode: 1, timestamp: 1 }).status, "failed");
562
690
  assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, summary: "terminated", exitCode: 1, processSignal: "SIGTERM", timestamp: 1 }).status, "stopped");
563
691
  assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, summary: "terminated", results: [{ success: false, exitCode: 1, processSignal: "SIGTERM" }], timestamp: 1 }).status, "stopped");
@@ -256,7 +256,7 @@ test("enabled tabs use a worktree sequence and successful Pi sessions get cleanu
256
256
  assert.doesNotMatch(viewer, /(?:&|;)\s*exit(?:\s|$)/);
257
257
 
258
258
  const progressDir = path.join(TEMP_ROOT_DIR, "orca-progress");
259
- const manifestDir = path.join(dir, ".selesai", "subagents", "views", "orca");
259
+ const manifestDir = path.join(dir, ".pi-subagents", "views", "orca");
260
260
  const manifestName = fs.readdirSync(manifestDir).find((name) => name.startsWith(`${runId}-2-`) && name.endsWith(".json"));
261
261
  assert.ok(manifestName);
262
262
  const manifest = await readManifestWithState(path.join(manifestDir, manifestName), "open");
@@ -272,7 +272,7 @@ test("enabled tabs use a worktree sequence and successful Pi sessions get cleanu
272
272
  assert.match(text, /done output/);
273
273
  const quotedSessionFile = `'${fs.realpathSync(sessionFile).replace(/'/g, `'"'"'`)}'`;
274
274
  assert.ok(text.includes(`completed. To remove the Pi session for this run, run rm -- ${quotedSessionFile}`));
275
- assert.doesNotMatch(text, /find ~\/\.pi\/agent\/sessions/);
275
+ assert.doesNotMatch(text, /find ~\/\.selesai[\\/]agent\/sessions/);
276
276
 
277
277
  const nestedCwd = path.join(dir, "packages", "app");
278
278
  fs.mkdirSync(nestedCwd, { recursive: true });