@iowarp/clio-coder 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (988) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +50 -53
  3. package/dist/acp-FPR54DGL.js +138 -0
  4. package/dist/{agents-Q4MYPMUW.js → agents-OGPIHPJH.js} +33 -25
  5. package/dist/assets/codewiki.json +1 -0
  6. package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/LICENSE +21 -0
  7. package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/cgmanifest.json +16 -0
  8. package/dist/assets/grammars/notices/tree-sitter-wasms/LICENSE +24 -0
  9. package/dist/assets/grammars/tree-sitter-c-sharp.wasm +0 -0
  10. package/dist/assets/grammars/tree-sitter-c.wasm +0 -0
  11. package/dist/assets/grammars/tree-sitter-cpp.wasm +0 -0
  12. package/dist/assets/grammars/tree-sitter-go.wasm +0 -0
  13. package/dist/assets/grammars/tree-sitter-java.wasm +0 -0
  14. package/dist/assets/grammars/tree-sitter-javascript.wasm +0 -0
  15. package/dist/assets/grammars/tree-sitter-python.wasm +0 -0
  16. package/dist/assets/grammars/tree-sitter-ruby.wasm +0 -0
  17. package/dist/assets/grammars/tree-sitter-rust.wasm +0 -0
  18. package/dist/assets/grammars/tree-sitter-tsx.wasm +0 -0
  19. package/dist/assets/grammars/tree-sitter-typescript.wasm +0 -0
  20. package/dist/assets/grammars/tree-sitter.wasm +0 -0
  21. package/dist/{auth-O6HYIJ6J.js → auth-IC3K6NIZ.js} +22 -15
  22. package/dist/{chunk-KKKPTZLM.js → chunk-2STGEDFV.js} +8 -1
  23. package/dist/chunk-3I7MS7N2.js +40 -0
  24. package/dist/chunk-3R73A4XB.js +69 -0
  25. package/dist/{chunk-26BZQOAD.js → chunk-474KN5II.js} +86 -115
  26. package/dist/{chunk-NI4DEJMC.js → chunk-4QKXUHSR.js} +26 -3
  27. package/dist/{chunk-X3BR7HWV.js → chunk-5WIGXA4T.js} +7 -1
  28. package/dist/{chunk-TFSSEXL6.js → chunk-673JJUWJ.js} +14 -4
  29. package/dist/{chunk-GNGDQYDU.js → chunk-7SS2CTV2.js} +27922 -1249
  30. package/dist/{chunk-7RBKDI66.js → chunk-ASND7OZK.js} +104 -12
  31. package/dist/{chunk-X3NE4WVW.js → chunk-BNAZZHFG.js} +7 -1
  32. package/dist/{chunk-RQDWMVRB.js → chunk-BSU2YIWB.js} +9 -3
  33. package/dist/{chunk-CLJ5HLUD.js → chunk-C4PTHK7P.js} +24 -5
  34. package/dist/{chunk-QV47YRF4.js → chunk-CGKSTWHD.js} +7 -1
  35. package/dist/{chunk-BYEU76JP.js → chunk-DKGKUHFA.js} +34 -9
  36. package/dist/chunk-EYOKLTMF.js +170 -0
  37. package/dist/{chunk-PS4PFJQP.js → chunk-FCIH3BIZ.js} +15258 -14130
  38. package/dist/{chunk-2J63S4SF.js → chunk-FST4FYJB.js} +82 -14
  39. package/dist/{chunk-3DANZDGR.js → chunk-G34LV2PF.js} +34 -9
  40. package/dist/{chunk-BBUH4VAA.js → chunk-GAEBEQVI.js} +34 -21
  41. package/dist/chunk-GB6QRBXN.js +8543 -0
  42. package/dist/chunk-GEP36Y4X.js +12796 -0
  43. package/dist/{chunk-TKHQ4DGZ.js → chunk-H6F6BYOH.js} +496 -121
  44. package/dist/{chunk-UGYAX5YI.js → chunk-HQQID6OA.js} +10 -5
  45. package/dist/chunk-IHXBNWMM.js +137 -0
  46. package/dist/{chunk-ME6DNWIU.js → chunk-IS3ONKU3.js} +7 -1
  47. package/dist/{chunk-DXQNI4PC.js → chunk-IWHMRKLL.js} +8 -1
  48. package/dist/chunk-K2ITRMHZ.js +642 -0
  49. package/dist/{chunk-KBR4MZZR.js → chunk-K5XEMXTI.js} +7 -1
  50. package/dist/{chunk-AMFR5YA3.js → chunk-KKNLWXI6.js} +24 -10
  51. package/dist/{chunk-JWHCJDCI.js → chunk-KY56HMHH.js} +145 -61
  52. package/dist/{chunk-E3NYWENM.js → chunk-LU4TK2PR.js} +52 -7
  53. package/dist/{chunk-UWIJNAOB.js → chunk-M5T5VO65.js} +109 -42
  54. package/dist/{chunk-JPOWPFCU.js → chunk-MAR7Y6HW.js} +38 -16
  55. package/dist/chunk-N6F52NLF.js +6951 -0
  56. package/dist/{chunk-UHTSULZS.js → chunk-NMOX6HFD.js} +20 -7
  57. package/dist/{chunk-WD6AJM35.js → chunk-OLBBMFRD.js} +30 -13
  58. package/dist/{chunk-VOO7NYPP.js → chunk-PFEFKVGL.js} +22 -6
  59. package/dist/{chunk-6CWDTGUC.js → chunk-PIUMUEMV.js} +7 -1
  60. package/dist/chunk-PV4JUBVJ.js +462 -0
  61. package/dist/{chunk-527KG6XR.js → chunk-Q3RUPKEJ.js} +22 -4
  62. package/dist/{chunk-D5YD55AR.js → chunk-Q5WJOSJ7.js} +11 -1
  63. package/dist/{chunk-5N2FG33Q.js → chunk-R346GLFC.js} +7 -1
  64. package/dist/{chunk-4UQA7NCT.js → chunk-R5KLMSBV.js} +7 -2
  65. package/dist/{chunk-PIDUD6M2.js → chunk-RDLVBZEO.js} +11 -6
  66. package/dist/{chunk-O4EJEDHO.js → chunk-RPTR2H26.js} +40 -31
  67. package/dist/{chunk-HBU5MTAM.js → chunk-SVNXH4L2.js} +7 -1
  68. package/dist/{chunk-XNISANGE.js → chunk-TEKV33Q5.js} +122 -57
  69. package/dist/chunk-TXOTCRLG.js +15 -0
  70. package/dist/{chunk-VPAWTYLY.js → chunk-WEPFGWHJ.js} +7 -1
  71. package/dist/{chunk-HMYNFFY4.js → chunk-XPLRXC72.js} +7 -2
  72. package/dist/{chunk-UU3R62TT.js → chunk-XYWBQRDM.js} +13 -4
  73. package/dist/{chunk-7BHLZB3A.js → chunk-ZPY3JZ5E.js} +32 -10
  74. package/dist/cli/index.js +39 -33
  75. package/dist/clio-BZVGEUFJ.js +58 -0
  76. package/dist/{components-JVHMUBEB.js → components-DMAOEKFB.js} +26 -9
  77. package/dist/{config-ZFCDBMDC.js → config-IRUQ7SE4.js} +42 -31
  78. package/dist/configure-S7S6F6CL.js +32 -0
  79. package/dist/{context-CDXTP2MP.js → context-3KWFLHJG.js} +17 -11
  80. package/dist/{context-E3KIFVXI.js → context-5RADCKTR.js} +33 -28
  81. package/dist/{context-clear-3F4PLXOS.js → context-clear-7TSNPAAI.js} +36 -28
  82. package/dist/{context-index-Q7YSYTR3.js → context-index-W4RLWOQH.js} +10 -4
  83. package/dist/{docs-YIETIWZI.js → docs-5AWSPS37.js} +16 -10
  84. package/dist/doctor-UC5NAJYQ.js +77 -0
  85. package/dist/{eval-B3KZZESM.js → eval-U6TJHRLX.js} +77 -24
  86. package/dist/{evidence-V67CHM35.js → evidence-YEGUW4L3.js} +34 -26
  87. package/dist/{evolve-YDZSUQYA.js → evolve-TXARCTPG.js} +39 -26
  88. package/dist/{extensions-SRG7XCAH.js → extensions-OZFJ3A3G.js} +13 -7
  89. package/dist/{fleet-CA2CRTVG.js → fleet-6G3DHNYE.js} +45 -37
  90. package/dist/fleet-preflight-DSNT37JK.js +23 -0
  91. package/dist/{init-2OZDJE2D.js → init-KZ5QTF6M.js} +42 -34
  92. package/dist/{memory-3PIQQAKX.js → memory-73ESV5YC.js} +35 -27
  93. package/dist/{models-DY35XI7Y.js → models-A4PVNWJK.js} +34 -23
  94. package/dist/{paths-5OMXW7Z4.js → paths-C4H6IV77.js} +12 -6
  95. package/dist/preload-6WVMHX3A.js +13 -0
  96. package/dist/{reset-PIFYNOS3.js → reset-BGW6OGMV.js} +17 -11
  97. package/dist/{run-3VSPP24F.js → run-YTPEYQOH.js} +60 -48
  98. package/dist/{share-D36RQCXM.js → share-YIFFV4NQ.js} +16 -10
  99. package/dist/{skills-F2MRLELY.js → skills-2V6RA3OQ.js} +18 -11
  100. package/dist/{skills-eval-E2ZTW4PL.js → skills-eval-S2TVJO4F.js} +45 -36
  101. package/dist/{targets-DZMEZAH4.js → targets-TYXLPB23.js} +61 -59
  102. package/dist/{trace-7NYCUI2J.js → trace-GGOJ6Q6Z.js} +9 -3
  103. package/dist/{uninstall-AD3JWHBB.js → uninstall-LLLT4F4W.js} +14 -8
  104. package/dist/{upgrade-WYYBKGDY.js → upgrade-33G2LMM5.js} +45 -23
  105. package/dist/{usage-ULIDAGFF.js → usage-ZAFSXKKG.js} +35 -27
  106. package/dist/version-KGEG7EPO.js +21 -0
  107. package/dist/{wiki-generate-PKFIX6OB.js → wiki-generate-NUQCVOQ3.js} +55 -45
  108. package/dist/worker/entry.js +101 -43
  109. package/docs/README.md +12 -9
  110. package/docs/acp.md +28 -5
  111. package/docs/alcf-provider.md +1 -1
  112. package/docs/architecture.md +23 -2
  113. package/docs/artifact-placement.md +101 -0
  114. package/docs/artifact-versions.md +1 -1
  115. package/docs/built-in-agents.md +4 -2
  116. package/docs/capacity-and-scheduling.md +1 -1
  117. package/docs/commands-and-modes.md +125 -41
  118. package/docs/configuration-and-targets.md +88 -18
  119. package/docs/context-engine.md +6 -2
  120. package/docs/development-pipeline.md +77 -0
  121. package/docs/documentation-coverage.md +4 -4
  122. package/docs/documentation-guide.md +3 -3
  123. package/docs/environment-variables.md +2 -1
  124. package/docs/eval-runner.md +3 -3
  125. package/docs/evals-internal.md +2 -2
  126. package/docs/evidence-and-memory.md +6 -2
  127. package/docs/evolution.md +1 -1
  128. package/docs/exit-codes-and-output.md +1 -1
  129. package/docs/extensions-and-sharing.md +18 -11
  130. package/docs/fleet-demo-runbook.md +22 -23
  131. package/docs/fleet-dispatch.md +111 -12
  132. package/docs/glossary.md +107 -15
  133. package/docs/html/agents_blueprint.html +3 -3
  134. package/docs/html/alcf_blueprint.html +3 -3
  135. package/docs/html/architecture_blueprint.html +3 -3
  136. package/docs/html/commands_blueprint.html +187 -42
  137. package/docs/html/configuration_blueprint.html +7 -7
  138. package/docs/html/context_blueprint.html +3 -3
  139. package/docs/html/documentation_blueprint.html +1 -1
  140. package/docs/html/environment_blueprint.html +3 -3
  141. package/docs/html/eval_blueprint.html +4 -4
  142. package/docs/html/evals_internal_blueprint.html +3 -3
  143. package/docs/html/evolution_blueprint.html +3 -3
  144. package/docs/html/extensions_blueprint.html +3 -3
  145. package/docs/html/fleet_dispatch_blueprint.html +8 -8
  146. package/docs/html/index.html +5 -5
  147. package/docs/html/lifecycle_blueprint.html +4 -4
  148. package/docs/html/memory_blueprint.html +3 -3
  149. package/docs/html/middleware_blueprint.html +3 -3
  150. package/docs/html/models_blueprint.html +2 -2
  151. package/docs/html/observability_blueprint.html +2 -2
  152. package/docs/html/provider_adapter_blueprint.html +3 -3
  153. package/docs/html/safety_blueprint.html +3 -3
  154. package/docs/html/skills_blueprint.html +2 -2
  155. package/docs/html/soak_blueprint.html +3 -3
  156. package/docs/html/tool_usage_blueprint.html +4 -4
  157. package/docs/html/tools_blueprint.html +2 -2
  158. package/docs/html/trace_blueprint.html +3 -3
  159. package/docs/html/tui_design_blueprint.html +100 -40
  160. package/docs/html/validation_blueprint.html +1 -1
  161. package/docs/html/worker_dispatch_blueprint.html +3 -3
  162. package/docs/installation-and-lifecycle.md +62 -11
  163. package/docs/middleware-and-components.md +1 -1
  164. package/docs/model-catalog.md +2 -2
  165. package/docs/observability.md +3 -3
  166. package/docs/proactive-memory.md +12 -12
  167. package/docs/prompt-envelope-and-tools.md +2 -2
  168. package/docs/provider-adapter-cookbook.md +1 -1
  169. package/docs/release-cut-checklist.md +4 -0
  170. package/docs/safety-model.md +21 -5
  171. package/docs/scientific-validation.md +3 -3
  172. package/docs/session-lifecycle.md +2 -2
  173. package/docs/skills-marketplace.md +1 -1
  174. package/docs/time-conventions.md +101 -0
  175. package/docs/tool-usage.md +5 -4
  176. package/docs/trace-store.md +1 -1
  177. package/docs/troubleshooting.md +1 -1
  178. package/docs/tui-design.md +83 -27
  179. package/docs/worker-dispatch-mechanics.md +3 -3
  180. package/package.json +16 -16
  181. package/skills/README.md +5 -6
  182. package/skills/git/file-ticket/SKILL.md +107 -0
  183. package/skills/git/file-ticket/evals.md +54 -0
  184. package/skills/git/fix-issue/SKILL.md +106 -0
  185. package/skills/git/fix-issue/evals.md +71 -0
  186. package/skills/git/ship/SKILL.md +92 -0
  187. package/skills/git/ship/evals.md +72 -0
  188. package/skills/skill-marketplace.json +12 -20
  189. package/src/cli/acp.ts +88 -0
  190. package/src/cli/agents.ts +60 -0
  191. package/src/cli/args.ts +280 -0
  192. package/src/cli/argv.ts +170 -0
  193. package/src/cli/ask.ts +34 -0
  194. package/src/cli/auth.ts +468 -0
  195. package/src/cli/bootstrap-generate.ts +498 -0
  196. package/src/cli/clio.ts +34 -0
  197. package/src/cli/components.ts +195 -0
  198. package/src/cli/config-inspect.ts +330 -0
  199. package/src/cli/config.ts +72 -0
  200. package/src/cli/configure-interop.ts +67 -0
  201. package/src/cli/configure-layout.ts +172 -0
  202. package/src/cli/configure.ts +1487 -0
  203. package/src/cli/context-clear.ts +76 -0
  204. package/src/cli/context-index.ts +102 -0
  205. package/src/cli/context.ts +316 -0
  206. package/src/cli/default-target.ts +63 -0
  207. package/src/cli/docs.ts +340 -0
  208. package/src/cli/doctor.ts +54 -0
  209. package/src/cli/eval.ts +315 -0
  210. package/src/cli/evidence.ts +222 -0
  211. package/src/cli/evolve.ts +219 -0
  212. package/src/cli/extensions.ts +205 -0
  213. package/src/cli/fleet.ts +827 -0
  214. package/src/cli/index.ts +309 -0
  215. package/src/cli/init.ts +219 -0
  216. package/src/cli/initial-message.ts +47 -0
  217. package/src/cli/internal-dispatch.ts +55 -0
  218. package/src/cli/memory.ts +180 -0
  219. package/src/cli/models.ts +257 -0
  220. package/src/cli/modes/json-stream.ts +85 -0
  221. package/src/cli/modes/jsonl.ts +9 -0
  222. package/src/cli/modes/print.ts +589 -0
  223. package/src/cli/oauth-manual-input.ts +72 -0
  224. package/src/cli/oauth-select.ts +36 -0
  225. package/src/cli/output-guard.ts +48 -0
  226. package/src/cli/paths.ts +44 -0
  227. package/src/cli/provider-target.ts +113 -0
  228. package/src/cli/removal.ts +72 -0
  229. package/src/cli/reset.ts +253 -0
  230. package/src/cli/run.ts +497 -0
  231. package/src/cli/share.ts +243 -0
  232. package/src/cli/shared.ts +60 -0
  233. package/src/cli/skills-eval.ts +1225 -0
  234. package/src/cli/skills.ts +470 -0
  235. package/src/cli/steer-channel.ts +86 -0
  236. package/src/cli/targets.ts +1150 -0
  237. package/src/cli/text-layout.ts +102 -0
  238. package/src/cli/trace.ts +274 -0
  239. package/src/cli/uninstall.ts +405 -0
  240. package/src/cli/upgrade.ts +270 -0
  241. package/src/cli/usage.ts +869 -0
  242. package/src/cli/validate-model.ts +28 -0
  243. package/src/cli/version.ts +7 -0
  244. package/src/cli/wiki-generate.ts +509 -0
  245. package/src/core/artifact-paths.ts +43 -0
  246. package/src/core/bash-exec.ts +242 -0
  247. package/src/core/boot-trace.ts +37 -0
  248. package/src/core/bus-events.ts +728 -0
  249. package/src/core/bus-trace.ts +53 -0
  250. package/src/core/c-header-language.ts +265 -0
  251. package/src/core/clio-repo.ts +78 -0
  252. package/src/core/config.ts +1593 -0
  253. package/src/core/context-floor.ts +33 -0
  254. package/src/core/defaults.ts +625 -0
  255. package/src/core/domain-loader.ts +170 -0
  256. package/src/core/event-bus.ts +94 -0
  257. package/src/core/file-references.ts +282 -0
  258. package/src/core/guardrails.ts +185 -0
  259. package/src/core/headless-permission.ts +21 -0
  260. package/src/core/init.ts +223 -0
  261. package/src/core/json-payload.ts +58 -0
  262. package/src/core/package-root.ts +54 -0
  263. package/src/core/path-canonical.ts +37 -0
  264. package/src/core/process-identity.ts +41 -0
  265. package/src/core/recent-models.ts +91 -0
  266. package/src/core/release.ts +2 -0
  267. package/src/core/residency-protection.ts +56 -0
  268. package/src/core/resolve-config-value.ts +88 -0
  269. package/src/core/response-schema.ts +279 -0
  270. package/src/core/run-overrides.ts +77 -0
  271. package/src/core/safe-exec.ts +193 -0
  272. package/src/core/safe-id.ts +37 -0
  273. package/src/core/safe-resource-write.ts +162 -0
  274. package/src/core/session-routing.ts +339 -0
  275. package/src/core/settings-layers.ts +220 -0
  276. package/src/core/shared-bus.ts +8 -0
  277. package/src/core/shell-quote.ts +26 -0
  278. package/src/core/skill-activation.ts +190 -0
  279. package/src/core/startup-timer.ts +37 -0
  280. package/src/core/state-file-lock.ts +207 -0
  281. package/src/core/termination.ts +202 -0
  282. package/src/core/timers.ts +38 -0
  283. package/src/core/tool-names.ts +65 -0
  284. package/src/core/verification-scripts.ts +13 -0
  285. package/src/core/workspace-files.ts +492 -0
  286. package/src/core/xdg.ts +177 -0
  287. package/src/domains/agents/builtins/architect.md +4 -4
  288. package/src/domains/agents/builtins/coder.md +3 -2
  289. package/src/domains/agents/builtins/debugger.md +1 -1
  290. package/src/domains/agents/builtins/documenter.md +1 -1
  291. package/src/domains/agents/builtins/git-master.md +3 -3
  292. package/src/domains/agents/builtins/provenance.md +1 -1
  293. package/src/domains/agents/builtins/researcher.md +1 -1
  294. package/src/domains/agents/builtins/scout.md +1 -1
  295. package/src/domains/agents/builtins/tester.md +1 -1
  296. package/src/domains/agents/builtins/verifier.md +1 -1
  297. package/src/domains/agents/builtins/wiki-writer.md +1 -1
  298. package/src/domains/agents/catalog.ts +172 -0
  299. package/src/domains/agents/contract.ts +16 -0
  300. package/src/domains/agents/extension.ts +110 -0
  301. package/src/domains/agents/fleet-commands.ts +154 -0
  302. package/src/domains/agents/fleet-contract.ts +797 -0
  303. package/src/domains/agents/frontmatter.ts +40 -0
  304. package/src/domains/agents/index.ts +102 -0
  305. package/src/domains/agents/manifest.ts +6 -0
  306. package/src/domains/agents/recipe-schema.ts +202 -0
  307. package/src/domains/agents/recipe.ts +125 -0
  308. package/src/domains/agents/registry.ts +111 -0
  309. package/src/domains/agents/result-contract.ts +1265 -0
  310. package/src/domains/agents/spec.ts +371 -0
  311. package/src/domains/agents/write-boundary.ts +109 -0
  312. package/src/domains/components/diff.ts +99 -0
  313. package/src/domains/components/hash.ts +21 -0
  314. package/src/domains/components/index.ts +3 -0
  315. package/src/domains/components/scan.ts +304 -0
  316. package/src/domains/components/snapshot.ts +108 -0
  317. package/src/domains/components/types.ts +82 -0
  318. package/src/domains/config/agent-namespace.ts +13 -0
  319. package/src/domains/config/classify.ts +94 -0
  320. package/src/domains/config/contract.ts +22 -0
  321. package/src/domains/config/extension.ts +136 -0
  322. package/src/domains/config/index.ts +13 -0
  323. package/src/domains/config/keybindings.ts +164 -0
  324. package/src/domains/config/manifest.ts +6 -0
  325. package/src/domains/config/watcher.ts +41 -0
  326. package/src/domains/context/adoption.ts +880 -0
  327. package/src/domains/context/bootstrap-prompt.ts +273 -0
  328. package/src/domains/context/bootstrap.ts +1409 -0
  329. package/src/domains/context/clear.ts +86 -0
  330. package/src/domains/context/clio-md.ts +327 -0
  331. package/src/domains/context/codewiki/cmake.ts +356 -0
  332. package/src/domains/context/codewiki/cooperative.ts +46 -0
  333. package/src/domains/context/codewiki/digest.ts +135 -0
  334. package/src/domains/context/codewiki/grammar-assets.ts +33 -0
  335. package/src/domains/context/codewiki/indexer.ts +1614 -0
  336. package/src/domains/context/codewiki/tree-sitter.ts +771 -0
  337. package/src/domains/context/contract.ts +62 -0
  338. package/src/domains/context/excluded-dirs.ts +1 -0
  339. package/src/domains/context/extension.ts +407 -0
  340. package/src/domains/context/fingerprint.ts +196 -0
  341. package/src/domains/context/index.ts +164 -0
  342. package/src/domains/context/init-options.ts +69 -0
  343. package/src/domains/context/manifest.ts +6 -0
  344. package/src/domains/context/operator-profile.ts +157 -0
  345. package/src/domains/context/project-metadata.ts +510 -0
  346. package/src/domains/context/project-rules.ts +227 -0
  347. package/src/domains/context/prompt-context.ts +62 -0
  348. package/src/domains/context/refresh.ts +170 -0
  349. package/src/domains/context/sibling-files.ts +10 -0
  350. package/src/domains/context/state.ts +237 -0
  351. package/src/domains/context/wiki/assemble.ts +303 -0
  352. package/src/domains/context/wiki/frontmatter.ts +210 -0
  353. package/src/domains/context/wiki/generate.ts +590 -0
  354. package/src/domains/context/wiki/layout.ts +111 -0
  355. package/src/domains/context/wiki/meta.ts +221 -0
  356. package/src/domains/context/wiki/plan-store.ts +217 -0
  357. package/src/domains/context/wiki/plan.ts +250 -0
  358. package/src/domains/context/wiki/prompts.ts +270 -0
  359. package/src/domains/context/wiki/staleness.ts +198 -0
  360. package/src/domains/dispatch/active-route-planner.ts +168 -0
  361. package/src/domains/dispatch/admission-queue.ts +150 -0
  362. package/src/domains/dispatch/admission.ts +334 -0
  363. package/src/domains/dispatch/agent-candidates.ts +307 -0
  364. package/src/domains/dispatch/agent-ledger-hub.ts +71 -0
  365. package/src/domains/dispatch/agent-ledger-store.ts +257 -0
  366. package/src/domains/dispatch/agent-ledger.ts +212 -0
  367. package/src/domains/dispatch/agent-plan-adapter.ts +85 -0
  368. package/src/domains/dispatch/assignment-events.ts +132 -0
  369. package/src/domains/dispatch/assignment-reconcile.ts +76 -0
  370. package/src/domains/dispatch/assignment-store.ts +162 -0
  371. package/src/domains/dispatch/assignment.ts +278 -0
  372. package/src/domains/dispatch/backoff.ts +59 -0
  373. package/src/domains/dispatch/batch-store.ts +134 -0
  374. package/src/domains/dispatch/batch-tracker.ts +66 -0
  375. package/src/domains/dispatch/capability-match.ts +155 -0
  376. package/src/domains/dispatch/capacity-lease.ts +413 -0
  377. package/src/domains/dispatch/code-step-store.ts +52 -0
  378. package/src/domains/dispatch/code-step.ts +368 -0
  379. package/src/domains/dispatch/contract.ts +278 -0
  380. package/src/domains/dispatch/dedup.ts +133 -0
  381. package/src/domains/dispatch/event-pump.ts +251 -0
  382. package/src/domains/dispatch/execution-handoff.ts +29 -0
  383. package/src/domains/dispatch/execution-plan.ts +328 -0
  384. package/src/domains/dispatch/execution-role.ts +277 -0
  385. package/src/domains/dispatch/execution-scheduler.ts +583 -0
  386. package/src/domains/dispatch/extension.ts +5869 -0
  387. package/src/domains/dispatch/failure-classification.ts +141 -0
  388. package/src/domains/dispatch/fleet-plan.ts +225 -0
  389. package/src/domains/dispatch/fleet-preflight.ts +394 -0
  390. package/src/domains/dispatch/gate-decisions.ts +1040 -0
  391. package/src/domains/dispatch/gate-role-prompts.ts +58 -0
  392. package/src/domains/dispatch/heartbeat.ts +27 -0
  393. package/src/domains/dispatch/index.ts +137 -0
  394. package/src/domains/dispatch/joint-route-adapter.ts +114 -0
  395. package/src/domains/dispatch/joint-route-resolver.ts +283 -0
  396. package/src/domains/dispatch/manifest.ts +6 -0
  397. package/src/domains/dispatch/orphan-recovery.ts +224 -0
  398. package/src/domains/dispatch/outcome.ts +109 -0
  399. package/src/domains/dispatch/phase-timing.ts +47 -0
  400. package/src/domains/dispatch/placement.ts +202 -0
  401. package/src/domains/dispatch/receipt-findings.ts +255 -0
  402. package/src/domains/dispatch/receipt-integrity.ts +378 -0
  403. package/src/domains/dispatch/recovery-candidates.ts +74 -0
  404. package/src/domains/dispatch/reproducibility.ts +48 -0
  405. package/src/domains/dispatch/reservation-store.ts +501 -0
  406. package/src/domains/dispatch/route-approval.ts +177 -0
  407. package/src/domains/dispatch/route-candidates.ts +57 -0
  408. package/src/domains/dispatch/route-decision.ts +1079 -0
  409. package/src/domains/dispatch/route-facts.ts +187 -0
  410. package/src/domains/dispatch/route-history.ts +238 -0
  411. package/src/domains/dispatch/route-observer.ts +488 -0
  412. package/src/domains/dispatch/route-policy.ts +327 -0
  413. package/src/domains/dispatch/route-quality.ts +256 -0
  414. package/src/domains/dispatch/route-readiness.ts +120 -0
  415. package/src/domains/dispatch/routing-intent.ts +287 -0
  416. package/src/domains/dispatch/run-identity.ts +56 -0
  417. package/src/domains/dispatch/scout-transition.ts +130 -0
  418. package/src/domains/dispatch/state.ts +277 -0
  419. package/src/domains/dispatch/tool-stats.ts +132 -0
  420. package/src/domains/dispatch/transport.ts +209 -0
  421. package/src/domains/dispatch/types.ts +785 -0
  422. package/src/domains/dispatch/validation-grounding.ts +154 -0
  423. package/src/domains/dispatch/validation.ts +721 -0
  424. package/src/domains/dispatch/worker-protocol.ts +275 -0
  425. package/src/domains/dispatch/worker-spawn.ts +566 -0
  426. package/src/domains/dispatch/write-boundary-enforcer.ts +97 -0
  427. package/src/domains/dispatch/write-boundary.ts +426 -0
  428. package/src/domains/eval/artifacts/redact.ts +42 -0
  429. package/src/domains/eval/artifacts/store.ts +173 -0
  430. package/src/domains/eval/compare/compare.ts +33 -0
  431. package/src/domains/eval/compare/gates.ts +66 -0
  432. package/src/domains/eval/compare/thresholds.ts +92 -0
  433. package/src/domains/eval/harness-metrics.ts +62 -0
  434. package/src/domains/eval/index.ts +66 -0
  435. package/src/domains/eval/metrics/chaos-stream.ts +93 -0
  436. package/src/domains/eval/metrics/context.ts +34 -0
  437. package/src/domains/eval/metrics/coverage.ts +21 -0
  438. package/src/domains/eval/metrics/evidence.ts +182 -0
  439. package/src/domains/eval/metrics/fleet-loop-stream.ts +196 -0
  440. package/src/domains/eval/metrics/invariants.ts +736 -0
  441. package/src/domains/eval/metrics/latency.ts +4 -0
  442. package/src/domains/eval/metrics/token-stream.ts +139 -0
  443. package/src/domains/eval/metrics/tokens.ts +51 -0
  444. package/src/domains/eval/metrics/tool-calls.ts +7 -0
  445. package/src/domains/eval/proactive-memory.ts +279 -0
  446. package/src/domains/eval/provenance.ts +35 -0
  447. package/src/domains/eval/report.ts +234 -0
  448. package/src/domains/eval/reports/json.ts +5 -0
  449. package/src/domains/eval/reports/junit.ts +23 -0
  450. package/src/domains/eval/reports/markdown.ts +20 -0
  451. package/src/domains/eval/reports/swe-jsonl.ts +16 -0
  452. package/src/domains/eval/reports/text.ts +25 -0
  453. package/src/domains/eval/run-compare.ts +274 -0
  454. package/src/domains/eval/runner.ts +244 -0
  455. package/src/domains/eval/runners/clio-run.ts +148 -0
  456. package/src/domains/eval/runners/context-index.ts +56 -0
  457. package/src/domains/eval/runners/context-init.ts +203 -0
  458. package/src/domains/eval/runners/external-command.ts +350 -0
  459. package/src/domains/eval/runtime-paths.ts +171 -0
  460. package/src/domains/eval/schema/artifact.ts +75 -0
  461. package/src/domains/eval/schema/suite.ts +235 -0
  462. package/src/domains/eval/schema/validate.ts +339 -0
  463. package/src/domains/eval/store.ts +392 -0
  464. package/src/domains/eval/suites/load.ts +74 -0
  465. package/src/domains/eval/suites/matrix.ts +19 -0
  466. package/src/domains/eval/suites/resolve.ts +44 -0
  467. package/src/domains/eval/suites/run.ts +325 -0
  468. package/src/domains/eval/task-file.ts +186 -0
  469. package/src/domains/eval/types.ts +141 -0
  470. package/src/domains/eval/verifiers/command.ts +27 -0
  471. package/src/domains/eval/verifiers/file-exists.ts +6 -0
  472. package/src/domains/eval/verifiers/patch.ts +22 -0
  473. package/src/domains/eval/workspaces/git.ts +41 -0
  474. package/src/domains/eval/workspaces/local.ts +17 -0
  475. package/src/domains/eval/workspaces/temp-copy.ts +29 -0
  476. package/src/domains/evidence/build.ts +1714 -0
  477. package/src/domains/evidence/eval.ts +605 -0
  478. package/src/domains/evidence/failure-attribution.ts +54 -0
  479. package/src/domains/evidence/finish-contract-map.ts +42 -0
  480. package/src/domains/evidence/index.ts +55 -0
  481. package/src/domains/evidence/provenance.ts +200 -0
  482. package/src/domains/evidence/redact.ts +81 -0
  483. package/src/domains/evidence/store.ts +291 -0
  484. package/src/domains/evidence/types.ts +325 -0
  485. package/src/domains/evolution/SELF_EDIT_GATE.md +91 -0
  486. package/src/domains/evolution/index.ts +26 -0
  487. package/src/domains/evolution/manifest.ts +154 -0
  488. package/src/domains/evolution/validate.ts +304 -0
  489. package/src/domains/extensions/contract.ts +22 -0
  490. package/src/domains/extensions/discovery.ts +193 -0
  491. package/src/domains/extensions/extension.ts +46 -0
  492. package/src/domains/extensions/index.ts +34 -0
  493. package/src/domains/extensions/manager.ts +30 -0
  494. package/src/domains/extensions/manifest.ts +6 -0
  495. package/src/domains/extensions/resources.ts +38 -0
  496. package/src/domains/extensions/state.ts +224 -0
  497. package/src/domains/extensions/types.ts +83 -0
  498. package/src/domains/interop/consent.ts +185 -0
  499. package/src/domains/interop/contract.ts +29 -0
  500. package/src/domains/interop/detect.ts +173 -0
  501. package/src/domains/interop/extension.ts +59 -0
  502. package/src/domains/interop/index.ts +34 -0
  503. package/src/domains/interop/manifest.ts +6 -0
  504. package/src/domains/interop/registry.ts +139 -0
  505. package/src/domains/interop/state.ts +92 -0
  506. package/src/domains/interop/types.ts +109 -0
  507. package/src/domains/lifecycle/contract.ts +11 -0
  508. package/src/domains/lifecycle/doctor.ts +536 -0
  509. package/src/domains/lifecycle/extension.ts +23 -0
  510. package/src/domains/lifecycle/index.ts +21 -0
  511. package/src/domains/lifecycle/install-method.ts +51 -0
  512. package/src/domains/lifecycle/manifest.ts +6 -0
  513. package/src/domains/lifecycle/migrations/index.ts +94 -0
  514. package/src/domains/lifecycle/state.ts +93 -0
  515. package/src/domains/lifecycle/upgrade-notice.ts +35 -0
  516. package/src/domains/lifecycle/version.ts +35 -0
  517. package/src/domains/memory/index.ts +128 -0
  518. package/src/domains/memory/operations.ts +143 -0
  519. package/src/domains/memory/prompt-section.ts +86 -0
  520. package/src/domains/memory/proposal.ts +180 -0
  521. package/src/domains/memory/store.ts +163 -0
  522. package/src/domains/memory/task-bank.ts +229 -0
  523. package/src/domains/memory/task-memory-handoff.ts +222 -0
  524. package/src/domains/memory/task-memory-policy.ts +602 -0
  525. package/src/domains/memory/task-memory-status.ts +58 -0
  526. package/src/domains/memory/task-memory-telemetry.ts +346 -0
  527. package/src/domains/memory/task-memory-trace.ts +89 -0
  528. package/src/domains/memory/types.ts +74 -0
  529. package/src/domains/memory/validate.ts +297 -0
  530. package/src/domains/middleware/budget.ts +216 -0
  531. package/src/domains/middleware/contract.ts +35 -0
  532. package/src/domains/middleware/dispatch-nudge.ts +469 -0
  533. package/src/domains/middleware/extension.ts +129 -0
  534. package/src/domains/middleware/hook-receipts.ts +70 -0
  535. package/src/domains/middleware/hooks-io.ts +174 -0
  536. package/src/domains/middleware/hooks.ts +432 -0
  537. package/src/domains/middleware/index.ts +154 -0
  538. package/src/domains/middleware/manifest.ts +6 -0
  539. package/src/domains/middleware/memory-intervention.ts +701 -0
  540. package/src/domains/middleware/rules.ts +42 -0
  541. package/src/domains/middleware/runtime.ts +333 -0
  542. package/src/domains/middleware/skills-reminder.ts +187 -0
  543. package/src/domains/middleware/snapshot.ts +71 -0
  544. package/src/domains/middleware/stalled-turn.ts +71 -0
  545. package/src/domains/middleware/task-board-reminder.ts +87 -0
  546. package/src/domains/middleware/task-nudge.ts +77 -0
  547. package/src/domains/middleware/tool-choice-control.ts +42 -0
  548. package/src/domains/middleware/types.ts +131 -0
  549. package/src/domains/middleware/validate.ts +329 -0
  550. package/src/domains/observability/accountability.ts +67 -0
  551. package/src/domains/observability/contract.ts +168 -0
  552. package/src/domains/observability/cost.ts +206 -0
  553. package/src/domains/observability/evidence-index.ts +123 -0
  554. package/src/domains/observability/extension.ts +281 -0
  555. package/src/domains/observability/index.ts +78 -0
  556. package/src/domains/observability/manifest.ts +10 -0
  557. package/src/domains/observability/metrics.ts +37 -0
  558. package/src/domains/observability/projection.ts +477 -0
  559. package/src/domains/observability/telemetry.ts +89 -0
  560. package/src/domains/observability/trace-store.ts +1454 -0
  561. package/src/domains/prompts/compiler.ts +556 -0
  562. package/src/domains/prompts/contract.ts +30 -0
  563. package/src/domains/prompts/extension.ts +304 -0
  564. package/src/domains/prompts/fragment-loader.ts +139 -0
  565. package/src/domains/prompts/fragments/identity/clio.md +3 -2
  566. package/src/domains/prompts/fragments/identity/self-awareness.md +63 -0
  567. package/src/domains/prompts/fragments/operating/contract.md +10 -1
  568. package/src/domains/prompts/hash.ts +73 -0
  569. package/src/domains/prompts/index.ts +32 -0
  570. package/src/domains/prompts/manifest.ts +8 -0
  571. package/src/domains/prompts/memory-intervention.ts +57 -0
  572. package/src/domains/prompts/preload.ts +82 -0
  573. package/src/domains/providers/auth/api-key.ts +47 -0
  574. package/src/domains/providers/auth/backend-file.ts +69 -0
  575. package/src/domains/providers/auth/backend-memory.ts +17 -0
  576. package/src/domains/providers/auth/index.ts +37 -0
  577. package/src/domains/providers/auth/oauth.ts +36 -0
  578. package/src/domains/providers/auth/storage.ts +667 -0
  579. package/src/domains/providers/capabilities.ts +50 -0
  580. package/src/domains/providers/catalog.ts +162 -0
  581. package/src/domains/providers/contract.ts +151 -0
  582. package/src/domains/providers/credentials.ts +20 -0
  583. package/src/domains/providers/eligibility.ts +24 -0
  584. package/src/domains/providers/errors.ts +64 -0
  585. package/src/domains/providers/extension.ts +536 -0
  586. package/src/domains/providers/index.ts +142 -0
  587. package/src/domains/providers/knowledge-base-path.ts +102 -0
  588. package/src/domains/providers/manifest.ts +6 -0
  589. package/src/domains/providers/model-capabilities.ts +136 -0
  590. package/src/domains/providers/model-discovery.ts +144 -0
  591. package/src/domains/providers/model-family.ts +32 -0
  592. package/src/domains/providers/model-runtime-capabilities.ts +712 -0
  593. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +2 -2
  594. package/src/domains/providers/plugins.ts +59 -0
  595. package/src/domains/providers/probe/fingerprint.ts +44 -0
  596. package/src/domains/providers/probe/http.ts +114 -0
  597. package/src/domains/providers/probe/reasoning.ts +111 -0
  598. package/src/domains/providers/registry.ts +166 -0
  599. package/src/domains/providers/resolver.ts +156 -0
  600. package/src/domains/providers/runtime-resolution.ts +720 -0
  601. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +90 -0
  602. package/src/domains/providers/runtimes/builtins.ts +78 -0
  603. package/src/domains/providers/runtimes/claude/claude-code.ts +38 -0
  604. package/src/domains/providers/runtimes/claude/claude-sdk.ts +38 -0
  605. package/src/domains/providers/runtimes/claude/common.ts +55 -0
  606. package/src/domains/providers/runtimes/cloud/alcf.ts +171 -0
  607. package/src/domains/providers/runtimes/cloud/anthropic-max.ts +71 -0
  608. package/src/domains/providers/runtimes/cloud/anthropic.ts +43 -0
  609. package/src/domains/providers/runtimes/cloud/bedrock.ts +35 -0
  610. package/src/domains/providers/runtimes/cloud/deepseek.ts +50 -0
  611. package/src/domains/providers/runtimes/cloud/google.ts +41 -0
  612. package/src/domains/providers/runtimes/cloud/groq.ts +46 -0
  613. package/src/domains/providers/runtimes/cloud/mistral.ts +46 -0
  614. package/src/domains/providers/runtimes/cloud/openai-codex.ts +49 -0
  615. package/src/domains/providers/runtimes/cloud/openai.ts +46 -0
  616. package/src/domains/providers/runtimes/cloud/openrouter.ts +111 -0
  617. package/src/domains/providers/runtimes/common/lmstudio-logger.ts +32 -0
  618. package/src/domains/providers/runtimes/common/local-synth.ts +165 -0
  619. package/src/domains/providers/runtimes/common/probe-helpers.ts +474 -0
  620. package/src/domains/providers/runtimes/local-native/lemonade-anthropic.ts +27 -0
  621. package/src/domains/providers/runtimes/local-native/lemonade-openai.ts +26 -0
  622. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +88 -0
  623. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +207 -0
  624. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +180 -0
  625. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +126 -0
  626. package/src/domains/providers/runtimes/local-native/llamacpp.ts +98 -0
  627. package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +491 -0
  628. package/src/domains/providers/runtimes/local-native/ollama-native.ts +117 -0
  629. package/src/domains/providers/runtimes/local-native/sglang.ts +26 -0
  630. package/src/domains/providers/runtimes/local-native/vllm.ts +27 -0
  631. package/src/domains/providers/runtimes/protocol/anthropic-compat.ts +104 -0
  632. package/src/domains/providers/runtimes/protocol/anthropic-messages.ts +28 -0
  633. package/src/domains/providers/runtimes/protocol/bedrock.ts +26 -0
  634. package/src/domains/providers/runtimes/protocol/google.ts +27 -0
  635. package/src/domains/providers/runtimes/protocol/openai-compat.ts +145 -0
  636. package/src/domains/providers/support.ts +238 -0
  637. package/src/domains/providers/thinking-control-policy.ts +85 -0
  638. package/src/domains/providers/types/capability-flags.ts +114 -0
  639. package/src/domains/providers/types/cost-provenance.ts +7 -0
  640. package/src/domains/providers/types/inference.ts +41 -0
  641. package/src/domains/providers/types/knowledge-base.ts +156 -0
  642. package/src/domains/providers/types/local-model-quirks.ts +218 -0
  643. package/src/domains/providers/types/runtime-descriptor.ts +170 -0
  644. package/src/domains/providers/types/target-descriptor.ts +38 -0
  645. package/src/domains/resources/collision.ts +92 -0
  646. package/src/domains/resources/common-loader.ts +140 -0
  647. package/src/domains/resources/contract.ts +25 -0
  648. package/src/domains/resources/extension.ts +61 -0
  649. package/src/domains/resources/index.ts +74 -0
  650. package/src/domains/resources/loader.ts +86 -0
  651. package/src/domains/resources/manifest.ts +6 -0
  652. package/src/domains/resources/prompts/loader.ts +232 -0
  653. package/src/domains/resources/prompts/substitute.ts +65 -0
  654. package/src/domains/resources/skills/content-hash.ts +105 -0
  655. package/src/domains/resources/skills/evals.ts +175 -0
  656. package/src/domains/resources/skills/install.ts +410 -0
  657. package/src/domains/resources/skills/loader.ts +978 -0
  658. package/src/domains/resources/skills/marketplace.ts +255 -0
  659. package/src/domains/resources/skills/provenance-pin.ts +115 -0
  660. package/src/domains/safety/action-classifier.ts +268 -0
  661. package/src/domains/safety/approval-axis.ts +23 -0
  662. package/src/domains/safety/audit.ts +487 -0
  663. package/src/domains/safety/autonomy.ts +174 -0
  664. package/src/domains/safety/call-target.ts +59 -0
  665. package/src/domains/safety/contract.ts +65 -0
  666. package/src/domains/safety/damage-control.ts +44 -0
  667. package/src/domains/safety/default-path-policy.ts +117 -0
  668. package/src/domains/safety/extension.ts +383 -0
  669. package/src/domains/safety/finish-contract-registration.ts +128 -0
  670. package/src/domains/safety/finish-contract.ts +554 -0
  671. package/src/domains/safety/index.ts +44 -0
  672. package/src/domains/safety/loop-detector.ts +143 -0
  673. package/src/domains/safety/manifest.ts +6 -0
  674. package/src/domains/safety/path-policy.ts +164 -0
  675. package/src/domains/safety/policy-engine.ts +994 -0
  676. package/src/domains/safety/project-policy.ts +391 -0
  677. package/src/domains/safety/protected-artifacts-registration.ts +189 -0
  678. package/src/domains/safety/protected-artifacts.ts +993 -0
  679. package/src/domains/safety/rejection-feedback.ts +102 -0
  680. package/src/domains/safety/rigor.ts +72 -0
  681. package/src/domains/safety/rule-compiler.ts +48 -0
  682. package/src/domains/safety/rule-pack-loader.ts +92 -0
  683. package/src/domains/safety/run-effects.ts +197 -0
  684. package/src/domains/safety/scope.ts +61 -0
  685. package/src/domains/scheduling/budget.ts +35 -0
  686. package/src/domains/scheduling/cluster.ts +175 -0
  687. package/src/domains/scheduling/contract.ts +30 -0
  688. package/src/domains/scheduling/extension.ts +91 -0
  689. package/src/domains/scheduling/index.ts +12 -0
  690. package/src/domains/scheduling/manifest.ts +6 -0
  691. package/src/domains/session/archive-readers.ts +208 -0
  692. package/src/domains/session/checkpoint.ts +38 -0
  693. package/src/domains/session/compaction/auto.ts +89 -0
  694. package/src/domains/session/compaction/branch-summary.ts +186 -0
  695. package/src/domains/session/compaction/compact.ts +493 -0
  696. package/src/domains/session/compaction/cut-point.ts +171 -0
  697. package/src/domains/session/compaction/defaults.ts +34 -0
  698. package/src/domains/session/compaction/mask-observations.ts +265 -0
  699. package/src/domains/session/compaction/session-entries.ts +18 -0
  700. package/src/domains/session/compaction/tokens.ts +147 -0
  701. package/src/domains/session/context-accounting.ts +583 -0
  702. package/src/domains/session/context-ledger.ts +348 -0
  703. package/src/domains/session/contract.ts +144 -0
  704. package/src/domains/session/cwd-fallback.ts +65 -0
  705. package/src/domains/session/entries.ts +509 -0
  706. package/src/domains/session/extension.ts +322 -0
  707. package/src/domains/session/history.ts +172 -0
  708. package/src/domains/session/index.ts +84 -0
  709. package/src/domains/session/manager.ts +128 -0
  710. package/src/domains/session/manifest.ts +6 -0
  711. package/src/domains/session/migrations/index.ts +27 -0
  712. package/src/domains/session/prompt-manifest.ts +173 -0
  713. package/src/domains/session/protected-artifact-journal.ts +241 -0
  714. package/src/domains/session/protected-artifacts.ts +90 -0
  715. package/src/domains/session/retry.ts +238 -0
  716. package/src/domains/session/task-board.ts +375 -0
  717. package/src/domains/session/tree/active-path.ts +98 -0
  718. package/src/domains/session/tree/fork.ts +160 -0
  719. package/src/domains/session/tree/manager.ts +128 -0
  720. package/src/domains/session/tree/navigator.ts +177 -0
  721. package/src/domains/session/tree/preview.ts +259 -0
  722. package/src/domains/session/usage.ts +148 -0
  723. package/src/domains/session/workspace/git-probe.ts +161 -0
  724. package/src/domains/session/workspace/index.ts +4 -0
  725. package/src/domains/session/workspace/project-type.ts +217 -0
  726. package/src/domains/session/workspace/snapshot.ts +74 -0
  727. package/src/domains/share/archive.ts +684 -0
  728. package/src/domains/share/contract.ts +9 -0
  729. package/src/domains/share/extension.ts +26 -0
  730. package/src/domains/share/index.ts +28 -0
  731. package/src/domains/share/manifest.ts +6 -0
  732. package/src/engine/acp/adapter.ts +397 -0
  733. package/src/engine/acp/errors.ts +32 -0
  734. package/src/engine/acp/event-mapper.ts +154 -0
  735. package/src/engine/acp/server.ts +656 -0
  736. package/src/engine/acp/tool-mediator.ts +686 -0
  737. package/src/engine/acp/transport.ts +654 -0
  738. package/src/engine/acp/types.ts +170 -0
  739. package/src/engine/agent.ts +29 -0
  740. package/src/engine/ai.ts +202 -0
  741. package/src/engine/alcf-oauth.ts +198 -0
  742. package/src/engine/antigravity/subprocess-runtime.ts +205 -0
  743. package/src/engine/apis/degraded-inference.ts +95 -0
  744. package/src/engine/apis/index.ts +20 -0
  745. package/src/engine/apis/llamacpp-residency.ts +252 -0
  746. package/src/engine/apis/lmstudio-native.ts +1438 -0
  747. package/src/engine/apis/lmstudio-residency.ts +92 -0
  748. package/src/engine/apis/ollama-native.ts +507 -0
  749. package/src/engine/apis/openai-completions.ts +716 -0
  750. package/src/engine/apis/output-budget.ts +99 -0
  751. package/src/engine/apis/residency-lock.ts +29 -0
  752. package/src/engine/apis/residency.ts +640 -0
  753. package/src/engine/apis/resident-models.ts +42 -0
  754. package/src/engine/apis/sampling-overrides.ts +38 -0
  755. package/src/engine/apis/thinking-replay.ts +11 -0
  756. package/src/engine/claude/sdk-runtime.ts +685 -0
  757. package/src/engine/claude/session-id.ts +5 -0
  758. package/src/engine/claude/subprocess-runtime.ts +316 -0
  759. package/src/engine/claude/tool-safety.ts +400 -0
  760. package/src/engine/external-subprocess.ts +23 -0
  761. package/src/engine/harmony-response.ts +165 -0
  762. package/src/engine/loop-guard.ts +1127 -0
  763. package/src/engine/oauth.ts +192 -0
  764. package/src/engine/pi-mono-names.ts +13 -0
  765. package/src/engine/provider-payload.ts +315 -0
  766. package/src/engine/select-list.ts +53 -0
  767. package/src/engine/session.ts +834 -0
  768. package/src/engine/strip-tokenizer-sentinels.ts +125 -0
  769. package/src/engine/tui.ts +103 -0
  770. package/src/engine/types.ts +66 -0
  771. package/src/engine/worker-events.ts +100 -0
  772. package/src/engine/worker-runtime.ts +1022 -0
  773. package/src/engine/worker-tools.ts +203 -0
  774. package/src/entry/orchestrator.ts +1873 -0
  775. package/src/interactive/application-controller.ts +346 -0
  776. package/src/interactive/bus-notices.ts +265 -0
  777. package/src/interactive/chat-loop-messages.ts +626 -0
  778. package/src/interactive/chat-loop-policy.ts +18 -0
  779. package/src/interactive/chat-loop.ts +865 -0
  780. package/src/interactive/chat-panel.ts +1694 -0
  781. package/src/interactive/chat-renderer.ts +1038 -0
  782. package/src/interactive/clio-editor.ts +166 -0
  783. package/src/interactive/command-fallbacks.ts +23 -0
  784. package/src/interactive/command-output.ts +85 -0
  785. package/src/interactive/context-activity.ts +203 -0
  786. package/src/interactive/context-meter.ts +223 -0
  787. package/src/interactive/context-overlay.ts +220 -0
  788. package/src/interactive/cost-overlay.ts +279 -0
  789. package/src/interactive/dispatch-board.ts +1319 -0
  790. package/src/interactive/dispatch-steering.ts +86 -0
  791. package/src/interactive/editor-bash.ts +46 -0
  792. package/src/interactive/editor-steer.ts +51 -0
  793. package/src/interactive/editor-submit.ts +303 -0
  794. package/src/interactive/external-editor.ts +67 -0
  795. package/src/interactive/follow-up-queue-panel.ts +75 -0
  796. package/src/interactive/footer/dashboard.ts +534 -0
  797. package/src/interactive/footer/notifications.ts +275 -0
  798. package/src/interactive/footer/widgets.ts +1246 -0
  799. package/src/interactive/footer-panel.ts +187 -0
  800. package/src/interactive/format-time.ts +85 -0
  801. package/src/interactive/index.ts +11 -0
  802. package/src/interactive/interactive-application.ts +755 -0
  803. package/src/interactive/interactive-event-projection.ts +380 -0
  804. package/src/interactive/interactive-input-runtime.ts +261 -0
  805. package/src/interactive/interactive-presentation.ts +452 -0
  806. package/src/interactive/interactive-shell.ts +134 -0
  807. package/src/interactive/interactive-slash-runtime.ts +492 -0
  808. package/src/interactive/interactive-subscriptions.ts +111 -0
  809. package/src/interactive/interactive-tickers.ts +152 -0
  810. package/src/interactive/keybinding-manager.ts +496 -0
  811. package/src/interactive/layout.ts +20 -0
  812. package/src/interactive/leader-key.ts +143 -0
  813. package/src/interactive/loop-guard-interrupt.ts +101 -0
  814. package/src/interactive/memory-overlay.ts +280 -0
  815. package/src/interactive/overlay-ask-user-lifecycle.ts +110 -0
  816. package/src/interactive/overlay-auth-lifecycle.ts +308 -0
  817. package/src/interactive/overlay-frame.ts +502 -0
  818. package/src/interactive/overlay-general-openers.ts +217 -0
  819. package/src/interactive/overlay-key-routing.ts +213 -0
  820. package/src/interactive/overlay-lifecycle.ts +413 -0
  821. package/src/interactive/overlay-model-selectors.ts +127 -0
  822. package/src/interactive/overlay-permission-lifecycle.ts +344 -0
  823. package/src/interactive/overlay-resource-openers.ts +112 -0
  824. package/src/interactive/overlay-session-lifecycle.ts +270 -0
  825. package/src/interactive/overlay-transitions.ts +57 -0
  826. package/src/interactive/overlays/agents.ts +69 -0
  827. package/src/interactive/overlays/ask-user.ts +921 -0
  828. package/src/interactive/overlays/auth-dialog.ts +221 -0
  829. package/src/interactive/overlays/context-reset.ts +108 -0
  830. package/src/interactive/overlays/cwd-fallback.ts +141 -0
  831. package/src/interactive/overlays/extensions.ts +54 -0
  832. package/src/interactive/overlays/help-reference.ts +140 -0
  833. package/src/interactive/overlays/interop.ts +139 -0
  834. package/src/interactive/overlays/keybinding-detail.ts +90 -0
  835. package/src/interactive/overlays/list-overlay.ts +606 -0
  836. package/src/interactive/overlays/message-picker.ts +155 -0
  837. package/src/interactive/overlays/model-selector.ts +1091 -0
  838. package/src/interactive/overlays/prompts.ts +72 -0
  839. package/src/interactive/overlays/session-selector-search.ts +31 -0
  840. package/src/interactive/overlays/session-selector.ts +266 -0
  841. package/src/interactive/overlays/settings.ts +3363 -0
  842. package/src/interactive/overlays/skills-hub.ts +237 -0
  843. package/src/interactive/overlays/tree-selector.ts +387 -0
  844. package/src/interactive/permission-overlay.ts +170 -0
  845. package/src/interactive/render-trace.ts +125 -0
  846. package/src/interactive/renderers/branch-summary.ts +86 -0
  847. package/src/interactive/renderers/code-ink.ts +420 -0
  848. package/src/interactive/renderers/compaction-summary.ts +112 -0
  849. package/src/interactive/renderers/diff.ts +126 -0
  850. package/src/interactive/renderers/notice.ts +28 -0
  851. package/src/interactive/renderers/retry-status.ts +31 -0
  852. package/src/interactive/renderers/structured.ts +89 -0
  853. package/src/interactive/renderers/tool-execution.ts +1104 -0
  854. package/src/interactive/renderers/worker-entry.ts +264 -0
  855. package/src/interactive/session-last-turn.ts +186 -0
  856. package/src/interactive/session-transcript.ts +105 -0
  857. package/src/interactive/session-usage-reseed.ts +74 -0
  858. package/src/interactive/slash-autocomplete.ts +364 -0
  859. package/src/interactive/slash-commands.ts +1319 -0
  860. package/src/interactive/slash-spec.ts +453 -0
  861. package/src/interactive/status/controller.ts +256 -0
  862. package/src/interactive/status/index.ts +23 -0
  863. package/src/interactive/status/state-machine.ts +530 -0
  864. package/src/interactive/status/summary.ts +170 -0
  865. package/src/interactive/status/types.ts +121 -0
  866. package/src/interactive/status/verbs.ts +163 -0
  867. package/src/interactive/status/watchdog.ts +24 -0
  868. package/src/interactive/tasks-overlay.ts +186 -0
  869. package/src/interactive/theme/components.ts +55 -0
  870. package/src/interactive/theme/glyphs.ts +70 -0
  871. package/src/interactive/theme/index.ts +15 -0
  872. package/src/interactive/theme/labels.ts +88 -0
  873. package/src/interactive/theme/rules.ts +138 -0
  874. package/src/interactive/theme/segments.ts +88 -0
  875. package/src/interactive/theme/tokens.ts +121 -0
  876. package/src/interactive/thinking-level-policy.ts +6 -0
  877. package/src/interactive/tool-prose-loop.ts +122 -0
  878. package/src/interactive/tool-prose-registration.ts +48 -0
  879. package/src/interactive/turn-context.ts +854 -0
  880. package/src/interactive/turn-middleware.ts +283 -0
  881. package/src/interactive/turn-persistence.ts +441 -0
  882. package/src/interactive/turn-queues.ts +169 -0
  883. package/src/interactive/turn-recovery.ts +306 -0
  884. package/src/interactive/turn-runtime.ts +802 -0
  885. package/src/interactive/turn-state.ts +123 -0
  886. package/src/interactive/view/artifacts.ts +1342 -0
  887. package/src/interactive/view/view-overlay.ts +732 -0
  888. package/src/interactive/welcome-dashboard.ts +536 -0
  889. package/src/interactive/worker-receipts.ts +94 -0
  890. package/src/interactive/worker-replay.ts +111 -0
  891. package/src/interactive/worker-share.ts +100 -0
  892. package/src/interactive/worker-stream.ts +442 -0
  893. package/src/interactive/workspace-facts.ts +141 -0
  894. package/src/tools/agent-tools.ts +382 -0
  895. package/src/tools/artifact.ts +103 -0
  896. package/src/tools/ask-user.ts +656 -0
  897. package/src/tools/bash.ts +176 -0
  898. package/src/tools/bootstrap.ts +474 -0
  899. package/src/tools/codewiki/code-nav.ts +563 -0
  900. package/src/tools/codewiki/shared.ts +115 -0
  901. package/src/tools/compete-worktrees.ts +898 -0
  902. package/src/tools/context/docs-engine.ts +619 -0
  903. package/src/tools/context/index.ts +435 -0
  904. package/src/tools/credential-present.ts +151 -0
  905. package/src/tools/dispatch-arguments.ts +228 -0
  906. package/src/tools/dispatch-plan.ts +741 -0
  907. package/src/tools/dispatch-scout.ts +461 -0
  908. package/src/tools/dispatch.ts +3338 -0
  909. package/src/tools/edit-diff.ts +534 -0
  910. package/src/tools/edit.ts +128 -0
  911. package/src/tools/executables.ts +22 -0
  912. package/src/tools/file-mutation-queue.ts +35 -0
  913. package/src/tools/find.ts +292 -0
  914. package/src/tools/grep.ts +495 -0
  915. package/src/tools/ignore-policy.ts +169 -0
  916. package/src/tools/ledger.ts +206 -0
  917. package/src/tools/ls.ts +110 -0
  918. package/src/tools/monitor.ts +797 -0
  919. package/src/tools/network-policy.ts +29 -0
  920. package/src/tools/observation.ts +421 -0
  921. package/src/tools/observers.ts +99 -0
  922. package/src/tools/path-utils.ts +79 -0
  923. package/src/tools/policy.ts +154 -0
  924. package/src/tools/profiles.ts +108 -0
  925. package/src/tools/read.ts +173 -0
  926. package/src/tools/registry.ts +1157 -0
  927. package/src/tools/result-shaping.ts +204 -0
  928. package/src/tools/safe-exec.ts +128 -0
  929. package/src/tools/spawn-hygiene.ts +182 -0
  930. package/src/tools/steer.ts +88 -0
  931. package/src/tools/string-enum.ts +15 -0
  932. package/src/tools/tasks.ts +168 -0
  933. package/src/tools/truncate-utf8.ts +17 -0
  934. package/src/tools/truncate.ts +202 -0
  935. package/src/tools/verify/frontend.ts +685 -0
  936. package/src/tools/verify/index.ts +66 -0
  937. package/src/tools/verify/scripts.ts +108 -0
  938. package/src/tools/web-fetch.ts +777 -0
  939. package/src/tools/worker-evidence.ts +215 -0
  940. package/src/tools/write.ts +52 -0
  941. package/src/utils/exif-orientation.ts +192 -0
  942. package/src/utils/git.ts +24 -0
  943. package/src/utils/image-resize.ts +148 -0
  944. package/src/utils/photon.ts +103 -0
  945. package/src/worker/control-lane.ts +22 -0
  946. package/src/worker/entry.ts +257 -0
  947. package/src/worker/event-projection.ts +56 -0
  948. package/src/worker/heartbeat.ts +24 -0
  949. package/src/worker/ledger-mirror.ts +95 -0
  950. package/src/worker/ndjson.ts +40 -0
  951. package/src/worker/protocol.ts +529 -0
  952. package/src/worker/resource-facts.ts +79 -0
  953. package/src/worker/runtime-registry.ts +12 -0
  954. package/src/worker/spec-contract.ts +654 -0
  955. package/src/worker/stdin-demux.ts +316 -0
  956. package/dist/acp-UMLFVA3F.js +0 -92
  957. package/dist/chunk-262G75JS.js +0 -35
  958. package/dist/chunk-5LDRNKX2.js +0 -1063
  959. package/dist/chunk-67MTHP2E.js +0 -135
  960. package/dist/chunk-GOTUR54M.js +0 -9
  961. package/dist/chunk-TPOCL34A.js +0 -2876
  962. package/dist/chunk-XV4ZJ6ZM.js +0 -3177
  963. package/dist/clio-KIQ5SNDS.js +0 -53
  964. package/dist/configure-G4E3A2PG.js +0 -27
  965. package/dist/doctor-M5HJJZOL.js +0 -61
  966. package/dist/domains/agents/builtins/architect.md +0 -33
  967. package/dist/domains/agents/builtins/coder.md +0 -31
  968. package/dist/domains/agents/builtins/context-bootstrap.md +0 -38
  969. package/dist/domains/agents/builtins/debugger.md +0 -30
  970. package/dist/domains/agents/builtins/documenter.md +0 -31
  971. package/dist/domains/agents/builtins/git-master.md +0 -30
  972. package/dist/domains/agents/builtins/provenance.md +0 -30
  973. package/dist/domains/agents/builtins/researcher.md +0 -71
  974. package/dist/domains/agents/builtins/scout.md +0 -42
  975. package/dist/domains/agents/builtins/tester.md +0 -31
  976. package/dist/domains/agents/builtins/verifier.md +0 -30
  977. package/dist/domains/agents/builtins/wiki-writer.md +0 -41
  978. package/dist/fleet-preflight-CLIAX7YR.js +0 -21
  979. package/dist/preload-KZVHET2B.js +0 -11
  980. package/dist/version-ROZ6CZKH.js +0 -16
  981. package/skills/git/commit-crafting/SKILL.md +0 -79
  982. package/skills/git/commit-crafting/evals.md +0 -92
  983. package/skills/git/create-pr/SKILL.md +0 -116
  984. package/skills/git/create-pr/evals.md +0 -114
  985. package/skills/git/investigate-issue/SKILL.md +0 -139
  986. package/skills/git/investigate-issue/evals.md +0 -94
  987. package/skills/git/review-changes/SKILL.md +0 -103
  988. package/skills/git/review-changes/evals.md +0 -85
