@iowarp/clio-coder 0.3.8 → 0.3.9

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 (333) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +7 -3
  3. package/dist/{acp-U67UHUK2.js → acp-7LOELQFP.js} +6 -6
  4. package/dist/{agents-YU6SGALZ.js → agents-FIBG2SHA.js} +27 -25
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-5ZPJOIVG.js → auth-OI4LIH2I.js} +11 -12
  7. package/dist/{builtins-C6JMZVV6.js → builtins-AD25UL3C.js} +5 -5
  8. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  9. package/dist/chunk-3DPEIQKN.js +113 -0
  10. package/dist/{chunk-4SPRNWDE.js → chunk-3DUR4WUA.js} +15 -15
  11. package/dist/{chunk-VHN4MY6O.js → chunk-3MRC2YSQ.js} +2 -2
  12. package/dist/{chunk-5DHKRSMQ.js → chunk-3UUY7R3Z.js} +11 -7
  13. package/dist/{chunk-IGWKHNIQ.js → chunk-3V5AYSEQ.js} +8 -8
  14. package/dist/{chunk-FHJEP5SW.js → chunk-465CC7FK.js} +8 -5
  15. package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
  16. package/dist/{chunk-A3WNZD3P.js → chunk-4H6ULJ3H.js} +67 -21
  17. package/dist/{chunk-TB5666IT.js → chunk-4LJX2PUC.js} +3 -3
  18. package/dist/{chunk-XWSF374K.js → chunk-56KB5IJP.js} +2 -2
  19. package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
  20. package/dist/{chunk-2HEJ2F35.js → chunk-5HFBWUMU.js} +20 -8
  21. package/dist/{chunk-WNIJTQQK.js → chunk-5PVQ4SRS.js} +78 -6
  22. package/dist/{chunk-DYIM5TJT.js → chunk-5QKCQQ3E.js} +262 -6
  23. package/dist/{chunk-TYPGUK6W.js → chunk-5T7RBWN2.js} +111 -5
  24. package/dist/{chunk-IIZWH4XA.js → chunk-774ILSRL.js} +2 -2
  25. package/dist/chunk-7C6RYZGQ.js +391 -0
  26. package/dist/{chunk-TANS5ZJS.js → chunk-AD7Y7STJ.js} +3 -3
  27. package/dist/{chunk-RWSI4YD7.js → chunk-AEYBF3TB.js} +33 -12
  28. package/dist/{chunk-DGSYXYMX.js → chunk-AMKHQW3C.js} +2 -2
  29. package/dist/{chunk-VWZOAB7K.js → chunk-B5XRQOLB.js} +7 -7
  30. package/dist/{chunk-WXY7KU3G.js → chunk-BVDVID7E.js} +2 -2
  31. package/dist/{chunk-PMDBGQSJ.js → chunk-CA42X6KT.js} +2 -2
  32. package/dist/{chunk-HLE42MG7.js → chunk-D73KXYPF.js} +3 -3
  33. package/dist/{chunk-5Q2VVUKB.js → chunk-DG4M6ZUE.js} +3 -3
  34. package/dist/{chunk-ME6CCNFO.js → chunk-EBOC7MT3.js} +6 -6
  35. package/dist/{chunk-MXKJU4JB.js → chunk-ECUO3KDP.js} +48 -7
  36. package/dist/{chunk-26LEYJZH.js → chunk-FALJGAWU.js} +2 -2
  37. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  38. package/dist/{chunk-7RGZWPB6.js → chunk-GAYUJ7LE.js} +67 -13
  39. package/dist/{chunk-VCBR6CU7.js → chunk-HAY4ZE2P.js} +2 -2
  40. package/dist/{chunk-FBVTI2TJ.js → chunk-HCBCAYZU.js} +11 -130
  41. package/dist/{chunk-WSB3FPX7.js → chunk-HJB5IUKP.js} +32 -136
  42. package/dist/{chunk-J3YUBZWY.js → chunk-HKO36JWF.js} +33 -5
  43. package/dist/{chunk-E77JEWSD.js → chunk-HPCTNZM2.js} +6 -36
  44. package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
  45. package/dist/{chunk-NMPKI6XL.js → chunk-JEQQR47K.js} +37 -18
  46. package/dist/{chunk-3BINW3FP.js → chunk-KV2AOLDF.js} +24 -4
  47. package/dist/{chunk-YS5VLNH5.js → chunk-LXPJXFM5.js} +7 -7
  48. package/dist/{chunk-7RFXX52T.js → chunk-MIX5N5AC.js} +271 -41
  49. package/dist/{chunk-K4XHGFR5.js → chunk-MLOK6ZOS.js} +1297 -218
  50. package/dist/{chunk-ZNLWCMVZ.js → chunk-MV2VUEJC.js} +2 -2
  51. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  52. package/dist/{chunk-5H3GB5BO.js → chunk-N3PBVRTZ.js} +4 -382
  53. package/dist/{chunk-2HFZQUHL.js → chunk-N5XKWMDW.js} +17 -7
  54. package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
  55. package/dist/{chunk-GU2UIAFZ.js → chunk-NQ6UCCOD.js} +3 -3
  56. package/dist/{chunk-N22QMJKY.js → chunk-NZU6YDNV.js} +4 -4
  57. package/dist/{chunk-ZVJ5BLO2.js → chunk-O6I4CIEU.js} +151 -13
  58. package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
  59. package/dist/{chunk-VAWNZU7Z.js → chunk-P3JGPQFL.js} +2 -2
  60. package/dist/{chunk-IJ7RPIYJ.js → chunk-PNY46YEY.js} +20 -3
  61. package/dist/{chunk-U6MBIEMB.js → chunk-PZ4I4JE2.js} +56 -35
  62. package/dist/{chunk-GPIEI3LY.js → chunk-QQ7EKM72.js} +2 -2
  63. package/dist/{chunk-WLFILSD5.js → chunk-R7LNVMCS.js} +66 -28
  64. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  65. package/dist/chunk-RKKLTLYB.js +45 -0
  66. package/dist/{chunk-TT36MB5S.js → chunk-RKRLDWD3.js} +3 -1
  67. package/dist/{chunk-TTHACPOM.js → chunk-S4COXYBG.js} +456 -18
  68. package/dist/{chunk-WWCZ5F23.js → chunk-T3Z6VAAF.js} +69 -10
  69. package/dist/{chunk-GN57SG4G.js → chunk-TD7UE2L5.js} +9 -7
  70. package/dist/{chunk-TLQJPP24.js → chunk-TEO2TLVN.js} +523 -322
  71. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  72. package/dist/{chunk-KTYTFRMB.js → chunk-VKBMFOYV.js} +17 -15
  73. package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
  74. package/dist/{chunk-P43ETTHK.js → chunk-VPTUJU4P.js} +2 -2
  75. package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
  76. package/dist/{chunk-EMYUUSFG.js → chunk-WXCJ7VME.js} +5 -5
  77. package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
  78. package/dist/{chunk-JOZYP4GM.js → chunk-YKOFT37S.js} +5 -5
  79. package/dist/chunk-YSEHGPCT.js +127 -0
  80. package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
  81. package/dist/cli/index.js +27 -27
  82. package/dist/{clio-QVTYJ57A.js → clio-LT5V7SSZ.js} +6 -6
  83. package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +4 -4
  84. package/dist/{config-LW5IJFQN.js → config-RXS5T3JT.js} +71 -43
  85. package/dist/{configure-7XIZCOU4.js → configure-2WYWSCSD.js} +14 -15
  86. package/dist/{context-Y6Y7QPR6.js → context-I3BTOTCS.js} +12 -12
  87. package/dist/{context-L3WL3X7K.js → context-MVOORGMF.js} +34 -33
  88. package/dist/{context-N52ZA626.js → context-PALKKQYL.js} +20 -20
  89. package/dist/{context-clear-MBQRLSDQ.js → context-clear-N2WOYZ2K.js} +34 -33
  90. package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
  91. package/dist/{context-working-set-GS6DSO7F.js → context-working-set-MIEVECVZ.js} +10 -11
  92. package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-VVA4SRRH.js} +34 -33
  93. package/dist/doctor-TWBWFK5V.js +165 -0
  94. package/dist/{eval-BEC2WHDA.js → eval-IJ5VEZDJ.js} +2016 -142
  95. package/dist/{evidence-REJUMSKM.js → evidence-L5APPXNV.js} +29 -28
  96. package/dist/{evolve-PY5ZBA5K.js → evolve-RGNKFJ52.js} +29 -28
  97. package/dist/{extensions-HVKU65YU.js → extensions-7WYWUX5A.js} +9 -3
  98. package/dist/{fleet-7WZEWRFA.js → fleet-6CNVBZZP.js} +87 -55
  99. package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-L2SXSYEI.js} +6 -6
  100. package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-2J3OOIPO.js} +14 -12
  101. package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-CZRJ4JP5.js} +3 -4
  102. package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-C5RI6DP7.js} +16 -15
  103. package/dist/{init-OG3TPGQG.js → init-VBN2ACVA.js} +50 -48
  104. package/dist/{library-CNTMPLRF.js → library-JHGUMLY2.js} +13 -11
  105. package/dist/{memory-6IS7F275.js → memory-K4OQIYWG.js} +31 -30
  106. package/dist/{models-ENRJDA5W.js → models-2NCZUWDD.js} +23 -22
  107. package/dist/{monitor-XLDVO7TN.js → monitor-MMVTJABD.js} +35 -34
  108. package/dist/{orchestrator-6KSPYRHA.js → orchestrator-ZKBPCHW6.js} +1627 -294
  109. package/dist/{reset-RZ4ER727.js → reset-DD5JGOY3.js} +3 -3
  110. package/dist/{run-Y2CNK5RU.js → run-QEGNX7FL.js} +56 -55
  111. package/dist/{share-A55GYP6Z.js → share-JKD3BQMW.js} +13 -11
  112. package/dist/{skills-ALC5J6AT.js → skills-LMQIKDOZ.js} +14 -12
  113. package/dist/{skills-eval-JPBEBYQU.js → skills-eval-I7X2774U.js} +34 -32
  114. package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
  115. package/dist/{support-MIETYA5E.js → support-I7LOJLIF.js} +4 -4
  116. package/dist/{targets-VGNXIR3S.js → targets-RUSR6B5Z.js} +58 -32
  117. package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-QYVORFR4.js} +6 -4
  118. package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
  119. package/dist/{upgrade-FUSUAGHR.js → upgrade-XANW3FXB.js} +17 -16
  120. package/dist/{usage-N4MKVHKD.js → usage-4H7ZRXQT.js} +88 -44
  121. package/dist/{verifiers-YAWOJ3H2.js → verifiers-UZXNBZEB.js} +6 -6
  122. package/dist/{verify-LTDHYBGY.js → verify-BVKWTNDL.js} +5 -5
  123. package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-MY7WV2QI.js} +49 -47
  124. package/dist/worker/entry.js +29 -33
  125. package/docs/alcf-provider.md +1 -1
  126. package/docs/architecture.md +1 -1
  127. package/docs/artifact-versions.md +6 -1
  128. package/docs/built-in-agents.md +1 -1
  129. package/docs/capacity-and-scheduling.md +23 -2
  130. package/docs/commands-and-modes.md +1 -1
  131. package/docs/configuration-and-targets.md +30 -3
  132. package/docs/context-engine.md +63 -4
  133. package/docs/documentation-coverage.md +3 -3
  134. package/docs/documentation-guide.md +1 -1
  135. package/docs/environment-variables.md +2 -0
  136. package/docs/eval-runner.md +262 -11
  137. package/docs/evals-internal.md +72 -2
  138. package/docs/evidence-and-memory.md +11 -10
  139. package/docs/evolution.md +1 -1
  140. package/docs/extensions-and-sharing.md +3 -1
  141. package/docs/fleet-dispatch.md +4 -4
  142. package/docs/installation-and-lifecycle.md +1 -1
  143. package/docs/middleware-and-components.md +1 -1
  144. package/docs/model-catalog.md +1 -1
  145. package/docs/observability.md +53 -2
  146. package/docs/proactive-memory.md +127 -14
  147. package/docs/prompt-envelope-and-tools.md +19 -1
  148. package/docs/provider-adapter-cookbook.md +1 -1
  149. package/docs/release-cut-checklist.md +19 -3
  150. package/docs/safety-model.md +1 -1
  151. package/docs/scientific-validation.md +1 -1
  152. package/docs/skills-marketplace.md +1 -1
  153. package/docs/tool-usage.md +1 -1
  154. package/docs/trace-store.md +1 -1
  155. package/docs/troubleshooting.md +87 -0
  156. package/docs/tui-design.md +1 -1
  157. package/docs/worker-dispatch-mechanics.md +1 -1
  158. package/package.json +2 -1
  159. package/src/cli/agents.ts +1 -1
  160. package/src/cli/config-inspect.ts +33 -6
  161. package/src/cli/config.ts +1 -1
  162. package/src/cli/doctor-state-size.ts +82 -0
  163. package/src/cli/doctor.ts +3 -1
  164. package/src/cli/eval.ts +80 -16
  165. package/src/cli/extensions.ts +5 -1
  166. package/src/cli/fleet.ts +32 -3
  167. package/src/cli/targets.ts +44 -13
  168. package/src/cli/trace.ts +63 -4
  169. package/src/cli/usage.ts +63 -14
  170. package/src/core/bus-events.ts +29 -1
  171. package/src/core/cache-telemetry.ts +42 -0
  172. package/src/core/config.ts +18 -0
  173. package/src/core/defaults.ts +36 -6
  174. package/src/core/endpoint-key.ts +27 -0
  175. package/src/core/residency-target-key.ts +25 -0
  176. package/src/core/response-schema.ts +36 -2
  177. package/src/domains/config/classify.ts +3 -0
  178. package/src/domains/context/codewiki/coordinator.ts +12 -4
  179. package/src/domains/dispatch/admission.ts +40 -3
  180. package/src/domains/dispatch/capacity-lease.ts +98 -9
  181. package/src/domains/dispatch/contract.ts +11 -0
  182. package/src/domains/dispatch/execution-plan.ts +44 -4
  183. package/src/domains/dispatch/extension.ts +166 -42
  184. package/src/domains/dispatch/fleet-run.ts +23 -3
  185. package/src/domains/dispatch/heartbeat.ts +32 -8
  186. package/src/domains/dispatch/index.ts +3 -0
  187. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  188. package/src/domains/dispatch/reservation-store.ts +116 -8
  189. package/src/domains/dispatch/state.ts +4 -0
  190. package/src/domains/dispatch/worker-spawn.ts +25 -11
  191. package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
  192. package/src/domains/dispatch/write-boundary.ts +62 -1
  193. package/src/domains/eval/artifacts/store.ts +62 -0
  194. package/src/domains/eval/compare/behavioral.ts +224 -0
  195. package/src/domains/eval/compare/compare.ts +355 -2
  196. package/src/domains/eval/compare/envelope.ts +128 -0
  197. package/src/domains/eval/compare/gates.ts +24 -6
  198. package/src/domains/eval/compare/thresholds.ts +30 -3
  199. package/src/domains/eval/execution-provenance.ts +240 -0
  200. package/src/domains/eval/metrics/aggregate.ts +136 -0
  201. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  202. package/src/domains/eval/metrics/tracked.ts +413 -0
  203. package/src/domains/eval/provenance.ts +117 -0
  204. package/src/domains/eval/reports/comparison.ts +128 -0
  205. package/src/domains/eval/reports/junit.ts +17 -3
  206. package/src/domains/eval/reports/markdown.ts +3 -3
  207. package/src/domains/eval/reports/text.ts +14 -0
  208. package/src/domains/eval/run-compare.ts +20 -0
  209. package/src/domains/eval/runners/clio-run.ts +127 -0
  210. package/src/domains/eval/runners/external-command.ts +28 -3
  211. package/src/domains/eval/schema/adapter.ts +111 -0
  212. package/src/domains/eval/schema/artifact.ts +20 -0
  213. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  214. package/src/domains/eval/schema/behavioral.ts +520 -0
  215. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  216. package/src/domains/eval/schema/serving.ts +74 -0
  217. package/src/domains/eval/schema/suite.ts +38 -8
  218. package/src/domains/eval/schema/validate.ts +58 -3
  219. package/src/domains/eval/schema/verdict.ts +237 -0
  220. package/src/domains/eval/suites/resolve.ts +2 -0
  221. package/src/domains/eval/suites/run.ts +264 -33
  222. package/src/domains/eval/verifiers/command.ts +2 -1
  223. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  224. package/src/domains/evidence/build.ts +2 -13
  225. package/src/domains/evidence/eval.ts +2 -12
  226. package/src/domains/evidence/findings-markdown.ts +33 -0
  227. package/src/domains/evidence/run-trust.ts +7 -113
  228. package/src/domains/evidence/trust-projection.ts +2 -2
  229. package/src/domains/extensions/compatibility.ts +285 -0
  230. package/src/domains/extensions/discovery.ts +38 -3
  231. package/src/domains/extensions/resources.ts +1 -1
  232. package/src/domains/extensions/state.ts +12 -3
  233. package/src/domains/extensions/types.ts +2 -0
  234. package/src/domains/lifecycle/doctor.ts +69 -1
  235. package/src/domains/memory/index.ts +14 -0
  236. package/src/domains/memory/task-bank-promotion.ts +64 -0
  237. package/src/domains/memory/task-memory-policy.ts +77 -8
  238. package/src/domains/memory/task-memory-spend.ts +131 -0
  239. package/src/domains/memory/task-memory-status.ts +7 -0
  240. package/src/domains/memory/task-memory-telemetry.ts +2 -0
  241. package/src/domains/middleware/index.ts +1 -0
  242. package/src/domains/middleware/memory-intervention.ts +69 -5
  243. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  244. package/src/domains/observability/background-memory-usage.ts +140 -0
  245. package/src/domains/observability/cost.ts +1 -1
  246. package/src/domains/observability/index.ts +7 -0
  247. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  248. package/src/domains/observability/trace-store.ts +192 -2
  249. package/src/domains/prompts/compiler.ts +100 -13
  250. package/src/domains/providers/endpoint-capacity.ts +96 -0
  251. package/src/domains/providers/index.ts +10 -0
  252. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  253. package/src/domains/providers/runtime-resolution.ts +8 -1
  254. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  255. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  256. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  257. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  258. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  259. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  260. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  261. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  262. package/src/domains/providers/types/capability-flags.ts +2 -0
  263. package/src/domains/providers/types/target-descriptor.ts +2 -0
  264. package/src/domains/resources/prompts/loader.ts +95 -33
  265. package/src/domains/safety/call-target.ts +52 -0
  266. package/src/domains/safety/run-effects.ts +35 -4
  267. package/src/domains/session/context-accounting.ts +52 -1
  268. package/src/domains/session/context-ledger.ts +37 -13
  269. package/src/domains/session/index.ts +6 -0
  270. package/src/domains/session/prompt-cache.ts +140 -0
  271. package/src/domains/session/prompt-manifest.ts +42 -0
  272. package/src/engine/acp/adapter.ts +18 -3
  273. package/src/engine/ai.ts +35 -0
  274. package/src/engine/apis/llamacpp-residency.ts +55 -3
  275. package/src/engine/apis/lmstudio.ts +25 -5
  276. package/src/engine/apis/ollama-native.ts +2 -1
  277. package/src/engine/apis/openai-completions.ts +80 -17
  278. package/src/engine/apis/residency-lock.ts +3 -1
  279. package/src/engine/apis/residency.ts +34 -1
  280. package/src/engine/provider-payload.ts +29 -1
  281. package/src/entry/orchestrator.ts +176 -30
  282. package/src/interactive/chat-loop-messages.ts +26 -7
  283. package/src/interactive/chat-loop.ts +318 -41
  284. package/src/interactive/chat-panel.ts +62 -8
  285. package/src/interactive/clio-editor.ts +45 -8
  286. package/src/interactive/context-activity.ts +5 -1
  287. package/src/interactive/context-meter.ts +1 -1
  288. package/src/interactive/context-overlay.ts +40 -10
  289. package/src/interactive/cost-overlay.ts +64 -6
  290. package/src/interactive/dispatch-board.ts +84 -12
  291. package/src/interactive/fleet-run-preview.ts +41 -15
  292. package/src/interactive/handoff-round.ts +41 -2
  293. package/src/interactive/interactive-application.ts +24 -1
  294. package/src/interactive/interactive-input-runtime.ts +8 -0
  295. package/src/interactive/interactive-presentation.ts +4 -0
  296. package/src/interactive/interactive-shell.ts +20 -17
  297. package/src/interactive/interactive-slash-runtime.ts +27 -4
  298. package/src/interactive/memory-overlay.ts +8 -0
  299. package/src/interactive/mutation-preview.ts +295 -0
  300. package/src/interactive/overlay-general-openers.ts +16 -0
  301. package/src/interactive/overlay-key-routing.ts +38 -0
  302. package/src/interactive/overlay-lifecycle.ts +38 -5
  303. package/src/interactive/overlay-permission-lifecycle.ts +22 -2
  304. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  305. package/src/interactive/overlays/ask-user.ts +91 -19
  306. package/src/interactive/overlays/help-reference.ts +4 -0
  307. package/src/interactive/overlays/prompts.ts +11 -1
  308. package/src/interactive/overlays/settings.ts +35 -1
  309. package/src/interactive/permission-hint.ts +34 -2
  310. package/src/interactive/permission-overlay.ts +159 -9
  311. package/src/interactive/prewarm.ts +197 -0
  312. package/src/interactive/render-trace.ts +162 -15
  313. package/src/interactive/renderers/tool-execution.ts +4 -0
  314. package/src/interactive/side-question.ts +58 -1
  315. package/src/interactive/status/controller.ts +11 -0
  316. package/src/interactive/status/state-machine.ts +54 -2
  317. package/src/interactive/status/types.ts +7 -0
  318. package/src/interactive/terminal-lease.ts +2 -0
  319. package/src/interactive/turn-context.ts +299 -31
  320. package/src/interactive/turn-persistence.ts +14 -4
  321. package/src/interactive/turn-prewarm.ts +364 -0
  322. package/src/interactive/turn-queues.ts +7 -4
  323. package/src/interactive/turn-runtime.ts +8 -1
  324. package/src/interactive/turn-state.ts +23 -0
  325. package/src/interactive/view/view-overlay.ts +28 -3
  326. package/src/tools/ask-user.ts +43 -2
  327. package/src/tools/dispatch-plan.ts +17 -9
  328. package/src/tools/dispatch-scout.ts +1 -1
  329. package/src/tools/registry.ts +16 -0
  330. package/dist/chunk-AOCYTWAV.js +0 -449
  331. package/dist/chunk-HWUFFB6L.js +0 -83
  332. package/dist/chunk-R346GLFC.js +0 -31
  333. package/dist/doctor-M7YEDGAE.js +0 -91
