@iowarp/clio-coder 0.3.1 → 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 (595) hide show
  1. package/CHANGELOG.md +90 -2
  2. package/CONTRIBUTING.md +23 -23
  3. package/README.md +284 -613
  4. package/dist/{acp-FPR54DGL.js → acp-BIYHVZIM.js} +43 -53
  5. package/dist/{agents-OGPIHPJH.js → agents-YT6SSRIT.js} +43 -26
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-IC3K6NIZ.js → auth-5TWEIYDN.js} +20 -12
  8. package/dist/{chunk-PV4JUBVJ.js → chunk-2EHAIA3X.js} +40 -21
  9. package/dist/{chunk-IS3ONKU3.js → chunk-2IR2NMPA.js} +6 -4
  10. package/dist/chunk-2SFS6XQE.js +122 -0
  11. package/dist/chunk-2VTFPG5O.js +48 -0
  12. package/dist/{chunk-MAR7Y6HW.js → chunk-3ZXDFGR5.js} +23 -16
  13. package/dist/chunk-4BJ5BYCE.js +61 -0
  14. package/dist/{chunk-474KN5II.js → chunk-4BPJXDWC.js} +111 -181
  15. package/dist/chunk-4KLWL3UC.js +18 -0
  16. package/dist/chunk-4VP4KH3K.js +962 -0
  17. package/dist/chunk-4ZG3XFUR.js +77 -0
  18. package/dist/chunk-5B2AEOW5.js +5407 -0
  19. package/dist/{chunk-K2ITRMHZ.js → chunk-5TSRNF4G.js} +6 -138
  20. package/dist/{chunk-OLBBMFRD.js → chunk-5UUP6MWO.js} +24 -62
  21. package/dist/chunk-65DEGPJ6.js +52 -0
  22. package/dist/chunk-6EJMN2Y3.js +17 -0
  23. package/dist/chunk-6EJV5X2W.js +16405 -0
  24. package/dist/chunk-6N5PTWMY.js +136 -0
  25. package/dist/chunk-6XLNIQDB.js +27 -0
  26. package/dist/{chunk-TEKV33Q5.js → chunk-77VKQEHF.js} +65 -33
  27. package/dist/chunk-7CR24IG7.js +242 -0
  28. package/dist/{chunk-M5T5VO65.js → chunk-7EYHLWU7.js} +837 -635
  29. package/dist/chunk-7MNJORFF.js +22 -0
  30. package/dist/{chunk-KY56HMHH.js → chunk-A3CYT5EX.js} +125 -31
  31. package/dist/chunk-AGYYIBLL.js +1069 -0
  32. package/dist/chunk-AO4RKG4M.js +277 -0
  33. package/dist/{chunk-GB6QRBXN.js → chunk-APJ265NV.js} +54 -1187
  34. package/dist/chunk-ARBGF5F7.js +174 -0
  35. package/dist/{chunk-673JJUWJ.js → chunk-BMEMKKIT.js} +2 -2
  36. package/dist/chunk-CBCAPZAA.js +229 -0
  37. package/dist/chunk-CMZWFGD2.js +352 -0
  38. package/dist/chunk-ECH6PKUQ.js +39 -0
  39. package/dist/chunk-ED4KHGC3.js +143 -0
  40. package/dist/chunk-EKMEHE4H.js +340 -0
  41. package/dist/{chunk-RPTR2H26.js → chunk-EPVUXGXG.js} +21 -15
  42. package/dist/chunk-FCSXB6T2.js +338 -0
  43. package/dist/chunk-FJ3H4MN5.js +48 -0
  44. package/dist/chunk-FQ4SKYE4.js +29 -0
  45. package/dist/chunk-G2DE3C7R.js +644 -0
  46. package/dist/chunk-G4BMMOKF.js +182 -0
  47. package/dist/{chunk-ZPY3JZ5E.js → chunk-GGXXDWE4.js} +183 -1233
  48. package/dist/chunk-HC4CLZ2Y.js +68 -0
  49. package/dist/{chunk-LU4TK2PR.js → chunk-HFSBBKSQ.js} +5 -56
  50. package/dist/{chunk-PIUMUEMV.js → chunk-HKIYEGME.js} +10 -6
  51. package/dist/chunk-I4HZDVNP.js +73 -0
  52. package/dist/{chunk-4QKXUHSR.js → chunk-IGLFWIYI.js} +70 -20
  53. package/dist/chunk-IKCO5N3L.js +162 -0
  54. package/dist/chunk-IR4CFBFN.js +56 -0
  55. package/dist/{chunk-PFEFKVGL.js → chunk-J5HN4RYU.js} +13 -11
  56. package/dist/{chunk-R5KLMSBV.js → chunk-J5Q24KAG.js} +2 -2
  57. package/dist/{chunk-K5XEMXTI.js → chunk-JVCV3ICN.js} +1 -1
  58. package/dist/chunk-KJ5LWLOE.js +1077 -0
  59. package/dist/chunk-LBMZMYH2.js +285 -0
  60. package/dist/{chunk-G34LV2PF.js → chunk-LBNRH5WM.js} +84 -170
  61. package/dist/{chunk-H6F6BYOH.js → chunk-LZSJBIVT.js} +7003 -7434
  62. package/dist/{chunk-HQQID6OA.js → chunk-M6SHUN7Q.js} +5 -5
  63. package/dist/chunk-MAW544W2.js +1882 -0
  64. package/dist/chunk-MBS4V7ZP.js +217 -0
  65. package/dist/{chunk-FST4FYJB.js → chunk-MFFY33HR.js} +99 -140
  66. package/dist/chunk-MNA4JGU4.js +255 -0
  67. package/dist/chunk-MQSRRFWA.js +3428 -0
  68. package/dist/{chunk-BSU2YIWB.js → chunk-MVVUPGPW.js} +131 -136
  69. package/dist/chunk-OAO4GE4M.js +619 -0
  70. package/dist/chunk-OHHN2SO4.js +5135 -0
  71. package/dist/chunk-OKGUZO2U.js +34 -0
  72. package/dist/{chunk-GAEBEQVI.js → chunk-OOJYHWRB.js} +32 -346
  73. package/dist/{chunk-Q5WJOSJ7.js → chunk-OQ33BKR3.js} +2 -1
  74. package/dist/chunk-OQE5J4C6.js +73 -0
  75. package/dist/{chunk-KKNLWXI6.js → chunk-ORBHGJC5.js} +8 -8
  76. package/dist/chunk-POHLU5DW.js +1186 -0
  77. package/dist/chunk-QKMUKYO7.js +4961 -0
  78. package/dist/{chunk-ASND7OZK.js → chunk-QTYWRVRA.js} +13 -13
  79. package/dist/{chunk-EYOKLTMF.js → chunk-SRF2PJNW.js} +17 -3
  80. package/dist/chunk-SST6Z5JA.js +80 -0
  81. package/dist/chunk-STBPMHSX.js +2456 -0
  82. package/dist/chunk-T6YILFSB.js +80 -0
  83. package/dist/chunk-TZTZS7QK.js +227 -0
  84. package/dist/chunk-UOV2BYIW.js +107 -0
  85. package/dist/{chunk-Q3RUPKEJ.js → chunk-V4RXGQ5Q.js} +58 -189
  86. package/dist/chunk-VAKQQHWR.js +434 -0
  87. package/dist/chunk-VG7TBQIY.js +128 -0
  88. package/dist/chunk-VJWL6YS5.js +244 -0
  89. package/dist/chunk-WEH5XRJQ.js +32 -0
  90. package/dist/chunk-WMSVI4G2.js +2095 -0
  91. package/dist/chunk-WVO7V2QY.js +797 -0
  92. package/dist/chunk-X4RCMKVQ.js +641 -0
  93. package/dist/chunk-X75S7HFS.js +374 -0
  94. package/dist/chunk-XN3L4EYL.js +46 -0
  95. package/dist/{chunk-RDLVBZEO.js → chunk-YCWGATWI.js} +6 -4
  96. package/dist/chunk-YHZX5GEU.js +193 -0
  97. package/dist/chunk-YXLYO42X.js +91 -0
  98. package/dist/{chunk-NMOX6HFD.js → chunk-ZDOOVTXZ.js} +29 -77
  99. package/dist/chunk-ZI647VB5.js +37 -0
  100. package/dist/{chunk-C4PTHK7P.js → chunk-ZWLZP4ZT.js} +5 -5
  101. package/dist/cli/index.js +62 -54
  102. package/dist/clio-4LY5K2AC.js +25 -0
  103. package/dist/code-nav-7AX6FYE6.js +600 -0
  104. package/dist/codewiki/build-worker.js +66 -0
  105. package/dist/compile-cache-CVJMMODC.js +18 -0
  106. package/dist/{components-DMAOEKFB.js → components-KELWS457.js} +11 -6
  107. package/dist/{config-IRUQ7SE4.js → config-GTLUW2PR.js} +92 -55
  108. package/dist/configure-R6A64DHX.js +42 -0
  109. package/dist/context-5VKGUVJJ.js +866 -0
  110. package/dist/{context-3KWFLHJG.js → context-JFZEJ7W5.js} +15 -13
  111. package/dist/{context-5RADCKTR.js → context-RW5HC47S.js} +71 -35
  112. package/dist/{context-clear-7TSNPAAI.js → context-clear-6ZHBAZZT.js} +54 -28
  113. package/dist/{context-index-W4RLWOQH.js → context-index-BZ4UYMTC.js} +30 -24
  114. package/dist/dispatch-runner-VKBRCWQC.js +1997 -0
  115. package/dist/{docs-5AWSPS37.js → docs-2C2LTVT2.js} +23 -10
  116. package/dist/{doctor-UC5NAJYQ.js → doctor-KI767GSN.js} +27 -17
  117. package/dist/{eval-U6TJHRLX.js → eval-XSSNATB4.js} +29 -16
  118. package/dist/{evidence-YEGUW4L3.js → evidence-UA6AWDQQ.js} +46 -26
  119. package/dist/{evolve-TXARCTPG.js → evolve-QNTFGV6Z.js} +45 -25
  120. package/dist/{extensions-OZFJ3A3G.js → extensions-QVDOHDGJ.js} +16 -7
  121. package/dist/{fleet-6G3DHNYE.js → fleet-Q7UOMUSG.js} +163 -54
  122. package/dist/{fleet-preflight-DSNT37JK.js → fleet-preflight-DDN536IT.js} +7 -4
  123. package/dist/{init-KZ5QTF6M.js → init-WBB65ZHQ.js} +69 -32
  124. package/dist/{memory-73ESV5YC.js → memory-MD3O64RI.js} +48 -27
  125. package/dist/{models-A4PVNWJK.js → models-BZU34YWD.js} +39 -25
  126. package/dist/monitor-MEQA5C3I.js +661 -0
  127. package/dist/{chunk-FCIH3BIZ.js → orchestrator-CGFKEP27.js} +11832 -8687
  128. package/dist/{paths-C4H6IV77.js → paths-UXLN5YYZ.js} +10 -5
  129. package/dist/{preload-6WVMHX3A.js → preload-P6DGH2PZ.js} +2 -2
  130. package/dist/{reset-BGW6OGMV.js → reset-L2FQEE3E.js} +16 -10
  131. package/dist/{run-YTPEYQOH.js → run-IV4Q6RLN.js} +101 -61
  132. package/dist/{share-YIFFV4NQ.js → share-S5BZQC5I.js} +15 -8
  133. package/dist/{skills-2V6RA3OQ.js → skills-LQEKRDTN.js} +34 -14
  134. package/dist/{skills-eval-S2TVJO4F.js → skills-eval-3DC4HEWS.js} +70 -34
  135. package/dist/steer-GGWFUJUD.js +77 -0
  136. package/dist/{targets-TYXLPB23.js → targets-C4SSGQOB.js} +43 -27
  137. package/dist/terminal-lease-IT5JW2NR.js +395 -0
  138. package/dist/{trace-GGOJ6Q6Z.js → trace-PNCASAXC.js} +41 -16
  139. package/dist/{chunk-N6F52NLF.js → tree-sitter-HGKH6LG4.js} +28 -2306
  140. package/dist/{uninstall-LLLT4F4W.js → uninstall-FZCQCDKC.js} +10 -5
  141. package/dist/{upgrade-33G2LMM5.js → upgrade-7TT7SQ3G.js} +45 -25
  142. package/dist/{usage-ZAFSXKKG.js → usage-GV4PKT3M.js} +62 -31
  143. package/dist/verify-G6V4D2G7.js +716 -0
  144. package/dist/web-fetch-2YHJ3KTG.js +638 -0
  145. package/dist/{wiki-generate-NUQCVOQ3.js → wiki-generate-DQF6Z66B.js} +74 -34
  146. package/dist/worker/entry.js +221 -36
  147. package/dist/workspace-G4ZWUIPR.js +22 -0
  148. package/docs/README.md +22 -17
  149. package/docs/acp.md +168 -16
  150. package/docs/alcf-provider.md +1 -1
  151. package/docs/architecture.md +136 -7
  152. package/docs/artifact-versions.md +1 -1
  153. package/docs/built-in-agents.md +1 -1
  154. package/docs/capacity-and-scheduling.md +1 -1
  155. package/docs/commands-and-modes.md +114 -71
  156. package/docs/config-knobs-audit.md +1 -3
  157. package/docs/configuration-and-targets.md +174 -46
  158. package/docs/context-engine.md +29 -6
  159. package/docs/development-pipeline.md +26 -1
  160. package/docs/dispatch-architecture-rationale.md +1 -1
  161. package/docs/documentation-coverage.md +2 -2
  162. package/docs/documentation-guide.md +1 -1
  163. package/docs/environment-variables.md +13 -5
  164. package/docs/eval-runner.md +1 -1
  165. package/docs/evals-internal.md +1 -1
  166. package/docs/evidence-and-memory.md +6 -2
  167. package/docs/evolution.md +2 -2
  168. package/docs/exit-codes-and-output.md +15 -9
  169. package/docs/extensions-and-sharing.md +9 -9
  170. package/docs/fleet-dispatch.md +7 -5
  171. package/docs/git-commit-provenance.md +120 -0
  172. package/docs/glossary.md +1 -1
  173. package/docs/installation-and-lifecycle.md +34 -27
  174. package/docs/middleware-and-components.md +1 -1
  175. package/docs/model-catalog.md +45 -14
  176. package/docs/observability.md +8 -5
  177. package/docs/performance-methodology.md +491 -0
  178. package/docs/pi-boundary.md +72 -0
  179. package/docs/proactive-memory.md +3 -3
  180. package/docs/prompt-envelope-and-tools.md +24 -3
  181. package/docs/provider-adapter-cookbook.md +57 -4
  182. package/docs/release-cut-checklist.md +129 -115
  183. package/docs/safety-model.md +9 -5
  184. package/docs/scientific-validation.md +3 -3
  185. package/docs/session-lifecycle.md +55 -12
  186. package/docs/skills-marketplace.md +12 -8
  187. package/docs/time-conventions.md +1 -1
  188. package/docs/tool-usage.md +3 -3
  189. package/docs/trace-store.md +1 -1
  190. package/docs/troubleshooting.md +10 -7
  191. package/docs/tui-design.md +47 -10
  192. package/docs/worker-dispatch-mechanics.md +1 -1
  193. package/package.json +19 -22
  194. package/skills/coding/ast-grep/SKILL.md +136 -0
  195. package/skills/coding/ast-grep/evals.md +56 -0
  196. package/skills/coding/ast-grep/references/rule_reference.md +297 -0
  197. package/skills/coding/coding-standards/SKILL.md +113 -0
  198. package/skills/coding/coding-standards/evals.md +34 -0
  199. package/skills/coding/prototype/SKILL.md +86 -0
  200. package/skills/coding/prototype/evals.md +42 -0
  201. package/skills/coding/prototype/references/LOGIC.md +67 -0
  202. package/skills/coding/prototype/references/UI.md +112 -0
  203. package/skills/coding/tdd/SKILL.md +101 -0
  204. package/skills/coding/tdd/evals.md +41 -0
  205. package/skills/coding/tdd/references/mocking.md +59 -0
  206. package/skills/coding/tdd/references/tests.md +77 -0
  207. package/skills/context/context-handoff/SKILL.md +126 -0
  208. package/skills/context/context-handoff/evals.md +57 -0
  209. package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
  210. package/skills/context/context-prime/SKILL.md +95 -0
  211. package/skills/context/context-prime/evals.md +54 -0
  212. package/skills/meta/clio-dev/SKILL.md +91 -0
  213. package/skills/meta/clio-dev/evals.md +45 -0
  214. package/skills/meta/clio-test/SKILL.md +130 -0
  215. package/skills/meta/clio-test/evals.md +43 -0
  216. package/skills/meta/clio-test/references/harness.md +97 -0
  217. package/skills/meta/clio-test/references/test-map.md +59 -0
  218. package/skills/meta/credentials/SKILL.md +125 -0
  219. package/skills/meta/credentials/evals.md +104 -0
  220. package/skills/meta/find-skills/SKILL.md +72 -0
  221. package/skills/meta/find-skills/evals.md +47 -0
  222. package/skills/meta/herdr/SKILL.md +127 -0
  223. package/skills/meta/herdr/evals.md +38 -0
  224. package/skills/meta/skill-craft/SKILL.md +102 -0
  225. package/skills/meta/skill-craft/evals.md +41 -0
  226. package/skills/planning/architecture/SKILL.md +129 -0
  227. package/skills/planning/architecture/evals.md +36 -0
  228. package/skills/planning/backlog/SKILL.md +90 -0
  229. package/skills/planning/backlog/evals.md +43 -0
  230. package/skills/planning/prd/SKILL.md +82 -0
  231. package/skills/planning/prd/evals.md +49 -0
  232. package/skills/planning/product-intent/SKILL.md +112 -0
  233. package/skills/planning/product-intent/evals.md +36 -0
  234. package/skills/planning/tech-spec/SKILL.md +115 -0
  235. package/skills/planning/tech-spec/evals.md +47 -0
  236. package/skills/registry.yaml +136 -0
  237. package/skills/research/arxiv-literature/SKILL.md +104 -0
  238. package/skills/research/arxiv-literature/evals.md +58 -0
  239. package/skills/research/experiment-protocol/SKILL.md +122 -0
  240. package/skills/research/experiment-protocol/evals.md +91 -0
  241. package/skills/research/scientific-debugging/SKILL.md +119 -0
  242. package/skills/research/scientific-debugging/evals.md +138 -0
  243. package/skills/research/scientific-modernization/SKILL.md +138 -0
  244. package/skills/research/scientific-modernization/evals.md +84 -0
  245. package/skills/workflow/design-council/SKILL.md +139 -0
  246. package/skills/workflow/design-council/evals.md +97 -0
  247. package/skills/workflow/grill-me/SKILL.md +186 -0
  248. package/skills/workflow/grill-me/evals.md +78 -0
  249. package/skills/workflow/workflow-distiller/SKILL.md +136 -0
  250. package/skills/workflow/workflow-distiller/evals.md +107 -0
  251. package/src/cli/acp.ts +31 -4
  252. package/src/cli/clio.ts +68 -6
  253. package/src/cli/config-inspect.ts +28 -22
  254. package/src/cli/configure.ts +47 -9
  255. package/src/cli/context-clear.ts +2 -2
  256. package/src/cli/context-index.ts +21 -23
  257. package/src/cli/context.ts +13 -8
  258. package/src/cli/default-target.ts +9 -17
  259. package/src/cli/docs.ts +11 -5
  260. package/src/cli/evidence.ts +4 -1
  261. package/src/cli/extensions.ts +10 -1
  262. package/src/cli/fleet.ts +47 -6
  263. package/src/cli/index.ts +55 -26
  264. package/src/cli/memory.ts +3 -1
  265. package/src/cli/models.ts +1 -1
  266. package/src/cli/modes/json-stream.ts +37 -1
  267. package/src/cli/modes/print.ts +24 -9
  268. package/src/cli/run.ts +2 -2
  269. package/src/cli/skills-eval.ts +23 -8
  270. package/src/cli/skills.ts +19 -4
  271. package/src/cli/targets.ts +4 -0
  272. package/src/cli/text-layout.ts +15 -5
  273. package/src/cli/trace.ts +62 -14
  274. package/src/cli/upgrade.ts +18 -2
  275. package/src/cli/usage.ts +10 -3
  276. package/src/cli/wiki-generate.ts +2 -1
  277. package/src/core/agent-environment.ts +7 -0
  278. package/src/core/bash-exec.ts +72 -1
  279. package/src/core/boot-trace.ts +9 -4
  280. package/src/core/bus-events.ts +20 -4
  281. package/src/core/commit-attribution.ts +157 -0
  282. package/src/core/compile-cache.ts +159 -0
  283. package/src/core/config.ts +131 -2
  284. package/src/core/defaults.ts +39 -5
  285. package/src/core/domain-loader.ts +12 -5
  286. package/src/core/git-commit-attribution.ts +362 -0
  287. package/src/core/incomplete-installation.ts +45 -0
  288. package/src/core/response-schema.ts +1 -1
  289. package/src/core/safe-exec.ts +13 -1
  290. package/src/core/settings-layers.ts +155 -21
  291. package/src/core/skill-activation.ts +1 -1
  292. package/src/core/startup-timer.ts +3 -3
  293. package/src/core/state-file-lock.ts +13 -1
  294. package/src/core/termination.ts +78 -5
  295. package/src/domains/config/classify.ts +15 -3
  296. package/src/domains/config/extension.ts +19 -13
  297. package/src/domains/config/index.ts +10 -0
  298. package/src/domains/config/keybindings.ts +42 -6
  299. package/src/domains/context/bootstrap-prompt.ts +1 -1
  300. package/src/domains/context/bootstrap.ts +111 -18
  301. package/src/domains/context/clear.ts +16 -11
  302. package/src/domains/context/clio-md.ts +111 -9
  303. package/src/domains/context/codewiki/artifact.ts +400 -0
  304. package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
  305. package/src/domains/context/codewiki/build-worker.ts +54 -0
  306. package/src/domains/context/codewiki/coordinator.ts +182 -0
  307. package/src/domains/context/codewiki/indexer.ts +59 -144
  308. package/src/domains/context/codewiki/paths.ts +67 -0
  309. package/src/domains/context/codewiki/schema.ts +80 -0
  310. package/src/domains/context/codewiki/tree-sitter.ts +1 -1
  311. package/src/domains/context/contract.ts +11 -5
  312. package/src/domains/context/extension.ts +94 -143
  313. package/src/domains/context/fingerprint.ts +3 -1
  314. package/src/domains/context/index.ts +12 -22
  315. package/src/domains/context/project-metadata.ts +19 -0
  316. package/src/domains/context/prompt-context.ts +9 -10
  317. package/src/domains/context/refresh.ts +29 -21
  318. package/src/domains/context/runtime.ts +17 -0
  319. package/src/domains/context/wiki/generate.ts +39 -34
  320. package/src/domains/context/wiki/plan.ts +1 -1
  321. package/src/domains/context/wiki/prompts.ts +21 -8
  322. package/src/domains/dispatch/code-step.ts +20 -1
  323. package/src/domains/dispatch/extension.ts +158 -21
  324. package/src/domains/dispatch/failure-classification.ts +6 -0
  325. package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
  326. package/src/domains/dispatch/orphan-recovery.ts +50 -8
  327. package/src/domains/dispatch/receipt-integrity.ts +5 -0
  328. package/src/domains/dispatch/state.ts +31 -6
  329. package/src/domains/dispatch/transport.ts +2 -1
  330. package/src/domains/dispatch/types.ts +14 -0
  331. package/src/domains/dispatch/worker-spawn.ts +21 -2
  332. package/src/domains/eval/metrics/context.ts +1 -1
  333. package/src/domains/eval/types.ts +0 -1
  334. package/src/domains/evidence/build.ts +41 -1
  335. package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
  336. package/src/domains/lifecycle/migrations/index.ts +24 -4
  337. package/src/domains/middleware/hooks-io.ts +12 -0
  338. package/src/domains/middleware/skills-reminder.ts +30 -15
  339. package/src/domains/prompts/compiler.ts +142 -84
  340. package/src/domains/prompts/contract.ts +18 -2
  341. package/src/domains/prompts/extension.ts +39 -7
  342. package/src/domains/prompts/fragment-loader.ts +0 -1
  343. package/src/domains/prompts/fragments/identity/clio.md +2 -4
  344. package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
  345. package/src/domains/prompts/fragments/identity/self-awareness.md +1 -45
  346. package/src/domains/prompts/fragments/operating/contract.md +4 -50
  347. package/src/domains/prompts/fragments/operating/delegation.md +42 -0
  348. package/src/domains/prompts/fragments/operating/skills.md +26 -0
  349. package/src/domains/prompts/fragments/operating/worker.md +16 -0
  350. package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
  351. package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
  352. package/src/domains/prompts/fragments/safety/read-only.md +4 -4
  353. package/src/domains/prompts/fragments/safety/suggest.md +2 -2
  354. package/src/domains/prompts/fragments/wiki/page.md +10 -0
  355. package/src/domains/prompts/fragments/wiki/plan.md +10 -0
  356. package/src/domains/prompts/preload.ts +3 -3
  357. package/src/domains/providers/auth/api-key.ts +1 -1
  358. package/src/domains/providers/auth/backend-file.ts +20 -10
  359. package/src/domains/providers/auth/backend-memory.ts +59 -4
  360. package/src/domains/providers/auth/boot-status.ts +65 -0
  361. package/src/domains/providers/auth/oauth.ts +2 -1
  362. package/src/domains/providers/auth/storage.ts +97 -38
  363. package/src/domains/providers/capabilities.ts +12 -4
  364. package/src/domains/providers/contract.ts +15 -4
  365. package/src/domains/providers/extension.ts +18 -6
  366. package/src/domains/providers/model-runtime-capabilities.ts +15 -4
  367. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +118 -35
  368. package/src/domains/providers/plugins.ts +5 -3
  369. package/src/domains/providers/probe/fingerprint.ts +25 -5
  370. package/src/domains/providers/registry.ts +31 -10
  371. package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
  372. package/src/domains/providers/runtimes/builtins.ts +2 -2
  373. package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
  374. package/src/domains/providers/runtimes/common/local-synth.ts +6 -7
  375. package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
  376. package/src/domains/providers/support.ts +6 -3
  377. package/src/domains/providers/types/local-model-quirks.ts +7 -9
  378. package/src/domains/providers/types/runtime-descriptor.ts +12 -1
  379. package/src/domains/providers/types/target-descriptor.ts +22 -0
  380. package/src/domains/resources/contract.ts +0 -1
  381. package/src/domains/resources/extension.ts +1 -3
  382. package/src/domains/resources/loader.ts +3 -4
  383. package/src/domains/resources/prompts/loader.ts +16 -2
  384. package/src/domains/resources/prompts/substitute.ts +1 -65
  385. package/src/domains/resources/skills/content-hash.ts +2 -0
  386. package/src/domains/resources/skills/install.ts +17 -0
  387. package/src/domains/resources/skills/loader.ts +17 -10
  388. package/src/domains/resources/skills/marketplace.ts +55 -9
  389. package/src/domains/safety/action-classifier.ts +4 -2
  390. package/src/domains/safety/audit.ts +8 -2
  391. package/src/domains/safety/extension.ts +1 -1
  392. package/src/domains/session/compaction/branch-summary.ts +3 -2
  393. package/src/domains/session/compaction/cut-point.ts +2 -1
  394. package/src/domains/session/compaction/tokens.ts +2 -1
  395. package/src/domains/session/context-ledger.ts +14 -0
  396. package/src/domains/session/contract.ts +15 -0
  397. package/src/domains/session/decision-board.ts +190 -0
  398. package/src/domains/session/entries.ts +66 -3
  399. package/src/domains/session/extension.ts +93 -12
  400. package/src/domains/session/retry.ts +10 -18
  401. package/src/domains/session/session-artifacts.ts +107 -0
  402. package/src/domains/session/task-board.ts +207 -13
  403. package/src/domains/session/tree/active-path.ts +44 -5
  404. package/src/domains/session/tree/fork.ts +26 -27
  405. package/src/domains/session/tree/preview.ts +2 -2
  406. package/src/domains/session/workspace/git-probe.ts +17 -11
  407. package/src/domains/user-tasks/store.ts +297 -0
  408. package/src/engine/acp/errors.ts +96 -0
  409. package/src/engine/acp/server.ts +1728 -146
  410. package/src/engine/acp/transport.ts +135 -14
  411. package/src/engine/acp/types.ts +26 -0
  412. package/src/engine/agent.ts +3 -3
  413. package/src/engine/ai.ts +32 -27
  414. package/src/engine/alcf-oauth.ts +26 -19
  415. package/src/engine/api-registry.ts +223 -0
  416. package/src/engine/apis/index.ts +3 -7
  417. package/src/engine/apis/llamacpp-residency.ts +49 -9
  418. package/src/engine/apis/lmstudio-residency.ts +5 -21
  419. package/src/engine/apis/lmstudio.ts +243 -0
  420. package/src/engine/apis/ollama-native.ts +24 -3
  421. package/src/engine/apis/openai-completions.ts +170 -91
  422. package/src/engine/apis/residency.ts +139 -3
  423. package/src/engine/apis/types.ts +16 -0
  424. package/src/engine/env-api-keys.ts +98 -0
  425. package/src/engine/gemma-channel-filter.ts +223 -0
  426. package/src/engine/instrumented-tui.ts +192 -0
  427. package/src/engine/messages.ts +14 -0
  428. package/src/engine/models.ts +42 -0
  429. package/src/engine/oauth.ts +16 -12
  430. package/src/engine/prompt-templates.ts +1 -0
  431. package/src/engine/provider-payload.ts +16 -59
  432. package/src/engine/strip-tokenizer-sentinels.ts +1 -1
  433. package/src/engine/truncate.ts +9 -0
  434. package/src/engine/tui.ts +17 -9
  435. package/src/engine/types.ts +3 -6
  436. package/src/engine/worker-runtime-capabilities.ts +5 -0
  437. package/src/engine/worker-runtime.ts +1 -1
  438. package/src/engine/worker-tools.ts +9 -4
  439. package/src/entry/boot-options.ts +50 -0
  440. package/src/entry/orchestrator.ts +288 -150
  441. package/src/interactive/application-controller.ts +89 -2
  442. package/src/interactive/chat-loop.ts +266 -41
  443. package/src/interactive/chat-panel.ts +173 -47
  444. package/src/interactive/chat-renderer.ts +262 -72
  445. package/src/interactive/clio-editor.ts +3 -8
  446. package/src/interactive/command-fallbacks.ts +2 -2
  447. package/src/interactive/context-overlay.ts +27 -1
  448. package/src/interactive/editor-submit.ts +228 -24
  449. package/src/interactive/export-html/ansi-to-html.ts +161 -0
  450. package/src/interactive/export-html/index.ts +51 -0
  451. package/src/interactive/export-html/template.ts +45 -0
  452. package/src/interactive/export-html/tool-renderer.ts +54 -0
  453. package/src/interactive/footer/dashboard.ts +4 -0
  454. package/src/interactive/footer/notifications.ts +1 -1
  455. package/src/interactive/footer/widgets.ts +20 -2
  456. package/src/interactive/footer-panel.ts +2 -2
  457. package/src/interactive/format-time.ts +14 -2
  458. package/src/interactive/interactive-application.ts +201 -17
  459. package/src/interactive/interactive-event-projection.ts +6 -1
  460. package/src/interactive/interactive-input-runtime.ts +50 -4
  461. package/src/interactive/interactive-presentation.ts +151 -19
  462. package/src/interactive/interactive-shell.ts +268 -14
  463. package/src/interactive/interactive-slash-runtime.ts +176 -114
  464. package/src/interactive/interactive-tickers.ts +38 -7
  465. package/src/interactive/keybinding-manager.ts +1 -1
  466. package/src/interactive/layout.ts +40 -3
  467. package/src/interactive/overlay-frame.ts +1 -1
  468. package/src/interactive/overlay-general-openers.ts +58 -1
  469. package/src/interactive/overlay-key-routing.ts +3 -0
  470. package/src/interactive/overlay-lifecycle.ts +13 -0
  471. package/src/interactive/overlay-permission-lifecycle.ts +2 -1
  472. package/src/interactive/overlay-session-lifecycle.ts +69 -12
  473. package/src/interactive/overlays/decisions.ts +300 -0
  474. package/src/interactive/overlays/help-reference.ts +15 -10
  475. package/src/interactive/overlays/model-selector.ts +34 -16
  476. package/src/interactive/overlays/session-selector.ts +18 -0
  477. package/src/interactive/overlays/settings.ts +105 -17
  478. package/src/interactive/overlays/skills-hub.ts +4 -4
  479. package/src/interactive/overlays/tree-selector.ts +41 -6
  480. package/src/interactive/render-trace.ts +499 -90
  481. package/src/interactive/renderers/compaction-summary.ts +2 -2
  482. package/src/interactive/renderers/diff.ts +115 -104
  483. package/src/interactive/renderers/mermaid.ts +53 -0
  484. package/src/interactive/renderers/tool-execution.ts +386 -133
  485. package/src/interactive/renderers/worker-entry.ts +20 -4
  486. package/src/interactive/session-switch-settlement.ts +10 -0
  487. package/src/interactive/slash-autocomplete.ts +6 -114
  488. package/src/interactive/slash-commands.ts +135 -47
  489. package/src/interactive/slash-spec.ts +9 -38
  490. package/src/interactive/status/controller.ts +5 -1
  491. package/src/interactive/stdout-backpressure.ts +99 -0
  492. package/src/interactive/stream-pacer.ts +530 -0
  493. package/src/interactive/stream-pacing-policy.ts +66 -0
  494. package/src/interactive/tasks-overlay.ts +368 -14
  495. package/src/interactive/terminal-lease.ts +485 -0
  496. package/src/interactive/theme/tokens.ts +1 -1
  497. package/src/interactive/turn-context.ts +4 -3
  498. package/src/interactive/turn-persistence.ts +30 -13
  499. package/src/interactive/turn-queues.ts +12 -0
  500. package/src/interactive/turn-recovery.ts +25 -8
  501. package/src/interactive/turn-runtime.ts +79 -12
  502. package/src/interactive/turn-state.ts +10 -0
  503. package/src/interactive/view/artifacts.ts +114 -4
  504. package/src/interactive/view/view-overlay.ts +3 -0
  505. package/src/interactive/welcome-dashboard.ts +17 -16
  506. package/src/interactive/worker-receipts.ts +52 -3
  507. package/src/interactive/worker-stream.ts +5 -1
  508. package/src/tools/agent-tools.ts +23 -3
  509. package/src/tools/artifact.ts +2 -2
  510. package/src/tools/ask-user.ts +23 -13
  511. package/src/tools/bash.ts +30 -2
  512. package/src/tools/bootstrap.ts +34 -431
  513. package/src/tools/builtin-tool-catalog.ts +265 -0
  514. package/src/tools/codewiki/code-nav-surface.ts +29 -0
  515. package/src/tools/codewiki/code-nav.ts +8 -22
  516. package/src/tools/codewiki/shared.ts +41 -38
  517. package/src/tools/context/docs-engine.ts +14 -3
  518. package/src/tools/context/index.ts +107 -28
  519. package/src/tools/context/surface.ts +19 -0
  520. package/src/tools/core-bootstrap.ts +168 -0
  521. package/src/tools/credential-present.ts +5 -5
  522. package/src/tools/dispatch-admission.ts +533 -0
  523. package/src/tools/dispatch-background.ts +54 -0
  524. package/src/tools/dispatch-event-text.ts +6 -0
  525. package/src/tools/dispatch-plan.ts +9 -4
  526. package/src/tools/dispatch-run-events.ts +238 -0
  527. package/src/tools/dispatch-runner.ts +2370 -0
  528. package/src/tools/dispatch-scout-admission.ts +295 -0
  529. package/src/tools/dispatch-types.ts +77 -0
  530. package/src/tools/dispatch.ts +67 -3161
  531. package/src/tools/find.ts +4 -2
  532. package/src/tools/grep.ts +2 -2
  533. package/src/tools/lazy-tool.ts +60 -0
  534. package/src/tools/ledger.ts +3 -3
  535. package/src/tools/monitor-surface.ts +36 -0
  536. package/src/tools/monitor.ts +2 -32
  537. package/src/tools/observers.ts +2 -2
  538. package/src/tools/registry.ts +39 -27
  539. package/src/tools/safe-exec.ts +2 -2
  540. package/src/tools/steer-surface.ts +17 -0
  541. package/src/tools/steer.ts +2 -13
  542. package/src/tools/tasks.ts +108 -11
  543. package/src/tools/truncate.ts +25 -184
  544. package/src/tools/verify/frontend.ts +3 -1
  545. package/src/tools/verify/index.ts +3 -38
  546. package/src/tools/verify/surface.ts +46 -0
  547. package/src/tools/web-fetch-surface.ts +23 -0
  548. package/src/tools/web-fetch.ts +2 -20
  549. package/src/tools/write.ts +7 -2
  550. package/src/worker/entry.ts +39 -2
  551. package/src/worker/spec-contract.ts +26 -5
  552. package/dist/chunk-7SS2CTV2.js +0 -61361
  553. package/dist/chunk-DKGKUHFA.js +0 -924
  554. package/dist/chunk-GEP36Y4X.js +0 -12796
  555. package/dist/chunk-XYWBQRDM.js +0 -137
  556. package/dist/clio-BZVGEUFJ.js +0 -58
  557. package/dist/configure-S7S6F6CL.js +0 -32
  558. package/docs/html/agents_blueprint.html +0 -936
  559. package/docs/html/alcf_blueprint.html +0 -324
  560. package/docs/html/architecture_blueprint.html +0 -850
  561. package/docs/html/commands_blueprint.html +0 -939
  562. package/docs/html/config_knobs_audit_blueprint.html +0 -178
  563. package/docs/html/configuration_blueprint.html +0 -1080
  564. package/docs/html/context_blueprint.html +0 -603
  565. package/docs/html/documentation_blueprint.html +0 -832
  566. package/docs/html/environment_blueprint.html +0 -404
  567. package/docs/html/eval_blueprint.html +0 -743
  568. package/docs/html/evals_internal_blueprint.html +0 -190
  569. package/docs/html/evolution_blueprint.html +0 -674
  570. package/docs/html/extensions_blueprint.html +0 -2065
  571. package/docs/html/fleet_dispatch_blueprint.html +0 -286
  572. package/docs/html/index.html +0 -919
  573. package/docs/html/lifecycle_blueprint.html +0 -723
  574. package/docs/html/memory_blueprint.html +0 -699
  575. package/docs/html/middleware_blueprint.html +0 -664
  576. package/docs/html/models_blueprint.html +0 -2366
  577. package/docs/html/observability_blueprint.html +0 -683
  578. package/docs/html/provider_adapter_blueprint.html +0 -245
  579. package/docs/html/safety_blueprint.html +0 -1386
  580. package/docs/html/shared.css +0 -571
  581. package/docs/html/shared.js +0 -143
  582. package/docs/html/skills_blueprint.html +0 -671
  583. package/docs/html/soak_blueprint.html +0 -182
  584. package/docs/html/tool_usage_blueprint.html +0 -350
  585. package/docs/html/tools_blueprint.html +0 -2249
  586. package/docs/html/trace_blueprint.html +0 -235
  587. package/docs/html/tui_design_blueprint.html +0 -374
  588. package/docs/html/validation_blueprint.html +0 -961
  589. package/docs/html/worker_dispatch_blueprint.html +0 -231
  590. package/src/core/release.ts +0 -2
  591. package/src/domains/providers/runtimes/common/lmstudio-logger.ts +0 -32
  592. package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +0 -491
  593. package/src/engine/apis/lmstudio-native.ts +0 -1438
  594. package/src/engine/apis/thinking-replay.ts +0 -11
  595. package/src/tools/string-enum.ts +0 -15
