@iowarp/clio-coder 0.3.9 → 0.4.1
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 +92 -0
- package/NOTICE +33 -0
- package/README.md +121 -16
- package/dist/{acp-7LOELQFP.js → acp-ZILU3AUO.js} +19 -18
- package/dist/{agents-FIBG2SHA.js → agents-HYWGBGQR.js} +53 -39
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-OI4LIH2I.js → auth-N3QT7CBO.js} +24 -21
- package/dist/{builtins-AD25UL3C.js → builtins-UJLMOVOV.js} +6 -6
- package/dist/{chunk-I4HZDVNP.js → chunk-2LEAKD4C.js} +2 -3
- package/dist/{chunk-465CC7FK.js → chunk-2NM363SV.js} +60 -64
- package/dist/{chunk-4H6ULJ3H.js → chunk-2VG7KLYV.js} +1046 -1370
- package/dist/{chunk-SST6Z5JA.js → chunk-336NSSKI.js} +1 -1
- package/dist/{chunk-NZU6YDNV.js → chunk-3I5NY75V.js} +5 -5
- package/dist/{chunk-47CMYGET.js → chunk-3QSOM6PA.js} +2 -2
- package/dist/chunk-42FMPA75.js +101 -0
- package/dist/{chunk-HAY4ZE2P.js → chunk-5PSMVOLM.js} +2 -2
- package/dist/{chunk-IHKBWSXF.js → chunk-5QIAJV2D.js} +3 -3
- package/dist/chunk-5YHDIDBP.js +97 -0
- package/dist/chunk-6CCS4G3W.js +1529 -0
- package/dist/{chunk-2JDWVJND.js → chunk-6DWBAZ5U.js} +2 -2
- package/dist/{chunk-BVDVID7E.js → chunk-6FN3E6KX.js} +6 -6
- package/dist/{chunk-DG4M6ZUE.js → chunk-6HMJX2VU.js} +59 -59
- package/dist/{chunk-3MRC2YSQ.js → chunk-6I5ILFOF.js} +3 -2
- package/dist/chunk-6NJQITNH.js +1412 -0
- package/dist/chunk-6VC4OV3Z.js +365 -0
- package/dist/{chunk-LU7P4LHA.js → chunk-72GZI5EV.js} +2 -2
- package/dist/{chunk-5T7RBWN2.js → chunk-74YWRRU5.js} +9 -8
- package/dist/{chunk-VPTUJU4P.js → chunk-7EPLI7VL.js} +2 -2
- package/dist/{chunk-CA42X6KT.js → chunk-7OBGU7UB.js} +7 -7
- package/dist/{chunk-56KB5IJP.js → chunk-7PWAODYW.js} +3 -3
- package/dist/chunk-7RY5VZPH.js +573 -0
- package/dist/{chunk-OEDBCISO.js → chunk-ABLSQ6JX.js} +3 -3
- package/dist/{chunk-5HFBWUMU.js → chunk-AFKWHWXF.js} +13 -23
- package/dist/{chunk-FWDFM5ZU.js → chunk-AK5XEFVZ.js} +3 -3
- package/dist/{chunk-WXCJ7VME.js → chunk-AKB4GYDL.js} +7 -7
- package/dist/chunk-AP73CFDC.js +308 -0
- package/dist/{chunk-4LJX2PUC.js → chunk-BBTJOK6Y.js} +5 -5
- package/dist/{chunk-QQ7EKM72.js → chunk-BDPT6GTK.js} +13 -61
- package/dist/chunk-BJGUKIG4.js +302 -0
- package/dist/chunk-BMS5RKQY.js +27 -0
- package/dist/{chunk-TD7UE2L5.js → chunk-BTGG6BG2.js} +81 -40
- package/dist/chunk-BTK3SSSK.js +67 -0
- package/dist/{chunk-M6L6IDJG.js → chunk-BWH6N36D.js} +1 -1
- package/dist/chunk-BWW4HLO4.js +301 -0
- package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
- package/dist/{chunk-HJB5IUKP.js → chunk-C537JADH.js} +18 -18
- package/dist/{chunk-774ILSRL.js → chunk-CDNVLKUX.js} +5 -4
- package/dist/{chunk-HPCTNZM2.js → chunk-CTJ4RNAA.js} +4 -4
- package/dist/{chunk-AEYBF3TB.js → chunk-CWVRRIEI.js} +2 -2
- package/dist/chunk-D4CJV46V.js +462 -0
- package/dist/chunk-D7E7WCDF.js +27 -0
- package/dist/{chunk-5PVQ4SRS.js → chunk-E67WX76H.js} +10 -126
- package/dist/{chunk-FQ4SKYE4.js → chunk-EBY63B7G.js} +2 -5
- package/dist/chunk-EIMVLWB3.js +1842 -0
- package/dist/{chunk-AMKHQW3C.js → chunk-FEFIFZTL.js} +3 -3
- package/dist/{chunk-RAPCMZL4.js → chunk-FSP7CMNU.js} +9 -1
- package/dist/{chunk-3DUR4WUA.js → chunk-FYUN5KZ3.js} +25 -25
- package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
- package/dist/chunk-GIZNH63R.js +40 -0
- package/dist/chunk-GPPB3JBE.js +688 -0
- package/dist/{chunk-B5XRQOLB.js → chunk-GVQJ5CCZ.js} +17 -11
- package/dist/chunk-HHV2GANA.js +88 -0
- package/dist/chunk-HJWWJ6IL.js +184 -0
- package/dist/{chunk-RKRLDWD3.js → chunk-HLW2MRKE.js} +1 -1
- package/dist/chunk-HRQMFLJP.js +75 -0
- package/dist/{chunk-AD7Y7STJ.js → chunk-HUAS7ITX.js} +107 -97
- package/dist/{chunk-O6I4CIEU.js → chunk-I66EAJFY.js} +6 -6
- package/dist/{chunk-S4COXYBG.js → chunk-IG7BCQBA.js} +129 -106
- package/dist/{chunk-N3PBVRTZ.js → chunk-IXJT6DCX.js} +9 -7
- package/dist/chunk-J4HBWF6Y.js +2635 -0
- package/dist/chunk-J5LZHVIT.js +284 -0
- package/dist/{chunk-GAYUJ7LE.js → chunk-JA5QWE4Z.js} +544 -227
- package/dist/{chunk-B5CSFE7B.js → chunk-JIEGK6UF.js} +6 -174
- package/dist/{chunk-VKBMFOYV.js → chunk-JZWT5J3Y.js} +23 -27
- package/dist/chunk-K6BF4U2H.js +1785 -0
- package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
- package/dist/{chunk-YSEHGPCT.js → chunk-MA3H6DM5.js} +6 -6
- package/dist/{chunk-EBOC7MT3.js → chunk-MOPSG2X7.js} +9 -9
- package/dist/{chunk-KV2AOLDF.js → chunk-MQXIVJ35.js} +18 -29
- package/dist/{chunk-P3JGPQFL.js → chunk-N5UK64DP.js} +4 -4
- package/dist/chunk-NDDGJUXD.js +81 -0
- package/dist/chunk-NEKRRTYW.js +56 -0
- package/dist/{chunk-4ZG3XFUR.js → chunk-NLFAQR7Z.js} +12 -1
- package/dist/{chunk-VO2LKSTM.js → chunk-O42A54GG.js} +2 -2
- package/dist/{chunk-T3Z6VAAF.js → chunk-OJTRZGR3.js} +53 -92
- package/dist/{chunk-5DQRIYDZ.js → chunk-PBP4B7XR.js} +3 -3
- package/dist/{chunk-NQ6UCCOD.js → chunk-PJJ6MY27.js} +8 -8
- package/dist/chunk-PMZCIOCJ.js +25 -0
- package/dist/{chunk-ZGVHUX3M.js → chunk-PMZVFWSR.js} +2 -2
- package/dist/{chunk-7C6RYZGQ.js → chunk-PVAMAVBB.js} +5 -4
- package/dist/chunk-QDC3K2U3.js +262 -0
- package/dist/{chunk-JEQQR47K.js → chunk-QKIFBZKT.js} +173 -14
- package/dist/{chunk-R7LNVMCS.js → chunk-QMXC4JB7.js} +70 -78
- package/dist/chunk-QSNYB6ZV.js +195 -0
- package/dist/chunk-QTFGO774.js +644 -0
- package/dist/{chunk-ODFEOB4F.js → chunk-R23Z6K6I.js} +27 -9
- package/dist/{chunk-3UUY7R3Z.js → chunk-R32CLGZ6.js} +14 -14
- package/dist/chunk-R346GLFC.js +31 -0
- package/dist/chunk-RKSR6VSF.js +132 -0
- package/dist/{chunk-N5XKWMDW.js → chunk-SCYB3HA4.js} +73 -27
- package/dist/{chunk-PZ4I4JE2.js → chunk-SINK3QR6.js} +135 -125
- package/dist/{chunk-5QKCQQ3E.js → chunk-SUW5DORT.js} +2 -2
- package/dist/chunk-T56WDKA5.js +183 -0
- package/dist/{chunk-ECUO3KDP.js → chunk-TD3PGPQA.js} +87 -26
- package/dist/{chunk-HKO36JWF.js → chunk-THYWACCR.js} +31 -3
- package/dist/{chunk-LXPJXFM5.js → chunk-TVH4ONAM.js} +18 -12
- package/dist/chunk-TVHHYFHE.js +255 -0
- package/dist/chunk-U2WB7TZS.js +271 -0
- package/dist/{chunk-NNNWO6F2.js → chunk-UAPGZHYC.js} +237 -33
- package/dist/{chunk-MIX5N5AC.js → chunk-UEDMSP56.js} +415 -160
- package/dist/{chunk-WIE7ZOSW.js → chunk-UXN6JT4W.js} +3 -3
- package/dist/{chunk-CMZWFGD2.js → chunk-VAVR4IJY.js} +19 -15
- package/dist/{chunk-MV2VUEJC.js → chunk-VEGN6WIQ.js} +2 -2
- package/dist/chunk-VKFQTNDV.js +66 -0
- package/dist/{chunk-FALJGAWU.js → chunk-VKRH2TCS.js} +2 -2
- package/dist/{chunk-A2NJGIB3.js → chunk-VN3SHNBN.js} +2 -2
- package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
- package/dist/{chunk-UOSL25KY.js → chunk-VXMFAE2W.js} +1 -1
- package/dist/{chunk-EQ63NRB7.js → chunk-WNP7O5WZ.js} +3 -3
- package/dist/chunk-WR6U3OVP.js +45 -0
- package/dist/{chunk-TEO2TLVN.js → chunk-XKA2ICR3.js} +1051 -1196
- package/dist/{chunk-3V5AYSEQ.js → chunk-Y45G3AXC.js} +72 -65
- package/dist/{chunk-J5Q24KAG.js → chunk-Y4CAGMM6.js} +2 -2
- package/dist/chunk-YAKGTXEV.js +17 -0
- package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
- package/dist/{chunk-YW7UVM5V.js → chunk-ZAZB4JMW.js} +55 -11
- package/dist/{chunk-HCBCAYZU.js → chunk-ZJLUDYFY.js} +11 -11
- package/dist/{chunk-HKIYEGME.js → chunk-ZTJUVLTU.js} +2 -2
- package/dist/{chunk-IFBNV6H6.js → chunk-ZW55JB7N.js} +3 -3
- package/dist/cli/index.js +48 -37
- package/dist/clio-IT3G3VQH.js +25 -0
- package/dist/{code-nav-LMW275PA.js → code-nav-RK6S7F6E.js} +8 -8
- package/dist/codewiki/build-worker.js +1 -1
- package/dist/{compile-cache-CVJMMODC.js → compile-cache-OQAVB7Y5.js} +3 -5
- package/dist/{components-ZFA3SAER.js → components-UBWCQSRW.js} +6 -6
- package/dist/{config-RXS5T3JT.js → config-3QZRWZJF.js} +73 -61
- package/dist/configure-FL7Y3KJF.js +54 -0
- package/dist/{context-MVOORGMF.js → context-5HE7ODYK.js} +61 -49
- package/dist/{context-I3BTOTCS.js → context-KYQFRVDC.js} +11 -11
- package/dist/{context-PALKKQYL.js → context-XNHL75JV.js} +43 -28
- package/dist/{context-clear-N2WOYZ2K.js → context-clear-N545L53A.js} +60 -48
- package/dist/{context-working-set-MIEVECVZ.js → context-working-set-QHKXSV2F.js} +15 -13
- package/dist/{dispatch-runner-VVA4SRRH.js → dispatch-runner-RGIE5PCT.js} +109 -72
- package/dist/{docs-7LQ23DLM.js → docs-5NAF6AU7.js} +7 -13
- package/dist/doctor-ZGPEGHIP.js +772 -0
- package/dist/{eval-IJ5VEZDJ.js → eval-GXLL44RD.js} +106 -86
- package/dist/eval-inventory-HBWSWQOK.js +315 -0
- package/dist/{evidence-L5APPXNV.js → evidence-HWLBRH3Q.js} +209 -51
- package/dist/{evolve-RGNKFJ52.js → evolve-FTZBMNVW.js} +57 -45
- package/dist/{extensions-7WYWUX5A.js → extensions-VHRBEID7.js} +7 -7
- package/dist/{fleet-6CNVBZZP.js → fleet-CKZHJWZJ.js} +215 -114
- package/dist/{fleet-commands-L2SXSYEI.js → fleet-commands-EXDXBMV6.js} +13 -12
- package/dist/fleet-decisions-OTHB6KRL.js +157 -0
- package/dist/{fleet-graph-2J3OOIPO.js → fleet-graph-YTEZUCUT.js} +20 -17
- package/dist/fleet-inspect-SS6YMDCK.js +445 -0
- package/dist/{fleet-preflight-CZRJ4JP5.js → fleet-preflight-PBY4VYOM.js} +6 -9
- package/dist/{fleet-validate-C5RI6DP7.js → fleet-validate-KMEM5L3S.js} +25 -20
- package/dist/fleet-verify-QD5M7E7Q.js +178 -0
- package/dist/fleet-view-WAMJYNDT.js +94 -0
- package/dist/{init-VBN2ACVA.js → init-5XQRBOFV.js} +79 -66
- package/dist/install-NKNG7KD4.js +13 -0
- package/dist/interop-34TVO25M.js +116 -0
- package/dist/{library-JHGUMLY2.js → library-3QY6KF57.js} +25 -21
- package/dist/{memory-K4OQIYWG.js → memory-L4UTIIIW.js} +59 -47
- package/dist/{models-2NCZUWDD.js → models-ZVX3QOWE.js} +47 -37
- package/dist/{monitor-MMVTJABD.js → monitor-CEKVSYTS.js} +67 -57
- package/dist/{orchestrator-ZKBPCHW6.js → orchestrator-77BAP6BC.js} +7633 -7699
- package/dist/panes-7STHOAUJ.js +103 -0
- package/dist/panes-SHAUIRXY.js +60 -0
- package/dist/{paths-DBXMZMDU.js → paths-L7LGY6RN.js} +6 -6
- package/dist/{reset-DD5JGOY3.js → reset-EOLM7GVE.js} +15 -14
- package/dist/resources-74GKTLSF.js +115 -0
- package/dist/{run-QEGNX7FL.js → run-HBAUJNNZ.js} +139 -98
- package/dist/{share-JKD3BQMW.js → share-G3APVLVP.js} +25 -21
- package/dist/{skills-LMQIKDOZ.js → skills-35HHUKCR.js} +37 -27
- package/dist/{skills-eval-I7X2774U.js → skills-eval-QN4HSHDC.js} +80 -73
- package/dist/skills-inventory-J357J34F.js +129 -0
- package/dist/slash-commands-JZZCQA32.js +79 -0
- package/dist/{steer-CF5TDANS.js → steer-XAVHJM22.js} +4 -4
- package/dist/{support-I7LOJLIF.js → support-U7QOWY26.js} +7 -7
- package/dist/{targets-RUSR6B5Z.js → targets-DSM6CY3M.js} +67 -59
- package/dist/{terminal-lease-QYVORFR4.js → terminal-lease-JOPFUVEM.js} +15 -10
- package/dist/tools-MKNWVPBH.js +29 -0
- package/dist/{trace-ODOQIVIW.js → trace-ECQ7TIYZ.js} +194 -7
- package/dist/tui-primitives-K5PASC3O.js +17 -0
- package/dist/{uninstall-ZJF5H5ZN.js → uninstall-N34PCTGJ.js} +6 -8
- package/dist/{upgrade-XANW3FXB.js → upgrade-H7TOM7YL.js} +37 -62
- package/dist/{usage-4H7ZRXQT.js → usage-X52N3IDJ.js} +70 -59
- package/dist/{verifiers-UZXNBZEB.js → verifiers-EJTVVSMA.js} +174 -14
- package/dist/{verify-BVKWTNDL.js → verify-YJL6XET2.js} +12 -11
- package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-MPIFL3LL.js} +3 -4
- package/dist/{wiki-generate-MY7WV2QI.js → wiki-generate-4NDZTQ4B.js} +86 -75
- package/dist/with-panes-OBOBFIIR.js +818 -0
- package/dist/worker/entry.js +131 -91
- package/docs/README.md +3 -2
- package/docs/acp.md +25 -4
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +4 -1
- package/docs/built-in-agents.md +40 -32
- package/docs/capacity-and-scheduling.md +43 -5
- package/docs/commands-and-modes.md +39 -4
- package/docs/config-knobs-audit.md +3 -0
- package/docs/configuration-and-targets.md +138 -167
- package/docs/context-engine.md +1 -1
- package/docs/development-pipeline.md +19 -0
- package/docs/dispatch-typed-intent.md +385 -0
- package/docs/documentation-coverage.md +2 -2
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +3 -1
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +2 -2
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +3 -1
- package/docs/extensions-and-sharing.md +25 -1
- package/docs/fleet-demo-runbook.md +6 -3
- package/docs/fleet-dispatch.md +30 -7
- package/docs/installation-and-lifecycle.md +16 -3
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +10 -1
- package/docs/observability.md +3 -3
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +1 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +164 -177
- 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 +16 -11
- package/docs/troubleshooting.md +2 -2
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/evals/README.md +5 -0
- package/evals/behavioral-corpus-grader.mjs +103 -0
- package/evals/behavioral-machinery-baseline.json +2276 -0
- package/evals/behavioral-machinery-driver.ts +250 -0
- package/evals/behavioral-machinery-support.ts +253 -0
- package/evals/behavioral-machinery.yaml +614 -0
- package/evals/behavioral-model-negative-control.yaml +68 -0
- package/evals/behavioral-model.yaml +216 -0
- package/evals/fixtures/behavioral-facts.txt +3 -0
- package/evals/fixtures/behavioral-main.ts +7 -0
- package/evals/fixtures/decoy/note.txt +2 -0
- package/evals/fixtures/private/credentials.txt +1 -0
- package/evals/tracked-metrics-baseline.yaml +229 -0
- package/evals/tracked-metrics-grader.mjs +129 -0
- package/package.json +8 -16
- package/skills/README.md +6 -6
- package/skills/coding/ast-grep/SKILL.md +8 -2
- package/skills/coding/coding-standards/SKILL.md +8 -2
- package/skills/coding/prototype/SKILL.md +8 -2
- package/skills/coding/tdd/SKILL.md +8 -2
- package/skills/context/context-handoff/SKILL.md +12 -3
- package/skills/context/context-handoff/evals.md +3 -3
- package/skills/context/context-prime/SKILL.md +8 -2
- package/skills/context/context-prime/evals.md +2 -2
- package/skills/git/file-ticket/SKILL.md +9 -4
- package/skills/git/file-ticket/evals.md +2 -2
- package/skills/git/fix-issue/SKILL.md +12 -5
- package/skills/git/fix-issue/evals.md +4 -4
- package/skills/git/resolve-merge-conflicts/SKILL.md +8 -2
- package/skills/git/resolve-merge-conflicts/evals.md +2 -2
- package/skills/git/ship/SKILL.md +9 -3
- package/skills/git/ship/evals.md +4 -4
- package/skills/git/worktree-create/SKILL.md +7 -2
- package/skills/git/worktree-create/evals.md +6 -6
- package/skills/git/worktree-merge/SKILL.md +7 -2
- package/skills/git/worktree-merge/evals.md +6 -6
- package/skills/meta/{clio-dev → clio-coder-dev}/SKILL.md +14 -8
- package/skills/meta/{clio-dev → clio-coder-dev}/evals.md +3 -3
- package/skills/meta/{clio-test → clio-coder-test}/SKILL.md +11 -5
- package/skills/meta/{clio-test → clio-coder-test}/evals.md +1 -1
- package/skills/meta/credentials/SKILL.md +9 -2
- package/skills/meta/find-skills/SKILL.md +8 -2
- package/skills/meta/herdr/SKILL.md +8 -2
- package/skills/meta/skill-craft/SKILL.md +21 -4
- package/skills/planning/architecture/SKILL.md +9 -3
- package/skills/planning/backlog/SKILL.md +8 -2
- package/skills/planning/prd/SKILL.md +8 -2
- package/skills/planning/product-intent/SKILL.md +8 -2
- package/skills/planning/tech-spec/SKILL.md +9 -3
- package/skills/registry.yaml +66 -66
- package/skills/research/arxiv-literature/SKILL.md +8 -2
- package/skills/research/experiment-protocol/SKILL.md +9 -2
- package/skills/research/scientific-debugging/SKILL.md +9 -2
- package/skills/research/scientific-debugging/evals.md +2 -2
- package/skills/research/scientific-modernization/SKILL.md +9 -2
- package/skills/skill-marketplace.json +257 -38
- package/skills/workflow/cut-it/SKILL.md +10 -2
- package/skills/workflow/design-council/SKILL.md +11 -2
- package/skills/workflow/grill-me/SKILL.md +8 -2
- package/skills/workflow/workflow-distiller/SKILL.md +8 -2
- package/src/cli/acp.ts +8 -4
- package/src/cli/argv.ts +25 -0
- package/src/cli/auth.ts +2 -2
- package/src/cli/bootstrap-generate.ts +5 -5
- package/src/cli/clio.ts +6 -1
- package/src/cli/config-inspect.ts +3 -3
- package/src/cli/configure-interop.ts +1 -1
- package/src/cli/configure.ts +50 -48
- package/src/cli/default-target.ts +1 -1
- package/src/cli/docs.ts +6 -6
- package/src/cli/doctor-naming.ts +323 -0
- package/src/cli/doctor-panes.ts +156 -0
- package/src/cli/doctor-toolchain.ts +78 -0
- package/src/cli/doctor.ts +34 -1
- package/src/cli/eval-inventory.ts +436 -0
- package/src/cli/eval.ts +13 -0
- package/src/cli/evidence-detail.ts +88 -0
- package/src/cli/evidence-inventory.ts +183 -0
- package/src/cli/evidence.ts +30 -5
- package/src/cli/fleet-decisions.ts +69 -0
- package/src/cli/fleet-inspect.ts +334 -0
- package/src/cli/fleet-preflight.ts +1 -1
- package/src/cli/fleet-verify.ts +133 -0
- package/src/cli/fleet-view.ts +827 -0
- package/src/cli/fleet.ts +154 -40
- 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/models.ts +3 -3
- package/src/cli/modes/print.ts +1 -1
- package/src/cli/panes.ts +35 -0
- package/src/cli/reset.ts +6 -3
- package/src/cli/run.ts +67 -13
- package/src/cli/share.ts +1 -1
- package/src/cli/skills-eval.ts +11 -11
- package/src/cli/skills-inventory.ts +185 -0
- package/src/cli/skills.ts +16 -13
- package/src/cli/targets.ts +23 -24
- package/src/cli/tools.ts +321 -0
- package/src/cli/trace-inspect.ts +252 -0
- package/src/cli/trace.ts +64 -2
- package/src/cli/uninstall.ts +2 -2
- package/src/cli/upgrade.ts +29 -34
- package/src/cli/usage.ts +2 -2
- package/src/cli/validate-model.ts +1 -1
- package/src/cli/verifiers-inspect.ts +347 -0
- package/src/cli/verifiers.ts +9 -0
- package/src/cli/wiki-generate.ts +5 -5
- package/src/core/bash-exec.ts +1 -1
- package/src/core/boot-trace.ts +3 -3
- package/src/core/bus-events.ts +4 -0
- package/src/core/bus-trace.ts +2 -2
- package/src/core/compile-cache.ts +1 -1
- package/src/core/config.ts +1070 -845
- package/src/core/defaults.ts +342 -463
- package/src/core/domain-loader.ts +6 -4
- package/src/core/event-bus.ts +1 -1
- package/src/core/file-references.ts +78 -88
- package/src/core/git-commit-attribution.ts +3 -9
- package/src/core/guardrails.ts +24 -4
- package/src/core/naming-compat.ts +45 -0
- package/src/core/naming-events.ts +62 -0
- package/src/core/recent-models.ts +1 -7
- package/src/core/residency-protection.ts +4 -8
- package/src/core/resolve-config-value.ts +1 -1
- package/src/core/response-model-id.ts +0 -14
- package/src/core/response-schema.ts +44 -4
- package/src/core/session-routing.ts +84 -74
- package/src/core/skill-activation.ts +18 -0
- package/src/core/termination.ts +4 -4
- package/src/core/theme-token-hex.ts +43 -0
- package/src/core/timers.ts +2 -12
- package/src/core/tool-names.ts +2 -1
- package/src/core/xdg.ts +1 -1
- package/src/domains/agents/catalog.ts +2 -12
- package/src/domains/agents/extension.ts +3 -3
- package/src/domains/agents/fleets/build-review.md +0 -3
- package/src/domains/agents/fleets/build-test.md +0 -3
- package/src/domains/agents/registry.ts +7 -7
- package/src/domains/agents/result-contract-filesystem.ts +32 -0
- package/src/domains/agents/result-contract.ts +165 -36
- package/src/domains/agents/spec.ts +0 -11
- package/src/domains/config/classify.ts +37 -46
- package/src/domains/config/extension.ts +5 -5
- package/src/domains/config/keybindings.ts +48 -47
- package/src/domains/config/watcher.ts +1 -1
- package/src/domains/context/codewiki/indexer.ts +1 -1
- package/src/domains/context/extension.ts +3 -3
- package/src/domains/context/prompt-context.ts +1 -1
- package/src/domains/context/wiki/assemble.ts +2 -2
- package/src/domains/context/working-set/replay/metrics.ts +0 -4
- package/src/domains/context/working-set/replay/report.ts +1 -1
- package/src/domains/context/working-set/replay/synthetic.ts +2 -2
- package/src/domains/dispatch/active-route-planner.ts +2 -2
- package/src/domains/dispatch/admission-error.ts +9 -0
- package/src/domains/dispatch/admission.ts +22 -21
- package/src/domains/dispatch/agent-candidates.ts +1 -1
- package/src/domains/dispatch/agent-ledger-store.ts +1 -1
- package/src/domains/dispatch/agent-ledger.ts +1 -1
- package/src/domains/dispatch/assignment.ts +5 -5
- package/src/domains/dispatch/capacity-lease.ts +23 -6
- package/src/domains/dispatch/code-step.ts +1 -1
- package/src/domains/dispatch/contract.ts +2 -2
- package/src/domains/dispatch/council-topology.ts +398 -0
- package/src/domains/dispatch/extension.ts +287 -111
- package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
- package/src/domains/dispatch/fleet-plan.ts +7 -2
- package/src/domains/dispatch/fleet-preflight.ts +8 -17
- package/src/domains/dispatch/fleet-run.ts +59 -3
- package/src/domains/dispatch/gate-decisions.ts +36 -15
- package/src/domains/dispatch/gate-role-prompts.ts +9 -0
- package/src/domains/dispatch/gate-topology.ts +289 -0
- package/src/domains/dispatch/index.ts +19 -0
- package/src/domains/dispatch/intent-compatibility.ts +330 -0
- package/src/domains/dispatch/intent.ts +85 -1
- package/src/domains/dispatch/joint-route-resolver.ts +1 -1
- package/src/domains/dispatch/path-scope.ts +0 -61
- package/src/domains/dispatch/placement.ts +2 -2
- package/src/domains/dispatch/receipt-findings.ts +1 -1
- package/src/domains/dispatch/receipt-integrity.ts +20 -8
- package/src/domains/dispatch/reservation-store.ts +24 -27
- package/src/domains/dispatch/route-decision.ts +1 -1
- package/src/domains/dispatch/route-history.ts +1 -1
- package/src/domains/dispatch/route-policy.ts +1 -1
- package/src/domains/dispatch/route-readiness.ts +0 -28
- package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
- package/src/domains/dispatch/run-event-journal.ts +599 -0
- package/src/domains/dispatch/state.ts +45 -1
- package/src/domains/dispatch/tool-stats.ts +1 -1
- package/src/domains/dispatch/transport.ts +2 -19
- package/src/domains/dispatch/types.ts +16 -3
- package/src/domains/dispatch/validation.ts +33 -8
- package/src/domains/dispatch/worker-protocol.ts +2 -2
- package/src/domains/eval/artifacts/store.ts +5 -4
- package/src/domains/eval/compare/compare.ts +3 -16
- package/src/domains/eval/compare/thresholds.ts +0 -8
- package/src/domains/eval/execution-provenance.ts +1 -1
- package/src/domains/eval/inventory.ts +113 -0
- package/src/domains/eval/metrics/token-stream.ts +0 -7
- package/src/domains/eval/report.ts +1 -1
- package/src/domains/eval/runner.ts +1 -1
- package/src/domains/eval/runners/clio-run.ts +7 -6
- package/src/domains/eval/runners/external-command.ts +4 -3
- package/src/domains/eval/schema/artifact.ts +1 -1
- package/src/domains/eval/schema/behavioral-metrics.ts +3 -2
- package/src/domains/eval/schema/behavioral.ts +7 -6
- package/src/domains/eval/schema/execution-envelope.ts +4 -2
- package/src/domains/eval/schema/naming.ts +10 -0
- package/src/domains/eval/schema/serving.ts +31 -0
- package/src/domains/eval/schema/suite.ts +2 -2
- package/src/domains/eval/schema/validate.ts +9 -5
- package/src/domains/eval/schema/verdict.ts +4 -2
- package/src/domains/eval/store.ts +12 -2
- package/src/domains/eval/suites/run.ts +12 -6
- package/src/domains/eval/types.ts +1 -1
- package/src/domains/eval/workspaces/git.ts +1 -1
- package/src/domains/eval/workspaces/temp-copy.ts +1 -1
- package/src/domains/evidence/build.ts +13 -5
- package/src/domains/evidence/store.ts +6 -0
- package/src/domains/interop/consent.ts +7 -7
- package/src/domains/interop/extension.ts +1 -1
- package/src/domains/lifecycle/doctor.ts +27 -21
- package/src/domains/lifecycle/migrations/2026-09-01-clio-coder-naming.ts +155 -0
- package/src/domains/lifecycle/migrations/2026-09-01-retire-panes-knobs.ts +59 -0
- package/src/domains/lifecycle/migrations/2026-09-01-settings-v2.ts +338 -0
- package/src/domains/lifecycle/migrations/index.ts +20 -1
- package/src/domains/lifecycle/naming-history.ts +153 -0
- package/src/domains/lifecycle/naming-mutable-state.ts +104 -0
- package/src/domains/lifecycle/naming-resources.ts +261 -0
- package/src/domains/lifecycle/naming-tool-markers.ts +139 -0
- package/src/domains/lifecycle/naming-yazi.ts +81 -0
- package/src/domains/lifecycle/upgrade-notice.ts +1 -1
- package/src/domains/memory/index.ts +0 -1
- package/src/domains/memory/task-memory-handoff.ts +6 -3
- package/src/domains/memory/task-memory-policy.ts +6 -10
- package/src/domains/memory/task-memory-telemetry.ts +1 -0
- package/src/domains/middleware/index.ts +7 -0
- package/src/domains/middleware/marketplace-offer.ts +339 -0
- package/src/domains/middleware/memory-intervention.ts +33 -21
- package/src/domains/middleware/runtime.ts +2 -2
- package/src/domains/mux/contract.ts +566 -0
- package/src/domains/mux/detect.ts +182 -0
- package/src/domains/mux/dock-controller.ts +365 -0
- package/src/domains/mux/extension.ts +53 -0
- package/src/domains/mux/index.ts +116 -0
- package/src/domains/mux/manifest.ts +6 -0
- package/src/domains/mux/operations.ts +192 -0
- package/src/domains/mux/pane-registry.ts +90 -0
- package/src/domains/mux/protocol.ts +62 -0
- package/src/domains/mux/socket-client.ts +964 -0
- package/src/domains/mux/types.ts +265 -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 +182 -0
- package/src/domains/mux/yazi/profile.ts +299 -0
- package/src/domains/mux/yazi/session.ts +251 -0
- package/src/domains/mux/yazi/theme.ts +30 -0
- package/src/domains/observability/cost.ts +21 -0
- package/src/domains/observability/extension.ts +1 -1
- package/src/domains/observability/index.ts +2 -0
- package/src/domains/observability/out-of-turn-usage.ts +2 -2
- package/src/domains/observability/projection.ts +1 -1
- package/src/domains/observability/trace-store.ts +63 -11
- package/src/domains/prompts/compiler.ts +2 -2
- package/src/domains/prompts/extension.ts +3 -3
- package/src/domains/prompts/fragments/identity/clio-worker.md +1 -1
- package/src/domains/prompts/fragments/operating/skills.md +25 -18
- package/src/domains/providers/endpoint-capacity.ts +154 -22
- package/src/domains/providers/endpoint-slots-store.ts +189 -0
- package/src/domains/providers/extension.ts +25 -8
- package/src/domains/providers/index.ts +22 -0
- package/src/domains/providers/knowledge-base-path.ts +1 -1
- package/src/domains/providers/model-runtime-capabilities.ts +46 -2
- package/src/domains/providers/models/cloud-models/alcf.yaml +1 -1
- package/src/domains/providers/models/local-models/{clio-local-coding-targets.yaml → clio-coder-local-coding-targets.yaml} +4 -4
- package/src/domains/providers/plugins.ts +1 -1
- package/src/domains/providers/resolver.ts +1 -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/cloud/alcf.ts +4 -4
- package/src/domains/providers/runtimes/common/lmstudio-http.ts +1 -5
- package/src/domains/providers/runtimes/common/local-synth.ts +3 -3
- package/src/domains/providers/runtimes/common/probe-helpers.ts +2 -2
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +2 -9
- package/src/domains/providers/runtimes/protocol/litellm.ts +285 -0
- package/src/domains/providers/support.ts +1 -0
- package/src/domains/providers/target-model-cache.ts +124 -0
- package/src/domains/providers/types/knowledge-base.ts +13 -0
- package/src/domains/providers/types/local-model-quirks.ts +2 -2
- package/src/domains/providers/types/target-descriptor.ts +3 -3
- package/src/domains/resources/extension.ts +1 -1
- package/src/domains/resources/index.ts +3 -0
- package/src/domains/resources/library.ts +6 -6
- package/src/domains/resources/prompts/loader.ts +1 -1
- package/src/domains/resources/skills/content-hash.ts +7 -6
- package/src/domains/resources/skills/evals.ts +1 -1
- package/src/domains/resources/skills/install.ts +36 -12
- package/src/domains/resources/skills/loader.ts +72 -13
- package/src/domains/resources/skills/marketplace.ts +21 -2
- package/src/domains/resources/skills/promotion.ts +214 -0
- package/src/domains/safety/action-classifier.ts +6 -0
- package/src/domains/safety/audit.ts +2 -2
- package/src/domains/safety/finish-contract.ts +2 -1
- package/src/domains/safety/run-effects.ts +1 -1
- package/src/domains/scheduling/extension.ts +4 -4
- package/src/domains/session/archive-readers.ts +9 -1
- package/src/domains/session/compaction/branch-summary.ts +4 -0
- package/src/domains/session/compaction/compact.ts +1 -1
- package/src/domains/session/compaction/tokens.ts +4 -0
- package/src/domains/session/context-accounting.ts +1 -1
- package/src/domains/session/decision-board.ts +1 -1
- package/src/domains/session/extension.ts +1 -1
- package/src/domains/session/handoff.ts +2 -2
- package/src/domains/session/prompt-manifest.ts +0 -5
- package/src/domains/session/protected-artifact-journal.ts +12 -6
- package/src/domains/session/retry.ts +1 -1
- package/src/domains/share/archive.ts +85 -36
- 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 +3 -3
- package/src/engine/acp/event-mapper.ts +3 -3
- package/src/engine/acp/server.ts +440 -96
- package/src/engine/acp/tool-mediator.ts +2 -2
- package/src/engine/acp/types.ts +20 -2
- package/src/engine/alcf-oauth.ts +5 -5
- package/src/engine/antigravity/subprocess-runtime.ts +2 -2
- package/src/engine/api-registry.ts +1 -1
- package/src/engine/apis/llamacpp-residency.ts +1 -19
- package/src/engine/apis/lmstudio.ts +1 -6
- package/src/engine/apis/ollama-native.ts +3 -3
- package/src/engine/apis/openai-completions.ts +2 -2
- package/src/engine/apis/residency.ts +3 -9
- package/src/engine/claude/sdk-module.ts +87 -0
- package/src/engine/claude/sdk-runtime.ts +25 -17
- package/src/engine/claude/subprocess-runtime.ts +1 -1
- package/src/engine/claude/tool-safety.ts +4 -4
- package/src/engine/loop-guard.ts +0 -5
- package/src/engine/session.ts +14 -7
- package/src/engine/tui-primitives.ts +21 -0
- package/src/engine/tui.ts +1 -0
- package/src/engine/worker-events.ts +6 -6
- package/src/engine/worker-runtime.ts +16 -26
- package/src/entry/boot-options.ts +2 -0
- package/src/entry/orchestrator.ts +207 -90
- package/src/entry/panes-activation.ts +31 -0
- package/src/entry/with-panes.ts +20 -0
- package/src/interactive/application-controller.ts +21 -21
- package/src/interactive/bus-notices.ts +2 -2
- package/src/interactive/chat-loop.ts +8 -8
- package/src/interactive/chat-panel.ts +1 -1
- package/src/interactive/clio-editor.ts +105 -1
- package/src/interactive/context-activity.ts +8 -6
- package/src/interactive/context-meter.ts +3 -6
- package/src/interactive/context-overlay.ts +2 -1
- package/src/interactive/cost-overlay.ts +3 -1
- package/src/interactive/council-grid.ts +3 -5
- package/src/interactive/council.ts +11 -0
- package/src/interactive/dispatch-board.ts +56 -49
- package/src/interactive/editor-bash.ts +21 -0
- package/src/interactive/editor-steer.ts +9 -4
- package/src/interactive/editor-submit.ts +24 -18
- package/src/interactive/external-editor.ts +1 -1
- package/src/interactive/file-reference-completion.ts +360 -0
- package/src/interactive/footer/dashboard.ts +15 -17
- package/src/interactive/footer/notifications.ts +14 -8
- package/src/interactive/footer/widgets.ts +2 -44
- package/src/interactive/interactive-application.ts +229 -62
- package/src/interactive/interactive-event-projection.ts +1 -1
- package/src/interactive/interactive-input-runtime.ts +9 -2
- package/src/interactive/interactive-presentation.ts +21 -16
- package/src/interactive/interactive-shell.ts +2 -2
- package/src/interactive/interactive-slash-runtime.ts +99 -23
- package/src/interactive/interactive-subscriptions.ts +1 -1
- package/src/interactive/interactive-tickers.ts +1 -1
- package/src/interactive/keybinding-manager.ts +4 -4
- package/src/interactive/layout.ts +1 -1
- package/src/interactive/memory-overlay.ts +3 -2
- package/src/interactive/modal-marker.ts +151 -0
- package/src/interactive/mutation-preview.ts +1 -1
- package/src/interactive/mux-bridge.ts +214 -0
- package/src/interactive/overlay-frame.ts +62 -14
- package/src/interactive/overlay-general-openers.ts +7 -2
- package/src/interactive/overlay-key-routing.ts +21 -10
- package/src/interactive/overlay-lifecycle.ts +20 -4
- package/src/interactive/overlay-model-selectors.ts +43 -6
- package/src/interactive/overlay-permission-lifecycle.ts +90 -22
- package/src/interactive/overlay-transitions.ts +18 -4
- package/src/interactive/overlays/agents.ts +3 -2
- package/src/interactive/overlays/ask-user.ts +161 -41
- package/src/interactive/overlays/auth-dialog.ts +1 -0
- package/src/interactive/overlays/context-reset.ts +29 -4
- package/src/interactive/overlays/cwd-fallback.ts +29 -4
- package/src/interactive/overlays/decisions.ts +15 -13
- package/src/interactive/overlays/extensions.ts +1 -0
- package/src/interactive/overlays/fleet-run-approval.ts +4 -3
- package/src/interactive/overlays/handoff-review.ts +3 -2
- package/src/interactive/overlays/help-reference.ts +2 -0
- package/src/interactive/overlays/interop.ts +2 -1
- package/src/interactive/overlays/library-install-confirm.ts +2 -1
- 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 +2 -1
- package/src/interactive/overlays/model-scope.ts +86 -0
- package/src/interactive/overlays/model-selector.ts +40 -31
- package/src/interactive/overlays/prompts.ts +11 -4
- package/src/interactive/overlays/session-selector.ts +3 -2
- package/src/interactive/overlays/settings-sections.ts +30 -0
- package/src/interactive/overlays/settings.ts +849 -268
- package/src/interactive/overlays/side-question.ts +3 -2
- package/src/interactive/overlays/skills-hub.ts +11 -19
- package/src/interactive/overlays/tree-selector.ts +3 -2
- package/src/interactive/pane-policy.ts +46 -0
- package/src/interactive/panes-runtime.ts +318 -0
- package/src/interactive/permission-overlay.ts +2 -2
- package/src/interactive/renderers/compaction-summary.ts +29 -0
- package/src/interactive/renderers/retry-status.ts +12 -4
- package/src/interactive/renderers/tool-execution.ts +4 -4
- package/src/interactive/renderers/worker-entry.ts +122 -14
- package/src/interactive/session-transcript.ts +3 -3
- package/src/interactive/slash-autocomplete.ts +454 -165
- package/src/interactive/slash-commands.ts +521 -189
- package/src/interactive/slash-spec.ts +160 -17
- package/src/interactive/status/controller.ts +2 -2
- package/src/interactive/stream-pacing-policy.ts +1 -1
- package/src/interactive/tasks-overlay.ts +21 -13
- package/src/interactive/terminal-lease.ts +6 -7
- package/src/interactive/theme/tokens.ts +3 -14
- package/src/interactive/turn-context.ts +58 -13
- package/src/interactive/turn-persistence.ts +2 -2
- package/src/interactive/turn-prewarm.ts +2 -2
- package/src/interactive/turn-runtime.ts +8 -8
- package/src/interactive/turn-state.ts +1 -1
- package/src/interactive/view/artifacts.ts +120 -9
- package/src/interactive/view/view-overlay.ts +14 -10
- package/src/interactive/watch-pane.ts +180 -0
- package/src/interactive/welcome-dashboard.ts +15 -15
- package/src/interactive/worker-progress.ts +11 -5
- package/src/interactive/worker-receipts.ts +20 -2
- package/src/interactive/worker-stream.ts +6 -4
- package/src/interactive/yazi-bridge.ts +451 -0
- package/src/tools/ask-user.ts +3 -3
- package/src/tools/bash.ts +1 -1
- package/src/tools/bootstrap.ts +26 -2
- package/src/tools/builtin-tool-catalog.ts +16 -1
- package/src/tools/compete-worktrees.ts +108 -17
- package/src/tools/context/index.ts +15 -1
- package/src/tools/core-bootstrap.ts +2 -1
- package/src/tools/dispatch-admission.ts +18 -5
- package/src/tools/dispatch-arguments.ts +21 -21
- package/src/tools/dispatch-plan.ts +0 -11
- package/src/tools/dispatch-run-events.ts +145 -28
- package/src/tools/dispatch-runner.ts +40 -13
- package/src/tools/dispatch-types.ts +15 -3
- package/src/tools/dispatch.ts +2 -2
- package/src/tools/executables.ts +17 -14
- package/src/tools/find.ts +1 -6
- package/src/tools/monitor.ts +4 -4
- package/src/tools/observation.ts +54 -4
- package/src/tools/observers.ts +0 -33
- 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/task-worktree.ts +28 -7
- package/src/tools/verify/authoring.ts +3 -6
- package/src/tools/verify/frontend.ts +1 -1
- package/src/tools/web-fetch.ts +1 -1
- package/src/worker/entry.ts +4 -3
- package/src/worker/protocol.ts +16 -14
- package/src/worker/spec-contract.ts +2 -2
- package/src/worker/stdin-demux.ts +2 -2
- package/dist/chunk-D73KXYPF.js +0 -37
- package/dist/chunk-MLOK6ZOS.js +0 -2888
- package/dist/chunk-PNY46YEY.js +0 -1581
- package/dist/chunk-XDOQXGFO.js +0 -515
- package/dist/chunk-YKOFT37S.js +0 -279
- package/dist/clio-LT5V7SSZ.js +0 -26
- package/dist/configure-2WYWSCSD.js +0 -49
- package/dist/doctor-TWBWFK5V.js +0 -165
- package/src/engine/apis/degraded-inference.ts +0 -95
- /package/skills/meta/{clio-test → clio-coder-test}/references/harness.md +0 -0
- /package/skills/meta/{clio-test → clio-coder-test}/references/test-map.md +0 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append-only run event journal.
|
|
3
|
+
*
|
|
4
|
+
* Live dispatch events exist only in `DispatchRunEventRegistry`'s in-memory
|
|
5
|
+
* tail (src/tools/dispatch-run-events.ts), so a separate process cannot see a
|
|
6
|
+
* run in flight: the ledger and receipts are durable but the event stream is
|
|
7
|
+
* not. This module tees that same display tail to disk so a viewer process
|
|
8
|
+
* (`clio-coder fleet view`) can follow a run without sharing the orchestrator's
|
|
9
|
+
* heap.
|
|
10
|
+
*
|
|
11
|
+
* On-disk layout under `clioStateDir()`:
|
|
12
|
+
* runs/<runId>/events.ndjson one JSON object per line, newest last
|
|
13
|
+
*
|
|
14
|
+
* Every line carries a monotonically increasing `seq` (per run, starting at 1)
|
|
15
|
+
* and a wall-clock `at`. Line kinds:
|
|
16
|
+
* open run opened, carries agentId
|
|
17
|
+
* event one display-tail entry; the only droppable kind
|
|
18
|
+
* journal_truncated written once, the moment dropping begins
|
|
19
|
+
* receipt sealed receipt facts (outcome, exit code, digest)
|
|
20
|
+
* terminal always the last write for a run
|
|
21
|
+
*
|
|
22
|
+
* Bounding follows the worker bulk lane's philosophy. Display-only `event`
|
|
23
|
+
* lines are dropped once a run's journal crosses {@link
|
|
24
|
+
* RUN_EVENT_JOURNAL_CAP_BYTES}, and a single `journal_truncated` marker records
|
|
25
|
+
* that dropping began. Lifecycle (`open`, `terminal`) and receipt-bearing lines
|
|
26
|
+
* are never dropped, so a capped journal still answers what the run was and how
|
|
27
|
+
* it ended.
|
|
28
|
+
*
|
|
29
|
+
* Writes are buffered and flushed with one `appendFileSync` per batch rather
|
|
30
|
+
* than queued onto an async chain. A queue would need a timer to drain, and a
|
|
31
|
+
* timer on the dispatch event path either keeps a finished process alive or has
|
|
32
|
+
* to be unref'd and then races finalization; batching keeps the syscall count
|
|
33
|
+
* proportional to bytes written and leaves no handle behind. Every
|
|
34
|
+
* non-droppable line flushes immediately, so the durable file is never behind
|
|
35
|
+
* on the facts a viewer needs.
|
|
36
|
+
*
|
|
37
|
+
* A sink failure (ENOSPC, EPERM, a removed state root) degrades the whole
|
|
38
|
+
* journal to off after one notice. Nothing here ever throws into a dispatch.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import { appendFileSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
42
|
+
import { join } from "node:path";
|
|
43
|
+
import { normalizeClioCoderEventType } from "../../core/naming-events.js";
|
|
44
|
+
import { clioStateDir, stateRootRemoved } from "../../core/xdg.js";
|
|
45
|
+
|
|
46
|
+
/** Directory name under the state root holding one subdirectory per run. */
|
|
47
|
+
export const RUN_EVENT_JOURNAL_DIR = "runs";
|
|
48
|
+
/** File name inside each run's journal directory. */
|
|
49
|
+
export const RUN_EVENT_JOURNAL_FILE = "events.ndjson";
|
|
50
|
+
/** Per-run size cap; crossing it drops display-only lines behind one marker. */
|
|
51
|
+
export const RUN_EVENT_JOURNAL_CAP_BYTES = 2 * 1024 * 1024;
|
|
52
|
+
/** Pending bytes that trigger a flush of droppable lines. */
|
|
53
|
+
export const RUN_EVENT_JOURNAL_FLUSH_BYTES = 8 * 1024;
|
|
54
|
+
/** Per-process env override, resolved ahead of settings (see runEventJournalEnabled). */
|
|
55
|
+
export const RUN_EVENT_JOURNAL_ENV_VAR = "CLIO_CODER_RUN_JOURNAL";
|
|
56
|
+
|
|
57
|
+
interface JournalLineBase {
|
|
58
|
+
seq: number;
|
|
59
|
+
at: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Everything on a journal line except the `seq`/`at` envelope the writer adds. */
|
|
63
|
+
export type RunEventJournalBody =
|
|
64
|
+
| { kind: "open"; runId: string; agentId: string }
|
|
65
|
+
| { kind: "event"; type: string; detail?: string }
|
|
66
|
+
| { kind: "journal_truncated"; reason: string; droppedFromSeq: number }
|
|
67
|
+
| {
|
|
68
|
+
kind: "receipt";
|
|
69
|
+
outcome: string;
|
|
70
|
+
exitCode: number | null;
|
|
71
|
+
digest?: string;
|
|
72
|
+
receiptPath?: string;
|
|
73
|
+
attemptRunId?: string;
|
|
74
|
+
}
|
|
75
|
+
| { kind: "terminal"; outcome: string; detail?: string };
|
|
76
|
+
|
|
77
|
+
export type RunEventJournalLine = JournalLineBase & RunEventJournalBody;
|
|
78
|
+
|
|
79
|
+
/** One display-tail entry, structurally the registry's `RunTailEntry`. */
|
|
80
|
+
export interface RunEventJournalEntry {
|
|
81
|
+
at: string;
|
|
82
|
+
type: string;
|
|
83
|
+
detail?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Sealed receipt facts worth keeping even when display lines are being dropped. */
|
|
87
|
+
export interface RunEventJournalReceipt {
|
|
88
|
+
outcome: string;
|
|
89
|
+
exitCode: number | null;
|
|
90
|
+
digest?: string | undefined;
|
|
91
|
+
receiptPath?: string | undefined;
|
|
92
|
+
/** The terminal attempt's run id, recorded only when it differs from the logical run. */
|
|
93
|
+
attemptRunId?: string | undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* What the run event registry writes to. Every method is best-effort and
|
|
98
|
+
* returns void: a journal that cannot write degrades itself and the dispatch
|
|
99
|
+
* never learns about it.
|
|
100
|
+
*/
|
|
101
|
+
export interface RunEventJournalSink {
|
|
102
|
+
open(runId: string, agentId: string): void;
|
|
103
|
+
append(runId: string, entry: RunEventJournalEntry): void;
|
|
104
|
+
receipt(runId: string, receipt: RunEventJournalReceipt): void;
|
|
105
|
+
terminal(runId: string, outcome: string, detail?: string): void;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface RunEventJournal extends RunEventJournalSink {
|
|
109
|
+
/** Flush any buffered droppable lines. Called by tests and by terminal writes. */
|
|
110
|
+
flush(): void;
|
|
111
|
+
/** True once a write failed and the journal turned itself off. */
|
|
112
|
+
degraded(): boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Enablement
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
let configuredEnabled: boolean | undefined;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Install the `panes.journal` setting. Called from the composition root beside
|
|
123
|
+
* `configureGuardrails` so the journal never reads settings.yaml itself; a
|
|
124
|
+
* settings read on the dispatch event path would be both a cost and a throw
|
|
125
|
+
* site (readSettings rejects an invalid file).
|
|
126
|
+
*/
|
|
127
|
+
export function configureRunEventJournal(enabled: boolean | undefined): void {
|
|
128
|
+
configuredEnabled = enabled;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function parseEnvFlag(raw: string | undefined): boolean | undefined {
|
|
132
|
+
if (raw === undefined) return undefined;
|
|
133
|
+
const normalized = raw.trim().toLowerCase();
|
|
134
|
+
if (normalized === "1" || normalized === "true" || normalized === "on") return true;
|
|
135
|
+
if (normalized === "0" || normalized === "false" || normalized === "off") return false;
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** env override > configured settings > on. */
|
|
140
|
+
function runEventJournalEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
141
|
+
return parseEnvFlag(env[RUN_EVENT_JOURNAL_ENV_VAR]) ?? configuredEnabled ?? true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Paths
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
/** Root holding one journal directory per run. */
|
|
149
|
+
export function runEventJournalRoot(): string {
|
|
150
|
+
return join(clioStateDir(), RUN_EVENT_JOURNAL_DIR);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** One run's journal directory. */
|
|
154
|
+
function runEventJournalDir(runId: string, root?: string): string {
|
|
155
|
+
return join(root ?? runEventJournalRoot(), runId);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** One run's NDJSON file. */
|
|
159
|
+
export function runEventJournalPath(runId: string, root?: string): string {
|
|
160
|
+
return join(runEventJournalDir(runId, root), RUN_EVENT_JOURNAL_FILE);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The run whose journal was written to most recently, or null when none exists.
|
|
165
|
+
*
|
|
166
|
+
* `/panes open logs` tails it. Reading mtimes off the journal files is the
|
|
167
|
+
* cheapest honest answer: the ledger orders runs by admission, and the run an
|
|
168
|
+
* operator wants to watch is the one still producing lines, which is not always
|
|
169
|
+
* the newest admitted one.
|
|
170
|
+
*/
|
|
171
|
+
export function newestRunEventJournalRunId(root?: string): string | null {
|
|
172
|
+
if (stateRootRemoved()) return null;
|
|
173
|
+
const dir = root ?? runEventJournalRoot();
|
|
174
|
+
let names: string[];
|
|
175
|
+
try {
|
|
176
|
+
names = readdirSync(dir);
|
|
177
|
+
} catch {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
let newest: { runId: string; at: number } | null = null;
|
|
181
|
+
for (const runId of names) {
|
|
182
|
+
if (!isSafeRunId(runId)) continue;
|
|
183
|
+
const stats = statSync(runEventJournalPath(runId, dir), { throwIfNoEntry: false });
|
|
184
|
+
if (stats === undefined || !stats.isFile()) continue;
|
|
185
|
+
const at = stats.mtimeMs;
|
|
186
|
+
if (newest === null || at > newest.at) newest = { runId, at };
|
|
187
|
+
}
|
|
188
|
+
return newest?.runId ?? null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Drop the journal directories of runs that left the ledger ring. Best-effort:
|
|
193
|
+
* retention is bookkeeping and must never fail the persist that triggered it.
|
|
194
|
+
*/
|
|
195
|
+
export function removeRunEventJournals(runIds: Iterable<string>, root?: string): void {
|
|
196
|
+
if (stateRootRemoved()) return;
|
|
197
|
+
for (const runId of runIds) {
|
|
198
|
+
if (!isSafeRunId(runId)) continue;
|
|
199
|
+
try {
|
|
200
|
+
rmSync(runEventJournalDir(runId, root), { recursive: true, force: true });
|
|
201
|
+
} catch {
|
|
202
|
+
// A directory we could not remove is retained, not an error.
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Run ids are generated by `newRunId()` as base36, and fleet/gate ids add a
|
|
209
|
+
* prefix and hex. Nothing legitimate contains a path separator, so anything
|
|
210
|
+
* that does is refused rather than resolved: this function's only two callers
|
|
211
|
+
* build a path from the id and then remove or write it.
|
|
212
|
+
*/
|
|
213
|
+
function isSafeRunId(runId: string): boolean {
|
|
214
|
+
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(runId) && runId !== "." && runId !== "..";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// Writer
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
interface RunJournalState {
|
|
222
|
+
path: string;
|
|
223
|
+
seq: number;
|
|
224
|
+
bytes: number;
|
|
225
|
+
pending: string[];
|
|
226
|
+
pendingBytes: number;
|
|
227
|
+
dropping: boolean;
|
|
228
|
+
closed: boolean;
|
|
229
|
+
touchedAt: number;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Runs tracked at once. Bounded so a long-lived orchestrator that never sees a
|
|
234
|
+
* terminal event for some run (a crash, an abandoned batch) cannot grow this
|
|
235
|
+
* map without limit. Eviction only forgets the byte counter; the file itself is
|
|
236
|
+
* untouched and a later write to an evicted run reopens it with a fresh count.
|
|
237
|
+
*/
|
|
238
|
+
const RUN_STATE_LIMIT = 256;
|
|
239
|
+
|
|
240
|
+
export interface CreateRunEventJournalOptions {
|
|
241
|
+
/** Override the journal root. Defaults to `clioStateDir()/runs`, resolved per write. */
|
|
242
|
+
root?: string;
|
|
243
|
+
capBytes?: number;
|
|
244
|
+
flushBytes?: number;
|
|
245
|
+
warn?: (message: string) => void;
|
|
246
|
+
now?: () => Date;
|
|
247
|
+
/** Override enablement. Defaults to {@link runEventJournalEnabled}. */
|
|
248
|
+
isEnabled?: () => boolean;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function createRunEventJournal(options: CreateRunEventJournalOptions = {}): RunEventJournal {
|
|
252
|
+
const capBytes = options.capBytes ?? RUN_EVENT_JOURNAL_CAP_BYTES;
|
|
253
|
+
const flushBytes = options.flushBytes ?? RUN_EVENT_JOURNAL_FLUSH_BYTES;
|
|
254
|
+
const warn = options.warn ?? ((message: string) => process.stderr.write(`[clio-coder:journal] ${message}\n`));
|
|
255
|
+
const now = options.now ?? (() => new Date());
|
|
256
|
+
const isEnabled = options.isEnabled ?? (() => runEventJournalEnabled());
|
|
257
|
+
const runs = new Map<string, RunJournalState>();
|
|
258
|
+
let degraded = false;
|
|
259
|
+
|
|
260
|
+
const degrade = (error: unknown): void => {
|
|
261
|
+
if (degraded) return;
|
|
262
|
+
degraded = true;
|
|
263
|
+
runs.clear();
|
|
264
|
+
warn(
|
|
265
|
+
`run event journal disabled for this process: ${error instanceof Error ? error.message : String(error)}. ` +
|
|
266
|
+
"Dispatch is unaffected; `clio-coder fleet view` will have no transcript for runs started from here.",
|
|
267
|
+
);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const pruneRunStates = (): void => {
|
|
271
|
+
while (runs.size > RUN_STATE_LIMIT) {
|
|
272
|
+
let oldestKey: string | null = null;
|
|
273
|
+
let oldestSeen = Number.POSITIVE_INFINITY;
|
|
274
|
+
for (const [key, state] of runs) {
|
|
275
|
+
if (state.pending.length > 0 || state.touchedAt >= oldestSeen) continue;
|
|
276
|
+
oldestKey = key;
|
|
277
|
+
oldestSeen = state.touchedAt;
|
|
278
|
+
}
|
|
279
|
+
if (oldestKey === null) break;
|
|
280
|
+
runs.delete(oldestKey);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const stateFor = (runId: string): RunJournalState | null => {
|
|
285
|
+
if (degraded || !isSafeRunId(runId) || !isEnabled()) return null;
|
|
286
|
+
const existing = runs.get(runId);
|
|
287
|
+
if (existing !== undefined) {
|
|
288
|
+
if (existing.closed) return null;
|
|
289
|
+
existing.touchedAt = Date.now();
|
|
290
|
+
return existing;
|
|
291
|
+
}
|
|
292
|
+
if (stateRootRemoved()) return null;
|
|
293
|
+
let path: string;
|
|
294
|
+
try {
|
|
295
|
+
const dir = runEventJournalDir(runId, options.root);
|
|
296
|
+
mkdirSync(dir, { recursive: true });
|
|
297
|
+
path = join(dir, RUN_EVENT_JOURNAL_FILE);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
degrade(error);
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
// A run reopened after state eviction (or after a resume) continues the
|
|
303
|
+
// existing file, so the byte counter starts from what is already there
|
|
304
|
+
// and the cap keeps meaning the same thing.
|
|
305
|
+
let bytes = 0;
|
|
306
|
+
let seq = 0;
|
|
307
|
+
try {
|
|
308
|
+
const stat = statSync(path);
|
|
309
|
+
bytes = stat.size;
|
|
310
|
+
} catch {
|
|
311
|
+
// First write for this run; the file does not exist yet.
|
|
312
|
+
}
|
|
313
|
+
if (bytes > 0) seq = countJournalSeq(path);
|
|
314
|
+
const state: RunJournalState = {
|
|
315
|
+
path,
|
|
316
|
+
seq,
|
|
317
|
+
bytes,
|
|
318
|
+
pending: [],
|
|
319
|
+
pendingBytes: 0,
|
|
320
|
+
dropping: bytes >= capBytes,
|
|
321
|
+
closed: false,
|
|
322
|
+
touchedAt: Date.now(),
|
|
323
|
+
};
|
|
324
|
+
runs.set(runId, state);
|
|
325
|
+
pruneRunStates();
|
|
326
|
+
return state;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const flushState = (state: RunJournalState): void => {
|
|
330
|
+
if (state.pending.length === 0) return;
|
|
331
|
+
const payload = state.pending.join("");
|
|
332
|
+
state.pending = [];
|
|
333
|
+
state.pendingBytes = 0;
|
|
334
|
+
if (stateRootRemoved()) return;
|
|
335
|
+
try {
|
|
336
|
+
appendFileSync(state.path, payload, "utf8");
|
|
337
|
+
} catch (error) {
|
|
338
|
+
degrade(error);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const write = (state: RunJournalState, line: RunEventJournalBody, droppable: boolean): void => {
|
|
343
|
+
if (droppable && state.dropping) return;
|
|
344
|
+
if (droppable && state.bytes + state.pendingBytes >= capBytes) {
|
|
345
|
+
state.dropping = true;
|
|
346
|
+
write(state, { kind: "journal_truncated", reason: "per-run size cap", droppedFromSeq: state.seq + 1 }, false);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
state.seq += 1;
|
|
350
|
+
const serialized = `${JSON.stringify({ seq: state.seq, at: now().toISOString(), ...line })}\n`;
|
|
351
|
+
const size = Buffer.byteLength(serialized, "utf8");
|
|
352
|
+
state.pending.push(serialized);
|
|
353
|
+
state.pendingBytes += size;
|
|
354
|
+
state.bytes += size;
|
|
355
|
+
if (!droppable || state.pendingBytes >= flushBytes) flushState(state);
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
open(runId, agentId): void {
|
|
360
|
+
const state = stateFor(runId);
|
|
361
|
+
if (state === null) return;
|
|
362
|
+
// Reopening a run that already has lines is a resume or a second
|
|
363
|
+
// registration of the same logical run; the file keeps its history and
|
|
364
|
+
// gains a fresh open line rather than being rewritten.
|
|
365
|
+
write(state, { kind: "open", runId, agentId }, false);
|
|
366
|
+
},
|
|
367
|
+
append(runId, entry): void {
|
|
368
|
+
const state = stateFor(runId);
|
|
369
|
+
if (state === null) return;
|
|
370
|
+
write(
|
|
371
|
+
state,
|
|
372
|
+
entry.detail === undefined
|
|
373
|
+
? { kind: "event", type: entry.type }
|
|
374
|
+
: { kind: "event", type: entry.type, detail: entry.detail },
|
|
375
|
+
true,
|
|
376
|
+
);
|
|
377
|
+
},
|
|
378
|
+
receipt(runId, receipt): void {
|
|
379
|
+
const state = stateFor(runId);
|
|
380
|
+
if (state === null) return;
|
|
381
|
+
write(
|
|
382
|
+
state,
|
|
383
|
+
{
|
|
384
|
+
kind: "receipt",
|
|
385
|
+
outcome: receipt.outcome,
|
|
386
|
+
exitCode: receipt.exitCode,
|
|
387
|
+
...(receipt.digest !== undefined ? { digest: receipt.digest } : {}),
|
|
388
|
+
...(receipt.receiptPath !== undefined ? { receiptPath: receipt.receiptPath } : {}),
|
|
389
|
+
...(receipt.attemptRunId !== undefined ? { attemptRunId: receipt.attemptRunId } : {}),
|
|
390
|
+
},
|
|
391
|
+
false,
|
|
392
|
+
);
|
|
393
|
+
},
|
|
394
|
+
terminal(runId, outcome, detail): void {
|
|
395
|
+
const state = stateFor(runId);
|
|
396
|
+
if (state === null) return;
|
|
397
|
+
write(state, detail === undefined ? { kind: "terminal", outcome } : { kind: "terminal", outcome, detail }, false);
|
|
398
|
+
flushState(state);
|
|
399
|
+
// Closing here is what makes the terminal line the last write: any
|
|
400
|
+
// later event for this run finds a closed state and is discarded.
|
|
401
|
+
state.closed = true;
|
|
402
|
+
},
|
|
403
|
+
flush(): void {
|
|
404
|
+
for (const state of runs.values()) flushState(state);
|
|
405
|
+
},
|
|
406
|
+
degraded(): boolean {
|
|
407
|
+
return degraded;
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Highest `seq` already in a run's file. Only read when a journal is reopened,
|
|
414
|
+
* which happens on resume or after state eviction, never on the common path.
|
|
415
|
+
*/
|
|
416
|
+
function countJournalSeq(path: string): number {
|
|
417
|
+
try {
|
|
418
|
+
const raw = readFileSync(path, "utf8");
|
|
419
|
+
let highest = 0;
|
|
420
|
+
for (const line of raw.split("\n")) {
|
|
421
|
+
if (line.length === 0) continue;
|
|
422
|
+
const parsed = parseJournalLine(line);
|
|
423
|
+
if (parsed !== null && parsed.seq > highest) highest = parsed.seq;
|
|
424
|
+
}
|
|
425
|
+
return highest;
|
|
426
|
+
} catch {
|
|
427
|
+
return 0;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// ---------------------------------------------------------------------------
|
|
432
|
+
// Process-wide default sink
|
|
433
|
+
// ---------------------------------------------------------------------------
|
|
434
|
+
|
|
435
|
+
let defaultJournal: RunEventJournal | null = null;
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* The sink every run event registry attaches to unless it was handed one.
|
|
439
|
+
* Created lazily so importing the registry costs nothing, and shared so two
|
|
440
|
+
* registries in one process cannot interleave two byte counters over one file.
|
|
441
|
+
*
|
|
442
|
+
* A run's file path is resolved on its first write and then held, so a state
|
|
443
|
+
* root that moves mid-process (only tests do that) leaves already-opened runs
|
|
444
|
+
* pointing at the old root. Run ids are random, so a fresh root always brings
|
|
445
|
+
* fresh ids with it and nothing observable depends on re-resolving.
|
|
446
|
+
*/
|
|
447
|
+
export function defaultRunEventJournal(): RunEventJournal {
|
|
448
|
+
defaultJournal ??= createRunEventJournal();
|
|
449
|
+
return defaultJournal;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// ---------------------------------------------------------------------------
|
|
453
|
+
// Reader
|
|
454
|
+
// ---------------------------------------------------------------------------
|
|
455
|
+
|
|
456
|
+
/** Tail bytes a reader will pull, so following a capped journal stays cheap. */
|
|
457
|
+
export const RUN_EVENT_JOURNAL_READ_TAIL_BYTES = 256 * 1024;
|
|
458
|
+
|
|
459
|
+
export interface ReadRunEventJournalOptions {
|
|
460
|
+
root?: string;
|
|
461
|
+
/** Keep at most this many lines, newest last. */
|
|
462
|
+
maxLines?: number;
|
|
463
|
+
tailBytes?: number;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export interface RunEventJournalRead {
|
|
467
|
+
path: string;
|
|
468
|
+
present: boolean;
|
|
469
|
+
bytes: number;
|
|
470
|
+
lines: RunEventJournalLine[];
|
|
471
|
+
/** True when the writer dropped display lines, or when this read skipped a file head. */
|
|
472
|
+
truncated: boolean;
|
|
473
|
+
terminal: { outcome: string; detail?: string } | null;
|
|
474
|
+
agentId: string | null;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function parseJournalLine(raw: string): RunEventJournalLine | null {
|
|
478
|
+
let parsed: unknown;
|
|
479
|
+
try {
|
|
480
|
+
parsed = JSON.parse(raw);
|
|
481
|
+
} catch {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
485
|
+
const record = parsed as Record<string, unknown>;
|
|
486
|
+
if (typeof record.seq !== "number" || typeof record.at !== "string" || typeof record.kind !== "string") return null;
|
|
487
|
+
switch (record.kind) {
|
|
488
|
+
case "open":
|
|
489
|
+
if (typeof record.runId !== "string" || typeof record.agentId !== "string") return null;
|
|
490
|
+
return { seq: record.seq, at: record.at, kind: "open", runId: record.runId, agentId: record.agentId };
|
|
491
|
+
case "event": {
|
|
492
|
+
if (typeof record.type !== "string") return null;
|
|
493
|
+
const detail = typeof record.detail === "string" ? record.detail : undefined;
|
|
494
|
+
return {
|
|
495
|
+
seq: record.seq,
|
|
496
|
+
at: record.at,
|
|
497
|
+
kind: "event",
|
|
498
|
+
type: normalizeClioCoderEventType(record.type),
|
|
499
|
+
...(detail === undefined ? {} : { detail }),
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
case "journal_truncated":
|
|
503
|
+
return {
|
|
504
|
+
seq: record.seq,
|
|
505
|
+
at: record.at,
|
|
506
|
+
kind: "journal_truncated",
|
|
507
|
+
reason: typeof record.reason === "string" ? record.reason : "unknown",
|
|
508
|
+
droppedFromSeq: typeof record.droppedFromSeq === "number" ? record.droppedFromSeq : record.seq,
|
|
509
|
+
};
|
|
510
|
+
case "receipt": {
|
|
511
|
+
if (typeof record.outcome !== "string") return null;
|
|
512
|
+
return {
|
|
513
|
+
seq: record.seq,
|
|
514
|
+
at: record.at,
|
|
515
|
+
kind: "receipt",
|
|
516
|
+
outcome: record.outcome,
|
|
517
|
+
exitCode: typeof record.exitCode === "number" ? record.exitCode : null,
|
|
518
|
+
...(typeof record.digest === "string" ? { digest: record.digest } : {}),
|
|
519
|
+
...(typeof record.receiptPath === "string" ? { receiptPath: record.receiptPath } : {}),
|
|
520
|
+
...(typeof record.attemptRunId === "string" ? { attemptRunId: record.attemptRunId } : {}),
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
case "terminal": {
|
|
524
|
+
if (typeof record.outcome !== "string") return null;
|
|
525
|
+
const detail = typeof record.detail === "string" ? record.detail : undefined;
|
|
526
|
+
return {
|
|
527
|
+
seq: record.seq,
|
|
528
|
+
at: record.at,
|
|
529
|
+
kind: "terminal",
|
|
530
|
+
outcome: record.outcome,
|
|
531
|
+
...(detail === undefined ? {} : { detail }),
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
default:
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Read one run's journal. Reads at most the last {@link
|
|
541
|
+
* RUN_EVENT_JOURNAL_READ_TAIL_BYTES}, discarding a leading partial line, so a
|
|
542
|
+
* viewer polling a capped journal never pulls megabytes per tick. A head that
|
|
543
|
+
* was skipped reports as `truncated` for the same reason the writer's marker
|
|
544
|
+
* does: the transcript on screen is not the whole run.
|
|
545
|
+
*/
|
|
546
|
+
export function readRunEventJournal(runId: string, options: ReadRunEventJournalOptions = {}): RunEventJournalRead {
|
|
547
|
+
const path = runEventJournalPath(runId, options.root);
|
|
548
|
+
const empty: RunEventJournalRead = {
|
|
549
|
+
path,
|
|
550
|
+
present: false,
|
|
551
|
+
bytes: 0,
|
|
552
|
+
lines: [],
|
|
553
|
+
truncated: false,
|
|
554
|
+
terminal: null,
|
|
555
|
+
agentId: null,
|
|
556
|
+
};
|
|
557
|
+
if (!isSafeRunId(runId)) return empty;
|
|
558
|
+
const tailBytes = options.tailBytes ?? RUN_EVENT_JOURNAL_READ_TAIL_BYTES;
|
|
559
|
+
let raw: string;
|
|
560
|
+
let size: number;
|
|
561
|
+
let headDropped = false;
|
|
562
|
+
try {
|
|
563
|
+
size = statSync(path).size;
|
|
564
|
+
if (size <= tailBytes) {
|
|
565
|
+
raw = readFileSync(path, "utf8");
|
|
566
|
+
} else {
|
|
567
|
+
const buffer = readFileSync(path);
|
|
568
|
+
raw = buffer.subarray(size - tailBytes).toString("utf8");
|
|
569
|
+
const firstBreak = raw.indexOf("\n");
|
|
570
|
+
raw = firstBreak === -1 ? "" : raw.slice(firstBreak + 1);
|
|
571
|
+
headDropped = true;
|
|
572
|
+
}
|
|
573
|
+
} catch {
|
|
574
|
+
return empty;
|
|
575
|
+
}
|
|
576
|
+
const lines: RunEventJournalLine[] = [];
|
|
577
|
+
let truncated = headDropped;
|
|
578
|
+
let terminal: { outcome: string; detail?: string } | null = null;
|
|
579
|
+
let agentId: string | null = null;
|
|
580
|
+
for (const candidate of raw.split("\n")) {
|
|
581
|
+
if (candidate.length === 0) continue;
|
|
582
|
+
const parsed = parseJournalLine(candidate);
|
|
583
|
+
// A trailing partial line is the writer mid-flush, not corruption.
|
|
584
|
+
if (parsed === null) continue;
|
|
585
|
+
if (parsed.kind === "journal_truncated") truncated = true;
|
|
586
|
+
if (parsed.kind === "open") agentId = parsed.agentId;
|
|
587
|
+
if (parsed.kind === "terminal") {
|
|
588
|
+
terminal =
|
|
589
|
+
parsed.detail === undefined ? { outcome: parsed.outcome } : { outcome: parsed.outcome, detail: parsed.detail };
|
|
590
|
+
}
|
|
591
|
+
lines.push(parsed);
|
|
592
|
+
}
|
|
593
|
+
const maxLines = options.maxLines;
|
|
594
|
+
if (maxLines !== undefined && lines.length > maxLines) {
|
|
595
|
+
lines.splice(0, lines.length - maxLines);
|
|
596
|
+
truncated = true;
|
|
597
|
+
}
|
|
598
|
+
return { path, present: true, bytes: size, lines, truncated, terminal, agentId };
|
|
599
|
+
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { randomBytes } from "node:crypto";
|
|
22
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
22
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
23
23
|
import { join } from "node:path";
|
|
24
24
|
import { resolveGuardrail } from "../../core/guardrails.js";
|
|
25
25
|
import { withStateFileLock } from "../../core/state-file-lock.js";
|
|
@@ -28,6 +28,7 @@ import { atomicWrite } from "../../engine/session.js";
|
|
|
28
28
|
import type { ExecutionStepResult } from "./execution-scheduler.js";
|
|
29
29
|
import { computeReceiptFindingsSummary } from "./receipt-findings.js";
|
|
30
30
|
import { withReceiptIntegrity } from "./receipt-integrity.js";
|
|
31
|
+
import { removeRunEventJournals } from "./run-event-journal.js";
|
|
31
32
|
import type { RunEnvelope, RunReceipt, RunReceiptDraft, RunStatus } from "./types.js";
|
|
32
33
|
|
|
33
34
|
export interface FleetRunRecord {
|
|
@@ -107,6 +108,41 @@ export async function writeFleetRun(record: FleetRunRecord): Promise<void> {
|
|
|
107
108
|
await atomicWrite(fleetRunPath(record.id), `${JSON.stringify(record, null, 2)}\n`);
|
|
108
109
|
}
|
|
109
110
|
|
|
111
|
+
/**
|
|
112
|
+
* How many fleet-run records a single listing will open.
|
|
113
|
+
*
|
|
114
|
+
* The directory is unbounded and the newest root is only discoverable by
|
|
115
|
+
* reading each record's start stamp, so a scan is the only ordering available.
|
|
116
|
+
* The cap keeps that scan proportional on an installation that has accumulated
|
|
117
|
+
* roots for months, at the cost of missing a root older than the cap. That is
|
|
118
|
+
* the right trade for every caller here: they all want the recent end.
|
|
119
|
+
*/
|
|
120
|
+
export const MAX_FLEET_RUN_SCAN = 64;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Durable fleet-run roots, newest start first.
|
|
124
|
+
*
|
|
125
|
+
* Reads defensively for the same reason {@link readFleetRun} does: one record
|
|
126
|
+
* written by another build, half-written, or removed between the listing and
|
|
127
|
+
* the open must cost that root its row, never the whole listing.
|
|
128
|
+
*/
|
|
129
|
+
export function listFleetRuns(limit = MAX_FLEET_RUN_SCAN): FleetRunRecord[] {
|
|
130
|
+
let entries: string[];
|
|
131
|
+
try {
|
|
132
|
+
entries = readdirSync(join(clioStateDir(), "fleet-runs"));
|
|
133
|
+
} catch {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
const records: FleetRunRecord[] = [];
|
|
137
|
+
for (const name of entries.slice(0, MAX_FLEET_RUN_SCAN)) {
|
|
138
|
+
if (!name.endsWith(".json")) continue;
|
|
139
|
+
const record = readFleetRun(name.slice(0, -".json".length));
|
|
140
|
+
if (record !== null) records.push(record);
|
|
141
|
+
}
|
|
142
|
+
records.sort((a, b) => (Date.parse(b.startedAt) || 0) - (Date.parse(a.startedAt) || 0));
|
|
143
|
+
return records.slice(0, Math.max(0, limit));
|
|
144
|
+
}
|
|
145
|
+
|
|
110
146
|
// `RunEnvelope.heartbeatAt` deliberately persists only the absolute wall-clock
|
|
111
147
|
// anchor used for operator display and crash recovery. The monotonic heartbeat
|
|
112
148
|
// stamp belongs to the live transport in this process; serializing it would
|
|
@@ -323,6 +359,14 @@ export function openLedger(opts?: LedgerOptions): Ledger {
|
|
|
323
359
|
const capped = capRuns(merged, maxRuns);
|
|
324
360
|
runs = capped;
|
|
325
361
|
atomicWrite(target, JSON.stringify(capped, null, 2));
|
|
362
|
+
// A run that left the ring has no ledger row to view any more, so its
|
|
363
|
+
// event journal is unreachable state. Retention is bound to the ring
|
|
364
|
+
// rather than to a second policy so the two can never disagree about
|
|
365
|
+
// how much history exists.
|
|
366
|
+
if (merged.length > capped.length) {
|
|
367
|
+
const kept = new Set(capped.map((run) => run.id));
|
|
368
|
+
removeRunEventJournals(merged.filter((run) => !kept.has(run.id)).map((run) => run.id));
|
|
369
|
+
}
|
|
326
370
|
// Everything this process claimed is now on disk, so the mirror holds
|
|
327
371
|
// no newer state until the next write. Clearing here is what stops a
|
|
328
372
|
// long-lived process from re-asserting an old snapshot of its own rows
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pure aggregation helpers for the per-tool stats folded into a RunReceipt.
|
|
3
3
|
*
|
|
4
|
-
* Sourced from the worker's ToolTelemetry stream (`
|
|
4
|
+
* Sourced from the worker's ToolTelemetry stream (`clio_coder_tool_finish` IPC
|
|
5
5
|
* events). Kept as a small standalone module so receipts have a single source
|
|
6
6
|
* of truth for the aggregation invariants, and so loops, retries, errors,
|
|
7
7
|
* blocked admissions, and parallel-batch tool calls all flow through one
|