@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,1873 @@
1
+ import { join } from "node:path";
2
+ import chalk from "chalk";
3
+ import { modelBootstrapGenerate, resolveBootstrapRoute } from "../cli/bootstrap-generate.js";
4
+ import { runHeadlessMainAgent } from "../cli/modes/print.js";
5
+ import { BusChannels } from "../core/bus-events.js";
6
+ import { installBusTracer } from "../core/bus-trace.js";
7
+ import { type ClioSettings, readSettings, type SettingsMutator, updateSettings } from "../core/config.js";
8
+ import { DEFAULT_DELEGATION_PERMISSION_TIMEOUT_MS } from "../core/defaults.js";
9
+ import { loadDomains } from "../core/domain-loader.js";
10
+ import { expandInlineFileReferencesAsync } from "../core/file-references.js";
11
+ import { configureGuardrails } from "../core/guardrails.js";
12
+ import { HEADLESS_PERMISSION_DENIED_REASON } from "../core/headless-permission.js";
13
+ import { rememberRecentModel } from "../core/recent-models.js";
14
+ import { EXPERIMENTAL_RELEASE_WARNING } from "../core/release.js";
15
+ import { protectedResidencyModels } from "../core/residency-protection.js";
16
+ import {
17
+ applyOverrides,
18
+ applyRoutingPatch,
19
+ applySessionRouting,
20
+ diffRouting,
21
+ getAtPath,
22
+ isRoutingPath,
23
+ mergeRoutingPatchIntoSettings,
24
+ type RoutingPatch,
25
+ restoreRoutingFields,
26
+ routingChangeNotices,
27
+ routingPatchForId,
28
+ type SessionOverrides,
29
+ seedSessionRouting,
30
+ setAtPath,
31
+ } from "../core/session-routing.js";
32
+ import { getSharedBus } from "../core/shared-bus.js";
33
+ import { isSkillActivation } from "../core/skill-activation.js";
34
+ import { StartupTimer } from "../core/startup-timer.js";
35
+ import { getTerminationCoordinator } from "../core/termination.js";
36
+ import { clioDataDir, clioStateDir } from "../core/xdg.js";
37
+ import { renderAgentCatalogSectionsFromSpecs } from "../domains/agents/catalog.js";
38
+ import type { AgentsContract } from "../domains/agents/contract.js";
39
+ import { AgentsDomainModule } from "../domains/agents/index.js";
40
+ import type { ConfigContract } from "../domains/config/contract.js";
41
+ import { ConfigDomainModule } from "../domains/config/index.js";
42
+ import { type ContextContract, createContextDomainModule } from "../domains/context/index.js";
43
+ import { bootstrapInputFromInitOptions } from "../domains/context/init-options.js";
44
+ import type { DispatchContract } from "../domains/dispatch/contract.js";
45
+ import { createDispatchDedupRegistration } from "../domains/dispatch/dedup.js";
46
+ import { agentRoleFactsResolver } from "../domains/dispatch/execution-role.js";
47
+ import { readGateDecisionArtifacts, readPendingGateDecisions } from "../domains/dispatch/gate-decisions.js";
48
+ import { createDispatchDomainModule } from "../domains/dispatch/index.js";
49
+ import { type ExtensionsContract, ExtensionsDomainModule } from "../domains/extensions/index.js";
50
+ import { type InteropContract, InteropDomainModule } from "../domains/interop/index.js";
51
+ import {
52
+ describeUpgradeNotice,
53
+ ensureClioState,
54
+ LifecycleDomainModule,
55
+ takeUpgradeNotice,
56
+ } from "../domains/lifecycle/index.js";
57
+ import { getVersionInfo } from "../domains/lifecycle/version.js";
58
+ import {
59
+ buildMemoryPromptSection,
60
+ canonicalMemoryRepositoryIdentity,
61
+ createTaskMemoryTelemetrySink,
62
+ createTaskMemoryTrace,
63
+ loadMemoryRecordsSync,
64
+ renderTaskMemoryHandoffSource,
65
+ seedTaskMemoryFromNewestHandoff,
66
+ type TaskMemoryModelClient,
67
+ taskMemoryBankSize,
68
+ taskMemoryHandoffSeedOffer,
69
+ taskMemoryTracePath,
70
+ } from "../domains/memory/index.js";
71
+ import { TaskMemoryBank } from "../domains/memory/task-bank.js";
72
+ import {
73
+ createDetachedDispatchNudgeRegistration,
74
+ createReadOnlyExplorationNudgeRegistration,
75
+ createUnbackedWorkerClaimRegistration,
76
+ openDetachedBatchViews,
77
+ } from "../domains/middleware/dispatch-nudge.js";
78
+ import {
79
+ createHookReceiptLog,
80
+ createMiddlewareToolChoiceControl,
81
+ createSkillsReminderRegistration,
82
+ type ExtensionHookRoot,
83
+ installUserHooks,
84
+ type MiddlewareContract,
85
+ MiddlewareDomainModule,
86
+ writeMiddlewareDiagnosticToStderr,
87
+ } from "../domains/middleware/index.js";
88
+ import { createMemoryInterventionRegistration } from "../domains/middleware/memory-intervention.js";
89
+ import { createTaskBoardReminderRegistration } from "../domains/middleware/task-board-reminder.js";
90
+ import { createTaskNudgeRegistration } from "../domains/middleware/task-nudge.js";
91
+ import type { ObservabilityContract } from "../domains/observability/index.js";
92
+ import { ObservabilityDomainModule } from "../domains/observability/index.js";
93
+ import type { PromptsContract } from "../domains/prompts/contract.js";
94
+ import { createPromptsDomainModule } from "../domains/prompts/index.js";
95
+ import type { ProvidersContract, TargetDescriptor, ThinkingLevel } from "../domains/providers/index.js";
96
+ import {
97
+ AGENT_ROLE_TOOLS_REQUIRED_REASON,
98
+ applyModelCapabilityPatch,
99
+ firstRuntimeResolutionError,
100
+ isOrchestratorEligibleRuntime,
101
+ ProvidersDomainModule,
102
+ refineRuntimeTargetWithModelHints,
103
+ resolveModelCapabilities,
104
+ resolveModelRuntimeCapabilitiesForProviders,
105
+ resolveRuntimeTarget,
106
+ supportsAgentRoleTools,
107
+ targetRequiresAuth,
108
+ VALID_THINKING_LEVELS,
109
+ } from "../domains/providers/index.js";
110
+ import { getRuntimeRegistry } from "../domains/providers/registry.js";
111
+ import { registerBuiltinRuntimes } from "../domains/providers/runtimes/builtins.js";
112
+ import { createResourcesDomainModule, modelVisibleSkills, type ResourcesContract } from "../domains/resources/index.js";
113
+ import { DEFAULT_RECENT_ENTRY_LIMIT } from "../domains/safety/finish-contract.js";
114
+ import { createFinishContractRegistration } from "../domains/safety/finish-contract-registration.js";
115
+ import type { AutonomyLevel, SafetyContract } from "../domains/safety/index.js";
116
+ import { parseRigorOverride, resolveRigor, SafetyDomainModule } from "../domains/safety/index.js";
117
+ import type { ProtectedArtifactState } from "../domains/safety/protected-artifacts.js";
118
+ import {
119
+ createProtectedArtifactsRegistration,
120
+ type ProtectedArtifactProtectEvent,
121
+ } from "../domains/safety/protected-artifacts-registration.js";
122
+ import type { SchedulingContract } from "../domains/scheduling/contract.js";
123
+ import { SchedulingDomainModule } from "../domains/scheduling/index.js";
124
+ import { type CompactResult, compact } from "../domains/session/compaction/compact.js";
125
+ import { collectSessionEntries } from "../domains/session/compaction/session-entries.js";
126
+ import { ceilChars, estimateAgentContextTokens } from "../domains/session/context-accounting.js";
127
+ import type { SessionContract, SessionMeta } from "../domains/session/contract.js";
128
+ import type { CompactionSummaryEntry, CompactionTrigger, SessionEntry } from "../domains/session/entries.js";
129
+ import { SessionDomainModule } from "../domains/session/index.js";
130
+ import {
131
+ clearPendingProtectedArtifact,
132
+ reconcilePendingProtectedArtifacts,
133
+ stagePendingProtectedArtifact,
134
+ } from "../domains/session/protected-artifact-journal.js";
135
+ import {
136
+ protectedArtifactEntryFromArtifact,
137
+ protectedArtifactStateFromSessionEntries,
138
+ } from "../domains/session/protected-artifacts.js";
139
+ import { createTaskBoardStore } from "../domains/session/task-board.js";
140
+ import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
141
+ import { type ShareContract, ShareDomainModule } from "../domains/share/index.js";
142
+ import { serveClioAcpAgent } from "../engine/acp/server.js";
143
+ import {
144
+ type AcpJsonRpcPeerTransport,
145
+ createStdioServerTransport,
146
+ type StdioServerTransportOptions,
147
+ } from "../engine/acp/transport.js";
148
+ import { completeEngineText } from "../engine/ai.js";
149
+ import { setProtectedModelsProvider } from "../engine/apis/residency.js";
150
+ import {
151
+ createLoopGuardRegistration,
152
+ INTERACTIVE_LOOP_BLOCK_BUDGET,
153
+ readOrchTurnToolCallBudget,
154
+ } from "../engine/loop-guard.js";
155
+ import { openSession, readSessionTailTurns, sessionCurrentPath, sessionPaths } from "../engine/session.js";
156
+ import type { EngineModel, ImageContent } from "../engine/types.js";
157
+ import { createChatLoop } from "../interactive/chat-loop.js";
158
+ import { buildReplayAgentMessagesFromTurns } from "../interactive/chat-renderer.js";
159
+ import { type RunIo, startInteractive } from "../interactive/index.js";
160
+ import {
161
+ detectPlatformKeybindingWarnings,
162
+ detectTerminalKeySupport,
163
+ formatInvalidKeybindingNotice,
164
+ formatPlatformKeybindingNotice,
165
+ validateKeybindings,
166
+ } from "../interactive/keybinding-manager.js";
167
+ import { subscribeLoopGuardStop } from "../interactive/loop-guard-interrupt.js";
168
+ import { createToolProseRegistration } from "../interactive/tool-prose-registration.js";
169
+ import { type AskUserHandler, cancelledAskUserResult } from "../tools/ask-user.js";
170
+ import { registerAllTools } from "../tools/bootstrap.js";
171
+ import { isGitRepository, recoverCleanupReadyCompeteGroups } from "../tools/compete-worktrees.js";
172
+ import { createDispatchBackgroundRegistry } from "../tools/dispatch.js";
173
+ import { coalescePathSink, createFileMutationObserver, createSkillActivationObserver } from "../tools/observers.js";
174
+ import { createRegistry } from "../tools/registry.js";
175
+
176
+ export interface BootResult {
177
+ exitCode: number;
178
+ bootTimeMs: number;
179
+ }
180
+
181
+ export interface HeadlessSamplingOverrides {
182
+ temperature?: number;
183
+ topP?: number;
184
+ topK?: number;
185
+ minP?: number;
186
+ presencePenalty?: number;
187
+ frequencyPenalty?: number;
188
+ repeatPenalty?: number;
189
+ }
190
+
191
+ export interface BootOptions {
192
+ /** Process-lifetime API key override applied to the active orchestrator target. */
193
+ apiKey?: string;
194
+ /** Suppress CLIO-CODER.md project-context injection for this run. */
195
+ noContextFiles?: boolean;
196
+ noSkills?: boolean;
197
+ skillPaths?: ReadonlyArray<string>;
198
+ /** Run one non-interactive main-agent turn. */
199
+ headless?: {
200
+ prompt: string;
201
+ images?: ReadonlyArray<ImageContent>;
202
+ workingContextPaths?: ReadonlyArray<string>;
203
+ mode?: "text" | "json";
204
+ jsonEvents?: "full" | "terminal";
205
+ target?: string;
206
+ model?: string;
207
+ thinking?: ThinkingLevel;
208
+ autonomy?: AutonomyLevel;
209
+ sampling?: HeadlessSamplingOverrides;
210
+ noSkills?: boolean;
211
+ skillPaths?: ReadonlyArray<string>;
212
+ steerChannel?: string;
213
+ /**
214
+ * Append this turn to an existing session instead of starting a fresh
215
+ * one. `latest` means the most recent session recorded for this
216
+ * workspace. A named session that cannot be resumed fails the run
217
+ * rather than silently starting a new one, because a caller that asked
218
+ * to continue a conversation would otherwise get an answer with no
219
+ * history behind it.
220
+ */
221
+ resumeSession?: { kind: "id"; id: string } | { kind: "latest" };
222
+ };
223
+ /** Serve Clio as an Agent Client Protocol v1 agent over JSON-RPC stdio. */
224
+ acp?: {
225
+ transport?: AcpJsonRpcPeerTransport;
226
+ transportOptions?: StdioServerTransportOptions;
227
+ /** Overrides delegation.defaults.permissionTimeoutMs for this server only. */
228
+ permissionTimeoutMs?: number;
229
+ };
230
+ }
231
+
232
+ /**
233
+ * The bannered boot is the whole of what a piped or CI invocation of bare
234
+ * `clio` shows, so it is the entire first impression for a stranger who is not
235
+ * on a TTY. It used to end in a hardcoded `ready`, a word with no relationship
236
+ * to anything: a machine with no target configured at all printed it and
237
+ * exited 0, which is the one state where the installation can do nothing.
238
+ *
239
+ * What the line reports now is the orchestrator target the settings actually
240
+ * declare, and when none is declared it says so and names the command that
241
+ * fixes it. The exit status stays 0 either way, because this path answers
242
+ * "did Clio boot", which it did, and CI scripts already depend on that
243
+ * answer; the readiness of the configuration is reported in words instead.
244
+ */
245
+ function bannerConfigurationLine(): string {
246
+ let settings: ClioSettings;
247
+ try {
248
+ settings = readSettings();
249
+ } catch {
250
+ return chalk.yellow("settings.yaml is not valid. Run `clio-coder doctor` for the exact keys.");
251
+ }
252
+ // A dangling chat target normalizes to null in the schema, so a deleted
253
+ // target arrives here as no target at all rather than as a name to report.
254
+ const targetId = settings.orchestrator?.target;
255
+ if (!targetId) {
256
+ return chalk.yellow("no model target configured. Run `clio-coder configure` to add one.");
257
+ }
258
+ const model = settings.orchestrator?.model;
259
+ return chalk.dim(`target ${targetId}${model ? ` · model ${model}` : " · no default model"}`);
260
+ }
261
+
262
+ function buildBanner(): string {
263
+ const { clio } = getVersionInfo();
264
+ return `
265
+ ${chalk.cyan("Clio Coder")}
266
+ ${chalk.dim(`v${clio} · CLIO: Context Layer for I/O · HPC & scientific software`)}
267
+ ${chalk.yellow.bold(EXPERIMENTAL_RELEASE_WARNING)}
268
+ ${bannerConfigurationLine()}
269
+ `;
270
+ }
271
+
272
+ function printJsonSessionHeader(meta: SessionMeta | null): Record<string, unknown> | null {
273
+ if (!meta) return null;
274
+ return {
275
+ type: "session",
276
+ version: meta.sessionFormatVersion ?? 1,
277
+ id: meta.id,
278
+ timestamp: meta.createdAt,
279
+ cwd: meta.cwd,
280
+ target: meta.target,
281
+ model: meta.model,
282
+ clioVersion: meta.clioVersion,
283
+ };
284
+ }
285
+
286
+ function applyHeadlessSettingsOverlay(
287
+ settings: ClioSettings,
288
+ overrides: BootOptions["headless"] | undefined,
289
+ ): ClioSettings {
290
+ const next = structuredClone(settings);
291
+ if (!overrides) return next;
292
+ const previousTarget = next.orchestrator.target;
293
+ if (overrides.target !== undefined) {
294
+ next.orchestrator.target = overrides.target;
295
+ if (overrides.model === undefined && (previousTarget !== overrides.target || !next.orchestrator.model)) {
296
+ const target = next.targets.find((entry) => entry.id === overrides.target);
297
+ if (target) next.orchestrator.model = target.defaultModel ?? null;
298
+ }
299
+ }
300
+ if (overrides.model !== undefined) next.orchestrator.model = overrides.model;
301
+ if (overrides.thinking !== undefined) next.orchestrator.thinkingLevel = overrides.thinking;
302
+ if (overrides.autonomy !== undefined) next.autonomy = overrides.autonomy;
303
+ return next;
304
+ }
305
+
306
+ interface CompactionResolution {
307
+ model: EngineModel;
308
+ targetId: string;
309
+ apiKey?: string;
310
+ }
311
+
312
+ function resolveTarget(providers: ProvidersContract, targetId: string | null | undefined): TargetDescriptor | null {
313
+ if (!targetId) return null;
314
+ return providers.getTarget(targetId);
315
+ }
316
+
317
+ function advanceThinkingLevel(current: ThinkingLevel, available: ReadonlyArray<ThinkingLevel>): ThinkingLevel {
318
+ const levels = available.length > 0 ? available : VALID_THINKING_LEVELS;
319
+ if (!levels.includes(current)) return levels[0] ?? "off";
320
+ const normalized = current;
321
+ const idx = levels.indexOf(normalized);
322
+ return levels[(idx + 1) % levels.length] ?? "off";
323
+ }
324
+
325
+ /**
326
+ * pi-ai's openai-completions provider refuses to stream without an apiKey even
327
+ * when the target is a local server that ignores the Authorization header
328
+ * entirely. The chat loop, the dispatch workers, and the background memory
329
+ * model all send this placeholder so a local llama.cpp or LM Studio endpoint
330
+ * works without the user inventing a credential.
331
+ */
332
+ const LOCAL_API_KEY_FALLBACK = "clio-local-target";
333
+
334
+ export async function resolveApiKeyForTarget(
335
+ target: TargetDescriptor,
336
+ providers: ProvidersContract,
337
+ ): Promise<string | undefined> {
338
+ const runtime = providers.getRuntime(target.runtime);
339
+ if (!runtime) return undefined;
340
+ // A target that needs no credential still needs the placeholder. Returning
341
+ // nothing here left compaction as the one path that did not send it, so
342
+ // `/context compact` and every automatic compaction at the window threshold
343
+ // failed on exactly the local runtimes Clio is built for, while ordinary
344
+ // turns against the same target succeeded.
345
+ if (!targetRequiresAuth(target, runtime)) return LOCAL_API_KEY_FALLBACK;
346
+ const resolved = await providers.auth.resolveForTarget(target, runtime);
347
+ return resolved.apiKey;
348
+ }
349
+
350
+ function createBackgroundMemoryModelClient(
351
+ providers: ProvidersContract,
352
+ settings: Readonly<ClioSettings>,
353
+ timeoutMs: number,
354
+ ): TaskMemoryModelClient | null {
355
+ const targetId = settings.background.target?.trim();
356
+ const wireModelId = settings.background.model?.trim();
357
+ if (!targetId || !wireModelId) return null;
358
+ // One model cannot both drive the action agent and think its way through a
359
+ // memory step: the memory call would contend with the agent's own decoding on
360
+ // the same server, and a reasoning model spends most of a memory step
361
+ // deliberating over a task it is not executing. Memory falls back to its free
362
+ // deterministic tier rather than degrading the work the operator asked for.
363
+ if (backgroundSharesReasoningModelWithOrchestrator(providers, settings)) return null;
364
+ const resolved = resolveRuntimeTarget(providers, {
365
+ targetId,
366
+ wireModelId,
367
+ // Memory reads a trajectory and writes a fixed envelope. Reasoning adds
368
+ // latency and, on a small local model, routinely consumes the entire
369
+ // output budget before the envelope is ever written.
370
+ requestedThinkingLevel: "off",
371
+ use: "orchestrator",
372
+ requireTools: false,
373
+ requireOutputBudget: true,
374
+ });
375
+ if (!resolved.ok) {
376
+ const detail = firstRuntimeResolutionError(resolved.diagnostics) ?? "background target resolution failed";
377
+ throw new Error(detail);
378
+ }
379
+ const kbHit = providers.knowledgeBase?.lookup(resolved.target.wireModelId) ?? null;
380
+ const model = resolved.target.runtime.synthesizeModel(resolved.target.target, resolved.target.wireModelId, kbHit);
381
+ const refined = refineRuntimeTargetWithModelHints(resolved.target, model, providers.knowledgeBase);
382
+ applyModelCapabilityPatch(model, refined.capabilities);
383
+ return {
384
+ async complete(request) {
385
+ const apiKey = targetRequiresAuth(refined.target, refined.runtime)
386
+ ? (await providers.auth.resolveForTarget(refined.target, refined.runtime)).apiKey
387
+ : LOCAL_API_KEY_FALLBACK;
388
+ return completeEngineText({
389
+ model,
390
+ systemPrompt: request.systemPrompt,
391
+ userPrompt: request.userPrompt,
392
+ maxTokens: request.maxTokens,
393
+ // Always off, never the operator's chat thinking level. A model that
394
+ // reasons anyway still works: `completeEngineText` keeps only text
395
+ // blocks, and the memory output budget leaves room for the preamble.
396
+ thinkingLevel: "off",
397
+ signal: request.signal,
398
+ timeoutMs,
399
+ ...(apiKey === undefined ? {} : { apiKey }),
400
+ });
401
+ },
402
+ };
403
+ }
404
+
405
+ /**
406
+ * Warn about a configured agent-role model the provider reports cannot call
407
+ * tools. Selection already refuses these, so reaching here means the config
408
+ * predates the check or was hand-edited; a run would otherwise fail later at
409
+ * dispatch admission with a message that names a missing tool rather than the
410
+ * model that cannot use any.
411
+ */
412
+ function agentRoleToolWarnings(providers: ProvidersContract, settings: Readonly<ClioSettings>): string[] {
413
+ const roles: ReadonlyArray<{ label: string; target: string | null; model: string | null }> = [
414
+ { label: "orchestrator", target: settings.orchestrator.target, model: settings.orchestrator.model },
415
+ { label: "workers.default", target: settings.workers.default.target, model: settings.workers.default.model },
416
+ ];
417
+ const warnings: string[] = [];
418
+ for (const role of roles) {
419
+ const targetId = role.target?.trim();
420
+ const wireModelId = role.model?.trim();
421
+ if (!targetId || !wireModelId) continue;
422
+ try {
423
+ const status = providers.list().find((entry) => entry.target.id === targetId);
424
+ if (!status) continue;
425
+ const capabilities = resolveModelCapabilities(status, wireModelId, providers.knowledgeBase);
426
+ if (supportsAgentRoleTools(capabilities)) continue;
427
+ warnings.push(
428
+ `${role.label} model '${wireModelId}' on target '${targetId}' ${AGENT_ROLE_TOOLS_REQUIRED_REASON}. ` +
429
+ `Pick another model, or state the correction in a model-catalog.d entry if the provider's flag is wrong.`,
430
+ );
431
+ } catch {
432
+ // An unresolvable capability is not evidence of a missing one.
433
+ }
434
+ }
435
+ return warnings;
436
+ }
437
+
438
+ /**
439
+ * True when the background role names the same model the orchestrator runs and
440
+ * that model reasons. Distinct models, or a non-reasoning shared model, are both
441
+ * fine; this only catches the one-model-does-everything configuration.
442
+ */
443
+ function backgroundSharesReasoningModelWithOrchestrator(
444
+ providers: ProvidersContract,
445
+ settings: Readonly<ClioSettings>,
446
+ ): boolean {
447
+ const backgroundModel = settings.background.model?.trim();
448
+ const orchestratorModel = settings.orchestrator.model?.trim();
449
+ if (!backgroundModel || backgroundModel !== orchestratorModel) return false;
450
+ if (settings.background.target?.trim() !== settings.orchestrator.target?.trim()) return false;
451
+ try {
452
+ const status = providers.list().find((entry) => entry.target.id === settings.background.target?.trim());
453
+ if (!status) return false;
454
+ return resolveModelCapabilities(status, backgroundModel, providers.knowledgeBase).reasoning === true;
455
+ } catch {
456
+ // An unresolvable capability is not evidence of a reasoning model; the
457
+ // operator's explicit configuration stands.
458
+ return false;
459
+ }
460
+ }
461
+
462
+ function synthesizeOrchestratorModel(
463
+ providers: ProvidersContract,
464
+ target: TargetDescriptor,
465
+ wireModelId: string,
466
+ ): EngineModel | null {
467
+ const runtime = providers.getRuntime(target.runtime);
468
+ if (!runtime) return null;
469
+ let model: EngineModel;
470
+ try {
471
+ const kbHit = providers.knowledgeBase?.lookup(wireModelId) ?? null;
472
+ model = runtime.synthesizeModel(target, wireModelId, kbHit);
473
+ } catch {
474
+ return null;
475
+ }
476
+ try {
477
+ const status = providers.list().find((entry) => entry.target.id === target.id);
478
+ if (status) {
479
+ const caps = resolveModelCapabilities(status, wireModelId, providers.knowledgeBase, {
480
+ detectedReasoning: providers.getDetectedReasoning(target.id, wireModelId),
481
+ });
482
+ applyModelCapabilityPatch(model, caps);
483
+ }
484
+ } catch {
485
+ // Older test doubles and degraded provider bundles may not expose live
486
+ // status. The synthesized model still carries runtime and catalog caps.
487
+ }
488
+ return model;
489
+ }
490
+
491
+ async function resolveCompactionModel(
492
+ settings: ClioSettings,
493
+ providers: ProvidersContract,
494
+ ): Promise<CompactionResolution | null> {
495
+ const targetId = settings.orchestrator?.target ?? null;
496
+ const wireModelId = settings.orchestrator?.model ?? null;
497
+ if (!targetId || !wireModelId) return null;
498
+ const target = resolveTarget(providers, targetId);
499
+ if (!target) return null;
500
+ const model = synthesizeOrchestratorModel(providers, target, wireModelId);
501
+ if (!model) return null;
502
+ const apiKey = await resolveApiKeyForTarget(target, providers);
503
+ const resolution: CompactionResolution = { model, targetId };
504
+ if (apiKey !== undefined) resolution.apiKey = apiKey;
505
+ return resolution;
506
+ }
507
+
508
+ function readSessionEntriesForCompact(sessionId: string): SessionEntry[] {
509
+ const reader = openSession(sessionId);
510
+ return collectSessionEntries(reader.turns(), sessionPaths(reader.meta()).current);
511
+ }
512
+
513
+ /**
514
+ * The finish-contract only inspects the window since the last user message
515
+ * (`recentEntries`, capped at 80), so it reads a bounded tail of the ledger
516
+ * instead of parsing the whole file every turn_end. 160 = twice the 80-entry cap
517
+ * leaves ample margin above any entries appended after the assistant turn, so the
518
+ * assessed window is byte-identical to the whole-file read (see the
519
+ * behaviour-equivalence contract test) while cost stays bounded by session
520
+ * *shape*, not session *length*.
521
+ */
522
+ const FINISH_CONTRACT_TAIL_ENTRIES = DEFAULT_RECENT_ENTRY_LIMIT * 2;
523
+
524
+ function readRecentSessionEntriesForContract(sessionId: string): SessionEntry[] {
525
+ return collectSessionEntries(
526
+ readSessionTailTurns(sessionId, FINISH_CONTRACT_TAIL_ENTRIES).entries,
527
+ sessionCurrentPath(sessionId),
528
+ );
529
+ }
530
+
531
+ function protectedArtifactStateForCurrentSession(
532
+ session: SessionContract,
533
+ ): ReturnType<typeof protectedArtifactStateFromSessionEntries> {
534
+ const meta = session.current();
535
+ if (!meta) return { artifacts: [] };
536
+ reconcilePendingProtectedArtifacts(session);
537
+ return protectedArtifactStateFromSessionEntries(readSessionEntriesForCompact(meta.id));
538
+ }
539
+
540
+ function appendProtectedArtifactRegistryEvent(
541
+ session: SessionContract | undefined,
542
+ event: ProtectedArtifactProtectEvent,
543
+ ): void {
544
+ const current = session?.current();
545
+ if (session === undefined || current === null || current === undefined) {
546
+ throw new Error("no active session is available for protected artifact persistence");
547
+ }
548
+ const pending = stagePendingProtectedArtifact(current.id, event);
549
+ session.appendEntry(
550
+ protectedArtifactEntryFromArtifact(event.artifact, {
551
+ parentTurnId: event.turnId ?? null,
552
+ toolName: event.toolName,
553
+ ...(event.toolCallId !== undefined ? { toolCallId: event.toolCallId } : {}),
554
+ ...(event.runId !== undefined ? { runId: event.runId } : {}),
555
+ ...(event.correlationId !== undefined ? { correlationId: event.correlationId } : {}),
556
+ }),
557
+ );
558
+ if (session.flushAppends === undefined) {
559
+ throw new Error("session does not expose the durable append flush required by protected artifact persistence");
560
+ }
561
+ session.flushAppends();
562
+ clearPendingProtectedArtifact(pending);
563
+ }
564
+
565
+ /**
566
+ * Fold a terminal dispatch payload's worker skill activations into the session
567
+ * ledger, tagged with the runId, so worker skill provenance sits next to
568
+ * main-agent activations.
569
+ *
570
+ * Both terminal channels carry them and the run finalizer emits exactly one of
571
+ * the two, so this cannot double-record. The orchestrator never observes worker
572
+ * tool calls directly, because a worker runs its own registry in its own
573
+ * subprocess, which makes this the only recording path there is. Returns how
574
+ * many were folded.
575
+ */
576
+ export function foldDispatchSkillActivations(
577
+ session: SessionContract | undefined,
578
+ payload: { runId?: unknown; skillActivations?: ReadonlyArray<unknown> } | undefined,
579
+ ): number {
580
+ const runId = payload?.runId;
581
+ if (typeof runId !== "string") return 0;
582
+ let folded = 0;
583
+ for (const activation of payload?.skillActivations ?? []) {
584
+ if (!isSkillActivation(activation)) continue;
585
+ appendSkillActivationRegistryEvent(session, { ...activation, runId });
586
+ folded += 1;
587
+ }
588
+ return folded;
589
+ }
590
+
591
+ function appendSkillActivationRegistryEvent(
592
+ session: SessionContract | undefined,
593
+ activation: Parameters<SessionContract["recordSkillActivation"]>[0],
594
+ ): void {
595
+ if (!session?.current()) return;
596
+ try {
597
+ session.recordSkillActivation(activation);
598
+ } catch {
599
+ // Activation metadata should never alter the result of a completed
600
+ // context(scope=skills) call. Missing ledger data is visible in diagnostics.
601
+ }
602
+ }
603
+
604
+ async function runCompactionFlow(
605
+ session: SessionContract,
606
+ settings: ClioSettings,
607
+ providers: ProvidersContract,
608
+ instructions?: string,
609
+ trigger?: CompactionTrigger,
610
+ ): Promise<CompactResult | null> {
611
+ const meta = session.current();
612
+ if (!meta) {
613
+ throw new Error("no current session to compact; start one with /new or /resume first");
614
+ }
615
+ const resolved = await resolveCompactionModel(settings, providers);
616
+ if (!resolved) {
617
+ throw new Error("no model configured; set orchestrator.target + orchestrator.model");
618
+ }
619
+ // Summarize only the active branch: after a /tree switch the raw file
620
+ // still holds abandoned sibling turns, and a summary that folds them in
621
+ // would persist abandoned content back into the active context. The full
622
+ // file read stays in place for the task board, protected artifacts, and
623
+ // the masking rewrite, which are session-global.
624
+ const activeLeafTurnId = session.tree(meta.id).leafId ?? undefined;
625
+ const entries = filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), activeLeafTurnId);
626
+ if (entries.length === 0) return null;
627
+
628
+ const result = await compact({
629
+ entries,
630
+ model: resolved.model,
631
+ ...(resolved.apiKey !== undefined ? { apiKey: resolved.apiKey } : {}),
632
+ ...(instructions !== undefined ? { instructions } : {}),
633
+ });
634
+ if (result.messagesSummarized === 0 || result.summary.length === 0) return null;
635
+
636
+ const entry: Omit<CompactionSummaryEntry, "turnId" | "timestamp"> = {
637
+ kind: "compactionSummary",
638
+ parentTurnId: result.firstKeptTurnId ?? null,
639
+ summary: result.summary,
640
+ tokensBefore: result.tokensBefore,
641
+ firstKeptTurnId: result.firstKeptTurnId ?? "",
642
+ messagesSummarized: result.messagesSummarized,
643
+ isSplitTurn: result.isSplitTurn,
644
+ tokensAfter: estimateTokensAfterCompaction(entries, result),
645
+ // The summarization call is a real model call. Persisting its provider
646
+ // usage on the entry is what puts it in front of `/cost` and `clio-coder usage
647
+ // report`, which folded the ledger and so counted every call but this one.
648
+ ...(result.usage !== undefined ? { usage: result.usage } : {}),
649
+ };
650
+ if (trigger !== undefined) entry.trigger = trigger;
651
+ session.appendEntry(entry);
652
+ return result;
653
+ }
654
+
655
+ /**
656
+ * Compose the production chat-loop compaction callback. Errors intentionally
657
+ * propagate: the chat loop owns activity failure reporting and distinguishes
658
+ * a thrown read/model/persistence failure from the legitimate null no-op that
659
+ * `runCompactionFlow` returns for an empty session or an unavailable cut.
660
+ */
661
+ export function createProductionAutoCompact(
662
+ session: SessionContract,
663
+ getSettings: () => ClioSettings,
664
+ providers: ProvidersContract,
665
+ ): (instructions?: string, trigger?: CompactionTrigger) => Promise<CompactResult | null> {
666
+ return (instructions, trigger) => runCompactionFlow(session, getSettings(), providers, instructions, trigger);
667
+ }
668
+
669
+ function estimateTokensFromSummary(summary: string): number {
670
+ // Mirrors the rough byte/4 heuristic the rest of the compaction stack
671
+ // uses for unmeasured payloads. Kept inline because this is the only
672
+ // caller; pi-mono's token estimator is provider-specific and we do not
673
+ // have a model handle at the persistence layer.
674
+ return Math.max(1, ceilChars(summary.length));
675
+ }
676
+
677
+ function estimateTokensAfterCompaction(entries: ReadonlyArray<SessionEntry>, result: CompactResult): number {
678
+ const synthetic: CompactionSummaryEntry = {
679
+ kind: "compactionSummary",
680
+ turnId: "__pending_compaction__",
681
+ parentTurnId: result.firstKeptTurnId ?? null,
682
+ timestamp: new Date(0).toISOString(),
683
+ summary: result.summary,
684
+ tokensBefore: result.tokensBefore,
685
+ firstKeptTurnId: result.firstKeptTurnId ?? "",
686
+ messagesSummarized: result.messagesSummarized,
687
+ isSplitTurn: result.isSplitTurn,
688
+ tokensAfter: estimateTokensFromSummary(result.summary),
689
+ };
690
+ const messages = buildReplayAgentMessagesFromTurns([...entries, synthetic]);
691
+ const tokens = estimateAgentContextTokens({ messages });
692
+ return tokens > 0 ? tokens : estimateTokensFromSummary(result.summary);
693
+ }
694
+
695
+ /**
696
+ * Alt+J / Alt+K step the orchestrator through the `scope` list of target
697
+ * ids or target/model refs. Absent scope is a no-op so unconfigured users
698
+ * feel nothing.
699
+ */
700
+ export function advanceScopedTarget(
701
+ settings: Readonly<ClioSettings>,
702
+ direction: "forward" | "backward",
703
+ ): { target: string; model: string | null } | null {
704
+ const scope = settings.scope ?? [];
705
+ if (scope.length === 0) return null;
706
+ const registry = getRuntimeRegistry();
707
+ if (registry.list().length === 0) registerBuiltinRuntimes(registry);
708
+ const filteredScope = scope.filter((entry) => {
709
+ const [targetId] = entry.split("/");
710
+ const target = settings.targets.find((e) => e.id === targetId);
711
+ if (!target) return false;
712
+ const runtime = registry.get(target.runtime);
713
+ return runtime !== null && isOrchestratorEligibleRuntime(runtime);
714
+ });
715
+ if (filteredScope.length === 0) return null;
716
+ const activeTarget = settings.orchestrator.target ?? "";
717
+ const activeModel = settings.orchestrator.model ?? "";
718
+ const activeCombinedRef = activeTarget.length > 0 && activeModel.length > 0 ? `${activeTarget}/${activeModel}` : "";
719
+ const idx = filteredScope.findIndex((entry) => entry === activeCombinedRef || entry === activeTarget);
720
+ const base = idx === -1 ? 0 : idx + (direction === "forward" ? 1 : filteredScope.length - 1);
721
+ const next = filteredScope[base % filteredScope.length];
722
+ if (!next) return null;
723
+ const [targetId, ...modelParts] = next.split("/");
724
+ if (!targetId) return null;
725
+ if (modelParts.length > 0) {
726
+ return { target: targetId, model: modelParts.join("/") };
727
+ }
728
+ if (activeTarget === targetId) {
729
+ return { target: targetId, model: activeModel || null };
730
+ }
731
+ const descriptor = settings.targets.find((entry) => entry.id === targetId);
732
+ return { target: targetId, model: descriptor?.defaultModel ?? null };
733
+ }
734
+
735
+ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootResult> {
736
+ const timer = new StartupTimer();
737
+ const bus = getSharedBus();
738
+ const termination = getTerminationCoordinator();
739
+ installBusTracer();
740
+ termination.installSignalHandlers();
741
+
742
+ ensureClioState();
743
+ timer.mark("install check");
744
+
745
+ // A hard-killed coordinator cannot run domain drains. Reconcile its compete
746
+ // process leases before dispatch startup scans the ledger, so abandoned rows
747
+ // observe dead workers and no stale worktree can be reused by this boot.
748
+ if (isGitRepository(process.cwd())) {
749
+ try {
750
+ const pendingGate = readPendingGateDecisions();
751
+ const pendingCompeteGroups = new Set<string>();
752
+ for (const handle of pendingGate.records) {
753
+ if (handle.record.kind === "output" && handle.record.topology === "compete") {
754
+ pendingCompeteGroups.add(handle.record.group);
755
+ } else if (handle.record.kind === "decision" && handle.record.decision.topology === "compete") {
756
+ pendingCompeteGroups.add(handle.record.decision.group);
757
+ }
758
+ }
759
+ const durableDecisions = readGateDecisionArtifacts();
760
+ const confirmedGroups = new Set(
761
+ durableDecisions
762
+ .filter(
763
+ ({ artifact }) =>
764
+ artifact.topology === "compete" &&
765
+ (artifact.outcome === "operator-confirmed" || artifact.outcome === "full-auto-applied"),
766
+ )
767
+ .map(({ artifact }) => artifact.group),
768
+ );
769
+ for (const { artifact } of durableDecisions) {
770
+ if (artifact.topology === "compete" && artifact.outcome === "winner" && !confirmedGroups.has(artifact.group)) {
771
+ pendingCompeteGroups.add(artifact.group);
772
+ }
773
+ }
774
+ const recovery = recoverCleanupReadyCompeteGroups(process.cwd(), {
775
+ preserveActiveGroups: pendingCompeteGroups,
776
+ preserveAllActive: pendingGate.errors.length > 0,
777
+ });
778
+ for (const failure of recovery.failed) {
779
+ process.stderr.write(`[dispatch] compete recovery preserved ${failure.group}: ${failure.message}\n`);
780
+ }
781
+ } catch (err) {
782
+ process.stderr.write(
783
+ `[dispatch] compete recovery failed closed: ${err instanceof Error ? err.message : String(err)}\n`,
784
+ );
785
+ }
786
+ }
787
+
788
+ let effectiveSettingsForDispatch: (() => Readonly<ClioSettings>) | null = null;
789
+ let protectedArtifactStateForDispatch: (() => ProtectedArtifactState) | null = null;
790
+
791
+ const result = await loadDomains([
792
+ ConfigDomainModule,
793
+ ExtensionsDomainModule,
794
+ InteropDomainModule,
795
+ createResourcesDomainModule({
796
+ skills: () => ({
797
+ disableDiscovery: options.noSkills === true || options.headless?.noSkills === true,
798
+ ...(options.skillPaths && options.skillPaths.length > 0
799
+ ? { explicitSkillPaths: options.skillPaths }
800
+ : options.headless?.skillPaths && options.headless.skillPaths.length > 0
801
+ ? { explicitSkillPaths: options.headless.skillPaths }
802
+ : {}),
803
+ }),
804
+ }),
805
+ ShareDomainModule,
806
+ createContextDomainModule({ noContextFiles: options.noContextFiles === true }),
807
+ ProvidersDomainModule,
808
+ SafetyDomainModule,
809
+ createPromptsDomainModule({
810
+ noContextFiles: options.noContextFiles === true,
811
+ }),
812
+ AgentsDomainModule,
813
+ MiddlewareDomainModule,
814
+ SessionDomainModule,
815
+ ObservabilityDomainModule,
816
+ SchedulingDomainModule,
817
+ // Dispatch resolves worker targets through the session's effective
818
+ // settings view once it exists (assigned below, after the config
819
+ // contract loads); until then it falls back to the shared snapshot.
820
+ createDispatchDomainModule({
821
+ getSettings: () => effectiveSettingsForDispatch?.(),
822
+ getProtectedArtifactState: () => protectedArtifactStateForDispatch?.() ?? { artifacts: [] },
823
+ autonomyOverride: options.headless?.autonomy !== undefined,
824
+ }),
825
+ LifecycleDomainModule,
826
+ ]);
827
+ timer.mark(`domains loaded (${result.loaded.length})`);
828
+
829
+ const dispatch = result.getContract<DispatchContract>("dispatch");
830
+ if (dispatch) {
831
+ termination.onDrain(async () => {
832
+ await dispatch.drain();
833
+ });
834
+ }
835
+ termination.onPersist(async () => {
836
+ await result.stop();
837
+ });
838
+
839
+ bus.emit(BusChannels.SessionStart, { at: Date.now() });
840
+ timer.mark("session_start fired");
841
+
842
+ const acpMode = options.acp !== undefined;
843
+ const interactive = !options.headless && !acpMode && process.env.CLIO_CODER_INTERACTIVE === "1";
844
+ if (!interactive && !options.headless && !acpMode) {
845
+ process.stdout.write(buildBanner());
846
+ if (process.env.CLIO_CODER_TIMING === "1") process.stdout.write(`${timer.report()}\n`);
847
+ }
848
+
849
+ const config = result.getContract<ConfigContract>("config");
850
+ const providers = result.getContract<ProvidersContract>("providers");
851
+ timer.mark("providers resolved");
852
+
853
+ if (options.apiKey) {
854
+ if (!providers) {
855
+ process.stderr.write("Clio Coder: --api-key supplied but providers domain unavailable; ignoring.\n");
856
+ } else {
857
+ const settingsNow = applyHeadlessSettingsOverlay(config?.get() ?? readSettings(), options.headless);
858
+ const activeTargetId = settingsNow.orchestrator?.target;
859
+ const target = resolveTarget(providers, activeTargetId);
860
+ const runtime = target ? providers.getRuntime(target.runtime) : null;
861
+ if (target && runtime) {
862
+ providers.auth.setRuntimeOverrideForTarget(target, runtime, options.apiKey);
863
+ } else {
864
+ process.stderr.write("Clio Coder: --api-key supplied but no active orchestrator target is configured; ignoring.\n");
865
+ }
866
+ }
867
+ }
868
+
869
+ if (!interactive && !options.headless && !acpMode) {
870
+ process.stdout.write(`${chalk.dim(" (non-interactive boot. pass CLIO_CODER_INTERACTIVE=1 to launch the TUI.)")}\n`);
871
+ await termination.shutdown(0);
872
+ return { exitCode: 0, bootTimeMs: timer.snapshot().totalMs };
873
+ }
874
+
875
+ const middleware = result.getContract<MiddlewareContract>("middleware");
876
+ const observability = result.getContract<ObservabilityContract>("observability");
877
+ const safety = result.getContract<SafetyContract>("safety");
878
+ const session = result.getContract<SessionContract>("session");
879
+ const prompts = result.getContract<PromptsContract>("prompts");
880
+ const agents = result.getContract<AgentsContract>("agents");
881
+ const resources = result.getContract<ResourcesContract>("resources");
882
+ const extensions = result.getContract<ExtensionsContract>("extensions");
883
+ const share = result.getContract<ShareContract>("share");
884
+ const contextDomain = result.getContract<ContextContract>("context");
885
+ const interop = result.getContract<InteropContract>("interop");
886
+ // Boot detection resolves paths only: no `--version` subprocess and no skill
887
+ // walk on the boot path. The hint is gated on `interactive`, which is already
888
+ // false under headless and ACP.
889
+ const interopReport = interactive && interop ? await interop.detect({ cwd: process.cwd() }) : null;
890
+ const initialNotices = interactive ? [...(contextDomain?.startupHints() ?? [])] : [];
891
+ // Once per version, interactive only: headless and ACP have no operator at
892
+ // the keyboard to tell, and the record is left unclaimed for the boot that does.
893
+ const upgrade = interactive ? takeUpgradeNotice() : null;
894
+ if (upgrade !== null) initialNotices.push(describeUpgradeNotice(upgrade));
895
+ if (interop && interopReport) {
896
+ const interopHint = interop.bootHint(interopReport);
897
+ if (interopHint !== null) initialNotices.push(interopHint);
898
+ }
899
+ if (!providers || !dispatch || !observability || !safety || !middleware) {
900
+ process.stderr.write(
901
+ "Clio Coder: chat mode requires safety + middleware + providers + dispatch + observability contracts; aborting.\n",
902
+ );
903
+ await termination.shutdown(1);
904
+ return { exitCode: 1, bootTimeMs: timer.snapshot().totalMs };
905
+ }
906
+
907
+ // A headless `--session`/`--continue` resolves here, where the session
908
+ // domain exists. It is a hard requirement rather than a hint: a caller that
909
+ // asked to continue a conversation must not receive an answer written
910
+ // without that conversation's history.
911
+ const requestedResume = options.headless?.resumeSession;
912
+ let headlessResumeFailure: string | null = null;
913
+ let resolvedResumeId: string | undefined;
914
+ if (requestedResume !== undefined) {
915
+ if (!session) {
916
+ headlessResumeFailure = "session continuation requires the session domain, which is not loaded";
917
+ } else if (requestedResume.kind === "id") {
918
+ resolvedResumeId = requestedResume.id;
919
+ } else {
920
+ const latest = session.history()[0];
921
+ if (latest === undefined) headlessResumeFailure = `no previous session recorded for ${process.cwd()}`;
922
+ else resolvedResumeId = latest.id;
923
+ }
924
+ }
925
+ const resumeId = resolvedResumeId ?? process.env.CLIO_CODER_RESUME_SESSION_ID?.trim();
926
+ let resumedSessionAtBoot = false;
927
+ if (resumeId && session && headlessResumeFailure === null) {
928
+ try {
929
+ session.resume(resumeId);
930
+ resumedSessionAtBoot = true;
931
+ } catch (err) {
932
+ const detail = err instanceof Error ? err.message : String(err);
933
+ if (requestedResume !== undefined) headlessResumeFailure = `failed to resume session ${resumeId}: ${detail}`;
934
+ else process.stderr.write(`Clio Coder: failed to resume session ${resumeId}: ${detail}\n`);
935
+ }
936
+ }
937
+ Reflect.deleteProperty(process.env, "CLIO_CODER_RESUME_SESSION_ID");
938
+ if (headlessResumeFailure !== null) {
939
+ process.stderr.write(`clio-coder run: ${headlessResumeFailure}\n`);
940
+ await termination.shutdown(2);
941
+ return { exitCode: 2, bootTimeMs: timer.snapshot().totalMs };
942
+ }
943
+
944
+ // Hook diagnostics ride the typed bus. The domain loader constructed the
945
+ // bundle with the stderr default; swap in a sink that publishes
946
+ // middleware.hookFailed (the interactive warn notice consumes it) and keep
947
+ // stderr for non-interactive runs, which have no notice subscriber.
948
+ middleware.setDiagnosticSink((diagnostic) => {
949
+ bus.emit(BusChannels.MiddlewareHookFailed, {
950
+ kind: diagnostic.kind,
951
+ registrationId: diagnostic.registrationId,
952
+ hook: diagnostic.hook,
953
+ at: Date.now(),
954
+ ...(diagnostic.kind === "hook_failed"
955
+ ? { message: diagnostic.message }
956
+ : {
957
+ elapsedMs: diagnostic.elapsedMs,
958
+ budgetMs: diagnostic.budgetMs,
959
+ steadyStateWarn: diagnostic.steadyStateWarn,
960
+ p50Ms: diagnostic.stats.p50Ms,
961
+ p95Ms: diagnostic.stats.p95Ms,
962
+ overCount: diagnostic.stats.overCount,
963
+ windowSamples: diagnostic.stats.window,
964
+ }),
965
+ });
966
+ if (!interactive) writeMiddlewareDiagnosticToStderr(diagnostic);
967
+ });
968
+
969
+ // Residency and runtime notices (model swaps, double residency, VRAM
970
+ // stress) reach the operator through the interactive notice renderer; a
971
+ // headless run has no subscriber, so mirror them to stderr there. Silent
972
+ // model swaps are exactly the failure mode the residency policy forbids.
973
+ if (!interactive) {
974
+ bus.on(BusChannels.RuntimeNotice, (payload: unknown) => {
975
+ const notice = payload as { level?: string; message?: string } | undefined;
976
+ if (typeof notice?.message !== "string") return;
977
+ process.stderr.write(`[clio:runtime] ${notice.level ?? "info"}: ${notice.message}\n`);
978
+ });
979
+ }
980
+
981
+ // Guardrail policy (tool-call budgets, tool byte caps, dispatch ledger cap)
982
+ // resolves settings-first with env as emergency override; install the
983
+ // settings section before any guard registration or tool reads it.
984
+ configureGuardrails((config?.get() ?? readSettings()).guardrails);
985
+
986
+ // Guard registrations on the middleware contract, in order: loop guard,
987
+ // protected artifacts (last among guards so it absorbs protect_path effects
988
+ // from everything before it), dispatch dedup. Workers register their own
989
+ // loop guard and protected-artifacts instances inside their subprocess in
990
+ // worker-runtime.ts; the orchestrator instances carry the bus and the
991
+ // session persistence sink.
992
+ middleware.registerHook(
993
+ createLoopGuardRegistration({
994
+ safety,
995
+ bus,
996
+ turnBlockBudget: INTERACTIVE_LOOP_BLOCK_BUDGET,
997
+ turnToolCallBudget: readOrchTurnToolCallBudget(),
998
+ // Interactive/headless/ACP all share this orchestrator guard: at the
999
+ // block budget, lock tools for the rest of the turn so the model
1000
+ // answers from what it gathered instead of hard-cancelling a turn that
1001
+ // may already hold the answer. The bounded backstop still cancels a
1002
+ // model that keeps calling tools.
1003
+ turnSynthesisLockout: true,
1004
+ }),
1005
+ );
1006
+ let initialProtectedArtifactState: ProtectedArtifactState | undefined;
1007
+ let initialProtectionReadError: string | null = null;
1008
+ if (session) {
1009
+ try {
1010
+ initialProtectedArtifactState = protectedArtifactStateForCurrentSession(session);
1011
+ } catch (error) {
1012
+ initialProtectionReadError = error instanceof Error ? error.message : String(error);
1013
+ }
1014
+ }
1015
+ const protectedArtifactsGuard = createProtectedArtifactsRegistration({
1016
+ ...(initialProtectedArtifactState !== undefined ? { initialState: initialProtectedArtifactState } : {}),
1017
+ onProtect: (event) => appendProtectedArtifactRegistryEvent(session, event),
1018
+ onDurabilityFailure: (health) => {
1019
+ bus.emit(BusChannels.MiddlewareHookFailed, {
1020
+ kind: "hook_failed",
1021
+ registrationId: "guard.protected-artifacts",
1022
+ hook: "before_tool",
1023
+ at: Date.now(),
1024
+ message: health.reason,
1025
+ });
1026
+ },
1027
+ });
1028
+ if (initialProtectionReadError !== null) {
1029
+ protectedArtifactsGuard.markDegraded(
1030
+ `initial session protection history could not be read: ${initialProtectionReadError}`,
1031
+ );
1032
+ }
1033
+ protectedArtifactStateForDispatch = () => {
1034
+ const health = protectedArtifactsGuard.health();
1035
+ if (health.kind === "degraded") {
1036
+ throw new Error(`dispatch: protected artifact durability degraded: ${health.reason}`);
1037
+ }
1038
+ return protectedArtifactsGuard.state();
1039
+ };
1040
+ middleware.registerHook(protectedArtifactsGuard);
1041
+ middleware.registerHook(createDispatchDedupRegistration());
1042
+ // Observers run after the guards; they emit no effects and their sinks are
1043
+ // best-effort (session ledger, codewiki refresh).
1044
+ middleware.registerHook(
1045
+ createSkillActivationObserver((activation) => appendSkillActivationRegistryEvent(session, activation)),
1046
+ );
1047
+ // First-turn skills reminder: user-message-visible text is the one channel
1048
+ // the battery-tested local models act on; suggestion protocol only, the
1049
+ // operator load gate is untouched.
1050
+ if (resources) {
1051
+ middleware.registerHook(
1052
+ createSkillsReminderRegistration({
1053
+ countModelVisibleSkills: () => modelVisibleSkills(resources.skills(process.cwd()).items).length,
1054
+ }),
1055
+ );
1056
+ }
1057
+ // Task-board reminder: same user-message-visible channel, fired once per
1058
+ // session when a request literally enumerates three or more steps. The
1059
+ // static routing line and tasks hint ask for the same board; battery-tested
1060
+ // local models only comply when the instruction rides the user message.
1061
+ middleware.registerHook(createTaskBoardReminderRegistration());
1062
+ const taskMemoryBank = new TaskMemoryBank();
1063
+ let taskMemorySessionId = session?.current()?.id ?? null;
1064
+ const ensureTaskMemorySession = (): void => {
1065
+ const currentSessionId = session?.current()?.id ?? null;
1066
+ if (currentSessionId === taskMemorySessionId) return;
1067
+ taskMemoryBank.clear();
1068
+ taskMemorySessionId = currentSessionId;
1069
+ };
1070
+ const memorySettings = (config?.get() ?? readSettings()).memory.intervention;
1071
+ // Bound late: the registration is built here, but the buffer a deferred
1072
+ // reminder lands in belongs to the chat loop that has not been composed yet.
1073
+ let deferredMemoryReminderSink: ((message: string) => void) | null = null;
1074
+ // Content-bearing, so it exists only when the operator named a file. The
1075
+ // telemetry row says which silence happened; this says what the model wrote.
1076
+ const memoryTracePath = taskMemoryTracePath();
1077
+ const memoryTrace = memoryTracePath === null ? null : createTaskMemoryTrace(memoryTracePath);
1078
+ const memoryIntervention = createMemoryInterventionRegistration({
1079
+ bank: taskMemoryBank,
1080
+ telemetry: createTaskMemoryTelemetrySink(),
1081
+ ...(memoryTrace === null ? {} : { onEnvelope: (envelope) => memoryTrace.record(envelope) }),
1082
+ // A headless run submits no further turn, so a detached step could only
1083
+ // finish after the process that would have read it has exited.
1084
+ deliversDeferredReminders: options.headless === undefined,
1085
+ onDeferredReminder: (message) => deferredMemoryReminderSink?.(message),
1086
+ getSettings: () => {
1087
+ ensureTaskMemorySession();
1088
+ return effectiveSettingsForDispatch?.().memory.intervention ?? memorySettings;
1089
+ },
1090
+ getModelClient: () => {
1091
+ const settings = effectiveSettingsForDispatch?.();
1092
+ return settings === undefined
1093
+ ? null
1094
+ : createBackgroundMemoryModelClient(providers, settings, settings.memory.intervention.timeoutMs);
1095
+ },
1096
+ });
1097
+ middleware.registerHook(memoryIntervention);
1098
+ const unsubscribeMemoryLoop = bus.on(BusChannels.LoopBlocked, () => memoryIntervention.signalLoop());
1099
+ termination.onDrain(() => unsubscribeMemoryLoop());
1100
+ if (contextDomain) {
1101
+ middleware.registerHook(
1102
+ createFileMutationObserver(coalescePathSink((paths) => contextDomain.noteFileChanges(paths))),
1103
+ );
1104
+ }
1105
+ // User-defined hooks: extensions and the project (.clio-coder/hooks.yaml,
1106
+ // .clio-coder/hooks.local.yaml) declare a conservative, receipted hook set on the
1107
+ // same effect machinery. They register after the guards, so safety stays
1108
+ // authoritative: a hook may add effects (including request block_tool) but
1109
+ // cannot grant a permission safety would deny. Loading is best-effort.
1110
+ const hookReceiptLog = createHookReceiptLog({ persistPath: join(clioStateDir(), "hook-receipts.json") });
1111
+ const extensionHookRoots: ExtensionHookRoot[] = (extensions?.list(process.cwd()) ?? [])
1112
+ .filter((ext) => ext.enabled && ext.effective)
1113
+ .map((ext) => ({ id: ext.id, rootPath: ext.rootPath }));
1114
+ const userHooks = installUserHooks({
1115
+ cwd: process.cwd(),
1116
+ extensions: extensionHookRoots,
1117
+ registerHook: (registration) => middleware.registerHook(registration),
1118
+ recordReceipt: (receipt) => hookReceiptLog.record(receipt),
1119
+ });
1120
+ if (!interactive) {
1121
+ for (const issue of userHooks.fileIssues) {
1122
+ process.stderr.write(`[clio:hooks] ${issue.message}\n`);
1123
+ }
1124
+ for (const issue of userHooks.issues) {
1125
+ process.stderr.write(`[clio:hooks] ${issue.source.sourcePath}#${issue.index}: ${issue.issues.join("; ")}\n`);
1126
+ }
1127
+ }
1128
+ termination.onDrain(() => hookReceiptLog.flush());
1129
+ // Autonomy is hot-reloaded for interactive and headless admissions. ACP
1130
+ // server prompts use the snapshot captured at session/new.
1131
+ let activeAcpSessionAutonomy: AutonomyLevel | null = null;
1132
+ // The one effective-autonomy resolution. Every admission surface (registry
1133
+ // admission, dispatch plan provenance, ACP session snapshot) resolves
1134
+ // through these two functions so a fallback added to one surface cannot
1135
+ // silently skip another.
1136
+ const resolveBaselineAutonomy = (): AutonomyLevel =>
1137
+ effectiveSettingsForDispatch?.().autonomy ??
1138
+ options.headless?.autonomy ??
1139
+ (config?.get() ?? readSettings()).autonomy ??
1140
+ "auto-edit";
1141
+ const resolveEffectiveAutonomy = (): AutonomyLevel => activeAcpSessionAutonomy ?? resolveBaselineAutonomy();
1142
+ const middlewareToolChoice = createMiddlewareToolChoiceControl();
1143
+ const toolRegistry = createRegistry({
1144
+ safety,
1145
+ middleware,
1146
+ onMiddlewareEffects: (effects) => middlewareToolChoice.apply(effects),
1147
+ autonomy: resolveEffectiveAutonomy,
1148
+ });
1149
+ const mainPermissionOrigin = acpMode ? "acp-server" : "main";
1150
+ toolRegistry.onPermissionRequired((call, decision, meta) => {
1151
+ bus.emit(BusChannels.PermissionRequested, {
1152
+ tool: call.tool,
1153
+ actionClass: decision.classification.actionClass,
1154
+ requestId: meta.requestId,
1155
+ origin: mainPermissionOrigin,
1156
+ axis: meta.axis,
1157
+ ...(decision.kind === "ask" ? { rejection: decision.rejection } : {}),
1158
+ ...(decision.policy?.ruleId !== undefined ? { ruleId: decision.policy.ruleId } : {}),
1159
+ ...(decision.policy?.policySource !== undefined ? { policySource: decision.policy.policySource } : {}),
1160
+ ...(decision.policy?.reasonCode !== undefined ? { reasonCode: decision.policy.reasonCode } : {}),
1161
+ });
1162
+ });
1163
+ let askUserHandler: AskUserHandler | null = null;
1164
+ // ask_user is interactive-only by design: it is a human interview tool and
1165
+ // headless/ACP surfaces have no operator to interview, so the tool is not
1166
+ // registered there at all (documented in `clio-coder run --help`). Skills that
1167
+ // interview fall back to their stated defaults when the tool is absent.
1168
+ const askUserBridge: AskUserHandler = async (questions, invokeOptions) =>
1169
+ askUserHandler ? await askUserHandler(questions, invokeOptions) : cancelledAskUserResult();
1170
+ // One task board per orchestrator: the tasks tool mutates it, the turn-end
1171
+ // open-tasks nudge reads it, and the footer/overlay render it. Keyed on the
1172
+ // current session id so resume/fork/new refolds it from taskLedger entries.
1173
+ const taskBoard = createTaskBoardStore({
1174
+ getSessionId: () => session?.current()?.id ?? null,
1175
+ readEntries: () => {
1176
+ const meta = session?.current();
1177
+ return meta ? readSessionEntriesForCompact(meta.id) : [];
1178
+ },
1179
+ appendEntry: (entry) => {
1180
+ session?.appendEntry(entry);
1181
+ },
1182
+ });
1183
+ // Link in-flight dispatch runs to the live board via the ledger's
1184
+ // activeRunIds field: a run is tracked from the moment its child process is
1185
+ // live until it finalizes either way. attach/detach are no-ops when no board
1186
+ // is declared, so an ambient dispatch never forces a board into existence.
1187
+ bus.on(BusChannels.DispatchStarted, (payload) => {
1188
+ if (typeof payload?.runId === "string") taskBoard.attachRun(payload.runId);
1189
+ });
1190
+ bus.on(BusChannels.DispatchCompleted, (payload) => {
1191
+ if (typeof payload?.runId !== "string") return;
1192
+ taskBoard.detachRun(payload.runId);
1193
+ foldDispatchSkillActivations(session, payload);
1194
+ });
1195
+ bus.on(BusChannels.DispatchFailed, (payload) => {
1196
+ if (typeof payload?.runId !== "string") return;
1197
+ taskBoard.detachRun(payload.runId);
1198
+ // A run that failed after loading a skill is the case the operator most
1199
+ // needs the provenance for, and the receipt already carried it here.
1200
+ foldDispatchSkillActivations(session, payload);
1201
+ });
1202
+ // Operator-initiated backgrounding is a TUI affordance: the registry is the
1203
+ // one object the dispatch tool and the keypress both hold.
1204
+ const dispatchBackground = createDispatchBackgroundRegistry();
1205
+ registerAllTools(toolRegistry, {
1206
+ ...(session ? { session } : {}),
1207
+ taskBoard,
1208
+ dispatch,
1209
+ bus,
1210
+ ...(interactive ? { askUser: askUserBridge } : {}),
1211
+ ...(agents ? { getAgentCatalog: () => renderAgentCatalogSectionsFromSpecs(agents.listSpecs()).stable } : {}),
1212
+ ...(agents ? { getAgentSpecs: () => agents.listSpecs() } : {}),
1213
+ ...(agents ? { getAgentRoleFacts: agentRoleFactsResolver((id: string) => agents.getSpec(id)) } : {}),
1214
+ // Same effective-autonomy resolution the registry admission uses, so plan
1215
+ // provenance and compete winner handling agree with the approval surface.
1216
+ getAutonomy: resolveEffectiveAutonomy,
1217
+ ...(interactive ? { dispatchBackground } : {}),
1218
+ getCostCeilingUsd: () => result.getContract<SchedulingContract>("scheduling")?.ceilingUsd() ?? 0,
1219
+ getSkillLoaderOptions: () => ({
1220
+ trustProjectCompatRoots: config?.get().skills.trustProjectCompatRoots === true,
1221
+ disableDiscovery: options.noSkills === true || options.headless?.noSkills === true,
1222
+ ...(options.skillPaths && options.skillPaths.length > 0
1223
+ ? { explicitSkillPaths: options.skillPaths }
1224
+ : options.headless?.skillPaths && options.headless.skillPaths.length > 0
1225
+ ? { explicitSkillPaths: options.headless.skillPaths }
1226
+ : {}),
1227
+ }),
1228
+ });
1229
+
1230
+ // Live routing is owned by this process. Seed it once from saved settings
1231
+ // (with any headless CLI overrides baked in); from here on every consumer
1232
+ // reads the effective view — shared snapshot + session routing overlay — so
1233
+ // another process writing settings.yaml can update defaults and the
1234
+ // target catalog but never redirect this session's routing.
1235
+ const sessionRouting = seedSessionRouting(
1236
+ applyHeadlessSettingsOverlay(config?.get() ?? readSettings(), options.headless),
1237
+ );
1238
+ // Non-routing settings a session changed "for this session only" via the
1239
+ // /settings overlay. Layered under the routing overlay in the effective
1240
+ // view, so the live session reflects them immediately while settings.yaml
1241
+ // (the global default for new sessions) stays untouched until the operator
1242
+ // chooses to save globally.
1243
+ const sessionOverrides: SessionOverrides = new Map(
1244
+ options.headless?.autonomy === undefined ? [] : [["autonomy", options.headless.autonomy]],
1245
+ );
1246
+ // The effective view is derived by deep-cloning the saved snapshot, and it
1247
+ // is read on the tool-admission hot path (every call resolves autonomy
1248
+ // through it). Memoize on the two things it depends on: the config
1249
+ // domain's snapshot identity (swapped wholesale on every update and
1250
+ // external hot reload) and a generation counter bumped by every session
1251
+ // routing/override mutation below. Without this, one tool call cost a full
1252
+ // settings structuredClone.
1253
+ let sessionStateGeneration = 0;
1254
+ let cachedSettingsBase: Readonly<ClioSettings> | null = null;
1255
+ let cachedSettingsGeneration = -1;
1256
+ let cachedSettingsView: ClioSettings | null = null;
1257
+ const bumpSessionState = (): void => {
1258
+ sessionStateGeneration += 1;
1259
+ };
1260
+ const getCurrentSettings = (): ClioSettings => {
1261
+ // Recents live in the data dir (core/recent-models.ts), never in
1262
+ // settings.yaml; consumers that need them call listRecentModels
1263
+ // directly, so an Alt+L pick in another session does not churn the
1264
+ // config watcher here.
1265
+ const base = config?.get();
1266
+ // No config domain (unit tests, degraded boot): readSettings() returns a
1267
+ // fresh object every call, so there is nothing stable to key a cache on.
1268
+ if (base === undefined) return applySessionRouting(applyOverrides(readSettings(), sessionOverrides), sessionRouting);
1269
+ if (
1270
+ cachedSettingsView !== null &&
1271
+ cachedSettingsBase === base &&
1272
+ cachedSettingsGeneration === sessionStateGeneration
1273
+ ) {
1274
+ return cachedSettingsView;
1275
+ }
1276
+ const view = applySessionRouting(applyOverrides(base, sessionOverrides), sessionRouting);
1277
+ cachedSettingsBase = base;
1278
+ cachedSettingsGeneration = sessionStateGeneration;
1279
+ cachedSettingsView = view;
1280
+ return view;
1281
+ };
1282
+ effectiveSettingsForDispatch = getCurrentSettings;
1283
+ const getTaskMemorySeedOffer = (): { source: string; count: number } | null => {
1284
+ return taskMemoryHandoffSeedOffer(process.cwd(), getCurrentSettings().memory.intervention.enabled);
1285
+ };
1286
+ const seedCurrentTaskMemoryFromHandoff = () => {
1287
+ ensureTaskMemorySession();
1288
+ return seedTaskMemoryFromNewestHandoff(
1289
+ taskMemoryBank,
1290
+ process.cwd(),
1291
+ getCurrentSettings().memory.intervention.enabled,
1292
+ );
1293
+ };
1294
+ if (resumedSessionAtBoot) {
1295
+ const offer = getTaskMemorySeedOffer();
1296
+ if (offer && offer.count > 0) {
1297
+ initialNotices.push(
1298
+ `task memory: ${offer.count} handoff entr${offer.count === 1 ? "y" : "ies"} available from ${offer.source}; run /memory seed to import`,
1299
+ );
1300
+ }
1301
+ }
1302
+ for (const warning of agentRoleToolWarnings(providers, getCurrentSettings())) {
1303
+ if (interactive) initialNotices.push(warning);
1304
+ else process.stderr.write(`${warning}\n`);
1305
+ }
1306
+ // Residency protection follows the live effective settings: the models the
1307
+ // operator's config references (orchestrator, worker default/profiles,
1308
+ // target defaults) may never be evicted by another Clio stream, and a
1309
+ // routing change updates the set on the next read.
1310
+ setProtectedModelsProvider(() => protectedResidencyModels(getCurrentSettings()));
1311
+
1312
+ const validatedKeybindings = validateKeybindings((config?.get() ?? readSettings()).keybindings ?? {});
1313
+ const invalidBindings = validatedKeybindings.invalid;
1314
+ if (invalidBindings.length > 0) {
1315
+ const notice = formatInvalidKeybindingNotice(invalidBindings);
1316
+ if (interactive) initialNotices.push(notice);
1317
+ else process.stderr.write(notice);
1318
+ }
1319
+ const platformWarnings = process.stdin.isTTY
1320
+ ? detectPlatformKeybindingWarnings(validatedKeybindings.valid, detectTerminalKeySupport(process.env))
1321
+ : [];
1322
+ if (platformWarnings.length > 0) {
1323
+ const notice = formatPlatformKeybindingNotice(platformWarnings);
1324
+ if (interactive) initialNotices.push(notice);
1325
+ else process.stderr.write(notice);
1326
+ }
1327
+ /**
1328
+ * Locked read-modify-write of saved settings. Routes through the config
1329
+ * contract (which refreshes its snapshot and dispatches change events) when
1330
+ * available, else straight through core updateSettings. Either way the
1331
+ * mutator runs against the freshest on-disk state under the advisory
1332
+ * settings lock, so two processes saving defaults at the same time cannot
1333
+ * interleave and drop each other's patches.
1334
+ */
1335
+ const persistSavedMutation = (mutator: SettingsMutator): void => {
1336
+ if (config?.update) {
1337
+ config.update(mutator);
1338
+ return;
1339
+ }
1340
+ updateSettings(mutator);
1341
+ };
1342
+ /**
1343
+ * Apply a routing change with one consistent scope: it takes effect in this
1344
+ * session immediately and writes through to saved settings as the default
1345
+ * for future sessions. Only the patched fields hit the file, so concurrent
1346
+ * sessions cannot clobber each other's saved defaults wholesale.
1347
+ */
1348
+ const updateSessionRouting = (patch: RoutingPatch, mutateSaved?: (saved: ClioSettings) => void): void => {
1349
+ applyRoutingPatch(sessionRouting, patch);
1350
+ bumpSessionState();
1351
+ persistSavedMutation((saved) => {
1352
+ mergeRoutingPatchIntoSettings(saved, patch);
1353
+ mutateSaved?.(saved);
1354
+ });
1355
+ };
1356
+ /**
1357
+ * Persist a whole-settings blob coming from the effective view (the
1358
+ * /settings overlay, favorites toggles). Routing edits in the blob are
1359
+ * absorbed into the session state and written through; everything else is
1360
+ * persisted without leaking this session's routing into the saved defaults.
1361
+ */
1362
+ const applySettingsBlob = (next: ClioSettings): void => {
1363
+ const patch = diffRouting(getCurrentSettings(), next);
1364
+ if (patch) {
1365
+ applyRoutingPatch(sessionRouting, patch);
1366
+ bumpSessionState();
1367
+ }
1368
+ persistSavedMutation((fresh) => {
1369
+ const persisted = structuredClone(next);
1370
+ restoreRoutingFields(persisted, fresh);
1371
+ // A whole-blob write (providers, favorites) must not globalize a
1372
+ // session-only override: restore every overridden leaf from the
1373
+ // fresh file so it stays session-local until explicitly saved.
1374
+ for (const path of sessionOverrides.keys()) setAtPath(persisted, path, getAtPath(fresh, path));
1375
+ if (patch) mergeRoutingPatchIntoSettings(persisted, patch);
1376
+ return persisted;
1377
+ });
1378
+ };
1379
+ /**
1380
+ * Commit a single /settings edit, keyed by its config-path id. `next` is the
1381
+ * effective view with the one leaf already changed.
1382
+ * - scope "session": apply live only. Routing ids feed the routing state;
1383
+ * every other id becomes a session override. settings.yaml is untouched.
1384
+ * - scope "global": apply live and persist just that leaf as the new
1385
+ * default, clearing any prior session override for it.
1386
+ * Restart-required ids (budget.concurrency, runtimePlugins) cannot apply
1387
+ * live, so the overlay only offers "global" for them; the file write is what
1388
+ * a later restart picks up.
1389
+ */
1390
+ const commitSetting = (id: string, next: ClioSettings, scope: "session" | "global"): void => {
1391
+ if (isRoutingPath(id)) {
1392
+ // Build the patch from `next` keyed by the edited id, not by diffing
1393
+ // against the live view: a prior session-only apply already moved the
1394
+ // routing state, so a diff would be empty and the global save would
1395
+ // silently no-op. Only the touched fields are persisted, so concurrent
1396
+ // sessions never clobber each other's saved routing.
1397
+ const patch = routingPatchForId(id, next);
1398
+ if (!patch) return;
1399
+ applyRoutingPatch(sessionRouting, patch);
1400
+ bumpSessionState();
1401
+ if (scope === "global") persistSavedMutation((saved) => mergeRoutingPatchIntoSettings(saved, patch));
1402
+ return;
1403
+ }
1404
+ const value = getAtPath(next, id);
1405
+ if (scope === "session") {
1406
+ sessionOverrides.set(id, value);
1407
+ bumpSessionState();
1408
+ return;
1409
+ }
1410
+ sessionOverrides.delete(id);
1411
+ bumpSessionState();
1412
+ persistSavedMutation((saved) => setAtPath(saved, id, value));
1413
+ };
1414
+ /** Alt+J / Alt+K: step this session's orchestrator through the scope list. */
1415
+ const cycleScopedSession = (direction: "forward" | "backward"): boolean => {
1416
+ const next = advanceScopedTarget(getCurrentSettings(), direction);
1417
+ if (!next) return false;
1418
+ updateSessionRouting({ orchestrator: { target: next.target, model: next.model } });
1419
+ return true;
1420
+ };
1421
+
1422
+ const readCurrentSessionEntries = (): ReadonlyArray<SessionEntry> => {
1423
+ if (session === undefined) return [];
1424
+ const meta = session.current();
1425
+ if (!meta) return [];
1426
+ reconcilePendingProtectedArtifacts(session);
1427
+ return readSessionEntriesForCompact(meta.id);
1428
+ };
1429
+
1430
+ // turn_end assessors, fired by the chat-loop when the final assistant
1431
+ // message of a run lands. Tool-prose first so its hard-block interruption
1432
+ // precedes the finish-contract advisory in effect order.
1433
+ middleware.registerHook(createToolProseRegistration());
1434
+ middleware.registerHook(createTaskNudgeRegistration({ getBoard: () => taskBoard.snapshot() }));
1435
+ middleware.registerHook(createReadOnlyExplorationNudgeRegistration());
1436
+ middleware.registerHook(createUnbackedWorkerClaimRegistration());
1437
+ middleware.registerHook(
1438
+ createDetachedDispatchNudgeRegistration({ getOpenBatches: () => openDetachedBatchViews(dispatch) }),
1439
+ );
1440
+ if (session) {
1441
+ middleware.registerHook(
1442
+ createFinishContractRegistration({
1443
+ // Tail-scoped: the contract only needs the last-user-message window, so
1444
+ // it parses a bounded ledger tail per turn_end rather than the whole
1445
+ // file (which grows unbounded with session length).
1446
+ readSessionEntries: () => {
1447
+ const meta = session.current();
1448
+ return meta ? readRecentSessionEntriesForContract(meta.id) : null;
1449
+ },
1450
+ resolveRigor: () =>
1451
+ resolveRigor({ cwd: process.cwd(), override: parseRigorOverride(process.env.CLIO_CODER_RIGOR) }),
1452
+ recordDecision: (record) => safety.audit.recordCompletionContract?.(record),
1453
+ }),
1454
+ );
1455
+ }
1456
+
1457
+ const chat = createChatLoop({
1458
+ getSettings: getCurrentSettings,
1459
+ providers,
1460
+ middleware,
1461
+ middlewareToolChoice,
1462
+ protectedArtifacts: {
1463
+ replace: (state) => protectedArtifactsGuard.replaceState(state),
1464
+ markDegraded: (reason) => protectedArtifactsGuard.markDegraded(reason),
1465
+ },
1466
+ knownTargets: () => new Set(providers.list().map((entry) => entry.target.id)),
1467
+ observability,
1468
+ bus,
1469
+ ...(prompts ? { prompts } : {}),
1470
+ ...(session ? { session } : {}),
1471
+ getMemorySection: () => {
1472
+ try {
1473
+ const records = loadMemoryRecordsSync(clioDataDir());
1474
+ return buildMemoryPromptSection(records, {
1475
+ activeRepository: canonicalMemoryRepositoryIdentity(process.cwd()),
1476
+ }).section;
1477
+ } catch {
1478
+ return "";
1479
+ }
1480
+ },
1481
+ getTaskMemoryHandoffSource: () => {
1482
+ ensureTaskMemorySession();
1483
+ return renderTaskMemoryHandoffSource(taskMemoryBank.snapshot());
1484
+ },
1485
+ registerDeferredReminderSink: (sink) => {
1486
+ deferredMemoryReminderSink = sink;
1487
+ },
1488
+ ...(session
1489
+ ? {
1490
+ readSessionEntries: readCurrentSessionEntries,
1491
+ autoCompact: createProductionAutoCompact(session, getCurrentSettings, providers),
1492
+ }
1493
+ : {}),
1494
+ toolRegistry,
1495
+ });
1496
+
1497
+ // Coordinated shutdown (SIGINT/SIGTERM, TUI quit) must abort any in-flight
1498
+ // turn before domains stop. The agent abort fans out to every running
1499
+ // tool's AbortSignal, and bash-exec answers it by signalling the tool's
1500
+ // detached process group. Without this, a headless SIGINT exited the CLI
1501
+ // while a running tool's children survived as orphans of init.
1502
+ termination.onDrain(async () => {
1503
+ chat.dispose();
1504
+ // The abort fans out to running tools, but their results still land and
1505
+ // persist through the aborted run's subscribers. Domains (the session
1506
+ // writer among them) stop in the persist phase, strictly after drain, so
1507
+ // awaiting settlement here makes a session append after session stop
1508
+ // impossible by ordering.
1509
+ await chat.whenSettled();
1510
+ });
1511
+
1512
+ // A boot-time resume (CLIO_CODER_RESUME_SESSION_ID) must replay the resumed
1513
+ // session into the chat loop the same way the interactive /resume overlay
1514
+ // does. Without this, the first submit runs with an empty provider context
1515
+ // and parents its user turn at null, appending a second root that silently
1516
+ // abandons the resumed session's active path. The leaf id is restored even
1517
+ // when rebuilding replay messages fails, so parenting stays correct and
1518
+ // only the provider context degrades.
1519
+ if (resumedSessionAtBoot && session) {
1520
+ const resumedMeta = session.current();
1521
+ if (resumedMeta) {
1522
+ let leafTurnId: string | null = null;
1523
+ try {
1524
+ leafTurnId = session.tree(resumedMeta.id).leafId;
1525
+ } catch (err) {
1526
+ process.stderr.write(
1527
+ `Clio Coder: failed to read resumed session tree ${resumedMeta.id}: ${err instanceof Error ? err.message : String(err)}\n`,
1528
+ );
1529
+ }
1530
+ try {
1531
+ chat.resetForSession(leafTurnId, buildReplayAgentMessagesFromTurns(readCurrentSessionEntries()));
1532
+ } catch (err) {
1533
+ chat.resetForSession(leafTurnId);
1534
+ process.stderr.write(
1535
+ `Clio Coder: failed to replay resumed session context ${resumedMeta.id}: ${err instanceof Error ? err.message : String(err)}\n`,
1536
+ );
1537
+ }
1538
+ }
1539
+ }
1540
+
1541
+ if (options.acp) {
1542
+ // ACP-served sessions get the same routing isolation as interactive
1543
+ // ones, but ACP v1 has no channel for agent-initiated advisory text:
1544
+ // the session/update union (agent_message_chunk, agent_thought_chunk,
1545
+ // tool_call*, plan, …) carries turn content, and notifications outside
1546
+ // an active session/prompt would break strict clients (see the matching
1547
+ // note in src/engine/acp/server.ts). The external-divergence and
1548
+ // target-removed notices therefore go to the session ledger as `custom`
1549
+ // entries, where /resume and session tooling can surface them.
1550
+ // ACP is operatorless too: bound a runaway turn with the shared
1551
+ // interrupt->stop subscriber, the same way the headless path does.
1552
+ const unsubscribeAcpLoopGuardStop = subscribeLoopGuardStop(bus, chat);
1553
+ const unsubscribeAcpRoutingNotices = bus.on(BusChannels.ConfigNextTurn, (payload) => {
1554
+ const evt = payload as { diff?: { nextTurn?: string[] }; settings?: Readonly<ClioSettings> } | null | undefined;
1555
+ if (!evt?.settings || !Array.isArray(evt.diff?.nextTurn)) return;
1556
+ if (!session?.current()) return;
1557
+ const notices = routingChangeNotices(evt.diff.nextTurn, evt.settings, getCurrentSettings());
1558
+ for (const notice of notices) {
1559
+ try {
1560
+ session.appendEntry({
1561
+ kind: "custom",
1562
+ customType: "clio.routing-notice",
1563
+ parentTurnId: null,
1564
+ data: { kind: notice.kind, level: notice.level, text: notice.text },
1565
+ });
1566
+ } catch {
1567
+ // Advisory only; a ledger write failure must not affect the
1568
+ // ACP turn loop.
1569
+ }
1570
+ }
1571
+ });
1572
+ try {
1573
+ const transport = options.acp.transport ?? createStdioServerTransport(options.acp.transportOptions);
1574
+ const code = await serveClioAcpAgent({
1575
+ transport,
1576
+ chat,
1577
+ ...(session ? { session } : {}),
1578
+ toolRegistry,
1579
+ bus,
1580
+ autonomy: resolveBaselineAutonomy,
1581
+ onActiveSessionAutonomyChange: (level) => {
1582
+ activeAcpSessionAutonomy = level;
1583
+ },
1584
+ cwd: process.cwd(),
1585
+ version: getVersionInfo().clio,
1586
+ permissionTimeoutMs:
1587
+ options.acp.permissionTimeoutMs ??
1588
+ config?.get().delegation.defaults.permissionTimeoutMs ??
1589
+ DEFAULT_DELEGATION_PERMISSION_TIMEOUT_MS,
1590
+ });
1591
+ chat.dispose();
1592
+ await dispatch.drain();
1593
+ await result.stop();
1594
+ return { exitCode: code, bootTimeMs: timer.snapshot().totalMs };
1595
+ } finally {
1596
+ unsubscribeAcpRoutingNotices();
1597
+ unsubscribeAcpLoopGuardStop();
1598
+ }
1599
+ }
1600
+
1601
+ if (options.headless) {
1602
+ // Operatorless: there is no TUI subscriber to turn a loop-guard interrupt
1603
+ // into a run stop, so a degenerate local model would spin until an
1604
+ // external timeout (each call blocked, the agent loop never aborted). Wire
1605
+ // the shared interrupt->stop subscriber so the run ends with the same
1606
+ // durable closing turn the interactive surface produces.
1607
+ const unsubscribeLoopGuardStop = subscribeLoopGuardStop(bus, chat);
1608
+ const headlessPermissionReason = HEADLESS_PERMISSION_DENIED_REASON;
1609
+ const unsubscribeHeadlessPermission = toolRegistry.onPermissionRequired((call, decision, meta) => {
1610
+ bus.emit(BusChannels.PermissionResolved, {
1611
+ status: "denied",
1612
+ requestId: meta.requestId,
1613
+ origin: "main",
1614
+ decidedBy: "policy:no-operator",
1615
+ tool: call.tool,
1616
+ actionClass: decision.classification.actionClass,
1617
+ reason: headlessPermissionReason,
1618
+ requestedBy: "headless",
1619
+ });
1620
+ toolRegistry.cancelParkedCalls(headlessPermissionReason);
1621
+ });
1622
+ try {
1623
+ const parsedSkillRequest = resources?.parsePendingSkillRequests(options.headless.prompt, process.cwd()) ?? {
1624
+ text: options.headless.prompt,
1625
+ pendingSkillRequests: [],
1626
+ };
1627
+ const promptExpansion = resources?.expandPromptTemplate(parsedSkillRequest.text, process.cwd());
1628
+ // A prompt named as `/name` is a request for that template. When the
1629
+ // template refuses, the run says why and stops; sending the literal
1630
+ // `/name` on to the model spends a turn answering a command it cannot
1631
+ // run, and the operator never sees the refusal.
1632
+ if (promptExpansion?.expanded === false && promptExpansion.refusal) {
1633
+ process.stderr.write(`clio-coder: ${promptExpansion.refusal.message}\n`);
1634
+ await termination.shutdown(1);
1635
+ return { exitCode: 1, bootTimeMs: timer.snapshot().totalMs };
1636
+ }
1637
+ const fileExpansion = await expandInlineFileReferencesAsync(
1638
+ promptExpansion?.expanded ? promptExpansion.text : parsedSkillRequest.text,
1639
+ {
1640
+ cwd: process.cwd(),
1641
+ includeImages: true,
1642
+ missing: "leave",
1643
+ },
1644
+ );
1645
+ const images = [...(options.headless.images ?? []), ...fileExpansion.images];
1646
+ const workingContextPaths = [...(options.headless.workingContextPaths ?? []), ...fileExpansion.referencedPaths];
1647
+ const code = await runHeadlessMainAgent(chat, {
1648
+ prompt: fileExpansion.text,
1649
+ ...(images.length > 0 ? { images } : {}),
1650
+ ...(workingContextPaths.length > 0 ? { workingContextPaths } : {}),
1651
+ ...(options.headless.sampling ? { sampling: options.headless.sampling } : {}),
1652
+ ...(parsedSkillRequest.pendingSkillRequests.length > 0
1653
+ ? { pendingSkillRequests: parsedSkillRequest.pendingSkillRequests }
1654
+ : {}),
1655
+ mode: options.headless.mode ?? "text",
1656
+ ...(options.headless.jsonEvents ? { jsonEvents: options.headless.jsonEvents } : {}),
1657
+ ...(options.headless.steerChannel ? { steerChannel: options.headless.steerChannel } : {}),
1658
+ getSessionHeader: () => printJsonSessionHeader(session?.current() ?? null),
1659
+ });
1660
+ await termination.shutdown(code);
1661
+ return { exitCode: code, bootTimeMs: timer.snapshot().totalMs };
1662
+ } finally {
1663
+ unsubscribeHeadlessPermission();
1664
+ unsubscribeLoopGuardStop();
1665
+ }
1666
+ }
1667
+
1668
+ timer.mark("first TUI paint");
1669
+ await startInteractive({
1670
+ bus,
1671
+ providers,
1672
+ dispatch,
1673
+ ...(agents ? { agents } : {}),
1674
+ observability,
1675
+ chat,
1676
+ ...(initialNotices.length > 0 ? { initialNotices } : {}),
1677
+ ...(resources ? { resources } : {}),
1678
+ ...(extensions ? { extensions } : {}),
1679
+ ...(interop ? { interop } : {}),
1680
+ ...(share ? { share } : {}),
1681
+ toolRegistry,
1682
+ ...(session ? { session } : {}),
1683
+ ...(session ? { readSessionEntries: readCurrentSessionEntries } : {}),
1684
+ getTaskBoard: () => taskBoard.snapshot(),
1685
+ getTaskMemoryStatus: () => {
1686
+ ensureTaskMemorySession();
1687
+ const settings = getCurrentSettings();
1688
+ const bank = taskMemoryBank.snapshot();
1689
+ return {
1690
+ enabled: settings.memory.intervention.enabled,
1691
+ tier: settings.background.target && settings.background.model ? "llm" : "rules",
1692
+ size: taskMemoryBankSize(bank),
1693
+ lastDecision: memoryIntervention.lastDecision(),
1694
+ bank,
1695
+ activity: memoryIntervention.recentActivity(),
1696
+ stepInFlight: memoryIntervention.stepInFlight(),
1697
+ };
1698
+ },
1699
+ getTaskMemorySeedOffer,
1700
+ seedTaskMemory: seedCurrentTaskMemoryFromHandoff,
1701
+ stateDir: clioStateDir(),
1702
+ dataDir: clioDataDir(),
1703
+ registerAskUserHandler: (handler) => {
1704
+ askUserHandler = handler;
1705
+ return () => {
1706
+ if (askUserHandler === handler) askUserHandler = null;
1707
+ };
1708
+ },
1709
+ getSettings: getCurrentSettings,
1710
+ getFleetNodes: () => result.getContract<SchedulingContract>("scheduling")?.fleet?.list() ?? [],
1711
+ onBackgroundDispatch: () => dispatchBackground.backgroundNewest(),
1712
+ ...(session ? { getSessionId: () => session.current()?.id ?? null } : {}),
1713
+ ...(contextDomain
1714
+ ? {
1715
+ getContextState: (cwd?: string) => contextDomain.contextState(cwd),
1716
+ onInit: async (
1717
+ options: {
1718
+ preview?: boolean;
1719
+ adopt?: boolean;
1720
+ applyClioMd?: boolean;
1721
+ rewriteClioMd?: boolean;
1722
+ proposeClioMd?: boolean;
1723
+ includeGlobalImports?: boolean;
1724
+ heuristic?: boolean;
1725
+ },
1726
+ runIo?: RunIo,
1727
+ ) => {
1728
+ // Interactive context-init explores the repo with the configured target by
1729
+ // default, grounded in the freshly built codewiki, and falls back to the
1730
+ // deterministic heuristic when no target is reachable. --heuristic and
1731
+ // --preview skip model generation.
1732
+ const useModel = options.heuristic !== true && options.preview !== true;
1733
+ const bootstrapOptions = bootstrapInputFromInitOptions(options);
1734
+ await contextDomain.runBootstrap({
1735
+ cwd: process.cwd(),
1736
+ confirmGitignore: () => true,
1737
+ adopt: options.adopt === true,
1738
+ ...bootstrapOptions,
1739
+ ...(useModel
1740
+ ? {
1741
+ generate: modelBootstrapGenerate({
1742
+ dispatch,
1743
+ resolveRoute: () => {
1744
+ if (!config) throw new Error("context-bootstrap configuration unavailable");
1745
+ return resolveBootstrapRoute(config.get());
1746
+ },
1747
+ // Names the agent that actually ran and reports the throw as
1748
+ // what it is. "Scout unavailable" was wrong twice over: the
1749
+ // agent is context-bootstrap, and the same line was printed
1750
+ // for a worker that failed, a worker whose answer the loop
1751
+ // guard removed, and a worker that succeeded and whose
1752
+ // payload the reader then refused.
1753
+ onFallback: (err, mode) =>
1754
+ runIo?.stderr(
1755
+ `context init: context-bootstrap did not produce a handbook, using ${mode === "existing" ? "the existing CLIO-CODER.md" : "the heuristic writer"} (${err.message})\n`,
1756
+ ),
1757
+ }),
1758
+ modelId: "configured-clio-target",
1759
+ }
1760
+ : {}),
1761
+ });
1762
+ },
1763
+ onContextClear: async (options: { all?: boolean; confirmed?: boolean; confirmedAll?: boolean }) => {
1764
+ await contextDomain.runContextClear({
1765
+ cwd: process.cwd(),
1766
+ all: options.all === true,
1767
+ io: {
1768
+ stdout: (s) => process.stdout.write(s),
1769
+ stderr: (s) => process.stderr.write(s),
1770
+ },
1771
+ confirmContext: () => options.confirmed === true,
1772
+ confirmAll: () => options.confirmedAll === true,
1773
+ });
1774
+ },
1775
+ onContextRefresh: async () => {
1776
+ await contextDomain.runContextRefresh({
1777
+ cwd: process.cwd(),
1778
+ io: {
1779
+ stdout: (s) => process.stdout.write(s),
1780
+ stderr: (s) => process.stderr.write(s),
1781
+ },
1782
+ });
1783
+ },
1784
+ }
1785
+ : {}),
1786
+ onSetThinkingLevel: (level) => {
1787
+ const current = getCurrentSettings();
1788
+ const nextLevel =
1789
+ resolveModelRuntimeCapabilitiesForProviders(
1790
+ providers,
1791
+ current.orchestrator.target,
1792
+ current.orchestrator.model,
1793
+ level,
1794
+ )?.thinking.effectiveLevel ?? "off";
1795
+ updateSessionRouting({ orchestrator: { thinkingLevel: nextLevel } });
1796
+ },
1797
+ onCycleThinking: () => {
1798
+ const current = getCurrentSettings();
1799
+ const thinking = resolveModelRuntimeCapabilitiesForProviders(
1800
+ providers,
1801
+ current.orchestrator.target,
1802
+ current.orchestrator.model,
1803
+ current.orchestrator.thinkingLevel ?? "off",
1804
+ )?.thinking;
1805
+ const effectiveAvailable = thinking?.supportedLevels ?? (["off"] as ThinkingLevel[]);
1806
+ const nextLevel = advanceThinkingLevel(
1807
+ thinking?.effectiveLevel ?? current.orchestrator.thinkingLevel ?? "off",
1808
+ effectiveAvailable,
1809
+ );
1810
+ updateSessionRouting({ orchestrator: { thinkingLevel: nextLevel } });
1811
+ },
1812
+ onSelectModel: ({ target, model }) => {
1813
+ const registry = getRuntimeRegistry();
1814
+ const settings = getCurrentSettings();
1815
+ const descriptor = settings.targets.find((e) => e.id === target);
1816
+ if (descriptor) {
1817
+ const runtime = registry.get(descriptor.runtime);
1818
+ if (!runtime) {
1819
+ throw new Error(
1820
+ `cannot use target '${target}' as orchestrator target because runtime '${descriptor.runtime}' is not registered`,
1821
+ );
1822
+ }
1823
+ if (!isOrchestratorEligibleRuntime(runtime)) {
1824
+ throw new Error(
1825
+ `cannot use target '${target}' as orchestrator target because runtime '${runtime.id}' is not an HTTP/native runtime`,
1826
+ );
1827
+ }
1828
+ }
1829
+ updateSessionRouting({ orchestrator: { target, model } });
1830
+ rememberRecentModel(`${target}/${model}`, getCurrentSettings().modelSelector.recentLimit);
1831
+ },
1832
+ writeSettings: (next) => applySettingsBlob(next),
1833
+ commitSetting: (id, next, scope) => commitSetting(id, next, scope),
1834
+ ...(session
1835
+ ? {
1836
+ onResumeSession: (sessionId) => {
1837
+ try {
1838
+ const previousSessionId = session.current()?.id ?? null;
1839
+ session.resume(sessionId);
1840
+ if (previousSessionId !== sessionId) ensureTaskMemorySession();
1841
+ } catch (err) {
1842
+ process.stderr.write(
1843
+ `[/resume] failed to resume ${sessionId}: ${err instanceof Error ? err.message : String(err)}\n`,
1844
+ );
1845
+ }
1846
+ },
1847
+ onNewSession: () => {
1848
+ session.create({ cwd: process.cwd() });
1849
+ ensureTaskMemorySession();
1850
+ },
1851
+ onForkSession: (parentTurnId) => {
1852
+ try {
1853
+ session.fork(parentTurnId);
1854
+ ensureTaskMemorySession();
1855
+ } catch (err) {
1856
+ process.stderr.write(
1857
+ `[/fork] failed at turn ${parentTurnId}: ${err instanceof Error ? err.message : String(err)}\n`,
1858
+ );
1859
+ }
1860
+ },
1861
+ onCompact: async (instructions) => {
1862
+ await chat.compact(instructions);
1863
+ },
1864
+ }
1865
+ : {}),
1866
+ onCycleScopedModelForward: () => cycleScopedSession("forward"),
1867
+ onCycleScopedModelBackward: () => cycleScopedSession("backward"),
1868
+ onShutdown: async () => {
1869
+ await termination.shutdown(0);
1870
+ },
1871
+ });
1872
+ return { exitCode: 0, bootTimeMs: timer.snapshot().totalMs };
1873
+ }