@@ -4,6 +4,10 @@ import { loadMemoryRecordsSync, type MemoryRecord } from "../domains/memory/inde
4
4
  import type { ObservabilityContract } from "../domains/observability/index.js";
5
5
  import type { ContextLedger } from "../domains/session/context-ledger.js";
6
6
  import type { SessionMeta } from "../domains/session/index.js";
7
+ import { foldSessionArtifacts } from "../domains/session/session-artifacts.js";
8
+ import { foldSessionTaskHistory } from "../domains/session/task-board.js";
9
+ import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
10
+ import type { UserTasksStore } from "../domains/user-tasks/store.js";
7
11
  import type { TUI } from "../engine/tui.js";
8
12
  import { type OpenContextOverlayOptions, openContextOverlay } from "./context-overlay.js";
9
13
  import { openCostOverlay } from "./cost-overlay.js";
@@ -20,7 +24,8 @@ import {
20
24
  contextResetOptions,
21
25
  openContextResetOverlay,
22
26
  } from "./overlays/context-reset.js";
23
- import type { ContextClearCommandOptions } from "./slash-commands.js";
27
+ import { formatDecisionCorrectionTurn, openDecisionsOverlay } from "./overlays/decisions.js";
28
+ import { type ContextClearCommandOptions, formatUserTaskHandoff } from "./slash-commands.js";
24
29
  import { openTasksOverlay } from "./tasks-overlay.js";
