@iowarp/clio-coder 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/README.md +13 -4
- package/dist/{acp-SK4MD6MM.js → acp-7LOELQFP.js} +13 -13
- package/dist/{agents-2FN2K6ME.js → agents-FIBG2SHA.js} +41 -37
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-QIYZWM5I.js → auth-OI4LIH2I.js} +31 -24
- package/dist/builtins-AD25UL3C.js +17 -0
- package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
- package/dist/chunk-3DPEIQKN.js +113 -0
- package/dist/{chunk-EOOQZZDE.js → chunk-3DUR4WUA.js} +19 -19
- package/dist/{chunk-WHJYKASB.js → chunk-3MRC2YSQ.js} +2 -2
- package/dist/{chunk-EBEFWSGL.js → chunk-3UUY7R3Z.js} +14 -10
- package/dist/{chunk-LADCF22A.js → chunk-3V5AYSEQ.js} +113 -54
- package/dist/{chunk-BMWK7ZIZ.js → chunk-465CC7FK.js} +16 -13
- package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
- package/dist/{chunk-CEYBNUGC.js → chunk-4H6ULJ3H.js} +378 -36
- package/dist/{chunk-YTYFXUI3.js → chunk-4LJX2PUC.js} +9 -9
- package/dist/{chunk-DOOEX22V.js → chunk-56KB5IJP.js} +5 -5
- package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
- package/dist/{chunk-TSHXZTOQ.js → chunk-5HFBWUMU.js} +23 -11
- package/dist/{chunk-5UJ6ECTS.js → chunk-5PVQ4SRS.js} +80 -8
- package/dist/{chunk-ZWLZP4ZT.js → chunk-5QKCQQ3E.js} +359 -17
- package/dist/{chunk-6M7VS3J3.js → chunk-5T7RBWN2.js} +111 -5
- package/dist/chunk-774ILSRL.js +172 -0
- package/dist/chunk-7C6RYZGQ.js +391 -0
- package/dist/{chunk-GH5622CP.js → chunk-A2NJGIB3.js} +2 -2
- package/dist/{chunk-C4JBQ5SR.js → chunk-AD7Y7STJ.js} +6 -6
- package/dist/{chunk-GEYXPTRF.js → chunk-AEYBF3TB.js} +33 -12
- package/dist/{chunk-2SFS6XQE.js → chunk-AMKHQW3C.js} +3 -2
- package/dist/{chunk-D4MDIG46.js → chunk-B5CSFE7B.js} +7 -7
- package/dist/{chunk-MXI6J5JF.js → chunk-B5XRQOLB.js} +10 -10
- package/dist/{chunk-X2KV5FXT.js → chunk-BVDVID7E.js} +2 -2
- package/dist/{chunk-JNXPYBB4.js → chunk-CA42X6KT.js} +3 -3
- package/dist/{chunk-VREKEFLL.js → chunk-D73KXYPF.js} +3 -3
- package/dist/{chunk-JTSEDYVQ.js → chunk-DG4M6ZUE.js} +7 -7
- package/dist/{chunk-DQA7QLMD.js → chunk-EBOC7MT3.js} +10 -25
- package/dist/{chunk-KZ2H5X4G.js → chunk-ECUO3KDP.js} +129 -14
- package/dist/{chunk-JRIO5UD2.js → chunk-EQ63NRB7.js} +5 -5
- package/dist/{chunk-YD734TPH.js → chunk-FALJGAWU.js} +2 -2
- package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
- package/dist/{chunk-XEGB6BCN.js → chunk-GAYUJ7LE.js} +68 -14
- package/dist/{chunk-UND3GU2L.js → chunk-H7IXIC72.js} +2 -2
- package/dist/{chunk-IR4CFBFN.js → chunk-HAY4ZE2P.js} +12 -12
- package/dist/{chunk-UVDSQ6LW.js → chunk-HCBCAYZU.js} +74 -147
- package/dist/{chunk-4DWFMQDR.js → chunk-HJB5IUKP.js} +89 -145
- package/dist/{chunk-M4AKACEO.js → chunk-HKO36JWF.js} +33 -5
- package/dist/{chunk-KCMKRQX4.js → chunk-HPCTNZM2.js} +45 -82
- package/dist/{chunk-465YSENW.js → chunk-IFBNV6H6.js} +3 -3
- package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
- package/dist/chunk-JEQQR47K.js +3025 -0
- package/dist/{chunk-FO5ZOVUY.js → chunk-KV2AOLDF.js} +27 -7
- package/dist/chunk-LU7P4LHA.js +33 -0
- package/dist/{chunk-6TUKSZVF.js → chunk-LXPJXFM5.js} +11 -11
- package/dist/{chunk-VQNODYQ4.js → chunk-MIX5N5AC.js} +488 -3668
- package/dist/chunk-MLOK6ZOS.js +2888 -0
- package/dist/{chunk-ZZMN5OM4.js → chunk-MV2VUEJC.js} +2 -2
- package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
- package/dist/{chunk-OBMAI2DP.js → chunk-N3PBVRTZ.js} +12 -388
- package/dist/{chunk-WJHBC77E.js → chunk-N5XKWMDW.js} +17 -7
- package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
- package/dist/{chunk-UFQ3F4FW.js → chunk-NQ6UCCOD.js} +4 -4
- package/dist/chunk-NUGM5KR6.js +165 -0
- package/dist/{chunk-DMD2AGVS.js → chunk-NZU6YDNV.js} +20 -18
- package/dist/{chunk-WHGPSPT5.js → chunk-O6I4CIEU.js} +151 -13
- package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
- package/dist/{chunk-PD3MESLB.js → chunk-P3JGPQFL.js} +4 -4
- package/dist/{chunk-UHXRNZ2J.js → chunk-PNY46YEY.js} +23 -6
- package/dist/{chunk-THKY7CD7.js → chunk-PZ4I4JE2.js} +134 -29
- package/dist/{chunk-SROCI7ZU.js → chunk-QQ7EKM72.js} +5 -5
- package/dist/{chunk-QCTRSGHQ.js → chunk-R7LNVMCS.js} +91 -53
- package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
- package/dist/chunk-RKKLTLYB.js +45 -0
- package/dist/{chunk-OB5HIGJY.js → chunk-RKRLDWD3.js} +4 -1
- package/dist/{chunk-DJNLUABN.js → chunk-S4COXYBG.js} +588 -32
- package/dist/{chunk-3HAPLH5M.js → chunk-T3Z6VAAF.js} +172 -11
- package/dist/{chunk-FOT2FX5J.js → chunk-TD7UE2L5.js} +12 -10
- package/dist/{chunk-UUANF5CR.js → chunk-TEO2TLVN.js} +856 -967
- package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
- package/dist/{chunk-EELBMBT6.js → chunk-VKBMFOYV.js} +74 -15
- package/dist/chunk-VO2LKSTM.js +165 -0
- package/dist/{chunk-5C77SEEY.js → chunk-VPTUJU4P.js} +3 -3
- package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
- package/dist/{chunk-J7PIKKWC.js → chunk-WXCJ7VME.js} +8 -8
- package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
- package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
- package/dist/{chunk-AB4XIIVB.js → chunk-YKOFT37S.js} +6 -6
- package/dist/chunk-YSEHGPCT.js +127 -0
- package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
- package/dist/cli/index.js +32 -32
- package/dist/{clio-WBVQEBKO.js → clio-LT5V7SSZ.js} +9 -9
- package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +5 -5
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-F7OEATSO.js → components-ZFA3SAER.js} +8 -8
- package/dist/{config-TRBL3RCF.js → config-RXS5T3JT.js} +98 -65
- package/dist/{configure-OLCVPHNM.js → configure-2WYWSCSD.js} +26 -22
- package/dist/{context-MJIJ6GOX.js → context-I3BTOTCS.js} +12 -12
- package/dist/{context-XEWE3MOJ.js → context-MVOORGMF.js} +54 -47
- package/dist/{context-WFPKQSM6.js → context-PALKKQYL.js} +28 -28
- package/dist/{context-clear-KNOS2JPB.js → context-clear-N2WOYZ2K.js} +53 -46
- package/dist/{context-index-SSR5ECNE.js → context-index-HNG3MOME.js} +6 -6
- package/dist/{context-working-set-EUXAZI6N.js → context-working-set-MIEVECVZ.js} +17 -18
- package/dist/{dispatch-runner-B7MTOVKL.js → dispatch-runner-VVA4SRRH.js} +90 -61
- package/dist/{docs-FLJTIDSE.js → docs-7LQ23DLM.js} +8 -8
- package/dist/doctor-TWBWFK5V.js +165 -0
- package/dist/eval-IJ5VEZDJ.js +4483 -0
- package/dist/{evidence-JZNBUOQZ.js → evidence-L5APPXNV.js} +68 -61
- package/dist/{evolve-FJVC4KKI.js → evolve-RGNKFJ52.js} +47 -40
- package/dist/{extensions-IQL36S7K.js → extensions-7WYWUX5A.js} +13 -7
- package/dist/{fleet-BDKYJFCP.js → fleet-6CNVBZZP.js} +113 -76
- package/dist/{fleet-commands-ZFIWZSB3.js → fleet-commands-L2SXSYEI.js} +10 -10
- package/dist/{fleet-graph-Y6HPXIVF.js → fleet-graph-2J3OOIPO.js} +17 -15
- package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-CZRJ4JP5.js} +5 -6
- package/dist/{fleet-validate-BIYREGIK.js → fleet-validate-C5RI6DP7.js} +20 -19
- package/dist/{init-LQUB5COQ.js → init-VBN2ACVA.js} +70 -63
- package/dist/{library-NJAHIGG4.js → library-JHGUMLY2.js} +22 -20
- package/dist/{memory-OG6HOYKM.js → memory-K4OQIYWG.js} +49 -42
- package/dist/{models-5ZG5XY7J.js → models-2NCZUWDD.js} +35 -29
- package/dist/{monitor-TJ7AMTGB.js → monitor-MMVTJABD.js} +64 -45
- package/dist/{orchestrator-WZYB54DM.js → orchestrator-ZKBPCHW6.js} +1971 -520
- package/dist/{paths-XUC7GS6E.js → paths-DBXMZMDU.js} +5 -5
- package/dist/registry-LG64LTF4.js +11 -0
- package/dist/{reset-PXQT45IY.js → reset-DD5JGOY3.js} +11 -11
- package/dist/{run-FQ74YF62.js → run-QEGNX7FL.js} +89 -83
- package/dist/{share-FW7SVCL3.js → share-JKD3BQMW.js} +20 -18
- package/dist/{skills-7E7IRB3R.js → skills-LMQIKDOZ.js} +23 -21
- package/dist/{skills-eval-LI75W6OK.js → skills-eval-I7X2774U.js} +59 -52
- package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
- package/dist/support-I7LOJLIF.js +38 -0
- package/dist/{targets-4CIFKCTW.js → targets-RUSR6B5Z.js} +77 -42
- package/dist/{terminal-lease-WUZY7ZV5.js → terminal-lease-QYVORFR4.js} +6 -4
- package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
- package/dist/{uninstall-7FV7IP4E.js → uninstall-ZJF5H5ZN.js} +8 -8
- package/dist/{upgrade-K2HVIVMQ.js → upgrade-XANW3FXB.js} +29 -26
- package/dist/{usage-GTZELZQX.js → usage-4H7ZRXQT.js} +110 -61
- package/dist/{verifiers-RLAHT27O.js → verifiers-UZXNBZEB.js} +13 -13
- package/dist/{verify-BX3BRKH5.js → verify-BVKWTNDL.js} +9 -9
- package/dist/{wiki-generate-ASIFASCN.js → wiki-generate-MY7WV2QI.js} +76 -69
- package/dist/worker/entry.js +69 -66
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +11 -5
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +23 -2
- package/docs/commands-and-modes.md +2 -2
- package/docs/configuration-and-targets.md +37 -5
- package/docs/context-engine.md +63 -4
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +2 -0
- package/docs/eval-runner.md +262 -11
- package/docs/evals-internal.md +72 -2
- package/docs/evidence-and-memory.md +77 -12
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +3 -1
- package/docs/fleet-dispatch.md +34 -9
- package/docs/glossary.md +21 -1
- package/docs/installation-and-lifecycle.md +1 -1
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +54 -3
- package/docs/proactive-memory.md +127 -14
- package/docs/prompt-envelope-and-tools.md +22 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +60 -41
- package/docs/safety-model.md +1 -1
- package/docs/scientific-validation.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +1 -1
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +87 -0
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +2 -2
- package/src/cli/agents.ts +1 -1
- package/src/cli/argv.ts +5 -0
- package/src/cli/config-inspect.ts +33 -6
- package/src/cli/config.ts +1 -1
- package/src/cli/configure.ts +107 -23
- package/src/cli/doctor-state-size.ts +82 -0
- package/src/cli/doctor.ts +7 -1
- package/src/cli/eval.ts +80 -16
- package/src/cli/evidence.ts +30 -25
- package/src/cli/extensions.ts +5 -1
- package/src/cli/fleet-preflight.ts +2 -12
- package/src/cli/fleet.ts +32 -3
- package/src/cli/shared.ts +1 -0
- package/src/cli/targets.ts +45 -11
- package/src/cli/trace.ts +63 -4
- package/src/cli/usage.ts +63 -14
- package/src/cli/validate-model.ts +60 -5
- package/src/core/bus-events.ts +54 -1
- package/src/core/cache-telemetry.ts +42 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/config.ts +18 -0
- package/src/core/defaults.ts +36 -6
- package/src/core/endpoint-key.ts +27 -0
- package/src/core/path-boundary.ts +100 -0
- package/src/core/residency-target-key.ts +25 -0
- package/src/core/response-schema.ts +36 -2
- package/src/domains/agents/extension.ts +2 -11
- package/src/domains/agents/fleet-contract.ts +30 -12
- package/src/domains/agents/recipe.ts +7 -1
- package/src/domains/agents/registry.ts +73 -5
- package/src/domains/agents/result-contract.ts +128 -17
- package/src/domains/agents/write-boundary.ts +15 -50
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/context/codewiki/coordinator.ts +12 -4
- package/src/domains/context/project-rules.ts +51 -1
- package/src/domains/dispatch/admission.ts +40 -3
- package/src/domains/dispatch/assignment-reconcile.ts +22 -5
- package/src/domains/dispatch/assignment-store.ts +151 -14
- package/src/domains/dispatch/capacity-lease.ts +98 -9
- package/src/domains/dispatch/contract.ts +26 -1
- package/src/domains/dispatch/delegation-plan.ts +2 -5
- package/src/domains/dispatch/execution-plan.ts +44 -4
- package/src/domains/dispatch/execution-role.ts +9 -1
- package/src/domains/dispatch/extension.ts +309 -96
- package/src/domains/dispatch/fleet-run.ts +78 -4
- package/src/domains/dispatch/gate-role-prompts.ts +38 -0
- package/src/domains/dispatch/heartbeat.ts +32 -8
- package/src/domains/dispatch/index.ts +6 -1
- package/src/domains/dispatch/intent-requirements.ts +40 -0
- package/src/domains/dispatch/intent.ts +84 -8
- package/src/domains/dispatch/orphan-recovery.ts +5 -0
- package/src/domains/dispatch/path-scope.ts +370 -0
- package/src/domains/dispatch/receipt-integrity.ts +2 -1
- package/src/domains/dispatch/reservation-store.ts +116 -8
- package/src/domains/dispatch/state.ts +4 -0
- package/src/domains/dispatch/types.ts +14 -7
- package/src/domains/dispatch/validation.ts +6 -3
- package/src/domains/dispatch/worker-spawn.ts +25 -11
- package/src/domains/dispatch/write-boundary-enforcer.ts +62 -0
- package/src/domains/dispatch/write-boundary.ts +262 -22
- package/src/domains/eval/artifacts/store.ts +62 -0
- package/src/domains/eval/compare/behavioral.ts +224 -0
- package/src/domains/eval/compare/compare.ts +355 -2
- package/src/domains/eval/compare/envelope.ts +128 -0
- package/src/domains/eval/compare/gates.ts +24 -6
- package/src/domains/eval/compare/thresholds.ts +30 -3
- package/src/domains/eval/execution-provenance.ts +240 -0
- package/src/domains/eval/metrics/aggregate.ts +136 -0
- package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
- package/src/domains/eval/metrics/evidence.ts +79 -2
- package/src/domains/eval/metrics/tracked.ts +413 -0
- package/src/domains/eval/provenance.ts +117 -0
- package/src/domains/eval/reports/comparison.ts +128 -0
- package/src/domains/eval/reports/junit.ts +17 -3
- package/src/domains/eval/reports/markdown.ts +3 -3
- package/src/domains/eval/reports/text.ts +14 -0
- package/src/domains/eval/run-compare.ts +20 -0
- package/src/domains/eval/runners/clio-run.ts +139 -2
- package/src/domains/eval/runners/external-command.ts +28 -3
- package/src/domains/eval/schema/adapter.ts +111 -0
- package/src/domains/eval/schema/artifact.ts +20 -0
- package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
- package/src/domains/eval/schema/behavioral.ts +520 -0
- package/src/domains/eval/schema/execution-envelope.ts +194 -0
- package/src/domains/eval/schema/serving.ts +74 -0
- package/src/domains/eval/schema/suite.ts +38 -8
- package/src/domains/eval/schema/validate.ts +58 -3
- package/src/domains/eval/schema/verdict.ts +237 -0
- package/src/domains/eval/suites/resolve.ts +2 -0
- package/src/domains/eval/suites/run.ts +264 -33
- package/src/domains/eval/verifiers/command.ts +2 -1
- package/src/domains/eval/workspaces/temp-copy.ts +145 -13
- package/src/domains/evidence/build.ts +68 -21
- package/src/domains/evidence/eval.ts +2 -12
- package/src/domains/evidence/findings-markdown.ts +33 -0
- package/src/domains/evidence/index.ts +21 -0
- package/src/domains/evidence/provenance.ts +46 -11
- package/src/domains/evidence/run-trust.ts +7 -113
- package/src/domains/evidence/trust-projection.ts +274 -0
- package/src/domains/evidence/trust-status.ts +145 -17
- package/src/domains/evidence/types.ts +4 -0
- package/src/domains/extensions/compatibility.ts +285 -0
- package/src/domains/extensions/discovery.ts +126 -4
- package/src/domains/extensions/resources.ts +21 -9
- package/src/domains/extensions/state.ts +18 -5
- package/src/domains/extensions/types.ts +6 -1
- package/src/domains/lifecycle/doctor.ts +209 -2
- package/src/domains/memory/index.ts +14 -0
- package/src/domains/memory/task-bank-promotion.ts +64 -0
- package/src/domains/memory/task-memory-policy.ts +77 -8
- package/src/domains/memory/task-memory-spend.ts +131 -0
- package/src/domains/memory/task-memory-status.ts +7 -0
- package/src/domains/memory/task-memory-telemetry.ts +2 -0
- package/src/domains/middleware/index.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +69 -5
- package/src/domains/middleware/memory-step-endpoint.ts +71 -0
- package/src/domains/observability/background-memory-usage.ts +140 -0
- package/src/domains/observability/cost.ts +1 -1
- package/src/domains/observability/index.ts +7 -0
- package/src/domains/observability/out-of-turn-usage.ts +51 -2
- package/src/domains/observability/trace-store.ts +192 -2
- package/src/domains/prompts/compiler.ts +100 -13
- package/src/domains/prompts/contract.ts +3 -5
- package/src/domains/providers/endpoint-capacity.ts +96 -0
- package/src/domains/providers/extension.ts +30 -2
- package/src/domains/providers/index.ts +10 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
- package/src/domains/providers/runtime-resolution.ts +8 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
- package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
- package/src/domains/providers/types/capability-flags.ts +2 -0
- package/src/domains/providers/types/target-descriptor.ts +2 -0
- package/src/domains/resources/common-loader.ts +3 -0
- package/src/domains/resources/prompts/loader.ts +184 -17
- package/src/domains/safety/call-target.ts +52 -0
- package/src/domains/safety/policy-engine.ts +5 -5
- package/src/domains/safety/run-effects.ts +96 -2
- package/src/domains/safety/scope.ts +7 -12
- package/src/domains/session/context-accounting.ts +52 -1
- package/src/domains/session/context-ledger.ts +37 -13
- package/src/domains/session/index.ts +6 -0
- package/src/domains/session/prompt-cache.ts +140 -0
- package/src/domains/session/prompt-manifest.ts +42 -0
- package/src/engine/acp/adapter.ts +18 -3
- package/src/engine/acp/server.ts +4 -1
- package/src/engine/ai.ts +35 -0
- package/src/engine/apis/llamacpp-residency.ts +55 -3
- package/src/engine/apis/lmstudio.ts +25 -5
- package/src/engine/apis/ollama-native.ts +2 -1
- package/src/engine/apis/openai-completions.ts +80 -17
- package/src/engine/apis/residency-lock.ts +3 -1
- package/src/engine/apis/residency.ts +34 -1
- package/src/engine/prompt-templates.ts +18 -1
- package/src/engine/provider-payload.ts +29 -1
- package/src/engine/worker-runtime.ts +6 -3
- package/src/entry/orchestrator.ts +176 -30
- package/src/interactive/chat-loop-messages.ts +26 -7
- package/src/interactive/chat-loop.ts +318 -41
- package/src/interactive/chat-panel.ts +62 -8
- package/src/interactive/clio-editor.ts +45 -8
- package/src/interactive/context-activity.ts +5 -1
- package/src/interactive/context-meter.ts +1 -1
- package/src/interactive/context-overlay.ts +40 -10
- package/src/interactive/cost-overlay.ts +64 -6
- package/src/interactive/dispatch-board.ts +127 -5
- package/src/interactive/fleet-run-preview.ts +41 -15
- package/src/interactive/handoff-round.ts +41 -2
- package/src/interactive/interactive-application.ts +24 -1
- package/src/interactive/interactive-event-projection.ts +14 -0
- package/src/interactive/interactive-input-runtime.ts +8 -0
- package/src/interactive/interactive-presentation.ts +4 -0
- package/src/interactive/interactive-shell.ts +20 -17
- package/src/interactive/interactive-slash-runtime.ts +27 -4
- package/src/interactive/memory-overlay.ts +8 -0
- package/src/interactive/mutation-preview.ts +295 -0
- package/src/interactive/overlay-general-openers.ts +16 -0
- package/src/interactive/overlay-key-routing.ts +38 -0
- package/src/interactive/overlay-lifecycle.ts +38 -5
- package/src/interactive/overlay-permission-lifecycle.ts +22 -2
- package/src/interactive/overlay-session-lifecycle.ts +73 -9
- package/src/interactive/overlays/ask-user.ts +91 -19
- package/src/interactive/overlays/help-reference.ts +4 -0
- package/src/interactive/overlays/prompts.ts +11 -1
- package/src/interactive/overlays/settings.ts +176 -48
- package/src/interactive/permission-hint.ts +34 -2
- package/src/interactive/permission-overlay.ts +159 -9
- package/src/interactive/prewarm.ts +197 -0
- package/src/interactive/render-trace.ts +162 -15
- package/src/interactive/renderers/tool-execution.ts +4 -0
- package/src/interactive/side-question.ts +58 -1
- package/src/interactive/slash-commands.ts +7 -2
- package/src/interactive/status/controller.ts +11 -0
- package/src/interactive/status/state-machine.ts +54 -2
- package/src/interactive/status/types.ts +7 -0
- package/src/interactive/terminal-lease.ts +2 -0
- package/src/interactive/turn-context.ts +299 -31
- package/src/interactive/turn-persistence.ts +14 -4
- package/src/interactive/turn-prewarm.ts +364 -0
- package/src/interactive/turn-queues.ts +7 -4
- package/src/interactive/turn-runtime.ts +8 -1
- package/src/interactive/turn-state.ts +23 -0
- package/src/interactive/view/artifacts.ts +42 -9
- package/src/interactive/view/view-overlay.ts +43 -6
- package/src/interactive/worker-receipts.ts +14 -2
- package/src/interactive/worker-stream.ts +8 -0
- package/src/tools/ask-user.ts +43 -2
- package/src/tools/dispatch-admission.ts +12 -13
- package/src/tools/dispatch-arguments.ts +27 -0
- package/src/tools/dispatch-plan.ts +46 -9
- package/src/tools/dispatch-runner.ts +48 -13
- package/src/tools/dispatch-scout.ts +1 -1
- package/src/tools/monitor.ts +13 -0
- package/src/tools/registry.ts +16 -0
- package/src/tools/worker-evidence.ts +19 -13
- package/src/worker/spec-contract.ts +2 -1
- package/dist/chunk-AOCYTWAV.js +0 -449
- package/dist/chunk-HWUFFB6L.js +0 -83
- package/dist/chunk-R346GLFC.js +0 -31
- package/dist/chunk-ZGH7FGS5.js +0 -1079
- package/dist/doctor-RN4YKO2X.js +0 -87
- package/dist/eval-RUBJVSNQ.js +0 -2557
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asDirectoryPathBoundary,
|
|
3
|
+
normalizePathBoundaryEntry,
|
|
4
|
+
PathBoundaryError,
|
|
5
|
+
pathBoundaryCovers,
|
|
6
|
+
resolvePathBoundary,
|
|
7
|
+
} from "../../core/path-boundary.js";
|
|
8
|
+
import type { DispatchRequest } from "./contract.js";
|
|
9
|
+
|
|
10
|
+
/** Path-like tokens retained for requests that do not carry typed intent. */
|
|
11
|
+
const DISPATCH_PATH_TOKEN_RE = /(?:[\w.-]+\/)+[\w.-]+|\b[\w-]+\.[A-Za-z0-9]{1,8}\b/g;
|
|
12
|
+
const HTTP_URL_RE = /\bhttps?:\/\/\S+/gu;
|
|
13
|
+
const POSIX_ABSOLUTE_TOKEN_RE = /(?:^|[\s("'])((?:\/[\w.~+-]+)+)/gu;
|
|
14
|
+
const WINDOWS_ABSOLUTE_TOKEN_RE = /(?:^|[\s("'])([A-Za-z]:[\\/][^\s"']+)/gu;
|
|
15
|
+
const BACKSLASH_PATH_RE = /\b[A-Za-z_.-][\w.-]*\\(?:(?:[\w.-]+\\)+[\w.-]+|[\w-]+\.[A-Za-z0-9]{1,8})\b/gu;
|
|
16
|
+
const DOUBLE_SEPARATOR_PATH_RE = /\b[\w.-]+\/\/[\w./-]+/gu;
|
|
17
|
+
|
|
18
|
+
export type DispatchPathProvenanceKind = "declared" | "derived" | "inferred";
|
|
19
|
+
export type DispatchPathConfidence = "certain" | "high" | "medium" | "low";
|
|
20
|
+
export type DispatchPathProvenanceSource =
|
|
21
|
+
| "intent.read_roots"
|
|
22
|
+
| "intent.write_roots"
|
|
23
|
+
| "intent.relevant_paths"
|
|
24
|
+
| "writeRoots"
|
|
25
|
+
| "task"
|
|
26
|
+
| "briefing";
|
|
27
|
+
export type DispatchPathProvenanceReason =
|
|
28
|
+
| "explicit_intent"
|
|
29
|
+
| "legacy_write_roots"
|
|
30
|
+
| "task_path_token"
|
|
31
|
+
| "briefing_path_token";
|
|
32
|
+
|
|
33
|
+
export interface DispatchPathProvenanceEvidence {
|
|
34
|
+
provenance: DispatchPathProvenanceKind;
|
|
35
|
+
source: DispatchPathProvenanceSource;
|
|
36
|
+
confidence: DispatchPathConfidence;
|
|
37
|
+
reason: DispatchPathProvenanceReason;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DispatchPathProvenanceEntry {
|
|
41
|
+
path: string;
|
|
42
|
+
evidence: DispatchPathProvenanceEvidence[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface DispatchPathScopeProvenance {
|
|
46
|
+
version: 1;
|
|
47
|
+
mode: "declared" | "legacy-inferred";
|
|
48
|
+
workingContextPaths: DispatchPathProvenanceEntry[];
|
|
49
|
+
writeBoundaries: DispatchPathProvenanceEntry[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface DispatchPathScope {
|
|
53
|
+
source: "declared" | "inferred";
|
|
54
|
+
workingContextPaths: ReadonlyArray<string>;
|
|
55
|
+
writeBoundaries: ReadonlyArray<string>;
|
|
56
|
+
inferredOnlyPaths: ReadonlyArray<string>;
|
|
57
|
+
provenance: DispatchPathScopeProvenance;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface DeclaredScopeReplacementNotice {
|
|
61
|
+
code: "typed_scope_replaced_inferred_paths";
|
|
62
|
+
level: "warning";
|
|
63
|
+
omittedPaths: ReadonlyArray<string>;
|
|
64
|
+
message: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface LegacyScopeInferenceNotice {
|
|
68
|
+
code: "legacy_scope_inferred" | "legacy_scope_empty";
|
|
69
|
+
level: "warning";
|
|
70
|
+
paths: ReadonlyArray<{
|
|
71
|
+
path: string;
|
|
72
|
+
policy: "working-context" | "write-boundary";
|
|
73
|
+
provenance: DispatchPathProvenanceKind;
|
|
74
|
+
source: DispatchPathProvenanceSource;
|
|
75
|
+
confidence: DispatchPathConfidence;
|
|
76
|
+
}>;
|
|
77
|
+
message: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type DispatchPathScopeInferenceErrorCode = "legacy_scope_path_absolute" | "legacy_scope_path_malformed";
|
|
81
|
+
|
|
82
|
+
export class DispatchPathScopeInferenceError extends Error {
|
|
83
|
+
readonly code: DispatchPathScopeInferenceErrorCode;
|
|
84
|
+
readonly source: "task" | "briefing";
|
|
85
|
+
readonly token: string;
|
|
86
|
+
|
|
87
|
+
constructor(code: DispatchPathScopeInferenceErrorCode, source: "task" | "briefing", token: string, detail: string) {
|
|
88
|
+
super(`${code}: ${source} contains ${detail} '${token}'; declare intent explicitly`);
|
|
89
|
+
this.name = "DispatchPathScopeInferenceError";
|
|
90
|
+
this.code = code;
|
|
91
|
+
this.source = source;
|
|
92
|
+
this.token = token;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const DECLARED_EVIDENCE = {
|
|
97
|
+
readRoots: {
|
|
98
|
+
provenance: "declared",
|
|
99
|
+
source: "intent.read_roots",
|
|
100
|
+
confidence: "certain",
|
|
101
|
+
reason: "explicit_intent",
|
|
102
|
+
},
|
|
103
|
+
writeRoots: {
|
|
104
|
+
provenance: "declared",
|
|
105
|
+
source: "intent.write_roots",
|
|
106
|
+
confidence: "certain",
|
|
107
|
+
reason: "explicit_intent",
|
|
108
|
+
},
|
|
109
|
+
relevantPaths: {
|
|
110
|
+
provenance: "declared",
|
|
111
|
+
source: "intent.relevant_paths",
|
|
112
|
+
confidence: "certain",
|
|
113
|
+
reason: "explicit_intent",
|
|
114
|
+
},
|
|
115
|
+
} as const satisfies Record<"readRoots" | "writeRoots" | "relevantPaths", DispatchPathProvenanceEvidence>;
|
|
116
|
+
|
|
117
|
+
function cloneEvidence(evidence: DispatchPathProvenanceEvidence): DispatchPathProvenanceEvidence {
|
|
118
|
+
return { ...evidence };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function addProvenance(
|
|
122
|
+
entries: Map<string, DispatchPathProvenanceEntry>,
|
|
123
|
+
path: string,
|
|
124
|
+
evidence: DispatchPathProvenanceEvidence,
|
|
125
|
+
): void {
|
|
126
|
+
const current = entries.get(path);
|
|
127
|
+
if (current === undefined) {
|
|
128
|
+
entries.set(path, { path, evidence: [cloneEvidence(evidence)] });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (!current.evidence.some((entry) => entry.source === evidence.source)) {
|
|
132
|
+
current.evidence.push(cloneEvidence(evidence));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function sortedEntries(entries: Map<string, DispatchPathProvenanceEntry>): DispatchPathProvenanceEntry[] {
|
|
137
|
+
return [...entries.values()]
|
|
138
|
+
.sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0))
|
|
139
|
+
.map((entry) => ({ path: entry.path, evidence: entry.evidence.map(cloneEvidence) }));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Build the required field-level provenance for normalized declared intent. */
|
|
143
|
+
export function declaredIntentPathProvenance(input: {
|
|
144
|
+
readRoots: ReadonlyArray<string>;
|
|
145
|
+
writeRoots: ReadonlyArray<string>;
|
|
146
|
+
relevantPaths: ReadonlyArray<string>;
|
|
147
|
+
}): DispatchPathProvenanceEntry[] {
|
|
148
|
+
const entries = new Map<string, DispatchPathProvenanceEntry>();
|
|
149
|
+
for (const path of input.readRoots) addProvenance(entries, path, DECLARED_EVIDENCE.readRoots);
|
|
150
|
+
for (const path of input.writeRoots) addProvenance(entries, path, DECLARED_EVIDENCE.writeRoots);
|
|
151
|
+
for (const path of input.relevantPaths) addProvenance(entries, path, DECLARED_EVIDENCE.relevantPaths);
|
|
152
|
+
return sortedEntries(entries);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function inferredPaths(req: Pick<DispatchRequest, "task" | "briefing">): string[] {
|
|
156
|
+
const paths = new Set<string>();
|
|
157
|
+
const text = `${req.task}\n${req.briefing ?? ""}`;
|
|
158
|
+
for (const match of text.matchAll(DISPATCH_PATH_TOKEN_RE)) {
|
|
159
|
+
const token = inferredPathToken(match[0]);
|
|
160
|
+
if (token.startsWith("http://") || token.startsWith("https://")) continue;
|
|
161
|
+
paths.add(token);
|
|
162
|
+
}
|
|
163
|
+
return [...paths];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function inferredPathToken(token: string): string {
|
|
167
|
+
return token.replace(/\.+$/u, "");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function failMalformedText(text: string, source: "task" | "briefing"): void {
|
|
171
|
+
const withoutUrls = text.replace(HTTP_URL_RE, (url) => " ".repeat(url.length));
|
|
172
|
+
const absolute = POSIX_ABSOLUTE_TOKEN_RE.exec(withoutUrls)?.[1];
|
|
173
|
+
POSIX_ABSOLUTE_TOKEN_RE.lastIndex = 0;
|
|
174
|
+
if (absolute !== undefined) {
|
|
175
|
+
throw new DispatchPathScopeInferenceError("legacy_scope_path_absolute", source, absolute, "an absolute path token");
|
|
176
|
+
}
|
|
177
|
+
const windowsAbsolute = WINDOWS_ABSOLUTE_TOKEN_RE.exec(withoutUrls)?.[1];
|
|
178
|
+
WINDOWS_ABSOLUTE_TOKEN_RE.lastIndex = 0;
|
|
179
|
+
if (windowsAbsolute !== undefined) {
|
|
180
|
+
throw new DispatchPathScopeInferenceError(
|
|
181
|
+
"legacy_scope_path_absolute",
|
|
182
|
+
source,
|
|
183
|
+
windowsAbsolute,
|
|
184
|
+
"an absolute path token",
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
for (const pattern of [BACKSLASH_PATH_RE, DOUBLE_SEPARATOR_PATH_RE]) {
|
|
188
|
+
const malformed = pattern.exec(withoutUrls)?.[0];
|
|
189
|
+
pattern.lastIndex = 0;
|
|
190
|
+
if (malformed !== undefined) {
|
|
191
|
+
throw new DispatchPathScopeInferenceError(
|
|
192
|
+
"legacy_scope_path_malformed",
|
|
193
|
+
source,
|
|
194
|
+
malformed,
|
|
195
|
+
"a malformed path token",
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function strictInferredPaths(
|
|
202
|
+
text: string,
|
|
203
|
+
source: "task" | "briefing",
|
|
204
|
+
): Array<{ path: string; evidence: DispatchPathProvenanceEvidence }> {
|
|
205
|
+
failMalformedText(text, source);
|
|
206
|
+
const withoutUrls = text.replace(HTTP_URL_RE, (url) => " ".repeat(url.length));
|
|
207
|
+
const evidence: DispatchPathProvenanceEvidence =
|
|
208
|
+
source === "task"
|
|
209
|
+
? { provenance: "inferred", source, confidence: "medium", reason: "task_path_token" }
|
|
210
|
+
: { provenance: "inferred", source, confidence: "low", reason: "briefing_path_token" };
|
|
211
|
+
const result: Array<{ path: string; evidence: DispatchPathProvenanceEvidence }> = [];
|
|
212
|
+
for (const match of withoutUrls.matchAll(DISPATCH_PATH_TOKEN_RE)) {
|
|
213
|
+
const token = inferredPathToken(match[0]);
|
|
214
|
+
try {
|
|
215
|
+
result.push({ path: normalizePathBoundaryEntry(token), evidence });
|
|
216
|
+
} catch (error) {
|
|
217
|
+
const code =
|
|
218
|
+
error instanceof PathBoundaryError && error.code === "absolute"
|
|
219
|
+
? "legacy_scope_path_absolute"
|
|
220
|
+
: "legacy_scope_path_malformed";
|
|
221
|
+
throw new DispatchPathScopeInferenceError(
|
|
222
|
+
code,
|
|
223
|
+
source,
|
|
224
|
+
token,
|
|
225
|
+
code === "legacy_scope_path_absolute" ? "an absolute path token" : "a malformed path token",
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function legacyPathScope(req: DispatchRequest, cwd: string): DispatchPathScope {
|
|
233
|
+
const workingEntries = new Map<string, DispatchPathProvenanceEntry>();
|
|
234
|
+
const writeEntries = new Map<string, DispatchPathProvenanceEntry>();
|
|
235
|
+
const writeEvidence: DispatchPathProvenanceEvidence = {
|
|
236
|
+
provenance: "derived",
|
|
237
|
+
source: "writeRoots",
|
|
238
|
+
confidence: "high",
|
|
239
|
+
reason: "legacy_write_roots",
|
|
240
|
+
};
|
|
241
|
+
for (const root of req.writeRoots ?? []) {
|
|
242
|
+
addProvenance(workingEntries, root, writeEvidence);
|
|
243
|
+
addProvenance(writeEntries, asDirectoryPathBoundary(resolvePathBoundary(cwd, root)), writeEvidence);
|
|
244
|
+
}
|
|
245
|
+
for (const candidate of strictInferredPaths(req.task, "task")) {
|
|
246
|
+
addProvenance(workingEntries, candidate.path, candidate.evidence);
|
|
247
|
+
}
|
|
248
|
+
if (req.briefing !== undefined) {
|
|
249
|
+
for (const candidate of strictInferredPaths(req.briefing, "briefing")) {
|
|
250
|
+
addProvenance(workingEntries, candidate.path, candidate.evidence);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const workingContext = [...workingEntries.values()].map((entry) => ({
|
|
254
|
+
path: entry.path,
|
|
255
|
+
evidence: entry.evidence.map(cloneEvidence),
|
|
256
|
+
}));
|
|
257
|
+
const writeBoundaries = [...writeEntries.values()].map((entry) => ({
|
|
258
|
+
path: entry.path,
|
|
259
|
+
evidence: entry.evidence.map(cloneEvidence),
|
|
260
|
+
}));
|
|
261
|
+
return {
|
|
262
|
+
source: "inferred",
|
|
263
|
+
workingContextPaths: workingContext.map((entry) => entry.path),
|
|
264
|
+
writeBoundaries: writeBoundaries.map((entry) => entry.path),
|
|
265
|
+
inferredOnlyPaths: [],
|
|
266
|
+
provenance: {
|
|
267
|
+
version: 1,
|
|
268
|
+
mode: "legacy-inferred",
|
|
269
|
+
workingContextPaths: workingContext,
|
|
270
|
+
writeBoundaries,
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Resolve every policy-bearing path projection for one dispatch request. */
|
|
276
|
+
export function resolveDispatchPathScope(req: DispatchRequest): DispatchPathScope {
|
|
277
|
+
const cwd = req.cwd ?? process.cwd();
|
|
278
|
+
if (req.intent === undefined) return legacyPathScope(req, cwd);
|
|
279
|
+
|
|
280
|
+
const declaredProvenance = declaredIntentPathProvenance(req.intent);
|
|
281
|
+
const declaredPaths = declaredProvenance.map((entry) => entry.path);
|
|
282
|
+
const inferredOnlyPaths = inferredPaths(req).filter((candidate) => !pathBoundaryCovers(declaredPaths, candidate));
|
|
283
|
+
const writeEvidence = DECLARED_EVIDENCE.writeRoots;
|
|
284
|
+
const writeBoundaries = req.intent.writeRoots.map((root) => ({
|
|
285
|
+
path: resolvePathBoundary(cwd, root),
|
|
286
|
+
evidence: [cloneEvidence(writeEvidence)],
|
|
287
|
+
}));
|
|
288
|
+
return {
|
|
289
|
+
source: "declared",
|
|
290
|
+
workingContextPaths: declaredPaths,
|
|
291
|
+
writeBoundaries: writeBoundaries.map((entry) => entry.path),
|
|
292
|
+
inferredOnlyPaths,
|
|
293
|
+
provenance: {
|
|
294
|
+
version: 1,
|
|
295
|
+
mode: "declared",
|
|
296
|
+
workingContextPaths: declaredProvenance,
|
|
297
|
+
writeBoundaries,
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** Render the named diagnostic for the declared-scope replacement tradeoff. */
|
|
303
|
+
export function declaredScopeReplacementDiagnostic(scope: DispatchPathScope): string | null {
|
|
304
|
+
if (scope.source !== "declared" || scope.inferredOnlyPaths.length === 0) return null;
|
|
305
|
+
return `typed_scope_replaced_inferred_paths: typed intent omitted prose-inferred paths ${scope.inferredOnlyPaths.join(
|
|
306
|
+
", ",
|
|
307
|
+
)}; those paths did not select project rules or expand worker authority`;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** Render the interactive warning for the declared-scope replacement tradeoff. */
|
|
311
|
+
export function declaredScopeReplacementNotice(scope: DispatchPathScope): DeclaredScopeReplacementNotice | null {
|
|
312
|
+
if (scope.source !== "declared" || scope.inferredOnlyPaths.length === 0) return null;
|
|
313
|
+
const omittedPaths = [...scope.inferredOnlyPaths];
|
|
314
|
+
return {
|
|
315
|
+
code: "typed_scope_replaced_inferred_paths",
|
|
316
|
+
level: "warning",
|
|
317
|
+
omittedPaths,
|
|
318
|
+
message: `[dispatch scope] typed intent replaced prose path inference; omitted paths: ${omittedPaths.join(
|
|
319
|
+
", ",
|
|
320
|
+
)}. Those paths did not select project rules or expand worker authority.`,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function legacyNoticePaths(scope: DispatchPathScope): LegacyScopeInferenceNotice["paths"] {
|
|
325
|
+
const paths: Array<LegacyScopeInferenceNotice["paths"][number]> = [];
|
|
326
|
+
for (const [policy, entries] of [
|
|
327
|
+
["working-context", scope.provenance.workingContextPaths],
|
|
328
|
+
["write-boundary", scope.provenance.writeBoundaries],
|
|
329
|
+
] as const) {
|
|
330
|
+
for (const entry of entries) {
|
|
331
|
+
for (const evidence of entry.evidence) {
|
|
332
|
+
paths.push({
|
|
333
|
+
path: entry.path,
|
|
334
|
+
policy,
|
|
335
|
+
provenance: evidence.provenance,
|
|
336
|
+
source: evidence.source,
|
|
337
|
+
confidence: evidence.confidence,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return paths;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** Render the operator-visible warning for a legacy scope resolution. */
|
|
346
|
+
export function legacyScopeInferenceNotice(scope: DispatchPathScope): LegacyScopeInferenceNotice | null {
|
|
347
|
+
if (scope.source !== "inferred") return null;
|
|
348
|
+
const paths = legacyNoticePaths(scope);
|
|
349
|
+
if (paths.length === 0) {
|
|
350
|
+
return {
|
|
351
|
+
code: "legacy_scope_empty",
|
|
352
|
+
level: "warning",
|
|
353
|
+
paths,
|
|
354
|
+
message:
|
|
355
|
+
"[dispatch scope] legacy dispatch has no declared intent and inferred no policy-bearing paths. Path-scoped rules received no inferred match input, and worker authority did not expand.",
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
const rendered = paths
|
|
359
|
+
.map(
|
|
360
|
+
(entry) =>
|
|
361
|
+
`${entry.policy} ${entry.path} (provenance=${entry.provenance} source=${entry.source} confidence=${entry.confidence})`,
|
|
362
|
+
)
|
|
363
|
+
.join("; ");
|
|
364
|
+
return {
|
|
365
|
+
code: "legacy_scope_inferred",
|
|
366
|
+
level: "warning",
|
|
367
|
+
paths,
|
|
368
|
+
message: `[dispatch scope] legacy dispatch resolved policy-bearing scope without declared intent: ${rendered}. Review this scope before execution.`,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
@@ -10,7 +10,7 @@ import type { RunEnvelope, RunReceipt, RunReceiptDraft, RunReceiptIntegrity, Run
|
|
|
10
10
|
* base, so there are no historical receipts to keep verifying: a receipt is
|
|
11
11
|
* either this version or it is not a receipt.
|
|
12
12
|
*/
|
|
13
|
-
export const RUN_RECEIPT_INTEGRITY_VERSION: RunReceiptIntegrity["version"] =
|
|
13
|
+
export const RUN_RECEIPT_INTEGRITY_VERSION: RunReceiptIntegrity["version"] = 20;
|
|
14
14
|
export type ReceiptIntegrityVersion = RunReceiptIntegrity["version"];
|
|
15
15
|
export type ReceiptIntegrityField = keyof RunReceiptDraft;
|
|
16
16
|
export const RUN_RECEIPT_INTEGRITY_ALGORITHM = "sha256";
|
|
@@ -77,6 +77,7 @@ export const RECEIPT_INTEGRITY_FIELD_COVERAGE = {
|
|
|
77
77
|
requestOrigin: true,
|
|
78
78
|
task: true,
|
|
79
79
|
intent: true,
|
|
80
|
+
pathScope: true,
|
|
80
81
|
// Historical receipts omit this field, so their receipt and ledger payloads remain unchanged.
|
|
81
82
|
budget: true,
|
|
82
83
|
targetId: true,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { withStateFileLockSync } from "../../core/state-file-lock.js";
|
|
3
|
+
import { endpointLabel } from "../providers/endpoint-capacity.js";
|
|
3
4
|
import {
|
|
4
5
|
acquireCapacityLease,
|
|
5
6
|
activeCapacityDrainUnsafe,
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
type CapacityLimits,
|
|
8
9
|
type CapacityStateFile,
|
|
9
10
|
capacityStateLockPath,
|
|
11
|
+
describeEndpointCapacityHolders,
|
|
10
12
|
readCapacityStateUnsafe,
|
|
11
13
|
writeCapacityStateUnsafe,
|
|
12
14
|
} from "./capacity-lease.js";
|
|
@@ -19,9 +21,10 @@ export type ReservationMemberStatus = "held" | "consumed" | "released";
|
|
|
19
21
|
export type ReservationStatus = "active" | "released" | "rolled_back" | "expired";
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
|
-
* A reservation holds
|
|
23
|
-
* concurrency slot, a per-node slot, and a budget
|
|
24
|
-
*
|
|
24
|
+
* A reservation holds four scarce things and nothing else: a global
|
|
25
|
+
* concurrency slot, a per-node slot, an inference-endpoint slot, and a budget
|
|
26
|
+
* upper bound. Node and endpoint identity matter only because slots bind on
|
|
27
|
+
* those dimensions. Agent, target, model, and runtime
|
|
25
28
|
* identity are route identity, which the failover envelope owns
|
|
26
29
|
* (`assertRouteWithinApprovedEnvelope`); pinning them here made a
|
|
27
30
|
* plan-approved dispatch unable to fail over at all.
|
|
@@ -30,6 +33,7 @@ export interface ReservationPlanTask {
|
|
|
30
33
|
memberId: string;
|
|
31
34
|
wave: number;
|
|
32
35
|
nodeId: string;
|
|
36
|
+
endpointKey?: string;
|
|
33
37
|
costUpperBoundUsd: number;
|
|
34
38
|
}
|
|
35
39
|
|
|
@@ -54,17 +58,31 @@ export interface DispatchReservationRecord {
|
|
|
54
58
|
export interface ReservationCapacitySnapshot {
|
|
55
59
|
global: { active: number; limit: number };
|
|
56
60
|
nodes: Readonly<Record<string, { active: number; limit: number }>>;
|
|
61
|
+
endpoints: Readonly<Record<string, { active: number; limit: number; leases?: number; foregroundStreams?: number }>>;
|
|
57
62
|
budget: { currentUsd: number; ceilingUsd: number };
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
export interface ReservationAllocation {
|
|
61
66
|
globalSlots: number;
|
|
62
67
|
nodeSlots: Readonly<Record<string, number>>;
|
|
68
|
+
endpointSlots: Readonly<Record<string, number>>;
|
|
63
69
|
budgetUsd: number;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
export type ReservationPlanResult = { ok: true; allocation: ReservationAllocation } | { ok: false; reason: string };
|
|
67
73
|
|
|
74
|
+
function endpointCapacityDenial(input: {
|
|
75
|
+
endpointKey: string;
|
|
76
|
+
limit: number;
|
|
77
|
+
used: number;
|
|
78
|
+
requested: number;
|
|
79
|
+
leases: number;
|
|
80
|
+
reservations: number;
|
|
81
|
+
foregroundStreams: number;
|
|
82
|
+
}): string {
|
|
83
|
+
return `dispatch: admission denied: endpoint '${endpointLabel(input.endpointKey)}' capacity exceeded (${input.used + input.requested}/${input.limit} slots): ${describeEndpointCapacityHolders(input)}; reduce the same-wave worker count, collect in-flight runs, or point workers at a second server`;
|
|
84
|
+
}
|
|
85
|
+
|
|
68
86
|
function copyRecord(record: DispatchReservationRecord): DispatchReservationRecord {
|
|
69
87
|
return { ...record, members: record.members.map((member) => ({ ...member })) };
|
|
70
88
|
}
|
|
@@ -96,6 +114,7 @@ function validRecord(value: unknown): value is DispatchReservationRecord {
|
|
|
96
114
|
typeof member.memberId === "string" &&
|
|
97
115
|
Number.isInteger(member.wave) &&
|
|
98
116
|
typeof member.nodeId === "string" &&
|
|
117
|
+
(member.endpointKey === undefined || typeof member.endpointKey === "string") &&
|
|
99
118
|
typeof member.costUpperBoundUsd === "number" &&
|
|
100
119
|
Number.isFinite(member.costUpperBoundUsd) &&
|
|
101
120
|
member.costUpperBoundUsd >= 0 &&
|
|
@@ -145,9 +164,13 @@ function reservationAllocation(tasks: ReadonlyArray<ReservationPlanTask>): Reser
|
|
|
145
164
|
),
|
|
146
165
|
);
|
|
147
166
|
const nodeSlots = peakBy(tasks.map((task) => ({ wave: task.wave, key: task.nodeId })));
|
|
167
|
+
const endpointSlots = peakBy(
|
|
168
|
+
tasks.flatMap((task) => (task.endpointKey === undefined ? [] : [{ wave: task.wave, key: task.endpointKey }])),
|
|
169
|
+
);
|
|
148
170
|
return {
|
|
149
171
|
globalSlots,
|
|
150
172
|
nodeSlots,
|
|
173
|
+
endpointSlots,
|
|
151
174
|
budgetUsd: tasks.reduce((sum, task) => sum + task.costUpperBoundUsd, 0),
|
|
152
175
|
};
|
|
153
176
|
}
|
|
@@ -191,6 +214,26 @@ function allocateReservation(
|
|
|
191
214
|
return { ok: false, reason: `node '${nodeId}' capacity exceeded (${used + slots}/${node.limit})` };
|
|
192
215
|
}
|
|
193
216
|
}
|
|
217
|
+
for (const [endpointKey, slots] of Object.entries(requested.endpointSlots)) {
|
|
218
|
+
const endpoint = capacity.endpoints[endpointKey];
|
|
219
|
+
if (!endpoint || !Number.isFinite(endpoint.limit)) continue;
|
|
220
|
+
const heldSlots = held.reduce((sum, allocation) => sum + (allocation.endpointSlots[endpointKey] ?? 0), 0);
|
|
221
|
+
const used = endpoint.active + heldSlots;
|
|
222
|
+
if (used + slots > endpoint.limit) {
|
|
223
|
+
return {
|
|
224
|
+
ok: false,
|
|
225
|
+
reason: endpointCapacityDenial({
|
|
226
|
+
endpointKey,
|
|
227
|
+
limit: endpoint.limit,
|
|
228
|
+
used,
|
|
229
|
+
requested: slots,
|
|
230
|
+
leases: endpoint.leases ?? endpoint.active - (endpoint.foregroundStreams ?? 0),
|
|
231
|
+
reservations: heldSlots,
|
|
232
|
+
foregroundStreams: endpoint.foregroundStreams ?? 0,
|
|
233
|
+
}),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
194
237
|
const existingBudget = activeRecords.reduce((sum, record) => sum + outstandingBudget(record), 0);
|
|
195
238
|
const projected = capacity.budget.currentUsd + existingBudget + requested.budgetUsd;
|
|
196
239
|
if (projected >= capacity.budget.ceilingUsd) {
|
|
@@ -225,7 +268,9 @@ export function createDispatchReservation(input: {
|
|
|
225
268
|
if (!planned.ok) {
|
|
226
269
|
replaceReservations(state, records);
|
|
227
270
|
writeCapacityStateUnsafe(state);
|
|
228
|
-
throw new Error(
|
|
271
|
+
throw new Error(
|
|
272
|
+
planned.reason.startsWith("dispatch:") ? planned.reason : `dispatch: reservation denied: ${planned.reason}`,
|
|
273
|
+
);
|
|
229
274
|
}
|
|
230
275
|
const createdAt = new Date(nowMs).toISOString();
|
|
231
276
|
const record: DispatchReservationRecord = {
|
|
@@ -250,6 +295,7 @@ export function transferDispatchReservationToLease(input: {
|
|
|
250
295
|
memberId: string;
|
|
251
296
|
assignmentId: string;
|
|
252
297
|
nodeId: string;
|
|
298
|
+
endpointKey?: string;
|
|
253
299
|
limits: CapacityLimits;
|
|
254
300
|
nowMs?: number;
|
|
255
301
|
ttlMs?: number;
|
|
@@ -258,6 +304,7 @@ export function transferDispatchReservationToLease(input: {
|
|
|
258
304
|
return acquireCapacityLease({
|
|
259
305
|
assignmentId: input.assignmentId,
|
|
260
306
|
nodeId: input.nodeId,
|
|
307
|
+
...(input.endpointKey !== undefined ? { endpointKey: input.endpointKey } : {}),
|
|
261
308
|
limits: input.limits,
|
|
262
309
|
nowMs,
|
|
263
310
|
...(input.ttlMs !== undefined ? { ttlMs: input.ttlMs } : {}),
|
|
@@ -290,6 +337,7 @@ export function rebindDispatchReservationMember(input: {
|
|
|
290
337
|
ownerId: string;
|
|
291
338
|
memberId: string;
|
|
292
339
|
nodeId: string;
|
|
340
|
+
endpointKey?: string;
|
|
293
341
|
costUpperBoundUsd: number;
|
|
294
342
|
capacity: ReservationCapacitySnapshot;
|
|
295
343
|
nowMs?: number;
|
|
@@ -304,11 +352,42 @@ export function rebindDispatchReservationMember(input: {
|
|
|
304
352
|
if (member.status === "released") {
|
|
305
353
|
throw new Error(`dispatch: reservation member '${input.memberId}' was already released`);
|
|
306
354
|
}
|
|
307
|
-
if (
|
|
355
|
+
if (
|
|
356
|
+
member.nodeId === input.nodeId &&
|
|
357
|
+
member.endpointKey === input.endpointKey &&
|
|
358
|
+
member.costUpperBoundUsd === input.costUpperBoundUsd
|
|
359
|
+
) {
|
|
308
360
|
return copyRecord(record);
|
|
309
361
|
}
|
|
310
|
-
|
|
311
362
|
const others = records.filter((entry) => entry.status === "active" && entry.ownerId !== input.ownerId);
|
|
363
|
+
if (member.endpointKey !== input.endpointKey && input.endpointKey !== undefined) {
|
|
364
|
+
const endpointKey = input.endpointKey;
|
|
365
|
+
const endpoint = input.capacity.endpoints[endpointKey];
|
|
366
|
+
if (endpoint !== undefined && Number.isFinite(endpoint.limit)) {
|
|
367
|
+
const siblingSlots = record.members.filter(
|
|
368
|
+
(entry) => entry !== member && entry.status !== "released" && entry.endpointKey === endpointKey,
|
|
369
|
+
).length;
|
|
370
|
+
const otherHeld = others.reduce(
|
|
371
|
+
(sum, entry) => sum + (reservationAllocation(outstandingTasks(entry)).endpointSlots[endpointKey] ?? 0),
|
|
372
|
+
0,
|
|
373
|
+
);
|
|
374
|
+
const used = endpoint.active + otherHeld + siblingSlots;
|
|
375
|
+
if (used + 1 > endpoint.limit) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
endpointCapacityDenial({
|
|
378
|
+
endpointKey,
|
|
379
|
+
limit: endpoint.limit,
|
|
380
|
+
used,
|
|
381
|
+
requested: 1,
|
|
382
|
+
leases: endpoint.leases ?? endpoint.active - (endpoint.foregroundStreams ?? 0),
|
|
383
|
+
reservations: otherHeld + siblingSlots,
|
|
384
|
+
foregroundStreams: endpoint.foregroundStreams ?? 0,
|
|
385
|
+
}),
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
312
391
|
if (member.nodeId !== input.nodeId) {
|
|
313
392
|
const node = input.capacity.nodes[input.nodeId];
|
|
314
393
|
if (node !== undefined && Number.isFinite(node.limit)) {
|
|
@@ -342,6 +421,8 @@ export function rebindDispatchReservationMember(input: {
|
|
|
342
421
|
}
|
|
343
422
|
|
|
344
423
|
member.nodeId = input.nodeId;
|
|
424
|
+
if (input.endpointKey === undefined) delete member.endpointKey;
|
|
425
|
+
else member.endpointKey = input.endpointKey;
|
|
345
426
|
member.costUpperBoundUsd = Math.max(0, input.costUpperBoundUsd);
|
|
346
427
|
writeStore(records);
|
|
347
428
|
return copyRecord(record);
|
|
@@ -370,6 +451,26 @@ export function releaseDispatchReservationMember(
|
|
|
370
451
|
});
|
|
371
452
|
}
|
|
372
453
|
|
|
454
|
+
/** Settle every member after the scheduler has finished the reserved plan. */
|
|
455
|
+
export function releaseDispatchReservation(ownerId: string, nowMs = Date.now()): DispatchReservationRecord | null {
|
|
456
|
+
return withStateFileLockSync(capacityStateLockPath(), () => {
|
|
457
|
+
const records = readStore();
|
|
458
|
+
const record = records.find((entry) => entry.ownerId === ownerId);
|
|
459
|
+
if (!record) return null;
|
|
460
|
+
if (record.status !== "active") return copyRecord(record);
|
|
461
|
+
const settledAt = new Date(nowMs).toISOString();
|
|
462
|
+
for (const member of record.members) {
|
|
463
|
+
if (member.status === "released") continue;
|
|
464
|
+
member.status = "released";
|
|
465
|
+
member.releasedAt = settledAt;
|
|
466
|
+
}
|
|
467
|
+
record.status = "released";
|
|
468
|
+
record.settledAt = settledAt;
|
|
469
|
+
writeStore(records);
|
|
470
|
+
return copyRecord(record);
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
373
474
|
export function rollbackDispatchReservation(ownerId: string, nowMs = Date.now()): DispatchReservationRecord | null {
|
|
374
475
|
return withStateFileLockSync(capacityStateLockPath(), () => {
|
|
375
476
|
const records = readStore();
|
|
@@ -488,14 +589,21 @@ export function reservedBudgetUsd(): number {
|
|
|
488
589
|
.reduce((sum, record) => sum + outstandingBudget(record), 0);
|
|
489
590
|
}
|
|
490
591
|
|
|
491
|
-
export function reservedCapacity(): {
|
|
592
|
+
export function reservedCapacity(): {
|
|
593
|
+
globalSlots: number;
|
|
594
|
+
nodeSlots: Readonly<Record<string, number>>;
|
|
595
|
+
endpointSlots: Readonly<Record<string, number>>;
|
|
596
|
+
} {
|
|
492
597
|
let globalSlots = 0;
|
|
493
598
|
const nodeSlots: Record<string, number> = {};
|
|
599
|
+
const endpointSlots: Record<string, number> = {};
|
|
494
600
|
for (const record of readStore().filter((entry) => entry.status === "active")) {
|
|
495
601
|
const allocation = reservationAllocation(outstandingTasks(record));
|
|
496
602
|
globalSlots += allocation.globalSlots;
|
|
497
603
|
for (const [nodeId, count] of Object.entries(allocation.nodeSlots))
|
|
498
604
|
nodeSlots[nodeId] = (nodeSlots[nodeId] ?? 0) + count;
|
|
605
|
+
for (const [endpointKey, count] of Object.entries(allocation.endpointSlots))
|
|
606
|
+
endpointSlots[endpointKey] = (endpointSlots[endpointKey] ?? 0) + count;
|
|
499
607
|
}
|
|
500
|
-
return { globalSlots, nodeSlots };
|
|
608
|
+
return { globalSlots, nodeSlots, endpointSlots };
|
|
501
609
|
}
|
|
@@ -107,6 +107,10 @@ export async function writeFleetRun(record: FleetRunRecord): Promise<void> {
|
|
|
107
107
|
await atomicWrite(fleetRunPath(record.id), `${JSON.stringify(record, null, 2)}\n`);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// `RunEnvelope.heartbeatAt` deliberately persists only the absolute wall-clock
|
|
111
|
+
// anchor used for operator display and crash recovery. The monotonic heartbeat
|
|
112
|
+
// stamp belongs to the live transport in this process; serializing it would
|
|
113
|
+
// give a restarted process a number from an origin it cannot share.
|
|
110
114
|
function readRuns(): RunEnvelope[] {
|
|
111
115
|
const path = runsPath();
|
|
112
116
|
if (!existsSync(path)) return [];
|