@@ -24,6 +24,7 @@ import {
24
24
  type SessionEntry,
25
25
  } from "../session/index.js";
26
26
  import { attributeEvidenceFailure } from "./failure-attribution.js";
27
+ import { renderEvidenceFindingsMarkdown } from "./findings-markdown.js";
27
28
  import { compareCodepoints as compareStrings } from "./ordering.js";
28
29
  import { extractRunProvenance, provenanceTranscriptLines } from "./provenance.js";
29
30
  import { createRedactionTally, redactSecretsDeep, redactSecretsText } from "./redact.js";
@@ -159,7 +160,6 @@ export async function buildEvidence(options: BuildEvidenceOptions): Promise<Evid
159
160
  evidenceId,
160
161
  runSources,
161
162
  gateDecisions: gateDecisions.decisions,
162
- auditRows: auditLinks.rows,
163
163
  validationEvidence,
164
164
  });
165
165
  const findings = buildFindings(runSources, trustStatusRaw, sessionLinks, auditLinks, protectedArtifactsRaw);
@@ -630,7 +630,7 @@ async function writeEvidenceFiles(
630
630
  await writeJson(join(directory, "trust-status.json"), trustStatus);
631
631
  await writeJson(join(directory, "protected-artifacts.json"), protectedArtifacts);
632
632
  await writeJson(join(directory, "findings.json"), findingsFile(overview.evidenceId, [...findings]));
633
- await writeFile(join(directory, "findings.md"), renderFindings(findings), "utf8");
633
+ await writeFile(join(directory, "findings.md"), renderEvidenceFindingsMarkdown(findings, trustStatus), "utf8");
634
634
  }
