@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
@@ -16,6 +16,7 @@ import { Type } from "typebox";
16
16
  import { Value } from "typebox/value";
17
17
  import { resolvePackageRoot } from "../../core/package-root.js";
18
18
  import { clioConfigDir } from "../../core/xdg.js";
19
+ import { enabledExtensionResourceRoots } from "../extensions/index.js";
19
20
  import { type FleetCommandRegistry, loadFleetCommands } from "./fleet-commands.js";
20
21
  import { parseFrontmatter } from "./frontmatter.js";
21
22
  import { normalizeWriteBoundary, WRITE_BOUNDARY_MAX_ENTRIES } from "./write-boundary.js";
@@ -198,7 +199,7 @@ export interface FleetContract {
198
199
  path: string;
199
200
  }
200
201
 
201
- export type FleetContractSource = "builtin" | "user" | "project";
202
+ export type FleetContractSource = "builtin" | "extension" | "user" | "project";
202
203
 
203
204
  export interface FleetContractListing {
204
205
  name: string;
@@ -758,6 +759,7 @@ export function parseFleetContract(raw: string, sourcePath: string): FleetContra
758
759
  }
759
760
  if (version < FLEET_WRITE_BOUNDARY_VERSION) assertNoWritesBefore(frontmatter, sourcePath, version);
760
761
  if (version < FLEET_DYNAMIC_STEP_VERSION) assertNoV5FieldsBefore(frontmatter, sourcePath, version);
762
+ if (version >= FLEET_DYNAMIC_STEP_VERSION) assertNoGateWrites(frontmatter, sourcePath);
761
763
  const schemaError = firstSchemaError(frontmatter, version);
762
764
  if (schemaError !== null) {
763
765
  throw new Error(`fleet contract ${sourcePath}: ${schemaError}`);
@@ -849,6 +851,17 @@ function assertNoWritesBefore(frontmatter: Record<string, unknown>, sourcePath:
849
851
  }
850
852
  }
851
853
 
