@selesai/code 0.9.11 → 0.9.12

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 (580) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/config.d.ts +4 -6
  3. package/dist/config.js +10 -14
  4. package/dist/core/agent-session.d.ts +0 -1
  5. package/dist/core/agent-session.js +3 -49
  6. package/dist/core/extensions/types.d.ts +0 -2
  7. package/dist/core/settings-manager.d.ts +0 -2
  8. package/dist/core/settings-manager.js +0 -3
  9. package/dist/core/tools/index.d.ts +0 -1
  10. package/dist/core/tools/index.js +0 -1
  11. package/dist/defaults/settings.json +0 -1
  12. package/dist/extensions/model-prompt-injector/index.test.ts +0 -10
  13. package/dist/extensions/model-prompt-injector/index.ts +0 -3
  14. package/dist/extensions/pi-intercom/CHANGELOG.md +53 -16
  15. package/dist/extensions/pi-intercom/README.md +41 -8
  16. package/dist/extensions/pi-intercom/banner.png +0 -0
  17. package/dist/extensions/pi-intercom/broker/broker.ts +498 -215
  18. package/dist/extensions/pi-intercom/broker/client.ts +39 -12
  19. package/dist/extensions/pi-intercom/broker/extension.test.ts +102 -3
  20. package/dist/extensions/pi-intercom/broker/paths.test.ts +2 -2
  21. package/dist/extensions/pi-intercom/broker/protocol.ts +26 -0
  22. package/dist/extensions/pi-intercom/broker/spawn.test.ts +5 -20
  23. package/dist/extensions/pi-intercom/config.test.ts +41 -0
  24. package/dist/extensions/pi-intercom/config.ts +21 -0
  25. package/dist/extensions/pi-intercom/extension-api.ts +40 -0
  26. package/dist/extensions/pi-intercom/index.ts +362 -26
  27. package/dist/extensions/pi-intercom/intercom.integration.test.ts +819 -13
  28. package/dist/extensions/pi-intercom/package.json +1 -1
  29. package/dist/extensions/pi-intercom/project-agent.ts +2 -2
  30. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +8 -110
  31. package/dist/extensions/pi-intercom/types.ts +32 -5
  32. package/dist/extensions/pi-intercom/ui/inline-message.ts +6 -0
  33. package/dist/extensions/pi-subagents/.github/assets/subagent-fleet-inspector.png +0 -0
  34. package/dist/extensions/pi-subagents/.github/workflows/release.yml +28 -0
  35. package/dist/extensions/pi-subagents/.github/workflows/test.yml +51 -0
  36. package/dist/extensions/pi-subagents/.greptile/config.json +33 -0
  37. package/dist/extensions/pi-subagents/.greptile/files.json +8 -0
  38. package/dist/extensions/pi-subagents/.greptile/rules.md +16 -0
  39. package/dist/extensions/pi-subagents/CHANGELOG.md +309 -87
  40. package/dist/extensions/pi-subagents/README.md +15 -17
  41. package/dist/extensions/pi-subagents/agents/oracle.md +3 -1
  42. package/dist/extensions/pi-subagents/agents/researcher.md +32 -13
  43. package/dist/extensions/pi-subagents/agents/reviewer.md +13 -2
  44. package/dist/extensions/pi-subagents/agents/scout.md +2 -2
  45. package/dist/extensions/pi-subagents/agents/worker.md +2 -1
  46. package/dist/extensions/pi-subagents/async-retention-discovery-worker.mjs +180 -0
  47. package/dist/extensions/pi-subagents/docs/configuration.md +106 -27
  48. package/dist/extensions/pi-subagents/docs/extension-api.md +85 -21
  49. package/dist/extensions/pi-subagents/docs/missions.md +41 -4
  50. package/dist/extensions/pi-subagents/docs/models.md +52 -9
  51. package/dist/extensions/pi-subagents/docs/observability.md +103 -16
  52. package/dist/extensions/pi-subagents/docs/tool-reference.md +60 -45
  53. package/dist/extensions/pi-subagents/docs/watchdog.md +60 -3
  54. package/dist/extensions/pi-subagents/docs/workflows.md +181 -7
  55. package/dist/extensions/pi-subagents/index.ts +10 -1
  56. package/dist/extensions/pi-subagents/install.mjs +4 -4
  57. package/dist/extensions/pi-subagents/package-lock.json +1483 -1470
  58. package/dist/extensions/pi-subagents/package.json +6 -3
  59. package/dist/extensions/pi-subagents/prompts/council.md +60 -0
  60. package/dist/extensions/pi-subagents/prompts/parallel-review.md +5 -1
  61. package/dist/extensions/pi-subagents/prompts/review-loop.md +9 -5
  62. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +254 -0
  63. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +18 -17
  64. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +31 -9
  65. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +107 -140
  66. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +9 -12
  67. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +39 -0
  68. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +44 -92
  69. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +261 -368
  70. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +6 -2
  71. package/dist/extensions/pi-subagents/src/agents/agents.ts +388 -85
  72. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +14 -0
  73. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +418 -0
  74. package/dist/extensions/pi-subagents/src/agents/skills.ts +1 -1
  75. package/dist/extensions/pi-subagents/src/api/agents.ts +7 -0
  76. package/dist/extensions/pi-subagents/src/api/external-job-provider.ts +195 -0
  77. package/dist/extensions/pi-subagents/src/api/preflight.ts +88 -27
  78. package/dist/extensions/pi-subagents/src/api/project-panes.ts +2 -0
  79. package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -0
  80. package/dist/extensions/pi-subagents/src/extension/config.ts +20 -11
  81. package/dist/extensions/pi-subagents/src/extension/doctor.ts +14 -6
  82. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -4
  83. package/dist/extensions/pi-subagents/src/extension/index.ts +270 -187
  84. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +65 -28
  85. package/dist/extensions/pi-subagents/src/extension/rpc.ts +120 -11
  86. package/dist/extensions/pi-subagents/src/extension/schemas.ts +46 -64
  87. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +51 -37
  88. package/dist/extensions/pi-subagents/src/extension/tool-result.ts +19 -0
  89. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -2
  90. package/dist/extensions/pi-subagents/src/inspectors/herdr/client.ts +3 -3
  91. package/dist/extensions/pi-subagents/src/inspectors/herdr/focus.ts +55 -0
  92. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -1
  93. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +230 -46
  94. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +26 -4
  95. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +32 -10
  96. package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +6 -1
  97. package/dist/extensions/pi-subagents/src/missions/store.ts +4 -9
  98. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +2 -2
  99. package/dist/extensions/pi-subagents/src/policy/authority.ts +2 -0
  100. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +3 -1
  101. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +31 -8
  102. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +210 -52
  103. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +63 -33
  104. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +71 -17
  105. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +886 -0
  106. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +14 -5
  107. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +49 -56
  108. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +1 -0
  109. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +3 -33
  110. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +5 -0
  111. package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +5 -1
  112. package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +22 -12
  113. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +105 -71
  114. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +68 -19
  115. package/dist/extensions/pi-subagents/src/runs/background/index-segment.ts +59 -0
  116. package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +443 -0
  117. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +94 -10
  118. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +158 -90
  119. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +141 -53
  120. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +8 -5
  121. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +14 -4
  122. package/dist/extensions/pi-subagents/src/runs/background/run-id-query.ts +7 -0
  123. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +13 -14
  124. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +46 -25
  125. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +823 -0
  126. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +9 -3
  127. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +11 -1
  128. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +594 -216
  129. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +17 -13
  130. package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +138 -0
  131. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +23 -4
  132. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +82 -4
  133. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +5 -3
  134. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +21 -14
  135. package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +23 -2
  136. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +153 -58
  137. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -9
  138. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1283 -1657
  139. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +274 -0
  140. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +6 -5
  141. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +59 -10
  142. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
  143. package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +1 -3
  144. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +36 -0
  145. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +52 -2
  146. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  147. package/dist/extensions/pi-subagents/src/runs/shared/extension-bindings.ts +78 -0
  148. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +2 -0
  149. package/dist/extensions/pi-subagents/src/runs/shared/external-job-bridge.ts +460 -0
  150. package/dist/extensions/pi-subagents/src/runs/shared/external-job-runner.ts +389 -0
  151. package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +10 -0
  152. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
  153. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +243 -0
  154. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +201 -85
  155. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +106 -39
  156. package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +145 -0
  157. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +150 -27
  158. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +46 -4
  159. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +19 -15
  160. package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +5 -1
  161. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +86 -2
  162. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +9 -9
  163. package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +0 -6
  164. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +18 -4
  165. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +51 -7
  166. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +62 -21
  167. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +1 -3
  168. package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +1 -3
  169. package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +1 -23
  170. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +14 -2
  171. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +2 -2
  172. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +22 -2
  173. package/dist/extensions/pi-subagents/src/shared/capacity-resilient-json.ts +102 -0
  174. package/dist/extensions/pi-subagents/src/shared/completion-owner.ts +14 -0
  175. package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +49 -1
  176. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +59 -1
  177. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +12 -5
  178. package/dist/extensions/pi-subagents/src/shared/model-info.ts +20 -0
  179. package/dist/extensions/pi-subagents/src/shared/prompt-resources.ts +0 -40
  180. package/dist/extensions/pi-subagents/src/shared/settings.ts +11 -30
  181. package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +52 -0
  182. package/dist/extensions/pi-subagents/src/shared/types.ts +228 -49
  183. package/dist/extensions/pi-subagents/src/shared/utils.ts +11 -3
  184. package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +10 -0
  185. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +0 -162
  186. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +2 -5
  187. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +105 -13
  188. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +6 -36
  189. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +829 -0
  190. package/dist/extensions/pi-subagents/src/tui/fleet.ts +1415 -0
  191. package/dist/extensions/pi-subagents/src/tui/render.ts +78 -7
  192. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +1 -1
  193. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +1 -0
  194. package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +59 -51
  195. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +8 -2
  196. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +2 -1
  197. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +4 -4
  198. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +471 -86
  199. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +140 -0
  200. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  201. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +3 -0
  202. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +2 -0
  203. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +17 -0
  204. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +57 -0
  205. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +16 -0
  206. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +15 -0
  207. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +18 -0
  208. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +9 -0
  209. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +10 -0
  210. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +10 -0
  211. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +20 -0
  212. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +3 -0
  213. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +96 -0
  214. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +119 -0
  215. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +85 -0
  216. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +620 -0
  217. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +5 -0
  218. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +56 -0
  219. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +32 -0
  220. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +49 -0
  221. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +93 -0
  222. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +128 -0
  223. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +7 -0
  224. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +38 -0
  225. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +3 -0
  226. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +15 -0
  227. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +9 -0
  228. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +29 -0
  229. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +2 -0
  230. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +22 -0
  231. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +37 -0
  232. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +34 -0
  233. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +12 -0
  234. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +23 -0
  235. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +171 -0
  236. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +1261 -0
  237. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +20 -0
  238. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +54 -0
  239. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +22 -0
  240. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +13 -0
  241. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +358 -0
  242. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +77 -0
  243. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +512 -0
  244. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +43 -0
  245. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +121 -0
  246. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +82 -0
  247. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +17 -0
  248. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +7 -0
  249. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +210 -0
  250. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +15 -0
  251. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +54 -0
  252. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +4 -0
  253. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +55 -0
  254. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +2 -0
  255. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +5 -0
  256. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +30 -0
  257. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +206 -0
  258. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +15 -0
  259. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +106 -0
  260. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +19 -0
  261. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +356 -0
  262. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +297 -0
  263. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +60 -0
  264. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +15 -0
  265. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +18 -0
  266. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +28 -0
  267. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +3 -0
  268. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +10 -0
  269. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +68 -0
  270. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +106 -0
  271. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +51 -0
  272. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +6 -0
  273. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +35 -0
  274. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +37 -0
  275. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +40 -0
  276. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +37 -0
  277. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +52 -0
  278. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +10 -0
  279. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +35 -0
  280. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +24 -0
  281. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  282. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +70 -0
  283. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +26 -0
  284. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +36 -0
  285. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +19 -0
  286. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts +3 -0
  287. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +61 -0
  288. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +23 -0
  289. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +3 -0
  290. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +10 -0
  291. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +42 -0
  292. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +35 -0
  293. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +12 -0
  294. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +33 -0
  295. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +9 -0
  296. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +34 -0
  297. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +22 -0
  298. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +34 -0
  299. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
  300. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
  301. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
  302. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +102 -0
  303. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  304. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +21 -0
  305. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +19 -0
  306. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +20 -0
  307. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +23 -0
  308. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +12 -0
  309. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
  310. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
  311. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +22 -0
  312. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +23 -0
  313. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +26 -0
  314. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
  315. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +32 -0
  316. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +34 -0
  317. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
  318. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +52 -0
  319. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +54 -0
  320. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +33 -0
  321. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
  322. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
  323. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +95 -0
  324. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +77 -0
  325. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
  326. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
  327. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +28 -0
  328. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +11 -0
  329. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
  330. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +63 -0
  331. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +94 -0
  332. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +23 -0
  333. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
  334. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +14 -0
  335. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
  336. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +394 -0
  337. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +12 -0
  338. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +29 -0
  339. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +120 -0
  340. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +28 -0
  341. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +17 -0
  342. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +246 -0
  343. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +20 -0
  344. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +457 -0
  345. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +8 -0
  346. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +3 -0
  347. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +2 -0
  348. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +22 -0
  349. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +18 -0
  350. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +11 -0
  351. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +11 -0
  352. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +4 -0
  353. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +4 -0
  354. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +5 -0
  355. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +8 -0
  356. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +5 -0
  357. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +26 -0
  358. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +7 -0
  359. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +10 -0
  360. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +18 -0
  361. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +30 -0
  362. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +2 -0
  363. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +16 -0
  364. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +3 -0
  365. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +3 -0
  366. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +9 -0
  367. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +31 -0
  368. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +21 -0
  369. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +2 -0
  370. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +31 -0
  371. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +5 -0
  372. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +12 -0
  373. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +3 -0
  374. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +15 -0
  375. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +3 -0
  376. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/index.mjs +1 -1
  377. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +0 -54
  378. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1053 -414
  379. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +194 -4
  380. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +59 -4
  381. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +0 -77
  382. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +107 -6
  383. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +211 -897
  384. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +265 -375
  385. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +77 -7
  386. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +1 -1
  387. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +10 -10
  388. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +547 -30
  389. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +2112 -310
  390. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +274 -34
  391. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +1 -1
  392. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +20 -3
  393. package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +9 -0
  394. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +30 -5
  395. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
  396. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +4 -2
  397. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +1 -0
  398. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +197 -0
  399. package/dist/extensions/pi-subagents/test/unit/agent-contract.test.ts +14 -0
  400. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -1
  401. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +204 -27
  402. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +444 -145
  403. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +256 -4
  404. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +9 -7
  405. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +152 -22
  406. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +29 -0
  407. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +205 -23
  408. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +579 -0
  409. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +24 -0
  410. package/dist/extensions/pi-subagents/test/unit/authority-policy.test.ts +1 -0
  411. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +3 -3
  412. package/dist/extensions/pi-subagents/test/unit/capacity-resilient-json.test.ts +155 -0
  413. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +2 -2
  414. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +7 -0
  415. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +123 -0
  416. package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +81 -3
  417. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +109 -7
  418. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +2 -2
  419. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +5 -2
  420. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -2
  421. package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +79 -0
  422. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +22 -0
  423. package/dist/extensions/pi-subagents/test/unit/external-job-runner.test.ts +961 -0
  424. package/dist/extensions/pi-subagents/test/unit/file-system-retry.test.ts +64 -0
  425. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +1110 -0
  426. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +1664 -0
  427. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +81 -1
  428. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +138 -11
  429. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +41 -2
  430. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +210 -79
  431. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +95 -0
  432. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +596 -0
  433. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
  434. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +3 -5
  435. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +167 -0
  436. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +319 -18
  437. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +78 -0
  438. package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +19 -5
  439. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +44 -0
  440. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +129 -0
  441. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +231 -2
  442. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +28 -1
  443. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +106 -2
  444. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +204 -7
  445. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +59 -4
  446. package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +2 -6
  447. package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +34 -2
  448. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +116 -6
  449. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +12 -3
  450. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +56 -61
  451. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +416 -1
  452. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +9 -2
  453. package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +42 -0
  454. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +21 -30
  455. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +86 -11
  456. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +49 -1
  457. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +233 -5
  458. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +28 -5
  459. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +323 -20
  460. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +66 -10
  461. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +68 -5
  462. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +174 -0
  463. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +679 -0
  464. package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +67 -0
  465. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +55 -44
  466. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +399 -84
  467. package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +13 -0
  468. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  469. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +15 -10
  470. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +11 -7
  471. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -0
  472. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +40 -1
  473. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +10 -2
  474. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +38 -3
  475. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +110 -12
  476. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +204 -19
  477. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +25 -0
  478. package/dist/extensions/pi-subagents/test/unit/thinking-ceiling.test.ts +73 -0
  479. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +90 -50
  480. package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +1 -2
  481. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +234 -7
  482. package/dist/extensions/pi-subagents/test/unit/watch-strategy.test.ts +24 -0
  483. package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +36 -0
  484. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +37 -9
  485. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +302 -0
  486. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +167 -7
  487. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +113 -0
  488. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +6 -6
  489. package/dist/extensions/ponytail/index.js +31 -25
  490. package/dist/extensions/ponytail/ponytail-config.cjs +34 -7
  491. package/dist/extensions/ponytail/ponytail-instructions.cjs +7 -9
  492. package/dist/extensions/ponytail/test/extension.test.js +7 -8
  493. package/dist/extensions/ponytail/test/helpers.test.js +1 -1
  494. package/dist/skills/brandkit/SKILL.md +799 -0
  495. package/dist/skills/design-taste-frontend/SKILL.md +1207 -0
  496. package/dist/skills/design-taste-frontend-v1/SKILL.md +227 -0
  497. package/dist/skills/full-output-enforcement/SKILL.md +50 -0
  498. package/dist/skills/gpt-taste/SKILL.md +75 -0
  499. package/dist/skills/high-end-visual-design/SKILL.md +99 -0
  500. package/dist/skills/image-to-code/SKILL.md +1229 -0
  501. package/dist/skills/imagegen-frontend-mobile/SKILL.md +1466 -0
  502. package/dist/skills/imagegen-frontend-web/SKILL.md +988 -0
  503. package/dist/skills/industrial-brutalist-ui/SKILL.md +93 -0
  504. package/dist/skills/minimalist-ui/SKILL.md +86 -0
  505. package/dist/skills/pi-subagents/SKILL.md +18 -17
  506. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +31 -9
  507. package/dist/skills/pi-subagents/references/execution-controls.md +95 -143
  508. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +9 -12
  509. package/dist/skills/pi-subagents/references/multi-lane-orchestration.md +39 -0
  510. package/dist/skills/pi-subagents/references/prompting-and-roles.md +45 -94
  511. package/dist/skills/ponytail/SKILL.md +12 -8
  512. package/dist/skills/ponytail-audit/SKILL.md +0 -1
  513. package/dist/skills/ponytail-debt/SKILL.md +0 -1
  514. package/dist/skills/ponytail-gain/SKILL.md +0 -1
  515. package/dist/skills/ponytail-help/SKILL.md +4 -3
  516. package/dist/skills/ponytail-review/SKILL.md +0 -1
  517. package/dist/skills/redesign-existing-projects/SKILL.md +179 -0
  518. package/dist/skills/stitch-design-taste/SKILL.md +185 -0
  519. package/dist/skills/unlazy/CHANGELOG.md +100 -0
  520. package/dist/skills/unlazy/CONTRIBUTING.md +66 -0
  521. package/dist/skills/unlazy/LICENSE +21 -0
  522. package/dist/skills/unlazy/README.md +242 -0
  523. package/dist/skills/unlazy/SECURITY.md +70 -0
  524. package/dist/skills/unlazy/SKILL.md +100 -0
  525. package/dist/skills/unlazy/package.json +13 -0
  526. package/dist/skills/unlazy/references/dispatch.md +82 -0
  527. package/dist/skills/unlazy/references/gates.md +132 -0
  528. package/dist/skills/unlazy/references/method.md +49 -0
  529. package/dist/skills/unlazy/references/orchestration.md +99 -0
  530. package/dist/skills/unlazy/references/parallel.md +123 -0
  531. package/dist/skills/unlazy/references/token-economy.md +39 -0
  532. package/dist/skills/unlazy/research/validation-protocol.md +81 -0
  533. package/dist/skills/unlazy/scripts/dispatch-check.mjs +108 -0
  534. package/dist/skills/unlazy/scripts/gate-check.mjs +894 -0
  535. package/dist/skills/unlazy/scripts/gate-lint.mjs +178 -0
  536. package/dist/skills/unlazy/scripts/install-hooks.mjs +206 -0
  537. package/dist/skills/unlazy/scripts/lib/check-supervisor.mjs +46 -0
  538. package/dist/skills/unlazy/scripts/lib/dispatch.mjs +283 -0
  539. package/dist/skills/unlazy/scripts/lib/gates.mjs +629 -0
  540. package/dist/skills/unlazy/scripts/lib/process-tree.mjs +161 -0
  541. package/dist/skills/unlazy/scripts/lib/regex-worker.mjs +9 -0
  542. package/dist/skills/unlazy/scripts/stop-hook.mjs +174 -0
  543. package/dist/skills/unlazy/templates/PLAN.md +65 -0
  544. package/dist/skills/unlazy/templates/gates-leaf.md +51 -0
  545. package/dist/skills/unlazy/templates/gates-node.md +50 -0
  546. package/dist/skills/unlazy/tests/contract-tests.mjs +127 -0
  547. package/dist/skills/unlazy/tests/dispatch-tests.mjs +481 -0
  548. package/dist/skills/unlazy/tests/hardening-tests.mjs +903 -0
  549. package/dist/skills/unlazy/tests/lint-tests.mjs +242 -0
  550. package/dist/skills/unlazy/tests/run-tests.mjs +554 -0
  551. package/dist/skills/unlazy/tests/self-check.mjs +165 -0
  552. package/dist/skills/unlazy/tests/stress-tests.mjs +356 -0
  553. package/dist/skills/web-design-guidelines/SKILL.md +40 -0
  554. package/docs/settings.md +0 -5
  555. package/package.json +1 -1
  556. package/dist/core/tools/schema-prune.d.ts +0 -16
  557. package/dist/core/tools/schema-prune.js +0 -39
  558. package/dist/core/tools/schema-prune.test.d.ts +0 -1
  559. package/dist/core/tools/schema-prune.test.js +0 -111
  560. package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +0 -172
  561. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +0 -55
  562. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +0 -61
  563. package/dist/extensions/pi-subagents/src/agents/task-aware-routing.ts +0 -132
  564. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +0 -181
  565. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +0 -1354
  566. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +0 -1581
  567. package/dist/extensions/pi-subagents/src/shared/env.ts +0 -16
  568. package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +0 -71
  569. package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +0 -136
  570. package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +0 -41
  571. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +0 -466
  572. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +0 -1859
  573. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +0 -733
  574. package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +0 -63
  575. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +0 -326
  576. package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +0 -103
  577. package/dist/extensions/pi-subagents/test/unit/inline-subagents.test.ts +0 -127
  578. package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +0 -73
  579. package/dist/extensions/pi-subagents/test/unit/task-aware-routing.test.ts +0 -213
  580. package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +0 -65
