@iowarp/clio-coder 0.3.3 → 0.3.6

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 (370) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/CONTRIBUTING.md +7 -7
  3. package/README.md +3 -3
  4. package/dist/{acp-P2AQILE2.js → acp-2BEHC4DL.js} +9 -8
  5. package/dist/{agents-72W3BI7I.js → agents-LNNFTM53.js} +29 -24
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5TWEIYDN.js → auth-KXXFI2VS.js} +14 -10
  8. package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
  9. package/dist/{chunk-YCWGATWI.js → chunk-24I7BN55.js} +2 -2
  10. package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
  11. package/dist/chunk-3BPUFZDL.js +37 -0
  12. package/dist/chunk-43AOLP7E.js +375 -0
  13. package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
  14. package/dist/{chunk-6SGHMWE3.js → chunk-5JGRAMKL.js} +5 -5
  15. package/dist/{chunk-V6RTAOC2.js → chunk-6US73PDB.js} +572 -51
  16. package/dist/{chunk-5UFT4SUX.js → chunk-6XXKFVSN.js} +3 -3
  17. package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
  18. package/dist/chunk-AOCYTWAV.js +449 -0
  19. package/dist/chunk-CFGTUFWB.js +67 -0
  20. package/dist/chunk-CJUB2JJ2.js +1478 -0
  21. package/dist/{chunk-FNTMWMX5.js → chunk-CKXWIANG.js} +14 -12
  22. package/dist/{chunk-PIWWS5BL.js → chunk-CYQKWTG3.js} +63 -78
  23. package/dist/{chunk-LZSJBIVT.js → chunk-DJVECN66.js} +271 -762
  24. package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
  25. package/dist/{chunk-ZWMF7253.js → chunk-E2ER4LJF.js} +304 -9
  26. package/dist/{chunk-STBPMHSX.js → chunk-EKY57CSP.js} +51 -84
  27. package/dist/{chunk-DUYJ5IO6.js → chunk-EYPA3EGJ.js} +12 -4
  28. package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
  29. package/dist/chunk-FYYLNIL5.js +313 -0
  30. package/dist/{chunk-OQ33BKR3.js → chunk-G7MUEIGA.js} +3 -60
  31. package/dist/chunk-GEYXPTRF.js +613 -0
  32. package/dist/chunk-GOXNB3AO.js +261 -0
  33. package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
  34. package/dist/chunk-HWUFFB6L.js +83 -0
  35. package/dist/{chunk-4XUGQOHA.js → chunk-K7T3E2SR.js} +15 -8
  36. package/dist/chunk-K7VKOLQQ.js +15 -0
  37. package/dist/{chunk-UFIIWP2H.js → chunk-KHSFENX2.js} +8 -8
  38. package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
  39. package/dist/chunk-LCGCVYZ4.js +57 -0
  40. package/dist/chunk-LL4KHSZI.js +22 -0
  41. package/dist/{chunk-PAJK6MAQ.js → chunk-LYF7OHWH.js} +42 -15
  42. package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
  43. package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
  44. package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
  45. package/dist/{chunk-TZK7PACC.js → chunk-NILBFAPG.js} +14 -8
  46. package/dist/chunk-ODFEOB4F.js +1082 -0
  47. package/dist/{chunk-AGYYIBLL.js → chunk-OH3TOQTB.js} +6 -2
  48. package/dist/chunk-OZNBF4L3.js +23 -0
  49. package/dist/{chunk-DSELYM6W.js → chunk-PBTHKCPN.js} +30 -10
  50. package/dist/{verify-375KUB3Y.js → chunk-PCZJO5TI.js} +127 -42
  51. package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
  52. package/dist/{chunk-SRDMMSEP.js → chunk-QM3F2GKX.js} +1063 -1645
  53. package/dist/{chunk-X6IAEBZR.js → chunk-QNQHSOLF.js} +7 -7
  54. package/dist/{chunk-OC7FIQPC.js → chunk-R46L2BIR.js} +10 -7
  55. package/dist/{chunk-2TLUCQVG.js → chunk-RD5U66HV.js} +3 -3
  56. package/dist/{chunk-6N5PTWMY.js → chunk-RY3LY4J5.js} +50 -13
  57. package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
  58. package/dist/{chunk-OOJYHWRB.js → chunk-TSHXZTOQ.js} +6 -5
  59. package/dist/chunk-TZSKNMZG.js +434 -0
  60. package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
  61. package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
  62. package/dist/{chunk-COU2UHX6.js → chunk-VEZEGCGW.js} +170 -2
  63. package/dist/chunk-W6GROXXM.js +69 -0
  64. package/dist/{chunk-OAO4GE4M.js → chunk-WHGPSPT5.js} +2 -2
  65. package/dist/chunk-WHJYKASB.js +677 -0
  66. package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
  67. package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
  68. package/dist/{chunk-TZTZS7QK.js → chunk-XE2VEJHX.js} +5 -3
  69. package/dist/{chunk-LM5TQCJZ.js → chunk-XF5N4U5A.js} +8 -7
  70. package/dist/{chunk-LWLEKMDQ.js → chunk-XXQNGV4M.js} +1073 -552
  71. package/dist/{chunk-KZWTDYJF.js → chunk-XYDYPRZI.js} +7 -7
  72. package/dist/chunk-ZGVHUX3M.js +66 -0
  73. package/dist/{chunk-LW6DSM3M.js → chunk-ZRGEBJ4T.js} +1192 -1119
  74. package/dist/{chunk-2DJ2KNFG.js → chunk-ZXF4XRKW.js} +202 -40
  75. package/dist/chunk-ZZMN5OM4.js +122 -0
  76. package/dist/cli/index.js +34 -30
  77. package/dist/{clio-JOU4FXVA.js → clio-M2KGYUFZ.js} +7 -6
  78. package/dist/{code-nav-7AX6FYE6.js → code-nav-GQNL7XA6.js} +8 -6
  79. package/dist/codewiki/build-worker.js +4 -4
  80. package/dist/{components-KELWS457.js → components-5TTYYX6G.js} +3 -3
  81. package/dist/{config-XCDVKR23.js → config-XUUYQIWO.js} +47 -35
  82. package/dist/{configure-4GAP54ZW.js → configure-IHJ7YOMV.js} +18 -15
  83. package/dist/{context-77FM5DV5.js → context-74JLXAWD.js} +18 -10
  84. package/dist/{context-4UOGGLQ5.js → context-75MIWW3U.js} +41 -29
  85. package/dist/{context-5VKGUVJJ.js → context-ZQ7SIFJV.js} +85 -9
  86. package/dist/{context-clear-XXJRLCJJ.js → context-clear-GYKWNUML.js} +41 -29
  87. package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
  88. package/dist/context-working-set-UX5KEP4J.js +1553 -0
  89. package/dist/{dispatch-runner-QPRDDBDX.js → dispatch-runner-GIJBHNFL.js} +47 -32
  90. package/dist/{docs-2C2LTVT2.js → docs-6FZSCG5B.js} +3 -3
  91. package/dist/{doctor-HR46URBJ.js → doctor-SVJ5BZCW.js} +12 -12
  92. package/dist/{eval-XSSNATB4.js → eval-CG6LLBLD.js} +54 -238
  93. package/dist/{evidence-6HG2PY2B.js → evidence-ZYFIEN42.js} +57 -28
  94. package/dist/{evolve-K7YU3NCY.js → evolve-QGEXEMDW.js} +36 -25
  95. package/dist/{extensions-QVDOHDGJ.js → extensions-ADGNCJJD.js} +3 -3
  96. package/dist/{fleet-VY3HHKN6.js → fleet-S5R4ZOQY.js} +73 -44
  97. package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-BHSNPBMH.js} +3 -3
  98. package/dist/{init-JYGXI3FK.js → init-5DRU55YR.js} +49 -37
  99. package/dist/memory-7YKKR6UC.js +467 -0
  100. package/dist/{models-I5QWSEOM.js → models-ZPOLRU2C.js} +24 -21
  101. package/dist/{monitor-GE4ID3IA.js → monitor-US5F5YGZ.js} +73 -46
  102. package/dist/{orchestrator-EM5MC3HM.js → orchestrator-E2AL4T5N.js} +1624 -1007
  103. package/dist/{paths-UXLN5YYZ.js → paths-E7KYAQWE.js} +3 -3
  104. package/dist/{reset-L2FQEE3E.js → reset-KZ652EK6.js} +6 -5
  105. package/dist/{run-ZU3QMZPZ.js → run-SRNBKDWD.js} +76 -54
  106. package/dist/{share-S5BZQC5I.js → share-CGZE33UP.js} +7 -6
  107. package/dist/{skills-X5VXCRNQ.js → skills-S2X4DLY5.js} +4 -4
  108. package/dist/{skills-eval-WKIHWTHR.js → skills-eval-W2GGIC4R.js} +40 -29
  109. package/dist/{targets-SNCPI2NR.js → targets-54SWINWB.js} +28 -23
  110. package/dist/{terminal-lease-BNAHVHBS.js → terminal-lease-SAIF2OGY.js} +6 -4
  111. package/dist/{uninstall-FZCQCDKC.js → uninstall-BVLWXKBT.js} +3 -3
  112. package/dist/{upgrade-JQHHPQ4K.js → upgrade-JKAR27XC.js} +20 -19
  113. package/dist/{usage-OR4O5SMZ.js → usage-MSAWCLX4.js} +79 -36
  114. package/dist/verifiers-NCBTHHN2.js +1220 -0
  115. package/dist/verify-X5HDROLA.js +25 -0
  116. package/dist/{wiki-generate-UEXP2ARI.js → wiki-generate-GUSOQ6ZP.js} +50 -37
  117. package/dist/worker/entry.js +90 -70
  118. package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
  119. package/docs/README.md +8 -7
  120. package/docs/acp.md +1 -1
  121. package/docs/alcf-provider.md +1 -1
  122. package/docs/architecture.md +2 -2
  123. package/docs/artifact-placement.md +1 -2
  124. package/docs/artifact-versions.md +1 -1
  125. package/docs/built-in-agents.md +1 -1
  126. package/docs/capacity-and-scheduling.md +1 -1
  127. package/docs/commands-and-modes.md +60 -26
  128. package/docs/config-knobs-audit.md +1 -2
  129. package/docs/configuration-and-targets.md +26 -1
  130. package/docs/context-engine.md +67 -13
  131. package/docs/context-working-set.md +194 -0
  132. package/docs/development-pipeline.md +1 -1
  133. package/docs/documentation-coverage.md +6 -6
  134. package/docs/documentation-guide.md +7 -6
  135. package/docs/environment-variables.md +2 -1
  136. package/docs/eval-runner.md +1 -1
  137. package/docs/evals-internal.md +4 -32
  138. package/docs/evidence-and-memory.md +139 -7
  139. package/docs/evolution.md +1 -1
  140. package/docs/exit-codes-and-output.md +1 -1
  141. package/docs/extensions-and-sharing.md +2 -2
  142. package/docs/fleet-dispatch.md +49 -8
  143. package/docs/glossary.md +21 -1
  144. package/docs/installation-and-lifecycle.md +2 -2
  145. package/docs/middleware-and-components.md +19 -2
  146. package/docs/model-catalog.md +7 -9
  147. package/docs/observability.md +4 -4
  148. package/docs/proactive-memory.md +26 -16
  149. package/docs/prompt-envelope-and-tools.md +7 -5
  150. package/docs/provider-adapter-cookbook.md +1 -1
  151. package/docs/release-cut-checklist.md +43 -40
  152. package/docs/safety-model.md +49 -8
  153. package/docs/scientific-validation.md +21 -3
  154. package/docs/session-lifecycle.md +3 -3
  155. package/docs/skills-marketplace.md +1 -1
  156. package/docs/tool-usage.md +79 -12
  157. package/docs/trace-store.md +1 -1
  158. package/docs/troubleshooting.md +1 -1
  159. package/docs/tui-design.md +38 -4
  160. package/docs/worker-dispatch-mechanics.md +11 -1
  161. package/package.json +13 -13
  162. package/skills/meta/clio-test/SKILL.md +20 -17
  163. package/skills/meta/clio-test/evals.md +3 -3
  164. package/skills/meta/clio-test/references/harness.md +35 -6
  165. package/skills/meta/clio-test/references/test-map.md +20 -10
  166. package/skills/registry.yaml +2 -2
  167. package/skills/skill-marketplace.json +1 -1
  168. package/src/cli/agents.ts +2 -3
  169. package/src/cli/argv.ts +14 -1
  170. package/src/cli/context-working-set.ts +513 -0
  171. package/src/cli/context.ts +8 -0
  172. package/src/cli/evidence.ts +20 -2
  173. package/src/cli/fleet.ts +15 -0
  174. package/src/cli/index.ts +5 -1
  175. package/src/cli/memory.ts +272 -10
  176. package/src/cli/modes/json-stream.ts +2 -2
  177. package/src/cli/modes/print.ts +12 -1
  178. package/src/cli/run.ts +22 -2
  179. package/src/cli/targets.ts +12 -3
  180. package/src/cli/usage.ts +55 -7
  181. package/src/cli/verifiers.ts +325 -0
  182. package/src/core/bash-exec.ts +39 -14
  183. package/src/core/bus-events.ts +22 -4
  184. package/src/core/config.ts +54 -0
  185. package/src/core/defaults.ts +50 -3
  186. package/src/core/response-model-id.ts +134 -0
  187. package/src/core/toml.ts +62 -0
  188. package/src/core/verification-scripts.ts +6 -0
  189. package/src/core/workspace-files.ts +0 -1
  190. package/src/domains/agents/builtins/architect.md +1 -1
  191. package/src/domains/agents/builtins/verifier.md +3 -0
  192. package/src/domains/agents/catalog.ts +5 -4
  193. package/src/domains/agents/recipe.ts +54 -14
  194. package/src/domains/agents/result-contract.ts +7 -4
  195. package/src/domains/config/classify.ts +1 -0
  196. package/src/domains/context/bootstrap.ts +36 -27
  197. package/src/domains/context/project-metadata.ts +19 -63
  198. package/src/domains/context/prompt-context.ts +8 -0
  199. package/src/domains/context/working-set/contract.ts +161 -0
  200. package/src/domains/context/working-set/defaults.ts +28 -0
  201. package/src/domains/context/working-set/engine.ts +203 -0
  202. package/src/domains/context/working-set/fold.ts +62 -0
  203. package/src/domains/context/working-set/horizon.ts +38 -0
  204. package/src/domains/context/working-set/marker.ts +103 -0
  205. package/src/domains/context/working-set/path-index.ts +436 -0
  206. package/src/domains/context/working-set/payload.ts +152 -0
  207. package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
  208. package/src/domains/context/working-set/policies/index.ts +20 -0
  209. package/src/domains/context/working-set/policies/structural.ts +160 -0
  210. package/src/domains/context/working-set/project.ts +132 -0
  211. package/src/domains/context/working-set/protect.ts +109 -0
  212. package/src/domains/context/working-set/recall.ts +177 -0
  213. package/src/domains/context/working-set/replay/controls.ts +112 -0
  214. package/src/domains/context/working-set/replay/load-clio.ts +199 -0
  215. package/src/domains/context/working-set/replay/metrics.ts +185 -0
  216. package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
  217. package/src/domains/context/working-set/replay/report.ts +139 -0
  218. package/src/domains/context/working-set/replay/runner.ts +325 -0
  219. package/src/domains/context/working-set/replay/synthetic.ts +422 -0
  220. package/src/domains/context/working-set/replay/trace.ts +21 -0
  221. package/src/domains/context/working-set/visible.ts +54 -0
  222. package/src/domains/dispatch/budget-envelope.ts +396 -0
  223. package/src/domains/dispatch/contract.ts +2 -0
  224. package/src/domains/dispatch/extension.ts +81 -27
  225. package/src/domains/dispatch/orphan-recovery.ts +1 -0
  226. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  227. package/src/domains/dispatch/state.ts +1 -0
  228. package/src/domains/dispatch/types.ts +10 -3
  229. package/src/domains/dispatch/validation.ts +14 -0
  230. package/src/domains/dispatch/worker-spawn.ts +14 -3
  231. package/src/domains/eval/metrics/evidence.ts +0 -116
  232. package/src/domains/eval/metrics/invariants.ts +1 -1
  233. package/src/domains/eval/runners/clio-run.ts +1 -10
  234. package/src/domains/eval/runners/external-command.ts +2 -29
  235. package/src/domains/eval/schema/suite.ts +0 -7
  236. package/src/domains/eval/suites/run.ts +1 -7
  237. package/src/domains/evidence/build.ts +112 -45
  238. package/src/domains/evidence/eval.ts +24 -7
  239. package/src/domains/evidence/index.ts +53 -0
  240. package/src/domains/evidence/ordering.ts +12 -0
  241. package/src/domains/evidence/run-trust.ts +221 -0
  242. package/src/domains/evidence/store.ts +46 -6
  243. package/src/domains/evidence/trust-status.ts +854 -0
  244. package/src/domains/evidence/types.ts +26 -0
  245. package/src/domains/memory/index.ts +22 -0
  246. package/src/domains/memory/operations.ts +58 -1
  247. package/src/domains/memory/promotion.ts +281 -0
  248. package/src/domains/memory/prompt-section.ts +25 -5
  249. package/src/domains/memory/proposal.ts +51 -7
  250. package/src/domains/memory/task-bank.ts +3 -2
  251. package/src/domains/memory/task-memory-handoff.ts +181 -24
  252. package/src/domains/memory/task-memory-policy.ts +3 -1
  253. package/src/domains/memory/types.ts +37 -0
  254. package/src/domains/memory/validate.ts +178 -0
  255. package/src/domains/middleware/memory-intervention.ts +38 -25
  256. package/src/domains/middleware/runtime.ts +6 -0
  257. package/src/domains/middleware/skills-reminder.ts +19 -4
  258. package/src/domains/middleware/stalled-turn.ts +208 -5
  259. package/src/domains/middleware/types.ts +10 -0
  260. package/src/domains/observability/contract.ts +6 -1
  261. package/src/domains/observability/cost.ts +20 -4
  262. package/src/domains/observability/extension.ts +2 -2
  263. package/src/domains/providers/index.ts +3 -0
  264. package/src/domains/providers/model-discovery.ts +9 -0
  265. package/src/domains/providers/runtime-resolution.ts +38 -1
  266. package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
  267. package/src/domains/providers/types/context-window-slots.ts +18 -0
  268. package/src/domains/providers/types/runtime-descriptor.ts +3 -1
  269. package/src/domains/safety/autonomy.ts +1 -1
  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/default-path-policy.ts +8 -0
  273. package/src/domains/safety/finish-contract.ts +4 -3
  274. package/src/domains/safety/policy-engine.ts +48 -6
  275. package/src/domains/safety/redaction.ts +73 -0
  276. package/src/domains/session/compaction/compact.ts +23 -1
  277. package/src/domains/session/compaction/cut-point.ts +2 -0
  278. package/src/domains/session/compaction/tokens.ts +16 -1
  279. package/src/domains/session/context-ledger.ts +12 -1
  280. package/src/domains/session/decision-board.ts +4 -0
  281. package/src/domains/session/entries.ts +110 -1
  282. package/src/domains/session/history.ts +68 -19
  283. package/src/domains/session/manager.ts +9 -2
  284. package/src/domains/session/migrations/index.ts +22 -3
  285. package/src/domains/session/usage.ts +24 -7
  286. package/src/engine/acp/event-mapper.ts +7 -0
  287. package/src/engine/acp/server.ts +32 -2
  288. package/src/engine/agent.ts +18 -1
  289. package/src/engine/apis/lmstudio.ts +25 -4
  290. package/src/engine/apis/openai-completions.ts +147 -22
  291. package/src/engine/claude/sdk-runtime.ts +8 -2
  292. package/src/engine/claude/tool-safety.ts +13 -0
  293. package/src/engine/loop-guard.ts +27 -3
  294. package/src/engine/session.ts +9 -3
  295. package/src/engine/worker-events.ts +4 -3
  296. package/src/engine/worker-runtime.ts +59 -54
  297. package/src/entry/orchestrator.ts +34 -5
  298. package/src/interactive/chat-loop-messages.ts +40 -6
  299. package/src/interactive/chat-loop.ts +13 -0
  300. package/src/interactive/chat-panel.ts +17 -1
  301. package/src/interactive/chat-renderer.ts +49 -24
  302. package/src/interactive/clio-editor.ts +44 -7
  303. package/src/interactive/context-meter.ts +10 -0
  304. package/src/interactive/context-overlay.ts +120 -7
  305. package/src/interactive/context-recall-command.ts +110 -0
  306. package/src/interactive/cost-overlay.ts +39 -8
  307. package/src/interactive/dispatch-board.ts +212 -35
  308. package/src/interactive/footer/widgets.ts +13 -0
  309. package/src/interactive/interactive-application.ts +6 -1
  310. package/src/interactive/interactive-input-runtime.ts +11 -1
  311. package/src/interactive/interactive-presentation.ts +11 -1
  312. package/src/interactive/interactive-slash-runtime.ts +37 -1
  313. package/src/interactive/memory-overlay.ts +89 -4
  314. package/src/interactive/model-session-replay.ts +21 -0
  315. package/src/interactive/overlay-ask-user-lifecycle.ts +1 -1
  316. package/src/interactive/overlay-frame.ts +5 -2
  317. package/src/interactive/overlay-general-openers.ts +46 -1
  318. package/src/interactive/overlay-key-routing.ts +41 -1
  319. package/src/interactive/overlay-lifecycle.ts +11 -4
  320. package/src/interactive/overlay-permission-lifecycle.ts +23 -8
  321. package/src/interactive/overlay-session-lifecycle.ts +8 -4
  322. package/src/interactive/overlay-transitions.ts +11 -0
  323. package/src/interactive/overlays/ask-user.ts +74 -30
  324. package/src/interactive/overlays/decisions.ts +3 -1
  325. package/src/interactive/permission-hint.ts +35 -0
  326. package/src/interactive/permission-overlay.ts +95 -45
  327. package/src/interactive/renderers/tool-execution.ts +37 -51
  328. package/src/interactive/session-last-turn.ts +8 -1
  329. package/src/interactive/session-transcript.ts +2 -2
  330. package/src/interactive/session-usage-reseed.ts +36 -10
  331. package/src/interactive/slash-commands.ts +31 -4
  332. package/src/interactive/status/summary.ts +5 -0
  333. package/src/interactive/status/types.ts +5 -0
  334. package/src/interactive/terminal-lease.ts +1 -0
  335. package/src/interactive/turn-context.ts +333 -110
  336. package/src/interactive/turn-middleware.ts +7 -6
  337. package/src/interactive/turn-runtime.ts +37 -8
  338. package/src/interactive/turn-state.ts +3 -0
  339. package/src/interactive/worker-progress.ts +440 -0
  340. package/src/interactive/worker-stream.ts +51 -110
  341. package/src/tools/agent-tools.ts +39 -7
  342. package/src/tools/ask-user.ts +21 -1
  343. package/src/tools/bash.ts +144 -82
  344. package/src/tools/builtin-tool-catalog.ts +11 -5
  345. package/src/tools/context/index.ts +107 -5
  346. package/src/tools/context/surface.ts +3 -2
  347. package/src/tools/core-bootstrap.ts +21 -0
  348. package/src/tools/dispatch-arguments.ts +8 -0
  349. package/src/tools/dispatch-event-text.ts +19 -0
  350. package/src/tools/dispatch-runner.ts +9 -7
  351. package/src/tools/dispatch.ts +24 -1
  352. package/src/tools/monitor.ts +43 -20
  353. package/src/tools/registry.ts +72 -10
  354. package/src/tools/result-disposition.ts +706 -0
  355. package/src/tools/result-shaping.ts +321 -20
  356. package/src/tools/safe-exec.ts +2 -0
  357. package/src/tools/verify/authoring.ts +1120 -0
  358. package/src/tools/verify/catalog.ts +346 -0
  359. package/src/tools/verify/index.ts +13 -3
  360. package/src/tools/verify/scripts.ts +135 -37
  361. package/src/tools/verify/surface.ts +9 -5
  362. package/src/tools/worker-evidence.ts +54 -12
  363. package/src/worker/spec-contract.ts +43 -3
  364. package/dist/chunk-J7CWMCQD.js +0 -255
  365. package/dist/chunk-T6YILFSB.js +0 -80
  366. package/dist/chunk-VAKQQHWR.js +0 -434
  367. package/dist/chunk-VPAYEGVX.js +0 -184
  368. package/dist/chunk-XBXAASKX.js +0 -18
  369. package/dist/memory-WFZMGYHX.js +0 -236
  370. package/src/domains/eval/metrics/chaos-stream.ts +0 -93
