@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
@@ -0,0 +1,152 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import type { Message } from "@earendil-works/pi-ai";
4
+ import { ABORT_RECOVERY_PROMPT, planAbortRecovery } from "../../src/runs/shared/abort-recovery.ts";
5
+
6
+ function messages(...entries: unknown[]): Message[] {
7
+ return entries as Message[];
8
+ }
9
+
10
+ const progress = {
11
+ role: "assistant",
12
+ content: [{ type: "text", text: "Implemented the helper and running validation." }],
13
+ usage: { input: 10, output: 5, cacheRead: 0, cacheWrite: 0, cost: { total: 0 } },
14
+ };
15
+ const aborted = {
16
+ role: "assistant",
17
+ content: [],
18
+ stopReason: "aborted",
19
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: { total: 0 } },
20
+ };
21
+
22
+ function base(overrides: Partial<Parameters<typeof planAbortRecovery>[0]> = {}) {
23
+ return {
24
+ messages: messages(progress, aborted),
25
+ processSignal: undefined,
26
+ sessionAvailable: true,
27
+ alreadyResumed: false,
28
+ ...overrides,
29
+ };
30
+ }
31
+
32
+ describe("planAbortRecovery", () => {
33
+ it("does not recover an abort without verified compaction settlement", () => {
34
+ assert.deepEqual(planAbortRecovery(base()), { action: "settle", reason: "compaction settlement not verified" });
35
+ });
36
+
37
+ it("plans the same retained-session continuation for a compaction-induced abort", () => {
38
+ assert.deepEqual(planAbortRecovery(base({ afterCompactionSettlement: true, processSignal: "SIGTERM" })), { action: "resume", prompt: ABORT_RECOVERY_PROMPT });
39
+ });
40
+
41
+ it("accepts a completed tool result as useful progress", () => {
42
+ const toolProgress = messages(
43
+ { role: "assistant", content: [{ type: "toolCall", id: "call-1", name: "bash", arguments: {} }], usage: { output: 1 } },
44
+ { role: "toolResult", toolCallId: "call-1", isError: false, content: [{ type: "text", text: "tests passed" }] },
45
+ aborted,
46
+ );
47
+ assert.equal(planAbortRecovery(base({ messages: toolProgress, afterCompactionSettlement: true })).action, "resume");
48
+ });
49
+
50
+ it("fails closed without progress, a retained session, or a first-attempt permit", () => {
51
+ assert.equal(planAbortRecovery(base({ messages: messages(aborted) })).action, "settle");
52
+ assert.equal(planAbortRecovery(base({ sessionAvailable: false })).action, "settle");
53
+ assert.equal(planAbortRecovery(base({ alreadyResumed: true })).action, "settle");
54
+ });
55
+
56
+ it("does not reinterpret an ordinary empty completion as an abort", () => {
57
+ const emptyStop = { ...aborted, stopReason: "stop" };
58
+ assert.equal(planAbortRecovery(base({ messages: messages(progress, emptyStop), processSignal: undefined })).action, "settle");
59
+ });
60
+
61
+ it("does not treat a process signal alone as provider abort evidence", () => {
62
+ const emptyStop = { ...aborted, stopReason: "stop" };
63
+ assert.equal(planAbortRecovery(base({ messages: messages(progress, emptyStop), processSignal: "SIGKILL" })).action, "settle");
64
+ });
65
+
66
+ it("does not resume when a process signal accompanies abort evidence", () => {
67
+ assert.deepEqual(planAbortRecovery(base({ processSignal: "SIGTERM" })), { action: "settle", reason: "process terminated by signal" });
68
+ assert.deepEqual(planAbortRecovery(base({
69
+ messages: messages(progress),
70
+ error: "Connection reset by provider transport",
71
+ processSignal: "SIGKILL",
72
+ })), { action: "settle", reason: "process terminated by signal" });
73
+ });
74
+
75
+ it("excludes operator control, timeouts, budgets, schema and acceptance failures", () => {
76
+ for (const excluded of [
77
+ { stopped: true },
78
+ { interrupted: true },
79
+ { timedOut: true },
80
+ { toolBudgetExhausted: true },
81
+ { usageBudgetExhausted: true },
82
+ { structuredOutputFailed: true },
83
+ { acceptanceFailed: true },
84
+ ]) {
85
+ assert.equal(planAbortRecovery(base(excluded)).action, "settle");
86
+ }
87
+ });
88
+
89
+ it("fails closed while a tool call is unresolved", () => {
90
+ const inFlight = messages(
91
+ { role: "assistant", content: [{ type: "toolCall", id: "call-1", name: "bash", arguments: {} }], usage: { output: 1 } },
92
+ aborted,
93
+ );
94
+ assert.deepEqual(planAbortRecovery(base({ messages: inFlight, currentTool: "bash" })), { action: "settle", reason: "active tool 'bash' remains in flight" });
95
+ assert.deepEqual(planAbortRecovery(base({ messages: inFlight, currentTool: undefined })), { action: "settle", reason: "unresolved tool call remains in transcript" });
96
+ });
97
+
98
+ it("names the exact blocker when compaction abort recovery is unsafe", () => {
99
+ assert.deepEqual(planAbortRecovery(base({ sessionAvailable: false, afterCompactionSettlement: true })), {
100
+ action: "settle",
101
+ reason: "retained session unavailable",
102
+ diagnostic: "Compaction-induced child abort could not be resumed safely: retained session unavailable.",
103
+ });
104
+ assert.deepEqual(planAbortRecovery(base({ currentTool: "bash", afterCompactionSettlement: true })), {
105
+ action: "settle",
106
+ reason: "active tool 'bash' remains in flight",
107
+ diagnostic: "Compaction-induced child abort could not be resumed safely: active tool 'bash' remains in flight.",
108
+ });
109
+ });
110
+
111
+ it("requires terminal assistant abort evidence for compaction recovery", () => {
112
+ assert.deepEqual(planAbortRecovery(base({
113
+ messages: messages(progress),
114
+ processSignal: undefined,
115
+ error: "Connection reset by provider transport",
116
+ })), { action: "settle", reason: "compaction settlement not verified" });
117
+ assert.deepEqual(planAbortRecovery(base({
118
+ messages: messages(progress),
119
+ processSignal: undefined,
120
+ error: "Connection reset by provider transport",
121
+ afterCompactionSettlement: true,
122
+ })), { action: "settle", reason: "terminal assistant abort evidence not verified" });
123
+ assert.equal(planAbortRecovery(base({
124
+ messages: messages(progress, { ...aborted, stopReason: "error", errorMessage: "This operation was aborted" }),
125
+ processSignal: undefined,
126
+ error: "This operation was aborted",
127
+ afterCompactionSettlement: true,
128
+ })).action, "resume");
129
+ });
130
+
131
+ it("does not recover a generic transport abort after a normally settled compaction", () => {
132
+ const settled = {
133
+ role: "assistant",
134
+ content: [{ type: "text", text: "Compaction completed and the child settled normally." }],
135
+ stopReason: "stop",
136
+ usage: { input: 10, output: 5, cacheRead: 0, cacheWrite: 0, cost: { total: 0 } },
137
+ };
138
+ assert.deepEqual(planAbortRecovery(base({
139
+ messages: messages(progress, settled),
140
+ error: "Connection reset by provider transport",
141
+ afterCompactionSettlement: true,
142
+ })), { action: "settle", reason: "terminal assistant abort evidence not verified" });
143
+ });
144
+
145
+ it("does not reinterpret a network-flavored task tool failure as a provider abort", () => {
146
+ assert.equal(planAbortRecovery(base({
147
+ messages: messages(progress),
148
+ processSignal: undefined,
149
+ error: "bash failed (exit 1): request connection error",
150
+ })).action, "settle");
151
+ });
152
+ });
@@ -364,4 +364,52 @@ describe("active async capacity", () => {
364
364
  fs.rmSync(rootDir, { recursive: true, force: true });
365
365
  }
366
366
  });
