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