635
635
 
636
636
  function rawTraceRows(runSources: ReadonlyArray<EvidenceRunSource>): EvidenceRawTraceRow[] {
@@ -1493,17 +1493,6 @@ function axisFindings(offset: number, source: EvidenceRunSource, status: Canonic
1493
1493
  return out;
1494
1494
  }
1495
1495
 
1496
- function renderFindings(findings: ReadonlyArray<EvidenceFinding>): string {
1497
- if (findings.length === 0) return "# Findings\n\nNo findings.\n";
1498
- const lines = ["# Findings", ""];
1499
- for (const item of findings) {
1500
- const run = item.runId === null ? "" : ` run=${item.runId}`;
1501
- lines.push(`- ${item.id} [${item.severity}] ${item.tag}${run}: ${item.message}`);
1502
- }
1503
- lines.push("");
1504
- return `${lines.join("\n")}\n`;
1505
- }
1506
-
1507
1496
  async function readRunLedger(stateDir: string): Promise<RunLedgerRows> {
1508
1497
  const target = join(stateDir, "runs.json");
1509
1498
  let raw: string;
@@ -5,6 +5,7 @@ import { readGateDecisionArtifactsForRunIds } from "../dispatch/index.js";
5
5
  import { redactArtifactForStorage } from "../eval/artifacts/redact.js";
6
6
  import type { EvalCommandResult, EvalRunArtifact, EvalRunRecord } from "../eval/index.js";
7
7
  import { loadEvalArtifact } from "../eval/index.js";
8
+ import { renderEvidenceFindingsMarkdown } from "./findings-markdown.js";
8
9
  import { compareCodepoints as compareStrings } from "./ordering.js";
9
10
  import { buildEvidenceTrustStatusFile } from "./run-trust.js";
10
11
  import { evidenceDirectory, findingsFile } from "./store.js";
@@ -95,7 +96,6 @@ export async function buildEvalEvidence(options: BuildEvalEvidenceOptions): Prom
95
96
  evidenceId,
96
97
  runSources: linkedRuns.runSources,
97
98
  gateDecisions: gateDecisions.decisions,
98
- auditRows: linkedRuns.auditRows,
99
99
  });
