@iowarp/clio-coder 0.3.7 → 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 (400) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +13 -4
  3. package/dist/{acp-SK4MD6MM.js → acp-7LOELQFP.js} +13 -13
  4. package/dist/{agents-2FN2K6ME.js → agents-FIBG2SHA.js} +41 -37
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-QIYZWM5I.js → auth-OI4LIH2I.js} +31 -24
  7. package/dist/builtins-AD25UL3C.js +17 -0
  8. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  9. package/dist/chunk-3DPEIQKN.js +113 -0
  10. package/dist/{chunk-EOOQZZDE.js → chunk-3DUR4WUA.js} +19 -19
  11. package/dist/{chunk-WHJYKASB.js → chunk-3MRC2YSQ.js} +2 -2
  12. package/dist/{chunk-EBEFWSGL.js → chunk-3UUY7R3Z.js} +14 -10
  13. package/dist/{chunk-LADCF22A.js → chunk-3V5AYSEQ.js} +113 -54
  14. package/dist/{chunk-BMWK7ZIZ.js → chunk-465CC7FK.js} +16 -13
  15. package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
  16. package/dist/{chunk-CEYBNUGC.js → chunk-4H6ULJ3H.js} +378 -36
  17. package/dist/{chunk-YTYFXUI3.js → chunk-4LJX2PUC.js} +9 -9
  18. package/dist/{chunk-DOOEX22V.js → chunk-56KB5IJP.js} +5 -5
  19. package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
  20. package/dist/{chunk-TSHXZTOQ.js → chunk-5HFBWUMU.js} +23 -11
  21. package/dist/{chunk-5UJ6ECTS.js → chunk-5PVQ4SRS.js} +80 -8
  22. package/dist/{chunk-ZWLZP4ZT.js → chunk-5QKCQQ3E.js} +359 -17
  23. package/dist/{chunk-6M7VS3J3.js → chunk-5T7RBWN2.js} +111 -5
  24. package/dist/chunk-774ILSRL.js +172 -0
  25. package/dist/chunk-7C6RYZGQ.js +391 -0
  26. package/dist/{chunk-GH5622CP.js → chunk-A2NJGIB3.js} +2 -2
  27. package/dist/{chunk-C4JBQ5SR.js → chunk-AD7Y7STJ.js} +6 -6
  28. package/dist/{chunk-GEYXPTRF.js → chunk-AEYBF3TB.js} +33 -12
  29. package/dist/{chunk-2SFS6XQE.js → chunk-AMKHQW3C.js} +3 -2
  30. package/dist/{chunk-D4MDIG46.js → chunk-B5CSFE7B.js} +7 -7
  31. package/dist/{chunk-MXI6J5JF.js → chunk-B5XRQOLB.js} +10 -10
  32. package/dist/{chunk-X2KV5FXT.js → chunk-BVDVID7E.js} +2 -2
  33. package/dist/{chunk-JNXPYBB4.js → chunk-CA42X6KT.js} +3 -3
  34. package/dist/{chunk-VREKEFLL.js → chunk-D73KXYPF.js} +3 -3
  35. package/dist/{chunk-JTSEDYVQ.js → chunk-DG4M6ZUE.js} +7 -7
  36. package/dist/{chunk-DQA7QLMD.js → chunk-EBOC7MT3.js} +10 -25
  37. package/dist/{chunk-KZ2H5X4G.js → chunk-ECUO3KDP.js} +129 -14
  38. package/dist/{chunk-JRIO5UD2.js → chunk-EQ63NRB7.js} +5 -5
  39. package/dist/{chunk-YD734TPH.js → chunk-FALJGAWU.js} +2 -2
  40. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  41. package/dist/{chunk-XEGB6BCN.js → chunk-GAYUJ7LE.js} +68 -14
  42. package/dist/{chunk-UND3GU2L.js → chunk-H7IXIC72.js} +2 -2
  43. package/dist/{chunk-IR4CFBFN.js → chunk-HAY4ZE2P.js} +12 -12
  44. package/dist/{chunk-UVDSQ6LW.js → chunk-HCBCAYZU.js} +74 -147
  45. package/dist/{chunk-4DWFMQDR.js → chunk-HJB5IUKP.js} +89 -145
  46. package/dist/{chunk-M4AKACEO.js → chunk-HKO36JWF.js} +33 -5
  47. package/dist/{chunk-KCMKRQX4.js → chunk-HPCTNZM2.js} +45 -82
  48. package/dist/{chunk-465YSENW.js → chunk-IFBNV6H6.js} +3 -3
  49. package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
  50. package/dist/chunk-JEQQR47K.js +3025 -0
  51. package/dist/{chunk-FO5ZOVUY.js → chunk-KV2AOLDF.js} +27 -7
  52. package/dist/chunk-LU7P4LHA.js +33 -0
  53. package/dist/{chunk-6TUKSZVF.js → chunk-LXPJXFM5.js} +11 -11
  54. package/dist/{chunk-VQNODYQ4.js → chunk-MIX5N5AC.js} +488 -3668
  55. package/dist/chunk-MLOK6ZOS.js +2888 -0
  56. package/dist/{chunk-ZZMN5OM4.js → chunk-MV2VUEJC.js} +2 -2
  57. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  58. package/dist/{chunk-OBMAI2DP.js → chunk-N3PBVRTZ.js} +12 -388
  59. package/dist/{chunk-WJHBC77E.js → chunk-N5XKWMDW.js} +17 -7
  60. package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
  61. package/dist/{chunk-UFQ3F4FW.js → chunk-NQ6UCCOD.js} +4 -4
  62. package/dist/chunk-NUGM5KR6.js +165 -0
  63. package/dist/{chunk-DMD2AGVS.js → chunk-NZU6YDNV.js} +20 -18
  64. package/dist/{chunk-WHGPSPT5.js → chunk-O6I4CIEU.js} +151 -13
  65. package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
  66. package/dist/{chunk-PD3MESLB.js → chunk-P3JGPQFL.js} +4 -4
  67. package/dist/{chunk-UHXRNZ2J.js → chunk-PNY46YEY.js} +23 -6
  68. package/dist/{chunk-THKY7CD7.js → chunk-PZ4I4JE2.js} +134 -29
  69. package/dist/{chunk-SROCI7ZU.js → chunk-QQ7EKM72.js} +5 -5
  70. package/dist/{chunk-QCTRSGHQ.js → chunk-R7LNVMCS.js} +91 -53
  71. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  72. package/dist/chunk-RKKLTLYB.js +45 -0
  73. package/dist/{chunk-OB5HIGJY.js → chunk-RKRLDWD3.js} +4 -1
  74. package/dist/{chunk-DJNLUABN.js → chunk-S4COXYBG.js} +588 -32
  75. package/dist/{chunk-3HAPLH5M.js → chunk-T3Z6VAAF.js} +172 -11
  76. package/dist/{chunk-FOT2FX5J.js → chunk-TD7UE2L5.js} +12 -10
  77. package/dist/{chunk-UUANF5CR.js → chunk-TEO2TLVN.js} +856 -967
  78. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  79. package/dist/{chunk-EELBMBT6.js → chunk-VKBMFOYV.js} +74 -15
  80. package/dist/chunk-VO2LKSTM.js +165 -0
  81. package/dist/{chunk-5C77SEEY.js → chunk-VPTUJU4P.js} +3 -3
  82. package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
  83. package/dist/{chunk-J7PIKKWC.js → chunk-WXCJ7VME.js} +8 -8
  84. package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
  85. package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
  86. package/dist/{chunk-AB4XIIVB.js → chunk-YKOFT37S.js} +6 -6
  87. package/dist/chunk-YSEHGPCT.js +127 -0
  88. package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
  89. package/dist/cli/index.js +32 -32
  90. package/dist/{clio-WBVQEBKO.js → clio-LT5V7SSZ.js} +9 -9
  91. package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +5 -5
  92. package/dist/codewiki/build-worker.js +4 -4
  93. package/dist/{components-F7OEATSO.js → components-ZFA3SAER.js} +8 -8
  94. package/dist/{config-TRBL3RCF.js → config-RXS5T3JT.js} +98 -65
  95. package/dist/{configure-OLCVPHNM.js → configure-2WYWSCSD.js} +26 -22
  96. package/dist/{context-MJIJ6GOX.js → context-I3BTOTCS.js} +12 -12
  97. package/dist/{context-XEWE3MOJ.js → context-MVOORGMF.js} +54 -47
  98. package/dist/{context-WFPKQSM6.js → context-PALKKQYL.js} +28 -28
  99. package/dist/{context-clear-KNOS2JPB.js → context-clear-N2WOYZ2K.js} +53 -46
  100. package/dist/{context-index-SSR5ECNE.js → context-index-HNG3MOME.js} +6 -6
  101. package/dist/{context-working-set-EUXAZI6N.js → context-working-set-MIEVECVZ.js} +17 -18
  102. package/dist/{dispatch-runner-B7MTOVKL.js → dispatch-runner-VVA4SRRH.js} +90 -61
  103. package/dist/{docs-FLJTIDSE.js → docs-7LQ23DLM.js} +8 -8
  104. package/dist/doctor-TWBWFK5V.js +165 -0
  105. package/dist/eval-IJ5VEZDJ.js +4483 -0
  106. package/dist/{evidence-JZNBUOQZ.js → evidence-L5APPXNV.js} +68 -61
  107. package/dist/{evolve-FJVC4KKI.js → evolve-RGNKFJ52.js} +47 -40
  108. package/dist/{extensions-IQL36S7K.js → extensions-7WYWUX5A.js} +13 -7
  109. package/dist/{fleet-BDKYJFCP.js → fleet-6CNVBZZP.js} +113 -76
  110. package/dist/{fleet-commands-ZFIWZSB3.js → fleet-commands-L2SXSYEI.js} +10 -10
  111. package/dist/{fleet-graph-Y6HPXIVF.js → fleet-graph-2J3OOIPO.js} +17 -15
  112. package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-CZRJ4JP5.js} +5 -6
  113. package/dist/{fleet-validate-BIYREGIK.js → fleet-validate-C5RI6DP7.js} +20 -19
  114. package/dist/{init-LQUB5COQ.js → init-VBN2ACVA.js} +70 -63
  115. package/dist/{library-NJAHIGG4.js → library-JHGUMLY2.js} +22 -20
  116. package/dist/{memory-OG6HOYKM.js → memory-K4OQIYWG.js} +49 -42
  117. package/dist/{models-5ZG5XY7J.js → models-2NCZUWDD.js} +35 -29
  118. package/dist/{monitor-TJ7AMTGB.js → monitor-MMVTJABD.js} +64 -45
  119. package/dist/{orchestrator-WZYB54DM.js → orchestrator-ZKBPCHW6.js} +1971 -520
  120. package/dist/{paths-XUC7GS6E.js → paths-DBXMZMDU.js} +5 -5
  121. package/dist/registry-LG64LTF4.js +11 -0
  122. package/dist/{reset-PXQT45IY.js → reset-DD5JGOY3.js} +11 -11
  123. package/dist/{run-FQ74YF62.js → run-QEGNX7FL.js} +89 -83
  124. package/dist/{share-FW7SVCL3.js → share-JKD3BQMW.js} +20 -18
  125. package/dist/{skills-7E7IRB3R.js → skills-LMQIKDOZ.js} +23 -21
  126. package/dist/{skills-eval-LI75W6OK.js → skills-eval-I7X2774U.js} +59 -52
  127. package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
  128. package/dist/support-I7LOJLIF.js +38 -0
  129. package/dist/{targets-4CIFKCTW.js → targets-RUSR6B5Z.js} +77 -42
  130. package/dist/{terminal-lease-WUZY7ZV5.js → terminal-lease-QYVORFR4.js} +6 -4
  131. package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
  132. package/dist/{uninstall-7FV7IP4E.js → uninstall-ZJF5H5ZN.js} +8 -8
  133. package/dist/{upgrade-K2HVIVMQ.js → upgrade-XANW3FXB.js} +29 -26
  134. package/dist/{usage-GTZELZQX.js → usage-4H7ZRXQT.js} +110 -61
  135. package/dist/{verifiers-RLAHT27O.js → verifiers-UZXNBZEB.js} +13 -13
  136. package/dist/{verify-BX3BRKH5.js → verify-BVKWTNDL.js} +9 -9
  137. package/dist/{wiki-generate-ASIFASCN.js → wiki-generate-MY7WV2QI.js} +76 -69
  138. package/dist/worker/entry.js +69 -66
  139. package/docs/alcf-provider.md +1 -1
  140. package/docs/architecture.md +1 -1
  141. package/docs/artifact-versions.md +11 -5
  142. package/docs/built-in-agents.md +1 -1
  143. package/docs/capacity-and-scheduling.md +23 -2
  144. package/docs/commands-and-modes.md +2 -2
  145. package/docs/configuration-and-targets.md +37 -5
  146. package/docs/context-engine.md +63 -4
  147. package/docs/documentation-coverage.md +3 -3
  148. package/docs/documentation-guide.md +1 -1
  149. package/docs/environment-variables.md +2 -0
  150. package/docs/eval-runner.md +262 -11
  151. package/docs/evals-internal.md +72 -2
  152. package/docs/evidence-and-memory.md +77 -12
  153. package/docs/evolution.md +1 -1
  154. package/docs/extensions-and-sharing.md +3 -1
  155. package/docs/fleet-dispatch.md +34 -9
  156. package/docs/glossary.md +21 -1
  157. package/docs/installation-and-lifecycle.md +1 -1
  158. package/docs/middleware-and-components.md +1 -1
  159. package/docs/model-catalog.md +1 -1
  160. package/docs/observability.md +54 -3
  161. package/docs/proactive-memory.md +127 -14
  162. package/docs/prompt-envelope-and-tools.md +22 -2
  163. package/docs/provider-adapter-cookbook.md +1 -1
  164. package/docs/release-cut-checklist.md +60 -41
  165. package/docs/safety-model.md +1 -1
  166. package/docs/scientific-validation.md +1 -1
  167. package/docs/skills-marketplace.md +1 -1
  168. package/docs/tool-usage.md +1 -1
  169. package/docs/trace-store.md +1 -1
  170. package/docs/troubleshooting.md +87 -0
  171. package/docs/tui-design.md +1 -1
  172. package/docs/worker-dispatch-mechanics.md +1 -1
  173. package/package.json +2 -2
  174. package/src/cli/agents.ts +1 -1
  175. package/src/cli/argv.ts +5 -0
  176. package/src/cli/config-inspect.ts +33 -6
  177. package/src/cli/config.ts +1 -1
  178. package/src/cli/configure.ts +107 -23
  179. package/src/cli/doctor-state-size.ts +82 -0
  180. package/src/cli/doctor.ts +7 -1
  181. package/src/cli/eval.ts +80 -16
  182. package/src/cli/evidence.ts +30 -25
  183. package/src/cli/extensions.ts +5 -1
  184. package/src/cli/fleet-preflight.ts +2 -12
  185. package/src/cli/fleet.ts +32 -3
  186. package/src/cli/shared.ts +1 -0
  187. package/src/cli/targets.ts +45 -11
  188. package/src/cli/trace.ts +63 -4
  189. package/src/cli/usage.ts +63 -14
  190. package/src/cli/validate-model.ts +60 -5
  191. package/src/core/bus-events.ts +54 -1
  192. package/src/core/cache-telemetry.ts +42 -0
  193. package/src/core/commit-attribution.ts +4 -4
  194. package/src/core/config.ts +18 -0
  195. package/src/core/defaults.ts +36 -6
  196. package/src/core/endpoint-key.ts +27 -0
  197. package/src/core/path-boundary.ts +100 -0
  198. package/src/core/residency-target-key.ts +25 -0
  199. package/src/core/response-schema.ts +36 -2
  200. package/src/domains/agents/extension.ts +2 -11
  201. package/src/domains/agents/fleet-contract.ts +30 -12
  202. package/src/domains/agents/recipe.ts +7 -1
  203. package/src/domains/agents/registry.ts +73 -5
  204. package/src/domains/agents/result-contract.ts +128 -17
  205. package/src/domains/agents/write-boundary.ts +15 -50
  206. package/src/domains/config/classify.ts +3 -0
  207. package/src/domains/context/codewiki/coordinator.ts +12 -4
  208. package/src/domains/context/project-rules.ts +51 -1
  209. package/src/domains/dispatch/admission.ts +40 -3
  210. package/src/domains/dispatch/assignment-reconcile.ts +22 -5
  211. package/src/domains/dispatch/assignment-store.ts +151 -14
  212. package/src/domains/dispatch/capacity-lease.ts +98 -9
  213. package/src/domains/dispatch/contract.ts +26 -1
  214. package/src/domains/dispatch/delegation-plan.ts +2 -5
  215. package/src/domains/dispatch/execution-plan.ts +44 -4
  216. package/src/domains/dispatch/execution-role.ts +9 -1
  217. package/src/domains/dispatch/extension.ts +309 -96
  218. package/src/domains/dispatch/fleet-run.ts +78 -4
  219. package/src/domains/dispatch/gate-role-prompts.ts +38 -0
  220. package/src/domains/dispatch/heartbeat.ts +32 -8
  221. package/src/domains/dispatch/index.ts +6 -1
  222. package/src/domains/dispatch/intent-requirements.ts +40 -0
  223. package/src/domains/dispatch/intent.ts +84 -8
  224. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  225. package/src/domains/dispatch/path-scope.ts +370 -0
  226. package/src/domains/dispatch/receipt-integrity.ts +2 -1
  227. package/src/domains/dispatch/reservation-store.ts +116 -8
  228. package/src/domains/dispatch/state.ts +4 -0
  229. package/src/domains/dispatch/types.ts +14 -7
  230. package/src/domains/dispatch/validation.ts +6 -3
  231. package/src/domains/dispatch/worker-spawn.ts +25 -11
  232. package/src/domains/dispatch/write-boundary-enforcer.ts +62 -0
  233. package/src/domains/dispatch/write-boundary.ts +262 -22
  234. package/src/domains/eval/artifacts/store.ts +62 -0
  235. package/src/domains/eval/compare/behavioral.ts +224 -0
  236. package/src/domains/eval/compare/compare.ts +355 -2
  237. package/src/domains/eval/compare/envelope.ts +128 -0
  238. package/src/domains/eval/compare/gates.ts +24 -6
  239. package/src/domains/eval/compare/thresholds.ts +30 -3
  240. package/src/domains/eval/execution-provenance.ts +240 -0
  241. package/src/domains/eval/metrics/aggregate.ts +136 -0
  242. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  243. package/src/domains/eval/metrics/evidence.ts +79 -2
  244. package/src/domains/eval/metrics/tracked.ts +413 -0
  245. package/src/domains/eval/provenance.ts +117 -0
  246. package/src/domains/eval/reports/comparison.ts +128 -0
  247. package/src/domains/eval/reports/junit.ts +17 -3
  248. package/src/domains/eval/reports/markdown.ts +3 -3
  249. package/src/domains/eval/reports/text.ts +14 -0
  250. package/src/domains/eval/run-compare.ts +20 -0
  251. package/src/domains/eval/runners/clio-run.ts +139 -2
  252. package/src/domains/eval/runners/external-command.ts +28 -3
  253. package/src/domains/eval/schema/adapter.ts +111 -0
  254. package/src/domains/eval/schema/artifact.ts +20 -0
  255. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  256. package/src/domains/eval/schema/behavioral.ts +520 -0
  257. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  258. package/src/domains/eval/schema/serving.ts +74 -0
  259. package/src/domains/eval/schema/suite.ts +38 -8
  260. package/src/domains/eval/schema/validate.ts +58 -3
  261. package/src/domains/eval/schema/verdict.ts +237 -0
  262. package/src/domains/eval/suites/resolve.ts +2 -0
  263. package/src/domains/eval/suites/run.ts +264 -33
  264. package/src/domains/eval/verifiers/command.ts +2 -1
  265. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  266. package/src/domains/evidence/build.ts +68 -21
  267. package/src/domains/evidence/eval.ts +2 -12
  268. package/src/domains/evidence/findings-markdown.ts +33 -0
  269. package/src/domains/evidence/index.ts +21 -0
  270. package/src/domains/evidence/provenance.ts +46 -11
  271. package/src/domains/evidence/run-trust.ts +7 -113
  272. package/src/domains/evidence/trust-projection.ts +274 -0
  273. package/src/domains/evidence/trust-status.ts +145 -17
  274. package/src/domains/evidence/types.ts +4 -0
  275. package/src/domains/extensions/compatibility.ts +285 -0
  276. package/src/domains/extensions/discovery.ts +126 -4
  277. package/src/domains/extensions/resources.ts +21 -9
  278. package/src/domains/extensions/state.ts +18 -5
  279. package/src/domains/extensions/types.ts +6 -1
  280. package/src/domains/lifecycle/doctor.ts +209 -2
  281. package/src/domains/memory/index.ts +14 -0
  282. package/src/domains/memory/task-bank-promotion.ts +64 -0
  283. package/src/domains/memory/task-memory-policy.ts +77 -8
  284. package/src/domains/memory/task-memory-spend.ts +131 -0
  285. package/src/domains/memory/task-memory-status.ts +7 -0
  286. package/src/domains/memory/task-memory-telemetry.ts +2 -0
  287. package/src/domains/middleware/index.ts +1 -0
  288. package/src/domains/middleware/memory-intervention.ts +69 -5
  289. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  290. package/src/domains/observability/background-memory-usage.ts +140 -0
  291. package/src/domains/observability/cost.ts +1 -1
  292. package/src/domains/observability/index.ts +7 -0
  293. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  294. package/src/domains/observability/trace-store.ts +192 -2
  295. package/src/domains/prompts/compiler.ts +100 -13
  296. package/src/domains/prompts/contract.ts +3 -5
  297. package/src/domains/providers/endpoint-capacity.ts +96 -0
  298. package/src/domains/providers/extension.ts +30 -2
  299. package/src/domains/providers/index.ts +10 -0
  300. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  301. package/src/domains/providers/runtime-resolution.ts +8 -1
  302. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  303. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  304. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  305. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  306. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  307. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  308. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  309. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  310. package/src/domains/providers/types/capability-flags.ts +2 -0
  311. package/src/domains/providers/types/target-descriptor.ts +2 -0
  312. package/src/domains/resources/common-loader.ts +3 -0
  313. package/src/domains/resources/prompts/loader.ts +184 -17
  314. package/src/domains/safety/call-target.ts +52 -0
  315. package/src/domains/safety/policy-engine.ts +5 -5
  316. package/src/domains/safety/run-effects.ts +96 -2
  317. package/src/domains/safety/scope.ts +7 -12
  318. package/src/domains/session/context-accounting.ts +52 -1
  319. package/src/domains/session/context-ledger.ts +37 -13
  320. package/src/domains/session/index.ts +6 -0
  321. package/src/domains/session/prompt-cache.ts +140 -0
  322. package/src/domains/session/prompt-manifest.ts +42 -0
  323. package/src/engine/acp/adapter.ts +18 -3
  324. package/src/engine/acp/server.ts +4 -1
  325. package/src/engine/ai.ts +35 -0
  326. package/src/engine/apis/llamacpp-residency.ts +55 -3
  327. package/src/engine/apis/lmstudio.ts +25 -5
  328. package/src/engine/apis/ollama-native.ts +2 -1
  329. package/src/engine/apis/openai-completions.ts +80 -17
  330. package/src/engine/apis/residency-lock.ts +3 -1
  331. package/src/engine/apis/residency.ts +34 -1
  332. package/src/engine/prompt-templates.ts +18 -1
  333. package/src/engine/provider-payload.ts +29 -1
  334. package/src/engine/worker-runtime.ts +6 -3
  335. package/src/entry/orchestrator.ts +176 -30
  336. package/src/interactive/chat-loop-messages.ts +26 -7
  337. package/src/interactive/chat-loop.ts +318 -41
  338. package/src/interactive/chat-panel.ts +62 -8
  339. package/src/interactive/clio-editor.ts +45 -8
  340. package/src/interactive/context-activity.ts +5 -1
  341. package/src/interactive/context-meter.ts +1 -1
  342. package/src/interactive/context-overlay.ts +40 -10
  343. package/src/interactive/cost-overlay.ts +64 -6
  344. package/src/interactive/dispatch-board.ts +127 -5
  345. package/src/interactive/fleet-run-preview.ts +41 -15
  346. package/src/interactive/handoff-round.ts +41 -2
  347. package/src/interactive/interactive-application.ts +24 -1
  348. package/src/interactive/interactive-event-projection.ts +14 -0
  349. package/src/interactive/interactive-input-runtime.ts +8 -0
  350. package/src/interactive/interactive-presentation.ts +4 -0
  351. package/src/interactive/interactive-shell.ts +20 -17
  352. package/src/interactive/interactive-slash-runtime.ts +27 -4
  353. package/src/interactive/memory-overlay.ts +8 -0
  354. package/src/interactive/mutation-preview.ts +295 -0
  355. package/src/interactive/overlay-general-openers.ts +16 -0
  356. package/src/interactive/overlay-key-routing.ts +38 -0
  357. package/src/interactive/overlay-lifecycle.ts +38 -5
  358. package/src/interactive/overlay-permission-lifecycle.ts +22 -2
  359. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  360. package/src/interactive/overlays/ask-user.ts +91 -19
  361. package/src/interactive/overlays/help-reference.ts +4 -0
  362. package/src/interactive/overlays/prompts.ts +11 -1
  363. package/src/interactive/overlays/settings.ts +176 -48
  364. package/src/interactive/permission-hint.ts +34 -2
  365. package/src/interactive/permission-overlay.ts +159 -9
  366. package/src/interactive/prewarm.ts +197 -0
  367. package/src/interactive/render-trace.ts +162 -15
  368. package/src/interactive/renderers/tool-execution.ts +4 -0
  369. package/src/interactive/side-question.ts +58 -1
  370. package/src/interactive/slash-commands.ts +7 -2
  371. package/src/interactive/status/controller.ts +11 -0
  372. package/src/interactive/status/state-machine.ts +54 -2
  373. package/src/interactive/status/types.ts +7 -0
  374. package/src/interactive/terminal-lease.ts +2 -0
  375. package/src/interactive/turn-context.ts +299 -31
  376. package/src/interactive/turn-persistence.ts +14 -4
  377. package/src/interactive/turn-prewarm.ts +364 -0
  378. package/src/interactive/turn-queues.ts +7 -4
  379. package/src/interactive/turn-runtime.ts +8 -1
  380. package/src/interactive/turn-state.ts +23 -0
  381. package/src/interactive/view/artifacts.ts +42 -9
  382. package/src/interactive/view/view-overlay.ts +43 -6
  383. package/src/interactive/worker-receipts.ts +14 -2
  384. package/src/interactive/worker-stream.ts +8 -0
  385. package/src/tools/ask-user.ts +43 -2
  386. package/src/tools/dispatch-admission.ts +12 -13
  387. package/src/tools/dispatch-arguments.ts +27 -0
  388. package/src/tools/dispatch-plan.ts +46 -9
  389. package/src/tools/dispatch-runner.ts +48 -13
  390. package/src/tools/dispatch-scout.ts +1 -1
  391. package/src/tools/monitor.ts +13 -0
  392. package/src/tools/registry.ts +16 -0
  393. package/src/tools/worker-evidence.ts +19 -13
  394. package/src/worker/spec-contract.ts +2 -1
  395. package/dist/chunk-AOCYTWAV.js +0 -449
  396. package/dist/chunk-HWUFFB6L.js +0 -83
  397. package/dist/chunk-R346GLFC.js +0 -31
  398. package/dist/chunk-ZGH7FGS5.js +0 -1079
  399. package/dist/doctor-RN4YKO2X.js +0 -87
  400. package/dist/eval-RUBJVSNQ.js +0 -2557
