@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
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What one fleet node is told about its own answer.
|
|
3
|
+
*
|
|
4
|
+
* A fleet contract renders a single prompt body and the plan compiler gives it
|
|
5
|
+
* to every agent node, because the body describes the whole chain and each node
|
|
6
|
+
* does one part of it. That is fine for the work description and wrong for the
|
|
7
|
+
* answer: nodes in the same chain hold different result contracts, so a body
|
|
8
|
+
* that names one contract names the wrong one for every other node. A loop's
|
|
9
|
+
* check node is where the mismatch is fatal rather than merely untidy. It is
|
|
10
|
+
* dispatched with `verifier-report` as its expected contract, and in
|
|
11
|
+
* `build-review` and `build-test` it received a body ending in "Answer with
|
|
12
|
+
* your `mutation-report`". The verifier complied with the instruction it was
|
|
13
|
+
* given, the gate held it to the schema that instruction never mentioned, and
|
|
14
|
+
* both were behaving correctly: every review cycle in those two fleets ended in
|
|
15
|
+
* `review gate produced no structured verdict` and the bounded revise loop was
|
|
16
|
+
* unresolvable by construction.
|
|
17
|
+
*
|
|
18
|
+
* So each node states its own contract, appended to the shared body, and the
|
|
19
|
+
* precedence line settles the conflict explicitly rather than leaving it to the
|
|
20
|
+
* model to guess which of two shapes the run will actually validate. This is
|
|
21
|
+
* the same remedy `COUNCIL_VOTE_MEMBER_DIRECTIVE` applies to a voting council
|
|
22
|
+
* member, for the same reason and with the same shape of failure behind it
|
|
23
|
+
* (#230): a run whose postcondition differs from the shape its prompt names
|
|
24
|
+
* fails on every input, not intermittently.
|
|
25
|
+
*
|
|
26
|
+
* The shape itself is quoted from `resultContractShape`, the one place a
|
|
27
|
+
* contract's wire example is written, so this directive cannot drift from the
|
|
28
|
+
* validator that judges the answer.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { type ResultContract, resultContractShape } from "../agents/result-contract.js";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The answer directive for a node holding this contract, or null when the kind
|
|
35
|
+
* alone does not fix the shape.
|
|
36
|
+
*
|
|
37
|
+
* `architect-plan` names the path it must write, and a plan compiler resolving
|
|
38
|
+
* agents holds only the contract's kind. Inventing a path to render would state
|
|
39
|
+
* a location the validator does not check, so that kind gets no directive and
|
|
40
|
+
* its recipe keeps sole authority over the shape. Every other kind is fully
|
|
41
|
+
* determined by its kind and can be quoted exactly.
|
|
42
|
+
*/
|
|
43
|
+
export function fleetNodeAnswerDirective(kind: ResultContract["kind"]): string | null {
|
|
44
|
+
if (kind === "architect-plan") return null;
|
|
45
|
+
return [
|
|
46
|
+
`Your result contract for this step is \`${kind}\`.`,
|
|
47
|
+
"The task above describes the whole chain. Any result shape it states belongs to the step that produces it, and does not apply to you.",
|
|
48
|
+
`End with exactly this shape and nothing else: ${resultContractShape({ kind })}`,
|
|
49
|
+
// The wire example carries literal names and evidence, and a small model
|
|
50
|
+
// handed one on a first attempt copies them: the first run of this
|
|
51
|
+
// directive came back claiming `npm run typecheck` / `exit 0` in the same
|
|
52
|
+
// breath as admitting it could not run a command. The fields are the
|
|
53
|
+
// contract; the values in the example are not.
|
|
54
|
+
"Its field values are an example. Fill them with what this run actually produced or observed, and never report a name or evidence you did not.",
|
|
55
|
+
].join("\n");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The shared body plus the node's own answer directive. */
|
|
59
|
+
export function renderFleetNodeTask(task: string, kind: ResultContract["kind"]): string {
|
|
60
|
+
const directive = fleetNodeAnswerDirective(kind);
|
|
61
|
+
return directive === null ? task : `${task}\n\n${directive}`;
|
|
62
|
+
}
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
type ExecutionPlanStepInput,
|
|
33
33
|
} from "./execution-plan.js";
|
|
34
34
|
import type { ExecutionRole } from "./execution-role.js";
|
|
35
|
+
import { renderFleetNodeTask } from "./fleet-node-prompt.js";
|
|
35
36
|
|
|
36
37
|
/** Where an agent node sits, so the caller can resolve its role and authority. */
|
|
37
38
|
export interface FleetPlanAgentContext {
|
|
@@ -58,7 +59,11 @@ export interface FleetPlanAgentResolution {
|
|
|
58
59
|
|
|
59
60
|
export interface CompileFleetPlanInput {
|
|
60
61
|
contract: FleetContract;
|
|
61
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Rendered prompt body. It describes the whole chain, so every agent node
|
|
64
|
+
* carries it, followed by that node's own answer directive: the nodes hold
|
|
65
|
+
* different result contracts and the body can only ever name one of them.
|
|
66
|
+
*/
|
|
62
67
|
task: string;
|
|
63
68
|
resolveAgent(context: FleetPlanAgentContext): FleetPlanAgentResolution;
|
|
64
69
|
}
|
|
@@ -143,7 +148,7 @@ export function compileFleetExecutionPlan(input: CompileFleetPlanInput): Executi
|
|
|
143
148
|
agentId: context.agentId,
|
|
144
149
|
scope: context.scope,
|
|
145
150
|
dependencies: [...dependencies],
|
|
146
|
-
task,
|
|
151
|
+
task: renderFleetNodeTask(task, resolved.expectedResultContract),
|
|
147
152
|
expectedResultContract: resolved.expectedResultContract,
|
|
148
153
|
requestedAuthority: resolved.requestedAuthority,
|
|
149
154
|
approvedAuthority: resolved.approvedAuthority,
|
|
@@ -21,6 +21,8 @@ import type { FleetCommandRegistry } from "../agents/fleet-commands.js";
|
|
|
21
21
|
import type { FleetContract } from "../agents/fleet-contract.js";
|
|
22
22
|
import { resultContractAuthorship } from "../agents/result-contract.js";
|
|
23
23
|
import type { AgentSpec } from "../agents/spec.js";
|
|
24
|
+
import { aggregateCostAmounts, type CostAggregate, renderCostAmount } from "../observability/cost.js";
|
|
25
|
+
import type { CostProvenance } from "../providers/index.js";
|
|
24
26
|
import { claimAssignmentVerdict, recordAssignmentAttempt, settleStoredAssignment } from "./assignment-store.js";
|
|
25
27
|
import { runCodeStep } from "./code-step.js";
|
|
26
28
|
import { codeStepDir, writeCodeStepRecord } from "./code-step-store.js";
|
|
@@ -43,9 +45,11 @@ import {
|
|
|
43
45
|
materializePendingGateDecision,
|
|
44
46
|
stagePendingGateDecision,
|
|
45
47
|
} from "./gate-decisions.js";
|
|
48
|
+
import { type DispatchIntent, declaredScopeIntent } from "./intent.js";
|
|
46
49
|
import { verifyReceiptIntegrity } from "./receipt-integrity.js";
|
|
47
50
|
import { type FleetRunRecord, writeFleetRun } from "./state.js";
|
|
48
51
|
import type { RunReceipt } from "./types.js";
|
|
52
|
+
import type { WriteBoundaryAttribution } from "./write-boundary.js";
|
|
49
53
|
import { createWriteBoundaryEnforcer } from "./write-boundary-enforcer.js";
|
|
50
54
|
|
|
51
55
|
/** Owner id for a plan with no model runs; it holds nothing to release. */
|
|
@@ -82,6 +86,15 @@ export interface FleetRunStepOutcome extends FleetRunStepEvent {
|
|
|
82
86
|
/** Terminal run id, which is the receipt id for an agent step. */
|
|
83
87
|
terminalRunId: string;
|
|
84
88
|
costUsd: number;
|
|
89
|
+
/**
|
|
90
|
+
* Where the number in `costUsd` came from, carried beside it because the two
|
|
91
|
+
* are one fact. A gateway-routed run seals `costUsd: 0` with `unknown`: its
|
|
92
|
+
* tokens were measured and nobody priced them. A renderer that reads the
|
|
93
|
+
* amount without this prints `$0.0000` and turns unpriced work into a
|
|
94
|
+
* measured zero. `known_free` on a deterministic step is the honest reading:
|
|
95
|
+
* no model ran, so the zero is something code observed rather than missed.
|
|
96
|
+
*/
|
|
97
|
+
costProvenance: CostProvenance;
|
|
85
98
|
/** The sealed agent receipt when this was an agent step. */
|
|
86
99
|
receipt?: RunReceipt;
|
|
87
100
|
/** The durable code-step record when this was a deterministic step. */
|
|
@@ -191,7 +204,18 @@ export interface FleetRunOutcome {
|
|
|
191
204
|
planHash: string;
|
|
192
205
|
result: ExecutionPlanResult;
|
|
193
206
|
receipts: ReadonlyArray<RunReceipt>;
|
|
207
|
+
/**
|
|
208
|
+
* The bare sum of every receipt's `costUsd`, which adds unpriced zeros to
|
|
209
|
+
* priced dollars and so cannot be rendered on its own. Retained for the
|
|
210
|
+
* interactive fleet overlay, which still reads it. Render `totalCost`.
|
|
211
|
+
*/
|
|
194
212
|
totalCostUsd: number;
|
|
213
|
+
/**
|
|
214
|
+
* The fold that keeps unpriced calls distinguishable from priced ones, so a
|
|
215
|
+
* run whose every receipt came back `unknown` renders as not measured rather
|
|
216
|
+
* than as a total of zero dollars.
|
|
217
|
+
*/
|
|
218
|
+
totalCost: CostAggregate;
|
|
195
219
|
/** Steps that ran unconditionally, excluding nodes a resolved loop skipped. */
|
|
196
220
|
requiredStepCount: number;
|
|
197
221
|
succeededStepCount: number;
|
|
@@ -216,6 +240,24 @@ function fleetGateCorrelation(subject: RunReceipt, decider: RunReceipt): GateDec
|
|
|
216
240
|
return { agent, target, modelFamily, runtime, node, independent };
|
|
217
241
|
}
|
|
218
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Typed scope for one fleet step, drawn from the boundary its contract already
|
|
245
|
+
* declared. Returns null when the contract declared nothing for this position,
|
|
246
|
+
* which is every step of a pre-v4 contract and every readonly step: those keep
|
|
247
|
+
* the legacy inference path rather than being handed an empty declaration that
|
|
248
|
+
* would silently switch off path-scoped rule selection for them.
|
|
249
|
+
*
|
|
250
|
+
* A contract whose declared paths do not survive the boundary grammar is a
|
|
251
|
+
* compile-time authoring bug, not a dispatch-time one, so a malformed entry
|
|
252
|
+
* falls back to inference here rather than aborting a run mid-wave; the
|
|
253
|
+
* contract loader is the surface that refuses it.
|
|
254
|
+
*/
|
|
255
|
+
function fleetStepIntent(writes: ReadonlyArray<string> | undefined): DispatchIntent | null {
|
|
256
|
+
if (writes === undefined || writes.length === 0) return null;
|
|
257
|
+
const built = declaredScopeIntent({ relevantPaths: writes });
|
|
258
|
+
return built.ok ? built.intent : null;
|
|
259
|
+
}
|
|
260
|
+
|
|
219
261
|
export function fleetPlanWaveIndex(plan: ExecutionPlan, stepId: string): number {
|
|
220
262
|
return plan.waves.findIndex((wave) => wave.includes(stepId));
|
|
221
263
|
}
|
|
@@ -278,10 +320,29 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
|
|
|
278
320
|
// A code step runs a registered command in a subprocess. Nothing
|
|
279
321
|
// enumerates what that command wrote, so the step contributes no record
|
|
280
322
|
// and its window falls back to blaming the whole diff.
|
|
281
|
-
if (step === undefined
|
|
323
|
+
if (step === undefined) return null;
|
|
324
|
+
if (step.kind !== "agent") {
|
|
325
|
+
return {
|
|
326
|
+
recorded: [],
|
|
327
|
+
complete: false,
|
|
328
|
+
downgrades: [
|
|
329
|
+
{
|
|
330
|
+
reason: "code_step_untracked",
|
|
331
|
+
tool: null,
|
|
332
|
+
toolCallId: null,
|
|
333
|
+
runId: null,
|
|
334
|
+
stepId,
|
|
335
|
+
},
|
|
336
|
+
],
|
|
337
|
+
} satisfies WriteBoundaryAttribution;
|
|
338
|
+
}
|
|
282
339
|
const receipt = receiptsByStep.get(stepId);
|
|
283
340
|
if (receipt === undefined) return null;
|
|
284
|
-
|
|
341
|
+
if (dispatch.observedRunWriteAttribution !== undefined) {
|
|
342
|
+
return dispatch.observedRunWriteAttribution(receipt.runId);
|
|
343
|
+
}
|
|
344
|
+
const recorded = dispatch.observedRunWrites?.(receipt.runId) ?? null;
|
|
345
|
+
return recorded === null ? null : { recorded, complete: true, downgrades: [] };
|
|
285
346
|
},
|
|
286
347
|
onVerdict(verdict, path) {
|
|
287
348
|
// Concurrent changes are reported too. The window did not fail for them
|
|
@@ -318,6 +379,9 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
|
|
|
318
379
|
succeeded: true,
|
|
319
380
|
terminalRunId: replayedResult.terminalRunId,
|
|
320
381
|
costUsd: receiptsByStep.get(stepId)?.costUsd ?? 0,
|
|
382
|
+
// A replay whose receipt no longer exists on disk has no cost claim at
|
|
383
|
+
// all, which is `unknown` rather than a zero the projection invented.
|
|
384
|
+
costProvenance: receiptsByStep.get(stepId)?.costProvenance ?? "unknown",
|
|
321
385
|
result: replayedResult,
|
|
322
386
|
});
|
|
323
387
|
await fileStepAttempt(replayedResult.terminalRunId);
|
|
@@ -414,11 +478,24 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
|
|
|
414
478
|
// The fleet write boundary is deliberately enforced after the step. The enforcer in
|
|
415
479
|
// write-boundary-enforcer.ts snapshots and verifies the step window. Pre-emptive
|
|
416
480
|
// confinement for declared commands would require a command sandbox, which does not exist.
|
|
481
|
+
//
|
|
482
|
+
// That enforcement split is exactly why a step's declared `writes:` reaches
|
|
483
|
+
// typed intent as `relevant_paths` rather than as `write_roots`. The
|
|
484
|
+
// contract already said what this position may change and the enforcer
|
|
485
|
+
// already holds it; restating it as intent write roots would mint a second
|
|
486
|
+
// grant enforced at the per-tool worker seam, which refuses outright on the
|
|
487
|
+
// subprocess and ACP runtimes a fleet may legitimately route a step to.
|
|
488
|
+
// Carrying it as declared scope changes no authority and gets the
|
|
489
|
+
// contract's own paths into project-rule selection and worker context,
|
|
490
|
+
// where prose tokens scraped from the rendered prompt were standing in for
|
|
491
|
+
// a declaration the contract had already made.
|
|
492
|
+
const stepIntent = fleetStepIntent(step.writes);
|
|
417
493
|
const request: DispatchRequest = {
|
|
418
494
|
agentId: step.agentId,
|
|
419
495
|
executionRole: step.executionRole,
|
|
420
496
|
task: step.task,
|
|
421
497
|
cwd: workspaceRoot,
|
|
498
|
+
...(stepIntent === null ? {} : { intent: stepIntent }),
|
|
422
499
|
predecessorHandoffs: handoffs,
|
|
423
500
|
requestOrigin: "user",
|
|
424
501
|
lineage: {
|
|
@@ -571,11 +648,12 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
|
|
|
571
648
|
succeeded: stepSucceeded,
|
|
572
649
|
terminalRunId: receipt.runId,
|
|
573
650
|
costUsd: receipt.costUsd,
|
|
651
|
+
costProvenance: receipt.costProvenance,
|
|
574
652
|
receipt,
|
|
575
653
|
...(failureReason !== undefined ? { failureReason } : {}),
|
|
576
654
|
});
|
|
577
655
|
notice(
|
|
578
|
-
`step ${step.id} ${step.agentId}: ${stepSucceeded ? "succeeded" : "failed"}${failureReason !== undefined ? ` reason=${failureReason}` : ""} assignment=${handle.runId} terminal-run=${receipt.runId} cost
|
|
656
|
+
`step ${step.id} ${step.agentId}: ${stepSucceeded ? "succeeded" : "failed"}${failureReason !== undefined ? ` reason=${failureReason}` : ""} assignment=${handle.runId} terminal-run=${receipt.runId} cost=${renderCostAmount(receipt.costUsd, receipt.costProvenance)}`,
|
|
579
657
|
);
|
|
580
658
|
return {
|
|
581
659
|
stepId: step.id,
|
|
@@ -638,6 +716,8 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
|
|
|
638
716
|
succeeded: outcome.report.passed,
|
|
639
717
|
terminalRunId: outcome.record.runId,
|
|
640
718
|
costUsd: 0,
|
|
719
|
+
// No model ran, so nothing needed pricing. This zero was observed.
|
|
720
|
+
costProvenance: "known_free",
|
|
641
721
|
codeStep: outcome.record,
|
|
642
722
|
recordPath,
|
|
643
723
|
});
|
|
@@ -704,7 +784,7 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
|
|
|
704
784
|
cancel: (assignmentId) => dispatch.abort(assignmentId),
|
|
705
785
|
release: (ownerId) => {
|
|
706
786
|
if (ownerId === NO_WORKER_RESERVATION) return;
|
|
707
|
-
dispatch.reservations?.
|
|
787
|
+
dispatch.reservations?.release(ownerId);
|
|
708
788
|
},
|
|
709
789
|
releaseUnconsumed: (ownerId) => {
|
|
710
790
|
if (ownerId === NO_WORKER_RESERVATION) return;
|
|
@@ -783,6 +863,9 @@ export async function executeFleetRun(input: ExecuteFleetRunInput): Promise<Flee
|
|
|
783
863
|
result,
|
|
784
864
|
receipts,
|
|
785
865
|
totalCostUsd: receipts.reduce((sum, receipt) => sum + receipt.costUsd, 0),
|
|
866
|
+
totalCost: aggregateCostAmounts(
|
|
867
|
+
receipts.map((receipt) => ({ usd: receipt.costUsd, provenance: receipt.costProvenance })),
|
|
868
|
+
),
|
|
786
869
|
requiredStepCount: required.length,
|
|
787
870
|
succeededStepCount,
|
|
788
871
|
resolvedLoopCount: result.loops.length - failedLoops.length,
|
|
@@ -298,10 +298,20 @@ export interface StagePendingGateDecisionOptions {
|
|
|
298
298
|
stateDir?: string;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Where the durable decision artifacts live.
|
|
303
|
+
*
|
|
304
|
+
* Exported so a read-only projection can bound its own scan without duplicating
|
|
305
|
+
* the layout. Nothing outside this module may compose an artifact path from it.
|
|
306
|
+
*/
|
|
307
|
+
export function gateDecisionsDirectory(stateDir = clioStateDir()): string {
|
|
302
308
|
return join(stateDir, "gate-decisions");
|
|
303
309
|
}
|
|
304
310
|
|
|
311
|
+
function decisionsDirectory(stateDir = clioStateDir()): string {
|
|
312
|
+
return gateDecisionsDirectory(stateDir);
|
|
313
|
+
}
|
|
314
|
+
|
|
305
315
|
function pendingDecisionsDirectory(stateDir = clioStateDir()): string {
|
|
306
316
|
return join(decisionsDirectory(stateDir), "pending");
|
|
307
317
|
}
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import { COUNCIL_BALLOT_SHAPE, COUNCIL_BALLOT_VERDICT_MAX_BYTES } from "../agents/result-contract.js";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Roster bounds for a council. Two is the point of the topology: one seated
|
|
7
|
+
* agent is a plain dispatch, and there is nothing to compare. Capacity denials
|
|
8
|
+
* quote the floor because it is the reason a council cannot answer saturation
|
|
9
|
+
* by dispatching fewer members.
|
|
10
|
+
*/
|
|
11
|
+
export const COUNCIL_MIN_MEMBERS = 2;
|
|
12
|
+
export const COUNCIL_MAX_MEMBERS = 5;
|
|
13
|
+
|
|
5
14
|
export const REVIEWER_GATE_PROMPT = [
|
|
6
15
|
"You are a strict, read-only reviewer of one implementation run.",
|
|
7
16
|
"Inspect the supplied result and repository state with read-only tools.",
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only projection of the durable coordinator gate decisions.
|
|
3
|
+
*
|
|
4
|
+
* Unlike a council, a gate has a record of its own. A reviewer verdict and a
|
|
5
|
+
* judge's winner cannot be written back into the subject receipts without
|
|
6
|
+
* invalidating them, so the coordinator seals a separate integrity-covered
|
|
7
|
+
* artifact per decision that links the decider receipt to every subject
|
|
8
|
+
* receipt. {@link gate-decisions.ts} owns writing and verifying those; this
|
|
9
|
+
* module reads a bounded newest-first window of them and projects the shape.
|
|
10
|
+
*
|
|
11
|
+
* Two things stay behind. The decision `detail` is free prose that interpolates
|
|
12
|
+
* a thrown message, a pipeline failure reason, a protected artifact list, or a
|
|
13
|
+
* plan hash, so it is classified against a closed set and never quoted. And the
|
|
14
|
+
* receipt digests are omitted outright: a verified artifact carries one per
|
|
15
|
+
* subject by construction, so repeating them would be a fingerprint that tells
|
|
16
|
+
* a reader nothing the run id does not.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { clioStateDir } from "../../core/xdg.js";
|
|
22
|
+
import type { GateRouteCorrelation } from "./execution-role.js";
|
|
23
|
+
import {
|
|
24
|
+
type GateDecisionArtifact,
|
|
25
|
+
type GateDecisionOutcome,
|
|
26
|
+
gateDecisionsDirectory,
|
|
27
|
+
verifyGateDecisionArtifact,
|
|
28
|
+
} from "./gate-decisions.js";
|
|
29
|
+
|
|
30
|
+
/** How many decisions one projection reports, newest first. */
|
|
31
|
+
export const GATE_TOPOLOGY_MAX_DECISIONS = 8;
|
|
32
|
+
|
|
33
|
+
/** Subject receipts named per decision. A compete gate judges at most four. */
|
|
34
|
+
export const GATE_TOPOLOGY_MAX_SUBJECTS = 6;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* How many artifact files one listing will open.
|
|
38
|
+
*
|
|
39
|
+
* The directory is unbounded and a decision id sorts by its group before its
|
|
40
|
+
* timestamp, so the newest decision is only discoverable by reading each file's
|
|
41
|
+
* `createdAt`. The cap keeps the scan proportional on an installation that has
|
|
42
|
+
* accumulated gates for months, at the cost of missing a decision older than
|
|
43
|
+
* the cap. That is the right trade here, the same one {@link listFleetRuns}
|
|
44
|
+
* takes, because this window only ever wants the recent end.
|
|
45
|
+
*/
|
|
46
|
+
export const GATE_TOPOLOGY_MAX_SCAN = 128;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Why a gate ended the way it did, as a closed set.
|
|
50
|
+
*
|
|
51
|
+
* Every member is the coordinator's own classification of its own branch. The
|
|
52
|
+
* durable `detail` string those branches write is not repeated: several
|
|
53
|
+
* interpolate a receipt's failure reason, the names of protected artifacts a
|
|
54
|
+
* candidate touched, or an approval request id, and any of the three would
|
|
55
|
+
* carry prose across a boundary that exists to stop exactly that. Anything this
|
|
56
|
+
* classifier does not recognise becomes `unclassified` rather than crossing.
|
|
57
|
+
*/
|
|
58
|
+
export type GateDecisionReason =
|
|
59
|
+
| "all-candidates-failed"
|
|
60
|
+
| "builder-run-failed"
|
|
61
|
+
| "reviewer-run-failed"
|
|
62
|
+
| "reviewer-checks-failed"
|
|
63
|
+
| "reviewer-report-invalid"
|
|
64
|
+
| "judge-run-failed"
|
|
65
|
+
| "judge-result-invalid"
|
|
66
|
+
| "judge-winner-out-of-range"
|
|
67
|
+
| "judge-picked-failed-candidate"
|
|
68
|
+
| "winner-touches-protected-artifact"
|
|
69
|
+
| "operator-confirmed-winner"
|
|
70
|
+
| "full-auto-applied-winner"
|
|
71
|
+
| "unclassified";
|
|
72
|
+
|
|
73
|
+
export const GATE_DECISION_REASONS: ReadonlyArray<GateDecisionReason> = [
|
|
74
|
+
"all-candidates-failed",
|
|
75
|
+
"builder-run-failed",
|
|
76
|
+
"reviewer-run-failed",
|
|
77
|
+
"reviewer-checks-failed",
|
|
78
|
+
"reviewer-report-invalid",
|
|
79
|
+
"judge-run-failed",
|
|
80
|
+
"judge-result-invalid",
|
|
81
|
+
"judge-winner-out-of-range",
|
|
82
|
+
"judge-picked-failed-candidate",
|
|
83
|
+
"winner-touches-protected-artifact",
|
|
84
|
+
"operator-confirmed-winner",
|
|
85
|
+
"full-auto-applied-winner",
|
|
86
|
+
"unclassified",
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Prefix rules, first match wins, ordered so no rule shadows another.
|
|
91
|
+
*
|
|
92
|
+
* These are matched against the exact strings the coordinator writes. A rule
|
|
93
|
+
* failing to match costs its decision the specific reason and nothing else,
|
|
94
|
+
* because `unclassified` is a real answer here: the operator still learns that
|
|
95
|
+
* the gate ended and how, just not which branch produced the text.
|
|
96
|
+
*/
|
|
97
|
+
const REASON_RULES: ReadonlyArray<readonly [string, GateDecisionReason]> = [
|
|
98
|
+
["every candidate builder failed", "all-candidates-failed"],
|
|
99
|
+
["builder ended ", "builder-run-failed"],
|
|
100
|
+
["reviewer ended ", "reviewer-run-failed"],
|
|
101
|
+
["reviewer reported ", "reviewer-checks-failed"],
|
|
102
|
+
["reviewer did not return a valid verifier report", "reviewer-report-invalid"],
|
|
103
|
+
["judge ended ", "judge-run-failed"],
|
|
104
|
+
["judge result must", "judge-result-invalid"],
|
|
105
|
+
["judge result has unknown fields", "judge-result-invalid"],
|
|
106
|
+
["judge winner must be an integer", "judge-winner-out-of-range"],
|
|
107
|
+
["judge picked failed or missing candidate", "judge-picked-failed-candidate"],
|
|
108
|
+
["judge-selected candidate ", "winner-touches-protected-artifact"],
|
|
109
|
+
["operator confirmation ", "operator-confirmed-winner"],
|
|
110
|
+
["full-auto applied ", "full-auto-applied-winner"],
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The identifier shape the decision store already guarantees for both its ids
|
|
115
|
+
* and, transitively, its groups.
|
|
116
|
+
*
|
|
117
|
+
* A decision id is minted from a sanitized group plus a timestamp and random
|
|
118
|
+
* suffix, and a group is a coordinator-minted gate id. Re-enforcing the shape
|
|
119
|
+
* here means whatever wrote the file, the identifiers this projection reports
|
|
120
|
+
* can carry no separator, space, quote, or control byte.
|
|
121
|
+
*/
|
|
122
|
+
const SAFE_GATE_IDENTIFIER = /^[A-Za-z0-9._:-]{1,128}$/u;
|
|
123
|
+
|
|
124
|
+
/** The independence facts sealed on the decision, exactly as the artifact holds them. */
|
|
125
|
+
export type GateDecisionCorrelationFacts = Pick<
|
|
126
|
+
GateRouteCorrelation,
|
|
127
|
+
"agent" | "target" | "modelFamily" | "runtime" | "node" | "independent"
|
|
128
|
+
>;
|
|
129
|
+
|
|
130
|
+
export interface GateTopologyWinner {
|
|
131
|
+
/** 1-based candidate ordinal the judge picked. */
|
|
132
|
+
readonly index: number;
|
|
133
|
+
readonly runId: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface GateTopologyDecision {
|
|
137
|
+
readonly id: string;
|
|
138
|
+
readonly group: string;
|
|
139
|
+
readonly topology: "review" | "compete";
|
|
140
|
+
readonly cycle: number;
|
|
141
|
+
readonly outcome: GateDecisionOutcome;
|
|
142
|
+
readonly decidedAt: string;
|
|
143
|
+
/** Runs this decision graded, in the order the coordinator sealed them. */
|
|
144
|
+
readonly subjects: readonly string[];
|
|
145
|
+
readonly subjectsTruncated: boolean;
|
|
146
|
+
/** The run that produced the verdict, or null when none ran. */
|
|
147
|
+
readonly decider: string | null;
|
|
148
|
+
/** Present exactly when a decider ran; the store requires the pair. */
|
|
149
|
+
readonly correlation: GateDecisionCorrelationFacts | null;
|
|
150
|
+
readonly winner: GateTopologyWinner | null;
|
|
151
|
+
/** A prior judge decision this one confirmed, by id. */
|
|
152
|
+
readonly confirms: string | null;
|
|
153
|
+
readonly reason: GateDecisionReason | null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface GateTopologyResult {
|
|
157
|
+
/**
|
|
158
|
+
* Whether this installation has a decision store at all.
|
|
159
|
+
*
|
|
160
|
+
* An installation that has never run a gate and one whose gates all aged out
|
|
161
|
+
* both report no decisions, and they are different operator states, so the
|
|
162
|
+
* fact is read rather than inferred from an empty list.
|
|
163
|
+
*/
|
|
164
|
+
readonly present: boolean;
|
|
165
|
+
readonly decisions: readonly GateTopologyDecision[];
|
|
166
|
+
/** True when the scanned window held decisions older than the reported ones. */
|
|
167
|
+
readonly truncated: boolean;
|
|
168
|
+
/**
|
|
169
|
+
* Files in the scanned window whose integrity did not verify.
|
|
170
|
+
*
|
|
171
|
+
* The store's own read API drops these silently, which is right for a
|
|
172
|
+
* consumer that must not act on untrusted evidence and wrong for an operator
|
|
173
|
+
* surface: a gate artifact that no longer authenticates is exactly the thing
|
|
174
|
+
* worth knowing about. Counted, never opened for content.
|
|
175
|
+
*/
|
|
176
|
+
readonly unverifiable: number;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function classifyReason(detail: unknown): GateDecisionReason | null {
|
|
180
|
+
if (typeof detail !== "string" || detail.length === 0) return null;
|
|
181
|
+
return REASON_RULES.find(([prefix]) => detail.startsWith(prefix))?.[1] ?? "unclassified";
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function safeIdentifier(value: unknown): string | null {
|
|
185
|
+
return typeof value === "string" && SAFE_GATE_IDENTIFIER.test(value) ? value : null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Newest decision first. An unparseable stamp sorts oldest rather than throwing. */
|
|
189
|
+
function byNewest(a: GateDecisionArtifact, b: GateDecisionArtifact): number {
|
|
190
|
+
return (Date.parse(b.createdAt) || 0) - (Date.parse(a.createdAt) || 0);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Read a bounded window of verified decision artifacts, counting the rest.
|
|
195
|
+
*
|
|
196
|
+
* Reads defensively per file for the same reason the ledger scan does: one
|
|
197
|
+
* artifact written by another build, half-written, or removed between the
|
|
198
|
+
* listing and the open must cost that decision its row, never the whole
|
|
199
|
+
* listing. A file that parses but fails its own integrity check is a different
|
|
200
|
+
* state and is counted rather than dropped.
|
|
201
|
+
*/
|
|
202
|
+
function readDecisionWindow(stateDir: string): {
|
|
203
|
+
present: boolean;
|
|
204
|
+
artifacts: GateDecisionArtifact[];
|
|
205
|
+
unverifiable: number;
|
|
206
|
+
} {
|
|
207
|
+
const directory = gateDecisionsDirectory(stateDir);
|
|
208
|
+
if (!existsSync(directory)) return { present: false, artifacts: [], unverifiable: 0 };
|
|
209
|
+
let entries: string[];
|
|
210
|
+
try {
|
|
211
|
+
entries = readdirSync(directory).filter((name) => name.endsWith(".json"));
|
|
212
|
+
} catch {
|
|
213
|
+
// The directory is there and unreadable, which is not the same as absent.
|
|
214
|
+
return { present: true, artifacts: [], unverifiable: 0 };
|
|
215
|
+
}
|
|
216
|
+
const artifacts: GateDecisionArtifact[] = [];
|
|
217
|
+
let unverifiable = 0;
|
|
218
|
+
for (const name of entries.slice(0, GATE_TOPOLOGY_MAX_SCAN)) {
|
|
219
|
+
let artifact: GateDecisionArtifact;
|
|
220
|
+
try {
|
|
221
|
+
artifact = JSON.parse(readFileSync(join(directory, name), "utf8")) as GateDecisionArtifact;
|
|
222
|
+
} catch {
|
|
223
|
+
unverifiable += 1;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (verifyGateDecisionArtifact(artifact).ok) artifacts.push(artifact);
|
|
227
|
+
else unverifiable += 1;
|
|
228
|
+
}
|
|
229
|
+
artifacts.sort(byNewest);
|
|
230
|
+
return { present: true, artifacts, unverifiable };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function projectDecision(artifact: GateDecisionArtifact): GateTopologyDecision | null {
|
|
234
|
+
const id = safeIdentifier(artifact.id);
|
|
235
|
+
const group = safeIdentifier(artifact.group);
|
|
236
|
+
// The store mints both and already refuses either outside this shape, so a
|
|
237
|
+
// stored value failing it means something else wrote the file. A decision
|
|
238
|
+
// this projection cannot name is one it will not report.
|
|
239
|
+
if (id === null || group === null) return null;
|
|
240
|
+
const allSubjects = artifact.subjects.map((subject) => safeIdentifier(subject.runId));
|
|
241
|
+
if (allSubjects.some((runId) => runId === null)) return null;
|
|
242
|
+
const subjects = (allSubjects as string[]).slice(0, GATE_TOPOLOGY_MAX_SUBJECTS);
|
|
243
|
+
const decider = artifact.decider === undefined ? null : safeIdentifier(artifact.decider.runId);
|
|
244
|
+
if (artifact.decider !== undefined && decider === null) return null;
|
|
245
|
+
let winner: GateTopologyWinner | null = null;
|
|
246
|
+
if (artifact.winner !== undefined) {
|
|
247
|
+
const runId = safeIdentifier(artifact.winner.subject.runId);
|
|
248
|
+
if (runId === null) return null;
|
|
249
|
+
winner = { index: artifact.winner.index, runId };
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
id,
|
|
253
|
+
group,
|
|
254
|
+
topology: artifact.topology,
|
|
255
|
+
cycle: artifact.cycle,
|
|
256
|
+
outcome: artifact.outcome,
|
|
257
|
+
decidedAt: new Date(artifact.createdAt).toISOString(),
|
|
258
|
+
subjects,
|
|
259
|
+
subjectsTruncated: allSubjects.length > subjects.length,
|
|
260
|
+
decider,
|
|
261
|
+
correlation: artifact.correlation === undefined ? null : { ...artifact.correlation },
|
|
262
|
+
winner,
|
|
263
|
+
confirms: artifact.confirmation === undefined ? null : safeIdentifier(artifact.confirmation.id),
|
|
264
|
+
reason: classifyReason(artifact.detail),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Bounded newest-first coordinator gate decisions.
|
|
270
|
+
*
|
|
271
|
+
* Selected by this function, never by a caller's argument, so a host that
|
|
272
|
+
* invokes the command behind it knows the process cannot be steered into a
|
|
273
|
+
* different window.
|
|
274
|
+
*/
|
|
275
|
+
export function gateTopology(stateDir: string = clioStateDir()): GateTopologyResult {
|
|
276
|
+
const { present, artifacts, unverifiable } = readDecisionWindow(stateDir);
|
|
277
|
+
const window = artifacts.slice(0, GATE_TOPOLOGY_MAX_DECISIONS);
|
|
278
|
+
const decisions: GateTopologyDecision[] = [];
|
|
279
|
+
for (const artifact of window) {
|
|
280
|
+
const decision = projectDecision(artifact);
|
|
281
|
+
if (decision !== null) decisions.push(decision);
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
present,
|
|
285
|
+
decisions,
|
|
286
|
+
truncated: artifacts.length > window.length || decisions.length !== window.length,
|
|
287
|
+
unverifiable,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pure heartbeat watchdog classifier (Phase 6 slice 3).
|
|
3
3
|
*
|
|
4
|
-
* Maps a last-seen
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Maps a last-seen monotonic stamp to one of three states. The dispatch domain
|
|
5
|
+
* drives this on each scheduler tick; the orchestrator uses `stale` to surface
|
|
6
|
+
* a warning in the UI and `dead` to reap a subprocess whose event stream has
|
|
7
|
+
* gone quiet for too long.
|
|
8
8
|
*
|
|
9
|
-
* No I/O, no clocks of its own. Callers pass
|
|
10
|
-
* and easy to test.
|
|
9
|
+
* No I/O, no clocks of its own. Callers pass both monotonic values to keep the
|
|
10
|
+
* function pure and easy to test.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
export interface HeartbeatSpec {
|
|
@@ -17,10 +17,34 @@ export interface HeartbeatSpec {
|
|
|
17
17
|
|
|
18
18
|
export type HeartbeatStatus = "alive" | "stale" | "dead";
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Mutable heartbeat facts shared by a transport and the dispatch watchdog.
|
|
22
|
+
* `current` is an absolute wall-clock instant for display and persistence.
|
|
23
|
+
* `monotonic` is from this process's monotonic clock and is the only value
|
|
24
|
+
* liveness code may compare with a later clock read.
|
|
25
|
+
*
|
|
26
|
+
* The monotonic field is optional only for injected legacy test handles. Real
|
|
27
|
+
* transports always provide it. Treating an old handle's `current` as its
|
|
28
|
+
* monotonic stamp preserves the structural test seam without reintroducing a
|
|
29
|
+
* subtraction between two wall-clock reads in the product path.
|
|
30
|
+
*/
|
|
31
|
+
export interface HeartbeatStamp {
|
|
32
|
+
current: number;
|
|
33
|
+
monotonic?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
20
36
|
export const DEFAULT_HEARTBEAT_SPEC: HeartbeatSpec = { windowMs: 5000, graceMs: 10000 };
|
|
21
37
|
|
|
22
|
-
export function
|
|
23
|
-
|
|
38
|
+
export function heartbeatMonotonicAt(heartbeat: HeartbeatStamp): number {
|
|
39
|
+
return heartbeat.monotonic ?? heartbeat.current;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function classifyHeartbeat(
|
|
43
|
+
heartbeatMonotonicAt: number,
|
|
44
|
+
monotonicNow: number,
|
|
45
|
+
spec: HeartbeatSpec,
|
|
46
|
+
): HeartbeatStatus {
|
|
47
|
+
const age = monotonicNow - heartbeatMonotonicAt;
|
|
24
48
|
if (age <= spec.windowMs) return "alive";
|
|
25
49
|
if (age <= spec.windowMs + spec.graceMs) return "stale";
|
|
26
50
|
return "dead";
|