367
+
368
+ it("reclaims only old failed runs with dead runners under the abandoned-timeout policy", () => {
369
+ const cases = [
370
+ { name: "dead old", state: "failed", pidLiveness: "dead" as const, lastActivityAt: 0, threshold: 1_000, releases: true },
371
+ { name: "dead recent", state: "failed", pidLiveness: "dead" as const, lastActivityAt: 9_500, threshold: 1_000, releases: false },
372
+ { name: "alive old", state: "failed", pidLiveness: "alive" as const, lastActivityAt: 0, threshold: 1_000, releases: false },
373
+ { name: "unknown old", state: "failed", pidLiveness: "unknown" as const, lastActivityAt: 0, threshold: 1_000, releases: false },
374
+ { name: "strict mode", state: "failed", pidLiveness: "dead" as const, lastActivityAt: 0, threshold: false as const, releases: false },
375
+ { name: "successful old", state: "complete", pidLiveness: "dead" as const, lastActivityAt: 0, threshold: 1_000, releases: false },
376
+ ];
377
+ for (const [index, testCase] of cases.entries()) {
378
+ const rootDir = tempRoot();
379
+ const asyncDir = path.join(rootDir, "runs", `run-${index}`);
380
+ try {
381
+ const handle = acquireActiveAsyncCapacity({ sessionId: "session-policy", limit: 1, runId: `run-${index}`, kind: "runner", asyncDir }, { rootDir });
382
+ assert.ok(handle);
383
+ handle.markStarted(`runner-${index}`);
384
+ writeJson(path.join(asyncDir, "status.json"), {
385
+ runId: `run-${index}`,
386
+ sessionId: "session-policy",
387
+ mode: "single",
388
+ state: testCase.state,
389
+ pid: 50_000 + index,
390
+ startedAt: 0,
391
+ lastActivityAt: testCase.lastActivityAt,
392
+ processTerminal: { version: 1, state: "unknown", runId: `run-${index}`, runnerProcessInstanceId: `runner-${index}`, reason: "stale-repair" },
393
+ });
394
+ const options = {
395
+ rootDir,
396
+ now: () => 10_000,
397
+ pidLiveness: () => testCase.pidLiveness,
398
+ abandonedSlotReleaseAfterMs: testCase.threshold,
399
+ };
400
+ const inspection = inspectActiveAsyncCapacityOwner({ runId: `run-${index}`, sessionId: "session-policy", asyncDir }, options);
401
+ assert.equal(inspection.release.state, testCase.releases ? "releasable" : "retained", testCase.name);
402
+ if (testCase.releases) {
403
+ assert.match(inspection.release.reason, /abandoned-timeout/);
404
+ assert.match(inspection.release.reason, /process proof unknown/);
405
+ assert.deepEqual(getActiveAsyncCapacitySnapshot("session-policy", 1, options), { used: 0, limit: 1 });
406
+ assert.match(fs.readFileSync(path.join(asyncDir, "events.jsonl"), "utf-8"), /"releasedBy":"abandoned-timeout"/);
407
+ } else {
408
+ assert.deepEqual(getActiveAsyncCapacitySnapshot("session-policy", 1, options), { used: 1, limit: 1 });
409
+ }
410
+ } finally {
411
+ fs.rmSync(rootDir, { recursive: true, force: true });
412
+ }
413
+ }
414
+ });
367
415
  });