@@ -0,0 +1,396 @@
1
+ import type { WorkerBudget } from "../../worker/spec-contract.js";
2
+ import type { AgentBudget, AgentBudgetPhase } from "../agents/recipe.js";
3
+
4
+ /** Invocation-level phase requested by dispatch. */
5
+ export interface DispatchBudgetPhase {
6
+ toolCalls: number;
7
+ readReserve: number;
8
+ }
9
+
10
+ /** Typed invocation request. The ceiling preauthorizes growth on a later phase. */
11
+ export interface DispatchBudgetRequest extends DispatchBudgetPhase {
12
+ retryRevision?: DispatchBudgetPhase;
13
+ }
14
+
15
+ export type BudgetEnvelopeReasonCode =
16
+ | "global-cap-clamp"
17
+ | "read-reserve-unavailable-clamp"
18
+ | "read-reserve-cap-clamp"
19
+ | "retry-growth-authorized"
20
+ | "retry-growth-denied"
21
+ | "revision-growth-authorized"
22
+ | "revision-growth-denied";
23
+
24
+ export interface BudgetEnvelopeReason {
25
+ code: BudgetEnvelopeReasonCode;
26
+ detail: string;
27
+ }
28
+
29
+ export interface RunToolBudgetEnvelope {
30
+ version: 1;
31
+ policy: {
32
+ recipeId: string;
33
+ default: AgentBudgetPhase & { synthesis: boolean };
34
+ maximum: AgentBudgetPhase;
35
+ exact: boolean;
36
+ };
37
+ request: DispatchBudgetRequest | null;
38
+ effective: WorkerBudget;
39
+ reasons: ReadonlyArray<BudgetEnvelopeReason>;
40
+ }
41
+
42
+ export type BudgetAdmissionDenialCode =
43
+ | "exact-recipe-policy"
44
+ | "recipe-maximum"
45
+ | "global-cap"
46
+ | "ceiling-below-request";
47
+
48
+ export class BudgetAdmissionError extends Error {
49
+ readonly code: BudgetAdmissionDenialCode;
50
+
51
+ constructor(code: BudgetAdmissionDenialCode, message: string) {
52
+ super(`dispatch: budget admission denied (${code}): ${message}`);
53
+ this.name = "BudgetAdmissionError";
54
+ this.code = code;
55
+ }
56
+ }
57
+
58
+ function isRecord(value: unknown): value is Record<string, unknown> {
59
+ return typeof value === "object" && value !== null && !Array.isArray(value);
60
+ }
61
+
62
+ function clonePhase(value: unknown, prefix: string): DispatchBudgetPhase {
63
+ if (!isRecord(value)) throw new Error(`${prefix} must be an object`);
64
+ for (const key of Object.keys(value)) {
65
+ if (key !== "toolCalls" && key !== "readReserve") throw new Error(`${prefix}.${key} is unknown`);
66
+ }
67
+ for (const key of ["toolCalls", "readReserve"] as const) {
68
+ if (!Object.hasOwn(value, key)) throw new Error(`${prefix}.${key} is required`);
69
+ }
70
+ const toolCalls = value.toolCalls;
71
+ if (typeof toolCalls !== "number" || !Number.isSafeInteger(toolCalls) || toolCalls <= 0) {
72
+ throw new Error(`${prefix}.toolCalls must be a positive safe integer`);
73
+ }
74
+ const readReserve = value.readReserve;
75
+ if (typeof readReserve !== "number" || !Number.isSafeInteger(readReserve) || readReserve < 0) {
76
+ throw new Error(`${prefix}.readReserve must be a non-negative safe integer`);
77
+ }
78
+ if (readReserve >= toolCalls) throw new Error(`${prefix}.readReserve must be less than ${prefix}.toolCalls`);
79
+ return { toolCalls, readReserve };
80
+ }
81
+
82
+ /** Strict validation and cloning for model, CLI, and persisted request inputs. */
83
+ export function cloneDispatchBudgetRequest(value: unknown, prefix = "budget"): DispatchBudgetRequest {
84
+ if (!isRecord(value)) throw new Error(`${prefix} must be an object`);
85
+ for (const key of Object.keys(value)) {
86
+ if (key !== "toolCalls" && key !== "readReserve" && key !== "retryRevision") {
87
+ throw new Error(`${prefix}.${key} is unknown`);
88
+ }
89
+ }
90
+ const phase = clonePhase({ toolCalls: value.toolCalls, readReserve: value.readReserve }, prefix);
91
+ const retryRevision =
92
+ value.retryRevision === undefined ? undefined : clonePhase(value.retryRevision, `${prefix}.retryRevision`);
93
+ return { ...phase, ...(retryRevision === undefined ? {} : { retryRevision }) };
94
+ }
95
+
96
+ function freezeEnvelope(envelope: RunToolBudgetEnvelope): RunToolBudgetEnvelope {
97
+ Object.freeze(envelope.policy.default);
98
+ Object.freeze(envelope.policy.maximum);
99
+ Object.freeze(envelope.policy);
100
+ if (envelope.request !== null) {
101
+ if (envelope.request.retryRevision !== undefined) Object.freeze(envelope.request.retryRevision);
102
+ Object.freeze(envelope.request);
103
+ }
104
+ Object.freeze(envelope.effective);
105
+ for (const reason of envelope.reasons) Object.freeze(reason);
106
+ Object.freeze(envelope.reasons);
107
+ return Object.freeze(envelope);
108
+ }
109
+
110
+ function phaseWithin(candidate: DispatchBudgetPhase, maximum: AgentBudgetPhase): boolean {
111
+ return candidate.toolCalls <= maximum.toolCalls && candidate.readReserve <= maximum.readReserve;
112
+ }
113
+
114
+ function samePhase(left: DispatchBudgetPhase, right: AgentBudgetPhase): boolean {
115
+ return left.toolCalls === right.toolCalls && left.readReserve === right.readReserve;
116
+ }
117
+
118
+ function assertRequestAdmitted(input: { request: DispatchBudgetRequest; policy: AgentBudget; hardCap: number }): void {
119
+ const { request, policy, hardCap } = input;
120
+ const authoredMaximum = policy.maximum ?? policy;
121
+ if (policy.maximum === undefined && !samePhase(request, policy)) {
122
+ throw new BudgetAdmissionError(
123
+ "exact-recipe-policy",
124
+ `the recipe pins ${policy.toolCalls} tool calls with a read reserve of ${policy.readReserve}`,
125
+ );
126
+ }
127
+ if (!phaseWithin(request, authoredMaximum)) {
128
+ throw new BudgetAdmissionError(
129
+ "recipe-maximum",
130
+ `the requested phase ${request.toolCalls}/${request.readReserve} exceeds recipe maximum ${authoredMaximum.toolCalls}/${authoredMaximum.readReserve}`,
131
+ );
132
+ }
133
+ if (request.toolCalls > hardCap) {
134
+ throw new BudgetAdmissionError(
135
+ "global-cap",
136
+ `the requested ${request.toolCalls} tool calls exceed the operator cap of ${hardCap}`,
137
+ );
138
+ }
139
+ const ceiling = request.retryRevision;
140
+ if (ceiling === undefined) return;
141
+ if (ceiling.toolCalls < request.toolCalls || ceiling.readReserve < request.readReserve) {
142
+ throw new BudgetAdmissionError(
143
+ "ceiling-below-request",
144
+ `retryRevision ${ceiling.toolCalls}/${ceiling.readReserve} must not be smaller than the requested phase ${request.toolCalls}/${request.readReserve}`,
145
+ );
146
+ }
147
+ if (policy.maximum === undefined && !samePhase(ceiling, policy)) {
148
+ throw new BudgetAdmissionError(
149
+ "exact-recipe-policy",
150
+ `the recipe pins ${policy.toolCalls} tool calls with a read reserve of ${policy.readReserve}`,
151
+ );
152
+ }
153
+ if (!phaseWithin(ceiling, authoredMaximum)) {
154
+ throw new BudgetAdmissionError(
155
+ "recipe-maximum",
156
+ `retryRevision ${ceiling.toolCalls}/${ceiling.readReserve} exceeds recipe maximum ${authoredMaximum.toolCalls}/${authoredMaximum.readReserve}`,
157
+ );
158
+ }
159
+ if (ceiling.toolCalls > hardCap) {
160
+ throw new BudgetAdmissionError(
161
+ "global-cap",
162
+ `retryRevision ${ceiling.toolCalls} exceeds the operator cap of ${hardCap}`,
163
+ );
164
+ }
165
+ }
166
+
167
+ export interface ResolveToolBudgetEnvelopeInput {
168
+ recipeId: string;
169
+ policy: AgentBudget;
170
+ request?: DispatchBudgetRequest;
171
+ hardCap: number;
172
+ hasReadTool: boolean;
173
+ retry: boolean;
174
+ revision: boolean;
175
+ }
176
+
177
+ /** Resolve the one immutable envelope used by admission, enforcement, and evidence. */
178
+ export function resolveToolBudgetEnvelope(input: ResolveToolBudgetEnvelopeInput): RunToolBudgetEnvelope {
179
+ if (!Number.isSafeInteger(input.hardCap) || input.hardCap <= 0) {
180
+ throw new Error("dispatch: worker tool-call cap must be a positive safe integer");
181
+ }
182
+ if (input.request !== undefined)
183
+ assertRequestAdmitted({ request: input.request, policy: input.policy, hardCap: input.hardCap });
184
+
185
+ const request = input.request === undefined ? null : cloneDispatchBudgetRequest(input.request);
186
+ const base = request ?? input.policy;
187
+ const reasons: BudgetEnvelopeReason[] = [];
188
+ let selected: DispatchBudgetPhase = base;
189
+ const phaseKind = input.retry ? "retry" : input.revision ? "revision" : null;
190
+ if (phaseKind !== null) {
191
+ const ceiling = request?.retryRevision;
192
+ if (ceiling !== undefined && !samePhase(ceiling, base)) {
193
+ selected = ceiling;
194
+ reasons.push({
195
+ code: phaseKind === "retry" ? "retry-growth-authorized" : "revision-growth-authorized",
196
+ detail: `${phaseKind} phase grew from ${base.toolCalls}/${base.readReserve} to the preauthorized ceiling ${ceiling.toolCalls}/${ceiling.readReserve}`,
197
+ });
198
+ } else {
199
+ reasons.push({
200
+ code: phaseKind === "retry" ? "retry-growth-denied" : "revision-growth-denied",
201
+ detail:
202
+ ceiling === undefined
203
+ ? `${phaseKind} phase retained ${base.toolCalls}/${base.readReserve} because the original request declared no ceiling`
204
+ : `${phaseKind} phase retained ${base.toolCalls}/${base.readReserve} because the declared ceiling did not authorize growth`,
205
+ });
206
+ }
207
+ }
208
+ const revisionCeiling = phaseKind === null ? request?.retryRevision : undefined;
209
+ if (revisionCeiling !== undefined && !samePhase(revisionCeiling, selected)) {
210
+ reasons.push({
211
+ code: "revision-growth-authorized",
212
+ detail: `a result-contract revision may grow from ${selected.toolCalls}/${selected.readReserve} to the preauthorized ceiling ${revisionCeiling.toolCalls}/${revisionCeiling.readReserve}`,
213
+ });
214
+ }
215
+
216
+ let toolCalls = selected.toolCalls;
217
+ if (toolCalls > input.hardCap) {
218
+ reasons.push({
219
+ code: "global-cap-clamp",
220
+ detail: `tool calls were clamped from ${toolCalls} to the operator cap of ${input.hardCap}`,
221
+ });
222
+ toolCalls = input.hardCap;
223
+ }
224
+ let readReserve = selected.readReserve;
225
+ if (!input.hasReadTool && readReserve > 0) {
226
+ reasons.push({
227
+ code: "read-reserve-unavailable-clamp",
228
+ detail: `read reserve was clamped from ${readReserve} to 0 because the read tool is unavailable`,
229
+ });
230
+ readReserve = 0;
231
+ }
232
+ const maximumEffectiveReserve = Math.max(0, toolCalls - 1);
233
+ if (readReserve > maximumEffectiveReserve) {
234
+ reasons.push({
235
+ code: "read-reserve-cap-clamp",
236
+ detail: `read reserve was clamped from ${readReserve} to ${maximumEffectiveReserve} after the tool-call cap`,
237
+ });
238
+ readReserve = maximumEffectiveReserve;
239
+ }
240
+ let effectiveRevision: DispatchBudgetPhase | undefined;
241
+ if (revisionCeiling !== undefined && !samePhase(revisionCeiling, selected)) {
242
+ let revisionReadReserve = revisionCeiling.readReserve;
243
+ if (!input.hasReadTool && revisionReadReserve > 0) {
244
+ reasons.push({
245
+ code: "read-reserve-unavailable-clamp",
246
+ detail: `result-contract revision read reserve was clamped from ${revisionReadReserve} to 0 because the read tool is unavailable`,
247
+ });
248
+ revisionReadReserve = 0;
249
+ }
250
+ effectiveRevision = { toolCalls: revisionCeiling.toolCalls, readReserve: revisionReadReserve };
251
+ }
252
+
253
+ const authoredMaximum = input.policy.maximum ?? input.policy;
254
+ return freezeEnvelope({
255
+ version: 1,
256
+ policy: {
257
+ recipeId: input.recipeId,
258
+ default: {
259
+ toolCalls: input.policy.toolCalls,
260
+ readReserve: input.policy.readReserve,
261
+ synthesis: input.policy.synthesis,
262
+ },
263
+ maximum: { toolCalls: authoredMaximum.toolCalls, readReserve: authoredMaximum.readReserve },
264
+ exact: input.policy.maximum === undefined,
265
+ },
266
+ request,
267
+ effective: {
268
+ toolCalls,
269
+ readReserve,
270
+ synthesis: input.policy.synthesis,
271
+ hardCap: input.hardCap,
272
+ ...(effectiveRevision === undefined ? {} : { revision: effectiveRevision }),
273
+ },
274
+ reasons,
275
+ });
276
+ }
277
+
278
+ export function formatBudgetPolicy(envelope: RunToolBudgetEnvelope): string {
279
+ const policy = envelope.policy;
280
+ const range = policy.exact
281
+ ? `exact ${policy.default.toolCalls}/${policy.default.readReserve}`
282
+ : `default ${policy.default.toolCalls}/${policy.default.readReserve}, max ${policy.maximum.toolCalls}/${policy.maximum.readReserve}`;
283
+ return `${policy.recipeId} ${range}, synthesis=${policy.default.synthesis ? "on" : "off"}`;
284
+ }
285
+
286
+ export function formatBudgetRequest(envelope: RunToolBudgetEnvelope): string {
287
+ const request = envelope.request;
288
+ if (request === null) return "recipe default";
289
+ const ceiling = request.retryRevision;
290
+ return ceiling === undefined
291
+ ? `${request.toolCalls}/${request.readReserve}, no retry/revision growth`
292
+ : `${request.toolCalls}/${request.readReserve}, retry/revision ceiling ${ceiling.toolCalls}/${ceiling.readReserve}`;
293
+ }
294
+
295
+ export function formatEffectiveBudget(envelope: RunToolBudgetEnvelope): string {
296
+ const budget = envelope.effective;
297
+ const revision = budget.revision
298
+ ? `, result revision ${budget.revision.toolCalls}/${budget.revision.readReserve}`
299
+ : "";
300
+ return `${budget.toolCalls}/${budget.readReserve}${revision}, lifetime cap ${budget.hardCap}, synthesis=${budget.synthesis ? "on" : "off"}`;
301
+ }
302
+
303
+ export function formatBudgetReasons(envelope: RunToolBudgetEnvelope): string {
304
+ return envelope.reasons.length === 0 ? "none" : envelope.reasons.map((reason) => reason.code).join(",");
305
+ }
306
+
307
+ const BUDGET_REASON_CODES: ReadonlySet<string> = new Set<BudgetEnvelopeReasonCode>([
308
+ "global-cap-clamp",
309
+ "read-reserve-unavailable-clamp",
310
+ "read-reserve-cap-clamp",
311
+ "retry-growth-authorized",
312
+ "retry-growth-denied",
313
+ "revision-growth-authorized",
314
+ "revision-growth-denied",
315
+ ]);
316
+
317
+ /** Safely project an envelope that crossed a persistence or event boundary. */
318
+ export function cloneRunToolBudgetEnvelope(value: unknown): RunToolBudgetEnvelope | undefined {
319
+ try {
320
+ if (!isRecord(value) || value.version !== 1 || !isRecord(value.policy) || !isRecord(value.effective)) {
321
+ return undefined;
322
+ }
323
+ const recipeId = value.policy.recipeId;
324
+ if (typeof recipeId !== "string" || recipeId.length === 0 || typeof value.policy.exact !== "boolean") {
325
+ return undefined;
326
+ }
327
+ if (!isRecord(value.policy.default) || typeof value.policy.default.synthesis !== "boolean") return undefined;
328
+ const defaultPhase = clonePhase(
329
+ { toolCalls: value.policy.default.toolCalls, readReserve: value.policy.default.readReserve },
330
+ "budget.policy.default",
331
+ );
332
+ const maximum = clonePhase(value.policy.maximum, "budget.policy.maximum");
333
+ const request = value.request === null ? null : cloneDispatchBudgetRequest(value.request);
334
+ const effectivePhase = clonePhase(
335
+ { toolCalls: value.effective.toolCalls, readReserve: value.effective.readReserve },
336
+ "budget.effective",
337
+ );
338
+ const revision =
339
+ value.effective.revision === undefined
340
+ ? undefined
341
+ : clonePhase(value.effective.revision, "budget.effective.revision");
342
+ if (
343
+ typeof value.effective.synthesis !== "boolean" ||
344
+ typeof value.effective.hardCap !== "number" ||
345
+ !Number.isSafeInteger(value.effective.hardCap) ||
346
+ value.effective.hardCap <= 0 ||
347
+ !Array.isArray(value.reasons)
348
+ ) {
349
+ return undefined;
350
+ }
351
+ if (
352
+ maximum.toolCalls < defaultPhase.toolCalls ||
353
+ maximum.readReserve < defaultPhase.readReserve ||
354
+ (value.policy.exact && !samePhase(maximum, defaultPhase)) ||
355
+ effectivePhase.toolCalls > value.effective.hardCap ||
356
+ value.effective.synthesis !== value.policy.default.synthesis ||
357
+ (revision !== undefined &&
358
+ (revision.toolCalls < effectivePhase.toolCalls ||
359
+ revision.toolCalls > value.effective.hardCap ||
360
+ (revision.toolCalls === effectivePhase.toolCalls && revision.readReserve <= effectivePhase.readReserve)))
361
+ ) {
362
+ return undefined;
363
+ }
364
+ const reasons: BudgetEnvelopeReason[] = [];
365
+ for (const reason of value.reasons) {
366
+ if (
367
+ !isRecord(reason) ||
368
+ typeof reason.code !== "string" ||
369
+ !BUDGET_REASON_CODES.has(reason.code) ||
370
+ typeof reason.detail !== "string"
371
+ ) {
372
+ return undefined;
373
+ }
374
+ reasons.push({ code: reason.code as BudgetEnvelopeReasonCode, detail: reason.detail });
375
+ }
376
+ return freezeEnvelope({
377
+ version: 1,
378
+ policy: {
379
+ recipeId,
380
+ default: { ...defaultPhase, synthesis: value.policy.default.synthesis },
381
+ maximum,
382
+ exact: value.policy.exact,
383
+ },
384
+ request,
385
+ effective: {
386
+ ...effectivePhase,
387
+ synthesis: value.effective.synthesis,
388
+ hardCap: value.effective.hardCap,
389
+ ...(revision === undefined ? {} : { revision }),
390
+ },
391
+ reasons,
392
+ });
393
+ } catch {
394
+ return undefined;
395
+ }
396
+ }
@@ -6,6 +6,7 @@ import type { ProtectedArtifactState } from "../safety/protected-artifacts.js";
6
6
  import type { AssignmentId, DispatchAssignment } from "./assignment.js";