100
100
  await writeEvalEvidenceFiles(
101
101
  directory,
@@ -258,7 +258,7 @@ async function writeEvalEvidenceFiles(
258
258
  await writeJson(join(directory, "protected-artifacts.json"), emptyProtected);
259
259
  await writeJson(join(directory, "eval-result.json"), redactedArtifact);
260
260
  await writeJson(join(directory, "findings.json"), findingsFile(overview.evidenceId, [...findings]));
261
- await writeFile(join(directory, "findings.md"), renderFindings(findings), "utf8");
261
+ await writeFile(join(directory, "findings.md"), renderEvidenceFindingsMarkdown(findings, trustStatus), "utf8");
262
262
  }
263
263
 
264
264
  function rawEvalTraceRows(artifact: EvalRunArtifact): EvidenceEvalRawTraceRow[] {
@@ -360,16 +360,6 @@ function renderEvalTranscript(artifact: EvalRunArtifact, overview: EvidenceOverv
360
360
  return `${lines.join("\n")}\n`;
361
361
  }
362
362
 
363
- function renderFindings(findings: ReadonlyArray<EvidenceFinding>): string {
364
- if (findings.length === 0) return "No findings.\n";
365
- const lines = ["# Findings", ""];
366
- for (const finding of findings) {
367
- lines.push(`- ${finding.id} [${finding.severity}] ${finding.tag}: ${finding.message}`);
368
- }
369
- lines.push("");
370
- return `${lines.join("\n")}\n`;
371
- }
372
-
373
363
  async function writeJson(path: string, value: unknown): Promise<void> {
374
364
  await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
375
365
  }
@@ -0,0 +1,33 @@
1
+ import { formatTrustAxes, formatTrustSummary, trustVerdict } from "./trust-projection.js";
2
+ import type { EvidenceFinding, EvidenceTrustStatusFile } from "./types.js";
3
+
4
+ /** Render the canonical verdict before the diagnostic records in every evidence bundle. */
5
+ export function renderEvidenceFindingsMarkdown(
6
+ findings: ReadonlyArray<EvidenceFinding>,
7
+ trustStatus: EvidenceTrustStatusFile,
8
+ ): string {
9
+ const lines = ["# Findings", "", "## Canonical trust", ""];
10
+ if (trustStatus.runs.length === 0) {
11
+ lines.push("No linked run trust status.");
12
+ } else {
13
+ for (const run of trustStatus.runs) {
14
+ lines.push(
15
+ `- run=${run.runId}`,
16
+ ` tier: ${trustVerdict(run.status)}`,
17
+ ` summary: ${formatTrustSummary(run.status)}`,
18
+ ` axes: ${formatTrustAxes(run.status)}`,
19
+ );
20
+ }
21
+ }
22
+ lines.push("", "## Finding records", "");
23
+ if (findings.length === 0) {
24
+ lines.push("No findings.");
25
+ } else {
26
+ for (const item of findings) {
27
+ const run = item.runId === null ? "" : ` run=${item.runId}`;
28
+ lines.push(`- ${item.id} [${item.severity}] ${item.tag}${run}: ${item.message}`);
29
+ }
30
+ }
31
+ lines.push("");
32
+ return `${lines.join("\n")}\n`;
33
+ }
@@ -1,9 +1,7 @@
1
1
  import { type GateDecisionArtifact, verifyGateDecisionArtifact } from "../dispatch/gate-decisions.js";
2
2
  import type { RunEnvelope, RunReceipt } from "../dispatch/types.js";
3
- import type { FinishContractAssessment, FinishContractEvidenceKind } from "../safety/finish-contract.js";
4
3
  import { compareCodepoints as compareStrings } from "./ordering.js";
5
4
  import {
6
- adaptFinishContractCompletionStatus,
7
5
  adaptGateDecisionReviewStatus,
8
6
  adaptGroundedEvidenceValidationStatus,
9
7
  type CanonicalTrustStatus,
@@ -12,7 +10,7 @@ import {
12
10
  isTrustStatusIdentifier,
13
11
  type TrustArtifactReference,
14
12
  } from "./trust-status.js";
15
- import { EVIDENCE_VERSION, type EvidenceAuditLinkedRow, type EvidenceTrustStatusFile } from "./types.js";
13
+ import { EVIDENCE_VERSION, type EvidenceTrustStatusFile } from "./types.js";
16
14
 
17
15
  export interface EvidenceRunTrustSource {
18
16
  envelope: RunEnvelope;
@@ -23,15 +21,15 @@ export interface BuildEvidenceTrustStatusInput {
23
21
  evidenceId: string;
24
22
  runSources: ReadonlyArray<EvidenceRunTrustSource>;
25
23
  gateDecisions: ReadonlyArray<GateDecisionArtifact>;
26
- auditRows: ReadonlyArray<EvidenceAuditLinkedRow>;
27
24
  validationEvidence?: ReadonlyMap<string, ReadonlyArray<TrustArtifactReference>>;
28
25
  }
29
26
 
30
27
  /**
31
28
  * Compose every evidence-linked trust axis at one pure boundary. Both the
32
29
  * run/session and eval builders call this function, so the same authenticated
33
- * receipt, gate decision, and exact finish-contract row cannot project
34
- * differently merely because it was bundled through a different entry point.
30
+ * receipt and gate decision cannot project differently merely because the
31
+ * receipt was bundled through a different entry point. Completion evidence
32
+ * stays receipt-derived so an audit row cannot change one surface alone.
35
33
  */
36
34
  export function buildEvidenceTrustStatusFile(input: BuildEvidenceTrustStatusInput): EvidenceTrustStatusFile {
37
35
  return {
@@ -44,7 +42,6 @@ export function buildEvidenceTrustStatusFile(input: BuildEvidenceTrustStatusInpu
44
42
  evidenceId: input.evidenceId,
45
43
  source,
46
44
  gateDecisions: input.gateDecisions,
47
- auditRows: input.auditRows,
48
45
  validationEvidence: input.validationEvidence?.get(source.envelope.id) ?? [],
49
46
  }),
50
47
  })),
@@ -55,22 +52,15 @@ interface ComposeEvidenceRunTrustInput {
55
52
  evidenceId: string;
56
53
  source: EvidenceRunTrustSource;
57
54
  gateDecisions: ReadonlyArray<GateDecisionArtifact>;
58
- auditRows: ReadonlyArray<EvidenceAuditLinkedRow>;
59
55
  validationEvidence: ReadonlyArray<TrustArtifactReference>;
60
56
  }
61
57
 
62
58
  function composeEvidenceRunTrustStatus(input: ComposeEvidenceRunTrustInput): CanonicalTrustStatus {
63
59
  const inspection = inspectRunReceiptTrustStatus(input.source.receipt, input.source.envelope);
64
- // A receipt that was presented and rejected authenticates nothing about the
65
- // run it names. Its integrity failure stays on `artifactIntegrity`; no other
66
- // axis may reach a trust-granting state on the strength of that run's own
67
- // self-reports. A missing or unchecked receipt is not a rejection.
68
- const receiptRejected = inspection.status.artifactIntegrity.state === "failed";
69
60
  let status = inspection.status;
70
- // Only independently observed executions ground validation. The
71
- // completion-contract audit row is the run's own self-report: it feeds
72
- // `completionEvidence` below and nothing else. The cross-axis rules this
73
- // enforces are listed under "composition rules" in docs/evidence-and-memory.md.
61
+ // Only independently observed executions ground validation. A completion
62
+ // contract audit row is the run's own self-report and cannot override the
63
+ // receipt-derived completion axis on this surface alone.
74
64
  const grounded = input.validationEvidence.filter((artifact) => isTrustStatusIdentifier(artifact.id));
75
65
  if (
76
66
  grounded.length > 0 &&
@@ -106,25 +96,6 @@ function composeEvidenceRunTrustStatus(input: ComposeEvidenceRunTrustInput): Can
106
96
  independentReview: adaptGateDecisionReviewStatus(gate, input.source.envelope.id, verification),
107
97
  });
108
98
  }
109
-
110
- const finish = latestFinishContractForRun(input.auditRows, input.source.envelope.id);
111
- if (finish !== null) {
112
- const completion = adaptFinishContractCompletionStatus(finish.assessment, {
113
- sourceId: finish.sourceId,
114
- artifacts: finish.artifacts,
115
- });
116
- status = composeTrustStatus(status, {
117
- completionEvidence:
118
- receiptRejected && completion.state === "evidenced"
119
- ? {
120
- state: "unknown",
121
- source: { kind: "finish_contract", id: finish.sourceId },
122
- authority: { kind: "clio", id: "finish-contract" },
123
- artifacts: finish.artifacts,
124
- }
125
- : completion,
126
- });
127
- }
128
99
  return status;
129
100
  }
