@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,6 +21,7 @@ import type { FleetCommandRegistry } from "../agents/fleet-commands.js";
21
21
  import type { FleetContract } from "../agents/fleet-contract.js";
22
22
  import { resultContractAuthorship } from "../agents/result-contract.js";
23
23
  import type { AgentSpec } from "../agents/spec.js";
24
+ import { claimAssignmentVerdict, recordAssignmentAttempt, settleStoredAssignment } from "./assignment-store.js";
24
25
  import { runCodeStep } from "./code-step.js";
25
26
  import { codeStepDir, writeCodeStepRecord } from "./code-step-store.js";
26
27
  import type { DispatchContract, DispatchRequest } from "./contract.js";
@@ -45,6 +46,7 @@ import {
45
46
  import { verifyReceiptIntegrity } from "./receipt-integrity.js";
46
47
  import { type FleetRunRecord, writeFleetRun } from "./state.js";
47
48
  import type { RunReceipt } from "./types.js";
49
+ import type { WriteBoundaryAttribution } from "./write-boundary.js";
48
50
  import { createWriteBoundaryEnforcer } from "./write-boundary-enforcer.js";
49
51
 
50
52
  /** Owner id for a plan with no model runs; it holds nothing to release. */
@@ -247,13 +249,66 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
247
249
  dynamicPlans: [],
248
250
  };
249
251
  await writeFleetRun(fleetRunRecord);
252
+ // Every step of this run dispatches under `fleetRootId` as its lineage root,
253
+ // so every agent step also settles the assignment row of that id. One step is
254
+ // not the run, and letting the last one to finish write the row reported a
255
+ // green step of a run that aborted at step 2 of 7 as `succeeded`. The run
256
+ // claims the verdict here and answers for itself at the end.
257
+ await claimAssignmentVerdict(fleetRootId, "fleet");
258
+ /** File one step's terminal run id, whichever kind produced it. */
259
+ const fileStepAttempt = (terminalRunId: string): Promise<unknown> =>
260
+ recordAssignmentAttempt(fleetRootId, terminalRunId);
261
+ /**
262
+ * The run's own word on its ledger row. A settle that cannot be written
263
+ * leaves the row `running`, which startup reconciliation resolves to failed,
264
+ * so a lost write never becomes a false success.
265
+ */
266
+ const settleFleetAssignment = (cleanRun: boolean): Promise<unknown> =>
267
+ settleStoredAssignment(
268
+ fleetRootId,
269
+ fleetRunRecord.steps.at(-1)?.result.terminalRunId ?? fleetRootId,
270
+ cleanRun ? "succeeded" : "failed",
271
+ "fleet",
272
+ ).catch(() => undefined);
250
273
  const boundaryEnforcer = createWriteBoundaryEnforcer({
251
274
  root: workspaceRoot,
252
275
  rootId: fleetRootId,
253
276
  boundaryFor: (stepId) => livePlan.steps.find((step) => step.id === stepId)?.writes,
277
+ recordedWritesFor: (stepId) => {
278
+ const step = livePlan.steps.find((entry) => entry.id === stepId);
279
+ // A code step runs a registered command in a subprocess. Nothing
280
+ // enumerates what that command wrote, so the step contributes no record
281
+ // and its window falls back to blaming the whole diff.
282
+ if (step === undefined) return null;
283
+ if (step.kind !== "agent") {
284
+ return {
285
+ recorded: [],
286
+ complete: false,
287
+ downgrades: [
288
+ {
289
+ reason: "code_step_untracked",
290
+ tool: null,
291
+ toolCallId: null,
292
+ runId: null,
293
+ stepId,
294
+ },
295
+ ],
296
+ } satisfies WriteBoundaryAttribution;
297
+ }
298
+ const receipt = receiptsByStep.get(stepId);
299
+ if (receipt === undefined) return null;
300
+ if (dispatch.observedRunWriteAttribution !== undefined) {
301
+ return dispatch.observedRunWriteAttribution(receipt.runId);
302
+ }
303
+ const recorded = dispatch.observedRunWrites?.(receipt.runId) ?? null;
304
+ return recorded === null ? null : { recorded, complete: true, downgrades: [] };
305
+ },
254
306
  onVerdict(verdict, path) {
255
- if (verdict.reason === null) return;
256
- notice(`write boundary ${verdict.window}: ${verdict.detail ?? verdict.reason} record=${path}`, "write-boundary");
307
+ // Concurrent changes are reported too. The window did not fail for them
308
+ // and nothing was touched, and an operator whose file changed under a
309
+ // running fleet should hear it from the fleet.
310
+ if (verdict.detail === null) return;
311
+ notice(`write boundary ${verdict.window}: ${verdict.detail} record=${path}`, "write-boundary");
257
312
  },
258
313
  });
