@iowarp/clio-coder 0.3.8 → 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 +49 -0
- package/README.md +7 -3
- package/dist/{acp-U67UHUK2.js → acp-7LOELQFP.js} +6 -6
- package/dist/{agents-YU6SGALZ.js → agents-FIBG2SHA.js} +27 -25
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-5ZPJOIVG.js → auth-OI4LIH2I.js} +11 -12
- package/dist/{builtins-C6JMZVV6.js → builtins-AD25UL3C.js} +5 -5
- package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
- package/dist/chunk-3DPEIQKN.js +113 -0
- package/dist/{chunk-4SPRNWDE.js → chunk-3DUR4WUA.js} +15 -15
- package/dist/{chunk-VHN4MY6O.js → chunk-3MRC2YSQ.js} +2 -2
- package/dist/{chunk-5DHKRSMQ.js → chunk-3UUY7R3Z.js} +11 -7
- package/dist/{chunk-IGWKHNIQ.js → chunk-3V5AYSEQ.js} +8 -8
- package/dist/{chunk-FHJEP5SW.js → chunk-465CC7FK.js} +8 -5
- package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
- package/dist/{chunk-A3WNZD3P.js → chunk-4H6ULJ3H.js} +67 -21
- package/dist/{chunk-TB5666IT.js → chunk-4LJX2PUC.js} +3 -3
- package/dist/{chunk-XWSF374K.js → chunk-56KB5IJP.js} +2 -2
- package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
- package/dist/{chunk-2HEJ2F35.js → chunk-5HFBWUMU.js} +20 -8
- package/dist/{chunk-WNIJTQQK.js → chunk-5PVQ4SRS.js} +78 -6
- package/dist/{chunk-DYIM5TJT.js → chunk-5QKCQQ3E.js} +262 -6
- package/dist/{chunk-TYPGUK6W.js → chunk-5T7RBWN2.js} +111 -5
- package/dist/{chunk-IIZWH4XA.js → chunk-774ILSRL.js} +2 -2
- package/dist/chunk-7C6RYZGQ.js +391 -0
- package/dist/{chunk-TANS5ZJS.js → chunk-AD7Y7STJ.js} +3 -3
- package/dist/{chunk-RWSI4YD7.js → chunk-AEYBF3TB.js} +33 -12
- package/dist/{chunk-DGSYXYMX.js → chunk-AMKHQW3C.js} +2 -2
- package/dist/{chunk-VWZOAB7K.js → chunk-B5XRQOLB.js} +7 -7
- package/dist/{chunk-WXY7KU3G.js → chunk-BVDVID7E.js} +2 -2
- package/dist/{chunk-PMDBGQSJ.js → chunk-CA42X6KT.js} +2 -2
- package/dist/{chunk-HLE42MG7.js → chunk-D73KXYPF.js} +3 -3
- package/dist/{chunk-5Q2VVUKB.js → chunk-DG4M6ZUE.js} +3 -3
- package/dist/{chunk-ME6CCNFO.js → chunk-EBOC7MT3.js} +6 -6
- package/dist/{chunk-MXKJU4JB.js → chunk-ECUO3KDP.js} +48 -7
- package/dist/{chunk-26LEYJZH.js → chunk-FALJGAWU.js} +2 -2
- package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
- package/dist/{chunk-7RGZWPB6.js → chunk-GAYUJ7LE.js} +67 -13
- package/dist/{chunk-VCBR6CU7.js → chunk-HAY4ZE2P.js} +2 -2
- package/dist/{chunk-FBVTI2TJ.js → chunk-HCBCAYZU.js} +11 -130
- package/dist/{chunk-WSB3FPX7.js → chunk-HJB5IUKP.js} +32 -136
- package/dist/{chunk-J3YUBZWY.js → chunk-HKO36JWF.js} +33 -5
- package/dist/{chunk-E77JEWSD.js → chunk-HPCTNZM2.js} +6 -36
- package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
- package/dist/{chunk-NMPKI6XL.js → chunk-JEQQR47K.js} +37 -18
- package/dist/{chunk-3BINW3FP.js → chunk-KV2AOLDF.js} +24 -4
- package/dist/{chunk-YS5VLNH5.js → chunk-LXPJXFM5.js} +7 -7
- package/dist/{chunk-7RFXX52T.js → chunk-MIX5N5AC.js} +271 -41
- package/dist/{chunk-K4XHGFR5.js → chunk-MLOK6ZOS.js} +1297 -218
- package/dist/{chunk-ZNLWCMVZ.js → chunk-MV2VUEJC.js} +2 -2
- package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
- package/dist/{chunk-5H3GB5BO.js → chunk-N3PBVRTZ.js} +4 -382
- package/dist/{chunk-2HFZQUHL.js → chunk-N5XKWMDW.js} +17 -7
- package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
- package/dist/{chunk-GU2UIAFZ.js → chunk-NQ6UCCOD.js} +3 -3
- package/dist/{chunk-N22QMJKY.js → chunk-NZU6YDNV.js} +4 -4
- package/dist/{chunk-ZVJ5BLO2.js → chunk-O6I4CIEU.js} +151 -13
- package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
- package/dist/{chunk-VAWNZU7Z.js → chunk-P3JGPQFL.js} +2 -2
- package/dist/{chunk-IJ7RPIYJ.js → chunk-PNY46YEY.js} +20 -3
- package/dist/{chunk-U6MBIEMB.js → chunk-PZ4I4JE2.js} +56 -35
- package/dist/{chunk-GPIEI3LY.js → chunk-QQ7EKM72.js} +2 -2
- package/dist/{chunk-WLFILSD5.js → chunk-R7LNVMCS.js} +66 -28
- package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
- package/dist/chunk-RKKLTLYB.js +45 -0
- package/dist/{chunk-TT36MB5S.js → chunk-RKRLDWD3.js} +3 -1
- package/dist/{chunk-TTHACPOM.js → chunk-S4COXYBG.js} +456 -18
- package/dist/{chunk-WWCZ5F23.js → chunk-T3Z6VAAF.js} +69 -10
- package/dist/{chunk-GN57SG4G.js → chunk-TD7UE2L5.js} +9 -7
- package/dist/{chunk-TLQJPP24.js → chunk-TEO2TLVN.js} +523 -322
- package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
- package/dist/{chunk-KTYTFRMB.js → chunk-VKBMFOYV.js} +17 -15
- package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
- package/dist/{chunk-P43ETTHK.js → chunk-VPTUJU4P.js} +2 -2
- package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
- package/dist/{chunk-EMYUUSFG.js → chunk-WXCJ7VME.js} +5 -5
- package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
- package/dist/{chunk-JOZYP4GM.js → chunk-YKOFT37S.js} +5 -5
- package/dist/chunk-YSEHGPCT.js +127 -0
- package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
- package/dist/cli/index.js +27 -27
- package/dist/{clio-QVTYJ57A.js → clio-LT5V7SSZ.js} +6 -6
- package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +4 -4
- package/dist/{config-LW5IJFQN.js → config-RXS5T3JT.js} +71 -43
- package/dist/{configure-7XIZCOU4.js → configure-2WYWSCSD.js} +14 -15
- package/dist/{context-Y6Y7QPR6.js → context-I3BTOTCS.js} +12 -12
- package/dist/{context-L3WL3X7K.js → context-MVOORGMF.js} +34 -33
- package/dist/{context-N52ZA626.js → context-PALKKQYL.js} +20 -20
- package/dist/{context-clear-MBQRLSDQ.js → context-clear-N2WOYZ2K.js} +34 -33
- package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
- package/dist/{context-working-set-GS6DSO7F.js → context-working-set-MIEVECVZ.js} +10 -11
- package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-VVA4SRRH.js} +34 -33
- package/dist/doctor-TWBWFK5V.js +165 -0
- package/dist/{eval-BEC2WHDA.js → eval-IJ5VEZDJ.js} +2016 -142
- package/dist/{evidence-REJUMSKM.js → evidence-L5APPXNV.js} +29 -28
- package/dist/{evolve-PY5ZBA5K.js → evolve-RGNKFJ52.js} +29 -28
- package/dist/{extensions-HVKU65YU.js → extensions-7WYWUX5A.js} +9 -3
- package/dist/{fleet-7WZEWRFA.js → fleet-6CNVBZZP.js} +87 -55
- package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-L2SXSYEI.js} +6 -6
- package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-2J3OOIPO.js} +14 -12
- package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-CZRJ4JP5.js} +3 -4
- package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-C5RI6DP7.js} +16 -15
- package/dist/{init-OG3TPGQG.js → init-VBN2ACVA.js} +50 -48
- package/dist/{library-CNTMPLRF.js → library-JHGUMLY2.js} +13 -11
- package/dist/{memory-6IS7F275.js → memory-K4OQIYWG.js} +31 -30
- package/dist/{models-ENRJDA5W.js → models-2NCZUWDD.js} +23 -22
- package/dist/{monitor-XLDVO7TN.js → monitor-MMVTJABD.js} +35 -34
- package/dist/{orchestrator-6KSPYRHA.js → orchestrator-ZKBPCHW6.js} +1627 -294
- package/dist/{reset-RZ4ER727.js → reset-DD5JGOY3.js} +3 -3
- package/dist/{run-Y2CNK5RU.js → run-QEGNX7FL.js} +56 -55
- package/dist/{share-A55GYP6Z.js → share-JKD3BQMW.js} +13 -11
- package/dist/{skills-ALC5J6AT.js → skills-LMQIKDOZ.js} +14 -12
- package/dist/{skills-eval-JPBEBYQU.js → skills-eval-I7X2774U.js} +34 -32
- package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
- package/dist/{support-MIETYA5E.js → support-I7LOJLIF.js} +4 -4
- package/dist/{targets-VGNXIR3S.js → targets-RUSR6B5Z.js} +58 -32
- package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-QYVORFR4.js} +6 -4
- package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
- package/dist/{upgrade-FUSUAGHR.js → upgrade-XANW3FXB.js} +17 -16
- package/dist/{usage-N4MKVHKD.js → usage-4H7ZRXQT.js} +88 -44
- package/dist/{verifiers-YAWOJ3H2.js → verifiers-UZXNBZEB.js} +6 -6
- package/dist/{verify-LTDHYBGY.js → verify-BVKWTNDL.js} +5 -5
- package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-MY7WV2QI.js} +49 -47
- package/dist/worker/entry.js +29 -33
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +6 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +23 -2
- package/docs/commands-and-modes.md +1 -1
- package/docs/configuration-and-targets.md +30 -3
- 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 +11 -10
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +3 -1
- package/docs/fleet-dispatch.md +4 -4
- 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 +53 -2
- package/docs/proactive-memory.md +127 -14
- package/docs/prompt-envelope-and-tools.md +19 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +19 -3
- 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 -1
- package/src/cli/agents.ts +1 -1
- package/src/cli/config-inspect.ts +33 -6
- package/src/cli/config.ts +1 -1
- package/src/cli/doctor-state-size.ts +82 -0
- package/src/cli/doctor.ts +3 -1
- package/src/cli/eval.ts +80 -16
- package/src/cli/extensions.ts +5 -1
- package/src/cli/fleet.ts +32 -3
- package/src/cli/targets.ts +44 -13
- package/src/cli/trace.ts +63 -4
- package/src/cli/usage.ts +63 -14
- package/src/core/bus-events.ts +29 -1
- package/src/core/cache-telemetry.ts +42 -0
- 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/residency-target-key.ts +25 -0
- package/src/core/response-schema.ts +36 -2
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/context/codewiki/coordinator.ts +12 -4
- package/src/domains/dispatch/admission.ts +40 -3
- package/src/domains/dispatch/capacity-lease.ts +98 -9
- package/src/domains/dispatch/contract.ts +11 -0
- package/src/domains/dispatch/execution-plan.ts +44 -4
- package/src/domains/dispatch/extension.ts +166 -42
- package/src/domains/dispatch/fleet-run.ts +23 -3
- package/src/domains/dispatch/heartbeat.ts +32 -8
- package/src/domains/dispatch/index.ts +3 -0
- package/src/domains/dispatch/orphan-recovery.ts +5 -0
- package/src/domains/dispatch/reservation-store.ts +116 -8
- package/src/domains/dispatch/state.ts +4 -0
- package/src/domains/dispatch/worker-spawn.ts +25 -11
- package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
- package/src/domains/dispatch/write-boundary.ts +62 -1
- 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/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 +127 -0
- 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 +2 -13
- package/src/domains/evidence/eval.ts +2 -12
- package/src/domains/evidence/findings-markdown.ts +33 -0
- package/src/domains/evidence/run-trust.ts +7 -113
- package/src/domains/evidence/trust-projection.ts +2 -2
- package/src/domains/extensions/compatibility.ts +285 -0
- package/src/domains/extensions/discovery.ts +38 -3
- package/src/domains/extensions/resources.ts +1 -1
- package/src/domains/extensions/state.ts +12 -3
- package/src/domains/extensions/types.ts +2 -0
- package/src/domains/lifecycle/doctor.ts +69 -1
- 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/providers/endpoint-capacity.ts +96 -0
- 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/prompts/loader.ts +95 -33
- package/src/domains/safety/call-target.ts +52 -0
- package/src/domains/safety/run-effects.ts +35 -4
- 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/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/provider-payload.ts +29 -1
- 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 +84 -12
- 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-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 +35 -1
- 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/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/view-overlay.ts +28 -3
- package/src/tools/ask-user.ts +43 -2
- package/src/tools/dispatch-plan.ts +17 -9
- package/src/tools/dispatch-scout.ts +1 -1
- package/src/tools/registry.ts +16 -0
- package/dist/chunk-AOCYTWAV.js +0 -449
- package/dist/chunk-HWUFFB6L.js +0 -83
- package/dist/chunk-R346GLFC.js +0 -31
- package/dist/doctor-M7YEDGAE.js +0 -91
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { createHash, randomBytes } from "node:crypto";
|
|
13
13
|
import { type Dirent, existsSync, readdirSync, readFileSync } from "node:fs";
|
|
14
14
|
import { isAbsolute, relative, resolve as resolvePath, sep } from "node:path";
|
|
15
|
+
import { performance } from "node:perf_hooks";
|
|
15
16
|
import { BusChannels, type DispatchCompletedPayload, type DispatchRunIdentity } from "../../core/bus-events.js";
|
|
16
17
|
import { DEFAULT_SETTINGS, type DelegationToolGovernance } from "../../core/defaults.js";
|
|
17
18
|
import type { DomainBundle, DomainContext, DomainExtension } from "../../core/domain-loader.js";
|
|
@@ -76,6 +77,9 @@ import { type EffectivePricing, resolveEffectivePricing } from "../providers/cat
|
|
|
76
77
|
import {
|
|
77
78
|
type CapabilityFlags,
|
|
78
79
|
canonicalizeWireModelId,
|
|
80
|
+
type EndpointCapacity,
|
|
81
|
+
endpointCapacitiesForStatuses,
|
|
82
|
+
endpointCapacityForStatus,
|
|
79
83
|
firstRuntimeResolutionError,
|
|
80
84
|
isDispatchEligibleRuntime,
|
|
81
85
|
type ProvidersContract,
|
|
@@ -166,7 +170,14 @@ import {
|
|
|
166
170
|
} from "./failure-classification.js";
|
|
167
171
|
import { routeFactVerdict } from "./fleet-preflight.js";
|
|
168
172
|
import { competeStanceLiner, isBoundedGateRolePrompt } from "./gate-role-prompts.js";
|
|
169
|
-
import {
|
|
173
|
+
import {
|
|
174
|
+
classifyHeartbeat,
|
|
175
|
+
DEFAULT_HEARTBEAT_SPEC,
|
|
176
|
+
type HeartbeatSpec,
|
|
177
|
+
type HeartbeatStamp,
|
|
178
|
+
type HeartbeatStatus,
|
|
179
|
+
heartbeatMonotonicAt,
|
|
180
|
+
} from "./heartbeat.js";
|
|
170
181
|
import { hostVerificationRejection, runHostVerification } from "./host-verification.js";
|
|
171
182
|
import { renderDispatchIntentRequirements } from "./intent-requirements.js";
|
|
172
183
|
import { adaptJointRouteInput } from "./joint-route-adapter.js";
|
|
@@ -205,6 +216,7 @@ import {
|
|
|
205
216
|
planQueueSlot,
|
|
206
217
|
type ReservationCapacitySnapshot,
|
|
207
218
|
rebindDispatchReservationMember,
|
|
219
|
+
releaseDispatchReservation,
|
|
208
220
|
releaseDispatchReservationMember,
|
|
209
221
|
reservedBudgetUsd,
|
|
210
222
|
reservedPlanPeakSlots,
|
|
@@ -286,6 +298,7 @@ import {
|
|
|
286
298
|
type SpawnOptions as WorkerSpawnOptions,
|
|
287
299
|
type WorkerSpec,
|
|
288
300
|
} from "./worker-spawn.js";
|
|
301
|
+
import type { WriteBoundaryAttribution, WriteBoundaryAttributionDowngrade } from "./write-boundary.js";
|
|
289
302
|
|
|
290
303
|
interface RunTokenMeter {
|
|
291
304
|
inputTokens: number;
|
|
@@ -337,7 +350,7 @@ interface ActiveRun {
|
|
|
337
350
|
/** ACP event-inactivity window; null for native runs (heartbeat spec governs those). */
|
|
338
351
|
stallTimeoutMs: number | null;
|
|
339
352
|
lineage: RunLineage;
|
|
340
|
-
heartbeatAt:
|
|
353
|
+
heartbeatAt: HeartbeatStamp | null;
|
|
341
354
|
heartbeatStatus: HeartbeatStatus;
|
|
342
355
|
meter: RunTokenMeter;
|
|
343
356
|
pricing: EffectivePricing["rates"];
|
|
@@ -373,6 +386,8 @@ export interface DispatchBundleOptions {
|
|
|
373
386
|
heartbeatIntervalMs?: number;
|
|
374
387
|
resilienceCooldownMs?: number;
|
|
375
388
|
now?: () => number;
|
|
389
|
+
/** Monotonic clock for heartbeat age and ACP event-inactivity spans. */
|
|
390
|
+
monotonicNow?: () => number;
|
|
376
391
|
/** Immutable per-attempt session settings view; falls back to the shared snapshot. */
|
|
377
392
|
getSettings?: () => EffectiveSettings;
|
|
378
393
|
/** Live hard-block state cloned into each mediated worker spec. */
|
|
@@ -2397,6 +2412,7 @@ export function createDispatchBundle(
|
|
|
2397
2412
|
return DEFAULT_RESILIENCE_COOLDOWN_MS;
|
|
2398
2413
|
};
|
|
2399
2414
|
const now = options?.now ?? (() => Date.now());
|
|
2415
|
+
const monotonicNow = options?.monotonicNow ?? (() => performance.now());
|
|
2400
2416
|
// Durable evidence owner used by shadow observation and active readiness.
|
|
2401
2417
|
const routeObserver: RouteObserver = options?.routeObserver ?? createRouteObserver({});
|
|
2402
2418
|
|
|
@@ -2406,9 +2422,9 @@ export function createDispatchBundle(
|
|
|
2406
2422
|
const targetCooldowns = new Map<string, { until: number; reason: string }>();
|
|
2407
2423
|
const ownedReservations = new Set<string>();
|
|
2408
2424
|
/**
|
|
2409
|
-
*
|
|
2410
|
-
*
|
|
2411
|
-
*
|
|
2425
|
+
* Attribution from each finalized run's successful tool calls, newest last.
|
|
2426
|
+
* An incomplete record retains its paths as a lower bound beside the reasons
|
|
2427
|
+
* it cannot be read as a closed list.
|
|
2412
2428
|
*
|
|
2413
2429
|
* Held in memory rather than sealed into the receipt: the only reader is the
|
|
2414
2430
|
* write boundary closing a window seconds later in this process, and a
|
|
@@ -2416,13 +2432,13 @@ export function createDispatchBundle(
|
|
|
2416
2432
|
* Bounded because a long session finalizes many runs and none of this is
|
|
2417
2433
|
* needed once its window has closed.
|
|
2418
2434
|
*/
|
|
2419
|
-
const
|
|
2420
|
-
const
|
|
2421
|
-
|
|
2422
|
-
while (
|
|
2423
|
-
const oldest =
|
|
2435
|
+
const runWriteAttributions = new Map<string, WriteBoundaryAttribution>();
|
|
2436
|
+
const recordRunWriteAttribution = (runId: string, attribution: WriteBoundaryAttribution): void => {
|
|
2437
|
+
runWriteAttributions.set(runId, attribution);
|
|
2438
|
+
while (runWriteAttributions.size > RUN_WRITE_TARGET_HISTORY) {
|
|
2439
|
+
const oldest = runWriteAttributions.keys().next();
|
|
2424
2440
|
if (oldest.done === true) break;
|
|
2425
|
-
|
|
2441
|
+
runWriteAttributions.delete(oldest.value);
|
|
2426
2442
|
}
|
|
2427
2443
|
};
|
|
2428
2444
|
const capacityAdmission = createCapacityAdmissionController({
|
|
@@ -2430,7 +2446,7 @@ export function createDispatchBundle(
|
|
|
2430
2446
|
const settings = getEffectiveSettings();
|
|
2431
2447
|
const nodes: Record<string, number> = { local: configuredGlobalCapacity(settings) };
|
|
2432
2448
|
for (const node of settings?.fleet?.nodes ?? []) nodes[node.id] = node.maxWorkers;
|
|
2433
|
-
return { global: configuredGlobalCapacity(settings), nodes };
|
|
2449
|
+
return { global: configuredGlobalCapacity(settings), nodes, endpoints: configuredEndpointLimits() };
|
|
2434
2450
|
},
|
|
2435
2451
|
now,
|
|
2436
2452
|
reservedPlanPeak: (planId) =>
|
|
@@ -2443,6 +2459,21 @@ export function createDispatchBundle(
|
|
|
2443
2459
|
}),
|
|
2444
2460
|
);
|
|
2445
2461
|
|
|
2462
|
+
function configuredEndpointLimits(): Readonly<Record<string, number>> {
|
|
2463
|
+
return Object.fromEntries(
|
|
2464
|
+
Object.entries(endpointCapacitiesForStatuses(providers.list())).map(([key, value]) => [key, value.limit]),
|
|
2465
|
+
);
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
function endpointCapacityForTarget(targetId: string): EndpointCapacity | null {
|
|
2469
|
+
const status = providers.list().find((entry) => entry.target.id === targetId);
|
|
2470
|
+
if (status === undefined) return null;
|
|
2471
|
+
const endpoint = endpointCapacityForStatus(status);
|
|
2472
|
+
if (endpoint === null) return null;
|
|
2473
|
+
const configured = endpointCapacitiesForStatuses(providers.list())[endpoint.key];
|
|
2474
|
+
return configured ?? endpoint;
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2446
2477
|
function reservationCapacitySnapshot(settings: EffectiveSettings): ReservationCapacitySnapshot {
|
|
2447
2478
|
const preflight = scheduling.preflight();
|
|
2448
2479
|
// Reservation planning compares against a fresh read, never the display cache.
|
|
@@ -2452,9 +2483,21 @@ export function createDispatchBundle(
|
|
|
2452
2483
|
};
|
|
2453
2484
|
for (const node of settings?.fleet?.nodes ?? [])
|
|
2454
2485
|
nodes[node.id] = { active: usage.nodes[node.id] ?? 0, limit: node.maxWorkers };
|
|
2486
|
+
const endpoints = Object.fromEntries(
|
|
2487
|
+
Object.entries(configuredEndpointLimits()).map(([key, limit]) => [
|
|
2488
|
+
key,
|
|
2489
|
+
{
|
|
2490
|
+
active: usage.endpoints[key] ?? 0,
|
|
2491
|
+
limit,
|
|
2492
|
+
leases: usage.endpointHolders[key]?.leases ?? 0,
|
|
2493
|
+
foregroundStreams: usage.endpointHolders[key]?.foregroundStreams ?? 0,
|
|
2494
|
+
},
|
|
2495
|
+
]),
|
|
2496
|
+
);
|
|
2455
2497
|
return {
|
|
2456
2498
|
global: { active: usage.global, limit: configuredGlobalCapacity(settings) },
|
|
2457
2499
|
nodes,
|
|
2500
|
+
endpoints,
|
|
2458
2501
|
budget: { currentUsd: preflight.currentUsd, ceilingUsd: preflight.ceilingUsd },
|
|
2459
2502
|
};
|
|
2460
2503
|
}
|
|
@@ -2466,6 +2509,7 @@ export function createDispatchBundle(
|
|
|
2466
2509
|
memberId: task.memberId,
|
|
2467
2510
|
wave: task.wave,
|
|
2468
2511
|
nodeId: task.resolution.node.id,
|
|
2512
|
+
...(task.resolution.endpoint !== undefined ? { endpointKey: task.resolution.endpoint.key } : {}),
|
|
2469
2513
|
costUpperBoundUsd: task.resolution.routeApproval?.totalCostUpperBoundUsd ?? task.resolution.costUpperBoundUsd,
|
|
2470
2514
|
})),
|
|
2471
2515
|
capacity: reservationCapacitySnapshot(getEffectiveSettings()),
|
|
@@ -2520,7 +2564,12 @@ export function createDispatchBundle(
|
|
|
2520
2564
|
return scheduling.maxWorkers?.() ?? (configured === "auto" || configured === undefined ? 4 : Math.max(1, configured));
|
|
2521
2565
|
}
|
|
2522
2566
|
|
|
2523
|
-
async function admitAssignmentCapacity(
|
|
2567
|
+
async function admitAssignmentCapacity(
|
|
2568
|
+
req: DispatchRequest,
|
|
2569
|
+
nodeId: string,
|
|
2570
|
+
timing: RunPhaseMarks,
|
|
2571
|
+
endpoint?: EndpointCapacity | null,
|
|
2572
|
+
) {
|
|
2524
2573
|
const queuedAt = now();
|
|
2525
2574
|
timing.queuedAt = new Date(queuedAt).toISOString();
|
|
2526
2575
|
const assignmentId = req.lineage?.rootRunId ?? `pending-${queuedAt.toString(36)}-${randomBytes(6).toString("hex")}`;
|
|
@@ -2538,9 +2587,16 @@ export function createDispatchBundle(
|
|
|
2538
2587
|
const admitted = await capacityAdmission.admit({
|
|
2539
2588
|
assignmentId,
|
|
2540
2589
|
nodeId,
|
|
2590
|
+
...(endpoint !== null && endpoint !== undefined ? { endpointKey: endpoint.key } : {}),
|
|
2541
2591
|
deadlineAt,
|
|
2542
2592
|
...plan,
|
|
2543
|
-
|
|
2593
|
+
// A fleet's first attempt carries lineage and still owns the held
|
|
2594
|
+
// reservation member that admission must transfer into its lease.
|
|
2595
|
+
// Retries carry the same reservation identity after that member was
|
|
2596
|
+
// consumed, so they reacquire/rebind the assignment lease normally.
|
|
2597
|
+
...(req.reservation !== undefined && (req.lineage === undefined || req.lineage.attempt === 0)
|
|
2598
|
+
? { reservation: req.reservation }
|
|
2599
|
+
: {}),
|
|
2544
2600
|
});
|
|
2545
2601
|
timing.admittedAt = new Date(admitted.admittedAt).toISOString();
|
|
2546
2602
|
return admitted.lease;
|
|
@@ -2557,6 +2613,7 @@ export function createDispatchBundle(
|
|
|
2557
2613
|
function rebindReservationSlot(
|
|
2558
2614
|
req: DispatchRequest,
|
|
2559
2615
|
nodeId: string,
|
|
2616
|
+
endpoint: EndpointCapacity | null,
|
|
2560
2617
|
costUsd: number,
|
|
2561
2618
|
settings: EffectiveSettings,
|
|
2562
2619
|
): void {
|
|
@@ -2565,6 +2622,7 @@ export function createDispatchBundle(
|
|
|
2565
2622
|
const rebind = {
|
|
2566
2623
|
...req.reservation,
|
|
2567
2624
|
nodeId,
|
|
2625
|
+
...(endpoint !== null ? { endpointKey: endpoint.key } : {}),
|
|
2568
2626
|
costUpperBoundUsd: req.routeApproval?.totalCostUpperBoundUsd ?? costUsd,
|
|
2569
2627
|
capacity,
|
|
2570
2628
|
nowMs: now(),
|
|
@@ -2672,6 +2730,21 @@ export function createDispatchBundle(
|
|
|
2672
2730
|
|
|
2673
2731
|
type RetryScheduleResult = { scheduled: true } | { scheduled: false; settlementDetail?: string };
|
|
2674
2732
|
|
|
2733
|
+
/** A reserved plan member owns its retry chain independently of the shared fleet root assignment. */
|
|
2734
|
+
function retryChainIsLive(run: ActiveRun): boolean {
|
|
2735
|
+
if (run.aborted) return false;
|
|
2736
|
+
const reservation = run.req.reservation;
|
|
2737
|
+
if (reservation === undefined) return assignments.get(run.lineage.rootRunId)?.status === "running";
|
|
2738
|
+
try {
|
|
2739
|
+
const record = getDispatchReservation(reservation.ownerId);
|
|
2740
|
+
const member = record?.members.find((entry) => entry.memberId === reservation.memberId);
|
|
2741
|
+
return record?.status === "active" && member !== undefined && member.status !== "released";
|
|
2742
|
+
} catch (error) {
|
|
2743
|
+
reportDispatchDiagnostic(`read retry liveness for reservation member ${reservation.memberId}`, error);
|
|
2744
|
+
return false;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2675
2748
|
function maybeScheduleRetry(
|
|
2676
2749
|
run: ActiveRun,
|
|
2677
2750
|
outcome: RunOutcome,
|
|
@@ -2681,7 +2754,7 @@ export function createDispatchBundle(
|
|
|
2681
2754
|
): RetryScheduleResult {
|
|
2682
2755
|
if (draining) return { scheduled: false };
|
|
2683
2756
|
const rootRunId = run.lineage.rootRunId;
|
|
2684
|
-
if (
|
|
2757
|
+
if (!retryChainIsLive(run)) return { scheduled: false };
|
|
2685
2758
|
if (!RETRYABLE_OUTCOMES.has(outcome)) {
|
|
2686
2759
|
retryBackoff.delete(rootRunId);
|
|
2687
2760
|
return { scheduled: false };
|
|
@@ -2828,7 +2901,7 @@ export function createDispatchBundle(
|
|
|
2828
2901
|
reason = "retry",
|
|
2829
2902
|
decision: RetryDecision = decideRetry("internal", run.lineage.attempt, assignmentPolicyFor(run.req).maxRetries),
|
|
2830
2903
|
): Promise<void> {
|
|
2831
|
-
if (draining ||
|
|
2904
|
+
if (draining || !retryChainIsLive(run)) return;
|
|
2832
2905
|
const excludesNode = decision.excludedRouteParts.includes("node");
|
|
2833
2906
|
// Record the failed node so placement can choose an eligible survivor.
|
|
2834
2907
|
const rerouteHops =
|
|
@@ -3010,8 +3083,8 @@ export function createDispatchBundle(
|
|
|
3010
3083
|
}
|
|
3011
3084
|
}
|
|
3012
3085
|
|
|
3013
|
-
function heartbeatIso(
|
|
3014
|
-
return new Date(
|
|
3086
|
+
function heartbeatIso(heartbeat: HeartbeatStamp): string {
|
|
3087
|
+
return new Date(heartbeat.current).toISOString();
|
|
3015
3088
|
}
|
|
3016
3089
|
|
|
3017
3090
|
function heartbeatRunStatus(status: HeartbeatStatus): RunStatus {
|
|
@@ -3032,7 +3105,7 @@ export function createDispatchBundle(
|
|
|
3032
3105
|
event: {
|
|
3033
3106
|
type: "heartbeat_status",
|
|
3034
3107
|
status,
|
|
3035
|
-
heartbeatAt: run.heartbeatAt ? heartbeatIso(run.heartbeatAt
|
|
3108
|
+
heartbeatAt: run.heartbeatAt ? heartbeatIso(run.heartbeatAt) : null,
|
|
3036
3109
|
},
|
|
3037
3110
|
});
|
|
3038
3111
|
}
|
|
@@ -3050,16 +3123,16 @@ export function createDispatchBundle(
|
|
|
3050
3123
|
*/
|
|
3051
3124
|
function checkActiveHeartbeats(): void {
|
|
3052
3125
|
if (!ledger) return;
|
|
3053
|
-
const
|
|
3126
|
+
const tickMonotonic = monotonicNow();
|
|
3054
3127
|
for (const run of active.values()) {
|
|
3055
3128
|
if (run.aborted || run.stallKilled || !run.heartbeatAt) continue;
|
|
3056
|
-
const
|
|
3057
|
-
if (!Number.isFinite(
|
|
3129
|
+
const heartbeatMonotonic = heartbeatMonotonicAt(run.heartbeatAt);
|
|
3130
|
+
if (!Number.isFinite(heartbeatMonotonic) || !Number.isFinite(run.heartbeatAt.current)) continue;
|
|
3058
3131
|
if (run.runtimeKind === "acp-delegation") {
|
|
3059
|
-
ledger.update(run.runId, { heartbeatAt: heartbeatIso(
|
|
3132
|
+
ledger.update(run.runId, { heartbeatAt: heartbeatIso(run.heartbeatAt) });
|
|
3060
3133
|
const stallMs = run.stallTimeoutMs;
|
|
3061
3134
|
if (stallMs === null || stallMs <= 0) continue;
|
|
3062
|
-
if (
|
|
3135
|
+
if (tickMonotonic - heartbeatMonotonic <= stallMs) continue;
|
|
3063
3136
|
run.stallKilled = true;
|
|
3064
3137
|
run.heartbeatStatus = "dead";
|
|
3065
3138
|
emitHeartbeatStatus(run, "dead");
|
|
@@ -3070,10 +3143,10 @@ export function createDispatchBundle(
|
|
|
3070
3143
|
}
|
|
3071
3144
|
continue;
|
|
3072
3145
|
}
|
|
3073
|
-
const status = classifyHeartbeat(
|
|
3146
|
+
const status = classifyHeartbeat(heartbeatMonotonic, tickMonotonic, heartbeatSpec);
|
|
3074
3147
|
const patch: Partial<RunEnvelope> = {
|
|
3075
3148
|
status: heartbeatRunStatus(status),
|
|
3076
|
-
heartbeatAt: heartbeatIso(
|
|
3149
|
+
heartbeatAt: heartbeatIso(run.heartbeatAt),
|
|
3077
3150
|
};
|
|
3078
3151
|
ledger.update(run.runId, patch);
|
|
3079
3152
|
// Node staleness display feeds off the freshest worker heartbeat.
|
|
@@ -3493,7 +3566,7 @@ export function createDispatchBundle(
|
|
|
3493
3566
|
|
|
3494
3567
|
assertBudgetAdmitsRoute(req, { rates: null, provenance: "unknown" }, settings);
|
|
3495
3568
|
|
|
3496
|
-
const capacityLease = await admitAssignmentCapacity(req, "local", timing);
|
|
3569
|
+
const capacityLease = await admitAssignmentCapacity(req, "local", timing, null);
|
|
3497
3570
|
const leaseSlot = createLeaseSlotGuard(capacityAdmission, capacityLease.leaseId, req.lineage !== undefined);
|
|
3498
3571
|
|
|
3499
3572
|
// Resolve the ledger before starting the ACP process: an external agent
|
|
@@ -3519,6 +3592,8 @@ export function createDispatchBundle(
|
|
|
3519
3592
|
safety,
|
|
3520
3593
|
autonomy: lifecycle.autonomy,
|
|
3521
3594
|
clientVersion: readClioVersion(),
|
|
3595
|
+
now,
|
|
3596
|
+
monotonicNow,
|
|
3522
3597
|
});
|
|
3523
3598
|
} catch (error) {
|
|
3524
3599
|
leaseSlot.release();
|
|
@@ -3707,7 +3782,7 @@ export function createDispatchBundle(
|
|
|
3707
3782
|
ledgerRef.update(envelope.id, {
|
|
3708
3783
|
status: "running",
|
|
3709
3784
|
pid: acp.pid,
|
|
3710
|
-
heartbeatAt: heartbeatIso(acp.heartbeatAt
|
|
3785
|
+
heartbeatAt: heartbeatIso(acp.heartbeatAt),
|
|
3711
3786
|
lineage,
|
|
3712
3787
|
identity,
|
|
3713
3788
|
node: LOCAL_RUN_NODE,
|
|
@@ -4104,7 +4179,7 @@ export function createDispatchBundle(
|
|
|
4104
4179
|
cacheReadTokenCount: receiptDraft.cacheReadTokenCount ?? 0,
|
|
4105
4180
|
cacheWriteTokenCount: receiptDraft.cacheWriteTokenCount ?? 0,
|
|
4106
4181
|
reasoningTokenCount: receiptDraft.reasoningTokenCount ?? 0,
|
|
4107
|
-
heartbeatAt: heartbeatIso(acp.heartbeatAt
|
|
4182
|
+
heartbeatAt: heartbeatIso(acp.heartbeatAt),
|
|
4108
4183
|
};
|
|
4109
4184
|
ledgerRef.update(envelope.id, ledgerPatch);
|
|
4110
4185
|
const receipt = ledgerRef.recordReceipt(envelope.id, sealRouteDecision(receiptDraft, routeDecision));
|
|
@@ -4262,7 +4337,7 @@ export function createDispatchBundle(
|
|
|
4262
4337
|
"dispatch: writeRoots cannot be enforced on an ACP delegation target; the external agent runs its own tool surface. Dispatch to a native or claude-sdk worker.",
|
|
4263
4338
|
);
|
|
4264
4339
|
}
|
|
4265
|
-
rebindReservationSlot(req, "local", UNKNOWN_PRICING_ADMISSION_ESTIMATE_USD, settings);
|
|
4340
|
+
rebindReservationSlot(req, "local", null, UNKNOWN_PRICING_ADMISSION_ESTIMATE_USD, settings);
|
|
4266
4341
|
routeObservation = observeShadowRoute(req, undefined, settings);
|
|
4267
4342
|
return attachRouteObservation({
|
|
4268
4343
|
...(await dispatchAcpDelegation(req, settings, timing, routeObservation.decision, observer)),
|
|
@@ -4296,10 +4371,12 @@ export function createDispatchBundle(
|
|
|
4296
4371
|
model: lifecycle.target.wireModelId,
|
|
4297
4372
|
node: placement?.node?.id ?? "local",
|
|
4298
4373
|
};
|
|
4374
|
+
const endpoint = endpointCapacityForTarget(lifecycle.target.target.id);
|
|
4299
4375
|
assertRouteWithinApprovedEnvelope(req, effectiveRoute);
|
|
4300
4376
|
rebindReservationSlot(
|
|
4301
4377
|
req,
|
|
4302
4378
|
effectiveRoute.node,
|
|
4379
|
+
endpoint,
|
|
4303
4380
|
conservativeRouteAdmissionEstimateUsd(lifecycle.target.effectivePricing, admissionMaxOutputTokens(settings)),
|
|
4304
4381
|
settings,
|
|
4305
4382
|
);
|
|
@@ -4318,7 +4395,7 @@ export function createDispatchBundle(
|
|
|
4318
4395
|
shadow: () => observeShadowRoute(req, placed, settings),
|
|
4319
4396
|
});
|
|
4320
4397
|
|
|
4321
|
-
const capacityLease = await admitAssignmentCapacity(req, placement?.node.id ?? "local", timing);
|
|
4398
|
+
const capacityLease = await admitAssignmentCapacity(req, placement?.node.id ?? "local", timing, endpoint);
|
|
4322
4399
|
const leaseSlot = createLeaseSlotGuard(capacityAdmission, capacityLease.leaseId, req.lineage !== undefined);
|
|
4323
4400
|
|
|
4324
4401
|
const ledgerRef = (() => {
|
|
@@ -4387,6 +4464,8 @@ export function createDispatchBundle(
|
|
|
4387
4464
|
try {
|
|
4388
4465
|
worker = (placement?.spawn ?? spawnWorker)(spec, {
|
|
4389
4466
|
cwd: lifecycle.cwd,
|
|
4467
|
+
now,
|
|
4468
|
+
monotonicNow,
|
|
4390
4469
|
...(agentLedgerId !== null ? { onLedgerPost } : {}),
|
|
4391
4470
|
});
|
|
4392
4471
|
} catch (error) {
|
|
@@ -4443,10 +4522,21 @@ export function createDispatchBundle(
|
|
|
4443
4522
|
const upstreamResponses: RunReceiptUpstreamResponse[] = [];
|
|
4444
4523
|
const skillActivations: SkillActivation[] = [];
|
|
4445
4524
|
const finishContractEntries: unknown[] = [];
|
|
4525
|
+
let runIdForPermissionAudit: string | null = null;
|
|
4446
4526
|
// The worker's own tool calls, folded into what this run changed and what
|
|
4447
4527
|
// it validated. The sealed mutation-report contract is measured against
|
|
4448
4528
|
// this, so a reported path the run never touched cannot seal as fact.
|
|
4449
|
-
const runEffects = createRunEffectsRecorder(lifecycle.cwd
|
|
4529
|
+
const runEffects = createRunEffectsRecorder(lifecycle.cwd, {
|
|
4530
|
+
onWriteRecordDowngraded(downgrade) {
|
|
4531
|
+
const runId = runIdForPermissionAudit;
|
|
4532
|
+
if (runId === null) return;
|
|
4533
|
+
context.bus.emit(BusChannels.DispatchProgress, {
|
|
4534
|
+
runId,
|
|
4535
|
+
agentId: req.agentId,
|
|
4536
|
+
event: { type: "clio_write_record_downgraded", payload: downgrade },
|
|
4537
|
+
});
|
|
4538
|
+
},
|
|
4539
|
+
});
|
|
4450
4540
|
let finishContractAssistantText = "";
|
|
4451
4541
|
let finishContractAssistantTurnId: string | null = null;
|
|
4452
4542
|
let failureMessage: string | undefined;
|
|
@@ -4456,7 +4546,6 @@ export function createDispatchBundle(
|
|
|
4456
4546
|
const acceptsOutcomeCodeEvents =
|
|
4457
4547
|
lifecycle.runtimeKind === "http" ||
|
|
4458
4548
|
(lifecycle.runtimeKind === "sdk" && lifecycle.target.runtime.id === "claude-sdk");
|
|
4459
|
-
let runIdForPermissionAudit: string | null = null;
|
|
4460
4549
|
let workerPolicyPermissionCounter = 0;
|
|
4461
4550
|
const outputCapture = createWorkerOutputCapture();
|
|
4462
4551
|
const markObservedPhase = (field: "firstModelTokenAt" | "firstToolAt"): void => {
|
|
@@ -4739,7 +4828,7 @@ export function createDispatchBundle(
|
|
|
4739
4828
|
...(req.council !== undefined ? { council: req.council } : {}),
|
|
4740
4829
|
...(req.plan !== undefined ? { plan: req.plan } : {}),
|
|
4741
4830
|
...(lifecycle.personaOverride ? { personaOverride: lifecycle.personaOverride } : {}),
|
|
4742
|
-
...(heartbeatAt ? { heartbeatAt: heartbeatIso(heartbeatAt
|
|
4831
|
+
...(heartbeatAt ? { heartbeatAt: heartbeatIso(heartbeatAt) } : {}),
|
|
4743
4832
|
});
|
|
4744
4833
|
observer?.onAdmitted({
|
|
4745
4834
|
runId: envelope.id,
|
|
@@ -4755,6 +4844,7 @@ export function createDispatchBundle(
|
|
|
4755
4844
|
// per-worker meter. All optional so consumers degrade to local/plain.
|
|
4756
4845
|
const fleetIdentity = {
|
|
4757
4846
|
budget: lifecycle.budgetEnvelope,
|
|
4847
|
+
...(endpoint !== null ? { endpoint: { key: endpoint.key, label: endpoint.label, limit: endpoint.limit } } : {}),
|
|
4758
4848
|
...(placement !== undefined && placement !== null ? { node: placement.node.id } : {}),
|
|
4759
4849
|
...(req.gate !== undefined ? { gate: { role: req.gate.role, cycle: req.gate.cycle } } : {}),
|
|
4760
4850
|
...(req.council !== undefined ? { council: req.council } : {}),
|
|
@@ -5331,7 +5421,7 @@ export function createDispatchBundle(
|
|
|
5331
5421
|
...(receiptDraft.cacheWriteTokenCount !== undefined
|
|
5332
5422
|
? { cacheWriteTokenCount: receiptDraft.cacheWriteTokenCount }
|
|
5333
5423
|
: {}),
|
|
5334
|
-
...(activeRun.heartbeatAt ? { heartbeatAt: heartbeatIso(activeRun.heartbeatAt
|
|
5424
|
+
...(activeRun.heartbeatAt ? { heartbeatAt: heartbeatIso(activeRun.heartbeatAt) } : {}),
|
|
5335
5425
|
};
|
|
5336
5426
|
if (receiptDraft.reasoningTokenCount !== undefined) {
|
|
5337
5427
|
ledgerPatch.reasoningTokenCount = receiptDraft.reasoningTokenCount;
|
|
@@ -5354,9 +5444,28 @@ export function createDispatchBundle(
|
|
|
5354
5444
|
// the set. That run's window keeps today's blame-the-whole-diff
|
|
5355
5445
|
// behaviour; a run that never touched such a tool keeps the
|
|
5356
5446
|
// protection.
|
|
5357
|
-
const
|
|
5358
|
-
|
|
5359
|
-
|
|
5447
|
+
const toolTelemetryComplete = receipt.safety?.toolTelemetry?.coverage === "complete";
|
|
5448
|
+
const downgrades: WriteBoundaryAttributionDowngrade[] = observedRunEffects.writeRecordDowngrades.map((entry) => ({
|
|
5449
|
+
reason: entry.reason,
|
|
5450
|
+
tool: entry.tool,
|
|
5451
|
+
toolCallId: entry.toolCallId,
|
|
5452
|
+
runId: envelope.id,
|
|
5453
|
+
stepId: null,
|
|
5454
|
+
}));
|
|
5455
|
+
if (!toolTelemetryComplete) {
|
|
5456
|
+
downgrades.push({
|
|
5457
|
+
reason: "incomplete_tool_telemetry",
|
|
5458
|
+
tool: null,
|
|
5459
|
+
toolCallId: null,
|
|
5460
|
+
runId: envelope.id,
|
|
5461
|
+
stepId: null,
|
|
5462
|
+
});
|
|
5463
|
+
}
|
|
5464
|
+
recordRunWriteAttribution(envelope.id, {
|
|
5465
|
+
recorded: [...observedRunEffects.mutatedPaths],
|
|
5466
|
+
complete: toolTelemetryComplete && observedRunEffects.writeRecordComplete,
|
|
5467
|
+
downgrades,
|
|
5468
|
+
});
|
|
5360
5469
|
await ledgerRef.persist();
|
|
5361
5470
|
if (worktreeReceipt?.applied === true && req.taskWorktree !== undefined) {
|
|
5362
5471
|
try {
|
|
@@ -5628,12 +5737,14 @@ export function createDispatchBundle(
|
|
|
5628
5737
|
allowedTools: effectiveTools,
|
|
5629
5738
|
settings,
|
|
5630
5739
|
});
|
|
5740
|
+
const endpoint = endpointCapacityForTarget(target.target.id);
|
|
5631
5741
|
return {
|
|
5632
5742
|
agentId: req.agentId,
|
|
5633
5743
|
specFingerprint: agentSpecFingerprint(agentSpec),
|
|
5634
5744
|
targetId: target.target.id,
|
|
5635
5745
|
wireModelId: target.wireModelId,
|
|
5636
5746
|
runtimeId: target.runtime.id,
|
|
5747
|
+
...(endpoint !== null ? { endpoint: { key: endpoint.key, label: endpoint.label, limit: endpoint.limit } } : {}),
|
|
5637
5748
|
node: previewNode(req).node,
|
|
5638
5749
|
thinkingLevel: target.thinkingLevel,
|
|
5639
5750
|
toolSignature: toolSignature(effectiveTools),
|
|
@@ -6133,19 +6244,21 @@ export function createDispatchBundle(
|
|
|
6133
6244
|
*/
|
|
6134
6245
|
function snapshot(): DispatchSnapshot {
|
|
6135
6246
|
const tickNow = now();
|
|
6247
|
+
const tickMonotonic = monotonicNow();
|
|
6136
6248
|
const running: DispatchSnapshot["running"] = [];
|
|
6137
6249
|
const totals = { ...finalizedTotals };
|
|
6138
6250
|
const costAmounts = [...finalizedCosts];
|
|
6139
6251
|
for (const run of active.values()) {
|
|
6140
6252
|
let heartbeat: "alive" | "stale" | "dead" | "n/a" = "n/a";
|
|
6141
|
-
if (run.heartbeatAt && Number.isFinite(run.heartbeatAt
|
|
6253
|
+
if (run.heartbeatAt && Number.isFinite(heartbeatMonotonicAt(run.heartbeatAt))) {
|
|
6254
|
+
const heartbeatMonotonic = heartbeatMonotonicAt(run.heartbeatAt);
|
|
6142
6255
|
if (run.runtimeKind === "acp-delegation") {
|
|
6143
6256
|
const stallMs = run.stallTimeoutMs;
|
|
6144
6257
|
if (stallMs !== null && stallMs > 0) {
|
|
6145
|
-
heartbeat =
|
|
6258
|
+
heartbeat = tickMonotonic - heartbeatMonotonic > stallMs ? "dead" : "alive";
|
|
6146
6259
|
}
|
|
6147
6260
|
} else {
|
|
6148
|
-
heartbeat = classifyHeartbeat(
|
|
6261
|
+
heartbeat = classifyHeartbeat(heartbeatMonotonic, tickMonotonic, heartbeatSpec);
|
|
6149
6262
|
}
|
|
6150
6263
|
}
|
|
6151
6264
|
const meter = run.meter;
|
|
@@ -6271,6 +6384,7 @@ export function createDispatchBundle(
|
|
|
6271
6384
|
routeCandidates,
|
|
6272
6385
|
reservations: {
|
|
6273
6386
|
prepare: prepareReservation,
|
|
6387
|
+
release: (ownerId) => releaseDispatchReservation(ownerId, now()),
|
|
6274
6388
|
rollback: (ownerId) => rollbackDispatchReservation(ownerId, now()),
|
|
6275
6389
|
rollbackUnconsumed: (ownerId) => rollbackUnconsumedDispatchReservation(ownerId, now()),
|
|
6276
6390
|
get: getDispatchReservation,
|
|
@@ -6291,7 +6405,17 @@ export function createDispatchBundle(
|
|
|
6291
6405
|
observedRunWrites(runId) {
|
|
6292
6406
|
// A run this process never finalized, or finalized long enough ago to
|
|
6293
6407
|
// have been evicted, is unknown rather than silent, so both answer null.
|
|
6294
|
-
|
|
6408
|
+
const attribution = runWriteAttributions.get(runId);
|
|
6409
|
+
return attribution?.complete === true ? [...attribution.recorded] : null;
|
|
6410
|
+
},
|
|
6411
|
+
observedRunWriteAttribution(runId) {
|
|
6412
|
+
const attribution = runWriteAttributions.get(runId);
|
|
6413
|
+
if (attribution === undefined) return null;
|
|
6414
|
+
return {
|
|
6415
|
+
recorded: [...attribution.recorded],
|
|
6416
|
+
complete: attribution.complete,
|
|
6417
|
+
downgrades: attribution.downgrades?.map((entry) => ({ ...entry })) ?? [],
|
|
6418
|
+
};
|
|
6295
6419
|
},
|
|
6296
6420
|
assignments: {
|
|
6297
6421
|
get: (id) => assignments.get(id),
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
import { verifyReceiptIntegrity } from "./receipt-integrity.js";
|
|
47
47
|
import { type FleetRunRecord, writeFleetRun } from "./state.js";
|
|
48
48
|
import type { RunReceipt } from "./types.js";
|
|
49
|
+
import type { WriteBoundaryAttribution } from "./write-boundary.js";
|
|
49
50
|
import { createWriteBoundaryEnforcer } from "./write-boundary-enforcer.js";
|
|
50
51
|
|
|
51
52
|
/** Owner id for a plan with no model runs; it holds nothing to release. */
|
|
@@ -278,10 +279,29 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
|
|
|
278
279
|
// A code step runs a registered command in a subprocess. Nothing
|
|
279
280
|
// enumerates what that command wrote, so the step contributes no record
|
|
280
281
|
// and its window falls back to blaming the whole diff.
|
|
281
|
-
if (step === undefined
|
|
282
|
+
if (step === undefined) return null;
|
|
283
|
+
if (step.kind !== "agent") {
|
|
284
|
+
return {
|
|
285
|
+
recorded: [],
|
|
286
|
+
complete: false,
|
|
287
|
+
downgrades: [
|
|
288
|
+
{
|
|
289
|
+
reason: "code_step_untracked",
|
|
290
|
+
tool: null,
|
|
291
|
+
toolCallId: null,
|
|
292
|
+
runId: null,
|
|
293
|
+
stepId,
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
} satisfies WriteBoundaryAttribution;
|
|
297
|
+
}
|
|
282
298
|
const receipt = receiptsByStep.get(stepId);
|
|
283
299
|
if (receipt === undefined) return null;
|
|
284
|
-
|
|
300
|
+
if (dispatch.observedRunWriteAttribution !== undefined) {
|
|
301
|
+
return dispatch.observedRunWriteAttribution(receipt.runId);
|
|
302
|
+
}
|
|
303
|
+
const recorded = dispatch.observedRunWrites?.(receipt.runId) ?? null;
|
|
304
|
+
return recorded === null ? null : { recorded, complete: true, downgrades: [] };
|
|
285
305
|
},
|
|
286
306
|
onVerdict(verdict, path) {
|
|
287
307
|
// Concurrent changes are reported too. The window did not fail for them
|
|
@@ -704,7 +724,7 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
|
|
|
704
724
|
cancel: (assignmentId) => dispatch.abort(assignmentId),
|
|
705
725
|
release: (ownerId) => {
|
|
706
726
|
if (ownerId === NO_WORKER_RESERVATION) return;
|
|
707
|
-
dispatch.reservations?.
|
|
727
|
+
dispatch.reservations?.release(ownerId);
|
|
708
728
|
},
|
|
709
729
|
releaseUnconsumed: (ownerId) => {
|
|
710
730
|
if (ownerId === NO_WORKER_RESERVATION) return;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pure heartbeat watchdog classifier (Phase 6 slice 3).
|
|
3
3
|
*
|
|
4
|
-
* Maps a last-seen
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Maps a last-seen monotonic stamp to one of three states. The dispatch domain
|
|
5
|
+
* drives this on each scheduler tick; the orchestrator uses `stale` to surface
|
|
6
|
+
* a warning in the UI and `dead` to reap a subprocess whose event stream has
|
|
7
|
+
* gone quiet for too long.
|
|
8
8
|
*
|
|
9
|
-
* No I/O, no clocks of its own. Callers pass
|
|
10
|
-
* and easy to test.
|
|
9
|
+
* No I/O, no clocks of its own. Callers pass both monotonic values to keep the
|
|
10
|
+
* function pure and easy to test.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
export interface HeartbeatSpec {
|
|
@@ -17,10 +17,34 @@ export interface HeartbeatSpec {
|
|
|
17
17
|
|
|
18
18
|
export type HeartbeatStatus = "alive" | "stale" | "dead";
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Mutable heartbeat facts shared by a transport and the dispatch watchdog.
|
|
22
|
+
* `current` is an absolute wall-clock instant for display and persistence.
|
|
23
|
+
* `monotonic` is from this process's monotonic clock and is the only value
|
|
24
|
+
* liveness code may compare with a later clock read.
|
|
25
|
+
*
|
|
26
|
+
* The monotonic field is optional only for injected legacy test handles. Real
|
|
27
|
+
* transports always provide it. Treating an old handle's `current` as its
|
|
28
|
+
* monotonic stamp preserves the structural test seam without reintroducing a
|
|
29
|
+
* subtraction between two wall-clock reads in the product path.
|
|
30
|
+
*/
|
|
31
|
+
export interface HeartbeatStamp {
|
|
32
|
+
current: number;
|
|
33
|
+
monotonic?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
20
36
|
export const DEFAULT_HEARTBEAT_SPEC: HeartbeatSpec = { windowMs: 5000, graceMs: 10000 };
|
|
21
37
|
|
|
22
|
-
export function
|
|
23
|
-
|
|
38
|
+
export function heartbeatMonotonicAt(heartbeat: HeartbeatStamp): number {
|
|
39
|
+
return heartbeat.monotonic ?? heartbeat.current;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function classifyHeartbeat(
|
|
43
|
+
heartbeatMonotonicAt: number,
|
|
44
|
+
monotonicNow: number,
|
|
45
|
+
spec: HeartbeatSpec,
|
|
46
|
+
): HeartbeatStatus {
|
|
47
|
+
const age = monotonicNow - heartbeatMonotonicAt;
|
|
24
48
|
if (age <= spec.windowMs) return "alive";
|
|
25
49
|
if (age <= spec.windowMs + spec.graceMs) return "stale";
|
|
26
50
|
return "dead";
|
|
@@ -161,12 +161,15 @@ export {
|
|
|
161
161
|
export type {
|
|
162
162
|
WorkspaceSnapshot,
|
|
163
163
|
WriteBoundaryAttribution,
|
|
164
|
+
WriteBoundaryAttributionDowngrade,
|
|
165
|
+
WriteBoundaryAttributionDowngradeReason,
|
|
164
166
|
WriteBoundaryStatus,
|
|
165
167
|
WriteBoundaryVerdict,
|
|
166
168
|
} from "./write-boundary.js";
|
|
167
169
|
export {
|
|
168
170
|
assertWriteBoundaryVisibleToGit,
|
|
169
171
|
captureWorkspaceSnapshot,
|
|
172
|
+
describeWriteBoundaryAttributionDowngrade,
|
|
170
173
|
diffWorkspace,
|
|
171
174
|
enforceWriteBoundary,
|
|
172
175
|
WRITE_BOUNDARY_VIOLATION_REASON,
|
|
@@ -172,6 +172,11 @@ function sealedEnvelopeFor(runId: string): RunEnvelope | null {
|
|
|
172
172
|
* whole orchestrator downtime, which is not a measurement of anything. The
|
|
173
173
|
* last heartbeat is the honest bound; a row that never heartbeat has only its
|
|
174
174
|
* own start, which reads as a zero-length run rather than an invented one.
|
|
175
|
+
*
|
|
176
|
+
* A restart has no shared monotonic origin with the process that owned the
|
|
177
|
+
* worker, so recovery never estimates heartbeat age. It first adjudicates the
|
|
178
|
+
* host-scoped pid and uses the persisted wall-clock heartbeat only as this
|
|
179
|
+
* display/evidence bound after that process is known to be gone.
|
|
175
180
|
*/
|
|
176
181
|
function closeAbandonedRows(ledger: Ledger): { closed: number; sealed: number } {
|
|
177
182
|
let closed = 0;
|