130
101
 
@@ -142,80 +113,3 @@ function latestGateDecisionForRun(
142
113
  .at(-1) ?? null
143
114
  );
144
115
  }
145
-
146
- interface EvidenceFinishContractProjection {
147
- sourceId: string;
148
- assessment: FinishContractAssessment;
149
- artifacts: TrustArtifactReference[];
150
- }
151
-
152
- function latestFinishContractForRun(
153
- rows: ReadonlyArray<EvidenceAuditLinkedRow>,
154
- runId: string,
155
- ): EvidenceFinishContractProjection | null {
156
- const candidates = rows
157
- .filter((row) => row.auditKind === "completion_contract" && row.runId === runId && row.confidence === "exact")
158
- .flatMap((row) => {
159
- const assessment = finishContractAssessmentFromAudit(row.row);
160
- if (assessment === null) return [];
161
- const correlationId = readOptionalString(row.row.correlationId) ?? `${runId}:completion-contract`;
162
- // A malformed row is dropped from the trust projection rather than
163
- // thrown out of the whole forensic build. `readAuditRows` already
164
- // reports the malformed line, so the bundle stays honest about it.
165
- if (!isTrustStatusIdentifier(correlationId)) return [];
166
- const turnId = readOptionalString(row.row.turnId);
167
- const artifacts: TrustArtifactReference[] = [
168
- { kind: "finish_contract_evidence", id: correlationId },
169
- ...(turnId === null || !isTrustStatusIdentifier(turnId) ? [] : [{ kind: "session_entry" as const, id: turnId }]),
170
- ];
171
- return [{ sourceId: correlationId, assessment, artifacts, timestamp: row.ts ?? "" }];
172
- })
173
- .sort(
174
- (left, right) => compareStrings(left.timestamp, right.timestamp) || compareStrings(left.sourceId, right.sourceId),
175
- );
176
- const latest = candidates.at(-1);
177
- return latest === undefined
178
- ? null
179
- : { sourceId: latest.sourceId, assessment: latest.assessment, artifacts: latest.artifacts };
180
- }
181
-
182
- function finishContractAssessmentFromAudit(row: Record<string, unknown>): FinishContractAssessment | null {
183
- const reason = readOptionalString(row.reason);
184
- const decision = readOptionalString(row.decision);
185
- const mutatedPaths = Array.isArray(row.mutatedPaths)
186
- ? row.mutatedPaths.filter((entry): entry is string => typeof entry === "string")
187
- : [];
188
- const turnId = readOptionalString(row.turnId);
189
- const evidenceKinds = Array.isArray(row.evidenceKinds) ? row.evidenceKinds.filter(isFinishContractEvidenceKind) : [];
190
- const evidence = evidenceKinds.map((kind) => ({
191
- kind,
192
- summary: "recorded by the finish contract",
193
- ...(turnId === null ? {} : { turnId }),
194
- }));
195
- if (decision === "ok" && reason === "no_mutation") {
196
- return { kind: "ok", reason, evidence: [], mutatedPaths };
197
- }
198
- if (decision === "ok" && reason === "validation_evidence") {
199
- return { kind: "ok", reason, evidence, mutatedPaths };
200
- }
201
- if (decision === "ok" && reason === "explicit_limitation") {
202
- return { kind: "ok", reason, evidence: [], mutatedPaths };
203
- }
204
- if (decision === "engage" && reason === "unvalidated_mutation") {
205
- return { kind: "engage", reason, message: "finish contract engaged", evidence: [], mutatedPaths };
206
- }
207
- return null;
208
- }
209
-
210
- function isFinishContractEvidenceKind(value: unknown): value is FinishContractEvidenceKind {
211
- return value === "validation_command" || value === "protected_artifact" || value === "dispatch_receipt";
212
- }
213
-
214
- /**
215
- * A blank or whitespace-only optional field is absent, not an identifier. The
216
- * `??` fallbacks below depend on it, and an empty string would otherwise reach
217
- * `normalizeArtifactReference` and abort the entire bundle.
218
- */
219
- function readOptionalString(value: unknown): string | null {
220
- return typeof value === "string" && value.trim().length > 0 ? value : null;
221
- }
@@ -184,9 +184,9 @@ export function formatTrustSummary(status: CanonicalTrustStatus): string {
184
184
  ].join("; ");
