@iowarp/clio-coder 0.3.9 → 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 +53 -0
- package/NOTICE +33 -0
- package/README.md +7 -2
- package/dist/{acp-7LOELQFP.js → acp-G5WJBNCT.js} +13 -12
- package/dist/{agents-FIBG2SHA.js → agents-FMV2Q5G4.js} +39 -36
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-OI4LIH2I.js → auth-3IDSJEIK.js} +16 -15
- package/dist/{builtins-AD25UL3C.js → builtins-XCZWXSC7.js} +3 -3
- package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
- package/dist/{chunk-56KB5IJP.js → chunk-2OQE55CK.js} +3 -3
- package/dist/{chunk-N3PBVRTZ.js → chunk-32KWKNSF.js} +9 -7
- package/dist/{chunk-VKBMFOYV.js → chunk-36EJLSQQ.js} +23 -27
- package/dist/{chunk-465CC7FK.js → chunk-3BT2XMV4.js} +16 -13
- package/dist/{chunk-NQ6UCCOD.js → chunk-3URVFKWK.js} +7 -7
- package/dist/{chunk-WIE7ZOSW.js → chunk-3XML7CDN.js} +2 -2
- package/dist/chunk-42FMPA75.js +101 -0
- package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
- package/dist/{chunk-HAY4ZE2P.js → chunk-5PSMVOLM.js} +2 -2
- package/dist/{chunk-JEQQR47K.js → chunk-6DB53AJS.js} +168 -9
- package/dist/{chunk-KV2AOLDF.js → chunk-76ONBSIA.js} +5 -25
- package/dist/{chunk-FALJGAWU.js → chunk-7MCTRUCE.js} +2 -2
- package/dist/{chunk-5T7RBWN2.js → chunk-AB44T6BB.js} +5 -5
- package/dist/chunk-B7OBL7PK.js +1405 -0
- package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
- package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
- package/dist/{chunk-O6I4CIEU.js → chunk-BKFJHQCA.js} +6 -6
- package/dist/chunk-BKFM6EJV.js +462 -0
- package/dist/chunk-BMS5RKQY.js +27 -0
- package/dist/{chunk-MV2VUEJC.js → chunk-BPKCPIL7.js} +2 -2
- package/dist/{chunk-TEO2TLVN.js → chunk-BUMFYQFY.js} +964 -1120
- package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
- package/dist/chunk-C2LTL2W6.js +2447 -0
- package/dist/{chunk-EBOC7MT3.js → chunk-CODPRO7Q.js} +8 -8
- package/dist/{chunk-HPCTNZM2.js → chunk-CTJ4RNAA.js} +4 -4
- package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
- package/dist/{chunk-MIX5N5AC.js → chunk-DQITNCXG.js} +394 -154
- package/dist/{chunk-S4COXYBG.js → chunk-DYJP44XW.js} +129 -106
- package/dist/{chunk-N5XKWMDW.js → chunk-F4CKPOEQ.js} +6 -6
- package/dist/{chunk-AMKHQW3C.js → chunk-FEFIFZTL.js} +3 -3
- package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
- package/dist/{chunk-7C6RYZGQ.js → chunk-GKF55TAZ.js} +3 -3
- package/dist/{chunk-B5XRQOLB.js → chunk-GR5G2PVF.js} +9 -8
- package/dist/{chunk-XDOQXGFO.js → chunk-GXNLGKAB.js} +64 -8
- package/dist/chunk-HHV2GANA.js +88 -0
- package/dist/{chunk-774ILSRL.js → chunk-HI63TFOG.js} +4 -3
- package/dist/{chunk-R7LNVMCS.js → chunk-HJJTYUHX.js} +64 -72
- package/dist/{chunk-RKRLDWD3.js → chunk-HLW2MRKE.js} +1 -1
- package/dist/{chunk-CA42X6KT.js → chunk-HWHKMHUA.js} +6 -6
- package/dist/chunk-HZHHCK24.js +1631 -0
- package/dist/{chunk-HJB5IUKP.js → chunk-I5VEOC6I.js} +12 -12
- package/dist/chunk-IBEBSCYA.js +564 -0
- package/dist/chunk-IQ7KR472.js +362 -0
- package/dist/{chunk-4H6ULJ3H.js → chunk-J4W7KFM7.js} +921 -990
- package/dist/{chunk-4LJX2PUC.js → chunk-JDG2WCRO.js} +5 -5
- package/dist/{chunk-NZU6YDNV.js → chunk-K5C3NCBD.js} +4 -4
- package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
- package/dist/{chunk-NNNWO6F2.js → chunk-KFZI4NIL.js} +93 -3
- package/dist/chunk-KMVISBZR.js +132 -0
- package/dist/{chunk-BVDVID7E.js → chunk-LDQ2ZF2M.js} +2 -2
- package/dist/{chunk-OEDBCISO.js → chunk-LQ3DZAMX.js} +2 -2
- package/dist/{chunk-PZ4I4JE2.js → chunk-LY4S7GJC.js} +124 -116
- package/dist/{chunk-3DUR4WUA.js → chunk-MLKNTWH2.js} +19 -19
- package/dist/chunk-NEKRRTYW.js +56 -0
- package/dist/{chunk-5DQRIYDZ.js → chunk-NHCZP4K7.js} +2 -2
- package/dist/chunk-NHLBIGRH.js +1506 -0
- package/dist/chunk-NQQH3YT7.js +302 -0
- package/dist/chunk-NYS75XW5.js +15 -0
- package/dist/{chunk-TD7UE2L5.js → chunk-O4XIVISU.js} +7 -7
- package/dist/{chunk-WXCJ7VME.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-HKO36JWF.js → chunk-QBJA7R7N.js} +31 -3
- package/dist/chunk-QDC3K2U3.js +262 -0
- package/dist/{chunk-5HFBWUMU.js → chunk-QLFS5GO2.js} +4 -4
- package/dist/{chunk-GAYUJ7LE.js → chunk-QLL7ILRG.js} +41 -32
- package/dist/{chunk-LXPJXFM5.js → chunk-QREDIESB.js} +7 -7
- package/dist/chunk-QSNYB6ZV.js +195 -0
- package/dist/chunk-R346GLFC.js +31 -0
- package/dist/{chunk-VPTUJU4P.js → chunk-SJ5ZKQ4S.js} +2 -2
- package/dist/{chunk-QQ7EKM72.js → chunk-SP2RXXYO.js} +6 -54
- package/dist/chunk-SUCTJL45.js +45 -0
- package/dist/{chunk-5QKCQQ3E.js → chunk-SUW5DORT.js} +2 -2
- package/dist/chunk-T56WDKA5.js +183 -0
- package/dist/chunk-TVHHYFHE.js +255 -0
- package/dist/{chunk-IHKBWSXF.js → chunk-TZ3SGWZZ.js} +2 -2
- package/dist/{chunk-ECUO3KDP.js → chunk-U77AMWDL.js} +47 -7
- package/dist/{chunk-3UUY7R3Z.js → chunk-ULC6OTWO.js} +7 -7
- package/dist/{chunk-AEYBF3TB.js → chunk-UM7N4G5A.js} +2 -2
- package/dist/{chunk-3V5AYSEQ.js → chunk-UXMFQ54G.js} +43 -36
- package/dist/{chunk-YW7UVM5V.js → chunk-V5DHCITQ.js} +34 -1
- package/dist/{chunk-47CMYGET.js → chunk-VAZSBTKF.js} +2 -2
- package/dist/{chunk-3MRC2YSQ.js → chunk-VEO4AP2K.js} +2 -2
- package/dist/{chunk-PNY46YEY.js → chunk-VFA6GDY5.js} +47 -3
- package/dist/chunk-VO67MWHC.js +75 -0
- package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
- package/dist/{chunk-AD7Y7STJ.js → chunk-VYMXRQI6.js} +36 -22
- package/dist/{chunk-T3Z6VAAF.js → chunk-W5VSYASO.js} +19 -17
- package/dist/{chunk-5PVQ4SRS.js → chunk-WZR7K7ZX.js} +7 -123
- package/dist/{chunk-DG4M6ZUE.js → chunk-X3YGUTOB.js} +4 -4
- package/dist/chunk-X75E3D2N.js +686 -0
- package/dist/{chunk-P3JGPQFL.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-YSEHGPCT.js → chunk-Z2RR6MAK.js} +3 -3
- package/dist/{chunk-HCBCAYZU.js → chunk-Z4TXYIEG.js} +5 -5
- package/dist/cli/index.js +47 -36
- package/dist/{clio-LT5V7SSZ.js → clio-2JXHBBY5.js} +7 -7
- package/dist/{code-nav-LMW275PA.js → code-nav-3YYRMYNF.js} +6 -6
- 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-RXS5T3JT.js → config-QZPCMYSO.js} +58 -50
- package/dist/{configure-2WYWSCSD.js → configure-TEGEBYCA.js} +21 -19
- package/dist/{context-I3BTOTCS.js → context-AV7OEZ4D.js} +11 -11
- package/dist/{context-MVOORGMF.js → context-E6H5RNMC.js} +49 -41
- package/dist/{context-PALKKQYL.js → context-GSXUE4CT.js} +25 -23
- package/dist/{context-clear-N2WOYZ2K.js → context-clear-SHIBYK6T.js} +48 -40
- package/dist/{context-working-set-MIEVECVZ.js → context-working-set-5ZGKPGZQ.js} +11 -10
- package/dist/{dispatch-runner-VVA4SRRH.js → dispatch-runner-EFMJT4LD.js} +88 -60
- package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
- package/dist/doctor-QOA5FNY5.js +313 -0
- package/dist/{eval-IJ5VEZDJ.js → eval-TFBYQH4H.js} +66 -64
- package/dist/eval-inventory-SXH7PDKX.js +316 -0
- package/dist/{evidence-L5APPXNV.js → evidence-ERGESKGN.js} +198 -44
- package/dist/{evolve-RGNKFJ52.js → evolve-VDXTSYCJ.js} +46 -38
- package/dist/{extensions-7WYWUX5A.js → extensions-7BGBHN57.js} +6 -6
- package/dist/{fleet-6CNVBZZP.js → fleet-2RRVDF2V.js} +189 -101
- package/dist/{fleet-commands-L2SXSYEI.js → fleet-commands-VJ726XIA.js} +10 -10
- package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
- package/dist/{fleet-graph-2J3OOIPO.js → fleet-graph-JF5QOATM.js} +16 -15
- package/dist/fleet-inspect-VLY4S7QM.js +442 -0
- package/dist/{fleet-preflight-CZRJ4JP5.js → fleet-preflight-AIZUEJOY.js} +6 -5
- package/dist/{fleet-validate-C5RI6DP7.js → fleet-validate-AJRPDMDV.js} +20 -18
- package/dist/fleet-verify-JFEL2L3H.js +175 -0
- package/dist/fleet-view-ZCON35AG.js +102 -0
- package/dist/{init-VBN2ACVA.js → init-DN2WWLFE.js} +64 -56
- package/dist/install-XGLBQY5E.js +13 -0
- package/dist/interop-OZBKXAYL.js +114 -0
- package/dist/{library-JHGUMLY2.js → library-YWZG7IMW.js} +19 -18
- package/dist/{memory-K4OQIYWG.js → memory-I4C4HMLW.js} +48 -40
- package/dist/{models-2NCZUWDD.js → models-CEYXJBO6.js} +30 -28
- package/dist/{monitor-MMVTJABD.js → monitor-NZ6GCI3P.js} +53 -47
- package/dist/{orchestrator-ZKBPCHW6.js → orchestrator-GCGQ4N5I.js} +6311 -7145
- 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-DD5JGOY3.js → reset-DQ6FGCSH.js} +13 -11
- package/dist/resources-BB3MVJMD.js +111 -0
- package/dist/{run-QEGNX7FL.js → run-H2GQDUER.js} +113 -82
- package/dist/{share-JKD3BQMW.js → share-GTJN6A5O.js} +18 -17
- package/dist/{skills-LMQIKDOZ.js → skills-L55TEW6R.js} +31 -24
- package/dist/{skills-eval-I7X2774U.js → skills-eval-XVXPH2JI.js} +61 -52
- package/dist/skills-inventory-S4MXPJFV.js +126 -0
- package/dist/slash-commands-ZSGASKJC.js +77 -0
- package/dist/{steer-CF5TDANS.js → steer-RZGSCY4R.js} +3 -3
- package/dist/{support-I7LOJLIF.js → support-PKEUNNQL.js} +4 -4
- package/dist/{targets-RUSR6B5Z.js → targets-NCPZ644J.js} +31 -29
- package/dist/{terminal-lease-QYVORFR4.js → terminal-lease-44SV3YCN.js} +4 -4
- package/dist/tools-DAF3DI3C.js +27 -0
- package/dist/{trace-ODOQIVIW.js → trace-FYVW2MQA.js} +149 -7
- package/dist/tui-primitives-2AKXQNZK.js +13 -0
- package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
- package/dist/{upgrade-XANW3FXB.js → upgrade-3XPP6OQL.js} +26 -24
- package/dist/{usage-4H7ZRXQT.js → usage-3NLHGTU2.js} +57 -49
- package/dist/{verifiers-UZXNBZEB.js → verifiers-SSQONKRT.js} +171 -12
- package/dist/{verify-BVKWTNDL.js → verify-3U6J7FZI.js} +9 -9
- package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
- package/dist/{wiki-generate-MY7WV2QI.js → wiki-generate-CEHYGPGQ.js} +67 -59
- package/dist/with-panes-MKB46MPQ.js +782 -0
- package/dist/worker/entry.js +101 -66
- 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 +4 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +43 -5
- package/docs/commands-and-modes.md +31 -2
- package/docs/configuration-and-targets.md +40 -10
- package/docs/context-engine.md +1 -1
- package/docs/development-pipeline.md +19 -0
- package/docs/dispatch-typed-intent.md +385 -0
- package/docs/documentation-coverage.md +2 -2
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +1 -0
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +2 -2
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +25 -1
- package/docs/fleet-dispatch.md +22 -1
- 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 +3 -3
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +1 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- 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 +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +4 -2
- package/src/cli/acp.ts +6 -2
- package/src/cli/argv.ts +25 -0
- package/src/cli/configure.ts +23 -21
- package/src/cli/doctor-panes.ts +124 -0
- package/src/cli/doctor-toolchain.ts +57 -0
- package/src/cli/doctor.ts +20 -1
- package/src/cli/eval-inventory.ts +436 -0
- package/src/cli/eval.ts +13 -0
- 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/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 +148 -37
- 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/tools.ts +321 -0
- package/src/cli/trace-inspect.ts +252 -0
- package/src/cli/trace.ts +23 -2
- package/src/cli/verifiers-inspect.ts +347 -0
- package/src/cli/verifiers.ts +9 -0
- package/src/core/bus-events.ts +4 -0
- package/src/core/config.ts +49 -0
- package/src/core/defaults.ts +93 -7
- package/src/core/response-schema.ts +44 -4
- 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 +3 -0
- package/src/domains/dispatch/admission-error.ts +9 -0
- package/src/domains/dispatch/admission.ts +22 -21
- package/src/domains/dispatch/capacity-lease.ts +21 -1
- package/src/domains/dispatch/council-topology.ts +398 -0
- package/src/domains/dispatch/extension.ts +222 -50
- 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 +64 -1
- 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/index.ts +19 -0
- package/src/domains/dispatch/intent-compatibility.ts +330 -0
- package/src/domains/dispatch/intent.ts +85 -1
- package/src/domains/dispatch/reservation-store.ts +24 -4
- 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 +45 -1
- package/src/domains/dispatch/types.ts +13 -0
- package/src/domains/dispatch/validation.ts +33 -8
- package/src/domains/eval/compare/compare.ts +3 -16
- package/src/domains/eval/inventory.ts +113 -0
- package/src/domains/eval/schema/serving.ts +31 -0
- package/src/domains/evidence/store.ts +6 -0
- package/src/domains/memory/index.ts +0 -1
- package/src/domains/memory/task-memory-policy.ts +6 -10
- package/src/domains/memory/task-memory-telemetry.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +33 -21
- 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/cost.ts +21 -0
- package/src/domains/observability/index.ts +2 -0
- package/src/domains/observability/trace-store.ts +42 -0
- package/src/domains/providers/endpoint-capacity.ts +154 -22
- package/src/domains/providers/endpoint-slots-store.ts +189 -0
- package/src/domains/providers/extension.ts +20 -3
- package/src/domains/providers/index.ts +22 -0
- package/src/domains/providers/model-runtime-capabilities.ts +32 -0
- package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
- package/src/domains/providers/runtimes/builtins.ts +2 -0
- 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/resources/index.ts +3 -0
- package/src/domains/resources/skills/loader.ts +33 -0
- package/src/domains/safety/action-classifier.ts +6 -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/server.ts +413 -70
- package/src/engine/acp/types.ts +19 -1
- package/src/engine/claude/sdk-module.ts +98 -0
- package/src/engine/claude/sdk-runtime.ts +19 -11
- 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 +103 -5
- package/src/entry/panes-activation.ts +31 -0
- package/src/entry/with-panes.ts +20 -0
- package/src/interactive/context-overlay.ts +1 -0
- package/src/interactive/cost-overlay.ts +2 -0
- package/src/interactive/council-grid.ts +1 -3
- package/src/interactive/council.ts +11 -0
- package/src/interactive/dispatch-board.ts +26 -16
- package/src/interactive/interactive-application.ts +153 -6
- package/src/interactive/interactive-input-runtime.ts +7 -0
- package/src/interactive/interactive-slash-runtime.ts +36 -6
- package/src/interactive/memory-overlay.ts +1 -0
- package/src/interactive/modal-marker.ts +170 -0
- package/src/interactive/mux-bridge.ts +214 -0
- package/src/interactive/overlay-frame.ts +58 -2
- package/src/interactive/overlay-general-openers.ts +1 -0
- package/src/interactive/overlay-key-routing.ts +14 -3
- package/src/interactive/overlay-lifecycle.ts +19 -3
- package/src/interactive/overlay-model-selectors.ts +40 -3
- package/src/interactive/overlay-permission-lifecycle.ts +90 -22
- package/src/interactive/overlay-transitions.ts +18 -4
- package/src/interactive/overlays/agents.ts +1 -0
- package/src/interactive/overlays/ask-user.ts +136 -30
- 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 +2 -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 +1 -0
- package/src/interactive/overlays/session-selector.ts +1 -0
- package/src/interactive/overlays/settings-sections.ts +30 -0
- package/src/interactive/overlays/settings.ts +579 -44
- 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/renderers/compaction-summary.ts +29 -0
- package/src/interactive/renderers/worker-entry.ts +122 -14
- package/src/interactive/slash-commands.ts +251 -15
- package/src/interactive/tasks-overlay.ts +1 -0
- package/src/interactive/theme/tokens.ts +3 -14
- package/src/interactive/turn-context.ts +47 -2
- package/src/interactive/view/artifacts.ts +109 -1
- package/src/interactive/view/view-overlay.ts +1 -0
- 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/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-run-events.ts +134 -19
- package/src/tools/dispatch-runner.ts +29 -7
- 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/dist/chunk-D73KXYPF.js +0 -37
- package/dist/chunk-MLOK6ZOS.js +0 -2888
- package/dist/chunk-YKOFT37S.js +0 -279
- package/dist/doctor-TWBWFK5V.js +0 -165
|
@@ -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
|
+
}
|