@iowarp/clio-coder 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1187) hide show
  1. package/CHANGELOG.md +123 -2
  2. package/CONTRIBUTING.md +23 -23
  3. package/README.md +295 -627
  4. package/dist/acp-BIYHVZIM.js +128 -0
  5. package/dist/{agents-Q4MYPMUW.js → agents-YT6SSRIT.js} +52 -27
  6. package/dist/assets/codewiki.json +1 -0
  7. package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/LICENSE +21 -0
  8. package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/cgmanifest.json +16 -0
  9. package/dist/assets/grammars/notices/tree-sitter-wasms/LICENSE +24 -0
  10. package/dist/assets/grammars/tree-sitter-c-sharp.wasm +0 -0
  11. package/dist/assets/grammars/tree-sitter-c.wasm +0 -0
  12. package/dist/assets/grammars/tree-sitter-cpp.wasm +0 -0
  13. package/dist/assets/grammars/tree-sitter-go.wasm +0 -0
  14. package/dist/assets/grammars/tree-sitter-java.wasm +0 -0
  15. package/dist/assets/grammars/tree-sitter-javascript.wasm +0 -0
  16. package/dist/assets/grammars/tree-sitter-python.wasm +0 -0
  17. package/dist/assets/grammars/tree-sitter-ruby.wasm +0 -0
  18. package/dist/assets/grammars/tree-sitter-rust.wasm +0 -0
  19. package/dist/assets/grammars/tree-sitter-tsx.wasm +0 -0
  20. package/dist/assets/grammars/tree-sitter-typescript.wasm +0 -0
  21. package/dist/assets/grammars/tree-sitter.wasm +0 -0
  22. package/dist/{auth-O6HYIJ6J.js → auth-5TWEIYDN.js} +30 -15
  23. package/dist/chunk-2EHAIA3X.js +481 -0
  24. package/dist/{chunk-ME6DNWIU.js → chunk-2IR2NMPA.js} +12 -4
  25. package/dist/chunk-2SFS6XQE.js +122 -0
  26. package/dist/{chunk-KKKPTZLM.js → chunk-2STGEDFV.js} +8 -1
  27. package/dist/chunk-2VTFPG5O.js +48 -0
  28. package/dist/chunk-3I7MS7N2.js +40 -0
  29. package/dist/chunk-3R73A4XB.js +69 -0
  30. package/dist/{chunk-JPOWPFCU.js → chunk-3ZXDFGR5.js} +52 -23
  31. package/dist/chunk-4BJ5BYCE.js +61 -0
  32. package/dist/{chunk-26BZQOAD.js → chunk-4BPJXDWC.js} +161 -260
  33. package/dist/chunk-4KLWL3UC.js +18 -0
  34. package/dist/{chunk-5LDRNKX2.js → chunk-4VP4KH3K.js} +161 -262
  35. package/dist/chunk-4ZG3XFUR.js +77 -0
  36. package/dist/chunk-5B2AEOW5.js +5407 -0
  37. package/dist/chunk-5TSRNF4G.js +510 -0
  38. package/dist/{chunk-WD6AJM35.js → chunk-5UUP6MWO.js} +48 -69
  39. package/dist/{chunk-X3BR7HWV.js → chunk-5WIGXA4T.js} +7 -1
  40. package/dist/chunk-65DEGPJ6.js +52 -0
  41. package/dist/chunk-6EJMN2Y3.js +17 -0
  42. package/dist/chunk-6EJV5X2W.js +16405 -0
  43. package/dist/chunk-6N5PTWMY.js +136 -0
  44. package/dist/chunk-6XLNIQDB.js +27 -0
  45. package/dist/{chunk-XNISANGE.js → chunk-77VKQEHF.js} +171 -74
  46. package/dist/chunk-7CR24IG7.js +242 -0
  47. package/dist/{chunk-UWIJNAOB.js → chunk-7EYHLWU7.js} +907 -638
  48. package/dist/chunk-7MNJORFF.js +22 -0
  49. package/dist/{chunk-JWHCJDCI.js → chunk-A3CYT5EX.js} +257 -79
  50. package/dist/chunk-AGYYIBLL.js +1069 -0
  51. package/dist/chunk-AO4RKG4M.js +277 -0
  52. package/dist/chunk-APJ265NV.js +7410 -0
  53. package/dist/chunk-ARBGF5F7.js +174 -0
  54. package/dist/{chunk-TFSSEXL6.js → chunk-BMEMKKIT.js} +14 -4
  55. package/dist/{chunk-X3NE4WVW.js → chunk-BNAZZHFG.js} +7 -1
  56. package/dist/chunk-CBCAPZAA.js +229 -0
  57. package/dist/{chunk-QV47YRF4.js → chunk-CGKSTWHD.js} +7 -1
  58. package/dist/chunk-CMZWFGD2.js +352 -0
  59. package/dist/chunk-ECH6PKUQ.js +39 -0
  60. package/dist/{chunk-67MTHP2E.js → chunk-ED4KHGC3.js} +13 -5
  61. package/dist/chunk-EKMEHE4H.js +340 -0
  62. package/dist/{chunk-O4EJEDHO.js → chunk-EPVUXGXG.js} +50 -35
  63. package/dist/chunk-FCSXB6T2.js +338 -0
  64. package/dist/chunk-FJ3H4MN5.js +48 -0
  65. package/dist/chunk-FQ4SKYE4.js +29 -0
  66. package/dist/chunk-G2DE3C7R.js +644 -0
  67. package/dist/chunk-G4BMMOKF.js +182 -0
  68. package/dist/{chunk-7BHLZB3A.js → chunk-GGXXDWE4.js} +190 -1218
  69. package/dist/chunk-HC4CLZ2Y.js +68 -0
  70. package/dist/{chunk-E3NYWENM.js → chunk-HFSBBKSQ.js} +55 -61
  71. package/dist/chunk-HKIYEGME.js +30 -0
  72. package/dist/chunk-I4HZDVNP.js +73 -0
  73. package/dist/chunk-IGLFWIYI.js +161 -0
  74. package/dist/chunk-IHXBNWMM.js +137 -0
  75. package/dist/chunk-IKCO5N3L.js +162 -0
  76. package/dist/chunk-IR4CFBFN.js +56 -0
  77. package/dist/{chunk-DXQNI4PC.js → chunk-IWHMRKLL.js} +8 -1
  78. package/dist/{chunk-VOO7NYPP.js → chunk-J5HN4RYU.js} +27 -9
  79. package/dist/{chunk-4UQA7NCT.js → chunk-J5Q24KAG.js} +7 -2
  80. package/dist/{chunk-KBR4MZZR.js → chunk-JVCV3ICN.js} +7 -1
  81. package/dist/chunk-KJ5LWLOE.js +1077 -0
  82. package/dist/chunk-LBMZMYH2.js +285 -0
  83. package/dist/{chunk-3DANZDGR.js → chunk-LBNRH5WM.js} +100 -161
  84. package/dist/{chunk-TKHQ4DGZ.js → chunk-LZSJBIVT.js} +7358 -7414
  85. package/dist/{chunk-UGYAX5YI.js → chunk-M6SHUN7Q.js} +13 -8
  86. package/dist/chunk-MAW544W2.js +1882 -0
  87. package/dist/chunk-MBS4V7ZP.js +217 -0
  88. package/dist/{chunk-2J63S4SF.js → chunk-MFFY33HR.js} +167 -140
  89. package/dist/chunk-MNA4JGU4.js +255 -0
  90. package/dist/chunk-MQSRRFWA.js +3428 -0
  91. package/dist/{chunk-RQDWMVRB.js → chunk-MVVUPGPW.js} +138 -137
  92. package/dist/chunk-OAO4GE4M.js +619 -0
  93. package/dist/chunk-OHHN2SO4.js +5135 -0
  94. package/dist/chunk-OKGUZO2U.js +34 -0
  95. package/dist/{chunk-BBUH4VAA.js → chunk-OOJYHWRB.js} +60 -361
  96. package/dist/{chunk-D5YD55AR.js → chunk-OQ33BKR3.js} +12 -1
  97. package/dist/chunk-OQE5J4C6.js +73 -0
  98. package/dist/{chunk-AMFR5YA3.js → chunk-ORBHGJC5.js} +26 -12
  99. package/dist/chunk-POHLU5DW.js +1186 -0
  100. package/dist/chunk-QKMUKYO7.js +4961 -0
  101. package/dist/{chunk-7RBKDI66.js → chunk-QTYWRVRA.js} +104 -12
  102. package/dist/{chunk-5N2FG33Q.js → chunk-R346GLFC.js} +7 -1
  103. package/dist/chunk-SRF2PJNW.js +184 -0
  104. package/dist/chunk-SST6Z5JA.js +80 -0
  105. package/dist/{chunk-XV4ZJ6ZM.js → chunk-STBPMHSX.js} +473 -1194
  106. package/dist/{chunk-HBU5MTAM.js → chunk-SVNXH4L2.js} +7 -1
  107. package/dist/chunk-T6YILFSB.js +80 -0
  108. package/dist/chunk-TXOTCRLG.js +15 -0
  109. package/dist/chunk-TZTZS7QK.js +227 -0
  110. package/dist/chunk-UOV2BYIW.js +107 -0
  111. package/dist/{chunk-527KG6XR.js → chunk-V4RXGQ5Q.js} +72 -185
  112. package/dist/chunk-VAKQQHWR.js +434 -0
  113. package/dist/chunk-VG7TBQIY.js +128 -0
  114. package/dist/chunk-VJWL6YS5.js +244 -0
  115. package/dist/chunk-WEH5XRJQ.js +32 -0
  116. package/dist/{chunk-VPAWTYLY.js → chunk-WEPFGWHJ.js} +7 -1
  117. package/dist/chunk-WMSVI4G2.js +2095 -0
  118. package/dist/chunk-WVO7V2QY.js +797 -0
  119. package/dist/chunk-X4RCMKVQ.js +641 -0
  120. package/dist/chunk-X75S7HFS.js +374 -0
  121. package/dist/chunk-XN3L4EYL.js +46 -0
  122. package/dist/{chunk-HMYNFFY4.js → chunk-XPLRXC72.js} +7 -2
  123. package/dist/{chunk-PIDUD6M2.js → chunk-YCWGATWI.js} +12 -5
  124. package/dist/chunk-YHZX5GEU.js +193 -0
  125. package/dist/chunk-YXLYO42X.js +91 -0
  126. package/dist/{chunk-UHTSULZS.js → chunk-ZDOOVTXZ.js} +38 -73
  127. package/dist/chunk-ZI647VB5.js +37 -0
  128. package/dist/{chunk-CLJ5HLUD.js → chunk-ZWLZP4ZT.js} +25 -6
  129. package/dist/cli/index.js +70 -56
  130. package/dist/clio-4LY5K2AC.js +25 -0
  131. package/dist/code-nav-7AX6FYE6.js +600 -0
  132. package/dist/codewiki/build-worker.js +66 -0
  133. package/dist/compile-cache-CVJMMODC.js +18 -0
  134. package/dist/{components-JVHMUBEB.js → components-KELWS457.js} +31 -9
  135. package/dist/{config-ZFCDBMDC.js → config-GTLUW2PR.js} +104 -56
  136. package/dist/configure-R6A64DHX.js +42 -0
  137. package/dist/context-5VKGUVJJ.js +866 -0
  138. package/dist/{context-CDXTP2MP.js → context-JFZEJ7W5.js} +21 -13
  139. package/dist/{context-E3KIFVXI.js → context-RW5HC47S.js} +77 -36
  140. package/dist/{context-clear-3F4PLXOS.js → context-clear-6ZHBAZZT.js} +64 -30
  141. package/dist/{context-index-Q7YSYTR3.js → context-index-BZ4UYMTC.js} +36 -24
  142. package/dist/dispatch-runner-VKBRCWQC.js +1997 -0
  143. package/dist/{docs-YIETIWZI.js → docs-2C2LTVT2.js} +32 -13
  144. package/dist/doctor-KI767GSN.js +87 -0
  145. package/dist/{eval-B3KZZESM.js → eval-XSSNATB4.js} +93 -27
  146. package/dist/{evidence-V67CHM35.js → evidence-UA6AWDQQ.js} +55 -27
  147. package/dist/{evolve-YDZSUQYA.js → evolve-QNTFGV6Z.js} +59 -26
  148. package/dist/{extensions-SRG7XCAH.js → extensions-QVDOHDGJ.js} +23 -8
  149. package/dist/{fleet-CA2CRTVG.js → fleet-Q7UOMUSG.js} +173 -56
  150. package/dist/fleet-preflight-DDN536IT.js +26 -0
  151. package/dist/{init-2OZDJE2D.js → init-WBB65ZHQ.js} +79 -34
  152. package/dist/{memory-3PIQQAKX.js → memory-MD3O64RI.js} +57 -28
  153. package/dist/{models-DY35XI7Y.js → models-BZU34YWD.js} +49 -24
  154. package/dist/monitor-MEQA5C3I.js +661 -0
  155. package/dist/{chunk-PS4PFJQP.js → orchestrator-CGFKEP27.js} +22837 -18564
  156. package/dist/{paths-5OMXW7Z4.js → paths-UXLN5YYZ.js} +17 -6
  157. package/dist/preload-P6DGH2PZ.js +13 -0
  158. package/dist/{reset-PIFYNOS3.js → reset-L2FQEE3E.js} +25 -13
  159. package/dist/{run-3VSPP24F.js → run-IV4Q6RLN.js} +115 -63
  160. package/dist/{share-D36RQCXM.js → share-S5BZQC5I.js} +23 -10
  161. package/dist/{skills-F2MRLELY.js → skills-LQEKRDTN.js} +42 -15
  162. package/dist/{skills-eval-E2ZTW4PL.js → skills-eval-3DC4HEWS.js} +90 -45
  163. package/dist/steer-GGWFUJUD.js +77 -0
  164. package/dist/{targets-DZMEZAH4.js → targets-C4SSGQOB.js} +78 -60
  165. package/dist/terminal-lease-IT5JW2NR.js +395 -0
  166. package/dist/{trace-7NYCUI2J.js → trace-PNCASAXC.js} +48 -17
  167. package/dist/tree-sitter-HGKH6LG4.js +4673 -0
  168. package/dist/{uninstall-AD3JWHBB.js → uninstall-FZCQCDKC.js} +19 -8
  169. package/dist/{upgrade-WYYBKGDY.js → upgrade-7TT7SQ3G.js} +66 -24
  170. package/dist/{usage-ULIDAGFF.js → usage-GV4PKT3M.js} +71 -32
  171. package/dist/verify-G6V4D2G7.js +716 -0
  172. package/dist/version-KGEG7EPO.js +21 -0
  173. package/dist/web-fetch-2YHJ3KTG.js +638 -0
  174. package/dist/{wiki-generate-PKFIX6OB.js → wiki-generate-DQF6Z66B.js} +98 -48
  175. package/dist/worker/entry.js +297 -54
  176. package/dist/workspace-G4ZWUIPR.js +22 -0
  177. package/docs/README.md +29 -21
  178. package/docs/acp.md +191 -16
  179. package/docs/alcf-provider.md +1 -1
  180. package/docs/architecture.md +157 -7
  181. package/docs/artifact-placement.md +101 -0
  182. package/docs/artifact-versions.md +1 -1
  183. package/docs/built-in-agents.md +4 -2
  184. package/docs/capacity-and-scheduling.md +1 -1
  185. package/docs/commands-and-modes.md +224 -97
  186. package/docs/config-knobs-audit.md +1 -3
  187. package/docs/configuration-and-targets.md +236 -38
  188. package/docs/context-engine.md +34 -7
  189. package/docs/development-pipeline.md +102 -0
  190. package/docs/dispatch-architecture-rationale.md +1 -1
  191. package/docs/documentation-coverage.md +4 -4
  192. package/docs/documentation-guide.md +3 -3
  193. package/docs/environment-variables.md +15 -6
  194. package/docs/eval-runner.md +3 -3
  195. package/docs/evals-internal.md +2 -2
  196. package/docs/evidence-and-memory.md +10 -2
  197. package/docs/evolution.md +2 -2
  198. package/docs/exit-codes-and-output.md +15 -9
  199. package/docs/extensions-and-sharing.md +25 -18
  200. package/docs/fleet-demo-runbook.md +22 -23
  201. package/docs/fleet-dispatch.md +117 -16
  202. package/docs/git-commit-provenance.md +120 -0
  203. package/docs/glossary.md +107 -15
  204. package/docs/installation-and-lifecycle.md +75 -17
  205. package/docs/middleware-and-components.md +1 -1
  206. package/docs/model-catalog.md +46 -15
  207. package/docs/observability.md +8 -5
  208. package/docs/performance-methodology.md +491 -0
  209. package/docs/pi-boundary.md +72 -0
  210. package/docs/proactive-memory.md +14 -14
  211. package/docs/prompt-envelope-and-tools.md +25 -4
  212. package/docs/provider-adapter-cookbook.md +57 -4
  213. package/docs/release-cut-checklist.md +129 -111
  214. package/docs/safety-model.md +28 -8
  215. package/docs/scientific-validation.md +3 -3
  216. package/docs/session-lifecycle.md +55 -12
  217. package/docs/skills-marketplace.md +12 -8
  218. package/docs/time-conventions.md +101 -0
  219. package/docs/tool-usage.md +6 -5
  220. package/docs/trace-store.md +1 -1
  221. package/docs/troubleshooting.md +10 -7
  222. package/docs/tui-design.md +121 -28
  223. package/docs/worker-dispatch-mechanics.md +3 -3
  224. package/package.json +33 -36
  225. package/skills/README.md +5 -6
  226. package/skills/coding/ast-grep/SKILL.md +136 -0
  227. package/skills/coding/ast-grep/evals.md +56 -0
  228. package/skills/coding/ast-grep/references/rule_reference.md +297 -0
  229. package/skills/coding/coding-standards/SKILL.md +113 -0
  230. package/skills/coding/coding-standards/evals.md +34 -0
  231. package/skills/coding/prototype/SKILL.md +86 -0
  232. package/skills/coding/prototype/evals.md +42 -0
  233. package/skills/coding/prototype/references/LOGIC.md +67 -0
  234. package/skills/coding/prototype/references/UI.md +112 -0
  235. package/skills/coding/tdd/SKILL.md +101 -0
  236. package/skills/coding/tdd/evals.md +41 -0
  237. package/skills/coding/tdd/references/mocking.md +59 -0
  238. package/skills/coding/tdd/references/tests.md +77 -0
  239. package/skills/context/context-handoff/SKILL.md +126 -0
  240. package/skills/context/context-handoff/evals.md +57 -0
  241. package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
  242. package/skills/context/context-prime/SKILL.md +95 -0
  243. package/skills/context/context-prime/evals.md +54 -0
  244. package/skills/git/file-ticket/SKILL.md +107 -0
  245. package/skills/git/file-ticket/evals.md +54 -0
  246. package/skills/git/fix-issue/SKILL.md +106 -0
  247. package/skills/git/fix-issue/evals.md +71 -0
  248. package/skills/git/ship/SKILL.md +92 -0
  249. package/skills/git/ship/evals.md +72 -0
  250. package/skills/meta/clio-dev/SKILL.md +91 -0
  251. package/skills/meta/clio-dev/evals.md +45 -0
  252. package/skills/meta/clio-test/SKILL.md +130 -0
  253. package/skills/meta/clio-test/evals.md +43 -0
  254. package/skills/meta/clio-test/references/harness.md +97 -0
  255. package/skills/meta/clio-test/references/test-map.md +59 -0
  256. package/skills/meta/credentials/SKILL.md +125 -0
  257. package/skills/meta/credentials/evals.md +104 -0
  258. package/skills/meta/find-skills/SKILL.md +72 -0
  259. package/skills/meta/find-skills/evals.md +47 -0
  260. package/skills/meta/herdr/SKILL.md +127 -0
  261. package/skills/meta/herdr/evals.md +38 -0
  262. package/skills/meta/skill-craft/SKILL.md +102 -0
  263. package/skills/meta/skill-craft/evals.md +41 -0
  264. package/skills/planning/architecture/SKILL.md +129 -0
  265. package/skills/planning/architecture/evals.md +36 -0
  266. package/skills/planning/backlog/SKILL.md +90 -0
  267. package/skills/planning/backlog/evals.md +43 -0
  268. package/skills/planning/prd/SKILL.md +82 -0
  269. package/skills/planning/prd/evals.md +49 -0
  270. package/skills/planning/product-intent/SKILL.md +112 -0
  271. package/skills/planning/product-intent/evals.md +36 -0
  272. package/skills/planning/tech-spec/SKILL.md +115 -0
  273. package/skills/planning/tech-spec/evals.md +47 -0
  274. package/skills/registry.yaml +136 -0
  275. package/skills/research/arxiv-literature/SKILL.md +104 -0
  276. package/skills/research/arxiv-literature/evals.md +58 -0
  277. package/skills/research/experiment-protocol/SKILL.md +122 -0
  278. package/skills/research/experiment-protocol/evals.md +91 -0
  279. package/skills/research/scientific-debugging/SKILL.md +119 -0
  280. package/skills/research/scientific-debugging/evals.md +138 -0
  281. package/skills/research/scientific-modernization/SKILL.md +138 -0
  282. package/skills/research/scientific-modernization/evals.md +84 -0
  283. package/skills/skill-marketplace.json +12 -20
  284. package/skills/workflow/design-council/SKILL.md +139 -0
  285. package/skills/workflow/design-council/evals.md +97 -0
  286. package/skills/workflow/grill-me/SKILL.md +186 -0
  287. package/skills/workflow/grill-me/evals.md +78 -0
  288. package/skills/workflow/workflow-distiller/SKILL.md +136 -0
  289. package/skills/workflow/workflow-distiller/evals.md +107 -0
  290. package/src/cli/acp.ts +115 -0
  291. package/src/cli/agents.ts +60 -0
  292. package/src/cli/args.ts +280 -0
  293. package/src/cli/argv.ts +170 -0
  294. package/src/cli/ask.ts +34 -0
  295. package/src/cli/auth.ts +468 -0
  296. package/src/cli/bootstrap-generate.ts +498 -0
  297. package/src/cli/clio.ts +96 -0
  298. package/src/cli/components.ts +195 -0
  299. package/src/cli/config-inspect.ts +336 -0
  300. package/src/cli/config.ts +72 -0
  301. package/src/cli/configure-interop.ts +67 -0
  302. package/src/cli/configure-layout.ts +172 -0
  303. package/src/cli/configure.ts +1525 -0
  304. package/src/cli/context-clear.ts +76 -0
  305. package/src/cli/context-index.ts +100 -0
  306. package/src/cli/context.ts +321 -0
  307. package/src/cli/default-target.ts +55 -0
  308. package/src/cli/docs.ts +346 -0
  309. package/src/cli/doctor.ts +54 -0
  310. package/src/cli/eval.ts +315 -0
  311. package/src/cli/evidence.ts +225 -0
  312. package/src/cli/evolve.ts +219 -0
  313. package/src/cli/extensions.ts +214 -0
  314. package/src/cli/fleet.ts +868 -0
  315. package/src/cli/index.ts +338 -0
  316. package/src/cli/init.ts +219 -0
  317. package/src/cli/initial-message.ts +47 -0
  318. package/src/cli/internal-dispatch.ts +55 -0
  319. package/src/cli/memory.ts +182 -0
  320. package/src/cli/models.ts +257 -0
  321. package/src/cli/modes/json-stream.ts +121 -0
  322. package/src/cli/modes/jsonl.ts +9 -0
  323. package/src/cli/modes/print.ts +604 -0
  324. package/src/cli/oauth-manual-input.ts +72 -0
  325. package/src/cli/oauth-select.ts +36 -0
  326. package/src/cli/output-guard.ts +48 -0
  327. package/src/cli/paths.ts +44 -0
  328. package/src/cli/provider-target.ts +113 -0
  329. package/src/cli/removal.ts +72 -0
  330. package/src/cli/reset.ts +253 -0
  331. package/src/cli/run.ts +497 -0
  332. package/src/cli/share.ts +243 -0
  333. package/src/cli/shared.ts +60 -0
  334. package/src/cli/skills-eval.ts +1240 -0
  335. package/src/cli/skills.ts +485 -0
  336. package/src/cli/steer-channel.ts +86 -0
  337. package/src/cli/targets.ts +1154 -0
  338. package/src/cli/text-layout.ts +112 -0
  339. package/src/cli/trace.ts +322 -0
  340. package/src/cli/uninstall.ts +405 -0
  341. package/src/cli/upgrade.ts +286 -0
  342. package/src/cli/usage.ts +876 -0
  343. package/src/cli/validate-model.ts +28 -0
  344. package/src/cli/version.ts +7 -0
  345. package/src/cli/wiki-generate.ts +510 -0
  346. package/src/core/agent-environment.ts +7 -0
  347. package/src/core/artifact-paths.ts +43 -0
  348. package/src/core/bash-exec.ts +313 -0
  349. package/src/core/boot-trace.ts +42 -0
  350. package/src/core/bus-events.ts +744 -0
  351. package/src/core/bus-trace.ts +53 -0
  352. package/src/core/c-header-language.ts +265 -0
  353. package/src/core/clio-repo.ts +78 -0
  354. package/src/core/commit-attribution.ts +157 -0
  355. package/src/core/compile-cache.ts +159 -0
  356. package/src/core/config.ts +1722 -0
  357. package/src/core/context-floor.ts +33 -0
  358. package/src/core/defaults.ts +659 -0
  359. package/src/core/domain-loader.ts +177 -0
  360. package/src/core/event-bus.ts +94 -0
  361. package/src/core/file-references.ts +282 -0
  362. package/src/core/git-commit-attribution.ts +362 -0
  363. package/src/core/guardrails.ts +185 -0
  364. package/src/core/headless-permission.ts +21 -0
  365. package/src/core/incomplete-installation.ts +45 -0
  366. package/src/core/init.ts +223 -0
  367. package/src/core/json-payload.ts +58 -0
  368. package/src/core/package-root.ts +54 -0
  369. package/src/core/path-canonical.ts +37 -0
  370. package/src/core/process-identity.ts +41 -0
  371. package/src/core/recent-models.ts +91 -0
  372. package/src/core/residency-protection.ts +56 -0
  373. package/src/core/resolve-config-value.ts +88 -0
  374. package/src/core/response-schema.ts +279 -0
  375. package/src/core/run-overrides.ts +77 -0
  376. package/src/core/safe-exec.ts +205 -0
  377. package/src/core/safe-id.ts +37 -0
  378. package/src/core/safe-resource-write.ts +162 -0
  379. package/src/core/session-routing.ts +339 -0
  380. package/src/core/settings-layers.ts +354 -0
  381. package/src/core/shared-bus.ts +8 -0
  382. package/src/core/shell-quote.ts +26 -0
  383. package/src/core/skill-activation.ts +190 -0
  384. package/src/core/startup-timer.ts +37 -0
  385. package/src/core/state-file-lock.ts +219 -0
  386. package/src/core/termination.ts +275 -0
  387. package/src/core/timers.ts +38 -0
  388. package/src/core/tool-names.ts +65 -0
  389. package/src/core/verification-scripts.ts +13 -0
  390. package/src/core/workspace-files.ts +492 -0
  391. package/src/core/xdg.ts +177 -0
  392. package/src/domains/agents/builtins/architect.md +4 -4
  393. package/src/domains/agents/builtins/coder.md +3 -2
  394. package/src/domains/agents/builtins/debugger.md +1 -1
  395. package/src/domains/agents/builtins/documenter.md +1 -1
  396. package/src/domains/agents/builtins/git-master.md +3 -3
  397. package/src/domains/agents/builtins/provenance.md +1 -1
  398. package/src/domains/agents/builtins/researcher.md +1 -1
  399. package/src/domains/agents/builtins/scout.md +1 -1
  400. package/src/domains/agents/builtins/tester.md +1 -1
  401. package/src/domains/agents/builtins/verifier.md +1 -1
  402. package/src/domains/agents/builtins/wiki-writer.md +1 -1
  403. package/src/domains/agents/catalog.ts +172 -0
  404. package/src/domains/agents/contract.ts +16 -0
  405. package/src/domains/agents/extension.ts +110 -0
  406. package/src/domains/agents/fleet-commands.ts +154 -0
  407. package/src/domains/agents/fleet-contract.ts +797 -0
  408. package/src/domains/agents/frontmatter.ts +40 -0
  409. package/src/domains/agents/index.ts +102 -0
  410. package/src/domains/agents/manifest.ts +6 -0
  411. package/src/domains/agents/recipe-schema.ts +202 -0
  412. package/src/domains/agents/recipe.ts +125 -0
  413. package/src/domains/agents/registry.ts +111 -0
  414. package/src/domains/agents/result-contract.ts +1265 -0
  415. package/src/domains/agents/spec.ts +371 -0
  416. package/src/domains/agents/write-boundary.ts +109 -0
  417. package/src/domains/components/diff.ts +99 -0
  418. package/src/domains/components/hash.ts +21 -0
  419. package/src/domains/components/index.ts +3 -0
  420. package/src/domains/components/scan.ts +304 -0
  421. package/src/domains/components/snapshot.ts +108 -0
  422. package/src/domains/components/types.ts +82 -0
  423. package/src/domains/config/agent-namespace.ts +13 -0
  424. package/src/domains/config/classify.ts +106 -0
  425. package/src/domains/config/contract.ts +22 -0
  426. package/src/domains/config/extension.ts +142 -0
  427. package/src/domains/config/index.ts +23 -0
  428. package/src/domains/config/keybindings.ts +200 -0
  429. package/src/domains/config/manifest.ts +6 -0
  430. package/src/domains/config/watcher.ts +41 -0
  431. package/src/domains/context/adoption.ts +880 -0
  432. package/src/domains/context/bootstrap-prompt.ts +273 -0
  433. package/src/domains/context/bootstrap.ts +1502 -0
  434. package/src/domains/context/clear.ts +91 -0
  435. package/src/domains/context/clio-md.ts +429 -0
  436. package/src/domains/context/codewiki/artifact.ts +400 -0
  437. package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
  438. package/src/domains/context/codewiki/build-worker.ts +54 -0
  439. package/src/domains/context/codewiki/cmake.ts +356 -0
  440. package/src/domains/context/codewiki/cooperative.ts +46 -0
  441. package/src/domains/context/codewiki/coordinator.ts +182 -0
  442. package/src/domains/context/codewiki/digest.ts +135 -0
  443. package/src/domains/context/codewiki/grammar-assets.ts +33 -0
  444. package/src/domains/context/codewiki/indexer.ts +1529 -0
  445. package/src/domains/context/codewiki/paths.ts +67 -0
  446. package/src/domains/context/codewiki/schema.ts +80 -0
  447. package/src/domains/context/codewiki/tree-sitter.ts +771 -0
  448. package/src/domains/context/contract.ts +68 -0
  449. package/src/domains/context/excluded-dirs.ts +1 -0
  450. package/src/domains/context/extension.ts +358 -0
  451. package/src/domains/context/fingerprint.ts +198 -0
  452. package/src/domains/context/index.ts +154 -0
  453. package/src/domains/context/init-options.ts +69 -0
  454. package/src/domains/context/manifest.ts +6 -0
  455. package/src/domains/context/operator-profile.ts +157 -0
  456. package/src/domains/context/project-metadata.ts +529 -0
  457. package/src/domains/context/project-rules.ts +227 -0
  458. package/src/domains/context/prompt-context.ts +61 -0
  459. package/src/domains/context/refresh.ts +178 -0
  460. package/src/domains/context/runtime.ts +17 -0
  461. package/src/domains/context/sibling-files.ts +10 -0
  462. package/src/domains/context/state.ts +237 -0
  463. package/src/domains/context/wiki/assemble.ts +303 -0
  464. package/src/domains/context/wiki/frontmatter.ts +210 -0
  465. package/src/domains/context/wiki/generate.ts +595 -0
  466. package/src/domains/context/wiki/layout.ts +111 -0
  467. package/src/domains/context/wiki/meta.ts +221 -0
  468. package/src/domains/context/wiki/plan-store.ts +217 -0
  469. package/src/domains/context/wiki/plan.ts +250 -0
  470. package/src/domains/context/wiki/prompts.ts +283 -0
  471. package/src/domains/context/wiki/staleness.ts +198 -0
  472. package/src/domains/dispatch/active-route-planner.ts +168 -0
  473. package/src/domains/dispatch/admission-queue.ts +150 -0
  474. package/src/domains/dispatch/admission.ts +334 -0
  475. package/src/domains/dispatch/agent-candidates.ts +307 -0
  476. package/src/domains/dispatch/agent-ledger-hub.ts +71 -0
  477. package/src/domains/dispatch/agent-ledger-store.ts +257 -0
  478. package/src/domains/dispatch/agent-ledger.ts +212 -0
  479. package/src/domains/dispatch/agent-plan-adapter.ts +85 -0
  480. package/src/domains/dispatch/assignment-events.ts +132 -0
  481. package/src/domains/dispatch/assignment-reconcile.ts +76 -0
  482. package/src/domains/dispatch/assignment-store.ts +162 -0
  483. package/src/domains/dispatch/assignment.ts +278 -0
  484. package/src/domains/dispatch/backoff.ts +59 -0
  485. package/src/domains/dispatch/batch-store.ts +134 -0
  486. package/src/domains/dispatch/batch-tracker.ts +66 -0
  487. package/src/domains/dispatch/capability-match.ts +155 -0
  488. package/src/domains/dispatch/capacity-lease.ts +413 -0
  489. package/src/domains/dispatch/code-step-store.ts +52 -0
  490. package/src/domains/dispatch/code-step.ts +387 -0
  491. package/src/domains/dispatch/contract.ts +278 -0
  492. package/src/domains/dispatch/dedup.ts +133 -0
  493. package/src/domains/dispatch/event-pump.ts +251 -0
  494. package/src/domains/dispatch/execution-handoff.ts +29 -0
  495. package/src/domains/dispatch/execution-plan.ts +328 -0
  496. package/src/domains/dispatch/execution-role.ts +277 -0
  497. package/src/domains/dispatch/execution-scheduler.ts +583 -0
  498. package/src/domains/dispatch/extension.ts +6006 -0
  499. package/src/domains/dispatch/failure-classification.ts +147 -0
  500. package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
  501. package/src/domains/dispatch/fleet-plan.ts +225 -0
  502. package/src/domains/dispatch/fleet-preflight.ts +394 -0
  503. package/src/domains/dispatch/gate-decisions.ts +1040 -0
  504. package/src/domains/dispatch/gate-role-prompts.ts +58 -0
  505. package/src/domains/dispatch/heartbeat.ts +27 -0
  506. package/src/domains/dispatch/index.ts +137 -0
  507. package/src/domains/dispatch/joint-route-adapter.ts +114 -0
  508. package/src/domains/dispatch/joint-route-resolver.ts +283 -0
  509. package/src/domains/dispatch/manifest.ts +6 -0
  510. package/src/domains/dispatch/orphan-recovery.ts +266 -0
  511. package/src/domains/dispatch/outcome.ts +109 -0
  512. package/src/domains/dispatch/phase-timing.ts +47 -0
  513. package/src/domains/dispatch/placement.ts +202 -0
  514. package/src/domains/dispatch/receipt-findings.ts +255 -0
  515. package/src/domains/dispatch/receipt-integrity.ts +383 -0
  516. package/src/domains/dispatch/recovery-candidates.ts +74 -0
  517. package/src/domains/dispatch/reproducibility.ts +48 -0
  518. package/src/domains/dispatch/reservation-store.ts +501 -0
  519. package/src/domains/dispatch/route-approval.ts +177 -0
  520. package/src/domains/dispatch/route-candidates.ts +57 -0
  521. package/src/domains/dispatch/route-decision.ts +1079 -0
  522. package/src/domains/dispatch/route-facts.ts +187 -0
  523. package/src/domains/dispatch/route-history.ts +238 -0
  524. package/src/domains/dispatch/route-observer.ts +488 -0
  525. package/src/domains/dispatch/route-policy.ts +327 -0
  526. package/src/domains/dispatch/route-quality.ts +256 -0
  527. package/src/domains/dispatch/route-readiness.ts +120 -0
  528. package/src/domains/dispatch/routing-intent.ts +287 -0
  529. package/src/domains/dispatch/run-identity.ts +56 -0
  530. package/src/domains/dispatch/scout-transition.ts +130 -0
  531. package/src/domains/dispatch/state.ts +302 -0
  532. package/src/domains/dispatch/tool-stats.ts +132 -0
  533. package/src/domains/dispatch/transport.ts +210 -0
  534. package/src/domains/dispatch/types.ts +799 -0
  535. package/src/domains/dispatch/validation-grounding.ts +154 -0
  536. package/src/domains/dispatch/validation.ts +721 -0
  537. package/src/domains/dispatch/worker-protocol.ts +275 -0
  538. package/src/domains/dispatch/worker-spawn.ts +585 -0
  539. package/src/domains/dispatch/write-boundary-enforcer.ts +97 -0
  540. package/src/domains/dispatch/write-boundary.ts +426 -0
  541. package/src/domains/eval/artifacts/redact.ts +42 -0
  542. package/src/domains/eval/artifacts/store.ts +173 -0
  543. package/src/domains/eval/compare/compare.ts +33 -0
  544. package/src/domains/eval/compare/gates.ts +66 -0
  545. package/src/domains/eval/compare/thresholds.ts +92 -0
  546. package/src/domains/eval/harness-metrics.ts +62 -0
  547. package/src/domains/eval/index.ts +66 -0
  548. package/src/domains/eval/metrics/chaos-stream.ts +93 -0
  549. package/src/domains/eval/metrics/context.ts +34 -0
  550. package/src/domains/eval/metrics/coverage.ts +21 -0
  551. package/src/domains/eval/metrics/evidence.ts +182 -0
  552. package/src/domains/eval/metrics/fleet-loop-stream.ts +196 -0
  553. package/src/domains/eval/metrics/invariants.ts +736 -0
  554. package/src/domains/eval/metrics/latency.ts +4 -0
  555. package/src/domains/eval/metrics/token-stream.ts +139 -0
  556. package/src/domains/eval/metrics/tokens.ts +51 -0
  557. package/src/domains/eval/metrics/tool-calls.ts +7 -0
  558. package/src/domains/eval/proactive-memory.ts +279 -0
  559. package/src/domains/eval/provenance.ts +35 -0
  560. package/src/domains/eval/report.ts +234 -0
  561. package/src/domains/eval/reports/json.ts +5 -0
  562. package/src/domains/eval/reports/junit.ts +23 -0
  563. package/src/domains/eval/reports/markdown.ts +20 -0
  564. package/src/domains/eval/reports/swe-jsonl.ts +16 -0
  565. package/src/domains/eval/reports/text.ts +25 -0
  566. package/src/domains/eval/run-compare.ts +274 -0
  567. package/src/domains/eval/runner.ts +244 -0
  568. package/src/domains/eval/runners/clio-run.ts +148 -0
  569. package/src/domains/eval/runners/context-index.ts +56 -0
  570. package/src/domains/eval/runners/context-init.ts +203 -0
  571. package/src/domains/eval/runners/external-command.ts +350 -0
  572. package/src/domains/eval/runtime-paths.ts +171 -0
  573. package/src/domains/eval/schema/artifact.ts +75 -0
  574. package/src/domains/eval/schema/suite.ts +235 -0
  575. package/src/domains/eval/schema/validate.ts +339 -0
  576. package/src/domains/eval/store.ts +392 -0
  577. package/src/domains/eval/suites/load.ts +74 -0
  578. package/src/domains/eval/suites/matrix.ts +19 -0
  579. package/src/domains/eval/suites/resolve.ts +44 -0
  580. package/src/domains/eval/suites/run.ts +325 -0
  581. package/src/domains/eval/task-file.ts +186 -0
  582. package/src/domains/eval/types.ts +140 -0
  583. package/src/domains/eval/verifiers/command.ts +27 -0
  584. package/src/domains/eval/verifiers/file-exists.ts +6 -0
  585. package/src/domains/eval/verifiers/patch.ts +22 -0
  586. package/src/domains/eval/workspaces/git.ts +41 -0
  587. package/src/domains/eval/workspaces/local.ts +17 -0
  588. package/src/domains/eval/workspaces/temp-copy.ts +29 -0
  589. package/src/domains/evidence/build.ts +1754 -0
  590. package/src/domains/evidence/eval.ts +605 -0
  591. package/src/domains/evidence/failure-attribution.ts +54 -0
  592. package/src/domains/evidence/finish-contract-map.ts +42 -0
  593. package/src/domains/evidence/index.ts +55 -0
  594. package/src/domains/evidence/provenance.ts +200 -0
  595. package/src/domains/evidence/redact.ts +81 -0
  596. package/src/domains/evidence/store.ts +291 -0
  597. package/src/domains/evidence/types.ts +325 -0
  598. package/src/domains/evolution/SELF_EDIT_GATE.md +91 -0
  599. package/src/domains/evolution/index.ts +26 -0
  600. package/src/domains/evolution/manifest.ts +154 -0
  601. package/src/domains/evolution/validate.ts +304 -0
  602. package/src/domains/extensions/contract.ts +22 -0
  603. package/src/domains/extensions/discovery.ts +193 -0
  604. package/src/domains/extensions/extension.ts +46 -0
  605. package/src/domains/extensions/index.ts +34 -0
  606. package/src/domains/extensions/manager.ts +30 -0
  607. package/src/domains/extensions/manifest.ts +6 -0
  608. package/src/domains/extensions/resources.ts +38 -0
  609. package/src/domains/extensions/state.ts +224 -0
  610. package/src/domains/extensions/types.ts +83 -0
  611. package/src/domains/interop/consent.ts +185 -0
  612. package/src/domains/interop/contract.ts +29 -0
  613. package/src/domains/interop/detect.ts +173 -0
  614. package/src/domains/interop/extension.ts +59 -0
  615. package/src/domains/interop/index.ts +34 -0
  616. package/src/domains/interop/manifest.ts +6 -0
  617. package/src/domains/interop/registry.ts +139 -0
  618. package/src/domains/interop/state.ts +92 -0
  619. package/src/domains/interop/types.ts +109 -0
  620. package/src/domains/lifecycle/contract.ts +11 -0
  621. package/src/domains/lifecycle/doctor.ts +536 -0
  622. package/src/domains/lifecycle/extension.ts +23 -0
  623. package/src/domains/lifecycle/index.ts +21 -0
  624. package/src/domains/lifecycle/install-method.ts +51 -0
  625. package/src/domains/lifecycle/manifest.ts +6 -0
  626. package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
  627. package/src/domains/lifecycle/migrations/index.ts +114 -0
  628. package/src/domains/lifecycle/state.ts +93 -0
  629. package/src/domains/lifecycle/upgrade-notice.ts +35 -0
  630. package/src/domains/lifecycle/version.ts +35 -0
  631. package/src/domains/memory/index.ts +128 -0
  632. package/src/domains/memory/operations.ts +143 -0
  633. package/src/domains/memory/prompt-section.ts +86 -0
  634. package/src/domains/memory/proposal.ts +180 -0
  635. package/src/domains/memory/store.ts +163 -0
  636. package/src/domains/memory/task-bank.ts +229 -0
  637. package/src/domains/memory/task-memory-handoff.ts +222 -0
  638. package/src/domains/memory/task-memory-policy.ts +602 -0
  639. package/src/domains/memory/task-memory-status.ts +58 -0
  640. package/src/domains/memory/task-memory-telemetry.ts +346 -0
  641. package/src/domains/memory/task-memory-trace.ts +89 -0
  642. package/src/domains/memory/types.ts +74 -0
  643. package/src/domains/memory/validate.ts +297 -0
  644. package/src/domains/middleware/budget.ts +216 -0
  645. package/src/domains/middleware/contract.ts +35 -0
  646. package/src/domains/middleware/dispatch-nudge.ts +469 -0
  647. package/src/domains/middleware/extension.ts +129 -0
  648. package/src/domains/middleware/hook-receipts.ts +70 -0
  649. package/src/domains/middleware/hooks-io.ts +186 -0
  650. package/src/domains/middleware/hooks.ts +432 -0
  651. package/src/domains/middleware/index.ts +154 -0
  652. package/src/domains/middleware/manifest.ts +6 -0
  653. package/src/domains/middleware/memory-intervention.ts +701 -0
  654. package/src/domains/middleware/rules.ts +42 -0
  655. package/src/domains/middleware/runtime.ts +333 -0
  656. package/src/domains/middleware/skills-reminder.ts +202 -0
  657. package/src/domains/middleware/snapshot.ts +71 -0
  658. package/src/domains/middleware/stalled-turn.ts +71 -0
  659. package/src/domains/middleware/task-board-reminder.ts +87 -0
  660. package/src/domains/middleware/task-nudge.ts +77 -0
  661. package/src/domains/middleware/tool-choice-control.ts +42 -0
  662. package/src/domains/middleware/types.ts +131 -0
  663. package/src/domains/middleware/validate.ts +329 -0
  664. package/src/domains/observability/accountability.ts +67 -0
  665. package/src/domains/observability/contract.ts +168 -0
  666. package/src/domains/observability/cost.ts +206 -0
  667. package/src/domains/observability/evidence-index.ts +123 -0
  668. package/src/domains/observability/extension.ts +281 -0
  669. package/src/domains/observability/index.ts +78 -0
  670. package/src/domains/observability/manifest.ts +10 -0
  671. package/src/domains/observability/metrics.ts +37 -0
  672. package/src/domains/observability/projection.ts +477 -0
  673. package/src/domains/observability/telemetry.ts +89 -0
  674. package/src/domains/observability/trace-store.ts +1454 -0
  675. package/src/domains/prompts/compiler.ts +614 -0
  676. package/src/domains/prompts/contract.ts +46 -0
  677. package/src/domains/prompts/extension.ts +336 -0
  678. package/src/domains/prompts/fragment-loader.ts +138 -0
  679. package/src/domains/prompts/fragments/identity/clio.md +5 -6
  680. package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
  681. package/src/domains/prompts/fragments/identity/self-awareness.md +19 -0
  682. package/src/domains/prompts/fragments/operating/contract.md +4 -41
  683. package/src/domains/prompts/fragments/operating/delegation.md +42 -0
  684. package/src/domains/prompts/fragments/operating/skills.md +26 -0
  685. package/src/domains/prompts/fragments/operating/worker.md +16 -0
  686. package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
  687. package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
  688. package/src/domains/prompts/fragments/safety/read-only.md +4 -4
  689. package/src/domains/prompts/fragments/safety/suggest.md +2 -2
  690. package/src/domains/prompts/fragments/wiki/page.md +10 -0
  691. package/src/domains/prompts/fragments/wiki/plan.md +10 -0
  692. package/src/domains/prompts/hash.ts +73 -0
  693. package/src/domains/prompts/index.ts +32 -0
  694. package/src/domains/prompts/manifest.ts +8 -0
  695. package/src/domains/prompts/memory-intervention.ts +57 -0
  696. package/src/domains/prompts/preload.ts +82 -0
  697. package/src/domains/providers/auth/api-key.ts +47 -0
  698. package/src/domains/providers/auth/backend-file.ts +79 -0
  699. package/src/domains/providers/auth/backend-memory.ts +72 -0
  700. package/src/domains/providers/auth/boot-status.ts +65 -0
  701. package/src/domains/providers/auth/index.ts +37 -0
  702. package/src/domains/providers/auth/oauth.ts +37 -0
  703. package/src/domains/providers/auth/storage.ts +726 -0
  704. package/src/domains/providers/capabilities.ts +58 -0
  705. package/src/domains/providers/catalog.ts +162 -0
  706. package/src/domains/providers/contract.ts +162 -0
  707. package/src/domains/providers/credentials.ts +20 -0
  708. package/src/domains/providers/eligibility.ts +24 -0
  709. package/src/domains/providers/errors.ts +64 -0
  710. package/src/domains/providers/extension.ts +548 -0
  711. package/src/domains/providers/index.ts +142 -0
  712. package/src/domains/providers/knowledge-base-path.ts +102 -0
  713. package/src/domains/providers/manifest.ts +6 -0
  714. package/src/domains/providers/model-capabilities.ts +136 -0
  715. package/src/domains/providers/model-discovery.ts +144 -0
  716. package/src/domains/providers/model-family.ts +32 -0
  717. package/src/domains/providers/model-runtime-capabilities.ts +723 -0
  718. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +120 -37
  719. package/src/domains/providers/plugins.ts +61 -0
  720. package/src/domains/providers/probe/fingerprint.ts +64 -0
  721. package/src/domains/providers/probe/http.ts +114 -0
  722. package/src/domains/providers/probe/reasoning.ts +111 -0
  723. package/src/domains/providers/registry.ts +187 -0
  724. package/src/domains/providers/resolver.ts +156 -0
  725. package/src/domains/providers/runtime-resolution.ts +720 -0
  726. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +90 -0
  727. package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
  728. package/src/domains/providers/runtimes/builtins.ts +78 -0
  729. package/src/domains/providers/runtimes/claude/claude-code.ts +38 -0
  730. package/src/domains/providers/runtimes/claude/claude-sdk.ts +38 -0
  731. package/src/domains/providers/runtimes/claude/common.ts +55 -0
  732. package/src/domains/providers/runtimes/cloud/alcf.ts +171 -0
  733. package/src/domains/providers/runtimes/cloud/anthropic-max.ts +71 -0
  734. package/src/domains/providers/runtimes/cloud/anthropic.ts +43 -0
  735. package/src/domains/providers/runtimes/cloud/bedrock.ts +35 -0
  736. package/src/domains/providers/runtimes/cloud/deepseek.ts +50 -0
  737. package/src/domains/providers/runtimes/cloud/google.ts +41 -0
  738. package/src/domains/providers/runtimes/cloud/groq.ts +46 -0
  739. package/src/domains/providers/runtimes/cloud/mistral.ts +46 -0
  740. package/src/domains/providers/runtimes/cloud/openai-codex.ts +49 -0
  741. package/src/domains/providers/runtimes/cloud/openai.ts +46 -0
  742. package/src/domains/providers/runtimes/cloud/openrouter.ts +111 -0
  743. package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
  744. package/src/domains/providers/runtimes/common/local-synth.ts +164 -0
  745. package/src/domains/providers/runtimes/common/probe-helpers.ts +474 -0
  746. package/src/domains/providers/runtimes/local-native/lemonade-anthropic.ts +27 -0
  747. package/src/domains/providers/runtimes/local-native/lemonade-openai.ts +26 -0
  748. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +88 -0
  749. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +207 -0
  750. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +180 -0
  751. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +126 -0
  752. package/src/domains/providers/runtimes/local-native/llamacpp.ts +98 -0
  753. package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
  754. package/src/domains/providers/runtimes/local-native/ollama-native.ts +117 -0
  755. package/src/domains/providers/runtimes/local-native/sglang.ts +26 -0
  756. package/src/domains/providers/runtimes/local-native/vllm.ts +27 -0
  757. package/src/domains/providers/runtimes/protocol/anthropic-compat.ts +104 -0
  758. package/src/domains/providers/runtimes/protocol/anthropic-messages.ts +28 -0
  759. package/src/domains/providers/runtimes/protocol/bedrock.ts +26 -0
  760. package/src/domains/providers/runtimes/protocol/google.ts +27 -0
  761. package/src/domains/providers/runtimes/protocol/openai-compat.ts +145 -0
  762. package/src/domains/providers/support.ts +241 -0
  763. package/src/domains/providers/thinking-control-policy.ts +85 -0
  764. package/src/domains/providers/types/capability-flags.ts +114 -0
  765. package/src/domains/providers/types/cost-provenance.ts +7 -0
  766. package/src/domains/providers/types/inference.ts +41 -0
  767. package/src/domains/providers/types/knowledge-base.ts +156 -0
  768. package/src/domains/providers/types/local-model-quirks.ts +216 -0
  769. package/src/domains/providers/types/runtime-descriptor.ts +181 -0
  770. package/src/domains/providers/types/target-descriptor.ts +60 -0
  771. package/src/domains/resources/collision.ts +92 -0
  772. package/src/domains/resources/common-loader.ts +140 -0
  773. package/src/domains/resources/contract.ts +24 -0
  774. package/src/domains/resources/extension.ts +59 -0
  775. package/src/domains/resources/index.ts +74 -0
  776. package/src/domains/resources/loader.ts +85 -0
  777. package/src/domains/resources/manifest.ts +6 -0
  778. package/src/domains/resources/prompts/loader.ts +246 -0
  779. package/src/domains/resources/prompts/substitute.ts +1 -0
  780. package/src/domains/resources/skills/content-hash.ts +107 -0
  781. package/src/domains/resources/skills/evals.ts +175 -0
  782. package/src/domains/resources/skills/install.ts +427 -0
  783. package/src/domains/resources/skills/loader.ts +985 -0
  784. package/src/domains/resources/skills/marketplace.ts +301 -0
  785. package/src/domains/resources/skills/provenance-pin.ts +115 -0
  786. package/src/domains/safety/action-classifier.ts +270 -0
  787. package/src/domains/safety/approval-axis.ts +23 -0
  788. package/src/domains/safety/audit.ts +493 -0
  789. package/src/domains/safety/autonomy.ts +174 -0
  790. package/src/domains/safety/call-target.ts +59 -0
  791. package/src/domains/safety/contract.ts +65 -0
  792. package/src/domains/safety/damage-control.ts +44 -0
  793. package/src/domains/safety/default-path-policy.ts +117 -0
  794. package/src/domains/safety/extension.ts +383 -0
  795. package/src/domains/safety/finish-contract-registration.ts +128 -0
  796. package/src/domains/safety/finish-contract.ts +554 -0
  797. package/src/domains/safety/index.ts +44 -0
  798. package/src/domains/safety/loop-detector.ts +143 -0
  799. package/src/domains/safety/manifest.ts +6 -0
  800. package/src/domains/safety/path-policy.ts +164 -0
  801. package/src/domains/safety/policy-engine.ts +994 -0
  802. package/src/domains/safety/project-policy.ts +391 -0
  803. package/src/domains/safety/protected-artifacts-registration.ts +189 -0
  804. package/src/domains/safety/protected-artifacts.ts +993 -0
  805. package/src/domains/safety/rejection-feedback.ts +102 -0
  806. package/src/domains/safety/rigor.ts +72 -0
  807. package/src/domains/safety/rule-compiler.ts +48 -0
  808. package/src/domains/safety/rule-pack-loader.ts +92 -0
  809. package/src/domains/safety/run-effects.ts +197 -0
  810. package/src/domains/safety/scope.ts +61 -0
  811. package/src/domains/scheduling/budget.ts +35 -0
  812. package/src/domains/scheduling/cluster.ts +175 -0
  813. package/src/domains/scheduling/contract.ts +30 -0
  814. package/src/domains/scheduling/extension.ts +91 -0
  815. package/src/domains/scheduling/index.ts +12 -0
  816. package/src/domains/scheduling/manifest.ts +6 -0
  817. package/src/domains/session/archive-readers.ts +208 -0
  818. package/src/domains/session/checkpoint.ts +38 -0
  819. package/src/domains/session/compaction/auto.ts +89 -0
  820. package/src/domains/session/compaction/branch-summary.ts +187 -0
  821. package/src/domains/session/compaction/compact.ts +493 -0
  822. package/src/domains/session/compaction/cut-point.ts +172 -0
  823. package/src/domains/session/compaction/defaults.ts +34 -0
  824. package/src/domains/session/compaction/mask-observations.ts +265 -0
  825. package/src/domains/session/compaction/session-entries.ts +18 -0
  826. package/src/domains/session/compaction/tokens.ts +148 -0
  827. package/src/domains/session/context-accounting.ts +583 -0
  828. package/src/domains/session/context-ledger.ts +362 -0
  829. package/src/domains/session/contract.ts +159 -0
  830. package/src/domains/session/cwd-fallback.ts +65 -0
  831. package/src/domains/session/decision-board.ts +190 -0
  832. package/src/domains/session/entries.ts +572 -0
  833. package/src/domains/session/extension.ts +403 -0
  834. package/src/domains/session/history.ts +172 -0
  835. package/src/domains/session/index.ts +84 -0
  836. package/src/domains/session/manager.ts +128 -0
  837. package/src/domains/session/manifest.ts +6 -0
  838. package/src/domains/session/migrations/index.ts +27 -0
  839. package/src/domains/session/prompt-manifest.ts +173 -0
  840. package/src/domains/session/protected-artifact-journal.ts +241 -0
  841. package/src/domains/session/protected-artifacts.ts +90 -0
  842. package/src/domains/session/retry.ts +230 -0
  843. package/src/domains/session/session-artifacts.ts +107 -0
  844. package/src/domains/session/task-board.ts +569 -0
  845. package/src/domains/session/tree/active-path.ts +137 -0
  846. package/src/domains/session/tree/fork.ts +159 -0
  847. package/src/domains/session/tree/manager.ts +128 -0
  848. package/src/domains/session/tree/navigator.ts +177 -0
  849. package/src/domains/session/tree/preview.ts +259 -0
  850. package/src/domains/session/usage.ts +148 -0
  851. package/src/domains/session/workspace/git-probe.ts +167 -0
  852. package/src/domains/session/workspace/index.ts +4 -0
  853. package/src/domains/session/workspace/project-type.ts +217 -0
  854. package/src/domains/session/workspace/snapshot.ts +74 -0
  855. package/src/domains/share/archive.ts +684 -0
  856. package/src/domains/share/contract.ts +9 -0
  857. package/src/domains/share/extension.ts +26 -0
  858. package/src/domains/share/index.ts +28 -0
  859. package/src/domains/share/manifest.ts +6 -0
  860. package/src/domains/user-tasks/store.ts +297 -0
  861. package/src/engine/acp/adapter.ts +397 -0
  862. package/src/engine/acp/errors.ts +128 -0
  863. package/src/engine/acp/event-mapper.ts +154 -0
  864. package/src/engine/acp/server.ts +2238 -0
  865. package/src/engine/acp/tool-mediator.ts +686 -0
  866. package/src/engine/acp/transport.ts +775 -0
  867. package/src/engine/acp/types.ts +196 -0
  868. package/src/engine/agent.ts +29 -0
  869. package/src/engine/ai.ts +207 -0
  870. package/src/engine/alcf-oauth.ts +205 -0
  871. package/src/engine/antigravity/subprocess-runtime.ts +205 -0
  872. package/src/engine/api-registry.ts +223 -0
  873. package/src/engine/apis/degraded-inference.ts +95 -0
  874. package/src/engine/apis/index.ts +16 -0
  875. package/src/engine/apis/llamacpp-residency.ts +292 -0
  876. package/src/engine/apis/lmstudio-residency.ts +76 -0
  877. package/src/engine/apis/lmstudio.ts +243 -0
  878. package/src/engine/apis/ollama-native.ts +528 -0
  879. package/src/engine/apis/openai-completions.ts +795 -0
  880. package/src/engine/apis/output-budget.ts +99 -0
  881. package/src/engine/apis/residency-lock.ts +29 -0
  882. package/src/engine/apis/residency.ts +776 -0
  883. package/src/engine/apis/resident-models.ts +42 -0
  884. package/src/engine/apis/sampling-overrides.ts +38 -0
  885. package/src/engine/apis/types.ts +16 -0
  886. package/src/engine/claude/sdk-runtime.ts +685 -0
  887. package/src/engine/claude/session-id.ts +5 -0
  888. package/src/engine/claude/subprocess-runtime.ts +316 -0
  889. package/src/engine/claude/tool-safety.ts +400 -0
  890. package/src/engine/env-api-keys.ts +98 -0
  891. package/src/engine/external-subprocess.ts +23 -0
  892. package/src/engine/gemma-channel-filter.ts +223 -0
  893. package/src/engine/harmony-response.ts +165 -0
  894. package/src/engine/instrumented-tui.ts +192 -0
  895. package/src/engine/loop-guard.ts +1127 -0
  896. package/src/engine/messages.ts +14 -0
  897. package/src/engine/models.ts +42 -0
  898. package/src/engine/oauth.ts +196 -0
  899. package/src/engine/pi-mono-names.ts +13 -0
  900. package/src/engine/prompt-templates.ts +1 -0
  901. package/src/engine/provider-payload.ts +272 -0
  902. package/src/engine/select-list.ts +53 -0
  903. package/src/engine/session.ts +834 -0
  904. package/src/engine/strip-tokenizer-sentinels.ts +125 -0
  905. package/src/engine/truncate.ts +9 -0
  906. package/src/engine/tui.ts +111 -0
  907. package/src/engine/types.ts +63 -0
  908. package/src/engine/worker-events.ts +100 -0
  909. package/src/engine/worker-runtime-capabilities.ts +5 -0
  910. package/src/engine/worker-runtime.ts +1022 -0
  911. package/src/engine/worker-tools.ts +208 -0
  912. package/src/entry/boot-options.ts +50 -0
  913. package/src/entry/orchestrator.ts +2011 -0
  914. package/src/interactive/application-controller.ts +433 -0
  915. package/src/interactive/bus-notices.ts +265 -0
  916. package/src/interactive/chat-loop-messages.ts +626 -0
  917. package/src/interactive/chat-loop-policy.ts +18 -0
  918. package/src/interactive/chat-loop.ts +1090 -0
  919. package/src/interactive/chat-panel.ts +1820 -0
  920. package/src/interactive/chat-renderer.ts +1228 -0
  921. package/src/interactive/clio-editor.ts +161 -0
  922. package/src/interactive/command-fallbacks.ts +23 -0
  923. package/src/interactive/command-output.ts +85 -0
  924. package/src/interactive/context-activity.ts +203 -0
  925. package/src/interactive/context-meter.ts +223 -0
  926. package/src/interactive/context-overlay.ts +246 -0
  927. package/src/interactive/cost-overlay.ts +279 -0
  928. package/src/interactive/dispatch-board.ts +1319 -0
  929. package/src/interactive/dispatch-steering.ts +86 -0
  930. package/src/interactive/editor-bash.ts +46 -0
  931. package/src/interactive/editor-steer.ts +51 -0
  932. package/src/interactive/editor-submit.ts +507 -0
  933. package/src/interactive/export-html/ansi-to-html.ts +161 -0
  934. package/src/interactive/export-html/index.ts +51 -0
  935. package/src/interactive/export-html/template.ts +45 -0
  936. package/src/interactive/export-html/tool-renderer.ts +54 -0
  937. package/src/interactive/external-editor.ts +67 -0
  938. package/src/interactive/follow-up-queue-panel.ts +75 -0
  939. package/src/interactive/footer/dashboard.ts +538 -0
  940. package/src/interactive/footer/notifications.ts +275 -0
  941. package/src/interactive/footer/widgets.ts +1264 -0
  942. package/src/interactive/footer-panel.ts +187 -0
  943. package/src/interactive/format-time.ts +97 -0
  944. package/src/interactive/index.ts +11 -0
  945. package/src/interactive/interactive-application.ts +939 -0
  946. package/src/interactive/interactive-event-projection.ts +385 -0
  947. package/src/interactive/interactive-input-runtime.ts +307 -0
  948. package/src/interactive/interactive-presentation.ts +584 -0
  949. package/src/interactive/interactive-shell.ts +388 -0
  950. package/src/interactive/interactive-slash-runtime.ts +554 -0
  951. package/src/interactive/interactive-subscriptions.ts +111 -0
  952. package/src/interactive/interactive-tickers.ts +183 -0
  953. package/src/interactive/keybinding-manager.ts +496 -0
  954. package/src/interactive/layout.ts +57 -0
  955. package/src/interactive/leader-key.ts +143 -0
  956. package/src/interactive/loop-guard-interrupt.ts +101 -0
  957. package/src/interactive/memory-overlay.ts +280 -0
  958. package/src/interactive/overlay-ask-user-lifecycle.ts +110 -0
  959. package/src/interactive/overlay-auth-lifecycle.ts +308 -0
  960. package/src/interactive/overlay-frame.ts +502 -0
  961. package/src/interactive/overlay-general-openers.ts +274 -0
  962. package/src/interactive/overlay-key-routing.ts +216 -0
  963. package/src/interactive/overlay-lifecycle.ts +426 -0
  964. package/src/interactive/overlay-model-selectors.ts +127 -0
  965. package/src/interactive/overlay-permission-lifecycle.ts +345 -0
  966. package/src/interactive/overlay-resource-openers.ts +112 -0
  967. package/src/interactive/overlay-session-lifecycle.ts +327 -0
  968. package/src/interactive/overlay-transitions.ts +57 -0
  969. package/src/interactive/overlays/agents.ts +69 -0
  970. package/src/interactive/overlays/ask-user.ts +921 -0
  971. package/src/interactive/overlays/auth-dialog.ts +221 -0
  972. package/src/interactive/overlays/context-reset.ts +108 -0
  973. package/src/interactive/overlays/cwd-fallback.ts +141 -0
  974. package/src/interactive/overlays/decisions.ts +300 -0
  975. package/src/interactive/overlays/extensions.ts +54 -0
  976. package/src/interactive/overlays/help-reference.ts +145 -0
  977. package/src/interactive/overlays/interop.ts +139 -0
  978. package/src/interactive/overlays/keybinding-detail.ts +90 -0
  979. package/src/interactive/overlays/list-overlay.ts +606 -0
  980. package/src/interactive/overlays/message-picker.ts +155 -0
  981. package/src/interactive/overlays/model-selector.ts +1109 -0
  982. package/src/interactive/overlays/prompts.ts +72 -0
  983. package/src/interactive/overlays/session-selector-search.ts +31 -0
  984. package/src/interactive/overlays/session-selector.ts +284 -0
  985. package/src/interactive/overlays/settings.ts +3451 -0
  986. package/src/interactive/overlays/skills-hub.ts +237 -0
  987. package/src/interactive/overlays/tree-selector.ts +422 -0
  988. package/src/interactive/permission-overlay.ts +170 -0
  989. package/src/interactive/render-trace.ts +534 -0
  990. package/src/interactive/renderers/branch-summary.ts +86 -0
  991. package/src/interactive/renderers/code-ink.ts +420 -0
  992. package/src/interactive/renderers/compaction-summary.ts +112 -0
  993. package/src/interactive/renderers/diff.ts +137 -0
  994. package/src/interactive/renderers/mermaid.ts +53 -0
  995. package/src/interactive/renderers/notice.ts +28 -0
  996. package/src/interactive/renderers/retry-status.ts +31 -0
  997. package/src/interactive/renderers/structured.ts +89 -0
  998. package/src/interactive/renderers/tool-execution.ts +1357 -0
  999. package/src/interactive/renderers/worker-entry.ts +280 -0
  1000. package/src/interactive/session-last-turn.ts +186 -0
  1001. package/src/interactive/session-switch-settlement.ts +10 -0
  1002. package/src/interactive/session-transcript.ts +105 -0
  1003. package/src/interactive/session-usage-reseed.ts +74 -0
  1004. package/src/interactive/slash-autocomplete.ts +256 -0
  1005. package/src/interactive/slash-commands.ts +1407 -0
  1006. package/src/interactive/slash-spec.ts +424 -0
  1007. package/src/interactive/status/controller.ts +260 -0
  1008. package/src/interactive/status/index.ts +23 -0
  1009. package/src/interactive/status/state-machine.ts +530 -0
  1010. package/src/interactive/status/summary.ts +170 -0
  1011. package/src/interactive/status/types.ts +121 -0
  1012. package/src/interactive/status/verbs.ts +163 -0
  1013. package/src/interactive/status/watchdog.ts +24 -0
  1014. package/src/interactive/stdout-backpressure.ts +99 -0
  1015. package/src/interactive/stream-pacer.ts +530 -0
  1016. package/src/interactive/stream-pacing-policy.ts +66 -0
  1017. package/src/interactive/tasks-overlay.ts +540 -0
  1018. package/src/interactive/terminal-lease.ts +485 -0
  1019. package/src/interactive/theme/components.ts +55 -0
  1020. package/src/interactive/theme/glyphs.ts +70 -0
  1021. package/src/interactive/theme/index.ts +15 -0
  1022. package/src/interactive/theme/labels.ts +88 -0
  1023. package/src/interactive/theme/rules.ts +138 -0
  1024. package/src/interactive/theme/segments.ts +88 -0
  1025. package/src/interactive/theme/tokens.ts +121 -0
  1026. package/src/interactive/thinking-level-policy.ts +6 -0
  1027. package/src/interactive/tool-prose-loop.ts +122 -0
  1028. package/src/interactive/tool-prose-registration.ts +48 -0
  1029. package/src/interactive/turn-context.ts +855 -0
  1030. package/src/interactive/turn-middleware.ts +283 -0
  1031. package/src/interactive/turn-persistence.ts +458 -0
  1032. package/src/interactive/turn-queues.ts +181 -0
  1033. package/src/interactive/turn-recovery.ts +323 -0
  1034. package/src/interactive/turn-runtime.ts +869 -0
  1035. package/src/interactive/turn-state.ts +133 -0
  1036. package/src/interactive/view/artifacts.ts +1452 -0
  1037. package/src/interactive/view/view-overlay.ts +735 -0
  1038. package/src/interactive/welcome-dashboard.ts +537 -0
  1039. package/src/interactive/worker-receipts.ts +143 -0
  1040. package/src/interactive/worker-replay.ts +111 -0
  1041. package/src/interactive/worker-share.ts +100 -0
  1042. package/src/interactive/worker-stream.ts +446 -0
  1043. package/src/interactive/workspace-facts.ts +141 -0
  1044. package/src/tools/agent-tools.ts +402 -0
  1045. package/src/tools/artifact.ts +103 -0
  1046. package/src/tools/ask-user.ts +666 -0
  1047. package/src/tools/bash.ts +204 -0
  1048. package/src/tools/bootstrap.ts +77 -0
  1049. package/src/tools/builtin-tool-catalog.ts +265 -0
  1050. package/src/tools/codewiki/code-nav-surface.ts +29 -0
  1051. package/src/tools/codewiki/code-nav.ts +549 -0
  1052. package/src/tools/codewiki/shared.ts +118 -0
  1053. package/src/tools/compete-worktrees.ts +898 -0
  1054. package/src/tools/context/docs-engine.ts +630 -0
  1055. package/src/tools/context/index.ts +514 -0
  1056. package/src/tools/context/surface.ts +19 -0
  1057. package/src/tools/core-bootstrap.ts +168 -0
  1058. package/src/tools/credential-present.ts +151 -0
  1059. package/src/tools/dispatch-admission.ts +533 -0
  1060. package/src/tools/dispatch-arguments.ts +228 -0
  1061. package/src/tools/dispatch-background.ts +54 -0
  1062. package/src/tools/dispatch-event-text.ts +6 -0
  1063. package/src/tools/dispatch-plan.ts +746 -0
  1064. package/src/tools/dispatch-run-events.ts +238 -0
  1065. package/src/tools/dispatch-runner.ts +2370 -0
  1066. package/src/tools/dispatch-scout-admission.ts +295 -0
  1067. package/src/tools/dispatch-scout.ts +461 -0
  1068. package/src/tools/dispatch-types.ts +77 -0
  1069. package/src/tools/dispatch.ts +244 -0
  1070. package/src/tools/edit-diff.ts +534 -0
  1071. package/src/tools/edit.ts +128 -0
  1072. package/src/tools/executables.ts +22 -0
  1073. package/src/tools/file-mutation-queue.ts +35 -0
  1074. package/src/tools/find.ts +294 -0
  1075. package/src/tools/grep.ts +495 -0
  1076. package/src/tools/ignore-policy.ts +169 -0
  1077. package/src/tools/lazy-tool.ts +60 -0
  1078. package/src/tools/ledger.ts +206 -0
  1079. package/src/tools/ls.ts +110 -0
  1080. package/src/tools/monitor-surface.ts +36 -0
  1081. package/src/tools/monitor.ts +767 -0
  1082. package/src/tools/network-policy.ts +29 -0
  1083. package/src/tools/observation.ts +421 -0
  1084. package/src/tools/observers.ts +99 -0
  1085. package/src/tools/path-utils.ts +79 -0
  1086. package/src/tools/policy.ts +154 -0
  1087. package/src/tools/profiles.ts +108 -0
  1088. package/src/tools/read.ts +173 -0
  1089. package/src/tools/registry.ts +1169 -0
  1090. package/src/tools/result-shaping.ts +204 -0
  1091. package/src/tools/safe-exec.ts +128 -0
  1092. package/src/tools/spawn-hygiene.ts +182 -0
  1093. package/src/tools/steer-surface.ts +17 -0
  1094. package/src/tools/steer.ts +77 -0
  1095. package/src/tools/tasks.ts +265 -0
  1096. package/src/tools/truncate-utf8.ts +17 -0
  1097. package/src/tools/truncate.ts +43 -0
  1098. package/src/tools/verify/frontend.ts +687 -0
  1099. package/src/tools/verify/index.ts +31 -0
  1100. package/src/tools/verify/scripts.ts +108 -0
  1101. package/src/tools/verify/surface.ts +46 -0
  1102. package/src/tools/web-fetch-surface.ts +23 -0
  1103. package/src/tools/web-fetch.ts +759 -0
  1104. package/src/tools/worker-evidence.ts +215 -0
  1105. package/src/tools/write.ts +57 -0
  1106. package/src/utils/exif-orientation.ts +192 -0
  1107. package/src/utils/git.ts +24 -0
  1108. package/src/utils/image-resize.ts +148 -0
  1109. package/src/utils/photon.ts +103 -0
  1110. package/src/worker/control-lane.ts +22 -0
  1111. package/src/worker/entry.ts +294 -0
  1112. package/src/worker/event-projection.ts +56 -0
  1113. package/src/worker/heartbeat.ts +24 -0
  1114. package/src/worker/ledger-mirror.ts +95 -0
  1115. package/src/worker/ndjson.ts +40 -0
  1116. package/src/worker/protocol.ts +529 -0
  1117. package/src/worker/resource-facts.ts +79 -0
  1118. package/src/worker/runtime-registry.ts +12 -0
  1119. package/src/worker/spec-contract.ts +675 -0
  1120. package/src/worker/stdin-demux.ts +316 -0
  1121. package/dist/acp-UMLFVA3F.js +0 -92
  1122. package/dist/chunk-262G75JS.js +0 -35
  1123. package/dist/chunk-6CWDTGUC.js +0 -20
  1124. package/dist/chunk-BYEU76JP.js +0 -899
  1125. package/dist/chunk-GNGDQYDU.js +0 -34688
  1126. package/dist/chunk-GOTUR54M.js +0 -9
  1127. package/dist/chunk-NI4DEJMC.js +0 -88
  1128. package/dist/chunk-TPOCL34A.js +0 -2876
  1129. package/dist/chunk-UU3R62TT.js +0 -128
  1130. package/dist/clio-KIQ5SNDS.js +0 -53
  1131. package/dist/configure-G4E3A2PG.js +0 -27
  1132. package/dist/doctor-M5HJJZOL.js +0 -61
  1133. package/dist/domains/agents/builtins/architect.md +0 -33
  1134. package/dist/domains/agents/builtins/coder.md +0 -31
  1135. package/dist/domains/agents/builtins/context-bootstrap.md +0 -38
  1136. package/dist/domains/agents/builtins/debugger.md +0 -30
  1137. package/dist/domains/agents/builtins/documenter.md +0 -31
  1138. package/dist/domains/agents/builtins/git-master.md +0 -30
  1139. package/dist/domains/agents/builtins/provenance.md +0 -30
  1140. package/dist/domains/agents/builtins/researcher.md +0 -71
  1141. package/dist/domains/agents/builtins/scout.md +0 -42
  1142. package/dist/domains/agents/builtins/tester.md +0 -31
  1143. package/dist/domains/agents/builtins/verifier.md +0 -30
  1144. package/dist/domains/agents/builtins/wiki-writer.md +0 -41
  1145. package/dist/fleet-preflight-CLIAX7YR.js +0 -21
  1146. package/dist/preload-KZVHET2B.js +0 -11
  1147. package/dist/version-ROZ6CZKH.js +0 -16
  1148. package/docs/html/agents_blueprint.html +0 -936
  1149. package/docs/html/alcf_blueprint.html +0 -324
  1150. package/docs/html/architecture_blueprint.html +0 -850
  1151. package/docs/html/commands_blueprint.html +0 -794
  1152. package/docs/html/config_knobs_audit_blueprint.html +0 -178
  1153. package/docs/html/configuration_blueprint.html +0 -1080
  1154. package/docs/html/context_blueprint.html +0 -603
  1155. package/docs/html/documentation_blueprint.html +0 -832
  1156. package/docs/html/environment_blueprint.html +0 -404
  1157. package/docs/html/eval_blueprint.html +0 -743
  1158. package/docs/html/evals_internal_blueprint.html +0 -190
  1159. package/docs/html/evolution_blueprint.html +0 -674
  1160. package/docs/html/extensions_blueprint.html +0 -2065
  1161. package/docs/html/fleet_dispatch_blueprint.html +0 -286
  1162. package/docs/html/index.html +0 -919
  1163. package/docs/html/lifecycle_blueprint.html +0 -723
  1164. package/docs/html/memory_blueprint.html +0 -699
  1165. package/docs/html/middleware_blueprint.html +0 -664
  1166. package/docs/html/models_blueprint.html +0 -2366
  1167. package/docs/html/observability_blueprint.html +0 -683
  1168. package/docs/html/provider_adapter_blueprint.html +0 -245
  1169. package/docs/html/safety_blueprint.html +0 -1386
  1170. package/docs/html/shared.css +0 -571
  1171. package/docs/html/shared.js +0 -143
  1172. package/docs/html/skills_blueprint.html +0 -671
  1173. package/docs/html/soak_blueprint.html +0 -182
  1174. package/docs/html/tool_usage_blueprint.html +0 -350
  1175. package/docs/html/tools_blueprint.html +0 -2249
  1176. package/docs/html/trace_blueprint.html +0 -235
  1177. package/docs/html/tui_design_blueprint.html +0 -314
  1178. package/docs/html/validation_blueprint.html +0 -961
  1179. package/docs/html/worker_dispatch_blueprint.html +0 -231
  1180. package/skills/git/commit-crafting/SKILL.md +0 -79
  1181. package/skills/git/commit-crafting/evals.md +0 -92
  1182. package/skills/git/create-pr/SKILL.md +0 -116
  1183. package/skills/git/create-pr/evals.md +0 -114
  1184. package/skills/git/investigate-issue/SKILL.md +0 -139
  1185. package/skills/git/investigate-issue/evals.md +0 -94
  1186. package/skills/git/review-changes/SKILL.md +0 -103
  1187. package/skills/git/review-changes/evals.md +0 -85