185
185
  }
186
186
 
187
- /** The body under its versioned label, for surfaces that print one line per run. */
187
+ /** The tier and body under one versioned label for receipt-facing surfaces. */
188
188
  export function formatTrustSummaryLine(status: CanonicalTrustStatus): string {
189
- return `trust v${TRUST_SUMMARY_VERSION}: ${formatTrustSummary(status)}`;
189
+ return `trust v${TRUST_SUMMARY_VERSION}: ${trustVerdict(status)}; ${formatTrustSummary(status)}`;
190
190
  }
191
191
 
192
192
  /**
@@ -0,0 +1,285 @@
1
+ /**
2
+ * SemVer range evaluation for extension compatibility.
3
+ *
4
+ * Extension manifests are untrusted package input, so this parser is bounded
5
+ * and fail-closed. It accepts the ordinary npm range vocabulary authors use in
6
+ * manifests: comparator sets, `||`, exact and partial versions, wildcards,
7
+ * hyphen ranges, caret ranges, and tilde ranges. Tags and malformed or
8
+ * overlong expressions are rejected instead of being treated as compatible.
9
+ */
10
+
11
+ import { getVersionInfo } from "../lifecycle/version.js";
12
+
13
+ const MAX_RANGE_CHARS = 256;
14
+
15
+ interface SemVer {
16
+ major: number;
17
+ minor: number;
18
+ patch: number;
19
+ prerelease: ReadonlyArray<string | number>;
20
+ }
21
+
22
+ interface PartialVersion {
23
+ major: number | null;
24
+ minor: number | null;
25
+ patch: number | null;
26
+ prerelease: ReadonlyArray<string | number>;
27
+ }
28
+
29
+ type Predicate = (version: SemVer) => boolean;
30
+
31
+ const NUMERIC = "(?:0|[1-9]\\d*)";
32
+ const IDENTIFIER = "[0-9A-Za-z-]+";
33
+ const VERSION_RE = new RegExp(
34
+ `^v?(${NUMERIC})\\.(${NUMERIC})\\.(${NUMERIC})(?:-(${IDENTIFIER}(?:\\.${IDENTIFIER})*))?(?:\\+${IDENTIFIER}(?:\\.${IDENTIFIER})*)?$`,
35
+ );
36
+ const PARTIAL_RE = new RegExp(
37
+ `^v?(${NUMERIC}|[xX*])(?:\\.(${NUMERIC}|[xX*]))?(?:\\.(${NUMERIC}|[xX*]))?(?:-(${IDENTIFIER}(?:\\.${IDENTIFIER})*))?(?:\\+${IDENTIFIER}(?:\\.${IDENTIFIER})*)?$`,
38
+ );
39
+
40
+ function prerelease(value: string | undefined): ReadonlyArray<string | number> | null {
41
+ if (value === undefined) return [];
42
+ const out: Array<string | number> = [];
43
+ for (const identifier of value.split(".")) {
44
+ if (identifier.length === 0) return null;
45
+ if (/^\d+$/u.test(identifier)) {
46
+ if (identifier.length > 1 && identifier.startsWith("0")) return null;
47
+ const parsed = Number(identifier);
48
+ if (!Number.isSafeInteger(parsed)) return null;
49
+ out.push(parsed);
50
+ } else {
51
+ out.push(identifier);
52
+ }
53
+ }
54
+ return out;
55
+ }
56
+
57
+ function parseSemVer(value: string): SemVer | null {
58
+ const match = VERSION_RE.exec(value);
59
+ if (match === null) return null;
60
+ const parsedPrerelease = prerelease(match[4]);
61
+ if (parsedPrerelease === null) return null;
62
+ const major = Number(match[1]);
63
+ const minor = Number(match[2]);
64
+ const patch = Number(match[3]);
65
+ if (![major, minor, patch].every(Number.isSafeInteger)) return null;
66
+ return { major, minor, patch, prerelease: parsedPrerelease };
67
+ }
68
+
69
+ function parsePart(value: string | undefined): number | null {
70
+ if (value === undefined || value === "x" || value === "X" || value === "*") return null;
71
+ const parsed = Number(value);
72
+ return Number.isSafeInteger(parsed) ? parsed : null;
73
+ }
74
+
75
+ function parsePartial(value: string): PartialVersion | null {
76
+ const match = PARTIAL_RE.exec(value);
77
+ if (match === null) return null;
78
+ for (const part of match.slice(1, 4)) {
79
+ if (part === undefined || part === "x" || part === "X" || part === "*") continue;
80
+ if (!Number.isSafeInteger(Number(part))) return null;
81
+ }
82
+ const major = parsePart(match[1]);
83
+ const minor = parsePart(match[2]);
84
+ const patch = parsePart(match[3]);
85
+ // A wildcard closes the core. `1.x.3` is not a range with a coherent
86
+ // meaning, and accepting it differently from npm would be worse than a
87
+ // parse-time refusal.
88
+ if (
89
+ (major === null && (match[2] !== undefined || match[3] !== undefined)) ||
90
+ (minor === null && match[3] !== undefined)
91
+ ) {
92
+ return null;
93
+ }
94
+ const parsedPrerelease = prerelease(match[4]);
95
+ if (parsedPrerelease === null) return null;
96
+ if (parsedPrerelease.length > 0 && (major === null || minor === null || patch === null)) return null;
97
+ return { major, minor, patch, prerelease: parsedPrerelease };
98
+ }
99
+
100
+ function compareIdentifiers(left: string | number, right: string | number): number {
101
+ if (left === right) return 0;
102
+ if (typeof left === "number" && typeof right === "number") return left < right ? -1 : 1;
103
+ if (typeof left === "number") return -1;
104
+ if (typeof right === "number") return 1;
105
+ return left < right ? -1 : 1;
106
+ }
107
+
108
+ function compare(left: SemVer, right: SemVer): number {
109
+ for (const key of ["major", "minor", "patch"] as const) {
110
+ if (left[key] !== right[key]) return left[key] < right[key] ? -1 : 1;
111
+ }
112
+ if (left.prerelease.length === 0 || right.prerelease.length === 0) {
113
+ if (left.prerelease.length === right.prerelease.length) return 0;
114
+ return left.prerelease.length === 0 ? 1 : -1;
115
+ }
116
+ const length = Math.max(left.prerelease.length, right.prerelease.length);
117
+ for (let index = 0; index < length; index += 1) {
118
+ const leftPart = left.prerelease[index];
119
+ const rightPart = right.prerelease[index];
120
+ if (leftPart === undefined || rightPart === undefined) {
121
+ if (leftPart === rightPart) return 0;
122
+ return leftPart === undefined ? -1 : 1;
123
+ }
124
+ const order = compareIdentifiers(leftPart, rightPart);
125
+ if (order !== 0) return order;
126
+ }
127
+ return 0;
128
+ }
129
+
130
+ function floor(partial: PartialVersion): SemVer {
131
+ return {
132
+ major: partial.major ?? 0,
133
+ minor: partial.minor ?? 0,
134
+ patch: partial.patch ?? 0,
135
+ prerelease: partial.prerelease,
136
+ };
137
+ }
138
+
139
+ function nextWildcard(partial: PartialVersion): SemVer | null {
140
+ if (partial.major === null) return null;
141
+ if (partial.minor === null) return { major: partial.major + 1, minor: 0, patch: 0, prerelease: [] };
142
+ if (partial.patch === null) return { major: partial.major, minor: partial.minor + 1, patch: 0, prerelease: [] };
143
+ return null;
144
+ }
145
+
146
+ function lowerBound(version: SemVer, inclusive: boolean): Predicate {
147
+ return (candidate) => (inclusive ? compare(candidate, version) >= 0 : compare(candidate, version) > 0);
148
+ }
149
+
150
+ function upperBound(version: SemVer, inclusive: boolean): Predicate {
151
+ return (candidate) => (inclusive ? compare(candidate, version) <= 0 : compare(candidate, version) < 0);
152
+ }
153
+
154
+ function core(version: SemVer): string {
155
+ return `${version.major}.${version.minor}.${version.patch}`;
156
+ }
157
+
158
+ function predicatesForToken(token: string): { predicates: Predicate[]; prereleaseCore: string | null } | null {
159
+ const match = /^(<=|>=|<|>|=|~|\^)?(.+)$/u.exec(token);
160
+ if (match === null) return null;
161
+ const operator = match[1] ?? "";
162
+ const partial = parsePartial(match[2] ?? "");
163
+ if (partial === null) return null;
164
+ const base = floor(partial);
165
+ const wildcardUpper = nextWildcard(partial);
166
+ const prereleaseCore = partial.prerelease.length > 0 ? core(base) : null;
167
+
168
+ if (operator === "" || operator === "=") {
169
+ if (partial.major === null) return { predicates: [], prereleaseCore };
170
+ if (wildcardUpper !== null) {
171
+ return { predicates: [lowerBound(base, true), upperBound(wildcardUpper, false)], prereleaseCore };
172
+ }
173
+ return { predicates: [(candidate) => compare(candidate, base) === 0], prereleaseCore };
174
+ }
175
+ if (operator === ">=") return { predicates: [lowerBound(base, true)], prereleaseCore };
176
+ if (operator === "<") return { predicates: [upperBound(base, false)], prereleaseCore };
177
+ if (operator === ">") {
178
+ return {
179
+ predicates: [wildcardUpper === null ? lowerBound(base, false) : lowerBound(wildcardUpper, true)],
180
+ prereleaseCore,
181
+ };
182
+ }
183
+ if (operator === "<=") {
184
+ return {
185
+ predicates: [wildcardUpper === null ? upperBound(base, true) : upperBound(wildcardUpper, false)],
186
+ prereleaseCore,
187
+ };
188
+ }
189
+ if (partial.major === null) return { predicates: [], prereleaseCore };
190
+ if (operator === "~") {
191
+ const upper =
192
+ partial.minor === null
193
+ ? { major: partial.major + 1, minor: 0, patch: 0, prerelease: [] }
194
+ : { major: partial.major, minor: partial.minor + 1, patch: 0, prerelease: [] };
195
+ return { predicates: [lowerBound(base, true), upperBound(upper, false)], prereleaseCore };
196
+ }
197
+ const upper =
198
+ partial.major > 0
199
+ ? { major: partial.major + 1, minor: 0, patch: 0, prerelease: [] }
200
+ : partial.minor === null
201
+ ? { major: 1, minor: 0, patch: 0, prerelease: [] }
202
+ : (partial.minor ?? 0) > 0
203
+ ? { major: 0, minor: (partial.minor ?? 0) + 1, patch: 0, prerelease: [] }
204
+ : partial.patch === null
205
+ ? { major: 0, minor: 1, patch: 0, prerelease: [] }
206
+ : { major: 0, minor: 0, patch: (partial.patch ?? 0) + 1, prerelease: [] };
207
+ return { predicates: [lowerBound(base, true), upperBound(upper, false)], prereleaseCore };
208
+ }
209
+
210
+ function predicatesForSet(raw: string): { predicates: Predicate[]; prereleaseCores: ReadonlySet<string> } | null {
211
+ const hyphen = /^(\S+)\s+-\s+(\S+)$/u.exec(raw);
212
+ if (hyphen !== null) {
213
+ const left = parsePartial(hyphen[1] ?? "");
214
+ const right = parsePartial(hyphen[2] ?? "");
215
+ if (left === null || right === null || left.major === null || right.major === null) return null;
216
+ const rightUpper = nextWildcard(right);
217
+ return {
218
+ predicates: [
219
+ lowerBound(floor(left), true),
220
+ rightUpper === null ? upperBound(floor(right), true) : upperBound(rightUpper, false),
221
+ ],
222
+ prereleaseCores: new Set(
223
+ [
224
+ left.prerelease.length > 0 ? core(floor(left)) : null,
225
+ right.prerelease.length > 0 ? core(floor(right)) : null,
226
+ ].filter((entry): entry is string => entry !== null),
227
+ ),
228
+ };
229
+ }
230
+ const normalized = raw.replace(/(<=|>=|<|>|=|~|\^)\s+/gu, "$1");
231
+ const tokens = normalized.split(/\s+/u).filter((entry) => entry.length > 0);
232
+ if (tokens.length === 0) return null;
233
+ const predicates: Predicate[] = [];
234
+ const prereleaseCores = new Set<string>();
235
+ for (const token of tokens) {
236
+ const parsed = predicatesForToken(token);
237
+ if (parsed === null) return null;
238
+ predicates.push(...parsed.predicates);
239
+ if (parsed.prereleaseCore !== null) prereleaseCores.add(parsed.prereleaseCore);
240
+ }
241
+ return { predicates, prereleaseCores };
242
+ }
243
+
244
+ function parseRange(
245
+ range: string,
246
+ ): ReadonlyArray<{ predicates: Predicate[]; prereleaseCores: ReadonlySet<string> }> | null {
247
+ const trimmed = range.trim();
248
+ if (trimmed.length === 0 || trimmed.length > MAX_RANGE_CHARS) return null;
249
+ const sets = trimmed.split("||");
250
+ if (sets.some((set) => set.trim().length === 0)) return null;
251
+ const parsed = sets.map((set) => predicatesForSet(set.trim()));
252
+ return parsed.every((set) => set !== null) ? (parsed as Array<NonNullable<(typeof parsed)[number]>>) : null;
253
+ }
254
+
255
+ export function isValidSemVerRange(range: string): boolean {
256
+ return parseRange(range) !== null;
257
+ }
258
+
259
+ export function satisfiesSemVerRange(version: string, range: string): boolean {
260
+ const candidate = parseSemVer(version);
261
+ const sets = parseRange(range);
262
+ if (candidate === null || sets === null) return false;
263
+ return sets.some((set) => {
264
+ if (candidate.prerelease.length > 0 && !set.prereleaseCores.has(core(candidate))) return false;
265
+ return set.predicates.every((predicate) => predicate(candidate));
266
+ });
267
+ }
268
+
269
+ export interface ClioCompatibilityEvaluation {
270
+ rangeValid: boolean;
271
+ satisfied: boolean;
272
+ runningVersion: string;
273
+ }
274
+
275
+ export function evaluateClioCompatibility(
276
+ range: string,
277
+ runningVersion = getVersionInfo().clio,
278
+ ): ClioCompatibilityEvaluation {
279
+ const rangeValid = isValidSemVerRange(range);
280
+ return {
281
+ rangeValid,
282
+ satisfied: rangeValid && satisfiesSemVerRange(runningVersion, range),
283
+ runningVersion,
284
+ };
285
+ }
@@ -1,6 +1,7 @@
1
1
  import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