@@ -7,7 +7,7 @@ import { afterEach, describe, it } from "node:test";
7
7
  import { handleManagementAction } from "../../src/agents/agent-management.ts";
8
8
  import { serializeAgent } from "../../src/agents/agent-serializer.ts";
9
9
  import { parseChain, serializeChain } from "../../src/agents/chain-serializer.ts";
10
- import { discoverAgents, discoverAgentsAll, type AgentConfig } from "../../src/agents/agents.ts";
10
+ import { discoverAgents, discoverAgentsAll, inspectAgentDefinitionDirectory, type AgentConfig } from "../../src/agents/agents.ts";
11
11
  import { parseFrontmatter } from "../../src/agents/frontmatter.ts";
12
12
  import { buildPiArgs } from "../../src/runs/shared/pi-args.ts";
13
13
  import { THINKING_LEVELS } from "../../src/shared/model-info.ts";
@@ -101,6 +101,38 @@ afterEach(() => {
101
101
  }
102
102
  });
103
103
 
104
+ describe("agent definition directory inspection", () => {
105
+ it("distinguishes absent, empty, candidates, unreadable, and non-directory paths", () => {
106
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-agent-inspection-"));
107
+ tempDirs.push(root);
108
+ const empty = path.join(root, "empty");
109
+ const candidates = path.join(root, "candidates");
110
+ const file = path.join(root, "not-directory");
111
+ fs.mkdirSync(empty);
112
+ fs.mkdirSync(candidates);
113
+ fs.writeFileSync(path.join(candidates, "worker.md"), "---\nname: worker\ndescription: Worker\n---\n", "utf-8");
114
+ fs.writeFileSync(file, "not a directory", "utf-8");
115
+ assert.equal(inspectAgentDefinitionDirectory(path.join(root, "absent")).state, "absent");
116
+ assert.equal(inspectAgentDefinitionDirectory(empty).state, "empty");
117
+ assert.deepEqual(inspectAgentDefinitionDirectory(candidates), { state: "candidates", files: [path.join(candidates, "worker.md")] });
118
+ assert.equal(inspectAgentDefinitionDirectory(file).state, "not-directory");
119
+ assert.equal(inspectAgentDefinitionDirectory(path.join(root, "blocked"), {
120
+ existsSync: () => true,
121
+ statSync: () => ({ isDirectory: () => true }),
122
+ readdirSync: () => { throw new Error("permission denied"); },
123
+ }).state, "unreadable");
124
+ });
125
+
126
+ it("reports cached builtin and configured project definition paths without synthesizing a root", () => withTempHome(() => {
127
+ const project = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-agent-report-"));
128
+ tempDirs.push(project);
129
+ writeJson(path.join(project, ".selesai", "settings.json"), { subagents: { projectRootResolution: "nearest" } });
130
+ const discovered = discoverAgents(project, "project");
131
+ assert.ok(discovered.directories.some((entry) => entry.source === "builtin"));
132
+ assert.deepEqual(discovered.directories.filter((entry) => entry.source === "project").map((entry) => entry.path), [path.join(project, ".agents"), path.join(project, ".selesai", "agents")]);
133
+ }));
134
+ });
135
+
104
136
  describe("folded frontmatter blocks", () => {
105
137
  it("folds ordinary lines and paragraph breaks for > and >-", () => {
106
138
  const expected = "first line second line\nthird line";
@@ -215,6 +247,28 @@ Review carefully.`.replace(" description:", "description:"));
215
247
  assert.deepEqual(discoverAgents(project, "project").agents.find((agent) => agent.name === "external")?.runner, external.runner);
216
248
  }));
217
249
 
250
+ it("keeps external-cli stop code-owned while refusing capability widening", () => withTempHome(() => {
251
+ const project = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-runner-capabilities-"));
252
+ tempDirs.push(project);
253
+ const agentPath = path.join(project, ".selesai", "agents", "external.md");
254
+ writeAgent(agentPath, `---\nname: external\ndescription: External runner\nrunner:\n type: external-cli\n command: node\n capabilities:\n steer: false\n---\nReview.`);
255
+ assert.deepEqual(discoverAgents(project, "project").agents.find((agent) => agent.name === "external")?.runner, {
256
+ type: "external-cli",
257
+ command: "node",
258
+ capabilities: { steer: false },
259
+ });
260
+
261
+ writeAgent(agentPath, `---\nname: external\ndescription: External runner\nrunner:\n type: external-cli\n command: node\n capabilities:\n stop: false\n---\nReview.`);
262
+ const narrowedStop = discoverAgents(project, "project");
263
+ assert.equal(narrowedStop.agents.some((agent) => agent.name === "external"), false);
264
+ assert.match(narrowedStop.agentDiagnostics?.[0]?.error ?? "", /capabilities has unsupported fields: stop/);
265
+
266
+ writeAgent(agentPath, `---\nname: external\ndescription: External runner\nrunner:\n type: external-cli\n command: node\n capabilities:\n steer: true\n---\nReview.`);
267
+ const widened = discoverAgents(project, "project");
268
+ assert.equal(widened.agents.some((agent) => agent.name === "external"), false);
269
+ assert.match(widened.agentDiagnostics?.[0]?.error ?? "", /steer may only be false; user config cannot widen code-owned external adapter capabilities/);
270
+ }));
271
+
218
272
  it("parses and serializes an external-job runner", () => withTempHome(() => {
219
273
  const project = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-external-job-agent-"));
220
274
  tempDirs.push(project);
@@ -270,6 +324,25 @@ Review carefully.`);
270
324
  assert.equal(discovered.agentDiagnostics?.[0]?.name, "broken");
271
325
  }));