@@ -0,0 +1,1157 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import type { TSchema } from "typebox";
3
+ import { isWorkerToolCallCapExceededReason } from "../core/guardrails.js";
4
+ import { HEADLESS_PERMISSION_DENIED_MARKER } from "../core/headless-permission.js";
5
+ import {
6
+ evaluateSkillToolSurface,
7
+ type PendingSkillToolPolicy,
8
+ type SkillToolSurfaceViolation,
9
+ } from "../core/skill-activation.js";
10
+ import { type ToolName, ToolNames } from "../core/tool-names.js";
11
+ import type { MiddlewareContract } from "../domains/middleware/contract.js";
12
+ import type { MiddlewareEffect, MiddlewareHookInput, MiddlewareMetadataValue } from "../domains/middleware/types.js";
13
+ import type { ActionClass, ClassifierCall } from "../domains/safety/action-classifier.js";
14
+ import { approvalAxisId } from "../domains/safety/approval-axis.js";
15
+ import {
16
+ type AutonomyExposure,
17
+ type AutonomyLevel,
18
+ autonomyAskRejection,
19
+ autonomyDenyRejection,
20
+ DEFAULT_AUTONOMY_EXPOSURE,
21
+ DEFAULT_AUTONOMY_LEVEL,
22
+ mapAutonomy,
23
+ } from "../domains/safety/autonomy.js";
24
+ import type { SafetyContract, SafetyDecision } from "../domains/safety/contract.js";
25
+ import { hashToolCall } from "../domains/safety/loop-detector.js";
26
+ import { detectValidationCommand } from "../domains/safety/protected-artifacts.js";
27
+ import { askUserExposure } from "./ask-user.js";
28
+ import { type DispatchPlanView, describeDispatchPlan } from "./dispatch-plan.js";
29
+ import { shapeToolResult } from "./result-shaping.js";
30
+
31
+ /**
32
+ * Tool registry. Admission point for every tool call. Delegates classification
33
+ * and policy decisions to the safety domain, parks one-shot confirmation asks,
34
+ * and runs admitted tool bodies. Never throws on safety rejections; the caller
35
+ * surfaces the rejection message back to the model.
36
+ */
37
+
38
+ /**
39
+ * Per-tool execution mode override forwarded to pi-agent-core's
40
+ * `AgentTool.executionMode`. Parallel tools may run concurrently with other
41
+ * parallel tool calls; sequential tools run one at a time and prevent any
42
+ * other tool in the batch from running in parallel with them. Leaving this
43
+ * undefined defers to the agent loop's global `toolExecution` setting.
44
+ */
45
+ export type ToolExecutionMode = "sequential" | "parallel";
46
+ export type ToolSourceScope = "core" | "domain";
47
+ export type ToolRetrySafety = "idempotent" | "retry_safe" | "not_retry_safe" | "unknown";
48
+ export type ToolCostLatencyClass = "local_fast" | "local_medium" | "local_slow" | "network" | "agent";
49
+
50
+ export interface ToolSourceInfo {
51
+ path: string;
52
+ scope: ToolSourceScope;
53
+ }
54
+
55
+ export interface ToolResultSizePolicy {
56
+ kind: "exact" | "bounded" | "summary" | "truncate";
57
+ maxBytes?: number;
58
+ followUpHint?: string;
59
+ }
60
+
61
+ export interface ToolMetadata {
62
+ /** Short statement of the tool's purpose for audit/UI surfaces. */
63
+ objective: string;
64
+ /** Stable UI label shown in compact renderers. */
65
+ uiLabel: string;
66
+ /** Whether automatic recovery may safely retry an unfinished call. */
67
+ retrySafety: ToolRetrySafety;
68
+ /** Expected result-size behavior at the registry boundary. */
69
+ resultSizePolicy: ToolResultSizePolicy;
70
+ /** Coarse cost/latency bucket for dashboard diagnostics. */
71
+ costLatency: ToolCostLatencyClass;
72
+ /**
73
+ * One sentence of usage guidance rendered into the session Tool Contract
74
+ * when this tool is on the frozen surface. Hints render sorted by tool
75
+ * name, so the compiled prompt stays byte-stable per surface. Most tools
76
+ * need none; the schema description covers them.
77
+ */
78
+ promptHint?: string;
79
+ }
80
+
81
+ export interface ToolSpec {
82
+ name: ToolName;
83
+ description: string;
84
+ sourceInfo?: ToolSourceInfo;
85
+ metadata?: ToolMetadata;
86
+ /**
87
+ * TypeBox schema advertised to the model so it knows which named
88
+ * parameters the tool accepts. Must be a Type.Object(...). Runtime
89
+ * validation still happens inside `run()`, so the schema is advisory
90
+ * to the model, not an enforcement boundary.
91
+ */
92
+ parameters: TSchema;
93
+ /** Base action class for this tool when arguments are trivial. */
94
+ baseActionClass: ActionClass;
95
+ /**
96
+ * Per-tool execution mode. Read-only tools set `"parallel"` so the model
97
+ * can batch scans; mutating or filesystem-racing tools set `"sequential"`
98
+ * so two `bash` or `edit` calls in the same batch never run concurrently.
99
+ */
100
+ executionMode?: ToolExecutionMode;
101
+ /**
102
+ * Optional argument normalizer applied before the tool body (and its own
103
+ * internal validation). Mirrors pi's `prepareArguments`: lets a tool accept
104
+ * the common weak-model argument shapes (legacy top-level fields, a
105
+ * JSON-string array) without hand-parsing inside every `run`. Must be pure
106
+ * and idempotent; a throwing normalizer is ignored and the raw args pass
107
+ * through. Tools that also want direct `run` calls normalized should invoke
108
+ * the same function at the top of `run`.
109
+ */
110
+ prepareArguments?(args: Record<string, unknown>): Record<string, unknown>;
111
+ /**
112
+ * Synchronous admission planner. Unlike `prepareArguments`, this runs before
113
+ * safety/autonomy mapping so approval-sensitive tools can attach the exact
114
+ * immutable artifact—and provisional resources—that admission and execution
115
+ * share. Resource owners must pair it with `disposeAdmissionArguments`.
116
+ */
117
+ prepareAdmissionArguments?(args: Record<string, unknown>): Record<string, unknown>;
118
+ /** Release provisional resources when prepared admission is denied or execution returns. */
119
+ disposeAdmissionArguments?(args: Record<string, unknown>): void;
120
+ /** Trusted admission artifact renderer used by policy after preparation. */
121
+ describeDispatchPlan?(args: Record<string, unknown>): DispatchPlanView;
122
+ /** Execute the tool. Only called after admission. */
123
+ run(args: Record<string, unknown>, options?: ToolInvokeOptions): Promise<ToolResult>;
124
+ }
125
+
126
+ export type ToolResultDetails = Record<string, unknown>;
127
+
128
+ export type ToolResult =
129
+ | {
130
+ kind: "ok";
131
+ output: string;
132
+ details?: ToolResultDetails;
133
+ /**
134
+ * Early-termination hint propagated to pi-agent-core's
135
+ * `AgentToolResult.terminate`. When every finalized tool result in
136
+ * the current batch sets this to true, the agent loop stops without
137
+ * a follow-up LLM call. Used by terminal artifact writers where
138
+ * writing the artifact is the whole turn.
139
+ */
140
+ terminate?: boolean;
141
+ }
142
+ | { kind: "error"; message: string; details?: ToolResultDetails };
143
+
144
+ export interface RegistryDeps {
145
+ safety: SafetyContract;
146
+ /**
147
+ * Hook layer. The loop guard, registered on `before_tool` by both
148
+ * composition roots (entry/orchestrator.ts and worker-runtime.ts via
149
+ * engine/loop-guard.ts), observes every call attempt through this contract;
150
+ * the registry feeds it `metadata.callFingerprint` and runs `before_tool`
151
+ * for safety-blocked attempts too, so repetition of rejected calls stays
152
+ * observable.
153
+ */
154
+ middleware?: MiddlewareContract;
155
+ /**
156
+ * Provider-routing effects emitted by tool hooks apply to the next model
157
+ * round. The registry owns those hook calls, so it forwards the complete
158
+ * effect batch to the composition root after evaluation. Consumers may only
159
+ * narrow routing (`require_tool` / `lock_tools`); admission still happens
160
+ * through this registry on the resulting call.
161
+ */
162
+ onMiddlewareEffects?: (effects: ReadonlyArray<MiddlewareEffect>, input: MiddlewareHookInput) => void;
163
+ /**
164
+ * Live autonomy level (sd-01 §2.2). Read per admission so hot-reloaded
165
+ * settings apply to the next call. The orchestrator wires this to current
166
+ * settings; workers wire it to the level carried on their WorkerSpec.
167
+ * Absent means the default level (M7: auto-edit).
168
+ */
169
+ autonomy?: () => AutonomyLevel;
170
+ }
171
+
172
+ export interface ToolInvokeOptions {
173
+ signal?: AbortSignal;
174
+ runId?: string;
175
+ sessionId?: string;
176
+ turnId?: string;
177
+ toolCallId?: string;
178
+ correlationId?: string;
179
+ pendingSkillPolicy?: PendingSkillToolPolicy;
180
+ askUserPolicy?: AskUserToolPolicy;
181
+ /** Registry-authenticated one-shot operator approval for this execution. */
182
+ approval?: { requestId: string; requestedBy: string; actionClass: ActionClass };
183
+ /**
184
+ * How long this call sat parked awaiting an operator decision, reported when
185
+ * the park resolves. A caller timing the invocation is measuring the tool,
186
+ * and the park is the operator, so the two have to be separable: without
187
+ * this a `npm test` approved after a minute of reading was recorded as a
188
+ * one-minute test run in the transcript, the receipt, and toolStats.
189
+ */
190
+ onParked?: (parkedMs: number) => void;
191
+ }
192
+
193
+ export type AskUserInterviewStatus = "idle" | "active" | "complete" | "cancelled";
194
+
195
+ export interface AskUserTranscriptQuestion {
196
+ question: string;
197
+ header?: string;
198
+ options?: Array<{ label: string; description?: string }>;
199
+ multi_select?: boolean;
200
+ }
201
+
202
+ export interface AskUserTranscriptAnswer {
203
+ question: string;
204
+ answer: string;
205
+ }
206
+
207
+ export interface AskUserTranscriptDecision {
208
+ key: string;
209
+ value: string;
210
+ label?: string;
211
+ source_question?: string;
212
+ }
213
+
214
+ export interface AskUserTranscriptRound {
215
+ round: number;
216
+ requestedAt: string;
217
+ answeredAt?: string;
218
+ questions: AskUserTranscriptQuestion[];
219
+ answers: AskUserTranscriptAnswer[];
220
+ cancelled?: boolean;
221
+ }
222
+
223
+ export interface AskUserToolPolicy {
224
+ id: string;
225
+ status: AskUserInterviewStatus;
226
+ startedAt: string;
227
+ updatedAt: string;
228
+ endedAt?: string;
229
+ sessionId?: string;
230
+ turnId?: string;
231
+ transcriptPath?: string;
232
+ summary?: string;
233
+ rounds: AskUserTranscriptRound[];
234
+ decisions: AskUserTranscriptDecision[];
235
+ inFlight: boolean;
236
+ cancelled: boolean;
237
+ answerCount: number;
238
+ callCount: number;
239
+ maxCalls: number;
240
+ askedQuestionKeys: Set<string>;
241
+ }
242
+
243
+ /**
244
+ * One-shot elevation grant. The interactive layer issues this when the user
245
+ * confirms a single parked tool call without changing persistent posture.
246
+ * The registry consumes the grant on the next `resumeParkedCalls` pass so
247
+ * exactly one parked call receives elevated admission; subsequent calls go
248
+ * back through the normal safety gate.
249
+ */
250
+ export interface OneShotGrant {
251
+ /** Parked action class approved for this single admission pass. */
252
+ actionClass: ActionClass;
253
+ /** Parked approval request approved for this single admission pass. */
254
+ requestId?: string;
255
+ /** Free-form origin tag carried into audit (`tool`, `keybind:single`, ...). */
256
+ requestedBy: string;
257
+ }
258
+
259
+ export interface PermissionRequiredMeta {
260
+ requestId: string;
261
+ axis: string;
262
+ /**
263
+ * Provider tool-call id carried on the parked call's invoke options, when
264
+ * one exists. The interactive layer uses it to correlate the park to its
265
+ * transcript tool segment so a parked call renders as awaiting approval
266
+ * instead of running. Purely descriptive: park/resume semantics never
267
+ * read it.
268
+ */
269
+ toolCallId?: string;
270
+ }
271
+
272
+ export interface ToolRegistry {
273
+ register(spec: ToolSpec): void;
274
+ /** Tools visible in the single operating posture. Models only see these. */
275
+ listVisible(): ReadonlyArray<ToolSpec>;
276
+ /** Tools registered overall. For /audit, /doctor. */
277
+ listAll(): ReadonlyArray<ToolSpec>;
278
+ /** Lookup by tool id. */
279
+ get(name: ToolName): ToolSpec | undefined;
280
+ /** Tool names registered in the single operating posture. */
281
+ listRegistered(): ReadonlyArray<ToolName>;
282
+ /**
283
+ * Admission point. Classifies, evaluates safety, and either runs or
284
+ * returns a rejection. Never throws on safety rejections. When the
285
+ * a safety ask or confirmable action is encountered, the returned promise
286
+ * stays pending until a resume or cancel method resolves it.
287
+ */
288
+ invoke(call: ClassifierCall, options?: ToolInvokeOptions): Promise<RegistryVerdict>;
289
+ /**
290
+ * True while at least one call awaits operator confirmation. The interactive
291
+ * layer reads this from `closeOverlay()` to re-open the confirmation overlay
292
+ * whenever an unrelated overlay closes with a parked call still pending.
293
+ */
294
+ hasParkedCalls(): boolean;
295
+ /** Number of calls currently waiting for operator confirmation. */
296
+ parkedCount(): number;
297
+ /**
298
+ * Re-fire the permission-required signal for the oldest parked call without
299
+ * changing queue order or resolving anything.
300
+ */
301
+ renotifyHead(): void;
302
+ /**
303
+ * Re-run admission for every parked call. When `grant` is provided the
304
+ * grant covers one parked action class. Calls admitted on retry
305
+ * execute and their original promise resolves with the result. Calls still
306
+ * waiting for confirmation stay parked.
307
+ */
308
+ resumeParkedCalls(grant?: OneShotGrant): Promise<void>;
309
+ /**
310
+ * Resolve one parked call with a `blocked` verdict carrying `reason`.
311
+ * Returns true when the request was found.
312
+ */
313
+ cancelParkedCall(requestId: string, reason: string): boolean;
314
+ /**
315
+ * Resolve every parked call with a `blocked` verdict carrying `reason`.
316
+ * Used when the confirmation overlay is cancelled so the agent loop sees a
317
+ * clean rejection instead of an indefinitely pending tool call.
318
+ */
319
+ cancelParkedCalls(reason: string): void;
320
+ /**
321
+ * Subscribe to the signal fired when a call is parked awaiting permission
322
+ * confirmation. Returns an unsubscribe handle.
323
+ */
324
+ onPermissionRequired(
325
+ listener: (call: ClassifierCall, decision: SafetyDecision, meta: PermissionRequiredMeta) => void,
326
+ ): () => void;
327
+ /**
328
+ * Subscribe to the signal fired when the autonomy mapping auto-denies a
329
+ * call (deny dispositions, today only at read-only). The verdict already
330
+ * carries the rejection; this exists for operator-facing notices.
331
+ */
332
+ onAutonomyDenied(listener: (call: ClassifierCall, decision: SafetyDecision, level: AutonomyLevel) => void): () => void;
333
+ }
334
+
335
+ export type RegistryVerdict =
336
+ | { kind: "ok"; result: ToolResult; decision: SafetyDecision }
337
+ | { kind: "blocked"; reason: string; decision: SafetyDecision }
338
+ | { kind: "not_visible"; reason: string };
339
+
340
+ interface ParkedCall {
341
+ call: ClassifierCall;
342
+ decision: SafetyDecision;
343
+ meta: PermissionRequiredMeta;
344
+ resolve: (verdict: RegistryVerdict) => void;
345
+ /**
346
+ * Seals the park at the instant the operator's decision lands, before the
347
+ * admitted body runs. Idempotent, so the resolve paths that never reach a
348
+ * body still report exactly one park.
349
+ */
350
+ closePark: () => void;
351
+ options?: ToolInvokeOptions;
352
+ abortCleanup?: () => void;
353
+ }
354
+
355
+ export function createRegistry(deps: RegistryDeps): ToolRegistry {
356
+ const tools = new Map<ToolName, ToolSpec>();
357
+ const parked: ParkedCall[] = [];
358
+ const permissionListeners = new Set<
359
+ (call: ClassifierCall, decision: SafetyDecision, meta: PermissionRequiredMeta) => void
360
+ >();
361
+ const autonomyDeniedListeners = new Set<
362
+ (call: ClassifierCall, decision: SafetyDecision, level: AutonomyLevel) => void
363
+ >();
364
+ let approvalRequestCounter = 0;
365
+ const approvalRequestToken = randomBytes(4).toString("hex");
366
+
367
+ const runSpec = async (
368
+ spec: ToolSpec,
369
+ call: ClassifierCall,
370
+ decision: SafetyDecision,
371
+ options?: ToolInvokeOptions,
372
+ ): Promise<RegistryVerdict> => {
373
+ // The hook layer is the only control stage past safety admission. Guards
374
+ // (loop, protected artifacts, dispatch dedup) are before_tool
375
+ // registrations; the first block_tool effect decides the verdict.
376
+ const beforeEffects = runToolHook("before_tool", spec, call, decision, options);
377
+ const block = firstBlockToolEffect(beforeEffects);
378
+ if (block) {
379
+ const verdict = guardBlockedVerdict(decision, call.tool, block.reason);
380
+ recordRegistryDisposition(call, verdict.decision, "blocked", {
381
+ reasonCode: GUARD_BLOCK_REASON_CODE,
382
+ reasons: [block.reason],
383
+ });
384
+ return verdict;
385
+ }
386
+ try {
387
+ const preparedArgs = prepareToolArgs(spec, call.args ?? {});
388
+ const result = shapeToolResult(spec, await spec.run(preparedArgs, options), options);
389
+ const afterEffects = runToolHook("after_tool", spec, call, decision, options, result);
390
+ const finalResult = shapeToolResult(spec, applyToolResultEffects(result, afterEffects), options);
391
+ return { kind: "ok", result: finalResult, decision };
392
+ } catch (err) {
393
+ const message = err instanceof Error ? err.message : String(err);
394
+ const result = shapeToolResult(spec, { kind: "error", message }, options);
395
+ const afterEffects = runToolHook("after_tool", spec, call, decision, options, result);
396
+ return {
397
+ kind: "ok",
398
+ result: shapeToolResult(spec, applyToolResultEffects(result, afterEffects), options),
399
+ decision,
400
+ };
401
+ } finally {
402
+ disposeAdmissionArgs(spec, call.args ?? {});
403
+ }
404
+ };
405
+
406
+ const runToolHook = (
407
+ hook: "before_tool" | "after_tool",
408
+ spec: ToolSpec,
409
+ call: ClassifierCall,
410
+ decision: SafetyDecision,
411
+ options: ToolInvokeOptions | undefined,
412
+ result?: ToolResult,
413
+ ): ReadonlyArray<MiddlewareEffect> => {
414
+ if (!deps.middleware) return [];
415
+ const input = buildToolHookInput(hook, spec, call, decision, "operating", options, result);
416
+ const effects = deps.middleware.runHook(input).effects;
417
+ try {
418
+ deps.onMiddlewareEffects?.(effects, input);
419
+ } catch {
420
+ // A provider-routing sink is advisory to this call. It may narrow the
421
+ // next request, but it must never throw through tool admission.
422
+ }
423
+ return effects;
424
+ };
425
+
426
+ type AdmitOutcome =
427
+ | { kind: "terminal"; verdict: RegistryVerdict }
428
+ | { kind: "execute"; spec: ToolSpec; decision: SafetyDecision }
429
+ | { kind: "park"; decision: SafetyDecision; axis: string };
430
+
431
+ const admit = (call: ClassifierCall, grant?: OneShotGrant, options?: ToolInvokeOptions): AdmitOutcome => {
432
+ const spec = tools.get(call.tool as ToolName);
433
+ if (!spec) {
434
+ return { kind: "terminal", verdict: { kind: "not_visible", reason: `tool not registered: ${call.tool}` } };
435
+ }
436
+ const level = deps.autonomy?.() ?? DEFAULT_AUTONOMY_LEVEL;
437
+ const decision = applyRegisteredToolClassification(deps.safety.evaluate(call, grant ? "confirmed" : undefined), spec);
438
+ // Stage 1, the safety net (level-independent): engine blocks are final;
439
+ // engine asks are confirm rails that park at every level. read-only is
440
+ // the exception by definition: approvals are never invoked there, so a
441
+ // confirm rail resolves as the same auto-deny as any other mutation.
442
+ if (decision.kind === "block") {
443
+ return { kind: "terminal", verdict: { kind: "blocked", reason: decision.rejection.short, decision } };
444
+ }
445
+ // Stage 1.5, the skill tool surface: a loaded SKILL.md that declares
446
+ // allowed-tools or disallowed-tools narrows the surface until the
447
+ // policy's lifetime ends (turn end for the main agent, run end for
448
+ // workers). Narrowing only blocks; it never grants, and an out-of-
449
+ // surface call blocks terminally instead of parking for confirmation.
450
+ const surfaceViolation = evaluateSkillToolSurface(options?.pendingSkillPolicy, call.tool);
451
+ if (surfaceViolation) {
452
+ const verdict = skillSurfaceBlockedVerdict(decision, call.tool, surfaceViolation);
453
+ recordRegistryDisposition(call, verdict.decision, "blocked", {
454
+ reasonCode: "skill_surface",
455
+ reasons: [verdict.reason],
456
+ });
457
+ return { kind: "terminal", verdict };
458
+ }
459
+ const actionClass = decision.classification.actionClass;
460
+ if (decision.kind === "ask") {
461
+ if (level === "read-only") {
462
+ const verdict = autonomyDenyVerdict(decision, level, call.tool, actionClass);
463
+ recordRegistryDisposition(call, verdict.decision, "denied", { reasonCode: `autonomy:${level}` });
464
+ notifyAutonomyDenied(call, verdict.decision, level);
465
+ return { kind: "terminal", verdict };
466
+ }
467
+ if (grant?.actionClass === actionClass) return { kind: "execute", spec, decision };
468
+ return { kind: "park", decision, axis: approvalAxisId(decision, level) };
469
+ }
470
+ // One-shot grant: resumeParkedCalls re-admits exactly the parked call
471
+ // the operator approved, with a confirmed posture. The engine converts
472
+ // its confirm rail to an allow (including M3 git ask rules), so the
473
+ // grant match executes directly instead of re-entering the mapping.
474
+ if (grant?.actionClass === actionClass) {
475
+ return { kind: "execute", spec, decision };
476
+ }
477
+ if (actionClass === "git_destructive") {
478
+ recordRegistryDisposition(call, decision, "blocked", {
479
+ reasons: [`action ${actionClass} is hard-blocked`],
480
+ reasonCode: "classification:git_destructive",
481
+ });
482
+ return {
483
+ kind: "terminal",
484
+ verdict: {
485
+ kind: "blocked",
486
+ reason: `action ${actionClass} is hard-blocked`,
487
+ decision,
488
+ },
489
+ };
490
+ }
491
+ // Stage 2, the autonomy mapping (sd-01 §2.3): the net passed; the level
492
+ // decides run / ask / deny per action class. Plan-scale dispatch calls
493
+ // (multi-task, compete, remote node) carry the plan flag so supervised
494
+ // levels route them through one plan approval.
495
+ const dispatchPlan =
496
+ call.tool === ToolNames.Dispatch
497
+ ? (spec.describeDispatchPlan?.(call.args ?? {}) ?? describeDispatchPlan(call.args))
498
+ : null;
499
+ const planScale = dispatchPlan?.planScale === true;
500
+ // The exposure tier rides on the args of the gate that raised it, so the
501
+ // workflow author, not the classifier, decides what counts as outward.
502
+ // ask_user is the only tool that declares one today.
503
+ const exposure = call.tool === ToolNames.AskUser ? askUserExposure(call.args) : DEFAULT_AUTONOMY_EXPOSURE;
504
+ const disposition = mapAutonomy(level, actionClass, {
505
+ executeRecognized: decision.policy?.execRecognition !== "unrecognized",
506
+ ...(planScale ? { dispatchPlanScale: true } : {}),
507
+ ...(exposure === "outward" ? { exposure } : {}),
508
+ });
509
+ if (disposition === "deny") {
510
+ const verdict = autonomyDenyVerdict(decision, level, call.tool, actionClass);
511
+ recordRegistryDisposition(call, verdict.decision, "denied", { reasonCode: `autonomy:${level}` });
512
+ notifyAutonomyDenied(call, verdict.decision, level);
513
+ return { kind: "terminal", verdict };
514
+ }
515
+ if (disposition === "ask") {
516
+ const askDecision =
517
+ planScale && dispatchPlan !== null
518
+ ? toDispatchPlanAskDecision(decision, level, dispatchPlan)
519
+ : toAutonomyAskDecision(decision, level, call.tool, actionClass, exposure);
520
+ return { kind: "park", decision: askDecision, axis: approvalAxisId(askDecision, level) };
521
+ }
522
+ recordRegistryDisposition(call, decision, "allowed");
523
+ return { kind: "execute", spec, decision };
524
+ };
525
+
526
+ const recordRegistryDisposition = (
527
+ call: ClassifierCall,
528
+ decision: SafetyDecision,
529
+ auditDecision: "allowed" | "blocked" | "permission_requested" | "denied",
530
+ overrides?: { reasons?: ReadonlyArray<string>; reasonCode?: string; requestId?: string },
531
+ ): void => {
532
+ // Row sequence for net-pass calls: safety.evaluate writes `classified`;
533
+ // registry admission writes the final autonomy disposition. Confirmed
534
+ // re-admissions keep their existing `allowed` row from safety.evaluate.
535
+ // When the registry, not the policy engine, made the final call, the
536
+ // caller passes a reasonCode override so the row does not repeat the
537
+ // net pass's "allowed" code on a non-allowed decision.
538
+ const reasons = overrides?.reasons;
539
+ deps.safety.audit.recordToolCall?.({
540
+ tool: call.tool,
541
+ classification: decision.classification,
542
+ decision: auditDecision,
543
+ args: call.args,
544
+ ...(overrides?.requestId !== undefined ? { requestId: overrides.requestId } : {}),
545
+ ...(decision.policy !== undefined ? { policy: decision.policy } : {}),
546
+ ...(overrides?.reasonCode !== undefined ? { reasonCode: overrides.reasonCode } : {}),
547
+ ...(reasons !== undefined ? { reasons } : decision.kind === "allow" ? {} : { reasons: [decision.rejection.detail] }),
548
+ });
549
+ };
550
+
551
+ /**
552
+ * Loop-observe a safety-blocked attempt. The verdict stands and every
553
+ * effect is discarded; this exists so the before_tool loop guard sees
554
+ * rejected attempts too. Without it, a model repeating an identical
555
+ * blocked call would never trip the detector (the former worker guard sat
556
+ * in front of admission and had this coverage).
557
+ */
558
+ const observeBlockedAttempt = (
559
+ call: ClassifierCall,
560
+ verdict: RegistryVerdict,
561
+ options?: ToolInvokeOptions,
562
+ ): RegistryVerdict | null => {
563
+ if (verdict.kind !== "blocked") return null;
564
+ return guardOverrideForRejectedAttempt(
565
+ call,
566
+ verdict.decision,
567
+ observeRejectedAttempt(call, verdict.decision, options),
568
+ );
569
+ };
570
+
571
+ /**
572
+ * Run before_tool hooks for an attempt that will not execute, so repetition
573
+ * detectors see it. Most effects do not change the rejection itself; the
574
+ * worker tool-call cap is the exception, because it is the deterministic
575
+ * run bound for denied-call spirals. The returned value is the first
576
+ * block_tool reason (the loop guard's actionable feedback), which the
577
+ * park-denial path substitutes for its generic reason so a model retrying a
578
+ * denied call learns to stop instead of looping until the run times out.
579
+ */
580
+ const observeRejectedAttempt = (
581
+ call: ClassifierCall,
582
+ decision: SafetyDecision,
583
+ options?: ToolInvokeOptions,
584
+ ): string | null => {
585
+ if (!deps.middleware) return null;
586
+ const spec = tools.get(call.tool as ToolName);
587
+ if (!spec) return null;
588
+ const effects = runToolHook("before_tool", spec, call, decision, options);
589
+ return firstBlockToolEffect(effects)?.reason ?? null;
590
+ };
591
+
592
+ const guardOverrideForRejectedAttempt = (
593
+ call: ClassifierCall,
594
+ decision: SafetyDecision,
595
+ reason: string | null,
596
+ ): RegistryVerdict | null => {
597
+ if (reason === null || !isWorkerToolCallCapExceededReason(reason)) return null;
598
+ const verdict = guardBlockedVerdict(decision, call.tool, reason);
599
+ recordRegistryDisposition(call, verdict.decision, "blocked", {
600
+ reasonCode: GUARD_BLOCK_REASON_CODE,
601
+ reasons: [reason],
602
+ });
603
+ return verdict;
604
+ };
605
+
606
+ const cleanupParkedEntry = (entry: ParkedCall): void => {
607
+ entry.abortCleanup?.();
608
+ delete entry.abortCleanup;
609
+ };
610
+
611
+ const resolveParkedAsBlocked = (entry: ParkedCall, reason: string): void => {
612
+ cleanupParkedEntry(entry);
613
+ disposeAdmissionArgs(tools.get(entry.call.tool as ToolName), entry.call.args ?? {});
614
+ // A denied/cancelled park is still a model attempt: observe it so
615
+ // identical retries trip the loop detector. When the detector fires, its
616
+ // reason replaces the generic denial so the model gets recovery guidance.
617
+ const loopReason = observeRejectedAttempt(entry.call, entry.decision, entry.options);
618
+ entry.resolve(
619
+ guardOverrideForRejectedAttempt(entry.call, entry.decision, loopReason) ??
620
+ parkAnsweredBlockedVerdict(entry.decision, entry.call.tool, loopReason ?? reason),
621
+ );
622
+ };
623
+
624
+ const reparkEntry = (entry: ParkedCall, index?: number): void => {
625
+ if (index === undefined || index < 0 || index >= parked.length) {
626
+ parked.push(entry);
627
+ return;
628
+ }
629
+ parked.splice(index, 0, entry);
630
+ };
631
+
632
+ const nextApprovalRequestId = (): string => `apr-${approvalRequestToken}-${++approvalRequestCounter}`;
633
+
634
+ const notifyPermissionRequired = (
635
+ call: ClassifierCall,
636
+ decision: SafetyDecision,
637
+ meta: PermissionRequiredMeta,
638
+ ): void => {
639
+ for (const listener of permissionListeners) {
640
+ try {
641
+ listener(call, decision, meta);
642
+ } catch {
643
+ // Listener errors never abort admission; they are surfaced via
644
+ // whatever observability the caller wires up.
645
+ }
646
+ }
647
+ };
648
+
649
+ const notifyAutonomyDenied = (call: ClassifierCall, decision: SafetyDecision, level: AutonomyLevel): void => {
650
+ for (const listener of autonomyDeniedListeners) {
651
+ try {
652
+ listener(call, decision, level);
653
+ } catch {
654
+ // Same contract as permission listeners: never abort admission.
655
+ }
656
+ }
657
+ };
658
+
659
+ const cancelParkedCallById = (requestId: string, reason: string): boolean => {
660
+ const index = parked.findIndex((entry) => entry.meta.requestId === requestId);
661
+ if (index === -1) return false;
662
+ const [entry] = parked.splice(index, 1);
663
+ if (!entry) return false;
664
+ resolveParkedAsBlocked(entry, reason);
665
+ const next = parked[0];
666
+ if (next) notifyPermissionRequired(next.call, next.decision, next.meta);
667
+ return true;
668
+ };
669
+
670
+ return {
671
+ register(spec) {
672
+ tools.set(spec.name, spec);
673
+ },
674
+ listAll: () => Array.from(tools.values()),
675
+ get: (name) => tools.get(name),
676
+ listRegistered: () => Array.from(tools.keys()),
677
+ listVisible: () => Array.from(tools.values()),
678
+ async invoke(call, options) {
679
+ const admissionCall = prepareAdmissionCall(tools.get(call.tool as ToolName), call);
680
+ const outcome = admit(admissionCall, undefined, options);
681
+ if (outcome.kind === "terminal") {
682
+ disposeAdmissionArgs(tools.get(admissionCall.tool as ToolName), admissionCall.args ?? {});
683
+ return observeBlockedAttempt(admissionCall, outcome.verdict, options) ?? outcome.verdict;
684
+ }
685
+ if (outcome.kind === "execute") return runSpec(outcome.spec, admissionCall, outcome.decision, options);
686
+ const abortReason = "run aborted before the operator decided";
687
+ if (options?.signal?.aborted) {
688
+ disposeAdmissionArgs(tools.get(admissionCall.tool as ToolName), admissionCall.args ?? {});
689
+ const loopReason = observeRejectedAttempt(admissionCall, outcome.decision, options);
690
+ return { kind: "blocked", reason: loopReason ?? abortReason, decision: outcome.decision };
691
+ }
692
+ return new Promise<RegistryVerdict>((resolve) => {
693
+ const meta: PermissionRequiredMeta = {
694
+ requestId: nextApprovalRequestId(),
695
+ axis: outcome.axis,
696
+ ...(options?.toolCallId !== undefined && options.toolCallId.length > 0 ? { toolCallId: options.toolCallId } : {}),
697
+ };
698
+ recordRegistryDisposition(admissionCall, outcome.decision, "permission_requested", { requestId: meta.requestId });
699
+ // The park ends when the operator decides, not when the verdict
700
+ // resolves: an approved call runs its body inside the resume pass,
701
+ // so measuring to the resolve would fold the tool's own execution
702
+ // into the park and the caller would subtract it. A dispatch that
703
+ // fanned out for 224.8s settled as a 15ms line that way (issue #82).
704
+ // closePark seals it at the decision; settle covers every path that
705
+ // ends a park without reaching a body, including one added later,
706
+ // because they all resolve the entry through this function.
707
+ const parkedAtClock = performance.now();
708
+ let parkReported = false;
709
+ const closePark = (): void => {
710
+ if (parkReported) return;
711
+ parkReported = true;
712
+ options?.onParked?.(Math.round(performance.now() - parkedAtClock));
713
+ };
714
+ const settle = (verdict: RegistryVerdict): void => {
715
+ closePark();
716
+ resolve(verdict);
717
+ };
718
+ const parkedCall: ParkedCall = {
719
+ call: admissionCall,
720
+ decision: outcome.decision,
721
+ meta,
722
+ resolve: settle,
723
+ closePark,
724
+ };
725
+ if (options !== undefined) parkedCall.options = options;
726
+ if (options?.signal) {
727
+ const onAbort = (): void => {
728
+ cancelParkedCallById(meta.requestId, abortReason);
729
+ };
730
+ options.signal.addEventListener("abort", onAbort, { once: true });
731
+ parkedCall.abortCleanup = () => {
732
+ options.signal?.removeEventListener("abort", onAbort);
733
+ };
734
+ }
735
+ parked.push(parkedCall);
736
+ notifyPermissionRequired(admissionCall, outcome.decision, meta);
737
+ });
738
+ },
739
+ hasParkedCalls: () => parked.length > 0,
740
+ parkedCount: () => parked.length,
741
+ renotifyHead() {
742
+ const next = parked[0];
743
+ if (next) notifyPermissionRequired(next.call, next.decision, next.meta);
744
+ },
745
+ async resumeParkedCalls(grant?: OneShotGrant) {
746
+ if (parked.length === 0) return;
747
+ const pending: Array<{ entry: ParkedCall; reparkIndex?: number }> = [];
748
+ if (grant === undefined) {
749
+ pending.push(...parked.splice(0, parked.length).map((entry) => ({ entry })));
750
+ } else if (grant.requestId !== undefined) {
751
+ const index = parked.findIndex((entry) => entry.meta.requestId === grant.requestId);
752
+ if (index === -1) {
753
+ const next = parked[0];
754
+ if (next) notifyPermissionRequired(next.call, next.decision, next.meta);
755
+ return;
756
+ }
757
+ const [entry] = parked.splice(index, 1);
758
+ if (entry) pending.push({ entry, reparkIndex: index });
759
+ } else {
760
+ const [entry] = parked.splice(0, 1);
761
+ if (entry) pending.push({ entry });
762
+ }
763
+ for (const { entry, reparkIndex } of pending) {
764
+ // A one-shot grant covers only the parked call it selected. Calls
765
+ // that parked while the overlay was already open remain queued and
766
+ // need their own confirmation, so a concurrent privileged call
767
+ // cannot ride along on a grant approved for another call.
768
+ if (grant !== undefined && entry.decision.classification.actionClass !== grant.actionClass) {
769
+ reparkEntry(entry, reparkIndex);
770
+ continue;
771
+ }
772
+ const outcome = admit(entry.call, grant, entry.options);
773
+ if (outcome.kind === "park") {
774
+ reparkEntry(entry, reparkIndex);
775
+ continue;
776
+ }
777
+ cleanupParkedEntry(entry);
778
+ if (outcome.kind === "terminal") {
779
+ disposeAdmissionArgs(tools.get(entry.call.tool as ToolName), entry.call.args ?? {});
780
+ entry.resolve(observeBlockedAttempt(entry.call, outcome.verdict, entry.options) ?? outcome.verdict);
781
+ continue;
782
+ }
783
+ const approvedOptions: ToolInvokeOptions | undefined =
784
+ grant === undefined
785
+ ? entry.options
786
+ : {
787
+ ...(entry.options ?? {}),
788
+ approval: {
789
+ requestId: entry.meta.requestId,
790
+ requestedBy: grant.requestedBy,
791
+ actionClass: grant.actionClass,
792
+ },
793
+ };
794
+ // Seal the park before the body runs. Everything after this line is
795
+ // the tool working, and the caller charges that to the tool.
796
+ entry.closePark();
797
+ entry.resolve(await runSpec(outcome.spec, entry.call, outcome.decision, approvedOptions));
798
+ }
799
+ const next = parked[0];
800
+ if (next) notifyPermissionRequired(next.call, next.decision, next.meta);
801
+ },
802
+ cancelParkedCall(requestId, reason) {
803
+ return cancelParkedCallById(requestId, reason);
804
+ },
805
+ cancelParkedCalls(reason) {
806
+ if (parked.length === 0) return;
807
+ const pending = parked.splice(0, parked.length);
808
+ for (const entry of pending) {
809
+ resolveParkedAsBlocked(entry, reason);
810
+ }
811
+ },
812
+ onPermissionRequired(listener) {
813
+ permissionListeners.add(listener);
814
+ return () => {
815
+ permissionListeners.delete(listener);
816
+ };
817
+ },
818
+ onAutonomyDenied(listener) {
819
+ autonomyDeniedListeners.add(listener);
820
+ return () => {
821
+ autonomyDeniedListeners.delete(listener);
822
+ };
823
+ },
824
+ };
825
+ }
826
+
827
+ /**
828
+ * Run a tool's optional `prepareArguments` normalizer before its body. A
829
+ * throwing or non-object result is discarded so a buggy normalizer can never
830
+ * abort admission; the raw args pass through unchanged.
831
+ */
832
+ function prepareToolArgs(spec: ToolSpec, args: Record<string, unknown>): Record<string, unknown> {
833
+ if (!spec.prepareArguments) return args;
834
+ try {
835
+ const prepared = spec.prepareArguments(args);
836
+ return prepared !== null && typeof prepared === "object" && !Array.isArray(prepared) ? prepared : args;
837
+ } catch {
838
+ return args;
839
+ }
840
+ }
841
+
842
+ function disposeAdmissionArgs(spec: ToolSpec | undefined, args: Record<string, unknown>): void {
843
+ try {
844
+ spec?.disposeAdmissionArguments?.(args);
845
+ } catch {
846
+ // Cleanup is best-effort here; durable reservation expiry is the backstop.
847
+ }
848
+ }
849
+
850
+ function prepareAdmissionCall(spec: ToolSpec | undefined, call: ClassifierCall): ClassifierCall {
851
+ if (!spec?.prepareAdmissionArguments) return call;
852
+ try {
853
+ const prepared = spec.prepareAdmissionArguments(call.args ?? {});
854
+ if (prepared === null || typeof prepared !== "object" || Array.isArray(prepared)) return call;
855
+ return { ...call, args: prepared };
856
+ } catch {
857
+ return call;
858
+ }
859
+ }
860
+
861
+ function applyRegisteredToolClassification(decision: SafetyDecision, spec: ToolSpec): SafetyDecision {
862
+ if (decision.classification.actionClass !== "unknown") return decision;
863
+ const classification = {
864
+ actionClass: spec.baseActionClass,
865
+ reasons: [`registered tool: ${spec.name}`],
866
+ };
867
+ return decision.kind === "allow" ? { kind: "allow", classification } : { ...decision, classification };
868
+ }
869
+
870
+ /** Final reason code for a before_tool guard block, matching the audit convention (sd-01 §2.5). */
871
+ const GUARD_BLOCK_REASON_CODE = "guard_block";
872
+
873
+ /**
874
+ * Terminal blocked verdict for a before_tool guard block (loop guard,
875
+ * protected artifacts, dispatch dedup, declarative block rules) on a call
876
+ * whose admission already passed. The decision is re-shaped as a block, and
877
+ * the carried policy's net-pass reasonCode is replaced with the guard axis,
878
+ * so downstream consumers (worker finish events, dispatch receipts, audit)
879
+ * count a blocked safety decision instead of repeating the admission's allow.
880
+ */
881
+ function guardBlockedVerdict(
882
+ decision: SafetyDecision,
883
+ tool: string,
884
+ reason: string,
885
+ ): Extract<RegistryVerdict, { kind: "blocked" }> {
886
+ const blocked: SafetyDecision = {
887
+ kind: "block",
888
+ classification: decision.classification,
889
+ rejection: {
890
+ short: `${tool} blocked: tool guard`,
891
+ detail: reason,
892
+ hints: [],
893
+ },
894
+ ...(decision.policy !== undefined ? { policy: { ...decision.policy, reasonCode: GUARD_BLOCK_REASON_CODE } } : {}),
895
+ };
896
+ return { kind: "blocked", reason, decision: blocked };
897
+ }
898
+
899
+ /**
900
+ * Longest guidance sentence carried into a headless denial. The model-facing
901
+ * composer caps a line at 300 characters and interpolates policy text verbatim,
902
+ * so bound it here too and leave room for the elision marker.
903
+ */
904
+ const HEADLESS_GUIDANCE_MAX_CHARS = 240;
905
+
906
+ /**
907
+ * The recovery guidance a headless denial should repeat, or null when the
908
+ * parked decision has none.
909
+ *
910
+ * A headless run answers every ask with one sentence: nobody can confirm, rerun
911
+ * interactively. That says nothing about what would have run, so a headless
912
+ * model that hits an unrecognized-bash rail retries the same shape until the run
913
+ * ends. An unrecognized execution decision carries the working form in its
914
+ * reasons, and those reasons reach the audit record and the interactive approval
915
+ * overlay but not the model. The first reason states the axis the denial already
916
+ * implies, so only the reasons after it are candidates, and only the first of
917
+ * those, bounded: this is a nudge toward the recognized spelling, not a dump of
918
+ * the policy's reason list.
919
+ */
920
+ function headlessDenialGuidance(decision: SafetyDecision, reason: string): string | null {
921
+ // Interactive denials are left exactly as they were: the operator saw the
922
+ // reasons in the approval overlay before answering.
923
+ if (!reason.startsWith(HEADLESS_PERMISSION_DENIED_MARKER)) return null;
924
+ const policy = decision.policy;
925
+ if (policy?.execRecognition !== "unrecognized") return null;
926
+ const guidance = policy.reasons.slice(1).find((entry) => entry.trim().length > 0);
927
+ if (guidance === undefined) return null;
928
+ const trimmed = guidance.trim();
929
+ return trimmed.length > HEADLESS_GUIDANCE_MAX_CHARS ? `${trimmed.slice(0, HEADLESS_GUIDANCE_MAX_CHARS)}…` : trimmed;
930
+ }
931
+
932
+ /**
933
+ * Terminal blocked verdict for a parked call that has been answered: denied at
934
+ * the confirmation prompt, cancelled with the turn, or settled by an abort.
935
+ *
936
+ * The ask decision's rejection explains that the call *is* parked and what
937
+ * approving it would do. Once the answer is in, that text is stale, and
938
+ * `formatModelRejection` printed it as a second paragraph restating a denial the
939
+ * reason had already stated, followed by four approval hints for an approval
940
+ * that is not coming. The answer is the whole message; the composer still closes
941
+ * it with the standing pivot instruction.
942
+ *
943
+ * A headless answer is the exception: nobody will ever approve it, so the detail
944
+ * carries one line of recovery guidance after the denial sentence. The sentence
945
+ * stays the detail's prefix, which the skill-eval permission-wall recognizer
946
+ * depends on.
947
+ */
948
+ function parkAnsweredBlockedVerdict(
949
+ decision: SafetyDecision,
950
+ tool: string,
951
+ reason: string,
952
+ ): Extract<RegistryVerdict, { kind: "blocked" }> {
953
+ const guidance = headlessDenialGuidance(decision, reason);
954
+ const blocked: SafetyDecision = {
955
+ kind: "block",
956
+ classification: decision.classification,
957
+ rejection: {
958
+ short: `${tool} blocked: ${decision.classification.actionClass} was not approved`,
959
+ detail: guidance === null ? reason : `${reason}\n${guidance}`,
960
+ hints: [],
961
+ },
962
+ ...(decision.policy !== undefined ? { policy: decision.policy } : {}),
963
+ };
964
+ return { kind: "blocked", reason, decision: blocked };
965
+ }
966
+
967
+ /**
968
+ * Terminal blocked verdict for a call outside the merged tool surface the
969
+ * loaded skills declared. The reason carries the remediation, since blocked
970
+ * reasons are what the model reads; context and ask_user stay exempt at
971
+ * the evaluator so the message can honestly point at ask_user.
972
+ */
973
+ function skillSurfaceBlockedVerdict(
974
+ decision: SafetyDecision,
975
+ tool: string,
976
+ violation: SkillToolSurfaceViolation,
977
+ ): Extract<RegistryVerdict, { kind: "blocked" }> {
978
+ const reason =
979
+ violation.disallowedBy.length > 0
980
+ ? `${tool} is disallowed by the active skill(s) ${violation.disallowedBy.join(", ")} (disallowed-tools). The narrowing ends when the skill policy's turn or worker run ends. Work within the skill workflow; if it genuinely needs this step, use ask_user when available or state the blocker in your reply.`
981
+ : `${tool} is outside the tool surface declared by the active skill(s) ${violation.skills.join(", ")}. Tools are narrowed to: ${(violation.mergedAllowedTools ?? []).join(", ")} (plus context and ask_user). The narrowing ends when the skill policy's turn or worker run ends. Work within the skill workflow; if it genuinely needs this step, use ask_user when available or state the blocker in your reply.`;
982
+ const blocked: SafetyDecision = {
983
+ kind: "block",
984
+ classification: decision.classification,
985
+ rejection: {
986
+ short: `${tool} blocked: outside active skill tool surface`,
987
+ detail: reason,
988
+ hints: ["Skill narrowing never grants tools; it only blocks calls outside the declared workflow surface."],
989
+ },
990
+ ...(decision.policy !== undefined ? { policy: decision.policy } : {}),
991
+ };
992
+ return { kind: "blocked", reason, decision: blocked };
993
+ }
994
+
995
+ /**
996
+ * Terminal blocked verdict for an autonomy `deny` disposition (read-only).
997
+ * The decision is re-shaped as a block so downstream consumers (worker
998
+ * events, dispatch receipts) report it as a denial, not a pending ask.
999
+ */
1000
+ function autonomyDenyVerdict(
1001
+ decision: SafetyDecision,
1002
+ level: AutonomyLevel,
1003
+ tool: string,
1004
+ actionClass: ActionClass,
1005
+ ): Extract<RegistryVerdict, { kind: "blocked" }> {
1006
+ const rejection = autonomyDenyRejection(level, tool, actionClass);
1007
+ const blocked: SafetyDecision = {
1008
+ kind: "block",
1009
+ classification: decision.classification,
1010
+ rejection,
1011
+ ...(decision.policy !== undefined ? { policy: decision.policy } : {}),
1012
+ };
1013
+ return { kind: "blocked", reason: rejection.short, decision: blocked };
1014
+ }
1015
+
1016
+ /**
1017
+ * Park-shaped decision for an autonomy `ask` disposition. The engine passed
1018
+ * the call, so the rejection names the level as the asking axis; overlays and
1019
+ * non-interactive deniers read it from here. An outward-exposure park names
1020
+ * the tier instead of the action class, because the class is not why it asked.
1021
+ */
1022
+ function toAutonomyAskDecision(
1023
+ decision: SafetyDecision,
1024
+ level: AutonomyLevel,
1025
+ tool: string,
1026
+ actionClass: ActionClass,
1027
+ exposure: AutonomyExposure = DEFAULT_AUTONOMY_EXPOSURE,
1028
+ ): SafetyDecision {
1029
+ return {
1030
+ kind: "ask",
1031
+ classification: decision.classification,
1032
+ rejection: autonomyAskRejection(level, tool, actionClass, exposure),
1033
+ ...(decision.policy !== undefined ? { policy: decision.policy } : {}),
1034
+ };
1035
+ }
1036
+
1037
+ /**
1038
+ * Plan-approval ask for a plan-scale dispatch call. The rejection detail IS
1039
+ * the plan artifact (topology, per-task agent/model/node), so the approval
1040
+ * overlay shows the operator exactly what one approval will launch.
1041
+ */
1042
+ function toDispatchPlanAskDecision(
1043
+ decision: SafetyDecision,
1044
+ level: AutonomyLevel,
1045
+ plan: DispatchPlanView,
1046
+ ): SafetyDecision {
1047
+ return {
1048
+ kind: "ask",
1049
+ classification: decision.classification,
1050
+ rejection: {
1051
+ short: `dispatch plan needs approval (${plan.topology}, ${plan.taskCount} task(s)) at autonomy ${level}`,
1052
+ detail: `Approving this call approves the whole plan:\n${plan.text}`,
1053
+ hints: [
1054
+ "One approval covers every run in the plan, including remote placements.",
1055
+ "Deny to keep the fleet idle and revise the plan first.",
1056
+ ],
1057
+ },
1058
+ ...(decision.policy !== undefined ? { policy: decision.policy } : {}),
1059
+ };
1060
+ }
1061
+
1062
+ function buildToolHookInput(
1063
+ hook: "before_tool" | "after_tool",
1064
+ spec: ToolSpec,
1065
+ call: ClassifierCall,
1066
+ decision: SafetyDecision,
1067
+ posture: string,
1068
+ options: ToolInvokeOptions | undefined,
1069
+ result: ToolResult | undefined,
1070
+ ): MiddlewareHookInput {
1071
+ const metadata: Record<string, MiddlewareMetadataValue> = {
1072
+ posture,
1073
+ actionClass: decision.classification.actionClass,
1074
+ decisionKind: decision.kind,
1075
+ };
1076
+ // Stable call identity for repetition detectors (engine/loop-guard.ts).
1077
+ // Computed for before_tool only; after_tool consumers identify the call
1078
+ // via toolCallId.
1079
+ if (hook === "before_tool") metadata.callFingerprint = hashToolCall(spec.name, call.args ?? {});
1080
+ const validationCommand = detectedValidationCommand(call);
1081
+ if (validationCommand !== null) {
1082
+ metadata.validationCommand = validationCommand;
1083
+ if (result?.kind === "ok") metadata.validationExitCode = 0;
1084
+ }
1085
+ if (call.tool !== spec.name) metadata.requestedToolName = call.tool;
1086
+ if (result !== undefined) {
1087
+ metadata.resultKind = result.kind;
1088
+ if (result.kind === "error") metadata.errorMessage = result.message;
1089
+ if (result.kind === "ok" && result.terminate === true) metadata.terminate = true;
1090
+ // Result identity for the stagnation detector (engine/loop-guard.ts):
1091
+ // consecutive same-shape calls whose outputs hash identically are not
1092
+ // producing new information, whatever their size arguments say.
1093
+ if (result.kind === "ok" && typeof result.output === "string") {
1094
+ metadata.resultFingerprint = createHash("sha256").update(result.output).digest("hex");
1095
+ metadata.resultBytes = Buffer.byteLength(result.output, "utf8");
1096
+ }
1097
+ }
1098
+
1099
+ const input: MiddlewareHookInput = {
1100
+ hook,
1101
+ toolName: spec.name,
1102
+ metadata,
1103
+ };
1104
+ if (call.args !== undefined) input.toolArgs = call.args;
1105
+ if (result?.details !== undefined) input.toolResultDetails = result.details;
1106
+ if (options?.runId !== undefined) input.runId = options.runId;
1107
+ if (options?.sessionId !== undefined) input.sessionId = options.sessionId;
1108
+ if (options?.turnId !== undefined) input.turnId = options.turnId;
1109
+ if (options?.toolCallId !== undefined) input.toolCallId = options.toolCallId;
1110
+ if (options?.correlationId !== undefined) input.correlationId = options.correlationId;
1111
+ return input;
1112
+ }
1113
+
1114
+ function commandArg(args: Record<string, unknown> | undefined): string | null {
1115
+ if (!args) return null;
1116
+ return typeof args.command === "string" && args.command.length > 0 ? args.command : null;
1117
+ }
1118
+
1119
+ function detectedValidationCommand(call: ClassifierCall): string | null {
1120
+ if (call.tool !== ToolNames.Bash) return null;
1121
+ const command = commandArg(call.args);
1122
+ if (command === null) return null;
1123
+ const detected = detectValidationCommand(command);
1124
+ return detected.kind === "validation" ? detected.matched : null;
1125
+ }
1126
+
1127
+ function firstBlockToolEffect(
1128
+ effects: ReadonlyArray<MiddlewareEffect>,
1129
+ ): Extract<MiddlewareEffect, { kind: "block_tool" }> | null {
1130
+ for (const effect of effects) {
1131
+ if (effect.kind === "block_tool") return effect;
1132
+ }
1133
+ return null;
1134
+ }
1135
+
1136
+ function applyToolResultEffects(result: ToolResult, effects: ReadonlyArray<MiddlewareEffect>): ToolResult {
1137
+ const annotations = annotationMessages(effects);
1138
+ if (annotations.length === 0) return result;
1139
+ const suffix = `\n\n${annotations.join("\n")}`;
1140
+ if (result.kind === "ok") {
1141
+ const annotated: ToolResult = { kind: "ok", output: `${result.output}${suffix}` };
1142
+ if (result.details !== undefined) annotated.details = result.details;
1143
+ if (result.terminate === true) annotated.terminate = true;
1144
+ return annotated;
1145
+ }
1146
+ return { kind: "error", message: `${result.message}${suffix}` };
1147
+ }
1148
+
1149
+ function annotationMessages(effects: ReadonlyArray<MiddlewareEffect>): string[] {
1150
+ const messages: string[] = [];
1151
+ for (const effect of effects) {
1152
+ if (effect.kind !== "annotate_tool_result") continue;
1153
+ const severity = effect.severity ?? "info";
1154
+ messages.push(`[middleware:${severity}] ${effect.message}`);
1155
+ }
1156
+ return messages;
1157
+ }