@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
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* append advances `state.lastTurnId`.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import type { BackendCompletionTimings } from "../core/cache-telemetry.js";
|
|
8
9
|
import type { ClioSettings } from "../core/config.js";
|
|
9
10
|
import type { MiddlewareToolChoiceControl } from "../domains/middleware/index.js";
|
|
10
11
|
import type { ObservabilityContract } from "../domains/observability/contract.js";
|
|
@@ -39,7 +40,7 @@ export interface TurnPersistenceDeps {
|
|
|
39
40
|
/** Remove a queued-mirror entry once the engine injects it. */
|
|
40
41
|
removeQueuedMirrorEntry: (text: string) => void;
|
|
41
42
|
/** Prompt-cache record for a persisted assistant call (T3.2 + cold stamp). */
|
|
42
|
-
promptCachePayloadForAssistant: (usage: Usage) => Record<string, unknown>;
|
|
43
|
+
promptCachePayloadForAssistant: (usage: Usage, backend?: BackendCompletionTimings) => Record<string, unknown>;
|
|
43
44
|
/**
|
|
44
45
|
* Prompt-side tokens the live context accounting holds for the turn in
|
|
45
46
|
* flight. Used only to record what an interrupted call is known to have
|
|
@@ -228,7 +229,8 @@ export function createTurnPersistence(deps: TurnPersistenceDeps): TurnPersistenc
|
|
|
228
229
|
if (timing) payload.timing = timing;
|
|
229
230
|
const usage = (message as { usage?: Usage }).usage;
|
|
230
231
|
if (usage && typeof usage === "object") {
|
|
231
|
-
|
|
232
|
+
const backendTimings = (message as { backendTimings?: BackendCompletionTimings }).backendTimings;
|
|
233
|
+
payload.promptCache = deps.promptCachePayloadForAssistant(usage, backendTimings);
|
|
232
234
|
}
|
|
233
235
|
// A cancelled turn's provider usage is the object the stream started with
|
|
234
236
|
// and never updated. Record what the call is known to have spent instead of
|
|
@@ -278,8 +280,16 @@ export function createTurnPersistence(deps: TurnPersistenceDeps): TurnPersistenc
|
|
|
278
280
|
|
|
279
281
|
appendQueuedUserTurn(message: AgentMessage): void {
|
|
280
282
|
if (message?.role !== "user") return;
|
|
281
|
-
|
|
282
|
-
|
|
283
|
+
// Byte-exact. Trimming here wrote a second, shortened row that the
|
|
284
|
+
// assistant then parented to, so the model's parent turn disagreed with
|
|
285
|
+
// the expansion the operator actually submitted: `/raw bar` produced a
|
|
286
|
+
// correct 5-byte row and a 3-byte one, and the 3-byte one won (issue
|
|
287
|
+
// #244). Only the emptiness test reads the trimmed form, and the
|
|
288
|
+
// echo/mirror lookups match on the same bytes the engine was given, so a
|
|
289
|
+
// payload with edge whitespace is recognized as the echo it is instead
|
|
290
|
+
// of being persisted a second time.
|
|
291
|
+
const text = extractUserText(message);
|
|
292
|
+
if (text.trim().length === 0) return;
|
|
283
293
|
if (deps.consumePersistedEcho(text)) return;
|
|
284
294
|
deps.removeQueuedMirrorEntry(text);
|
|
285
295
|
if (!deps.session) return;
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-warm ownership: when the prefix is sent, what stops it, and what it
|
|
3
|
+
* records. The round itself lives in `prewarm.ts`.
|
|
4
|
+
*
|
|
5
|
+
* Three moments hand the backend a prefix the operator has not paid for yet:
|
|
6
|
+
* the session prompt compiling at session start, a resume rebuilding the
|
|
7
|
+
* message array, and a compaction settling. All three leave a known prefix and
|
|
8
|
+
* an operator who is reading rather than waiting on a turn. Everything else is
|
|
9
|
+
* a refusal: another tier, a turn in flight, a running dispatch, a surface with
|
|
10
|
+
* nobody watching.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { BusChannels } from "../core/bus-events.js";
|
|
14
|
+
import type { ClioSettings } from "../core/config.js";
|
|
15
|
+
import type { SafeEventBus } from "../core/event-bus.js";
|
|
16
|
+
import type { ProvidersContract } from "../domains/providers/index.js";
|
|
17
|
+
import type { SessionContract } from "../domains/session/contract.js";
|
|
18
|
+
import type { Usage } from "../engine/types.js";
|
|
19
|
+
import { type PrewarmTrigger, prewarmPromptTokens, runPrewarmRound } from "./prewarm.js";
|
|
20
|
+
import type { TurnContext } from "./turn-context.js";
|
|
21
|
+
import type { AgentRuntime, ChatTurnState } from "./turn-state.js";
|
|
22
|
+
|
|
23
|
+
/** Why a pre-warm did not run. Returned for contracts; never shown to the operator. */
|
|
24
|
+
export type PrewarmSkipReason =
|
|
25
|
+
| "disabled"
|
|
26
|
+
| "surface"
|
|
27
|
+
| "turn-active"
|
|
28
|
+
| "dispatch-active"
|
|
29
|
+
| "tier"
|
|
30
|
+
| "unresolved"
|
|
31
|
+
| "superseded";
|
|
32
|
+
|
|
33
|
+
export type PrewarmOutcome = { ran: true; trigger: PrewarmTrigger } | { ran: false; reason: PrewarmSkipReason };
|
|
34
|
+
|
|
35
|
+
export interface TurnPrewarmDeps {
|
|
36
|
+
state: ChatTurnState;
|
|
37
|
+
getSettings: () => Readonly<ClioSettings>;
|
|
38
|
+
providers: ProvidersContract;
|
|
39
|
+
session?: SessionContract | undefined;
|
|
40
|
+
context: TurnContext;
|
|
41
|
+
bus?: SafeEventBus | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* True on a surface where a person is about to type the next turn. False for
|
|
44
|
+
* headless `run`: the pre-warm buys latency an unattended run never spends.
|
|
45
|
+
* Workers never reach here at all; they do not build a chat loop.
|
|
46
|
+
*/
|
|
47
|
+
isLatencySurface: () => boolean;
|
|
48
|
+
/** True while a submit owns, or is about to own, the turn state machine. */
|
|
49
|
+
isTurnActive: () => boolean;
|
|
50
|
+
/**
|
|
51
|
+
* True while an attached dispatch is running. Detached runs are counted from
|
|
52
|
+
* the bus instead, so a composition without this predicate still stands down
|
|
53
|
+
* for the worker traffic the bus reports.
|
|
54
|
+
*/
|
|
55
|
+
hasActiveDispatch: () => boolean;
|
|
56
|
+
/** Resolve the runtime and credentials the next turn would use, or a refusal. */
|
|
57
|
+
prepareRuntime: (
|
|
58
|
+
signal: AbortSignal,
|
|
59
|
+
) => Promise<{ ok: true; runtime: AgentRuntime; apiKey: string | undefined } | { ok: false; reason: string }>;
|
|
60
|
+
/** Freeze the session tool surface onto the agent, exactly as a submit does. */
|
|
61
|
+
applySessionTools: (runtime: AgentRuntime) => void;
|
|
62
|
+
/** Report the round's provider usage under its own `/cost` label. */
|
|
63
|
+
recordUsage: (runtime: AgentRuntime, usage: Usage | null) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Claim one in-flight request on the runtime's endpoint for the duration of
|
|
66
|
+
* the round, returning the release handle. This is the seam brief 06 (#250)
|
|
67
|
+
* fills: on a merged tree the chat loop passes
|
|
68
|
+
*
|
|
69
|
+
* (runtime) => {
|
|
70
|
+
* const key = canonicalEndpointKey(runtime.runtimeResolution.target);
|
|
71
|
+
* return key === null ? null : registerForegroundStream(key);
|
|
72
|
+
* }
|
|
73
|
+
*
|
|
74
|
+
* from `src/domains/providers/index.ts`, so endpoint capacity counts the
|
|
75
|
+
* pre-warm the same way it counts the orchestrator's streaming turn. Until
|
|
76
|
+
* then it is absent and `dispatchActive()` stands in with a blanket skip.
|
|
77
|
+
*/
|
|
78
|
+
registerEndpointSlot?: (runtime: AgentRuntime) => (() => void) | null;
|
|
79
|
+
/** Test seam. Production uses the real provider round. */
|
|
80
|
+
runPrewarm?: typeof runPrewarmRound;
|
|
81
|
+
/**
|
|
82
|
+
* Whether a submit aborts the round's request. Defaults to the measured
|
|
83
|
+
* backend behavior; see {@link ABORT_ROUND_ON_SUBMIT}.
|
|
84
|
+
*/
|
|
85
|
+
abortRoundOnSubmit?: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface TurnPrewarm {
|
|
89
|
+
/**
|
|
90
|
+
* Queue a pre-warm for the next tick. Repeated calls before it fires collapse
|
|
91
|
+
* onto one round: boot and a boot-time resume both land in the same tick, and
|
|
92
|
+
* warming the pre-resume history would be work thrown away.
|
|
93
|
+
*/
|
|
94
|
+
schedule(trigger: PrewarmTrigger): void;
|
|
95
|
+
/** Stop an in-flight or queued pre-warm. The operator's turn owns the slot now. */
|
|
96
|
+
cancel(): void;
|
|
97
|
+
/** Resolves once nothing is queued or in flight. For contracts and shutdown. */
|
|
98
|
+
settled(): Promise<PrewarmOutcome | null>;
|
|
99
|
+
dispose(): void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Whether a submit aborts the in-flight pre-warm's HTTP request, or only stops
|
|
104
|
+
* treating it as the current one.
|
|
105
|
+
*
|
|
106
|
+
* Measured against the operator's llama.cpp router, build `b226-2115b73d8`,
|
|
107
|
+
* Qwen3.8-27B, `--parallel 1`: aborting the request 1.5 s into a 47,620-token
|
|
108
|
+
* prefill did not cancel the server's work. The server finished prefilling, so
|
|
109
|
+
* the prefix did survive the abort, and the very next request read 47,596 of
|
|
110
|
+
* 47,620 tokens from cache and spent 927 ms in prompt. That request also waited
|
|
111
|
+
* 89.5 s of wall clock for the abandoned one to finish, on a server that serves
|
|
112
|
+
* one slot. Letting the pre-warm complete instead cost 89.3 s plus a 1.3 s turn:
|
|
113
|
+
* the same wall clock, arrived at honestly.
|
|
114
|
+
*
|
|
115
|
+
* The abort therefore frees no slot and saves the operator no time on this
|
|
116
|
+
* backend. All it does is discard the usage and timings of prefill the server
|
|
117
|
+
* performed, which is spend the ledger should carry. So a submit detaches the
|
|
118
|
+
* round: Clio stops calling it the current pre-warm and never waits on it, and
|
|
119
|
+
* it still records what it cost. Set this true for a backend that honors
|
|
120
|
+
* cancellation, where dropping the request genuinely releases the slot.
|
|
121
|
+
*/
|
|
122
|
+
const ABORT_ROUND_ON_SUBMIT = false;
|
|
123
|
+
|
|
124
|
+
export function createTurnPrewarm(deps: TurnPrewarmDeps): TurnPrewarm {
|
|
125
|
+
const round = deps.runPrewarm ?? runPrewarmRound;
|
|
126
|
+
const abortOnSubmit = deps.abortRoundOnSubmit ?? ABORT_ROUND_ON_SUBMIT;
|
|
127
|
+
let pendingTrigger: PrewarmTrigger | null = null;
|
|
128
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
129
|
+
let active: AbortController | null = null;
|
|
130
|
+
// Rounds a submit or a session switch let go of. Their result is still
|
|
131
|
+
// recorded, but it no longer describes the prefix the next turn will send, so
|
|
132
|
+
// it never reaches the `/context` line.
|
|
133
|
+
const detached = new WeakSet<AbortController>();
|
|
134
|
+
let inFlight: Promise<PrewarmOutcome | null> = Promise.resolve(null);
|
|
135
|
+
// Dispatch traffic Clio can see without an endpoint registry: the run
|
|
136
|
+
// lifecycle the bus already publishes. `hasActiveDispatch` covers the attached
|
|
137
|
+
// calls the composition root holds; this covers detached ones. Both are the
|
|
138
|
+
// stand-in for #250's per-endpoint slot accounting.
|
|
139
|
+
let dispatchesInFlight = 0;
|
|
140
|
+
const unsubscribeDispatch = [
|
|
141
|
+
deps.bus?.on(BusChannels.DispatchStarted, () => {
|
|
142
|
+
dispatchesInFlight += 1;
|
|
143
|
+
}) ?? null,
|
|
144
|
+
...[BusChannels.DispatchCompleted, BusChannels.DispatchFailed].map(
|
|
145
|
+
(channel) =>
|
|
146
|
+
deps.bus?.on(channel, () => {
|
|
147
|
+
dispatchesInFlight = Math.max(0, dispatchesInFlight - 1);
|
|
148
|
+
}) ?? null,
|
|
149
|
+
),
|
|
150
|
+
];
|
|
151
|
+
const dispatchActive = (): boolean => dispatchesInFlight > 0 || deps.hasActiveDispatch();
|
|
152
|
+
|
|
153
|
+
const admissionRefusal = (): PrewarmSkipReason | null => {
|
|
154
|
+
if (deps.getSettings().prewarm?.enabled === false) return "disabled";
|
|
155
|
+
if (!deps.isLatencySurface()) return "surface";
|
|
156
|
+
if (deps.state.streaming || deps.isTurnActive()) return "turn-active";
|
|
157
|
+
// Fleet interaction. Without per-endpoint slot accounting a pre-warm cannot
|
|
158
|
+
// know whether a worker is already occupying the server it would warm, so
|
|
159
|
+
// it stands down whenever any dispatch is outstanding. `registerEndpointSlot`
|
|
160
|
+
// is the other half: the round claims a slot on its endpoint while its
|
|
161
|
+
// request is out, so capacity sees it. Once that registry answers here too,
|
|
162
|
+
// this blanket skip narrows to "the endpoint is at its bound".
|
|
163
|
+
if (dispatchActive()) return "dispatch-active";
|
|
164
|
+
// The tier is settled from configuration before anything is resolved. The
|
|
165
|
+
// resolved runtime is checked again below, but asking here means a target
|
|
166
|
+
// that was never eligible costs no capability probe, no agent, and no
|
|
167
|
+
// notice from either.
|
|
168
|
+
const targetId = deps.getSettings().orchestrator?.target?.trim();
|
|
169
|
+
const runtimeId = targetId ? deps.providers.getTarget(targetId)?.runtime : undefined;
|
|
170
|
+
if (runtimeId === undefined || deps.providers.getRuntime(runtimeId)?.tier !== "local-native") return "tier";
|
|
171
|
+
return null;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const runOnce = async (trigger: PrewarmTrigger, controller: AbortController): Promise<PrewarmOutcome> => {
|
|
175
|
+
const refusal = admissionRefusal();
|
|
176
|
+
if (refusal) return { ran: false, reason: refusal };
|
|
177
|
+
|
|
178
|
+
const prepared = await deps.prepareRuntime(controller.signal);
|
|
179
|
+
if (!prepared.ok) return { ran: false, reason: "unresolved" };
|
|
180
|
+
const runtime = prepared.runtime;
|
|
181
|
+
if (deps.providers.getRuntime(runtime.runtimeId)?.tier !== "local-native") {
|
|
182
|
+
// Off on every other tier regardless of the setting. A cloud provider
|
|
183
|
+
// caches by exact prefix on its own schedule and bills the request; a
|
|
184
|
+
// pre-warm there is spend without a latency win.
|
|
185
|
+
return { ran: false, reason: "tier" };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
deps.applySessionTools(runtime);
|
|
189
|
+
await deps.context.ensureSessionPrompt(runtime);
|
|
190
|
+
|
|
191
|
+
// Re-check after the awaits: a submit or a dispatch may have started while
|
|
192
|
+
// the prompt compiled, and the pre-warm must never be the request the
|
|
193
|
+
// operator's turn queues behind.
|
|
194
|
+
const lateRefusal = admissionRefusal();
|
|
195
|
+
if (lateRefusal) return { ran: false, reason: lateRefusal };
|
|
196
|
+
if (detached.has(controller) || controller.signal.aborted) return { ran: false, reason: "superseded" };
|
|
197
|
+
|
|
198
|
+
// The claim is taken before the request goes out and released in the
|
|
199
|
+
// finally, including on the detach path, because the request the server is
|
|
200
|
+
// still finishing is the one occupying the slot.
|
|
201
|
+
const releaseEndpointSlot = deps.registerEndpointSlot?.(runtime) ?? null;
|
|
202
|
+
let result: Awaited<ReturnType<typeof round>>;
|
|
203
|
+
try {
|
|
204
|
+
result = await round({
|
|
205
|
+
model: runtime.agent.state.model,
|
|
206
|
+
state: {
|
|
207
|
+
systemPrompt: runtime.agent.state.systemPrompt,
|
|
208
|
+
messages: runtime.agent.state.messages,
|
|
209
|
+
tools: runtime.agent.state.tools,
|
|
210
|
+
thinkingLevel: runtime.agent.state.thinkingLevel ?? "off",
|
|
211
|
+
},
|
|
212
|
+
...(prepared.apiKey !== undefined ? { apiKey: prepared.apiKey } : {}),
|
|
213
|
+
signal: controller.signal,
|
|
214
|
+
});
|
|
215
|
+
} finally {
|
|
216
|
+
releaseEndpointSlot?.();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// The prefill the server did is spend whatever the operator did next, so a
|
|
220
|
+
// let-go round is recorded exactly like a completed one. Only the
|
|
221
|
+
// `/context` line is withheld: it answers "is the prefix the next turn
|
|
222
|
+
// wants already resident", and a detached round is no longer about that
|
|
223
|
+
// prefix.
|
|
224
|
+
const wasDetached = detached.has(controller) || controller.signal.aborted;
|
|
225
|
+
const promptTokens = prewarmPromptTokens(result);
|
|
226
|
+
if (!wasDetached) {
|
|
227
|
+
deps.context.notePrewarm({
|
|
228
|
+
tokens: promptTokens,
|
|
229
|
+
ms: result.timing.apiMs,
|
|
230
|
+
aborted: result.aborted,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
deps.recordUsage(runtime, result.usage);
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
deps.session?.appendEntry({
|
|
237
|
+
kind: "custom",
|
|
238
|
+
customType: "prewarm",
|
|
239
|
+
// Never a model message and never a rendered transcript block: a
|
|
240
|
+
// pre-warm is a fact about the backend, not something the session said.
|
|
241
|
+
display: false,
|
|
242
|
+
parentTurnId: deps.state.lastTurnId,
|
|
243
|
+
data: {
|
|
244
|
+
trigger,
|
|
245
|
+
target: runtime.targetId,
|
|
246
|
+
model: runtime.wireModelId,
|
|
247
|
+
promptTokens,
|
|
248
|
+
aborted: result.aborted,
|
|
249
|
+
detached: wasDetached,
|
|
250
|
+
timing: result.timing,
|
|
251
|
+
promptCache: {
|
|
252
|
+
input: result.usage?.input ?? 0,
|
|
253
|
+
cacheRead: result.usage?.cacheRead ?? 0,
|
|
254
|
+
cacheWrite: result.usage?.cacheWrite ?? 0,
|
|
255
|
+
...(result.backend ? { backend: { ...result.backend } } : {}),
|
|
256
|
+
},
|
|
257
|
+
...(result.errorMessage ? { error: result.errorMessage } : {}),
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
} catch {
|
|
261
|
+
// The ledger entry is diagnostics. A session that refuses it must not
|
|
262
|
+
// turn an optimization into a failure.
|
|
263
|
+
}
|
|
264
|
+
return { ran: true, trigger };
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Let go of the round in flight. On a backend that honors cancellation this
|
|
269
|
+
* also aborts the request; see {@link ABORT_ROUND_ON_SUBMIT} for why the
|
|
270
|
+
* measured one does not.
|
|
271
|
+
*/
|
|
272
|
+
const releaseActiveRound = (): void => {
|
|
273
|
+
const controller = active;
|
|
274
|
+
if (controller === null) return;
|
|
275
|
+
detached.add(controller);
|
|
276
|
+
if (abortOnSubmit) controller.abort();
|
|
277
|
+
active = null;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const fire = (): void => {
|
|
281
|
+
timer = null;
|
|
282
|
+
const trigger = pendingTrigger;
|
|
283
|
+
pendingTrigger = null;
|
|
284
|
+
if (trigger === null) return;
|
|
285
|
+
const controller = new AbortController();
|
|
286
|
+
active = controller;
|
|
287
|
+
const previous = inFlight;
|
|
288
|
+
inFlight = previous
|
|
289
|
+
.catch(() => null)
|
|
290
|
+
.then(() => runOnce(trigger, controller))
|
|
291
|
+
.catch(() => null)
|
|
292
|
+
.finally(() => {
|
|
293
|
+
if (active === controller) active = null;
|
|
294
|
+
});
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
schedule(trigger: PrewarmTrigger): void {
|
|
299
|
+
// A newer trigger describes a newer prefix; the older round is no longer
|
|
300
|
+
// warming history this session has.
|
|
301
|
+
releaseActiveRound();
|
|
302
|
+
pendingTrigger = trigger;
|
|
303
|
+
if (timer !== null) return;
|
|
304
|
+
// Ref'd on purpose, same reasoning as `settled()` below: the timer is due
|
|
305
|
+
// in 0 ms, so it holds the event loop for one tick at most, and a Node 22
|
|
306
|
+
// loop that drains past a due unref'd timer would otherwise never start
|
|
307
|
+
// the round a test or caller is already awaiting.
|
|
308
|
+
timer = setTimeout(fire, 0);
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
cancel(): void {
|
|
312
|
+
pendingTrigger = null;
|
|
313
|
+
if (timer !== null) {
|
|
314
|
+
clearTimeout(timer);
|
|
315
|
+
timer = null;
|
|
316
|
+
}
|
|
317
|
+
releaseActiveRound();
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
settled(): Promise<PrewarmOutcome | null> {
|
|
321
|
+
// One hop past the scheduling tick so a caller that just scheduled a
|
|
322
|
+
// round waits for that round rather than for the previous one. The timer
|
|
323
|
+
// is deliberately ref'd, unlike the scheduling timer above: the caller is
|
|
324
|
+
// awaiting this promise, so the event loop must stay alive for the one
|
|
325
|
+
// tick that lets the scheduled round start. Node 22 drains the loop past
|
|
326
|
+
// a due unref'd timer when nothing else holds it, which left this promise
|
|
327
|
+
// pending forever and cancelled every later test in the lane; Node 24
|
|
328
|
+
// happens to fire the due timer first, which is why the hang never
|
|
329
|
+
// reproduced on a 24.x development machine.
|
|
330
|
+
return new Promise((resolve) => {
|
|
331
|
+
setTimeout(() => {
|
|
332
|
+
resolve(inFlight.catch(() => null));
|
|
333
|
+
}, 0);
|
|
334
|
+
});
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
dispose(): void {
|
|
338
|
+
for (const unsubscribe of unsubscribeDispatch) unsubscribe?.();
|
|
339
|
+
pendingTrigger = null;
|
|
340
|
+
if (timer !== null) {
|
|
341
|
+
clearTimeout(timer);
|
|
342
|
+
timer = null;
|
|
343
|
+
}
|
|
344
|
+
// Shutdown always aborts: the process is going away, and a socket held
|
|
345
|
+
// open for a round nobody will read is not a latency question.
|
|
346
|
+
active?.abort();
|
|
347
|
+
active = null;
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Subscribe the pre-warm to compaction settling. A summary or an eviction moves
|
|
354
|
+
* the byte prefix, so the next turn is known to be cold, and the operator is
|
|
355
|
+
* usually reading the summary rather than typing. The idle guard inside
|
|
356
|
+
* `schedule` is what keeps this from firing for the auto-compaction a submit
|
|
357
|
+
* runs on its way to the provider.
|
|
358
|
+
*/
|
|
359
|
+
export function subscribePrewarmToCompaction(bus: SafeEventBus | undefined, prewarm: TurnPrewarm): () => void {
|
|
360
|
+
const unsubscribe = bus?.on(BusChannels.CompactionEnd, () => {
|
|
361
|
+
prewarm.schedule("compaction");
|
|
362
|
+
});
|
|
363
|
+
return () => unsubscribe?.();
|
|
364
|
+
}
|
|
@@ -83,14 +83,17 @@ export function createTurnQueues(deps: TurnQueuesDeps): TurnQueues {
|
|
|
83
83
|
// works. The former per-steer transcript notice duplicated the panel and
|
|
84
84
|
// left a permanent line for a transient state.
|
|
85
85
|
const enqueue = (text: string, kind: QueuedMessageKind): boolean => {
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
// The payload crosses to the model exactly as it was submitted; only the
|
|
87
|
+
// emptiness test reads a trimmed copy. A queued turn that shortened its own
|
|
88
|
+
// text here would land in the ledger disagreeing with the expansion that
|
|
89
|
+
// produced it, which is the same defect the persisted echo had (issue #244).
|
|
90
|
+
if (text.trim().length === 0 || !state.streaming || !state.runtime) return false;
|
|
88
91
|
const message = {
|
|
89
92
|
role: "user",
|
|
90
|
-
content:
|
|
93
|
+
content: text,
|
|
91
94
|
timestamp: Date.now(),
|
|
92
95
|
} as AgentMessage;
|
|
93
|
-
queuedMirror.push({ text
|
|
96
|
+
queuedMirror.push({ text, kind });
|
|
94
97
|
if (kind === "steer") {
|
|
95
98
|
state.runtime.agent.steer(message);
|
|
96
99
|
} else {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { performance } from "node:perf_hooks";
|
|
9
|
+
import type { BackendCompletionTimings } from "../core/cache-telemetry.js";
|
|
9
10
|
import type { ClioSettings } from "../core/config.js";
|
|
10
11
|
import { attributedModelId } from "../core/response-model-id.js";
|
|
11
12
|
import type { MiddlewareToolChoiceControl } from "../domains/middleware/index.js";
|
|
@@ -192,6 +193,10 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
|
|
|
192
193
|
use: "orchestrator",
|
|
193
194
|
requireTools: false,
|
|
194
195
|
requireOutputBudget: true,
|
|
196
|
+
// A resumed session already knows what this backend had open; without
|
|
197
|
+
// it the first turn budgets against the probed server-wide figure and
|
|
198
|
+
// corrects only once discovery reports a loaded window (issue #227).
|
|
199
|
+
knownLoadedContextWindow: context.rememberedLoadedContextWindow(targetId, wireModelId),
|
|
195
200
|
});
|
|
196
201
|
if (!resolved.ok) {
|
|
197
202
|
const failure = resolved.diagnostics.find((entry) => entry.severity === "error") ?? resolved.diagnostics[0];
|
|
@@ -315,6 +320,7 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
|
|
|
315
320
|
use: "orchestrator",
|
|
316
321
|
requireTools: false,
|
|
317
322
|
requireOutputBudget: true,
|
|
323
|
+
knownLoadedContextWindow: context.rememberedLoadedContextWindow(target.target.id, target.wireModelId),
|
|
318
324
|
});
|
|
319
325
|
if (!refreshed.ok) return;
|
|
320
326
|
const liveModel = state.runtime.agent.state.model;
|
|
@@ -850,7 +856,8 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
|
|
|
850
856
|
if (isAssistant && usage && typeof usage === "object" && runFirstCallVerdict === null) {
|
|
851
857
|
const input = typeof usage.input === "number" ? usage.input : 0;
|
|
852
858
|
const cacheRead = typeof usage.cacheRead === "number" ? usage.cacheRead : 0;
|
|
853
|
-
|
|
859
|
+
const backendTimings = (enrichedEvent.message as { backendTimings?: BackendCompletionTimings }).backendTimings;
|
|
860
|
+
runFirstCallVerdict = backendCacheVerdict(input, cacheRead, backendTimings);
|
|
854
861
|
}
|
|
855
862
|
if (usage) {
|
|
856
863
|
context.reconcileUsage(usage);
|
|
@@ -53,6 +53,13 @@ export interface ChatLoopRunSnapshot {
|
|
|
53
53
|
cwd: string;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* What a consumed prompt is doing before it owns the stream. `compacting` is
|
|
58
|
+
* the sub-state that made the defect visible, because it is the one that lasts
|
|
59
|
+
* long enough for an operator to conclude the keystroke was lost.
|
|
60
|
+
*/
|
|
61
|
+
export type TurnPreparationPhase = "idle" | "preparing" | "compacting";
|
|
62
|
+
|
|
56
63
|
export interface ChatTurnState {
|
|
57
64
|
/** Live agent runtime; built or hot-swapped by turn-runtime. */
|
|
58
65
|
runtime: AgentRuntime | null;
|
|
@@ -60,6 +67,20 @@ export interface ChatTurnState {
|
|
|
60
67
|
lastTurnId: string | null;
|
|
61
68
|
/** True while a submit is in flight. */
|
|
62
69
|
streaming: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Where a consumed prompt is between the editor and the live stream.
|
|
72
|
+
*
|
|
73
|
+
* The editor is cleared and the prompt painted into the transcript before
|
|
74
|
+
* admission, and everything from the capability probe through pre-submit
|
|
75
|
+
* compaction to the prompt compile happens after that and before
|
|
76
|
+
* `streaming`. With nothing naming that window the composer went straight
|
|
77
|
+
* back to `MESSAGE` and the footer still read the previous turn as done, so
|
|
78
|
+
* a 77-second compaction was indistinguishable from a dropped Enter
|
|
79
|
+
* (issue #251).
|
|
80
|
+
*/
|
|
81
|
+
turnPreparation: TurnPreparationPhase;
|
|
82
|
+
/** When the current preparation window opened, for its elapsed counter. */
|
|
83
|
+
turnPreparationSince: number;
|
|
63
84
|
/** Thinking level the active request runs under; read by onPayload. */
|
|
64
85
|
currentThinkingLevel: ThinkingLevel;
|
|
65
86
|
/** Provider context rebuilt on session switch, consumed by the next runtime build. */
|
|
@@ -115,6 +136,8 @@ export function createTurnState(initialThinkingLevel: ThinkingLevel): ChatTurnSt
|
|
|
115
136
|
runtime: null,
|
|
116
137
|
lastTurnId: null,
|
|
117
138
|
streaming: false,
|
|
139
|
+
turnPreparation: "idle",
|
|
140
|
+
turnPreparationSince: 0,
|
|
118
141
|
currentThinkingLevel: initialThinkingLevel,
|
|
119
142
|
replayedContextMessages: [],
|
|
120
143
|
activeUserTurnId: null,
|
|
@@ -5,12 +5,14 @@ import type { DispatchContract } from "../../domains/dispatch/contract.js";
|
|
|
5
5
|
import { isReceiptIntegrity, verifyReceiptIntegrity } from "../../domains/dispatch/receipt-integrity.js";
|
|
6
6
|
import type { RunEnvelope, RunReceipt } from "../../domains/dispatch/types.js";
|
|
7
7
|
import { evidenceDirectory, inspectEvidence, listEvidenceOverviews } from "../../domains/evidence/store.js";
|
|
8
|
-
import { formatTrustSummaryLine } from "../../domains/evidence/trust-projection.js";
|
|
8
|
+
import { formatTrustSummaryLine, trustStateWord } from "../../domains/evidence/trust-projection.js";
|
|
9
9
|
import {
|
|
10
10
|
inspectRunReceiptTrustStatus,
|
|
11
11
|
type ReceiptIntegrityOutcome,
|
|
12
12
|
retiredReceiptIntegrity,
|
|
13
13
|
retiredReceiptIntegrityReason,
|
|
14
|
+
TRUST_STATUS_AXES,
|
|
15
|
+
type TrustStatusAxis,
|
|
14
16
|
} from "../../domains/evidence/trust-status.js";
|
|
15
17
|
import type { EvidenceFinding, EvidenceOverview, EvidenceSource } from "../../domains/evidence/types.js";
|
|
16
18
|
import { type AccountabilitySummary, readAccountabilitySummary } from "../../domains/observability/index.js";
|
|
@@ -113,6 +115,20 @@ const JSON_PRETTY_MAX_BYTES = 10 * 1024 * 1024;
|
|
|
113
115
|
|
|
114
116
|
export type ReceiptVerifyResult = ReceiptIntegrityOutcome;
|
|
115
117
|
|
|
118
|
+
export interface ReceiptVerificationCheck {
|
|
119
|
+
name: string;
|
|
120
|
+
ok: boolean;
|
|
121
|
+
evidence: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type ReceiptVerificationReport = ReceiptIntegrityOutcome & {
|
|
125
|
+
receiptPath: string;
|
|
126
|
+
sealedDigest: string | null;
|
|
127
|
+
trustSummary: string | null;
|
|
128
|
+
compromised: boolean;
|
|
129
|
+
checks: ReceiptVerificationCheck[];
|
|
130
|
+
};
|
|
131
|
+
|
|
116
132
|
/**
|
|
117
133
|
* What a verify action reports. `retired` marks a seal this build does not
|
|
118
134
|
* verify because its integrity version was retired: not a pass, and not the
|
|
@@ -435,6 +451,98 @@ export function verifyReceiptFile(stateDir: string, runId: string): ReceiptVerif
|
|
|
435
451
|
return verifyReceiptIntegrity(r as unknown as RunReceipt, ledger.envelope);
|
|
436
452
|
}
|
|
437
453
|
|
|
454
|
+
const RECEIPT_FILE_FAILURE = /^(?:receipt file not found|read error:)/u;
|
|
455
|
+
const RECEIPT_CONTRACT_FAILURE =
|
|
456
|
+
/^(?:invalid json:|receipt is not an object|missing field:|runId invalid:|runId mismatch:|agentId invalid:|task invalid:|targetId invalid:|wireModelId invalid:|runtimeId invalid:|runtimeKind invalid:|exitCode out of range:|tokenCount out of range:|inputTokenCount out of range:|outputTokenCount out of range:|cacheReadTokenCount out of range:|cacheWriteTokenCount out of range:|costUsd out of range:|reasoningTokenCount out of range:|startedAt not ISO-8601:|endedAt not ISO-8601:|clioVersion empty|piMonoVersion invalid:|platform invalid:|nodeVersion invalid:|toolCalls out of range:|toolStats\[|compiledPromptHash invalid:|staticCompositionHash invalid:|sessionId invalid:|integrity invalid|execution role invalid|routing intent invalid|route decision invalid)/u;
|
|
457
|
+
const LEDGER_FAILURE = /^(?:run ledger not found|run not found in ledger|ledger mismatch:)/u;
|
|
458
|
+
|
|
459
|
+
function receiptIntegrityDigest(value: unknown): string | null {
|
|
460
|
+
if (!isRecord(value) || value.algorithm !== "sha256" || !isNonEmptyString(value.digest)) return null;
|
|
461
|
+
return `sha256:${value.digest}`;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function trustCheckEvidence(status: ReturnType<typeof inspectRunReceiptTrustStatus>["status"], axis: string): string {
|
|
465
|
+
const entry = status[axis as keyof typeof status];
|
|
466
|
+
if (typeof entry !== "object" || entry === null || !("state" in entry)) return "canonical trust projection";
|
|
467
|
+
if (entry.state === "absent") return `reason=${entry.reason}`;
|
|
468
|
+
const refs = entry.artifacts.map((artifact) => `${artifact.kind}:${artifact.id}`);
|
|
469
|
+
return refs.length > 0 ? refs.join(", ") : `${entry.authority.kind}:${entry.authority.id}`;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function compromisedTrustAxes(status: ReturnType<typeof inspectRunReceiptTrustStatus>["status"]): TrustStatusAxis[] {
|
|
473
|
+
return TRUST_STATUS_AXES.filter((axis) => {
|
|
474
|
+
const state = status[axis].state;
|
|
475
|
+
if (axis === "artifactIntegrity") return state === "failed";
|
|
476
|
+
if (axis === "validationGrounding") return state === "failed" || state === "ungrounded";
|
|
477
|
+
if (axis === "independentReview") return state === "failed" || state === "not_independent";
|
|
478
|
+
if (axis === "contextProvenance") return state === "invalid";
|
|
479
|
+
if (axis === "autonomyEnforcement") return state === "bypassed";
|
|
480
|
+
return false;
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Actionable `/view verify` report without changing the low-level verifier contract. */
|
|
485
|
+
export function verifyReceiptFileReport(stateDir: string, runId: string): ReceiptVerificationReport {
|
|
486
|
+
const receiptPath = receiptFilePath(stateDir, runId);
|
|
487
|
+
const result = verifyReceiptFile(stateDir, runId);
|
|
488
|
+
let receipt: RunReceipt | null = null;
|
|
489
|
+
try {
|
|
490
|
+
receipt = JSON.parse(readFileSync(receiptPath, "utf8")) as RunReceipt;
|
|
491
|
+
} catch {
|
|
492
|
+
// The verifier's named file or JSON check below retains the actionable error.
|
|
493
|
+
}
|
|
494
|
+
const sealedDigest = receiptIntegrityDigest(receipt?.integrity);
|
|
495
|
+
const ledger = readRunEnvelope(stateDir, runId);
|
|
496
|
+
const inspection = receipt === null ? null : inspectRunReceiptTrustStatus(receipt, ledger.ok ? ledger.envelope : null);
|
|
497
|
+
const trustSummary = inspection === null ? null : formatTrustSummaryLine(inspection.status);
|
|
498
|
+
const compromiseAxes = inspection === null ? [] : compromisedTrustAxes(inspection.status);
|
|
499
|
+
const checks: ReceiptVerificationCheck[] = [];
|
|
500
|
+
|
|
501
|
+
if (!result.ok && RECEIPT_FILE_FAILURE.test(result.reason)) {
|
|
502
|
+
checks.push({ name: "receipt file", ok: false, evidence: `${receiptPath}: ${result.reason}` });
|
|
503
|
+
} else {
|
|
504
|
+
checks.push({ name: "receipt file", ok: true, evidence: receiptPath });
|
|
505
|
+
if (!result.ok && (RECEIPT_CONTRACT_FAILURE.test(result.reason) || result.retired !== undefined)) {
|
|
506
|
+
checks.push({ name: "receipt contract", ok: false, evidence: result.reason });
|
|
507
|
+
} else {
|
|
508
|
+
checks.push({
|
|
509
|
+
name: "receipt contract",
|
|
510
|
+
ok: true,
|
|
511
|
+
evidence: `current integrity v${receipt?.integrity.version ?? "unknown"}`,
|
|
512
|
+
});
|
|
513
|
+
if (!result.ok && LEDGER_FAILURE.test(result.reason)) {
|
|
514
|
+
checks.push({ name: "run ledger", ok: false, evidence: result.reason });
|
|
515
|
+
} else {
|
|
516
|
+
checks.push({ name: "run ledger", ok: true, evidence: `run ${runId} fields match` });
|
|
517
|
+
checks.push({
|
|
518
|
+
name: "sealed digest",
|
|
519
|
+
ok: result.ok,
|
|
520
|
+
evidence: result.ok ? `${sealedDigest ?? "digest unavailable"} matches receipt and ledger` : result.reason,
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (inspection !== null) {
|
|
527
|
+
for (const axis of compromiseAxes) {
|
|
528
|
+
checks.push({
|
|
529
|
+
name: `trust ${axis}`,
|
|
530
|
+
ok: false,
|
|
531
|
+
evidence: `${trustStateWord(axis, inspection.status[axis].state)}; ${trustCheckEvidence(inspection.status, axis)}`,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return {
|
|
537
|
+
...result,
|
|
538
|
+
receiptPath,
|
|
539
|
+
sealedDigest,
|
|
540
|
+
trustSummary,
|
|
541
|
+
compromised: compromiseAxes.length > 0,
|
|
542
|
+
checks,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
438
546
|
function pushCapped(lines: string[], line: string, maxLines: number): boolean {
|
|
439
547
|
if (lines.length >= maxLines) return false;
|
|
440
548
|
lines.push(line);
|