@iowarp/clio-coder 0.3.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/NOTICE +33 -0
- package/README.md +7 -2
- package/dist/{acp-7LOELQFP.js → acp-G5WJBNCT.js} +13 -12
- package/dist/{agents-FIBG2SHA.js → agents-FMV2Q5G4.js} +39 -36
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-OI4LIH2I.js → auth-3IDSJEIK.js} +16 -15
- package/dist/{builtins-AD25UL3C.js → builtins-XCZWXSC7.js} +3 -3
- package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
- package/dist/{chunk-56KB5IJP.js → chunk-2OQE55CK.js} +3 -3
- package/dist/{chunk-N3PBVRTZ.js → chunk-32KWKNSF.js} +9 -7
- package/dist/{chunk-VKBMFOYV.js → chunk-36EJLSQQ.js} +23 -27
- package/dist/{chunk-465CC7FK.js → chunk-3BT2XMV4.js} +16 -13
- package/dist/{chunk-NQ6UCCOD.js → chunk-3URVFKWK.js} +7 -7
- package/dist/{chunk-WIE7ZOSW.js → chunk-3XML7CDN.js} +2 -2
- package/dist/chunk-42FMPA75.js +101 -0
- package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
- package/dist/{chunk-HAY4ZE2P.js → chunk-5PSMVOLM.js} +2 -2
- package/dist/{chunk-JEQQR47K.js → chunk-6DB53AJS.js} +168 -9
- package/dist/{chunk-KV2AOLDF.js → chunk-76ONBSIA.js} +5 -25
- package/dist/{chunk-FALJGAWU.js → chunk-7MCTRUCE.js} +2 -2
- package/dist/{chunk-5T7RBWN2.js → chunk-AB44T6BB.js} +5 -5
- package/dist/chunk-B7OBL7PK.js +1405 -0
- package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
- package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
- package/dist/{chunk-O6I4CIEU.js → chunk-BKFJHQCA.js} +6 -6
- package/dist/chunk-BKFM6EJV.js +462 -0
- package/dist/chunk-BMS5RKQY.js +27 -0
- package/dist/{chunk-MV2VUEJC.js → chunk-BPKCPIL7.js} +2 -2
- package/dist/{chunk-TEO2TLVN.js → chunk-BUMFYQFY.js} +964 -1120
- package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
- package/dist/chunk-C2LTL2W6.js +2447 -0
- package/dist/{chunk-EBOC7MT3.js → chunk-CODPRO7Q.js} +8 -8
- package/dist/{chunk-HPCTNZM2.js → chunk-CTJ4RNAA.js} +4 -4
- package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
- package/dist/{chunk-MIX5N5AC.js → chunk-DQITNCXG.js} +394 -154
- package/dist/{chunk-S4COXYBG.js → chunk-DYJP44XW.js} +129 -106
- package/dist/{chunk-N5XKWMDW.js → chunk-F4CKPOEQ.js} +6 -6
- package/dist/{chunk-AMKHQW3C.js → chunk-FEFIFZTL.js} +3 -3
- package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
- package/dist/{chunk-7C6RYZGQ.js → chunk-GKF55TAZ.js} +3 -3
- package/dist/{chunk-B5XRQOLB.js → chunk-GR5G2PVF.js} +9 -8
- package/dist/{chunk-XDOQXGFO.js → chunk-GXNLGKAB.js} +64 -8
- package/dist/chunk-HHV2GANA.js +88 -0
- package/dist/{chunk-774ILSRL.js → chunk-HI63TFOG.js} +4 -3
- package/dist/{chunk-R7LNVMCS.js → chunk-HJJTYUHX.js} +64 -72
- package/dist/{chunk-RKRLDWD3.js → chunk-HLW2MRKE.js} +1 -1
- package/dist/{chunk-CA42X6KT.js → chunk-HWHKMHUA.js} +6 -6
- package/dist/chunk-HZHHCK24.js +1631 -0
- package/dist/{chunk-HJB5IUKP.js → chunk-I5VEOC6I.js} +12 -12
- package/dist/chunk-IBEBSCYA.js +564 -0
- package/dist/chunk-IQ7KR472.js +362 -0
- package/dist/{chunk-4H6ULJ3H.js → chunk-J4W7KFM7.js} +921 -990
- package/dist/{chunk-4LJX2PUC.js → chunk-JDG2WCRO.js} +5 -5
- package/dist/{chunk-NZU6YDNV.js → chunk-K5C3NCBD.js} +4 -4
- package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
- package/dist/{chunk-NNNWO6F2.js → chunk-KFZI4NIL.js} +93 -3
- package/dist/chunk-KMVISBZR.js +132 -0
- package/dist/{chunk-BVDVID7E.js → chunk-LDQ2ZF2M.js} +2 -2
- package/dist/{chunk-OEDBCISO.js → chunk-LQ3DZAMX.js} +2 -2
- package/dist/{chunk-PZ4I4JE2.js → chunk-LY4S7GJC.js} +124 -116
- package/dist/{chunk-3DUR4WUA.js → chunk-MLKNTWH2.js} +19 -19
- package/dist/chunk-NEKRRTYW.js +56 -0
- package/dist/{chunk-5DQRIYDZ.js → chunk-NHCZP4K7.js} +2 -2
- package/dist/chunk-NHLBIGRH.js +1506 -0
- package/dist/chunk-NQQH3YT7.js +302 -0
- package/dist/chunk-NYS75XW5.js +15 -0
- package/dist/{chunk-TD7UE2L5.js → chunk-O4XIVISU.js} +7 -7
- package/dist/{chunk-WXCJ7VME.js → chunk-O6TL7WWY.js} +6 -6
- package/dist/chunk-OQBA45DZ.js +97 -0
- package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
- package/dist/chunk-PMZCIOCJ.js +25 -0
- package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
- package/dist/{chunk-HKO36JWF.js → chunk-QBJA7R7N.js} +31 -3
- package/dist/chunk-QDC3K2U3.js +262 -0
- package/dist/{chunk-5HFBWUMU.js → chunk-QLFS5GO2.js} +4 -4
- package/dist/{chunk-GAYUJ7LE.js → chunk-QLL7ILRG.js} +41 -32
- package/dist/{chunk-LXPJXFM5.js → chunk-QREDIESB.js} +7 -7
- package/dist/chunk-QSNYB6ZV.js +195 -0
- package/dist/chunk-R346GLFC.js +31 -0
- package/dist/{chunk-VPTUJU4P.js → chunk-SJ5ZKQ4S.js} +2 -2
- package/dist/{chunk-QQ7EKM72.js → chunk-SP2RXXYO.js} +6 -54
- package/dist/chunk-SUCTJL45.js +45 -0
- package/dist/{chunk-5QKCQQ3E.js → chunk-SUW5DORT.js} +2 -2
- package/dist/chunk-T56WDKA5.js +183 -0
- package/dist/chunk-TVHHYFHE.js +255 -0
- package/dist/{chunk-IHKBWSXF.js → chunk-TZ3SGWZZ.js} +2 -2
- package/dist/{chunk-ECUO3KDP.js → chunk-U77AMWDL.js} +47 -7
- package/dist/{chunk-3UUY7R3Z.js → chunk-ULC6OTWO.js} +7 -7
- package/dist/{chunk-AEYBF3TB.js → chunk-UM7N4G5A.js} +2 -2
- package/dist/{chunk-3V5AYSEQ.js → chunk-UXMFQ54G.js} +43 -36
- package/dist/{chunk-YW7UVM5V.js → chunk-V5DHCITQ.js} +34 -1
- package/dist/{chunk-47CMYGET.js → chunk-VAZSBTKF.js} +2 -2
- package/dist/{chunk-3MRC2YSQ.js → chunk-VEO4AP2K.js} +2 -2
- package/dist/{chunk-PNY46YEY.js → chunk-VFA6GDY5.js} +47 -3
- package/dist/chunk-VO67MWHC.js +75 -0
- package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
- package/dist/{chunk-AD7Y7STJ.js → chunk-VYMXRQI6.js} +36 -22
- package/dist/{chunk-T3Z6VAAF.js → chunk-W5VSYASO.js} +19 -17
- package/dist/{chunk-5PVQ4SRS.js → chunk-WZR7K7ZX.js} +7 -123
- package/dist/{chunk-DG4M6ZUE.js → chunk-X3YGUTOB.js} +4 -4
- package/dist/chunk-X75E3D2N.js +686 -0
- package/dist/{chunk-P3JGPQFL.js → chunk-YDFRH54B.js} +4 -4
- package/dist/chunk-YJX4SHTD.js +40 -0
- package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
- package/dist/{chunk-YSEHGPCT.js → chunk-Z2RR6MAK.js} +3 -3
- package/dist/{chunk-HCBCAYZU.js → chunk-Z4TXYIEG.js} +5 -5
- package/dist/cli/index.js +47 -36
- package/dist/{clio-LT5V7SSZ.js → clio-2JXHBBY5.js} +7 -7
- package/dist/{code-nav-LMW275PA.js → code-nav-3YYRMYNF.js} +6 -6
- package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
- package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
- package/dist/{config-RXS5T3JT.js → config-QZPCMYSO.js} +58 -50
- package/dist/{configure-2WYWSCSD.js → configure-TEGEBYCA.js} +21 -19
- package/dist/{context-I3BTOTCS.js → context-AV7OEZ4D.js} +11 -11
- package/dist/{context-MVOORGMF.js → context-E6H5RNMC.js} +49 -41
- package/dist/{context-PALKKQYL.js → context-GSXUE4CT.js} +25 -23
- package/dist/{context-clear-N2WOYZ2K.js → context-clear-SHIBYK6T.js} +48 -40
- package/dist/{context-working-set-MIEVECVZ.js → context-working-set-5ZGKPGZQ.js} +11 -10
- package/dist/{dispatch-runner-VVA4SRRH.js → dispatch-runner-EFMJT4LD.js} +88 -60
- package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
- package/dist/doctor-QOA5FNY5.js +313 -0
- package/dist/{eval-IJ5VEZDJ.js → eval-TFBYQH4H.js} +66 -64
- package/dist/eval-inventory-SXH7PDKX.js +316 -0
- package/dist/{evidence-L5APPXNV.js → evidence-ERGESKGN.js} +198 -44
- package/dist/{evolve-RGNKFJ52.js → evolve-VDXTSYCJ.js} +46 -38
- package/dist/{extensions-7WYWUX5A.js → extensions-7BGBHN57.js} +6 -6
- package/dist/{fleet-6CNVBZZP.js → fleet-2RRVDF2V.js} +189 -101
- package/dist/{fleet-commands-L2SXSYEI.js → fleet-commands-VJ726XIA.js} +10 -10
- package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
- package/dist/{fleet-graph-2J3OOIPO.js → fleet-graph-JF5QOATM.js} +16 -15
- package/dist/fleet-inspect-VLY4S7QM.js +442 -0
- package/dist/{fleet-preflight-CZRJ4JP5.js → fleet-preflight-AIZUEJOY.js} +6 -5
- package/dist/{fleet-validate-C5RI6DP7.js → fleet-validate-AJRPDMDV.js} +20 -18
- package/dist/fleet-verify-JFEL2L3H.js +175 -0
- package/dist/fleet-view-ZCON35AG.js +102 -0
- package/dist/{init-VBN2ACVA.js → init-DN2WWLFE.js} +64 -56
- package/dist/install-XGLBQY5E.js +13 -0
- package/dist/interop-OZBKXAYL.js +114 -0
- package/dist/{library-JHGUMLY2.js → library-YWZG7IMW.js} +19 -18
- package/dist/{memory-K4OQIYWG.js → memory-I4C4HMLW.js} +48 -40
- package/dist/{models-2NCZUWDD.js → models-CEYXJBO6.js} +30 -28
- package/dist/{monitor-MMVTJABD.js → monitor-NZ6GCI3P.js} +53 -47
- package/dist/{orchestrator-ZKBPCHW6.js → orchestrator-GCGQ4N5I.js} +6311 -7145
- package/dist/panes-HMABYVO4.js +58 -0
- package/dist/panes-KY6W3V2E.js +103 -0
- package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
- package/dist/{reset-DD5JGOY3.js → reset-DQ6FGCSH.js} +13 -11
- package/dist/resources-BB3MVJMD.js +111 -0
- package/dist/{run-QEGNX7FL.js → run-H2GQDUER.js} +113 -82
- package/dist/{share-JKD3BQMW.js → share-GTJN6A5O.js} +18 -17
- package/dist/{skills-LMQIKDOZ.js → skills-L55TEW6R.js} +31 -24
- package/dist/{skills-eval-I7X2774U.js → skills-eval-XVXPH2JI.js} +61 -52
- package/dist/skills-inventory-S4MXPJFV.js +126 -0
- package/dist/slash-commands-ZSGASKJC.js +77 -0
- package/dist/{steer-CF5TDANS.js → steer-RZGSCY4R.js} +3 -3
- package/dist/{support-I7LOJLIF.js → support-PKEUNNQL.js} +4 -4
- package/dist/{targets-RUSR6B5Z.js → targets-NCPZ644J.js} +31 -29
- package/dist/{terminal-lease-QYVORFR4.js → terminal-lease-44SV3YCN.js} +4 -4
- package/dist/tools-DAF3DI3C.js +27 -0
- package/dist/{trace-ODOQIVIW.js → trace-FYVW2MQA.js} +149 -7
- package/dist/tui-primitives-2AKXQNZK.js +13 -0
- package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
- package/dist/{upgrade-XANW3FXB.js → upgrade-3XPP6OQL.js} +26 -24
- package/dist/{usage-4H7ZRXQT.js → usage-3NLHGTU2.js} +57 -49
- package/dist/{verifiers-UZXNBZEB.js → verifiers-SSQONKRT.js} +171 -12
- package/dist/{verify-BVKWTNDL.js → verify-3U6J7FZI.js} +9 -9
- package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
- package/dist/{wiki-generate-MY7WV2QI.js → wiki-generate-CEHYGPGQ.js} +67 -59
- package/dist/with-panes-MKB46MPQ.js +782 -0
- package/dist/worker/entry.js +101 -66
- package/docs/README.md +3 -2
- package/docs/acp.md +24 -3
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +4 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +43 -5
- package/docs/commands-and-modes.md +31 -2
- package/docs/configuration-and-targets.md +40 -10
- package/docs/context-engine.md +1 -1
- package/docs/development-pipeline.md +19 -0
- package/docs/dispatch-typed-intent.md +385 -0
- package/docs/documentation-coverage.md +2 -2
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +1 -0
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +2 -2
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +25 -1
- package/docs/fleet-dispatch.md +22 -1
- package/docs/installation-and-lifecycle.md +15 -2
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +10 -1
- package/docs/observability.md +3 -3
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +1 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/safety-model.md +2 -2
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +18 -8
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +4 -2
- package/src/cli/acp.ts +6 -2
- package/src/cli/argv.ts +25 -0
- package/src/cli/configure.ts +23 -21
- package/src/cli/doctor-panes.ts +124 -0
- package/src/cli/doctor-toolchain.ts +57 -0
- package/src/cli/doctor.ts +20 -1
- package/src/cli/eval-inventory.ts +436 -0
- package/src/cli/eval.ts +13 -0
- package/src/cli/evidence-detail.ts +88 -0
- package/src/cli/evidence-inventory.ts +183 -0
- package/src/cli/evidence.ts +30 -5
- package/src/cli/fleet-decisions.ts +69 -0
- package/src/cli/fleet-inspect.ts +334 -0
- package/src/cli/fleet-verify.ts +133 -0
- package/src/cli/fleet-view.ts +810 -0
- package/src/cli/fleet.ts +148 -37
- package/src/cli/index.ts +14 -2
- package/src/cli/interop-inspect.ts +128 -0
- package/src/cli/interop.ts +34 -0
- package/src/cli/panes.ts +35 -0
- package/src/cli/reset.ts +5 -2
- package/src/cli/run.ts +58 -0
- package/src/cli/skills-inventory.ts +185 -0
- package/src/cli/skills.ts +16 -13
- package/src/cli/tools.ts +321 -0
- package/src/cli/trace-inspect.ts +252 -0
- package/src/cli/trace.ts +23 -2
- package/src/cli/verifiers-inspect.ts +347 -0
- package/src/cli/verifiers.ts +9 -0
- package/src/core/bus-events.ts +4 -0
- package/src/core/config.ts +49 -0
- package/src/core/defaults.ts +93 -7
- package/src/core/response-schema.ts +44 -4
- package/src/core/theme-token-hex.ts +43 -0
- package/src/core/tool-names.ts +2 -1
- package/src/core/xdg.ts +1 -1
- package/src/domains/agents/fleets/build-review.md +0 -3
- package/src/domains/agents/fleets/build-test.md +0 -3
- package/src/domains/agents/result-contract-filesystem.ts +32 -0
- package/src/domains/agents/result-contract.ts +164 -35
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/dispatch/admission-error.ts +9 -0
- package/src/domains/dispatch/admission.ts +22 -21
- package/src/domains/dispatch/capacity-lease.ts +21 -1
- package/src/domains/dispatch/council-topology.ts +398 -0
- package/src/domains/dispatch/extension.ts +222 -50
- package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
- package/src/domains/dispatch/fleet-plan.ts +7 -2
- package/src/domains/dispatch/fleet-run.ts +64 -1
- package/src/domains/dispatch/gate-decisions.ts +11 -1
- package/src/domains/dispatch/gate-role-prompts.ts +9 -0
- package/src/domains/dispatch/gate-topology.ts +289 -0
- package/src/domains/dispatch/index.ts +19 -0
- package/src/domains/dispatch/intent-compatibility.ts +330 -0
- package/src/domains/dispatch/intent.ts +85 -1
- package/src/domains/dispatch/reservation-store.ts +24 -4
- package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
- package/src/domains/dispatch/run-event-journal.ts +598 -0
- package/src/domains/dispatch/state.ts +45 -1
- package/src/domains/dispatch/types.ts +13 -0
- package/src/domains/dispatch/validation.ts +33 -8
- package/src/domains/eval/compare/compare.ts +3 -16
- package/src/domains/eval/inventory.ts +113 -0
- package/src/domains/eval/schema/serving.ts +31 -0
- package/src/domains/evidence/store.ts +6 -0
- package/src/domains/memory/index.ts +0 -1
- package/src/domains/memory/task-memory-policy.ts +6 -10
- package/src/domains/memory/task-memory-telemetry.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +33 -21
- package/src/domains/mux/contract.ts +434 -0
- package/src/domains/mux/detect.ts +158 -0
- package/src/domains/mux/extension.ts +47 -0
- package/src/domains/mux/index.ts +96 -0
- package/src/domains/mux/manifest.ts +6 -0
- package/src/domains/mux/operations.ts +164 -0
- package/src/domains/mux/pane-registry.ts +90 -0
- package/src/domains/mux/protocol.ts +49 -0
- package/src/domains/mux/socket-client.ts +816 -0
- package/src/domains/mux/types.ts +222 -0
- package/src/domains/mux/viewer-command.ts +59 -0
- package/src/domains/mux/yazi/assets/init.lua +2 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
- package/src/domains/mux/yazi/assets/yazi.toml +17 -0
- package/src/domains/mux/yazi/event-stream.ts +180 -0
- package/src/domains/mux/yazi/profile.ts +299 -0
- package/src/domains/mux/yazi/session.ts +228 -0
- package/src/domains/mux/yazi/theme.ts +30 -0
- package/src/domains/observability/cost.ts +21 -0
- package/src/domains/observability/index.ts +2 -0
- package/src/domains/observability/trace-store.ts +42 -0
- package/src/domains/providers/endpoint-capacity.ts +154 -22
- package/src/domains/providers/endpoint-slots-store.ts +189 -0
- package/src/domains/providers/extension.ts +20 -3
- package/src/domains/providers/index.ts +22 -0
- package/src/domains/providers/model-runtime-capabilities.ts +32 -0
- package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
- package/src/domains/providers/runtimes/builtins.ts +2 -0
- package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
- package/src/domains/providers/support.ts +1 -0
- package/src/domains/providers/target-model-cache.ts +124 -0
- package/src/domains/resources/index.ts +3 -0
- package/src/domains/resources/skills/loader.ts +33 -0
- package/src/domains/safety/action-classifier.ts +6 -0
- package/src/domains/toolchain/archive.ts +175 -0
- package/src/domains/toolchain/contract.ts +28 -0
- package/src/domains/toolchain/extension.ts +47 -0
- package/src/domains/toolchain/index.ts +39 -0
- package/src/domains/toolchain/install.ts +327 -0
- package/src/domains/toolchain/manifest.ts +8 -0
- package/src/domains/toolchain/paths.ts +34 -0
- package/src/domains/toolchain/registry.ts +265 -0
- package/src/domains/toolchain/remove.ts +218 -0
- package/src/domains/toolchain/resolve.ts +182 -0
- package/src/domains/toolchain/types.ts +113 -0
- package/src/domains/toolchain/version.ts +88 -0
- package/src/engine/acp/server.ts +413 -70
- package/src/engine/acp/types.ts +19 -1
- package/src/engine/claude/sdk-module.ts +98 -0
- package/src/engine/claude/sdk-runtime.ts +19 -11
- package/src/engine/tui-primitives.ts +21 -0
- package/src/engine/tui.ts +1 -0
- package/src/engine/worker-runtime.ts +2 -13
- package/src/entry/boot-options.ts +2 -0
- package/src/entry/orchestrator.ts +103 -5
- package/src/entry/panes-activation.ts +31 -0
- package/src/entry/with-panes.ts +20 -0
- package/src/interactive/context-overlay.ts +1 -0
- package/src/interactive/cost-overlay.ts +2 -0
- package/src/interactive/council-grid.ts +1 -3
- package/src/interactive/council.ts +11 -0
- package/src/interactive/dispatch-board.ts +26 -16
- package/src/interactive/interactive-application.ts +153 -6
- package/src/interactive/interactive-input-runtime.ts +7 -0
- package/src/interactive/interactive-slash-runtime.ts +36 -6
- package/src/interactive/memory-overlay.ts +1 -0
- package/src/interactive/modal-marker.ts +170 -0
- package/src/interactive/mux-bridge.ts +214 -0
- package/src/interactive/overlay-frame.ts +58 -2
- package/src/interactive/overlay-general-openers.ts +1 -0
- package/src/interactive/overlay-key-routing.ts +14 -3
- package/src/interactive/overlay-lifecycle.ts +19 -3
- package/src/interactive/overlay-model-selectors.ts +40 -3
- package/src/interactive/overlay-permission-lifecycle.ts +90 -22
- package/src/interactive/overlay-transitions.ts +18 -4
- package/src/interactive/overlays/agents.ts +1 -0
- package/src/interactive/overlays/ask-user.ts +136 -30
- package/src/interactive/overlays/auth-dialog.ts +1 -0
- package/src/interactive/overlays/context-reset.ts +1 -0
- package/src/interactive/overlays/cwd-fallback.ts +1 -0
- package/src/interactive/overlays/decisions.ts +11 -11
- package/src/interactive/overlays/extensions.ts +1 -0
- package/src/interactive/overlays/fleet-run-approval.ts +1 -0
- package/src/interactive/overlays/handoff-review.ts +1 -0
- package/src/interactive/overlays/help-reference.ts +2 -0
- package/src/interactive/overlays/interop.ts +1 -0
- package/src/interactive/overlays/library-install-confirm.ts +1 -0
- package/src/interactive/overlays/library-tabs.ts +28 -0
- package/src/interactive/overlays/list-overlay.ts +10 -1
- package/src/interactive/overlays/message-picker.ts +1 -0
- package/src/interactive/overlays/model-scope.ts +86 -0
- package/src/interactive/overlays/model-selector.ts +1 -0
- package/src/interactive/overlays/prompts.ts +1 -0
- package/src/interactive/overlays/session-selector.ts +1 -0
- package/src/interactive/overlays/settings-sections.ts +30 -0
- package/src/interactive/overlays/settings.ts +579 -44
- package/src/interactive/overlays/side-question.ts +1 -0
- package/src/interactive/overlays/skills-hub.ts +3 -11
- package/src/interactive/overlays/tree-selector.ts +1 -0
- package/src/interactive/pane-policy.ts +46 -0
- package/src/interactive/panes-runtime.ts +292 -0
- package/src/interactive/renderers/compaction-summary.ts +29 -0
- package/src/interactive/renderers/worker-entry.ts +122 -14
- package/src/interactive/slash-commands.ts +251 -15
- package/src/interactive/tasks-overlay.ts +1 -0
- package/src/interactive/theme/tokens.ts +3 -14
- package/src/interactive/turn-context.ts +47 -2
- package/src/interactive/view/artifacts.ts +109 -1
- package/src/interactive/view/view-overlay.ts +1 -0
- package/src/interactive/watch-pane.ts +152 -0
- package/src/interactive/worker-progress.ts +7 -1
- package/src/interactive/worker-receipts.ts +19 -1
- package/src/interactive/worker-stream.ts +5 -0
- package/src/interactive/yazi-bridge.ts +444 -0
- package/src/tools/bootstrap.ts +26 -2
- package/src/tools/builtin-tool-catalog.ts +15 -0
- package/src/tools/compete-worktrees.ts +83 -2
- package/src/tools/core-bootstrap.ts +2 -1
- package/src/tools/dispatch-admission.ts +14 -3
- package/src/tools/dispatch-arguments.ts +20 -20
- package/src/tools/dispatch-run-events.ts +134 -19
- package/src/tools/dispatch-runner.ts +29 -7
- package/src/tools/dispatch-types.ts +15 -3
- package/src/tools/dispatch.ts +1 -1
- package/src/tools/executables.ts +17 -14
- package/src/tools/observation.ts +54 -4
- package/src/tools/panes-surface.ts +38 -0
- package/src/tools/panes.ts +112 -0
- package/src/tools/policy.ts +10 -1
- package/src/tools/presentation.ts +1 -0
- package/dist/chunk-D73KXYPF.js +0 -37
- package/dist/chunk-MLOK6ZOS.js +0 -2888
- package/dist/chunk-YKOFT37S.js +0 -279
- package/dist/doctor-TWBWFK5V.js +0 -165
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pane layer's cross-domain surface.
|
|
3
|
+
*
|
|
4
|
+
* Every method is best-effort. A mux failure never fails a dispatch, never
|
|
5
|
+
* throws at a caller, and never blocks the event loop on a dead socket: it logs
|
|
6
|
+
* and degrades to `available() === false`, which the consumers read as "use the
|
|
7
|
+
* native fleet surfaces instead". That is why every method here returns a
|
|
8
|
+
* fallback value rather than rejecting.
|
|
9
|
+
*
|
|
10
|
+
* Two ownership rules run through the whole file. Clio acts only on panes it
|
|
11
|
+
* created, tracked in `pane-registry.ts` and tagged with the `clio_owner`
|
|
12
|
+
* metadata token so `clio doctor` can find orphans. The one documented
|
|
13
|
+
* exception is Clio's own hosting pane in guest mode, which `reportSelf`
|
|
14
|
+
* writes to under SA-3 of the v0.4.0 cycle plan.
|
|
15
|
+
*
|
|
16
|
+
* What is deliberately absent: per-run viewer panes. The v0.4.0 phase 3/4
|
|
17
|
+
* integration opened a pane per dispatched run in a hidden Fleet tab, with a
|
|
18
|
+
* focus ladder, run-state reporting, and resume adoption keyed on run ids. All
|
|
19
|
+
* of that projected fleet state the native surfaces already show onto panes
|
|
20
|
+
* nobody had asked for. The one run-viewing surface is now the workers-view
|
|
21
|
+
* watch pane (src/interactive/watch-pane.ts): a single utility pane following
|
|
22
|
+
* a selection file, opened on operator demand and retargeted by file writes
|
|
23
|
+
* that never touch this socket.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { DomainContract } from "../../core/domain-loader.js";
|
|
27
|
+
import type { MuxDetection } from "./detect.js";
|
|
28
|
+
import { createPaneRegistry, type MuxPaneRegistry, paneRecord } from "./pane-registry.js";
|
|
29
|
+
import { muxSupportsMethod } from "./protocol.js";
|
|
30
|
+
import type {
|
|
31
|
+
MuxClient,
|
|
32
|
+
MuxSubscription,
|
|
33
|
+
MuxWorktreeCreatedResult,
|
|
34
|
+
MuxWorktreeCreateRequest,
|
|
35
|
+
} from "./socket-client.js";
|
|
36
|
+
import {
|
|
37
|
+
MuxError,
|
|
38
|
+
type MuxEvent,
|
|
39
|
+
type MuxLog,
|
|
40
|
+
type MuxMode,
|
|
41
|
+
type MuxNotificationSound,
|
|
42
|
+
type MuxPanePurpose,
|
|
43
|
+
type MuxPaneRecord,
|
|
44
|
+
type MuxPaneRef,
|
|
45
|
+
type MuxSelfReport,
|
|
46
|
+
} from "./types.js";
|
|
47
|
+
|
|
48
|
+
/** Metadata source for everything Clio's pane layer writes. */
|
|
49
|
+
const METADATA_SOURCE = "clio:mux";
|
|
50
|
+
/** Agent-authority source for Clio's own hosting pane, per SA-3. */
|
|
51
|
+
const SELF_AGENT_SOURCE = "clio:coder";
|
|
52
|
+
/** Token every Clio-created pane carries so orphans are findable. */
|
|
53
|
+
const OWNER_TOKEN_KEY = "clio_owner";
|
|
54
|
+
const OWNER_TOKEN_VALUE = "clio:mux";
|
|
55
|
+
/** herdr caps a metadata token value at 80 characters. */
|
|
56
|
+
const TOKEN_VALUE_MAX = 80;
|
|
57
|
+
/** How long `available()` stays false after a transport failure before probing again. */
|
|
58
|
+
const DEGRADE_COOLDOWN_MS = 5_000;
|
|
59
|
+
|
|
60
|
+
export interface MuxOpenUtilityPaneRequest {
|
|
61
|
+
argv: ReadonlyArray<string>;
|
|
62
|
+
cwd: string;
|
|
63
|
+
label: string;
|
|
64
|
+
/** Operator-facing pane title. Omitted when the caller wants the shell default. */
|
|
65
|
+
title?: string;
|
|
66
|
+
purpose?: MuxPanePurpose;
|
|
67
|
+
direction?: "right" | "down";
|
|
68
|
+
env?: Readonly<Record<string, string>>;
|
|
69
|
+
/**
|
|
70
|
+
* Absolute path the pane's stdout is redirected into. Full-screen programs
|
|
71
|
+
* such as Yazi reopen `/dev/tty` for their interface when stdout is not a tty.
|
|
72
|
+
*/
|
|
73
|
+
stdoutPath?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface MuxNotifyRequest {
|
|
77
|
+
title: string;
|
|
78
|
+
body?: string;
|
|
79
|
+
sound?: MuxNotificationSound;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface MuxContract extends DomainContract {
|
|
83
|
+
readonly mode: MuxMode;
|
|
84
|
+
/** `mode !== "none"` and the socket is currently healthy. */
|
|
85
|
+
available(): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* The rung detection resolved to, with the socket it answered on and the
|
|
88
|
+
* protocol recorded from the handshake. `/panes` and `clio-coder doctor`
|
|
89
|
+
* print it; the notify and worktree paths gate on its protocol.
|
|
90
|
+
*/
|
|
91
|
+
detection(): Readonly<MuxDetection>;
|
|
92
|
+
openUtilityPane(request: MuxOpenUtilityPaneRequest): Promise<MuxPaneRef | null>;
|
|
93
|
+
/** Close one Clio-created pane by pane id. Refuses a pane Clio did not create. */
|
|
94
|
+
closePane(paneId: string): Promise<boolean>;
|
|
95
|
+
/**
|
|
96
|
+
* Re-adopt one pane of the given purpose that outlived the process that made
|
|
97
|
+
* it. A fresh `session.snapshot` is scanned for a pane carrying Clio's owner
|
|
98
|
+
* token and a matching `role` token; the first hit is recorded and returned.
|
|
99
|
+
* This is what stops a restarted session's Enter-in-the-workers-view from
|
|
100
|
+
* opening a second watch pane beside the surviving one.
|
|
101
|
+
*/
|
|
102
|
+
adoptPane(request: { purpose: MuxPanePurpose; label: string }): Promise<MuxPaneRef | null>;
|
|
103
|
+
notify(request: MuxNotifyRequest): Promise<void>;
|
|
104
|
+
/** Optional compete storage route. Protocol-gated with a native Git fallback at the caller. */
|
|
105
|
+
worktreeCreate(request: MuxWorktreeCreateRequest): Promise<MuxWorktreeCreatedResult | null>;
|
|
106
|
+
/** Remove a herdr worktree workspace. False tells the caller to use native Git cleanup. */
|
|
107
|
+
worktreeRemove(workspaceId: string, options?: { force?: boolean }): Promise<boolean>;
|
|
108
|
+
/**
|
|
109
|
+
* Called when a pane Clio owns leaves, whether the user closed it or the
|
|
110
|
+
* program in it exited. Callers record it and do not reopen: a closed pane
|
|
111
|
+
* is a decision, not a fault.
|
|
112
|
+
*/
|
|
113
|
+
onPaneGone(handler: (record: MuxPaneRecord) => void): () => void;
|
|
114
|
+
/** Panes Clio created, with their purpose. */
|
|
115
|
+
list(): ReadonlyArray<MuxPaneRecord>;
|
|
116
|
+
/**
|
|
117
|
+
* Report Clio's own state on its hosting pane (SA-3). Returns false when
|
|
118
|
+
* there is no pane to report on, which is every mode but guest.
|
|
119
|
+
*/
|
|
120
|
+
reportSelf(report: MuxSelfReport): Promise<boolean>;
|
|
121
|
+
shutdown(): Promise<void>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface MuxRuntimeOptions {
|
|
125
|
+
detection: MuxDetection;
|
|
126
|
+
client: MuxClient | null;
|
|
127
|
+
log?: MuxLog;
|
|
128
|
+
now?: () => number;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The contract plus the lifecycle handles the domain extension drives. */
|
|
132
|
+
export interface MuxRuntime {
|
|
133
|
+
contract: MuxContract;
|
|
134
|
+
start(): Promise<void>;
|
|
135
|
+
stop(): Promise<void>;
|
|
136
|
+
/** Test seam: the registry the contract reconciles against. */
|
|
137
|
+
registry: MuxPaneRegistry;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** POSIX single-quoting, so an argv element with spaces or quotes survives the shell. */
|
|
141
|
+
function shellQuote(argv: ReadonlyArray<string>): string {
|
|
142
|
+
return argv.map((arg) => `'${arg.replaceAll("'", `'\\''`)}'`).join(" ");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function token(value: string | undefined | null): string | null {
|
|
146
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
147
|
+
return value.length > TOKEN_VALUE_MAX ? value.slice(0, TOKEN_VALUE_MAX) : value;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function createMuxRuntime(options: MuxRuntimeOptions): MuxRuntime {
|
|
151
|
+
const { detection, client } = options;
|
|
152
|
+
const log = options.log ?? ((): void => undefined);
|
|
153
|
+
const now = options.now ?? Date.now;
|
|
154
|
+
const registry = createPaneRegistry();
|
|
155
|
+
|
|
156
|
+
let healthy = client !== null;
|
|
157
|
+
let probeHealthAt = 0;
|
|
158
|
+
let stopped = false;
|
|
159
|
+
let subscription: MuxSubscription | null = null;
|
|
160
|
+
const paneGoneHandlers = new Set<(record: MuxPaneRecord) => void>();
|
|
161
|
+
|
|
162
|
+
const degrade = (what: string, error: unknown): void => {
|
|
163
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
164
|
+
const transport = error instanceof MuxError && (error.kind === "transport" || error.kind === "timeout");
|
|
165
|
+
if (transport) {
|
|
166
|
+
healthy = false;
|
|
167
|
+
probeHealthAt = now() + DEGRADE_COOLDOWN_MS;
|
|
168
|
+
}
|
|
169
|
+
log(transport ? "warning" : "debug", `mux ${what} failed: ${message}`);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const usable = (): boolean => {
|
|
173
|
+
if (stopped || client === null || detection.mode === "none") return false;
|
|
174
|
+
return healthy || now() >= probeHealthAt;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Runs one mux interaction and swallows everything it can throw. The fallback
|
|
179
|
+
* is what the caller sees when the pane layer is not there, which is the same
|
|
180
|
+
* value it sees in `none` mode.
|
|
181
|
+
*/
|
|
182
|
+
const attempt = async <T>(what: string, run: (live: MuxClient) => Promise<T>, fallback: T): Promise<T> => {
|
|
183
|
+
if (!usable() || client === null) return fallback;
|
|
184
|
+
try {
|
|
185
|
+
const value = await run(client);
|
|
186
|
+
healthy = true;
|
|
187
|
+
return value;
|
|
188
|
+
} catch (error) {
|
|
189
|
+
degrade(what, error);
|
|
190
|
+
return fallback;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const tagOwner = async (
|
|
195
|
+
live: MuxClient,
|
|
196
|
+
paneId: string,
|
|
197
|
+
tokens: Readonly<Record<string, string | null>>,
|
|
198
|
+
title?: string,
|
|
199
|
+
): Promise<void> => {
|
|
200
|
+
await live.paneReportMetadata({
|
|
201
|
+
paneId,
|
|
202
|
+
source: METADATA_SOURCE,
|
|
203
|
+
...(title === undefined ? {} : { title }),
|
|
204
|
+
tokens: { [OWNER_TOKEN_KEY]: OWNER_TOKEN_VALUE, ...tokens },
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const onEvent = (event: MuxEvent): void => {
|
|
209
|
+
const dropped = registry.forget(event.paneId);
|
|
210
|
+
if (dropped) {
|
|
211
|
+
log("debug", `mux pane ${event.paneId} left on ${event.kind}; dropped from the registry`);
|
|
212
|
+
for (const handler of paneGoneHandlers) {
|
|
213
|
+
try {
|
|
214
|
+
handler(dropped);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
log("debug", `mux pane-gone handler threw: ${error instanceof Error ? error.message : String(error)}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const contract: MuxContract = {
|
|
223
|
+
mode: detection.mode,
|
|
224
|
+
|
|
225
|
+
available(): boolean {
|
|
226
|
+
return usable();
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
detection(): Readonly<MuxDetection> {
|
|
230
|
+
return detection;
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
async openUtilityPane(request: MuxOpenUtilityPaneRequest): Promise<MuxPaneRef | null> {
|
|
234
|
+
return await attempt(
|
|
235
|
+
"openUtilityPane",
|
|
236
|
+
async (live) => {
|
|
237
|
+
const anchor = detection.self.paneId ?? (await live.paneCurrent()).paneId;
|
|
238
|
+
const pane = await live.paneSplit({
|
|
239
|
+
direction: request.direction ?? "right",
|
|
240
|
+
targetPaneId: anchor,
|
|
241
|
+
cwd: request.cwd,
|
|
242
|
+
focus: false,
|
|
243
|
+
...(request.env ? { env: request.env } : {}),
|
|
244
|
+
});
|
|
245
|
+
const ref: MuxPaneRef = { paneId: pane.paneId, tabId: pane.tabId, workspaceId: pane.workspaceId };
|
|
246
|
+
const purpose = request.purpose ?? "utility";
|
|
247
|
+
registry.record(paneRecord(ref, { purpose, label: request.label, openedAt: now() }));
|
|
248
|
+
const title = request.title;
|
|
249
|
+
const titleSupported = title !== undefined && muxSupportsMethod(detection.server, "pane.rename");
|
|
250
|
+
if (titleSupported) {
|
|
251
|
+
try {
|
|
252
|
+
await live.paneRename(pane.paneId, title);
|
|
253
|
+
} catch {
|
|
254
|
+
// Presentation is optional. A server that advertises the floor but
|
|
255
|
+
// lacks or refuses rename must not strand an otherwise healthy pane.
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// The `role` token is what adoptPane finds again after a restart.
|
|
259
|
+
await tagOwner(live, pane.paneId, { role: token(purpose) }, titleSupported ? title : undefined);
|
|
260
|
+
if (request.argv.length > 0) {
|
|
261
|
+
// herdr has no argv parameter on pane.split, so the command goes in
|
|
262
|
+
// through the pane's shell. `exec` replaces the shell so the pane
|
|
263
|
+
// exits with the program and emits pane.exited for reconciliation.
|
|
264
|
+
const redirect = request.stdoutPath ? ` > ${shellQuote([request.stdoutPath])}` : "";
|
|
265
|
+
await live.paneSendText(pane.paneId, `exec ${shellQuote(request.argv)}${redirect}\n`);
|
|
266
|
+
}
|
|
267
|
+
return ref;
|
|
268
|
+
},
|
|
269
|
+
null,
|
|
270
|
+
);
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
async closePane(paneId: string): Promise<boolean> {
|
|
274
|
+
// The ownership rule, restated for the operator-facing path: `/panes
|
|
275
|
+
// close` addresses panes by id and must refuse anything the registry
|
|
276
|
+
// does not hold.
|
|
277
|
+
if (!registry.owns(paneId)) return false;
|
|
278
|
+
return await attempt(
|
|
279
|
+
"closePane",
|
|
280
|
+
async (live) => {
|
|
281
|
+
await live.paneClose(paneId);
|
|
282
|
+
registry.forget(paneId);
|
|
283
|
+
return true;
|
|
284
|
+
},
|
|
285
|
+
false,
|
|
286
|
+
);
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
async adoptPane(request: { purpose: MuxPanePurpose; label: string }): Promise<MuxPaneRef | null> {
|
|
290
|
+
const existing = registry.byPurpose(request.purpose);
|
|
291
|
+
if (existing) return existing.ref;
|
|
292
|
+
return await attempt(
|
|
293
|
+
"adoptPane",
|
|
294
|
+
async (live) => {
|
|
295
|
+
const snapshot = await live.snapshot();
|
|
296
|
+
for (const pane of snapshot.panes) {
|
|
297
|
+
if (pane.tokens[OWNER_TOKEN_KEY] !== OWNER_TOKEN_VALUE) continue;
|
|
298
|
+
if (pane.tokens.role !== request.purpose) continue;
|
|
299
|
+
if (registry.owns(pane.paneId)) continue;
|
|
300
|
+
// Another workspace's pane belongs to another session's screen;
|
|
301
|
+
// adopting it would retarget a surface the operator cannot see.
|
|
302
|
+
if (detection.self.workspaceId !== null && pane.workspaceId !== detection.self.workspaceId) continue;
|
|
303
|
+
const ref: MuxPaneRef = { paneId: pane.paneId, tabId: pane.tabId, workspaceId: pane.workspaceId };
|
|
304
|
+
registry.record(
|
|
305
|
+
paneRecord(ref, { purpose: request.purpose, label: request.label, openedAt: now(), adopted: true }),
|
|
306
|
+
);
|
|
307
|
+
log("info", `mux adopted a ${request.purpose} pane left open by a previous session`);
|
|
308
|
+
return ref;
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
},
|
|
312
|
+
null,
|
|
313
|
+
);
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
async notify(request: MuxNotifyRequest): Promise<void> {
|
|
317
|
+
if (!muxSupportsMethod(detection.server, "notification.show")) {
|
|
318
|
+
log("debug", `mux notify skipped, server protocol is below the notification.show floor: ${request.title}`);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
await attempt(
|
|
322
|
+
"notify",
|
|
323
|
+
async (live) => {
|
|
324
|
+
const result = await live.notificationShow({
|
|
325
|
+
title: request.title,
|
|
326
|
+
...(request.body ? { body: request.body } : {}),
|
|
327
|
+
...(request.sound ? { sound: request.sound } : {}),
|
|
328
|
+
});
|
|
329
|
+
// A suppressed toast is the operator's own herdr config talking
|
|
330
|
+
// (disabled, rate limited, no foreground client). Reporting it as a
|
|
331
|
+
// warning would turn their setting into Clio's error.
|
|
332
|
+
if (!result.shown) log("debug", `mux notify not shown (${result.reason}): ${request.title}`);
|
|
333
|
+
return undefined;
|
|
334
|
+
},
|
|
335
|
+
undefined,
|
|
336
|
+
);
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
async worktreeCreate(request: MuxWorktreeCreateRequest): Promise<MuxWorktreeCreatedResult | null> {
|
|
340
|
+
if (!muxSupportsMethod(detection.server, "worktree.create")) {
|
|
341
|
+
log("debug", "mux worktree.create skipped, server protocol is below the worktree floor");
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
return await attempt("worktreeCreate", (live) => live.worktreeCreate(request), null);
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
async worktreeRemove(workspaceId: string, removeOptions = {}): Promise<boolean> {
|
|
348
|
+
if (!muxSupportsMethod(detection.server, "worktree.remove")) {
|
|
349
|
+
log("debug", "mux worktree.remove skipped, server protocol is below the worktree floor");
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
return await attempt(
|
|
353
|
+
"worktreeRemove",
|
|
354
|
+
async (live) => {
|
|
355
|
+
await live.worktreeRemove(workspaceId, removeOptions);
|
|
356
|
+
return true;
|
|
357
|
+
},
|
|
358
|
+
false,
|
|
359
|
+
);
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
onPaneGone(handler: (record: MuxPaneRecord) => void): () => void {
|
|
363
|
+
paneGoneHandlers.add(handler);
|
|
364
|
+
return () => {
|
|
365
|
+
paneGoneHandlers.delete(handler);
|
|
366
|
+
};
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
list(): ReadonlyArray<MuxPaneRecord> {
|
|
370
|
+
return registry.list();
|
|
371
|
+
},
|
|
372
|
+
|
|
373
|
+
async reportSelf(report: MuxSelfReport): Promise<boolean> {
|
|
374
|
+
const paneId = detection.self.paneId;
|
|
375
|
+
if (detection.mode !== "guest" || !paneId) return false;
|
|
376
|
+
return await attempt(
|
|
377
|
+
"reportSelf",
|
|
378
|
+
async (live) => {
|
|
379
|
+
await live.paneReportAgent({
|
|
380
|
+
paneId,
|
|
381
|
+
source: SELF_AGENT_SOURCE,
|
|
382
|
+
agent: "clio-coder",
|
|
383
|
+
state: report.state,
|
|
384
|
+
...(report.message ? { message: report.message } : {}),
|
|
385
|
+
});
|
|
386
|
+
if (report.tokens || report.stateLabels || report.ttlMs !== undefined) {
|
|
387
|
+
await live.paneReportMetadata({
|
|
388
|
+
paneId,
|
|
389
|
+
source: METADATA_SOURCE,
|
|
390
|
+
...(report.tokens ? { tokens: report.tokens } : {}),
|
|
391
|
+
...(report.stateLabels ? { stateLabels: report.stateLabels } : {}),
|
|
392
|
+
...(report.ttlMs !== undefined ? { ttlMs: report.ttlMs } : {}),
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
return true;
|
|
396
|
+
},
|
|
397
|
+
false,
|
|
398
|
+
);
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
async shutdown(): Promise<void> {
|
|
402
|
+
if (stopped) return;
|
|
403
|
+
stopped = true;
|
|
404
|
+
subscription?.close();
|
|
405
|
+
subscription = null;
|
|
406
|
+
paneGoneHandlers.clear();
|
|
407
|
+
// Pane ownership is server-side and intentionally survives process
|
|
408
|
+
// shutdown. Keep the local snapshot too so the contract itself proves
|
|
409
|
+
// shutdown tears down sockets, never panes.
|
|
410
|
+
if (client) await client.close().catch(() => undefined);
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const start = async (): Promise<void> => {
|
|
415
|
+
if (detection.mode === "none" || client === null) return;
|
|
416
|
+
try {
|
|
417
|
+
subscription = await client.subscribe(["pane.closed", "pane.exited"], onEvent, {
|
|
418
|
+
onResync: (snapshot) => {
|
|
419
|
+
// A reconnect means events were missed. The snapshot is the authority
|
|
420
|
+
// on which panes still exist, so anything Clio thinks it owns that is
|
|
421
|
+
// absent from it is gone.
|
|
422
|
+
const dropped = registry.reconcile(snapshot.panes.map((pane) => pane.paneId));
|
|
423
|
+
if (dropped.length > 0) {
|
|
424
|
+
log("info", `mux reconciled ${dropped.length} pane(s) closed while the socket was down`);
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
} catch (error) {
|
|
429
|
+
degrade("event subscription", error);
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
return { contract, start, stop: contract.shutdown, registry };
|
|
434
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The capability ladder from spec 4.2.
|
|
3
|
+
*
|
|
4
|
+
* Guest mode requires all of: `HERDR_ENV=1`, a connectable socket, and a `ping`
|
|
5
|
+
* answered inside one second. The three conditions are checked in that order
|
|
6
|
+
* and the first one that fails ends detection, which is what makes the `none`
|
|
7
|
+
* path free: with `HERDR_ENV` unset nothing here opens a file descriptor, and a
|
|
8
|
+
* contract test pins that by making `net.connect` throw for the duration of the
|
|
9
|
+
* call.
|
|
10
|
+
*
|
|
11
|
+
* Embedded mode resolves to `none` until Phase 5 lands the session bootstrap.
|
|
12
|
+
* Asking for it is not an error; it degrades with a reason the doctor surface
|
|
13
|
+
* can print.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { createMuxClient, type MuxClient } from "./socket-client.js";
|
|
19
|
+
import type { MuxLog, MuxMode, MuxSelfLocation, MuxServerInfo } from "./types.js";
|
|
20
|
+
|
|
21
|
+
/** Spec 4.2 gives the ping one second to answer before the rung is refused. */
|
|
22
|
+
const PING_TIMEOUT_MS = 1_000;
|
|
23
|
+
|
|
24
|
+
export type MuxEnablement = "auto" | "embedded" | "off";
|
|
25
|
+
|
|
26
|
+
export interface MuxDetection {
|
|
27
|
+
mode: MuxMode;
|
|
28
|
+
socketPath: string | null;
|
|
29
|
+
server: MuxServerInfo | null;
|
|
30
|
+
self: MuxSelfLocation;
|
|
31
|
+
/** Socket paths considered, in resolution order, for doctor output. */
|
|
32
|
+
candidates: ReadonlyArray<string>;
|
|
33
|
+
/** Human-readable reason, always set; for `guest` it names the socket that answered. */
|
|
34
|
+
reason: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface DetectMuxOptions {
|
|
38
|
+
env?: NodeJS.ProcessEnv;
|
|
39
|
+
enabled?: MuxEnablement;
|
|
40
|
+
pingTimeoutMs?: number;
|
|
41
|
+
log?: MuxLog;
|
|
42
|
+
/** Injection seam for tests; production opens a real client. */
|
|
43
|
+
openClient?: (socketPath: string) => MuxClient;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* herdr resolves its config dir through `XDG_CONFIG_HOME` first and falls back
|
|
48
|
+
* to the platform default, which on Linux is `~/.config/herdr`. Mirroring that
|
|
49
|
+
* matters: a user with `XDG_CONFIG_HOME` set has no `~/.config/herdr` at all,
|
|
50
|
+
* and the spec's literal `~/.config/herdr` would miss every one of their
|
|
51
|
+
* sockets.
|
|
52
|
+
*/
|
|
53
|
+
function herdrConfigDir(env: NodeJS.ProcessEnv): string {
|
|
54
|
+
const xdg = env.XDG_CONFIG_HOME;
|
|
55
|
+
if (typeof xdg === "string" && xdg.length > 0) return join(xdg, "herdr");
|
|
56
|
+
const home = typeof env.HOME === "string" && env.HOME.length > 0 ? env.HOME : homedir();
|
|
57
|
+
return join(home, ".config", "herdr");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Socket paths to try, in the order spec 4.2 fixes. */
|
|
61
|
+
export function resolveSocketCandidates(env: NodeJS.ProcessEnv): ReadonlyArray<string> {
|
|
62
|
+
const candidates: string[] = [];
|
|
63
|
+
const explicit = env.HERDR_SOCKET_PATH;
|
|
64
|
+
if (typeof explicit === "string" && explicit.length > 0) candidates.push(explicit);
|
|
65
|
+
const configDir = herdrConfigDir(env);
|
|
66
|
+
const session = env.HERDR_SESSION;
|
|
67
|
+
if (typeof session === "string" && session.length > 0) {
|
|
68
|
+
candidates.push(join(configDir, "sessions", session, "herdr.sock"));
|
|
69
|
+
}
|
|
70
|
+
candidates.push(join(configDir, "herdr.sock"));
|
|
71
|
+
return [...new Set(candidates)];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readSelfLocation(env: NodeJS.ProcessEnv): MuxSelfLocation {
|
|
75
|
+
const read = (key: string): string | null => {
|
|
76
|
+
const value = env[key];
|
|
77
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
workspaceId: read("HERDR_WORKSPACE_ID"),
|
|
81
|
+
tabId: read("HERDR_TAB_ID"),
|
|
82
|
+
paneId: read("HERDR_PANE_ID"),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function none(reason: string, candidates: ReadonlyArray<string> = []): MuxDetection {
|
|
87
|
+
return {
|
|
88
|
+
mode: "none",
|
|
89
|
+
socketPath: null,
|
|
90
|
+
server: null,
|
|
91
|
+
self: { workspaceId: null, tabId: null, paneId: null },
|
|
92
|
+
candidates,
|
|
93
|
+
reason,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The detected rung plus the live client when one was reached. The client is
|
|
99
|
+
* the same connection the ping went over, so the caller inherits a warm socket
|
|
100
|
+
* rather than reconnecting.
|
|
101
|
+
*/
|
|
102
|
+
export interface MuxDetectionResult {
|
|
103
|
+
detection: MuxDetection;
|
|
104
|
+
client: MuxClient | null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function detectMux(options: DetectMuxOptions = {}): Promise<MuxDetectionResult> {
|
|
108
|
+
const env = options.env ?? process.env;
|
|
109
|
+
const enabled = options.enabled ?? "auto";
|
|
110
|
+
const log = options.log ?? ((): void => undefined);
|
|
111
|
+
|
|
112
|
+
if (enabled === "off") {
|
|
113
|
+
return { detection: none("panes are turned off"), client: null };
|
|
114
|
+
}
|
|
115
|
+
if (enabled === "embedded") {
|
|
116
|
+
// Phase 5 owns the session bootstrap. Until then the honest answer is that
|
|
117
|
+
// Clio cannot provide panes, not that it failed to find them.
|
|
118
|
+
return { detection: none("embedded mode is not implemented yet; it ships in phase 5"), client: null };
|
|
119
|
+
}
|
|
120
|
+
if (env.HERDR_ENV !== "1") {
|
|
121
|
+
return { detection: none("HERDR_ENV is not 1, so Clio is not running inside a pane host"), client: null };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const candidates = resolveSocketCandidates(env);
|
|
125
|
+
const openClient = options.openClient ?? ((socketPath: string) => createMuxClient({ socketPath, log }));
|
|
126
|
+
const pingTimeoutMs = options.pingTimeoutMs ?? PING_TIMEOUT_MS;
|
|
127
|
+
|
|
128
|
+
for (const socketPath of candidates) {
|
|
129
|
+
const client = openClient(socketPath);
|
|
130
|
+
try {
|
|
131
|
+
const server = await client.ping({ timeoutMs: pingTimeoutMs });
|
|
132
|
+
return {
|
|
133
|
+
detection: {
|
|
134
|
+
mode: "guest",
|
|
135
|
+
socketPath,
|
|
136
|
+
server,
|
|
137
|
+
self: readSelfLocation(env),
|
|
138
|
+
candidates,
|
|
139
|
+
reason: `guest mode on ${socketPath} (herdr ${server.version}, protocol ${server.protocol})`,
|
|
140
|
+
},
|
|
141
|
+
client,
|
|
142
|
+
};
|
|
143
|
+
} catch (error) {
|
|
144
|
+
log("debug", `mux ping ${socketPath} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
145
|
+
await client.close().catch(() => undefined);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
detection: none(
|
|
151
|
+
candidates.length > 0
|
|
152
|
+
? `no herdr socket answered a ping: tried ${candidates.join(", ")}`
|
|
153
|
+
: "no herdr socket candidates resolved",
|
|
154
|
+
candidates,
|
|
155
|
+
),
|
|
156
|
+
client: null,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain wiring for the pane layer.
|
|
3
|
+
*
|
|
4
|
+
* `createExtension` runs the detection ladder once at boot. This module only
|
|
5
|
+
* loads on a `--with-panes` boot (src/entry/with-panes.ts), so a plain session
|
|
6
|
+
* never reaches this code at all; within an active session, detection failures
|
|
7
|
+
* are never fatal, and a wedged herdr server delays boot by at most the
|
|
8
|
+
* one-second ping budget per socket candidate.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { DomainBundle, DomainContext, DomainExtension } from "../../core/domain-loader.js";
|
|
12
|
+
import { createMuxRuntime, type MuxContract } from "./contract.js";
|
|
13
|
+
import { detectMux, type MuxEnablement } from "./detect.js";
|
|
14
|
+
import type { MuxLog } from "./types.js";
|
|
15
|
+
|
|
16
|
+
export interface MuxDomainOptions {
|
|
17
|
+
/** `off` skips detection entirely; `embedded` degrades to `none` until phase 5. */
|
|
18
|
+
enabled?: MuxEnablement;
|
|
19
|
+
env?: NodeJS.ProcessEnv;
|
|
20
|
+
log?: MuxLog;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function createMuxBundle(
|
|
24
|
+
_context: DomainContext,
|
|
25
|
+
options: MuxDomainOptions = {},
|
|
26
|
+
): Promise<DomainBundle<MuxContract>> {
|
|
27
|
+
const log = options.log ?? ((): void => undefined);
|
|
28
|
+
const { detection, client } = await detectMux({
|
|
29
|
+
...(options.enabled ? { enabled: options.enabled } : {}),
|
|
30
|
+
...(options.env ? { env: options.env } : {}),
|
|
31
|
+
log,
|
|
32
|
+
});
|
|
33
|
+
log(detection.mode === "none" ? "debug" : "info", `mux mode ${detection.mode}: ${detection.reason}`);
|
|
34
|
+
|
|
35
|
+
const runtime = createMuxRuntime({ detection, client, log });
|
|
36
|
+
|
|
37
|
+
const extension: DomainExtension = {
|
|
38
|
+
async start(): Promise<void> {
|
|
39
|
+
await runtime.start();
|
|
40
|
+
},
|
|
41
|
+
async stop(): Promise<void> {
|
|
42
|
+
await runtime.stop();
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return { extension, contract: runtime.contract };
|
|
47
|
+
}
|