@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,994 @@
1
+ import { createHash } from "node:crypto";
2
+ import path from "node:path";
3
+ import { artifactDefaultPath } from "../../core/artifact-paths.js";
4
+ import { canonicalizeExistingPath } from "../../core/path-canonical.js";
5
+ import { ToolNames } from "../../core/tool-names.js";
6
+ import { clioConfigDir } from "../../core/xdg.js";
7
+ import { type ActionClass, type Classification, type ClassifierCall, classify } from "./action-classifier.js";
8
+ import type { DamageControlMatch, DamageControlRule } from "./damage-control.js";
9
+ import { DEFAULT_DAMAGE_CONTROL_PATH_POLICY, mergePathPolicyInputs } from "./default-path-policy.js";
10
+ import {
11
+ type CompiledPathPolicy,
12
+ compilePathPolicy,
13
+ evaluatePathPolicy,
14
+ type PathPolicyDecision,
15
+ type PathPolicyOperation,
16
+ } from "./path-policy.js";
17
+ import {
18
+ type LoadedProjectSafetyPolicy,
19
+ loadProjectSafetyPolicy,
20
+ type ProjectCommandPolicy,
21
+ } from "./project-policy.js";
22
+ import {
23
+ extractCommandDeleteTargets,
24
+ extractCommandWriteTargets,
25
+ inlineShellScript,
26
+ tokenizeShellLike,
27
+ } from "./protected-artifacts.js";
28
+ import { formatRejection, type RejectionMessage } from "./rejection-feedback.js";
29
+ import { getCachedDefaultRulePacks, type PackId, type RulePacks } from "./rule-pack-loader.js";
30
+
31
+ export type SafetyPolicySource =
32
+ | "damage-control:base"
33
+ | "project-policy"
34
+ | "project-policy-invalid"
35
+ | "builtin-command-allowlist"
36
+ | "builtin-classifier"
37
+ | "none";
38
+
39
+ export interface SafetyPolicyDecision {
40
+ /**
41
+ * Net verdict (sd-01 §2.2): `block` is final at every autonomy level,
42
+ * `ask` is a net rail demanding operator confirmation at every level, and
43
+ * `allow` means the net passed; the autonomy mapping decides what happens
44
+ * next at the admission seam (tools/registry.ts, acp/tool-mediator.ts).
45
+ */
46
+ kind: "allow" | "ask" | "block";
47
+ classification: Classification;
48
+ tool: string;
49
+ actionClass: ActionClass;
50
+ reasons: ReadonlyArray<string>;
51
+ ruleId?: string;
52
+ reasonCode: string;
53
+ command?: string;
54
+ cwd: string;
55
+ posture?: string;
56
+ policySource: SafetyPolicySource;
57
+ policyHash?: string;
58
+ projectPolicyPath?: string;
59
+ match?: DamageControlMatch;
60
+ rejection?: RejectionMessage;
61
+ /**
62
+ * Execute-class passes only: whether the command is in the no-prompt set
63
+ * (built-in allowlist, project policy command, typed execution tool). The
64
+ * autonomy mapping asks for unrecognized execution below full-auto.
65
+ */
66
+ execRecognition?: "recognized" | "unrecognized";
67
+ }
68
+
69
+ export interface SafetyPolicyMetadata {
70
+ version: 1;
71
+ rulePackHash: string;
72
+ rulePackVersion: number;
73
+ activeRuleIds: ReadonlyArray<string>;
74
+ projectPolicyPath: string | null;
75
+ projectPolicyHash: string | null;
76
+ projectPolicyValid: boolean;
77
+ projectPolicyErrors: ReadonlyArray<string>;
78
+ cwd: string;
79
+ }
80
+
81
+ export interface SafetyPolicyEngine {
82
+ evaluate(call: ClassifierCall, posture?: string): SafetyPolicyDecision;
83
+ metadata(posture?: string): SafetyPolicyMetadata;
84
+ }
85
+
86
+ export interface SafetyPolicyEngineOptions {
87
+ cwd?: string;
88
+ rulePacks?: RulePacks;
89
+ projectPolicy?: LoadedProjectSafetyPolicy;
90
+ /**
91
+ * Absolute directories a write-class tool call is confined to for this run.
92
+ * When present and non-empty, a write/edit target outside every root is a
93
+ * final BLOCK (reason code "write-root"). Empty or absent disables the check.
94
+ * Enforced at the worker safety seam so both the native worker registry and
95
+ * the Claude SDK hook path inherit it.
96
+ */
97
+ writeRoots?: ReadonlyArray<string>;
98
+ }
99
+
100
+ interface SourcedRule {
101
+ rule: DamageControlRule;
102
+ source: SafetyPolicySource;
103
+ }
104
+
105
+ const BUILTIN_ALLOWLIST: ReadonlyArray<{ id: string; re: RegExp }> = [
106
+ { id: "builtin:pwd", re: /^pwd$/ },
107
+ { id: "builtin:ls", re: /^ls(?:\s+(-[A-Za-z0-9]+|\.[/\w.-]*|[/\w.-]+))*$/ },
108
+ { id: "builtin:git-status", re: /^git\s+status(?:\s+--short|\s+--branch|\s+-sb)*$/ },
109
+ { id: "builtin:git-diff", re: /^git\s+diff(?:\s+--cached|\s+--stat|\s+--name-only|\s+--\s+[\w./-]+)*$/ },
110
+ { id: "builtin:git-log", re: /^git\s+log\s+--oneline(?:\s+-n\s+[1-9]\d{0,2})?(?:\s+--\s+[\w./-]+)?$/ },
111
+ { id: "builtin:npm-test", re: /^npm\s+(?:test|run\s+test)(?:\s+--\s+[\w=./:-]+(?:\s+[\w=./:-]+)*)?$/ },
112
+ { id: "builtin:npm-lint", re: /^npm\s+run\s+lint(?:\s+--\s+[\w=./:-]+(?:\s+[\w=./:-]+)*)?$/ },
113
+ { id: "builtin:npm-build", re: /^npm\s+run\s+build(?:\s+--\s+[\w=./:-]+(?:\s+[\w=./:-]+)*)?$/ },
114
+ { id: "builtin:npm-typecheck", re: /^npm\s+run\s+typecheck(?:\s+--\s+[\w=./:-]+(?:\s+[\w=./:-]+)*)?$/ },
115
+ { id: "builtin:npm-ci-script", re: /^npm\s+run\s+ci(?:\s+--\s+[\w=./:-]+(?:\s+[\w=./:-]+)*)?$/ },
116
+ { id: "builtin:pytest", re: /^pytest(?:\s+[\w=./:-]+)*$/ },
117
+ { id: "builtin:python-pytest", re: /^python(?:3(?:\.\d+)?)?\s+-m\s+pytest(?:\s+[\w=./:-]+)*$/ },
118
+ { id: "builtin:cargo-test", re: /^cargo\s+test(?:\s+[\w=./:-]+)*$/ },
119
+ { id: "builtin:go-test", re: /^go\s+test(?:\s+[\w=./:-]+)*$/ },
120
+ { id: "builtin:make-test", re: /^make\s+test(?:\s+[\w=./:-]+)*$/ },
121
+ ];
122
+
123
+ /**
124
+ * What the recognized spelling looks like, carried on every unrecognized-bash
125
+ * decision. It rides the decision's reasons so the approval overlay, the audit
126
+ * record, and any surface that renders them all say the same thing: an agent
127
+ * that hits this rail should learn the form that passes instead of retrying the
128
+ * same shape. A headless run still answers the resulting ask with its own
129
+ * denial sentence, which does not carry these reasons.
130
+ */
131
+ const BASH_RECOGNIZED_FORM_HINT =
132
+ "Recognized forms run without asking: one command per bash call, or a && chain whose every step is recognized, with cwd passed as the cwd argument instead of a leading cd.";
133
+
134
+ const EXECUTION_TOOLS = new Set<string>([ToolNames.Bash, ToolNames.Verify]);
135
+
136
+ /**
137
+ * Tools write-root confinement refuses by name, whatever their arguments: they
138
+ * run a project script, a shell, or an unconfined child, so no lexical check
139
+ * can bound where they write. Exported because a run that declares write roots
140
+ * must not be *offered* them: a tool that is guaranteed to be refused is a
141
+ * budget the model spends learning it cannot use it, and the refusal reads to
142
+ * the model as a mistake it should retry. The class-based check below still
143
+ * catches anything that classifies as execute or dispatch at call time.
144
+ */
145
+ export const WRITE_ROOT_REFUSED_TOOLS: ReadonlySet<string> = new Set<string>([
146
+ ToolNames.Bash,
147
+ ToolNames.Verify,
148
+ ToolNames.Dispatch,
149
+ ]);
150
+
151
+ // Write-class tools (action class "write"). bash is execute class and runs its
152
+ // own loop, so it is not covered by the lexical write-root containment below.
153
+ const WRITE_ROOT_TOOLS = new Set<string>([ToolNames.Write, ToolNames.Edit, ToolNames.Artifact]);
154
+
155
+ function writeRootTargetPath(call: ClassifierCall): string | null {
156
+ if (!WRITE_ROOT_TOOLS.has(call.tool)) return null;
157
+ const target = pathArg(call.args);
158
+ if (target !== null) return target;
159
+ if (call.tool === ToolNames.Artifact) return artifactDefaultPath(call.args?.kind);
160
+ return null;
161
+ }
162
+
163
+ /**
164
+ * Under active write-root confinement, a tool that can write outside the roots
165
+ * without a path argument the lexical check can inspect. Execute-class tools run
166
+ * project scripts or a shell; dispatch spawns a worker not bound to these roots.
167
+ */
168
+ function isWriteConfinementEscape(call: ClassifierCall, actionClass: string): boolean {
169
+ if (WRITE_ROOT_REFUSED_TOOLS.has(call.tool)) return true;
170
+ return actionClass === "execute" || actionClass === "dispatch";
171
+ }
172
+
173
+ /**
174
+ * Lexical write-root containment. The target is resolved against the worker cwd
175
+ * and must equal a root or sit beneath it (`root` + path separator). The check
176
+ * is lexical and does not chase symlinks, so a symlink inside a root that points
177
+ * outside is not detected here. Returns the block reason, or null when allowed.
178
+ */
179
+ function evaluateWriteRoots(roots: ReadonlyArray<string>, writeRootCwd: string, call: ClassifierCall): string | null {
180
+ if (roots.length === 0) return null;
181
+ const target = writeRootTargetPath(call);
182
+ if (target === null) return null;
183
+ const resolved = path.resolve(writeRootCwd, target);
184
+ for (const root of roots) {
185
+ if (resolved === root || resolved.startsWith(`${root}${path.sep}`)) return null;
186
+ }
187
+ return `write target '${target}' resolves to '${resolved}', which is outside the permitted write roots for this run: ${roots.join(", ")}`;
188
+ }
189
+
190
+ export function createSafetyPolicyEngine(options: SafetyPolicyEngineOptions = {}): SafetyPolicyEngine {
191
+ const cwd = canonicalizeExistingPath(path.resolve(options.cwd ?? process.cwd()));
192
+ // Write-root containment resolves lexically, so it keeps its own un-canonicalized
193
+ // cwd and roots to compare like against like (the design mandates no symlink chasing).
194
+ const writeRootCwd = path.resolve(options.cwd ?? process.cwd());
195
+ const writeRoots = (options.writeRoots ?? []).map((root) => path.resolve(root));
196
+ const packs = options.rulePacks ?? getCachedDefaultRulePacks();
197
+ const projectPolicy = options.projectPolicy ?? loadProjectSafetyPolicy(cwd);
198
+ const projectPolicyRoot =
199
+ projectPolicy.path === null ? cwd : path.dirname(path.dirname(canonicalizeExistingPath(projectPolicy.path)));
200
+ const pathPolicyInput = projectPolicy.disableDefaultPathPolicy
201
+ ? projectPolicy.pathPolicy
202
+ : mergePathPolicyInputs(DEFAULT_DAMAGE_CONTROL_PATH_POLICY, projectPolicy.pathPolicy);
203
+ // Clio's own secret store, by absolute path. The static default list carries
204
+ // the `credentials.yaml` literal; the expansion has to happen here because
205
+ // the list cannot call config helpers at module scope.
206
+ const expandedDefaults = projectPolicy.disableDefaultPathPolicy
207
+ ? pathPolicyInput
208
+ : mergePathPolicyInputs(pathPolicyInput, { zeroAccessPaths: clioCredentialStorePaths() });
209
+ const pathPolicy = compilePathPolicy(expandedDefaults, projectPolicyRoot);
210
+ // Bash-read scanning tests argument tokens against zero-access entries only:
211
+ // read-only paths stay readable from bash by design, secrets do not.
212
+ const zeroAccessPolicy: CompiledPathPolicy = {
213
+ root: pathPolicy.root,
214
+ entries: pathPolicy.entries.filter((entry) => entry.kind === "zeroAccessPaths"),
215
+ diagnostics: [],
216
+ };
217
+
218
+ // Base pack rules apply at every posture, so they are sourced once at
219
+ // construction. Building the array inside evaluate() re-allocated it on
220
+ // every admission for no behavioral gain.
221
+ const sourcedRules: SourcedRule[] = packs.base.rules.map((rule) => ({ rule, source: "damage-control:base" }));
222
+
223
+ return {
224
+ evaluate(call, posture) {
225
+ const rawClassification = classify(call);
226
+ const command = commandArg(call.args);
227
+ const callCwd = cwdArg(call.args, cwd);
228
+ const scan = damageControlScan(call);
229
+ const hit = scan ? matchSourcedRule(scan, sourcedRules) : null;
230
+ const classification = effectiveClassification(rawClassification, hit?.match);
231
+
232
+ const base = baseDecision(call, classification, callCwd, posture, command);
233
+
234
+ // Worker write-root containment (Slice C). A write-class tool whose
235
+ // target escapes every permitted root is a final block, ranked ahead of
236
+ // the git/system-modify/path-policy rails so an out-of-root write reports
237
+ // "write-root" even when the classifier escalated it to system_modify
238
+ // (e.g. a write outside cwd). No-op unless the run carries writeRoots.
239
+ const writeRootReason = evaluateWriteRoots(writeRoots, writeRootCwd, call);
240
+ if (writeRootReason !== null) {
241
+ return blockDecision(base, {
242
+ ruleId: "write-root",
243
+ reasonCode: "write-root",
244
+ reasons: [writeRootReason],
245
+ policySource: "builtin-classifier",
246
+ });
247
+ }
248
+
249
+ // Write-confinement is only honest if the run cannot escape the roots by
250
+ // running an arbitrary command or spawning an unconfined child. Under
251
+ // active writeRoots, execute-class tools (bash, verify, which run project
252
+ // scripts) and dispatch (which spawns a worker not bound to these roots)
253
+ // are blocked outright: they can mutate the filesystem outside the roots.
254
+ if (writeRoots.length > 0 && isWriteConfinementEscape(call, classification.actionClass)) {
255
+ return blockDecision(base, {
256
+ ruleId: "write-root",
257
+ reasonCode: "write-root",
258
+ reasons: [
259
+ `tool '${call.tool}' (${classification.actionClass}) can mutate the filesystem outside the permitted write roots and is blocked under write-root confinement`,
260
+ ],
261
+ policySource: "builtin-classifier",
262
+ });
263
+ }
264
+
265
+ // An explicit `ask: true` damage-control rule is an authored
266
+ // confirm rail and takes precedence over classifier escalation for
267
+ // the same command (sd-01 M3). Without this, the unconditional
268
+ // git_destructive block made every authored git ask rule dead
269
+ // config. Commands matched only by classifier patterns, and rules
270
+ // with `block: true`, stay hard blocks. The ask rail itself runs
271
+ // only after the fail-closed and path-policy blocks below: hard
272
+ // blocks always win, and the ask rail decides among survivors.
273
+ const askRule = hit?.match.ask === true && hit.match.block !== true;
274
+ if (
275
+ !askRule &&
276
+ (classification.actionClass === "git_destructive" ||
277
+ hit?.match.actionClass === "git_destructive" ||
278
+ hit?.match.block === true)
279
+ ) {
280
+ const blockInput: Omit<
281
+ SafetyPolicyDecision,
282
+ "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
283
+ > = {
284
+ reasonCode: hit ? `damage-control:${hit.match.ruleId}` : "classification:git_destructive",
285
+ reasons: [...classification.reasons, ...(hit ? [hit.match.reason] : [])],
286
+ policySource: hit?.source ?? "damage-control:base",
287
+ };
288
+ if (hit?.match.ruleId !== undefined) blockInput.ruleId = hit.match.ruleId;
289
+ if (hit?.match !== undefined) blockInput.match = hit.match;
290
+ return blockDecision(base, blockInput);
291
+ }
292
+
293
+ if (!projectPolicy.valid && EXECUTION_TOOLS.has(call.tool)) {
294
+ const blockInput: Omit<
295
+ SafetyPolicyDecision,
296
+ "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
297
+ > = {
298
+ ruleId: "project-policy-invalid",
299
+ reasonCode: "project-policy-invalid",
300
+ reasons: [`project safety policy is invalid and execution fails closed: ${projectPolicy.errors.join("; ")}`],
301
+ policySource: "project-policy-invalid",
302
+ };
303
+ if (projectPolicy.hash !== null) blockInput.policyHash = projectPolicy.hash;
304
+ if (projectPolicy.path !== null) blockInput.projectPolicyPath = projectPolicy.path;
305
+ return blockDecision(base, blockInput);
306
+ }
307
+
308
+ // The path policy runs regardless of project policy validity. When
309
+ // `.clio-coder/safety.yaml` is invalid, the loader has already dropped every
310
+ // project-authored path entry and forced `disableDefaultPathPolicy`
311
+ // off, so the compiled policy here carries exactly the built-in
312
+ // defaults. Evaluating it unconditionally keeps default credential
313
+ // protection (`.env`, `~/.ssh/`, `credentials.yaml`, ...) active on a
314
+ // broken config, which is the fail-closed intent; project-authored
315
+ // additions and exemptions stay gated on validity inside the loader.
316
+ const pathBlock = evaluateProjectPathPolicy(pathPolicy, call, callCwd);
317
+ if (pathBlock !== null) {
318
+ const blockInput: Omit<
319
+ SafetyPolicyDecision,
320
+ "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
321
+ > = {
322
+ ruleId: pathBlock.reasonCode,
323
+ reasonCode: pathBlock.reasonCode,
324
+ reasons: [pathBlock.reason],
325
+ policySource: "project-policy",
326
+ };
327
+ if (projectPolicy.hash !== null) blockInput.policyHash = projectPolicy.hash;
328
+ if (projectPolicy.path !== null) blockInput.projectPolicyPath = projectPolicy.path;
329
+ return blockDecision(base, blockInput);
330
+ }
331
+ // Bash reads of zero-access paths. pathPolicyTargets extracts only
332
+ // write/delete targets from bash, so `cat .env` used to run and its
333
+ // output persisted into the transcript and evidence previews. Any
334
+ // path-like argument token matching a zero-access entry blocks the
335
+ // command; the one carve-out is the exit-code-only presence check
336
+ // (`grep -q`/`grep -sq` with a ^NAME= pattern), which is the safe
337
+ // protocol the credentials skill teaches.
338
+ if (call.tool === ToolNames.Bash && command !== null) {
339
+ const secretRead = evaluateBashZeroAccessRead(zeroAccessPolicy, command, callCwd);
340
+ if (secretRead !== null) {
341
+ const blockInput: Omit<
342
+ SafetyPolicyDecision,
343
+ "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
344
+ > = {
345
+ ruleId: "secret_path_bash",
346
+ reasonCode: "secret_path_bash",
347
+ reasons: [
348
+ `bash read of zero-access path blocked: '${secretRead.token}' matches ${secretRead.entrySource}. Check presence with exit codes only (grep -sq "^NAME=" <file>); have the user supply values through their own terminal (read -s), never through chat or command output.`,
349
+ ],
350
+ policySource: "project-policy",
351
+ };
352
+ if (projectPolicy.hash !== null) blockInput.policyHash = projectPolicy.hash;
353
+ if (projectPolicy.path !== null) blockInput.projectPolicyPath = projectPolicy.path;
354
+ return blockDecision(base, blockInput);
355
+ }
356
+ }
357
+
358
+ // The authored ask rail (sd-01 M3) decides only among calls that
359
+ // survived every hard block above. A confirmed posture admits the
360
+ // matched command; an unconfirmed one parks it for confirmation.
361
+ if (askRule && hit !== null && posture !== "confirmed") {
362
+ return askDecision(base, {
363
+ ruleId: hit.match.ruleId,
364
+ reasonCode: `damage-control:${hit.match.ruleId}`,
365
+ reasons: [...classification.reasons, hit.match.reason, "damage-control rule requires confirmation"],
366
+ policySource: hit.source,
367
+ match: hit.match,
368
+ });
369
+ }
370
+ if (askRule && hit !== null && posture === "confirmed") {
371
+ return allowDecision(base, {
372
+ ruleId: hit.match.ruleId,
373
+ reasonCode: `damage-control:${hit.match.ruleId}`,
374
+ reasons: [...classification.reasons, hit.match.reason, "damage-control rule confirmed by operator"],
375
+ policySource: hit.source,
376
+ match: hit.match,
377
+ });
378
+ }
379
+
380
+ if (classification.actionClass === "system_modify") {
381
+ const input: Omit<
382
+ SafetyPolicyDecision,
383
+ "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
384
+ > = {
385
+ ruleId: "system-modify-confirm",
386
+ reasonCode: "system-modify-confirm",
387
+ reasons: [...classification.reasons, "system-level changes require one-shot confirmation at every autonomy level"],
388
+ policySource: "builtin-classifier",
389
+ };
390
+ return posture === "confirmed" ? allowDecision(base, input) : askDecision(base, input);
391
+ }
392
+
393
+ if (call.tool === ToolNames.Bash && classification.actionClass === "execute") {
394
+ const bash = evaluateBashPolicy(command ?? "", callCwd, cwd, posture, projectPolicy);
395
+ if (bash.kind === "block") return blockDecision(base, bash);
396
+ if (bash.kind === "ask") return askDecision(base, bash);
397
+ return allowDecision(base, bash);
398
+ }
399
+
400
+ const allowInput: Omit<
401
+ SafetyPolicyDecision,
402
+ "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
403
+ > = {
404
+ reasonCode: "allowed",
405
+ reasons: classification.reasons,
406
+ policySource: hit?.source ?? "none",
407
+ };
408
+ if (hit?.match.ruleId !== undefined) allowInput.ruleId = hit.match.ruleId;
409
+ if (hit?.match !== undefined) allowInput.match = hit.match;
410
+ // The typed execution tool (verify) is bounded by its own check
411
+ // allowlist, so it sits in the no-prompt set.
412
+ if (classification.actionClass === "execute") allowInput.execRecognition = "recognized";
413
+ return allowDecision(base, allowInput);
414
+ },
415
+ metadata() {
416
+ return {
417
+ version: 1,
418
+ rulePackHash: rulePackHash(packs),
419
+ rulePackVersion: packs.base.version,
420
+ activeRuleIds: sourcedRules.map((entry) => entry.rule.id),
421
+ projectPolicyPath: projectPolicy.path,
422
+ projectPolicyHash: projectPolicy.hash,
423
+ projectPolicyValid: projectPolicy.valid,
424
+ projectPolicyErrors: [...projectPolicy.errors, ...pathPolicy.diagnostics],
425
+ cwd,
426
+ };
427
+ },
428
+ };
429
+ }
430
+
431
+ /** Absolute path of Clio's provider secret store, when resolvable. */
432
+ function clioCredentialStorePaths(): string[] {
433
+ try {
434
+ return [path.join(clioConfigDir(), "credentials.yaml")];
435
+ } catch {
436
+ return [];
437
+ }
438
+ }
439
+
440
+ /**
441
+ * The exact safe presence form: `grep -q` or `grep -sq` (either flag order)
442
+ * with a `^NAME=`-shaped pattern and a single file argument. Exit code only;
443
+ * the value never enters context. Anything broader stays blocked.
444
+ */
445
+ const SAFE_PRESENCE_RE = /^\s*grep\s+(?:-(?:q|sq|qs)\s+)('[^']*'|"[^"]*"|\S+)\s+\S+\s*$/;
446
+
447
+ function isSafePresenceCheck(command: string): boolean {
448
+ const match = SAFE_PRESENCE_RE.exec(command);
449
+ if (!match || match[1] === undefined) return false;
450
+ const pattern = stripQuotes(match[1]);
451
+ return /^\^[A-Za-z_][A-Za-z0-9_]*=/.test(pattern);
452
+ }
453
+
454
+ function stripQuotes(token: string): string {
455
+ if (token.length >= 2) {
456
+ const first = token[0];
457
+ const last = token[token.length - 1];
458
+ if ((first === '"' && last === '"') || (first === "'" && last === "'")) return token.slice(1, -1);
459
+ }
460
+ return token;
461
+ }
462
+
463
+ /**
464
+ * Split a bash command into candidate path tokens. Quotes bind: a quoted
465
+ * string is one token, so `git commit -m "handle .env parsing"` yields the
466
+ * whole message (not a path) while `cat ".env"` still yields `.env`. Shell
467
+ * metacharacters outside quotes act as separators. Tokens are matched as
468
+ * paths against the compiled zero-access entries (which handle tilde
469
+ * expansion and globs), never against a list of reader binaries: a
470
+ * zero-access path appearing anywhere in the command is the signal.
471
+ */
472
+ function bashPathTokenCandidates(command: string): string[] {
473
+ const candidates: string[] = [];
474
+ for (const token of tokenizeShellLike(command)) {
475
+ if (token.length === 0) continue;
476
+ if (token === ";" || token === "&&" || token === "||" || token === "|" || token === ">" || token === ">>") continue;
477
+ if (token === "<" || token === "<<") continue;
478
+ // A token containing whitespace came from a quoted string of prose, not
479
+ // a path argument; and flags are not paths unless they embed one
480
+ // (--file=~/.aws/credentials).
481
+ if (/\s/.test(token)) continue;
482
+ if (token.startsWith("-") && !token.includes("/") && !token.includes("=")) continue;
483
+ candidates.push(token);
484
+ const eq = token.indexOf("=");
485
+ if (eq > 0 && eq < token.length - 1) candidates.push(token.slice(eq + 1));
486
+ }
487
+ return candidates;
488
+ }
489
+
490
+ function evaluateBashZeroAccessRead(
491
+ zeroAccessPolicy: CompiledPathPolicy,
492
+ command: string,
493
+ callCwd: string,
494
+ ): { token: string; entrySource: string } | null {
495
+ if (zeroAccessPolicy.entries.length === 0) return null;
496
+ if (isSafePresenceCheck(command)) return null;
497
+ for (const token of bashPathTokenCandidates(command)) {
498
+ const decision = evaluatePathPolicy(zeroAccessPolicy, "read", token, callCwd);
499
+ if (decision.kind === "block") {
500
+ return { token, entrySource: `zeroAccessPaths entry ${decision.matchedPath}` };
501
+ }
502
+ }
503
+ return null;
504
+ }
505
+
506
+ function evaluateProjectPathPolicy(
507
+ policy: CompiledPathPolicy,
508
+ call: ClassifierCall,
509
+ callCwd: string,
510
+ ): Extract<PathPolicyDecision, { kind: "block" }> | null {
511
+ if (policy.entries.length === 0) return null;
512
+ for (const target of pathPolicyTargets(call)) {
513
+ const decision = evaluatePathPolicy(policy, target.operation, target.path, callCwd);
514
+ if (decision.kind === "block") return decision;
515
+ }
516
+ return null;
517
+ }
518
+
519
+ function pathPolicyTargets(call: ClassifierCall): Array<{ operation: PathPolicyOperation; path: string }> {
520
+ const args = call.args;
521
+ switch (call.tool) {
522
+ case ToolNames.Read:
523
+ case ToolNames.Ls:
524
+ case ToolNames.Grep:
525
+ case ToolNames.Find: {
526
+ const target = pathArg(args) ?? ".";
527
+ return [{ operation: "read", path: target }];
528
+ }
529
+ case ToolNames.Write:
530
+ case ToolNames.Edit: {
531
+ const target = pathArg(args);
532
+ return target === null ? [] : [{ operation: "write", path: target }];
533
+ }
534
+ case ToolNames.Artifact:
535
+ return [{ operation: "write", path: pathArg(args) ?? artifactDefaultPath(args?.kind) }];
536
+ case ToolNames.CredentialPresent:
537
+ // Sanctioned typed presence check: it may inspect secret-shaped paths
538
+ // internally, but its tool contract can return only boolean metadata.
539
+ return [];
540
+ case ToolNames.Bash: {
541
+ const command = commandArg(args);
542
+ if (command === null) return [];
543
+ return [
544
+ ...extractCommandWriteTargets(command).map((target) => ({ operation: "write" as const, path: target })),
545
+ ...extractCommandDeleteTargets(command).map((target) => ({ operation: "delete" as const, path: target })),
546
+ ];
547
+ }
548
+ default:
549
+ return [];
550
+ }
551
+ }
552
+
553
+ /**
554
+ * Bash net evaluation (sd-01 §2.2). Net blocks: empty commands and cwd
555
+ * escapes. Net confirms: project policy `requireConfirmation` and command
556
+ * substitution (the content-hiding channel). Everything else passes with an
557
+ * `execRecognition` tag: project policy commands and the built-in no-prompt
558
+ * allowlist are recognized; arbitrary bash, including commands with
559
+ * sequencing operators, is unrecognized and the autonomy mapping at the
560
+ * admission seam decides whether it runs, asks, or is denied.
561
+ */
562
+ function evaluateBashPolicy(
563
+ command: string,
564
+ callCwd: string,
565
+ workspaceRoot: string,
566
+ posture: string | undefined,
567
+ policy: LoadedProjectSafetyPolicy,
568
+ ): Omit<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"> {
569
+ if (command.trim().length === 0) {
570
+ return {
571
+ kind: "block",
572
+ ruleId: "bash-empty-command",
573
+ reasonCode: "bash-empty-command",
574
+ reasons: ["bash command must not be empty"],
575
+ policySource: "builtin-command-allowlist",
576
+ };
577
+ }
578
+ const projectMatch = matchingProjectCommand(policy, command, callCwd);
579
+ if (projectMatch) {
580
+ const base: Omit<
581
+ SafetyPolicyDecision,
582
+ "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
583
+ > = {
584
+ ruleId: projectMatch.id,
585
+ reasonCode: `project-policy:${projectMatch.id}`,
586
+ reasons: [`allowed by project safety policy command '${projectMatch.id}'`],
587
+ policySource: "project-policy" as const,
588
+ execRecognition: "recognized",
589
+ };
590
+ if (policy.hash !== null) base.policyHash = policy.hash;
591
+ if (policy.path !== null) base.projectPolicyPath = policy.path;
592
+ if (projectMatch.requireConfirmation && posture !== "confirmed") {
593
+ return {
594
+ ...base,
595
+ kind: "ask",
596
+ reasons: [...base.reasons, "project policy requires confirmation"],
597
+ };
598
+ }
599
+ return { ...base, kind: "allow" };
600
+ }
601
+ if (!isUnderOrSame(callCwd, workspaceRoot)) {
602
+ return {
603
+ kind: "block",
604
+ ruleId: "bash-cwd-escape",
605
+ reasonCode: "bash-cwd-escape",
606
+ reasons: [
607
+ `bash cwd '${callCwd}' escapes workspace root '${workspaceRoot}'; use a typed tool or a project policy entry with explicit cwd`,
608
+ ],
609
+ policySource: "builtin-command-allowlist",
610
+ };
611
+ }
612
+ // Command substitution is the content-hiding channel: the net cannot scan
613
+ // what `$(...)` or backticks produce at runtime, so it stays an ask rail at
614
+ // every level, including full-auto (sd-01 M5). A confirmed posture (one-shot
615
+ // grant) admits it like any other confirm rail.
616
+ // A command that is nothing but `sh -c '<script>'` is recognized by its
617
+ // script. Recognition can only narrow this way: the script is what runs, and
618
+ // judging the wrapper instead is what let the wrapper be a bypass.
619
+ const recognitionCommand = inlineShellScript(command) ?? command;
620
+ if (hasCommandSubstitution(recognitionCommand) && posture !== "confirmed") {
621
+ return {
622
+ kind: "ask",
623
+ ruleId: "bash-command-substitution",
624
+ reasonCode: "bash-command-substitution",
625
+ reasons: ["command substitution hides the executed content from the safety net and requires one-shot confirmation"],
626
+ policySource: "builtin-command-allowlist",
627
+ execRecognition: "unrecognized",
628
+ };
629
+ }
630
+ const chain = recognizeCommandChain(recognitionCommand, callCwd, workspaceRoot, policy);
631
+ if (chain !== null) {
632
+ const chainReasons = [`every step of the && chain is recognized: ${chain.ruleIds.join(", ")}`];
633
+ if (chain.requiresConfirmation && posture !== "confirmed") {
634
+ return {
635
+ kind: "ask",
636
+ ruleId: "bash-recognized-chain",
637
+ reasonCode: "bash-recognized-chain",
638
+ reasons: [...chainReasons, "project policy requires confirmation for one step"],
639
+ policySource: "builtin-command-allowlist",
640
+ execRecognition: "recognized",
641
+ };
642
+ }
643
+ return {
644
+ kind: "allow",
645
+ ruleId: "bash-recognized-chain",
646
+ reasonCode: "bash-recognized-chain",
647
+ reasons: chainReasons,
648
+ policySource: "builtin-command-allowlist",
649
+ execRecognition: "recognized",
650
+ };
651
+ }
652
+ // Remaining sequencing operators (pipes, ;, redirects, and && chains with an
653
+ // unrecognized member) defeat per-command recognition, so the command is
654
+ // unrecognized by definition: the autonomy mapping asks at suggest/auto-edit,
655
+ // runs at full-auto, denies at read-only. The rule pack has already scanned
656
+ // the full string, so a destructive verb behind an operator was caught before
657
+ // this point.
658
+ if (hasSequencingOperators(command)) {
659
+ return {
660
+ kind: "allow",
661
+ ruleId: "bash-shell-operators",
662
+ reasonCode: "bash-shell-operators",
663
+ reasons: [
664
+ "shell operators defeat per-command recognition; the autonomy level decides admission",
665
+ BASH_RECOGNIZED_FORM_HINT,
666
+ ],
667
+ policySource: "builtin-command-allowlist",
668
+ execRecognition: "unrecognized",
669
+ };
670
+ }
671
+ for (const entry of BUILTIN_ALLOWLIST) {
672
+ if (entry.re.test(recognitionCommand)) {
673
+ return {
674
+ kind: "allow",
675
+ ruleId: entry.id,
676
+ reasonCode: entry.id,
677
+ reasons: [`matched built-in no-prompt command allowlist '${entry.id}'`],
678
+ policySource: "builtin-command-allowlist",
679
+ execRecognition: "recognized",
680
+ };
681
+ }
682
+ }
683
+ return {
684
+ kind: "allow",
685
+ ruleId: "bash-unrecognized",
686
+ reasonCode: "bash-unrecognized",
687
+ reasons: [
688
+ "bash command is outside the no-prompt set; the autonomy level decides admission",
689
+ BASH_RECOGNIZED_FORM_HINT,
690
+ ],
691
+ policySource: "builtin-command-allowlist",
692
+ execRecognition: "unrecognized",
693
+ };
694
+ }
695
+
696
+ function baseDecision(
697
+ call: ClassifierCall,
698
+ classification: Classification,
699
+ cwd: string,
700
+ posture: string | undefined,
701
+ command: string | null,
702
+ ): Pick<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"> {
703
+ const out: Pick<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"> = {
704
+ classification,
705
+ tool: call.tool,
706
+ actionClass: classification.actionClass,
707
+ cwd,
708
+ };
709
+ if (posture !== undefined) out.posture = posture;
710
+ if (command !== null) out.command = command;
711
+ return out;
712
+ }
713
+
714
+ function allowDecision(
715
+ base: Pick<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
716
+ input: Omit<SafetyPolicyDecision, "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
717
+ ): SafetyPolicyDecision {
718
+ return { ...base, ...input, kind: "allow" };
719
+ }
720
+
721
+ function askDecision(
722
+ base: Pick<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
723
+ input: Omit<SafetyPolicyDecision, "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
724
+ ): SafetyPolicyDecision {
725
+ const rejectionInput: Parameters<typeof formatRejection>[0] = {
726
+ tool: base.tool,
727
+ actionClass: base.classification.actionClass,
728
+ reasons: input.reasons,
729
+ };
730
+ if (base.posture !== undefined) rejectionInput.posture = base.posture;
731
+ if (input.ruleId !== undefined) rejectionInput.ruleId = input.ruleId;
732
+ const rejection = formatRejection(rejectionInput);
733
+ return { ...base, ...input, kind: "ask", rejection };
734
+ }
735
+
736
+ function blockDecision(
737
+ base: Pick<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
738
+ input: Omit<SafetyPolicyDecision, "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
739
+ ): SafetyPolicyDecision {
740
+ const rejectionInput: Parameters<typeof formatRejection>[0] = {
741
+ tool: base.tool,
742
+ actionClass: base.classification.actionClass,
743
+ reasons: input.reasons,
744
+ };
745
+ if (base.posture !== undefined) rejectionInput.posture = base.posture;
746
+ if (input.ruleId !== undefined) rejectionInput.ruleId = input.ruleId;
747
+ const rejection = formatRejection(rejectionInput);
748
+ return { ...base, ...input, kind: "block", rejection };
749
+ }
750
+
751
+ function matchSourcedRule(commandString: string, rules: ReadonlyArray<SourcedRule>) {
752
+ for (const entry of rules) {
753
+ if (entry.rule.pattern.test(commandString)) {
754
+ const match: DamageControlMatch = {
755
+ ruleId: entry.rule.id,
756
+ reason: `matched ${entry.rule.id}: ${entry.rule.description}`,
757
+ actionClass: entry.rule.class,
758
+ block: entry.rule.block,
759
+ };
760
+ if (entry.rule.ask !== undefined) match.ask = entry.rule.ask;
761
+ return { match, source: entry.source };
762
+ }
763
+ }
764
+ return null;
765
+ }
766
+
767
+ // Excludes "unknown" because damage control overrides must classify actions into concrete, actionable categories.
768
+ const ACTION_CLASSES = new Set<ActionClass>([
769
+ "read",
770
+ "write",
771
+ "execute",
772
+ "system_modify",
773
+ "git_destructive",
774
+ "dispatch",
775
+ ]);
776
+
777
+ function effectiveClassification(
778
+ classification: Classification,
779
+ match: DamageControlMatch | undefined,
780
+ ): Classification {
781
+ if (!match || !ACTION_CLASSES.has(match.actionClass as ActionClass)) return classification;
782
+ const actionClass = match.actionClass as ActionClass;
783
+ if (actionClass === classification.actionClass) return classification;
784
+ return {
785
+ actionClass,
786
+ reasons: [...classification.reasons, `damage-control:${match.ruleId}`],
787
+ };
788
+ }
789
+
790
+ function matchingProjectCommand(
791
+ policy: LoadedProjectSafetyPolicy,
792
+ command: string,
793
+ cwd: string,
794
+ ): ProjectCommandPolicy | null {
795
+ if (!policy.valid || policy.path === null) return null;
796
+ const policyRoot = path.dirname(path.dirname(policy.path));
797
+ for (const entry of policy.commands) {
798
+ if (entry.command !== command) continue;
799
+ if (entry.shellOperators === "deny" && hasShellOperators(command)) continue;
800
+ const allowedCwd = entry.cwd !== undefined ? path.resolve(policyRoot, entry.cwd) : policyRoot;
801
+ if (!isUnderOrSame(cwd, allowedCwd)) continue;
802
+ return entry;
803
+ }
804
+ return null;
805
+ }
806
+
807
+ /** Operators that end chain recognition; only `&&` keeps a chain readable. */
808
+ const CHAIN_BREAKING_TOKENS: ReadonlySet<string> = new Set([";", "|", "||", ">", ">>", "<", "<<"]);
809
+
810
+ /** Chain length ceiling. Real compound calls are two or three steps. */
811
+ const CHAIN_MAX_SEGMENTS = 6;
812
+
813
+ interface ChainRecognition {
814
+ ruleIds: ReadonlyArray<string>;
815
+ requiresConfirmation: boolean;
816
+ }
817
+
818
+ /**
819
+ * Recognition for `cd <workspace dir> && <recognized command>` and the longer
820
+ * `&&` chains built from the same parts. Every member is checked on its own and
821
+ * the chain takes its most restrictive member's verdict, so the chain can never
822
+ * admit something its members would not.
823
+ *
824
+ * This exists because the compound form is what a model reaches for first and
825
+ * the flat rule cost more than it bought: in a recorded live drive 34 of 75
826
+ * calls were blocked, nearly all of them `cd x && y`, since an unrecognized
827
+ * execute asks below full-auto and a headless run answers every ask with a
828
+ * denial (REPORT-dispatch-drive-1.md S2). Meanwhile `sh -c '<anything>'` sailed
829
+ * past the same rail, so the rail was mostly taxing the honest spelling. The
830
+ * `sh -c` half is closed at both ends now: this function recognizes such a
831
+ * command by its inner script, and the shared write-target scanner reads inside
832
+ * the script too.
833
+ */
834
+ function recognizeCommandChain(
835
+ command: string,
836
+ callCwd: string,
837
+ workspaceRoot: string,
838
+ policy: LoadedProjectSafetyPolicy,
839
+ ): ChainRecognition | null {
840
+ const segments: string[][] = [];
841
+ let current: string[] = [];
842
+ for (const token of tokenizeShellLike(command)) {
843
+ if (token === "&&") {
844
+ segments.push(current);
845
+ current = [];
846
+ continue;
847
+ }
848
+ if (CHAIN_BREAKING_TOKENS.has(token)) return null;
849
+ current.push(token);
850
+ }
851
+ segments.push(current);
852
+ if (segments.length < 2 || segments.length > CHAIN_MAX_SEGMENTS) return null;
853
+ const ruleIds: string[] = [];
854
+ let requiresConfirmation = false;
855
+ for (const segment of segments) {
856
+ if (segment.length === 0) return null;
857
+ if (segment[0] === "cd") {
858
+ // A `cd` that stays inside the workspace re-bases nothing the net cares
859
+ // about; one that leaves it is the laundering pattern the action
860
+ // classifier already escalates, so it is not recognizable here.
861
+ if (segment.length !== 2) return null;
862
+ const target = segment[1] ?? "";
863
+ if (target.startsWith("~") || !isUnderOrSame(path.resolve(callCwd, target), workspaceRoot)) return null;
864
+ ruleIds.push("builtin:cd-workspace");
865
+ continue;
866
+ }
867
+ // Re-rendered from tokens, so quoting is gone: a member that needed its
868
+ // quotes fails the allowlist regex and the whole chain stays unrecognized.
869
+ const rendered = segment.join(" ");
870
+ const projectMatch = matchingProjectCommand(policy, rendered, callCwd);
871
+ if (projectMatch) {
872
+ ruleIds.push(projectMatch.id);
873
+ if (projectMatch.requireConfirmation) requiresConfirmation = true;
874
+ continue;
875
+ }
876
+ const builtin = BUILTIN_ALLOWLIST.find((entry) => entry.re.test(rendered));
877
+ if (builtin === undefined) return null;
878
+ ruleIds.push(builtin.id);
879
+ }
880
+ return { ruleIds, requiresConfirmation };
881
+ }
882
+
883
+ /**
884
+ * Sequencing and redirection operators. These defeat per-command allowlist
885
+ * matching but hide nothing from the rule pack, which scans the full string.
886
+ */
887
+ function hasSequencingOperators(command: string): boolean {
888
+ return /(\|\||&&|;|\||>>?|<|\n|\r)/.test(command);
889
+ }
890
+
891
+ /**
892
+ * Content-hiding constructs: `$(...)` and backticks execute text the net
893
+ * cannot see until runtime. Kept separate from sequencing (sd-01 M5) so they
894
+ * can stay ask-gated at full-auto.
895
+ */
896
+ function hasCommandSubstitution(command: string): boolean {
897
+ return /(`|\$\()/.test(command);
898
+ }
899
+
900
+ /** Any shell operator at all; project policy entries with `shellOperators: deny` reject both kinds. */
901
+ function hasShellOperators(command: string): boolean {
902
+ return hasSequencingOperators(command) || hasCommandSubstitution(command);
903
+ }
904
+
905
+ function isUnderOrSame(child: string, parent: string): boolean {
906
+ const rel = path.relative(
907
+ canonicalizeExistingPath(path.resolve(parent)),
908
+ canonicalizeExistingPath(path.resolve(child)),
909
+ );
910
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
911
+ }
912
+
913
+ function commandArg(args: Record<string, unknown> | undefined): string | null {
914
+ return typeof args?.command === "string" ? args.command : null;
915
+ }
916
+
917
+ function pathArg(args: Record<string, unknown> | undefined): string | null {
918
+ if (!args) return null;
919
+ const candidate = args.path ?? args.file_path ?? args.filePath;
920
+ return typeof candidate === "string" && candidate.length > 0 ? candidate : null;
921
+ }
922
+
923
+ function cwdArg(args: Record<string, unknown> | undefined, fallback: string): string {
924
+ const resolved = typeof args?.cwd === "string" && args.cwd.length > 0 ? path.resolve(fallback, args.cwd) : fallback;
925
+ return canonicalizeExistingPath(resolved);
926
+ }
927
+
928
+ /**
929
+ * Tools whose arguments are a file's contents rather than a command line.
930
+ * Their damage-control scan is the destination path alone.
931
+ */
932
+ const CONTENT_BEARING_TOOLS: ReadonlySet<string> = new Set([ToolNames.Write, ToolNames.Edit, ToolNames.Artifact]);
933
+
934
+ /**
935
+ * The text damage-control rules are matched against.
936
+ *
937
+ * Every rule in the pack is a command pattern: shell (`rm -rf /`, `chmod -R
938
+ * 777`), cloud CLI (`aws s3 rm --recursive`), or SQL (`DROP TABLE`). Matching
939
+ * them against a file's contents asks whether the file *mentions* a dangerous
940
+ * command, which is a different question from whether the call *runs* one, and
941
+ * the two are indistinguishable once the text is in the haystack.
942
+ *
943
+ * That cost a real feature. `clio-coder context wiki` could not write its own
944
+ * `domains/safety.md`: the page documents what the classifier blocks, so it
945
+ * quotes `rm -rf /`, and the write was refused as `system_modify` with reason
946
+ * `damage-control:rm-rf-root`. The same defect blocks writing a SQL migration
947
+ * containing `DROP TABLE` or a test fixture for the classifier itself.
948
+ *
949
+ * Writing a file is not executing it. A script written with a destructive body
950
+ * still has to be run, and that run is an execute-class call scanned here in
951
+ * full. Only the destination path is scanned for a mutation tool, which keeps
952
+ * any path-shaped rule working; where the file may land is the write tool's own
953
+ * gate in `writePathClass`.
954
+ */
955
+ function damageControlScan(call: ClassifierCall): string {
956
+ if (!CONTENT_BEARING_TOOLS.has(call.tool)) return serializeArgs(call.args);
957
+ const pathArg = call.args?.path;
958
+ return typeof pathArg === "string" ? pathArg : "";
959
+ }
960
+
961
+ function serializeArgs(args?: Record<string, unknown>): string {
962
+ if (!args) return "";
963
+ const parts: string[] = [];
964
+ for (const v of Object.values(args)) {
965
+ if (v == null) continue;
966
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") parts.push(String(v));
967
+ else {
968
+ try {
969
+ parts.push(JSON.stringify(v));
970
+ } catch {
971
+ // ignore values that cannot be serialized
972
+ }
973
+ }
974
+ }
975
+ return parts.join(" ");
976
+ }
977
+
978
+ function rulePackHash(packs: RulePacks): string {
979
+ const payload: Record<PackId, Array<Record<string, unknown>>> = {
980
+ base: packPayload(packs.base.rules),
981
+ };
982
+ return createHash("sha256").update(JSON.stringify(payload), "utf8").digest("hex");
983
+ }
984
+
985
+ function packPayload(rules: ReadonlyArray<DamageControlRule>): Array<Record<string, unknown>> {
986
+ return rules.map((rule) => ({
987
+ id: rule.id,
988
+ description: rule.description,
989
+ pattern: rule.pattern.source,
990
+ class: rule.class,
991
+ block: rule.block,
992
+ ...(rule.ask !== undefined ? { ask: rule.ask } : {}),
993
+ }));
994
+ }