@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
|
@@ -140,8 +140,16 @@ export function coordinateCodewikiWrite(
|
|
|
140
140
|
options: CodewikiCoordinateOptions = {},
|
|
141
141
|
): Promise<CodewikiCoordinatedResult | null> {
|
|
142
142
|
const workspace = resolve(cwd);
|
|
143
|
-
return enqueueWorkspace(workspace, () =>
|
|
144
|
-
|
|
143
|
+
return enqueueWorkspace(workspace, () => {
|
|
144
|
+
// Taking the lease creates the lock's parent, so a project that was never
|
|
145
|
+
// indexed gained an empty `.clio-coder/` from every successful write: the
|
|
146
|
+
// incremental refresh is contractually a no-op there, and it was
|
|
147
|
+
// materializing state before it could say so (issue #248). The check runs
|
|
148
|
+
// inside the queue, so a writer that built the codewiki ahead of this task
|
|
149
|
+
// has already finished and this one sees it. The recheck under the lock
|
|
150
|
+
// stays, and is what still decides the race.
|
|
151
|
+
if (options.requireExisting && !existsSync(codewikiPath(workspace))) return Promise.resolve(null);
|
|
152
|
+
return withStateFileLock(codewikiPath(workspace), async () => {
|
|
145
153
|
if (options.requireExisting && !existsSync(codewikiPath(workspace))) return null;
|
|
146
154
|
const current = options.readCurrent?.(workspace) ?? readCodewiki(workspace);
|
|
147
155
|
const request = await select(current, workspace);
|
|
@@ -152,8 +160,8 @@ export function coordinateCodewikiWrite(
|
|
|
152
160
|
if (wrote) writeCodewiki(workspace, worker.codewiki);
|
|
153
161
|
await options.afterCommit?.(worker, workspace);
|
|
154
162
|
return { codewiki: !worker.changed && current ? current : worker.codewiki, worker, wrote };
|
|
155
|
-
})
|
|
156
|
-
);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
157
165
|
}
|
|
158
166
|
|
|
159
167
|
/** Serialize a non-build artifact transaction such as reset with every writer. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { endpointLabel } from "../providers/endpoint-capacity.js";
|
|
2
3
|
import type { ActionClass } from "../safety/action-classifier.js";
|
|
3
4
|
import type { ScopeSpec } from "../safety/scope.js";
|
|
4
5
|
import { createAdmissionQueue } from "./admission-queue.js";
|
|
@@ -7,6 +8,8 @@ import {
|
|
|
7
8
|
type CapacityLease,
|
|
8
9
|
type CapacityLimits,
|
|
9
10
|
capacityLeaseUsage,
|
|
11
|
+
describeEndpointCapacityHolders,
|
|
12
|
+
type EndpointCapacityHolders,
|
|
10
13
|
heartbeatCapacityLease,
|
|
11
14
|
releaseCapacityLease,
|
|
12
15
|
renameCapacityLeaseAssignment,
|
|
@@ -41,6 +44,7 @@ export interface CapacityAdmissionController {
|
|
|
41
44
|
admit(input: {
|
|
42
45
|
assignmentId: string;
|
|
43
46
|
nodeId: string;
|
|
47
|
+
endpointKey?: string;
|
|
44
48
|
deadlineAt: number;
|
|
45
49
|
priority?: number;
|
|
46
50
|
planId?: string;
|
|
@@ -105,13 +109,31 @@ const PUMP_MAX_MS = 500;
|
|
|
105
109
|
function describeAdmissionFailure(input: {
|
|
106
110
|
state: "canceled" | "timed_out";
|
|
107
111
|
nodeId: string;
|
|
112
|
+
endpointKey?: string;
|
|
108
113
|
waitedMs: number;
|
|
109
114
|
overdueAtQueueMs: number;
|
|
110
115
|
queueDepth: number;
|
|
111
116
|
limits: CapacityLimits;
|
|
112
|
-
usage: {
|
|
117
|
+
usage: {
|
|
118
|
+
global: number;
|
|
119
|
+
nodes: Readonly<Record<string, number>>;
|
|
120
|
+
endpoints: Readonly<Record<string, number>>;
|
|
121
|
+
endpointHolders?: Readonly<Record<string, EndpointCapacityHolders>>;
|
|
122
|
+
};
|
|
113
123
|
}): string {
|
|
114
124
|
if (input.state === "canceled") return "dispatch: admission canceled before a capacity slot opened";
|
|
125
|
+
if (input.endpointKey !== undefined) {
|
|
126
|
+
const endpointLimit = input.limits.endpoints[input.endpointKey];
|
|
127
|
+
const endpointActive = input.usage.endpoints[input.endpointKey] ?? 0;
|
|
128
|
+
if (endpointLimit !== undefined && endpointActive >= endpointLimit) {
|
|
129
|
+
const holders = input.usage.endpointHolders?.[input.endpointKey];
|
|
130
|
+
const occupancy =
|
|
131
|
+
holders === undefined
|
|
132
|
+
? "the supplied capacity snapshot does not attribute the occupied slots"
|
|
133
|
+
: describeEndpointCapacityHolders(holders);
|
|
134
|
+
return `dispatch: admission denied: endpoint '${endpointLabel(input.endpointKey)}' capacity reached (${endpointActive}/${endpointLimit} slots): ${occupancy}; collect in-flight runs or point workers at a second server`;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
115
137
|
const nodeLimit = input.limits.nodes[input.nodeId];
|
|
116
138
|
const nodeActive = input.usage.nodes[input.nodeId] ?? 0;
|
|
117
139
|
const where =
|
|
@@ -142,7 +164,12 @@ function describeAdmissionFailure(input: {
|
|
|
142
164
|
export function createCapacityAdmissionController(options: {
|
|
143
165
|
limits: () => CapacityLimits;
|
|
144
166
|
/** Live lease counts for the timeout message; defaults to the durable state file. */
|
|
145
|
-
usage?: () => {
|
|
167
|
+
usage?: () => {
|
|
168
|
+
global: number;
|
|
169
|
+
nodes: Readonly<Record<string, number>>;
|
|
170
|
+
endpoints: Readonly<Record<string, number>>;
|
|
171
|
+
endpointHolders?: Readonly<Record<string, EndpointCapacityHolders>>;
|
|
172
|
+
};
|
|
146
173
|
now?: () => number;
|
|
147
174
|
maxQueueSize?: number;
|
|
148
175
|
queueCeilingMs?: number;
|
|
@@ -175,7 +202,14 @@ export function createCapacityAdmissionController(options: {
|
|
|
175
202
|
admittedLease = request.value();
|
|
176
203
|
return true;
|
|
177
204
|
} catch (error) {
|
|
178
|
-
//
|
|
205
|
+
// Endpoint saturation is a named refusal because waiting would keep a
|
|
206
|
+
// model request queued behind the same inference scheduler. Existing
|
|
207
|
+
// node and global admission retain their bounded wait behavior.
|
|
208
|
+
if (error instanceof Error && /endpoint '.*' capacity reached/u.test(error.message)) {
|
|
209
|
+
queue.fail(request.requestId, error);
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
// Node and global capacity can wait. Anything else fails the request.
|
|
179
213
|
if (error instanceof Error && /capacity reached/.test(error.message)) return false;
|
|
180
214
|
queue.fail(request.requestId, error instanceof Error ? error : new Error(String(error)));
|
|
181
215
|
return false;
|
|
@@ -229,12 +263,14 @@ export function createCapacityAdmissionController(options: {
|
|
|
229
263
|
memberId: input.reservation.memberId,
|
|
230
264
|
assignmentId: input.assignmentId,
|
|
231
265
|
nodeId: input.nodeId,
|
|
266
|
+
...(input.endpointKey !== undefined ? { endpointKey: input.endpointKey } : {}),
|
|
232
267
|
limits: options.limits(),
|
|
233
268
|
nowMs: now(),
|
|
234
269
|
})
|
|
235
270
|
: acquireCapacityLease({
|
|
236
271
|
assignmentId: input.assignmentId,
|
|
237
272
|
nodeId: input.nodeId,
|
|
273
|
+
...(input.endpointKey !== undefined ? { endpointKey: input.endpointKey } : {}),
|
|
238
274
|
limits: options.limits(),
|
|
239
275
|
nowMs: now(),
|
|
240
276
|
}),
|
|
@@ -246,6 +282,7 @@ export function createCapacityAdmissionController(options: {
|
|
|
246
282
|
describeAdmissionFailure({
|
|
247
283
|
state: outcome.state,
|
|
248
284
|
nodeId: input.nodeId,
|
|
285
|
+
...(input.endpointKey !== undefined ? { endpointKey: input.endpointKey } : {}),
|
|
249
286
|
waitedMs: now() - queuedAt,
|
|
250
287
|
overdueAtQueueMs: queuedAt - input.deadlineAt,
|
|
251
288
|
queueDepth: queue.size(),
|
|
@@ -6,6 +6,7 @@ import { BIRTH_TOKEN_SOURCE_AVAILABLE, processAlive, processBirthToken } from ".
|
|
|
6
6
|
import { FILE_LOCK_ACQUIRE_TIMEOUT_MS, withStateFileLockSync } from "../../core/state-file-lock.js";
|
|
7
7
|
import { clioStateDir } from "../../core/xdg.js";
|
|
8
8
|
import { atomicWrite } from "../../engine/session.js";
|
|
9
|
+
import { endpointLabel, foregroundStreamUsage } from "../providers/endpoint-capacity.js";
|
|
9
10
|
|
|
10
11
|
export { processBirthToken } from "../../core/process-identity.js";
|
|
11
12
|
|
|
@@ -30,6 +31,8 @@ export interface CapacityLease {
|
|
|
30
31
|
leaseId: string;
|
|
31
32
|
assignmentId: string;
|
|
32
33
|
nodeId: string;
|
|
34
|
+
/** Canonical inference endpoint. Absent only on leases written before endpoint capacity existed. */
|
|
35
|
+
endpointKey?: string;
|
|
33
36
|
/** Owner host. Absent only on records written before the field existed; those are read as this host. */
|
|
34
37
|
host?: string;
|
|
35
38
|
ownerPid: number;
|
|
@@ -43,6 +46,7 @@ export interface CapacityLease {
|
|
|
43
46
|
export interface CapacityLimits {
|
|
44
47
|
global: number;
|
|
45
48
|
nodes: Readonly<Record<string, number>>;
|
|
49
|
+
endpoints: Readonly<Record<string, number>>;
|
|
46
50
|
}
|
|
47
51
|
export interface CapacityDrain {
|
|
48
52
|
requestedByPid: number;
|
|
@@ -81,6 +85,7 @@ function valid(value: unknown): value is CapacityLease {
|
|
|
81
85
|
typeof lease.leaseId === "string" &&
|
|
82
86
|
typeof lease.assignmentId === "string" &&
|
|
83
87
|
typeof lease.nodeId === "string" &&
|
|
88
|
+
(lease.endpointKey === undefined || typeof lease.endpointKey === "string") &&
|
|
84
89
|
(lease.host === undefined || typeof lease.host === "string") &&
|
|
85
90
|
Number.isInteger(lease.ownerPid) &&
|
|
86
91
|
typeof lease.processBirthToken === "string" &&
|
|
@@ -182,9 +187,14 @@ const MEMBER_STATUSES = new Set(["held", "consumed", "released"]);
|
|
|
182
187
|
* rejected here as well: silently skipping an unreadable record would under-count
|
|
183
188
|
* capacity and admit past the cap.
|
|
184
189
|
*/
|
|
185
|
-
function heldReservationUsage(values: ReadonlyArray<unknown>): {
|
|
190
|
+
function heldReservationUsage(values: ReadonlyArray<unknown>): {
|
|
191
|
+
global: number;
|
|
192
|
+
nodes: Record<string, number>;
|
|
193
|
+
endpoints: Record<string, number>;
|
|
194
|
+
} {
|
|
186
195
|
let global = 0;
|
|
187
196
|
const nodes: Record<string, number> = {};
|
|
197
|
+
const endpoints: Record<string, number> = {};
|
|
188
198
|
for (const value of values) {
|
|
189
199
|
if (typeof value !== "object" || value === null)
|
|
190
200
|
throw new Error("dispatch capacity store has invalid reservation records");
|
|
@@ -194,10 +204,11 @@ function heldReservationUsage(values: ReadonlyArray<unknown>): { global: number;
|
|
|
194
204
|
if (record.status !== "active") continue;
|
|
195
205
|
const byWave = new Map<number, number>();
|
|
196
206
|
const byNodeWave = new Map<string, Map<number, number>>();
|
|
207
|
+
const byEndpointWave = new Map<string, Map<number, number>>();
|
|
197
208
|
for (const raw of record.members) {
|
|
198
209
|
if (typeof raw !== "object" || raw === null)
|
|
199
210
|
throw new Error("dispatch capacity store has invalid reservation records");
|
|
200
|
-
const member = raw as { status?: unknown; wave?: unknown; nodeId?: unknown };
|
|
211
|
+
const member = raw as { status?: unknown; wave?: unknown; nodeId?: unknown; endpointKey?: unknown };
|
|
201
212
|
if (
|
|
202
213
|
typeof member.status !== "string" ||
|
|
203
214
|
!MEMBER_STATUSES.has(member.status) ||
|
|
@@ -211,13 +222,53 @@ function heldReservationUsage(values: ReadonlyArray<unknown>): { global: number;
|
|
|
211
222
|
const nodeWaves = byNodeWave.get(member.nodeId) ?? new Map<number, number>();
|
|
212
223
|
nodeWaves.set(wave, (nodeWaves.get(wave) ?? 0) + 1);
|
|
213
224
|
byNodeWave.set(member.nodeId, nodeWaves);
|
|
225
|
+
if (member.endpointKey !== undefined && typeof member.endpointKey !== "string")
|
|
226
|
+
throw new Error("dispatch capacity store has invalid reservation records");
|
|
227
|
+
if (typeof member.endpointKey === "string") {
|
|
228
|
+
const endpointWaves = byEndpointWave.get(member.endpointKey) ?? new Map<number, number>();
|
|
229
|
+
endpointWaves.set(wave, (endpointWaves.get(wave) ?? 0) + 1);
|
|
230
|
+
byEndpointWave.set(member.endpointKey, endpointWaves);
|
|
231
|
+
}
|
|
214
232
|
}
|
|
215
233
|
global += Math.max(0, ...byWave.values());
|
|
216
234
|
for (const [id, waves] of byNodeWave) nodes[id] = (nodes[id] ?? 0) + Math.max(0, ...waves.values());
|
|
235
|
+
for (const [key, waves] of byEndpointWave) endpoints[key] = (endpoints[key] ?? 0) + Math.max(0, ...waves.values());
|
|
217
236
|
}
|
|
218
|
-
return { global, nodes };
|
|
237
|
+
return { global, nodes, endpoints };
|
|
219
238
|
}
|
|
220
|
-
|
|
239
|
+
|
|
240
|
+
export interface EndpointCapacityHolders {
|
|
241
|
+
leases: number;
|
|
242
|
+
reservations: number;
|
|
243
|
+
foregroundStreams: number;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Name the endpoint-capacity addends that are actually nonzero. */
|
|
247
|
+
export function describeEndpointCapacityHolders(holders: EndpointCapacityHolders): string {
|
|
248
|
+
const parts: string[] = [];
|
|
249
|
+
const add = (count: number, singular: string, plural: string): void => {
|
|
250
|
+
if (count > 0) parts.push(`${count} ${count === 1 ? singular : plural}`);
|
|
251
|
+
};
|
|
252
|
+
add(holders.leases, "active lease", "active leases");
|
|
253
|
+
add(holders.reservations, "held reservation", "held reservations");
|
|
254
|
+
add(holders.foregroundStreams, "foreground stream", "foreground streams");
|
|
255
|
+
if (parts.length === 0) return "no active lease, held reservation, or foreground stream currently holds a slot";
|
|
256
|
+
const named =
|
|
257
|
+
parts.length === 1
|
|
258
|
+
? parts[0]
|
|
259
|
+
: parts.length === 2
|
|
260
|
+
? `${parts[0]} and ${parts[1]}`
|
|
261
|
+
: `${parts.slice(0, -1).join(", ")}, and ${parts.at(-1)}`;
|
|
262
|
+
const total = holders.leases + holders.reservations + holders.foregroundStreams;
|
|
263
|
+
return `${named} ${total === 1 ? "holds" : "hold"} ${total === 1 ? "the slot" : "the slots"}`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function assertCapacity(
|
|
267
|
+
file: CapacityStateFile,
|
|
268
|
+
nodeId: string,
|
|
269
|
+
endpointKey: string | undefined,
|
|
270
|
+
limits: CapacityLimits,
|
|
271
|
+
): void {
|
|
221
272
|
const held = heldReservationUsage(file.reservations);
|
|
222
273
|
const globalUsed = file.leases.length + held.global;
|
|
223
274
|
// The denial names the compliant next move, not just the gate. A model told
|
|
@@ -235,10 +286,28 @@ function assertCapacity(file: CapacityStateFile, nodeId: string, limits: Capacit
|
|
|
235
286
|
if (used >= nodeLimit)
|
|
236
287
|
throw new Error(`dispatch: admission denied: node '${nodeId}' capacity reached (${used}/${nodeLimit})`);
|
|
237
288
|
}
|
|
289
|
+
if (endpointKey !== undefined) {
|
|
290
|
+
const endpointLimit = limits.endpoints[endpointKey];
|
|
291
|
+
if (endpointLimit !== undefined) {
|
|
292
|
+
if (endpointLimit < 1)
|
|
293
|
+
throw new Error(`dispatch: admission denied: endpoint '${endpointLabel(endpointKey)}' capacity unavailable`);
|
|
294
|
+
const holders: EndpointCapacityHolders = {
|
|
295
|
+
leases: file.leases.filter((lease) => lease.endpointKey === endpointKey).length,
|
|
296
|
+
reservations: held.endpoints[endpointKey] ?? 0,
|
|
297
|
+
foregroundStreams: foregroundStreamUsage()[endpointKey] ?? 0,
|
|
298
|
+
};
|
|
299
|
+
const used = holders.leases + holders.reservations + holders.foregroundStreams;
|
|
300
|
+
if (used >= endpointLimit)
|
|
301
|
+
throw new Error(
|
|
302
|
+
`dispatch: admission denied: endpoint '${endpointLabel(endpointKey)}' capacity reached (${used}/${endpointLimit} slots): ${describeEndpointCapacityHolders(holders)}; collect in-flight runs or point workers at a second server`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
238
306
|
}
|
|
239
307
|
export function acquireCapacityLease(input: {
|
|
240
308
|
assignmentId: string;
|
|
241
309
|
nodeId: string;
|
|
310
|
+
endpointKey?: string;
|
|
242
311
|
limits: CapacityLimits;
|
|
243
312
|
nowMs?: number;
|
|
244
313
|
ttlMs?: number;
|
|
@@ -259,10 +328,12 @@ export function acquireCapacityLease(input: {
|
|
|
259
328
|
);
|
|
260
329
|
const existing = file.leases.find((lease) => lease.assignmentId === input.assignmentId);
|
|
261
330
|
if (existing) {
|
|
262
|
-
if (existing.nodeId !== input.nodeId) {
|
|
331
|
+
if (existing.nodeId !== input.nodeId || existing.endpointKey !== input.endpointKey) {
|
|
263
332
|
const without = { ...file, leases: file.leases.filter((lease) => lease !== existing) };
|
|
264
|
-
assertCapacity(without, input.nodeId, input.limits);
|
|
333
|
+
assertCapacity(without, input.nodeId, input.endpointKey, input.limits);
|
|
265
334
|
existing.nodeId = input.nodeId;
|
|
335
|
+
if (input.endpointKey === undefined) delete existing.endpointKey;
|
|
336
|
+
else existing.endpointKey = input.endpointKey;
|
|
266
337
|
}
|
|
267
338
|
existing.heartbeatAt = new Date(nowMs).toISOString();
|
|
268
339
|
existing.expiresAt = new Date(nowMs + (input.ttlMs ?? DEFAULT_CAPACITY_LEASE_TTL_MS)).toISOString();
|
|
@@ -270,7 +341,7 @@ export function acquireCapacityLease(input: {
|
|
|
270
341
|
return clone(existing);
|
|
271
342
|
}
|
|
272
343
|
input.onAcquiredUnderLock?.(file);
|
|
273
|
-
assertCapacity(file, input.nodeId, input.limits);
|
|
344
|
+
assertCapacity(file, input.nodeId, input.endpointKey, input.limits);
|
|
274
345
|
const token = input.processBirthToken ?? processBirthToken(input.ownerPid ?? process.pid);
|
|
275
346
|
if (!token) throw new Error("dispatch: cannot establish process birth token");
|
|
276
347
|
const at = new Date(nowMs).toISOString();
|
|
@@ -278,6 +349,7 @@ export function acquireCapacityLease(input: {
|
|
|
278
349
|
leaseId: `lease-${nowMs.toString(36)}-${randomBytes(6).toString("hex")}`,
|
|
279
350
|
assignmentId: input.assignmentId,
|
|
280
351
|
nodeId: input.nodeId,
|
|
352
|
+
...(input.endpointKey !== undefined ? { endpointKey: input.endpointKey } : {}),
|
|
281
353
|
host: hostname(),
|
|
282
354
|
ownerPid: input.ownerPid ?? process.pid,
|
|
283
355
|
processBirthToken: token,
|
|
@@ -365,11 +437,28 @@ export function capacityDrain(nowMs = Date.now()): CapacityDrain | null {
|
|
|
365
437
|
export function capacityLeaseUsage(options?: { nowMs?: number; probe?: LeaseOwnerProbe }): {
|
|
366
438
|
global: number;
|
|
367
439
|
nodes: Readonly<Record<string, number>>;
|
|
440
|
+
endpoints: Readonly<Record<string, number>>;
|
|
441
|
+
endpointHolders: Readonly<Record<string, EndpointCapacityHolders>>;
|
|
368
442
|
} {
|
|
369
443
|
const leases = listCapacityLeases(options);
|
|
370
444
|
const nodes: Record<string, number> = {};
|
|
371
|
-
|
|
372
|
-
|
|
445
|
+
const foreground = foregroundStreamUsage();
|
|
446
|
+
const endpoints: Record<string, number> = { ...foreground };
|
|
447
|
+
const endpointHolders: Record<string, EndpointCapacityHolders> = Object.fromEntries(
|
|
448
|
+
Object.entries(foreground).map(([endpointKey, foregroundStreams]) => [
|
|
449
|
+
endpointKey,
|
|
450
|
+
{ leases: 0, reservations: 0, foregroundStreams },
|
|
451
|
+
]),
|
|
452
|
+
);
|
|
453
|
+
for (const lease of leases) {
|
|
454
|
+
nodes[lease.nodeId] = (nodes[lease.nodeId] ?? 0) + 1;
|
|
455
|
+
if (lease.endpointKey !== undefined) {
|
|
456
|
+
endpoints[lease.endpointKey] = (endpoints[lease.endpointKey] ?? 0) + 1;
|
|
457
|
+
const holders = endpointHolders[lease.endpointKey] ?? { leases: 0, reservations: 0, foregroundStreams: 0 };
|
|
458
|
+
endpointHolders[lease.endpointKey] = { ...holders, leases: holders.leases + 1 };
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return { global: leases.length, nodes, endpoints, endpointHolders };
|
|
373
462
|
}
|
|
374
463
|
|
|
375
464
|
/**
|
|
@@ -13,6 +13,7 @@ import type { ApprovedAssignmentRoute } from "./route-approval.js";
|
|
|
13
13
|
import type { RouteDecisionV1 } from "./route-decision.js";
|
|
14
14
|
import type { RunEnvelope, RunLineage, RunNodeIdentity, RunPhaseDurations, RunReceipt, RunStatus } from "./types.js";
|
|
15
15
|
import type { DispatchFailoverCandidate, JobSpec } from "./validation.js";
|
|
16
|
+
import type { WriteBoundaryAttribution } from "./write-boundary.js";
|
|
16
17
|
|
|
17
18
|
export interface DispatchRequest extends JobSpec {
|
|
18
19
|
/** Coordinator-owned result contract override for a fleet plan step. */
|
|
@@ -93,6 +94,8 @@ export interface DispatchPlanTaskResolution {
|
|
|
93
94
|
targetId: string;
|
|
94
95
|
wireModelId: string;
|
|
95
96
|
runtimeId: string;
|
|
97
|
+
/** Canonical inference scheduler identity and its independent request-slot bound. */
|
|
98
|
+
endpoint?: { key: string; label: string; limit: number };
|
|
96
99
|
node: RunNodeIdentity;
|
|
97
100
|
/** Effective thinking control for this route; null when the runtime has none. */
|
|
98
101
|
thinkingLevel: string | null;
|
|
@@ -209,6 +212,7 @@ export interface DispatchContract {
|
|
|
209
212
|
topology: ReservationTopology;
|
|
210
213
|
tasks: ReadonlyArray<{ memberId: string; wave: number; resolution: DispatchPlanTaskResolution }>;
|
|
211
214
|
}): DispatchReservationRecord;
|
|
215
|
+
release(ownerId: string): DispatchReservationRecord | null;
|
|
212
216
|
rollback(ownerId: string): DispatchReservationRecord | null;
|
|
213
217
|
rollbackUnconsumed(ownerId: string): DispatchReservationRecord | null;
|
|
214
218
|
get(ownerId: string): DispatchReservationRecord | null;
|
|
@@ -266,6 +270,13 @@ export interface DispatchContract {
|
|
|
266
270
|
*/
|
|
267
271
|
observedRunWrites?(runId: string): ReadonlyArray<string> | null;
|
|
268
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Full write-record witness, including durable downgrade causes. New callers
|
|
275
|
+
* use this surface; observedRunWrites remains as the closed-list compatibility
|
|
276
|
+
* view for integrations that only know paths or null.
|
|
277
|
+
*/
|
|
278
|
+
observedRunWriteAttribution?(runId: string): WriteBoundaryAttribution | null;
|
|
279
|
+
|
|
269
280
|
/** In-memory logical assignment state and complete finalized-attempt history. */
|
|
270
281
|
readonly assignments?: {
|
|
271
282
|
/** Live assignment, including its terminal promise and detailed attempt refs. */
|
|
@@ -59,6 +59,8 @@ export interface ExecutionPlanAgentStep {
|
|
|
59
59
|
/** Exact per-step route defaults from a version 5 fleet contract. */
|
|
60
60
|
target?: string;
|
|
61
61
|
profile?: string;
|
|
62
|
+
/** Resolved inference scheduler bound used when waves are packed. */
|
|
63
|
+
endpoint?: { key: string; limit: number; foregroundHeld?: number };
|
|
62
64
|
/** Version 5 gate authoring and baseline execution facts. */
|
|
63
65
|
gate?: { path: string; commandId: string };
|
|
64
66
|
/** Version 5 dynamic planning facts. */
|
|
@@ -163,7 +165,12 @@ function canonicalSteps(steps: ReadonlyArray<ExecutionPlanStepInput>): Execution
|
|
|
163
165
|
}
|
|
164
166
|
|
|
165
167
|
export function executionPlanWaves(
|
|
166
|
-
steps: ReadonlyArray<{
|
|
168
|
+
steps: ReadonlyArray<{
|
|
169
|
+
id: string;
|
|
170
|
+
dependencies: ReadonlyArray<string>;
|
|
171
|
+
kind?: "agent" | "code";
|
|
172
|
+
endpoint?: { key: string; limit: number; foregroundHeld?: number };
|
|
173
|
+
}>,
|
|
167
174
|
maxWorkers: number,
|
|
168
175
|
): string[][] {
|
|
169
176
|
if (!Number.isInteger(maxWorkers) || maxWorkers < 1)
|
|
@@ -191,11 +198,24 @@ export function executionPlanWaves(
|
|
|
191
198
|
// capacity lease, so they ride the first chunk of their ready set without
|
|
192
199
|
// displacing an agent step or forcing an extra wave.
|
|
193
200
|
const codeReady = ready.filter((step) => step.kind === "code").map((step) => step.id);
|
|
194
|
-
const agentReady = ready.filter((step) => step.kind !== "code")
|
|
201
|
+
const agentReady = ready.filter((step) => step.kind !== "code");
|
|
195
202
|
const chunks: string[][] = [];
|
|
196
|
-
|
|
197
|
-
|
|
203
|
+
let chunk: string[] = [];
|
|
204
|
+
let endpointCounts = new Map<string, number>();
|
|
205
|
+
for (const step of agentReady) {
|
|
206
|
+
const endpoint = step.endpoint;
|
|
207
|
+
const endpointLimit =
|
|
208
|
+
endpoint === undefined ? Number.POSITIVE_INFINITY : Math.max(1, endpoint.limit - (endpoint.foregroundHeld ?? 0));
|
|
209
|
+
const endpointFull = endpoint !== undefined && (endpointCounts.get(endpoint.key) ?? 0) >= endpointLimit;
|
|
210
|
+
if (chunk.length >= maxWorkers || endpointFull) {
|
|
211
|
+
chunks.push(chunk);
|
|
212
|
+
chunk = [];
|
|
213
|
+
endpointCounts = new Map<string, number>();
|
|
214
|
+
}
|
|
215
|
+
chunk.push(step.id);
|
|
216
|
+
if (endpoint !== undefined) endpointCounts.set(endpoint.key, (endpointCounts.get(endpoint.key) ?? 0) + 1);
|
|
198
217
|
}
|
|
218
|
+
if (chunk.length > 0) chunks.push(chunk);
|
|
199
219
|
if (chunks.length === 0) chunks.push([]);
|
|
200
220
|
chunks[0] = [...codeReady, ...(chunks[0] ?? [])];
|
|
201
221
|
for (const wave of chunks) {
|
|
@@ -317,6 +337,26 @@ export function compileExecutionPlan(input: ExecutionPlanInput): ExecutionPlan {
|
|
|
317
337
|
return { ...canonical, hash: createHash("sha256").update(JSON.stringify(canonical)).digest("hex") };
|
|
318
338
|
}
|
|
319
339
|
|
|
340
|
+
/** Recompile one approved graph after route preflight supplies endpoint bounds. */
|
|
341
|
+
export function bindExecutionPlanEndpoints(
|
|
342
|
+
plan: ExecutionPlan,
|
|
343
|
+
bindings: Readonly<Record<string, { key: string; limit: number; foregroundHeld?: number } | undefined>>,
|
|
344
|
+
): ExecutionPlan {
|
|
345
|
+
return compileExecutionPlan({
|
|
346
|
+
topology: plan.topology,
|
|
347
|
+
rootTask: plan.rootTask,
|
|
348
|
+
maxWorkers: plan.maxWorkers,
|
|
349
|
+
...(plan.writers === 1 ? { writers: 1 as const } : {}),
|
|
350
|
+
onFailure: plan.onFailure,
|
|
351
|
+
steps: plan.steps.map((step) => {
|
|
352
|
+
if (step.kind === "code") return step;
|
|
353
|
+
const endpoint = bindings[step.id];
|
|
354
|
+
return endpoint === undefined ? step : { ...step, endpoint: { ...endpoint } };
|
|
355
|
+
}),
|
|
356
|
+
loops: plan.loops,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
320
360
|
/** Transitive dependency closure of one plan step. */
|
|
321
361
|
export function executionPlanAncestors(plan: ExecutionPlan, stepId: string): ReadonlySet<string> {
|
|
322
362
|
const byId = new Map(plan.steps.map((step) => [step.id, step]));
|