@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
@@ -21,18 +21,27 @@
21
21
  * Everything is pinned to the baseline commit recorded at snapshot time, so a
22
22
  * commit step that moves HEAD without touching the working tree reads as "no
23
23
  * change" instead of as a wholesale rewrite.
24
+ *
25
+ * A window's diff answers "what changed", never "who changed it". An operator
26
+ * editing the checkout while a fleet runs produces changes inside the window
27
+ * that no step made, and rolling those back to the baseline destroys work
28
+ * nobody asked Clio to touch. So a change is blamed on the window only when it
29
+ * intersects what the window's own runs recorded writing; everything else is
30
+ * reported as an unattributed concurrent change and left exactly as it is.
24
31
  */
25
32
 
26
33
  import { execFileSync } from "node:child_process";
27
34
  import { createHash } from "node:crypto";
28
35
  import { existsSync, lstatSync, mkdirSync, readlinkSync, realpathSync, rmSync, writeFileSync } from "node:fs";
29
- import { dirname, join, relative, resolve, sep } from "node:path";
36
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
37
+ import { asDirectoryPathBoundary, pathBoundaryEntryCovers } from "../../core/path-boundary.js";
30
38
  import { clioStateDir } from "../../core/xdg.js";
31
39
  import { atomicWrite } from "../../engine/session.js";
32
40
  import { isGitRepository } from "../../tools/compete-worktrees.js";
33
41
  import {
34
42
  describeWriteBoundary,
35
43
  normalizeWriteBoundary,
44
+ normalizeWriteBoundaryEntry,
36
45
  type WriteBoundary,
37
46
  writeBoundaryCovers,
38
47
  } from "../agents/write-boundary.js";