@@ -0,0 +1,903 @@
1
+ #!/usr/bin/env node
2
+ // Security, parser, execution, and writeback regressions. Zero dependencies.
3
+
4
+ import {
5
+ appendFileSync, chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync,
6
+ readdirSync, realpathSync, renameSync, rmSync, symlinkSync, writeFileSync,
7
+ } from "node:fs";
8
+ import { execFile, spawnSync } from "node:child_process";
9
+ import { delimiter, dirname, join, win32 } from "node:path";
10
+ import { tmpdir } from "node:os";
11
+ import { fileURLToPath } from "node:url";
12
+ import {
13
+ terminateProcessTree, windowsTaskkillPath, WINDOWS_TASKKILL_TIMEOUT_MS,
14
+ } from "../scripts/lib/process-tree.mjs";
15
+
16
+ const HERE = dirname(fileURLToPath(import.meta.url));
17
+ const GATE_CHECK = join(HERE, "..", "scripts", "gate-check.mjs");
18
+ const STOP_HOOK = join(HERE, "..", "scripts", "stop-hook.mjs");
19
+ const WINDOWS_ENV = { SystemRoot: "C:\\Windows", WINDIR: "C:\\Windows", SystemDrive: "C:" };
20
+ const tests = [];
21
+ const test = (name, fn) => tests.push({ name, fn });
22
+
23
+ function sandbox() {
24
+ // macOS and custom TMPDIR values can expose lexical aliases (for example
25
+ // /var versus /private/var). Match the checker's canonical CWD semantics at
26
+ // the fixture boundary so every path assertion is portable.
27
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), "unlazy-hardening-")));
28
+ const approvals = realpathSync(mkdtempSync(join(tmpdir(), "unlazy-approval-")));
29
+ return {
30
+ dir, approvals,
31
+ path(rel) { return join(dir, rel); },
32
+ write(rel, text) {
33
+ const path = join(dir, rel);
34
+ mkdirSync(dirname(path), { recursive: true });
35
+ writeFileSync(path, text);
36
+ return path;
37
+ },
38
+ read(rel) { return readFileSync(join(dir, rel), "utf8"); },
39
+ cleanup() {
40
+ rmSync(dir, { recursive: true, force: true });
41
+ rmSync(approvals, { recursive: true, force: true });
42
+ },
43
+ };
44
+ }
45
+
46
+ function run(script, args, options = {}) {
47
+ return new Promise((done) => {
48
+ const child = execFile(process.execPath, [script, ...args], {
49
+ cwd: options.cwd,
50
+ encoding: "utf8",
51
+ maxBuffer: 16 * 1024 * 1024,
52
+ env: { ...process.env, ...(options.env || {}) },
53
+ }, (error, stdout, stderr) => {
54
+ done({ code: error ? (typeof error.code === "number" ? error.code : 1) : 0, out: (stdout || "") + (stderr || "") });
55
+ });
56
+ if (options.stdin !== undefined) child.stdin.end(options.stdin);
57
+ });
58
+ }
59
+
60
+ function gateRun(s, args, options = {}) {
61
+ const action = args.some((arg) => ["--status", "--claim", "--release", "--list-scopes", "--log", "--bind", "--help"].includes(arg));
62
+ const actual = options.approve !== false && !action && !args.includes("--approve") ? ["--approve", ...args] : args;
63
+ return run(GATE_CHECK, actual, {
64
+ cwd: s.dir,
65
+ env: { UNLAZY_APPROVAL_DIR: s.approvals, ...(options.env || {}) },
66
+ });
67
+ }
68
+
69
+ const gate = (id, title, check, expect, extra = "") =>
70
+ "- [ ] " + id + ": " + title + "\n" +
71
+ (check !== null ? " CHECK: " + check + "\n" : "") +
72
+ (expect !== null ? " EXPECT: " + expect + "\n" : "") + extra +
73
+ " EVIDENCE: pending\n";
74
+
75
+ const assert = (condition, message) => { if (!condition) throw new Error(message); };
76
+ const has = (text, value, label = "output") => assert(text.includes(value), label + " missing " + JSON.stringify(value) + "\n" + text);
77
+ const lacks = (text, value, label = "output") => assert(!text.includes(value), label + " unexpectedly includes " + JSON.stringify(value) + "\n" + text);
78
+
79
+ async function waitForPath(path, timeoutMs = 5000) {
80
+ const deadline = Date.now() + timeoutMs;
81
+ while (!existsSync(path)) {
82
+ if (Date.now() >= deadline) throw new Error("timed out waiting for " + path);
83
+ await new Promise((done) => setTimeout(done, 20));
84
+ }
85
+ }
86
+
87
+ function processExists(pid) {
88
+ try { process.kill(pid, 0); return true; }
89
+ catch (error) { return error.code === "EPERM"; }
90
+ }
91
+
92
+ async function waitForProcessExit(pid, timeoutMs = 5000) {
93
+ const deadline = Date.now() + timeoutMs;
94
+ while (processExists(pid)) {
95
+ if (Date.now() >= deadline) throw new Error("process " + pid + " remained alive after cleanup");
96
+ await new Promise((done) => setTimeout(done, 25));
97
+ }
98
+ }
99
+
100
+ test("approval: status is read-only and an unapproved CHECK is printed but not run", async () => {
101
+ const s = sandbox();
102
+ try {
103
+ s.write("check.mjs", "import { writeFileSync } from 'node:fs'; writeFileSync('ran.txt','yes'); console.log('OK');\n");
104
+ s.write("GATES.md", gate("G1", "approval", "node check.mjs", "OK"));
105
+ const before = s.read("GATES.md");
106
+ const status = await gateRun(s, ["--status"], { approve: false, env: { UNLAZY_SHELL: "definitely-missing-shell" } });
107
+ assert(status.code === 1, "status should report unmet");
108
+ assert(s.read("GATES.md") === before, "status changed ledger");
109
+ assert(!readdirSync(s.approvals).length, "status wrote approval state");
110
+ const denied = await gateRun(s, [], { approve: false });
111
+ assert(denied.code === 1, "unapproved run should remain unmet");
112
+ has(denied.out, "APPROVAL REQUIRED GATES:G1");
113
+ has(denied.out, "NOT RUN");
114
+ assert(!s.path("ran.txt") || !await fileExists(s.path("ran.txt")), "unapproved CHECK executed");
115
+ s.write("GATES.md", "- [x] G1: claimed\n CHECK: node check.mjs\n EXPECT: OK\n EVIDENCE: claimed\n");
116
+ const unverified = await gateRun(s, ["--reverify"], { approve: false });
117
+ assert(unverified.code === 1, "unapproved reverify must not certify existing evidence\n" + unverified.out);
118
+ has(unverified.out, "GATES:G1 (reverify not run)");
119
+ } finally { s.cleanup(); }
120
+ });
121
+
122
+ async function fileExists(path) {
123
+ try { await import("node:fs/promises").then((fs) => fs.access(path)); return true; }
124
+ catch { return false; }
125
+ }
126
+
127
+ test("approval: token binds the full oracle and changing CWD invalidates it", async () => {
128
+ const s = sandbox();
129
+ try {
130
+ for (const dir of ["a", "b"]) s.write(dir + "/check.mjs", "console.log('OK');\n");
131
+ s.write("GATES.md", gate("G1", "oracle", "node check.mjs", "OK", " CWD: a\n"));
132
+ const first = await gateRun(s, []);
133
+ assert(first.code === 0, first.out);
134
+ const tokens = readdirSync(s.approvals).filter((name) => name.endsWith(".json"));
135
+ assert(tokens.length === 1, "expected one approval token");
136
+ const token = JSON.parse(readFileSync(join(s.approvals, tokens[0]), "utf8"));
137
+ for (const key of ["check", "expect", "cwd", "shell", "timeoutMs", "maxOutputBytes", "regexTimeoutMs", "platform", "path"]) {
138
+ assert(Object.prototype.hasOwnProperty.call(token.oracle, key), "approval oracle missing " + key);
139
+ }
140
+ s.write("GATES.md", gate("G1", "oracle", "node check.mjs", "OK", " CWD: b\n"));
141
+ const changed = await gateRun(s, [], { approve: false });
142
+ assert(changed.code === 1, changed.out);
143
+ has(changed.out, "APPROVAL REQUIRED");
144
+ has(changed.out, join(s.dir, "b"));
145
+ } finally { s.cleanup(); }
146
+ });
147
+
148
+ test("approval: storage failures use the infrastructure exit code", async () => {
149
+ const s = sandbox();
150
+ try {
151
+ s.write("ok.mjs", "console.log('OK');\n");
152
+ s.write("GATES.md", gate("G1", "approval storage", "node ok.mjs", "OK"));
153
+ rmSync(s.approvals, { recursive: true, force: true });
154
+ writeFileSync(s.approvals, "not a directory\n");
155
+ const result = await gateRun(s, ["--approve"], {
156
+ approve: false,
157
+ env: { UNLAZY_APPROVAL_DIR: join(s.approvals, "records") },
158
+ });
159
+ assert(result.code === 2, "approval storage failure returned " + result.code + "\n" + result.out);
160
+ has(result.out, "infrastructure failure prevented 1 approval");
161
+ } finally { s.cleanup(); }
162
+ });
163
+
164
+ test("approval: a lexical outside alias cannot resolve back into the repository", async () => {
165
+ const s = sandbox();
166
+ try {
167
+ s.write("marker.mjs", "import { writeFileSync } from 'node:fs'; writeFileSync('ran.txt','yes'); console.log('OK');\n");
168
+ s.write("GATES.md", gate("G1", "canonical approval boundary", "node marker.mjs", "OK"));
169
+ const approved = await gateRun(s, []);
170
+ assert(approved.code === 0, approved.out);
171
+
172
+ const inside = s.path("repo-controlled-approvals");
173
+ renameSync(s.approvals, inside);
174
+ symlinkSync(inside, s.approvals, process.platform === "win32" ? "junction" : "dir");
175
+ rmSync(s.path("ran.txt"), { force: true });
176
+ s.write("GATES.md", gate("G1", "canonical approval boundary", "node marker.mjs", "OK"));
177
+
178
+ const replay = await gateRun(s, [], { approve: false });
179
+ assert(replay.code === 2, "aliased approval store returned " + replay.code + "\n" + replay.out);
180
+ has(replay.out, "could not validate approval");
181
+ assert(!existsSync(s.path("ran.txt")), "a repo-controlled approval alias authorized CHECK execution");
182
+ } finally { s.cleanup(); }
183
+ });
184
+
185
+ test("approval: an existing store must remain private to its owner", async () => {
186
+ if (process.platform === "win32") return;
187
+ const s = sandbox();
188
+ try {
189
+ s.write("ok.mjs", "console.log('OK');\n");
190
+ s.write("GATES.md", gate("G1", "private approval store", "node ok.mjs", "OK"));
191
+ chmodSync(s.approvals, 0o755);
192
+ const result = await gateRun(s, [], { approve: false });
193
+ assert(result.code === 2, result.out);
194
+ has(result.out, "must not grant group or other permissions");
195
+ } finally { s.cleanup(); }
196
+ });
197
+
198
+ test("approval: a FIFO record is rejected without blocking or executing", async () => {
199
+ if (process.platform === "win32") return;
200
+ const s = sandbox();
201
+ try {
202
+ s.write("marker.mjs", "import { writeFileSync } from 'node:fs'; writeFileSync('ran.txt','yes'); console.log('OK');\n");
203
+ s.write("GATES.md", gate("G1", "regular approval record", "node marker.mjs", "OK"));
204
+ const first = await gateRun(s, []);
205
+ assert(first.code === 0, first.out);
206
+ const token = readdirSync(s.approvals).find((name) => name.endsWith(".json"));
207
+ assert(token, "approval token was not created");
208
+ rmSync(join(s.approvals, token));
209
+ const made = spawnSync("mkfifo", [join(s.approvals, token)], { encoding: "utf8" });
210
+ assert(made.status === 0, "could not create FIFO fixture: " + made.stderr);
211
+ rmSync(s.path("ran.txt"), { force: true });
212
+ s.write("GATES.md", gate("G1", "regular approval record", "node marker.mjs", "OK"));
213
+
214
+ const started = Date.now();
215
+ const replay = await gateRun(s, [], { approve: false });
216
+ assert(replay.code === 2, replay.out);
217
+ assert(Date.now() - started < 1800, "FIFO approval record blocked");
218
+ has(replay.out, "could not validate approval");
219
+ assert(!existsSync(s.path("ran.txt")), "FIFO record authorized CHECK execution");
220
+ } finally { s.cleanup(); }
221
+ });
222
+
223
+ test("shell: resolution and PATH context are visible, invalid overrides are usage errors", async () => {
224
+ const s = sandbox();
225
+ try {
226
+ s.write("ok.mjs", "console.log('SHELL-OK');\n");
227
+ s.write("GATES.md", gate("G1", "shell", "node ok.mjs", "SHELL-OK"));
228
+ const good = await gateRun(s, []);
229
+ assert(good.code === 0, good.out);
230
+ has(good.out, "shell=");
231
+ has(good.out, "PATH=");
232
+ const ledger = s.read("GATES.md");
233
+ has(ledger, "exit=0; shell=");
234
+ has(ledger, "; path=");
235
+ s.write("GATES.md", gate("G1", "shell", "node ok.mjs", "SHELL-OK"));
236
+ const bad = await gateRun(s, ["--shell", "definitely-missing-shell"], { approve: false });
237
+ assert(bad.code === 2, bad.out);
238
+ has(bad.out, "cannot resolve command shell");
239
+ } finally { s.cleanup(); }
240
+ });
241
+
242
+ test("execution: exit zero and EXPECT must both pass", async () => {
243
+ const s = sandbox();
244
+ try {
245
+ s.write("bad.mjs", "console.log('MATCH'); process.exitCode = 7;\n");
246
+ s.write("GATES.md", gate("G1", "nonzero", "node bad.mjs", "MATCH"));
247
+ const result = await gateRun(s, []);
248
+ assert(result.code === 1, result.out);
249
+ has(result.out, "exit=7; EXPECT=matched");
250
+ has(s.read("GATES.md"), "- [ ] G1");
251
+ } finally { s.cleanup(); }
252
+ });
253
+
254
+ test("execution: failure summaries retain early assertion diagnostics", async () => {
255
+ const s = sandbox();
256
+ try {
257
+ s.write("bad.mjs", [
258
+ "console.error('AssertionError: expected 3 but received 4');",
259
+ "for (let index = 0; index < 20; index++) console.error('trailer-' + index);",
260
+ "process.exitCode = 1;",
261
+ "",
262
+ ].join("\n"));
263
+ s.write("GATES.md", gate("G1", "diagnostic", "node bad.mjs", "WILL-NOT-MATCH"));
264
+ const result = await gateRun(s, []);
265
+ assert(result.code === 1, result.out);
266
+ has(result.out, "AssertionError: expected 3 but received 4");
267
+ has(result.out, "trailer-19");
268
+ } finally { s.cleanup(); }
269
+ });
270
+
271
+ test("evidence: successful output is fingerprinted instead of persisted or echoed", async () => {
272
+ const s = sandbox();
273
+ const sentinel = "token-shaped-private-value-7f13b9";
274
+ try {
275
+ s.write("private.mjs", "console.log('" + sentinel + "'); console.log('CHECK_OK');\n");
276
+ s.write("GATES.md", gate("G1", "private output", "node private.mjs", "CHECK_OK"));
277
+ const result = await gateRun(s, []);
278
+ assert(result.code === 0, result.out);
279
+ lacks(result.out, sentinel, "success transcript");
280
+ has(result.out, "output=sha256=");
281
+ const ledger = s.read("GATES.md");
282
+ lacks(ledger, sentinel, "persisted evidence");
283
+ has(ledger, "EXPECT=matched; output-sha256=");
284
+ has(ledger, "; output-bytes=");
285
+ } finally { s.cleanup(); }
286
+ });
287
+
288
+ test("terminal: repository titles and diagnostics cannot emit controls or bidi overrides", async () => {
289
+ const s = sandbox();
290
+ try {
291
+ s.write("GATES.md", "- [ ] G1: title\u001b]0;owned\u0007\u202eevil\n EVIDENCE: pending\n");
292
+ const status = await gateRun(s, ["--status"], { approve: false });
293
+ assert(status.code === 1, status.out);
294
+ assert(!/[\u001b\u0007\u202e]/.test(status.out), "terminal controls survived status output");
295
+
296
+ s.write("bad.mjs", "console.error('failure\\u001b[31m red\\u001b[0m'); process.exitCode=1;\n");
297
+ s.write("GATES.md", gate("G2", "controlled failure", "node bad.mjs", "NEVER"));
298
+ const failed = await gateRun(s, []);
299
+ assert(failed.code === 1, failed.out);
300
+ assert(!failed.out.includes("\u001b"), "terminal escape survived failure output");
301
+ has(failed.out, "failure [31m red [0m");
302
+ } finally { s.cleanup(); }
303
+ });
304
+
305
+ test("reverify: a stale success is demoted and a reproducible success stays met", async () => {
306
+ const s = sandbox();
307
+ try {
308
+ s.write("bad.mjs", "console.log('WRONG');\n");
309
+ s.write("GATES.md", "- [x] G1: forged\n CHECK: node bad.mjs\n EXPECT: RIGHT\n EVIDENCE: forged\n");
310
+ const plain = await gateRun(s, []);
311
+ assert(plain.code === 0, plain.out);
312
+ const failed = await gateRun(s, ["--reverify"]);
313
+ assert(failed.code === 1, failed.out);
314
+ has(failed.out, "FAIL GATES:G1");
315
+ has(s.read("GATES.md"), "- [ ] G1");
316
+ has(s.read("GATES.md"), "EVIDENCE: pending");
317
+ s.write("good.mjs", "console.log('RIGHT');\n");
318
+ s.write("GATES.md", "- [x] G1: real\n CHECK: node good.mjs\n EXPECT: RIGHT\n EVIDENCE: RIGHT\n");
319
+ const passed = await gateRun(s, ["--reverify"]);
320
+ assert(passed.code === 0, passed.out);
321
+ has(passed.out, "reverified: 1");
322
+ } finally { s.cleanup(); }
323
+ });
324
+
325
+ test("parser: fenced examples are ignored and CRLF plus missing EVIDENCE are preserved", async () => {
326
+ const s = sandbox();
327
+ try {
328
+ s.write("ok.mjs", "console.log('OK');\n");
329
+ s.write("GATES.md", [
330
+ "- [ ] G1: real", " CHECK: node ok.mjs", " EXPECT: OK",
331
+ "```markdown", "- [ ] BAD: example", " CHECK: node absent.mjs", " EXPECT: BAD", "```", "",
332
+ ].join("\r\n"));
333
+ const result = await gateRun(s, []);
334
+ assert(result.code === 0, result.out);
335
+ lacks(result.out, "BAD:");
336
+ const after = s.read("GATES.md");
337
+ has(after, "- [x] G1: real\r\n");
338
+ has(after, "EVIDENCE: exit=0;");
339
+ assert(!/(^|[^\r])\n/.test(after), "write introduced bare LF");
340
+ } finally { s.cleanup(); }
341
+ });
342
+
343
+ test("parser: Markdown fence length and closing syntax are respected", async () => {
344
+ const s = sandbox();
345
+ try {
346
+ s.write("ok.mjs", "console.log('REAL-OK');\n");
347
+ s.write("GATES.md", [
348
+ "# Gates",
349
+ "",
350
+ "````markdown",
351
+ "```",
352
+ "- [ ] FAKE: remains fenced",
353
+ " CHECK: node absent.mjs",
354
+ " EXPECT: FAKE",
355
+ "```not-a-closing-fence",
356
+ "````",
357
+ "",
358
+ "- [ ] G1: real gate after the fence",
359
+ " CHECK: node ok.mjs",
360
+ " EXPECT: REAL-OK",
361
+ " EVIDENCE: pending",
362
+ "",
363
+ ].join("\n"));
364
+ const result = await gateRun(s, []);
365
+ assert(result.code === 0, result.out);
366
+ lacks(result.out, "FAKE:");
367
+ has(result.out, "PASS GATES:G1");
368
+ } finally { s.cleanup(); }
369
+ });
370
+
371
+ test("parser: malformed ledgers are usage errors in checker and blocking in hook", async () => {
372
+ const cases = [
373
+ ["zero", "# Gates only\n", "zero live gates"],
374
+ ["duplicate", "- [ ] G1: a\n EVIDENCE: pending\n- [ ] G1: b\n EVIDENCE: pending\n", "duplicate gate id"],
375
+ ["blank abandon", "- [ ] G1: a\n EVIDENCE: pending\nABANDON: G1\n", "non-blank reason"],
376
+ ["invalid regex", "- [ ] G1: a\n CHECK: node x.mjs\n EXPECT: /[/\n EVIDENCE: pending\n", "invalid EXPECT regex"],
377
+ ["incomplete", "- [ ] G1: a\n CHECK: node x.mjs\n EVIDENCE: pending\n", "require both"],
378
+ ["unindented", "- [ ] G1: a\nCHECK: node x.mjs\nEXPECT: OK\nEVIDENCE: pending\n", "unindented"],
379
+ ["orphan attribute", " CHECK: node x.mjs\n- [ ] G1: manual\n EVIDENCE: pending\n", "orphan CHECK"],
380
+ ["missing id", "- [ ] outcome without id\n EVIDENCE: pending\n", "explicit ID"],
381
+ ["blank outcome", "- [ ] G1:\n EVIDENCE: pending\n", "outcome is blank"],
382
+ ["unknown abandon", "- [x] G1: done\n EVIDENCE: measured\nABANDON: TYPO wrong id\n", "ABANDON references unknown gate"],
383
+ ];
384
+ for (const [name, text, expected] of cases) {
385
+ const s = sandbox();
386
+ try {
387
+ s.write("GATES.md", text);
388
+ const status = await gateRun(s, ["--status"], { approve: false });
389
+ assert(status.code === 2, name + " should exit 2\n" + status.out);
390
+ has(status.out, expected, name);
391
+ const hook = await run(STOP_HOOK, [], { cwd: s.dir, stdin: JSON.stringify({ cwd: s.dir, session_id: name }) });
392
+ has(hook.out, '"decision":"block"', name + " hook");
393
+ has(hook.out, "PARSE", name + " hook");
394
+ } finally { s.cleanup(); }
395
+ }
396
+ });
397
+
398
+ test("regex: a decisive regex succeeds and catastrophic matching is bounded", async () => {
399
+ const s = sandbox();
400
+ try {
401
+ s.write("good.mjs", "console.log('total: 42 items');\n");
402
+ s.write("GATES.md", gate("G1", "regex", "node good.mjs", "/total: \\d+ items/"));
403
+ const good = await gateRun(s, []);
404
+ assert(good.code === 0, good.out);
405
+ s.write("evil.mjs", "console.log('a'.repeat(30000) + '!');\n");
406
+ s.write("GATES.md", gate("G1", "bounded", "node evil.mjs", "/(a+)+$/"));
407
+ const start = Date.now();
408
+ const bad = await gateRun(s, []);
409
+ const elapsed = Date.now() - start;
410
+ assert(bad.code === 1, bad.out);
411
+ has(bad.out, "EXPECT regex exceeded 250ms");
412
+ assert(elapsed < 5000, "regex run was not bounded: " + elapsed + "ms");
413
+ } finally { s.cleanup(); }
414
+ });
415
+
416
+ test("regex: worker startup is outside the match budget and concurrency is capped", async () => {
417
+ const s = sandbox();
418
+ try {
419
+ s.write("simple.mjs", "console.log('total: 42 items');\n");
420
+ let ledger = "";
421
+ for (let index = 1; index <= 32; index++) {
422
+ ledger += gate("G" + index, "simple regex " + index, "node simple.mjs", "/total: \\d+ items/");
423
+ }
424
+ s.write("GATES.md", ledger);
425
+ const result = await gateRun(s, ["--jobs", "32", "--timeout", "10"]);
426
+ assert(result.code === 0, result.out);
427
+ lacks(result.out, "worker startup exceeded");
428
+ lacks(result.out, "EXPECT regex exceeded");
429
+ has(result.out, "PASS GATES:G32");
430
+ } finally { s.cleanup(); }
431
+ });
432
+
433
+ test("execution: output is capped and overflow cannot certify a gate", async () => {
434
+ const s = sandbox();
435
+ try {
436
+ s.write("large.mjs", "process.stdout.write('x'.repeat(1100000)); console.log('FINAL');\n");
437
+ s.write("GATES.md", gate("G1", "bounded output", "node large.mjs", "FINAL"));
438
+ const result = await gateRun(s, []);
439
+ assert(result.code === 1, result.out.slice(-2000));
440
+ has(result.out, "output exceeded 1048576 bytes");
441
+ assert(result.out.length < 1100000, "transcript leaked the full output");
442
+ } finally { s.cleanup(); }
443
+ });
444
+
445
+ test("jobs: rolling concurrency is opt-in and output stays in gate order", async () => {
446
+ const s = sandbox();
447
+ try {
448
+ for (let index = 1; index <= 3; index++) {
449
+ s.write("g" + index + ".mjs",
450
+ "import { appendFileSync } from 'node:fs';\n" +
451
+ "appendFileSync('order.log','start" + index + "\\n');\n" +
452
+ "setTimeout(()=>{appendFileSync('order.log','end" + index + "\\n'); console.log('OK" + index + "');},250);\n");
453
+ }
454
+ s.write("GATES.md", gate("G1", "one", "node g1.mjs", "OK1") +
455
+ gate("G2", "two", "node g2.mjs", "OK2") + gate("G3", "three", "node g3.mjs", "OK3"));
456
+ const result = await gateRun(s, ["--jobs", "2"]);
457
+ assert(result.code === 0, result.out);
458
+ const order = s.read("order.log").trim().split(/\r?\n/);
459
+ const firstEnd = order.findIndex((line) => line.startsWith("end"));
460
+ assert(firstEnd >= 2, "two checks did not overlap: " + order.join(","));
461
+ assert(result.out.indexOf("PASS GATES:G1") < result.out.indexOf("PASS GATES:G2") &&
462
+ result.out.indexOf("PASS GATES:G2") < result.out.indexOf("PASS GATES:G3"), "transcript order was nondeterministic\n" + result.out);
463
+ } finally { s.cleanup(); }
464
+ });
465
+
466
+ test("jobs: runner waits for stdio close so delayed descendant output is visible", async () => {
467
+ const s = sandbox();
468
+ try {
469
+ s.write("delayed.mjs",
470
+ "import { spawn } from 'node:child_process';\n" +
471
+ "spawn(process.execPath,['-e',\"setTimeout(()=>console.log('LATE_OK'),250)\"],{stdio:['ignore','inherit','inherit']});\n");
472
+ s.write("GATES.md", gate("G1", "late output", "node delayed.mjs", "LATE_OK"));
473
+ const result = await gateRun(s, []);
474
+ assert(result.code === 0, result.out);
475
+ has(result.out, "PASS GATES:G1");
476
+ has(result.out, "output=sha256=");
477
+ // EXPECT is printed during approval; the successful process output itself
478
+ // is represented only by the fingerprint above.
479
+ } finally { s.cleanup(); }
480
+ });
481
+
482
+ test("writeback: a result cannot certify a gate whose oracle changed in flight", async () => {
483
+ const s = sandbox();
484
+ try {
485
+ s.write("slow.mjs", "import { writeFileSync } from 'node:fs'; writeFileSync('started.txt','yes'); setTimeout(()=>console.log('OLD'),800);\n");
486
+ s.write("GATES.md", gate("G1", "stale", "node slow.mjs", "OLD"));
487
+ const running = gateRun(s, []);
488
+ await waitForPath(s.path("started.txt"));
489
+ s.write("GATES.md", gate("G1", "stale", "node slow.mjs", "NEW"));
490
+ const result = await running;
491
+ has(result.out, "STALE GATES:G1");
492
+ const after = s.read("GATES.md");
493
+ has(after, "EXPECT: NEW");
494
+ has(after, "- [ ] G1");
495
+ } finally { s.cleanup(); }
496
+ });
497
+
498
+ test("reverify: a stale in-flight result cannot leave old evidence falsely green", async () => {
499
+ const s = sandbox();
500
+ try {
501
+ s.write("slow.mjs", "import { writeFileSync } from 'node:fs'; writeFileSync('started.txt','yes'); setTimeout(()=>console.log('OLD'),800);\n");
502
+ s.write("GATES.md", "- [x] G1: stale met\n CHECK: node slow.mjs\n EXPECT: OLD\n EVIDENCE: old evidence\n");
503
+ const running = gateRun(s, ["--reverify"]);
504
+ await waitForPath(s.path("started.txt"));
505
+ s.write("GATES.md", "- [x] G1: stale met\n CHECK: node slow.mjs\n EXPECT: NEW\n EVIDENCE: old evidence\n");
506
+ const result = await running;
507
+ assert(result.code === 1, "stale reverify returned " + result.code + "\n" + result.out);
508
+ has(result.out, "STALE GATES:G1");
509
+ has(result.out, "GATES:G1 (stale result discarded)");
510
+ lacks(result.out, "ALL MET");
511
+ lacks(result.out, "reverified: 1");
512
+ assert(s.read("GATES.md").includes("EXPECT: NEW\n EVIDENCE: old evidence"), "newer ledger was clobbered");
513
+ } finally { s.cleanup(); }
514
+ });
515
+
516
+ test("CLI: numeric bounds, incompatible modes, and file types fail with exit 2", async () => {
517
+ const s = sandbox();
518
+ try {
519
+ s.write("GATES.md", "- [ ] G1: manual\n EVIDENCE: pending\n");
520
+ mkdirSync(s.path("directory.md"));
521
+ const cases = [
522
+ ["--jobs", "0"], ["--jobs", "1.5"], ["--jobs", "Infinity"], ["--jobs", "65"],
523
+ ["--timeout", "0"], ["--timeout", "1.5"], ["--timeout", "86401"], ["--timeout", "Infinity"], ["--timeout"],
524
+ ["--status", "--reverify"], ["--status", "--approve"], ["directory.md"],
525
+ ];
526
+ for (const args of cases) {
527
+ const result = await gateRun(s, args, { approve: false });
528
+ assert(result.code === 2, args.join(" ") + " expected 2, got " + result.code + "\n" + result.out);
529
+ }
530
+ } finally { s.cleanup(); }
531
+ });
532
+
533
+ test("CLI: trusted help layout remains multiline", async () => {
534
+ const s = sandbox();
535
+ try {
536
+ const result = await gateRun(s, ["--help"], { approve: false });
537
+ assert(result.code === 0, result.out);
538
+ has(result.out, "usage: gate-check.mjs [options] [file ...]\n\nrun modes:\n");
539
+ has(result.out, "\npipeline actions:\n");
540
+ assert(result.out.trimEnd().split("\n").length > 20, "help output was flattened\n" + result.out);
541
+ } finally { s.cleanup(); }
542
+ });
543
+
544
+ test("targeting: explicit files anchor relative commands and every positional file is honored", async () => {
545
+ const s = sandbox();
546
+ try {
547
+ for (const name of ["a", "b"]) {
548
+ s.write(name + "/check.mjs", "console.log('" + name.toUpperCase() + "');\n");
549
+ s.write(name + "/leaf.md", gate("G1", name, "node check.mjs", name.toUpperCase()));
550
+ }
551
+ const result = await gateRun(s, ["--timeout", "5", "a/leaf.md", "b/leaf.md"]);
552
+ assert(result.code === 0, result.out);
553
+ has(result.out, "PASS leaf:G1");
554
+ has(s.read("a/leaf.md"), "cwd=" + realpathSync(join(s.dir, "a")));
555
+ has(s.read("b/leaf.md"), "cwd=" + realpathSync(join(s.dir, "b")));
556
+ } finally { s.cleanup(); }
557
+ });
558
+
559
+ test("leases: unsafe declarations, unknown leaves, and wildcard witnesses fail closed", async () => {
560
+ const s = sandbox();
561
+ try {
562
+ s.write(".unlazy/a/gates/leaf-a.md", "OWNS: src/a*.js\n\n" + gate("G1", "a", null, null));
563
+ s.write(".unlazy/b/gates/leaf-b.md", "OWNS: src/ab*.js\n\n" + gate("G1", "b", null, null));
564
+ const first = await gateRun(s, ["--scope", "a", "--leaf", "leaf-a", "--claim"], { approve: false });
565
+ assert(first.code === 0, first.out);
566
+ const overlap = await gateRun(s, ["--scope", "b", "--leaf", "leaf-b", "--claim"], { approve: false });
567
+ assert(overlap.code === 3, overlap.out);
568
+ has(overlap.out, "CONFLICT src/ab*.js overlaps src/a*.js");
569
+ const unknown = await gateRun(s, ["--scope", "a", "--leaf", "missing", "--claim"], { approve: false });
570
+ assert(unknown.code === 2, unknown.out);
571
+ has(unknown.out, "unknown --leaf missing");
572
+ const released = await gateRun(s, ["--scope", "a", "--release"], { approve: false });
573
+ assert(released.code === 0, released.out);
574
+ s.write(".unlazy/dot-a/gates/leaf.md", "OWNS: src/./same.js\n\n" + gate("G1", "dot a", null, null));
575
+ s.write(".unlazy/dot-b/gates/leaf.md", "OWNS: src/same.js\n\n" + gate("G1", "dot b", null, null));
576
+ const dotFirst = await gateRun(s, ["--scope", "dot-a", "--leaf", "leaf", "--claim"], { approve: false });
577
+ assert(dotFirst.code === 0, dotFirst.out);
578
+ has(dotFirst.out, "src/same.js");
579
+ const dotOverlap = await gateRun(s, ["--scope", "dot-b", "--leaf", "leaf", "--claim"], { approve: false });
580
+ assert(dotOverlap.code === 3, dotOverlap.out);
581
+ has(dotOverlap.out, "CONFLICT src/same.js overlaps src/same.js");
582
+ await gateRun(s, ["--scope", "dot-a", "--release"], { approve: false });
583
+ s.write(".unlazy/prefix-a/gates/leaf.md", "OWNS: src/api\n\n" + gate("G1", "prefix a", null, null));
584
+ s.write(".unlazy/prefix-b/gates/leaf.md", "OWNS: src/api/**\n\n" + gate("G1", "prefix b", null, null));
585
+ const prefixFirst = await gateRun(s, ["--scope", "prefix-a", "--leaf", "leaf", "--claim"], { approve: false });
586
+ assert(prefixFirst.code === 0, prefixFirst.out);
587
+ const prefixOverlap = await gateRun(s, ["--scope", "prefix-b", "--leaf", "leaf", "--claim"], { approve: false });
588
+ assert(prefixOverlap.code === 3, prefixOverlap.out);
589
+ has(prefixOverlap.out, "CONFLICT src/api/** overlaps src/api");
590
+ s.write(".unlazy/c/gates/leaf-c.md", "OWNS: ../outside/**\n\n" + gate("G1", "c", null, null));
591
+ const unsafe = await gateRun(s, ["--scope", "c", "--leaf", "leaf-c", "--claim"], { approve: false });
592
+ assert(unsafe.code === 2, unsafe.out);
593
+ has(unsafe.out, "cannot contain traversal");
594
+ } finally { s.cleanup(); }
595
+ });
596
+
597
+ test("parser: an indented ABANDON is diagnosed instead of silently ignored", async () => {
598
+ const s = sandbox();
599
+ try {
600
+ // Every other attribute must be indented, so indenting the abandonment is
601
+ // the natural mistake. Ignoring the line silently leaves the gate unmet
602
+ // with no diagnostic, so the honest exit fails for a formatting reason the
603
+ // author is never told about.
604
+ s.write("GATES.md", [
605
+ "# Gates: indented abandonment",
606
+ "",
607
+ "Scope: an abandonment indented like every other attribute",
608
+ "",
609
+ "- [ ] G1: upstream export reconciles",
610
+ " EVIDENCE: pending",
611
+ " ABANDON: G1 upstream export was withdrawn",
612
+ "",
613
+ ].join("\n"));
614
+ const result = await gateRun(s, ["--status"], { approve: false });
615
+ assert(result.code === 2, "expected a parse error, got " + result.code + "\n" + result.out);
616
+ has(result.out, "indented ABANDON");
617
+ has(result.out, "column 1");
618
+ } finally { s.cleanup(); }
619
+ });
620
+
621
+ test("parser: a slash wrapped literal path warns that it became a regex", async () => {
622
+ const s = sandbox();
623
+ try {
624
+ // "/etc/app/conf/" is a plausible literal expectation, and the slash sniff
625
+ // silently turns it into the pattern etc/app/conf, whose dots would be
626
+ // wildcards. There is no way to express that literal, so the author needs
627
+ // to be told which reading applies.
628
+ s.write("show.mjs", "console.log('resolved /etc/app/conf/');\n");
629
+ s.write("GATES.md", gate("G1", "config path is reported", "node show.mjs", "/etc/app/conf/"));
630
+ const warned = await gateRun(s, ["--status"], { approve: false });
631
+ has(warned.out, "G1");
632
+ has(warned.out, "read as a regular expression");
633
+ assert(warned.code === 1, "a warning must not become a parse error, got " + warned.code);
634
+
635
+ // A deliberate pattern carries no unescaped inner slash and stays quiet.
636
+ s.write("GATES.md", gate("G2", "typecheck is clean", "node show.mjs", "/Found 0 errors/"));
637
+ const quiet = await gateRun(s, ["--status"], { approve: false });
638
+ lacks(quiet.out, "read as a regular expression");
639
+
640
+ // Escaping keeps the pattern reading without the warning.
641
+ s.write("GATES.md", gate("G3", "path pattern", "node show.mjs", "/etc\\/app/"));
642
+ const escaped = await gateRun(s, ["--status"], { approve: false });
643
+ lacks(escaped.out, "read as a regular expression");
644
+ } finally { s.cleanup(); }
645
+ });
646
+
647
+ test("win32 cleanup: taskkill ENOENT invokes child.kill fallback", async () => {
648
+ const calls = [];
649
+ const child = { pid: 4242, kill(signal) { calls.push(signal); return true; } };
650
+ const result = terminateProcessTree(child, {
651
+ platform: "win32",
652
+ env: { ...WINDOWS_ENV, PATH: "C:\\shadow" },
653
+ spawnSyncImpl: () => ({ status: null, signal: null, error: Object.assign(new Error("missing"), { code: "ENOENT" }) }),
654
+ });
655
+ assert(result.fallback && result.ok, JSON.stringify(result));
656
+ assert(calls.join(",") === "SIGKILL", "expected SIGKILL fallback, got " + calls.join(","));
657
+ has(result.diagnostic, "ENOENT");
658
+ });
659
+
660
+ test("win32 cleanup: taskkill itself has a bounded timeout", async () => {
661
+ const calls = [];
662
+ let invocation = null;
663
+ const child = { pid: 4243, kill(signal) { calls.push(signal); return true; } };
664
+ const result = terminateProcessTree(child, {
665
+ platform: "win32",
666
+ env: WINDOWS_ENV,
667
+ spawnSyncImpl: (command, args, options) => {
668
+ invocation = { command, args, options };
669
+ return { status: null, signal: "SIGKILL", error: Object.assign(new Error("timed out"), { code: "ETIMEDOUT" }) };
670
+ },
671
+ });
672
+ assert(invocation.options.timeout === WINDOWS_TASKKILL_TIMEOUT_MS, JSON.stringify(invocation));
673
+ assert(invocation.options.killSignal === "SIGKILL", JSON.stringify(invocation));
674
+ assert(result.ok && result.fallback, JSON.stringify(result));
675
+ assert(calls.join(",") === "SIGKILL", "helper timeout did not request direct fallback");
676
+ has(result.diagnostic, "ETIMEDOUT");
677
+ });
678
+
679
+ test("win32 cleanup: nonzero taskkill status invokes fallback and checker settles", async () => {
680
+ const calls = [];
681
+ const child = { pid: 4343, kill(signal) { calls.push(signal); return true; } };
682
+ const result = terminateProcessTree(child, {
683
+ platform: "win32",
684
+ env: WINDOWS_ENV,
685
+ spawnSyncImpl: () => ({ status: 5, signal: null, error: null }),
686
+ });
687
+ assert(result.fallback && result.ok, JSON.stringify(result));
688
+ assert(calls.length === 1, "fallback was not requested exactly once");
689
+ has(result.diagnostic, "exit 5");
690
+ });
691
+
692
+ test("win32 cleanup: a false child.kill result is reported as fallback failure", async () => {
693
+ const child = { pid: 4444, kill() { return false; } };
694
+ const result = terminateProcessTree(child, {
695
+ platform: "win32",
696
+ env: WINDOWS_ENV,
697
+ spawnSyncImpl: () => ({ status: 5, signal: null, error: null }),
698
+ });
699
+ assert(!result.ok && result.fallback, JSON.stringify(result));
700
+ has(result.diagnostic, "returned false");
701
+ });
702
+
703
+ test("win32 cleanup: internal taskkill resolution cannot be shadowed by test PATH", async () => {
704
+ const expected = win32.join("C:\\Windows", "System32", "taskkill.exe");
705
+ const resolved = windowsTaskkillPath({ ...WINDOWS_ENV, PATH: "C:\\attacker" });
706
+ assert(resolved === expected, "expected system taskkill, got " + resolved);
707
+ lacks(resolved.toLowerCase(), "attacker");
708
+ });
709
+
710
+ test("win32 cleanup: a non-C system drive keeps protected tree cleanup", async () => {
711
+ const expected = win32.join("D:\\Windows", "System32", "taskkill.exe");
712
+ const resolved = windowsTaskkillPath({
713
+ SystemRoot: "D:\\Windows",
714
+ WINDIR: "D:\\Windows",
715
+ SystemDrive: "D:",
716
+ PATH: "D:\\repo",
717
+ });
718
+ assert(resolved === expected, "expected non-C system taskkill, got " + resolved);
719
+ });
720
+
721
+ test("win32 cleanup: inconsistent system directory variables fail closed", async () => {
722
+ const mismatches = [
723
+ { SystemRoot: "D:\\Windows", WINDIR: "C:\\Windows", SystemDrive: "C:" },
724
+ { SystemRoot: "D:\\Windows", WINDIR: "D:\\Windows", SystemDrive: "C:" },
725
+ { SystemRoot: "D:\\Windows", WINDIR: "D:\\Windows" },
726
+ ];
727
+ for (const env of mismatches) {
728
+ assert(windowsTaskkillPath(env) === null, "trusted inconsistent Windows environment: " + JSON.stringify(env));
729
+ }
730
+ });
731
+
732
+ test("win32 cleanup: missing trusted system root skips PATH lookup", async () => {
733
+ let spawned = false;
734
+ let killed = false;
735
+ const child = { pid: 4545, kill() { killed = true; return true; } };
736
+ const result = terminateProcessTree(child, {
737
+ platform: "win32",
738
+ env: { PATH: "C:\\attacker" },
739
+ spawnSyncImpl: () => { spawned = true; return { status: 0 }; },
740
+ });
741
+ assert(windowsTaskkillPath({ PATH: "C:\\attacker" }) === null, "bare taskkill fallback remained enabled");
742
+ assert(!spawned, "untrusted PATH was used to resolve taskkill");
743
+ assert(killed && result.ok && result.fallback, JSON.stringify(result));
744
+ has(result.diagnostic, "trusted system taskkill path unavailable");
745
+ });
746
+
747
+ test("win32 cleanup: an arbitrary absolute SystemRoot is not executable trust", async () => {
748
+ let spawned = false;
749
+ let killed = false;
750
+ const child = { pid: 4546, kill() { killed = true; return true; } };
751
+ const hostile = { SystemRoot: "C:\\repo\\fake-windows", PATH: "C:\\repo" };
752
+ const result = terminateProcessTree(child, {
753
+ platform: "win32",
754
+ env: hostile,
755
+ spawnSyncImpl: () => { spawned = true; return { status: 0 }; },
756
+ });
757
+ assert(windowsTaskkillPath(hostile) === null, "repository SystemRoot was trusted");
758
+ assert(!spawned && killed && result.ok && result.fallback, JSON.stringify(result));
759
+ });
760
+
761
+ test("cleanup: an already-exited child is never signalled through a reusable PID", async () => {
762
+ let spawned = false;
763
+ let killed = false;
764
+ const child = { pid: 4646, exitCode: 0, signalCode: null, kill() { killed = true; return true; } };
765
+ const result = terminateProcessTree(child, {
766
+ platform: "win32",
767
+ env: WINDOWS_ENV,
768
+ spawnSyncImpl: () => { spawned = true; return { status: 0 }; },
769
+ });
770
+ assert(result.ok && result.diagnostic === "child already exited", JSON.stringify(result));
771
+ assert(!spawned && !killed, "an exited child PID was reused for cleanup");
772
+ });
773
+
774
+ test("posix cleanup: an exited supervisor never targets a reusable process group", async () => {
775
+ let group = null;
776
+ let direct = false;
777
+ const child = { pid: 4747, exitCode: 0, signalCode: null, kill() { direct = true; return true; } };
778
+ const result = terminateProcessTree(child, {
779
+ platform: "linux",
780
+ killGroup(pid, signal) { group = { pid, signal }; },
781
+ });
782
+ assert(result.ok && !result.fallback, JSON.stringify(result));
783
+ assert(result.diagnostic === "process supervisor already exited", JSON.stringify(result));
784
+ assert(group === null && !direct, "an exited supervisor's reusable PID/PGID was signalled");
785
+ });
786
+
787
+ test("win32 integration: a timed-out shell and nested descendant are both reaped", async () => {
788
+ if (process.platform !== "win32") return;
789
+ const s = sandbox();
790
+ const pids = [];
791
+ try {
792
+ s.write("descendant.mjs", "setInterval(() => {}, 1000);\n");
793
+ s.write("parent.mjs", [
794
+ "import { spawn } from 'node:child_process';",
795
+ "import { writeFileSync } from 'node:fs';",
796
+ "writeFileSync('shell.pid', String(process.ppid));",
797
+ "const child = spawn(process.execPath, ['descendant.mjs'], { stdio: 'inherit' });",
798
+ "writeFileSync('descendant.pid', String(child.pid));",
799
+ "setInterval(() => {}, 1000);",
800
+ "",
801
+ ].join("\n"));
802
+ s.write("GATES.md", gate("G1", "nested process times out", "node parent.mjs", "never printed"));
803
+ const started = Date.now();
804
+ const result = await gateRun(s, ["--timeout", "1"]);
805
+ const elapsed = Date.now() - started;
806
+ assert(result.code === 1, result.out);
807
+ has(result.out, "timed out after 1s");
808
+ assert(elapsed < 12000, "bounded timeout took " + elapsed + "ms\n" + result.out);
809
+ await waitForPath(s.path("shell.pid"));
810
+ await waitForPath(s.path("descendant.pid"));
811
+ pids.push(Number(s.read("shell.pid")), Number(s.read("descendant.pid")));
812
+ assert(pids.every((pid) => Number.isInteger(pid) && pid > 0), "invalid captured PIDs: " + pids.join(","));
813
+ await Promise.all(pids.map((pid) => waitForProcessExit(pid)));
814
+ } finally {
815
+ for (const pid of pids) try { process.kill(pid, "SIGKILL"); } catch { /* already gone */ }
816
+ s.cleanup();
817
+ }
818
+ });
819
+
820
+ test("execution: timeout settlement is bounded even when a detached descendant keeps pipes", async () => {
821
+ const s = sandbox();
822
+ let descendantPid = null;
823
+ try {
824
+ s.write("pipe-holder.mjs", "setInterval(() => {}, 1000);\n");
825
+ s.write("escape.mjs", [
826
+ "import { spawn } from 'node:child_process';",
827
+ "import { writeFileSync } from 'node:fs';",
828
+ "const child = spawn(process.execPath, ['pipe-holder.mjs'], { detached: true, stdio: ['ignore', 'inherit', 'inherit'] });",
829
+ "writeFileSync('pipe-holder.pid', String(child.pid));",
830
+ "child.unref();",
831
+ "setInterval(() => {}, 1000);",
832
+ "",
833
+ ].join("\n"));
834
+ s.write("GATES.md", gate("G1", "escaped pipe holder times out", "node escape.mjs", "never printed"));
835
+ const started = Date.now();
836
+ const result = await gateRun(s, ["--timeout", "1"]);
837
+ const elapsed = Date.now() - started;
838
+ assert(result.code === 1, result.out);
839
+ has(result.out, "timed out after 1s");
840
+ assert(elapsed < 7000, "checker did not settle independently after cleanup request: " + elapsed + "ms");
841
+ await waitForPath(s.path("pipe-holder.pid"));
842
+ descendantPid = Number(s.read("pipe-holder.pid"));
843
+ } finally {
844
+ if (Number.isInteger(descendantPid) && descendantPid > 0) {
845
+ try { process.kill(descendantPid, "SIGKILL"); } catch { /* already gone */ }
846
+ }
847
+ s.cleanup();
848
+ }
849
+ });
850
+
851
+ test("posix integration: an exited shell leader still has its ordinary descendants reaped", async () => {
852
+ if (process.platform === "win32") return;
853
+ const s = sandbox();
854
+ let descendantPid = null;
855
+ try {
856
+ s.write("ordinary-child.mjs", [
857
+ "import { writeFileSync } from 'node:fs';",
858
+ "setTimeout(() => writeFileSync('ordinary-marker.txt', 'survived'), 3000);",
859
+ "setInterval(() => {}, 1000);",
860
+ "",
861
+ ].join("\n"));
862
+ s.write("exiting-parent.mjs", [
863
+ "import { spawn } from 'node:child_process';",
864
+ "import { writeFileSync } from 'node:fs';",
865
+ "const child = spawn(process.execPath, ['ordinary-child.mjs'], { stdio: 'inherit' });",
866
+ "writeFileSync('ordinary-child.pid', String(child.pid));",
867
+ "child.unref();",
868
+ "",
869
+ ].join("\n"));
870
+ s.write("GATES.md", gate("G1", "orphaned group member times out", "node exiting-parent.mjs", "never printed"));
871
+ const result = await gateRun(s, ["--timeout", "1"]);
872
+ assert(result.code === 1, result.out);
873
+ has(result.out, "timed out after 1s");
874
+ has(result.out, "signal=SIGKILL");
875
+ await waitForPath(s.path("ordinary-child.pid"));
876
+ descendantPid = Number(s.read("ordinary-child.pid"));
877
+ await waitForProcessExit(descendantPid);
878
+ assert(!existsSync(s.path("ordinary-marker.txt")), "ordinary process-group descendant survived cleanup");
879
+ } finally {
880
+ if (Number.isInteger(descendantPid) && descendantPid > 0) {
881
+ try { process.kill(descendantPid, "SIGKILL"); } catch { /* already gone */ }
882
+ }
883
+ s.cleanup();
884
+ }
885
+ });
886
+
887
+ let passed = 0;
888
+ const failures = [];
889
+ for (const item of tests) {
890
+ try {
891
+ await item.fn();
892
+ passed++;
893
+ console.log("ok " + item.name);
894
+ } catch (error) {
895
+ failures.push(item.name);
896
+ console.log("FAIL " + item.name + "\n " + String(error.message).replace(/\n/g, "\n "));
897
+ }
898
+ }
899
+ console.log("\n" + passed + "/" + tests.length + " passed");
900
+ if (failures.length) {
901
+ console.log("failed: " + failures.join(", "));
902
+ process.exit(1);
903
+ }