25
30
  import { type ArtifactProviderDeps, createDefaultArtifactProviders } from "./view/artifacts.js";
26
31
  import { openViewOverlay } from "./view/view-overlay.js";
@@ -40,6 +45,10 @@ export interface OverlayGeneralOpenersDeps {
40
45
  renderTaskIsland: () => void;
41
46
  requestRender: () => void;
42
47
  getTaskBoard?: Parameters<typeof openTasksOverlay>[1];
48
+ userTasks?: UserTasksStore;
49
+ getDecisionBoard?: Parameters<typeof openDecisionsOverlay>[1];
50
+ supersedeDecision?: (interviewId: string, key: string, correction?: string) => unknown;
51
+ submitChat: (text: string) => void;
43
52
  getTaskMemoryStatus?: Parameters<typeof openMemoryOverlay>[1];
44
53
  dataDir: string;
45
54
  notify: (level: "info" | "success" | "warning" | "error", text: string, key?: string) => void;
@@ -56,6 +65,7 @@ export interface OverlayGeneralOpenersDeps {
56
65
  openContextOverlay?: typeof openContextOverlay;
57
66
  openContextResetOverlay?: typeof openContextResetOverlay;
58
67
  openTasksOverlay?: typeof openTasksOverlay;
68
+ openDecisionsOverlay?: typeof openDecisionsOverlay;
59
69
  openMemoryOverlay?: typeof openMemoryOverlay;
60
70
  openViewOverlay?: typeof openViewOverlay;
61
71
  }
@@ -66,6 +76,7 @@ export interface OverlayGeneralOpeners {
66
76
  openContextReset(): void;
67
77
  toggleFooter(): void;
68
78
  openTasks(): void;
79
+ openDecisions(): void;
69
80
  openMemory(): void;
70
81
  openView(initialFilter?: string): void;
71
82
  toggleDispatchBoard(): void;
@@ -76,6 +87,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
76
87
  const openContextOverlayFactory = deps.openContextOverlay ?? openContextOverlay;
77
88
  const openContextResetOverlayFactory = deps.openContextResetOverlay ?? openContextResetOverlay;
78
89
  const openTasksOverlayFactory = deps.openTasksOverlay ?? openTasksOverlay;
90
+ const openDecisionsOverlayFactory = deps.openDecisionsOverlay ?? openDecisionsOverlay;
79
91
  const openMemoryOverlayFactory = deps.openMemoryOverlay ?? openMemoryOverlay;
80
92
  const openViewOverlayFactory = deps.openViewOverlay ?? openViewOverlay;
81
93
  const showOverlayFrameFactory = deps.showOverlayFrame ?? showClioOverlayFrame;
@@ -137,6 +149,50 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
137
149
  deps.transitions.state = "tasks";
138
150
  deps.transitions.handle = openTasksOverlayFactory(deps.tui, () => deps.getTaskBoard?.() ?? null, {
139
151
  onClose: deps.closeOverlay,
152
+ getSessionSnapshot: () => {
153
+ const meta = deps.getSessionMeta();
154
+ const entries = filterEntriesToActivePath(deps.readSessionEntries?.() ?? [], meta?.pinnedLeafTurnId ?? undefined);
155
+ const workspace = meta?.cwd ?? process.cwd();
156
+ return {
157
+ history: foldSessionTaskHistory(entries),
158
+ artifacts: foldSessionArtifacts(entries, { workspace }),
159
+ };
160
+ },
161
+ ...(deps.userTasks
162
+ ? {
163
+ getUserTasks: () => deps.userTasks?.snapshot() ?? [],
164
+ onAddUserTask: (title: string) => void deps.userTasks?.add(title),
165
+ onHandUserTask: (id: string) => {
166
+ const task = deps.userTasks?.hand(id, deps.getSessionId?.() ?? undefined);
167
+ if (!task) throw new Error("operator task inbox is unavailable");
168
+ deps.submitChat(formatUserTaskHandoff(task));
169
+ },
170
+ onDoneUserTask: (id: string) => void deps.userTasks?.done(id),
171
+ onDropUserTask: (id: string) => void deps.userTasks?.drop(id),
172
+ }
173
+ : {}),
174
+ onOpenArtifact: (path: string) => openView(`workspace:${path}`),
175
+ requestRender: deps.requestRender,
176
+ workspace: deps.getSessionMeta()?.cwd ?? process.cwd(),
177
+ });
178
+ deps.requestRender();
179
+ };
180
+
181
+ const openDecisions = (): void => {
182
+ if (deps.transitions.state !== "closed") return;
183
+ deps.transitions.state = "decisions";
184
+ deps.transitions.handle = openDecisionsOverlayFactory(deps.tui, () => deps.getDecisionBoard?.() ?? [], {
185
+ onSupersede: (selection) => {
186
+ if (!deps.supersedeDecision) throw new Error("decision board is unavailable in this session");
187
+ deps.supersedeDecision(selection.interviewId, selection.key);
188
+ },
189
+ onCorrection: (selection, correction) => {
190
+ if (!deps.supersedeDecision) throw new Error("decision board is unavailable in this session");
191
+ deps.supersedeDecision(selection.interviewId, selection.key, correction);
192
+ deps.closeOverlay();
193
+ deps.submitChat(formatDecisionCorrectionTurn(selection, correction));
194
+ },
195
+ onClose: deps.closeOverlay,
140
196
  });
141
197
  deps.requestRender();
142
198
  };
@@ -210,6 +266,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
210
266
  openContextReset,
211
267
  toggleFooter,
212
268
  openTasks,
269
+ openDecisions,
213
270
  openMemory,
214
271
  openView,
215
272
  toggleDispatchBoard,
@@ -10,6 +10,7 @@ export type OverlayState =
10
10
  | "context-view"
11
11
  | "context-reset"
12
12
  | "tasks"
13
+ | "decisions"
13
14
  | "memory"
14
15
  | "view"
15
16
  | "model"
@@ -158,6 +159,7 @@ export function routeOverlayKey(
158
159
  if (overlayState === "closed") return false;
159
160
  if (
160
161
  (overlayState === "dispatch-board" && matches(data, "clio.dispatchBoard.toggle")) ||
162
+ (overlayState === "tasks" && matches(data, "clio.tasks.open")) ||
161
163
  (overlayState === "tree" && matches(data, "clio.session.tree")) ||
162
164
  (overlayState === "model" && matches(data, "clio.model.select")) ||
163
165
  (overlayState === "help" && matches(data, "clio.leader"))
@@ -180,6 +182,7 @@ export function routeOverlayKey(
180
182
  }
181
183
  if (overlayState === "context-reset") return false;
182
184
  if (overlayState === "tasks") return false;
185
+ if (overlayState === "decisions") return false;
183
186
  if (overlayState === "memory") return false;
184
187
  if (overlayState === "view") return false;
185
188
  if (overlayState === "model") return routeModelOverlayKey(data, deps);
@@ -33,6 +33,8 @@ export type OverlayLifecycleApplicationDeps = Pick<
33
33
  | "getSessionId"
34
34
  | "getSettings"
35
35
  | "getTaskBoard"
36
+ | "userTasks"
37
+ | "getDecisionBoard"
36
38
  | "getTaskMemoryStatus"
37
39
  | "interop"
38
40
  | "observability"
@@ -46,6 +48,7 @@ export type OverlayLifecycleApplicationDeps = Pick<
46
48
  | "resources"
47
49
  | "session"
48
50
  | "stateDir"
51
+ | "supersedeDecision"
49
52
  | "toolRegistry"
50
53
  | "writeSettings"
51
54
  >;
@@ -94,6 +97,7 @@ export interface OverlayLifecycleRuntimeDeps {
94
97
  openContextOverlay?: typeof import("./context-overlay.js").openContextOverlay;
95
98
  openContextResetOverlay?: typeof import("./overlays/context-reset.js").openContextResetOverlay;
96
99
  openTasksOverlay?: typeof import("./tasks-overlay.js").openTasksOverlay;
100
+ openDecisionsOverlay?: typeof import("./overlays/decisions.js").openDecisionsOverlay;
97
101
  openMemoryOverlay?: typeof import("./memory-overlay.js").openMemoryOverlay;
98
102
  openViewOverlay?: typeof import("./view/view-overlay.js").openViewOverlay;
99
103
  openHelpOverlay?: typeof import("./overlays/help-reference.js").openHelpOverlay;
@@ -118,6 +122,7 @@ export interface OverlayLifecycleController {
118
122
  openContextResetOverlayState(): void;
119
123
  toggleFooterDashboardState(): void;
120
124
  openTasksOverlayState(): void;
125
+ openDecisionsOverlayState(): void;
121
126
  openMemoryOverlayState(): void;
122
127
  openViewOverlayState(initialFilter?: string): void;
123
128
  openModelOverlayState(): void;
@@ -169,6 +174,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
169
174
  openContextOverlay: openContextOverlayFactory,
170
175
  openContextResetOverlay: openContextResetOverlayFactory,
171
176
  openTasksOverlay: openTasksOverlayFactory,
177
+ openDecisionsOverlay: openDecisionsOverlayFactory,
172
178
  openMemoryOverlay: openMemoryOverlayFactory,
173
179
  openViewOverlay: openViewOverlayFactory,
174
180
  openHelpOverlay: openHelpOverlayFactory,
@@ -334,6 +340,10 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
334
340
  renderTaskIsland: () => interactiveTickers.renderTaskIsland(),
335
341
  requestRender: () => tui.requestRender(),
336
342
  ...(deps.app.getTaskBoard ? { getTaskBoard: deps.app.getTaskBoard } : {}),
343
+ ...(deps.app.userTasks ? { userTasks: deps.app.userTasks } : {}),
344
+ ...(deps.app.getDecisionBoard ? { getDecisionBoard: deps.app.getDecisionBoard } : {}),
345
+ ...(deps.app.supersedeDecision ? { supersedeDecision: deps.app.supersedeDecision } : {}),
346
+ submitChat: (text) => deps.getSlashContext().submitChat(text),
337
347
  ...(deps.app.getTaskMemoryStatus ? { getTaskMemoryStatus: deps.app.getTaskMemoryStatus } : {}),
338
348
  dataDir: deps.app.dataDir,
339
349
  notify,
@@ -350,6 +360,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
350
360
  ...(openContextOverlayFactory ? { openContextOverlay: openContextOverlayFactory } : {}),
351
361
  ...(openContextResetOverlayFactory ? { openContextResetOverlay: openContextResetOverlayFactory } : {}),
352
362
  ...(openTasksOverlayFactory ? { openTasksOverlay: openTasksOverlayFactory } : {}),
363
+ ...(openDecisionsOverlayFactory ? { openDecisionsOverlay: openDecisionsOverlayFactory } : {}),
353
364
  ...(openMemoryOverlayFactory ? { openMemoryOverlay: openMemoryOverlayFactory } : {}),
354
365
  ...(openViewOverlayFactory ? { openViewOverlay: openViewOverlayFactory } : {}),
355
366
  });
@@ -362,6 +373,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
362
373
  const openContextResetOverlayState = overlayGeneralOpeners.openContextReset;
363
374
  const toggleFooterDashboardState = overlayGeneralOpeners.toggleFooter;
364
375
  const openTasksOverlayState = overlayGeneralOpeners.openTasks;
376
+ const openDecisionsOverlayState = overlayGeneralOpeners.openDecisions;
365
377
  const openMemoryOverlayState = overlayGeneralOpeners.openMemory;
366
378
  const openViewOverlayState = overlayGeneralOpeners.openView;
367
379
  const toggleDispatchBoardOverlay = overlayGeneralOpeners.toggleDispatchBoard;
@@ -380,6 +392,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
380
392
  openContextResetOverlayState,
381
393
  toggleFooterDashboardState,
382
394
  openTasksOverlayState,
395
+ openDecisionsOverlayState,
383
396
  openMemoryOverlayState,
384
397
  openViewOverlayState,
385
398
  openModelOverlayState: overlayModelSelectors.openModelOverlayState,
@@ -192,6 +192,7 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
192
192
  const unsubscribePermission =
193
193
  deps.toolRegistry?.onPermissionRequired((call, decision, meta) => {
194
194
  const autonomy = deps.getAutonomy();
195
+ const view = mainApprovalRequestView(call, decision, meta, autonomy, deps.toolRegistry?.parkedCount());
195
196
  // The dialog states the tool, the target, the action class, the axis
196
197
  // that asked, and the keys that answer it. While it is on screen the
197
198
  // notice repeats all of that one line above the transcript, so it is
@@ -207,13 +208,13 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
207
208
  type: "tool_approval_state",
208
209
  toolCallId: meta.toolCallId,
209
210
  state: "awaiting-approval",
211
+ view,
210
212
  });
211
213
  }
212
214
  if (deps.getOverlayState() !== "closed") {
213
215
  announceParked();
214
216
  return;
215
217
  }
216
- const view = mainApprovalRequestView(call, decision, meta, autonomy, deps.toolRegistry?.parkedCount());
217
218
  if (!deps.openPermissionOverlay(view)) {
218
219
  announceParked();
219
220
  return;
@@ -12,6 +12,7 @@ import { openMessagePickerOverlay } from "./overlays/message-picker.js";
12
12
  import { openSessionOverlay } from "./overlays/session-selector.js";
13
13
  import { openTreeOverlay } from "./overlays/tree-selector.js";
14
14
  import { lastTurnSummaryFromLedger } from "./session-last-turn.js";
15
+ import { settleChatBeforeSessionSwitch } from "./session-switch-settlement.js";
15
16
  import { reseedSessionUsageFromLedger, type SessionUsageSink } from "./session-usage-reseed.js";
16
17
  import type { SlashCommandContext } from "./slash-commands.js";
17
18
  import type { TurnSummary } from "./status/index.js";
@@ -20,7 +21,7 @@ export interface OverlaySessionLifecycleDeps {
20
21
  tui: TUI;
21
22
  transitions: OverlayTransitions;
22
23
  session?: SessionContract;
23
- chat: Pick<ChatLoop, "resetForSession">;
24
+ chat: Pick<ChatLoop, "cancel" | "isStreaming" | "resetForSession" | "whenSettled">;
24
25
  chatPanel: ChatPanel;
25
26
  /** The one transcript reset: the panel plus every view folded alongside it. */
26
27
  resetTranscript(): void;
@@ -113,20 +114,58 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
113
114
  deps.transitions.state = "resume";
114
115
  deps.transitions.handle = openResumeOverlay(deps.tui, {
115
116
  session,
116
- onResume: (sessionId) => {
117
+ onResume: async (sessionId) => {
118
+ const settlement = settleChatBeforeSessionSwitch(deps.chat);
119
+ if (settlement) await settlement;
117
120
  deps.onResumeSession?.(sessionId);
121
+ // onResumeSession (wired to session.resume) catches and stderr-logs
122
+ // its own failure rather than throwing here, so this is the only
123
+ // signal available: a successful switch always leaves session.current()
124
+ // pointing at the requested id. Without this check, a failed switch
125
+ // still replayed the target's transcript and moved the chat leaf to
126
+ // it while session.current() stayed on the session the operator
127
+ // started on (issue #93), so the next message was appended with a
128
+ // parent turn from a session that was never actually opened.
129
+ if (session.current()?.id !== sessionId) {
130
+ emitCommandNotice(
131
+ deps.getSlashNotice(),
132
+ "error",
133
+ "resume",
134
+ `could not switch to that session; staying on ${preResumeSessionId ?? "no session"}`,
135
+ );
136
+ deps.refreshFooter();
137
+ deps.requestRender();
138
+ return;
139
+ }
118
140
  try {
119
141
  const turns = deps.readStructuredEntries(sessionId);
120
- deps.resetTranscript();
121
- rehydrateChatPanelFromTurns(deps.chatPanel, turns);
122
- const replayMessages = buildReplayAgentMessagesFromTurns(turns);
142
+ // The leaf is read before the transcript is rebuilt because it is
143
+ // what the rebuild has to follow. resolveLeafOnOpen prefers a
144
+ // persisted `/tree` pin over the newest turn, so a session resumed
145
+ // on a pin extends from the pinned turn while the file still holds
146
+ // the abandoned branch after it. Replaying the file unfiltered
147
+ // rendered those abandoned turns as ordinary history above the
148
+ // prompt, disagreeing with the branch the next message parents onto
149
+ // and with the tip `/tree` marks (issue #107). The `/tree` switch
150
+ // path below has always scoped its replay to the selected turn;
151
+ // this is the same active-path filter, rooted at the leaf resume
152
+ // actually landed on.
123
153
  const leafTurnId = session.tree(sessionId).leafId;
154
+ // activeLeafTurnId, not uptoTurnId: this is a live branch about to
155
+ // be extended, so sidecars anchored to a path turn but written
156
+ // after it (a compaction summary covering the leaf, above all)
157
+ // still belong on screen. uptoTurnId is the historical-truncation
158
+ // variant `/tree` uses.
159
+ const replayOptions = leafTurnId ? { activeLeafTurnId: leafTurnId } : {};
160
+ deps.resetTranscript();
161
+ rehydrateChatPanelFromTurns(deps.chatPanel, turns, replayOptions);
162
+ const replayMessages = buildReplayAgentMessagesFromTurns(turns, replayOptions);
124
163
  deps.chat.resetForSession(leafTurnId, replayMessages);
125
164
  rescopeToBranch(session, turns, leafTurnId);
126
165
  } catch (error) {
127
166
  deps.stderr(`[/resume] transcript replay failed: ${error instanceof Error ? error.message : String(error)}\n`);
128
167
  }
129
- if (session.current()?.id === sessionId && sessionId !== preResumeSessionId) {
168
+ if (sessionId !== preResumeSessionId) {
130
169
  deps.announceTaskMemorySeedOffer();
131
170
  }
132
171
  deps.refreshFooter();
@@ -160,7 +199,9 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
160
199
  deps.transitions.state = "tree";
161
200
  deps.transitions.handle = openTreeSelector(deps.tui, {
162
201
  session,
163
- onSwitchTurn: (turnId) => {
202
+ onSwitchTurn: async (turnId) => {
203
+ const settlement = settleChatBeforeSessionSwitch(deps.chat);
204
+ if (settlement) await settlement;
164
205
  try {
165
206
  session.switchTurn(turnId);
166
207
  const sessionId = session.current()?.id ?? null;
@@ -195,7 +236,8 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
195
236
  return;
196
237
  }
197
238
  const session = deps.session;
198
- if (session.current() === null) {
239
+ const preForkSessionId = session.current()?.id ?? null;
240
+ if (preForkSessionId === null) {
199
241
  emitCommandNotice(
200
242
  deps.getSlashNotice(),
201
243
  "warn",
@@ -207,14 +249,29 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
207
249
  deps.transitions.state = "message-picker";
208
250
  deps.transitions.handle = openMessagePicker(deps.tui, {
209
251
  session,
210
- onFork: (parentTurnId) => {
252
+ onFork: async (parentTurnId) => {
253
+ const settlement = settleChatBeforeSessionSwitch(deps.chat);
254
+ if (settlement) await settlement;
211
255
  try {
212
256
  if (deps.onForkSession) deps.onForkSession(parentTurnId);
213
257
  else session.fork(parentTurnId);
214
- deps.resetTranscript();
215
258
  const forkedSessionId = session.current()?.id ?? null;
216
- if (forkedSessionId) replayFork(forkedSessionId, parentTurnId, session);
217
- else deps.chat.resetForSession(null);
259
+ // A successful fork always creates a fresh session id; landing back
260
+ // on the id fork started from means the fork threw and
261
+ // onForkSession swallowed it (orchestrator.ts stderr-logs there).
262
+ // Do not replay: session.current() did not move (issue #93's
263
+ // extension.ts/fork.ts ordering fix keeps it on the session the
264
+ // operator started on instead of orphaning it), so there is
265
+ // nothing new to replay and doing so anyway just re-renders the
266
+ // same transcript while hiding that the fork failed.
267
+ if (forkedSessionId === null || forkedSessionId === preForkSessionId) {
268
+ emitCommandNotice(deps.getSlashNotice(), "error", "fork", `fork failed; staying on ${preForkSessionId}`);
269
+ deps.refreshFooter();
270
+ deps.requestRender();
271
+ return;
272
+ }
273
+ deps.resetTranscript();
274
+ replayFork(forkedSessionId, parentTurnId, session);
218
275
  } catch (error) {
219
276
  deps.stderr(`[/fork] fork failed: ${error instanceof Error ? error.message : String(error)}\n`);
220
277
  }
@@ -0,0 +1,300 @@
1
+ import type { DecisionLedgerEntry, DecisionRecord } from "../../domains/session/entries.js";
2
+ import {
3
+ type Component,
4
+ Input,
5
+ matchesKey,
6
+ type OverlayHandle,
7
+ type TUI,
8
+ truncateToWidth,
9
+ visibleWidth,
10
+ wrapTextWithAnsi,
11
+ } from "../../engine/tui.js";
12
+ import { buildHint, showClioOverlayFrame } from "../overlay-frame.js";
13
+ import { clioTheme, GLYPH, rule } from "../theme/index.js";
14
+
15
+ const DEFAULT_CONTENT_WIDTH = 88;
16
+
17
+ export const DECISIONS_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH + 4;
18
+
19
+ export interface DecisionSelection {
20
+ interviewId: string;
21
+ key: string;
22
+ label: string;
23
+ value: string;
24
+ }
25
+
26
+ /** The operator-authored turn that makes a durable correction visible to the model. */
27
+ export function formatDecisionCorrectionTurn(selection: DecisionSelection, correction: string): string {
28
+ return `Decision "${selection.label}" (previously: ${selection.value}) is superseded by the operator. New direction: ${correction}. Acknowledge and adjust the plan.`;
29
+ }
30
+
31
+ export interface OpenDecisionsOverlayOptions {
32
+ onSupersede: (selection: DecisionSelection) => void;
33
+ onCorrection: (selection: DecisionSelection, correction: string) => void;
34
+ onClose: () => void;
35
+ requestRender?: () => void;
36
+ now?: () => number;
37
+ }
38
+
39
+ interface SelectableDecision {
40
+ interview: DecisionLedgerEntry;
41
+ decision: DecisionRecord;
42
+ }
43
+
44
+ function fitLine(text: string, width: number): string {
45
+ const safeWidth = Math.max(1, Math.floor(width));
46
+ return visibleWidth(text) <= safeWidth ? text : truncateToWidth(text, safeWidth, "…", true);
47
+ }
48
+
49
+ function relativeTime(timestamp: string, now: number): string {
50
+ const value = Date.parse(timestamp);
51
+ if (!Number.isFinite(value)) return "unknown time";
52
+ const seconds = Math.max(0, Math.floor((now - value) / 1000));
53
+ if (seconds < 60) return `${seconds}s ago`;
54
+ const minutes = Math.floor(seconds / 60);
55
+ if (minutes < 60) return `${minutes}m ago`;
56
+ const hours = Math.floor(minutes / 60);
57
+ if (hours < 24) return `${hours}h ago`;
58
+ return `${Math.floor(hours / 24)}d ago`;
59
+ }
60
+
61
+ function selectionFor(row: SelectableDecision): DecisionSelection {
62
+ return {
63
+ interviewId: row.interview.interviewId,
64
+ key: row.decision.key,
65
+ label: row.decision.label ?? row.decision.key,
66
+ value: row.decision.value,
67
+ };
68
+ }
69
+
70
+ function selectableRows(interviews: ReadonlyArray<DecisionLedgerEntry>): SelectableDecision[] {
71
+ return interviews.flatMap((interview) => interview.decisions.map((decision) => ({ interview, decision })));
72
+ }
73
+
74
+ function interviewHeader(interview: DecisionLedgerEntry, width: number, now: number): string[] {
75
+ const theme = clioTheme();
76
+ const rounds = `${interview.roundCount} round${interview.roundCount === 1 ? "" : "s"}`;
77
+ const status =
78
+ interview.interviewStatus === "complete" ? theme.fg("success", "complete") : theme.fg("warning", "cancelled");
79
+ const heading = `${theme.fg("accent", relativeTime(interview.endedAt, now))}${theme.fg("dim", " · ")}${theme.fg("muted", rounds)}${theme.fg("dim", " · ")}${status}`;
80
+ const lines = [fitLine(heading, width)];
81
+ if (interview.summary) {
82
+ lines.push(...wrapTextWithAnsi(theme.fg("dim", interview.summary), width).map((line) => fitLine(line, width)));
83
+ }
84
+ return lines;
85
+ }
86
+
87
+ function decisionLines(decision: DecisionRecord, selected: boolean, expanded: boolean, width: number): string[] {
88
+ const theme = clioTheme();
89
+ const cursor = selected ? theme.fg("accent", GLYPH.cursor) : " ";
90
+ const status = decision.status === "active" ? theme.fg("success", GLYPH.ok) : theme.fg("dim", GLYPH.cancelled);
91
+ const labelText = decision.label ?? decision.key;
92
+ const label = decision.status === "superseded" ? theme.fg("dim", labelText) : theme.fg("muted", labelText);
93
+ const value = decision.status === "superseded" ? theme.fg("dim", decision.value) : decision.value;
94
+ const lines = [fitLine(`${cursor} ${status} ${label}${theme.fg("dim", ":")} ${value}`, width)];
95
+ if (decision.status === "superseded" && decision.correction) {
96
+ lines.push(fitLine(` ${theme.fg("dim", "correction")} ${theme.fg("muted", decision.correction)}`, width));
97
+ }
98
+ if (selected && expanded) {
99
+ if (decision.source_question) {
100
+ lines.push(
101
+ ...wrapTextWithAnsi(`${theme.fg("dim", "question")} ${theme.fg("muted", decision.source_question)}`, width).map(
102
+ (line) => fitLine(` ${line}`, width),
103
+ ),
104
+ );
105
+ }
106
+ lines.push(
107
+ ...wrapTextWithAnsi(`${theme.fg("dim", "answer")} ${theme.fg("muted", decision.value)}`, width).map((line) =>
108
+ fitLine(` ${line}`, width),
109
+ ),
110
+ );
111
+ }
112
+ return lines;
113
+ }
114
+
115
+ export function formatDecisionsOverlayBodyLines(
116
+ interviews: ReadonlyArray<DecisionLedgerEntry>,
117
+ selectedIndex = 0,
118
+ expandedKey: string | null = null,
119
+ contentWidth = DEFAULT_CONTENT_WIDTH,
120
+ now = Date.now(),
121
+ ): string[] {
122
+ const width = Math.max(1, Math.floor(contentWidth));
123
+ const theme = clioTheme();
124
+ if (interviews.length === 0) {
125
+ return [
126
+ ...wrapTextWithAnsi(theme.fg("muted", "No interview decisions have been recorded on this branch."), width),
127
+ "",
128
+ ...wrapTextWithAnsi(theme.fg("dim", "Completed and cancelled ask_user interviews appear here."), width),
129
+ ].map((line) => fitLine(line, width));
130
+ }
131
+ const selected = selectableRows(interviews)[selectedIndex];
132
+ const lines: string[] = [];
133
+ for (let interviewIndex = 0; interviewIndex < interviews.length; interviewIndex += 1) {
134
+ const interview = interviews[interviewIndex];
135
+ if (!interview) continue;
136
+ if (interviewIndex > 0) lines.push(rule(theme, width));
137
+ lines.push(...interviewHeader(interview, width, now));
138
+ if (interview.decisions.length === 0) {
139
+ lines.push(fitLine(` ${theme.fg("dim", "No compact decisions were recorded.")}`, width));
140
+ continue;
141
+ }
142
+ for (const decision of interview.decisions) {
143
+ const isSelected =
144
+ selected?.interview.interviewId === interview.interviewId && selected.decision.key === decision.key;
145
+ const rowKey = `${interview.interviewId}:${decision.key}`;
146
+ lines.push(...decisionLines(decision, isSelected, isSelected && expandedKey === rowKey, width));
147
+ }
148
+ }
149
+ return lines;
150
+ }
151
+
152
+ class DecisionsOverlayBody implements Component {
153
+ private selectedIndex = 0;
154
+ private expandedKey: string | null = null;
155
+ private correctionInput: Input | null = null;
156
+ private status = "";
157
+
158
+ constructor(
159
+ private readonly getInterviews: () => ReadonlyArray<DecisionLedgerEntry>,
160
+ private readonly options: OpenDecisionsOverlayOptions,
161
+ ) {}
162
+
163
+ render(width: number): string[] {
164
+ const interviews = this.readInterviews();
165
+ const rows = selectableRows(interviews);
166
+ this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, Math.max(0, rows.length - 1)));
167
+ const body = formatDecisionsOverlayBodyLines(
168
+ interviews,
169
+ this.selectedIndex,
170
+ this.expandedKey,
171
+ width,
172
+ this.options.now?.() ?? Date.now(),
173
+ );
174
+ if (this.correctionInput) {
175
+ const theme = clioTheme();
176
+ body.push("", fitLine(theme.fg("accent", "New direction"), width));
177
+ body.push(
178
+ ...this.correctionInput
179
+ .render(Math.max(1, width))
180
+ .map((line) =>
181
+ fitLine(line.startsWith("> ") ? `${theme.fg("accent", `${GLYPH.cursor} `)}${line.slice(2)}` : line, width),
182
+ ),
183
+ );
184
+ }
185
+ if (this.status) body.push("", fitLine(clioTheme().fg("warning", this.status), width));
186
+ return body;
187
+ }
188
+
189
+ handleInput(data: string): void {
190
+ if (this.correctionInput) {
191
+ this.correctionInput.handleInput(data);
192
+ return;
193
+ }
194
+ const rows = selectableRows(this.readInterviews());
195
+ if (matchesKey(data, "esc")) {
196
+ this.options.onClose();
197
+ return;
198
+ }
199
+ if (rows.length === 0) return;
200
+ if (matchesKey(data, "up")) {
201
+ this.selectedIndex = Math.max(0, this.selectedIndex - 1);
202
+ this.expandedKey = null;
203
+ this.requestRender();
204
+ return;
205
+ }
206
+ if (matchesKey(data, "down")) {
207
+ this.selectedIndex = Math.min(rows.length - 1, this.selectedIndex + 1);
208
+ this.expandedKey = null;
209
+ this.requestRender();
210
+ return;
211
+ }
212
+ const selected = rows[this.selectedIndex];
213
+ if (!selected) return;
214
+ if (matchesKey(data, "enter") || data === "\n") {
215
+ const key = `${selected.interview.interviewId}:${selected.decision.key}`;
216
+ this.expandedKey = this.expandedKey === key ? null : key;
217
+ this.requestRender();
218
+ return;
219
+ }
220
+ if (matchesKey(data, "s")) {
221
+ try {
222
+ this.options.onSupersede(selectionFor(selected));
223
+ this.status = "Decision superseded.";
224
+ } catch (error) {
225
+ this.status = error instanceof Error ? error.message : String(error);
226
+ }
227
+ this.requestRender();
228
+ return;
229
+ }
230
+ if (matchesKey(data, "c")) this.beginCorrection(selected);
231
+ }
232
+
233
+ invalidate(): void {
234
+ this.correctionInput?.invalidate();
235
+ }
236
+
237
+ footerHint(): string {
238
+ return this.correctionInput
239
+ ? buildHint([{ key: "Enter", verb: "submit" }])
240
+ : buildHint([
241
+ { key: "↑↓", verb: "select" },
242
+ { key: "Enter", verb: "expand" },
243
+ { key: "s", verb: "supersede" },
244
+ { key: "c", verb: "correct" },
245
+ ]);
246
+ }
247
+
248
+ private readInterviews(): ReadonlyArray<DecisionLedgerEntry> {
249
+ try {
250
+ return this.getInterviews();
251
+ } catch (error) {
252
+ this.status = error instanceof Error ? error.message : String(error);
253
+ return [];
254
+ }
255
+ }
256
+
257
+ private beginCorrection(selected: SelectableDecision): void {
258
+ const input = new Input();
259
+ input.onSubmit = (value) => {
260
+ const correction = value.trim();
261
+ if (correction.length === 0) {
262
+ this.status = "Enter the operator's new direction.";
263
+ this.requestRender();
264
+ return;
265
+ }
266
+ try {
267
+ this.options.onCorrection(selectionFor(selected), correction);
268
+ } catch (error) {
269
+ this.status = error instanceof Error ? error.message : String(error);
270
+ this.requestRender();
271
+ }
272
+ };
273
+ input.onEscape = () => this.options.onClose();
274
+ this.correctionInput = input;
275
+ this.status = "";
276
+ this.requestRender();
277
+ }
278
+
279
+ private requestRender(): void {
280
+ this.options.requestRender?.();
281
+ }
282
+ }
283
+
284
+ /** Mount the settled interview decision board. */
285
+ export function openDecisionsOverlay(
286
+ tui: TUI,
287
+ getInterviews: () => ReadonlyArray<DecisionLedgerEntry>,
288
+ options: OpenDecisionsOverlayOptions,
289
+ ): OverlayHandle {
290
+ const body = new DecisionsOverlayBody(getInterviews, {
291
+ ...options,
292
+ requestRender: options.requestRender ?? (() => tui.requestRender()),
293
+ });
294
+ return showClioOverlayFrame(tui, body, {
295
+ anchor: "center",
296
+ width: DECISIONS_OVERLAY_WIDTH,
297
+ title: () => "Decisions",
298
+ footerHint: () => body.footerHint(),
299
+ });
300
+ }