@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,218 @@
|
|
|
1
|
+
import { readdirSync, rmdirSync, rmSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { toolchainRoot } from "./paths.js";
|
|
4
|
+
import { findPinnedTool } from "./registry.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Deleting vendored installs, and pruning the versions a pin bump superseded.
|
|
8
|
+
*
|
|
9
|
+
* Everything here only ever unlinks inside `<data>/tools/<id>/`, a directory
|
|
10
|
+
* Clio created by downloading it, so nothing an operator wrote is reachable
|
|
11
|
+
* from this file. That is what makes removal safe without a confirmation
|
|
12
|
+
* prompt: the worst outcome is a re-download of bytes the registry pins by
|
|
13
|
+
* checksum anyway.
|
|
14
|
+
*
|
|
15
|
+
* Version directories are the only thing considered. A name starting with `.`
|
|
16
|
+
* is an installer's staging or retired directory (`.<version>.incomplete-`,
|
|
17
|
+
* `.<version>.replaced-`), and a prune that raced an install and deleted one
|
|
18
|
+
* would corrupt that install. A full remove is different: it takes the whole
|
|
19
|
+
* `<id>` tree because no install of that tool is meant to survive it.
|
|
20
|
+
*
|
|
21
|
+
* A staging directory old enough to be dead is swept anyway. A killed install
|
|
22
|
+
* used to leave one behind permanently, invisible to every verb and reachable
|
|
23
|
+
* only by an operator who went reading under the data root.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export interface ToolRemoveOptions {
|
|
27
|
+
/** Vendor root override. Defaults to the data root's `tools` directory. */
|
|
28
|
+
root?: string;
|
|
29
|
+
/**
|
|
30
|
+
* How old a staging directory must be before it counts as abandoned.
|
|
31
|
+
* Defaults to `STALE_STAGING_MS`. Tests set it; nothing else should need to.
|
|
32
|
+
*/
|
|
33
|
+
staleStagingMs?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* How long a staging directory may sit before a sweep treats it as dead.
|
|
38
|
+
*
|
|
39
|
+
* The window is far shorter than it looks. `installPinnedTool` downloads every
|
|
40
|
+
* asset and verifies every checksum *before* it creates the staging directory,
|
|
41
|
+
* so that directory's whole life is a handful of local file writes and two
|
|
42
|
+
* renames: sub-second normally, seconds on a struggling disk. An hour is twelve
|
|
43
|
+
* times the installer's own per-asset download timeout and orders of magnitude
|
|
44
|
+
* past the writes, which leaves no plausible live install inside it.
|
|
45
|
+
*
|
|
46
|
+
* The pid in the name is deliberately not consulted. Pids are reused, a pid
|
|
47
|
+
* belonging to another user answers a liveness probe with EPERM rather than an
|
|
48
|
+
* honest yes or no, and a data root shared over a network filesystem makes the
|
|
49
|
+
* number meaningless. Age is the one signal that means the same thing
|
|
50
|
+
* everywhere.
|
|
51
|
+
*/
|
|
52
|
+
export const STALE_STAGING_MS = 3_600_000;
|
|
53
|
+
|
|
54
|
+
/** What one sweep of a tool's directory deleted, and what it could not. */
|
|
55
|
+
export interface ToolRemoveResult {
|
|
56
|
+
ok: boolean;
|
|
57
|
+
id: string;
|
|
58
|
+
/** `<root>/<id>`, whether or not it exists. */
|
|
59
|
+
dir: string;
|
|
60
|
+
/** Version directories this sweep deleted, oldest name first. */
|
|
61
|
+
removed: string[];
|
|
62
|
+
/** Version directories that survived a delete attempt, with the reason. */
|
|
63
|
+
failed: ReadonlyArray<{ version: string; error: string }>;
|
|
64
|
+
/** Abandoned staging directories this sweep collected, by name. */
|
|
65
|
+
staleStaging: string[];
|
|
66
|
+
message: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Version directories currently installed for a tool, sorted. */
|
|
70
|
+
export function installedToolVersions(id: string, options: ToolRemoveOptions = {}): string[] {
|
|
71
|
+
const dir = join(options.root ?? toolchainRoot(), id);
|
|
72
|
+
let names: string[];
|
|
73
|
+
try {
|
|
74
|
+
names = readdirSync(dir);
|
|
75
|
+
} catch {
|
|
76
|
+
// A tool nobody ever installed has no directory, which is not an error:
|
|
77
|
+
// the answer to "which versions are installed" is none.
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
return names
|
|
81
|
+
.filter((name) => !name.startsWith(".") && isDirectory(join(dir, name)))
|
|
82
|
+
.sort((a, b) => a.localeCompare(b));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Delete every vendored version of a tool, then the tool's own directory.
|
|
87
|
+
*
|
|
88
|
+
* A tool with nothing installed is a no-op that reports success: the operator
|
|
89
|
+
* asked for a state ("this tool is not vendored") that already holds, and
|
|
90
|
+
* failing them for it would make the verb unusable in a script.
|
|
91
|
+
*/
|
|
92
|
+
export function removeTool(id: string, options: ToolRemoveOptions = {}): ToolRemoveResult {
|
|
93
|
+
if (findPinnedTool(id) === null) {
|
|
94
|
+
const dir = join(options.root ?? toolchainRoot(), id);
|
|
95
|
+
return { ok: false, id, dir, removed: [], failed: [], staleStaging: [], message: `unknown tool: ${id}` };
|
|
96
|
+
}
|
|
97
|
+
return removeVersions(id, null, options);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Delete every vendored version of a tool except `keep`.
|
|
102
|
+
*
|
|
103
|
+
* Called by the installer once a version is in place, so a pin bump leaves one
|
|
104
|
+
* version directory behind rather than one per version the machine ever ran.
|
|
105
|
+
* The kept version is not required to exist: pruning to a version that is not
|
|
106
|
+
* installed empties the tool, which is what `--force` recovery wants.
|
|
107
|
+
*/
|
|
108
|
+
export function pruneSupersededVersions(id: string, keep: string, options: ToolRemoveOptions = {}): ToolRemoveResult {
|
|
109
|
+
return removeVersions(id, keep, options);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function removeVersions(id: string, keep: string | null, options: ToolRemoveOptions): ToolRemoveResult {
|
|
113
|
+
const root = options.root ?? toolchainRoot();
|
|
114
|
+
const dir = join(root, id);
|
|
115
|
+
const versions = installedToolVersions(id, { root }).filter((version) => version !== keep);
|
|
116
|
+
const removed: string[] = [];
|
|
117
|
+
const failed: { version: string; error: string }[] = [];
|
|
118
|
+
for (const version of versions) {
|
|
119
|
+
try {
|
|
120
|
+
rmSync(join(dir, version), { recursive: true, force: true });
|
|
121
|
+
removed.push(version);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
failed.push({ version, error: error instanceof Error ? error.message : String(error) });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Before the rmdir below, so an install killed weeks ago cannot keep the tool
|
|
127
|
+
// directory alive after every version of it is gone.
|
|
128
|
+
const staleStaging = sweepStaleStaging(dir, options.staleStagingMs ?? STALE_STAGING_MS);
|
|
129
|
+
if (keep === null && failed.length === 0) {
|
|
130
|
+
// The tool is gone, so its directory is litter. `rmdir` takes it only when
|
|
131
|
+
// it is empty, so a staging directory young enough to belong to a running
|
|
132
|
+
// installer makes this fail instead of destroying that install.
|
|
133
|
+
try {
|
|
134
|
+
rmdirSync(dir);
|
|
135
|
+
} catch {
|
|
136
|
+
// Either it was never there or something still lives in it. Neither
|
|
137
|
+
// changes what this call promised: no version of the tool remains.
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
ok: failed.length === 0,
|
|
142
|
+
id,
|
|
143
|
+
dir,
|
|
144
|
+
removed,
|
|
145
|
+
failed,
|
|
146
|
+
staleStaging,
|
|
147
|
+
message: describeRemoval(id, dir, keep, removed, failed, staleStaging),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Delete the abandoned staging directories under one tool.
|
|
153
|
+
*
|
|
154
|
+
* Only names the installer produces are considered, so an operator who parked
|
|
155
|
+
* something of their own under `<data>/tools/<id>` with a leading dot keeps it.
|
|
156
|
+
* A directory whose age cannot be read is left alone: an unreadable stat is not
|
|
157
|
+
* evidence of death.
|
|
158
|
+
*/
|
|
159
|
+
function sweepStaleStaging(dir: string, staleMs: number): string[] {
|
|
160
|
+
let names: string[];
|
|
161
|
+
try {
|
|
162
|
+
names = readdirSync(dir);
|
|
163
|
+
} catch {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
const cutoff = Date.now() - staleMs;
|
|
167
|
+
const swept: string[] = [];
|
|
168
|
+
for (const name of names.sort((a, b) => a.localeCompare(b))) {
|
|
169
|
+
if (!STAGING_NAME.test(name)) continue;
|
|
170
|
+
const stat = statSync(join(dir, name), { throwIfNoEntry: false });
|
|
171
|
+
if (stat === undefined || !stat.isDirectory() || stat.mtimeMs > cutoff) continue;
|
|
172
|
+
try {
|
|
173
|
+
rmSync(join(dir, name), { recursive: true, force: true });
|
|
174
|
+
swept.push(name);
|
|
175
|
+
} catch {
|
|
176
|
+
// A staging directory that resists deletion is litter, not a failure:
|
|
177
|
+
// nothing resolves it, and reporting it would turn a successful install
|
|
178
|
+
// into a failed one over a directory the ladder never looks at.
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return swept;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** `.<version>.incomplete-<pid>-<t>` and `.<version>.replaced-<pid>-<t>`. */
|
|
185
|
+
const STAGING_NAME = /^\.[^/]+\.(?:incomplete|replaced)-\d+-[a-z0-9]+$/;
|
|
186
|
+
|
|
187
|
+
function describeRemoval(
|
|
188
|
+
id: string,
|
|
189
|
+
dir: string,
|
|
190
|
+
keep: string | null,
|
|
191
|
+
removed: ReadonlyArray<string>,
|
|
192
|
+
failed: ReadonlyArray<{ version: string; error: string }>,
|
|
193
|
+
staleStaging: ReadonlyArray<string>,
|
|
194
|
+
): string {
|
|
195
|
+
// Reported as a suffix rather than as the headline: an abandoned staging
|
|
196
|
+
// directory is housekeeping, and the operator asked about versions.
|
|
197
|
+
const swept =
|
|
198
|
+
staleStaging.length === 0
|
|
199
|
+
? ""
|
|
200
|
+
: `; swept ${staleStaging.length} abandoned install ${staleStaging.length === 1 ? "directory" : "directories"}`;
|
|
201
|
+
const trouble = failed.map((entry) => `${entry.version} (${entry.error})`).join(", ");
|
|
202
|
+
if (failed.length > 0) {
|
|
203
|
+
const done = removed.length > 0 ? `removed ${removed.join(", ")}; ` : "";
|
|
204
|
+
return `${done}could not remove ${id} ${trouble} under ${dir}${swept}`;
|
|
205
|
+
}
|
|
206
|
+
if (removed.length === 0) {
|
|
207
|
+
return keep === null
|
|
208
|
+
? `${id} is not installed under ${dir}; nothing to remove${swept}`
|
|
209
|
+
: `${id} has no superseded versions under ${dir}${swept}`;
|
|
210
|
+
}
|
|
211
|
+
const what = `${id} ${removed.join(", ")}`;
|
|
212
|
+
const headline = keep === null ? `removed ${what} from ${dir}` : `pruned ${what} from ${dir}, keeping ${keep}`;
|
|
213
|
+
return `${headline}${swept}`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function isDirectory(path: string): boolean {
|
|
217
|
+
return statSync(path, { throwIfNoEntry: false })?.isDirectory() === true;
|
|
218
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { delimiter, join } from "node:path";
|
|
3
|
+
import { toolVersionDir, vendoredBinaryPath } from "./paths.js";
|
|
4
|
+
import { currentToolPlatform, findPinnedTool, findPinnedToolByBinary, PINNED_TOOLS } from "./registry.js";
|
|
5
|
+
import type { PinnedTool, ToolPathCandidate, ToolResolution, ToolStatus } from "./types.js";
|
|
6
|
+
import { probeBinaryVersion, satisfiesMinimum } from "./version.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The shared resolution ladder: PATH, then the vendored pin, then nothing.
|
|
10
|
+
*
|
|
11
|
+
* PATH comes first because a tool the operator installed themselves is the one
|
|
12
|
+
* they maintain, and Clio downloading a second copy of a program already on the
|
|
13
|
+
* machine is a surprise. It only wins when its version clears the registry's
|
|
14
|
+
* floor, because the alternative is Clio driving a surface that release does not
|
|
15
|
+
* have and reporting the resulting failure as a bug in the feature.
|
|
16
|
+
*
|
|
17
|
+
* Nothing here downloads, spawns a server, or writes to disk. It is safe on the
|
|
18
|
+
* interactive startup path and inside doctor, both of which promise to create
|
|
19
|
+
* nothing.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** First executable named `name` on PATH, or null. */
|
|
23
|
+
export function findExecutableOnPath(name: string): string | null {
|
|
24
|
+
const pathEnv = process.env.PATH;
|
|
25
|
+
if (!pathEnv) return null;
|
|
26
|
+
const candidates = process.platform === "win32" ? [`${name}.exe`, name] : [name];
|
|
27
|
+
for (const dir of pathEnv.split(delimiter)) {
|
|
28
|
+
if (!dir) continue;
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
const full = join(dir, candidate);
|
|
31
|
+
const stat = statSync(full, { throwIfNoEntry: false });
|
|
32
|
+
if (stat?.isFile() && (process.platform === "win32" || (stat.mode & 0o111) !== 0)) return full;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve one executable name through the ladder.
|
|
40
|
+
*
|
|
41
|
+
* A name that is not in the registry gets a plain PATH lookup: there is no pin
|
|
42
|
+
* to compare against and nothing vendored to fall back to, so the answer is
|
|
43
|
+
* whatever PATH says.
|
|
44
|
+
*/
|
|
45
|
+
export function resolveToolBinary(name: string): ToolResolution {
|
|
46
|
+
const entry = findPinnedToolByBinary(name) ?? findPinnedTool(name);
|
|
47
|
+
if (entry === null) {
|
|
48
|
+
const found = findExecutableOnPath(name);
|
|
49
|
+
return {
|
|
50
|
+
source: found === null ? "none" : "path",
|
|
51
|
+
binaryPath: found,
|
|
52
|
+
version: null,
|
|
53
|
+
entry: null,
|
|
54
|
+
pathCandidate: found === null ? null : { path: found, version: null, satisfiesMinimum: true },
|
|
55
|
+
vendoredPath: null,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// An entry asked for by tool id resolves its primary binary; asked for by
|
|
59
|
+
// binary name it resolves that binary, so `ya` does not silently become `yazi`.
|
|
60
|
+
const binary = entry.binaries.includes(name) ? name : entry.primaryBinary;
|
|
61
|
+
return resolveEntryBinary(entry, binary);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The ladder for a known registry row and one of its binaries. */
|
|
65
|
+
export function resolveEntryBinary(entry: PinnedTool, binary: string): ToolResolution {
|
|
66
|
+
const pathCandidate = probePathCandidate(entry, binary);
|
|
67
|
+
const vendored = vendoredBinaryPath(entry.id, entry.version, binary);
|
|
68
|
+
const vendoredStat = statSync(vendored, { throwIfNoEntry: false });
|
|
69
|
+
const vendoredPath = vendoredStat?.isFile() ? vendored : null;
|
|
70
|
+
|
|
71
|
+
if (pathCandidate?.satisfiesMinimum) {
|
|
72
|
+
return {
|
|
73
|
+
source: "path",
|
|
74
|
+
binaryPath: pathCandidate.path,
|
|
75
|
+
version: pathCandidate.version,
|
|
76
|
+
entry,
|
|
77
|
+
pathCandidate,
|
|
78
|
+
vendoredPath,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (vendoredPath !== null) {
|
|
82
|
+
return {
|
|
83
|
+
source: "vendored",
|
|
84
|
+
binaryPath: vendoredPath,
|
|
85
|
+
version: entry.version,
|
|
86
|
+
entry,
|
|
87
|
+
pathCandidate,
|
|
88
|
+
vendoredPath,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { source: "none", binaryPath: null, version: null, entry, pathCandidate, vendoredPath: null };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Per-entry state for the CLI and doctor. */
|
|
95
|
+
export function toolStatus(entry: PinnedTool): ToolStatus {
|
|
96
|
+
const platform = currentToolPlatform();
|
|
97
|
+
const supported = platform !== null && entry.downloads[platform] !== undefined;
|
|
98
|
+
const resolution = resolveEntryBinary(entry, entry.primaryBinary);
|
|
99
|
+
return {
|
|
100
|
+
id: entry.id,
|
|
101
|
+
version: entry.version,
|
|
102
|
+
license: entry.license,
|
|
103
|
+
platform,
|
|
104
|
+
supported,
|
|
105
|
+
installed: resolution.vendoredPath !== null,
|
|
106
|
+
installDir: toolVersionDir(entry.id, entry.version),
|
|
107
|
+
resolution,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Per-entry state for every row, in registry order. */
|
|
112
|
+
export function toolStatuses(): ToolStatus[] {
|
|
113
|
+
return PINNED_TOOLS.map((entry) => toolStatus(entry));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The one command that fixes a tool Clio cannot resolve. */
|
|
117
|
+
export function installRemedy(id: string): string {
|
|
118
|
+
return `clio-coder tools install ${id}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* What a rejected PATH copy was, and which floor it failed. Null when there is
|
|
123
|
+
* no PATH copy or when the one there was accepted.
|
|
124
|
+
*
|
|
125
|
+
* The floors are deliberately conservative: they sit at the pin for herdr and
|
|
126
|
+
* yazi because Clio drives socket and plugin surfaces verified against only
|
|
127
|
+
* that release. That is a defensible choice and a costly one, since it rejects
|
|
128
|
+
* binaries an operator installed themselves and that mostly work. The cost is
|
|
129
|
+
* only payable if the rejection is legible, so every renderer of a rejection
|
|
130
|
+
* names the path, the version found, and the floor it missed, and never reports
|
|
131
|
+
* a rejected copy as an absent one.
|
|
132
|
+
*/
|
|
133
|
+
export function describeFloorRejection(status: ToolStatus): string | null {
|
|
134
|
+
const candidate = status.resolution.pathCandidate;
|
|
135
|
+
if (candidate === null || candidate.satisfiesMinimum) return null;
|
|
136
|
+
const floor = status.resolution.entry?.minimumVersion ?? status.version;
|
|
137
|
+
const found = candidate.version === null ? "an unreadable version" : candidate.version;
|
|
138
|
+
return `PATH copy ${candidate.path} is ${found}, below the ${floor} floor`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* One sentence describing where a tool resolved and how its version compares to
|
|
143
|
+
* the pin. Shared by `clio-coder tools status` and the doctor rows so the two
|
|
144
|
+
* cannot drift into describing the same machine differently.
|
|
145
|
+
*/
|
|
146
|
+
export function describeResolution(status: ToolStatus): string {
|
|
147
|
+
const { resolution } = status;
|
|
148
|
+
const rejection = describeFloorRejection(status);
|
|
149
|
+
if (resolution.source === "path") {
|
|
150
|
+
const version = resolution.version ?? "unreadable version";
|
|
151
|
+
return `PATH ${resolution.binaryPath} (${version}, pin ${status.version})`;
|
|
152
|
+
}
|
|
153
|
+
if (resolution.source === "vendored") {
|
|
154
|
+
// The remedy is deliberately absent here. The rejection cost the operator
|
|
155
|
+
// nothing: Clio has a copy at the pin and is running it, and printing an
|
|
156
|
+
// install command that would change nothing is its own kind of dishonesty.
|
|
157
|
+
const rejected = rejection === null ? "" : `; ${rejection}, so Clio runs the vendored copy`;
|
|
158
|
+
return `vendored ${resolution.binaryPath} (${status.version})${rejected}`;
|
|
159
|
+
}
|
|
160
|
+
if (!status.supported) {
|
|
161
|
+
const platform = status.platform ?? `${process.platform}-${process.arch}`;
|
|
162
|
+
// No remedy either: there is no asset to install on this machine, so the
|
|
163
|
+
// rejection is the whole story and naming a command would be a dead end.
|
|
164
|
+
const rejected = rejection === null ? "" : `; ${rejection}`;
|
|
165
|
+
return `not installed and no pinned asset for this platform (${platform})${rejected}`;
|
|
166
|
+
}
|
|
167
|
+
if (rejection !== null) {
|
|
168
|
+
return `${rejection}, and nothing is vendored (install with \`${installRemedy(status.id)}\`)`;
|
|
169
|
+
}
|
|
170
|
+
return `not found (install with \`${installRemedy(status.id)}\`)`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function probePathCandidate(entry: PinnedTool, binary: string): ToolPathCandidate | null {
|
|
174
|
+
const found = findExecutableOnPath(binary);
|
|
175
|
+
if (found === null) return null;
|
|
176
|
+
// The floor is a property of the tool, and only the primary binary is asked
|
|
177
|
+
// for its version, so a secondary binary inherits the primary's verdict from
|
|
178
|
+
// the same install root rather than being probed with flags it may not have.
|
|
179
|
+
const probeTarget = binary === entry.primaryBinary ? found : (findExecutableOnPath(entry.primaryBinary) ?? found);
|
|
180
|
+
const version = probeBinaryVersion(probeTarget, entry.versionArgs);
|
|
181
|
+
return { path: found, version, satisfiesMinimum: satisfiesMinimum(version, entry.minimumVersion) };
|
|
182
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shapes for the pinned external-tool registry.
|
|
3
|
+
*
|
|
4
|
+
* Clio uses a handful of third-party terminal programs (a pane multiplexer, a
|
|
5
|
+
* file manager, a transfer tool). None of them is a dependency of the product:
|
|
6
|
+
* every consumer degrades when the tool is absent. What the registry adds is a
|
|
7
|
+
* single place where each tool's exact version, download location, checksum,
|
|
8
|
+
* license, and acceptable PATH floor are written down, so an install is
|
|
9
|
+
* reproducible and an operator can see which copy Clio would actually run.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Platforms the registry can describe a download for. */
|
|
13
|
+
export type ToolPlatform = "linux-x64" | "linux-arm64" | "darwin-x64" | "darwin-arm64" | "win32-x64";
|
|
14
|
+
|
|
15
|
+
/** How a downloaded asset is packed. `raw` is a bare executable. */
|
|
16
|
+
export type ToolArchiveKind = "raw" | "zip" | "tar.gz";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One platform's asset for one pinned tool.
|
|
20
|
+
*
|
|
21
|
+
* `sha256` is the hash of the asset exactly as downloaded, before any
|
|
22
|
+
* unpacking, so the verification happens on the bytes that crossed the network
|
|
23
|
+
* rather than on whatever an extractor produced from them.
|
|
24
|
+
*/
|
|
25
|
+
export interface PinnedToolDownload {
|
|
26
|
+
url: string;
|
|
27
|
+
sha256: string;
|
|
28
|
+
archive: ToolArchiveKind;
|
|
29
|
+
/**
|
|
30
|
+
* Binary name to its path inside the archive. A `raw` download has exactly
|
|
31
|
+
* one entry whose value is the empty string: the asset is the binary.
|
|
32
|
+
*/
|
|
33
|
+
binaryMembers: Readonly<Record<string, string>>;
|
|
34
|
+
/**
|
|
35
|
+
* Files copied beside the binaries, given as paths inside the archive. This
|
|
36
|
+
* is where a tool's own LICENSE comes from when upstream ships it in the
|
|
37
|
+
* release asset. Each lands under its basename in the version directory.
|
|
38
|
+
*/
|
|
39
|
+
documentMembers: ReadonlyArray<string>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A document fetched separately from the asset, for tools whose release asset
|
|
44
|
+
* is a bare binary carrying no license text. Pinned by checksum like the asset.
|
|
45
|
+
*/
|
|
46
|
+
export interface PinnedToolDocument {
|
|
47
|
+
name: string;
|
|
48
|
+
url: string;
|
|
49
|
+
sha256: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** One row of the pinned table. */
|
|
53
|
+
export interface PinnedTool {
|
|
54
|
+
id: string;
|
|
55
|
+
version: string;
|
|
56
|
+
summary: string;
|
|
57
|
+
homepage: string;
|
|
58
|
+
/** SPDX identifier of the upstream license. */
|
|
59
|
+
license: string;
|
|
60
|
+
/** Every executable the install places, in the order they are reported. */
|
|
61
|
+
binaries: ReadonlyArray<string>;
|
|
62
|
+
/** The executable whose `--version` answers for the whole tool. */
|
|
63
|
+
primaryBinary: string;
|
|
64
|
+
/**
|
|
65
|
+
* Lowest version Clio accepts from a copy already on PATH. Below this the
|
|
66
|
+
* resolution ladder falls through to the vendored copy, because the surface
|
|
67
|
+
* Clio drives is only known to exist at or above this version.
|
|
68
|
+
*/
|
|
69
|
+
minimumVersion: string;
|
|
70
|
+
/** Arguments that make the primary binary print its version. */
|
|
71
|
+
versionArgs: ReadonlyArray<string>;
|
|
72
|
+
downloads: Readonly<Partial<Record<ToolPlatform, PinnedToolDownload>>>;
|
|
73
|
+
/** Documents fetched alongside the asset. Usually empty. */
|
|
74
|
+
documents: ReadonlyArray<PinnedToolDocument>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Where a resolved binary came from. */
|
|
78
|
+
export type ToolSource = "path" | "vendored" | "none";
|
|
79
|
+
|
|
80
|
+
/** What a PATH candidate looked like, kept even when it was rejected. */
|
|
81
|
+
export interface ToolPathCandidate {
|
|
82
|
+
path: string;
|
|
83
|
+
version: string | null;
|
|
84
|
+
satisfiesMinimum: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The answer of one trip down the resolution ladder. */
|
|
88
|
+
export interface ToolResolution {
|
|
89
|
+
source: ToolSource;
|
|
90
|
+
/** Absolute path of the binary Clio would run, or null when unresolved. */
|
|
91
|
+
binaryPath: string | null;
|
|
92
|
+
/** Version of the resolved binary when it could be read. */
|
|
93
|
+
version: string | null;
|
|
94
|
+
/** The registry row this resolution used, when the name is a pinned tool. */
|
|
95
|
+
entry: PinnedTool | null;
|
|
96
|
+
/** A PATH copy that was found, whether or not it was accepted. */
|
|
97
|
+
pathCandidate: ToolPathCandidate | null;
|
|
98
|
+
/** The vendored copy's path when the pinned version is installed. */
|
|
99
|
+
vendoredPath: string | null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Per-entry state, the shape the CLI and doctor both render. */
|
|
103
|
+
export interface ToolStatus {
|
|
104
|
+
id: string;
|
|
105
|
+
version: string;
|
|
106
|
+
license: string;
|
|
107
|
+
platform: ToolPlatform | null;
|
|
108
|
+
/** False when the registry has no asset for the running platform. */
|
|
109
|
+
supported: boolean;
|
|
110
|
+
installed: boolean;
|
|
111
|
+
installDir: string;
|
|
112
|
+
resolution: ToolResolution;
|
|
113
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Version reading and comparison for third-party binaries.
|
|
5
|
+
*
|
|
6
|
+
* Every tool in the registry answers `--version` with something different:
|
|
7
|
+
* `herdr 0.8.2` on one line, `croc version 11.3.6`, and a six-line block from
|
|
8
|
+
* yazi whose version sits on the second line after a label. Nothing here tries
|
|
9
|
+
* to know those formats. The first dotted triple in the output is the version,
|
|
10
|
+
* which is true of all three and of every convention they might move to.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const VERSION_PATTERN = /(\d+)\.(\d+)\.(\d+)/;
|
|
14
|
+
|
|
15
|
+
/** How long a version probe may take before the binary is treated as unusable. */
|
|
16
|
+
const PROBE_TIMEOUT_MS = 2000;
|
|
17
|
+
|
|
18
|
+
/** The first dotted triple in some `--version` output. */
|
|
19
|
+
export function parseVersion(output: string): string | null {
|
|
20
|
+
const match = VERSION_PATTERN.exec(output);
|
|
21
|
+
if (!match) return null;
|
|
22
|
+
return `${match[1]}.${match[2]}.${match[3]}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Negative when `a` is older than `b`, zero when equal, positive when newer.
|
|
27
|
+
* Unparseable input sorts oldest, so an unreadable version never satisfies a
|
|
28
|
+
* minimum.
|
|
29
|
+
*/
|
|
30
|
+
export function compareVersions(a: string, b: string): number {
|
|
31
|
+
const left = numericParts(a);
|
|
32
|
+
const right = numericParts(b);
|
|
33
|
+
for (let i = 0; i < 3; i += 1) {
|
|
34
|
+
const diff = (left[i] ?? 0) - (right[i] ?? 0);
|
|
35
|
+
if (diff !== 0) return diff;
|
|
36
|
+
}
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** True when `found` is at least `minimum`. A null `found` never satisfies. */
|
|
41
|
+
export function satisfiesMinimum(found: string | null, minimum: string): boolean {
|
|
42
|
+
if (found === null) return false;
|
|
43
|
+
return compareVersions(found, minimum) >= 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Run a binary's version command and read the version out of it.
|
|
48
|
+
*
|
|
49
|
+
* Results are memoized per absolute path for the life of the process. The
|
|
50
|
+
* resolution ladder is consulted every time a pane preset or a doctor row wants
|
|
51
|
+
* a tool, and each miss would otherwise be a process spawn on the interactive
|
|
52
|
+
* path. A binary replaced under a running Clio keeps its first answer until
|
|
53
|
+
* restart, which is the same staleness every other PATH lookup here carries.
|
|
54
|
+
*/
|
|
55
|
+
export function probeBinaryVersion(binaryPath: string, args: ReadonlyArray<string>): string | null {
|
|
56
|
+
const cached = probeCache.get(binaryPath);
|
|
57
|
+
if (cached !== undefined) return cached;
|
|
58
|
+
let version: string | null = null;
|
|
59
|
+
try {
|
|
60
|
+
const result = spawnSync(binaryPath, [...args], {
|
|
61
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
62
|
+
encoding: "utf8",
|
|
63
|
+
// A version probe must never inherit a terminal or wait on input: some
|
|
64
|
+
// of these binaries are full-screen programs when given a tty.
|
|
65
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
66
|
+
});
|
|
67
|
+
if (result.error === undefined) {
|
|
68
|
+
version = parseVersion(`${result.stdout ?? ""}\n${result.stderr ?? ""}`);
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
version = null;
|
|
72
|
+
}
|
|
73
|
+
probeCache.set(binaryPath, version);
|
|
74
|
+
return version;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Drop memoized probe results. Tests that swap binaries under a path need it. */
|
|
78
|
+
export function resetVersionProbeCache(): void {
|
|
79
|
+
probeCache.clear();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const probeCache = new Map<string, string | null>();
|
|
83
|
+
|
|
84
|
+
function numericParts(value: string): number[] {
|
|
85
|
+
const match = VERSION_PATTERN.exec(value);
|
|
86
|
+
if (!match) return [0, 0, 0];
|
|
87
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
88
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
1
2
|
import {
|
|
2
3
|
DEFAULT_DELEGATION_CONNECT_TIMEOUT_MS,
|
|
3
4
|
DEFAULT_DELEGATION_TURN_TIMEOUT_MS,
|
|
4
5
|
type DelegationAgentConfig,
|
|
5
6
|
} from "../../core/defaults.js";
|
|
7
|
+
import type { HeartbeatStamp } from "../../domains/dispatch/heartbeat.js";
|
|
6
8
|
import type { AutonomyLevel } from "../../domains/safety/autonomy.js";
|
|
7
9
|
import type { SafetyContract } from "../../domains/safety/contract.js";
|
|
8
10
|
import type { AgentEvent } from "../types.js";
|
|
@@ -40,6 +42,10 @@ export interface AcpDelegationRunInput {
|
|
|
40
42
|
terminationWaitMs?: number;
|
|
41
43
|
/** Grace for session/cancel before abort escalates into hard termination. */
|
|
42
44
|
cancelGraceMs?: number;
|
|
45
|
+
/** Wall clock for the persisted heartbeat anchor. */
|
|
46
|
+
now?: () => number;
|
|
47
|
+
/** Monotonic clock for event-inactivity spans. */
|
|
48
|
+
monotonicNow?: () => number;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
export interface AcpDelegationRunHandle {
|
|
@@ -54,7 +60,7 @@ export interface AcpDelegationRunHandle {
|
|
|
54
60
|
* final observation bound expires.
|
|
55
61
|
*/
|
|
56
62
|
kill(): void;
|
|
57
|
-
heartbeatAt:
|
|
63
|
+
heartbeatAt: HeartbeatStamp;
|
|
58
64
|
toolCallLog(): ReturnType<AcpToolMediator["snapshot"]>["toolCallLog"];
|
|
59
65
|
}
|
|
60
66
|
|
|
@@ -176,7 +182,14 @@ export function startAcpDelegationRun(input: AcpDelegationRunInput): AcpDelegati
|
|
|
176
182
|
// peer into an unbounded request.
|
|
177
183
|
const connectTimeoutMs = input.agent.connectTimeoutMs ?? DEFAULT_DELEGATION_CONNECT_TIMEOUT_MS;
|
|
178
184
|
const turnTimeoutMs = input.agent.turnTimeoutMs ?? DEFAULT_DELEGATION_TURN_TIMEOUT_MS;
|
|
179
|
-
const
|
|
185
|
+
const now = input.now ?? Date.now;
|
|
186
|
+
const monotonicNow = input.monotonicNow ?? (() => performance.now());
|
|
187
|
+
const heartbeatWallAnchor = now();
|
|
188
|
+
const heartbeatMonotonicAnchor = monotonicNow();
|
|
189
|
+
const heartbeatAt: HeartbeatStamp = {
|
|
190
|
+
current: heartbeatWallAnchor,
|
|
191
|
+
monotonic: heartbeatMonotonicAnchor,
|
|
192
|
+
};
|
|
180
193
|
const queue = new AsyncEventQueue<AcpRunEvent>();
|
|
181
194
|
const usage = emptyUsage();
|
|
182
195
|
const mapper = new AcpEventMapper();
|
|
@@ -202,7 +215,9 @@ export function startAcpDelegationRun(input: AcpDelegationRunInput): AcpDelegati
|
|
|
202
215
|
cancelTimer = null;
|
|
203
216
|
};
|
|
204
217
|
const emit = (event: AcpRunEvent): void => {
|
|
205
|
-
|
|
218
|
+
const stamp = monotonicNow();
|
|
219
|
+
heartbeatAt.monotonic = stamp;
|
|
220
|
+
heartbeatAt.current = heartbeatWallAnchor + (stamp - heartbeatMonotonicAnchor);
|
|
206
221
|
queue.push(event);
|
|
207
222
|
};
|
|
208
223
|
const mediator = new AcpToolMediator({
|