@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
@@ -1,12 +1,7 @@
1
1
  import { createRequire as __clioCreateRequire } from "node:module"; const require = __clioCreateRequire(import.meta.url);
2
2
  import {
3
- DEFAULT_AUTONOMY_LEVEL,
4
- autonomyAskRejection,
5
- autonomyDenyRejection,
6
- isAutonomyLevel,
7
- mapAutonomy,
8
3
  typedValidationSummary
9
- } from "./chunk-STBPMHSX.js";
4
+ } from "./chunk-EKY57CSP.js";
10
5
  import {
11
6
  ROUTE_POLICY_VERSION,
12
7
  deriveExecutionRole,
@@ -15,36 +10,47 @@ import {
15
10
  isRoutingIntent,
16
11
  routePriorForLatencyClass,
17
12
  sameRouteIdentity
18
- } from "./chunk-GGXXDWE4.js";
13
+ } from "./chunk-22NAGB7X.js";
19
14
  import {
20
15
  AGENT_AUTOMATION_AUTHORITIES,
21
- agentSpecFingerprint,
16
+ agentSpecFingerprint
17
+ } from "./chunk-AOCYTWAV.js";
18
+ import {
22
19
  detectValidationCommand,
23
20
  extractCommandDeleteTargets,
24
21
  extractCommandWriteTargets,
25
22
  tokenizeShellLike,
26
23
  toolMutationPaths
27
- } from "./chunk-5UUP6MWO.js";
24
+ } from "./chunk-MV3K5QF2.js";
28
25
  import {
26
+ expandPath,
29
27
  resolveReadPath
30
28
  } from "./chunk-OQE5J4C6.js";
31
29
  import {
32
- isSkillActivation
33
- } from "./chunk-T6YILFSB.js";
30
+ DEFAULT_AUTONOMY_LEVEL,
31
+ autonomyAskRejection,
32
+ autonomyDenyRejection,
33
+ isAutonomyLevel,
34
+ mapAutonomy
35
+ } from "./chunk-HWUFFB6L.js";
34
36
  import {
35
37
  DEFAULT_MAX_LINES,
36
38
  OBSERVATION_POLICY_SLACK_BYTES,
37
39
  OBSERVE_SELF_CAPS,
40
+ deterministicDiagnosticSummary,
38
41
  finalizeObservation,
39
- formatSize,
40
42
  isSessionOffloadPath,
41
43
  observationBudgetExhausted,
42
44
  reserveObservation,
43
45
  splitLinesForCounting,
44
46
  truncateHead,
45
47
  truncateTail
46
- } from "./chunk-VAKQQHWR.js";
48
+ } from "./chunk-ODFEOB4F.js";
49
+ import {
50
+ formatSize
51
+ } from "./chunk-OZNBF4L3.js";
47
52
  import {
53
+ byteLength,
48
54
  truncateUtf8
49
55
  } from "./chunk-6XLNIQDB.js";
50
56
  import {
@@ -56,12 +62,24 @@ import {
56
62
  } from "./chunk-CGKSTWHD.js";
57
63
  import {
58
64
  THINKING_LEVELS
59
- } from "./chunk-ZDOOVTXZ.js";
65
+ } from "./chunk-4OC57DA6.js";
66
+ import {
67
+ gitCommitAttributionEnabled,
68
+ reportCommitAttributionDiagnostic,
69
+ resolveSafeCwd,
70
+ withManagedGitCommitAttributionEnvironment
71
+ } from "./chunk-VEZEGCGW.js";
72
+ import {
73
+ clampTimerDelayMs
74
+ } from "./chunk-FQ4SKYE4.js";
60
75
  import {
61
76
  GUARDRAIL_DEFAULTS,
62
77
  GUARDRAIL_ENV_VARS,
63
78
  resolveGuardrail
64
79
  } from "./chunk-4ZG3XFUR.js";
80
+ import {
81
+ AI_AGENT_NAME
82
+ } from "./chunk-6EJMN2Y3.js";
65
83
  import {
66
84
  init_esm_shims
67
85
  } from "./chunk-3R73A4XB.js";
@@ -137,6 +155,255 @@ function applyCodewikiWorkerPolicy(tools, context) {
137
155
  return tools.filter((tool) => !isCodewikiTool(tool));
138
156
  }
139
157
 
158
+ // src/core/bash-exec.ts
159
+ init_esm_shims();
160
+ import { spawn } from "node:child_process";
161
+ import { StringDecoder } from "node:string_decoder";
162
+ var BASH_HARD_CAP_BYTES = 16 * 1024 * 1024;
163
+ var CLIO_CONTROL_ENV_KEYS = ["CLIO_CODER_INTERACTIVE", "CLIO_CODER_RESUME_SESSION_ID"];
164
+ var BASH_UPDATE_THROTTLE_MS = 100;
165
+ function buildToolEnv() {
166
+ const env = { ...process.env };
167
+ env.AI_AGENT = AI_AGENT_NAME;
168
+ for (const key of CLIO_CONTROL_ENV_KEYS) {
169
+ Reflect.deleteProperty(env, key);
170
+ }
171
+ return env;
172
+ }
173
+ var LOGIN_ENV_CAPTURE_TIMEOUT_MS = 1e4;
174
+ var loginEnvCapture = null;
175
+ function parseNullDelimitedEnv(raw) {
176
+ const env = {};
177
+ for (const entry of raw.split("\0")) {
178
+ if (entry.length === 0) continue;
179
+ const eq = entry.indexOf("=");
180
+ if (eq <= 0) continue;
181
+ env[entry.slice(0, eq)] = entry.slice(eq + 1);
182
+ }
183
+ return typeof env.PATH === "string" && env.PATH.length > 0 ? env : null;
184
+ }
185
+ function captureLoginEnv() {
186
+ return new Promise((resolve) => {
187
+ let stdout = "";
188
+ let settled = false;
189
+ const finish = (value) => {
190
+ if (settled) return;
191
+ settled = true;
192
+ clearTimeout(timer);
193
+ resolve(value);
194
+ };
195
+ const child = spawn("/bin/bash", ["-lc", "env -0"], {
196
+ env: buildToolEnv(),
197
+ stdio: ["ignore", "pipe", "ignore"]
198
+ });
199
+ const timer = setTimeout(() => {
200
+ child.kill("SIGKILL");
201
+ finish(null);
202
+ }, LOGIN_ENV_CAPTURE_TIMEOUT_MS);
203
+ child.stdout?.on("data", (chunk) => {
204
+ stdout += chunk.toString("utf8");
205
+ });
206
+ child.on("error", () => finish(null));
207
+ child.on("close", (code) => finish(code === 0 ? parseNullDelimitedEnv(stdout) : null));
208
+ });
209
+ }
210
+ async function bashSpawnPlan() {
211
+ loginEnvCapture ??= captureLoginEnv();
212
+ const captured = await loginEnvCapture;
213
+ if (captured === null) return { mode: "-lc", env: buildToolEnv() };
214
+ const env = { ...process.env, ...captured };
215
+ env.AI_AGENT = AI_AGENT_NAME;
216
+ for (const key of CLIO_CONTROL_ENV_KEYS) {
217
+ Reflect.deleteProperty(env, key);
218
+ }
219
+ return { mode: "-c", env };
220
+ }
221
+ function combineBashOutput(result) {
222
+ const { stdout, stderr } = result;
223
+ return stderr.length > 0 ? `${stdout}${stdout.endsWith("\n") || stdout.length === 0 ? "" : "\n"}${stderr}` : stdout;
224
+ }
225
+ async function runBashCommand(command, options = {}) {
226
+ const plan = await bashSpawnPlan();
227
+ const attribution = withManagedGitCommitAttributionEnvironment(plan.env, {
228
+ cwd: options.cwd ?? process.cwd(),
229
+ enabled: gitCommitAttributionEnabled(process.env)
230
+ });
231
+ reportCommitAttributionDiagnostic(attribution.diagnostic);
232
+ return new Promise((resolve) => {
233
+ const timeout = clampTimerDelayMs(options.timeoutMs ?? 3e5);
234
+ let aborted = false;
235
+ let timedOut = false;
236
+ let settled = false;
237
+ let timeoutId = null;
238
+ let killGraceTimer = null;
239
+ let killSent = false;
240
+ let stdout = "";
241
+ let stderr = "";
242
+ const stdoutDecoder = new StringDecoder("utf8");
243
+ const stderrDecoder = new StringDecoder("utf8");
244
+ let decodersFinished = false;
245
+ let outputBytes = 0;
246
+ let outputCapped = false;
247
+ let updateTimer = null;
248
+ let updateDirty = false;
249
+ let lastUpdateAt = 0;
250
+ const child = spawn("/bin/bash", [plan.mode, command], {
251
+ ...options.cwd === void 0 ? {} : { cwd: options.cwd },
252
+ env: attribution.env,
253
+ detached: process.platform !== "win32",
254
+ stdio: ["ignore", "pipe", "pipe"]
255
+ });
256
+ const finishDecoders = () => {
257
+ if (decodersFinished) return;
258
+ decodersFinished = true;
259
+ if (outputCapped) return;
260
+ stdout += stdoutDecoder.end();
261
+ stderr += stderrDecoder.end();
262
+ };
263
+ const emitUpdate = () => {
264
+ if (options.onUpdate === void 0 || !updateDirty) return;
265
+ updateDirty = false;
266
+ lastUpdateAt = Date.now();
267
+ try {
268
+ options.onUpdate({ stdout, stderr, outputBytes: Math.min(outputBytes, BASH_HARD_CAP_BYTES) });
269
+ } catch {
270
+ }
271
+ };
272
+ const clearUpdateTimer = () => {
273
+ if (updateTimer === null) return;
274
+ clearTimeout(updateTimer);
275
+ updateTimer = null;
276
+ };
277
+ const scheduleUpdate = () => {
278
+ if (options.onUpdate === void 0) return;
279
+ updateDirty = true;
280
+ const delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);
281
+ if (delay <= 0) {
282
+ clearUpdateTimer();
283
+ emitUpdate();
284
+ return;
285
+ }
286
+ updateTimer ??= setTimeout(() => {
287
+ updateTimer = null;
288
+ emitUpdate();
289
+ }, delay);
290
+ };
291
+ const finishUpdates = () => {
292
+ clearUpdateTimer();
293
+ emitUpdate();
294
+ };
295
+ if (options.onUpdate !== void 0) {
296
+ updateDirty = true;
297
+ emitUpdate();
298
+ }
299
+ const clearKillGraceTimer = () => {
300
+ if (!killGraceTimer) return;
301
+ clearTimeout(killGraceTimer);
302
+ killGraceTimer = null;
303
+ };
304
+ const sendSignal = (signalName) => {
305
+ const pid = child.pid;
306
+ if (pid && process.platform !== "win32") {
307
+ try {
308
+ process.kill(-pid, signalName);
309
+ return;
310
+ } catch {
311
+ }
312
+ }
313
+ child.kill(signalName);
314
+ };
315
+ const killChild = () => {
316
+ if (killSent) return;
317
+ killSent = true;
318
+ sendSignal("SIGTERM");
319
+ killGraceTimer = setTimeout(() => {
320
+ sendSignal("SIGKILL");
321
+ }, 5e3);
322
+ };
323
+ function onAbort() {
324
+ aborted = true;
325
+ killChild();
326
+ }
327
+ if (timeout > 0) {
328
+ timeoutId = setTimeout(() => {
329
+ timedOut = true;
330
+ killChild();
331
+ }, timeout);
332
+ }
333
+ if (options.signal?.aborted) {
334
+ onAbort();
335
+ } else {
336
+ options.signal?.addEventListener("abort", onAbort, { once: true });
337
+ }
338
+ const appendChunk = (target, chunk) => {
339
+ if (outputCapped) return;
340
+ const remaining = BASH_HARD_CAP_BYTES - outputBytes;
341
+ const accepted = chunk.byteLength <= remaining ? chunk : chunk.subarray(0, Math.max(0, remaining));
342
+ outputBytes += accepted.byteLength;
343
+ if (accepted.byteLength > 0) {
344
+ const decoded = target === "stdout" ? stdoutDecoder.write(accepted) : stderrDecoder.write(accepted);
345
+ if (target === "stdout") stdout += decoded;
346
+ else stderr += decoded;
347
+ }
348
+ if (accepted.byteLength < chunk.byteLength) {
349
+ outputCapped = true;
350
+ scheduleUpdate();
351
+ killChild();
352
+ return;
353
+ }
354
+ scheduleUpdate();
355
+ };
356
+ child.stdout?.on("data", (chunk) => appendChunk("stdout", chunk));
357
+ child.stderr?.on("data", (chunk) => appendChunk("stderr", chunk));
358
+ child.on("error", (error) => {
359
+ if (settled) return;
360
+ settled = true;
361
+ if (timeoutId) clearTimeout(timeoutId);
362
+ clearKillGraceTimer();
363
+ finishDecoders();
364
+ finishUpdates();
365
+ options.signal?.removeEventListener("abort", onAbort);
366
+ resolve({
367
+ error,
368
+ stdout,
369
+ stderr,
370
+ exitCode: null,
371
+ signal: null,
372
+ aborted,
373
+ timedOut,
374
+ outputCapped,
375
+ outputBytes
376
+ });
377
+ });
378
+ child.on("close", (code, signalName) => {
379
+ if (settled) return;
380
+ settled = true;
381
+ if (timeoutId) clearTimeout(timeoutId);
382
+ clearKillGraceTimer();
383
+ finishDecoders();
384
+ finishUpdates();
385
+ options.signal?.removeEventListener("abort", onAbort);
386
+ const error = code === 0 && signalName === null ? null : {
387
+ name: "Error",
388
+ message: signalName ? `command terminated by ${signalName}` : `command exited with code ${code ?? "?"}`,
389
+ code: code ?? void 0,
390
+ signal: signalName ?? void 0
391
+ };
392
+ resolve({
393
+ error,
394
+ stdout,
395
+ stderr,
396
+ exitCode: code,
397
+ signal: signalName,
398
+ aborted,
399
+ timedOut,
400
+ outputCapped,
401
+ outputBytes
402
+ });
403
+ });
404
+ });
405
+ }
406
+
140
407
  // src/tools/network-policy.ts