259
314
  const receipts: RunReceipt[] = [];
@@ -285,6 +340,7 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
285
340
  costUsd: receiptsByStep.get(stepId)?.costUsd ?? 0,
286
341
  result: replayedResult,
287
342
  });
343
+ await fileStepAttempt(replayedResult.terminalRunId);
288
344
  }
289
345
  const planStep = (stepId: string) => livePlan.steps.find((entry) => entry.id === stepId);
290
346
 
@@ -322,6 +378,9 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
322
378
  try {
323
379
  result = await executePlan(livePlan, {
324
380
  preflight(step) {
381
+ // The fleet write boundary is deliberately enforced after the step. The enforcer in
382
+ // write-boundary-enforcer.ts snapshots and verifies the step window. Pre-emptive
383
+ // confinement for declared commands would require a command sandbox, which does not exist.
325
384
  const request: DispatchRequest = {
326
385
  agentId: step.agentId,
327
386
  executionRole: step.executionRole,
@@ -372,6 +431,9 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
372
431
  if (step.planParentId !== undefined && parentReceipt === undefined) {
373
432
  throw new Error(`fleet dynamic step '${step.id}' has no terminal plan-step receipt`);
374
433
  }
434
+ // The fleet write boundary is deliberately enforced after the step. The enforcer in
435
+ // write-boundary-enforcer.ts snapshots and verifies the step window. Pre-emptive
436
+ // confinement for declared commands would require a command sandbox, which does not exist.
375
437
  const request: DispatchRequest = {
376
438
  agentId: step.agentId,
377
439
  executionRole: step.executionRole,
@@ -662,7 +724,7 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
662
724
  cancel: (assignmentId) => dispatch.abort(assignmentId),
663
725
  release: (ownerId) => {
664
726
  if (ownerId === NO_WORKER_RESERVATION) return;
665
- dispatch.reservations?.rollbackUnconsumed(ownerId);
727
+ dispatch.reservations?.release(ownerId);
666
728
  },
667
729
  releaseUnconsumed: (ownerId) => {
668
730
  if (ownerId === NO_WORKER_RESERVATION) return;
@@ -671,6 +733,11 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
671
733
  onStepSettled: async (step, stepResult) => {
672
734
  fleetRunRecord.steps.push({ stepId: step.id, result: stepResult });
673
735
  await writeFleetRun(fleetRunRecord);
736
+ // The scheduler settles both kinds of step here, so this is where a
737
+ // deterministic step enters the assignment: a code step's `code-*` run
738
+ // id reads exactly like an agent receipt id, and its evidence sits under
739
+ // `code-steps/<fleetRootId>/`.
740
+ await fileStepAttempt(stepResult.terminalRunId);
674
741
  },
675
742
  spliceAfter(currentPlan, step, stepResult) {
676
743
  if (stepResult.delegationPlan === undefined) return null;
@@ -712,6 +779,9 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
712
779
  } catch (error) {
713
780
  fleetRunRecord.endedAt = new Date().toISOString();
714
781
  await writeFleetRun(fleetRunRecord);
782
+ // A run the scheduler could not carry to the end never reached a verdict,
783
+ // and a fleet that threw is not a fleet that worked.
784
+ await settleFleetAssignment(false);
715
785
  throw error;
716
786
  }
717
787
  fleetRunRecord.endedAt = new Date().toISOString();
@@ -723,6 +793,10 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
723
793
  const required = livePlan.steps.filter((step) => step.loop === undefined && !result.unneeded.includes(step.id));
724
794
  const failedLoops = result.loops.filter((loop) => !loop.resolved);
725
795
  const succeededStepCount = required.filter((step) => result.results.get(step.id)?.succeeded === true).length;
796
+ // A step a stop policy left unrun is counted but never succeeded, so a run
797
+ // that halted early can never reach the required count.
798
+ const cleanRun = failedLoops.length === 0 && result.skipped.length === 0 && succeededStepCount === required.length;
799
+ await settleFleetAssignment(cleanRun);
726
800
  return {
727
801
  rootId: fleetRootId,
728
802
  planHash: input.plan.hash,
@@ -732,6 +806,6 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
732
806
  requiredStepCount: required.length,
733
807
  succeededStepCount,
734
808
  resolvedLoopCount: result.loops.length - failedLoops.length,
735
- cleanRun: failedLoops.length === 0 && result.skipped.length === 0 && succeededStepCount === required.length,
809
+ cleanRun,
736
810
  };
737
811
  }
@@ -1,5 +1,7 @@
1
1
  /** Bounded role prompts shared by dispatch topology construction and ACP admission. */
2
2
 
3
+ import { COUNCIL_BALLOT_SHAPE, COUNCIL_BALLOT_VERDICT_MAX_BYTES } from "../agents/result-contract.js";
4
+
3
5
  export const REVIEWER_GATE_PROMPT = [
4
6
  "You are a strict, read-only reviewer of one implementation run.",
5
7
  "Inspect the supplied result and repository state with read-only tools.",
@@ -26,6 +28,42 @@ export const COUNCIL_JUDGE_PROMPT = [
26
28
  'End with a JSON object only: {"verdict":"...","text":"the supported synthesis"}.',
27
29
  ].join("\n");
28
30
 
31
+ /**
32
+ * What a council member is told when the synthesis is `vote`.
33
+ *
34
+ * The tally is a strict majority over the members' `verdict` fields, and until
35
+ * this directive existed nothing ever asked a member for one (#230). It rides
36
+ * on the member's task rather than on a system prompt, so the seated recipe
37
+ * keeps its own persona: a council's point is running *that* agent across
38
+ * several targets, and replacing its body with a ballot prompt would run a
39
+ * different agent under the same name. The matching `council-ballot` contract
40
+ * travels with it as a `resultContractOverride`, so the postcondition the run
41
+ * seals is the one this text asks for, and a member that answers in its
42
+ * recipe's shape instead gets the ordinary bounded repair rounds rather than a
43
+ * silent absence from the tally.
44
+ *
45
+ * The precedence line is not decoration. A recipe body states its own result
46
+ * shape (`researcher` asks for findings), so without it the member holds two
47
+ * contradictory instructions and small local models resolve the conflict by
48
+ * burying a verdict inside the recipe's shape, which is exactly what the 0.3.7
49
+ * release test observed and read as correct behavior.
50
+ */
51
+ export const COUNCIL_VOTE_MEMBER_DIRECTIVE = [
52
+ "This council round is a vote. Do the work the task asks for, then cast one ballot on it.",
53
+ `The verdict is a short comparable token the other members can match exactly, such as "yes", "no", "keep", or the name of an option you were given. It must be a single line of at most ${COUNCIL_BALLOT_VERDICT_MAX_BYTES} bytes and is lower-cased before the tally. Anything longer is reasoning: put that in text, along with the evidence behind it.`,
54
+ "Your recipe's own result shape does not apply to this run. The council reads the ballot and nothing else.",
55
+ `End with a JSON object only: ${COUNCIL_BALLOT_SHAPE}`,
56
+ ].join("\n");
57
+
58
+ /**
59
+ * The task a council member is given under `synthesis: "vote"`. Plan admission
60
+ * composes it here once, then the runner reuses the task pinned by that plan so
61
+ * the approval artifact shows the exact ask the member receives.
62
+ */
63
+ export function renderCouncilVoteMemberTask(originalTask: string): string {
64
+ return [originalTask, COUNCIL_VOTE_MEMBER_DIRECTIVE].join("\n\n");
65
+ }
66
+
29
67
  /**
30
68
  * Posture a compete candidate runs under. Candidates otherwise differ only by
31
69
  * worktree, and identical agents produce correlated output, so each candidate
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Pure heartbeat watchdog classifier (Phase 6 slice 3).
3
3
  *
4
- * Maps a last-seen heartbeat timestamp to one of three states. The dispatch
5
- * domain drives this on each scheduler tick; the orchestrator uses `stale` to
6
- * surface a warning in the UI and `dead` to reap a subprocess whose event
7
- * stream has gone quiet for too long.
4
+ * Maps a last-seen monotonic stamp to one of three states. The dispatch domain
5
+ * drives this on each scheduler tick; the orchestrator uses `stale` to surface
6
+ * a warning in the UI and `dead` to reap a subprocess whose event stream has
7
+ * gone quiet for too long.
8
8
  *
9
- * No I/O, no clocks of its own. Callers pass `now` to keep the function pure
10
- * and easy to test.
9
+ * No I/O, no clocks of its own. Callers pass both monotonic values to keep the
10
+ * function pure and easy to test.
11
11
  */
12
12
 
13
13
  export interface HeartbeatSpec {
@@ -17,10 +17,34 @@ export interface HeartbeatSpec {
17
17
 
18
18
  export type HeartbeatStatus = "alive" | "stale" | "dead";
19
19
 
20
+ /**
21
+ * Mutable heartbeat facts shared by a transport and the dispatch watchdog.
22
+ * `current` is an absolute wall-clock instant for display and persistence.
23
+ * `monotonic` is from this process's monotonic clock and is the only value
24
+ * liveness code may compare with a later clock read.
25
+ *
26
+ * The monotonic field is optional only for injected legacy test handles. Real
27
+ * transports always provide it. Treating an old handle's `current` as its
28
+ * monotonic stamp preserves the structural test seam without reintroducing a
29
+ * subtraction between two wall-clock reads in the product path.
30
+ */
31
+ export interface HeartbeatStamp {
32
+ current: number;
33
+ monotonic?: number;
34
+ }
35
+
20
36
  export const DEFAULT_HEARTBEAT_SPEC: HeartbeatSpec = { windowMs: 5000, graceMs: 10000 };
21
37
 
22
- export function classifyHeartbeat(heartbeatAt: number, now: number, spec: HeartbeatSpec): HeartbeatStatus {
23
- const age = now - heartbeatAt;
38
+ export function heartbeatMonotonicAt(heartbeat: HeartbeatStamp): number {
39
+ return heartbeat.monotonic ?? heartbeat.current;
40
+ }
41
+
42
+ export function classifyHeartbeat(
43
+ heartbeatMonotonicAt: number,
44
+ monotonicNow: number,
45
+ spec: HeartbeatSpec,
46
+ ): HeartbeatStatus {
47
+ const age = monotonicNow - heartbeatMonotonicAt;
24
48
  if (age <= spec.windowMs) return "alive";
25
49
  if (age <= spec.windowMs + spec.graceMs) return "stale";
26
50
  return "dead";
@@ -21,7 +21,7 @@ export type {
21
21
  AttemptRef,
22
22
  DispatchAssignment,
23
23
  } from "./assignment.js";
24
- export type { DurableAssignmentRecord } from "./assignment-store.js";
24
+ export type { AssignmentVerdictOwner, DurableAssignmentRecord } from "./assignment-store.js";
25
25
  export type { CodeStepOutcome, CodeStepRecord, CodeStepRunInput } from "./code-step.js";
26
26
  export {
27
27
  canonicalCodeReport,
@@ -160,11 +160,16 @@ export {
160
160
  } from "./validation.js";
161
161
  export type {
162
162
  WorkspaceSnapshot,
163
+ WriteBoundaryAttribution,
164
+ WriteBoundaryAttributionDowngrade,
165
+ WriteBoundaryAttributionDowngradeReason,
163
166
  WriteBoundaryStatus,
164
167
  WriteBoundaryVerdict,
165
168
  } from "./write-boundary.js";
166
169
  export {
170
+ assertWriteBoundaryVisibleToGit,
167
171
  captureWorkspaceSnapshot,
172
+ describeWriteBoundaryAttributionDowngrade,
168
173
  diffWorkspace,
169
174
  enforceWriteBoundary,
170
175
  WRITE_BOUNDARY_VIOLATION_REASON,
@@ -0,0 +1,40 @@
1
+ import type { DispatchIntent } from "./intent.js";
2
+
3
+ /** Render intent fields that describe required results rather than observed facts. */
4
+ export function renderDispatchIntentRequirements(intent: DispatchIntent | undefined): string | null {
5
+ if (intent === undefined || (intent.expectedOutputs.length === 0 && intent.verification.length === 0)) return null;
6
+ const lines = [
7
+ "# Declared Result Requirements",
8
+ "These entries are requirements to satisfy and verify. They are not evidence that an output exists or a check passed.",
9
+ ];
10
+ if (intent.expectedOutputs.length > 0) {
11
+ lines.push("", "Expected outputs:", ...intent.expectedOutputs.map((output) => `- ${output}`));
12
+ }
13
+ if (intent.verification.length > 0) {
14
+ lines.push(
15
+ "",
16
+ "Verification requirements:",
17
+ ...intent.verification.map(
18
+ (entry) => `- ${entry.check} must pass within its declared timeout of ${entry.timeoutMs}ms`,
19
+ ),
20
+ );
21
+ }
22
+ return lines.join("\n");
23
+ }
24
+
25
+ /** Build the reviewer assignment from the same requirements the builder received. */
26
+ export function renderDispatchReviewerTask(
27
+ originalTask: string,
28
+ builderRunId: string,
29
+ cycle: number,
30
+ intent: DispatchIntent | undefined,
31
+ ): string {
32
+ const requirements = renderDispatchIntentRequirements(intent);
33
+ return [
34
+ `Review the work of builder run ${builderRunId} (review cycle ${cycle}).`,
35
+ "The builder's final answer is provided as input data; verify it against the workspace, do not trust it blindly.",
36
+ "Original task the builder was given:",
37
+ originalTask,
38
+ ...(requirements === null ? [] : [requirements]),
39
+ ].join("\n\n");
40
+ }
@@ -1,6 +1,8 @@
1
1
  import path from "node:path";
2
+ import { normalizePathBoundaryEntry, PATH_BOUNDARY_MAX_ENTRIES, PathBoundaryError } from "../../core/path-boundary.js";
3
+ import { type DispatchPathProvenanceEntry, declaredIntentPathProvenance } from "./path-scope.js";
2
4
 
3
- export const DISPATCH_INTENT_PATH_LIST_CAP = 32;
5
+ export const DISPATCH_INTENT_PATH_LIST_CAP = PATH_BOUNDARY_MAX_ENTRIES;
4
6
  export const DISPATCH_INTENT_PATH_ENTRY_BYTES_CAP = 512;
5
7
  export const DISPATCH_INTENT_VERIFICATION_CAP = 8;
6
8
  export const DISPATCH_INTENT_TIMEOUT_MIN_MS = 1_000;
@@ -11,10 +13,12 @@ export interface DispatchIntentVerification {
11
13
  }
12
14
 
13
15
  export interface DispatchIntent {
14
- version: 1;
16
+ version: 2;
15
17
  readRoots: string[];
16
18
  writeRoots: string[];
17
19
  relevantPaths: string[];
20
+ /** Integrity-sealed source and confidence for every policy-bearing path. */
21
+ pathProvenance: DispatchPathProvenanceEntry[];
18
22
  expectedOutputs: string[];
19
23
  verification: DispatchIntentVerification[];
20
24
  }
@@ -42,7 +46,7 @@ function fail(reason: string, message: string): DispatchIntentNormalizationResul
42
46
  return { ok: false, reason, message };
43
47
  }
44
48
 
45
- function normalizePathEntry(value: unknown, field: string, index: number): string | DispatchIntentNormalizationResult {
49
+ function checkedPathText(value: unknown, field: string, index: number): string | DispatchIntentNormalizationResult {
46
50
  if (typeof value !== "string" || value.trim().length === 0) {
47
51
  return fail("intent_path_malformed", `${field}[${index}] must be a non-empty repository-relative POSIX path`);
48
52
  }
@@ -53,6 +57,42 @@ function normalizePathEntry(value: unknown, field: string, index: number): strin
53
57
  `${field}[${index}] exceeds the ${DISPATCH_INTENT_PATH_ENTRY_BYTES_CAP}-byte cap`,
54
58
  );
55
59
  }
60
+ return trimmed;
61
+ }
62
+
63
+ function normalizeScopePathEntry(
64
+ value: unknown,
65
+ field: string,
66
+ index: number,
67
+ ): string | DispatchIntentNormalizationResult {
68
+ const checked = checkedPathText(value, field, index);
69
+ if (typeof checked !== "string") return checked;
70
+ try {
71
+ return normalizePathBoundaryEntry(checked);
72
+ } catch (error) {
73
+ if (error instanceof PathBoundaryError) {
74
+ if (error.code === "absolute") {
75
+ return fail("intent_path_absolute", `${field}[${index}] must be a repository-relative POSIX path`);
76
+ }
77
+ if (error.code === "parent") {
78
+ return fail("intent_path_escapes_root", `${field}[${index}] escapes the repository root`);
79
+ }
80
+ }
81
+ return fail(
82
+ "intent_path_malformed",
83
+ `${field}[${index}] does not follow the repository path boundary grammar: ${error instanceof Error ? error.message : String(error)}`,
84
+ );
85
+ }
86
+ }
87
+
88
+ function normalizeExpectedOutputEntry(
89
+ value: unknown,
90
+ field: string,
91
+ index: number,
92
+ ): string | DispatchIntentNormalizationResult {
93
+ const checked = checkedPathText(value, field, index);
94
+ if (typeof checked !== "string") return checked;
95
+ const trimmed = checked;
56
96
  if (trimmed.includes("\\") || path.posix.isAbsolute(trimmed) || path.win32.isAbsolute(trimmed)) {
57
97
  return fail("intent_path_absolute", `${field}[${index}] must be a repository-relative POSIX path`);
58
98
  }
@@ -66,7 +106,15 @@ function normalizePathEntry(value: unknown, field: string, index: number): strin
66
106
  return normalized;
67
107
  }
68
108
 
69
- function normalizePathList(value: unknown, field: string): string[] | DispatchIntentNormalizationResult {
109
+ function normalizePathList(
110
+ value: unknown,
111
+ field: string,
112
+ normalizeEntry: (
113
+ value: unknown,
114
+ field: string,
115
+ index: number,
116
+ ) => string | DispatchIntentNormalizationResult = normalizeScopePathEntry,
117
+ ): string[] | DispatchIntentNormalizationResult {
70
118
  if (value === undefined) return [];
71
119
  if (!Array.isArray(value)) return fail("intent_path_malformed", `${field} must be an array`);
72
120
  if (value.length > DISPATCH_INTENT_PATH_LIST_CAP) {
@@ -74,7 +122,7 @@ function normalizePathList(value: unknown, field: string): string[] | DispatchIn
74
122
  }
75
123
  const normalized: string[] = [];
76
124
  for (const [index, entry] of value.entries()) {
77
- const result = normalizePathEntry(entry, field, index);
125
+ const result = normalizeEntry(entry, field, index);
78
126
  if (typeof result !== "string") return result;
79
127
  normalized.push(result);
80
128
  }
@@ -82,10 +130,26 @@ function normalizePathList(value: unknown, field: string): string[] | DispatchIn
82
130
  }
83
131
 
84
132
  export function isDispatchIntent(value: unknown): value is DispatchIntent {
85
- if (!isRecord(value) || value.version !== 1) return false;
133
+ if (!isRecord(value) || value.version !== 2) return false;
86
134
  for (const field of ["readRoots", "writeRoots", "relevantPaths", "expectedOutputs"] as const) {
87
135
  if (!Array.isArray(value[field]) || !value[field].every((entry) => typeof entry === "string")) return false;
88
136
  }
137
+ const readRoots = value.readRoots as string[];
138
+ const writeRoots = value.writeRoots as string[];
139
+ const relevantPaths = value.relevantPaths as string[];
140
+ if (
141
+ !Array.isArray(value.pathProvenance) ||
142
+ JSON.stringify(value.pathProvenance) !==
143
+ JSON.stringify(
144
+ declaredIntentPathProvenance({
145
+ readRoots,
146
+ writeRoots,
147
+ relevantPaths,
148
+ }),
149
+ )
150
+ ) {
151
+ return false;
152
+ }
89
153
  return (
90
154
  Array.isArray(value.verification) &&
91
155
  value.verification.every(
@@ -115,7 +179,11 @@ export function normalizeDispatchIntent(
115
179
  if (!Array.isArray(writeRoots)) return writeRoots;
116
180
  const relevantPaths = normalizePathList(raw.relevant_paths, "intent.relevant_paths");
117
181
  if (!Array.isArray(relevantPaths)) return relevantPaths;
118
- const expectedOutputs = normalizePathList(raw.expected_outputs, "intent.expected_outputs");
182
+ const expectedOutputs = normalizePathList(
183
+ raw.expected_outputs,
184
+ "intent.expected_outputs",
185
+ normalizeExpectedOutputEntry,
186
+ );
119
187
  if (!Array.isArray(expectedOutputs)) return expectedOutputs;
120
188
  const rawVerification = raw.verification ?? [];
121
189
  if (!Array.isArray(rawVerification)) {
@@ -154,6 +222,14 @@ export function normalizeDispatchIntent(
154
222
  }
155
223
  return {
156
224
  ok: true,
157
- intent: { version: 1, readRoots, writeRoots, relevantPaths, expectedOutputs, verification },
225
+ intent: {
226
+ version: 2,
227
+ readRoots,
228
+ writeRoots,
229
+ relevantPaths,
230
+ pathProvenance: declaredIntentPathProvenance({ readRoots, writeRoots, relevantPaths }),
231
+ expectedOutputs,
232
+ verification,
233
+ },
158
234
  };
159
235
  }
@@ -172,6 +172,11 @@ function sealedEnvelopeFor(runId: string): RunEnvelope | null {
172
172
  * whole orchestrator downtime, which is not a measurement of anything. The
173
173
  * last heartbeat is the honest bound; a row that never heartbeat has only its
174
174
  * own start, which reads as a zero-length run rather than an invented one.
175
+ *
176
+ * A restart has no shared monotonic origin with the process that owned the
177
+ * worker, so recovery never estimates heartbeat age. It first adjudicates the
178
+ * host-scoped pid and uses the persisted wall-clock heartbeat only as this
179
+ * display/evidence bound after that process is known to be gone.
175
180
  */
176
181
  function closeAbandonedRows(ledger: Ledger): { closed: number; sealed: number } {
177
182
  let closed = 0;