272
326
 
327
+ it("follows symlinked agent directories once without hiding diagnostics", () => withTempHome(() => {
328
+ const project = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-symlinked-agent-dir-"));
329
+ tempDirs.push(project);
330
+ const legacyAgentsRoot = path.join(project, ".agents");
331
+ const sharedAgents = path.join(project, "shared-agents");
332
+ const linkedAgents = path.join(legacyAgentsRoot, "agents");
333
+ fs.mkdirSync(legacyAgentsRoot, { recursive: true });
334
+ fs.mkdirSync(sharedAgents, { recursive: true });
335
+ writeAgent(path.join(sharedAgents, "linked.md"), "---\nname: linked\ndescription: Linked agent\n---\nBody");
336
+ writeAgent(path.join(sharedAgents, "broken.md"), "---\nname: broken\ndescription: Broken\nrunner:\n type: unknown\n---\nBody");
337
+ fs.symlinkSync(sharedAgents, linkedAgents, process.platform === "win32" ? "junction" : "dir");
338
+ fs.symlinkSync(path.join(sharedAgents, "missing"), path.join(sharedAgents, "dangling"), process.platform === "win32" ? "junction" : "dir");
339
+ if (process.platform !== "win32") fs.symlinkSync(sharedAgents, path.join(sharedAgents, "loop"), "dir");
340
+
341
+ const discovered = discoverAgents(project, "project");
342
+ assert.equal(discovered.agents.find((agent) => agent.name === "linked")?.filePath, path.join(linkedAgents, "linked.md"));
343
+ assert.equal(discovered.agentDiagnostics?.find((diagnostic) => diagnostic.name === "broken")?.filePath, path.join(linkedAgents, "broken.md"));
344
+ }));
345
+
273
346
  it("keeps a lower-priority agent available when a project override is malformed", () => withTempHome(() => {
274
347
  const project = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-invalid-agent-shadow-"));
275
348
  tempDirs.push(project);
@@ -622,7 +695,6 @@ Do work
622
695
  defaultAsync: false,
623
696
  defaultTimeoutMs: 90_000,
624
697
  defaultToolTimeoutMs: 600_000,
625
- defaultTurnBudget: { maxTurns: 12, graceTurns: 2 },
626
698
  defaultAcceptance: { level: "none", reason: "lightweight lookup" },
627
699
  };
628
700
 
@@ -630,7 +702,6 @@ Do work
630
702
  assert.match(serialized, /^async: false$/m);
631
703
  assert.match(serialized, /^timeoutMs: 90000$/m);
632
704
  assert.match(serialized, /^toolTimeoutMs: 600000$/m);
633
- assert.match(serialized, /^turnBudget: \{"maxTurns":12,"graceTurns":2\}$/m);
634
705
  assert.match(serialized, /^acceptance: \{"level":"none","reason":"lightweight lookup"\}$/m);
635
706
  writeAgent(filePath, serialized);
636
707
 
@@ -638,7 +709,6 @@ Do work
638
709
  assert.equal(worker?.defaultAsync, false);
639
710
  assert.equal(worker?.defaultTimeoutMs, 90_000);
640
711
  assert.equal(worker?.defaultToolTimeoutMs, 600_000);
641
- assert.deepEqual(worker?.defaultTurnBudget, { maxTurns: 12, graceTurns: 2 });
642
712
  assert.deepEqual(worker?.defaultAcceptance, { level: "none", reason: "lightweight lookup" });
643
713
  });
