@iowarp/clio-coder 0.3.4 → 0.3.7

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 (376) hide show
  1. package/CHANGELOG.md +65 -2
  2. package/CONTRIBUTING.md +6 -6
  3. package/README.md +16 -5
  4. package/dist/{acp-S5R4RR5B.js → acp-SK4MD6MM.js} +11 -11
  5. package/dist/{agents-P6DMMVZY.js → agents-2FN2K6ME.js} +33 -26
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-2XCZLPKS.js → auth-QIYZWM5I.js} +15 -15
  8. package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
  9. package/dist/{chunk-VAWWTKDP.js → chunk-3HAPLH5M.js} +11 -11
  10. package/dist/{chunk-YCWGATWI.js → chunk-465YSENW.js} +3 -3
  11. package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
  12. package/dist/{chunk-UZHIZC5S.js → chunk-4DWFMQDR.js} +61 -76
  13. package/dist/{chunk-ZWMF7253.js → chunk-5C3AQNDW.js} +328 -9
  14. package/dist/{chunk-22NAGB7X.js → chunk-5C77SEEY.js} +5 -94
  15. package/dist/{chunk-WPQLXFOZ.js → chunk-5FR74PWO.js} +3 -2
  16. package/dist/{chunk-35MKKU5R.js → chunk-5UJ6ECTS.js} +18 -10
  17. package/dist/{chunk-BRXQQJFP.js → chunk-6M7VS3J3.js} +571 -50
  18. package/dist/{chunk-QQK64KLB.js → chunk-6TUKSZVF.js} +141 -23
  19. package/dist/{chunk-N4CZJQRK.js → chunk-AB4XIIVB.js} +8 -6
  20. package/dist/{chunk-KRPY7NTG.js → chunk-BMWK7ZIZ.js} +14 -20
  21. package/dist/{chunk-4BPJXDWC.js → chunk-C4JBQ5SR.js} +30 -14
  22. package/dist/{chunk-ZYKPLLNQ.js → chunk-CEYBNUGC.js} +821 -83
  23. package/dist/{chunk-VEZEGCGW.js → chunk-D4MDIG46.js} +20 -18
  24. package/dist/chunk-DJNLUABN.js +843 -0
  25. package/dist/{chunk-BP4OYD6A.js → chunk-DMD2AGVS.js} +21 -2
  26. package/dist/{chunk-KOHPCX4K.js → chunk-DOOEX22V.js} +2 -2
  27. package/dist/chunk-DQA7QLMD.js +123 -0
  28. package/dist/chunk-DR52UMZW.js +21 -0
  29. package/dist/{chunk-3HZ5RWN2.js → chunk-EBEFWSGL.js} +9 -7
  30. package/dist/{chunk-EDRHSCIE.js → chunk-EELBMBT6.js} +128 -13
  31. package/dist/{chunk-HV5X7OR2.js → chunk-EOOQZZDE.js} +16 -14
  32. package/dist/{chunk-WR67VIZY.js → chunk-FOT2FX5J.js} +63 -5
  33. package/dist/{chunk-BPGS2WCQ.js → chunk-GEYXPTRF.js} +2 -1
  34. package/dist/{chunk-FYYLNIL5.js → chunk-GH5622CP.js} +2 -2
  35. package/dist/{chunk-BEY543CS.js → chunk-GOXNB3AO.js} +5 -2
  36. package/dist/chunk-GWS3VEIW.js +195 -0
  37. package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
  38. package/dist/chunk-HWUFFB6L.js +83 -0
  39. package/dist/{chunk-X6COSD2O.js → chunk-J7PIKKWC.js} +8 -436
  40. package/dist/{chunk-NILBFAPG.js → chunk-JNXPYBB4.js} +2 -2
  41. package/dist/{chunk-4VP4KH3K.js → chunk-JRIO5UD2.js} +4 -4
  42. package/dist/{chunk-K6WL7QZT.js → chunk-JTSEDYVQ.js} +7 -7
  43. package/dist/{chunk-QKMUKYO7.js → chunk-KCMKRQX4.js} +236 -84
  44. package/dist/chunk-KZ2H5X4G.js +1026 -0
  45. package/dist/{chunk-A2GZF7DC.js → chunk-LADCF22A.js} +13 -13
  46. package/dist/chunk-LCGCVYZ4.js +57 -0
  47. package/dist/chunk-M4AKACEO.js +382 -0
  48. package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
  49. package/dist/{chunk-4JUF2NNX.js → chunk-MXI6J5JF.js} +7 -7
  50. package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
  51. package/dist/{chunk-TTNYS3EA.js → chunk-OB5HIGJY.js} +1 -1
  52. package/dist/{chunk-7RXG6QRZ.js → chunk-OBMAI2DP.js} +61 -840
  53. package/dist/{chunk-5M54SPOL.js → chunk-ODFEOB4F.js} +161 -5
  54. package/dist/chunk-PD3MESLB.js +242 -0
  55. package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
  56. package/dist/{chunk-VMNQ6OZA.js → chunk-QCTRSGHQ.js} +963 -786
  57. package/dist/chunk-RVG5JXAL.js +41 -0
  58. package/dist/{chunk-RD5U66HV.js → chunk-SROCI7ZU.js} +7 -7
  59. package/dist/{chunk-MFFY33HR.js → chunk-THKY7CD7.js} +466 -205
  60. package/dist/{chunk-34475P3I.js → chunk-TSHXZTOQ.js} +5 -4
  61. package/dist/{chunk-PCZJO5TI.js → chunk-UFQ3F4FW.js} +13 -178
  62. package/dist/{chunk-AD2SYQYC.js → chunk-UHXRNZ2J.js} +121 -3
  63. package/dist/chunk-UND3GU2L.js +103 -0
  64. package/dist/{chunk-QQL5RT5M.js → chunk-UUANF5CR.js} +2323 -2114
  65. package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
  66. package/dist/chunk-UVDSQ6LW.js +472 -0
  67. package/dist/{chunk-QWU7ZBO7.js → chunk-VQNODYQ4.js} +215 -56
  68. package/dist/chunk-VREKEFLL.js +37 -0
  69. package/dist/{chunk-2TZWSW76.js → chunk-WHGPSPT5.js} +2 -2
  70. package/dist/{chunk-TW3WDMVS.js → chunk-WHJYKASB.js} +2 -2
  71. package/dist/{chunk-MEQ45TQ4.js → chunk-WJHBC77E.js} +21 -7
  72. package/dist/{chunk-HXG4IURW.js → chunk-X2KV5FXT.js} +2 -2
  73. package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
  74. package/dist/{chunk-2LZI5CAG.js → chunk-XEGB6BCN.js} +228 -36
  75. package/dist/{chunk-E25LMLRW.js → chunk-YD734TPH.js} +2 -2
  76. package/dist/{verifiers-4UUM6TEE.js → chunk-YTYFXUI3.js} +121 -372
  77. package/dist/{chunk-3JLKSKD7.js → chunk-ZGH7FGS5.js} +17 -7
  78. package/dist/{chunk-VSNATDE6.js → chunk-ZZMN5OM4.js} +2 -2
  79. package/dist/cli/index.js +34 -32
  80. package/dist/{clio-J5JIOIDS.js → clio-WBVQEBKO.js} +7 -7
  81. package/dist/{code-nav-AXCXSBHX.js → code-nav-FGGFIE7L.js} +7 -7
  82. package/dist/codewiki/build-worker.js +4 -4
  83. package/dist/{components-KELWS457.js → components-F7OEATSO.js} +5 -5
  84. package/dist/{config-OEBMIN2U.js → config-TRBL3RCF.js} +48 -41
  85. package/dist/{configure-PUQOSIXQ.js → configure-OLCVPHNM.js} +17 -17
  86. package/dist/{context-URSXPBCK.js → context-MJIJ6GOX.js} +12 -12
  87. package/dist/{context-EKDCKUUZ.js → context-WFPKQSM6.js} +26 -9
  88. package/dist/{context-MGSE4Z2T.js → context-XEWE3MOJ.js} +44 -37
  89. package/dist/{context-clear-KDAJRNUK.js → context-clear-KNOS2JPB.js} +44 -37
  90. package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
  91. package/dist/{context-working-set-SBKMPPI2.js → context-working-set-EUXAZI6N.js} +14 -13
  92. package/dist/{dispatch-runner-MSWN72NK.js → dispatch-runner-B7MTOVKL.js} +321 -60
  93. package/dist/{docs-2C2LTVT2.js → docs-FLJTIDSE.js} +5 -5
  94. package/dist/{doctor-7BSE27PJ.js → doctor-RN4YKO2X.js} +15 -15
  95. package/dist/{eval-IZGDOO4H.js → eval-RUBJVSNQ.js} +52 -236
  96. package/dist/{evidence-SR7WXB5B.js → evidence-JZNBUOQZ.js} +39 -33
  97. package/dist/{evolve-K7VE2CBX.js → evolve-FJVC4KKI.js} +39 -33
  98. package/dist/{extensions-QVDOHDGJ.js → extensions-IQL36S7K.js} +5 -5
  99. package/dist/{fleet-7XMJNQNF.js → fleet-BDKYJFCP.js} +243 -370
  100. package/dist/fleet-commands-ZFIWZSB3.js +70 -0
  101. package/dist/fleet-graph-Y6HPXIVF.js +125 -0
  102. package/dist/fleet-new-RDVJLHHH.js +48 -0
  103. package/dist/{fleet-preflight-AQNAH644.js → fleet-preflight-BHSNPBMH.js} +2 -2
  104. package/dist/fleet-validate-BIYREGIK.js +79 -0
  105. package/dist/{init-JGNPAYXT.js → init-LQUB5COQ.js} +57 -48
  106. package/dist/library-NJAHIGG4.js +217 -0
  107. package/dist/memory-OG6HOYKM.js +472 -0
  108. package/dist/{models-ZMMLFJNN.js → models-5ZG5XY7J.js} +23 -22
  109. package/dist/{monitor-2F3T5KHP.js → monitor-TJ7AMTGB.js} +69 -35
  110. package/dist/{orchestrator-ORHT43JB.js → orchestrator-WZYB54DM.js} +4868 -1189
  111. package/dist/{paths-UXLN5YYZ.js → paths-XUC7GS6E.js} +5 -5
  112. package/dist/{reset-NXGTYNUO.js → reset-PXQT45IY.js} +8 -8
  113. package/dist/{run-RF4WJGMT.js → run-FQ74YF62.js} +82 -62
  114. package/dist/{share-UT3W6E4M.js → share-FW7SVCL3.js} +34 -10
  115. package/dist/{skills-PSACKC5Q.js → skills-7E7IRB3R.js} +25 -9
  116. package/dist/{skills-eval-WJSI55RZ.js → skills-eval-LI75W6OK.js} +43 -35
  117. package/dist/{targets-PIIRAOYS.js → targets-4CIFKCTW.js} +27 -24
  118. package/dist/{terminal-lease-ULWXWNVY.js → terminal-lease-WUZY7ZV5.js} +5 -4
  119. package/dist/{uninstall-FZCQCDKC.js → uninstall-7FV7IP4E.js} +5 -5
  120. package/dist/{upgrade-346TZ6AV.js → upgrade-K2HVIVMQ.js} +21 -20
  121. package/dist/{usage-6KKXR32N.js → usage-GTZELZQX.js} +159 -59
  122. package/dist/verifiers-RLAHT27O.js +336 -0
  123. package/dist/{verify-X5HDROLA.js → verify-BX3BRKH5.js} +7 -6
  124. package/dist/{wiki-generate-7STOCIFZ.js → wiki-generate-ASIFASCN.js} +58 -48
  125. package/dist/worker/entry.js +98 -84
  126. package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
  127. package/docs/README.md +4 -3
  128. package/docs/acp.md +1 -1
  129. package/docs/alcf-provider.md +1 -1
  130. package/docs/architecture.md +2 -2
  131. package/docs/artifact-placement.md +1 -2
  132. package/docs/artifact-versions.md +10 -6
  133. package/docs/built-in-agents.md +26 -2
  134. package/docs/capacity-and-scheduling.md +1 -1
  135. package/docs/commands-and-modes.md +90 -8
  136. package/docs/configuration-and-targets.md +90 -2
  137. package/docs/context-engine.md +4 -2
  138. package/docs/context-working-set.md +4 -4
  139. package/docs/development-pipeline.md +1 -1
  140. package/docs/dispatch-architecture-rationale.md +1 -1
  141. package/docs/documentation-coverage.md +4 -4
  142. package/docs/documentation-guide.md +4 -4
  143. package/docs/eval-runner.md +1 -1
  144. package/docs/evals-internal.md +4 -45
  145. package/docs/evidence-and-memory.md +70 -10
  146. package/docs/evolution.md +1 -1
  147. package/docs/exit-codes-and-output.md +4 -1
  148. package/docs/extensions-and-sharing.md +6 -2
  149. package/docs/fleet-demo-runbook.md +2 -2
  150. package/docs/fleet-dispatch.md +224 -11
  151. package/docs/git-commit-provenance.md +2 -2
  152. package/docs/glossary.md +1 -1
  153. package/docs/installation-and-lifecycle.md +2 -2
  154. package/docs/middleware-and-components.md +20 -2
  155. package/docs/model-catalog.md +1 -1
  156. package/docs/observability.md +55 -8
  157. package/docs/proactive-memory.md +26 -16
  158. package/docs/prompt-envelope-and-tools.md +4 -2
  159. package/docs/provider-adapter-cookbook.md +1 -1
  160. package/docs/release-cut-checklist.md +83 -65
  161. package/docs/resource-library.md +59 -0
  162. package/docs/safety-model.md +29 -7
  163. package/docs/scientific-validation.md +3 -3
  164. package/docs/session-lifecycle.md +37 -1
  165. package/docs/skills-marketplace.md +16 -3
  166. package/docs/tool-usage.md +14 -7
  167. package/docs/trace-store.md +1 -1
  168. package/docs/troubleshooting.md +1 -1
  169. package/docs/tui-design.md +38 -4
  170. package/docs/worker-dispatch-mechanics.md +3 -3
  171. package/package.json +7 -4
  172. package/src/cli/agents.ts +2 -3
  173. package/src/cli/argv.ts +14 -1
  174. package/src/cli/fleet-commands.ts +37 -0
  175. package/src/cli/fleet-graph.ts +102 -0
  176. package/src/cli/fleet-new.ts +36 -0
  177. package/src/cli/fleet-preflight.ts +121 -0
  178. package/src/cli/fleet-validate.ts +30 -0
  179. package/src/cli/fleet.ts +188 -335
  180. package/src/cli/index.ts +4 -2
  181. package/src/cli/library.ts +190 -0
  182. package/src/cli/memory.ts +272 -10
  183. package/src/cli/modes/json-stream.ts +2 -2
  184. package/src/cli/modes/print.ts +12 -1
  185. package/src/cli/run.ts +22 -2
  186. package/src/cli/share.ts +13 -1
  187. package/src/cli/targets.ts +12 -3
  188. package/src/cli/usage.ts +160 -20
  189. package/src/core/bus-events.ts +7 -0
  190. package/src/core/commit-attribution.ts +4 -4
  191. package/src/core/config.ts +130 -0
  192. package/src/core/defaults.ts +81 -0
  193. package/src/core/response-model-id.ts +134 -0
  194. package/src/core/toml.ts +62 -0
  195. package/src/core/workspace-files.ts +0 -1
  196. package/src/domains/agents/builtins/architect.md +2 -1
  197. package/src/domains/agents/builtins/oracle.md +33 -0
  198. package/src/domains/agents/catalog.ts +18 -5
  199. package/src/domains/agents/fleet-contract.ts +278 -16
  200. package/src/domains/agents/index.ts +14 -0
  201. package/src/domains/agents/recipe.ts +54 -14
  202. package/src/domains/agents/result-contract.ts +242 -5
  203. package/src/domains/config/classify.ts +4 -0
  204. package/src/domains/context/bootstrap.ts +36 -27
  205. package/src/domains/context/project-metadata.ts +19 -63
  206. package/src/domains/context/prompt-context.ts +8 -0
  207. package/src/domains/context/working-set/policies/index.ts +3 -4
  208. package/src/domains/dispatch/active-route-planner.ts +14 -0
  209. package/src/domains/dispatch/backoff.ts +2 -1
  210. package/src/domains/dispatch/budget-envelope.ts +396 -0
  211. package/src/domains/dispatch/capability-match.ts +1 -0
  212. package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
  213. package/src/domains/dispatch/contract.ts +36 -0
  214. package/src/domains/dispatch/delegation-plan.ts +167 -0
  215. package/src/domains/dispatch/execution-plan.ts +76 -5
  216. package/src/domains/dispatch/execution-role.ts +3 -1
  217. package/src/domains/dispatch/execution-scheduler.ts +183 -67
  218. package/src/domains/dispatch/extension.ts +339 -36
  219. package/src/domains/dispatch/fleet-gate.ts +14 -0
  220. package/src/domains/dispatch/fleet-plan.ts +63 -3
  221. package/src/domains/dispatch/fleet-run.ts +737 -0
  222. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  223. package/src/domains/dispatch/host-verification.ts +178 -0
  224. package/src/domains/dispatch/index.ts +38 -0
  225. package/src/domains/dispatch/intent.ts +159 -0
  226. package/src/domains/dispatch/orphan-recovery.ts +1 -0
  227. package/src/domains/dispatch/receipt-integrity.ts +12 -4
  228. package/src/domains/dispatch/state.ts +37 -3
  229. package/src/domains/dispatch/types.ts +61 -9
  230. package/src/domains/dispatch/validation.ts +80 -6
  231. package/src/domains/dispatch/worker-spawn.ts +14 -3
  232. package/src/domains/eval/metrics/evidence.ts +0 -116
  233. package/src/domains/eval/metrics/invariants.ts +1 -1
  234. package/src/domains/eval/runners/clio-run.ts +1 -10
  235. package/src/domains/eval/runners/external-command.ts +2 -29
  236. package/src/domains/eval/schema/suite.ts +0 -7
  237. package/src/domains/eval/suites/run.ts +1 -7
  238. package/src/domains/evidence/trust-status.ts +10 -1
  239. package/src/domains/memory/index.ts +22 -0
  240. package/src/domains/memory/operations.ts +58 -1
  241. package/src/domains/memory/promotion.ts +281 -0
  242. package/src/domains/memory/prompt-section.ts +25 -5
  243. package/src/domains/memory/proposal.ts +51 -7
  244. package/src/domains/memory/task-bank.ts +3 -2
  245. package/src/domains/memory/task-memory-handoff.ts +181 -24
  246. package/src/domains/memory/task-memory-policy.ts +3 -1
  247. package/src/domains/memory/types.ts +37 -0
  248. package/src/domains/memory/validate.ts +178 -0
  249. package/src/domains/middleware/index.ts +15 -0
  250. package/src/domains/middleware/memory-intervention.ts +35 -25
  251. package/src/domains/middleware/runtime.ts +6 -0
  252. package/src/domains/middleware/skills-reminder.ts +19 -4
  253. package/src/domains/middleware/stalled-turn.ts +43 -1
  254. package/src/domains/middleware/types.ts +10 -0
  255. package/src/domains/middleware/watchdog.ts +281 -0
  256. package/src/domains/observability/contract.ts +9 -2
  257. package/src/domains/observability/cost.ts +31 -4
  258. package/src/domains/observability/extension.ts +2 -2
  259. package/src/domains/observability/index.ts +10 -0
  260. package/src/domains/observability/out-of-turn-usage.ts +223 -0
  261. package/src/domains/providers/index.ts +3 -0
  262. package/src/domains/providers/model-discovery.ts +9 -0
  263. package/src/domains/providers/runtime-resolution.ts +38 -1
  264. package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
  265. package/src/domains/providers/types/context-window-slots.ts +18 -0
  266. package/src/domains/providers/types/runtime-descriptor.ts +3 -1
  267. package/src/domains/resources/index.ts +20 -0
  268. package/src/domains/resources/library.ts +326 -0
  269. package/src/domains/resources/skills/marketplace.ts +37 -12
  270. package/src/domains/safety/call-target.ts +211 -14
  271. package/src/domains/safety/decision-presentation.ts +268 -0
  272. package/src/domains/safety/redaction.ts +73 -0
  273. package/src/domains/session/context-ledger.ts +10 -1
  274. package/src/domains/session/decision-board.ts +4 -0
  275. package/src/domains/session/entries.ts +3 -0
  276. package/src/domains/session/handoff.ts +629 -0
  277. package/src/domains/session/history.ts +68 -19
  278. package/src/domains/session/usage.ts +24 -7
  279. package/src/domains/share/archive.ts +67 -2
  280. package/src/engine/acp/event-mapper.ts +7 -0
  281. package/src/engine/acp/server.ts +29 -2
  282. package/src/engine/apis/lmstudio.ts +25 -4
  283. package/src/engine/apis/openai-completions.ts +147 -22
  284. package/src/engine/claude/sdk-runtime.ts +8 -2
  285. package/src/engine/claude/tool-safety.ts +13 -0
  286. package/src/engine/loop-guard.ts +27 -3
  287. package/src/engine/worker-events.ts +4 -3
  288. package/src/engine/worker-runtime.ts +59 -54
  289. package/src/entry/orchestrator.ts +55 -1
  290. package/src/interactive/bus-notices.ts +26 -0
  291. package/src/interactive/chat-loop-messages.ts +22 -0
  292. package/src/interactive/chat-loop.ts +248 -1
  293. package/src/interactive/chat-renderer.ts +41 -3
  294. package/src/interactive/clio-editor.ts +44 -7
  295. package/src/interactive/context-overlay.ts +43 -5
  296. package/src/interactive/cost-overlay.ts +70 -11
  297. package/src/interactive/council-dispatch.ts +30 -0
  298. package/src/interactive/council-grid.ts +213 -0
  299. package/src/interactive/council.ts +99 -0
  300. package/src/interactive/dispatch-board.ts +471 -50
  301. package/src/interactive/fleet-run-preview.ts +307 -0
  302. package/src/interactive/footer/notifications.ts +219 -0
  303. package/src/interactive/footer/widgets.ts +13 -0
  304. package/src/interactive/handoff-round.ts +56 -0
  305. package/src/interactive/interactive-application.ts +49 -2
  306. package/src/interactive/interactive-event-projection.ts +9 -1
  307. package/src/interactive/interactive-input-runtime.ts +11 -1
  308. package/src/interactive/interactive-presentation.ts +11 -1
  309. package/src/interactive/interactive-slash-runtime.ts +52 -2
  310. package/src/interactive/interactive-subscriptions.ts +14 -2
  311. package/src/interactive/memory-overlay.ts +89 -4
  312. package/src/interactive/oracle.ts +179 -0
  313. package/src/interactive/overlay-ask-user-lifecycle.ts +7 -1
  314. package/src/interactive/overlay-frame.ts +5 -2
  315. package/src/interactive/overlay-general-openers.ts +230 -2
  316. package/src/interactive/overlay-key-routing.ts +58 -2
  317. package/src/interactive/overlay-lifecycle.ts +52 -5
  318. package/src/interactive/overlay-permission-lifecycle.ts +33 -8
  319. package/src/interactive/overlay-resource-openers.ts +11 -3
  320. package/src/interactive/overlay-session-lifecycle.ts +234 -2
  321. package/src/interactive/overlay-transitions.ts +11 -0
  322. package/src/interactive/overlays/ask-user.ts +74 -30
  323. package/src/interactive/overlays/decisions.ts +3 -1
  324. package/src/interactive/overlays/fleet-run-approval.ts +208 -0
  325. package/src/interactive/overlays/handoff-review.ts +185 -0
  326. package/src/interactive/overlays/library-install-confirm.ts +151 -0
  327. package/src/interactive/overlays/list-overlay.ts +168 -2
  328. package/src/interactive/overlays/settings.ts +101 -4
  329. package/src/interactive/overlays/side-question.ts +139 -0
  330. package/src/interactive/overlays/skills-hub.ts +401 -15
  331. package/src/interactive/permission-hint.ts +35 -0
  332. package/src/interactive/permission-overlay.ts +95 -45
  333. package/src/interactive/renderers/tool-execution.ts +19 -49
  334. package/src/interactive/session-last-turn.ts +8 -1
  335. package/src/interactive/session-usage-reseed.ts +36 -10
  336. package/src/interactive/side-question.ts +171 -0
  337. package/src/interactive/slash-commands.ts +434 -7
  338. package/src/interactive/slash-spec.ts +19 -6
  339. package/src/interactive/status/summary.ts +5 -0
  340. package/src/interactive/status/types.ts +5 -0
  341. package/src/interactive/terminal-lease.ts +1 -0
  342. package/src/interactive/theme/tokens.ts +30 -0
  343. package/src/interactive/turn-context.ts +96 -23
  344. package/src/interactive/turn-middleware.ts +16 -1
  345. package/src/interactive/turn-runtime.ts +37 -8
  346. package/src/interactive/turn-state.ts +3 -0
  347. package/src/interactive/watchdog-run.ts +75 -0
  348. package/src/interactive/worker-progress.ts +440 -0
  349. package/src/interactive/worker-share.ts +56 -1
  350. package/src/interactive/worker-stream.ts +58 -110
  351. package/src/tools/agent-tools.ts +28 -3
  352. package/src/tools/ask-user.ts +21 -1
  353. package/src/tools/bootstrap.ts +3 -0
  354. package/src/tools/compete-worktrees.ts +13 -79
  355. package/src/tools/context/index.ts +2 -2
  356. package/src/tools/dispatch-admission.ts +242 -8
  357. package/src/tools/dispatch-arguments.ts +65 -1
  358. package/src/tools/dispatch-event-text.ts +19 -0
  359. package/src/tools/dispatch-plan.ts +136 -6
  360. package/src/tools/dispatch-runner.ts +319 -13
  361. package/src/tools/dispatch-types.ts +20 -1
  362. package/src/tools/dispatch.ts +96 -3
  363. package/src/tools/monitor.ts +31 -0
  364. package/src/tools/profiles.ts +18 -4
  365. package/src/tools/registry.ts +15 -5
  366. package/src/tools/result-disposition.ts +156 -0
  367. package/src/tools/result-shaping.ts +59 -1
  368. package/src/tools/task-worktree.ts +238 -0
  369. package/src/tools/verify/authoring.ts +116 -55
  370. package/src/tools/verify/scripts.ts +62 -0
  371. package/src/tools/worker-evidence.ts +21 -1
  372. package/src/worker/spec-contract.ts +44 -3
  373. package/dist/chunk-EFADSJET.js +0 -18
  374. package/dist/chunk-HC4CLZ2Y.js +0 -68
  375. package/dist/memory-4ALKDJ4Q.js +0 -246
  376. package/src/domains/eval/metrics/chaos-stream.ts +0 -93