141
408
  init_esm_shims();
142
409
  var NO_NETWORK_TOOLS_ENV = "CLIO_CODER_NO_NETWORK_TOOLS";
@@ -534,10 +801,13 @@ function validateAllowedTools(value) {
534
801
  }
535
802
  function validateWorkerBudget(value) {
536
803
  const budget = readRecord(value, "WorkerSpec.budget");
537
- const expected = ["hardCap", "readReserve", "synthesis", "toolCalls"];
804
+ const expected = ["hardCap", "readReserve", "revision", "synthesis", "toolCalls"];
538
805
  const actual = Object.keys(budget).sort();
539
- if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
540
- throw new Error(`WorkerSpec.budget must contain exactly: ${expected.join(", ")}`);
806
+ if (actual.some((key) => !expected.includes(key)) || actual.some((key) => key === "revision" && budget.revision === void 0)) {
807
+ throw new Error(`WorkerSpec.budget may contain only: ${expected.join(", ")}`);
808
+ }
809
+ for (const required of ["hardCap", "readReserve", "synthesis", "toolCalls"]) {
810
+ if (!Object.hasOwn(budget, required)) throw new Error(`WorkerSpec.budget.${required} is required`);
541
811
  }
542
812
  for (const key of ["toolCalls", "readReserve", "hardCap"]) {
543
813
  const value2 = budget[key];
@@ -554,6 +824,30 @@ function validateWorkerBudget(value) {
554
824
  throw new Error("WorkerSpec.budget.readReserve must be an integer in [0, toolCalls)");
555
825
  }
556
826
  if (typeof budget.synthesis !== "boolean") throw new Error("WorkerSpec.budget.synthesis must be a boolean");
827
+ if (budget.revision !== void 0) {
828
+ const revision = readRecord(budget.revision, "WorkerSpec.budget.revision");
829
+ const revisionKeys = Object.keys(revision).sort();
830
+ if (revisionKeys.length !== 2 || revisionKeys[0] !== "readReserve" || revisionKeys[1] !== "toolCalls") {
831
+ throw new Error("WorkerSpec.budget.revision must contain exactly: readReserve, toolCalls");
832
+ }
833
+ for (const key of ["toolCalls", "readReserve"]) {
834
+ if (typeof revision[key] !== "number" || !Number.isSafeInteger(revision[key])) {
835
+ throw new Error(`WorkerSpec.budget.revision.${key} must be a safe integer`);
836
+ }
837
+ }
838
+ if (revision.toolCalls < budget.toolCalls) {
839
+ throw new Error("WorkerSpec.budget.revision.toolCalls must not be smaller than WorkerSpec.budget.toolCalls");
840
+ }
841
+ if (revision.toolCalls === budget.toolCalls && revision.readReserve <= budget.readReserve) {
842
+ throw new Error("WorkerSpec.budget.revision must increase toolCalls or readReserve");
843
+ }
844
+ if (revision.toolCalls > budget.hardCap) {
845
+ throw new Error("WorkerSpec.budget.revision.toolCalls must not exceed WorkerSpec.budget.hardCap");
846
+ }
847
+ if (revision.readReserve < 0 || revision.readReserve >= revision.toolCalls) {
848
+ throw new Error("WorkerSpec.budget.revision.readReserve must be an integer in [0, toolCalls)");
849
+ }
850
+ }
557
851
  }
558
852
  function validateRuntimeCapabilityDecision(value, source) {
559
853
  const caps = readRecord(value, source);
@@ -777,6 +1071,293 @@ function validateRehydratedWorkerRuntime(spec, runtime) {
777
1071
  }
778
1072
  }
779
1073
 
1074
+ // src/domains/dispatch/budget-envelope.ts
1075
+ init_esm_shims();
1076
+ var BudgetAdmissionError = class extends Error {
1077
+ code;
1078
+ constructor(code, message) {
1079
+ super(`dispatch: budget admission denied (${code}): ${message}`);
1080
+ this.name = "BudgetAdmissionError";
1081
+ this.code = code;
1082
+ }
1083
+ };
1084
+ function isRecord(value) {
1085
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1086
+ }
1087
+ function clonePhase(value, prefix) {
1088
+ if (!isRecord(value)) throw new Error(`${prefix} must be an object`);
1089
+ for (const key of Object.keys(value)) {
1090
+ if (key !== "toolCalls" && key !== "readReserve") throw new Error(`${prefix}.${key} is unknown`);
1091
+ }
1092
+ for (const key of ["toolCalls", "readReserve"]) {
1093
+ if (!Object.hasOwn(value, key)) throw new Error(`${prefix}.${key} is required`);
1094
+ }
1095
+ const toolCalls = value.toolCalls;
1096
+ if (typeof toolCalls !== "number" || !Number.isSafeInteger(toolCalls) || toolCalls <= 0) {
1097
+ throw new Error(`${prefix}.toolCalls must be a positive safe integer`);
1098
+ }
1099
+ const readReserve = value.readReserve;
1100
+ if (typeof readReserve !== "number" || !Number.isSafeInteger(readReserve) || readReserve < 0) {
1101
+ throw new Error(`${prefix}.readReserve must be a non-negative safe integer`);
1102
+ }
1103
+ if (readReserve >= toolCalls) throw new Error(`${prefix}.readReserve must be less than ${prefix}.toolCalls`);
1104
+ return { toolCalls, readReserve };
1105
+ }
1106
+ function cloneDispatchBudgetRequest(value, prefix = "budget") {
1107
+ if (!isRecord(value)) throw new Error(`${prefix} must be an object`);
1108
+ for (const key of Object.keys(value)) {
1109
+ if (key !== "toolCalls" && key !== "readReserve" && key !== "retryRevision") {
1110
+ throw new Error(`${prefix}.${key} is unknown`);
1111
+ }
1112
+ }
1113
+ const phase = clonePhase({ toolCalls: value.toolCalls, readReserve: value.readReserve }, prefix);
1114
+ const retryRevision = value.retryRevision === void 0 ? void 0 : clonePhase(value.retryRevision, `${prefix}.retryRevision`);
1115
+ return { ...phase, ...retryRevision === void 0 ? {} : { retryRevision } };
1116
+ }
1117
+ function freezeEnvelope(envelope) {
1118
+ Object.freeze(envelope.policy.default);
1119
+ Object.freeze(envelope.policy.maximum);
1120
+ Object.freeze(envelope.policy);
1121
+ if (envelope.request !== null) {
1122
+ if (envelope.request.retryRevision !== void 0) Object.freeze(envelope.request.retryRevision);
1123
+ Object.freeze(envelope.request);
1124
+ }
1125
+ Object.freeze(envelope.effective);
1126
+ for (const reason of envelope.reasons) Object.freeze(reason);
1127
+ Object.freeze(envelope.reasons);
1128
+ return Object.freeze(envelope);
1129
+ }
1130
+ function phaseWithin(candidate, maximum) {
1131
+ return candidate.toolCalls <= maximum.toolCalls && candidate.readReserve <= maximum.readReserve;
1132
+ }
1133
+ function samePhase(left, right) {
1134
+ return left.toolCalls === right.toolCalls && left.readReserve === right.readReserve;
1135
+ }
1136
+ function assertRequestAdmitted(input) {
1137
+ const { request, policy, hardCap } = input;
1138
+ const authoredMaximum = policy.maximum ?? policy;
1139
+ if (policy.maximum === void 0 && !samePhase(request, policy)) {
1140
+ throw new BudgetAdmissionError(
1141
+ "exact-recipe-policy",
1142
+ `the recipe pins ${policy.toolCalls} tool calls with a read reserve of ${policy.readReserve}`
1143
+ );
1144
+ }
1145
+ if (!phaseWithin(request, authoredMaximum)) {
1146
+ throw new BudgetAdmissionError(
1147
+ "recipe-maximum",
1148
+ `the requested phase ${request.toolCalls}/${request.readReserve} exceeds recipe maximum ${authoredMaximum.toolCalls}/${authoredMaximum.readReserve}`
1149
+ );
1150
+ }
1151
+ if (request.toolCalls > hardCap) {
1152
+ throw new BudgetAdmissionError(
1153
+ "global-cap",
1154
+ `the requested ${request.toolCalls} tool calls exceed the operator cap of ${hardCap}`
1155
+ );
1156
+ }
1157
+ const ceiling = request.retryRevision;
1158
+ if (ceiling === void 0) return;
1159
+ if (ceiling.toolCalls < request.toolCalls || ceiling.readReserve < request.readReserve) {
1160
+ throw new BudgetAdmissionError(
1161
+ "ceiling-below-request",
1162
+ `retryRevision ${ceiling.toolCalls}/${ceiling.readReserve} must not be smaller than the requested phase ${request.toolCalls}/${request.readReserve}`
1163
+ );
1164
+ }
1165
+ if (policy.maximum === void 0 && !samePhase(ceiling, policy)) {
1166
+ throw new BudgetAdmissionError(
1167
+ "exact-recipe-policy",
1168
+ `the recipe pins ${policy.toolCalls} tool calls with a read reserve of ${policy.readReserve}`
1169
+ );
1170
+ }
1171
+ if (!phaseWithin(ceiling, authoredMaximum)) {
1172
+ throw new BudgetAdmissionError(
1173
+ "recipe-maximum",
1174
+ `retryRevision ${ceiling.toolCalls}/${ceiling.readReserve} exceeds recipe maximum ${authoredMaximum.toolCalls}/${authoredMaximum.readReserve}`
1175
+ );
1176
+ }
1177
+ if (ceiling.toolCalls > hardCap) {
1178
+ throw new BudgetAdmissionError(
1179
+ "global-cap",
1180
+ `retryRevision ${ceiling.toolCalls} exceeds the operator cap of ${hardCap}`
1181
+ );
1182
+ }
1183
+ }
1184
+ function resolveToolBudgetEnvelope(input) {
1185
+ if (!Number.isSafeInteger(input.hardCap) || input.hardCap <= 0) {
1186
+ throw new Error("dispatch: worker tool-call cap must be a positive safe integer");
1187
+ }
1188
+ if (input.request !== void 0)
1189
+ assertRequestAdmitted({ request: input.request, policy: input.policy, hardCap: input.hardCap });
1190
+ const request = input.request === void 0 ? null : cloneDispatchBudgetRequest(input.request);
1191
+ const base = request ?? input.policy;
1192
+ const reasons = [];
1193
+ let selected = base;
1194
+ const phaseKind = input.retry ? "retry" : input.revision ? "revision" : null;
1195
+ if (phaseKind !== null) {
1196
+ const ceiling = request?.retryRevision;
1197
+ if (ceiling !== void 0 && !samePhase(ceiling, base)) {
1198
+ selected = ceiling;
1199
+ reasons.push({
1200
+ code: phaseKind === "retry" ? "retry-growth-authorized" : "revision-growth-authorized",
1201
+ detail: `${phaseKind} phase grew from ${base.toolCalls}/${base.readReserve} to the preauthorized ceiling ${ceiling.toolCalls}/${ceiling.readReserve}`
1202
+ });
1203
+ } else {
1204
+ reasons.push({
1205
+ code: phaseKind === "retry" ? "retry-growth-denied" : "revision-growth-denied",
1206
+ detail: ceiling === void 0 ? `${phaseKind} phase retained ${base.toolCalls}/${base.readReserve} because the original request declared no ceiling` : `${phaseKind} phase retained ${base.toolCalls}/${base.readReserve} because the declared ceiling did not authorize growth`
1207
+ });
1208
+ }
1209
+ }
1210
+ const revisionCeiling = phaseKind === null ? request?.retryRevision : void 0;
1211
+ if (revisionCeiling !== void 0 && !samePhase(revisionCeiling, selected)) {
1212
+ reasons.push({
1213
+ code: "revision-growth-authorized",
1214
+ detail: `a result-contract revision may grow from ${selected.toolCalls}/${selected.readReserve} to the preauthorized ceiling ${revisionCeiling.toolCalls}/${revisionCeiling.readReserve}`
1215
+ });
1216
+ }
1217
+ let toolCalls = selected.toolCalls;
1218
+ if (toolCalls > input.hardCap) {
1219
+ reasons.push({
1220
+ code: "global-cap-clamp",
1221
+ detail: `tool calls were clamped from ${toolCalls} to the operator cap of ${input.hardCap}`
1222
+ });
1223
+ toolCalls = input.hardCap;
1224
+ }
1225
+ let readReserve = selected.readReserve;
1226
+ if (!input.hasReadTool && readReserve > 0) {
1227
+ reasons.push({
1228
+ code: "read-reserve-unavailable-clamp",
1229
+ detail: `read reserve was clamped from ${readReserve} to 0 because the read tool is unavailable`
1230
+ });
1231
+ readReserve = 0;
1232
+ }
1233
+ const maximumEffectiveReserve = Math.max(0, toolCalls - 1);
1234
+ if (readReserve > maximumEffectiveReserve) {
1235
+ reasons.push({
1236
+ code: "read-reserve-cap-clamp",
1237
+ detail: `read reserve was clamped from ${readReserve} to ${maximumEffectiveReserve} after the tool-call cap`
1238
+ });
1239
+ readReserve = maximumEffectiveReserve;
1240
+ }
1241
+ let effectiveRevision;
1242
+ if (revisionCeiling !== void 0 && !samePhase(revisionCeiling, selected)) {
1243
+ let revisionReadReserve = revisionCeiling.readReserve;
1244
+ if (!input.hasReadTool && revisionReadReserve > 0) {
1245
+ reasons.push({
1246
+ code: "read-reserve-unavailable-clamp",
1247
+ detail: `result-contract revision read reserve was clamped from ${revisionReadReserve} to 0 because the read tool is unavailable`
1248
+ });
1249
+ revisionReadReserve = 0;
1250
+ }
1251
+ effectiveRevision = { toolCalls: revisionCeiling.toolCalls, readReserve: revisionReadReserve };
1252
+ }
1253
+ const authoredMaximum = input.policy.maximum ?? input.policy;
1254
+ return freezeEnvelope({
1255
+ version: 1,
1256
+ policy: {
1257
+ recipeId: input.recipeId,
1258
+ default: {
1259
+ toolCalls: input.policy.toolCalls,
1260
+ readReserve: input.policy.readReserve,
1261
+ synthesis: input.policy.synthesis
1262
+ },
1263
+ maximum: { toolCalls: authoredMaximum.toolCalls, readReserve: authoredMaximum.readReserve },
1264
+ exact: input.policy.maximum === void 0
1265
+ },
1266
+ request,
1267
+ effective: {
1268
+ toolCalls,
1269
+ readReserve,
1270
+ synthesis: input.policy.synthesis,
1271
+ hardCap: input.hardCap,
1272
+ ...effectiveRevision === void 0 ? {} : { revision: effectiveRevision }
1273
+ },
1274
+ reasons
1275
+ });
1276
+ }
1277
+ function formatBudgetPolicy(envelope) {
1278
+ const policy = envelope.policy;
1279
+ const range = policy.exact ? `exact ${policy.default.toolCalls}/${policy.default.readReserve}` : `default ${policy.default.toolCalls}/${policy.default.readReserve}, max ${policy.maximum.toolCalls}/${policy.maximum.readReserve}`;
1280
+ return `${policy.recipeId} ${range}, synthesis=${policy.default.synthesis ? "on" : "off"}`;
1281
+ }
1282
+ function formatBudgetRequest(envelope) {
1283
+ const request = envelope.request;
1284
+ if (request === null) return "recipe default";
1285
+ const ceiling = request.retryRevision;
1286
+ return ceiling === void 0 ? `${request.toolCalls}/${request.readReserve}, no retry/revision growth` : `${request.toolCalls}/${request.readReserve}, retry/revision ceiling ${ceiling.toolCalls}/${ceiling.readReserve}`;
1287
+ }
1288
+ function formatEffectiveBudget(envelope) {
1289
+ const budget = envelope.effective;
1290
+ const revision = budget.revision ? `, result revision ${budget.revision.toolCalls}/${budget.revision.readReserve}` : "";
1291
+ return `${budget.toolCalls}/${budget.readReserve}${revision}, lifetime cap ${budget.hardCap}, synthesis=${budget.synthesis ? "on" : "off"}`;
1292
+ }
1293
+ function formatBudgetReasons(envelope) {
1294
+ return envelope.reasons.length === 0 ? "none" : envelope.reasons.map((reason) => reason.code).join(",");
1295
+ }
1296
+ var BUDGET_REASON_CODES = /* @__PURE__ */ new Set([
1297
+ "global-cap-clamp",
1298
+ "read-reserve-unavailable-clamp",
1299
+ "read-reserve-cap-clamp",
1300
+ "retry-growth-authorized",
1301
+ "retry-growth-denied",
1302
+ "revision-growth-authorized",
1303
+ "revision-growth-denied"
1304
+ ]);
1305
+ function cloneRunToolBudgetEnvelope(value) {
1306
+ try {
1307
+ if (!isRecord(value) || value.version !== 1 || !isRecord(value.policy) || !isRecord(value.effective)) {
1308
+ return void 0;
1309
+ }
1310
+ const recipeId = value.policy.recipeId;
1311
+ if (typeof recipeId !== "string" || recipeId.length === 0 || typeof value.policy.exact !== "boolean") {
1312
+ return void 0;
1313
+ }
1314
+ if (!isRecord(value.policy.default) || typeof value.policy.default.synthesis !== "boolean") return void 0;
1315
+ const defaultPhase = clonePhase(
1316
+ { toolCalls: value.policy.default.toolCalls, readReserve: value.policy.default.readReserve },
1317
+ "budget.policy.default"
1318
+ );
1319
+ const maximum = clonePhase(value.policy.maximum, "budget.policy.maximum");
1320
+ const request = value.request === null ? null : cloneDispatchBudgetRequest(value.request);
1321
+ const effectivePhase = clonePhase(
1322
+ { toolCalls: value.effective.toolCalls, readReserve: value.effective.readReserve },
1323
+ "budget.effective"
1324
+ );
1325
+ const revision = value.effective.revision === void 0 ? void 0 : clonePhase(value.effective.revision, "budget.effective.revision");
1326
+ if (typeof value.effective.synthesis !== "boolean" || typeof value.effective.hardCap !== "number" || !Number.isSafeInteger(value.effective.hardCap) || value.effective.hardCap <= 0 || !Array.isArray(value.reasons)) {
1327
+ return void 0;
1328
+ }
1329
+ if (maximum.toolCalls < defaultPhase.toolCalls || maximum.readReserve < defaultPhase.readReserve || value.policy.exact && !samePhase(maximum, defaultPhase) || effectivePhase.toolCalls > value.effective.hardCap || value.effective.synthesis !== value.policy.default.synthesis || revision !== void 0 && (revision.toolCalls < effectivePhase.toolCalls || revision.toolCalls > value.effective.hardCap || revision.toolCalls === effectivePhase.toolCalls && revision.readReserve <= effectivePhase.readReserve)) {
1330
+ return void 0;
1331
+ }
1332
+ const reasons = [];
1333
+ for (const reason of value.reasons) {
1334
+ if (!isRecord(reason) || typeof reason.code !== "string" || !BUDGET_REASON_CODES.has(reason.code) || typeof reason.detail !== "string") {
1335
+ return void 0;
1336
+ }
1337
+ reasons.push({ code: reason.code, detail: reason.detail });
1338
+ }
1339
+ return freezeEnvelope({
1340
+ version: 1,
1341
+ policy: {
1342
+ recipeId,
1343
+ default: { ...defaultPhase, synthesis: value.policy.default.synthesis },
1344
+ maximum,
1345
+ exact: value.policy.exact
1346
+ },
1347
+ request,
1348
+ effective: {
1349
+ ...effectivePhase,
1350
+ synthesis: value.effective.synthesis,
1351
+ hardCap: value.effective.hardCap,
1352
+ ...revision === void 0 ? {} : { revision }
1353
+ },
1354
+ reasons
1355
+ });
1356
+ } catch {
1357
+ return void 0;
1358
+ }
1359
+ }
1360
+
780
1361
  // src/domains/dispatch/gate-role-prompts.ts
781
1362
  init_esm_shims();
782
1363
  var REVIEWER_GATE_PROMPT = [
@@ -831,525 +1412,222 @@ function runKindSupportsLiveSteering(kind) {
831
1412
  return kind === "http" || kind === "sdk";
832
1413
  }
833
1414
 
834
- // src/domains/session/task-board.ts
1415
+ // src/domains/safety/call-target.ts
835
1416
  init_esm_shims();
836
- import { randomUUID } from "node:crypto";
837
1417
 
838
- // src/domains/session/entries.ts
1418
+ // src/domains/safety/redaction.ts
839
1419
  init_esm_shims();
840
- var SESSION_ENTRY_KINDS = [
841
- "message",
842
- "bashExecution",
843
- "custom",
844
- "modelChange",
845
- "thinkingLevelChange",
846
- "fileEntry",
847
- "branchSummary",
848
- "compactionSummary",
849
- "sessionInfo",
850
- "label",
851
- "protectedArtifact",
852
- "skillActivation",
853
- "taskLedger",
854
- "decisionLedger",
855
- "workerRun"
856
- ];
857
- function isRecord(value) {
858
- return !!value && typeof value === "object";
859
- }
860
- function isString(value) {
861
- return typeof value === "string";
862
- }
863
- function isOptionalString(value) {
864
- return value === void 0 || typeof value === "string";
865
- }
866
- function isOptionalBoolean(value) {
867
- return value === void 0 || typeof value === "boolean";
868
- }
869
- function isNumber(value) {
870
- return typeof value === "number" && Number.isFinite(value);
871
- }
872
- function isPositiveInteger(value) {
873
- return isNumber(value) && Number.isInteger(value) && value > 0;
874
- }
875
- function isOptionalNumber(value) {
876
- return value === void 0 || isNumber(value);
1420
+ function isPlainObject2(value) {
1421
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1422
+ }
1423
+ var SECRET_KEY_RE = /(?:api[-_]?key|access[-_]?key|auth(?:orization)?|credential|password|private[-_]?key|secret|token)/iu;
1424
+ var ENV_KEY_RE = /^env(?:ironment)?$/iu;
1425
+ var SECRET_STRING_RE = /(https?:\/\/[^\s/@]+:)[^\s/@]+@/giu;
1426
+ var SECRET_URL_PARAM_RE = /([?&](?:api[-_]?key|access[-_]?token|auth(?:orization)?|credential|password|secret|token)[^=\s]*=)[^&#\s]+/giu;
1427
+ var SECRET_ASSIGNMENT_RE = /(?<![A-Z0-9_])((?:[A-Z0-9_]*(?:ACCESS|API|AUTH|CREDENTIAL|KEY|PASS(?:WORD)?|PRIVATE|SECRET|TOKEN)[A-Z0-9_]*)=)[^\s;&|]+/giu;
1428
+ var SECRET_FLAG_RE = /(--?(?:[a-z0-9-]*(?:api[-_]?key|access[-_]?token|auth(?:orization)?|credential|password|secret|token)[a-z0-9-]*))(=|\s+)([^\s;&|]+)/giu;
1429
+ function redactSecretString(value) {
1430
+ return value.replace(SECRET_STRING_RE, "$1[redacted]@").replace(SECRET_URL_PARAM_RE, "$1[redacted]").replace(SECRET_ASSIGNMENT_RE, "$1[redacted]").replace(SECRET_FLAG_RE, "$1$2[redacted]");
1431
+ }
1432
+ function isSecretArgKey(key) {
1433
+ return SECRET_KEY_RE.test(key);
1434
+ }
1435
+ function isEnvironmentArgKey(key) {
1436
+ return ENV_KEY_RE.test(key);
1437
+ }
1438
+ function redactEnvironmentValue(value, depth) {
1439
+ if (depth > 8) return "[redacted nested values]";
1440
+ if (typeof value === "string") return "[redacted]";
1441
+ if (Array.isArray(value)) return value.map((item) => redactEnvironmentValue(item, depth + 1));
1442
+ if (!isPlainObject2(value)) return value;
1443
+ const out = {};
1444
+ for (const [childKey, childValue] of Object.entries(value)) {
1445
+ out[childKey] = redactEnvironmentValue(childValue, depth + 1);
1446
+ }
1447
+ return out;
877
1448
  }
878
- function isNullableString(value) {
879
- return value === null || typeof value === "string";
1449
+ function redactToolArgs(value, key = "", depth = 0) {
1450
+ if (isSecretArgKey(key)) return "[redacted]";
1451
+ if (isEnvironmentArgKey(key)) return redactEnvironmentValue(value, depth);
1452
+ if (typeof value === "string") return redactSecretString(value);
1453
+ if (depth > 8) return "[redacted nested values]";
1454
+ if (Array.isArray(value)) return value.map((item) => redactToolArgs(item, key, depth + 1));
1455
+ if (!isPlainObject2(value)) return value;
1456
+ const out = {};
1457
+ for (const [childKey, childValue] of Object.entries(value)) {
1458
+ out[childKey] = redactToolArgs(childValue, childKey, depth + 1);
1459
+ }
1460
+ return out;
880
1461
  }
881
- function isOneOf(value, allowed) {
882
- return typeof value === "string" && allowed.includes(value);
1462
+
1463
+ // src/domains/safety/call-target.ts
1464
+ var ESC_CHAR = String.fromCharCode(27);
1465
+ var BEL_CHAR = String.fromCharCode(7);
1466
+ var OSC_PATTERN = new RegExp(`${ESC_CHAR}\\][\\s\\S]*?(?:${BEL_CHAR}|${ESC_CHAR}\\\\|$)`, "g");
1467
+ var CSI_PATTERN = new RegExp(`${ESC_CHAR}\\[[0-9;?]*[0-9A-Za-z]`, "g");
1468
+ function sanitizeForDisplay(value) {
1469
+ const stripped = value.replace(OSC_PATTERN, "").replace(CSI_PATTERN, "");
1470
+ let out = "";
1471
+ for (const ch of stripped) {
1472
+ const code = ch.codePointAt(0) ?? 0;
1473
+ out += code < 32 || code === 127 ? " " : ch;
1474
+ }
1475
+ return out;
883
1476
  }
884
- function isStringArray(value) {
885
- return Array.isArray(value) && value.every((item) => typeof item === "string");
1477
+ function oneLine(value) {
1478
+ return value.replace(/\s+/g, " ").trim();
1479
+ }
1480
+ function sanitizeCallTargetText(value) {
1481
+ return oneLine(sanitizeForDisplay(value));
1482
+ }
1483
+ var CALL_ACTION_OBJECT_MAX_CHARS = 64;
1484
+ var CALL_TARGET_MAX_CHARS = 120;
1485
+ var CALL_ACTION_VOCABULARY = {
1486
+ read: { verb: "reading", field: "path" },
1487
+ edit: { verb: "editing", field: "path" },
1488
+ write: { verb: "writing", field: "path" },
1489
+ ls: { verb: "listing", field: "path" },
1490
+ bash: { verb: "running", field: "command" },
1491
+ grep: { verb: "searching", field: "pattern" },
1492
+ find: { verb: "finding", field: "pattern" },
1493
+ web_fetch: { verb: "fetching", field: "url" },
1494
+ git: { verb: "git", field: "op" },
1495
+ verify: { verb: "verifying", field: "check" },
1496
+ code_nav: { verb: "navigating", field: "query" },
1497
+ context: { verb: "context", field: "scope" },
1498
+ artifact: { verb: "writing", field: "kind" },
1499
+ monitor: { verb: "monitoring", field: "run_id" },
1500
+ steer: { verb: "steering", field: "run_id" },
1501
+ tasks: { verb: "tasks", field: "action" },
1502
+ dispatch: { verb: "dispatching", field: "agent" },
1503
+ // ACP tool kinds. A peer names its own argument fields, so these rely on
1504
+ // the shared allowlist below rather than on a field this side can predict.
1505
+ execute: { verb: "running", field: "command" },
1506
+ search: { verb: "searching", field: "query" },
1507
+ fetch: { verb: "fetching", field: "url" },
1508
+ delete: { verb: "deleting", field: "path" },
1509
+ move: { verb: "moving", field: "path" },
1510
+ think: { verb: "thinking", field: "" }
1511
+ };
1512
+ var CALL_ACTION_OBJECT_FIELDS = ["path", "file_path", "command", "pattern", "query", "url"];
1513
+ var CALL_TARGET_FIELDS = {
1514
+ read: ["path", "offset", "limit", "tail"],
1515
+ grep: ["pattern", "path", "mode", "glob", "ignore_case", "literal", "context", "limit", "include_ignored"],
1516
+ find: ["pattern", "path", "order", "limit", "include_ignored"],
1517
+ ls: ["path", "limit"],
1518
+ code_nav: ["mode", "query", "limit"],
1519
+ context: ["scope", "query", "name", "limit", "ref", "include_tree"],
1520
+ credential_present: ["name", "source", "file"],
1521
+ write: ["path"],
1522
+ edit: ["path"],
1523
+ bash: ["command", "cwd", "timeout_ms", "output_policy"],
1524
+ git: ["op", "path", "cached", "stat", "name_only", "limit", "cwd", "timeout_ms", "max_output_bytes"],
1525
+ verify: ["check", "path", "browser", "cwd", "timeout_ms", "max_output_bytes"],
1526
+ dispatch: [
1527
+ "list",
1528
+ "mode",
1529
+ "agent",
1530
+ "target",
1531
+ "model",
1532
+ "node",
1533
+ "autonomy",
1534
+ "tool_profile",
1535
+ "thinking_level",
1536
+ "detach",
1537
+ "timeout_ms"
1538
+ ],
1539
+ monitor: ["run_id", "mode"],
1540
+ steer: ["run_id", "action"],
1541
+ tasks: ["action", "id"],
1542
+ ledger: ["action", "kind", "path", "line", "target", "passed", "since"],
1543
+ web_fetch: ["url", "method", "timeout_ms", "max_bytes", "format"],
1544
+ ask_user: ["action", "mode", "max_rounds", "exposure"],
1545
+ artifact: ["kind", "path", "title"],
1546
+ // ACP tool kinds use only fields whose meaning the protocol defines.
1547
+ execute: ["command", "cwd"],
1548
+ search: ["query", "path"],
1549
+ fetch: ["url", "method"],
1550
+ delete: ["path"],
1551
+ move: ["path", "target"]
1552
+ };
1553
+ function boundActionObject(value) {
1554
+ if (typeof value !== "string") return null;
1555
+ const clean = sanitizeCallTargetText(redactSecretString(value));
1556
+ if (clean.length === 0) return null;
1557
+ if (clean.length <= CALL_ACTION_OBJECT_MAX_CHARS) return { object: clean, truncated: false };
1558
+ return { object: clean.slice(0, CALL_ACTION_OBJECT_MAX_CHARS), truncated: true };
1559
+ }
1560
+ function describeCallAction(tool, args) {
1561
+ const known = CALL_ACTION_VOCABULARY[tool];
1562
+ const candidates = known?.field ? [known.field, ...CALL_ACTION_OBJECT_FIELDS] : [...CALL_ACTION_OBJECT_FIELDS];
1563
+ let bounded = null;
1564
+ if (args) {
1565
+ for (const field of candidates) {
1566
+ if (isSecretArgKey(field)) continue;
1567
+ bounded = boundActionObject(args[field]);
1568
+ if (bounded !== null) break;
1569
+ }
1570
+ }
1571
+ if (known === void 0 && bounded === null) return null;
1572
+ const verb = known?.verb ?? "calling";
1573
+ if (bounded === null) return { verb };
1574
+ return { verb, object: bounded.object, ...bounded.truncated ? { truncated: true } : {} };
1575
+ }
1576
+ function renderAllowedTargetValue(value) {
1577
+ if (typeof value === "string") {
1578
+ const rendered = sanitizeCallTargetText(redactSecretString(value));
1579
+ return rendered.length > 0 ? rendered : null;
1580
+ }
1581
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
1582
+ if (typeof value === "boolean") return String(value);
1583
+ return null;
886
1584
  }
887
- function hasBaseSessionEntryFields(v) {
888
- return isString(v.turnId) && isNullableString(v.parentTurnId) && isString(v.timestamp);
1585
+ function countLabel(count, singular, plural) {
1586
+ return `${count} ${count === 1 ? singular : plural}`;
889
1587
  }
890
- var MESSAGE_ROLES = ["user", "assistant", "tool_call", "tool_result", "system", "checkpoint"];
891
- var FILE_OPERATIONS = ["read", "write", "edit", "create", "delete"];
892
- var COMPACTION_TRIGGERS = ["auto", "force", "overflow"];
893
- var PROTECTED_ARTIFACT_SOURCES = [
894
- "validation",
895
- "middleware",
896
- "user",
897
- "session"
898
- ];
899
- var TASK_LEDGER_STATUSES = ["pending", "active", "completed", "blocked", "cancelled"];
900
- var TASK_LEDGER_EVIDENCE_STATUSES = [
901
- "required",
902
- "pending",
903
- "passed",
904
- "failed",
905
- "missing"
906
- ];
907
- var DECISION_STATUSES = ["active", "superseded"];
908
- var DECISION_INTERVIEW_STATUSES = ["complete", "cancelled"];
909
- var WORKER_RUN_ORIGINS = ["user", "agent"];
910
- var WORKER_RUN_RUNTIME_KINDS = ["clio", "acp", "claude-sdk", "claude-code"];
911
- function isWorkerRunRuntime(value) {
912
- if (!isRecord(value)) return false;
913
- return isOneOf(value.kind, WORKER_RUN_RUNTIME_KINDS) && isOptionalString(value.targetId) && isOptionalString(value.wireModelId) && isOptionalString(value.peerId);
914
- }
915
- function isTaskLedgerGoal(value) {
916
- if (!isRecord(value)) return false;
917
- if (!isString(value.id) || !isString(value.title)) return false;
918
- if (!isOneOf(value.status, TASK_LEDGER_STATUSES)) return false;
919
- if (value.parentGoalId !== void 0 && !isNullableString(value.parentGoalId)) return false;
920
- const hasPairedProvenance = value.origin === "user" ? isString(value.userTaskId) && /^u[1-9]\d*$/.test(value.userTaskId) : (value.origin === void 0 || value.origin === "agent") && value.userTaskId === void 0;
921
- return isOptionalString(value.description) && hasPairedProvenance;
922
- }
923
- function isTaskLedgerEvidence(value) {
924
- if (!isRecord(value)) return false;
925
- if (!isString(value.id) || !isString(value.description)) return false;
926
- if (!isOneOf(value.status, TASK_LEDGER_EVIDENCE_STATUSES)) return false;
927
- return isOptionalString(value.command) && isOptionalString(value.artifactPath) && isOptionalString(value.observedAt) && isOptionalString(value.notes);
928
- }
929
- function isDecisionRecord(value) {
930
- if (!isRecord(value)) return false;
931
- if (!isString(value.key) || !/^[a-z0-9]+(?:_[a-z0-9]+)*$/.test(value.key)) return false;
932
- if (!isString(value.value) || !isOneOf(value.status, DECISION_STATUSES) || !isString(value.decidedAt)) return false;
933
- if (!isOptionalString(value.label) || !isOptionalString(value.source_question)) return false;
934
- if (!isOptionalString(value.revisedAt) || !isOptionalString(value.correction)) return false;
935
- return value.status === "superseded" ? isString(value.revisedAt) : value.revisedAt === void 0 && value.correction === void 0;
936
- }
937
- function isOptionalCompactionUsage(value) {
938
- if (value === void 0) return true;
939
- if (!isRecord(value)) return false;
940
- const cost = value.cost;
941
- return isNumber(value.input) && isNumber(value.output) && isNumber(value.cacheRead) && isNumber(value.cacheWrite) && isNumber(value.reasoning) && isNumber(value.totalTokens) && isNumber(value.apiCalls) && isRecord(cost) && isNumber(cost.total);
942
- }
943
- function isProtectedArtifact(value) {
944
- if (!isRecord(value)) return false;
945
- return isString(value.path) && isString(value.protectedAt) && isString(value.reason) && isOneOf(value.source, PROTECTED_ARTIFACT_SOURCES) && isOptionalString(value.validationCommand) && isOptionalNumber(value.validationExitCode);
946
- }
947
- function isSessionHeader(value) {
948
- if (!isRecord(value)) return false;
949
- return value.type === "session" && isPositiveInteger(value.version) && isString(value.id) && isString(value.timestamp) && isString(value.cwd) && isOptionalString(value.parentSession) && isOptionalString(value.parentTurnId);
950
- }
951
- function isSessionEntry(value) {
952
- if (!value || typeof value !== "object") return false;
953
- const v = value;
954
- if (!hasBaseSessionEntryFields(v)) return false;
955
- if (!isOneOf(v.kind, SESSION_ENTRY_KINDS)) return false;
956
- switch (v.kind) {
957
- case "message":
958
- return Object.hasOwn(v, "payload") && isOneOf(v.role, MESSAGE_ROLES);
959
- case "bashExecution":
960
- return isString(v.command) && isString(v.output) && (v.exitCode === null || isNumber(v.exitCode)) && typeof v.cancelled === "boolean" && typeof v.truncated === "boolean" && isOptionalString(v.fullOutputPath) && isOptionalBoolean(v.excludeFromContext);
961
- case "custom":
962
- return isString(v.customType) && isOptionalBoolean(v.display);
963
- case "modelChange":
964
- return isString(v.provider) && isString(v.modelId) && isOptionalString(v.target);
965
- case "thinkingLevelChange":
966
- return isString(v.thinkingLevel);
967
- case "fileEntry":
968
- return isString(v.path) && isOneOf(v.operation, FILE_OPERATIONS) && isOptionalNumber(v.bytes) && isOptionalString(v.hash);
969
- case "branchSummary":
970
- return isString(v.fromTurnId) && isString(v.summary);
971
- case "compactionSummary":
972
- return isString(v.summary) && isNumber(v.tokensBefore) && isString(v.firstKeptTurnId) && isOptionalCompactionUsage(v.usage) && (v.trigger === void 0 || isOneOf(v.trigger, COMPACTION_TRIGGERS)) && isOptionalNumber(v.tokensAfter) && isOptionalNumber(v.messagesSummarized) && isOptionalBoolean(v.isSplitTurn);
973
- case "sessionInfo":
974
- return isOptionalString(v.name) && isOptionalString(v.targetTurnId) && isOptionalString(v.label);
975
- case "label":
976
- return isString(v.targetTurnId) && isOptionalString(v.label);
977
- case "protectedArtifact":
978
- return v.action === "protect" && isProtectedArtifact(v.artifact) && isOptionalString(v.toolName) && isOptionalString(v.toolCallId) && isOptionalString(v.runId) && isOptionalString(v.correlationId);
979
- case "skillActivation":
980
- return isSkillActivation(v.activation);
981
- case "taskLedger":
982
- return isOptionalString(v.boardId) && Array.isArray(v.goals) && v.goals.every(isTaskLedgerGoal) && Array.isArray(v.subgoals) && v.subgoals.every(isTaskLedgerGoal) && isStringArray(v.activeRunIds) && Array.isArray(v.requiredValidationEvidence) && v.requiredValidationEvidence.every(isTaskLedgerEvidence);
983
- case "decisionLedger":
984
- return isString(v.interviewId) && isOneOf(v.interviewStatus, DECISION_INTERVIEW_STATUSES) && isString(v.startedAt) && isString(v.endedAt) && isNumber(v.roundCount) && Number.isInteger(v.roundCount) && v.roundCount >= 0 && isOptionalString(v.summary) && isOptionalString(v.transcriptPath) && Array.isArray(v.decisions) && v.decisions.every(isDecisionRecord);
985
- case "workerRun":
986
- return isString(v.assignmentId) && isString(v.runId) && isOneOf(v.origin, WORKER_RUN_ORIGINS) && isString(v.agentId) && isWorkerRunRuntime(v.runtime) && isOptionalString(v.parentToolCallId);
1588
+ function summarizeUnlistedTargetValue(value) {
1589
+ if (typeof value === "string") {
1590
+ return `<string ${countLabel(Buffer.byteLength(value, "utf8"), "byte", "bytes")}>`;
987
1591
  }
988
- return false;
1592
+ if (Array.isArray(value)) return `<array ${countLabel(value.length, "item", "items")}>`;
1593
+ if (value !== null && typeof value === "object") {
1594
+ return `<object ${countLabel(Object.keys(value).length, "field", "fields")}>`;
1595
+ }
1596
+ if (value === null) return "<null 0 values>";
1597
+ if (typeof value === "undefined") return "<undefined 0 values>";
1598
+ return `<${typeof value} 1 value>`;
989
1599
  }
990
-
991
- // src/domains/session/task-board.ts
992
- var TASK_BOARD_GOAL_ID = "board";
993
- var LEGACY_TASK_BOARD_ID = "legacy";
994
- function isOpenUserTask(task) {
995
- return task.origin === "user" && (task.status === "pending" || task.status === "active");
996
- }
997
- function taskBoardCounts(board) {
998
- const counts = {
999
- total: board.tasks.length,
1000
- completed: 0,
1001
- active: 0,
1002
- pending: 0,
1003
- blocked: 0,
1004
- cancelled: 0,
1005
- open: 0
1006
- };
1007
- for (const task of board.tasks) {
1008
- if (task.status === "completed") counts.completed += 1;
1009
- else if (task.status === "active") counts.active += 1;
1010
- else if (task.status === "pending") counts.pending += 1;
1011
- else if (task.status === "blocked") counts.blocked += 1;
1012
- else counts.cancelled += 1;
1013
- }
1014
- counts.open = counts.pending + counts.active;
1015
- return counts;
1016
- }
1017
- function boardStatus(tasks) {
1018
- const counts = taskBoardCounts({ tasks });
1019
- if (counts.total === 0) return "pending";
1020
- if (counts.active > 0) return "active";
1021
- if (counts.open === 0 && counts.blocked === 0) return "completed";
1022
- if (counts.blocked > 0 && counts.open === 0) return "blocked";
1023
- return "pending";
1024
- }
1025
- function toTaskLedgerEntryFields(board, now) {
1026
- const evidence = [];
1027
- const subgoals = board.tasks.map((task) => {
1028
- if (task.status === "completed" && task.evidence) {
1029
- evidence.push({
1030
- id: `${task.id}.evidence`,
1031
- description: task.evidence,
1032
- status: "passed",
1033
- observedAt: now.toISOString()
1034
- });
1035
- }
1036
- const goal = {
1037
- id: task.id,
1038
- title: task.title,
1039
- status: task.status,
1040
- parentGoalId: TASK_BOARD_GOAL_ID
1041
- };
1042
- if (task.origin) goal.origin = task.origin;
1043
- if (task.userTaskId) goal.userTaskId = task.userTaskId;
1044
- if (task.reason) goal.description = task.reason;
1045
- return goal;
1046
- });
1047
- return {
1048
- kind: "taskLedger",
1049
- parentTurnId: null,
1050
- boardId: board.boardId,
1051
- goals: [{ id: TASK_BOARD_GOAL_ID, title: board.title, status: boardStatus(board.tasks) }],
1052
- subgoals,
1053
- activeRunIds: [...board.activeRunIds],
1054
- requiredValidationEvidence: evidence
1055
- };
1600
+ function targetFieldName(value) {
1601
+ return sanitizeCallTargetText(value).slice(0, 32) || "field";
1056
1602
  }
1057
- function isTaskLedgerShaped(value) {
1058
- return isSessionEntry(value) && value.kind === "taskLedger";
1059
- }
1060
- function foldTaskBoard(entries) {
1061
- let last = null;
1062
- for (const raw of entries) {
1063
- if (isTaskLedgerShaped(raw)) last = toEntryView(raw);
1064
- }
1065
- return last;
1066
- }
1067
- function foldSessionTaskHistory(entries) {
1068
- const boards = /* @__PURE__ */ new Map();
1069
- for (const [index, raw] of entries.entries()) {
1070
- if (!isTaskLedgerShaped(raw)) continue;
1071
- const view = toEntryView(raw);
1072
- if (!view) continue;
1073
- const boardId = typeof raw.boardId === "string" && raw.boardId.length > 0 ? raw.boardId : LEGACY_TASK_BOARD_ID;
1074
- const timestamp = typeof raw.timestamp === "string" ? raw.timestamp : "";
1075
- if (boardId === LEGACY_TASK_BOARD_ID) {
1076
- boards.set(boardId, {
1077
- boardId,
1078
- title: view.title,
1079
- tasks: new Map(view.tasks.map((task) => [task.id, task])),
1080
- lastSnapshotAt: timestamp,
1081
- lastIndex: index
1082
- });
1603
+ function describeCallTarget(tool, args) {
1604
+ if (!args) return "";
1605
+ const allowedFields = CALL_TARGET_FIELDS[tool] ?? [];
1606
+ const allowed = new Set(allowedFields);
1607
+ const parts = [];
1608
+ for (const field of allowedFields) {
1609
+ if (!(field in args)) continue;
1610
+ if (isSecretArgKey(field)) {
1611
+ parts.push(`${targetFieldName(field)}=${summarizeUnlistedTargetValue(args[field])}`);
1083
1612
  continue;
1084
1613
  }
1085
- const existing = boards.get(boardId);
1086
- if (!existing) {
1087
- boards.set(boardId, {
1088
- boardId,
1089
- title: view.title,
1090
- tasks: new Map(view.tasks.map((task) => [task.id, task])),
1091
- lastSnapshotAt: timestamp,
1092
- lastIndex: index
1093
- });
1614
+ const rendered = renderAllowedTargetValue(args[field]);
1615
+ if (rendered === null) {
1616
+ parts.push(`${targetFieldName(field)}=${summarizeUnlistedTargetValue(args[field])}`);
1094
1617
  continue;
1095
1618
  }
1096
- existing.title = view.title;
1097
- for (const task of view.tasks) existing.tasks.set(task.id, task);
1098
- existing.lastSnapshotAt = timestamp;
1099
- existing.lastIndex = index;
1100
- }
1101
- return [...boards.values()].sort((left, right) => right.lastIndex - left.lastIndex).map(({ lastIndex: _lastIndex, tasks, ...board }) => ({ ...board, tasks: [...tasks.values()] }));
1102
- }
1103
- function toEntryView(entry) {
1104
- const boardGoal = entry.goals[0];
1105
- if (!boardGoal) return null;
1106
- const evidenceByTask = /* @__PURE__ */ new Map();
1107
- for (const item of entry.requiredValidationEvidence) {
1108
- const taskId = item.id.endsWith(".evidence") ? item.id.slice(0, -".evidence".length) : item.id;
1109
- evidenceByTask.set(taskId, item.description);
1110
- }
1111
- return {
1112
- boardId: entry.boardId ?? LEGACY_TASK_BOARD_ID,
1113
- title: boardGoal.title,
1114
- tasks: entry.subgoals.map((goal) => {
1115
- const task = {
1116
- id: goal.id,
1117
- title: goal.title,
1118
- status: goal.status,
1119
- origin: goal.origin ?? "agent"
1120
- };
1121
- if (goal.userTaskId) task.userTaskId = goal.userTaskId;
1122
- if (goal.description) task.reason = goal.description;
1123
- const evidence = evidenceByTask.get(goal.id);
1124
- if (evidence) task.evidence = evidence;
1125
- return task;
1126
- }),
1127
- // Run linkage is process-live: the runs recorded in old entries ended
1128
- // with the process that dispatched them, so a refold starts empty and
1129
- // the historical linkage stays readable in the earlier entries.
1130
- activeRunIds: []
1131
- };
1132
- }
1133
- function nextTaskId(tasks) {
1134
- let max = 0;
1135
- for (const task of tasks) {
1136
- const match = /^t(\d+)$/.exec(task.id);
1137
- if (match) max = Math.max(max, Number(match[1]));
1138
- }
1139
- return max + 1;
1140
- }
1141
- function normalizeTitles(titles) {
1142
- return titles.map((title) => title.trim()).filter((title) => title.length > 0);
1143
- }
1144
- function applyMutation(board, mutation) {
1145
- if (mutation.op === "plan") {
1146
- const titles = normalizeTitles(mutation.tasks);
1147
- if (mutation.title.trim().length === 0) return { error: "plan requires a non-empty title" };
1148
- if (titles.length === 0) return { error: "plan requires at least one task" };
1149
- const retained = board?.tasks.filter(isOpenUserTask) ?? [];
1150
- const notes2 = [];
1151
- const replacedOpen = board ? taskBoardCounts(board).open - retained.length : 0;
1152
- if (board && replacedOpen > 0) {
1153
- notes2.push(`replaced board "${board.title}" with ${replacedOpen} agent task(s) still open`);
1154
- }
1155
- if (retained.length > 0) notes2.push(`preserved ${retained.length} open operator task(s)`);
1156
- const start = nextTaskId(retained);
1157
- return {
1158
- board: {
1159
- boardId: "",
1160
- title: mutation.title.trim(),
1161
- tasks: [
1162
- ...retained,
1163
- ...titles.map((title, index) => ({
1164
- id: `t${start + index}`,
1165
- title,
1166
- status: "pending",
1167
- origin: "agent"
1168
- }))
1169
- ],
1170
- // Runs in flight outlive a board swap: they belong to the session,
1171
- // so the fresh board inherits the linkage until the runs finish.
1172
- activeRunIds: board?.activeRunIds ?? []
1173
- },
1174
- notes: notes2
1175
- };
1176
- }
1177
- if (mutation.op === "pick") {
1178
- const title = mutation.title.trim();
1179
- if (title.length === 0) return { error: "pick requires a non-empty operator task title" };
1180
- if (!/^u[1-9]\d*$/.test(mutation.userTaskId)) return { error: "pick requires an operator task id like u3" };
1181
- if (board?.tasks.some(
1182
- (task) => task.userTaskId === mutation.userTaskId && (task.status === "pending" || task.status === "active" || task.status === "completed")
1183
- )) {
1184
- return { error: `operator task ${mutation.userTaskId} is already linked to durable board work` };
1185
- }
1186
- const tasks2 = board?.tasks ?? [];
1187
- const picked = {
1188
- id: `t${nextTaskId(tasks2)}`,
1189
- title,
1190
- status: "pending",
1191
- origin: "user",
1192
- userTaskId: mutation.userTaskId
1193
- };
1194
- return {
1195
- board: {
1196
- boardId: board?.boardId ?? "",
1197
- title: board?.title ?? "Operator tasks",
1198
- tasks: [...tasks2, picked],
1199
- activeRunIds: board?.activeRunIds ?? []
1200
- },
1201
- notes: []
1202
- };
1619
+ parts.push(parts.length === 0 ? rendered : `${targetFieldName(field)}=${rendered}`);
1203
1620
  }
1204
- if (board === null) return { error: 'no task board yet; declare one with action="plan" first' };
1205
- if (mutation.op === "add") {
1206
- const titles = normalizeTitles(mutation.tasks);
1207
- if (titles.length === 0) return { error: "add requires at least one task" };
1208
- const start = nextTaskId(board.tasks);
1209
- const added = titles.map((title, index) => ({
1210
- id: `t${start + index}`,
1211
- title,
1212
- status: "pending",
1213
- origin: "agent"
1214
- }));
1215
- return { board: { ...board, tasks: [...board.tasks, ...added] }, notes: [] };
1216
- }
1217
- const target = board.tasks.find((task) => task.id === mutation.id);
1218
- if (!target) return { error: `task ${mutation.id} not found on the board` };
1219
- if (mutation.op === "block" && mutation.reason.trim().length === 0) {
1220
- return { error: "block requires a reason so the ledger records why work stopped" };
1221
- }
1222
- if (mutation.op === "done") {
1223
- if (mutation.evidence.trim().length === 0) {
1224
- return {
1225
- error: "done requires note as the evidence that the task actually finished (the command you ran, the file:line you verified). If the work did not happen, use block with a reason or drop it."
1226
- };
1227
- }
1228
- if (target.status === "pending") {
1229
- return {
1230
- error: `task ${target.id} was never started, so it cannot be completed. Start it and do the work, or record the truth with block (reason) or drop.`
1231
- };
1232
- }
1621
+ for (const [field, value] of Object.entries(args)) {
1622
+ if (allowed.has(field)) continue;
1623
+ parts.push(`${targetFieldName(field)}=${summarizeUnlistedTargetValue(value)}`);
1233
1624
  }
1234
- const notes = [];
1235
- const tasks = board.tasks.map((task) => {
1236
- if (task.id !== target.id) {
1237
- if (mutation.op === "start" && task.status === "active") {
1238
- notes.push(`parked ${task.id} back to pending (one task active at a time)`);
1239
- return { ...task, status: "pending" };
1240
- }
1241
- return task;
1242
- }
1243
- return applyStatusMutation(task, mutation);
1244
- });
1245
- return { board: { ...board, tasks }, notes };
1246
- }
1247
- function applyStatusMutation(task, mutation) {
1248
- switch (mutation.op) {
1249
- case "start":
1250
- return { ...task, status: "active" };
1251
- case "done": {
1252
- const done = { ...task, status: "completed", evidence: mutation.evidence.trim() };
1253
- delete done.reason;
1254
- return done;
1255
- }
1256
- case "block":
1257
- return { ...task, status: "blocked", reason: mutation.reason.trim() };
1258
- case "drop": {
1259
- const dropped = { ...task, status: "cancelled" };
1260
- if (mutation.reason?.trim()) dropped.reason = mutation.reason.trim();
1261
- return dropped;
1262
- }
1263
- }
1264
- }
1265
- function createTaskBoardStore(deps = {}) {
1266
- let cachedSessionId;
1267
- let dirty = true;
1268
- let board = null;
1269
- const syncToSession = () => {
1270
- const sessionId = deps.getSessionId?.() ?? null;
1271
- if (!dirty && cachedSessionId === sessionId) return;
1272
- dirty = false;
1273
- cachedSessionId = sessionId;
1274
- try {
1275
- board = deps.readEntries ? foldTaskBoard(deps.readEntries()) : null;
1276
- } catch {
1277
- board = null;
1278
- }
1279
- };
1280
- const persist = (next) => {
1281
- try {
1282
- deps.appendEntry?.(toTaskLedgerEntryFields(next, deps.now?.() ?? /* @__PURE__ */ new Date()));
1283
- } catch {
1284
- }
1285
- };
1286
- const mutationRequiresAcknowledgement = (current, mutation) => {
1287
- if (mutation.op === "pick") return true;
1288
- if (mutation.op === "plan") return current?.tasks.some(isOpenUserTask) ?? false;
1289
- if (mutation.op !== "done") return false;
1290
- return current?.tasks.some((task) => task.id === mutation.id && task.origin === "user") ?? false;
1291
- };
1292
- return {
1293
- snapshot() {
1294
- syncToSession();
1295
- return board;
1296
- },
1297
- cachedSnapshot() {
1298
- const sessionId = deps.getSessionId?.() ?? null;
1299
- return !dirty && cachedSessionId === sessionId ? board : null;
1300
- },
1301
- historySnapshot() {
1302
- syncToSession();
1303
- try {
1304
- return deps.readEntries ? foldSessionTaskHistory(deps.readEntries()) : [];
1305
- } catch {
1306
- return [];
1307
- }
1308
- },
1309
- apply(mutation) {
1310
- syncToSession();
1311
- const result = applyMutation(board, mutation);
1312
- if ("error" in result) return { ok: false, message: result.error };
1313
- const needsBoardId = mutation.op === "plan" || result.board.boardId.length === 0;
1314
- const nextBoard = needsBoardId ? { ...result.board, boardId: deps.createBoardId?.() ?? randomUUID() } : result.board;
1315
- if (mutationRequiresAcknowledgement(board, mutation)) {
1316
- if (!deps.appendEntry) {
1317
- return { ok: false, message: "durable task-ledger persistence is unavailable for operator-linked work" };
1318
- }
1319
- try {
1320
- deps.appendEntry(toTaskLedgerEntryFields(nextBoard, deps.now?.() ?? /* @__PURE__ */ new Date()));
1321
- } catch (error) {
1322
- const message = error instanceof Error ? error.message : String(error);
1323
- return { ok: false, message: `could not persist operator-linked task board: ${message}` };
1324
- }
1325
- board = nextBoard;
1326
- return { ok: true, board: nextBoard, notes: result.notes };
1327
- }
1328
- board = nextBoard;
1329
- persist(nextBoard);
1330
- return { ok: true, board: nextBoard, notes: result.notes };
1331
- },
1332
- attachRun(runId) {
1333
- syncToSession();
1334
- if (board === null || runId.length === 0 || board.activeRunIds.includes(runId)) return;
1335
- board = { ...board, activeRunIds: [...board.activeRunIds, runId] };
1336
- persist(board);
1337
- },
1338
- detachRun(runId) {
1339
- syncToSession();
1340
- if (board === null || !board.activeRunIds.includes(runId)) return;
1341
- board = { ...board, activeRunIds: board.activeRunIds.filter((id) => id !== runId) };
1342
- persist(board);
1343
- },
1344
- invalidate() {
1345
- dirty = true;
1346
- }
1347
- };
1625
+ return sanitizeCallTargetText(parts.join(" \xB7 ")).slice(0, CALL_TARGET_MAX_CHARS);
1348
1626
  }
1349
1627
 
1350
1628
  // src/engine/antigravity/subprocess-runtime.ts
1351
1629
  init_esm_shims();
1352
- import { spawn } from "node:child_process";
1630
+ import { spawn as spawn2 } from "node:child_process";
1353
1631
 
1354
1632
  // src/engine/external-subprocess.ts
1355
1633
  init_esm_shims();
@@ -1464,7 +1742,7 @@ async function readStdout(child, emit, state) {
1464
1742
  }
1465
1743
  function startAntigravityWorkerRun(input, emit) {
1466
1744
  const args = buildAgyArgs(input);
1467
- const child = spawn(ANTIGRAVITY_BINARY, args, {
1745
+ const child = spawn2(ANTIGRAVITY_BINARY, args, {
1468
1746
  cwd: process.cwd(),
1469
1747
  env: process.env,
1470
1748
  stdio: ["ignore", "pipe", "pipe"]
@@ -1519,7 +1797,7 @@ function startAntigravityWorkerRun(input, emit) {
1519
1797
 
1520
1798
  // src/engine/claude/subprocess-runtime.ts
1521
1799
  init_esm_shims();
1522
- import { spawn as spawn2 } from "node:child_process";
1800
+ import { spawn as spawn3 } from "node:child_process";
1523
1801
  import { createInterface } from "node:readline";
1524
1802
 
1525
1803
  // src/engine/claude/session-id.ts
@@ -1712,7 +1990,7 @@ async function readJsonLines(child, emit, state) {
1712
1990
  }
1713
1991
  function startClaudeCodeWorkerRun(input, emit) {
1714
1992
  const args = buildClaudeCodeArgs(input);
1715
- const child = spawn2("claude", args, {
1993
+ const child = spawn3("claude", args, {
1716
1994
  cwd: process.cwd(),
1717
1995
  env: process.env,
1718
1996
  stdio: ["ignore", "pipe", "pipe"]
@@ -1775,6 +2053,12 @@ function startClaudeCodeWorkerRun(input, emit) {
1775
2053
  init_esm_shims();
1776
2054
  var WORKER_RUNTIME_MEDIATES_CLIO_DISPATCH = false;
1777
2055
 
2056
+ // src/tools/builtin-tool-catalog.ts
2057
+ init_esm_shims();
2058
+
2059
+ // src/tools/bash.ts
2060
+ init_esm_shims();
2061
+
1778
2062
  // src/tools/presentation.ts
1779
2063
  init_esm_shims();
1780
2064
  var FOLDED = { foldDefault: "folded", showDiffWhenFolded: false, failureExcerpt: true };
@@ -1827,8 +2111,215 @@ function toolPresentationPolicy(toolName, args) {
1827
2111
  return TOOL_PRESENTATION[toolName] ?? FOLDED;
1828
2112
  }
1829
2113
 
1830
- // src/tools/builtin-tool-catalog.ts
1831
- init_esm_shims();
2114
+ // src/tools/bash.ts
2115
+ var BASH_DISPLAY_MAX_BYTES = 16 * 1024;
2116
+ var BASH_OUTPUT_POLICIES = /* @__PURE__ */ new Set(["full", "bounded", "summary", "metadata-only"]);
2117
+ function bashContextDisposition(policy) {
2118
+ if (policy === "full") {
2119
+ return { mode: "full", maxBytes: BASH_DISPLAY_MAX_BYTES, downgradeExcerpt: "tail" };
2120
+ }
2121
+ if (policy === "summary") {
2122
+ return {
2123
+ mode: "summary",
2124
+ maxBytes: BASH_DISPLAY_MAX_BYTES,
2125
+ strategy: "diagnostic",
2126
+ redact: true
2127
+ };
2128
+ }
2129
+ if (policy === "metadata-only") return { mode: "metadata-only", maxBytes: BASH_DISPLAY_MAX_BYTES };
2130
+ return { mode: "bounded", maxBytes: BASH_DISPLAY_MAX_BYTES, excerpt: "tail" };
2131
+ }
2132
+ var BASH_DEFAULT_RESULT_DISPOSITION = {
2133
+ presentation: {
2134
+ ...toolPresentationPolicy(ToolNames.Bash, void 0),
2135
+ maxBytes: BASH_DISPLAY_MAX_BYTES,
2136
+ overflow: "tail"
2137
+ },
2138
+ context: bashContextDisposition("bounded")
2139
+ };
2140
+ function normalizeBashArguments(args) {
2141
+ return args.output_policy === void 0 ? { ...args, output_policy: "bounded" } : args;
2142
+ }
2143
+ function bashOutputPolicy(args) {
2144
+ const value = args.output_policy;
2145
+ return typeof value === "string" && BASH_OUTPUT_POLICIES.has(value) ? value : null;
2146
+ }
2147
+ function resolveBashResultDisposition(args, declared = BASH_DEFAULT_RESULT_DISPOSITION) {
2148
+ const policy = bashOutputPolicy(args) ?? "bounded";
2149
+ const base = declared ?? BASH_DEFAULT_RESULT_DISPOSITION;
2150
+ return { presentation: base.presentation, context: bashContextDisposition(policy) };
2151
+ }
2152
+ function normalizedBashDisposition(policy) {
2153
+ return {
2154
+ presentation: { ...BASH_DEFAULT_RESULT_DISPOSITION.presentation, maxBytes: BASH_DISPLAY_MAX_BYTES },
2155
+ context: bashContextDisposition(policy)
2156
+ };
2157
+ }
2158
+ function shapeBashProgress(rawOutput, outputBytes, policy) {
2159
+ const disposition = normalizedBashDisposition(policy);
2160
+ let output;
2161
+ if (policy === "metadata-only") {
2162
+ output = `bash: running; captured ${outputBytes} bytes; stdout/stderr omitted by metadata-only policy`;
2163
+ } else if (policy === "summary") {
2164
+ output = deterministicDiagnosticSummary(rawOutput, BASH_DISPLAY_MAX_BYTES, true).text;
2165
+ } else {
2166
+ output = truncateTail(rawOutput, {
2167
+ maxLines: DEFAULT_MAX_LINES,
2168
+ maxBytes: disposition.context.maxBytes
2169
+ }).content;
2170
+ }
2171
+ return {
2172
+ kind: "ok",
2173
+ output,
2174
+ details: {
2175
+ resultSize: {
2176
+ bytes: outputBytes,
2177
+ shownBytes: byteLength(output),
2178
+ maxBytes: BASH_DISPLAY_MAX_BYTES,
2179
+ truncated: byteLength(rawOutput) > byteLength(output),
2180
+ policy
2181
+ }
2182
+ }
2183
+ };
2184
+ }
2185
+ function bashResultDetails(result, rawOutput, outcome) {
2186
+ return {
2187
+ outcome,
2188
+ exitCode: result.exitCode,
2189
+ signal: result.signal,
2190
+ timedOut: result.timedOut,
2191
+ aborted: result.aborted,
2192
+ outputCapped: result.outputCapped,
2193
+ outputBytes: result.outputBytes,
2194
+ retainedBytes: byteLength(rawOutput),
2195
+ stdoutBytes: byteLength(result.stdout),
2196
+ stderrBytes: byteLength(result.stderr)
2197
+ };
2198
+ }
2199
+ var OBSERVE_COMMAND_PATTERN = /^\s*(?:cat|head|tail|ls|grep|rg|find)\b/;
2200
+ var OBSERVE_NUDGE_SESSION_LIMIT = 64;
2201
+ var observeNudgeSeenSessions = /* @__PURE__ */ new Set();
2202
+ function observeToolsNudge(command, sessionId) {
2203
+ if (!OBSERVE_COMMAND_PATTERN.test(command)) return "";
2204
+ const key = sessionId ?? "no-session";
2205
+ if (observeNudgeSeenSessions.has(key)) return "";
2206
+ if (observeNudgeSeenSessions.size >= OBSERVE_NUDGE_SESSION_LIMIT) {
2207
+ const oldest = observeNudgeSeenSessions.values().next().value;
2208
+ if (oldest !== void 0) observeNudgeSeenSessions.delete(oldest);
2209
+ }
2210
+ observeNudgeSeenSessions.add(key);
2211
+ return "\n\n[note: prefer the structured observe tools over shell file inspection: read pages files with offset/limit, ls lists directories, grep and find search, code_nav maps symbols. Their results are capped and continuable; keep bash for builds, git, and scripts.]";
2212
+ }
2213
+ var bashTool = {
2214
+ name: ToolNames.Bash,
2215
+ description: "Execute a bash command inside the session workspace. output_policy controls model context: bounded keeps the diagnostic tail and is the default; summary keeps deterministic redacted diagnostics; metadata-only keeps facts and retrieval; full is admitted only within the result budget.",
2216
+ parameters: typebox_exports.Object({
2217
+ command: typebox_exports.String({ description: "Bash command to execute." }),
2218
+ cwd: typebox_exports.Optional(
2219
+ typebox_exports.String({
2220
+ description: "Omit this. The command already runs at the session workspace root. Set it only to run in a subdirectory of that root, as a relative path; an absolute path outside the root is blocked."
2221
+ })
2222
+ ),
2223
+ timeout_ms: typebox_exports.Optional(typebox_exports.Number({ description: "Timeout in milliseconds." })),
2224
+ output_policy: typebox_exports.Optional(
2225
+ typebox_exports.Union([typebox_exports.Literal("full"), typebox_exports.Literal("bounded"), typebox_exports.Literal("summary"), typebox_exports.Literal("metadata-only")], {
2226
+ description: "Model-context disposition. Omit for bounded tail output. Use summary for noisy runs, metadata-only when only status and retrieval matter, and full only for known-small output."
2227
+ })
2228
+ )
2229
+ }),
2230
+ baseActionClass: "execute",
2231
+ executionMode: "sequential",
2232
+ prepareArguments: normalizeBashArguments,
2233
+ resolveResultDisposition: resolveBashResultDisposition,
2234
+ async run(args, options) {
2235
+ args = normalizeBashArguments(args);
2236
+ if (typeof args.command !== "string" || args.command.length === 0) {
2237
+ return { kind: "error", message: "bash: missing command argument" };
2238
+ }
2239
+ const outputPolicy = bashOutputPolicy(args);
2240
+ if (outputPolicy === null) {
2241
+ return {
2242
+ kind: "error",
2243
+ message: "bash: output_policy must be one of full, bounded, summary, or metadata-only (omit it for bounded)",
2244
+ details: { outcome: "invalid-arguments", exitCode: null }
2245
+ };
2246
+ }
2247
+ const cwdArg = typeof args.cwd === "string" && args.cwd.length > 0 ? args.cwd : void 0;
2248
+ let cwd;
2249
+ try {
2250
+ cwd = resolveSafeCwd(cwdArg === void 0 ? void 0 : expandPath(cwdArg), process.cwd());
2251
+ } catch {
2252
+ return {
2253
+ kind: "error",
2254
+ message: `bash: cwd '${cwdArg}' escapes workspace root '${process.cwd()}'; use a typed tool or a project policy entry with explicit cwd`
2255
+ };
2256
+ }
2257
+ const timeout = typeof args.timeout_ms === "number" && args.timeout_ms > 0 ? args.timeout_ms : 3e5;
2258
+ try {
2259
+ const runOptions = {
2260
+ cwd,
2261
+ timeoutMs: timeout,
2262
+ ...options?.signal === void 0 ? {} : { signal: options.signal }
2263
+ };
2264
+ if (options?.onUpdate !== void 0) {
2265
+ runOptions.onUpdate = (progress) => {
2266
+ options.onUpdate?.(shapeBashProgress(combineBashOutput(progress), progress.outputBytes, outputPolicy));
2267
+ };
2268
+ }
2269
+ const result = await runBashCommand(args.command, runOptions);
2270
+ const { error, aborted, timedOut, outputCapped } = result;
2271
+ const rawOutput = combineBashOutput(result);
2272
+ const output = rawOutput.trim();
2273
+ if (aborted) {
2274
+ const status = "bash: command aborted";
2275
+ return {
2276
+ kind: "error",
2277
+ message: output.length > 0 ? `${output}
2278
+
2279
+ ${status}` : status,
2280
+ details: bashResultDetails(result, rawOutput, "abort")
2281
+ };
2282
+ }
2283
+ if (timedOut) {
2284
+ const status = `bash: command timed out after ${timeout}ms`;
2285
+ return {
2286
+ kind: "error",
2287
+ message: output.length > 0 ? `${output}
2288
+
2289
+ ${status}` : status,
2290
+ details: bashResultDetails(result, rawOutput, "timeout")
2291
+ };
2292
+ }
2293
+ if (outputCapped) {
2294
+ const status = `bash: command output exceeded ${BASH_HARD_CAP_BYTES} bytes and was stopped`;
2295
+ return {
2296
+ kind: "error",
2297
+ message: output.length > 0 ? `${output}
2298
+
2299
+ ${status}` : status,
2300
+ details: bashResultDetails(result, rawOutput, "output-cap")
2301
+ };
2302
+ }
2303
+ if (error) {
2304
+ const code = typeof error.code === "number" ? error.code : error.code;
2305
+ const status = `bash: command failed (exit ${code ?? "?"})`;
2306
+ const message = output.length > 0 ? `${output}
2307
+
2308
+ ${status}` : `${status}: ${error.message}`;
2309
+ return { kind: "error", message, details: bashResultDetails(result, rawOutput, "nonzero") };
2310
+ }
2311
+ const body = rawOutput.length > 0 ? rawOutput : "(no output)";
2312
+ return {
2313
+ kind: "ok",
2314
+ output: `${body}${observeToolsNudge(args.command, options?.sessionId)}`,
2315
+ details: bashResultDetails(result, rawOutput, "success")
2316
+ };
2317
+ } catch (err) {
2318
+ const msg = err instanceof Error ? err.message : String(err);
2319
+ return { kind: "error", message: `bash: ${msg}` };
2320
+ }
2321
+ }
2322
+ };
1832
2323
 
1833
2324
  // src/tools/read.ts
1834
2325
  init_esm_shims();
@@ -2031,7 +2522,7 @@ var TOOL_METADATA = {
2031
2522
  "Use a narrower query or scope to inspect omitted content."
2032
2523
  ),
2033
2524
  costLatency: "local_fast",
2034
- promptHint: 'Call context with scope="skills" to list installed and marketplace skills; when one matches the task, or the operator names a skill or asks how one works, suggest the operator run /skill <name> (a marketplace skill is offered for install) and never load it uninvited. When the user message carries a skill request, first load that skill via context (scope="skills", name=<skill>) before doing anything else.'
2525
+ promptHint: 'Call context with scope="skills" to list installed and marketplace skills; when one matches the task, or the operator names a skill or asks how one works, suggest the operator run /skill <name> (a marketplace skill is offered for install) and never load it uninvited. When the user message carries a skill request, first load that skill via context (scope="skills", name=<skill>) before doing anything else. When an [evicted ...] marker names content you need, recall it with context(scope="recall", ref=...); re-read the file only when the marker says it changed.'
2035
2526
  },
2036
2527
  [ToolNames.CredentialPresent]: {
2037
2528
  objective: "Check whether a credential key is present without returning its value.",
@@ -2059,15 +2550,18 @@ var TOOL_METADATA = {
2059
2550
  resultSizePolicy: exactMutationPolicy,
2060
2551
  costLatency: "local_fast"
2061
2552
  },
2062
- // EXECUTE: the safe-exec spine (bash offloads its own overflow).
2553
+ // EXECUTE: the safe-exec spine (canonical shaping offloads Bash overflow).
2063
2554
  [ToolNames.Bash]: {
2064
2555
  objective: "Execute an explicit shell command when narrower tools are insufficient.",
2065
2556
  uiLabel: "Shell",
2066
2557
  retrySafety: "unknown",
2067
- resultSizePolicy: summaryPolicy(
2068
- "Use a narrower command or a dedicated verification/read/search tool to inspect omitted output."
2069
- ),
2070
- costLatency: "local_slow"
2558
+ resultSizePolicy: {
2559
+ ...summaryPolicy("Use a narrower command or a dedicated verification/read/search tool to inspect omitted output."),
2560
+ offloadMaxBytes: BASH_HARD_CAP_BYTES
2561
+ },
2562
+ resultDisposition: BASH_DEFAULT_RESULT_DISPOSITION,
2563
+ costLatency: "local_slow",
2564
+ promptHint: "Bash output_policy defaults to bounded diagnostic tail. Use summary for noisy commands, metadata-only when only outcome and retrieval matter, and full only when output is known to fit the bounded result budget."
2071
2565
  },
2072
2566
  [ToolNames.Git]: {
2073
2567
  objective: "Read-only git inspection: status, diff, or log.",
@@ -2548,6 +3042,7 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
2548
3042
  "target",
2549
3043
  "model",
2550
3044
  "thinkingLevel",
3045
+ "budget",
2551
3046
  "node",
2552
3047
  "routingIntent",
2553
3048
  "failover",
@@ -2578,12 +3073,12 @@ var VALID_COMPETE_STANCES = new Set(COMPETE_STANCES);
2578
3073
  var VALID_THINKING = new Set(THINKING_LEVELS);
2579
3074
  var VALID_REQUEST_ORIGINS = /* @__PURE__ */ new Set(["user", "agent", "internal"]);
2580
3075
  var VALID_FAILOVER_MODES = /* @__PURE__ */ new Set(["none", "approved", "automatic"]);
2581
- function isPlainObject2(value) {
3076
+ function isPlainObject3(value) {
2582
3077
  return typeof value === "object" && value !== null && !Array.isArray(value);
2583
3078
  }
2584
3079
  function validateJobSpec(spec) {
2585
3080
  const errors = [];
2586
- if (!isPlainObject2(spec)) {
3081
+ if (!isPlainObject3(spec)) {
2587
3082
  return { ok: false, errors: ["spec must be an object"] };
2588
3083
  }
2589
3084
  for (const key of Object.keys(spec)) {
@@ -2672,7 +3167,7 @@ function validateJobSpec(spec) {
2672
3167
  errors.push("failover automatic is not allowed on a plan-approved dispatch");
2673
3168
  }
2674
3169
  if ("plannedNode" in spec && spec.plannedNode !== void 0) {
2675
- if (!isPlainObject2(spec.plannedNode)) {
3170
+ if (!isPlainObject3(spec.plannedNode)) {
2676
3171
  errors.push("plannedNode must be an object");
2677
3172
  } else {
2678
3173
  for (const key of Object.keys(spec.plannedNode)) {
@@ -2693,7 +3188,7 @@ function validateJobSpec(spec) {
2693
3188
  }
2694
3189
  }
2695
3190
  if ("protectedArtifactRemap" in spec && spec.protectedArtifactRemap !== void 0) {
2696
- if (!isPlainObject2(spec.protectedArtifactRemap) || Object.keys(spec.protectedArtifactRemap).some((key) => key !== "sourceRoot" && key !== "workerRoot") || typeof spec.protectedArtifactRemap.sourceRoot !== "string" || !path2.isAbsolute(spec.protectedArtifactRemap.sourceRoot) || typeof spec.protectedArtifactRemap.workerRoot !== "string" || !path2.isAbsolute(spec.protectedArtifactRemap.workerRoot)) {
3191
+ if (!isPlainObject3(spec.protectedArtifactRemap) || Object.keys(spec.protectedArtifactRemap).some((key) => key !== "sourceRoot" && key !== "workerRoot") || typeof spec.protectedArtifactRemap.sourceRoot !== "string" || !path2.isAbsolute(spec.protectedArtifactRemap.sourceRoot) || typeof spec.protectedArtifactRemap.workerRoot !== "string" || !path2.isAbsolute(spec.protectedArtifactRemap.workerRoot)) {
2697
3192
  errors.push("protectedArtifactRemap must carry absolute sourceRoot and workerRoot paths");
2698
3193
  }
2699
3194
  }
@@ -2707,6 +3202,14 @@ function validateJobSpec(spec) {
2707
3202
  errors.push("thinkingLevel must be one of: off|minimal|low|medium|high|xhigh");
2708
3203
  }
2709
3204
  }
3205
+ let budget;
3206
+ if ("budget" in spec && spec.budget !== void 0) {
3207
+ try {
3208
+ budget = cloneDispatchBudgetRequest(spec.budget);
3209
+ } catch (error) {
3210
+ errors.push(error instanceof Error ? error.message : String(error));
3211
+ }
3212
+ }
2710
3213
  if ("requiredCapabilities" in spec && spec.requiredCapabilities !== void 0) {
2711
3214
  if (!Array.isArray(spec.requiredCapabilities) || spec.requiredCapabilities.some((c) => typeof c !== "string")) {
2712
3215
  errors.push("requiredCapabilities must be a string[]");
@@ -2833,6 +3336,7 @@ function validateJobSpec(spec) {
2833
3336
  out.reroutes = spec.reroutes.map((hop) => ({ ...hop }));
2834
3337
  }
2835
3338
  if (typeof spec.thinkingLevel === "string") out.thinkingLevel = spec.thinkingLevel;
3339
+ if (budget !== void 0) out.budget = budget;
2836
3340
  if (Array.isArray(spec.requiredCapabilities)) {
2837
3341
  out.requiredCapabilities = spec.requiredCapabilities.map((c) => String(c));
2838
3342
  }
@@ -2869,7 +3373,7 @@ function isValidPredecessorHandoffs(value) {
2869
3373
  if (!Array.isArray(value) || value.length > EXECUTION_HANDOFF_MAX_ITEMS || Buffer.byteLength(JSON.stringify(value), "utf8") > EXECUTION_HANDOFF_MAX_TEXT_BYTES * 2)
2870
3374
  return false;
2871
3375
  return value.every((candidate) => {
2872
- if (!isPlainObject2(candidate)) return false;
3376
+ if (!isPlainObject3(candidate)) return false;
2873
3377
  const fields = [
2874
3378
  candidate.stepId,
2875
3379
  candidate.assignmentId,
@@ -2881,14 +3385,14 @@ function isValidPredecessorHandoffs(value) {
2881
3385
  });
2882
3386
  }
2883
3387
  function isValidPipelineInput(value) {
2884
- if (!isPlainObject2(value)) return false;
3388
+ if (!isPlainObject3(value)) return false;
2885
3389
  const fromRunOk = value.fromRunId === null || typeof value.fromRunId === "string" && value.fromRunId.length > 0;
2886
3390
  const positionOk = typeof value.position === "number" && Number.isInteger(value.position) && value.position >= 1;
2887
3391
  const textOk = typeof value.text === "string";
2888
3392
  return fromRunOk && positionOk && textOk;
2889
3393
  }
2890
3394
  function isValidReroute(value) {
2891
- if (!isPlainObject2(value)) return false;
3395
+ if (!isPlainObject3(value)) return false;
2892
3396
  const attemptOk = typeof value.attempt === "number" && Number.isInteger(value.attempt) && value.attempt >= 1;
2893
3397
  const fromOk = typeof value.fromNode === "string" && value.fromNode.length > 0;
2894
3398
  const toOk = typeof value.toNode === "string";
@@ -2896,7 +3400,7 @@ function isValidReroute(value) {
2896
3400
  return attemptOk && fromOk && toOk && reasonOk;
2897
3401
  }
2898
3402
  function isValidFailoverCandidate(value) {
2899
- if (!isPlainObject2(value)) return false;
3403
+ if (!isPlainObject3(value)) return false;
2900
3404
  if (Object.keys(value).some((key) => key !== "agentId" && key !== "target" && key !== "model" && key !== "node")) {
2901
3405
  return false;
2902
3406
  }
@@ -2905,26 +3409,26 @@ function isValidFailoverCandidate(value) {
2905
3409
  );
2906
3410
  }
2907
3411
  function isValidPlannedNode(value) {
2908
- if (!isPlainObject2(value) || typeof value.id !== "string" || value.id.trim().length === 0) return false;
3412
+ if (!isPlainObject3(value) || typeof value.id !== "string" || value.id.trim().length === 0) return false;
2909
3413
  if (value.kind === "local") return value.host === void 0;
2910
3414
  return value.kind === "ssh" && typeof value.host === "string" && value.host.trim().length > 0;
2911
3415
  }
2912
3416
  function isValidProtectedArtifactRemap(value) {
2913
- return isPlainObject2(value) && typeof value.sourceRoot === "string" && path2.isAbsolute(value.sourceRoot) && typeof value.workerRoot === "string" && path2.isAbsolute(value.workerRoot);
3417
+ return isPlainObject3(value) && typeof value.sourceRoot === "string" && path2.isAbsolute(value.sourceRoot) && typeof value.workerRoot === "string" && path2.isAbsolute(value.workerRoot);
2914
3418
  }
2915
3419
  var VALID_GATE_ROLES = /* @__PURE__ */ new Set(["builder", "reviewer", "candidate", "judge"]);
2916
3420
  var VALID_GATE_VERDICTS = /* @__PURE__ */ new Set(["pass", "fail", "revise"]);
2917
3421
  var VALID_PLAN_TOPOLOGIES = /* @__PURE__ */ new Set(["parallel", "sequential", "pipeline", "review", "compete", "detached", "fleet"]);
2918
3422
  var VALID_PLAN_APPROVALS = /* @__PURE__ */ new Set(["operator", "full-auto"]);
2919
3423
  function isValidGate(value) {
2920
- if (!isPlainObject2(value)) return false;
3424
+ if (!isPlainObject3(value)) return false;
2921
3425
  if (typeof value.role !== "string" || !VALID_GATE_ROLES.has(value.role)) return false;
2922
3426
  if (typeof value.group !== "string" || value.group.length === 0) return false;
2923
3427
  if (typeof value.cycle !== "number" || !Number.isInteger(value.cycle) || value.cycle < 1) return false;
2924
3428
  if (value.subjects !== void 0) {
2925
3429
  if (!Array.isArray(value.subjects)) return false;
2926
3430
  for (const subject of value.subjects) {
2927
- if (!isPlainObject2(subject)) return false;
3431
+ if (!isPlainObject3(subject)) return false;
2928
3432
  if (typeof subject.runId !== "string" || subject.runId.length === 0) return false;
2929
3433
  if (subject.digest !== null && typeof subject.digest !== "string") return false;
2930
3434
  }
@@ -2944,13 +3448,13 @@ function cloneGate(gate) {
2944
3448
  };
2945
3449
  }
2946
3450
  function isValidPlan(value) {
2947
- if (!isPlainObject2(value)) return false;
3451
+ if (!isPlainObject3(value)) return false;
2948
3452
  if (typeof value.hash !== "string" || value.hash.length === 0) return false;
2949
3453
  if (typeof value.topology !== "string" || !VALID_PLAN_TOPOLOGIES.has(value.topology)) return false;
2950
3454
  if (typeof value.taskCount !== "number" || !Number.isInteger(value.taskCount) || value.taskCount < 1) return false;
2951
3455
  if (typeof value.approval !== "string" || !VALID_PLAN_APPROVALS.has(value.approval)) return false;
2952
3456
  if (value.source !== null) {
2953
- if (!isPlainObject2(value.source) || value.source.kind !== "scout-transition" || typeof value.source.runId !== "string" || value.source.runId.length === 0 || typeof value.source.receiptDigest !== "string" || !/^[0-9a-f]{64}$/u.test(value.source.receiptDigest) || typeof value.source.executionPlanHash !== "string" || !/^[0-9a-f]{64}$/u.test(value.source.executionPlanHash))
3457
+ if (!isPlainObject3(value.source) || value.source.kind !== "scout-transition" || typeof value.source.runId !== "string" || value.source.runId.length === 0 || typeof value.source.receiptDigest !== "string" || !/^[0-9a-f]{64}$/u.test(value.source.receiptDigest) || typeof value.source.executionPlanHash !== "string" || !/^[0-9a-f]{64}$/u.test(value.source.executionPlanHash))
2954
3458
  return false;
2955
3459
  }
2956
3460
  if (value.approvalRequestId !== void 0 && (typeof value.approvalRequestId !== "string" || value.approvalRequestId.length === 0)) {
@@ -2965,7 +3469,7 @@ function isValidPlan(value) {
2965
3469
  return true;
2966
3470
  }
2967
3471
  function isValidLineage(value) {
2968
- if (!isPlainObject2(value)) return false;
3472
+ if (!isPlainObject3(value)) return false;
2969
3473
  const parentOk = value.parentRunId === null || typeof value.parentRunId === "string" && value.parentRunId.length > 0;
2970
3474
  const rootOk = typeof value.rootRunId === "string" && value.rootRunId.length > 0;
2971
3475
  const attemptOk = typeof value.attempt === "number" && Number.isInteger(value.attempt) && value.attempt >= 0;
@@ -3180,9 +3684,10 @@ function finishDecision(decision) {
3180
3684
  if (decision.kind === "ask") return "permission_requested";
3181
3685
  return "blocked";
3182
3686
  }
3183
- function emitToolFinish(emit, mapped, startedAtClock, decision, outcome, reason, reasonCode) {
3687
+ function emitToolFinish(emit, mapped, startedAtClock, decision, outcome, reason, reasonCode, toolCallId) {
3184
3688
  const event = {
3185
3689
  tool: mapped.clioToolName,
3690
+ ...toolCallId !== void 0 ? { toolCallId } : {},
3186
3691
  posture: "operating",
3187
3692
  durationMs: Math.round(performance.now() - startedAtClock),
3188
3693
  outcome,
@@ -3200,10 +3705,14 @@ function emitClaudeToolPermissionDecision(input) {
3200
3705
  const startedAt = Date.now();
3201
3706
  const startedAtClock = performance.now();
3202
3707
  const decision = evaluateClaudeToolPermission(input);
3708
+ const toolCallId = input.toolCallId?.trim() ? input.toolCallId : void 0;
3709
+ const action = describeCallAction(decision.mapped.clioToolName, decision.mapped.args);
3203
3710
  const start = {
3204
3711
  tool: decision.mapped.clioToolName,
3712
+ ...toolCallId !== void 0 ? { toolCallId } : {},
3205
3713
  posture: "operating",
3206
- startedAt
3714
+ startedAt,
3715
+ ...action !== null ? { action } : {}
3207
3716
  };
3208
3717
  input.emit({ type: "clio_tool_start", payload: start });
3209
3718
  if (decision.kind === "allow") {
@@ -3214,7 +3723,8 @@ function emitClaudeToolPermissionDecision(input) {
3214
3723
  decision.decision,
3215
3724
  "ok",
3216
3725
  decision.reason,
3217
- decision.reasonCode
3726
+ decision.reasonCode,
3727
+ toolCallId
3218
3728
  );
3219
3729
  return decision;
3220
3730
  }
@@ -3237,7 +3747,8 @@ function emitClaudeToolPermissionDecision(input) {
3237
3747
  decision.decision,
3238
3748
  "blocked",
3239
3749
  decision.reason,
3240
- decision.reasonCode
3750
+ decision.reasonCode,
3751
+ toolCallId
3241
3752
  );
3242
3753
  return decision;
3243
3754
  }
@@ -3419,6 +3930,11 @@ export {
3419
3930
  responseSchemaConflictsWithTools,
3420
3931
  UnsupportedResponseSchemaError,
3421
3932
  isResponseSchemaRejection,
3933
+ redactSecretString,
3934
+ redactToolArgs,
3935
+ sanitizeCallTargetText,
3936
+ describeCallAction,
3937
+ describeCallTarget,
3422
3938
  TOOL_PROFILE_NAMES,
3423
3939
  isToolProfileName,
3424
3940
  assertToolProfileEnforceable,
@@ -3433,8 +3949,11 @@ export {
3433
3949
  emitClaudeToolPermissionDecision,
3434
3950
  coerceToolInput,
3435
3951
  WORKER_RUNTIME_MEDIATES_CLIO_DISPATCH,
3952
+ combineBashOutput,
3953
+ runBashCommand,
3436
3954
  classifyResourceRead,
3437
3955
  toolPresentationPolicy,
3956
+ bashTool,
3438
3957
  readMaxBytes,
3439
3958
  readTool,
3440
3959
  toolPromptHintsForNames,
@@ -3461,6 +3980,13 @@ export {
3461
3980
  AGENT_LEDGER_PROMPT_MAX_CHARS,
3462
3981
  claimConflicts,
3463
3982
  renderAgentLedger,
3983
+ cloneDispatchBudgetRequest,
3984
+ resolveToolBudgetEnvelope,
3985
+ formatBudgetPolicy,
3986
+ formatBudgetRequest,
3987
+ formatEffectiveBudget,
3988
+ formatBudgetReasons,
3989
+ cloneRunToolBudgetEnvelope,
3464
3990
  REVIEWER_GATE_PROMPT,
3465
3991
  JUDGE_GATE_PROMPT,
3466
3992
  COMPETE_STANCES,
@@ -3472,11 +3998,6 @@ export {
3472
3998
  runKindSupportsLiveSteering,
3473
3999
  projectExecutionHandoffs,
3474
4000
  DISPATCH_BRIEFING_MAX_BYTES,
3475
- validateJobSpec,
3476
- isSessionHeader,
3477
- isSessionEntry,
3478
- taskBoardCounts,
3479
- foldSessionTaskHistory,
3480
- createTaskBoardStore
4001
+ validateJobSpec
3481
4002
  };
3482
- //# sourceMappingURL=chunk-LWLEKMDQ.js.map
4003
+ //# sourceMappingURL=chunk-XXQNGV4M.js.map