@@ -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
- import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
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,
@@ -39,13 +40,99 @@ function normalizeResources(value: unknown): ExtensionManifestResources {
39
40
  const out: ExtensionManifestResources = {};
40
41
  const skills = trimString(value.skills);
41
42
  const prompts = trimString(value.prompts);
43
+ const agents = trimString(value.agents);
44
+ const fleets = trimString(value.fleets);
42
45
  const themes = trimString(value.themes);
43
46
  if (skills) out.skills = skills;
44
47
  if (prompts) out.prompts = prompts;
48
+ if (agents) out.agents = agents;
49
+ if (fleets) out.fleets = fleets;
45
50
  if (themes) out.themes = themes;
46
51
  return out;
47
52
  }
48
53
 
54
+ function contained(root: string, candidate: string): boolean {
55
+ const relative = path.relative(root, candidate);
56
+ return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
57
+ }
58
+
59
+ function validateResourceTree(
60
+ root: string,
61
+ resourcePath: string,
62
+ kind: string,
63
+ diagnostics: ExtensionDiagnostic[],
64
+ ): void {
65
+ if (path.isAbsolute(resourcePath)) {
66
+ diagnostics.push({ type: "error", message: `resources.${kind} must be relative`, path: resourcePath });
67
+ return;
68
+ }
69
+ const resolvedRoot = path.resolve(root);
70
+ const resolved = path.resolve(resolvedRoot, resourcePath);
71
+ if (!contained(resolvedRoot, resolved) || resolved === resolvedRoot) {
72
+ diagnostics.push({ type: "error", message: `resources.${kind} escapes the extension root`, path: resourcePath });
73
+ return;
74
+ }
75
+ let canonicalRoot: string;
76
+ let canonicalResource: string;
77
+ try {
78
+ canonicalRoot = realpathSync(resolvedRoot);
79
+ canonicalResource = realpathSync(resolved);
80
+ if (!lstatSync(resolved).isDirectory()) {
81
+ diagnostics.push({ type: "error", message: `resources.${kind} is not a directory`, path: resolved });
82
+ return;
83
+ }
84
+ } catch (error) {
85
+ diagnostics.push({
86
+ type: "error",
87
+ message: `resources.${kind} cannot be resolved: ${error instanceof Error ? error.message : String(error)}`,
88
+ path: resolved,
89
+ });
90
+ return;
91
+ }
92
+ if (!contained(canonicalRoot, canonicalResource)) {
93
+ diagnostics.push({ type: "error", message: `resources.${kind} escapes through a symbolic link`, path: resolved });
94
+ return;
95
+ }
96
+
97
+ const pending = [resolved];
98
+ while (pending.length > 0) {
99
+ const directory = pending.pop();
100
+ if (!directory) continue;
101
+ for (const entry of readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
102
+ const candidate = path.join(directory, entry.name);
103
+ if (entry.isSymbolicLink()) {
104
+ try {
105
+ if (!contained(canonicalRoot, realpathSync(candidate))) {
106
+ diagnostics.push({
107
+ type: "error",
108
+ message: `resources.${kind} contains an escaping symbolic link`,
109
+ path: candidate,
110
+ });
111
+ }
112
+ } catch (error) {
113
+ diagnostics.push({
114
+ type: "error",
115
+ message: `resources.${kind} contains an unresolved symbolic link: ${error instanceof Error ? error.message : String(error)}`,
116
+ path: candidate,
117
+ });
118
+ }
119
+ } else if (entry.isDirectory()) {
120
+ pending.push(candidate);
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ function validateResourceRoots(
127
+ root: string,
128
+ resources: ExtensionManifestResources,
129
+ diagnostics: ExtensionDiagnostic[],
130
+ ): void {
131
+ for (const [kind, resourcePath] of Object.entries(resources)) {
132
+ if (resourcePath) validateResourceTree(root, resourcePath, kind, diagnostics);
133
+ }
134
+ }
135
+
49
136
  function stringArray(value: unknown): string[] | undefined {
50
137
  if (!Array.isArray(value)) return undefined;
51
138
  const out = value.map((entry) => trimString(entry)).filter((entry): entry is string => entry !== undefined);
@@ -80,9 +167,32 @@ export function parseExtensionManifest(
80
167
  const resources = normalizeResources(value.resources);
81
168
  const tools = stringArray(value.tools);
82
169
  const settings = stringArray(value.settings);
83
- const compatibility = isRecord(value.compatibility)
84
- ? { ...(trimString(value.compatibility.clio) ? { clio: trimString(value.compatibility.clio) as string } : {}) }
85
- : 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
+ }
86
196
  if (!id || !name || !version || !description || diagnostics.some((diag) => diag.type === "error")) {
87
197
  return { diagnostics };
88
198
  }
@@ -97,6 +207,17 @@ export function parseExtensionManifest(
97
207
  if (tools) manifest.tools = tools;
98
208
  if (settings) manifest.settings = settings;
99
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
+ }
100
221
  return { manifest, diagnostics };
101
222
  }
102
223
 
@@ -123,6 +244,7 @@ export function loadManifestFromRoot(root: string): ExtensionCandidate {
123
244
  }
124
245
  try {
125
246
  const parsed = parseExtensionManifest(readJsonOrYaml(manifestPath), manifestPath);
247
+ if (parsed.manifest) validateResourceRoots(root, parsed.manifest.resources, parsed.diagnostics);
126
248
  return {
127
249
  path: root,
128
250
  manifestPath,
@@ -1,4 +1,4 @@
1
- import { statSync } from "node:fs";
1
+ import { lstatSync, realpathSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { listInstalledExtensions } from "./state.js";
4
4
  import type { ExtensionResourceKind, ExtensionResourceRoot } from "./types.js";
@@ -7,8 +7,24 @@ export function extensionResourcePath(rootPath: string, resourcePath: string): s
7
7
  const root = path.resolve(rootPath);
8
8
  const full = path.resolve(root, resourcePath);
9
9
  const relative = path.relative(root, full);
10
- if (relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))) return full;
11
- return null;
10
+ if (relative === "" || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
11
+ return null;
12
+ try {
13
+ const rootReal = realpathSync(root);
14
+ const fullReal = realpathSync(full);
15
+ const canonicalRelative = path.relative(rootReal, fullReal);
16
+ if (
17
+ canonicalRelative === ".." ||
18
+ canonicalRelative.startsWith(`..${path.sep}`) ||
19
+ path.isAbsolute(canonicalRelative)
20
+ ) {
21
+ return null;
22
+ }
23
+ if (!lstatSync(full).isDirectory()) return null;
24
+ return full;
25
+ } catch {
26
+ return null;
27
+ }
12
28
  }
13
29
 
14
30
  export function enabledExtensionResourceRoots(
@@ -17,20 +33,16 @@ export function enabledExtensionResourceRoots(
17
33
  ): ExtensionResourceRoot[] {
18
34
  const roots: ExtensionResourceRoot[] = [];
19
35
  for (const entry of listInstalledExtensions(cwd)) {
20
- if (!entry.enabled || !entry.effective) continue;
36
+ if (!entry.enabled || !entry.compatible || !entry.effective) continue;
21
37
  const rel = entry.resources[kind];
22
38
  if (!rel) continue;
23
39
  const full = extensionResourcePath(entry.rootPath, rel);
24
40
  if (!full) continue;
25
- try {
26
- if (!statSync(full).isDirectory()) continue;
27
- } catch {
28
- continue;
29
- }
30
41
  roots.push({
31
42
  id: entry.id,
32
43
  scope: entry.scope,
33
44
  path: full,
45
+ rootPath: entry.rootPath,
34
46
  source: `extension:${entry.scope}:${entry.id}`,
35
47
  });
36
48
  }
@@ -2,6 +2,7 @@ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, r
2
2
  import path from "node:path";
3
3
  import { safeResourceWrite } from "../../core/safe-resource-write.js";
4
4
  import { clioConfigDir } from "../../core/xdg.js";
5
+ import { evaluateClioCompatibility } from "./compatibility.js";
5
6
  import { isRecord, loadManifestFromRoot, trimString } from "./discovery.js";
6
7
  import type {
7
8
  ExtensionInstallOptions,
@@ -63,6 +64,8 @@ function installedFromRoot(root: string, scope: ExtensionScope, state: Extension
63
64
  const candidate = loadManifestFromRoot(root);
64
65
  const manifest = candidate.manifest;
65
66
  if (!manifest || !candidate.manifestPath) return null;
67
+ const clioRange = manifest.compatibility?.clio;
68
+ const compatible = clioRange === undefined || evaluateClioCompatibility(clioRange).satisfied;
66
69
  return {
67
70
  id: manifest.id,
68
71
  name: manifest.name,
@@ -72,6 +75,7 @@ function installedFromRoot(root: string, scope: ExtensionScope, state: Extension
72
75
  rootPath: root,
73
76
  manifestPath: candidate.manifestPath,
74
77
  enabled: !state.disabled.includes(manifest.id),
78
+ compatible,
75
79
  effective: false,
76
80
  resources: manifest.resources,
77
81
  diagnostics: candidate.diagnostics,
@@ -102,13 +106,18 @@ export function listInstalledExtensions(cwd = process.cwd(), options: ExtensionL
102
106
  byId.set(entry.id, list);
103
107
  }
104
108
  for (const group of byId.values()) {
105
- const winner = [...group].sort((a, b) => scopeRank(a.scope) - scopeRank(b.scope)).at(-1);
109
+ const winner = group
110
+ .filter((entry) => entry.compatible)
111
+ .sort((a, b) => scopeRank(a.scope) - scopeRank(b.scope))
112
+ .at(-1);
106
113
  for (const entry of group) {
107
114
  entry.effective = entry === winner;
108
- if (!entry.effective && winner) entry.overriddenBy = winner.scope;
115
+ if (entry.compatible && !entry.effective && winner) entry.overriddenBy = winner.scope;
109
116
  }
110
117
  }
111
- const all = options.all === true ? entries : entries.filter((entry) => entry.effective);
118
+ // Incompatible packages remain visible by default so the load refusal and
119
+ // its version diagnostic cannot disappear with the resources it suppresses.
120
+ const all = options.all === true ? entries : entries.filter((entry) => entry.effective || !entry.compatible);
112
121
  return all.sort((a, b) => {
113
122
  const id = a.id.localeCompare(b.id);
114
123
  if (id !== 0) return id;
@@ -129,7 +138,7 @@ export function installExtension(sourcePath: string, options: ExtensionInstallOp
129
138
  const cwd = options.cwd ?? process.cwd();
130
139
  const source = path.resolve(sourcePath);
131
140
  const candidate = loadManifestFromRoot(source);
132
- if (!candidate.manifest) return { diagnostics: candidate.diagnostics };
141
+ if (!candidate.manifest || !candidate.valid) return { diagnostics: candidate.diagnostics };
133
142
  const targetRoot = path.join(extensionBaseDir(scope, cwd), candidate.manifest.id);
134
143
  if (existsSync(targetRoot)) {
135
144
  if (!options.force) {
@@ -152,7 +161,11 @@ export function installExtension(sourcePath: string, options: ExtensionInstallOp
152
161
  rmSync(backupRoot, { recursive: true, force: true });
153
162
  cpSync(source, stagingRoot, {
154
163
  recursive: true,
155
- filter: (src) => path.basename(src) !== "state.json",
164
+ // `state.json` beside the installed extension directories belongs to
165
+ // Clio's extension manager. An extension may legitimately ship nested
166
+ // resources with the same basename (for example a project-state JSON
167
+ // template), so exclude only a bookkeeping file at the package root.
168
+ filter: (src) => path.relative(source, src) !== "state.json",
156
169
  });
157
170
  if (existsSync(targetRoot)) {
158
171
  renameSync(targetRoot, backupRoot);
@@ -1,9 +1,11 @@
1
1
  export type ExtensionScope = "user" | "project";
2
- export type ExtensionResourceKind = "skills" | "prompts" | "themes";
2
+ export type ExtensionResourceKind = "skills" | "prompts" | "agents" | "fleets" | "themes";
3
3
 
4
4
  export interface ExtensionManifestResources {
5
5
  skills?: string;
6
6
  prompts?: string;
7
+ agents?: string;
8
+ fleets?: string;
7
9
  themes?: string;
8
10
  }
9
11
 
@@ -34,6 +36,8 @@ export interface InstalledExtension {
34
36
  rootPath: string;
35
37
  manifestPath: string;
36
38
  enabled: boolean;
39
+ /** Whether this package admits the running Clio version. */
40
+ compatible: boolean;
37
41
  effective: boolean;
38
42
  resources: ExtensionManifestResources;
39
43
  overriddenBy?: ExtensionScope;
@@ -52,6 +56,7 @@ export interface ExtensionResourceRoot {
52
56
  id: string;
53
57
  scope: ExtensionScope;
54
58
  path: string;
59
+ rootPath: string;
55
60
  source: string;
56
61
  }
57
62