@@ -0,0 +1,2011 @@
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 { formatBootTrace } from "../core/boot-trace.js";
6
+ import { BusChannels } from "../core/bus-events.js";
7
+ import { installBusTracer } from "../core/bus-trace.js";
8
+ import { type ClioSettings, readSettings, type SettingsMutator, updateSettings } from "../core/config.js";
9
+ import { DEFAULT_DELEGATION_PERMISSION_TIMEOUT_MS } from "../core/defaults.js";
10
+ import { loadDomains } from "../core/domain-loader.js";
11
+ import { expandInlineFileReferencesAsync } from "../core/file-references.js";
12
+ import { setGitCommitAttributionEnabled } from "../core/git-commit-attribution.js";
13
+ import { configureGuardrails } from "../core/guardrails.js";
14
+ import { HEADLESS_PERMISSION_DENIED_REASON } from "../core/headless-permission.js";
15
+ import { rememberRecentModel } from "../core/recent-models.js";
16
+ import { protectedResidencyModels } from "../core/residency-protection.js";
17
+ import {
18
+ applyOverrides,
19
+ applyRoutingPatch,
20
+ applySessionRouting,
21
+ diffRouting,
22
+ getAtPath,
23
+ isRoutingPath,
24
+ mergeRoutingPatchIntoSettings,
25
+ type RoutingPatch,
26
+ restoreRoutingFields,
27
+ routingChangeNotices,
28
+ routingPatchForId,
29
+ type SessionOverrides,
30
+ seedSessionRouting,
31
+ setAtPath,
32
+ } from "../core/session-routing.js";
33
+ import { getSharedBus } from "../core/shared-bus.js";
34
+ import { isSkillActivation } from "../core/skill-activation.js";
35
+ import { StartupTimer } from "../core/startup-timer.js";
36
+ import { getTerminationCoordinator } from "../core/termination.js";
37
+ import { clioDataDir, clioStateDir } from "../core/xdg.js";
38
+ import { renderAgentCatalogSectionsFromSpecs } from "../domains/agents/catalog.js";
39
+ import type { AgentsContract } from "../domains/agents/contract.js";
40
+ import { AgentsDomainModule } from "../domains/agents/index.js";
41
+ import type { ConfigContract } from "../domains/config/contract.js";
42
+ import { ConfigDomainModule, createConfigDomainModule } from "../domains/config/index.js";
43
+ import type { ContextContract } from "../domains/context/contract.js";
44
+ import { bootstrapInputFromInitOptions } from "../domains/context/init-options.js";
45
+ import { createContextDomainModule } from "../domains/context/runtime.js";
46
+ import type { DispatchContract } from "../domains/dispatch/contract.js";
47
+ import { createDispatchDedupRegistration } from "../domains/dispatch/dedup.js";
48
+ import { agentRoleFactsResolver } from "../domains/dispatch/execution-role.js";
49
+ import { readGateDecisionArtifacts, readPendingGateDecisions } from "../domains/dispatch/gate-decisions.js";
50
+ import { createDispatchDomainModule } from "../domains/dispatch/index.js";
51
+ import { type ExtensionsContract, ExtensionsDomainModule } from "../domains/extensions/index.js";
52
+ import { type InteropContract, InteropDomainModule } from "../domains/interop/index.js";
53
+ import {
54
+ describeUpgradeNotice,
55
+ ensureClioState,
56
+ LifecycleDomainModule,
57
+ takeUpgradeNotice,
58
+ } from "../domains/lifecycle/index.js";
59
+ import { getVersionInfo } from "../domains/lifecycle/version.js";
60
+ import {
61
+ buildMemoryPromptSection,
62
+ canonicalMemoryRepositoryIdentity,
63
+ createTaskMemoryTelemetrySink,
64
+ createTaskMemoryTrace,
65
+ loadMemoryRecordsSync,
66
+ renderTaskMemoryHandoffSource,
67
+ seedTaskMemoryFromNewestHandoff,
68
+ type TaskMemoryModelClient,
69
+ taskMemoryBankSize,
70
+ taskMemoryHandoffSeedOffer,
71
+ taskMemoryTracePath,
72
+ } from "../domains/memory/index.js";
73
+ import { TaskMemoryBank } from "../domains/memory/task-bank.js";
74
+ import {
75
+ createDetachedDispatchNudgeRegistration,
76
+ createReadOnlyExplorationNudgeRegistration,
77
+ createUnbackedWorkerClaimRegistration,
78
+ openDetachedBatchViews,
79
+ } from "../domains/middleware/dispatch-nudge.js";
80
+ import {
81
+ createHookReceiptLog,
82
+ createMiddlewareToolChoiceControl,
83
+ createSkillsReminderRegistration,
84
+ type ExtensionHookRoot,
85
+ installUserHooks,
86
+ type MiddlewareContract,
87
+ MiddlewareDomainModule,
88
+ writeMiddlewareDiagnosticToStderr,
89
+ } from "../domains/middleware/index.js";
90
+ import { createMemoryInterventionRegistration } from "../domains/middleware/memory-intervention.js";
91
+ import { createTaskBoardReminderRegistration } from "../domains/middleware/task-board-reminder.js";
92
+ import { createTaskNudgeRegistration } from "../domains/middleware/task-nudge.js";
93
+ import type { ObservabilityContract } from "../domains/observability/index.js";
94
+ import { ObservabilityDomainModule } from "../domains/observability/index.js";
95
+ import type { PromptsContract } from "../domains/prompts/contract.js";
96
+ import { createPromptsDomainModule } from "../domains/prompts/index.js";
97
+ import type { ProvidersContract, TargetDescriptor, ThinkingLevel } from "../domains/providers/index.js";
98
+ import {
99
+ AGENT_ROLE_TOOLS_REQUIRED_REASON,
100
+ applyModelCapabilityPatch,
101
+ firstRuntimeResolutionError,
102
+ isOrchestratorEligibleRuntime,
103
+ ProvidersDomainModule,
104
+ refineRuntimeTargetWithModelHints,
105
+ resolveModelCapabilities,
106
+ resolveModelRuntimeCapabilitiesForProviders,
107
+ resolveRuntimeTarget,
108
+ supportsAgentRoleTools,
109
+ targetRequiresAuth,
110
+ VALID_THINKING_LEVELS,
111
+ } from "../domains/providers/index.js";
112
+ import { getRuntimeRegistry } from "../domains/providers/registry.js";
113
+ import { registerBuiltinRuntimes } from "../domains/providers/runtimes/builtins.js";
114
+ import {
115
+ createResourcesDomainModule,
116
+ discoverMarketplaceSkills,
117
+ modelVisibleSkills,
118
+ type ResourcesContract,
119
+ } from "../domains/resources/index.js";
120
+ import { DEFAULT_RECENT_ENTRY_LIMIT } from "../domains/safety/finish-contract.js";
121
+ import { createFinishContractRegistration } from "../domains/safety/finish-contract-registration.js";
122
+ import type { AutonomyLevel, SafetyContract } from "../domains/safety/index.js";
123
+ import { parseRigorOverride, resolveRigor, SafetyDomainModule } from "../domains/safety/index.js";
124
+ import type { ProtectedArtifactState } from "../domains/safety/protected-artifacts.js";
125
+ import {
126
+ createProtectedArtifactsRegistration,
127
+ type ProtectedArtifactProtectEvent,
128
+ } from "../domains/safety/protected-artifacts-registration.js";
129
+ import type { SchedulingContract } from "../domains/scheduling/contract.js";
130
+ import { SchedulingDomainModule } from "../domains/scheduling/index.js";
131
+ import { type CompactResult, compact } from "../domains/session/compaction/compact.js";
132
+ import { collectSessionEntries } from "../domains/session/compaction/session-entries.js";
133
+ import { estimateTokens } from "../domains/session/compaction/tokens.js";
134
+ import { ceilChars } from "../domains/session/context-accounting.js";
135
+ import type { SessionContract, SessionMeta } from "../domains/session/contract.js";
136
+ import { createDecisionBoardStore } from "../domains/session/decision-board.js";
137
+ import type { CompactionSummaryEntry, CompactionTrigger, SessionEntry } from "../domains/session/entries.js";
138
+ import { SessionDomainModule } from "../domains/session/index.js";
139
+ import {
140
+ clearPendingProtectedArtifact,
141
+ reconcilePendingProtectedArtifacts,
142
+ stagePendingProtectedArtifact,
143
+ } from "../domains/session/protected-artifact-journal.js";
144
+ import {
145
+ protectedArtifactEntryFromArtifact,
146
+ protectedArtifactStateFromSessionEntries,
147
+ } from "../domains/session/protected-artifacts.js";
148
+ import { createTaskBoardStore } from "../domains/session/task-board.js";
149
+ import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
150
+ import { type ShareContract, ShareDomainModule } from "../domains/share/index.js";
151
+ import { createUserTasksStore } from "../domains/user-tasks/store.js";
152
+ import { type AcpSafeSettingsPatch, type AcpSafeSettingsSnapshot, serveClioAcpAgent } from "../engine/acp/server.js";
153
+ import { createStdioServerTransport } from "../engine/acp/transport.js";
154
+ import { completeEngineText } from "../engine/ai.js";
155
+ import { setProtectedModelsProvider } from "../engine/apis/residency.js";
156
+ import {
157
+ createLoopGuardRegistration,
158
+ INTERACTIVE_LOOP_BLOCK_BUDGET,
159
+ readOrchTurnToolCallBudget,
160
+ } from "../engine/loop-guard.js";
161
+ import { openSession, readSessionTailTurns, sessionCurrentPath, sessionPaths } from "../engine/session.js";
162
+ import type { EngineModel } from "../engine/types.js";
163
+ import { createChatLoop } from "../interactive/chat-loop.js";
164
+ import { buildReplayAgentMessagesFromTurns } from "../interactive/chat-renderer.js";
165
+ import { type RunIo, startInteractive } from "../interactive/index.js";
166
+ import type { BootOptions } from "./boot-options.js";
167
+
168
+ export type { BootOptions, HeadlessSamplingOverrides } from "./boot-options.js";
169
+
170
+ import {
171
+ detectPlatformKeybindingWarnings,
172
+ detectTerminalKeySupport,
173
+ formatInvalidKeybindingNotice,
174
+ formatPlatformKeybindingNotice,
175
+ validateKeybindings,
176
+ } from "../interactive/keybinding-manager.js";
177
+ import { subscribeLoopGuardStop } from "../interactive/loop-guard-interrupt.js";
178
+ import { BUILTIN_SLASH_COMMANDS } from "../interactive/slash-commands.js";
179
+ import { createToolProseRegistration } from "../interactive/tool-prose-registration.js";
180
+ import { type AskUserHandler, cancelledAskUserResult } from "../tools/ask-user.js";
181
+ import { registerAllTools } from "../tools/bootstrap.js";
182
+ import { isGitRepository, recoverCleanupReadyCompeteGroups } from "../tools/compete-worktrees.js";
183
+ import { createDispatchBackgroundRegistry } from "../tools/dispatch-background.js";
184
+ import { createFileMutationObserver, createSkillActivationObserver } from "../tools/observers.js";
185
+ import { createRegistry } from "../tools/registry.js";
186
+
187
+ export interface BootResult {
188
+ exitCode: number;
189
+ bootTimeMs: number;
190
+ }
191
+
192
+ /**
193
+ * The bannered boot is the whole of what a piped or CI invocation of bare
194
+ * `clio` shows, so it is the entire first impression for a stranger who is not
195
+ * on a TTY. It used to end in a hardcoded `ready`, a word with no relationship
196
+ * to anything: a machine with no target configured at all printed it and
197
+ * exited 0, which is the one state where the installation can do nothing.
198
+ *
199
+ * What the line reports now is the orchestrator target the settings actually
200
+ * declare, and when none is declared it says so and names the command that
201
+ * fixes it. The exit status stays 0 either way, because this path answers
202
+ * "did Clio boot", which it did, and CI scripts already depend on that
203
+ * answer; the readiness of the configuration is reported in words instead.
204
+ */
205
+ function bannerConfigurationLine(): string {
206
+ let settings: ClioSettings;
207
+ try {
208
+ settings = readSettings();
209
+ } catch {
210
+ return chalk.yellow("settings.yaml is not valid. Run `clio-coder doctor` for the exact keys.");
211
+ }
212
+ // A dangling chat target normalizes to null in the schema, so a deleted
213
+ // target arrives here as no target at all rather than as a name to report.
214
+ const targetId = settings.orchestrator?.target;
215
+ if (!targetId) {
216
+ return chalk.yellow("no model target configured. Run `clio-coder configure` to add one.");
217
+ }
218
+ const model = settings.orchestrator?.model;
219
+ return chalk.dim(`target ${targetId}${model ? ` · model ${model}` : " · no default model"}`);
220
+ }
221
+
222
+ function buildBanner(): string {
223
+ const { clio } = getVersionInfo();
224
+ return `
225
+ ${chalk.cyan("Clio Coder")}
226
+ ${chalk.dim(`v${clio} · CLIO: Context Layer for I/O · HPC & scientific software`)}
227
+ ${bannerConfigurationLine()}
228
+ `;
229
+ }
230
+
231
+ function printJsonSessionHeader(meta: SessionMeta | null): Record<string, unknown> | null {
232
+ if (!meta) return null;
233
+ return {
234
+ type: "session",
235
+ version: meta.sessionFormatVersion ?? 1,
236
+ id: meta.id,
237
+ timestamp: meta.createdAt,
238
+ cwd: meta.cwd,
239
+ target: meta.target,
240
+ model: meta.model,
241
+ clioVersion: meta.clioVersion,
242
+ };
243
+ }
244
+
245
+ function applyHeadlessSettingsOverlay(
246
+ settings: ClioSettings,
247
+ overrides: BootOptions["headless"] | undefined,
248
+ ): ClioSettings {
249
+ const next = structuredClone(settings);
250
+ if (!overrides) return next;
251
+ const previousTarget = next.orchestrator.target;
252
+ if (overrides.target !== undefined) {
253
+ next.orchestrator.target = overrides.target;
254
+ if (overrides.model === undefined && (previousTarget !== overrides.target || !next.orchestrator.model)) {
255
+ const target = next.targets.find((entry) => entry.id === overrides.target);
256
+ if (target) next.orchestrator.model = target.defaultModel ?? null;
257
+ }
258
+ }
259
+ if (overrides.model !== undefined) next.orchestrator.model = overrides.model;
260
+ if (overrides.thinking !== undefined) next.orchestrator.thinkingLevel = overrides.thinking;
261
+ if (overrides.autonomy !== undefined) next.autonomy = overrides.autonomy;
262
+ return next;
263
+ }
264
+
265
+ interface CompactionResolution {
266
+ model: EngineModel;
267
+ targetId: string;
268
+ apiKey?: string;
269
+ }
270
+
271
+ function resolveTarget(providers: ProvidersContract, targetId: string | null | undefined): TargetDescriptor | null {
272
+ if (!targetId) return null;
273
+ return providers.getTarget(targetId);
274
+ }
275
+
276
+ function advanceThinkingLevel(current: ThinkingLevel, available: ReadonlyArray<ThinkingLevel>): ThinkingLevel {
277
+ const levels = available.length > 0 ? available : VALID_THINKING_LEVELS;
278
+ if (!levels.includes(current)) return levels[0] ?? "off";
279
+ const normalized = current;
280
+ const idx = levels.indexOf(normalized);
281
+ return levels[(idx + 1) % levels.length] ?? "off";
282
+ }
283
+
284
+ /**
285
+ * pi-ai's openai-completions provider refuses to stream without an apiKey even
286
+ * when the target is a local server that ignores the Authorization header
287
+ * entirely. The chat loop, the dispatch workers, and the background memory
288
+ * model all send this placeholder so a local llama.cpp or LM Studio endpoint
289
+ * works without the user inventing a credential.
290
+ */
291
+ const LOCAL_API_KEY_FALLBACK = "clio-local-target";
292
+
293
+ export async function resolveApiKeyForTarget(
294
+ target: TargetDescriptor,
295
+ providers: ProvidersContract,
296
+ signal?: AbortSignal,
297
+ ): Promise<string | undefined> {
298
+ const runtime = providers.getRuntime(target.runtime);
299
+ if (!runtime) return undefined;
300
+ // A target that needs no credential still needs the placeholder. Returning
301
+ // nothing here left compaction as the one path that did not send it, so
302
+ // `/context compact` and every automatic compaction at the window threshold
303
+ // failed on exactly the local runtimes Clio is built for, while ordinary
304
+ // turns against the same target succeeded.
305
+ if (!targetRequiresAuth(target, runtime)) return LOCAL_API_KEY_FALLBACK;
306
+ const resolved = await providers.auth.resolveForTarget(target, runtime, signal ? { signal } : undefined);
307
+ return resolved.apiKey;
308
+ }
309
+
310
+ function createBackgroundMemoryModelClient(
311
+ providers: ProvidersContract,
312
+ settings: Readonly<ClioSettings>,
313
+ timeoutMs: number,
314
+ ): TaskMemoryModelClient | null {
315
+ const targetId = settings.background.target?.trim();
316
+ const wireModelId = settings.background.model?.trim();
317
+ if (!targetId || !wireModelId) return null;
318
+ // One model cannot both drive the action agent and think its way through a
319
+ // memory step: the memory call would contend with the agent's own decoding on
320
+ // the same server, and a reasoning model spends most of a memory step
321
+ // deliberating over a task it is not executing. Memory falls back to its free
322
+ // deterministic tier rather than degrading the work the operator asked for.
323
+ if (backgroundSharesReasoningModelWithOrchestrator(providers, settings)) return null;
324
+ const resolved = resolveRuntimeTarget(providers, {
325
+ targetId,
326
+ wireModelId,
327
+ // Memory reads a trajectory and writes a fixed envelope. Reasoning adds
328
+ // latency and, on a small local model, routinely consumes the entire
329
+ // output budget before the envelope is ever written.
330
+ requestedThinkingLevel: "off",
331
+ use: "orchestrator",
332
+ requireTools: false,
333
+ requireOutputBudget: true,
334
+ });
335
+ if (!resolved.ok) {
336
+ const detail = firstRuntimeResolutionError(resolved.diagnostics) ?? "background target resolution failed";
337
+ throw new Error(detail);
338
+ }
339
+ const kbHit = providers.knowledgeBase?.lookup(resolved.target.wireModelId) ?? null;
340
+ const model = resolved.target.runtime.synthesizeModel(resolved.target.target, resolved.target.wireModelId, kbHit);
341
+ const refined = refineRuntimeTargetWithModelHints(resolved.target, model, providers.knowledgeBase);
342
+ applyModelCapabilityPatch(model, refined.capabilities);
343
+ return {
344
+ async complete(request) {
345
+ const apiKey = targetRequiresAuth(refined.target, refined.runtime)
346
+ ? (await providers.auth.resolveForTarget(refined.target, refined.runtime, { signal: request.signal })).apiKey
347
+ : LOCAL_API_KEY_FALLBACK;
348
+ return completeEngineText({
349
+ model,
350
+ systemPrompt: request.systemPrompt,
351
+ userPrompt: request.userPrompt,
352
+ maxTokens: request.maxTokens,
353
+ // Always off, never the operator's chat thinking level. A model that
354
+ // reasons anyway still works: `completeEngineText` keeps only text
355
+ // blocks, and the memory output budget leaves room for the preamble.
356
+ thinkingLevel: "off",
357
+ signal: request.signal,
358
+ timeoutMs,
359
+ ...(apiKey === undefined ? {} : { apiKey }),
360
+ });
361
+ },
362
+ };
363
+ }
364
+
365
+ /**
366
+ * Warn about a configured agent-role model the provider reports cannot call
367
+ * tools. Selection already refuses these, so reaching here means the config
368
+ * predates the check or was hand-edited; a run would otherwise fail later at
369
+ * dispatch admission with a message that names a missing tool rather than the
370
+ * model that cannot use any.
371
+ */
372
+ function agentRoleToolWarnings(providers: ProvidersContract, settings: Readonly<ClioSettings>): string[] {
373
+ const roles: ReadonlyArray<{ label: string; target: string | null; model: string | null }> = [
374
+ { label: "orchestrator", target: settings.orchestrator.target, model: settings.orchestrator.model },
375
+ { label: "workers.default", target: settings.workers.default.target, model: settings.workers.default.model },
376
+ ];
377
+ const warnings: string[] = [];
378
+ for (const role of roles) {
379
+ const targetId = role.target?.trim();
380
+ const wireModelId = role.model?.trim();
381
+ if (!targetId || !wireModelId) continue;
382
+ try {
383
+ const status = providers.list().find((entry) => entry.target.id === targetId);
384
+ if (!status) continue;
385
+ const capabilities = resolveModelCapabilities(status, wireModelId, providers.knowledgeBase);
386
+ if (supportsAgentRoleTools(capabilities)) continue;
387
+ warnings.push(
388
+ `${role.label} model '${wireModelId}' on target '${targetId}' ${AGENT_ROLE_TOOLS_REQUIRED_REASON}. ` +
389
+ `Pick another model, or state the correction in a model-catalog.d entry if the provider's flag is wrong.`,
390
+ );
391
+ } catch {
392
+ // An unresolvable capability is not evidence of a missing one.
393
+ }
394
+ }
395
+ return warnings;
396
+ }
397
+
398
+ /**
399
+ * True when the background role names the same model the orchestrator runs and
400
+ * that model reasons. Distinct models, or a non-reasoning shared model, are both
401
+ * fine; this only catches the one-model-does-everything configuration.
402
+ */
403
+ function backgroundSharesReasoningModelWithOrchestrator(
404
+ providers: ProvidersContract,
405
+ settings: Readonly<ClioSettings>,
406
+ ): boolean {
407
+ const backgroundModel = settings.background.model?.trim();
408
+ const orchestratorModel = settings.orchestrator.model?.trim();
409
+ if (!backgroundModel || backgroundModel !== orchestratorModel) return false;
410
+ if (settings.background.target?.trim() !== settings.orchestrator.target?.trim()) return false;
411
+ try {
412
+ const status = providers.list().find((entry) => entry.target.id === settings.background.target?.trim());
413
+ if (!status) return false;
414
+ return resolveModelCapabilities(status, backgroundModel, providers.knowledgeBase).reasoning === true;
415
+ } catch {
416
+ // An unresolvable capability is not evidence of a reasoning model; the
417
+ // operator's explicit configuration stands.
418
+ return false;
419
+ }
420
+ }
421
+
422
+ export function synthesizeOrchestratorModel(
423
+ providers: ProvidersContract,
424
+ target: TargetDescriptor,
425
+ wireModelId: string,
426
+ ): EngineModel | null {
427
+ const runtime = providers.getRuntime(target.runtime);
428
+ if (!runtime) return null;
429
+ let model: EngineModel;
430
+ try {
431
+ const kbHit = providers.knowledgeBase?.lookup(wireModelId) ?? null;
432
+ model = runtime.synthesizeModel(target, wireModelId, kbHit);
433
+ } catch {
434
+ return null;
435
+ }
436
+ try {
437
+ const status = providers.list().find((entry) => entry.target.id === target.id);
438
+ if (status) {
439
+ const caps = resolveModelCapabilities(status, wireModelId, providers.knowledgeBase, {
440
+ detectedReasoning: providers.getDetectedReasoning(target.id, wireModelId),
441
+ });
442
+ applyModelCapabilityPatch(model, caps);
443
+ }
444
+ } catch {
445
+ // Older test doubles and degraded provider bundles may not expose live
446
+ // status. The synthesized model still carries runtime and catalog caps.
447
+ }
448
+ return model;
449
+ }
450
+
451
+ async function resolveCompactionModel(
452
+ settings: ClioSettings,
453
+ providers: ProvidersContract,
454
+ ): Promise<CompactionResolution | null> {
455
+ const targetId = settings.orchestrator?.target ?? null;
456
+ const wireModelId = settings.orchestrator?.model ?? null;
457
+ if (!targetId || !wireModelId) return null;
458
+ const target = resolveTarget(providers, targetId);
459
+ if (!target) return null;
460
+ const model = synthesizeOrchestratorModel(providers, target, wireModelId);
461
+ if (!model) return null;
462
+ const apiKey = await resolveApiKeyForTarget(target, providers);
463
+ const resolution: CompactionResolution = { model, targetId };
464
+ if (apiKey !== undefined) resolution.apiKey = apiKey;
465
+ return resolution;
466
+ }
467
+
468
+ function readSessionEntriesForCompact(sessionId: string): SessionEntry[] {
469
+ const reader = openSession(sessionId);
470
+ return collectSessionEntries(reader.turns(), sessionPaths(reader.meta()).current);
471
+ }
472
+
473
+ /**
474
+ * The finish-contract only inspects the window since the last user message
475
+ * (`recentEntries`, capped at 80), so it reads a bounded tail of the ledger
476
+ * instead of parsing the whole file every turn_end. 160 = twice the 80-entry cap
477
+ * leaves ample margin above any entries appended after the assistant turn, so the
478
+ * assessed window is byte-identical to the whole-file read (see the
479
+ * behaviour-equivalence contract test) while cost stays bounded by session
480
+ * *shape*, not session *length*.
481
+ */
482
+ const FINISH_CONTRACT_TAIL_ENTRIES = DEFAULT_RECENT_ENTRY_LIMIT * 2;
483
+
484
+ function readRecentSessionEntriesForContract(sessionId: string): SessionEntry[] {
485
+ return collectSessionEntries(
486
+ readSessionTailTurns(sessionId, FINISH_CONTRACT_TAIL_ENTRIES).entries,
487
+ sessionCurrentPath(sessionId),
488
+ );
489
+ }
490
+
491
+ function protectedArtifactStateForCurrentSession(
492
+ session: SessionContract,
493
+ ): ReturnType<typeof protectedArtifactStateFromSessionEntries> {
494
+ const meta = session.current();
495
+ if (!meta) return { artifacts: [] };
496
+ reconcilePendingProtectedArtifacts(session);
497
+ return protectedArtifactStateFromSessionEntries(readSessionEntriesForCompact(meta.id));
498
+ }
499
+
500
+ function appendProtectedArtifactRegistryEvent(
501
+ session: SessionContract | undefined,
502
+ event: ProtectedArtifactProtectEvent,
503
+ ): void {
504
+ const current = session?.current();
505
+ if (session === undefined || current === null || current === undefined) {
506
+ throw new Error("no active session is available for protected artifact persistence");
507
+ }
508
+ const pending = stagePendingProtectedArtifact(current.id, event);
509
+ session.appendEntry(
510
+ protectedArtifactEntryFromArtifact(event.artifact, {
511
+ parentTurnId: event.turnId ?? null,
512
+ toolName: event.toolName,
513
+ ...(event.toolCallId !== undefined ? { toolCallId: event.toolCallId } : {}),
514
+ ...(event.runId !== undefined ? { runId: event.runId } : {}),
515
+ ...(event.correlationId !== undefined ? { correlationId: event.correlationId } : {}),
516
+ }),
517
+ );
518
+ if (session.flushAppends === undefined) {
519
+ throw new Error("session does not expose the durable append flush required by protected artifact persistence");
520
+ }
521
+ session.flushAppends();
522
+ clearPendingProtectedArtifact(pending);
523
+ }
524
+
525
+ /**
526
+ * Fold a terminal dispatch payload's worker skill activations into the session
527
+ * ledger, tagged with the runId, so worker skill provenance sits next to
528
+ * main-agent activations.
529
+ *
530
+ * Both terminal channels carry them and the run finalizer emits exactly one of
531
+ * the two, so this cannot double-record. The orchestrator never observes worker
532
+ * tool calls directly, because a worker runs its own registry in its own
533
+ * subprocess, which makes this the only recording path there is. Returns how
534
+ * many were folded.
535
+ */
536
+ export function foldDispatchSkillActivations(
537
+ session: SessionContract | undefined,
538
+ payload: { runId?: unknown; skillActivations?: ReadonlyArray<unknown> } | undefined,
539
+ ): number {
540
+ const runId = payload?.runId;
541
+ if (typeof runId !== "string") return 0;
542
+ let folded = 0;
543
+ for (const activation of payload?.skillActivations ?? []) {
544
+ if (!isSkillActivation(activation)) continue;
545
+ appendSkillActivationRegistryEvent(session, { ...activation, runId });
546
+ folded += 1;
547
+ }
548
+ return folded;
549
+ }
550
+
551
+ function appendSkillActivationRegistryEvent(
552
+ session: SessionContract | undefined,
553
+ activation: Parameters<SessionContract["recordSkillActivation"]>[0],
554
+ ): void {
555
+ if (!session?.current()) return;
556
+ try {
557
+ session.recordSkillActivation(activation);
558
+ } catch {
559
+ // Activation metadata should never alter the result of a completed
560
+ // context(scope=skills) call. Missing ledger data is visible in diagnostics.
561
+ }
562
+ }
563
+
564
+ async function runCompactionFlow(
565
+ session: SessionContract,
566
+ settings: ClioSettings,
567
+ providers: ProvidersContract,
568
+ instructions?: string,
569
+ trigger?: CompactionTrigger,
570
+ ): Promise<CompactResult | null> {
571
+ const meta = session.current();
572
+ if (!meta) {
573
+ throw new Error("no current session to compact; start one with /new or /resume first");
574
+ }
575
+ const resolved = await resolveCompactionModel(settings, providers);
576
+ if (!resolved) {
577
+ throw new Error("no model configured; set orchestrator.target + orchestrator.model");
578
+ }
579
+ // Summarize only the active branch: after a /tree switch the raw file
580
+ // still holds abandoned sibling turns, and a summary that folds them in
581
+ // would persist abandoned content back into the active context. The full
582
+ // file read stays in place for protected artifacts and the masking
583
+ // rewrite, which are session-global. The task board is not: it used to
584
+ // read the full file too (last taskLedger entry in file order, with no
585
+ // branch filter at all), which was a second, independent instance of this
586
+ // same bug. See the taskBoard wiring below for the fix.
587
+ const activeLeafTurnId = session.tree(meta.id).leafId ?? undefined;
588
+ const entries = filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), activeLeafTurnId);
589
+ if (entries.length === 0) return null;
590
+
591
+ const result = await compact({
592
+ entries,
593
+ model: resolved.model,
594
+ ...(resolved.apiKey !== undefined ? { apiKey: resolved.apiKey } : {}),
595
+ ...(instructions !== undefined ? { instructions } : {}),
596
+ });
597
+ if (result.messagesSummarized === 0 || result.summary.length === 0) return null;
598
+
599
+ const entry: Omit<CompactionSummaryEntry, "turnId" | "timestamp"> = {
600
+ kind: "compactionSummary",
601
+ parentTurnId: result.firstKeptTurnId ?? null,
602
+ summary: result.summary,
603
+ tokensBefore: result.tokensBefore,
604
+ firstKeptTurnId: result.firstKeptTurnId ?? "",
605
+ messagesSummarized: result.messagesSummarized,
606
+ isSplitTurn: result.isSplitTurn,
607
+ tokensAfter: estimateTokensAfterCompaction(entries, result),
608
+ // The summarization call is a real model call. Persisting its provider
609
+ // usage on the entry is what puts it in front of `/cost` and `clio-coder usage
610
+ // report`, which folded the ledger and so counted every call but this one.
611
+ ...(result.usage !== undefined ? { usage: result.usage } : {}),
612
+ };
613
+ if (trigger !== undefined) entry.trigger = trigger;
614
+ session.appendEntry(entry);
615
+ return result;
616
+ }
617
+
618
+ /**
619
+ * Compose the production chat-loop compaction callback. Errors intentionally
620
+ * propagate: the chat loop owns activity failure reporting and distinguishes
621
+ * a thrown read/model/persistence failure from the legitimate null no-op that
622
+ * `runCompactionFlow` returns for an empty session or an unavailable cut.
623
+ */
624
+ export function createProductionAutoCompact(
625
+ session: SessionContract,
626
+ getSettings: () => ClioSettings,
627
+ providers: ProvidersContract,
628
+ ): (instructions?: string, trigger?: CompactionTrigger) => Promise<CompactResult | null> {
629
+ return (instructions, trigger) => runCompactionFlow(session, getSettings(), providers, instructions, trigger);
630
+ }
631
+
632
+ function estimateTokensFromSummary(summary: string): number {
633
+ // Mirrors the rough byte/4 heuristic the rest of the compaction stack
634
+ // uses for unmeasured payloads. Kept inline because this is the only
635
+ // caller; pi-mono's token estimator is provider-specific and we do not
636
+ // have a model handle at the persistence layer.
637
+ return Math.max(1, ceilChars(summary.length));
638
+ }
639
+
640
+ /**
641
+ * The post-compaction size, on the same scale as `tokensBefore`.
642
+ *
643
+ * `tokensBefore` is a whole-prompt figure: `calculateContextTokens` anchors it
644
+ * on the last assistant call's measured usage, so it carries the system prompt
645
+ * and tool schemas that compaction never touches. The after figure has to stay
646
+ * on that scale to be comparable, and the persistence layer has no model handle
647
+ * to re-measure with, so it is arithmetic on that same scale: drop what stops
648
+ * being replayed and add the summary that replaces it.
649
+ *
650
+ * Estimating it from the rebuilt message list instead reported `tokensBefore`
651
+ * back unchanged, which is what made /tree render "~16276 -> ~16276 tokens"
652
+ * beside a footer that said 16276 -> 11008 for the same compaction. That
653
+ * estimator anchors on the newest assistant usage, and the retained suffix
654
+ * still holds the assistant message whose usage describes the pre-compaction
655
+ * prompt. Anchoring on it reports precisely the number compaction removed.
656
+ */
657
+ function estimateTokensAfterCompaction(entries: ReadonlyArray<SessionEntry>, result: CompactResult): number {
658
+ let droppedTokens = 0;
659
+ for (const entry of entries.slice(0, result.firstKeptEntryIndex)) droppedTokens += estimateTokens(entry);
660
+ const summaryTokens = estimateTokensFromSummary(result.summary);
661
+ // The summary alone is the floor: a session whose dropped estimate exceeds
662
+ // the measured anchor must not report a negative or sub-summary context.
663
+ return Math.max(summaryTokens, result.tokensBefore - droppedTokens + summaryTokens);
664
+ }
665
+
666
+ /**
667
+ * Alt+J / Alt+K step the orchestrator through the `scope` list of target
668
+ * ids or target/model refs. Absent scope is a no-op so unconfigured users
669
+ * feel nothing.
670
+ */
671
+ export function advanceScopedTarget(
672
+ settings: Readonly<ClioSettings>,
673
+ direction: "forward" | "backward",
674
+ ): { target: string; model: string | null } | null {
675
+ const scope = settings.scope ?? [];
676
+ if (scope.length === 0) return null;
677
+ const registry = getRuntimeRegistry();
678
+ if (registry.list().length === 0) registerBuiltinRuntimes(registry);
679
+ const filteredScope = scope.filter((entry) => {
680
+ const [targetId] = entry.split("/");
681
+ const target = settings.targets.find((e) => e.id === targetId);
682
+ if (!target) return false;
683
+ const runtime = registry.get(target.runtime);
684
+ return runtime !== null && isOrchestratorEligibleRuntime(runtime);
685
+ });
686
+ if (filteredScope.length === 0) return null;
687
+ const activeTarget = settings.orchestrator.target ?? "";
688
+ const activeModel = settings.orchestrator.model ?? "";
689
+ const activeCombinedRef = activeTarget.length > 0 && activeModel.length > 0 ? `${activeTarget}/${activeModel}` : "";
690
+ const idx = filteredScope.findIndex((entry) => entry === activeCombinedRef || entry === activeTarget);
691
+ const base = idx === -1 ? 0 : idx + (direction === "forward" ? 1 : filteredScope.length - 1);
692
+ const next = filteredScope[base % filteredScope.length];
693
+ if (!next) return null;
694
+ const [targetId, ...modelParts] = next.split("/");
695
+ if (!targetId) return null;
696
+ if (modelParts.length > 0) {
697
+ return { target: targetId, model: modelParts.join("/") };
698
+ }
699
+ if (activeTarget === targetId) {
700
+ return { target: targetId, model: activeModel || null };
701
+ }
702
+ const descriptor = settings.targets.find((entry) => entry.id === targetId);
703
+ return { target: targetId, model: descriptor?.defaultModel ?? null };
704
+ }
705
+
706
+ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootResult> {
707
+ const bootStdout = (text: string): void => {
708
+ if (options.terminalLease) options.terminalLease.writeDiagnostic("stdout", text);
709
+ else process.stdout.write(text);
710
+ };
711
+ const bootStderr = (text: string): void => {
712
+ if (options.terminalLease) options.terminalLease.writeDiagnostic("stderr", text);
713
+ else process.stderr.write(text);
714
+ };
715
+ const timer = new StartupTimer(
716
+ options.terminalLease
717
+ ? (phase) => {
718
+ const line = formatBootTrace(phase);
719
+ if (line) options.terminalLease?.deferDiagnostic("stderr", line);
720
+ }
721
+ : undefined,
722
+ );
723
+ const bus = getSharedBus();
724
+ const termination = getTerminationCoordinator();
725
+ installBusTracer();
726
+ termination.installSignalHandlers();
727
+
728
+ ensureClioState();
729
+ timer.mark("install check");
730
+
731
+ // A hard-killed coordinator cannot run domain drains. Reconcile its compete
732
+ // process leases before dispatch startup scans the ledger, so abandoned rows
733
+ // observe dead workers and no stale worktree can be reused by this boot.
734
+ if (isGitRepository(process.cwd())) {
735
+ try {
736
+ const pendingGate = readPendingGateDecisions();
737
+ const pendingCompeteGroups = new Set<string>();
738
+ for (const handle of pendingGate.records) {
739
+ if (handle.record.kind === "output" && handle.record.topology === "compete") {
740
+ pendingCompeteGroups.add(handle.record.group);
741
+ } else if (handle.record.kind === "decision" && handle.record.decision.topology === "compete") {
742
+ pendingCompeteGroups.add(handle.record.decision.group);
743
+ }
744
+ }
745
+ const durableDecisions = readGateDecisionArtifacts();
746
+ const confirmedGroups = new Set(
747
+ durableDecisions
748
+ .filter(
749
+ ({ artifact }) =>
750
+ artifact.topology === "compete" &&
751
+ (artifact.outcome === "operator-confirmed" || artifact.outcome === "full-auto-applied"),
752
+ )
753
+ .map(({ artifact }) => artifact.group),
754
+ );
755
+ for (const { artifact } of durableDecisions) {
756
+ if (artifact.topology === "compete" && artifact.outcome === "winner" && !confirmedGroups.has(artifact.group)) {
757
+ pendingCompeteGroups.add(artifact.group);
758
+ }
759
+ }
760
+ const recovery = recoverCleanupReadyCompeteGroups(process.cwd(), {
761
+ preserveActiveGroups: pendingCompeteGroups,
762
+ preserveAllActive: pendingGate.errors.length > 0,
763
+ });
764
+ for (const failure of recovery.failed) {
765
+ bootStderr(`[dispatch] compete recovery preserved ${failure.group}: ${failure.message}\n`);
766
+ }
767
+ } catch (err) {
768
+ bootStderr(`[dispatch] compete recovery failed closed: ${err instanceof Error ? err.message : String(err)}\n`);
769
+ }
770
+ }
771
+
772
+ let effectiveSettingsForDispatch: (() => Readonly<ClioSettings>) | null = null;
773
+ let protectedArtifactStateForDispatch: (() => ProtectedArtifactState) | null = null;
774
+
775
+ const result = await loadDomains(
776
+ [
777
+ options.startupSettings ? createConfigDomainModule(options.startupSettings) : ConfigDomainModule,
778
+ ExtensionsDomainModule,
779
+ InteropDomainModule,
780
+ createResourcesDomainModule({
781
+ reservedPromptNames: new Set(BUILTIN_SLASH_COMMANDS.map((entry) => entry.name)),
782
+ skills: () => ({
783
+ disableDiscovery: options.noSkills === true || options.headless?.noSkills === true,
784
+ ...(options.skillPaths && options.skillPaths.length > 0
785
+ ? { explicitSkillPaths: options.skillPaths }
786
+ : options.headless?.skillPaths && options.headless.skillPaths.length > 0
787
+ ? { explicitSkillPaths: options.headless.skillPaths }
788
+ : {}),
789
+ }),
790
+ }),
791
+ ShareDomainModule,
792
+ createContextDomainModule({ noContextFiles: options.noContextFiles === true }),
793
+ ProvidersDomainModule,
794
+ SafetyDomainModule,
795
+ createPromptsDomainModule({
796
+ noContextFiles: options.noContextFiles === true,
797
+ }),
798
+ AgentsDomainModule,
799
+ MiddlewareDomainModule,
800
+ SessionDomainModule,
801
+ ObservabilityDomainModule,
802
+ SchedulingDomainModule,
803
+ // Dispatch resolves worker targets through the session's effective
804
+ // settings view once it exists (assigned below, after the config
805
+ // contract loads); until then it falls back to the shared snapshot.
806
+ createDispatchDomainModule({
807
+ getSettings: () => effectiveSettingsForDispatch?.(),
808
+ getProtectedArtifactState: () => protectedArtifactStateForDispatch?.() ?? { artifacts: [] },
809
+ autonomyOverride: options.headless?.autonomy !== undefined,
810
+ }),
811
+ LifecycleDomainModule,
812
+ ],
813
+ { diagnostic: bootStderr },
814
+ );
815
+ timer.mark(`domains loaded (${result.loaded.length})`);
816
+
817
+ const dispatch = result.getContract<DispatchContract>("dispatch");
818
+ if (dispatch) {
819
+ termination.onDrain(async () => {
820
+ await dispatch.drain();
821
+ });
822
+ }
823
+ termination.onPersist(async () => {
824
+ await result.stop();
825
+ });
826
+
827
+ bus.emit(BusChannels.SessionStart, { at: Date.now() });
828
+ timer.mark("session_start fired");
829
+
830
+ const acpMode = options.acp !== undefined;
831
+ const interactive = !options.headless && !acpMode && process.env.CLIO_CODER_INTERACTIVE === "1";
832
+ if (!interactive && !options.headless && !acpMode) {
833
+ bootStdout(buildBanner());
834
+ if (process.env.CLIO_CODER_TIMING === "1") bootStdout(`${timer.report()}\n`);
835
+ }
836
+
837
+ const config = result.getContract<ConfigContract>("config");
838
+ const providers = result.getContract<ProvidersContract>("providers");
839
+ timer.mark("providers resolved");
840
+
841
+ if (options.apiKey) {
842
+ if (!providers) {
843
+ bootStderr("Clio Coder: --api-key supplied but providers domain unavailable; ignoring.\n");
844
+ } else {
845
+ const settingsNow = applyHeadlessSettingsOverlay(config?.get() ?? readSettings(), options.headless);
846
+ const activeTargetId = settingsNow.orchestrator?.target;
847
+ const target = resolveTarget(providers, activeTargetId);
848
+ const runtime = target ? providers.getRuntime(target.runtime) : null;
849
+ if (target && runtime) {
850
+ providers.auth.setRuntimeOverrideForTarget(target, runtime, options.apiKey);
851
+ } else {
852
+ bootStderr("Clio Coder: --api-key supplied but no active orchestrator target is configured; ignoring.\n");
853
+ }
854
+ }
855
+ }
856
+
857
+ if (!interactive && !options.headless && !acpMode) {
858
+ bootStdout(`${chalk.dim(" (non-interactive boot. pass CLIO_CODER_INTERACTIVE=1 to launch the TUI.)")}\n`);
859
+ await termination.shutdown(0);
860
+ return { exitCode: 0, bootTimeMs: timer.snapshot().totalMs };
861
+ }
862
+
863
+ const middleware = result.getContract<MiddlewareContract>("middleware");
864
+ const observability = result.getContract<ObservabilityContract>("observability");
865
+ const safety = result.getContract<SafetyContract>("safety");
866
+ const session = result.getContract<SessionContract>("session");
867
+ const prompts = result.getContract<PromptsContract>("prompts");
868
+ const agents = result.getContract<AgentsContract>("agents");
869
+ const resources = result.getContract<ResourcesContract>("resources");
870
+ const extensions = result.getContract<ExtensionsContract>("extensions");
871
+ const share = result.getContract<ShareContract>("share");
872
+ const contextDomain = result.getContract<ContextContract>("context");
873
+ const interop = result.getContract<InteropContract>("interop");
874
+ // Boot detection resolves paths only: no `--version` subprocess and no skill
875
+ // walk on the boot path. The hint is gated on `interactive`, which is already
876
+ // false under headless and ACP.
877
+ const interopReport = interactive && interop ? await interop.detect({ cwd: process.cwd() }) : null;
878
+ const initialNotices = interactive ? [...(contextDomain?.startupHints() ?? [])] : [];
879
+ // Once per version, interactive only: headless and ACP have no operator at
880
+ // the keyboard to tell, and the record is left unclaimed for the boot that does.
881
+ const upgrade = interactive ? takeUpgradeNotice() : null;
882
+ if (upgrade !== null) initialNotices.push(describeUpgradeNotice(upgrade));
883
+ if (interop && interopReport) {
884
+ const interopHint = interop.bootHint(interopReport);
885
+ if (interopHint !== null) initialNotices.push(interopHint);
886
+ }
887
+ if (!providers || !dispatch || !observability || !safety || !middleware) {
888
+ bootStderr(
889
+ "Clio Coder: chat mode requires safety + middleware + providers + dispatch + observability contracts; aborting.\n",
890
+ );
891
+ await termination.shutdown(1);
892
+ return { exitCode: 1, bootTimeMs: timer.snapshot().totalMs };
893
+ }
894
+
895
+ // A headless `--session`/`--continue` resolves here, where the session
896
+ // domain exists. It is a hard requirement rather than a hint: a caller that
897
+ // asked to continue a conversation must not receive an answer written
898
+ // without that conversation's history.
899
+ const requestedResume = options.headless?.resumeSession;
900
+ let headlessResumeFailure: string | null = null;
901
+ let resolvedResumeId: string | undefined;
902
+ if (requestedResume !== undefined) {
903
+ if (!session) {
904
+ headlessResumeFailure = "session continuation requires the session domain, which is not loaded";
905
+ } else if (requestedResume.kind === "id") {
906
+ resolvedResumeId = requestedResume.id;
907
+ } else {
908
+ const latest = session.history()[0];
909
+ if (latest === undefined) headlessResumeFailure = `no previous session recorded for ${process.cwd()}`;
910
+ else resolvedResumeId = latest.id;
911
+ }
912
+ }
913
+ const resumeId = resolvedResumeId ?? process.env.CLIO_CODER_RESUME_SESSION_ID?.trim();
914
+ let resumedSessionAtBoot = false;
915
+ if (resumeId && session && headlessResumeFailure === null) {
916
+ try {
917
+ session.resume(resumeId);
918
+ resumedSessionAtBoot = true;
919
+ } catch (err) {
920
+ const detail = err instanceof Error ? err.message : String(err);
921
+ if (requestedResume !== undefined) headlessResumeFailure = `failed to resume session ${resumeId}: ${detail}`;
922
+ else bootStderr(`Clio Coder: failed to resume session ${resumeId}: ${detail}\n`);
923
+ }
924
+ }
925
+ Reflect.deleteProperty(process.env, "CLIO_CODER_RESUME_SESSION_ID");
926
+ if (headlessResumeFailure !== null) {
927
+ bootStderr(`clio-coder run: ${headlessResumeFailure}\n`);
928
+ await termination.shutdown(2);
929
+ return { exitCode: 2, bootTimeMs: timer.snapshot().totalMs };
930
+ }
931
+
932
+ // Hook diagnostics ride the typed bus. The domain loader constructed the
933
+ // bundle with the stderr default; swap in a sink that publishes
934
+ // middleware.hookFailed (the interactive warn notice consumes it) and keep
935
+ // stderr for non-interactive runs, which have no notice subscriber.
936
+ middleware.setDiagnosticSink((diagnostic) => {
937
+ bus.emit(BusChannels.MiddlewareHookFailed, {
938
+ kind: diagnostic.kind,
939
+ registrationId: diagnostic.registrationId,
940
+ hook: diagnostic.hook,
941
+ at: Date.now(),
942
+ ...(diagnostic.kind === "hook_failed"
943
+ ? { message: diagnostic.message }
944
+ : {
945
+ elapsedMs: diagnostic.elapsedMs,
946
+ budgetMs: diagnostic.budgetMs,
947
+ steadyStateWarn: diagnostic.steadyStateWarn,
948
+ p50Ms: diagnostic.stats.p50Ms,
949
+ p95Ms: diagnostic.stats.p95Ms,
950
+ overCount: diagnostic.stats.overCount,
951
+ windowSamples: diagnostic.stats.window,
952
+ }),
953
+ });
954
+ if (!interactive) writeMiddlewareDiagnosticToStderr(diagnostic);
955
+ });
956
+
957
+ // Residency and runtime notices (model swaps, double residency, VRAM
958
+ // stress) reach the operator through the interactive notice renderer; a
959
+ // headless run has no subscriber, so mirror them to stderr there. Silent
960
+ // model swaps are exactly the failure mode the residency policy forbids.
961
+ if (!interactive) {
962
+ bus.on(BusChannels.RuntimeNotice, (payload: unknown) => {
963
+ const notice = payload as { level?: string; message?: string } | undefined;
964
+ if (typeof notice?.message !== "string") return;
965
+ process.stderr.write(`[clio:runtime] ${notice.level ?? "info"}: ${notice.message}\n`);
966
+ });
967
+ }
968
+
969
+ // Guardrail policy (tool-call budgets, tool byte caps, dispatch ledger cap)
970
+ // resolves settings-first with env as emergency override; install the
971
+ // settings section before any guard registration or tool reads it.
972
+ configureGuardrails((config?.get() ?? readSettings()).guardrails);
973
+
974
+ // Guard registrations on the middleware contract, in order: loop guard,
975
+ // protected artifacts (last among guards so it absorbs protect_path effects
976
+ // from everything before it), dispatch dedup. Workers register their own
977
+ // loop guard and protected-artifacts instances inside their subprocess in
978
+ // worker-runtime.ts; the orchestrator instances carry the bus and the
979
+ // session persistence sink.
980
+ middleware.registerHook(
981
+ createLoopGuardRegistration({
982
+ safety,
983
+ bus,
984
+ turnBlockBudget: INTERACTIVE_LOOP_BLOCK_BUDGET,
985
+ turnToolCallBudget: readOrchTurnToolCallBudget(),
986
+ // Interactive/headless/ACP all share this orchestrator guard: at the
987
+ // block budget, lock tools for the rest of the turn so the model
988
+ // answers from what it gathered instead of hard-cancelling a turn that
989
+ // may already hold the answer. The bounded backstop still cancels a
990
+ // model that keeps calling tools.
991
+ turnSynthesisLockout: true,
992
+ }),
993
+ );
994
+ let initialProtectedArtifactState: ProtectedArtifactState | undefined;
995
+ let initialProtectionReadError: string | null = null;
996
+ if (session) {
997
+ try {
998
+ initialProtectedArtifactState = protectedArtifactStateForCurrentSession(session);
999
+ } catch (error) {
1000
+ initialProtectionReadError = error instanceof Error ? error.message : String(error);
1001
+ }
1002
+ }
1003
+ const protectedArtifactsGuard = createProtectedArtifactsRegistration({
1004
+ ...(initialProtectedArtifactState !== undefined ? { initialState: initialProtectedArtifactState } : {}),
1005
+ onProtect: (event) => appendProtectedArtifactRegistryEvent(session, event),
1006
+ onDurabilityFailure: (health) => {
1007
+ bus.emit(BusChannels.MiddlewareHookFailed, {
1008
+ kind: "hook_failed",
1009
+ registrationId: "guard.protected-artifacts",
1010
+ hook: "before_tool",
1011
+ at: Date.now(),
1012
+ message: health.reason,
1013
+ });
1014
+ },
1015
+ });
1016
+ if (initialProtectionReadError !== null) {
1017
+ protectedArtifactsGuard.markDegraded(
1018
+ `initial session protection history could not be read: ${initialProtectionReadError}`,
1019
+ );
1020
+ }
1021
+ protectedArtifactStateForDispatch = () => {
1022
+ const health = protectedArtifactsGuard.health();
1023
+ if (health.kind === "degraded") {
1024
+ throw new Error(`dispatch: protected artifact durability degraded: ${health.reason}`);
1025
+ }
1026
+ return protectedArtifactsGuard.state();
1027
+ };
1028
+ middleware.registerHook(protectedArtifactsGuard);
1029
+ middleware.registerHook(createDispatchDedupRegistration());
1030
+ // Observers run after the guards; they emit no effects and their sinks are
1031
+ // best-effort (session ledger, codewiki refresh).
1032
+ middleware.registerHook(
1033
+ createSkillActivationObserver((activation) => appendSkillActivationRegistryEvent(session, activation)),
1034
+ );
1035
+ // First-turn skills reminder: user-message-visible text is the one channel
1036
+ // the battery-tested local models act on; suggestion protocol only, the
1037
+ // operator load gate is untouched.
1038
+ if (resources) {
1039
+ middleware.registerHook(
1040
+ createSkillsReminderRegistration({
1041
+ countModelVisibleSkills: () => modelVisibleSkills(resources.skills(process.cwd()).items).length,
1042
+ // Same lookup context(scope="skills") lists under its Marketplace
1043
+ // heading, minus what is already installed, so the count the
1044
+ // reminder quotes is the count the listing will show.
1045
+ countInstallableSkills: () => {
1046
+ const installed = new Set(resources.skills(process.cwd()).items.map((skill) => skill.name));
1047
+ return discoverMarketplaceSkills({ cwd: process.cwd() }).skills.filter((skill) => !installed.has(skill.name))
1048
+ .length;
1049
+ },
1050
+ }),
1051
+ );
1052
+ }
1053
+ // Task-board reminder: same user-message-visible channel, fired once per
1054
+ // session when a request literally enumerates three or more steps. The
1055
+ // static routing line and tasks hint ask for the same board; battery-tested
1056
+ // local models only comply when the instruction rides the user message.
1057
+ middleware.registerHook(createTaskBoardReminderRegistration());
1058
+ const taskMemoryBank = new TaskMemoryBank();
1059
+ let taskMemorySessionId = session?.current()?.id ?? null;
1060
+ const ensureTaskMemorySession = (): void => {
1061
+ const currentSessionId = session?.current()?.id ?? null;
1062
+ if (currentSessionId === taskMemorySessionId) return;
1063
+ taskMemoryBank.clear();
1064
+ taskMemorySessionId = currentSessionId;
1065
+ };
1066
+ const memorySettings = (config?.get() ?? readSettings()).memory.intervention;
1067
+ // Bound late: the registration is built here, but the buffer a deferred
1068
+ // reminder lands in belongs to the chat loop that has not been composed yet.
1069
+ let deferredMemoryReminderSink: ((message: string) => void) | null = null;
1070
+ // Content-bearing, so it exists only when the operator named a file. The
1071
+ // telemetry row says which silence happened; this says what the model wrote.
1072
+ const memoryTracePath = taskMemoryTracePath();
1073
+ const memoryTrace = memoryTracePath === null ? null : createTaskMemoryTrace(memoryTracePath);
1074
+ const memoryIntervention = createMemoryInterventionRegistration({
1075
+ bank: taskMemoryBank,
1076
+ telemetry: createTaskMemoryTelemetrySink(),
1077
+ ...(memoryTrace === null ? {} : { onEnvelope: (envelope) => memoryTrace.record(envelope) }),
1078
+ // A headless run submits no further turn, so a detached step could only
1079
+ // finish after the process that would have read it has exited.
1080
+ deliversDeferredReminders: options.headless === undefined,
1081
+ onDeferredReminder: (message) => deferredMemoryReminderSink?.(message),
1082
+ getSettings: () => {
1083
+ ensureTaskMemorySession();
1084
+ return effectiveSettingsForDispatch?.().memory.intervention ?? memorySettings;
1085
+ },
1086
+ getModelClient: () => {
1087
+ const settings = effectiveSettingsForDispatch?.();
1088
+ return settings === undefined
1089
+ ? null
1090
+ : createBackgroundMemoryModelClient(providers, settings, settings.memory.intervention.timeoutMs);
1091
+ },
1092
+ });
1093
+ middleware.registerHook(memoryIntervention);
1094
+ const unsubscribeMemoryLoop = bus.on(BusChannels.LoopBlocked, () => memoryIntervention.signalLoop());
1095
+ termination.onDrain(() => unsubscribeMemoryLoop());
1096
+ if (contextDomain) {
1097
+ middleware.registerHook(createFileMutationObserver(({ paths }) => contextDomain.noteFileChanges(paths)));
1098
+ }
1099
+ // User-defined hooks: extensions and the project (.clio-coder/hooks.yaml,
1100
+ // .clio-coder/hooks.local.yaml) declare a conservative, receipted hook set on the
1101
+ // same effect machinery. They register after the guards, so safety stays
1102
+ // authoritative: a hook may add effects (including request block_tool) but
1103
+ // cannot grant a permission safety would deny. Loading is best-effort.
1104
+ const hookReceiptLog = createHookReceiptLog({ persistPath: join(clioStateDir(), "hook-receipts.json") });
1105
+ const extensionHookRoots: ExtensionHookRoot[] = (extensions?.list(process.cwd()) ?? [])
1106
+ .filter((ext) => ext.enabled && ext.effective)
1107
+ .map((ext) => ({ id: ext.id, rootPath: ext.rootPath }));
1108
+ const userHooks = installUserHooks({
1109
+ cwd: process.cwd(),
1110
+ extensions: extensionHookRoots,
1111
+ registerHook: (registration) => middleware.registerHook(registration),
1112
+ recordReceipt: (receipt) => hookReceiptLog.record(receipt),
1113
+ });
1114
+ if (!interactive) {
1115
+ for (const issue of userHooks.fileIssues) {
1116
+ process.stderr.write(`[clio:hooks] ${issue.message}\n`);
1117
+ }
1118
+ for (const issue of userHooks.issues) {
1119
+ process.stderr.write(`[clio:hooks] ${issue.source.sourcePath}#${issue.index}: ${issue.issues.join("; ")}\n`);
1120
+ }
1121
+ }
1122
+ termination.onDrain(() => hookReceiptLog.flush());
1123
+ // Autonomy is hot-reloaded for interactive and headless admissions. ACP
1124
+ // server prompts use the snapshot captured at session/new.
1125
+ let activeAcpSessionAutonomy: AutonomyLevel | null = null;
1126
+ // The one effective-autonomy resolution. Every admission surface (registry
1127
+ // admission, dispatch plan provenance, ACP session snapshot) resolves
1128
+ // through these two functions so a fallback added to one surface cannot
1129
+ // silently skip another.
1130
+ const resolveBaselineAutonomy = (): AutonomyLevel =>
1131
+ effectiveSettingsForDispatch?.().autonomy ??
1132
+ options.headless?.autonomy ??
1133
+ (config?.get() ?? readSettings()).autonomy ??
1134
+ "auto-edit";
1135
+ const resolveEffectiveAutonomy = (): AutonomyLevel => activeAcpSessionAutonomy ?? resolveBaselineAutonomy();
1136
+ const middlewareToolChoice = createMiddlewareToolChoiceControl();
1137
+ const toolRegistry = createRegistry({
1138
+ safety,
1139
+ middleware,
1140
+ onMiddlewareEffects: (effects) => middlewareToolChoice.apply(effects),
1141
+ autonomy: resolveEffectiveAutonomy,
1142
+ });
1143
+ const mainPermissionOrigin = acpMode ? "acp-server" : "main";
1144
+ toolRegistry.onPermissionRequired((call, decision, meta) => {
1145
+ bus.emit(BusChannels.PermissionRequested, {
1146
+ tool: call.tool,
1147
+ actionClass: decision.classification.actionClass,
1148
+ requestId: meta.requestId,
1149
+ origin: mainPermissionOrigin,
1150
+ axis: meta.axis,
1151
+ ...(decision.kind === "ask" ? { rejection: decision.rejection } : {}),
1152
+ ...(decision.policy?.ruleId !== undefined ? { ruleId: decision.policy.ruleId } : {}),
1153
+ ...(decision.policy?.policySource !== undefined ? { policySource: decision.policy.policySource } : {}),
1154
+ ...(decision.policy?.reasonCode !== undefined ? { reasonCode: decision.policy.reasonCode } : {}),
1155
+ });
1156
+ });
1157
+ let askUserHandler: AskUserHandler | null = null;
1158
+ // ask_user is interactive-only by design: it is a human interview tool and
1159
+ // headless/ACP surfaces have no operator to interview, so the tool is not
1160
+ // registered there at all (documented in `clio-coder run --help`). Skills that
1161
+ // interview fall back to their stated defaults when the tool is absent.
1162
+ const askUserBridge: AskUserHandler = async (questions, invokeOptions) =>
1163
+ askUserHandler ? await askUserHandler(questions, invokeOptions) : cancelledAskUserResult();
1164
+ const userTasks = createUserTasksStore({ cwd: process.cwd() });
1165
+ // One task board per orchestrator: the tasks tool mutates it, the turn-end
1166
+ // open-tasks nudge reads it, and the footer/overlay render it. Keyed on the
1167
+ // current session id so resume/fork/new refolds it from taskLedger entries.
1168
+ // Folding through filterEntriesToActivePath (not the raw file) is what
1169
+ // keeps a /resume from picking up whichever branch happened to write its
1170
+ // taskLedger entry last in file order; readEntries here used to skip that
1171
+ // filter entirely (issue #94).
1172
+ const taskBoard = createTaskBoardStore({
1173
+ getSessionId: () => session?.current()?.id ?? null,
1174
+ readEntries: () => {
1175
+ const meta = session?.current();
1176
+ if (!meta) return [];
1177
+ const leafTurnId = session?.tree(meta.id).leafId ?? undefined;
1178
+ return filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), leafTurnId);
1179
+ },
1180
+ appendEntry: (entry) => {
1181
+ session?.appendEntry(entry);
1182
+ },
1183
+ });
1184
+ // Prime the projection once at composition. Interactive repaint paths use
1185
+ // cachedSnapshot() below, so a first paint can never become a ledger read.
1186
+ taskBoard.snapshot();
1187
+ const decisionBoard = createDecisionBoardStore({
1188
+ getSessionId: () => session?.current()?.id ?? null,
1189
+ readEntries: () => {
1190
+ const meta = session?.current();
1191
+ if (!meta) return [];
1192
+ const leafTurnId = session?.tree(meta.id).leafId ?? undefined;
1193
+ return filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), leafTurnId);
1194
+ },
1195
+ getActiveLeafTurnId: () => {
1196
+ const meta = session?.current();
1197
+ return meta ? (session?.tree(meta.id).leafId ?? null) : null;
1198
+ },
1199
+ appendEntry: (entry) => {
1200
+ if (!session) throw new Error("decision board: no session ledger is available");
1201
+ session.appendEntry(entry);
1202
+ },
1203
+ });
1204
+ // getSessionId alone never notices a /tree switch: it moves the active
1205
+ // append point inside the same session, so the id-keyed cache above kept
1206
+ // showing the abandoned branch's board (issue #94). SessionTurnSwitched is
1207
+ // the signal that switch actually happened; invalidate() forces the next
1208
+ // read to refold from the now-current leaf.
1209
+ bus.on(BusChannels.SessionTurnSwitched, () => {
1210
+ taskBoard.invalidate();
1211
+ // The tree switch is the I/O boundary: refold eagerly here so the 250-ms
1212
+ // island ticker remains a cache-only consumer after changing branches.
1213
+ taskBoard.snapshot();
1214
+ decisionBoard.invalidate();
1215
+ });
1216
+ // Link in-flight dispatch runs to the live board via the ledger's
1217
+ // activeRunIds field: a run is tracked from the moment its child process is
1218
+ // live until it finalizes either way. attach/detach are no-ops when no board
1219
+ // is declared, so an ambient dispatch never forces a board into existence.
1220
+ bus.on(BusChannels.DispatchStarted, (payload) => {
1221
+ if (typeof payload?.runId === "string") taskBoard.attachRun(payload.runId);
1222
+ });
1223
+ bus.on(BusChannels.DispatchCompleted, (payload) => {
1224
+ if (typeof payload?.runId !== "string") return;
1225
+ taskBoard.detachRun(payload.runId);
1226
+ foldDispatchSkillActivations(session, payload);
1227
+ });
1228
+ bus.on(BusChannels.DispatchFailed, (payload) => {
1229
+ if (typeof payload?.runId !== "string") return;
1230
+ taskBoard.detachRun(payload.runId);
1231
+ // A run that failed after loading a skill is the case the operator most
1232
+ // needs the provenance for, and the receipt already carried it here.
1233
+ foldDispatchSkillActivations(session, payload);
1234
+ });
1235
+ // Operator-initiated backgrounding is a TUI affordance: the registry is the
1236
+ // one object the dispatch tool and the keypress both hold.
1237
+ const dispatchBackground = createDispatchBackgroundRegistry();
1238
+ registerAllTools(toolRegistry, {
1239
+ ...(session ? { session } : {}),
1240
+ taskBoard,
1241
+ userTasks,
1242
+ dispatch,
1243
+ bus,
1244
+ ...(interactive ? { askUser: askUserBridge } : {}),
1245
+ ...(agents ? { getAgentCatalog: () => renderAgentCatalogSectionsFromSpecs(agents.listSpecs()).stable } : {}),
1246
+ ...(agents ? { getAgentSpecs: () => agents.listSpecs() } : {}),
1247
+ ...(agents ? { getAgentRoleFacts: agentRoleFactsResolver((id: string) => agents.getSpec(id)) } : {}),
1248
+ // Same effective-autonomy resolution the registry admission uses, so plan
1249
+ // provenance and compete winner handling agree with the approval surface.
1250
+ getAutonomy: resolveEffectiveAutonomy,
1251
+ ...(interactive ? { dispatchBackground } : {}),
1252
+ getCostCeilingUsd: () => result.getContract<SchedulingContract>("scheduling")?.ceilingUsd() ?? 0,
1253
+ getSkillLoaderOptions: () => ({
1254
+ trustProjectCompatRoots: config?.get().skills.trustProjectCompatRoots === true,
1255
+ disableDiscovery: options.noSkills === true || options.headless?.noSkills === true,
1256
+ ...(options.skillPaths && options.skillPaths.length > 0
1257
+ ? { explicitSkillPaths: options.skillPaths }
1258
+ : options.headless?.skillPaths && options.headless.skillPaths.length > 0
1259
+ ? { explicitSkillPaths: options.headless.skillPaths }
1260
+ : {}),
1261
+ }),
1262
+ });
1263
+
1264
+ // Live routing is owned by this process. Seed it once from saved settings
1265
+ // (with any headless CLI overrides baked in); from here on every consumer
1266
+ // reads the effective view — shared snapshot + session routing overlay — so
1267
+ // another process writing settings.yaml can update defaults and the
1268
+ // target catalog but never redirect this session's routing.
1269
+ const sessionRouting = seedSessionRouting(
1270
+ applyHeadlessSettingsOverlay(config?.get() ?? readSettings(), options.headless),
1271
+ );
1272
+ // Non-routing settings a session changed "for this session only" via the
1273
+ // /settings overlay. Layered under the routing overlay in the effective
1274
+ // view, so the live session reflects them immediately while settings.yaml
1275
+ // (the global default for new sessions) stays untouched until the operator
1276
+ // chooses to save globally.
1277
+ const sessionOverrides: SessionOverrides = new Map(
1278
+ options.headless?.autonomy === undefined ? [] : [["autonomy", options.headless.autonomy]],
1279
+ );
1280
+ // The effective view is derived by deep-cloning the saved snapshot, and it
1281
+ // is read on the tool-admission hot path (every call resolves autonomy
1282
+ // through it). Memoize on the two things it depends on: the config
1283
+ // domain's snapshot identity (swapped wholesale on every update and
1284
+ // external hot reload) and a generation counter bumped by every session
1285
+ // routing/override mutation below. Without this, one tool call cost a full
1286
+ // settings structuredClone.
1287
+ let sessionStateGeneration = 0;
1288
+ let cachedSettingsBase: Readonly<ClioSettings> | null = null;
1289
+ let cachedSettingsGeneration = -1;
1290
+ let cachedSettingsView: ClioSettings | null = null;
1291
+ const bumpSessionState = (): void => {
1292
+ sessionStateGeneration += 1;
1293
+ // Child-process seams read the effective setting from the environment,
1294
+ // and the session view may override the saved value.
1295
+ setGitCommitAttributionEnabled(getCurrentSettings().attribution.gitCommits);
1296
+ };
1297
+ const getCurrentSettings = (): ClioSettings => {
1298
+ // Recents live in the data dir (core/recent-models.ts), never in
1299
+ // settings.yaml; consumers that need them call listRecentModels
1300
+ // directly, so an Alt+L pick in another session does not churn the
1301
+ // config watcher here.
1302
+ const base = config?.get();
1303
+ // No config domain (unit tests, degraded boot): readSettings() returns a
1304
+ // fresh object every call, so there is nothing stable to key a cache on.
1305
+ if (base === undefined) return applySessionRouting(applyOverrides(readSettings(), sessionOverrides), sessionRouting);
1306
+ if (
1307
+ cachedSettingsView !== null &&
1308
+ cachedSettingsBase === base &&
1309
+ cachedSettingsGeneration === sessionStateGeneration
1310
+ ) {
1311
+ return cachedSettingsView;
1312
+ }
1313
+ const view = applySessionRouting(applyOverrides(base, sessionOverrides), sessionRouting);
1314
+ cachedSettingsBase = base;
1315
+ cachedSettingsGeneration = sessionStateGeneration;
1316
+ cachedSettingsView = view;
1317
+ return view;
1318
+ };
1319
+ effectiveSettingsForDispatch = getCurrentSettings;
1320
+ bumpSessionState();
1321
+ // The config bundle publishes the saved value on reload; a session-scoped
1322
+ // override must win, so re-derive from the session view after it.
1323
+ const unsubscribeCommitAttributionSync = bus.on(BusChannels.ConfigHotReload, () => bumpSessionState());
1324
+ termination.onDrain(() => unsubscribeCommitAttributionSync());
1325
+ const getTaskMemorySeedOffer = (): { source: string; count: number } | null => {
1326
+ return taskMemoryHandoffSeedOffer(process.cwd(), getCurrentSettings().memory.intervention.enabled);
1327
+ };
1328
+ const seedCurrentTaskMemoryFromHandoff = () => {
1329
+ ensureTaskMemorySession();
1330
+ return seedTaskMemoryFromNewestHandoff(
1331
+ taskMemoryBank,
1332
+ process.cwd(),
1333
+ getCurrentSettings().memory.intervention.enabled,
1334
+ );
1335
+ };
1336
+ if (resumedSessionAtBoot) {
1337
+ const offer = getTaskMemorySeedOffer();
1338
+ if (offer && offer.count > 0) {
1339
+ initialNotices.push(
1340
+ `task memory: ${offer.count} handoff entr${offer.count === 1 ? "y" : "ies"} available from ${offer.source}; run /memory seed to import`,
1341
+ );
1342
+ }
1343
+ }
1344
+ for (const warning of agentRoleToolWarnings(providers, getCurrentSettings())) {
1345
+ if (interactive) initialNotices.push(warning);
1346
+ else process.stderr.write(`${warning}\n`);
1347
+ }
1348
+ // Residency protection follows the live effective settings: the models the
1349
+ // operator's config references (orchestrator, worker default/profiles,
1350
+ // target defaults) may never be evicted by another Clio stream, and a
1351
+ // routing change updates the set on the next read.
1352
+ setProtectedModelsProvider(() => protectedResidencyModels(getCurrentSettings()));
1353
+
1354
+ const validatedKeybindings = validateKeybindings((config?.get() ?? readSettings()).keybindings ?? {});
1355
+ const invalidBindings = validatedKeybindings.invalid;
1356
+ if (invalidBindings.length > 0) {
1357
+ const notice = formatInvalidKeybindingNotice(invalidBindings);
1358
+ if (interactive) initialNotices.push(notice);
1359
+ else process.stderr.write(notice);
1360
+ }
1361
+ const platformWarnings = process.stdin.isTTY
1362
+ ? detectPlatformKeybindingWarnings(validatedKeybindings.valid, detectTerminalKeySupport(process.env))
1363
+ : [];
1364
+ if (platformWarnings.length > 0) {
1365
+ const notice = formatPlatformKeybindingNotice(platformWarnings);
1366
+ if (interactive) initialNotices.push(notice);
1367
+ else process.stderr.write(notice);
1368
+ }
1369
+ /**
1370
+ * Locked read-modify-write of saved settings. Routes through the config
1371
+ * contract (which refreshes its snapshot and dispatches change events) when
1372
+ * available, else straight through core updateSettings. Either way the
1373
+ * mutator runs against the freshest on-disk state under the advisory
1374
+ * settings lock, so two processes saving defaults at the same time cannot
1375
+ * interleave and drop each other's patches.
1376
+ */
1377
+ const persistSavedMutation = (mutator: SettingsMutator): void => {
1378
+ if (config?.update) config.update(mutator);
1379
+ else updateSettings(mutator);
1380
+ bumpSessionState();
1381
+ };
1382
+ /**
1383
+ * Apply a routing change with one consistent scope: it takes effect in this
1384
+ * session immediately and writes through to saved settings as the default
1385
+ * for future sessions. Only the patched fields hit the file, so concurrent
1386
+ * sessions cannot clobber each other's saved defaults wholesale.
1387
+ */
1388
+ const updateSessionRouting = (patch: RoutingPatch, mutateSaved?: (saved: ClioSettings) => void): void => {
1389
+ applyRoutingPatch(sessionRouting, patch);
1390
+ bumpSessionState();
1391
+ persistSavedMutation((saved) => {
1392
+ mergeRoutingPatchIntoSettings(saved, patch);
1393
+ mutateSaved?.(saved);
1394
+ });
1395
+ };
1396
+ const readAcpSafeSettings = (): AcpSafeSettingsSnapshot => {
1397
+ const settings = getCurrentSettings();
1398
+ return {
1399
+ target: settings.orchestrator.target,
1400
+ model: settings.orchestrator.model,
1401
+ thinkingLevel: settings.orchestrator.thinkingLevel ?? "off",
1402
+ autonomy: settings.autonomy,
1403
+ };
1404
+ };
1405
+ /**
1406
+ * ACP safe settings are one atomic persisted mutation followed by infallible
1407
+ * in-process routing assignment. Persisting first avoids reporting a live
1408
+ * route that failed to become the future-session default.
1409
+ */
1410
+ const commitAcpSafeSettings = (patch: AcpSafeSettingsPatch): AcpSafeSettingsSnapshot => {
1411
+ const orchestrator: NonNullable<RoutingPatch["orchestrator"]> = {};
1412
+ if (patch["orchestrator.target"] !== undefined) orchestrator.target = patch["orchestrator.target"];
1413
+ if (patch["orchestrator.model"] !== undefined) orchestrator.model = patch["orchestrator.model"];
1414
+ if (patch["orchestrator.thinkingLevel"] !== undefined) {
1415
+ orchestrator.thinkingLevel = patch["orchestrator.thinkingLevel"];
1416
+ }
1417
+ const routingPatch: RoutingPatch | null = Object.keys(orchestrator).length > 0 ? { orchestrator } : null;
1418
+ persistSavedMutation((saved) => {
1419
+ if (routingPatch !== null) mergeRoutingPatchIntoSettings(saved, routingPatch);
1420
+ if (patch.autonomy !== undefined) saved.autonomy = patch.autonomy;
1421
+ });
1422
+ if (routingPatch !== null) {
1423
+ applyRoutingPatch(sessionRouting, routingPatch);
1424
+ bumpSessionState();
1425
+ }
1426
+ return readAcpSafeSettings();
1427
+ };
1428
+ /**
1429
+ * Persist a whole-settings blob coming from the effective view (the
1430
+ * /settings overlay, favorites toggles). Routing edits in the blob are
1431
+ * absorbed into the session state and written through; everything else is
1432
+ * persisted without leaking this session's routing into the saved defaults.
1433
+ */
1434
+ const applySettingsBlob = (next: ClioSettings): void => {
1435
+ const patch = diffRouting(getCurrentSettings(), next);
1436
+ if (patch) {
1437
+ applyRoutingPatch(sessionRouting, patch);
1438
+ bumpSessionState();
1439
+ }
1440
+ persistSavedMutation((fresh) => {
1441
+ const persisted = structuredClone(next);
1442
+ restoreRoutingFields(persisted, fresh);
1443
+ // A whole-blob write (providers, favorites) must not globalize a
1444
+ // session-only override: restore every overridden leaf from the
1445
+ // fresh file so it stays session-local until explicitly saved.
1446
+ for (const path of sessionOverrides.keys()) setAtPath(persisted, path, getAtPath(fresh, path));
1447
+ if (patch) mergeRoutingPatchIntoSettings(persisted, patch);
1448
+ return persisted;
1449
+ });
1450
+ };
1451
+ /**
1452
+ * Commit a single /settings edit, keyed by its config-path id. `next` is the
1453
+ * effective view with the one leaf already changed.
1454
+ * - scope "session": apply live only. Routing ids feed the routing state;
1455
+ * every other id becomes a session override. settings.yaml is untouched.
1456
+ * - scope "global": apply live and persist just that leaf as the new
1457
+ * default, clearing any prior session override for it.
1458
+ * Restart-required ids (budget.concurrency, runtimePlugins,
1459
+ * terminal.tuiMode, terminal.fullscreenScrollbar) cannot apply
1460
+ * live, so the overlay only offers "global" for them; the file write is what
1461
+ * a later restart picks up.
1462
+ */
1463
+ const commitSetting = (id: string, next: ClioSettings, scope: "session" | "global"): void => {
1464
+ if (isRoutingPath(id)) {
1465
+ // Build the patch from `next` keyed by the edited id, not by diffing
1466
+ // against the live view: a prior session-only apply already moved the
1467
+ // routing state, so a diff would be empty and the global save would
1468
+ // silently no-op. Only the touched fields are persisted, so concurrent
1469
+ // sessions never clobber each other's saved routing.
1470
+ const patch = routingPatchForId(id, next);
1471
+ if (!patch) return;
1472
+ applyRoutingPatch(sessionRouting, patch);
1473
+ bumpSessionState();
1474
+ if (scope === "global") persistSavedMutation((saved) => mergeRoutingPatchIntoSettings(saved, patch));
1475
+ return;
1476
+ }
1477
+ const value = getAtPath(next, id);
1478
+ if (scope === "session") {
1479
+ sessionOverrides.set(id, value);
1480
+ bumpSessionState();
1481
+ return;
1482
+ }
1483
+ sessionOverrides.delete(id);
1484
+ bumpSessionState();
1485
+ persistSavedMutation((saved) => setAtPath(saved, id, value));
1486
+ };
1487
+ /** Alt+J / Alt+K: step this session's orchestrator through the scope list. */
1488
+ const cycleScopedSession = (direction: "forward" | "backward"): boolean => {
1489
+ const next = advanceScopedTarget(getCurrentSettings(), direction);
1490
+ if (!next) return false;
1491
+ updateSessionRouting({ orchestrator: { target: next.target, model: next.model } });
1492
+ return true;
1493
+ };
1494
+
1495
+ const readCurrentSessionEntries = (): ReadonlyArray<SessionEntry> => {
1496
+ if (session === undefined) return [];
1497
+ const meta = session.current();
1498
+ if (!meta) return [];
1499
+ reconcilePendingProtectedArtifacts(session);
1500
+ return readSessionEntriesForCompact(meta.id);
1501
+ };
1502
+
1503
+ // turn_end assessors, fired by the chat-loop when the final assistant
1504
+ // message of a run lands. Tool-prose first so its hard-block interruption
1505
+ // precedes the finish-contract advisory in effect order.
1506
+ middleware.registerHook(createToolProseRegistration());
1507
+ middleware.registerHook(createTaskNudgeRegistration({ getBoard: () => taskBoard.snapshot() }));
1508
+ middleware.registerHook(createReadOnlyExplorationNudgeRegistration());
1509
+ middleware.registerHook(createUnbackedWorkerClaimRegistration());
1510
+ middleware.registerHook(
1511
+ createDetachedDispatchNudgeRegistration({ getOpenBatches: () => openDetachedBatchViews(dispatch) }),
1512
+ );
1513
+ if (session) {
1514
+ middleware.registerHook(
1515
+ createFinishContractRegistration({
1516
+ // Tail-scoped: the contract only needs the last-user-message window, so
1517
+ // it parses a bounded ledger tail per turn_end rather than the whole
1518
+ // file (which grows unbounded with session length).
1519
+ readSessionEntries: () => {
1520
+ const meta = session.current();
1521
+ return meta ? readRecentSessionEntriesForContract(meta.id) : null;
1522
+ },
1523
+ resolveRigor: () =>
1524
+ resolveRigor({ cwd: process.cwd(), override: parseRigorOverride(process.env.CLIO_CODER_RIGOR) }),
1525
+ recordDecision: (record) => safety.audit.recordCompletionContract?.(record),
1526
+ }),
1527
+ );
1528
+ }
1529
+
1530
+ const chat = createChatLoop({
1531
+ getSettings: getCurrentSettings,
1532
+ providers,
1533
+ middleware,
1534
+ middlewareToolChoice,
1535
+ protectedArtifacts: {
1536
+ replace: (state) => protectedArtifactsGuard.replaceState(state),
1537
+ markDegraded: (reason) => protectedArtifactsGuard.markDegraded(reason),
1538
+ },
1539
+ knownTargets: () => new Set(providers.list().map((entry) => entry.target.id)),
1540
+ observability,
1541
+ bus,
1542
+ ...(prompts ? { prompts } : {}),
1543
+ ...(session ? { session } : {}),
1544
+ getMemorySection: () => {
1545
+ try {
1546
+ const records = loadMemoryRecordsSync(clioDataDir());
1547
+ return buildMemoryPromptSection(records, {
1548
+ activeRepository: canonicalMemoryRepositoryIdentity(process.cwd()),
1549
+ }).section;
1550
+ } catch {
1551
+ return "";
1552
+ }
1553
+ },
1554
+ getTaskMemoryHandoffSource: () => {
1555
+ ensureTaskMemorySession();
1556
+ return renderTaskMemoryHandoffSource(taskMemoryBank.snapshot());
1557
+ },
1558
+ registerDeferredReminderSink: (sink) => {
1559
+ deferredMemoryReminderSink = sink;
1560
+ },
1561
+ onAskUserFinalized: (policy) => {
1562
+ decisionBoard.recordFinalizedInterview(policy);
1563
+ },
1564
+ ...(session
1565
+ ? {
1566
+ readSessionEntries: readCurrentSessionEntries,
1567
+ autoCompact: createProductionAutoCompact(session, getCurrentSettings, providers),
1568
+ }
1569
+ : {}),
1570
+ toolRegistry,
1571
+ hasAttachedDispatch: () => dispatchBackground.size() > 0,
1572
+ });
1573
+
1574
+ // Coordinated shutdown (SIGINT/SIGTERM, TUI quit) must abort any in-flight
1575
+ // turn before domains stop. The agent abort fans out to every running
1576
+ // tool's AbortSignal, and bash-exec answers it by signalling the tool's
1577
+ // detached process group. Without this, a headless SIGINT exited the CLI
1578
+ // while a running tool's children survived as orphans of init.
1579
+ termination.onDrain(async () => {
1580
+ chat.dispose();
1581
+ // The abort fans out to running tools, but their results still land and
1582
+ // persist through the aborted run's subscribers. Domains (the session
1583
+ // writer among them) stop in the persist phase, strictly after drain, so
1584
+ // awaiting settlement here makes a session append after session stop
1585
+ // impossible by ordering.
1586
+ await chat.whenSettled();
1587
+ });
1588
+
1589
+ // A boot-time resume (CLIO_CODER_RESUME_SESSION_ID) must replay the resumed
1590
+ // session into the chat loop the same way the interactive /resume overlay
1591
+ // does. Without this, the first submit runs with an empty provider context
1592
+ // and parents its user turn at null, appending a second root that silently
1593
+ // abandons the resumed session's active path. The leaf id is restored even
1594
+ // when rebuilding replay messages fails, so parenting stays correct and
1595
+ // only the provider context degrades.
1596
+ if (resumedSessionAtBoot && session) {
1597
+ const resumedMeta = session.current();
1598
+ if (resumedMeta) {
1599
+ let leafTurnId: string | null = null;
1600
+ try {
1601
+ leafTurnId = session.tree(resumedMeta.id).leafId;
1602
+ } catch (err) {
1603
+ bootStderr(
1604
+ `Clio Coder: failed to read resumed session tree ${resumedMeta.id}: ${err instanceof Error ? err.message : String(err)}\n`,
1605
+ );
1606
+ }
1607
+ try {
1608
+ // Scoped to the leaf resume landed on for the same reason the /resume
1609
+ // overlay is (issue #107): with a /tree pin persisted, the file still
1610
+ // holds the abandoned branch after the pinned turn, and replaying it
1611
+ // unfiltered seeds the provider with turns the next append does not
1612
+ // parent onto.
1613
+ chat.resetForSession(
1614
+ leafTurnId,
1615
+ buildReplayAgentMessagesFromTurns(readCurrentSessionEntries(), leafTurnId ? { activeLeafTurnId: leafTurnId } : {}),
1616
+ );
1617
+ } catch (err) {
1618
+ chat.resetForSession(leafTurnId);
1619
+ bootStderr(
1620
+ `Clio Coder: failed to replay resumed session context ${resumedMeta.id}: ${err instanceof Error ? err.message : String(err)}\n`,
1621
+ );
1622
+ }
1623
+ }
1624
+ }
1625
+
1626
+ if (options.acp) {
1627
+ // ACP-served sessions get the same routing isolation as interactive
1628
+ // ones, but ACP v1 has no channel for agent-initiated advisory text:
1629
+ // the session/update union (agent_message_chunk, agent_thought_chunk,
1630
+ // tool_call*, plan, …) carries turn content, and notifications outside
1631
+ // an active session/prompt would break strict clients (see the matching
1632
+ // note in src/engine/acp/server.ts). The external-divergence and
1633
+ // target-removed notices therefore go to the session ledger as `custom`
1634
+ // entries, where /resume and session tooling can surface them.
1635
+ // ACP is operatorless too: bound a runaway turn with the shared
1636
+ // interrupt->stop subscriber, the same way the headless path does.
1637
+ const unsubscribeAcpLoopGuardStop = subscribeLoopGuardStop(bus, chat);
1638
+ const unsubscribeAcpRoutingNotices = bus.on(BusChannels.ConfigNextTurn, (payload) => {
1639
+ const evt = payload as { diff?: { nextTurn?: string[] }; settings?: Readonly<ClioSettings> } | null | undefined;
1640
+ if (!evt?.settings || !Array.isArray(evt.diff?.nextTurn)) return;
1641
+ if (!session?.current()) return;
1642
+ const notices = routingChangeNotices(evt.diff.nextTurn, evt.settings, getCurrentSettings());
1643
+ for (const notice of notices) {
1644
+ try {
1645
+ session.appendEntry({
1646
+ kind: "custom",
1647
+ customType: "clio.routing-notice",
1648
+ parentTurnId: null,
1649
+ data: { kind: notice.kind, level: notice.level, text: notice.text },
1650
+ });
1651
+ } catch {
1652
+ // Advisory only; a ledger write failure must not affect the
1653
+ // ACP turn loop.
1654
+ }
1655
+ }
1656
+ });
1657
+ try {
1658
+ const transport = options.acp.transport ?? createStdioServerTransport(options.acp.transportOptions);
1659
+ const code = await serveClioAcpAgent({
1660
+ transport,
1661
+ chat,
1662
+ ...(session ? { session } : {}),
1663
+ ...(session
1664
+ ? {
1665
+ readSessionEntries: readSessionEntriesForCompact,
1666
+ buildReplayMessages: (entries: ReadonlyArray<SessionEntry>, leafTurnId: string | null) =>
1667
+ buildReplayAgentMessagesFromTurns(entries, leafTurnId === null ? {} : { activeLeafTurnId: leafTurnId }),
1668
+ }
1669
+ : {}),
1670
+ providers,
1671
+ settings: {
1672
+ read: readAcpSafeSettings,
1673
+ commit: commitAcpSafeSettings,
1674
+ },
1675
+ toolRegistry,
1676
+ bus,
1677
+ autonomy: resolveBaselineAutonomy,
1678
+ routing: () => {
1679
+ const settings = getCurrentSettings();
1680
+ return {
1681
+ target: settings.orchestrator.target,
1682
+ model: settings.orchestrator.model,
1683
+ };
1684
+ },
1685
+ onActiveSessionAutonomyChange: (level) => {
1686
+ activeAcpSessionAutonomy = level;
1687
+ },
1688
+ cwd: process.cwd(),
1689
+ version: getVersionInfo().clio,
1690
+ // Stdout belongs to JSON-RPC. Text this process did not author, such
1691
+ // as a provider's failure body, is kept off the wire and written to
1692
+ // the unstructured stderr tail instead.
1693
+ diagnostics: (line) => {
1694
+ process.stderr.write(`[clio:acp] ${line}\n`);
1695
+ },
1696
+ permissionTimeoutMs:
1697
+ options.acp.permissionTimeoutMs ??
1698
+ config?.get().delegation.defaults.permissionTimeoutMs ??
1699
+ DEFAULT_DELEGATION_PERMISSION_TIMEOUT_MS,
1700
+ });
1701
+ // Same ordering the termination drain hook relies on: an aborted turn's
1702
+ // tool results still land and persist through the run's subscribers,
1703
+ // and the session writer stops in result.stop() below. Awaiting
1704
+ // settlement here makes a session append after session stop impossible
1705
+ // by ordering rather than by timing.
1706
+ await chat.whenSettled();
1707
+ chat.dispose();
1708
+ await dispatch.drain();
1709
+ await result.stop();
1710
+ return { exitCode: code, bootTimeMs: timer.snapshot().totalMs };
1711
+ } finally {
1712
+ unsubscribeAcpRoutingNotices();
1713
+ unsubscribeAcpLoopGuardStop();
1714
+ }
1715
+ }
1716
+
1717
+ if (options.headless) {
1718
+ // Operatorless: there is no TUI subscriber to turn a loop-guard interrupt
1719
+ // into a run stop, so a degenerate local model would spin until an
1720
+ // external timeout (each call blocked, the agent loop never aborted). Wire
1721
+ // the shared interrupt->stop subscriber so the run ends with the same
1722
+ // durable closing turn the interactive surface produces.
1723
+ const unsubscribeLoopGuardStop = subscribeLoopGuardStop(bus, chat);
1724
+ const headlessPermissionReason = HEADLESS_PERMISSION_DENIED_REASON;
1725
+ const unsubscribeHeadlessPermission = toolRegistry.onPermissionRequired((call, decision, meta) => {
1726
+ bus.emit(BusChannels.PermissionResolved, {
1727
+ status: "denied",
1728
+ requestId: meta.requestId,
1729
+ origin: "main",
1730
+ decidedBy: "policy:no-operator",
1731
+ tool: call.tool,
1732
+ actionClass: decision.classification.actionClass,
1733
+ reason: headlessPermissionReason,
1734
+ requestedBy: "headless",
1735
+ });
1736
+ toolRegistry.cancelParkedCalls(headlessPermissionReason);
1737
+ });
1738
+ try {
1739
+ const parsedSkillRequest = resources?.parsePendingSkillRequests(options.headless.prompt, process.cwd()) ?? {
1740
+ text: options.headless.prompt,
1741
+ pendingSkillRequests: [],
1742
+ };
1743
+ const promptExpansion = resources?.expandPromptTemplate(parsedSkillRequest.text, process.cwd());
1744
+ // A prompt named as `/name` is a request for that template. When the
1745
+ // template refuses, the run says why and stops; sending the literal
1746
+ // `/name` on to the model spends a turn answering a command it cannot
1747
+ // run, and the operator never sees the refusal.
1748
+ if (promptExpansion?.expanded === false && promptExpansion.refusal) {
1749
+ process.stderr.write(`clio-coder: ${promptExpansion.refusal.message}\n`);
1750
+ await termination.shutdown(1);
1751
+ return { exitCode: 1, bootTimeMs: timer.snapshot().totalMs };
1752
+ }
1753
+ const fileExpansion = await expandInlineFileReferencesAsync(
1754
+ promptExpansion?.expanded ? promptExpansion.text : parsedSkillRequest.text,
1755
+ {
1756
+ cwd: process.cwd(),
1757
+ includeImages: true,
1758
+ missing: "leave",
1759
+ },
1760
+ );
1761
+ const images = [...(options.headless.images ?? []), ...fileExpansion.images];
1762
+ const workingContextPaths = [...(options.headless.workingContextPaths ?? []), ...fileExpansion.referencedPaths];
1763
+ const code = await runHeadlessMainAgent(chat, {
1764
+ prompt: fileExpansion.text,
1765
+ ...(images.length > 0 ? { images } : {}),
1766
+ ...(workingContextPaths.length > 0 ? { workingContextPaths } : {}),
1767
+ ...(options.headless.sampling ? { sampling: options.headless.sampling } : {}),
1768
+ ...(parsedSkillRequest.pendingSkillRequests.length > 0
1769
+ ? { pendingSkillRequests: parsedSkillRequest.pendingSkillRequests }
1770
+ : {}),
1771
+ mode: options.headless.mode ?? "text",
1772
+ ...(options.headless.jsonEvents ? { jsonEvents: options.headless.jsonEvents } : {}),
1773
+ ...(options.headless.steerChannel ? { steerChannel: options.headless.steerChannel } : {}),
1774
+ getSessionHeader: () => printJsonSessionHeader(session?.current() ?? null),
1775
+ });
1776
+ await termination.shutdown(code);
1777
+ return { exitCode: code, bootTimeMs: timer.snapshot().totalMs };
1778
+ } finally {
1779
+ unsubscribeHeadlessPermission();
1780
+ unsubscribeLoopGuardStop();
1781
+ }
1782
+ }
1783
+
1784
+ if (options.terminalLease) {
1785
+ for (const diagnostic of options.terminalLease.takeDiagnostics()) {
1786
+ const text = diagnostic.text.trimEnd();
1787
+ if (text.length > 0) initialNotices.push(text);
1788
+ }
1789
+ }
1790
+
1791
+ await startInteractive({
1792
+ bus,
1793
+ providers,
1794
+ dispatch,
1795
+ ...(agents ? { agents } : {}),
1796
+ observability,
1797
+ chat,
1798
+ ...(options.terminalLease
1799
+ ? {
1800
+ terminalLease: options.terminalLease,
1801
+ onHydratedFrameCommit: () => timer.mark("Stage 1 hydration"),
1802
+ }
1803
+ : { onFirstFrameCommit: () => timer.mark("first TUI paint") }),
1804
+ ...(initialNotices.length > 0 ? { initialNotices } : {}),
1805
+ ...(resources ? { resources } : {}),
1806
+ ...(extensions ? { extensions } : {}),
1807
+ ...(interop ? { interop } : {}),
1808
+ ...(share ? { share } : {}),
1809
+ toolRegistry,
1810
+ ...(session ? { session } : {}),
1811
+ ...(session ? { readSessionEntries: readCurrentSessionEntries } : {}),
1812
+ getTaskBoard: () => taskBoard.cachedSnapshot(),
1813
+ getDecisionBoard: () => decisionBoard.snapshot(),
1814
+ supersedeDecision: (interviewId, key, correction) => decisionBoard.supersede(interviewId, key, correction),
1815
+ userTasks,
1816
+ getTaskMemoryStatus: () => {
1817
+ ensureTaskMemorySession();
1818
+ const settings = getCurrentSettings();
1819
+ const bank = taskMemoryBank.snapshot();
1820
+ return {
1821
+ enabled: settings.memory.intervention.enabled,
1822
+ tier: settings.background.target && settings.background.model ? "llm" : "rules",
1823
+ size: taskMemoryBankSize(bank),
1824
+ lastDecision: memoryIntervention.lastDecision(),
1825
+ bank,
1826
+ activity: memoryIntervention.recentActivity(),
1827
+ stepInFlight: memoryIntervention.stepInFlight(),
1828
+ };
1829
+ },
1830
+ getTaskMemorySeedOffer,
1831
+ seedTaskMemory: seedCurrentTaskMemoryFromHandoff,
1832
+ stateDir: clioStateDir(),
1833
+ dataDir: clioDataDir(),
1834
+ registerAskUserHandler: (handler) => {
1835
+ askUserHandler = handler;
1836
+ return () => {
1837
+ if (askUserHandler === handler) askUserHandler = null;
1838
+ };
1839
+ },
1840
+ getSettings: getCurrentSettings,
1841
+ getFleetNodes: () => result.getContract<SchedulingContract>("scheduling")?.fleet?.list() ?? [],
1842
+ onBackgroundDispatch: () => dispatchBackground.backgroundNewest(),
1843
+ ...(session ? { getSessionId: () => session.current()?.id ?? null } : {}),
1844
+ ...(contextDomain
1845
+ ? {
1846
+ getContextState: (cwd?: string) => contextDomain.contextState(cwd),
1847
+ onInit: async (
1848
+ options: {
1849
+ preview?: boolean;
1850
+ adopt?: boolean;
1851
+ applyClioMd?: boolean;
1852
+ rewriteClioMd?: boolean;
1853
+ proposeClioMd?: boolean;
1854
+ includeGlobalImports?: boolean;
1855
+ heuristic?: boolean;
1856
+ },
1857
+ runIo?: RunIo,
1858
+ ) => {
1859
+ // Interactive context-init explores the repo with the configured target by
1860
+ // default, grounded in the freshly built codewiki, and falls back to the
1861
+ // deterministic heuristic when no target is reachable. --heuristic and
1862
+ // --preview skip model generation.
1863
+ const useModel = options.heuristic !== true && options.preview !== true;
1864
+ const bootstrapOptions = bootstrapInputFromInitOptions(options);
1865
+ await contextDomain.runBootstrap({
1866
+ cwd: process.cwd(),
1867
+ confirmGitignore: () => true,
1868
+ adopt: options.adopt === true,
1869
+ ...bootstrapOptions,
1870
+ ...(useModel
1871
+ ? {
1872
+ generate: modelBootstrapGenerate({
1873
+ dispatch,
1874
+ resolveRoute: () => {
1875
+ if (!config) throw new Error("context-bootstrap configuration unavailable");
1876
+ return resolveBootstrapRoute(config.get());
1877
+ },
1878
+ // Names the agent that actually ran and reports the throw as
1879
+ // what it is. "Scout unavailable" was wrong twice over: the
1880
+ // agent is context-bootstrap, and the same line was printed
1881
+ // for a worker that failed, a worker whose answer the loop
1882
+ // guard removed, and a worker that succeeded and whose
1883
+ // payload the reader then refused.
1884
+ onFallback: (err, mode) =>
1885
+ runIo?.stderr(
1886
+ `context init: context-bootstrap did not produce a handbook, using ${mode === "existing" ? "the existing CLIO-CODER.md" : "the heuristic writer"} (${err.message})\n`,
1887
+ ),
1888
+ }),
1889
+ modelId: "configured-clio-target",
1890
+ }
1891
+ : {}),
1892
+ });
1893
+ },
1894
+ onContextClear: async (options: { all?: boolean; confirmed?: boolean; confirmedAll?: boolean }) => {
1895
+ await contextDomain.runContextClear({
1896
+ cwd: process.cwd(),
1897
+ all: options.all === true,
1898
+ io: {
1899
+ stdout: (s) => process.stdout.write(s),
1900
+ stderr: (s) => process.stderr.write(s),
1901
+ },
1902
+ confirmContext: () => options.confirmed === true,
1903
+ confirmAll: () => options.confirmedAll === true,
1904
+ });
1905
+ },
1906
+ onContextRefresh: async () => {
1907
+ await contextDomain.runContextRefresh({
1908
+ cwd: process.cwd(),
1909
+ io: {
1910
+ stdout: (s) => process.stdout.write(s),
1911
+ stderr: (s) => process.stderr.write(s),
1912
+ },
1913
+ });
1914
+ },
1915
+ }
1916
+ : {}),
1917
+ onSetThinkingLevel: (level) => {
1918
+ const current = getCurrentSettings();
1919
+ const nextLevel =
1920
+ resolveModelRuntimeCapabilitiesForProviders(
1921
+ providers,
1922
+ current.orchestrator.target,
1923
+ current.orchestrator.model,
1924
+ level,
1925
+ )?.thinking.effectiveLevel ?? "off";
1926
+ updateSessionRouting({ orchestrator: { thinkingLevel: nextLevel } });
1927
+ },
1928
+ onCycleThinking: () => {
1929
+ const current = getCurrentSettings();
1930
+ const thinking = resolveModelRuntimeCapabilitiesForProviders(
1931
+ providers,
1932
+ current.orchestrator.target,
1933
+ current.orchestrator.model,
1934
+ current.orchestrator.thinkingLevel ?? "off",
1935
+ )?.thinking;
1936
+ const effectiveAvailable = thinking?.supportedLevels ?? (["off"] as ThinkingLevel[]);
1937
+ const nextLevel = advanceThinkingLevel(
1938
+ thinking?.effectiveLevel ?? current.orchestrator.thinkingLevel ?? "off",
1939
+ effectiveAvailable,
1940
+ );
1941
+ updateSessionRouting({ orchestrator: { thinkingLevel: nextLevel } });
1942
+ },
1943
+ onSelectModel: ({ target, model }) => {
1944
+ const registry = getRuntimeRegistry();
1945
+ const settings = getCurrentSettings();
1946
+ const descriptor = settings.targets.find((e) => e.id === target);
1947
+ if (descriptor) {
1948
+ const runtime = registry.get(descriptor.runtime);
1949
+ if (!runtime) {
1950
+ throw new Error(
1951
+ `cannot use target '${target}' as orchestrator target because runtime '${descriptor.runtime}' is not registered`,
1952
+ );
1953
+ }
1954
+ if (!isOrchestratorEligibleRuntime(runtime)) {
1955
+ throw new Error(
1956
+ `cannot use target '${target}' as orchestrator target because runtime '${runtime.id}' is not an HTTP/native runtime`,
1957
+ );
1958
+ }
1959
+ }
1960
+ updateSessionRouting({ orchestrator: { target, model } });
1961
+ rememberRecentModel(`${target}/${model}`, getCurrentSettings().modelSelector.recentLimit);
1962
+ },
1963
+ writeSettings: (next) => applySettingsBlob(next),
1964
+ commitSetting: (id, next, scope) => commitSetting(id, next, scope),
1965
+ ...(session
1966
+ ? {
1967
+ onResumeSession: (sessionId) => {
1968
+ try {
1969
+ const previousSessionId = session.current()?.id ?? null;
1970
+ session.resume(sessionId);
1971
+ taskBoard.snapshot();
1972
+ if (previousSessionId !== sessionId) ensureTaskMemorySession();
1973
+ } catch (err) {
1974
+ process.stderr.write(
1975
+ `[/resume] failed to resume ${sessionId}: ${err instanceof Error ? err.message : String(err)}\n`,
1976
+ );
1977
+ }
1978
+ },
1979
+ onNewSession: () => {
1980
+ const settings = getCurrentSettings();
1981
+ const input: { cwd: string; target?: string; model?: string } = { cwd: process.cwd() };
1982
+ if (settings.orchestrator.target) input.target = settings.orchestrator.target;
1983
+ if (settings.orchestrator.model) input.model = settings.orchestrator.model;
1984
+ session.create(input);
1985
+ taskBoard.snapshot();
1986
+ ensureTaskMemorySession();
1987
+ },
1988
+ onForkSession: (parentTurnId) => {
1989
+ try {
1990
+ session.fork(parentTurnId);
1991
+ taskBoard.snapshot();
1992
+ ensureTaskMemorySession();
1993
+ } catch (err) {
1994
+ process.stderr.write(
1995
+ `[/fork] failed at turn ${parentTurnId}: ${err instanceof Error ? err.message : String(err)}\n`,
1996
+ );
1997
+ }
1998
+ },
1999
+ onCompact: async (instructions) => {
2000
+ await chat.compact(instructions);
2001
+ },
2002
+ }
2003
+ : {}),
2004
+ onCycleScopedModelForward: () => cycleScopedSession("forward"),
2005
+ onCycleScopedModelBackward: () => cycleScopedSession("backward"),
2006
+ onShutdown: async () => {
2007
+ await termination.shutdown(0);
2008
+ },
2009
+ });
2010
+ return { exitCode: 0, bootTimeMs: timer.snapshot().totalMs };
2011
+ }