854
+ function assertNoGateWrites(frontmatter: Record<string, unknown>, sourcePath: string): void {
855
+ for (const value of Array.isArray(frontmatter.steps) ? frontmatter.steps : []) {
856
+ if (typeof value !== "object" || value === null) continue;
857
+ const step = value as Record<string, unknown>;
858
+ if (step.kind !== "gate" || typeof step.id !== "string" || step.id.length === 0 || !("writes" in step)) continue;
859
+ throw new Error(
860
+ `fleet contract ${sourcePath}: gate step '${step.id}' must not declare 'writes'; its write boundary is derived from 'path'`,
861
+ );
862
+ }
863
+ }
864
+
852
865
  function assertNoV5FieldsBefore(frontmatter: Record<string, unknown>, sourcePath: string, version: number): void {
853
866
  const forbidden = ["target", "profile"] as const;
854
867
  if ("writers" in frontmatter) {
@@ -958,13 +971,22 @@ function builtinFleetsDir(): string {
958
971
  return join(resolvePackageRoot(), "src", "domains", "agents", "fleets");
959
972
  }
960
973
 
974
+ function fleetSources(cwd: string): ReadonlyArray<{ dir: string; source: FleetContractSource }> {
975
+ return [
976
+ { dir: builtinFleetsDir(), source: "builtin" },
977
+ ...enabledExtensionResourceRoots("fleets", cwd)
978
+ .sort((left, right) => left.source.localeCompare(right.source))
979
+ .map((root) => ({ dir: root.path, source: "extension" as const })),
980
+ { dir: join(clioConfigDir(), "fleets"), source: "user" },
981
+ { dir: fleetsDir(cwd), source: "project" },
982
+ ];
983
+ }
984
+
961
985
  function fleetContractPath(cwd: string, name: string): { path: string; source: FleetContractSource } | null {
962
- const projectPath = join(fleetsDir(cwd), `${name}.md`);
963
- if (existsSync(projectPath)) return { path: projectPath, source: "project" };
964
- const userPath = join(clioConfigDir(), "fleets", `${name}.md`);
965
- if (existsSync(userPath)) return { path: userPath, source: "user" };
966
- const builtinPath = join(builtinFleetsDir(), `${name}.md`);
967
- if (existsSync(builtinPath)) return { path: builtinPath, source: "builtin" };
986
+ for (const source of [...fleetSources(cwd)].reverse()) {
987
+ const candidate = join(source.dir, `${name}.md`);
988
+ if (existsSync(candidate)) return { path: candidate, source: source.source };
989
+ }
968
990
  return null;
969
991
  }
970
992
 
@@ -1004,11 +1026,7 @@ export function listFleetContracts(cwd: string): FleetContractListing[] {
1004
1026
  registryError = err instanceof Error ? err.message : String(err);
1005
1027
  }
1006
1028
  const listings = new Map<string, FleetContractListing>();
1007
- const sources: ReadonlyArray<{ dir: string; source: FleetContractSource }> = [
1008
- { dir: builtinFleetsDir(), source: "builtin" },
1009
- { dir: join(clioConfigDir(), "fleets"), source: "user" },
1010
- { dir: fleetsDir(cwd), source: "project" },
1011
- ];
1029
+ const sources = fleetSources(cwd);
1012
1030
  for (const { dir, source } of sources) {
1013
1031
  for (const file of listDirectory(dir)) {
1014
1032
  const path = join(dir, file);
@@ -131,7 +131,7 @@ export interface AgentRecipe {
131
131
  resultContract: ResultContract;
132
132
  product?: AgentProduct;
133
133
  tags: ReadonlyArray<string>;
134
- source: "builtin" | "user" | "project";
134
+ source: "builtin" | "extension" | "user" | "project";
135
135
  filepath: string;
136
136
  /** Persona prompt body. */
137
137
  body: string;
@@ -140,6 +140,12 @@ export interface AgentRecipe {
140
140
  export interface RecipeSource {
141
141
  dir: string;
142
142
  source: AgentRecipe["source"];
143
+ /** Project root whose extension and skill resources are being discovered. */
144
+ cwd?: string;
145
+ /** Stable provenance used to correlate resources from one extension. */
146
+ origin?: string;
147
+ /** The declaring extension's skill root; extension agents may bind only here. */
148
+ skillRoot?: string;
143
149
  }
144
150
 
145
151
  export function recipeIdFromPath(absPath: string, rootDir: string): string {
@@ -1,5 +1,8 @@
1
- import { existsSync, readdirSync, readFileSync } from "node:fs";
1
+ import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
2
2
  import path from "node:path";
3
+ import { resolvePackageRoot } from "../../core/package-root.js";
4
+ import { clioConfigDir } from "../../core/xdg.js";
5
+ import { enabledExtensionResourceRoots } from "../extensions/index.js";
3
6
  import { loadSkills } from "../resources/skills/loader.js";
4
7
  import { parseFrontmatter } from "./frontmatter.js";
5
8
  import { type AgentRecipe, type RecipeSource, recipeIdFromPath } from "./recipe.js";
@@ -20,10 +23,29 @@ function resolveBoundSkills(recipe: AgentRecipe, source: RecipeSource): AgentRec
20
23
  // only the operator's discovered skill roots; a recipe cannot smuggle an
21
24
  // arbitrary filesystem path into worker context through a skill name.
22
25
  const packageSkills = path.resolve(source.dir, "..", "..", "..", "..", "skills");
23
- const skills = loadSkills({
24
- cwd: process.cwd(),
25
- ...(source.source === "builtin" && existsSync(packageSkills) ? { explicitSkillPaths: [packageSkills] } : {}),
26
- });
26
+ if (source.source === "extension" && source.skillRoot === undefined) {
27
+ throw new Error(`agent recipe: ${recipe.filepath}: extension declares bound skills but no skills resource root`);
28
+ }
29
+ const skills =
30
+ source.source === "extension"
31
+ ? loadSkills({
32
+ cwd: source.cwd ?? process.cwd(),
33
+ disableDiscovery: true,
34
+ explicitSkillPaths: source.skillRoot === undefined ? [] : [source.skillRoot],
35
+ })
36
+ : loadSkills({
37
+ cwd: source.cwd ?? process.cwd(),
38
+ ...(source.source === "builtin" && existsSync(packageSkills) ? { explicitSkillPaths: [packageSkills] } : {}),
39
+ });
40
+ if (source.source === "extension" && source.skillRoot !== undefined) {
41
+ const canonicalRoot = realpathSync(source.skillRoot);
42
+ for (const skill of skills.items) {
43
+ const relative = path.relative(canonicalRoot, realpathSync(skill.filePath));
44
+ if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
45
+ throw new Error(`agent recipe: ${recipe.filepath}: bound skill escapes its extension: ${skill.filePath}`);
46
+ }
47
+ }
48
+ }
27
49
  const byName = new Map(skills.items.map((skill) => [skill.name, skill.filePath]));
28
50
  const missing = recipe.skills.filter((skill) => !byName.has(skill));
29
51
  if (missing.length > 0) {
@@ -95,6 +117,10 @@ export function mergeRecipes(...sources: ReadonlyArray<ReadonlyArray<AgentRecipe
95
117
  continue;
96
118
  }
97
119
  const builtin = builtinById.get(recipe.id);
120
+ if (recipe.source === "extension" && builtin) {
121
+ process.stderr.write(`[clio:agents] ignore override id=${recipe.id} by=extension reason=reserved-builtin\n`);
122
+ continue;
123
+ }
98
124
  if (recipe.source === "user" && builtin && isShadowAgent(normalizeAgentSpec(builtin))) {
99
125
  process.stderr.write(`[clio:agents] ignore override id=${recipe.id} by=user reason=reserved-shadow\n`);
100
126
  continue;
@@ -109,3 +135,45 @@ export function mergeRecipes(...sources: ReadonlyArray<ReadonlyArray<AgentRecipe
109
135
  }
110
136
  return Array.from(byId.values()).sort((left, right) => left.id.localeCompare(right.id));
111
137
  }
138
+
139
+ /**
140
+ * Discover the one native-agent catalog used by listing, fleet admission, and
141
+ * dispatch. Extension recipes occupy the same precedence slot as extension
142
+ * fleets: after builtins and before operator/user and project recipes. Their
143
+ * skill bindings remain constrained to the declaring extension's skill root.
144
+ */
145
+ export function discoverAgentRecipes(
146
+ cwd = process.cwd(),
147
+ diagnostics: AgentRecipeDiagnostic[] = [],
148
+ ): ReadonlyArray<AgentRecipe> {
149
+ const builtin = loadRecipesFromDir(
150
+ {
151
+ dir: path.join(resolvePackageRoot(), "src", "domains", "agents", "builtins"),
152
+ source: "builtin",
153
+ cwd,
154
+ },
155
+ diagnostics,
156
+ );
157
+ const skillRoots = new Map(enabledExtensionResourceRoots("skills", cwd).map((root) => [root.source, root.path]));
158
+ const extensionRecipes = enabledExtensionResourceRoots("agents", cwd)
159
+ .sort((left, right) => left.source.localeCompare(right.source))
160
+ .map((root) => {
161
+ const skillRoot = skillRoots.get(root.source);
162
+ return loadRecipesFromDir(
163
+ {
164
+ dir: root.path,
165
+ source: "extension",
166
+ cwd,
167
+ origin: root.source,
168
+ ...(skillRoot === undefined ? {} : { skillRoot }),
169
+ },
170
+ diagnostics,
171
+ );
172
+ });
173
+ const user = loadRecipesFromDir({ dir: path.join(clioConfigDir(), "agents"), source: "user", cwd }, diagnostics);
174
+ const project = loadRecipesFromDir(
175
+ { dir: path.join(cwd, ".clio-coder", "agents"), source: "project", cwd },
176
+ diagnostics,
177
+ );
178
+ return mergeRecipes(builtin, ...extensionRecipes, user, project);
179
+ }
@@ -8,6 +8,27 @@ export type ResultContract =
8
8
  | { kind: "scout-report" }
9
9
  | { kind: "verifier-report" }
10
10
  | { kind: "council-report" }
11
+ /**
12
+ * One council member's ballot under `synthesis: "vote"`.
13
+ *
14
+ * The vote is a strict majority over the members' `verdict` fields, computed
15
+ * by the coordinator with no model call. Nothing asked a member for that
16
+ * field before this kind existed, and nothing could: an unnamed council
17
+ * seats the builtin `researcher`, whose `research-report` accepts only
18
+ * `source` and `findings`, so a member that emitted a verdict failed its own
19
+ * postcondition and a member that obeyed its postcondition emitted no
20
+ * verdict. Every vote therefore resolved to `no_verdict_field` with an empty
21
+ * tally, on every input (#230).
22
+ *
23
+ * The ballot is the coordinator's postcondition for that slot, not the
24
+ * recipe's, in the same way `reviewer` and the compete `judge` answer the
25
+ * gate that dispatched them. It rides in as a `resultContractOverride` on
26
+ * the member request, so the seated recipe keeps its persona and any recipe
27
+ * can be voted with. No recipe may declare it: `parseResultContract` rejects
28
+ * the kind, and a Scout subtask may not request it, because it is a
29
+ * topology's ballot rather than an agent's work product.
30
+ */
31
+ | { kind: "council-ballot" }
11
32
  | { kind: "debugger-report" }
12
33
  | { kind: "research-report" }
13
34
  | { kind: "mutation-report" }
@@ -273,6 +294,67 @@ export function parseDelegationPlanResult(output: string | null): DelegationPlan
273
294
  return { tasks };
274
295
  }
275
296
 
297
+ /**
298
+ * Longest verdict token a ballot may cast, in bytes.
299
+ *
300
+ * A tally groups members by their exact verdict string, so a verdict that is a
301
+ * sentence can only ever tie with itself and a council of any size reports
302
+ * `no_majority`. The bound is what makes a majority reachable: it forces the
303
+ * decision into a token members can match (`yes`, `no`, `keep`, an option
304
+ * name) and pushes the reasoning into `text`, where the report already reads
305
+ * it. A newline is refused for the same reason.
306
+ */
307
+ export const COUNCIL_BALLOT_VERDICT_MAX_BYTES = 64;
308
+
309
+ /**
310
+ * The ballot's wire shape, quoted verbatim to the member that must cast it and
311
+ * by the repair round that corrects it. Exported because the council composes
312
+ * the member's ask from it, which is the rule this module already states: a
313
+ * contract's example is written once so a prompt cannot drift from its
314
+ * validator.
315
+ */
316
+ export const COUNCIL_BALLOT_SHAPE = '{"verdict":"yes","text":"the answer and the evidence behind it"}';
317
+
318
+ interface CouncilBallot {
319
+ /** The comparable token the tally counts. */
320
+ verdict: string;
321
+ /** The member's answer in prose, which is what the council report shows. */
322
+ text: string;
323
+ }
324
+
325
+ /**
326
+ * Read a council member's ballot. The council reads a verdict out of the
327
+ * member's terminal text with its own reader, because the judge synthesis
328
+ * carries the same two fields under no contract at all; this one is what
329
+ * decides whether the member is allowed to seal that text as its result.
330
+ */
331
+ function parseCouncilBallot(output: string | null): CouncilBallot | null {
332
+ const parsed = parseJson(output);
333
+ if (!parsed.ok) return null;
334
+ const value = parsed.value;
335
+ if (!hasOnlyKeys(value, ["verdict", "text"]) || !string(value.verdict) || !string(value.text)) return null;
336
+ const verdict = value.verdict.trim();
337
+ if (verdict.includes("\n") || Buffer.byteLength(verdict, "utf8") > COUNCIL_BALLOT_VERDICT_MAX_BYTES) return null;
338
+ return { verdict, text: value.text.trim() };
339
+ }
340
+
341
+ function validateCouncilBallot(contract: ResultContract, output: string | null): ResultContractValidation {
342
+ const parsed = parseJson(output);
343
+ if (!parsed.ok) return failure(contract, "unmeasured", parsed.reason);
344
+ const ballot = parseCouncilBallot(output);
345
+ if (ballot === null) {
346
+ return failure(
347
+ contract,
348
+ "unmeasured",
349
+ `A council ballot must carry only verdict and text, both non-empty strings, with verdict a single line of at most ${COUNCIL_BALLOT_VERDICT_MAX_BYTES} bytes so the tally can match it against the other members`,
350
+ );
351
+ }
352
+ // Unmeasured, like every other contract whose shape carries no correctness
353
+ // claim: casting a well-formed ballot says nothing about whether the member
354
+ // was right, and the tally is a count of opinions rather than a check.
355
+ return success(contract, "unmeasured", ballot);
356
+ }
357
+
276
358
  export interface CouncilReportMember {
277
359
  label: string;
278
360
  runId: string;
@@ -1249,6 +1331,8 @@ export function validateResultContract(input: ResultContractValidationInput): Re
1249
1331
  ? failure(input.contract, "fail", "council-report is malformed")
1250
1332
  : success(input.contract, "unmeasured", { council: report });
1251
1333
  }
1334
+ case "council-ballot":
1335
+ return validateCouncilBallot(input.contract, input.output);
1252
1336
  case "debugger-report":
1253
1337
  return validateDebugger(input.contract, input.output);
1254
1338
  case "research-report":
@@ -1347,6 +1431,8 @@ function resultContractShape(contract: ResultContract): string {
1347
1431
  return '{"verdict":"pass","checks":[{"name":"npm run typecheck","passed":true,"evidence":"exit 0"}]}';
1348
1432
  case "council-report":
1349
1433
  return '{"members":[{"label":"alpha","runId":"run-1","round":1,"answer":"..."}],"synthesis":{"kind":"none"}}';
1434
+ case "council-ballot":
1435
+ return COUNCIL_BALLOT_SHAPE;
1350
1436
  case "debugger-report":
1351
1437
  return '{"diagnosis":"...","reproduction":"reproduced","evidence":["..."]}';
1352
1438
  case "research-report":
@@ -1463,8 +1549,31 @@ export function resultContractRepairMessages(
1463
1549
  ] as const;
1464
1550
  }
1465
1551
 
1466
- /** Strict frontmatter parser for the one recipe result-contract schema. */
1467
- export function parseResultContract(value: unknown, sourcePath: string): ResultContract {
1552
+ /** Kinds an agent recipe may declare in its frontmatter. */
1553
+ const RECIPE_DECLARABLE_KINDS = [
1554
+ "scout-report",
1555
+ "verifier-report",
1556
+ "council-report",
1557
+ "debugger-report",
1558
+ "research-report",
1559
+ "mutation-report",
1560
+ "provenance-report",
1561
+ "oracle-report",
1562
+ "external-delegation",
1563
+ "artifact-report",
1564
+ "context-handbook",
1565
+ "delegation-plan",
1566
+ ] as const;
1567
+
1568
+ /**
1569
+ * Kinds the coordinator authors and hands to a worker on the wire, which no
1570
+ * recipe may declare. A council ballot is the topology's postcondition for a
1571
+ * vote member's slot, so it arrives as a `resultContractOverride` on the
1572
+ * request and never out of frontmatter.
1573
+ */
1574
+ const COORDINATOR_AUTHORED_KINDS = ["council-ballot"] as const;
1575
+
1576
+ function parseContract(value: unknown, sourcePath: string, kinds: ReadonlyArray<string>): ResultContract {
1468
1577
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
1469
1578
  throw new Error(`agent recipe: ${sourcePath}: resultContract must be an object`);
1470
1579
  }
@@ -1481,22 +1590,24 @@ export function parseResultContract(value: unknown, sourcePath: string): ResultC
1481
1590
  }
1482
1591
  return { kind: "architect-plan", path: record.path };
1483
1592
  }
1484
- const kinds = [
1485
- "scout-report",
1486
- "verifier-report",
1487
- "council-report",
1488
- "debugger-report",
1489
- "research-report",
1490
- "mutation-report",
1491
- "provenance-report",
1492
- "oracle-report",
1493
- "external-delegation",
1494
- "artifact-report",
1495
- "context-handbook",
1496
- "delegation-plan",
1497
- ] as const;
1498
- if (!(kinds as ReadonlyArray<string>).includes(record.kind) || !only("kind")) {
1593
+ if (!kinds.includes(record.kind) || !only("kind")) {
1499
1594
  throw new Error(`agent recipe: ${sourcePath}: resultContract.kind is unsupported or has unknown keys`);
1500
1595
  }
1501
1596
  return { kind: record.kind as Exclude<ResultContract["kind"], "architect-plan"> };
1502
1597
  }
1598
+
1599
+ /** Strict frontmatter parser for the one recipe result-contract schema. */
1600
+ export function parseResultContract(value: unknown, sourcePath: string): ResultContract {
1601
+ return parseContract(value, sourcePath, RECIPE_DECLARABLE_KINDS);
1602
+ }
1603
+
1604
+ /**
1605
+ * The same parser for a contract that arrived on a WorkerSpec. It admits what a
1606
+ * recipe may declare plus what the coordinator may author, because a dispatch
1607
+ * request can override the seated recipe's postcondition and the worker has to
1608
+ * be able to read the contract it will be validated against. Keeping the recipe
1609
+ * parser strict is the point: a recipe still cannot claim a coordinator's kind.
1610
+ */
1611
+ export function parseWorkerResultContract(value: unknown, sourcePath: string): ResultContract {
1612
+ return parseContract(value, sourcePath, [...RECIPE_DECLARABLE_KINDS, ...COORDINATOR_AUTHORED_KINDS]);
1613
+ }
@@ -1,3 +1,12 @@
1
+ import {
2
+ normalizePathBoundary,
3
+ normalizePathBoundaryEntry,
4
+ PATH_BOUNDARY_MAX_ENTRIES,
5
+ type PathBoundary,
6
+ pathBoundariesOverlap,
7
+ pathBoundaryCovers,
8
+ } from "../../core/path-boundary.js";
9
+
1
10
  /**
2
11
  * Declared write boundaries: the repo paths a fleet step is allowed to change.
3
12
  *
@@ -30,37 +39,14 @@
30
39
  * a step that needs more of the tree than that should say `workspace` and mean
31
40
  * it in a contract that does not claim a boundary.
32
41
  */
33
- export const WRITE_BOUNDARY_MAX_ENTRIES = 32;
42
+ export const WRITE_BOUNDARY_MAX_ENTRIES = PATH_BOUNDARY_MAX_ENTRIES;
34
43
 
35
44
  /** A normalized allowlist. Empty means "changes nothing", which is `readonly`. */
36
- export type WriteBoundary = ReadonlyArray<string>;
37
-
38
- const GLOB_CHARACTERS = /[*?[\]{}]/u;
45
+ export type WriteBoundary = PathBoundary;
39
46
 
40
47
  /** Normalize one declared entry, or throw explaining exactly what is wrong. */
41
48
  export function normalizeWriteBoundaryEntry(entry: string): string {
42
- const raw = entry.trim();
43
- if (raw.length === 0) throw new Error("write boundary: entry must be a non-empty path");
44
- if (raw.includes("\\")) {
45
- throw new Error(`write boundary: entry '${entry}' must use '/' separators`);
46
- }
47
- if (GLOB_CHARACTERS.test(raw)) {
48
- throw new Error(`write boundary: entry '${entry}' looks like a glob, which v1 does not support`);
49
- }
50
- if (raw.startsWith("/") || /^[A-Za-z]:/u.test(raw)) {
51
- throw new Error(`write boundary: entry '${entry}' must be repository-relative, not absolute`);
52
- }
53
- const subtree = raw.endsWith("/");
54
- const segments = raw.split("/").filter((segment) => segment.length > 0);
55
- if (segments.length === 0) {
56
- throw new Error(`write boundary: entry '${entry}' must name a path inside the repository`);
57
- }
58
- for (const segment of segments) {
59
- if (segment === "..") throw new Error(`write boundary: entry '${entry}' must not contain '..'`);
60
- if (segment === ".") throw new Error(`write boundary: entry '${entry}' must not contain '.' segments`);
61
- }
62
- const joined = segments.join("/");
63
- return subtree ? `${joined}/` : joined;
49
+ return normalizePathBoundaryEntry(entry);
64
50
  }
65
51
 
66
52
  /**
@@ -69,38 +55,17 @@ export function normalizeWriteBoundaryEntry(entry: string): string {
69
55
  * and a sealed verdict can both carry.
70
56
  */
71
57
  export function normalizeWriteBoundary(entries: ReadonlyArray<string>): string[] {
72
- if (entries.length > WRITE_BOUNDARY_MAX_ENTRIES) {
73
- throw new Error(`write boundary: at most ${WRITE_BOUNDARY_MAX_ENTRIES} entries may be declared`);
74
- }
75
- const normalized = new Set(entries.map(normalizeWriteBoundaryEntry));
76
- // A file entry covered by a declared subtree is redundant, not an error: the
77
- // operator said the directory, and saying one of its files again changes
78
- // nothing about what is allowed.
79
- return [...normalized].sort();
80
- }
81
-
82
- /** Repo-relative path with the separators this grammar speaks. */
83
- function toRepoRelativePosix(path: string): string {
84
- return path.split("\\").join("/");
58
+ return normalizePathBoundary(entries);
85
59
  }
86
60
 
87
61
  /** Whether the boundary permits a change to one repo-relative path. */
88
62
  export function writeBoundaryCovers(boundary: WriteBoundary, path: string): boolean {
89
- const candidate = toRepoRelativePosix(path);
90
- return boundary.some((entry) => (entry.endsWith("/") ? candidate.startsWith(entry) : candidate === entry));
91
- }
92
-
93
- /** Whether two declared entries can describe the same path. */
94
- function entriesOverlap(left: string, right: string): boolean {
95
- if (left === right) return true;
96
- if (left.endsWith("/") && right.startsWith(left)) return true;
97
- if (right.endsWith("/") && left.startsWith(right)) return true;
98
- return false;
63
+ return pathBoundaryCovers(boundary, path);
99
64
  }
100
65
 
101
66
  /** Whether two boundaries share any path. Empty boundaries never overlap. */
102
67
  export function writeBoundariesOverlap(left: WriteBoundary, right: WriteBoundary): boolean {
103
- return left.some((entry) => right.some((other) => entriesOverlap(entry, other)));
68
+ return pathBoundariesOverlap(left, right);
104
69
  }
105
70
 
106
71
  /** Operator-facing rendering, used verbatim in violation messages. */
@@ -61,6 +61,9 @@ const NEXT_TURN_FIELDS = new Set<string>([
61
61
  "delegation",
62
62
  "compaction",
63
63
  "context",
64
+ // The pre-warm reads its setting when a trigger fires, and every trigger sits
65
+ // on a turn boundary, so a change takes effect before the next turn runs.
66
+ "prewarm",
64
67
  "retry",
65
68
  ]);
66
69
 
@@ -140,8 +140,16 @@ export function coordinateCodewikiWrite(
140
140
  options: CodewikiCoordinateOptions = {},
141
141
  ): Promise<CodewikiCoordinatedResult | null> {
142
142
  const workspace = resolve(cwd);
143
- return enqueueWorkspace(workspace, () =>
144
- withStateFileLock(codewikiPath(workspace), async () => {
143
+ return enqueueWorkspace(workspace, () => {
144
+ // Taking the lease creates the lock's parent, so a project that was never
145
+ // indexed gained an empty `.clio-coder/` from every successful write: the
146
+ // incremental refresh is contractually a no-op there, and it was
147
+ // materializing state before it could say so (issue #248). The check runs
148
+ // inside the queue, so a writer that built the codewiki ahead of this task
149
+ // has already finished and this one sees it. The recheck under the lock
150
+ // stays, and is what still decides the race.
151
+ if (options.requireExisting && !existsSync(codewikiPath(workspace))) return Promise.resolve(null);
152
+ return withStateFileLock(codewikiPath(workspace), async () => {
145
153
  if (options.requireExisting && !existsSync(codewikiPath(workspace))) return null;
146
154
  const current = options.readCurrent?.(workspace) ?? readCodewiki(workspace);
147
155
  const request = await select(current, workspace);
@@ -152,8 +160,8 @@ export function coordinateCodewikiWrite(
152
160
  if (wrote) writeCodewiki(workspace, worker.codewiki);
153
161
  await options.afterCommit?.(worker, workspace);
154
162
  return { codewiki: !worker.changed && current ? current : worker.codewiki, worker, wrote };
155
- }),
156
- );
163
+ });
164
+ });
157
165
  }
158
166
 
159
167
  /** Serialize a non-build artifact transaction such as reset with every writer. */
@@ -203,12 +203,62 @@ function ruleMatchesAnyPath(rule: ProjectRule, workingPaths: ReadonlyArray<strin
203
203
  continue;
204
204
  }
205
205
  for (const path of workingPaths) {
206
- if (regex.test(normalizeGlobInput(path))) return true;
206
+ const normalized = normalizeGlobInput(path);
207
+ if (regex.test(normalized)) return true;
208
+ if (normalized.endsWith("/") && globMatchesInsideDirectory(regex, pattern, normalized)) return true;
207
209
  }
208
210
  }
209
211
  return false;
210
212
  }
211
213
 
214
+ function materializedGlobVariants(pattern: string): string[] {
215
+ let variants = [""];
216
+ const normalized = normalizeGlobInput(pattern);
217
+ for (let index = 0; index < normalized.length; index += 1) {
218
+ const character = normalized[index];
219
+ if (character === undefined) break;
220
+ if (character === "*" && normalized[index + 1] === "*" && normalized[index + 2] === "/") {
221
+ variants = variants.flatMap((prefix) => [prefix, `${prefix}x/`]).slice(0, 64);
222
+ index += 2;
223
+ continue;
224
+ }
225
+ if (character === "*" && normalized[index + 1] === "*") {
226
+ variants = variants.map((prefix) => `${prefix}x`);
227
+ index += 1;
228
+ continue;
229
+ }
230
+ if (character === "*" || character === "?") {
231
+ variants = variants.map((prefix) => `${prefix}x`);
232
+ continue;
233
+ }
234
+ if (character === "[") {
235
+ const end = normalized.indexOf("]", index + 1);
236
+ const selected = end === -1 ? "x" : (normalized[index + 1] ?? "x");
237
+ variants = variants.map((prefix) => `${prefix}${selected}`);
238
+ index = end === -1 ? index : end;
239
+ continue;
240
+ }
241
+ variants = variants.map((prefix) => `${prefix}${character}`);
242
+ }
243
+ return variants;
244
+ }
245
+
246
+ function globMatchesInsideDirectory(regex: RegExp, pattern: string, directory: string): boolean {
247
+ const candidates = new Set<string>([`${directory}x`, `${directory}x/x`]);
248
+ for (const materialized of materializedGlobVariants(pattern)) {
249
+ if (materialized.startsWith(directory)) candidates.add(materialized);
250
+ const segments = materialized.split("/");
251
+ for (let index = 0; index < segments.length; index += 1) {
252
+ const suffix = segments.slice(index).join("/");
253
+ if (suffix.length > 0) candidates.add(`${directory}${suffix}`);
254
+ }
255
+ for (let index = 0; index < materialized.length; index += 1) {
256
+ candidates.add(`${directory}${materialized.slice(index)}`);
257
+ }
258
+ }
259
+ return [...candidates].some((candidate) => regex.test(candidate));
260
+ }
261
+
212
262
  /**
213
263
  * Select the rules that should load now. Unconditional rules (no `paths`) always
214
264
  * load; a path-scoped rule loads only when one of its globs matches a path