644
714
 
@@ -867,6 +937,59 @@ Plan the work.
867
937
  assert.equal(agent.source, "package");
868
938
  }));
869
939
 
940
+ it("loads bare HTTPS git packages from project Pi settings", () => withTempHome(() => {
941
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-settings-bare-git-package-"));
942
+ tempDirs.push(dir);
943
+ const packageRoot = path.join(dir, ".selesai", "git", "github.com", "user", "repo");
944
+ writeJson(path.join(dir, ".selesai", "settings.json"), {
945
+ packages: ["https://github.com/user/repo.git"],
946
+ });
947
+ writeJson(path.join(packageRoot, "package.json"), {
948
+ name: "bare-git-workflow",
949
+ "pi-subagents": { agents: ["./agents"] },
950
+ });
951
+ writeAgent(path.join(packageRoot, "agents", "planner.md"), `---
952
+ name: planner
953
+ package: bare-git-workflow
954
+ description: Plan from a bare HTTPS git package.
955
+ ---
956
+
957
+ Plan the work.
958
+ `);
959
+
960
+ const agent = discoverAgents(dir, "both").agents.find((candidate) => candidate.name === "bare-git-workflow.planner");
961
+ assert.ok(agent);
962
+ assert.equal(agent.source, "package");
963
+ assert.equal(agent.packageSourceRoot, packageRoot);
964
+ }));
965
+
966
+ it("loads bare HTTP git packages from user Pi settings", () => withTempHome((home) => {
967
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-user-settings-bare-git-package-"));
968
+ tempDirs.push(dir);
969
+ const agentDir = path.join(home, ".selesai", "agent");
970
+ const packageRoot = path.join(agentDir, "git", "git.example.com", "team", "repo");
971
+ writeJson(path.join(agentDir, "settings.json"), {
972
+ packages: ["http://git.example.com/team/repo.git"],
973
+ });
974
+ writeJson(path.join(packageRoot, "package.json"), {
975
+ name: "user-bare-git-workflow",
976
+ "pi-subagents": { agents: ["./agents"] },
977
+ });
978
+ writeAgent(path.join(packageRoot, "agents", "reviewer.md"), `---
979
+ name: reviewer
980
+ package: user-bare-git-workflow
981
+ description: Review from a bare HTTP git package.
982
+ ---
983
+
984
+ Review the work.
985
+ `);
986
+
987
+ const agent = discoverAgents(dir, "both").agents.find((candidate) => candidate.name === "user-bare-git-workflow.reviewer");
988
+ assert.ok(agent);
989
+ assert.equal(agent.source, "package");
990
+ assert.equal(agent.packageSourceRoot, packageRoot);
991
+ }));
992
+
870
993
  it("discovers project package agents when cwd is nested below the project root", () => withTempHome(() => {
871
994
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-nested-package-discovery-"));
872
995
  tempDirs.push(dir);
@@ -1313,6 +1436,39 @@ Inspect code
1313
1436
  });
1314
1437
  });
1315
1438
 