@@ -0,0 +1,238 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { createHash, randomBytes } from "node:crypto";
3
+ import { existsSync, mkdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
4
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
5
+
6
+ export type TaskWorktreeApply = "merge" | "preserve";
7
+
8
+ export interface TaskWorktree {
9
+ root: string;
10
+ runId: string;
11
+ path: string;
12
+ branch: string;
13
+ base: string;
14
+ ownerToken: string;
15
+ }
16
+
17
+ export interface TaskWorktreeReceipt {
18
+ path: string;
19
+ branch: string;
20
+ diffHash: string;
21
+ apply: TaskWorktreeApply;
22
+ applied: boolean;
23
+ reason?: string;
24
+ }
25
+
26
+ const SAFE_RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
27
+ const OWNER_FILE_SUFFIX = ".task-owner.json";
28
+ const COMMIT_IDENTITY = "clio-task";
29
+
30
+ function gitBytes(root: string, args: string[]): Buffer {
31
+ return execFileSync("git", ["-C", root, ...args], {
32
+ stdio: ["ignore", "pipe", "pipe"],
33
+ timeout: 30_000,
34
+ });
35
+ }
36
+
37
+ function git(root: string, args: string[]): string {
38
+ return gitBytes(root, args).toString("utf8").trim();
39
+ }
40
+
41
+ export function isCanonicalWorktreePathInside(parent: string, candidate: string): boolean {
42
+ let canonicalParent: string;
43
+ let canonicalCandidate: string;
44
+ try {
45
+ canonicalParent = realpathSync(parent);
46
+ } catch {
47
+ canonicalParent = resolve(parent);
48
+ }
49
+ try {
50
+ canonicalCandidate = realpathSync(candidate);
51
+ } catch {
52
+ canonicalCandidate = resolve(candidate);
53
+ }
54
+ const rel = relative(canonicalParent, canonicalCandidate);
55
+ return rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
56
+ }
57
+
58
+ export function commitWorktreePath(path: string, identity: string, message: string): boolean {
59
+ git(path, ["add", "-A"]);
60
+ if (git(path, ["status", "--porcelain"]).length === 0) return false;
61
+ git(path, [
62
+ "-c",
63
+ `user.name=${identity}`,
64
+ "-c",
65
+ `user.email=${identity}@local`,
66
+ "commit",
67
+ "-m",
68
+ message,
69
+ "--no-verify",
70
+ ]);
71
+ return true;
72
+ }
73
+
74
+ export function worktreeBranchDiffStat(root: string, branch: string): string {
75
+ try {
76
+ return git(root, ["diff", "--shortstat", `HEAD...${branch}`]) || "no changes";
77
+ } catch {
78
+ return "diff unavailable";
79
+ }
80
+ }
81
+
82
+ export function protectedPathsChangedByWorktreeBranch(
83
+ root: string,
84
+ branch: string,
85
+ protectedPaths: ReadonlyArray<string>,
86
+ ): string[] {
87
+ const canonical = realpathSync(root);
88
+ const protectedInside = protectedPaths
89
+ .map((path) => resolve(path))
90
+ .filter((path) => {
91
+ const rel = relative(canonical, path);
92
+ return rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
93
+ });
94
+ if (protectedInside.length === 0) return [];
95
+ const changed = git(canonical, ["diff", "--name-only", "-z", "--no-renames", `HEAD...${branch}`])
96
+ .split("\0")
97
+ .filter((path) => path.length > 0)
98
+ .map((path) => resolve(canonical, path));
99
+ const blocked = new Set<string>();
100
+ for (const candidate of changed) {
101
+ for (const protectedPath of protectedInside) {
102
+ const rel = relative(protectedPath, candidate);
103
+ if (
104
+ candidate === protectedPath ||
105
+ (rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel))
106
+ ) {
107
+ blocked.add(protectedPath);
108
+ }
109
+ }
110
+ }
111
+ return [...blocked].sort();
112
+ }
113
+
114
+ export function mergeWorktreeBranch(
115
+ root: string,
116
+ branch: string,
117
+ identity: string,
118
+ ): { ok: true } | { ok: false; reason: string } {
119
+ try {
120
+ git(root, [
121
+ "-c",
122
+ `user.name=${identity}`,
123
+ "-c",
124
+ `user.email=${identity}@local`,
125
+ "merge",
126
+ "--no-edit",
127
+ "--no-verify",
128
+ branch,
129
+ ]);
130
+ return { ok: true };
131
+ } catch (error) {
132
+ try {
133
+ git(root, ["merge", "--abort"]);
134
+ } catch {
135
+ // No merge remains to abort.
136
+ }
137
+ return {
138
+ ok: false,
139
+ reason: error instanceof Error ? (error.message.split("\n")[0] ?? "merge failed") : String(error),
140
+ };
141
+ }
142
+ }
143
+
144
+ function validateRunId(runId: string): void {
145
+ if (!SAFE_RUN_ID.test(runId) || runId === "." || runId === "..")
146
+ throw new Error(`invalid task worktree run id '${runId}'`);
147
+ }
148
+
149
+ export function gitCheckoutRoot(cwd: string): string | null {
150
+ try {
151
+ return realpathSync(git(cwd, ["rev-parse", "--show-toplevel"]));
152
+ } catch {
153
+ return null;
154
+ }
155
+ }
156
+
157
+ export function createTaskWorktree(root: string, runId: string, base?: string): TaskWorktree {
158
+ validateRunId(runId);
159
+ const canonical = realpathSync(root);
160
+ const resolvedBase = base ?? git(canonical, ["rev-parse", "HEAD"]);
161
+ const parent = join(canonical, ".clio-coder", "worktrees");
162
+ const path = join(parent, runId);
163
+ const branch = `clio/task/${runId}`;
164
+ mkdirSync(parent, { recursive: true });
165
+ if (!isCanonicalWorktreePathInside(parent, path))
166
+ throw new Error(`task worktree path escapes its parent for run ${runId}`);
167
+ git(canonical, ["worktree", "add", "-b", branch, path, resolvedBase]);
168
+ const ownerToken = randomBytes(16).toString("hex");
169
+ writeFileSync(
170
+ `${path}${OWNER_FILE_SUFFIX}`,
171
+ `${JSON.stringify({ version: 1, kind: "clio-task-worktree", root: canonical, runId, branch, base: resolvedBase, ownerToken }, null, 2)}\n`,
172
+ { encoding: "utf8", flag: "wx" },
173
+ );
174
+ return { root: canonical, runId, path, branch, base: resolvedBase, ownerToken };
175
+ }
176
+
177
+ function assertOwnership(worktree: TaskWorktree): void {
178
+ const expected = join(worktree.root, ".clio-coder", "worktrees", worktree.runId);
179
+ if (
180
+ resolve(worktree.path) !== resolve(expected) ||
181
+ !isCanonicalWorktreePathInside(join(worktree.root, ".clio-coder", "worktrees"), worktree.path)
182
+ ) {
183
+ throw new Error(`task worktree ${worktree.runId} has an invalid ownership path`);
184
+ }
185
+ if (!existsSync(`${worktree.path}${OWNER_FILE_SUFFIX}`))
186
+ throw new Error(`task worktree ${worktree.runId} has no ownership file`);
187
+ }
188
+
189
+ function commitTaskWorktree(worktree: TaskWorktree, message = `Clio task ${worktree.runId}`): boolean {
190
+ assertOwnership(worktree);
191
+ return commitWorktreePath(worktree.path, COMMIT_IDENTITY, message);
192
+ }
193
+
194
+ function taskWorktreeDiffHash(worktree: Pick<TaskWorktree, "root" | "base" | "branch">): string {
195
+ const bytes = gitBytes(worktree.root, ["diff", `${worktree.base}..${worktree.branch}`]);
196
+ return createHash("sha256").update(bytes).digest("hex");
197
+ }
198
+
199
+ function protectedPathsChangedByTaskBranch(
200
+ worktree: Pick<TaskWorktree, "root" | "branch">,
201
+ protectedPaths: ReadonlyArray<string>,
202
+ ): string[] {
203
+ return protectedPathsChangedByWorktreeBranch(worktree.root, worktree.branch, protectedPaths);
204
+ }
205
+
206
+ export function applyTaskWorktree(input: {
207
+ worktree: TaskWorktree;
208
+ apply: TaskWorktreeApply;
209
+ protectedPaths?: ReadonlyArray<string>;
210
+ }): TaskWorktreeReceipt {
211
+ assertOwnership(input.worktree);
212
+ commitTaskWorktree(input.worktree);
213
+ const receipt: TaskWorktreeReceipt = {
214
+ path: input.worktree.path,
215
+ branch: input.worktree.branch,
216
+ diffHash: taskWorktreeDiffHash(input.worktree),
217
+ apply: input.apply,
218
+ applied: false,
219
+ };
220
+ if (input.apply === "preserve") return receipt;
221
+ const protectedChanges = protectedPathsChangedByTaskBranch(input.worktree, input.protectedPaths ?? []);
222
+ if (protectedChanges.length > 0) return { ...receipt, reason: "protected_artifact_changed" };
223
+ const merged = mergeWorktreeBranch(input.worktree.root, input.worktree.branch, COMMIT_IDENTITY);
224
+ if (!merged.ok) return { ...receipt, reason: "worktree_merge_conflict" };
225
+ return { ...receipt, applied: true };
226
+ }
227
+
228
+ export function cleanupTaskWorktree(worktree: TaskWorktree, deleteBranch: boolean): void {
229
+ assertOwnership(worktree);
230
+ try {
231
+ git(worktree.root, ["worktree", "remove", "--force", worktree.path]);
232
+ } catch {
233
+ if (existsSync(worktree.path)) rmSync(worktree.path, { recursive: true, force: true });
234
+ git(worktree.root, ["worktree", "prune"]);
235
+ }
236
+ if (deleteBranch) git(worktree.root, ["branch", "-D", worktree.branch]);
237
+ rmSync(`${worktree.path}${OWNER_FILE_SUFFIX}`, { force: true });
238
+ }
@@ -12,6 +12,7 @@ import {
12
12
  import path from "node:path";
13
13
  import { isMap, isSeq, parseDocument, stringify, type YAMLMap } from "yaml";
14
14
  import { resolveSafeCwd, SAFE_EXEC_DEFAULT_TIMEOUT_MS } from "../../core/safe-exec.js";
15
+ import { parseTomlDocument, tomlTableAt } from "../../core/toml.js";
15
16
  import { isVerificationScriptName } from "../../core/verification-scripts.js";
16
17
  import { compareCodepoints } from "../../domains/evidence/ordering.js";
17
18
  import type { ToolResult } from "../registry.js";
@@ -23,7 +24,7 @@ import {
23
24
  parseProjectVerifierCatalogText,
24
25
  } from "./catalog.js";
25
26
  import { verifyTool } from "./index.js";
26
- import { discoverDeclaredChecksAtRoot } from "./scripts.js";
27
+ import { discoverDeclaredChecksAtRoot, discoverDeclaredProjectEntriesAtRoot } from "./scripts.js";
27
28
 
28
29
  export type VerifierProposalAuthority = "project-declared" | "toolchain-defined";
29
30
 
@@ -33,6 +34,8 @@ export type VerifierSignalKind =
33
34
  | "cargo"
34
35
  | "cmake-preset"
35
36
  | "python-runner"
37
+ | "just-recipe"
38
+ | "make-target"
36
39
  | "go-module"
37
40
  | "validation-contract"
38
41
  | "manual-entry";
@@ -148,6 +151,12 @@ export interface VerifierAuthoringWorkflowOptions {
148
151
  runCheck?: (id: string) => Promise<ToolResult>;
149
152
  }
150
153
 
154
+ export interface DeclaredProjectCommand {
155
+ id: string;
156
+ command: string[];
157
+ provenance: VerifierProvenance;
158
+ }
159
+
151
160
  interface RawProposal {
152
161
  preferredId: string;
153
162
  description: string;
@@ -288,8 +297,13 @@ function cargoProposals(workspaceRoot: string, diagnostics: string[]): RawPropos
288
297
  diagnostics.push(`${relative}: ${text.message}; Cargo discovery skipped.`);
289
298
  return [];
290
299
  }
291
- const workspace = /^\s*\[workspace(?:\]|\.)/mu.test(text);
292
- const packageManifest = /^\s*\[package\]/mu.test(text);
300
+ const document = parseTomlDocument(text);
301
+ if (document === null) {
302
+ diagnostics.push(`${relative}: invalid TOML; Cargo discovery skipped.`);
303
+ return [];
304
+ }
305
+ const workspace = tomlTableAt(document, ["workspace"]) !== null;
306
+ const packageManifest = tomlTableAt(document, ["package"]) !== null;
293
307
  if (!workspace && !packageManifest) {
294
308
  diagnostics.push(`${relative}: no [package] or [workspace] declaration was found; Cargo discovery skipped.`);
295
309
  return [];
@@ -364,73 +378,68 @@ function cmakeProposals(workspaceRoot: string, diagnostics: string[]): RawPropos
364
378
  return proposals;
365
379
  }
366
380
 
367
- function tomlSections(text: string): Map<string, string[]> {
368
- const sections = new Map<string, string[]>();
369
- let active = "";
370
- for (const line of text.split(/\r?\n/u)) {
371
- const header = /^\s*\[([^\]]+)\]\s*(?:#.*)?$/u.exec(line);
372
- if (header?.[1] !== undefined) {
373
- active = header[1].trim();
374
- if (!sections.has(active)) sections.set(active, []);
375
- continue;
376
- }
377
- if (active.length > 0) sections.get(active)?.push(line);
378
- }
379
- return sections;
380
- }
381
-
382
- function declaredScriptName(line: string): string | null {
383
- const match = /^\s*(?:"([^"]+)"|'([^']+)'|([A-Za-z0-9_.:-]+))\s*=\s*(?:"[^"]+"|'[^']+')\s*(?:#.*)?$/u.exec(line);
384
- return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;
385
- }
386
-
387
381
  function pythonProposals(workspaceRoot: string, diagnostics: string[]): RawProposal[] {
388
382
  const proposals: RawProposal[] = [];
389
383
  const pyprojectPath = "pyproject.toml";
390
384
  const pyproject = regularFileText(path.join(workspaceRoot, pyprojectPath), workspaceRoot);
391
385
  if (pyproject instanceof Error) diagnostics.push(`${pyprojectPath}: ${pyproject.message}; Python discovery skipped.`);
392
386
  if (typeof pyproject === "string") {
393
- const sections = tomlSections(pyproject);
394
- for (const [section, module, id, description, tags] of [
395
- ["tool.pytest.ini_options", "pytest", "python-pytest", "Run the declared pytest suite", ["python", "test"]],
396
- ["tool.tox", "tox", "python-tox", "Run the declared tox environments", ["python", "test"]],
397
- ["tool.nox", "nox", "python-nox", "Run the declared nox sessions", ["python", "test"]],
398
- ] as const) {
399
- if (!sections.has(section)) continue;
400
- proposals.push({
401
- preferredId: id,
402
- description,
403
- command: ["python", "-m", module],
404
- cwd: ".",
405
- timeoutMs: SAFE_EXEC_DEFAULT_TIMEOUT_MS,
406
- tags: [...tags],
407
- provenance: {
408
- kind: "python-runner",
409
- path: pyprojectPath,
410
- detail: `[${section}]`,
411
- authority: "toolchain-defined",
412
- },
413
- });
414
- }
415
- for (const section of ["project.scripts", "tool.poetry.scripts"]) {
416
- for (const line of sections.get(section) ?? []) {
417
- const name = declaredScriptName(line);
418
- if (name === null || !isVerificationScriptName(name)) continue;
387
+ const document = parseTomlDocument(pyproject);
388
+ if (document === null) {
389
+ diagnostics.push(`${pyprojectPath}: invalid TOML; Python discovery skipped.`);
390
+ } else {
391
+ for (const [path, section, module, id, description, tags] of [
392
+ [
393
+ ["tool", "pytest", "ini_options"],
394
+ "tool.pytest.ini_options",
395
+ "pytest",
396
+ "python-pytest",
397
+ "Run the declared pytest suite",
398
+ ["python", "test"],
399
+ ],
400
+ [["tool", "tox"], "tool.tox", "tox", "python-tox", "Run the declared tox environments", ["python", "test"]],
401
+ [["tool", "nox"], "tool.nox", "nox", "python-nox", "Run the declared nox sessions", ["python", "test"]],
402
+ ] as const) {
403
+ if (tomlTableAt(document, path) === null) continue;
419
404
  proposals.push({
420
- preferredId: `python-${slug(name, "check")}`,
421
- description: `Run declared Python entry point '${name}'`,
422
- command: [name],
405
+ preferredId: id,
406
+ description,
407
+ command: ["python", "-m", module],
423
408
  cwd: ".",
424
409
  timeoutMs: SAFE_EXEC_DEFAULT_TIMEOUT_MS,
425
- tags: [slug(name.split(/[:.-]/u)[0] ?? "python", "python"), "python"],
410
+ tags: [...tags],
426
411
  provenance: {
427
412
  kind: "python-runner",
428
413
  path: pyprojectPath,
429
- detail: `[${section}] entry '${name}'`,
430
- authority: "project-declared",
414
+ detail: `[${section}]`,
415
+ authority: "toolchain-defined",
431
416
  },
432
417
  });
433
418
  }
419
+ for (const [path, section] of [
420
+ [["project", "scripts"], "project.scripts"],
421
+ [["tool", "poetry", "scripts"], "tool.poetry.scripts"],
422
+ ] as const) {
423
+ const scripts = tomlTableAt(document, path);
424
+ if (scripts === null) continue;
425
+ for (const [name, target] of Object.entries(scripts)) {
426
+ if (typeof target !== "string" || !isVerificationScriptName(name)) continue;
427
+ proposals.push({
428
+ preferredId: `python-${slug(name, "check")}`,
429
+ description: `Run declared Python entry point '${name}'`,
430
+ command: [name],
431
+ cwd: ".",
432
+ timeoutMs: SAFE_EXEC_DEFAULT_TIMEOUT_MS,
433
+ tags: [slug(name.split(/[:.-]/u)[0] ?? "python", "python"), "python"],
434
+ provenance: {
435
+ kind: "python-runner",
436
+ path: pyprojectPath,
437
+ detail: `[${section}] entry '${name}'`,
438
+ authority: "project-declared",
439
+ },
440
+ });
441
+ }
442
+ }
434
443
  }
435
444
  }
436
445
 
@@ -465,6 +474,58 @@ function pythonProposals(workspaceRoot: string, diagnostics: string[]): RawPropo
465
474
  return proposals;
466
475
  }
467
476
 
477
+ /**
478
+ * Discover exact invocation vectors from project declarations for fleet command
479
+ * authoring. This shares the verifier authoring readers and provenance model,
480
+ * while retaining entries that are not verification-family names.
481
+ */
482
+ export function discoverDeclaredProjectCommands(workspaceRoot = process.cwd()): DeclaredProjectCommand[] {
483
+ const commands: DeclaredProjectCommand[] = [];
484
+ const add = (id: string, command: string[], pathValue: string, detail: string, kind: VerifierSignalKind): void => {
485
+ commands.push({
486
+ id,
487
+ command,
488
+ provenance: { kind, path: pathValue, detail, authority: "project-declared" },
489
+ });
490
+ };
491
+ for (const entry of discoverDeclaredProjectEntriesAtRoot(workspaceRoot)) {
492
+ add(entry.id, entry.command, entry.path, entry.detail, entry.kind);
493
+ }
494
+ const pyprojectText = regularFileText(path.join(workspaceRoot, "pyproject.toml"), workspaceRoot);
495
+ if (typeof pyprojectText === "string") {
496
+ const document = parseTomlDocument(pyprojectText);
497
+ if (document !== null) {
498
+ for (const [tablePath, label] of [
499
+ [["project", "scripts"], "project.scripts"],
500
+ [["tool", "poetry", "scripts"], "tool.poetry.scripts"],
501
+ ] as const) {
502
+ const scripts = tomlTableAt(document, tablePath);
503
+ if (scripts === null) continue;
504
+ for (const [name, target] of Object.entries(scripts).sort(([left], [right]) => compareCodepoints(left, right))) {
505
+ if (typeof target === "string") add(name, [name], "pyproject.toml", `[${label}] entry '${name}'`, "python-runner");
506
+ }
507
+ }
508
+ for (const [tablePath, module, id, label] of [
509
+ [["tool", "pytest", "ini_options"], "pytest", "pytest", "tool.pytest.ini_options"],
510
+ [["tool", "tox"], "tox", "tox", "tool.tox"],
511
+ [["tool", "nox"], "nox", "nox", "tool.nox"],
512
+ ] as const) {
513
+ if (tomlTableAt(document, tablePath) !== null)
514
+ add(id, ["python", "-m", module], "pyproject.toml", `[${label}]`, "python-runner");
515
+ }
516
+ }
517
+ }
518
+ const seen = new Set<string>();
519
+ return commands.filter((entry) => {
520
+ let id = slug(entry.id, "command");
521
+ let suffix = 2;
522
+ while (seen.has(id)) id = `${slug(entry.id, "command")}-${suffix++}`;
523
+ seen.add(id);
524
+ entry.id = id;
525
+ return true;
526
+ });
527
+ }
528
+
468
529
  function goProposals(workspaceRoot: string, diagnostics: string[]): RawProposal[] {
469
530
  const relative = "go.mod";
470
531
  const text = regularFileText(path.join(workspaceRoot, relative), workspaceRoot);
@@ -21,6 +21,14 @@ import {
21
21
 
22
22
  export type DeclaredCheckDiscoveryResult = { ok: true; sources: DeclaredCheckSource[] } | { ok: false; reason: string };
23
23
 
24
+ export interface DeclaredProjectEntry {
25
+ id: string;
26
+ command: string[];
27
+ path: string;
28
+ detail: string;
29
+ kind: "package-script" | "just-recipe" | "make-target";
30
+ }
31
+
24
32
  function repositoryRelativeCwd(workspaceRoot: string, resolved: string): string {
25
33
  const relative = path.relative(workspaceRoot, resolved);
26
34
  return relative.length === 0 ? "." : relative.split(path.sep).join("/");
@@ -43,6 +51,60 @@ function packageCheckSource(packageRoot: string, workspaceRoot: string): Declare
43
51
  return { kind: "package.json", path: packagePath, checks };
44
52
  }
45
53
 
54
+ /** Discover every exact project-declared entry without promoting it to a verifier check. */
55
+ export function discoverDeclaredProjectEntriesAtRoot(workspaceRoot: string): DeclaredProjectEntry[] {
56
+ const entries: DeclaredProjectEntry[] = [];
57
+ const packagePath = path.join(workspaceRoot, "package.json");
58
+ if (existsSync(packagePath)) {
59
+ const pkg = parsePackageJson(packagePath);
60
+ if (pkg.ok) {
61
+ for (const name of Object.keys(pkg.scripts).sort()) {
62
+ if (typeof pkg.scripts[name] !== "string") continue;
63
+ entries.push({
64
+ id: name,
65
+ command: ["npm", "run", name],
66
+ path: "package.json",
67
+ detail: `package.json script '${name}'`,
68
+ kind: "package-script",
69
+ });
70
+ }
71
+ }
72
+ }
73
+ for (const relative of ["justfile", "Justfile"] as const) {
74
+ const filePath = path.join(workspaceRoot, relative);
75
+ if (!existsSync(filePath)) continue;
76
+ const text = readFileSync(filePath, "utf8");
77
+ for (const match of text.matchAll(/^([A-Za-z0-9][A-Za-z0-9_-]*)\s*(?:[^:=\n]*)?:\s*(?:#.*)?$/gmu)) {
78
+ const name = match[1];
79
+ if (name === undefined || name.startsWith("_")) continue;
80
+ entries.push({
81
+ id: name,
82
+ command: ["just", name],
83
+ path: relative,
84
+ detail: `just recipe '${name}'`,
85
+ kind: "just-recipe",
86
+ });
87
+ }
88
+ break;
89
+ }
90
+ const makePath = path.join(workspaceRoot, "Makefile");
91
+ if (existsSync(makePath)) {
92
+ const text = readFileSync(makePath, "utf8");
93
+ for (const match of text.matchAll(/^([A-Za-z0-9][A-Za-z0-9_.-]*)\s*:(?![=])[^\n]*$/gmu)) {
94
+ const name = match[1];
95
+ if (name === undefined || name.startsWith(".")) continue;
96
+ entries.push({
97
+ id: name,
98
+ command: ["make", name],
99
+ path: "Makefile",
100
+ detail: `Makefile target '${name}'`,
101
+ kind: "make-target",
102
+ });
103
+ }
104
+ }
105
+ return entries;
106
+ }
107
+
46
108
  function providerCollision(sources: ReadonlyArray<DeclaredCheckSource>): string | null {
47
109
  const seen = new Map<string, DeclaredCheck>();
48
110
  for (const source of sources) {
@@ -1,7 +1,14 @@
1
1
  import { mentionsWorkerToolCallCap } from "../core/guardrails.js";
2
+ import {
3
+ formatBudgetPolicy,
4
+ formatBudgetReasons,
5
+ formatBudgetRequest,
6
+ formatEffectiveBudget,
7
+ } from "../domains/dispatch/budget-envelope.js";
2
8
  import type { ReceiptIntegrityResult } from "../domains/dispatch/receipt-integrity.js";
3
9
  import type { RunReceipt, RunReceiptVerification } from "../domains/dispatch/types.js";
4
10
  import { adaptRunReceiptTrustStatus, type CanonicalTrustStatus } from "../domains/evidence/trust-status.js";
11
+ import { receiptResponseModelIdObservationLabel } from "./dispatch-event-text.js";
5
12
 
6
13
  /** Matches a source citation the parent can independently spot-check. */
7
14
  const SOURCE_CITATION_PATTERN = /([\w./~-]+):(\d+)/g;
@@ -120,7 +127,7 @@ export function receiptEvidenceLabels(
120
127
  verification: RunReceiptVerification,
121
128
  integrity: ReceiptIntegrityResult,
122
129
  ): string[] {
123
- const status = adaptRunReceiptTrustStatus({ ...receipt, verification }, { integrity });
130
+ const status = adaptRunReceiptTrustStatus({ ...receipt, verification: verification }, { integrity });
124
131
  const canonical = canonicalTrustLabel(status);
125
132
  if (!integrity.ok) {
126
133
  return [canonical, `receipt_integrity=FAILED reason=${JSON.stringify(integrity.reason)}`];
@@ -136,10 +143,23 @@ export function receiptEvidenceLabels(
136
143
  sections.length > 0 ? ` sections:${[...sections].sort().join(",")}` : ""
137
144
  }${receipt.projectContext.contentHash !== undefined ? ` sha256:${receipt.projectContext.contentHash}` : ""}`;
138
145
  }
146
+ const responseModelIdObservation = receiptResponseModelIdObservationLabel(receipt);
147
+ const budget =
148
+ receipt.budget === undefined
149
+ ? []
150
+ : [
151
+ `budget_recipe_policy=${JSON.stringify(formatBudgetPolicy(receipt.budget))}`,
152
+ `budget_requested=${JSON.stringify(formatBudgetRequest(receipt.budget))}`,
153
+ `budget_effective=${JSON.stringify(formatEffectiveBudget(receipt.budget))}`,
154
+ `budget_reason=${JSON.stringify(formatBudgetReasons(receipt.budget))}`,
155
+ ];
139
156
  return [
140
157
  canonical,
141
158
  `receipt_integrity=verified/v${receipt.integrity.version}/${receipt.integrity.algorithm}`,
142
159
  `evidence_verification=${verification.state}/${verification.basis}`,
160
+ `host_verification=${receipt.hostVerification?.status ?? "not_requested"}`,
161
+ ...(responseModelIdObservation ? [responseModelIdObservation] : []),
162
+ ...budget,
143
163
  ...receiptAdmissionLabels(receipt),
144
164
  ...receiptActivityLabels(receipt, status),
145
165
  briefing,
@@ -48,6 +48,11 @@ export interface WorkerProtectedArtifactState {
48
48
  }
49
49
 
50
50
  /** Concrete dispatch-time budget after recipe policy and operator clamping. */
51
+ export interface WorkerBudgetPhase {
52
+ toolCalls: number;
53
+ readReserve: number;
54
+ }
55
+
51
56
  export interface WorkerBudget {
52
57
  /** Agent phase boundary before synthesis or bounded termination. */
53
58
  toolCalls: number;
@@ -57,6 +62,8 @@ export interface WorkerBudget {
57
62
  synthesis: boolean;
58
63
  /** Independent operator-owned attempt ceiling; recipes cannot widen it. */
59
64
  hardCap: number;
65
+ /** Optional ceiling that a bounded result-contract revision may activate. */
66
+ revision?: WorkerBudgetPhase;
60
67
  }
61
68
 
62
69
  interface WorkerSpecFields {
@@ -218,6 +225,7 @@ const TOOL_PROFILE_NAMES = [
218
225
  "minimal-local",
219
226
  "science-local",
220
227
  "full-agent",
228
+ "council-read-only",
221
229
  ] as const satisfies ReadonlyArray<ToolProfileName>;
222
230
  const WORKER_PRODUCTS = ["orientation"] as const satisfies ReadonlyArray<AgentProduct>;
223
231
  const TARGET_LIFECYCLES = ["user-managed", "clio-managed"] as const;
@@ -416,10 +424,16 @@ function validateAllowedTools(value: unknown): void {
416
424
 
417
425
  function validateWorkerBudget(value: unknown): void {
418
426
  const budget = readRecord(value, "WorkerSpec.budget");
419
- const expected = ["hardCap", "readReserve", "synthesis", "toolCalls"];
427
+ const expected = ["hardCap", "readReserve", "revision", "synthesis", "toolCalls"];
420
428
  const actual = Object.keys(budget).sort();
421
- if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
422
- throw new Error(`WorkerSpec.budget must contain exactly: ${expected.join(", ")}`);
429
+ if (
430
+ actual.some((key) => !expected.includes(key)) ||
431
+ actual.some((key) => key === "revision" && budget.revision === undefined)
432
+ ) {
433
+ throw new Error(`WorkerSpec.budget may contain only: ${expected.join(", ")}`);
434
+ }
435
+ for (const required of ["hardCap", "readReserve", "synthesis", "toolCalls"] as const) {
436
+ if (!Object.hasOwn(budget, required)) throw new Error(`WorkerSpec.budget.${required} is required`);
423
437
  }
424
438
  for (const key of ["toolCalls", "readReserve", "hardCap"] as const) {
425
439
  const value = budget[key];
@@ -436,6 +450,33 @@ function validateWorkerBudget(value: unknown): void {
436
450
  throw new Error("WorkerSpec.budget.readReserve must be an integer in [0, toolCalls)");
437
451
  }
438
452
  if (typeof budget.synthesis !== "boolean") throw new Error("WorkerSpec.budget.synthesis must be a boolean");
453
+ if (budget.revision !== undefined) {
454
+ const revision = readRecord(budget.revision, "WorkerSpec.budget.revision");
455
+ const revisionKeys = Object.keys(revision).sort();
456
+ if (revisionKeys.length !== 2 || revisionKeys[0] !== "readReserve" || revisionKeys[1] !== "toolCalls") {
457
+ throw new Error("WorkerSpec.budget.revision must contain exactly: readReserve, toolCalls");
458
+ }
459
+ for (const key of ["toolCalls", "readReserve"] as const) {
460
+ if (typeof revision[key] !== "number" || !Number.isSafeInteger(revision[key])) {
461
+ throw new Error(`WorkerSpec.budget.revision.${key} must be a safe integer`);
462
+ }
463
+ }
464
+ if ((revision.toolCalls as number) < (budget.toolCalls as number)) {
465
+ throw new Error("WorkerSpec.budget.revision.toolCalls must not be smaller than WorkerSpec.budget.toolCalls");
466
+ }
467
+ if (
468
+ (revision.toolCalls as number) === (budget.toolCalls as number) &&
469
+ (revision.readReserve as number) <= (budget.readReserve as number)
470
+ ) {
471
+ throw new Error("WorkerSpec.budget.revision must increase toolCalls or readReserve");
472
+ }
473
+ if ((revision.toolCalls as number) > (budget.hardCap as number)) {
474
+ throw new Error("WorkerSpec.budget.revision.toolCalls must not exceed WorkerSpec.budget.hardCap");
475
+ }
476
+ if ((revision.readReserve as number) < 0 || (revision.readReserve as number) >= (revision.toolCalls as number)) {
477
+ throw new Error("WorkerSpec.budget.revision.readReserve must be an integer in [0, toolCalls)");
478
+ }
479
+ }
439
480
  }
440
481
 
441
482
  function validateRuntimeCapabilityDecision(value: unknown, source: string): void {