@iowarp/clio-coder 0.3.8 → 0.4.0
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 +102 -0
- package/NOTICE +33 -0
- package/README.md +12 -3
- package/dist/{acp-U67UHUK2.js → acp-G5WJBNCT.js} +13 -12
- package/dist/{agents-YU6SGALZ.js → agents-FMV2Q5G4.js} +41 -36
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-5ZPJOIVG.js → auth-3IDSJEIK.js} +18 -18
- package/dist/{builtins-C6JMZVV6.js → builtins-XCZWXSC7.js} +5 -5
- package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
- package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
- package/dist/{chunk-XWSF374K.js → chunk-2OQE55CK.js} +3 -3
- package/dist/{chunk-5H3GB5BO.js → chunk-32KWKNSF.js} +8 -384
- package/dist/{chunk-KTYTFRMB.js → chunk-36EJLSQQ.js} +34 -36
- package/dist/{chunk-FHJEP5SW.js → chunk-3BT2XMV4.js} +19 -13
- package/dist/chunk-3DPEIQKN.js +113 -0
- package/dist/{chunk-GU2UIAFZ.js → chunk-3URVFKWK.js} +7 -7
- package/dist/{chunk-WEH5XRJQ.js → chunk-3XML7CDN.js} +3 -3
- package/dist/chunk-42FMPA75.js +101 -0
- package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
- package/dist/{chunk-VCBR6CU7.js → chunk-5PSMVOLM.js} +2 -2
- package/dist/{chunk-NMPKI6XL.js → chunk-6DB53AJS.js} +203 -25
- package/dist/{chunk-3BINW3FP.js → chunk-76ONBSIA.js} +2 -2
- package/dist/{chunk-26LEYJZH.js → chunk-7MCTRUCE.js} +2 -2
- package/dist/{chunk-TYPGUK6W.js → chunk-AB44T6BB.js} +111 -5
- package/dist/{chunk-K4XHGFR5.js → chunk-B7OBL7PK.js} +317 -721
- package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
- package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
- package/dist/{chunk-ZVJ5BLO2.js → chunk-BKFJHQCA.js} +154 -16
- package/dist/chunk-BKFM6EJV.js +462 -0
- package/dist/chunk-BMS5RKQY.js +27 -0
- package/dist/{chunk-ZNLWCMVZ.js → chunk-BPKCPIL7.js} +2 -2
- package/dist/{chunk-TLQJPP24.js → chunk-BUMFYQFY.js} +1394 -1349
- package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
- package/dist/chunk-C2LTL2W6.js +2447 -0
- package/dist/{chunk-ME6CCNFO.js → chunk-CODPRO7Q.js} +8 -8
- package/dist/{chunk-E77JEWSD.js → chunk-CTJ4RNAA.js} +7 -37
- package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
- package/dist/{chunk-7RFXX52T.js → chunk-DQITNCXG.js} +642 -172
- package/dist/{chunk-TTHACPOM.js → chunk-DYJP44XW.js} +578 -117
- package/dist/{chunk-2HFZQUHL.js → chunk-F4CKPOEQ.js} +18 -8
- package/dist/{chunk-DGSYXYMX.js → chunk-FEFIFZTL.js} +3 -3
- package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
- package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
- package/dist/chunk-GKF55TAZ.js +391 -0
- package/dist/{chunk-VWZOAB7K.js → chunk-GR5G2PVF.js} +9 -8
- package/dist/{chunk-4DGYLA73.js → chunk-GXNLGKAB.js} +80 -9
- package/dist/chunk-HHV2GANA.js +88 -0
- package/dist/{chunk-IIZWH4XA.js → chunk-HI63TFOG.js} +5 -4
- package/dist/{chunk-WLFILSD5.js → chunk-HJJTYUHX.js} +113 -83
- package/dist/{chunk-TT36MB5S.js → chunk-HLW2MRKE.js} +3 -1
- package/dist/{chunk-PMDBGQSJ.js → chunk-HWHKMHUA.js} +7 -7
- package/dist/chunk-HZHHCK24.js +1631 -0
- package/dist/{chunk-WSB3FPX7.js → chunk-I5VEOC6I.js} +39 -143
- package/dist/chunk-IBEBSCYA.js +564 -0
- package/dist/chunk-IQ7KR472.js +362 -0
- package/dist/{chunk-A3WNZD3P.js → chunk-J4W7KFM7.js} +949 -972
- package/dist/{chunk-TB5666IT.js → chunk-JDG2WCRO.js} +5 -5
- package/dist/{chunk-N22QMJKY.js → chunk-K5C3NCBD.js} +4 -4
- package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
- package/dist/{chunk-5C3AQNDW.js → chunk-KFZI4NIL.js} +216 -38
- package/dist/chunk-KMVISBZR.js +132 -0
- package/dist/{chunk-WXY7KU3G.js → chunk-LDQ2ZF2M.js} +2 -2
- package/dist/{chunk-XN3L4EYL.js → chunk-LQ3DZAMX.js} +3 -3
- package/dist/{chunk-U6MBIEMB.js → chunk-LY4S7GJC.js} +173 -144
- package/dist/{chunk-4SPRNWDE.js → chunk-MLKNTWH2.js} +19 -19
- package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
- package/dist/chunk-NEKRRTYW.js +56 -0
- package/dist/{chunk-SPULKLCF.js → chunk-NHCZP4K7.js} +3 -3
- package/dist/chunk-NHLBIGRH.js +1506 -0
- package/dist/chunk-NQQH3YT7.js +302 -0
- package/dist/chunk-NYS75XW5.js +15 -0
- package/dist/{chunk-GN57SG4G.js → chunk-O4XIVISU.js} +10 -8
- package/dist/{chunk-EMYUUSFG.js → chunk-O6TL7WWY.js} +6 -6
- package/dist/chunk-OQBA45DZ.js +97 -0
- package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
- package/dist/chunk-PMZCIOCJ.js +25 -0
- package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
- package/dist/{chunk-J3YUBZWY.js → chunk-QBJA7R7N.js} +62 -6
- package/dist/chunk-QDC3K2U3.js +262 -0
- package/dist/{chunk-2HEJ2F35.js → chunk-QLFS5GO2.js} +22 -10
- package/dist/{chunk-7RGZWPB6.js → chunk-QLL7ILRG.js} +95 -32
- package/dist/{chunk-YS5VLNH5.js → chunk-QREDIESB.js} +6 -6
- package/dist/chunk-QSNYB6ZV.js +195 -0
- package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
- package/dist/chunk-RKKLTLYB.js +45 -0
- package/dist/{chunk-P43ETTHK.js → chunk-SJ5ZKQ4S.js} +2 -2
- package/dist/{chunk-GPIEI3LY.js → chunk-SP2RXXYO.js} +6 -54
- package/dist/chunk-SUCTJL45.js +45 -0
- package/dist/{chunk-DYIM5TJT.js → chunk-SUW5DORT.js} +263 -7
- package/dist/chunk-T56WDKA5.js +183 -0
- package/dist/chunk-TVHHYFHE.js +255 -0
- package/dist/{chunk-FJ3H4MN5.js → chunk-TZ3SGWZZ.js} +3 -3
- package/dist/{chunk-MXKJU4JB.js → chunk-U77AMWDL.js} +91 -10
- package/dist/{chunk-5DHKRSMQ.js → chunk-ULC6OTWO.js} +11 -7
- package/dist/{chunk-RWSI4YD7.js → chunk-UM7N4G5A.js} +33 -12
- package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
- package/dist/{chunk-IGWKHNIQ.js → chunk-UXMFQ54G.js} +44 -37
- package/dist/{chunk-HFSBBKSQ.js → chunk-V5DHCITQ.js} +171 -3
- package/dist/{chunk-5WIGXA4T.js → chunk-VAZSBTKF.js} +111 -4
- package/dist/{chunk-VHN4MY6O.js → chunk-VEO4AP2K.js} +2 -2
- package/dist/{chunk-IJ7RPIYJ.js → chunk-VFA6GDY5.js} +65 -4
- package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
- package/dist/chunk-VO67MWHC.js +75 -0
- package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
- package/dist/{chunk-TANS5ZJS.js → chunk-VYMXRQI6.js} +36 -22
- package/dist/{chunk-WWCZ5F23.js → chunk-W5VSYASO.js} +77 -16
- package/dist/{chunk-WNIJTQQK.js → chunk-WZR7K7ZX.js} +72 -116
- package/dist/{chunk-5Q2VVUKB.js → chunk-X3YGUTOB.js} +4 -4
- package/dist/chunk-X75E3D2N.js +686 -0
- package/dist/{chunk-VAWNZU7Z.js → chunk-YDFRH54B.js} +4 -4
- package/dist/chunk-YJX4SHTD.js +40 -0
- package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
- package/dist/chunk-Z2RR6MAK.js +127 -0
- package/dist/{chunk-FBVTI2TJ.js → chunk-Z4TXYIEG.js} +12 -131
- package/dist/cli/index.js +47 -36
- package/dist/{clio-QVTYJ57A.js → clio-2JXHBBY5.js} +7 -7
- package/dist/{code-nav-FGGFIE7L.js → code-nav-3YYRMYNF.js} +8 -8
- package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
- package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
- package/dist/{config-LW5IJFQN.js → config-QZPCMYSO.js} +99 -63
- package/dist/{configure-7XIZCOU4.js → configure-TEGEBYCA.js} +23 -22
- package/dist/{context-Y6Y7QPR6.js → context-AV7OEZ4D.js} +12 -12
- package/dist/{context-L3WL3X7K.js → context-E6H5RNMC.js} +56 -47
- package/dist/{context-N52ZA626.js → context-GSXUE4CT.js} +29 -27
- package/dist/{context-clear-MBQRLSDQ.js → context-clear-SHIBYK6T.js} +55 -46
- package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
- package/dist/{context-working-set-GS6DSO7F.js → context-working-set-5ZGKPGZQ.js} +13 -13
- package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-EFMJT4LD.js} +93 -64
- package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
- package/dist/doctor-QOA5FNY5.js +313 -0
- package/dist/{eval-BEC2WHDA.js → eval-TFBYQH4H.js} +2032 -156
- package/dist/eval-inventory-SXH7PDKX.js +316 -0
- package/dist/{evidence-REJUMSKM.js → evidence-ERGESKGN.js} +203 -48
- package/dist/{evolve-PY5ZBA5K.js → evolve-VDXTSYCJ.js} +52 -43
- package/dist/{extensions-HVKU65YU.js → extensions-7BGBHN57.js} +13 -7
- package/dist/{fleet-7WZEWRFA.js → fleet-2RRVDF2V.js} +228 -108
- package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-VJ726XIA.js} +11 -11
- package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
- package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-JF5QOATM.js} +18 -15
- package/dist/fleet-inspect-VLY4S7QM.js +442 -0
- package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-AIZUEJOY.js} +5 -5
- package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-AJRPDMDV.js} +22 -19
- package/dist/fleet-verify-JFEL2L3H.js +175 -0
- package/dist/fleet-view-ZCON35AG.js +102 -0
- package/dist/{init-OG3TPGQG.js → init-DN2WWLFE.js} +72 -62
- package/dist/install-XGLBQY5E.js +13 -0
- package/dist/interop-OZBKXAYL.js +114 -0
- package/dist/{library-CNTMPLRF.js → library-YWZG7IMW.js} +21 -18
- package/dist/{memory-6IS7F275.js → memory-I4C4HMLW.js} +54 -45
- package/dist/{models-ENRJDA5W.js → models-CEYXJBO6.js} +33 -30
- package/dist/{monitor-XLDVO7TN.js → monitor-NZ6GCI3P.js} +59 -52
- package/dist/{orchestrator-6KSPYRHA.js → orchestrator-GCGQ4N5I.js} +7827 -7328
- package/dist/panes-HMABYVO4.js +58 -0
- package/dist/panes-KY6W3V2E.js +103 -0
- package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
- package/dist/{reset-RZ4ER727.js → reset-DQ6FGCSH.js} +13 -11
- package/dist/resources-BB3MVJMD.js +111 -0
- package/dist/{run-Y2CNK5RU.js → run-H2GQDUER.js} +119 -87
- package/dist/{share-A55GYP6Z.js → share-GTJN6A5O.js} +20 -17
- package/dist/{skills-ALC5J6AT.js → skills-L55TEW6R.js} +33 -24
- package/dist/{skills-eval-JPBEBYQU.js → skills-eval-XVXPH2JI.js} +67 -56
- package/dist/skills-inventory-S4MXPJFV.js +126 -0
- package/dist/slash-commands-ZSGASKJC.js +77 -0
- package/dist/{steer-GGWFUJUD.js → steer-RZGSCY4R.js} +4 -4
- package/dist/{support-MIETYA5E.js → support-PKEUNNQL.js} +6 -6
- package/dist/{targets-VGNXIR3S.js → targets-NCPZ644J.js} +68 -40
- package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-44SV3YCN.js} +6 -4
- package/dist/tools-DAF3DI3C.js +27 -0
- package/dist/{trace-PNCASAXC.js → trace-FYVW2MQA.js} +207 -10
- package/dist/tui-primitives-2AKXQNZK.js +13 -0
- package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
- package/dist/{upgrade-FUSUAGHR.js → upgrade-3XPP6OQL.js} +27 -24
- package/dist/{usage-N4MKVHKD.js → usage-3NLHGTU2.js} +114 -62
- package/dist/{verifiers-YAWOJ3H2.js → verifiers-SSQONKRT.js} +172 -13
- package/dist/{verify-LTDHYBGY.js → verify-3U6J7FZI.js} +10 -10
- package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
- package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-CEHYGPGQ.js} +75 -65
- package/dist/with-panes-MKB46MPQ.js +782 -0
- package/dist/worker/entry.js +106 -75
- package/docs/README.md +3 -2
- package/docs/acp.md +24 -3
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +9 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +62 -3
- package/docs/commands-and-modes.md +31 -2
- package/docs/configuration-and-targets.md +69 -12
- package/docs/context-engine.md +63 -4
- package/docs/development-pipeline.md +19 -0
- package/docs/dispatch-typed-intent.md +385 -0
- package/docs/documentation-coverage.md +5 -5
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +3 -0
- package/docs/eval-runner.md +262 -11
- package/docs/evals-internal.md +72 -2
- package/docs/evidence-and-memory.md +12 -11
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +27 -1
- package/docs/fleet-dispatch.md +25 -4
- package/docs/installation-and-lifecycle.md +15 -2
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +10 -1
- package/docs/observability.md +55 -4
- 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 +2 -2
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +18 -8
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +88 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +5 -2
- package/src/cli/acp.ts +6 -2
- package/src/cli/agents.ts +1 -1
- package/src/cli/argv.ts +25 -0
- package/src/cli/config-inspect.ts +33 -6
- package/src/cli/config.ts +1 -1
- package/src/cli/configure.ts +23 -21
- package/src/cli/doctor-panes.ts +124 -0
- package/src/cli/doctor-state-size.ts +82 -0
- package/src/cli/doctor-toolchain.ts +57 -0
- package/src/cli/doctor.ts +22 -1
- package/src/cli/eval-inventory.ts +436 -0
- package/src/cli/eval.ts +93 -16
- package/src/cli/evidence-detail.ts +88 -0
- package/src/cli/evidence-inventory.ts +183 -0
- package/src/cli/evidence.ts +30 -5
- package/src/cli/extensions.ts +5 -1
- package/src/cli/fleet-decisions.ts +69 -0
- package/src/cli/fleet-inspect.ts +334 -0
- package/src/cli/fleet-verify.ts +133 -0
- package/src/cli/fleet-view.ts +810 -0
- package/src/cli/fleet.ts +179 -39
- package/src/cli/index.ts +14 -2
- package/src/cli/interop-inspect.ts +128 -0
- package/src/cli/interop.ts +34 -0
- package/src/cli/panes.ts +35 -0
- package/src/cli/reset.ts +5 -2
- package/src/cli/run.ts +58 -0
- package/src/cli/skills-inventory.ts +185 -0
- package/src/cli/skills.ts +16 -13
- package/src/cli/targets.ts +44 -13
- package/src/cli/tools.ts +321 -0
- package/src/cli/trace-inspect.ts +252 -0
- package/src/cli/trace.ts +85 -5
- package/src/cli/usage.ts +63 -14
- package/src/cli/verifiers-inspect.ts +347 -0
- package/src/cli/verifiers.ts +9 -0
- package/src/core/bus-events.ts +33 -1
- package/src/core/cache-telemetry.ts +42 -0
- package/src/core/config.ts +67 -0
- package/src/core/defaults.ts +124 -8
- package/src/core/endpoint-key.ts +27 -0
- package/src/core/residency-target-key.ts +25 -0
- package/src/core/response-schema.ts +80 -6
- package/src/core/theme-token-hex.ts +43 -0
- package/src/core/tool-names.ts +2 -1
- package/src/core/xdg.ts +1 -1
- package/src/domains/agents/fleets/build-review.md +0 -3
- package/src/domains/agents/fleets/build-test.md +0 -3
- package/src/domains/agents/result-contract-filesystem.ts +32 -0
- package/src/domains/agents/result-contract.ts +164 -35
- package/src/domains/config/classify.ts +6 -0
- package/src/domains/context/codewiki/coordinator.ts +12 -4
- package/src/domains/dispatch/admission-error.ts +9 -0
- package/src/domains/dispatch/admission.ts +52 -14
- package/src/domains/dispatch/capacity-lease.ts +118 -9
- package/src/domains/dispatch/contract.ts +11 -0
- package/src/domains/dispatch/council-topology.ts +398 -0
- package/src/domains/dispatch/execution-plan.ts +44 -4
- package/src/domains/dispatch/extension.ts +378 -82
- package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
- package/src/domains/dispatch/fleet-plan.ts +7 -2
- package/src/domains/dispatch/fleet-run.ts +87 -4
- package/src/domains/dispatch/gate-decisions.ts +11 -1
- package/src/domains/dispatch/gate-role-prompts.ts +9 -0
- package/src/domains/dispatch/gate-topology.ts +289 -0
- package/src/domains/dispatch/heartbeat.ts +32 -8
- package/src/domains/dispatch/index.ts +22 -0
- package/src/domains/dispatch/intent-compatibility.ts +330 -0
- package/src/domains/dispatch/intent.ts +85 -1
- package/src/domains/dispatch/orphan-recovery.ts +5 -0
- package/src/domains/dispatch/reservation-store.ts +139 -11
- package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
- package/src/domains/dispatch/run-event-journal.ts +598 -0
- package/src/domains/dispatch/state.ts +49 -1
- package/src/domains/dispatch/types.ts +13 -0
- package/src/domains/dispatch/validation.ts +33 -8
- 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 +342 -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/inventory.ts +113 -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 +105 -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/store.ts +6 -0
- 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 -1
- package/src/domains/memory/task-bank-promotion.ts +64 -0
- package/src/domains/memory/task-memory-policy.ts +82 -17
- 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 +3 -0
- package/src/domains/middleware/index.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +97 -21
- package/src/domains/middleware/memory-step-endpoint.ts +71 -0
- package/src/domains/mux/contract.ts +434 -0
- package/src/domains/mux/detect.ts +158 -0
- package/src/domains/mux/extension.ts +47 -0
- package/src/domains/mux/index.ts +96 -0
- package/src/domains/mux/manifest.ts +6 -0
- package/src/domains/mux/operations.ts +164 -0
- package/src/domains/mux/pane-registry.ts +90 -0
- package/src/domains/mux/protocol.ts +49 -0
- package/src/domains/mux/socket-client.ts +816 -0
- package/src/domains/mux/types.ts +222 -0
- package/src/domains/mux/viewer-command.ts +59 -0
- package/src/domains/mux/yazi/assets/init.lua +2 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
- package/src/domains/mux/yazi/assets/yazi.toml +17 -0
- package/src/domains/mux/yazi/event-stream.ts +180 -0
- package/src/domains/mux/yazi/profile.ts +299 -0
- package/src/domains/mux/yazi/session.ts +228 -0
- package/src/domains/mux/yazi/theme.ts +30 -0
- package/src/domains/observability/background-memory-usage.ts +140 -0
- package/src/domains/observability/cost.ts +22 -1
- package/src/domains/observability/index.ts +9 -0
- package/src/domains/observability/out-of-turn-usage.ts +51 -2
- package/src/domains/observability/trace-store.ts +234 -2
- package/src/domains/prompts/compiler.ts +100 -13
- package/src/domains/providers/endpoint-capacity.ts +228 -0
- package/src/domains/providers/endpoint-slots-store.ts +189 -0
- package/src/domains/providers/extension.ts +20 -3
- package/src/domains/providers/index.ts +32 -0
- package/src/domains/providers/model-runtime-capabilities.ts +32 -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/boot-manifest.ts +1 -0
- package/src/domains/providers/runtimes/builtins.ts +2 -0
- 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/runtimes/protocol/litellm.ts +375 -0
- package/src/domains/providers/support.ts +1 -0
- package/src/domains/providers/target-model-cache.ts +124 -0
- package/src/domains/providers/types/capability-flags.ts +2 -0
- package/src/domains/providers/types/target-descriptor.ts +2 -0
- package/src/domains/resources/index.ts +3 -0
- package/src/domains/resources/prompts/loader.ts +95 -33
- package/src/domains/resources/skills/loader.ts +33 -0
- package/src/domains/safety/action-classifier.ts +6 -0
- 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/domains/toolchain/archive.ts +175 -0
- package/src/domains/toolchain/contract.ts +28 -0
- package/src/domains/toolchain/extension.ts +47 -0
- package/src/domains/toolchain/index.ts +39 -0
- package/src/domains/toolchain/install.ts +327 -0
- package/src/domains/toolchain/manifest.ts +8 -0
- package/src/domains/toolchain/paths.ts +34 -0
- package/src/domains/toolchain/registry.ts +265 -0
- package/src/domains/toolchain/remove.ts +218 -0
- package/src/domains/toolchain/resolve.ts +182 -0
- package/src/domains/toolchain/types.ts +113 -0
- package/src/domains/toolchain/version.ts +88 -0
- package/src/engine/acp/adapter.ts +18 -3
- package/src/engine/acp/server.ts +413 -70
- package/src/engine/acp/types.ts +19 -1
- package/src/engine/ai.ts +35 -0
- package/src/engine/apis/llamacpp-residency.ts +55 -3
- package/src/engine/apis/lmstudio.ts +25 -5
- package/src/engine/apis/ollama-native.ts +2 -1
- package/src/engine/apis/openai-completions.ts +80 -17
- package/src/engine/apis/residency-lock.ts +3 -1
- package/src/engine/apis/residency.ts +34 -1
- package/src/engine/claude/sdk-module.ts +98 -0
- package/src/engine/claude/sdk-runtime.ts +19 -11
- package/src/engine/provider-payload.ts +29 -1
- package/src/engine/tui-primitives.ts +21 -0
- package/src/engine/tui.ts +1 -0
- package/src/engine/worker-runtime.ts +2 -13
- package/src/entry/boot-options.ts +2 -0
- package/src/entry/orchestrator.ts +279 -35
- package/src/entry/panes-activation.ts +31 -0
- package/src/entry/with-panes.ts +20 -0
- 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 +41 -10
- package/src/interactive/cost-overlay.ts +66 -6
- package/src/interactive/council-grid.ts +1 -3
- package/src/interactive/council.ts +11 -0
- package/src/interactive/dispatch-board.ts +102 -20
- package/src/interactive/fleet-run-preview.ts +41 -15
- package/src/interactive/handoff-round.ts +41 -2
- package/src/interactive/interactive-application.ts +177 -7
- package/src/interactive/interactive-input-runtime.ts +15 -0
- package/src/interactive/interactive-presentation.ts +4 -0
- package/src/interactive/interactive-shell.ts +20 -17
- package/src/interactive/interactive-slash-runtime.ts +63 -10
- package/src/interactive/memory-overlay.ts +9 -0
- package/src/interactive/modal-marker.ts +170 -0
- package/src/interactive/mutation-preview.ts +295 -0
- package/src/interactive/mux-bridge.ts +214 -0
- package/src/interactive/overlay-frame.ts +58 -2
- package/src/interactive/overlay-general-openers.ts +17 -0
- package/src/interactive/overlay-key-routing.ts +52 -3
- package/src/interactive/overlay-lifecycle.ts +56 -7
- package/src/interactive/overlay-model-selectors.ts +40 -3
- package/src/interactive/overlay-permission-lifecycle.ts +112 -24
- package/src/interactive/overlay-session-lifecycle.ts +73 -9
- package/src/interactive/overlay-transitions.ts +18 -4
- package/src/interactive/overlays/agents.ts +1 -0
- package/src/interactive/overlays/ask-user.ts +227 -49
- package/src/interactive/overlays/auth-dialog.ts +1 -0
- package/src/interactive/overlays/context-reset.ts +1 -0
- package/src/interactive/overlays/cwd-fallback.ts +1 -0
- package/src/interactive/overlays/decisions.ts +11 -11
- package/src/interactive/overlays/extensions.ts +1 -0
- package/src/interactive/overlays/fleet-run-approval.ts +1 -0
- package/src/interactive/overlays/handoff-review.ts +1 -0
- package/src/interactive/overlays/help-reference.ts +6 -0
- package/src/interactive/overlays/interop.ts +1 -0
- package/src/interactive/overlays/library-install-confirm.ts +1 -0
- package/src/interactive/overlays/library-tabs.ts +28 -0
- package/src/interactive/overlays/list-overlay.ts +10 -1
- package/src/interactive/overlays/message-picker.ts +1 -0
- package/src/interactive/overlays/model-scope.ts +86 -0
- package/src/interactive/overlays/model-selector.ts +1 -0
- package/src/interactive/overlays/prompts.ts +12 -1
- package/src/interactive/overlays/session-selector.ts +1 -0
- package/src/interactive/overlays/settings-sections.ts +30 -0
- package/src/interactive/overlays/settings.ts +614 -45
- package/src/interactive/overlays/side-question.ts +1 -0
- package/src/interactive/overlays/skills-hub.ts +3 -11
- package/src/interactive/overlays/tree-selector.ts +1 -0
- package/src/interactive/pane-policy.ts +46 -0
- package/src/interactive/panes-runtime.ts +292 -0
- 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/compaction-summary.ts +29 -0
- package/src/interactive/renderers/tool-execution.ts +4 -0
- package/src/interactive/renderers/worker-entry.ts +122 -14
- package/src/interactive/side-question.ts +58 -1
- package/src/interactive/slash-commands.ts +251 -15
- 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/tasks-overlay.ts +1 -0
- package/src/interactive/terminal-lease.ts +2 -0
- package/src/interactive/theme/tokens.ts +3 -14
- package/src/interactive/turn-context.ts +346 -33
- package/src/interactive/turn-persistence.ts +14 -4
- package/src/interactive/turn-prewarm.ts +364 -0
- package/src/interactive/turn-queues.ts +7 -4
- package/src/interactive/turn-runtime.ts +8 -1
- package/src/interactive/turn-state.ts +23 -0
- package/src/interactive/view/artifacts.ts +109 -1
- package/src/interactive/view/view-overlay.ts +29 -3
- package/src/interactive/watch-pane.ts +152 -0
- package/src/interactive/worker-progress.ts +7 -1
- package/src/interactive/worker-receipts.ts +19 -1
- package/src/interactive/worker-stream.ts +5 -0
- package/src/interactive/yazi-bridge.ts +444 -0
- package/src/tools/ask-user.ts +43 -2
- package/src/tools/bootstrap.ts +26 -2
- package/src/tools/builtin-tool-catalog.ts +15 -0
- package/src/tools/compete-worktrees.ts +83 -2
- package/src/tools/core-bootstrap.ts +2 -1
- package/src/tools/dispatch-admission.ts +14 -3
- package/src/tools/dispatch-arguments.ts +20 -20
- package/src/tools/dispatch-plan.ts +17 -9
- package/src/tools/dispatch-run-events.ts +134 -19
- package/src/tools/dispatch-runner.ts +29 -7
- package/src/tools/dispatch-scout.ts +1 -1
- package/src/tools/dispatch-types.ts +15 -3
- package/src/tools/dispatch.ts +1 -1
- package/src/tools/executables.ts +17 -14
- package/src/tools/observation.ts +54 -4
- package/src/tools/panes-surface.ts +38 -0
- package/src/tools/panes.ts +112 -0
- package/src/tools/policy.ts +10 -1
- package/src/tools/presentation.ts +1 -0
- package/src/tools/registry.ts +16 -0
- package/dist/chunk-AOCYTWAV.js +0 -449
- package/dist/chunk-HLE42MG7.js +0 -37
- package/dist/chunk-HWUFFB6L.js +0 -83
- package/dist/chunk-JOZYP4GM.js +0 -279
- package/dist/doctor-M7YEDGAE.js +0 -91
|
@@ -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,73 @@ 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 };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface EndpointCapacityHolders {
|
|
241
|
+
leases: number;
|
|
242
|
+
reservations: number;
|
|
243
|
+
foregroundStreams: number;
|
|
219
244
|
}
|
|
220
|
-
|
|
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
|
+
/**
|
|
267
|
+
* The ways past a saturated inference endpoint, in the order an operator can
|
|
268
|
+
* act on them.
|
|
269
|
+
*
|
|
270
|
+
* `maxConcurrentRequests` is named because the conservative default of one slot
|
|
271
|
+
* is a guess about an unprobed server, and on a server started with
|
|
272
|
+
* `--parallel 4` it is simply wrong. Without that sentence the denial reads as
|
|
273
|
+
* a hardware limit and the operator has no reason to look for a setting.
|
|
274
|
+
*
|
|
275
|
+
* `rosterFloor` is the smallest same-wave width the caller's topology can have.
|
|
276
|
+
* A council runs its whole roster in one wave, so "dispatch fewer at once" is
|
|
277
|
+
* not a move it has; saying so keeps the remedy honest.
|
|
278
|
+
*/
|
|
279
|
+
export function endpointCapacityRemedy(input?: { rosterFloor?: number }): string {
|
|
280
|
+
const ways =
|
|
281
|
+
"set this target's maxConcurrentRequests to the slot count the server was started with, collect in-flight runs, or point workers at a second server";
|
|
282
|
+
if (input?.rosterFloor === undefined) return `reduce the same-wave worker count, ${ways}`;
|
|
283
|
+
return `a council runs its whole roster in one wave and cannot go below ${input.rosterFloor} members, so ${ways}`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function assertCapacity(
|
|
287
|
+
file: CapacityStateFile,
|
|
288
|
+
nodeId: string,
|
|
289
|
+
endpointKey: string | undefined,
|
|
290
|
+
limits: CapacityLimits,
|
|
291
|
+
): void {
|
|
221
292
|
const held = heldReservationUsage(file.reservations);
|
|
222
293
|
const globalUsed = file.leases.length + held.global;
|
|
223
294
|
// The denial names the compliant next move, not just the gate. A model told
|
|
@@ -235,10 +306,28 @@ function assertCapacity(file: CapacityStateFile, nodeId: string, limits: Capacit
|
|
|
235
306
|
if (used >= nodeLimit)
|
|
236
307
|
throw new Error(`dispatch: admission denied: node '${nodeId}' capacity reached (${used}/${nodeLimit})`);
|
|
237
308
|
}
|
|
309
|
+
if (endpointKey !== undefined) {
|
|
310
|
+
const endpointLimit = limits.endpoints[endpointKey];
|
|
311
|
+
if (endpointLimit !== undefined) {
|
|
312
|
+
if (endpointLimit < 1)
|
|
313
|
+
throw new Error(`dispatch: admission denied: endpoint '${endpointLabel(endpointKey)}' capacity unavailable`);
|
|
314
|
+
const holders: EndpointCapacityHolders = {
|
|
315
|
+
leases: file.leases.filter((lease) => lease.endpointKey === endpointKey).length,
|
|
316
|
+
reservations: held.endpoints[endpointKey] ?? 0,
|
|
317
|
+
foregroundStreams: foregroundStreamUsage()[endpointKey] ?? 0,
|
|
318
|
+
};
|
|
319
|
+
const used = holders.leases + holders.reservations + holders.foregroundStreams;
|
|
320
|
+
if (used >= endpointLimit)
|
|
321
|
+
throw new Error(
|
|
322
|
+
`dispatch: admission denied: endpoint '${endpointLabel(endpointKey)}' capacity reached (${used}/${endpointLimit} slots): ${describeEndpointCapacityHolders(holders)}; ${endpointCapacityRemedy()}`,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
238
326
|
}
|
|
239
327
|
export function acquireCapacityLease(input: {
|
|
240
328
|
assignmentId: string;
|
|
241
329
|
nodeId: string;
|
|
330
|
+
endpointKey?: string;
|
|
242
331
|
limits: CapacityLimits;
|
|
243
332
|
nowMs?: number;
|
|
244
333
|
ttlMs?: number;
|
|
@@ -259,10 +348,12 @@ export function acquireCapacityLease(input: {
|
|
|
259
348
|
);
|
|
260
349
|
const existing = file.leases.find((lease) => lease.assignmentId === input.assignmentId);
|
|
261
350
|
if (existing) {
|
|
262
|
-
if (existing.nodeId !== input.nodeId) {
|
|
351
|
+
if (existing.nodeId !== input.nodeId || existing.endpointKey !== input.endpointKey) {
|
|
263
352
|
const without = { ...file, leases: file.leases.filter((lease) => lease !== existing) };
|
|
264
|
-
assertCapacity(without, input.nodeId, input.limits);
|
|
353
|
+
assertCapacity(without, input.nodeId, input.endpointKey, input.limits);
|
|
265
354
|
existing.nodeId = input.nodeId;
|
|
355
|
+
if (input.endpointKey === undefined) delete existing.endpointKey;
|
|
356
|
+
else existing.endpointKey = input.endpointKey;
|
|
266
357
|
}
|
|
267
358
|
existing.heartbeatAt = new Date(nowMs).toISOString();
|
|
268
359
|
existing.expiresAt = new Date(nowMs + (input.ttlMs ?? DEFAULT_CAPACITY_LEASE_TTL_MS)).toISOString();
|
|
@@ -270,7 +361,7 @@ export function acquireCapacityLease(input: {
|
|
|
270
361
|
return clone(existing);
|
|
271
362
|
}
|
|
272
363
|
input.onAcquiredUnderLock?.(file);
|
|
273
|
-
assertCapacity(file, input.nodeId, input.limits);
|
|
364
|
+
assertCapacity(file, input.nodeId, input.endpointKey, input.limits);
|
|
274
365
|
const token = input.processBirthToken ?? processBirthToken(input.ownerPid ?? process.pid);
|
|
275
366
|
if (!token) throw new Error("dispatch: cannot establish process birth token");
|
|
276
367
|
const at = new Date(nowMs).toISOString();
|
|
@@ -278,6 +369,7 @@ export function acquireCapacityLease(input: {
|
|
|
278
369
|
leaseId: `lease-${nowMs.toString(36)}-${randomBytes(6).toString("hex")}`,
|
|
279
370
|
assignmentId: input.assignmentId,
|
|
280
371
|
nodeId: input.nodeId,
|
|
372
|
+
...(input.endpointKey !== undefined ? { endpointKey: input.endpointKey } : {}),
|
|
281
373
|
host: hostname(),
|
|
282
374
|
ownerPid: input.ownerPid ?? process.pid,
|
|
283
375
|
processBirthToken: token,
|
|
@@ -365,11 +457,28 @@ export function capacityDrain(nowMs = Date.now()): CapacityDrain | null {
|
|
|
365
457
|
export function capacityLeaseUsage(options?: { nowMs?: number; probe?: LeaseOwnerProbe }): {
|
|
366
458
|
global: number;
|
|
367
459
|
nodes: Readonly<Record<string, number>>;
|
|
460
|
+
endpoints: Readonly<Record<string, number>>;
|
|
461
|
+
endpointHolders: Readonly<Record<string, EndpointCapacityHolders>>;
|
|
368
462
|
} {
|
|
369
463
|
const leases = listCapacityLeases(options);
|
|
370
464
|
const nodes: Record<string, number> = {};
|
|
371
|
-
|
|
372
|
-
|
|
465
|
+
const foreground = foregroundStreamUsage();
|
|
466
|
+
const endpoints: Record<string, number> = { ...foreground };
|
|
467
|
+
const endpointHolders: Record<string, EndpointCapacityHolders> = Object.fromEntries(
|
|
468
|
+
Object.entries(foreground).map(([endpointKey, foregroundStreams]) => [
|
|
469
|
+
endpointKey,
|
|
470
|
+
{ leases: 0, reservations: 0, foregroundStreams },
|
|
471
|
+
]),
|
|
472
|
+
);
|
|
473
|
+
for (const lease of leases) {
|
|
474
|
+
nodes[lease.nodeId] = (nodes[lease.nodeId] ?? 0) + 1;
|
|
475
|
+
if (lease.endpointKey !== undefined) {
|
|
476
|
+
endpoints[lease.endpointKey] = (endpoints[lease.endpointKey] ?? 0) + 1;
|
|
477
|
+
const holders = endpointHolders[lease.endpointKey] ?? { leases: 0, reservations: 0, foregroundStreams: 0 };
|
|
478
|
+
endpointHolders[lease.endpointKey] = { ...holders, leases: holders.leases + 1 };
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return { global: leases.length, nodes, endpoints, endpointHolders };
|
|
373
482
|
}
|
|
374
483
|
|
|
375
484
|
/**
|
|
@@ -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. */
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only reconstruction of council topology from the ordinary run ledger.
|
|
3
|
+
*
|
|
4
|
+
* A council has no durable record of its own. Every one of its runs is an
|
|
5
|
+
* ordinary ledger row, and what makes it a council is the provenance the
|
|
6
|
+
* scheduler stamps on each row: a shared {@link RunCouncilProvenance.group},
|
|
7
|
+
* the seated member's `label`, the round it spoke in, and a `gate.role` of
|
|
8
|
+
* `member` or `synthesis`. So the topology is not stored anywhere; it is a
|
|
9
|
+
* grouping over rows that already exist, which is what this module computes.
|
|
10
|
+
*
|
|
11
|
+
* Everything here is a fact the scheduler wrote. Member answers, the judge's
|
|
12
|
+
* synthesis text, and the vote tally live in receipt output as free model prose
|
|
13
|
+
* and are deliberately outside this projection: what crosses is who was seated,
|
|
14
|
+
* where they ran, how many rounds they took, and whether the council reached a
|
|
15
|
+
* synthesis. This module is pure. It reads no file, opens no ledger, and takes
|
|
16
|
+
* the rows its caller already has.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { ExecutionRole } from "./execution-role.js";
|
|
20
|
+
import { COUNCIL_MAX_MEMBERS } from "./gate-role-prompts.js";
|
|
21
|
+
import type { RunEnvelope, RunOutcome, RunStatus } from "./types.js";
|
|
22
|
+
|
|
23
|
+
/** How many councils one projection reports, newest first. */
|
|
24
|
+
export const COUNCIL_TOPOLOGY_MAX_COUNCILS = 4;
|
|
25
|
+
|
|
26
|
+
/** Seated members named per council. Mirrors the harness's own roster ceiling. */
|
|
27
|
+
export const COUNCIL_TOPOLOGY_MAX_MEMBERS = COUNCIL_MAX_MEMBERS;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Turns reported per member. Admission caps a council at three rounds; the
|
|
31
|
+
* bound is restated rather than imported because raising the harness ceiling
|
|
32
|
+
* must widen this window deliberately, and `turnsTruncated` reports the gap in
|
|
33
|
+
* the meantime.
|
|
34
|
+
*/
|
|
35
|
+
export const COUNCIL_TOPOLOGY_MAX_TURNS = 3;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ledger rows one projection will read.
|
|
39
|
+
*
|
|
40
|
+
* A council is at most five members over three rounds plus a judge and a sealed
|
|
41
|
+
* report, so seventeen rows; four councils fit inside a fraction of this scan
|
|
42
|
+
* even when ordinary dispatch rows are interleaved. The cap is what keeps the
|
|
43
|
+
* projection proportional on an installation whose ring holds a thousand runs.
|
|
44
|
+
*/
|
|
45
|
+
export const COUNCIL_TOPOLOGY_MAX_SCAN = 256;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The synthesis label every council files its verdict under.
|
|
49
|
+
*
|
|
50
|
+
* It is a scheduler constant, not an operator string: member labels are matched
|
|
51
|
+
* against {@link COUNCIL_MEMBER_LABEL}, which this value cannot satisfy anyway,
|
|
52
|
+
* so the two namespaces cannot collide.
|
|
53
|
+
*/
|
|
54
|
+
const SYNTHESIS_LABEL = "synthesis";
|
|
55
|
+
|
|
56
|
+
/** The agent id the ledger files a sealed council report under. */
|
|
57
|
+
const SEALED_REPORT_AGENT_ID = "council-synthesis";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The exact task text {@link sealCouncilSynthesis} mints, per synthesis kind.
|
|
61
|
+
*
|
|
62
|
+
* A run's task is operator or model prose everywhere else in this ledger, so it
|
|
63
|
+
* is never repeated by this projection. The sealed report is the one row whose
|
|
64
|
+
* task the harness writes itself, from a closed set of three, which makes it the
|
|
65
|
+
* only durable record of which synthesis a council was configured for. It is
|
|
66
|
+
* classified against that set, not quoted: an unrecognised string yields no kind
|
|
67
|
+
* rather than crossing as text.
|
|
68
|
+
*/
|
|
69
|
+
const SEALED_REPORT_TASKS: ReadonlyArray<readonly [string, CouncilSynthesisKind]> = [
|
|
70
|
+
["Council none synthesis", "none"],
|
|
71
|
+
["Council vote synthesis", "vote"],
|
|
72
|
+
["Council judge synthesis", "judge"],
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The label shape both council entry paths already enforce.
|
|
77
|
+
*
|
|
78
|
+
* `dispatch` admission tests member labels against this pattern and settings
|
|
79
|
+
* validation rejects a roster member that fails it, so a stored label outside it
|
|
80
|
+
* was not written by either. Repeating it here makes the projection's own
|
|
81
|
+
* guarantee structural rather than inherited: whatever wrote the row, the label
|
|
82
|
+
* this module reports can carry no path, URL, quote, space, or control byte.
|
|
83
|
+
*/
|
|
84
|
+
const COUNCIL_MEMBER_LABEL = /^[a-z][a-z0-9_-]{0,31}$/u;
|
|
85
|
+
|
|
86
|
+
export type CouncilSynthesisKind = "none" | "vote" | "judge";
|
|
87
|
+
|
|
88
|
+
/** How a council's dispatch plan was admitted, when the row records one. */
|
|
89
|
+
export type CouncilApproval = "operator" | "full-auto";
|
|
90
|
+
|
|
91
|
+
/** Who asked for the council: the operator's `/council`, the model, or the runtime. */
|
|
92
|
+
export type CouncilOrigin = "user" | "agent" | "internal";
|
|
93
|
+
|
|
94
|
+
/** One member's contribution in one round. */
|
|
95
|
+
export interface CouncilMemberTurn {
|
|
96
|
+
readonly round: number;
|
|
97
|
+
readonly runId: string;
|
|
98
|
+
readonly status: RunStatus;
|
|
99
|
+
/** Null while the run has not finalized; absent on pre-taxonomy rows. */
|
|
100
|
+
readonly outcome: RunOutcome | null;
|
|
101
|
+
readonly terminal: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** One seated voice, with every round it spoke in, oldest round first. */
|
|
105
|
+
export interface CouncilMember {
|
|
106
|
+
readonly label: string;
|
|
107
|
+
readonly agentId: string;
|
|
108
|
+
readonly targetId: string;
|
|
109
|
+
readonly wireModelId: string;
|
|
110
|
+
/** The role its route statistics were filed under; a council seats researchers. */
|
|
111
|
+
readonly executionRole: ExecutionRole;
|
|
112
|
+
readonly turns: readonly CouncilMemberTurn[];
|
|
113
|
+
readonly turnsTruncated: boolean;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The judge dispatch a `judge` synthesis runs, as an ordinary ledger row. */
|
|
117
|
+
export interface CouncilJudge {
|
|
118
|
+
readonly runId: string;
|
|
119
|
+
readonly agentId: string;
|
|
120
|
+
readonly targetId: string;
|
|
121
|
+
readonly wireModelId: string;
|
|
122
|
+
readonly status: RunStatus;
|
|
123
|
+
readonly outcome: RunOutcome | null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface CouncilSynthesisRecord {
|
|
127
|
+
/**
|
|
128
|
+
* Which synthesis the council was configured for, classified from the sealed
|
|
129
|
+
* report's harness-minted task. Null when no sealed report is in the window,
|
|
130
|
+
* which is what an aborted council and an aged-out one both look like.
|
|
131
|
+
*/
|
|
132
|
+
readonly kind: CouncilSynthesisKind | null;
|
|
133
|
+
/** The sealed council-report row, whose text stays host-side. */
|
|
134
|
+
readonly sealedRunId: string | null;
|
|
135
|
+
/** Present only for a judge synthesis, and only while its row is in the window. */
|
|
136
|
+
readonly judge: CouncilJudge | null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface CouncilTopology {
|
|
140
|
+
readonly group: string;
|
|
141
|
+
readonly startedAt: string;
|
|
142
|
+
/** Null while any row of the council is still running. */
|
|
143
|
+
readonly endedAt: string | null;
|
|
144
|
+
readonly running: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Rounds the council was configured for, recovered from the sealed report's
|
|
147
|
+
* round stamp. Null when no sealed report is in the window.
|
|
148
|
+
*/
|
|
149
|
+
readonly roundsPlanned: number | null;
|
|
150
|
+
/** Highest round any member row records. Never exceeds `roundsPlanned`. */
|
|
151
|
+
readonly roundsObserved: number;
|
|
152
|
+
readonly origin: CouncilOrigin | null;
|
|
153
|
+
readonly approval: CouncilApproval | null;
|
|
154
|
+
readonly members: readonly CouncilMember[];
|
|
155
|
+
/** True when the council seated more members than this projection names. */
|
|
156
|
+
readonly membersTruncated: boolean;
|
|
157
|
+
/**
|
|
158
|
+
* Member rows whose stored label failed {@link COUNCIL_MEMBER_LABEL} and were
|
|
159
|
+
* therefore left unnamed rather than repeated. Non-zero means something other
|
|
160
|
+
* than council admission wrote a row into this group.
|
|
161
|
+
*/
|
|
162
|
+
readonly membersRejected: number;
|
|
163
|
+
readonly synthesis: CouncilSynthesisRecord;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function isRunStatus(value: unknown): value is RunStatus {
|
|
167
|
+
return (
|
|
168
|
+
value === "queued" ||
|
|
169
|
+
value === "running" ||
|
|
170
|
+
value === "completed" ||
|
|
171
|
+
value === "failed" ||
|
|
172
|
+
value === "interrupted" ||
|
|
173
|
+
value === "stale" ||
|
|
174
|
+
value === "dead"
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function isRunOutcome(value: unknown): value is RunOutcome {
|
|
179
|
+
return (
|
|
180
|
+
value === "succeeded" ||
|
|
181
|
+
value === "failed" ||
|
|
182
|
+
value === "timed_out" ||
|
|
183
|
+
value === "stalled" ||
|
|
184
|
+
value === "canceled" ||
|
|
185
|
+
value === "denied_by_policy" ||
|
|
186
|
+
value === "spawn_failed"
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function outcomeOf(row: RunEnvelope): RunOutcome | null {
|
|
191
|
+
return isRunOutcome(row.outcome) ? row.outcome : null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function statusOf(row: RunEnvelope): RunStatus {
|
|
195
|
+
// A row written by another build can carry a status this one does not know.
|
|
196
|
+
// Reporting it as `stale` is the closest honest answer: the projection has a
|
|
197
|
+
// row it cannot classify, which is exactly what that state already means.
|
|
198
|
+
return isRunStatus(row.status) ? row.status : "stale";
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function synthesisKindOf(task: unknown): CouncilSynthesisKind | null {
|
|
202
|
+
return SEALED_REPORT_TASKS.find(([text]) => text === task)?.[1] ?? null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Oldest round first, then by run id so a duplicate round still orders stably. */
|
|
206
|
+
function byRound(a: CouncilMemberTurn, b: CouncilMemberTurn): number {
|
|
207
|
+
return a.round - b.round || (a.runId < b.runId ? -1 : a.runId > b.runId ? 1 : 0);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
interface CouncilRows {
|
|
211
|
+
readonly group: string;
|
|
212
|
+
readonly members: RunEnvelope[];
|
|
213
|
+
readonly judges: RunEnvelope[];
|
|
214
|
+
readonly sealed: RunEnvelope[];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Split one group's rows by the part they played.
|
|
219
|
+
*
|
|
220
|
+
* A row is a member unless its council label is the synthesis constant, and a
|
|
221
|
+
* synthesis row is the sealed report only when its agent id is the one the
|
|
222
|
+
* ledger writer uses. That distinction matters: a judge council writes two
|
|
223
|
+
* synthesis rows, one an ordinary dispatch that consumed a model and one a
|
|
224
|
+
* zero-cost record of the council's own report, and conflating them would double
|
|
225
|
+
* the council's apparent judge.
|
|
226
|
+
*/
|
|
227
|
+
function partition(group: string, rows: ReadonlyArray<RunEnvelope>): CouncilRows {
|
|
228
|
+
const members: RunEnvelope[] = [];
|
|
229
|
+
const judges: RunEnvelope[] = [];
|
|
230
|
+
const sealed: RunEnvelope[] = [];
|
|
231
|
+
for (const row of rows) {
|
|
232
|
+
if (row.council?.label !== SYNTHESIS_LABEL) {
|
|
233
|
+
members.push(row);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (row.agentId === SEALED_REPORT_AGENT_ID) sealed.push(row);
|
|
237
|
+
else judges.push(row);
|
|
238
|
+
}
|
|
239
|
+
return { group, members, judges, sealed };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function projectMembers(rows: ReadonlyArray<RunEnvelope>): {
|
|
243
|
+
members: CouncilMember[];
|
|
244
|
+
truncated: boolean;
|
|
245
|
+
rejected: number;
|
|
246
|
+
} {
|
|
247
|
+
const byLabel = new Map<string, RunEnvelope[]>();
|
|
248
|
+
let rejected = 0;
|
|
249
|
+
for (const row of rows) {
|
|
250
|
+
const label = row.council?.label ?? "";
|
|
251
|
+
if (!COUNCIL_MEMBER_LABEL.test(label)) {
|
|
252
|
+
rejected += 1;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const bucket = byLabel.get(label);
|
|
256
|
+
if (bucket === undefined) byLabel.set(label, [row]);
|
|
257
|
+
else bucket.push(row);
|
|
258
|
+
}
|
|
259
|
+
const labels = [...byLabel.keys()].sort();
|
|
260
|
+
const named = labels.slice(0, COUNCIL_TOPOLOGY_MAX_MEMBERS);
|
|
261
|
+
const members: CouncilMember[] = [];
|
|
262
|
+
for (const label of named) {
|
|
263
|
+
const bucket = byLabel.get(label) ?? [];
|
|
264
|
+
// The rows arrive newest first, so the head is the member's latest round
|
|
265
|
+
// and carries the route it most recently ran on. A council can reroute a
|
|
266
|
+
// member between rounds, and the current route is the one an operator is
|
|
267
|
+
// reading the grid to check.
|
|
268
|
+
const head = bucket[0];
|
|
269
|
+
if (head === undefined) continue;
|
|
270
|
+
const turns = bucket
|
|
271
|
+
.map(
|
|
272
|
+
(row): CouncilMemberTurn => ({
|
|
273
|
+
round: row.council?.round ?? 1,
|
|
274
|
+
runId: row.id,
|
|
275
|
+
status: statusOf(row),
|
|
276
|
+
outcome: outcomeOf(row),
|
|
277
|
+
terminal: row.endedAt !== null,
|
|
278
|
+
}),
|
|
279
|
+
)
|
|
280
|
+
.sort(byRound);
|
|
281
|
+
const visible = turns.slice(0, COUNCIL_TOPOLOGY_MAX_TURNS);
|
|
282
|
+
members.push({
|
|
283
|
+
label,
|
|
284
|
+
agentId: head.agentId,
|
|
285
|
+
targetId: head.targetId,
|
|
286
|
+
wireModelId: head.wireModelId,
|
|
287
|
+
executionRole: head.executionRole,
|
|
288
|
+
turns: visible,
|
|
289
|
+
turnsTruncated: turns.length > visible.length,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
return { members, truncated: labels.length > named.length, rejected };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function projectSynthesis(rows: CouncilRows): CouncilSynthesisRecord {
|
|
296
|
+
const sealed = rows.sealed[0] ?? null;
|
|
297
|
+
const judge = rows.judges[0] ?? null;
|
|
298
|
+
return {
|
|
299
|
+
kind: sealed === null ? null : synthesisKindOf(sealed.task),
|
|
300
|
+
sealedRunId: sealed?.id ?? null,
|
|
301
|
+
judge:
|
|
302
|
+
judge === null
|
|
303
|
+
? null
|
|
304
|
+
: {
|
|
305
|
+
runId: judge.id,
|
|
306
|
+
agentId: judge.agentId,
|
|
307
|
+
targetId: judge.targetId,
|
|
308
|
+
wireModelId: judge.wireModelId,
|
|
309
|
+
status: statusOf(judge),
|
|
310
|
+
outcome: outcomeOf(judge),
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function originOf(rows: ReadonlyArray<RunEnvelope>): CouncilOrigin | null {
|
|
316
|
+
const value = rows.find((row) => row.requestOrigin !== undefined)?.requestOrigin;
|
|
317
|
+
return value === "user" || value === "agent" || value === "internal" ? value : null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function approvalOf(rows: ReadonlyArray<RunEnvelope>): CouncilApproval | null {
|
|
321
|
+
const value = rows.find((row) => row.plan !== undefined)?.plan?.approval;
|
|
322
|
+
return value === "operator" || value === "full-auto" ? value : null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** Earliest start over the council's rows, as an ISO stamp. */
|
|
326
|
+
function firstStart(rows: ReadonlyArray<RunEnvelope>): string {
|
|
327
|
+
let earliest = rows[0]?.startedAt ?? new Date(0).toISOString();
|
|
328
|
+
for (const row of rows) if (row.startedAt < earliest) earliest = row.startedAt;
|
|
329
|
+
return earliest;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Latest end, or null when any row has not finished. */
|
|
333
|
+
function lastEnd(rows: ReadonlyArray<RunEnvelope>): string | null {
|
|
334
|
+
let latest: string | null = null;
|
|
335
|
+
for (const row of rows) {
|
|
336
|
+
if (row.endedAt === null) return null;
|
|
337
|
+
if (latest === null || row.endedAt > latest) latest = row.endedAt;
|
|
338
|
+
}
|
|
339
|
+
return latest;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export interface CouncilTopologyResult {
|
|
343
|
+
readonly councils: readonly CouncilTopology[];
|
|
344
|
+
/** True when the scanned window held councils older than the reported ones. */
|
|
345
|
+
readonly truncated: boolean;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Group a newest-first ledger window into bounded council topologies.
|
|
350
|
+
*
|
|
351
|
+
* The rows must arrive in the order the ledger lists them, newest start first,
|
|
352
|
+
* because that ordering is what makes the first council encountered the newest
|
|
353
|
+
* one. Nothing is read from disk and no row is mutated.
|
|
354
|
+
*/
|
|
355
|
+
export function councilTopologies(rows: ReadonlyArray<RunEnvelope>): CouncilTopologyResult {
|
|
356
|
+
const grouped = new Map<string, RunEnvelope[]>();
|
|
357
|
+
for (const row of rows.slice(0, COUNCIL_TOPOLOGY_MAX_SCAN)) {
|
|
358
|
+
const group = row.council?.group;
|
|
359
|
+
if (typeof group !== "string" || group.length === 0) continue;
|
|
360
|
+
const bucket = grouped.get(group);
|
|
361
|
+
if (bucket === undefined) grouped.set(group, [row]);
|
|
362
|
+
else bucket.push(row);
|
|
363
|
+
}
|
|
364
|
+
const groups = [...grouped.entries()];
|
|
365
|
+
const window = groups.slice(0, COUNCIL_TOPOLOGY_MAX_COUNCILS);
|
|
366
|
+
const councils: CouncilTopology[] = [];
|
|
367
|
+
for (const [group, rowsInGroup] of window) {
|
|
368
|
+
const parts = partition(group, rowsInGroup);
|
|
369
|
+
const { members, truncated, rejected } = projectMembers(parts.members);
|
|
370
|
+
const synthesis = projectSynthesis(parts);
|
|
371
|
+
const roundsObserved = members.reduce(
|
|
372
|
+
(highest, member) => member.turns.reduce((inner, turn) => Math.max(inner, turn.round), highest),
|
|
373
|
+
0,
|
|
374
|
+
);
|
|
375
|
+
const sealedRound = parts.sealed[0]?.council?.round;
|
|
376
|
+
const roundsPlanned =
|
|
377
|
+
typeof sealedRound === "number" && Number.isInteger(sealedRound) && sealedRound > 0 ? sealedRound : null;
|
|
378
|
+
councils.push({
|
|
379
|
+
group,
|
|
380
|
+
startedAt: firstStart(rowsInGroup),
|
|
381
|
+
endedAt: lastEnd(rowsInGroup),
|
|
382
|
+
running: rowsInGroup.some((row) => row.endedAt === null),
|
|
383
|
+
// A sealed report stamps the configured round count, so a member seen in
|
|
384
|
+
// a later round than the report claims means the two disagree about the
|
|
385
|
+
// same council. Reporting the higher of the two keeps the invariant that
|
|
386
|
+
// observed never exceeds planned without hiding the member row.
|
|
387
|
+
roundsPlanned: roundsPlanned === null ? null : Math.max(roundsPlanned, roundsObserved),
|
|
388
|
+
roundsObserved,
|
|
389
|
+
origin: originOf(rowsInGroup),
|
|
390
|
+
approval: approvalOf(rowsInGroup),
|
|
391
|
+
members,
|
|
392
|
+
membersTruncated: truncated,
|
|
393
|
+
membersRejected: rejected,
|
|
394
|
+
synthesis,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
return { councils, truncated: groups.length > window.length };
|
|
398
|
+
}
|