1439
+ describe("agent frontmatter allowNestedSubagents", () => {
1440
+ it("serializes and parses explicit nested fanout authorization", () => {
1441
+ const agent: AgentConfig = {
1442
+ name: "delegator",
1443
+ description: "Delegator",
1444
+ systemPrompt: "Delegate focused work",
1445
+ systemPromptMode: "replace",
1446
+ inheritProjectContext: false,
1447
+ inheritSkills: false,
1448
+ source: "project",
1449
+ filePath: "/tmp/delegator.md",
1450
+ allowNestedSubagents: true,
1451
+ };
1452
+ assert.match(serializeAgent(agent), /allowNestedSubagents: true/);
1453
+
1454
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-agent-nested-fanout-"));
1455
+ tempDirs.push(dir);
1456
+ const agentsDir = path.join(dir, ".selesai", "agents");
1457
+ fs.mkdirSync(agentsDir, { recursive: true });
1458
+ fs.writeFileSync(path.join(agentsDir, "delegator.md"), `---
1459
+ name: delegator
1460
+ description: Delegator
1461
+ allowNestedSubagents: true
1462
+ ---
1463
+
1464
+ Delegate focused work
1465
+ `, "utf-8");
1466
+ const discovered = discoverAgents(dir, "project").agents.find((candidate) => candidate.name === "delegator");
1467
+ assert.equal(discovered?.allowNestedSubagents, true);
1468
+ assert.equal(discovered?.extraFields?.allowNestedSubagents, undefined);
1469
+ });
1470
+ });
1471
+
1316
1472
  describe("agent frontmatter thinking", () => {
1317
1473
  it("coerces frontmatter false strings to disabled thinking", () => {
1318
1474
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-agent-thinking-false-"));
@@ -1463,6 +1619,7 @@ describe("agent frontmatter prompt inheritance flags", () => {
1463
1619
  systemPrompt: "Do work",
1464
1620
  systemPromptMode: "replace",
1465
1621
  inheritProjectContext: true,
1622
+ inheritGlobalContext: true,
1466
1623
  inheritSkills: true,
1467
1624
  source: "project",
1468
1625
  filePath: "/tmp/worker.md",
@@ -1470,6 +1627,7 @@ describe("agent frontmatter prompt inheritance flags", () => {
1470
1627
 
1471
1628
  const serialized = serializeAgent(agent);
1472
1629
  assert.match(serialized, /inheritProjectContext: true/);
1630
+ assert.match(serialized, /inheritGlobalContext: true/);
1473
1631
  assert.match(serialized, /inheritSkills: true/);
1474
1632
  });
1475
1633
 
@@ -1482,6 +1640,7 @@ describe("agent frontmatter prompt inheritance flags", () => {
1482
1640
  name: worker
1483
1641
  description: Worker
1484
1642
  inheritProjectContext: true
1643
+ inheritGlobalContext: true
1485
1644
  inheritSkills: true
1486
1645
  ---
1487
1646
 
@@ -1491,8 +1650,27 @@ Do work
1491
1650
  const result = discoverAgents(dir, "project");
1492
1651
  const worker = result.agents.find((agent) => agent.name === "worker");
1493
1652
  assert.equal(worker?.inheritProjectContext, true);
1653
+ assert.equal(worker?.inheritGlobalContext, true);
1494
1654
  assert.equal(worker?.inheritSkills, true);
1495
1655
  });
1656
+
1657
+ it("defaults inheritGlobalContext to false when frontmatter omits it", () => {
1658
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-agent-prompt-inheritance-frontmatter-"));
1659
+ tempDirs.push(dir);
1660
+ const agentsDir = path.join(dir, ".selesai", "agents");
1661
+ fs.mkdirSync(agentsDir, { recursive: true });
1662
+ fs.writeFileSync(path.join(agentsDir, "worker.md"), `---
1663
+ name: worker
1664
+ description: Worker
1665
+ ---
1666
+
1667
+ Do work
1668
+ `, "utf-8");
1669
+
1670
+ const result = discoverAgents(dir, "project");
1671
+ const worker = result.agents.find((agent) => agent.name === "worker");
1672
+ assert.equal(worker?.inheritGlobalContext, false);
1673
+ });
1496
1674
  });
1497
1675
 
1498
1676
  describe("agent frontmatter subagentOnlyExtensions", () => {
@@ -2044,7 +2222,7 @@ Canonical prompt
2044
2222
  assert.equal(result.projectDir, path.join(dir, ".selesai", "agents"));
2045
2223
  });
2046
2224
 
2047
- it("discovers project chains from .pi/chains", () => {
2225
+ it("discovers project chains from .selesai/chains", () => {
2048
2226
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-project-chain-dirs-"));
2049
2227
  tempDirs.push(dir);
2050
2228
  fs.mkdirSync(path.join(dir, ".selesai", "agents"), { recursive: true });
@@ -2075,7 +2253,7 @@ Inspect canonical
2075
2253
  assert.equal(result.projectChainDir, path.join(dir, ".selesai", "chains"));
2076
2254
  });
2077
2255
 
2078
- it("prefers project .pi/chains over user chains on name collisions", () => {
2256
+ it("prefers project .selesai/chains over user chains on name collisions", () => {
2079
2257
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-project-chain-collision-"));
2080
2258
  const home = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-user-chain-home-"));
2081
2259
  tempDirs.push(dir, home);
@@ -60,6 +60,55 @@ describe("agent management config parsing", () => {
60
60
  assert.doesNotMatch(readText(result), /- scout \(builtin/);
61
61
  });
62
62
 
63
+ it("rejects management attempts to widen the reserved read-only Claude profile", () => {
64
+ const ctx = { cwd: tempDir, modelRegistry: { getAvailable: () => [] } };
65
+ const writerRunner = { type: "external-cli", adapter: "claude-code-writer", command: "claude" };
66
+ const unsafeCreate = handleCreate({ config: { name: "claude-code", description: "Unsafe shadow", scope: "project", runner: writerRunner } }, ctx);
67
+ assert.equal(unsafeCreate.isError, true);
68
+ assert.match(readText(unsafeCreate), /reserved for the read-only 'claude-code' adapter/);
69
+ const unsafeLocalName = handleCreate({ config: { name: "claude-code", package: "custom", description: "Unsafe local name", scope: "project", runner: writerRunner } }, ctx);
70
+ assert.equal(unsafeLocalName.isError, true);
71
+ assert.match(readText(unsafeLocalName), /Selection name 'claude-code' is reserved/);
72
+ const unsafeAlias = handleCreate({ config: { name: "aliased-writer", aliases: ["claude-code"], description: "Unsafe alias", scope: "project", runner: writerRunner } }, ctx);
73
+ assert.equal(unsafeAlias.isError, true);
74
+ assert.match(readText(unsafeAlias), /Selection name 'claude-code' is reserved/);
75
+
76
+ const readOnlyCreate = handleCreate({ config: { name: "claude-code", description: "Narrow shadow", scope: "project", runner: { type: "external-cli", adapter: "claude-code", command: "claude" } } }, ctx);
77
+ assert.equal(readOnlyCreate.isError, false);
78
+ const unsafeUpdate = handleUpdate({ agent: "claude-code", agentScope: "project", config: { runner: writerRunner } }, ctx);
79
+ assert.equal(unsafeUpdate.isError, true);
80
+ assert.match(readText(unsafeUpdate), /reserved for the read-only 'claude-code' adapter/);
81
+ assert.match(fs.readFileSync(path.join(tempDir, ".selesai", "agents", "claude-code.md"), "utf-8"), /adapter: claude-code\n/);
82
+
83
+ const writerCreate = handleCreate({ config: { name: "custom-writer", description: "Writer", scope: "project", runner: writerRunner } }, ctx);
84
+ assert.equal(writerCreate.isError, false);
85
+ const unsafeAliasUpdate = handleUpdate({ agent: "custom-writer", agentScope: "project", config: { aliases: ["claude-code"] } }, ctx);
86
+ assert.equal(unsafeAliasUpdate.isError, true);
87
+ assert.match(readText(unsafeAliasUpdate), /Selection name 'claude-code' is reserved/);
88
+ const unsafeRename = handleUpdate({ agent: "custom-writer", agentScope: "project", config: { name: "claude-code" } }, ctx);
89
+ assert.equal(unsafeRename.isError, true);
90
+ assert.match(readText(unsafeRename), /reserved for the read-only 'claude-code' adapter/);
91
+ });
92
+
93
+ it("rejects create, update, alias, and rename widening for Codex and Cursor", () => {
94
+ const ctx = { cwd: tempDir, modelRegistry: { getAvailable: () => [] } };
95
+ for (const [readOnly, writer, command] of [["codex-exec", "codex-exec-writer", "codex"], ["cursor-agent", "cursor-agent-writer", "cursor-agent"]] as const) {
96
+ const writerRunner = { type: "external-cli", adapter: writer, command };
97
+ assert.equal(handleCreate({ config: { name: readOnly, description: "Unsafe shadow", scope: "project", runner: writerRunner } }, ctx).isError, true);
98
+ assert.equal(handleCreate({ config: { name: readOnly, package: `custom-${readOnly}`, description: "Unsafe local name", scope: "project", runner: writerRunner } }, ctx).isError, true);
99
+ assert.equal(handleCreate({ config: { name: `${readOnly}-alias`, aliases: [readOnly], description: "Unsafe alias", scope: "project", runner: writerRunner } }, ctx).isError, true);
100
+
101
+ const readOnlyCreate = handleCreate({ config: { name: readOnly, description: "Narrow shadow", scope: "project", runner: { type: "external-cli", adapter: readOnly, command } } }, ctx);
102
+ assert.equal(readOnlyCreate.isError, false);
103
+ assert.equal(handleUpdate({ agent: readOnly, agentScope: "project", config: { runner: writerRunner } }, ctx).isError, true);
104
+
105
+ const customName = `custom-${writer}`;
106
+ assert.equal(handleCreate({ config: { name: customName, description: "Writer", scope: "project", runner: writerRunner } }, ctx).isError, false);
107
+ assert.equal(handleUpdate({ agent: customName, agentScope: "project", config: { aliases: [readOnly] } }, ctx).isError, true);
108
+ assert.equal(handleUpdate({ agent: customName, agentScope: "project", config: { name: readOnly } }, ctx).isError, true);
109
+ }
110
+ });
111
+
63
112
  it("lists valid agents and diagnoses malformed agent definitions", () => {
64
113
  const agentsDir = path.join(tempDir, ".selesai", "agents");
65
114
  fs.mkdirSync(agentsDir, { recursive: true });
@@ -499,7 +548,6 @@ Advise only.
499
548
  scope: "project",
500
549
  async: false,
501
550
  timeoutMs: 120_000,
502
- turnBudget: { maxTurns: 8, graceTurns: 2 },
503
551
  acceptance: { level: "none", reason: "lightweight reviewer" },
504
552
  outputMode: "file-only",
505
553
  },
@@ -512,7 +560,6 @@ Advise only.
512
560
  let content = fs.readFileSync(filePath, "utf-8");
513
561
  assert.match(content, /^async: false$/m);
514
562
  assert.match(content, /^timeoutMs: 120000$/m);
515
- assert.match(content, /^turnBudget: \{"maxTurns":8,"graceTurns":2\}$/m);
516
563
  assert.match(content, /^acceptance: \{"level":"none","reason":"lightweight reviewer"\}$/m);
517
564
  assert.match(content, /^outputMode: file-only$/m);
518
565
 
@@ -520,19 +567,17 @@ Advise only.
520
567
  assert.equal(got.isError, false);
521
568
  assert.match(readText(got), /Async: false/);
522
569
  assert.match(readText(got), /Timeout: 120000ms/);
523
- assert.match(readText(got), /Turn budget: \{"maxTurns":8,"graceTurns":2\}/);
524
570
  assert.match(readText(got), /Acceptance: \{"level":"none","reason":"lightweight reviewer"\}/);
525
571
  assert.match(readText(got), /Output mode: file-only/);
526
572
 
527
573
  const updated = handleUpdate(
528
- { agent: "background-reviewer", config: { async: true, timeoutMs: false, turnBudget: false, acceptance: "", outputMode: "inline" } },
574
+ { agent: "background-reviewer", config: { async: true, timeoutMs: false, acceptance: "", outputMode: "inline" } },
529
575
  ctx,
530
576
  );
531
577
  assert.equal(updated.isError, false);
532
578
  content = fs.readFileSync(filePath, "utf-8");
533
579
  assert.match(content, /^async: true$/m);
534
580
  assert.doesNotMatch(content, /^timeoutMs:/m);
535
- assert.doesNotMatch(content, /^turnBudget:/m);
536
581
  assert.doesNotMatch(content, /^acceptance:/m);
537
582
  assert.match(content, /^outputMode: inline$/m);
538
583
 
@@ -958,11 +1003,26 @@ Drive the failing test first.
958
1003
  assert.match(text, /^Builtin subagent models/m);
959
1004
  assert.match(text, /Current session model:\n openai\/gpt-5-mini/);
960
1005
  assert.match(text, /(?:^|\n)scout\n model:\n openai\/gpt-5-mini\n source: inherits current session model(?:\n|$)/);
1006
+ assert.match(text, /(?:^|\n)advisor\n model:\n openai\/gpt-5-mini\n source: inherits current session model(?:\n|$)/);
1007
+ assert.doesNotMatch(text, /advisor\n model:\n \(builtin definition not found\)/);
961
1008
  assert.match(text, /Available models in this session's registry/);
962
1009
  assert.match(text, / anthropic\/claude-sonnet-4\n openai\/gpt-5-mini/);
963
1010
  assert.match(text, /Use an exact provider\/id from this list when you pass model/);
964
1011
  });
965
1012
 
1013
+ it("resolves the advisor builtin alias in a filtered model mapping", () => {
1014
+ const result = handleManagementAction("models", { agent: "advisor" }, {
1015
+ cwd: tempDir,
1016
+ modelRegistry: { getAvailable: () => [{ provider: "openai", id: "gpt-5-mini" }] },
1017
+ model: { provider: "openai", id: "gpt-5-mini" },
1018
+ });
1019
+ const text = readText(result);
1020
+ assert.equal(result.isError, false);
1021
+ assert.match(text, /Agent: advisor/);
1022
+ assert.match(text, /Effective model:\n openai\/gpt-5-mini/);
1023
+ assert.doesNotMatch(text, /Builtin agent 'advisor' not found|source: missing/);
1024
+ });
1025
+
966
1026
  it("reports override source and disabled builtin state in runtime model mappings", () => {
967
1027
  const projectSettingsPath = path.join(tempDir, ".selesai", "settings.json");
968
1028
  fs.mkdirSync(path.dirname(projectSettingsPath), { recursive: true });
@@ -994,7 +1054,7 @@ Drive the failing test first.
994
1054
  assert.match(text, /Source: project override/);
995
1055
  assert.match(text, /Requested model setting:\n claude-sonnet-4/);
996
1056
  assert.match(text, /Disabled: true/);
997
- assert.match(text.replaceAll("\\", "/"), /Override file:\n .*\.pi\/settings\.json/);
1057
+ assert.match(text.replaceAll("\\", "/"), /Override file:\n .*\.selesai[\\/]settings\.json/);
998
1058
  });
999
1059
 
1000
1060
  it("rejects unknown builtin filters for runtime model mappings", () => {