4
+ import { evaluateClioCompatibility } from "./compatibility.js";
4
5
  import type {
5
6
  ClioExtensionManifest,
6
7
  ExtensionCandidate,
@@ -166,9 +167,32 @@ export function parseExtensionManifest(
166
167
  const resources = normalizeResources(value.resources);
167
168
  const tools = stringArray(value.tools);
168
169
  const settings = stringArray(value.settings);
169
- const compatibility = isRecord(value.compatibility)
170
- ? { ...(trimString(value.compatibility.clio) ? { clio: trimString(value.compatibility.clio) as string } : {}) }
171
- : undefined;
170
+ let compatibility: ClioExtensionManifest["compatibility"];
171
+ if (value.compatibility !== undefined) {
172
+ if (!isRecord(value.compatibility)) {
173
+ diagnostics.push({ type: "error", message: "compatibility must be an object", path: manifestPath });
174
+ } else if ("clio" in value.compatibility) {
175
+ const range = trimString(value.compatibility.clio);
176
+ if (range === undefined) {
177
+ diagnostics.push({
178
+ type: "error",
179
+ message: "compatibility.clio must be a non-empty semver range",
180
+ path: manifestPath,
181
+ });
182
+ } else {
183
+ const evaluation = evaluateClioCompatibility(range);
184
+ if (!evaluation.rangeValid) {
185
+ diagnostics.push({
186
+ type: "error",
187
+ message: `extension ${id ?? "<unknown>"} declares malformed compatibility.clio range '${range}'`,
188
+ path: manifestPath,
189
+ });
190
+ } else {
191
+ compatibility = { clio: range };
192
+ }
193
+ }
194
+ }
195
+ }
172
196
  if (!id || !name || !version || !description || diagnostics.some((diag) => diag.type === "error")) {
173
197
  return { diagnostics };
174
198
  }
@@ -183,6 +207,17 @@ export function parseExtensionManifest(
183
207
  if (tools) manifest.tools = tools;
184
208
  if (settings) manifest.settings = settings;
185
209
  if (compatibility && Object.keys(compatibility).length > 0) manifest.compatibility = compatibility;
210
+ const clioRange = manifest.compatibility?.clio;
211
+ if (clioRange !== undefined) {
212
+ const evaluation = evaluateClioCompatibility(clioRange);
213
+ if (!evaluation.satisfied) {
214
+ diagnostics.push({
215
+ type: "error",
216
+ message: `extension ${manifest.id} requires Clio '${clioRange}', but running Clio version is '${evaluation.runningVersion}'`,
217
+ path: manifestPath,
218
+ });
219
+ }
220
+ }
186
221
  return { manifest, diagnostics };
187
222
  }
188
223
 
@@ -33,7 +33,7 @@ export function enabledExtensionResourceRoots(
33
33
  ): ExtensionResourceRoot[] {
34
34
  const roots: ExtensionResourceRoot[] = [];
35
35
  for (const entry of listInstalledExtensions(cwd)) {
36
- if (!entry.enabled || !entry.effective) continue;
36
+ if (!entry.enabled || !entry.compatible || !entry.effective) continue;
37
37
  const rel = entry.resources[kind];
38
38
  if (!rel) continue;
39
39
  const full = extensionResourcePath(entry.rootPath, rel);