7
7
  import type { DurableAssignmentRecord } from "./assignment-store.js";
8
8
  import type { DetachedBatchRecord, RegisterDetachedBatchInput } from "./batch-store.js";
9
+ import type { RunToolBudgetEnvelope } from "./budget-envelope.js";
9
10
  import type { ExecutionRole } from "./execution-role.js";
10
11
  import type { DispatchReservationRecord, ReservationTopology } from "./reservation-store.js";
11
12
  import type { ApprovedAssignmentRoute } from "./route-approval.js";
@@ -111,6 +112,7 @@ export interface DispatchSnapshot {
111
112
  outcomePhase: string;
112
113
  heartbeat: "alive" | "stale" | "dead" | "n/a";
113
114
  lineage: RunLineage;
115
+ budget?: RunToolBudgetEnvelope;
114
116
  startedAt: string;
115
117
  /** Execution-only elapsed time retained for runtime monitoring. */
116
118
  elapsedMs: number;
@@ -19,6 +19,7 @@ import { GUARDRAIL_DEFAULTS } from "../../core/guardrails.js";
19
19
  import { readClioVersion, readPiMonoVersion } from "../../core/package-root.js";
20
20
  import { canonicalizeExistingPath } from "../../core/path-canonical.js";
21
21
  import { protectedResidencyModels } from "../../core/residency-protection.js";
22
+ import { responseModelIdObservationFromRecord } from "../../core/response-model-id.js";
22
23
  import {
23
24
  responseSchemaConflictsWithTools,
24
25
  runtimeSpeaksResponseSchemaDialect,
@@ -134,6 +135,7 @@ import {
134
135
  registerDetachedBatch,
135
136
  } from "./batch-store.js";
136
137
  import { type BatchState, createBatch, onRunComplete, snapshotBatch } from "./batch-tracker.js";
138
+ import { type RunToolBudgetEnvelope, resolveToolBudgetEnvelope } from "./budget-envelope.js";
137
139
  import { assessCapabilityMismatch, type CapabilityMismatch } from "./capability-match.js";
138
140
  import { capacityLeaseUsage, createNodeLeaseUsageReader } from "./capacity-lease.js";
139
141
  import type {
@@ -307,6 +309,7 @@ interface ActiveRun {
307
309
  requestOrigin?: DispatchRequestOrigin;
308
310
  agentId: string;
309
311
  task: string;
312
+ budget?: RunToolBudgetEnvelope;
310
313
  cwd: string;
311
314
  /** Fleet node this run was placed on; null when no placement resolved it. */
312
315
  node: RunNodeIdentity | null;
@@ -1287,6 +1290,7 @@ interface DispatchLifecycleStage {
1287
1290
  capabilityMismatch: CapabilityMismatch | null;
1288
1291
  effectiveAutonomy: AutonomyLevel;
1289
1292
  budget: WorkerBudget;
1293
+ budgetEnvelope: RunToolBudgetEnvelope;
1290
1294
  settings?: Readonly<ReturnType<ConfigContract["get"]>>;
1291
1295
  }
1292
1296
 
@@ -1411,7 +1415,7 @@ function assertWriteRootsEnforceable(runtime: RuntimeDescriptor, writeRoots: Rea
1411
1415
  function assertWorkerBudgetEnforceable(runtime: RuntimeDescriptor, hasDeclaredBudget: boolean): void {
1412
1416
  if (!hasDeclaredBudget || runtime.kind !== "subprocess") return;
1413
1417
  throw new Error(
1414
- `dispatch: runtime '${runtime.id}' cannot enforce an explicit agent budget because subprocess workers do not expose per-tool mediation; choose a native or claude-sdk worker`,
1418
+ `dispatch: runtime '${runtime.id}' cannot enforce an explicit dispatch budget because subprocess workers do not expose per-tool mediation; choose a native or claude-sdk worker`,
1415
1419
  );
1416
1420
  }
1417
1421
 
@@ -1543,26 +1547,36 @@ function assertPostRuntimeToolCompatibility(
1543
1547
  );
1544
1548
  }
1545
1549
 
1550
+ function workerToolCallHardCap(settings: EffectiveSettings): number {
1551
+ const rawEnvCap = process.env.CLIO_CODER_WORKER_TOOL_CALL_CAP?.trim();
1552
+ const parsedEnvCap = rawEnvCap && /^[1-9]\d*$/.test(rawEnvCap) ? Number(rawEnvCap) : Number.NaN;
1553
+ return Number.isSafeInteger(parsedEnvCap)
1554
+ ? parsedEnvCap
1555
+ : (settings?.guardrails.workerToolCallCap ?? GUARDRAIL_DEFAULTS.workerToolCallCap);
1556
+ }
1557
+
1546
1558
  function resolveEffectiveWorkerBudget(input: {
1559
+ req: DispatchRequest;
1560
+ recipeId: string;
1547
1561
  declared: ReturnType<typeof normalizeAgentSpec>["budget"];
1548
1562
  allowedTools: ReadonlyArray<ToolName>;
1549
1563
  settings: EffectiveSettings;
1550
- }): WorkerBudget {
1551
- const rawEnvCap = process.env.CLIO_CODER_WORKER_TOOL_CALL_CAP?.trim();
1552
- const parsedEnvCap = rawEnvCap && /^[1-9]\d*$/.test(rawEnvCap) ? Number(rawEnvCap) : Number.NaN;
1553
- const hardCap = Number.isSafeInteger(parsedEnvCap)
1554
- ? parsedEnvCap
1555
- : (input.settings?.guardrails.workerToolCallCap ?? GUARDRAIL_DEFAULTS.workerToolCallCap);
1564
+ }): RunToolBudgetEnvelope {
1565
+ const hardCap = workerToolCallHardCap(input.settings);
1556
1566
  const declared = input.declared ?? {
1557
1567
  toolCalls: hardCap,
1558
1568
  readReserve: Math.min(5, Math.max(0, hardCap - 1)),
1559
1569
  synthesis: true,
1560
1570
  };
1561
- const toolCalls = Math.min(declared.toolCalls, hardCap);
1562
- const readReserve = input.allowedTools.includes(ToolNames.Read)
1563
- ? Math.min(declared.readReserve, Math.max(0, toolCalls - 1))
1564
- : 0;
1565
- return { toolCalls, readReserve, synthesis: declared.synthesis, hardCap };
1571
+ return resolveToolBudgetEnvelope({
1572
+ recipeId: input.recipeId,
1573
+ policy: declared,
1574
+ ...(input.req.budget === undefined ? {} : { request: input.req.budget }),
1575
+ hardCap,
1576
+ hasReadTool: input.allowedTools.includes(ToolNames.Read),
1577
+ retry: (input.req.lineage?.attempt ?? 0) > 0,
1578
+ revision: input.req.gate?.role === "builder" && input.req.gate.cycle > 1 && input.req.gate.verdict === "revise",
1579
+ });
1566
1580
  }
1567
1581
 
1568
1582
  function frozenProtectedArtifactState(state: ProtectedArtifactState | undefined): ProtectedArtifactState {
@@ -3193,7 +3207,7 @@ export function createDispatchBundle(
3193
3207
  );
3194
3208
  enforceCapabilityGate(target.target.id, target.modelCapabilities, req.requiredCapabilities);
3195
3209
  assertRuntimeCanHonorWorkerPermissionMode(target.runtime, settings?.workers.onPermission ?? "deny");
3196
- assertWorkerBudgetEnforceable(target.runtime, spec.budget !== null);
3210
+ assertWorkerBudgetEnforceable(target.runtime, spec.budget !== null || req.budget !== undefined);
3197
3211
 
3198
3212
  const cwd = req.cwd ?? process.cwd();
3199
3213
  const sessionAutonomy = settings?.autonomy ?? "auto-edit";
@@ -3231,7 +3245,13 @@ export function createDispatchBundle(
3231
3245
  workingContextPaths: workerWorkingContextPaths(req),
3232
3246
  });
3233
3247
  const systemPrompt = compiledWorkerPrompt.systemPrompt;
3234
- const budget = resolveEffectiveWorkerBudget({ declared: spec.budget, allowedTools: effectiveTools, settings });
3248
+ const budgetEnvelope = resolveEffectiveWorkerBudget({
3249
+ req,
3250
+ recipeId: recipe.id,
3251
+ declared: spec.budget,
3252
+ allowedTools: effectiveTools,
3253
+ settings,
3254
+ });
3235
3255
  // Fetch structured project context only for tiers that receive it, so
3236
3256
  // read-only scouts never pay the CLIO-CODER.md read. The tier is spec policy
3237
3257
  // (capability-class default, recipe frontmatter override).
@@ -3291,7 +3311,8 @@ export function createDispatchBundle(
3291
3311
  operatorProfileApplied: compiledWorkerPrompt.operatorProfileApplied ?? false,
3292
3312
  capabilityMismatch,
3293
3313
  effectiveAutonomy,
3294
- budget,
3314
+ budget: budgetEnvelope.effective,
3315
+ budgetEnvelope,
3295
3316
  ...(settings ? { settings } : {}),
3296
3317
  };
3297
3318
  }
@@ -3473,6 +3494,7 @@ export function createDispatchBundle(
3473
3494
  role?: string;
3474
3495
  usage?: unknown;
3475
3496
  model?: unknown;
3497
+ responseModelIdObservation?: unknown;
3476
3498
  responseModel?: unknown;
3477
3499
  responseId?: unknown;
3478
3500
  stopReason?: unknown;
@@ -3522,12 +3544,16 @@ export function createDispatchBundle(
3522
3544
  tokenMeter.cacheReadTokens += typeof u.cacheRead === "number" ? u.cacheRead : 0;
3523
3545
  tokenMeter.cacheWriteTokens += typeof u.cacheWrite === "number" ? u.cacheWrite : 0;
3524
3546
  tokenMeter.reasoningTokens += extractReasoningTokenCount(u);
3525
- const model = readStringOrNull(event.message.model);
3526
- const responseModel = readStringOrNull(event.message.responseModel);
3527
- const responseId = readStringOrNull(event.message.responseId);
3528
- if (model !== null || responseModel !== null || responseId !== null) {
3529
- upstreamResponses.push({ model, responseModel, responseId });
3530
- }
3547
+ const requestedModelId = readStringOrNull(event.message.model);
3548
+ const responseModelIdObservation = responseModelIdObservationFromRecord(event.message, "not-observed");
3549
+ const differingResponseModelId = readStringOrNull(event.message.responseModel);
3550
+ const providerResponseId = readStringOrNull(event.message.responseId);
3551
+ upstreamResponses.push({
3552
+ requestedModelId,
3553
+ responseModelIdObservation,
3554
+ differingResponseModelId,
3555
+ providerResponseId,
3556
+ });
3531
3557
  if (event.message.stopReason === "error") {
3532
3558
  const message = readStringOrNull(event.message.errorMessage);
3533
3559
  if (message !== null) failureMessage = message;
@@ -4142,6 +4168,11 @@ export function createDispatchBundle(
4142
4168
  return handle;
4143
4169
  };
4144
4170
  if (req.delegationAgentId) {
4171
+ if (req.budget !== undefined) {
4172
+ throw new Error(
4173
+ "dispatch: budget envelopes cannot be enforced on an ACP delegation target; dispatch to a native or claude-sdk worker",
4174
+ );
4175
+ }
4145
4176
  assertPlannedNodeIdentity(req, { id: "local", kind: "local" });
4146
4177
  assertProtectedArtifactsEnforceable("acp-delegation", false, protectedArtifactState);
4147
4178
  if (req.responseSchema !== undefined) {
@@ -4372,6 +4403,7 @@ export function createDispatchBundle(
4372
4403
  role?: string;
4373
4404
  usage?: unknown;
4374
4405
  model?: unknown;
4406
+ responseModelIdObservation?: unknown;
4375
4407
  responseModel?: unknown;
4376
4408
  responseId?: unknown;
4377
4409
  stopReason?: unknown;
@@ -4477,12 +4509,16 @@ export function createDispatchBundle(
4477
4509
  tokenMeter.cacheReadTokens += typeof u.cacheRead === "number" ? u.cacheRead : 0;
4478
4510
  tokenMeter.cacheWriteTokens += typeof u.cacheWrite === "number" ? u.cacheWrite : 0;
4479
4511
  tokenMeter.reasoningTokens += extractReasoningTokenCount(u);
4480
- const model = readStringOrNull(event.message.model);
4481
- const responseModel = readStringOrNull(event.message.responseModel);
4482
- const responseId = readStringOrNull(event.message.responseId);
4483
- if (model !== null || responseModel !== null || responseId !== null) {
4484
- upstreamResponses.push({ model, responseModel, responseId });
4485
- }
4512
+ const requestedModelId = readStringOrNull(event.message.model);
4513
+ const responseModelIdObservation = responseModelIdObservationFromRecord(event.message, "not-observed");
4514
+ const differingResponseModelId = readStringOrNull(event.message.responseModel);
4515
+ const providerResponseId = readStringOrNull(event.message.responseId);
4516
+ upstreamResponses.push({
4517
+ requestedModelId,
4518
+ responseModelIdObservation,
4519
+ differingResponseModelId,
4520
+ providerResponseId,
4521
+ });
4486
4522
  if (event.message.stopReason === "error") {
4487
4523
  const message = readStringOrNull(event.message.errorMessage);
4488
4524
  if (message !== null) failureMessage = message;
@@ -4573,6 +4609,7 @@ export function createDispatchBundle(
4573
4609
  agentAudience: lifecycle.agentAudience,
4574
4610
  requestOrigin: lifecycle.requestOrigin,
4575
4611
  task: req.task,
4612
+ budget: lifecycle.budgetEnvelope,
4576
4613
  targetId: lifecycle.target.target.id,
4577
4614
  wireModelId: lifecycle.target.wireModelId,
4578
4615
  runtimeId: lifecycle.target.runtime.id,
@@ -4638,6 +4675,7 @@ export function createDispatchBundle(
4638
4675
  // reroute lineage depth, and the model context window for the
4639
4676
  // per-worker meter. All optional so consumers degrade to local/plain.
4640
4677
  const fleetIdentity = {
4678
+ budget: lifecycle.budgetEnvelope,
4641
4679
  ...(placement !== undefined && placement !== null ? { node: placement.node.id } : {}),
4642
4680
  ...(req.gate !== undefined ? { gate: { role: req.gate.role, cycle: req.gate.cycle } } : {}),
4643
4681
  ...(req.reroutes !== undefined && req.reroutes.length > 0 ? { rerouteCount: req.reroutes.length } : {}),
@@ -4743,6 +4781,7 @@ export function createDispatchBundle(
4743
4781
  requestOrigin: lifecycle.requestOrigin,
4744
4782
  agentId: req.agentId,
4745
4783
  task: req.task,
4784
+ budget: lifecycle.budgetEnvelope,
4746
4785
  cwd: lifecycle.cwd,
4747
4786
  node: placement?.node ?? null,
4748
4787
  aborted: false,
@@ -4817,6 +4856,7 @@ export function createDispatchBundle(
4817
4856
  agentAudience: lifecycle.agentAudience,
4818
4857
  requestOrigin: lifecycle.requestOrigin,
4819
4858
  task: req.task,
4859
+ budget: lifecycle.budgetEnvelope,
4820
4860
  targetId: lifecycle.target.target.id,
4821
4861
  wireModelId: lifecycle.target.wireModelId,
4822
4862
  runtimeId: lifecycle.target.runtime.id,
@@ -5306,6 +5346,11 @@ export function createDispatchBundle(
5306
5346
  req = validation.restore(validated.spec);
5307
5347
 
5308
5348
  if (req.delegationAgentId) {
5349
+ if (req.budget !== undefined) {
5350
+ throw new Error(
5351
+ "dispatch: budget envelopes cannot be enforced on an ACP delegation target; dispatch to a native or claude-sdk worker",
5352
+ );
5353
+ }
5309
5354
  const protectedArtifactState = getProtectedArtifactState();
5310
5355
  assertProtectedArtifactsEnforceable("acp-delegation", false, protectedArtifactState);
5311
5356
  if (req.responseSchema !== undefined) {
@@ -5369,6 +5414,7 @@ export function createDispatchBundle(
5369
5414
  );
5370
5415
  assertPostRuntimeToolCompatibility(req.agentId, agentSpec, effectiveTools, target);
5371
5416
  assertRuntimeCanHonorWorkerPermissionMode(target.runtime, settings?.workers.onPermission ?? "deny");
5417
+ assertWorkerBudgetEnforceable(target.runtime, agentSpec.budget !== null || req.budget !== undefined);
5372
5418
  assertResponseSchemaEnforceable(target.runtime, target.modelCapabilities, req.responseSchema, effectiveTools.length);
5373
5419
  assertWriteRootsEnforceable(target.runtime, req.writeRoots);
5374
5420
  assertProtectedArtifactsEnforceable(
@@ -5376,6 +5422,13 @@ export function createDispatchBundle(
5376
5422
  target.runtime.kind !== "subprocess",
5377
5423
  getProtectedArtifactState(),
5378
5424
  );
5425
+ resolveEffectiveWorkerBudget({
5426
+ req,
5427
+ recipeId: recipe.id,
5428
+ declared: agentSpec.budget,
5429
+ allowedTools: effectiveTools,
5430
+ settings,
5431
+ });
5379
5432
  return {
5380
5433
  agentId: req.agentId,
5381
5434
  specFingerprint: agentSpecFingerprint(agentSpec),
@@ -5773,6 +5826,7 @@ export function createDispatchBundle(
5773
5826
  runId: run.runId,
5774
5827
  agentId: run.agentId,
5775
5828
  task: run.task,
5829
+ ...(run.budget !== undefined ? { budget: run.budget } : {}),
5776
5830
  runtimeKind: run.runtimeKind,
5777
5831
  outcomePhase: run.stallKilled ? "terminating" : run.aborted ? "aborting" : "running",
5778
5832
  heartbeat,
@@ -55,6 +55,7 @@ function envelopeFromReceipt(receipt: RunReceipt, status: RunStatus, receiptPath
55
55
  ...(receipt.agentAudience !== undefined ? { agentAudience: receipt.agentAudience } : {}),
56
56
  ...(receipt.requestOrigin !== undefined ? { requestOrigin: receipt.requestOrigin } : {}),
57
57
  task: receipt.task,
58
+ ...(receipt.budget !== undefined ? { budget: receipt.budget } : {}),
58
59
  ...(receipt.briefing !== undefined ? { briefing: receipt.briefing } : {}),
59
60
  ...(receipt.steering !== undefined ? { steering: receipt.steering } : {}),
60
61
  targetId: receipt.targetId,