@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,816 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newline-delimited JSON client for a herdr server's Unix domain socket.
|
|
3
|
+
*
|
|
4
|
+
* Two connection kinds, because the server treats them differently:
|
|
5
|
+
*
|
|
6
|
+
* 1. One connection per request. herdr's `handle_connection` reads exactly one
|
|
7
|
+
* request line, writes the response, and closes
|
|
8
|
+
* (`src/api/server.rs:172`, verified against herdr 0.7.5 / protocol 17), so
|
|
9
|
+
* a second request written to the same socket dies with EPIPE. Spec 4.3
|
|
10
|
+
* describes a persistent request/response connection with a pending map;
|
|
11
|
+
* that is not what the pinned binary does. Each call still gets a monotonic
|
|
12
|
+
* id and still checks the echoed id, which catches a server answering
|
|
13
|
+
* something other than what was asked. A call with no response inside its
|
|
14
|
+
* budget rejects with {@link MuxRequestTimeout}.
|
|
15
|
+
* 2. One dedicated connection per `events.subscribe` stream. This is one of
|
|
16
|
+
* the two methods that keep a connection open (the other is
|
|
17
|
+
* `pane.graphics.stream`): herdr acknowledges once and then pushes event
|
|
18
|
+
* lines forever.
|
|
19
|
+
*
|
|
20
|
+
* Connect failures back off with a capped exponential delay, so a dead socket
|
|
21
|
+
* is not hammered once per call. After a subscription reconnect the client
|
|
22
|
+
* refetches `session.snapshot` and hands it to the resync handler: herdr's
|
|
23
|
+
* documented pattern is snapshot once and then trust events, and events missed
|
|
24
|
+
* while the socket was down are simply gone.
|
|
25
|
+
*
|
|
26
|
+
* The interactive app never shells out to the `herdr` CLI. CLI wrappers exist
|
|
27
|
+
* for humans, doctor, and scripts.
|
|
28
|
+
*
|
|
29
|
+
* This is the only module in the tree that reads herdr wire shapes. Every
|
|
30
|
+
* public method returns the Clio types from `types.ts`, and unknown fields on a
|
|
31
|
+
* response are ignored, which is herdr's stated forward-compatibility rule.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import * as net from "node:net";
|
|
35
|
+
import {
|
|
36
|
+
MuxError,
|
|
37
|
+
type MuxEvent,
|
|
38
|
+
type MuxEventKind,
|
|
39
|
+
type MuxLog,
|
|
40
|
+
type MuxNotificationSound,
|
|
41
|
+
type MuxPane,
|
|
42
|
+
type MuxReportableAgentState,
|
|
43
|
+
MuxRequestTimeout,
|
|
44
|
+
type MuxServerInfo,
|
|
45
|
+
type MuxSnapshot,
|
|
46
|
+
type MuxTab,
|
|
47
|
+
type MuxWorktree,
|
|
48
|
+
type MuxWorktreeSource,
|
|
49
|
+
muxErrorKind,
|
|
50
|
+
} from "./types.js";
|
|
51
|
+
|
|
52
|
+
/** Default per-request budget from spec 4.3. */
|
|
53
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 5_000;
|
|
54
|
+
/** Connect budget. Detection uses a tighter one; see `detect.ts`. */
|
|
55
|
+
const DEFAULT_CONNECT_TIMEOUT_MS = 2_000;
|
|
56
|
+
const DEFAULT_BACKOFF = { initialDelayMs: 250, maxDelayMs: 5_000, factor: 2 } as const;
|
|
57
|
+
/**
|
|
58
|
+
* A single response line larger than this means the peer is not a herdr server
|
|
59
|
+
* (or is wedged), so the connection is dropped rather than buffered forever.
|
|
60
|
+
*/
|
|
61
|
+
const MAX_LINE_BYTES = 8 * 1024 * 1024;
|
|
62
|
+
|
|
63
|
+
export interface MuxBackoffOptions {
|
|
64
|
+
initialDelayMs: number;
|
|
65
|
+
maxDelayMs: number;
|
|
66
|
+
factor: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface MuxClientOptions {
|
|
70
|
+
socketPath: string;
|
|
71
|
+
requestTimeoutMs?: number;
|
|
72
|
+
connectTimeoutMs?: number;
|
|
73
|
+
backoff?: MuxBackoffOptions;
|
|
74
|
+
log?: MuxLog;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface MuxSubscription {
|
|
78
|
+
close(): void;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface MuxSplitRequest {
|
|
82
|
+
direction: "right" | "down";
|
|
83
|
+
targetPaneId?: string;
|
|
84
|
+
workspaceId?: string;
|
|
85
|
+
cwd?: string;
|
|
86
|
+
env?: Readonly<Record<string, string>>;
|
|
87
|
+
focus?: boolean;
|
|
88
|
+
ratio?: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface MuxReportAgentRequest {
|
|
92
|
+
paneId: string;
|
|
93
|
+
source: string;
|
|
94
|
+
agent: string;
|
|
95
|
+
state: MuxReportableAgentState;
|
|
96
|
+
message?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface MuxReportMetadataRequest {
|
|
100
|
+
paneId: string;
|
|
101
|
+
source: string;
|
|
102
|
+
agent?: string;
|
|
103
|
+
title?: string;
|
|
104
|
+
displayAgent?: string;
|
|
105
|
+
tokens?: Readonly<Record<string, string | null>>;
|
|
106
|
+
stateLabels?: Readonly<Record<string, string>>;
|
|
107
|
+
ttlMs?: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface MuxSubscribeOptions {
|
|
111
|
+
/** Called after a dropped stream reconnects, with a freshly taken snapshot. */
|
|
112
|
+
onResync?: (snapshot: MuxSnapshot) => void;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface MuxNotificationRequest {
|
|
116
|
+
title: string;
|
|
117
|
+
body?: string;
|
|
118
|
+
sound?: MuxNotificationSound;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* What the server did with a toast. `shown: false` is a normal answer: the
|
|
123
|
+
* operator's own herdr config can disable toasts, rate-limit them, or have no
|
|
124
|
+
* foreground client to paint one, and none of those is a Clio failure.
|
|
125
|
+
*/
|
|
126
|
+
export interface MuxNotificationResult {
|
|
127
|
+
shown: boolean;
|
|
128
|
+
reason: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface MuxWorktreeListRequest {
|
|
132
|
+
workspaceId?: string;
|
|
133
|
+
cwd?: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface MuxWorktreeCreateRequest extends MuxWorktreeListRequest {
|
|
137
|
+
branch?: string;
|
|
138
|
+
base?: string;
|
|
139
|
+
path?: string;
|
|
140
|
+
label?: string;
|
|
141
|
+
focus?: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface MuxWorktreeOpenRequest extends MuxWorktreeListRequest {
|
|
145
|
+
branch?: string;
|
|
146
|
+
path?: string;
|
|
147
|
+
label?: string;
|
|
148
|
+
focus?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface MuxWorktreeListResult {
|
|
152
|
+
source: MuxWorktreeSource;
|
|
153
|
+
worktrees: ReadonlyArray<MuxWorktree>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface MuxWorktreeCreatedResult {
|
|
157
|
+
workspaceId: string;
|
|
158
|
+
tab: MuxTab;
|
|
159
|
+
rootPane: MuxPane;
|
|
160
|
+
worktree: MuxWorktree;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface MuxWorktreeOpenedResult extends MuxWorktreeCreatedResult {
|
|
164
|
+
alreadyOpen: boolean;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface MuxWorktreeRemovedResult {
|
|
168
|
+
workspaceId: string;
|
|
169
|
+
path: string;
|
|
170
|
+
forced: boolean;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The domain-facing surface. Phase 1 covers exactly the wire methods spec 4.3
|
|
175
|
+
* lists for this phase, plus `pane.send_text`, `pane.report_agent`,
|
|
176
|
+
* `pane.report_metadata` (see the notes on those methods below).
|
|
177
|
+
*/
|
|
178
|
+
export interface MuxClient {
|
|
179
|
+
readonly socketPath: string;
|
|
180
|
+
/** Whether the most recent request reached the server. Requests are one per connection. */
|
|
181
|
+
connected(): boolean;
|
|
182
|
+
/** Protocol/version recorded from the most recent successful ping or snapshot. */
|
|
183
|
+
server(): MuxServerInfo | null;
|
|
184
|
+
/** Most recent `session.snapshot`, including the post-reconnect refetch. */
|
|
185
|
+
cachedSnapshot(): MuxSnapshot | null;
|
|
186
|
+
|
|
187
|
+
ping(options?: { timeoutMs?: number }): Promise<MuxServerInfo>;
|
|
188
|
+
snapshot(): Promise<MuxSnapshot>;
|
|
189
|
+
paneCurrent(callerPaneId?: string): Promise<MuxPane>;
|
|
190
|
+
paneList(workspaceId?: string): Promise<ReadonlyArray<MuxPane>>;
|
|
191
|
+
paneSplit(request: MuxSplitRequest): Promise<MuxPane>;
|
|
192
|
+
/** Set the pane's operator-facing label. Available on herdr protocol 17. */
|
|
193
|
+
paneRename(paneId: string, label: string): Promise<void>;
|
|
194
|
+
paneClose(paneId: string): Promise<void>;
|
|
195
|
+
/** Pane ids sharing a tab with `paneId`, read off the tab's layout snapshot. */
|
|
196
|
+
paneLayout(paneId?: string): Promise<ReadonlyArray<string>>;
|
|
197
|
+
worktreeList(request?: MuxWorktreeListRequest): Promise<MuxWorktreeListResult>;
|
|
198
|
+
worktreeCreate(request: MuxWorktreeCreateRequest): Promise<MuxWorktreeCreatedResult>;
|
|
199
|
+
worktreeOpen(request: MuxWorktreeOpenRequest): Promise<MuxWorktreeOpenedResult>;
|
|
200
|
+
worktreeRemove(workspaceId: string, options?: { force?: boolean }): Promise<MuxWorktreeRemovedResult>;
|
|
201
|
+
/**
|
|
202
|
+
* Deliver a command line into a pane's shell. herdr has no argv parameter on
|
|
203
|
+
* `pane.split`, so this is the only way an argv utility pane can run what it
|
|
204
|
+
* was asked to run.
|
|
205
|
+
*/
|
|
206
|
+
paneSendText(paneId: string, text: string): Promise<void>;
|
|
207
|
+
/** Takes agent authority over a pane. Only ever called on Clio-owned panes and Clio's own pane. */
|
|
208
|
+
paneReportAgent(request: MuxReportAgentRequest): Promise<void>;
|
|
209
|
+
paneReportMetadata(request: MuxReportMetadataRequest): Promise<void>;
|
|
210
|
+
/**
|
|
211
|
+
* Ask the foreground client to paint a toast. Protocol-gated; see
|
|
212
|
+
* `protocol.ts`. The server answers whether it painted one and why not.
|
|
213
|
+
*/
|
|
214
|
+
notificationShow(request: MuxNotificationRequest): Promise<MuxNotificationResult>;
|
|
215
|
+
subscribe(
|
|
216
|
+
kinds: ReadonlyArray<MuxEventKind>,
|
|
217
|
+
handler: (event: MuxEvent) => void,
|
|
218
|
+
options?: MuxSubscribeOptions,
|
|
219
|
+
): Promise<MuxSubscription>;
|
|
220
|
+
|
|
221
|
+
close(): Promise<void>;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Opens one socket, or rejects with a typed transport error. */
|
|
225
|
+
function connectSocket(socketPath: string, timeoutMs: number): Promise<net.Socket> {
|
|
226
|
+
return new Promise((resolve, reject) => {
|
|
227
|
+
const socket = net.connect({ path: socketPath });
|
|
228
|
+
socket.setNoDelay(true);
|
|
229
|
+
const timer = setTimeout(() => {
|
|
230
|
+
socket.destroy();
|
|
231
|
+
reject(new MuxError("transport", `mux socket connect to ${socketPath} timed out after ${timeoutMs}ms`));
|
|
232
|
+
}, timeoutMs);
|
|
233
|
+
timer.unref?.();
|
|
234
|
+
const settle = (error: Error | null): void => {
|
|
235
|
+
clearTimeout(timer);
|
|
236
|
+
socket.removeListener("connect", onConnect);
|
|
237
|
+
socket.removeListener("error", onError);
|
|
238
|
+
if (error) {
|
|
239
|
+
socket.destroy();
|
|
240
|
+
reject(new MuxError("transport", `mux socket connect to ${socketPath} failed: ${error.message}`));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
resolve(socket);
|
|
244
|
+
};
|
|
245
|
+
const onConnect = (): void => settle(null);
|
|
246
|
+
const onError = (error: Error): void => settle(error);
|
|
247
|
+
socket.once("connect", onConnect);
|
|
248
|
+
socket.once("error", onError);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Splits an incoming byte stream into JSON lines.
|
|
254
|
+
*
|
|
255
|
+
* `onOverflow` fires when one line exceeds the sanity cap, which the caller
|
|
256
|
+
* treats as a dead connection rather than growing the buffer without bound.
|
|
257
|
+
*/
|
|
258
|
+
function readJsonLines(
|
|
259
|
+
socket: net.Socket,
|
|
260
|
+
onValue: (value: Record<string, unknown>) => void,
|
|
261
|
+
onOverflow: () => void,
|
|
262
|
+
): void {
|
|
263
|
+
let buffer = "";
|
|
264
|
+
socket.setEncoding("utf8");
|
|
265
|
+
socket.on("data", (chunk: string) => {
|
|
266
|
+
buffer += chunk;
|
|
267
|
+
if (buffer.length > MAX_LINE_BYTES) {
|
|
268
|
+
buffer = "";
|
|
269
|
+
onOverflow();
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
let newline = buffer.indexOf("\n");
|
|
273
|
+
while (newline >= 0) {
|
|
274
|
+
const line = buffer.slice(0, newline).trim();
|
|
275
|
+
buffer = buffer.slice(newline + 1);
|
|
276
|
+
if (line.length > 0) {
|
|
277
|
+
let parsed: unknown;
|
|
278
|
+
try {
|
|
279
|
+
parsed = JSON.parse(line);
|
|
280
|
+
} catch {
|
|
281
|
+
parsed = null;
|
|
282
|
+
}
|
|
283
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
284
|
+
onValue(parsed as Record<string, unknown>);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
newline = buffer.indexOf("\n");
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
293
|
+
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function requireString(source: Record<string, unknown>, key: string, context: string): string {
|
|
297
|
+
const value = source[key];
|
|
298
|
+
if (typeof value !== "string") {
|
|
299
|
+
throw new MuxError("protocol", `mux ${context} response is missing string field ${key}`);
|
|
300
|
+
}
|
|
301
|
+
return value;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function optionalString(source: Record<string, unknown>, key: string): string | null {
|
|
305
|
+
const value = source[key];
|
|
306
|
+
return typeof value === "string" ? value : null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function stringMap(value: unknown): Record<string, string> {
|
|
310
|
+
const source = asRecord(value);
|
|
311
|
+
if (!source) return {};
|
|
312
|
+
const out: Record<string, string> = {};
|
|
313
|
+
for (const [key, entry] of Object.entries(source)) {
|
|
314
|
+
if (typeof entry === "string") out[key] = entry;
|
|
315
|
+
}
|
|
316
|
+
return out;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const AGENT_STATES = new Set(["idle", "working", "blocked", "done", "unknown"]);
|
|
320
|
+
|
|
321
|
+
function readAgentState(value: unknown): MuxPane["agentState"] {
|
|
322
|
+
return typeof value === "string" && AGENT_STATES.has(value) ? (value as MuxPane["agentState"]) : "unknown";
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function readPane(value: unknown): MuxPane {
|
|
326
|
+
const source = asRecord(value);
|
|
327
|
+
if (!source) throw new MuxError("protocol", "mux pane response is not an object");
|
|
328
|
+
const revision = source.revision;
|
|
329
|
+
return {
|
|
330
|
+
paneId: requireString(source, "pane_id", "pane"),
|
|
331
|
+
tabId: requireString(source, "tab_id", "pane"),
|
|
332
|
+
workspaceId: requireString(source, "workspace_id", "pane"),
|
|
333
|
+
focused: source.focused === true,
|
|
334
|
+
agentState: readAgentState(source.agent_status),
|
|
335
|
+
revision: typeof revision === "number" ? revision : 0,
|
|
336
|
+
label: optionalString(source, "label"),
|
|
337
|
+
title: optionalString(source, "title"),
|
|
338
|
+
cwd: optionalString(source, "cwd"),
|
|
339
|
+
agent: optionalString(source, "agent"),
|
|
340
|
+
tokens: stringMap(source.tokens),
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function readTab(value: unknown): MuxTab {
|
|
345
|
+
const source = asRecord(value);
|
|
346
|
+
if (!source) throw new MuxError("protocol", "mux tab response is not an object");
|
|
347
|
+
const number = source.number;
|
|
348
|
+
const paneCount = source.pane_count;
|
|
349
|
+
return {
|
|
350
|
+
tabId: requireString(source, "tab_id", "tab"),
|
|
351
|
+
workspaceId: requireString(source, "workspace_id", "tab"),
|
|
352
|
+
number: typeof number === "number" ? number : 0,
|
|
353
|
+
label: optionalString(source, "label") ?? "",
|
|
354
|
+
focused: source.focused === true,
|
|
355
|
+
paneCount: typeof paneCount === "number" ? paneCount : 0,
|
|
356
|
+
agentState: readAgentState(source.agent_status),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function readWorktree(value: unknown): MuxWorktree {
|
|
361
|
+
const source = asRecord(value);
|
|
362
|
+
if (!source) throw new MuxError("protocol", "mux worktree response is not an object");
|
|
363
|
+
return {
|
|
364
|
+
path: requireString(source, "path", "worktree"),
|
|
365
|
+
branch: optionalString(source, "branch"),
|
|
366
|
+
isBare: source.is_bare === true,
|
|
367
|
+
isDetached: source.is_detached === true,
|
|
368
|
+
isPrunable: source.is_prunable === true,
|
|
369
|
+
isLinkedWorktree: source.is_linked_worktree === true,
|
|
370
|
+
openWorkspaceId: optionalString(source, "open_workspace_id"),
|
|
371
|
+
label: optionalString(source, "label") ?? "",
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function readWorktreeSource(value: unknown): MuxWorktreeSource {
|
|
376
|
+
const source = asRecord(value);
|
|
377
|
+
if (!source) throw new MuxError("protocol", "mux worktree source response is not an object");
|
|
378
|
+
return {
|
|
379
|
+
repoKey: requireString(source, "repo_key", "worktree source"),
|
|
380
|
+
repoName: requireString(source, "repo_name", "worktree source"),
|
|
381
|
+
repoRoot: requireString(source, "repo_root", "worktree source"),
|
|
382
|
+
sourceCheckoutPath: requireString(source, "source_checkout_path", "worktree source"),
|
|
383
|
+
sourceWorkspaceId: optionalString(source, "source_workspace_id"),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function readArray(source: Record<string, unknown>, key: string): unknown[] {
|
|
388
|
+
const value = source[key];
|
|
389
|
+
return Array.isArray(value) ? value : [];
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function readServerInfo(source: Record<string, unknown>): MuxServerInfo {
|
|
393
|
+
const protocol = source.protocol;
|
|
394
|
+
return {
|
|
395
|
+
version: optionalString(source, "version") ?? "",
|
|
396
|
+
protocol: typeof protocol === "number" ? protocol : 0,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function readSnapshot(value: unknown): MuxSnapshot {
|
|
401
|
+
const source = asRecord(value);
|
|
402
|
+
if (!source) throw new MuxError("protocol", "mux snapshot response is not an object");
|
|
403
|
+
return {
|
|
404
|
+
server: readServerInfo(source),
|
|
405
|
+
focusedPaneId: optionalString(source, "focused_pane_id"),
|
|
406
|
+
focusedTabId: optionalString(source, "focused_tab_id"),
|
|
407
|
+
focusedWorkspaceId: optionalString(source, "focused_workspace_id"),
|
|
408
|
+
panes: readArray(source, "panes").map(readPane),
|
|
409
|
+
tabs: readArray(source, "tabs").map(readTab),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** herdr names lifecycle event kinds with underscores on the wire and dots in subscriptions. */
|
|
414
|
+
const EVENT_KIND_BY_WIRE: Readonly<Record<string, MuxEventKind>> = {
|
|
415
|
+
pane_closed: "pane.closed",
|
|
416
|
+
pane_exited: "pane.exited",
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
function readEvent(line: Record<string, unknown>): MuxEvent | null {
|
|
420
|
+
const wireKind = line.event;
|
|
421
|
+
if (typeof wireKind !== "string") return null;
|
|
422
|
+
const kind = EVENT_KIND_BY_WIRE[wireKind];
|
|
423
|
+
if (!kind) return null;
|
|
424
|
+
const data = asRecord(line.data);
|
|
425
|
+
if (!data) return null;
|
|
426
|
+
const paneId = data.pane_id;
|
|
427
|
+
const workspaceId = data.workspace_id;
|
|
428
|
+
if (typeof paneId !== "string" || typeof workspaceId !== "string") return null;
|
|
429
|
+
return { kind, paneId, workspaceId };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** Drops undefined entries so an optional field is absent rather than `null` on the wire. */
|
|
433
|
+
function params(entries: Record<string, unknown>): Record<string, unknown> {
|
|
434
|
+
const out: Record<string, unknown> = {};
|
|
435
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
436
|
+
if (value !== undefined) out[key] = value;
|
|
437
|
+
}
|
|
438
|
+
return out;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export function createMuxClient(options: MuxClientOptions): MuxClient {
|
|
442
|
+
const socketPath = options.socketPath;
|
|
443
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
444
|
+
const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
445
|
+
const backoff = options.backoff ?? DEFAULT_BACKOFF;
|
|
446
|
+
const log = options.log ?? ((): void => undefined);
|
|
447
|
+
|
|
448
|
+
let disposed = false;
|
|
449
|
+
let nextId = 0;
|
|
450
|
+
let reachable = false;
|
|
451
|
+
let serverInfo: MuxServerInfo | null = null;
|
|
452
|
+
let snapshotCache: MuxSnapshot | null = null;
|
|
453
|
+
|
|
454
|
+
/** Consecutive failed connect attempts. */
|
|
455
|
+
let connectFailures = 0;
|
|
456
|
+
/** Wall clock before which a new connect is refused. */
|
|
457
|
+
let retryAt = 0;
|
|
458
|
+
|
|
459
|
+
const delayFor = (failures: number): number =>
|
|
460
|
+
Math.min(backoff.maxDelayMs, backoff.initialDelayMs * backoff.factor ** Math.max(0, failures - 1));
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* One request, one connection.
|
|
464
|
+
*
|
|
465
|
+
* The id counter and the response-id check survive from the pending-map
|
|
466
|
+
* design because they still catch a server answering something other than
|
|
467
|
+
* what was asked, but there is no pending map: a connection carries exactly
|
|
468
|
+
* one outstanding call and is destroyed as soon as it settles.
|
|
469
|
+
*/
|
|
470
|
+
const call = async (method: string, callParams: Record<string, unknown>, timeoutMs?: number): Promise<unknown> => {
|
|
471
|
+
if (disposed) throw new MuxError("transport", "mux client is closed");
|
|
472
|
+
const waitMs = retryAt - Date.now();
|
|
473
|
+
if (waitMs > 0) {
|
|
474
|
+
throw new MuxError("transport", `mux socket ${socketPath} is in backoff for another ${waitMs}ms`);
|
|
475
|
+
}
|
|
476
|
+
let socket: net.Socket;
|
|
477
|
+
try {
|
|
478
|
+
socket = await connectSocket(socketPath, connectTimeoutMs);
|
|
479
|
+
} catch (error) {
|
|
480
|
+
connectFailures += 1;
|
|
481
|
+
retryAt = Date.now() + delayFor(connectFailures);
|
|
482
|
+
reachable = false;
|
|
483
|
+
throw error;
|
|
484
|
+
}
|
|
485
|
+
connectFailures = 0;
|
|
486
|
+
retryAt = 0;
|
|
487
|
+
nextId += 1;
|
|
488
|
+
const id = `clio-${nextId}`;
|
|
489
|
+
const budget = timeoutMs ?? requestTimeoutMs;
|
|
490
|
+
try {
|
|
491
|
+
const result = await new Promise<unknown>((resolve, reject) => {
|
|
492
|
+
let settled = false;
|
|
493
|
+
const finish = (settle: () => void): void => {
|
|
494
|
+
if (settled) return;
|
|
495
|
+
settled = true;
|
|
496
|
+
clearTimeout(timer);
|
|
497
|
+
socket.destroy();
|
|
498
|
+
settle();
|
|
499
|
+
};
|
|
500
|
+
const timer = setTimeout(() => finish(() => reject(new MuxRequestTimeout(method, budget))), budget);
|
|
501
|
+
timer.unref?.();
|
|
502
|
+
readJsonLines(
|
|
503
|
+
socket,
|
|
504
|
+
(line) => {
|
|
505
|
+
const responseId = line.id;
|
|
506
|
+
if (typeof responseId !== "string") return;
|
|
507
|
+
const error = asRecord(line.error);
|
|
508
|
+
// herdr answers a request it could not even parse with an empty id,
|
|
509
|
+
// so an empty-id error line on this connection is still ours.
|
|
510
|
+
if (responseId !== id && !(responseId === "" && error)) return;
|
|
511
|
+
if (error) {
|
|
512
|
+
const code = typeof error.code === "string" ? error.code : "unknown";
|
|
513
|
+
const message = typeof error.message === "string" ? error.message : code;
|
|
514
|
+
finish(() => reject(new MuxError(muxErrorKind(code), message, { wireCode: code, method })));
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
finish(() => resolve(line.result));
|
|
518
|
+
},
|
|
519
|
+
() => finish(() => reject(new MuxError("protocol", `mux ${method} response line was oversized`, { method }))),
|
|
520
|
+
);
|
|
521
|
+
socket.on("error", (error: Error) =>
|
|
522
|
+
finish(() => reject(new MuxError("transport", `mux request ${method} failed: ${error.message}`, { method }))),
|
|
523
|
+
);
|
|
524
|
+
socket.on("close", () =>
|
|
525
|
+
finish(() => reject(new MuxError("transport", `mux request ${method} lost its connection`, { method }))),
|
|
526
|
+
);
|
|
527
|
+
socket.write(`${JSON.stringify({ id, method, params: callParams })}\n`, (error) => {
|
|
528
|
+
if (!error) return;
|
|
529
|
+
finish(() =>
|
|
530
|
+
reject(new MuxError("transport", `mux request ${method} failed to write: ${error.message}`, { method })),
|
|
531
|
+
);
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
reachable = true;
|
|
535
|
+
return result;
|
|
536
|
+
} catch (error) {
|
|
537
|
+
if (error instanceof MuxError && error.kind === "transport") reachable = false;
|
|
538
|
+
throw error;
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
const callObject = async (
|
|
543
|
+
method: string,
|
|
544
|
+
callParams: Record<string, unknown>,
|
|
545
|
+
timeoutMs?: number,
|
|
546
|
+
): Promise<Record<string, unknown>> => {
|
|
547
|
+
const result = asRecord(await call(method, callParams, timeoutMs));
|
|
548
|
+
if (!result) throw new MuxError("protocol", `mux ${method} returned a non-object result`, { method });
|
|
549
|
+
return result;
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
const snapshot = async (): Promise<MuxSnapshot> => {
|
|
553
|
+
const result = await callObject("session.snapshot", {});
|
|
554
|
+
const parsed = readSnapshot(result.snapshot);
|
|
555
|
+
snapshotCache = parsed;
|
|
556
|
+
serverInfo = parsed.server;
|
|
557
|
+
return parsed;
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
const subscribe = async (
|
|
561
|
+
kinds: ReadonlyArray<MuxEventKind>,
|
|
562
|
+
handler: (event: MuxEvent) => void,
|
|
563
|
+
subscribeOptions: MuxSubscribeOptions = {},
|
|
564
|
+
): Promise<MuxSubscription> => {
|
|
565
|
+
let closed = false;
|
|
566
|
+
let socket: net.Socket | null = null;
|
|
567
|
+
let retryTimer: NodeJS.Timeout | null = null;
|
|
568
|
+
let failures = 0;
|
|
569
|
+
|
|
570
|
+
const scheduleReopen = (): void => {
|
|
571
|
+
if (closed || disposed) return;
|
|
572
|
+
failures += 1;
|
|
573
|
+
const delay = delayFor(failures);
|
|
574
|
+
retryTimer = setTimeout(() => {
|
|
575
|
+
retryTimer = null;
|
|
576
|
+
void open(true);
|
|
577
|
+
}, delay);
|
|
578
|
+
retryTimer.unref?.();
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
const open = async (isReconnect: boolean): Promise<void> => {
|
|
582
|
+
if (closed || disposed) return;
|
|
583
|
+
let stream: net.Socket;
|
|
584
|
+
try {
|
|
585
|
+
stream = await connectSocket(socketPath, connectTimeoutMs);
|
|
586
|
+
} catch (error) {
|
|
587
|
+
log("debug", `mux event stream connect failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
588
|
+
scheduleReopen();
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (closed || disposed) {
|
|
592
|
+
stream.destroy();
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
socket = stream;
|
|
596
|
+
let acknowledged = false;
|
|
597
|
+
readJsonLines(
|
|
598
|
+
stream,
|
|
599
|
+
(line) => {
|
|
600
|
+
if (!acknowledged && typeof line.id === "string") {
|
|
601
|
+
acknowledged = true;
|
|
602
|
+
failures = 0;
|
|
603
|
+
if (isReconnect) {
|
|
604
|
+
// Events dropped while the socket was down are gone. Take a
|
|
605
|
+
// fresh snapshot and let the caller rebuild from it.
|
|
606
|
+
void snapshot().then(
|
|
607
|
+
(fresh) => subscribeOptions.onResync?.(fresh),
|
|
608
|
+
(error) =>
|
|
609
|
+
log("debug", `mux snapshot re-bootstrap failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
const event = readEvent(line);
|
|
615
|
+
if (event) handler(event);
|
|
616
|
+
},
|
|
617
|
+
() => stream.destroy(),
|
|
618
|
+
);
|
|
619
|
+
const onGone = (): void => {
|
|
620
|
+
if (socket !== stream) return;
|
|
621
|
+
socket = null;
|
|
622
|
+
stream.destroy();
|
|
623
|
+
scheduleReopen();
|
|
624
|
+
};
|
|
625
|
+
stream.on("error", onGone);
|
|
626
|
+
stream.on("close", onGone);
|
|
627
|
+
stream.write(
|
|
628
|
+
`${JSON.stringify({
|
|
629
|
+
id: `clio-sub-${kinds.join("+")}`,
|
|
630
|
+
method: "events.subscribe",
|
|
631
|
+
params: { subscriptions: kinds.map((kind) => ({ type: kind })) },
|
|
632
|
+
})}\n`,
|
|
633
|
+
);
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
await open(false);
|
|
637
|
+
|
|
638
|
+
return {
|
|
639
|
+
close(): void {
|
|
640
|
+
closed = true;
|
|
641
|
+
if (retryTimer) clearTimeout(retryTimer);
|
|
642
|
+
retryTimer = null;
|
|
643
|
+
socket?.destroy();
|
|
644
|
+
socket = null;
|
|
645
|
+
},
|
|
646
|
+
};
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
return {
|
|
650
|
+
socketPath,
|
|
651
|
+
connected(): boolean {
|
|
652
|
+
return reachable;
|
|
653
|
+
},
|
|
654
|
+
server(): MuxServerInfo | null {
|
|
655
|
+
return serverInfo;
|
|
656
|
+
},
|
|
657
|
+
cachedSnapshot(): MuxSnapshot | null {
|
|
658
|
+
return snapshotCache;
|
|
659
|
+
},
|
|
660
|
+
async ping(pingOptions = {}): Promise<MuxServerInfo> {
|
|
661
|
+
const result = await callObject("ping", {}, pingOptions.timeoutMs);
|
|
662
|
+
const info = readServerInfo(result);
|
|
663
|
+
serverInfo = info;
|
|
664
|
+
return info;
|
|
665
|
+
},
|
|
666
|
+
snapshot,
|
|
667
|
+
async paneCurrent(callerPaneId?: string): Promise<MuxPane> {
|
|
668
|
+
const result = await callObject("pane.current", params({ caller_pane_id: callerPaneId }));
|
|
669
|
+
return readPane(result.pane);
|
|
670
|
+
},
|
|
671
|
+
async paneList(workspaceId?: string): Promise<ReadonlyArray<MuxPane>> {
|
|
672
|
+
const result = await callObject("pane.list", params({ workspace_id: workspaceId }));
|
|
673
|
+
return readArray(result, "panes").map(readPane);
|
|
674
|
+
},
|
|
675
|
+
async paneSplit(request: MuxSplitRequest): Promise<MuxPane> {
|
|
676
|
+
const result = await callObject(
|
|
677
|
+
"pane.split",
|
|
678
|
+
params({
|
|
679
|
+
direction: request.direction,
|
|
680
|
+
target_pane_id: request.targetPaneId,
|
|
681
|
+
workspace_id: request.workspaceId,
|
|
682
|
+
cwd: request.cwd,
|
|
683
|
+
env: request.env,
|
|
684
|
+
focus: request.focus ?? false,
|
|
685
|
+
ratio: request.ratio,
|
|
686
|
+
}),
|
|
687
|
+
);
|
|
688
|
+
return readPane(result.pane);
|
|
689
|
+
},
|
|
690
|
+
async paneRename(paneId: string, label: string): Promise<void> {
|
|
691
|
+
await call("pane.rename", { pane_id: paneId, label });
|
|
692
|
+
},
|
|
693
|
+
async paneClose(paneId: string): Promise<void> {
|
|
694
|
+
await call("pane.close", { pane_id: paneId });
|
|
695
|
+
},
|
|
696
|
+
async paneLayout(paneId?: string): Promise<ReadonlyArray<string>> {
|
|
697
|
+
const result = await callObject("pane.layout", params({ pane_id: paneId }));
|
|
698
|
+
const layout = asRecord(result.layout);
|
|
699
|
+
if (!layout) throw new MuxError("protocol", "mux pane.layout returned no layout");
|
|
700
|
+
const ids: string[] = [];
|
|
701
|
+
for (const entry of readArray(layout, "panes")) {
|
|
702
|
+
const pane = asRecord(entry);
|
|
703
|
+
const id = pane ? pane.pane_id : undefined;
|
|
704
|
+
if (typeof id === "string") ids.push(id);
|
|
705
|
+
}
|
|
706
|
+
return ids;
|
|
707
|
+
},
|
|
708
|
+
async worktreeList(request = {}): Promise<MuxWorktreeListResult> {
|
|
709
|
+
const result = await callObject("worktree.list", params({ workspace_id: request.workspaceId, cwd: request.cwd }));
|
|
710
|
+
return { source: readWorktreeSource(result.source), worktrees: readArray(result, "worktrees").map(readWorktree) };
|
|
711
|
+
},
|
|
712
|
+
async worktreeCreate(request: MuxWorktreeCreateRequest): Promise<MuxWorktreeCreatedResult> {
|
|
713
|
+
const result = await callObject(
|
|
714
|
+
"worktree.create",
|
|
715
|
+
params({
|
|
716
|
+
workspace_id: request.workspaceId,
|
|
717
|
+
cwd: request.cwd,
|
|
718
|
+
branch: request.branch,
|
|
719
|
+
base: request.base,
|
|
720
|
+
path: request.path,
|
|
721
|
+
label: request.label,
|
|
722
|
+
focus: request.focus ?? false,
|
|
723
|
+
}),
|
|
724
|
+
);
|
|
725
|
+
const workspace = asRecord(result.workspace);
|
|
726
|
+
if (!workspace) throw new MuxError("protocol", "mux worktree.create returned no workspace");
|
|
727
|
+
return {
|
|
728
|
+
workspaceId: requireString(workspace, "workspace_id", "workspace"),
|
|
729
|
+
tab: readTab(result.tab),
|
|
730
|
+
rootPane: readPane(result.root_pane),
|
|
731
|
+
worktree: readWorktree(result.worktree),
|
|
732
|
+
};
|
|
733
|
+
},
|
|
734
|
+
async worktreeOpen(request: MuxWorktreeOpenRequest): Promise<MuxWorktreeOpenedResult> {
|
|
735
|
+
const result = await callObject(
|
|
736
|
+
"worktree.open",
|
|
737
|
+
params({
|
|
738
|
+
workspace_id: request.workspaceId,
|
|
739
|
+
cwd: request.cwd,
|
|
740
|
+
branch: request.branch,
|
|
741
|
+
path: request.path,
|
|
742
|
+
label: request.label,
|
|
743
|
+
focus: request.focus ?? false,
|
|
744
|
+
}),
|
|
745
|
+
);
|
|
746
|
+
const workspace = asRecord(result.workspace);
|
|
747
|
+
if (!workspace) throw new MuxError("protocol", "mux worktree.open returned no workspace");
|
|
748
|
+
return {
|
|
749
|
+
workspaceId: requireString(workspace, "workspace_id", "workspace"),
|
|
750
|
+
tab: readTab(result.tab),
|
|
751
|
+
rootPane: readPane(result.root_pane),
|
|
752
|
+
worktree: readWorktree(result.worktree),
|
|
753
|
+
alreadyOpen: result.already_open === true,
|
|
754
|
+
};
|
|
755
|
+
},
|
|
756
|
+
async worktreeRemove(workspaceId: string, removeOptions = {}): Promise<MuxWorktreeRemovedResult> {
|
|
757
|
+
const result = await callObject("worktree.remove", {
|
|
758
|
+
workspace_id: workspaceId,
|
|
759
|
+
force: removeOptions.force ?? false,
|
|
760
|
+
});
|
|
761
|
+
return {
|
|
762
|
+
workspaceId: requireString(result, "workspace_id", "worktree.remove"),
|
|
763
|
+
path: requireString(result, "path", "worktree.remove"),
|
|
764
|
+
forced: result.forced === true,
|
|
765
|
+
};
|
|
766
|
+
},
|
|
767
|
+
async paneSendText(paneId: string, text: string): Promise<void> {
|
|
768
|
+
await call("pane.send_text", { pane_id: paneId, text });
|
|
769
|
+
},
|
|
770
|
+
async paneReportAgent(request: MuxReportAgentRequest): Promise<void> {
|
|
771
|
+
await call(
|
|
772
|
+
"pane.report_agent",
|
|
773
|
+
params({
|
|
774
|
+
pane_id: request.paneId,
|
|
775
|
+
source: request.source,
|
|
776
|
+
agent: request.agent,
|
|
777
|
+
state: request.state,
|
|
778
|
+
message: request.message,
|
|
779
|
+
}),
|
|
780
|
+
);
|
|
781
|
+
},
|
|
782
|
+
async paneReportMetadata(request: MuxReportMetadataRequest): Promise<void> {
|
|
783
|
+
await call(
|
|
784
|
+
"pane.report_metadata",
|
|
785
|
+
params({
|
|
786
|
+
pane_id: request.paneId,
|
|
787
|
+
source: request.source,
|
|
788
|
+
agent: request.agent,
|
|
789
|
+
title: request.title,
|
|
790
|
+
display_agent: request.displayAgent,
|
|
791
|
+
tokens: request.tokens,
|
|
792
|
+
state_labels: request.stateLabels,
|
|
793
|
+
ttl_ms: request.ttlMs,
|
|
794
|
+
}),
|
|
795
|
+
);
|
|
796
|
+
},
|
|
797
|
+
async notificationShow(request: MuxNotificationRequest): Promise<MuxNotificationResult> {
|
|
798
|
+
const result = await callObject(
|
|
799
|
+
"notification.show",
|
|
800
|
+
params({ title: request.title, body: request.body, sound: request.sound }),
|
|
801
|
+
);
|
|
802
|
+
// `reason` is required on the wire, but a server that grows a new one
|
|
803
|
+
// must not turn a painted toast into a protocol error, so an unreadable
|
|
804
|
+
// reason degrades to the shown flag alone.
|
|
805
|
+
return {
|
|
806
|
+
shown: result.shown === true,
|
|
807
|
+
reason: optionalString(result, "reason") ?? (result.shown === true ? "shown" : "unknown"),
|
|
808
|
+
};
|
|
809
|
+
},
|
|
810
|
+
subscribe,
|
|
811
|
+
async close(): Promise<void> {
|
|
812
|
+
disposed = true;
|
|
813
|
+
reachable = false;
|
|
814
|
+
},
|
|
815
|
+
};
|
|
816
|
+
}
|