@@ -71,10 +80,15 @@ function gitBytes(root: string, args: ReadonlyArray<string>): Buffer {
71
80
  *
72
81
  * Enforcement sees exactly what git sees, so an ignored path is outside it: a
73
82
  * repository decides what counts as its own content, and this is not the place
74
- * to overrule that. The one thing subtracted is Clio's own state directory when
75
- * an operator has placed it inside the workspace. Receipts, code-step logs, and
76
- * boundary verdicts are the orchestrator writing its journal while the step
77
- * runs, and blaming the step for them would make every window a violation.
83
+ * to overrule that. The consequence is that a boundary declared over an ignored
84
+ * path could never be verified, which is why `assertWriteBoundaryVisibleToGit`
85
+ * refuses that declaration before anything runs rather than letting this
86
+ * function certify an unobserved window as clean.
87
+ *
88
+ * The one thing subtracted is Clio's own state directory when an operator has
89
+ * placed it inside the workspace. Receipts, code-step logs, and boundary
90
+ * verdicts are the orchestrator writing its journal while the step runs, and
91
+ * blaming the step for them would make every window a violation.
78
92
  */
79
93
  function dirtyPaths(root: string): string[] {
80
94
  const raw = git(root, ["status", "--porcelain=v1", "-z", "-uall", "--no-renames"]);
@@ -222,14 +236,98 @@ export interface WriteBoundaryUnrecoverable {
222
236
  }
223
237
 
224
238
  /**
225
- * `clean` means nothing outside the allowlist changed. `rolled-back` means
226
- * something did and the repository now looks as it did before. Anything else is
227
- * `rollback-incomplete`: the working tree is left exactly as the step made it
228
- * and handed to the operator with the list, because a rollback that guesses at
229
- * content it never recorded destroys work.
239
+ * `clean` means no change outside the allowlist is attributable to this
240
+ * window's steps. `rolled-back` means one was and the repository now looks as
241
+ * it did before. Anything else is `rollback-incomplete`: the working tree is
242
+ * left exactly as the step made it and handed to the operator with the list,
243
+ * because a rollback that guesses at content it never recorded destroys work.
244
+ *
245
+ * A `clean` verdict can still carry `unattributed` paths. Those changed inside
246
+ * the window and no run in it recorded writing them, so they are the
247
+ * operator's, another process's, or a build artifact's, and nothing here may
248
+ * touch them.
230
249
  */
231
250
  export type WriteBoundaryStatus = "clean" | "rolled-back" | "rollback-incomplete";
232
251
 
252
+ /**
253
+ * What the window's own runs recorded aiming a successful mutation at.
254
+ *
255
+ * This is the write-side witness the diff cannot be: the checkout says a path
256
+ * changed, and only the run's own tool-call record says whether the run is what
257
+ * changed it. The record comes from the same fold that grounds a sealed
258
+ * mutation report, so the boundary and the result contract judge one set of
259
+ * facts.
260
+ *
261
+ * A run can write through a channel no tool event enumerates, and then its path
262
+ * set is a lower bound rather than the whole list. That is not silently
263
+ * tolerated: the recorder knows which tools can do it (bash, verify, dispatch,
264
+ * steer, and any tool this process has no schema for) and reports the record
265
+ * open, which arrives here as `complete: false`. So the set is only ever read
266
+ * as a closed list for a run that could not have written outside it, and
267
+ * enforcement over a run that could stays exactly as strong as it was before
268
+ * attribution existed.
269
+ */
270
+ export interface WriteBoundaryAttribution {
271
+ /**
272
+ * Paths the window's runs aimed a successful mutation at. Absolute, or
273
+ * relative to the workspace root; anything resolving outside the workspace is
274
+ * dropped, because git could not have reported it as a change either.
275
+ */
276
+ recorded: ReadonlyArray<string>;
277
+ /**
278
+ * False when at least one step in the window has no closed write record: a
279
+ * code step running a registered command, a runtime that publishes no tool
280
+ * telemetry, a run whose telemetry came back with holes, or a run that
281
+ * called a tool able to write a path its arguments do not name. An open
282
+ * record cannot clear anybody, so the window falls back to blaming every
283
+ * change outside the allowlist, and the verdict says it did.
284
+ */
285
+ complete: boolean;
286
+ /** Durable causes for an incomplete record. Empty only when complete is true. */
287
+ downgrades?: ReadonlyArray<WriteBoundaryAttributionDowngrade>;
288
+ }
289
+
290
+ export type WriteBoundaryAttributionDowngradeReason =
291
+ | "opaque_tool_succeeded"
292
+ | "incomplete_tool_telemetry"
293
+ | "code_step_untracked"
294
+ | "write_record_unavailable";
295
+
296
+ /** Why one step could not offer a closed write record. */
297
+ export interface WriteBoundaryAttributionDowngrade {
298
+ reason: WriteBoundaryAttributionDowngradeReason;
299
+ /** Tool whose successful call opened the record, or null for a non-tool cause. */
300
+ tool: string | null;
301
+ /** Exact worker call when telemetry supplied one, or null for a non-tool cause. */
302
+ toolCallId: string | null;
303
+ /** Run that observed the cause, when one reached dispatch. */
304
+ runId: string | null;
305
+ /** Fleet step whose record was downgraded. */
306
+ stepId: string | null;
307
+ }
308
+
309
+ /** One operator-facing explanation shared by live and durable surfaces. */
310
+ export function describeWriteBoundaryAttributionDowngrade(downgrade: WriteBoundaryAttributionDowngrade): string {
311
+ if (downgrade.reason === "opaque_tool_succeeded") {
312
+ const tool = downgrade.tool ?? "unknown";
313
+ const call = downgrade.toolCallId === null ? "" : ` (call ${downgrade.toolCallId})`;
314
+ return `a successful '${tool}' call${call} can write paths its arguments do not name`;
315
+ }
316
+ if (downgrade.reason === "incomplete_tool_telemetry") {
317
+ return downgrade.runId === null
318
+ ? "tool telemetry was incomplete"
319
+ : `run '${downgrade.runId}' reported incomplete tool telemetry`;
320
+ }
321
+ if (downgrade.reason === "code_step_untracked") {
322
+ return downgrade.stepId === null
323
+ ? "a code step ran through the untracked command runner"
324
+ : `code step '${downgrade.stepId}' ran through the untracked command runner`;
325
+ }
326
+ return downgrade.stepId === null
327
+ ? "no usable write record was available"
328
+ : `step '${downgrade.stepId}' supplied no usable write record`;
329
+ }
330
+
233
331
  export interface WriteBoundaryVerdict {
234
332
  version: 1;
235
333
  /** Which scheduling window this covers, e.g. `wave-2` or `revalidate-suite.check.1`. */
@@ -243,12 +341,27 @@ export interface WriteBoundaryVerdict {
243
341
  checkedAt: string;
244
342
  changedPaths: ReadonlyArray<string>;
245
343
  violations: ReadonlyArray<string>;
344
+ /**
345
+ * Paths outside the allowlist that changed inside the window and that no run
346
+ * in it recorded writing. Recorded so the operator can see them, never rolled
347
+ * back: the window did not make them, so the baseline commit is not their
348
+ * prior content.
349
+ */
350
+ unattributed: ReadonlyArray<string>;
351
+ /** Whether every step in the window offered an enumerable write record. */
352
+ attributionComplete: boolean;
353
+ /** Why attribution was incomplete, including the causal tool call when known. */
354
+ attributionDowngrades: ReadonlyArray<WriteBoundaryAttributionDowngrade>;
246
355
  rolledBack: ReadonlyArray<WriteBoundaryRollback>;
247
356
  unrecoverable: ReadonlyArray<WriteBoundaryUnrecoverable>;
248
357
  status: WriteBoundaryStatus;
249
358
  /** Typed failure reason, or null when the window stayed inside its boundary. */
250
359
  reason: typeof WRITE_BOUNDARY_VIOLATION_REASON | null;
251
- /** Operator-facing message naming the offending paths and the declaration. */
360
+ /**
361
+ * Operator-facing message naming the offending paths and the declaration, or
362
+ * naming the concurrent changes the window saw but did not cause. Null only
363
+ * when neither happened.
364
+ */
252
365
  detail: string | null;
253
366
  digest: string;
254
367
  }
@@ -258,6 +371,12 @@ export interface EnforceWriteBoundaryInput {
258
371
  window: string;
259
372
  stepIds: ReadonlyArray<string>;
260
373
  allow: WriteBoundary;
374
+ /**
375
+ * What the window's runs recorded writing. Absent means no caller offered a
376
+ * record, which reads the same as an incomplete one: every change outside the
377
+ * allowlist is blamed on the window.
378
+ */
379
+ attribution?: WriteBoundaryAttribution;
261
380
  }
262
381
 
263
382
  function canonicalVerdict(verdict: Omit<WriteBoundaryVerdict, "digest">): string {
@@ -317,6 +436,22 @@ function rollbackPath(
317
436
  return { rolledBack: { path: change.path, action: "restored", restoredFrom: `${snapshot.head}:${change.path}` } };
318
437
  }
319
438
 
439
+ /**
440
+ * Whether one changed path falls inside what a run recorded writing.
441
+ *
442
+ * A recorded target is usually the exact file a write or edit named, but a
443
+ * command's delete target can be a directory, so a change under a recorded
444
+ * prefix counts as the same write. Matching is on segment boundaries: `work`
445
+ * does not cover `workspace/a.txt`.
446
+ */
447
+ function withinRecordedWrite(recorded: ReadonlySet<string>, path: string): boolean {
448
+ if (recorded.has(path)) return true;
449
+ for (const target of recorded) {
450
+ if (path.startsWith(`${target}/`)) return true;
451
+ }
452
+ return false;
453
+ }
454
+
320
455
  /**
321
456
  * Compare, roll back, and return the verdict. Never throws for a violation: a
322
457
  * violation is evidence the caller records and acts on, not an exception that
@@ -325,7 +460,34 @@ function rollbackPath(
325
460
  export function enforceWriteBoundary(input: EnforceWriteBoundaryInput): WriteBoundaryVerdict {
326
461
  const allow = normalizeWriteBoundary(input.allow);
327
462
  const changes = diffWorkspace(input.snapshot);
328
- const violations = changes.filter((change) => !writeBoundaryCovers(allow, change.path));
463
+ const outside = changes.filter((change) => !writeBoundaryCovers(allow, change.path));
464
+ const attributionComplete = input.attribution?.complete === true;
465
+ const attributionDowngrades: ReadonlyArray<WriteBoundaryAttributionDowngrade> = attributionComplete
466
+ ? []
467
+ : input.attribution?.downgrades !== undefined && input.attribution.downgrades.length > 0
468
+ ? input.attribution.downgrades.map((entry) => ({ ...entry }))
469
+ : [
470
+ {
471
+ reason: "write_record_unavailable",
472
+ tool: null,
473
+ toolCallId: null,
474
+ runId: null,
475
+ stepId: input.stepIds.length === 1 ? (input.stepIds[0] ?? null) : null,
476
+ },
477
+ ];
478
+ const recorded = new Set<string>();
479
+ for (const target of input.attribution?.recorded ?? []) {
480
+ const relativeTarget = repoRelative(input.snapshot.root, target);
481
+ if (relativeTarget !== null) recorded.add(relativeTarget);
482
+ }
483
+ // Without a complete record nothing can be cleared, so the window keeps the
484
+ // whole diff. With one, only what the runs actually wrote is theirs.
485
+ const violations = attributionComplete
486
+ ? outside.filter((change) => withinRecordedWrite(recorded, change.path))
487
+ : outside;
488
+ const unattributed = attributionComplete
489
+ ? outside.filter((change) => !withinRecordedWrite(recorded, change.path))
490
+ : [];
329
491
  const rolledBack: WriteBoundaryRollback[] = [];
330
492
  const unrecoverable: WriteBoundaryUnrecoverable[] = [];
331
493
  for (const change of violations) {
@@ -335,15 +497,15 @@ export function enforceWriteBoundary(input: EnforceWriteBoundaryInput): WriteBou
335
497
  }
336
498
  const status: WriteBoundaryStatus =
337
499
  violations.length === 0 ? "clean" : unrecoverable.length === 0 ? "rolled-back" : "rollback-incomplete";
338
- const attribution =
500
+ const who =
339
501
  input.stepIds.length === 1
340
502
  ? `step '${input.stepIds[0]}'`
341
503
  : `steps ${input.stepIds.map((id) => `'${id}'`).join(", ")} ran concurrently in one checkout, so the write cannot be attributed to one of them`;
342
- const detail =
504
+ const violationDetail =
343
505
  violations.length === 0
344
- ? null
506
+ ? []
345
507
  : [
346
- `${attribution} wrote outside its declared boundary.`,
508
+ `${who} wrote outside its declared boundary.`,
347
509
  `Unauthorized paths: ${violations.map((change) => change.path).join(", ")}.`,
348
510
  `Declared writes: ${describeWriteBoundary(allow)}.`,
349
511
  status === "rolled-back"
@@ -351,8 +513,23 @@ export function enforceWriteBoundary(input: EnforceWriteBoundaryInput): WriteBou
351
513
  : `Rollback is incomplete; the working tree is left as the step made it. Unrestorable: ${unrecoverable
352
514
  .map((entry) => `${entry.path} (${entry.reason})`)
353
515
  .join("; ")}.`,
516
+ attributionComplete
517
+ ? "Each was blamed on this window because a run in it recorded writing that path."
518
+ : `Attribution was incomplete because ${attributionDowngrades
519
+ .map(describeWriteBoundaryAttributionDowngrade)
520
+ .join("; ")}, so every change outside the declaration was blamed on this window.`,
354
521
  "If the change was legitimate, widen the step's `writes:` declaration to cover it.",
355
- ].join(" ");
522
+ ];
523
+ const concurrentDetail =
524
+ unattributed.length === 0
525
+ ? []
526
+ : [
527
+ `Concurrent changes were seen in this window that no run in it recorded writing: ${unattributed
528
+ .map((change) => change.path)
529
+ .join(", ")}.`,
530
+ "They were left untouched, because a path this window did not write is not this window's to restore.",
531
+ ];
532
+ const detail = [...violationDetail, ...concurrentDetail];
356
533
  const body: Omit<WriteBoundaryVerdict, "digest"> = {
357
534
  version: 1,
358
535
  window: input.window,
@@ -363,11 +540,14 @@ export function enforceWriteBoundary(input: EnforceWriteBoundaryInput): WriteBou
363
540
  checkedAt: new Date().toISOString(),
364
541
  changedPaths: changes.map((change) => change.path),
365
542
  violations: violations.map((change) => change.path),
543
+ unattributed: unattributed.map((change) => change.path),
544
+ attributionComplete,
545
+ attributionDowngrades,
366
546
  rolledBack,
367
547
  unrecoverable,
368
548
  status,
369
549
  reason: violations.length === 0 ? null : WRITE_BOUNDARY_VIOLATION_REASON,
370
- detail,
550
+ detail: detail.length === 0 ? null : detail.join(" "),
371
551
  };
372
552
  return { ...body, digest: createHash("sha256").update(canonicalVerdict(body), "utf8").digest("hex") };
373
553
  }
@@ -387,16 +567,76 @@ export function assertWriteBoundaryInsideRoot(root: string, allow: WriteBoundary
387
567
  // path a step is about to create.
388
568
  while (probe !== realRoot && !existsSync(probe)) probe = dirname(probe);
389
569
  const resolved = realpathSync(probe);
390
- if (resolved !== realRoot && !resolved.startsWith(realRoot + sep)) {
570
+ // The root is inside itself. The prefix walk above stops at the root when
571
+ // a declared directory does not exist yet, and `pathBoundaryEntryCovers`
572
+ // answers a different question: it asks whether a changed path falls under
573
+ // a declaration, where the directory entry itself is not a change git ever
574
+ // reports. Asking it about the root would refuse every boundary naming a
575
+ // path the step is about to create.
576
+ if (resolved !== realRoot && !pathBoundaryEntryCovers(asDirectoryPathBoundary(realRoot), resolved)) {
391
577
  throw new Error(`write boundary: entry '${entry}' resolves to ${resolved}, outside the workspace at ${realRoot}`);
392
578
  }
393
579
  }
394
580
  }
395
581
 
396
- /** Repo-relative form of an absolute path, or null when it is outside. */
582
+ /**
583
+ * Refuse a declaration git will never report on.
584
+ *
585
+ * `git status` does not list an ignored path, so a boundary entry the
586
+ * repository ignores names a region enforcement is blind to: every window over
587
+ * it is certified clean without a single observation, which is a stronger claim
588
+ * than "nothing was checked" and reads as a passing one. That is the same
589
+ * defect `assertWriteBoundaryInsideRoot` refuses for an entry that leaves the
590
+ * workspace through a symlink, so it is refused in the same place and at the
591
+ * same time, before anything runs.
592
+ *
593
+ * A tracked path is reportable whatever the ignore rules say, and git's own
594
+ * index-aware check is what decides that, so this never refuses a path the
595
+ * repository is actually following.
596
+ */
597
+ export function assertWriteBoundaryVisibleToGit(root: string, allow: WriteBoundary): void {
598
+ // Entry by entry rather than through `normalizeWriteBoundary`, whose 32-entry
599
+ // cap is a per-step declaration limit. A caller may legitimately ask this
600
+ // question about every entry a whole plan declared.
601
+ const entries = [...new Set(allow.map(normalizeWriteBoundaryEntry))].sort();
602
+ if (entries.length === 0) return;
603
+ const absolute = resolve(root);
604
+ let raw: string;
605
+ try {
606
+ raw = git(absolute, ["check-ignore", "-v", "-z", "--stdin"], `${entries.join("\0")}\0`);
607
+ } catch (error) {
608
+ // `check-ignore` exits 1 when nothing matched, which is the common answer
609
+ // and not a failure. Any other exit is a real inability to decide, and
610
+ // enforcement fails closed rather than assuming visibility.
611
+ const status = (error as { status?: number }).status;
612
+ if (status === 1) return;
613
+ throw new Error(
614
+ `write boundary: could not determine whether ${describeWriteBoundary(entries)} is ignored by git in ${absolute}: ${(error as Error).message}`,
615
+ );
616
+ }
617
+ const fields = raw.split("\0");
618
+ const refusals: string[] = [];
619
+ // `-v -z` emits source, line number, pattern, and pathname per match.
620
+ for (let index = 0; index + 3 < fields.length; index += 4) {
621
+ const [source, line, pattern, path] = fields.slice(index, index + 4);
622
+ if (path === undefined || path.length === 0) continue;
623
+ refusals.push(`'${path}' is ignored by ${source}:${line}:${pattern}`);
624
+ }
625
+ if (refusals.length === 0) return;
626
+ throw new Error(
627
+ `write boundary: ${refusals.join(", ")}. git status never reports an ignored path, so a boundary declared over one is verified against nothing and every window would be certified clean without an observation. Declare a path the repository tracks, or stop ignoring this one.`,
628
+ );
629
+ }
630
+
631
+ /**
632
+ * Workspace-relative form of a path, or null when it resolves outside. A
633
+ * relative input is taken as already workspace-relative, which is how a
634
+ * recorded write target and an absolute state directory both normalize here.
635
+ */
397
636
  function repoRelative(root: string, path: string): string | null {
398
- const rel = relative(resolve(root), resolve(path));
399
- if (rel.length === 0 || rel.startsWith("..") || rel.startsWith(`..${sep}`)) return null;
637
+ const base = resolve(root);
638
+ const rel = relative(base, resolve(base, path));
639
+ if (rel.length === 0 || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) return null;
400
640
  return rel.split(sep).join("/");
401
641
  }
402
642
 
@@ -3,6 +3,11 @@ import { join } from "node:path";
3
3
  import { assertSafeId } from "../../../core/safe-id.js";
4
4
  import { safeResourceWrite } from "../../../core/safe-resource-write.js";
5
5
  import type { EvalArtifactV4, EvalTokenAccountingV4 } from "../schema/artifact.js";
6
+ import { assertEvalBehaviorReferencesVerdictV1, parseEvalBehaviorVerdictV1 } from "../schema/behavioral.js";
7
+ import { parseEvalBehaviorMetricsV1 } from "../schema/behavioral-metrics.js";
8
+ import { parseEvalExecutionEnvelopeV1, parseEvalExecutionMatrixDimensionsV1 } from "../schema/execution-envelope.js";
9
+ import { parseEvalServingConfigurationV1 } from "../schema/serving.js";
10
+ import { parseEvalVerdictEnvelopeV1 } from "../schema/verdict.js";
6
11
  import { evalRoot } from "../store.js";
7
12
  import { redactArtifactForStorage } from "./redact.js";
8
13
 
@@ -45,6 +50,14 @@ export function parseEvalArtifactV4(value: unknown, source: string): EvalArtifac
45
50
  const summary = asRecord(value.summary, `${source}.summary`);
46
51
  const matrix = asRecord(value.matrix, `${source}.matrix`);
47
52
  const suite = asRecord(value.suite, `${source}.suite`);
53
+ const servingConfiguration =
54
+ value.servingConfiguration === undefined
55
+ ? undefined
56
+ : parseEvalServingConfigurationV1(value.servingConfiguration, `${source}.servingConfiguration`);
57
+ const aggregates =
58
+ value.aggregates === undefined
59
+ ? undefined
60
+ : (readArray(value, source, "aggregates") as NonNullable<EvalArtifactV4["aggregates"]>);
48
61
  return {
49
62
  version: 4,
50
63
  evalId: readString(value, source, "evalId"),
@@ -62,7 +75,11 @@ export function parseEvalArtifactV4(value: unknown, source: string): EvalArtifac
62
75
  target: readString(matrix, `${source}.matrix`, "target"),
63
76
  model: readNullableString(matrix, `${source}.matrix`, "model"),
64
77
  thinking: readNullableString(matrix, `${source}.matrix`, "thinking"),
78
+ ...(matrix.dimensions === undefined
79
+ ? {}
80
+ : { dimensions: parseEvalExecutionMatrixDimensionsV1(matrix.dimensions, `${source}.matrix.dimensions`) }),
65
81
  },
82
+ ...(servingConfiguration === undefined ? {} : { servingConfiguration }),
66
83
  summary: {
67
84
  runs: readNumber(summary, `${source}.summary`, "runs"),
68
85
  passed: readNumber(summary, `${source}.summary`, "passed"),
@@ -71,6 +88,7 @@ export function parseEvalArtifactV4(value: unknown, source: string): EvalArtifac
71
88
  tokens: parseTokenAccounting(summary.tokens, `${source}.summary.tokens`),
72
89
  wallTimeMs: readNumber(summary, `${source}.summary`, "wallTimeMs"),
73
90
  },
91
+ ...(aggregates === undefined ? {} : { aggregates }),
74
92
  results: readArray(value, source, "results").map((entry, index) => parseResult(entry, `${source}.results[${index}]`)),
75
93
  };
76
94
  }
@@ -108,6 +126,46 @@ function parseTokenAccounting(value: unknown, source: string): EvalTokenAccounti
108
126
  function parseResult(value: unknown, source: string): EvalArtifactV4["results"][number] {
109
127
  const record = asRecord(value, source);
110
128
  const target = asRecord(record.target, `${source}.target`);
129
+ const verdict =
130
+ record.verdict === undefined ? undefined : parseEvalVerdictEnvelopeV1(record.verdict, `${source}.verdict`);
131
+ const behavioral =
132
+ record.behavioral === undefined ? undefined : parseEvalBehaviorVerdictV1(record.behavioral, `${source}.behavioral`);
133
+ const behavioralMetrics =
134
+ record.behavioralMetrics === undefined
135
+ ? undefined
136
+ : parseEvalBehaviorMetricsV1(record.behavioralMetrics, `${source}.behavioralMetrics`);
137
+ const executionEnvelope =
138
+ record.executionEnvelope === undefined
139
+ ? undefined
140
+ : parseEvalExecutionEnvelopeV1(record.executionEnvelope, `${source}.executionEnvelope`);
141
+ if (behavioral !== undefined && verdict === undefined) {
142
+ throw new Error(`${source}.behavioral: sibling document requires a verdict`);
143
+ }
144
+ if (behavioral !== undefined && verdict !== undefined) {
145
+ assertEvalBehaviorReferencesVerdictV1(behavioral, verdict, `${source}.behavioral`);
146
+ }
147
+ if (behavioralMetrics !== undefined) {
148
+ if (behavioral === undefined) throw new Error(`${source}.behavioralMetrics: requires a behavioral verdict`);
149
+ if (behavioralMetrics.scenarioId !== readString(record, source, "taskId")) {
150
+ throw new Error(`${source}.behavioralMetrics.scenarioId: conflicts with result taskId`);
151
+ }
152
+ if (
153
+ behavioralMetrics.target.id !== readString(target, `${source}.target`, "id") ||
154
+ behavioralMetrics.target.model !== readNullableString(target, `${source}.target`, "model")
155
+ ) {
156
+ throw new Error(`${source}.behavioralMetrics.target: conflicts with result target`);
157
+ }
158
+ }
159
+ if (executionEnvelope !== undefined) {
160
+ if (behavioral === undefined) throw new Error(`${source}.executionEnvelope: requires a behavioral verdict`);
161
+ if (
162
+ executionEnvelope.target !== readString(target, `${source}.target`, "id") ||
163
+ executionEnvelope.corpus.id !== behavioral.corpus.id ||
164
+ executionEnvelope.corpus.version !== behavioral.corpus.version
165
+ ) {
166
+ throw new Error(`${source}.executionEnvelope: conflicts with result target or behavioral corpus`);
167
+ }
168
+ }
111
169
  return {
112
170
  assignmentId: readNullableString(record, source, "assignmentId"),
113
171
  terminalReceiptDigest: readNullableDigest(record, source, "terminalReceiptDigest"),
@@ -122,6 +180,10 @@ function parseResult(value: unknown, source: string): EvalArtifactV4["results"][
122
180
  failureClass: readNullableString(record, source, "failureClass"),
123
181
  metrics: asRecord(record.metrics, `${source}.metrics`) as Record<string, number | string | boolean | null>,
124
182
  artifacts: asRecord(record.artifacts, `${source}.artifacts`) as Record<string, string | string[] | null>,
183
+ ...(verdict === undefined ? {} : { verdict }),
184
+ ...(behavioral === undefined ? {} : { behavioral }),
185
+ ...(behavioralMetrics === undefined ? {} : { behavioralMetrics }),
186
+ ...(executionEnvelope === undefined ? {} : { executionEnvelope }),
125
187
  };
126
188
  }
127
189