@termix-it/cryptoclaw 1.0.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 +1615 -0
- package/LICENSE +21 -0
- package/README-header.png +0 -0
- package/README.md +331 -0
- package/assets/avatar-placeholder.svg +19 -0
- package/assets/chrome-extension/README.md +22 -0
- package/assets/chrome-extension/background.js +438 -0
- package/assets/chrome-extension/icons/icon128.png +0 -0
- package/assets/chrome-extension/icons/icon16.png +0 -0
- package/assets/chrome-extension/icons/icon32.png +0 -0
- package/assets/chrome-extension/icons/icon48.png +0 -0
- package/assets/chrome-extension/manifest.json +25 -0
- package/assets/chrome-extension/options.html +196 -0
- package/assets/chrome-extension/options.js +59 -0
- package/assets/dmg-background-small.png +0 -0
- package/assets/dmg-background.png +0 -0
- package/cryptoclaw.mjs +14 -0
- package/dist/accounts-BPjR5SyC.js +251 -0
- package/dist/accounts-CIGHxE7R.js +251 -0
- package/dist/acp-cli-DBWmJpUJ.js +924 -0
- package/dist/acp-cli-DG9nygBk.js +926 -0
- package/dist/agent-3MZVCC-N.js +702 -0
- package/dist/agent-IDE9krld.js +702 -0
- package/dist/agent-scope-DBl1We79.js +370 -0
- package/dist/agent-scope-DLfFOxP6.js +370 -0
- package/dist/agent-scope-e_oNRhGi.js +606 -0
- package/dist/archive-Dy3Ezb-5.js +85 -0
- package/dist/archive-iT9wNsml.js +85 -0
- package/dist/audit-Bj_dxNaD.js +1853 -0
- package/dist/audit-jaTyyQX1.js +1853 -0
- package/dist/auth-RTEHx2eI.js +192 -0
- package/dist/auth-health-BlspzqyF.js +149 -0
- package/dist/auth-health-CCM6Z3vK.js +149 -0
- package/dist/auth-phxCaFNX.js +192 -0
- package/dist/auth-profiles-DoD7YxsD.js +2939 -0
- package/dist/boolean-BsqeuxE6.js +30 -0
- package/dist/brew-CAcErcKz.js +46 -0
- package/dist/brew-CqnNFIkD.js +46 -0
- package/dist/build-info.json +5 -0
- package/dist/call-D2DXG0AC.js +278 -0
- package/dist/call-DqxlETxE.js +278 -0
- package/dist/canvas-host/a2ui/.bundle.hash +1 -0
- package/dist/canvas-host/a2ui/a2ui.bundle.js +17765 -0
- package/dist/canvas-host/a2ui/index.html +307 -0
- package/dist/channel-options-BHpXOBuP.js +32 -0
- package/dist/channel-options-DUkxROTo.js +62 -0
- package/dist/channel-selection-BdigfMZZ.js +51 -0
- package/dist/channel-selection-D7jFTFec.js +51 -0
- package/dist/channel-summary-CobMoKN6.js +1120 -0
- package/dist/channel-summary-DS0kcHm2.js +1120 -0
- package/dist/channels-cli-D7ZNRrq7.js +1414 -0
- package/dist/channels-cli-DBwffwqw.js +1413 -0
- package/dist/channels-status-issues-C2UGSPvH.js +18 -0
- package/dist/channels-status-issues-Dxmd7zYk.js +18 -0
- package/dist/chrome-CwbWzmh4.js +1973 -0
- package/dist/chrome-SVqOMFxA.js +1953 -0
- package/dist/clack-prompter-k_egRADS.js +92 -0
- package/dist/clack-prompter-nSmn5cZ2.js +92 -0
- package/dist/cli/daemon-cli.js +2 -0
- package/dist/cli-C9xiWnx9.js +86 -0
- package/dist/cli-D6C9z94O.js +88 -0
- package/dist/cli-utils-B7iQwCY5.js +43 -0
- package/dist/cli-utils-CgOu3WAB.js +43 -0
- package/dist/client-BaTYzXOU.js +1617 -0
- package/dist/client-N6zH1neq.js +1617 -0
- package/dist/command-format-9IsYy-9N.js +52 -0
- package/dist/command-format-B0bnyrEA.js +38 -0
- package/dist/command-format-n_udBm8l.js +52 -0
- package/dist/command-options-DL1PhuUF.js +33 -0
- package/dist/commands-Dpeoos11.js +230 -0
- package/dist/completion-cli-B5nR7jt8.js +434 -0
- package/dist/completion-cli-CZ8zPqPs.js +773 -0
- package/dist/config-DSpb2aMV.js +4916 -0
- package/dist/config-IwiWafT3.js +5657 -0
- package/dist/config-dQK4rbfx.js +4916 -0
- package/dist/config-guard-DwIb5Y-v.js +5719 -0
- package/dist/configure-7M9mLC4o.js +896 -0
- package/dist/configure-CivCcvkA.js +896 -0
- package/dist/constants-C2T4hQIk.js +65 -0
- package/dist/constants-DBvu3LzZ.js +65 -0
- package/dist/control-service-BXC31sNw.js +61 -0
- package/dist/control-service-lVzEzp9f.js +61 -0
- package/dist/control-ui/apple-touch-icon.png +0 -0
- package/dist/control-ui/assets/index-DQ-7PWhX.js +7553 -0
- package/dist/control-ui/assets/index-DQ-7PWhX.js.map +1 -0
- package/dist/control-ui/assets/index-nlpH70Eh.css +1 -0
- package/dist/control-ui/favicon-32.png +0 -0
- package/dist/control-ui/favicon.ico +0 -0
- package/dist/control-ui/favicon.svg +22 -0
- package/dist/control-ui/index.html +17 -0
- package/dist/cron-cli-DS8ytfYQ.js +455 -0
- package/dist/cron-cli-Djb3HUdz.js +457 -0
- package/dist/daemon-cli-KoaJ2nJX.js +758 -0
- package/dist/daemon-cli-ZQIKLvAO.js +758 -0
- package/dist/daemon-runtime-CHWbAfKs.js +517 -0
- package/dist/daemon-runtime-DEjUQVjL.js +517 -0
- package/dist/deliver-DVRzRmNN.js +2557 -0
- package/dist/deliver-OfX6bSYR.js +2587 -0
- package/dist/deliver-sPBfRlXT.js +2587 -0
- package/dist/deps-BSdWD2wF.js +27 -0
- package/dist/deps-CZOfeWEp.js +27 -0
- package/dist/devices-cli-DHSpDnCS.js +207 -0
- package/dist/devices-cli-b9iF_uOi.js +205 -0
- package/dist/directory-cli-D0dTuAem.js +247 -0
- package/dist/directory-cli-qniSxsHo.js +245 -0
- package/dist/dispatcher-DA3u3CwV.js +160 -0
- package/dist/dns-cli-C0JUNb9O.js +201 -0
- package/dist/dns-cli-DwwMpMuN.js +199 -0
- package/dist/docs-cli-BlFHCHDz.js +161 -0
- package/dist/docs-cli-BpSPrY3Y.js +160 -0
- package/dist/doctor-Da1XgFcW.js +2584 -0
- package/dist/doctor-qrtookj-.js +2585 -0
- package/dist/entry.js +1156 -0
- package/dist/env-B2Cd_6IS.js +32 -0
- package/dist/errors--S5IAxQx.js +1952 -0
- package/dist/exec-CLQSz0CI.js +246 -0
- package/dist/exec-CW_QjkBi.js +246 -0
- package/dist/exec-D7I5LU33.js +1099 -0
- package/dist/exec-approvals-CuXem6Li.js +1043 -0
- package/dist/exec-approvals-P7vwSw4O.js +1043 -0
- package/dist/exec-approvals-cli-Csgqkksl.js +386 -0
- package/dist/exec-approvals-cli-Dk4yHE2L.js +388 -0
- package/dist/extensionAPI.js +64497 -0
- package/dist/format-B7OjpGnt.js +34 -0
- package/dist/format-Dv1wz41T.js +34 -0
- package/dist/gateway-cli-CJ7H2DgV.js +17352 -0
- package/dist/gateway-cli-CrKl-bIH.js +17352 -0
- package/dist/gateway-rpc--CH0NWWR.js +28 -0
- package/dist/gateway-rpc-BPfCU2Vi.js +28 -0
- package/dist/github-copilot-auth-CcPEEb3Q.js +1190 -0
- package/dist/github-copilot-auth-h3beCc_l.js +1190 -0
- package/dist/github-copilot-token-C1sArkX4.js +103 -0
- package/dist/github-copilot-token-DL6Pou2p.js +103 -0
- package/dist/github-copilot-token-hMl1wyZp.js +103 -0
- package/dist/gmail-setup-utils-CnVYPhaQ.js +428 -0
- package/dist/gmail-setup-utils-pZ9NnOvB.js +428 -0
- package/dist/health-format-BcsR5dtU.js +1211 -0
- package/dist/health-format-Pl8qK65t.js +1212 -0
- package/dist/help-format-BzWwbeSF.js +17 -0
- package/dist/help-format-CUnac_bT.js +17 -0
- package/dist/helpers-5yebzF4C.js +25 -0
- package/dist/helpers-CQI-5xS9.js +25 -0
- package/dist/helpers-CzQjTUbz.js +10 -0
- package/dist/helpers-OUt4hxkS.js +10 -0
- package/dist/hooks/bundled/boot-md/HOOK.md +19 -0
- package/dist/hooks/bundled/command-logger/HOOK.md +122 -0
- package/dist/hooks/bundled/session-memory/HOOK.md +109 -0
- package/dist/hooks/bundled/soul-evil/HOOK.md +71 -0
- package/dist/hooks-cli-BKHTdsn4.js +1058 -0
- package/dist/hooks-cli-zkLY25DX.js +1060 -0
- package/dist/hooks-status-BETsyuUR.js +443 -0
- package/dist/hooks-status-nPgxLL-x.js +443 -0
- package/dist/image-BFNcQzKp.js +629 -0
- package/dist/image-CC_YKDB-.js +1421 -0
- package/dist/image-DAsWVn7Q.js +629 -0
- package/dist/index.js +5900 -0
- package/dist/installs-Byb3s_8P.js +425 -0
- package/dist/installs-CELeuogN.js +425 -0
- package/dist/is-main-B6kCyqsv.js +25 -0
- package/dist/is-main-CBExsRNQ.js +25 -0
- package/dist/links-Brcj2Oc-.js +15 -0
- package/dist/links-cf9h8BXu.js +15 -0
- package/dist/loader-CU4bkeKe.js +61131 -0
- package/dist/logging-5MtSkLpb.js +1 -0
- package/dist/logging-BJRQDTME.js +15 -0
- package/dist/logging-CD55MXc7.js +15 -0
- package/dist/logging-kuFzZMsG.js +1 -0
- package/dist/login-qr-BCr2GKX6.js +475 -0
- package/dist/login-qr-BMbekoZW.js +478 -0
- package/dist/login-qr-HIPJAnbT.js +478 -0
- package/dist/logs-cli-CM7-XOcZ.js +230 -0
- package/dist/logs-cli-aqjT7oHu.js +228 -0
- package/dist/manager-C5m5qpB0.js +2871 -0
- package/dist/manager-CtGvmGvM.js +2872 -0
- package/dist/manager-DpZlm3gk.js +2870 -0
- package/dist/manifest-registry-BdTzIlta.js +669 -0
- package/dist/manifest-registry-DVQHMj0y.js +669 -0
- package/dist/message-channel-CfYBy4y3.js +110 -0
- package/dist/message-channel-CvHWS3C2.js +110 -0
- package/dist/model-selection-Av0fLq51.js +2940 -0
- package/dist/model-selection-DuI_Ue9Q.js +2691 -0
- package/dist/models-cli-C0JOZ5oP.js +2541 -0
- package/dist/models-cli-gmkzOpbq.js +2543 -0
- package/dist/net-D4G66Xui.js +137 -0
- package/dist/net-YrbuVTd2.js +137 -0
- package/dist/node-cli-BND9LLkM.js +1457 -0
- package/dist/node-cli-DNRB94ib.js +1459 -0
- package/dist/node-service-DTgDFo7M.js +67 -0
- package/dist/node-service-JEBcPQat.js +67 -0
- package/dist/nodes-cli-BCWlqHyR.js +1208 -0
- package/dist/nodes-cli-DWSo5dX7.js +1210 -0
- package/dist/nodes-screen-CMvShBEB.js +157 -0
- package/dist/nodes-screen-Cok3-EJw.js +157 -0
- package/dist/note-C5M2AQOP.js +73 -0
- package/dist/note-DEz9ZK7G.js +73 -0
- package/dist/onboard-channels-B75LjjVZ.js +671 -0
- package/dist/onboard-channels-DJz2TusQ.js +671 -0
- package/dist/onboard-skills-ByHkU9nx.js +3615 -0
- package/dist/onboard-skills-Wknr7pxR.js +3615 -0
- package/dist/onboarding-BejDjkx5.js +3455 -0
- package/dist/openclaw-root-YSGWCNem.js +84 -0
- package/dist/openclaw-root-_zPXSqgo.js +84 -0
- package/dist/pairing-cli-CFqD3IdI.js +122 -0
- package/dist/pairing-cli-Cn5vt4oI.js +120 -0
- package/dist/pairing-store-BHzy16_t.js +391 -0
- package/dist/pairing-store-BOhO49Bi.js +391 -0
- package/dist/parse-BZz5lHzQ.js +23 -0
- package/dist/parse-Bw0oH-rT.js +23 -0
- package/dist/parse-log-line-BxDgv4Uo.js +44 -0
- package/dist/parse-log-line-CUrpqe1w.js +44 -0
- package/dist/parse-timeout-D1XX_zN_.js +16 -0
- package/dist/parse-timeout-Du-wHHNi.js +16 -0
- package/dist/path-env-DQ-mM2ya.js +77 -0
- package/dist/path-env-eQ-HoQtS.js +77 -0
- package/dist/paths-C3yk0MGu.js +43 -0
- package/dist/paths-C90k-Dud.js +166 -0
- package/dist/paths-CGrNQEMk.js +201 -0
- package/dist/paths-D2ytuv-2.js +201 -0
- package/dist/paths-Dlp4VNXa.js +40 -0
- package/dist/paths-KUslkJRs.js +43 -0
- package/dist/pi-embedded-helpers-B7ARjDEK.js +1298 -0
- package/dist/pi-embedded-helpers-CQ9sq3Uu.js +8475 -0
- package/dist/pi-embedded-helpers-FPVRDeKf.js +1298 -0
- package/dist/pi-model-discovery-DWTTaAgY.js +20 -0
- package/dist/pi-model-discovery-EhM2JAQo.js +20 -0
- package/dist/pi-model-discovery-EwKVHlZB.js +20 -0
- package/dist/pi-tools.policy-Du4RZy9R.js +230 -0
- package/dist/plugin-auto-enable-EBxTHjFJ.js +461 -0
- package/dist/plugin-auto-enable-RwRwIUKC.js +461 -0
- package/dist/plugin-sdk/index.d.ts +8453 -0
- package/dist/plugin-sdk/index.js +23445 -0
- package/dist/plugin-sdk/pi-model-discovery-Dw3A6oXH.js +37 -0
- package/dist/plugins-BI-p2ba9.js +495 -0
- package/dist/plugins-Dk4UBZwz.js +496 -0
- package/dist/plugins-cli-CMg0G56J.js +441 -0
- package/dist/plugins-cli-CX7QeUjq.js +443 -0
- package/dist/ports-nXmd4Efh.js +96 -0
- package/dist/program-EAMJAB_2.js +191 -0
- package/dist/progress-D-Oc-KAH.js +133 -0
- package/dist/progress-DTEUicRP.js +133 -0
- package/dist/prompt-style-BBlJlXtd.js +9 -0
- package/dist/prompt-style-BntC_Eoo.js +9 -0
- package/dist/prompts-C2D9_VZC.js +10 -0
- package/dist/prompts-FbZThK8w.js +10 -0
- package/dist/pw-ai-D2jD_dho.js +1651 -0
- package/dist/pw-ai-D6JUaD41.js +1650 -0
- package/dist/pw-ai-ba2bHPGA.js +1649 -0
- package/dist/qmd-manager-B-ijyM7o.js +615 -0
- package/dist/qmd-manager-C_Jah5eo.js +618 -0
- package/dist/qmd-manager-xo4abL0u.js +618 -0
- package/dist/redact-1PNP01B_.js +97 -0
- package/dist/redact-BIMJ3ntQ.js +94 -0
- package/dist/redact-CVRUv382.js +97 -0
- package/dist/register.subclis-CJGimm3-.js +348 -0
- package/dist/reply-DfepuqG0.js +61133 -0
- package/dist/rolldown-runtime-Cbj13DAv.js +20 -0
- package/dist/routes-DkckwrRx.js +2410 -0
- package/dist/routes-Q42qtNl1.js +2410 -0
- package/dist/rpc-B4lvrGrD.js +95 -0
- package/dist/rpc-CbWcXAQK.js +95 -0
- package/dist/run-main-JFY3X4Xh.js +194 -0
- package/dist/sandbox-BGqDfFaH.js +2945 -0
- package/dist/sandbox-D_Tt3WRq.js +2945 -0
- package/dist/sandbox-cli-CaxqKiTq.js +461 -0
- package/dist/sandbox-cli-DA0Cbrzl.js +463 -0
- package/dist/security-cli-BZjA_GAD.js +508 -0
- package/dist/security-cli-Ct-pfF2h.js +506 -0
- package/dist/server-context-B9ez46MY.js +740 -0
- package/dist/server-context-C53lhEx_.js +740 -0
- package/dist/server-node-events-Cb6QOeDH.js +218 -0
- package/dist/server-node-events-DONmQvJg.js +216 -0
- package/dist/service-C0ccl5rU.js +680 -0
- package/dist/service-audit-AsnhO40e.js +542 -0
- package/dist/service-audit-BM-iyLL7.js +542 -0
- package/dist/service-mrQPgOXl.js +680 -0
- package/dist/session-cost-usage-B3HzifR9.js +692 -0
- package/dist/session-cost-usage-DH3c4xJA.js +692 -0
- package/dist/session-key-C-ig2pxJ.js +177 -0
- package/dist/session-key-CYpWeuht.js +177 -0
- package/dist/shared-CBcCIWC0.js +74 -0
- package/dist/shared-CtNMbLRE.js +150 -0
- package/dist/shared-DCh7fkR2.js +150 -0
- package/dist/shared-Na5zjEUc.js +74 -0
- package/dist/skill-scanner-AfOudYI1.js +255 -0
- package/dist/skill-scanner-BoGjHXUZ.js +255 -0
- package/dist/skills-7V483a6m.js +693 -0
- package/dist/skills-CsZRBUj0.js +694 -0
- package/dist/skills-cli-DQaq5LBX.js +290 -0
- package/dist/skills-cli-dVugbaAb.js +288 -0
- package/dist/skills-status-4_4zVBvV.js +187 -0
- package/dist/skills-status-cD4rfMR9.js +187 -0
- package/dist/sqlite-C4DljFNL.js +215 -0
- package/dist/sqlite-CvQzxS7q.js +197 -0
- package/dist/sqlite-Dnmf3LS7.js +215 -0
- package/dist/status-BakhDLuG.js +27 -0
- package/dist/status-BqtiImKF.js +21 -0
- package/dist/status-DHPz4Mg_.js +3364 -0
- package/dist/status-DSoYX3Ep.js +27 -0
- package/dist/status-Drziap9H.js +21 -0
- package/dist/subsystem-Btuh5yZj.js +834 -0
- package/dist/system-cli-CUQswQPX.js +83 -0
- package/dist/system-cli-Cz7in_Xr.js +81 -0
- package/dist/systemd-CFHiVC1D.js +438 -0
- package/dist/systemd-hints-CgQqk98i.js +19 -0
- package/dist/systemd-hints-DKVCFZS3.js +19 -0
- package/dist/systemd-kY3NnWdi.js +438 -0
- package/dist/systemd-linger-CFK5jDdq.js +75 -0
- package/dist/systemd-linger-CxBBzOjC.js +75 -0
- package/dist/table-DAOPkdc_.js +279 -0
- package/dist/table-DDQGlZSe.js +279 -0
- package/dist/tailnet-CZq_ZSNX.js +42 -0
- package/dist/tailnet-D5wOWpOX.js +42 -0
- package/dist/tailscale-BY0igR48.js +252 -0
- package/dist/tailscale-CCLcQalf.js +225 -0
- package/dist/tool-display-B2rS2o6B.js +795 -0
- package/dist/tool-display-Zbh1CRw5.js +795 -0
- package/dist/transcript-events-BOK6eOU8.js +17 -0
- package/dist/transcript-events-D2kT5WSz.js +17 -0
- package/dist/transcript-events-JLH5W4He.js +17 -0
- package/dist/tui-DFVs6pRO.js +2672 -0
- package/dist/tui-cli-CBKZVd1C.js +58 -0
- package/dist/tui-cli-DexeJOgZ.js +56 -0
- package/dist/tui-qPHLsB-x.js +2672 -0
- package/dist/update-B2q3duJD.js +317 -0
- package/dist/update-DNnej3C6.js +317 -0
- package/dist/update-cli-BPUtZjbl.js +1031 -0
- package/dist/update-cli-ehTYbpaU.js +1032 -0
- package/dist/update-runner-BfncKnU6.js +1375 -0
- package/dist/update-runner-DAeygRSU.js +1375 -0
- package/dist/usage-format-C4JfTbSp.js +36 -0
- package/dist/usage-format-DvowRSs-.js +36 -0
- package/dist/utils-7IMqr8vR.js +189 -0
- package/dist/utils-D7jskKIf.js +192 -0
- package/dist/wallet-manager-7KHKMCbT.js +264 -0
- package/dist/wallet-manager-CT1ykq2O.js +264 -0
- package/dist/webhooks-cli-C0CV-1jG.js +310 -0
- package/dist/webhooks-cli-DT16BygW.js +312 -0
- package/dist/widearea-dns-BJZTAyT3.js +127 -0
- package/dist/widearea-dns-Dk82I4Xa.js +127 -0
- package/dist/ws-6_dFpKWQ.js +13 -0
- package/dist/ws-D64QKPe6.js +13 -0
- package/dist/ws-log-BhQmGM0R.js +267 -0
- package/dist/ws-log-DsyLcTqD.js +267 -0
- package/dist/wsl-C24YfxH9.js +160 -0
- package/docs/.i18n/README.md +31 -0
- package/docs/.i18n/glossary.zh-CN.json +210 -0
- package/docs/.i18n/zh-CN.tm.jsonl +1329 -0
- package/docs/CNAME +1 -0
- package/docs/assets/macos-onboarding/01-macos-warning.jpeg +0 -0
- package/docs/assets/macos-onboarding/02-local-networks.jpeg +0 -0
- package/docs/assets/macos-onboarding/03-security-notice.png +0 -0
- package/docs/assets/macos-onboarding/04-choose-gateway.png +0 -0
- package/docs/assets/macos-onboarding/05-permissions.png +0 -0
- package/docs/assets/openclaw-logo-text-dark.png +0 -0
- package/docs/assets/openclaw-logo-text.png +0 -0
- package/docs/assets/pixel-lobster.svg +60 -0
- package/docs/assets/showcase/agents-ui.jpg +0 -0
- package/docs/assets/showcase/bambu-cli.png +0 -0
- package/docs/assets/showcase/codexmonitor.png +0 -0
- package/docs/assets/showcase/gohome-grafana.png +0 -0
- package/docs/assets/showcase/ios-testflight.jpg +0 -0
- package/docs/assets/showcase/oura-health.png +0 -0
- package/docs/assets/showcase/padel-cli.svg +11 -0
- package/docs/assets/showcase/padel-screenshot.jpg +0 -0
- package/docs/assets/showcase/papla-tts.jpg +0 -0
- package/docs/assets/showcase/pr-review-telegram.jpg +0 -0
- package/docs/assets/showcase/roborock-screenshot.jpg +0 -0
- package/docs/assets/showcase/roborock-status.svg +13 -0
- package/docs/assets/showcase/roof-camera-sky.jpg +0 -0
- package/docs/assets/showcase/snag.png +0 -0
- package/docs/assets/showcase/tesco-shop.jpg +0 -0
- package/docs/assets/showcase/wienerlinien.png +0 -0
- package/docs/assets/showcase/wine-cellar-skill.jpg +0 -0
- package/docs/assets/showcase/winix-air-purifier.jpg +0 -0
- package/docs/assets/showcase/xuezh-pronunciation.jpeg +0 -0
- package/docs/automation/auth-monitoring.md +44 -0
- package/docs/automation/cron-jobs.md +468 -0
- package/docs/automation/cron-vs-heartbeat.md +282 -0
- package/docs/automation/gmail-pubsub.md +256 -0
- package/docs/automation/poll.md +69 -0
- package/docs/automation/webhook.md +163 -0
- package/docs/bedrock.md +176 -0
- package/docs/brave-search.md +41 -0
- package/docs/broadcast-groups.md +442 -0
- package/docs/channels/bluebubbles.md +338 -0
- package/docs/channels/discord.md +476 -0
- package/docs/channels/feishu.md +577 -0
- package/docs/channels/googlechat.md +250 -0
- package/docs/channels/grammy.md +31 -0
- package/docs/channels/imessage.md +299 -0
- package/docs/channels/index.md +46 -0
- package/docs/channels/line.md +186 -0
- package/docs/channels/location.md +56 -0
- package/docs/channels/matrix.md +233 -0
- package/docs/channels/mattermost.md +138 -0
- package/docs/channels/msteams.md +768 -0
- package/docs/channels/nextcloud-talk.md +136 -0
- package/docs/channels/nostr.md +233 -0
- package/docs/channels/signal.md +202 -0
- package/docs/channels/slack.md +548 -0
- package/docs/channels/telegram.md +769 -0
- package/docs/channels/tlon.md +132 -0
- package/docs/channels/troubleshooting.md +29 -0
- package/docs/channels/twitch.md +379 -0
- package/docs/channels/whatsapp.md +404 -0
- package/docs/channels/zalo.md +189 -0
- package/docs/channels/zalouser.md +140 -0
- package/docs/cli/acp.md +170 -0
- package/docs/cli/agent.md +24 -0
- package/docs/cli/agents.md +75 -0
- package/docs/cli/approvals.md +50 -0
- package/docs/cli/browser.md +107 -0
- package/docs/cli/channels.md +79 -0
- package/docs/cli/config.md +50 -0
- package/docs/cli/configure.md +33 -0
- package/docs/cli/cron.md +42 -0
- package/docs/cli/dashboard.md +16 -0
- package/docs/cli/devices.md +70 -0
- package/docs/cli/directory.md +63 -0
- package/docs/cli/dns.md +23 -0
- package/docs/cli/docs.md +15 -0
- package/docs/cli/doctor.md +41 -0
- package/docs/cli/gateway.md +202 -0
- package/docs/cli/health.md +21 -0
- package/docs/cli/hooks.md +304 -0
- package/docs/cli/index.md +1031 -0
- package/docs/cli/logs.md +24 -0
- package/docs/cli/memory.md +45 -0
- package/docs/cli/message.md +239 -0
- package/docs/cli/models.md +79 -0
- package/docs/cli/node.md +112 -0
- package/docs/cli/nodes.md +73 -0
- package/docs/cli/onboard.md +43 -0
- package/docs/cli/pairing.md +21 -0
- package/docs/cli/plugins.md +62 -0
- package/docs/cli/reset.md +17 -0
- package/docs/cli/sandbox.md +152 -0
- package/docs/cli/security.md +26 -0
- package/docs/cli/sessions.md +16 -0
- package/docs/cli/setup.md +29 -0
- package/docs/cli/skills.md +26 -0
- package/docs/cli/status.md +26 -0
- package/docs/cli/system.md +60 -0
- package/docs/cli/tui.md +23 -0
- package/docs/cli/uninstall.md +17 -0
- package/docs/cli/update.md +98 -0
- package/docs/cli/voicecall.md +34 -0
- package/docs/cli/webhooks.md +25 -0
- package/docs/concepts/agent-loop.md +146 -0
- package/docs/concepts/agent-workspace.md +233 -0
- package/docs/concepts/agent.md +123 -0
- package/docs/concepts/architecture.md +129 -0
- package/docs/concepts/channel-routing.md +114 -0
- package/docs/concepts/compaction.md +61 -0
- package/docs/concepts/context.md +161 -0
- package/docs/concepts/features.md +53 -0
- package/docs/concepts/group-messages.md +84 -0
- package/docs/concepts/groups.md +373 -0
- package/docs/concepts/markdown-formatting.md +130 -0
- package/docs/concepts/memory.md +546 -0
- package/docs/concepts/messages.md +154 -0
- package/docs/concepts/model-failover.md +149 -0
- package/docs/concepts/model-providers.md +316 -0
- package/docs/concepts/models.md +208 -0
- package/docs/concepts/multi-agent.md +376 -0
- package/docs/concepts/oauth.md +145 -0
- package/docs/concepts/presence.md +102 -0
- package/docs/concepts/queue.md +89 -0
- package/docs/concepts/retry.md +69 -0
- package/docs/concepts/session-pruning.md +122 -0
- package/docs/concepts/session-tool.md +193 -0
- package/docs/concepts/session.md +204 -0
- package/docs/concepts/sessions.md +10 -0
- package/docs/concepts/streaming.md +135 -0
- package/docs/concepts/system-prompt.md +115 -0
- package/docs/concepts/timezone.md +91 -0
- package/docs/concepts/typebox.md +289 -0
- package/docs/concepts/typing-indicators.md +68 -0
- package/docs/concepts/usage-tracking.md +35 -0
- package/docs/date-time.md +128 -0
- package/docs/debug/node-issue.md +83 -0
- package/docs/debugging.md +162 -0
- package/docs/diagnostics/flags.md +91 -0
- package/docs/docs.json +1736 -0
- package/docs/environment.md +81 -0
- package/docs/experiments/onboarding-config-protocol.md +40 -0
- package/docs/experiments/plans/cron-add-hardening.md +63 -0
- package/docs/experiments/plans/group-policy-hardening.md +40 -0
- package/docs/experiments/plans/openresponses-gateway.md +123 -0
- package/docs/experiments/proposals/model-config.md +36 -0
- package/docs/experiments/research/memory.md +228 -0
- package/docs/gateway/authentication.md +145 -0
- package/docs/gateway/background-process.md +93 -0
- package/docs/gateway/bonjour.md +167 -0
- package/docs/gateway/bridge-protocol.md +89 -0
- package/docs/gateway/cli-backends.md +225 -0
- package/docs/gateway/configuration-examples.md +606 -0
- package/docs/gateway/configuration.md +3411 -0
- package/docs/gateway/discovery.md +116 -0
- package/docs/gateway/doctor.md +282 -0
- package/docs/gateway/gateway-lock.md +34 -0
- package/docs/gateway/health.md +35 -0
- package/docs/gateway/heartbeat.md +362 -0
- package/docs/gateway/index.md +328 -0
- package/docs/gateway/local-models.md +150 -0
- package/docs/gateway/logging.md +113 -0
- package/docs/gateway/multiple-gateways.md +112 -0
- package/docs/gateway/network-model.md +17 -0
- package/docs/gateway/openai-http-api.md +118 -0
- package/docs/gateway/openresponses-http-api.md +317 -0
- package/docs/gateway/pairing.md +99 -0
- package/docs/gateway/protocol.md +221 -0
- package/docs/gateway/remote-gateway-readme.md +157 -0
- package/docs/gateway/remote.md +129 -0
- package/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md +128 -0
- package/docs/gateway/sandboxing.md +193 -0
- package/docs/gateway/security/formal-verification.md +164 -0
- package/docs/gateway/security/index.md +825 -0
- package/docs/gateway/tailscale.md +127 -0
- package/docs/gateway/tools-invoke-http-api.md +85 -0
- package/docs/gateway/troubleshooting.md +767 -0
- package/docs/help/faq.md +2829 -0
- package/docs/help/index.md +21 -0
- package/docs/help/troubleshooting.md +98 -0
- package/docs/hooks/soul-evil.md +69 -0
- package/docs/hooks.md +913 -0
- package/docs/images/feishu-step2-create-app.png +0 -0
- package/docs/images/feishu-step3-credentials.png +0 -0
- package/docs/images/feishu-step4-permissions.png +0 -0
- package/docs/images/feishu-step5-bot-capability.png +0 -0
- package/docs/images/feishu-step6-event-subscription.png +0 -0
- package/docs/images/groups-flow.svg +52 -0
- package/docs/images/mobile-ui-screenshot.png +0 -0
- package/docs/index.md +192 -0
- package/docs/install/ansible.md +208 -0
- package/docs/install/bun.md +59 -0
- package/docs/install/development-channels.md +75 -0
- package/docs/install/docker.md +567 -0
- package/docs/install/exe-dev.md +126 -0
- package/docs/install/fly.md +486 -0
- package/docs/install/gcp.md +503 -0
- package/docs/install/hetzner.md +330 -0
- package/docs/install/index.md +187 -0
- package/docs/install/installer.md +123 -0
- package/docs/install/macos-vm.md +281 -0
- package/docs/install/migrating.md +192 -0
- package/docs/install/nix.md +96 -0
- package/docs/install/node.md +78 -0
- package/docs/install/northflank.mdx +53 -0
- package/docs/install/railway.mdx +99 -0
- package/docs/install/render.mdx +165 -0
- package/docs/install/uninstall.md +128 -0
- package/docs/install/updating.md +228 -0
- package/docs/logging.md +350 -0
- package/docs/multi-agent-sandbox-tools.md +395 -0
- package/docs/network.md +54 -0
- package/docs/nodes/audio.md +114 -0
- package/docs/nodes/camera.md +156 -0
- package/docs/nodes/images.md +72 -0
- package/docs/nodes/index.md +341 -0
- package/docs/nodes/location-command.md +113 -0
- package/docs/nodes/media-understanding.md +379 -0
- package/docs/nodes/talk.md +90 -0
- package/docs/nodes/voicewake.md +65 -0
- package/docs/perplexity.md +80 -0
- package/docs/pi-dev.md +70 -0
- package/docs/pi.md +612 -0
- package/docs/platforms/android.md +148 -0
- package/docs/platforms/digitalocean.md +262 -0
- package/docs/platforms/index.md +53 -0
- package/docs/platforms/ios.md +107 -0
- package/docs/platforms/linux.md +94 -0
- package/docs/platforms/mac/bundled-gateway.md +73 -0
- package/docs/platforms/mac/canvas.md +125 -0
- package/docs/platforms/mac/child-process.md +69 -0
- package/docs/platforms/mac/dev-setup.md +102 -0
- package/docs/platforms/mac/health.md +34 -0
- package/docs/platforms/mac/icon.md +31 -0
- package/docs/platforms/mac/logging.md +57 -0
- package/docs/platforms/mac/menu-bar.md +81 -0
- package/docs/platforms/mac/peekaboo.md +65 -0
- package/docs/platforms/mac/permissions.md +44 -0
- package/docs/platforms/mac/release.md +85 -0
- package/docs/platforms/mac/remote.md +83 -0
- package/docs/platforms/mac/signing.md +47 -0
- package/docs/platforms/mac/skills.md +33 -0
- package/docs/platforms/mac/voice-overlay.md +60 -0
- package/docs/platforms/mac/voicewake.md +67 -0
- package/docs/platforms/mac/webchat.md +41 -0
- package/docs/platforms/mac/xpc.md +61 -0
- package/docs/platforms/macos.md +203 -0
- package/docs/platforms/oracle.md +303 -0
- package/docs/platforms/raspberry-pi.md +358 -0
- package/docs/platforms/windows.md +159 -0
- package/docs/plugin.md +664 -0
- package/docs/plugins/agent-tools.md +99 -0
- package/docs/plugins/manifest.md +71 -0
- package/docs/plugins/voice-call.md +284 -0
- package/docs/plugins/zalouser.md +81 -0
- package/docs/prose.md +134 -0
- package/docs/providers/anthropic.md +152 -0
- package/docs/providers/claude-max-api-proxy.md +148 -0
- package/docs/providers/cloudflare-ai-gateway.md +71 -0
- package/docs/providers/deepgram.md +93 -0
- package/docs/providers/github-copilot.md +72 -0
- package/docs/providers/glm.md +33 -0
- package/docs/providers/index.md +63 -0
- package/docs/providers/minimax.md +208 -0
- package/docs/providers/models.md +51 -0
- package/docs/providers/moonshot.md +142 -0
- package/docs/providers/ollama.md +277 -0
- package/docs/providers/openai.md +62 -0
- package/docs/providers/opencode.md +36 -0
- package/docs/providers/openrouter.md +37 -0
- package/docs/providers/qwen.md +53 -0
- package/docs/providers/synthetic.md +99 -0
- package/docs/providers/venice.md +267 -0
- package/docs/providers/vercel-ai-gateway.md +50 -0
- package/docs/providers/xiaomi.md +64 -0
- package/docs/providers/zai.md +36 -0
- package/docs/refactor/clawnet.md +417 -0
- package/docs/refactor/exec-host.md +316 -0
- package/docs/refactor/outbound-session-mirroring.md +85 -0
- package/docs/refactor/plugin-sdk.md +214 -0
- package/docs/refactor/strict-config.md +93 -0
- package/docs/reference/AGENTS.default.md +124 -0
- package/docs/reference/RELEASING.md +120 -0
- package/docs/reference/api-usage-costs.md +137 -0
- package/docs/reference/credits.md +27 -0
- package/docs/reference/device-models.md +47 -0
- package/docs/reference/rpc.md +43 -0
- package/docs/reference/session-management-compaction.md +285 -0
- package/docs/reference/templates/AGENTS.dev.md +83 -0
- package/docs/reference/templates/AGENTS.md +218 -0
- package/docs/reference/templates/BOOT.md +10 -0
- package/docs/reference/templates/BOOTSTRAP.md +61 -0
- package/docs/reference/templates/HEARTBEAT.md +11 -0
- package/docs/reference/templates/IDENTITY.dev.md +47 -0
- package/docs/reference/templates/IDENTITY.md +27 -0
- package/docs/reference/templates/SOUL.dev.md +76 -0
- package/docs/reference/templates/SOUL.md +42 -0
- package/docs/reference/templates/TOOLS.dev.md +24 -0
- package/docs/reference/templates/TOOLS.md +46 -0
- package/docs/reference/templates/USER.dev.md +18 -0
- package/docs/reference/templates/USER.md +22 -0
- package/docs/reference/test.md +50 -0
- package/docs/reference/transcript-hygiene.md +129 -0
- package/docs/reference/wizard.md +268 -0
- package/docs/scripts.md +28 -0
- package/docs/security/formal-verification.md +164 -0
- package/docs/start/bootstrapping.md +41 -0
- package/docs/start/docs-directory.md +64 -0
- package/docs/start/getting-started.md +120 -0
- package/docs/start/hubs.md +197 -0
- package/docs/start/lore.md +219 -0
- package/docs/start/onboarding.md +80 -0
- package/docs/start/openclaw.md +224 -0
- package/docs/start/pairing.md +86 -0
- package/docs/start/quickstart.md +22 -0
- package/docs/start/setup.md +162 -0
- package/docs/start/showcase.md +416 -0
- package/docs/start/wizard-cli-automation.md +141 -0
- package/docs/start/wizard-cli-reference.md +244 -0
- package/docs/start/wizard.md +108 -0
- package/docs/style.css +3 -0
- package/docs/testing.md +368 -0
- package/docs/token-use.md +112 -0
- package/docs/tools/agent-send.md +53 -0
- package/docs/tools/apply-patch.md +50 -0
- package/docs/tools/browser-linux-troubleshooting.md +139 -0
- package/docs/tools/browser-login.md +68 -0
- package/docs/tools/browser.md +576 -0
- package/docs/tools/chrome-extension.md +178 -0
- package/docs/tools/clawhub.md +257 -0
- package/docs/tools/creating-skills.md +54 -0
- package/docs/tools/elevated.md +57 -0
- package/docs/tools/exec-approvals.md +246 -0
- package/docs/tools/exec.md +179 -0
- package/docs/tools/firecrawl.md +61 -0
- package/docs/tools/index.md +512 -0
- package/docs/tools/llm-task.md +115 -0
- package/docs/tools/lobster.md +342 -0
- package/docs/tools/reactions.md +22 -0
- package/docs/tools/skills-config.md +76 -0
- package/docs/tools/skills.md +300 -0
- package/docs/tools/slash-commands.md +198 -0
- package/docs/tools/subagents.md +151 -0
- package/docs/tools/thinking.md +74 -0
- package/docs/tools/web.md +261 -0
- package/docs/tts.md +396 -0
- package/docs/tui.md +162 -0
- package/docs/vps.md +43 -0
- package/docs/web/control-ui.md +223 -0
- package/docs/web/dashboard.md +46 -0
- package/docs/web/index.md +116 -0
- package/docs/web/webchat.md +49 -0
- package/docs/whatsapp-openclaw-ai-zh.jpg +0 -0
- package/docs/whatsapp-openclaw.jpg +0 -0
- package/docs/zh-CN/AGENTS.md +59 -0
- package/docs/zh-CN/automation/auth-monitoring.md +47 -0
- package/docs/zh-CN/automation/cron-jobs.md +424 -0
- package/docs/zh-CN/automation/cron-vs-heartbeat.md +286 -0
- package/docs/zh-CN/automation/gmail-pubsub.md +249 -0
- package/docs/zh-CN/automation/poll.md +76 -0
- package/docs/zh-CN/automation/webhook.md +163 -0
- package/docs/zh-CN/bedrock.md +170 -0
- package/docs/zh-CN/brave-search.md +48 -0
- package/docs/zh-CN/broadcast-groups.md +449 -0
- package/docs/zh-CN/channels/bluebubbles.md +271 -0
- package/docs/zh-CN/channels/discord.md +468 -0
- package/docs/zh-CN/channels/feishu.md +629 -0
- package/docs/zh-CN/channels/googlechat.md +257 -0
- package/docs/zh-CN/channels/grammy.md +38 -0
- package/docs/zh-CN/channels/imessage.md +302 -0
- package/docs/zh-CN/channels/index.md +53 -0
- package/docs/zh-CN/channels/line.md +180 -0
- package/docs/zh-CN/channels/location.md +63 -0
- package/docs/zh-CN/channels/matrix.md +221 -0
- package/docs/zh-CN/channels/mattermost.md +144 -0
- package/docs/zh-CN/channels/msteams.md +775 -0
- package/docs/zh-CN/channels/nextcloud-talk.md +142 -0
- package/docs/zh-CN/channels/nostr.md +240 -0
- package/docs/zh-CN/channels/signal.md +209 -0
- package/docs/zh-CN/channels/slack.md +531 -0
- package/docs/zh-CN/channels/telegram.md +751 -0
- package/docs/zh-CN/channels/tlon.md +136 -0
- package/docs/zh-CN/channels/troubleshooting.md +36 -0
- package/docs/zh-CN/channels/twitch.md +385 -0
- package/docs/zh-CN/channels/whatsapp.md +411 -0
- package/docs/zh-CN/channels/zalo.md +196 -0
- package/docs/zh-CN/channels/zalouser.md +147 -0
- package/docs/zh-CN/cli/acp.md +173 -0
- package/docs/zh-CN/cli/agent.md +30 -0
- package/docs/zh-CN/cli/agents.md +82 -0
- package/docs/zh-CN/cli/approvals.md +57 -0
- package/docs/zh-CN/cli/browser.md +114 -0
- package/docs/zh-CN/cli/channels.md +86 -0
- package/docs/zh-CN/cli/config.md +57 -0
- package/docs/zh-CN/cli/configure.md +38 -0
- package/docs/zh-CN/cli/cron.md +43 -0
- package/docs/zh-CN/cli/dashboard.md +23 -0
- package/docs/zh-CN/cli/devices.md +74 -0
- package/docs/zh-CN/cli/directory.md +70 -0
- package/docs/zh-CN/cli/dns.md +30 -0
- package/docs/zh-CN/cli/docs.md +22 -0
- package/docs/zh-CN/cli/doctor.md +48 -0
- package/docs/zh-CN/cli/gateway.md +206 -0
- package/docs/zh-CN/cli/health.md +28 -0
- package/docs/zh-CN/cli/hooks.md +311 -0
- package/docs/zh-CN/cli/index.md +1032 -0
- package/docs/zh-CN/cli/logs.md +31 -0
- package/docs/zh-CN/cli/memory.md +52 -0
- package/docs/zh-CN/cli/message.md +246 -0
- package/docs/zh-CN/cli/models.md +85 -0
- package/docs/zh-CN/cli/node.md +115 -0
- package/docs/zh-CN/cli/nodes.md +80 -0
- package/docs/zh-CN/cli/onboard.md +36 -0
- package/docs/zh-CN/cli/pairing.md +28 -0
- package/docs/zh-CN/cli/plugins.md +66 -0
- package/docs/zh-CN/cli/reset.md +24 -0
- package/docs/zh-CN/cli/sandbox.md +158 -0
- package/docs/zh-CN/cli/security.md +33 -0
- package/docs/zh-CN/cli/sessions.md +23 -0
- package/docs/zh-CN/cli/setup.md +36 -0
- package/docs/zh-CN/cli/skills.md +33 -0
- package/docs/zh-CN/cli/status.md +33 -0
- package/docs/zh-CN/cli/system.md +63 -0
- package/docs/zh-CN/cli/tui.md +30 -0
- package/docs/zh-CN/cli/uninstall.md +24 -0
- package/docs/zh-CN/cli/update.md +101 -0
- package/docs/zh-CN/cli/voicecall.md +41 -0
- package/docs/zh-CN/cli/webhooks.md +32 -0
- package/docs/zh-CN/concepts/agent-loop.md +146 -0
- package/docs/zh-CN/concepts/agent-workspace.md +219 -0
- package/docs/zh-CN/concepts/agent.md +115 -0
- package/docs/zh-CN/concepts/architecture.md +123 -0
- package/docs/zh-CN/concepts/channel-routing.md +117 -0
- package/docs/zh-CN/concepts/compaction.md +67 -0
- package/docs/zh-CN/concepts/context.md +168 -0
- package/docs/zh-CN/concepts/features.md +59 -0
- package/docs/zh-CN/concepts/group-messages.md +91 -0
- package/docs/zh-CN/concepts/groups.md +379 -0
- package/docs/zh-CN/concepts/markdown-formatting.md +117 -0
- package/docs/zh-CN/concepts/memory.md +412 -0
- package/docs/zh-CN/concepts/messages.md +141 -0
- package/docs/zh-CN/concepts/model-failover.md +145 -0
- package/docs/zh-CN/concepts/model-providers.md +320 -0
- package/docs/zh-CN/concepts/models.md +196 -0
- package/docs/zh-CN/concepts/multi-agent.md +372 -0
- package/docs/zh-CN/concepts/oauth.md +151 -0
- package/docs/zh-CN/concepts/presence.md +99 -0
- package/docs/zh-CN/concepts/queue.md +94 -0
- package/docs/zh-CN/concepts/retry.md +76 -0
- package/docs/zh-CN/concepts/session-pruning.md +129 -0
- package/docs/zh-CN/concepts/session-tool.md +200 -0
- package/docs/zh-CN/concepts/session.md +166 -0
- package/docs/zh-CN/concepts/sessions.md +17 -0
- package/docs/zh-CN/concepts/streaming.md +133 -0
- package/docs/zh-CN/concepts/system-prompt.md +101 -0
- package/docs/zh-CN/concepts/timezone.md +96 -0
- package/docs/zh-CN/concepts/typebox.md +284 -0
- package/docs/zh-CN/concepts/typing-indicators.md +74 -0
- package/docs/zh-CN/concepts/usage-tracking.md +42 -0
- package/docs/zh-CN/date-time.md +129 -0
- package/docs/zh-CN/debug/node-issue.md +90 -0
- package/docs/zh-CN/debugging.md +160 -0
- package/docs/zh-CN/diagnostics/flags.md +98 -0
- package/docs/zh-CN/environment.md +88 -0
- package/docs/zh-CN/experiments/onboarding-config-protocol.md +47 -0
- package/docs/zh-CN/experiments/plans/cron-add-hardening.md +70 -0
- package/docs/zh-CN/experiments/plans/group-policy-hardening.md +45 -0
- package/docs/zh-CN/experiments/plans/openresponses-gateway.md +121 -0
- package/docs/zh-CN/experiments/proposals/model-config.md +42 -0
- package/docs/zh-CN/experiments/research/memory.md +235 -0
- package/docs/zh-CN/gateway/authentication.md +142 -0
- package/docs/zh-CN/gateway/background-process.md +100 -0
- package/docs/zh-CN/gateway/bonjour.md +174 -0
- package/docs/zh-CN/gateway/bridge-protocol.md +86 -0
- package/docs/zh-CN/gateway/cli-backends.md +213 -0
- package/docs/zh-CN/gateway/configuration-examples.md +587 -0
- package/docs/zh-CN/gateway/configuration.md +3332 -0
- package/docs/zh-CN/gateway/discovery.md +123 -0
- package/docs/zh-CN/gateway/doctor.md +238 -0
- package/docs/zh-CN/gateway/gateway-lock.md +41 -0
- package/docs/zh-CN/gateway/health.md +42 -0
- package/docs/zh-CN/gateway/heartbeat.md +274 -0
- package/docs/zh-CN/gateway/index.md +335 -0
- package/docs/zh-CN/gateway/local-models.md +157 -0
- package/docs/zh-CN/gateway/logging.md +114 -0
- package/docs/zh-CN/gateway/multiple-gateways.md +119 -0
- package/docs/zh-CN/gateway/network-model.md +23 -0
- package/docs/zh-CN/gateway/openai-http-api.md +125 -0
- package/docs/zh-CN/gateway/openresponses-http-api.md +317 -0
- package/docs/zh-CN/gateway/pairing.md +99 -0
- package/docs/zh-CN/gateway/protocol.md +220 -0
- package/docs/zh-CN/gateway/remote-gateway-readme.md +164 -0
- package/docs/zh-CN/gateway/remote.md +133 -0
- package/docs/zh-CN/gateway/sandbox-vs-tool-policy-vs-elevated.md +135 -0
- package/docs/zh-CN/gateway/sandboxing.md +188 -0
- package/docs/zh-CN/gateway/security/formal-verification.md +169 -0
- package/docs/zh-CN/gateway/security/index.md +777 -0
- package/docs/zh-CN/gateway/tailscale.md +124 -0
- package/docs/zh-CN/gateway/tools-invoke-http-api.md +92 -0
- package/docs/zh-CN/gateway/troubleshooting.md +771 -0
- package/docs/zh-CN/help/faq.md +2628 -0
- package/docs/zh-CN/help/index.md +28 -0
- package/docs/zh-CN/help/troubleshooting.md +104 -0
- package/docs/zh-CN/hooks/soul-evil.md +72 -0
- package/docs/zh-CN/hooks.md +919 -0
- package/docs/zh-CN/index.md +186 -0
- package/docs/zh-CN/install/ansible.md +215 -0
- package/docs/zh-CN/install/bun.md +65 -0
- package/docs/zh-CN/install/development-channels.md +81 -0
- package/docs/zh-CN/install/docker.md +532 -0
- package/docs/zh-CN/install/exe-dev.md +127 -0
- package/docs/zh-CN/install/fly.md +490 -0
- package/docs/zh-CN/install/gcp.md +510 -0
- package/docs/zh-CN/install/hetzner.md +337 -0
- package/docs/zh-CN/install/index.md +193 -0
- package/docs/zh-CN/install/installer.md +128 -0
- package/docs/zh-CN/install/macos-vm.md +288 -0
- package/docs/zh-CN/install/migrating.md +199 -0
- package/docs/zh-CN/install/nix.md +99 -0
- package/docs/zh-CN/install/node.md +85 -0
- package/docs/zh-CN/install/northflank.mdx +60 -0
- package/docs/zh-CN/install/railway.mdx +106 -0
- package/docs/zh-CN/install/render.mdx +169 -0
- package/docs/zh-CN/install/uninstall.md +135 -0
- package/docs/zh-CN/install/updating.md +233 -0
- package/docs/zh-CN/logging.md +329 -0
- package/docs/zh-CN/multi-agent-sandbox-tools.md +401 -0
- package/docs/zh-CN/network.md +59 -0
- package/docs/zh-CN/nodes/audio.md +120 -0
- package/docs/zh-CN/nodes/camera.md +162 -0
- package/docs/zh-CN/nodes/images.md +79 -0
- package/docs/zh-CN/nodes/index.md +348 -0
- package/docs/zh-CN/nodes/location-command.md +120 -0
- package/docs/zh-CN/nodes/media-understanding.md +380 -0
- package/docs/zh-CN/nodes/talk.md +97 -0
- package/docs/zh-CN/nodes/voicewake.md +72 -0
- package/docs/zh-CN/perplexity.md +84 -0
- package/docs/zh-CN/pi-dev.md +77 -0
- package/docs/zh-CN/pi.md +619 -0
- package/docs/zh-CN/platforms/android.md +155 -0
- package/docs/zh-CN/platforms/digitalocean.md +269 -0
- package/docs/zh-CN/platforms/index.md +60 -0
- package/docs/zh-CN/platforms/ios.md +114 -0
- package/docs/zh-CN/platforms/linux.md +101 -0
- package/docs/zh-CN/platforms/mac/bundled-gateway.md +75 -0
- package/docs/zh-CN/platforms/mac/canvas.md +128 -0
- package/docs/zh-CN/platforms/mac/child-process.md +73 -0
- package/docs/zh-CN/platforms/mac/dev-setup.md +109 -0
- package/docs/zh-CN/platforms/mac/health.md +41 -0
- package/docs/zh-CN/platforms/mac/icon.md +38 -0
- package/docs/zh-CN/platforms/mac/logging.md +64 -0
- package/docs/zh-CN/platforms/mac/menu-bar.md +88 -0
- package/docs/zh-CN/platforms/mac/peekaboo.md +62 -0
- package/docs/zh-CN/platforms/mac/permissions.md +46 -0
- package/docs/zh-CN/platforms/mac/release.md +92 -0
- package/docs/zh-CN/platforms/mac/remote.md +90 -0
- package/docs/zh-CN/platforms/mac/signing.md +54 -0
- package/docs/zh-CN/platforms/mac/skills.md +40 -0
- package/docs/zh-CN/platforms/mac/voice-overlay.md +67 -0
- package/docs/zh-CN/platforms/mac/voicewake.md +74 -0
- package/docs/zh-CN/platforms/mac/webchat.md +43 -0
- package/docs/zh-CN/platforms/mac/xpc.md +68 -0
- package/docs/zh-CN/platforms/macos.md +193 -0
- package/docs/zh-CN/platforms/oracle.md +310 -0
- package/docs/zh-CN/platforms/raspberry-pi.md +365 -0
- package/docs/zh-CN/platforms/windows.md +156 -0
- package/docs/zh-CN/plugin.md +639 -0
- package/docs/zh-CN/plugins/agent-tools.md +99 -0
- package/docs/zh-CN/plugins/manifest.md +68 -0
- package/docs/zh-CN/plugins/voice-call.md +250 -0
- package/docs/zh-CN/plugins/zalouser.md +88 -0
- package/docs/zh-CN/prose.md +141 -0
- package/docs/zh-CN/providers/anthropic.md +159 -0
- package/docs/zh-CN/providers/claude-max-api-proxy.md +155 -0
- package/docs/zh-CN/providers/deepgram.md +97 -0
- package/docs/zh-CN/providers/github-copilot.md +67 -0
- package/docs/zh-CN/providers/glm.md +39 -0
- package/docs/zh-CN/providers/index.md +68 -0
- package/docs/zh-CN/providers/minimax.md +206 -0
- package/docs/zh-CN/providers/models.md +55 -0
- package/docs/zh-CN/providers/moonshot.md +145 -0
- package/docs/zh-CN/providers/ollama.md +230 -0
- package/docs/zh-CN/providers/openai.md +68 -0
- package/docs/zh-CN/providers/opencode.md +41 -0
- package/docs/zh-CN/providers/openrouter.md +43 -0
- package/docs/zh-CN/providers/qwen.md +55 -0
- package/docs/zh-CN/providers/synthetic.md +102 -0
- package/docs/zh-CN/providers/venice.md +274 -0
- package/docs/zh-CN/providers/vercel-ai-gateway.md +57 -0
- package/docs/zh-CN/providers/xiaomi.md +68 -0
- package/docs/zh-CN/providers/zai.md +41 -0
- package/docs/zh-CN/refactor/clawnet.md +424 -0
- package/docs/zh-CN/refactor/exec-host.md +323 -0
- package/docs/zh-CN/refactor/outbound-session-mirroring.md +92 -0
- package/docs/zh-CN/refactor/plugin-sdk.md +221 -0
- package/docs/zh-CN/refactor/strict-config.md +100 -0
- package/docs/zh-CN/reference/AGENTS.default.md +131 -0
- package/docs/zh-CN/reference/RELEASING.md +123 -0
- package/docs/zh-CN/reference/api-usage-costs.md +136 -0
- package/docs/zh-CN/reference/credits.md +34 -0
- package/docs/zh-CN/reference/device-models.md +54 -0
- package/docs/zh-CN/reference/rpc.md +48 -0
- package/docs/zh-CN/reference/session-management-compaction.md +287 -0
- package/docs/zh-CN/reference/templates/AGENTS.dev.md +89 -0
- package/docs/zh-CN/reference/templates/AGENTS.md +225 -0
- package/docs/zh-CN/reference/templates/BOOT.md +17 -0
- package/docs/zh-CN/reference/templates/BOOTSTRAP.md +68 -0
- package/docs/zh-CN/reference/templates/HEARTBEAT.md +18 -0
- package/docs/zh-CN/reference/templates/IDENTITY.dev.md +54 -0
- package/docs/zh-CN/reference/templates/IDENTITY.md +35 -0
- package/docs/zh-CN/reference/templates/SOUL.dev.md +83 -0
- package/docs/zh-CN/reference/templates/SOUL.md +49 -0
- package/docs/zh-CN/reference/templates/TOOLS.dev.md +31 -0
- package/docs/zh-CN/reference/templates/TOOLS.md +53 -0
- package/docs/zh-CN/reference/templates/USER.dev.md +25 -0
- package/docs/zh-CN/reference/templates/USER.md +30 -0
- package/docs/zh-CN/reference/test.md +57 -0
- package/docs/zh-CN/reference/transcript-hygiene.md +109 -0
- package/docs/zh-CN/scripts.md +35 -0
- package/docs/zh-CN/security/formal-verification.md +171 -0
- package/docs/zh-CN/start/docs-directory.md +70 -0
- package/docs/zh-CN/start/getting-started.md +206 -0
- package/docs/zh-CN/start/hubs.md +200 -0
- package/docs/zh-CN/start/lore.md +226 -0
- package/docs/zh-CN/start/onboarding.md +105 -0
- package/docs/zh-CN/start/openclaw.md +248 -0
- package/docs/zh-CN/start/pairing.md +89 -0
- package/docs/zh-CN/start/quickstart.md +88 -0
- package/docs/zh-CN/start/setup.md +153 -0
- package/docs/zh-CN/start/showcase.md +423 -0
- package/docs/zh-CN/start/wizard.md +331 -0
- package/docs/zh-CN/testing.md +375 -0
- package/docs/zh-CN/token-use.md +119 -0
- package/docs/zh-CN/tools/agent-send.md +59 -0
- package/docs/zh-CN/tools/apply-patch.md +57 -0
- package/docs/zh-CN/tools/browser-linux-troubleshooting.md +144 -0
- package/docs/zh-CN/tools/browser-login.md +75 -0
- package/docs/zh-CN/tools/browser.md +553 -0
- package/docs/zh-CN/tools/chrome-extension.md +183 -0
- package/docs/zh-CN/tools/clawhub.md +209 -0
- package/docs/zh-CN/tools/creating-skills.md +61 -0
- package/docs/zh-CN/tools/elevated.md +64 -0
- package/docs/zh-CN/tools/exec-approvals.md +234 -0
- package/docs/zh-CN/tools/exec.md +169 -0
- package/docs/zh-CN/tools/firecrawl.md +68 -0
- package/docs/zh-CN/tools/index.md +515 -0
- package/docs/zh-CN/tools/llm-task.md +117 -0
- package/docs/zh-CN/tools/lobster.md +349 -0
- package/docs/zh-CN/tools/reactions.md +29 -0
- package/docs/zh-CN/tools/skills-config.md +78 -0
- package/docs/zh-CN/tools/skills.md +279 -0
- package/docs/zh-CN/tools/slash-commands.md +205 -0
- package/docs/zh-CN/tools/subagents.md +156 -0
- package/docs/zh-CN/tools/thinking.md +80 -0
- package/docs/zh-CN/tools/web.md +257 -0
- package/docs/zh-CN/tts.md +375 -0
- package/docs/zh-CN/tui.md +166 -0
- package/docs/zh-CN/vps.md +47 -0
- package/docs/zh-CN/web/control-ui.md +191 -0
- package/docs/zh-CN/web/dashboard.md +53 -0
- package/docs/zh-CN/web/index.md +118 -0
- package/docs/zh-CN/web/webchat.md +56 -0
- package/extensions/blockchain/cryptoclaw.plugin.json +17 -0
- package/extensions/blockchain/index.ts +284 -0
- package/extensions/blockchain/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/blockchain/node_modules/.bin/tsc +21 -0
- package/extensions/blockchain/node_modules/.bin/tsserver +21 -0
- package/extensions/blockchain/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/extensions/blockchain/package.json +16 -0
- package/extensions/blockchain/src/evm/chains.ts +150 -0
- package/extensions/blockchain/src/evm/services/abi/erc1155.ts +128 -0
- package/extensions/blockchain/src/evm/services/abi/erc20.ts +96 -0
- package/extensions/blockchain/src/evm/services/abi/erc721.ts +130 -0
- package/extensions/blockchain/src/evm/services/abi/erc8004-identity.ts +158 -0
- package/extensions/blockchain/src/evm/services/abi/erc8004-reputation.ts +120 -0
- package/extensions/blockchain/src/evm/services/abi/router-v2.ts +115 -0
- package/extensions/blockchain/src/evm/services/abi/router-v3.ts +81 -0
- package/extensions/blockchain/src/evm/services/agent-identity-config.test.ts +117 -0
- package/extensions/blockchain/src/evm/services/agent-identity-config.ts +75 -0
- package/extensions/blockchain/src/evm/services/agent-identity-store.ts +37 -0
- package/extensions/blockchain/src/evm/services/agent-identity.ts +262 -0
- package/extensions/blockchain/src/evm/services/balance.ts +76 -0
- package/extensions/blockchain/src/evm/services/blocks.ts +25 -0
- package/extensions/blockchain/src/evm/services/clients.ts +48 -0
- package/extensions/blockchain/src/evm/services/contracts.ts +52 -0
- package/extensions/blockchain/src/evm/services/dex-config.test.ts +145 -0
- package/extensions/blockchain/src/evm/services/dex-config.ts +164 -0
- package/extensions/blockchain/src/evm/services/ens.ts +27 -0
- package/extensions/blockchain/src/evm/services/security.test.ts +127 -0
- package/extensions/blockchain/src/evm/services/security.ts +110 -0
- package/extensions/blockchain/src/evm/services/swap.test.ts +204 -0
- package/extensions/blockchain/src/evm/services/swap.ts +534 -0
- package/extensions/blockchain/src/evm/services/tokens.ts +118 -0
- package/extensions/blockchain/src/evm/services/transactions.ts +54 -0
- package/extensions/blockchain/src/evm/services/transfer.ts +174 -0
- package/extensions/blockchain/src/evm/services/utils.ts +18 -0
- package/extensions/blockchain/src/evm/tools/block-tools.ts +34 -0
- package/extensions/blockchain/src/evm/tools/contract-tools.ts +138 -0
- package/extensions/blockchain/src/evm/tools/identity-tools.ts +305 -0
- package/extensions/blockchain/src/evm/tools/network-tools.ts +58 -0
- package/extensions/blockchain/src/evm/tools/nft-tools.ts +103 -0
- package/extensions/blockchain/src/evm/tools/security-tools.ts +38 -0
- package/extensions/blockchain/src/evm/tools/swap-tools.ts +314 -0
- package/extensions/blockchain/src/evm/tools/token-tools.ts +167 -0
- package/extensions/blockchain/src/evm/tools/tx-tools.ts +73 -0
- package/extensions/blockchain/src/evm/tools/wallet-tools.ts +196 -0
- package/extensions/blockchain/src/tx-gate/confirmation.ts +55 -0
- package/extensions/blockchain/src/tx-gate/spending-limits.ts +94 -0
- package/extensions/blockchain/src/wallet/active-wallet.ts +27 -0
- package/extensions/blockchain/src/wallet/key-guard.test.ts +156 -0
- package/extensions/blockchain/src/wallet/key-guard.ts +80 -0
- package/extensions/blockchain/src/wallet/keystore.ts +88 -0
- package/extensions/blockchain/src/wallet/wallet-manager.test.ts +215 -0
- package/extensions/blockchain/src/wallet/wallet-manager.ts +212 -0
- package/extensions/copilot-proxy/README.md +24 -0
- package/extensions/copilot-proxy/index.ts +149 -0
- package/extensions/copilot-proxy/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/copilot-proxy/openclaw.plugin.json +9 -0
- package/extensions/copilot-proxy/package.json +14 -0
- package/extensions/diagnostics-otel/index.ts +15 -0
- package/extensions/diagnostics-otel/node_modules/.bin/acorn +21 -0
- package/extensions/diagnostics-otel/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/diagnostics-otel/openclaw.plugin.json +8 -0
- package/extensions/diagnostics-otel/package.json +27 -0
- package/extensions/diagnostics-otel/src/service.test.ts +227 -0
- package/extensions/diagnostics-otel/src/service.ts +635 -0
- package/extensions/discord/index.ts +17 -0
- package/extensions/discord/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/discord/openclaw.plugin.json +9 -0
- package/extensions/discord/package.json +14 -0
- package/extensions/discord/src/channel.ts +422 -0
- package/extensions/discord/src/runtime.ts +14 -0
- package/extensions/imessage/index.ts +17 -0
- package/extensions/imessage/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/imessage/openclaw.plugin.json +9 -0
- package/extensions/imessage/package.json +14 -0
- package/extensions/imessage/src/channel.ts +294 -0
- package/extensions/imessage/src/runtime.ts +14 -0
- package/extensions/memory-core/index.ts +38 -0
- package/extensions/memory-core/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/memory-core/openclaw.plugin.json +9 -0
- package/extensions/memory-core/package.json +17 -0
- package/extensions/memory-lancedb/config.ts +139 -0
- package/extensions/memory-lancedb/index.test.ts +295 -0
- package/extensions/memory-lancedb/index.ts +608 -0
- package/extensions/memory-lancedb/node_modules/.bin/arrow2csv +21 -0
- package/extensions/memory-lancedb/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/memory-lancedb/node_modules/.bin/openai +21 -0
- package/extensions/memory-lancedb/openclaw.plugin.json +60 -0
- package/extensions/memory-lancedb/package.json +19 -0
- package/extensions/nostr/CHANGELOG.md +80 -0
- package/extensions/nostr/README.md +136 -0
- package/extensions/nostr/index.ts +68 -0
- package/extensions/nostr/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/nostr/node_modules/.bin/tsc +21 -0
- package/extensions/nostr/node_modules/.bin/tsserver +21 -0
- package/extensions/nostr/openclaw.plugin.json +9 -0
- package/extensions/nostr/package.json +34 -0
- package/extensions/nostr/src/channel.test.ts +151 -0
- package/extensions/nostr/src/channel.ts +353 -0
- package/extensions/nostr/src/config-schema.ts +90 -0
- package/extensions/nostr/src/metrics.ts +478 -0
- package/extensions/nostr/src/nostr-bus.fuzz.test.ts +533 -0
- package/extensions/nostr/src/nostr-bus.integration.test.ts +448 -0
- package/extensions/nostr/src/nostr-bus.test.ts +199 -0
- package/extensions/nostr/src/nostr-bus.ts +715 -0
- package/extensions/nostr/src/nostr-profile-http.test.ts +378 -0
- package/extensions/nostr/src/nostr-profile-http.ts +519 -0
- package/extensions/nostr/src/nostr-profile-import.test.ts +119 -0
- package/extensions/nostr/src/nostr-profile-import.ts +262 -0
- package/extensions/nostr/src/nostr-profile.fuzz.test.ts +477 -0
- package/extensions/nostr/src/nostr-profile.test.ts +410 -0
- package/extensions/nostr/src/nostr-profile.ts +277 -0
- package/extensions/nostr/src/nostr-state-store.test.ts +131 -0
- package/extensions/nostr/src/nostr-state-store.ts +226 -0
- package/extensions/nostr/src/runtime.ts +14 -0
- package/extensions/nostr/src/seen-tracker.ts +303 -0
- package/extensions/nostr/src/types.test.ts +157 -0
- package/extensions/nostr/src/types.ts +101 -0
- package/extensions/nostr/test/setup.ts +5 -0
- package/extensions/signal/index.ts +17 -0
- package/extensions/signal/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/signal/openclaw.plugin.json +9 -0
- package/extensions/signal/package.json +14 -0
- package/extensions/signal/src/channel.ts +315 -0
- package/extensions/signal/src/runtime.ts +14 -0
- package/extensions/slack/index.ts +17 -0
- package/extensions/slack/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/slack/openclaw.plugin.json +9 -0
- package/extensions/slack/package.json +14 -0
- package/extensions/slack/src/channel.ts +604 -0
- package/extensions/slack/src/runtime.ts +14 -0
- package/extensions/telegram/index.ts +17 -0
- package/extensions/telegram/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/telegram/openclaw.plugin.json +9 -0
- package/extensions/telegram/package.json +14 -0
- package/extensions/telegram/src/channel.ts +482 -0
- package/extensions/telegram/src/runtime.ts +14 -0
- package/extensions/voice-call/CHANGELOG.md +115 -0
- package/extensions/voice-call/README.md +139 -0
- package/extensions/voice-call/index.ts +493 -0
- package/extensions/voice-call/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/voice-call/openclaw.plugin.json +559 -0
- package/extensions/voice-call/package.json +19 -0
- package/extensions/voice-call/src/allowlist.ts +19 -0
- package/extensions/voice-call/src/cli.ts +279 -0
- package/extensions/voice-call/src/config.test.ts +234 -0
- package/extensions/voice-call/src/config.ts +523 -0
- package/extensions/voice-call/src/core-bridge.ts +159 -0
- package/extensions/voice-call/src/manager/context.ts +21 -0
- package/extensions/voice-call/src/manager/events.ts +188 -0
- package/extensions/voice-call/src/manager/lookup.ts +35 -0
- package/extensions/voice-call/src/manager/outbound.ts +275 -0
- package/extensions/voice-call/src/manager/state.ts +48 -0
- package/extensions/voice-call/src/manager/store.ts +91 -0
- package/extensions/voice-call/src/manager/timers.ts +89 -0
- package/extensions/voice-call/src/manager/twiml.ts +9 -0
- package/extensions/voice-call/src/manager.test.ts +224 -0
- package/extensions/voice-call/src/manager.ts +887 -0
- package/extensions/voice-call/src/media-stream.test.ts +96 -0
- package/extensions/voice-call/src/media-stream.ts +411 -0
- package/extensions/voice-call/src/providers/base.ts +67 -0
- package/extensions/voice-call/src/providers/index.ts +10 -0
- package/extensions/voice-call/src/providers/mock.ts +165 -0
- package/extensions/voice-call/src/providers/plivo.test.ts +27 -0
- package/extensions/voice-call/src/providers/plivo.ts +515 -0
- package/extensions/voice-call/src/providers/stt-openai-realtime.ts +311 -0
- package/extensions/voice-call/src/providers/telnyx.ts +371 -0
- package/extensions/voice-call/src/providers/tts-openai.ts +259 -0
- package/extensions/voice-call/src/providers/twilio/api.ts +42 -0
- package/extensions/voice-call/src/providers/twilio/webhook.ts +32 -0
- package/extensions/voice-call/src/providers/twilio.test.ts +60 -0
- package/extensions/voice-call/src/providers/twilio.ts +626 -0
- package/extensions/voice-call/src/response-generator.ts +158 -0
- package/extensions/voice-call/src/runtime.ts +212 -0
- package/extensions/voice-call/src/telephony-audio.ts +90 -0
- package/extensions/voice-call/src/telephony-tts.ts +104 -0
- package/extensions/voice-call/src/tunnel.ts +314 -0
- package/extensions/voice-call/src/types.ts +272 -0
- package/extensions/voice-call/src/utils.ts +14 -0
- package/extensions/voice-call/src/voice-mapping.ts +67 -0
- package/extensions/voice-call/src/webhook-security.test.ts +377 -0
- package/extensions/voice-call/src/webhook-security.ts +689 -0
- package/extensions/voice-call/src/webhook.ts +491 -0
- package/extensions/whatsapp/index.ts +17 -0
- package/extensions/whatsapp/node_modules/.bin/cryptoclaw +21 -0
- package/extensions/whatsapp/openclaw.plugin.json +9 -0
- package/extensions/whatsapp/package.json +14 -0
- package/extensions/whatsapp/src/channel.ts +508 -0
- package/extensions/whatsapp/src/runtime.ts +14 -0
- package/package.json +242 -0
- package/skills/aave-bsc/SKILL.md +55 -0
- package/skills/agent-identity/SKILL.md +48 -0
- package/skills/bird/SKILL.md +224 -0
- package/skills/canvas/SKILL.md +204 -0
- package/skills/coding-agent/SKILL.md +285 -0
- package/skills/coingecko/SKILL.md +114 -0
- package/skills/contract-deployer/SKILL.md +42 -0
- package/skills/debank/SKILL.md +143 -0
- package/skills/defi-dashboard/SKILL.md +49 -0
- package/skills/defillama/SKILL.md +117 -0
- package/skills/discord/SKILL.md +578 -0
- package/skills/dune/SKILL.md +178 -0
- package/skills/etherscan/SKILL.md +117 -0
- package/skills/four-meme/SKILL.md +80 -0
- package/skills/gas-tracker/SKILL.md +43 -0
- package/skills/github/SKILL.md +77 -0
- package/skills/macro-calendar/SKILL.md +132 -0
- package/skills/market-data/SKILL.md +44 -0
- package/skills/nft-manager/SKILL.md +46 -0
- package/skills/portfolio-tracker/SKILL.md +55 -0
- package/skills/security-check/SKILL.md +153 -0
- package/skills/tmux/SKILL.md +135 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/token-swap/SKILL.md +71 -0
- package/skills/wallet-manager/SKILL.md +65 -0
- package/skills/whale-watcher/SKILL.md +43 -0
|
@@ -0,0 +1,4916 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-Cbj13DAv.js";
|
|
2
|
+
import { _ as resolveStateDir, c as resolveConfigPathCandidate, d as resolveGatewayPort, f as resolveIsNixMode, g as resolveOAuthPath, h as resolveOAuthDir, i as isNixMode, l as resolveDefaultConfigCandidates, m as resolveNewStateDir, n as DEFAULT_GATEWAY_PORT, o as resolveCanonicalConfigPath, p as resolveLegacyStateDirs, r as STATE_DIR, s as resolveConfigPath, t as CONFIG_PATH, u as resolveGatewayLockDir } from "./paths-D2ytuv-2.js";
|
|
3
|
+
import { F as CHANNEL_IDS, W as normalizeChatChannelId } from "./entry.js";
|
|
4
|
+
import { A as parseModelRef, ct as loadShellEnvFallback, dt as shouldEnableShellEnvFallback, ht as DEFAULT_CONTEXT_TOKENS, lt as resolveShellEnvFallbackTimeoutMs, ut as shouldDeferShellEnvFallback } from "./auth-profiles-DoD7YxsD.js";
|
|
5
|
+
import { c as normalizeAgentId, n as DEFAULT_AGENT_ID } from "./session-key-C-ig2pxJ.js";
|
|
6
|
+
import { m as resolveUserPath } from "./utils-7IMqr8vR.js";
|
|
7
|
+
import { c as resolveDefaultAgentId, s as resolveAgentWorkspaceDir } from "./agent-scope-DBl1We79.js";
|
|
8
|
+
import { c as resolveEnableState, l as resolveMemorySlotDecision, s as normalizePluginsConfig, t as loadPluginManifestRegistry } from "./manifest-registry-BdTzIlta.js";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import os from "node:os";
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import JSON5 from "json5";
|
|
14
|
+
import crypto from "node:crypto";
|
|
15
|
+
import AjvPkg from "ajv";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
|
|
18
|
+
//#region src/version.ts
|
|
19
|
+
const CORE_PACKAGE_NAME = "cryptoclaw";
|
|
20
|
+
const PACKAGE_JSON_CANDIDATES = [
|
|
21
|
+
"../package.json",
|
|
22
|
+
"../../package.json",
|
|
23
|
+
"../../../package.json",
|
|
24
|
+
"./package.json"
|
|
25
|
+
];
|
|
26
|
+
const BUILD_INFO_CANDIDATES = [
|
|
27
|
+
"../build-info.json",
|
|
28
|
+
"../../build-info.json",
|
|
29
|
+
"./build-info.json"
|
|
30
|
+
];
|
|
31
|
+
function readVersionFromJsonCandidates(moduleUrl, candidates, opts = {}) {
|
|
32
|
+
try {
|
|
33
|
+
const require = createRequire(moduleUrl);
|
|
34
|
+
for (const candidate of candidates) try {
|
|
35
|
+
const parsed = require(candidate);
|
|
36
|
+
const version = parsed.version?.trim();
|
|
37
|
+
if (!version) continue;
|
|
38
|
+
if (opts.requirePackageName && parsed.name !== CORE_PACKAGE_NAME) continue;
|
|
39
|
+
return version;
|
|
40
|
+
} catch {}
|
|
41
|
+
return null;
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function readVersionFromPackageJsonForModuleUrl(moduleUrl) {
|
|
47
|
+
return readVersionFromJsonCandidates(moduleUrl, PACKAGE_JSON_CANDIDATES, { requirePackageName: true });
|
|
48
|
+
}
|
|
49
|
+
function readVersionFromBuildInfoForModuleUrl(moduleUrl) {
|
|
50
|
+
return readVersionFromJsonCandidates(moduleUrl, BUILD_INFO_CANDIDATES);
|
|
51
|
+
}
|
|
52
|
+
function resolveVersionFromModuleUrl(moduleUrl) {
|
|
53
|
+
return readVersionFromPackageJsonForModuleUrl(moduleUrl) || readVersionFromBuildInfoForModuleUrl(moduleUrl);
|
|
54
|
+
}
|
|
55
|
+
const VERSION = typeof __CRYPTOCLAW_VERSION__ === "string" && __CRYPTOCLAW_VERSION__ || process.env.CRYPTOCLAW_BUNDLED_VERSION || resolveVersionFromModuleUrl(import.meta.url) || "0.0.0";
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/config/agent-dirs.ts
|
|
59
|
+
var DuplicateAgentDirError = class extends Error {
|
|
60
|
+
constructor(duplicates) {
|
|
61
|
+
super(formatDuplicateAgentDirError(duplicates));
|
|
62
|
+
this.name = "DuplicateAgentDirError";
|
|
63
|
+
this.duplicates = duplicates;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
function canonicalizeAgentDir(agentDir) {
|
|
67
|
+
const resolved = path.resolve(agentDir);
|
|
68
|
+
if (process.platform === "darwin" || process.platform === "win32") return resolved.toLowerCase();
|
|
69
|
+
return resolved;
|
|
70
|
+
}
|
|
71
|
+
function collectReferencedAgentIds(cfg) {
|
|
72
|
+
const ids = /* @__PURE__ */ new Set();
|
|
73
|
+
const agents = Array.isArray(cfg.agents?.list) ? cfg.agents?.list : [];
|
|
74
|
+
const defaultAgentId = agents.find((agent) => agent?.default)?.id ?? agents[0]?.id ?? DEFAULT_AGENT_ID;
|
|
75
|
+
ids.add(normalizeAgentId(defaultAgentId));
|
|
76
|
+
for (const entry of agents) if (entry?.id) ids.add(normalizeAgentId(entry.id));
|
|
77
|
+
const bindings = cfg.bindings;
|
|
78
|
+
if (Array.isArray(bindings)) for (const binding of bindings) {
|
|
79
|
+
const id = binding?.agentId;
|
|
80
|
+
if (typeof id === "string" && id.trim()) ids.add(normalizeAgentId(id));
|
|
81
|
+
}
|
|
82
|
+
return [...ids];
|
|
83
|
+
}
|
|
84
|
+
function resolveEffectiveAgentDir(cfg, agentId, deps) {
|
|
85
|
+
const id = normalizeAgentId(agentId);
|
|
86
|
+
const trimmed = (Array.isArray(cfg.agents?.list) ? cfg.agents?.list.find((agent) => normalizeAgentId(agent.id) === id)?.agentDir : void 0)?.trim();
|
|
87
|
+
if (trimmed) return resolveUserPath(trimmed);
|
|
88
|
+
const root = resolveStateDir(deps?.env ?? process.env, deps?.homedir ?? os.homedir);
|
|
89
|
+
return path.join(root, "agents", id, "agent");
|
|
90
|
+
}
|
|
91
|
+
function findDuplicateAgentDirs(cfg, deps) {
|
|
92
|
+
const byDir = /* @__PURE__ */ new Map();
|
|
93
|
+
for (const agentId of collectReferencedAgentIds(cfg)) {
|
|
94
|
+
const agentDir = resolveEffectiveAgentDir(cfg, agentId, deps);
|
|
95
|
+
const key = canonicalizeAgentDir(agentDir);
|
|
96
|
+
const entry = byDir.get(key);
|
|
97
|
+
if (entry) entry.agentIds.push(agentId);
|
|
98
|
+
else byDir.set(key, {
|
|
99
|
+
agentDir,
|
|
100
|
+
agentIds: [agentId]
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return [...byDir.values()].filter((v) => v.agentIds.length > 1);
|
|
104
|
+
}
|
|
105
|
+
function formatDuplicateAgentDirError(dups) {
|
|
106
|
+
return [
|
|
107
|
+
"Duplicate agentDir detected (multi-agent config).",
|
|
108
|
+
"Each agent must have a unique agentDir; sharing it causes auth/session state collisions and token invalidation.",
|
|
109
|
+
"",
|
|
110
|
+
"Conflicts:",
|
|
111
|
+
...dups.map((d) => `- ${d.agentDir}: ${d.agentIds.map((id) => `"${id}"`).join(", ")}`),
|
|
112
|
+
"",
|
|
113
|
+
"Fix: remove the shared agents.list[].agentDir override (or give each agent its own directory).",
|
|
114
|
+
"If you want to share credentials, copy auth-profiles.json instead of sharing the entire agentDir."
|
|
115
|
+
].join("\n");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/config/agent-limits.ts
|
|
120
|
+
const DEFAULT_AGENT_MAX_CONCURRENT = 4;
|
|
121
|
+
const DEFAULT_SUBAGENT_MAX_CONCURRENT = 8;
|
|
122
|
+
function resolveAgentMaxConcurrent(cfg) {
|
|
123
|
+
const raw = cfg?.agents?.defaults?.maxConcurrent;
|
|
124
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return Math.max(1, Math.floor(raw));
|
|
125
|
+
return DEFAULT_AGENT_MAX_CONCURRENT;
|
|
126
|
+
}
|
|
127
|
+
function resolveSubagentMaxConcurrent(cfg) {
|
|
128
|
+
const raw = cfg?.agents?.defaults?.subagents?.maxConcurrent;
|
|
129
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return Math.max(1, Math.floor(raw));
|
|
130
|
+
return DEFAULT_SUBAGENT_MAX_CONCURRENT;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/config/talk.ts
|
|
135
|
+
function readTalkApiKeyFromProfile(deps = {}) {
|
|
136
|
+
const fsImpl = deps.fs ?? fs;
|
|
137
|
+
const osImpl = deps.os ?? os;
|
|
138
|
+
const pathImpl = deps.path ?? path;
|
|
139
|
+
const home = osImpl.homedir();
|
|
140
|
+
const candidates = [
|
|
141
|
+
".profile",
|
|
142
|
+
".zprofile",
|
|
143
|
+
".zshrc",
|
|
144
|
+
".bashrc"
|
|
145
|
+
].map((name) => pathImpl.join(home, name));
|
|
146
|
+
for (const candidate of candidates) {
|
|
147
|
+
if (!fsImpl.existsSync(candidate)) continue;
|
|
148
|
+
try {
|
|
149
|
+
const value = fsImpl.readFileSync(candidate, "utf-8").match(/(?:^|\n)\s*(?:export\s+)?ELEVENLABS_API_KEY\s*=\s*["']?([^\n"']+)["']?/)?.[1]?.trim();
|
|
150
|
+
if (value) return value;
|
|
151
|
+
} catch {}
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
function resolveTalkApiKey(env = process.env, deps = {}) {
|
|
156
|
+
const envValue = (env.ELEVENLABS_API_KEY ?? "").trim();
|
|
157
|
+
if (envValue) return envValue;
|
|
158
|
+
return readTalkApiKeyFromProfile(deps);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/config/defaults.ts
|
|
163
|
+
let defaultWarnState = { warned: false };
|
|
164
|
+
const DEFAULT_MODEL_ALIASES = {
|
|
165
|
+
opus: "anthropic/claude-opus-4-6",
|
|
166
|
+
sonnet: "anthropic/claude-sonnet-4-5",
|
|
167
|
+
gpt: "openai/gpt-5.2",
|
|
168
|
+
"gpt-mini": "openai/gpt-5-mini",
|
|
169
|
+
gemini: "google/gemini-3-pro-preview",
|
|
170
|
+
"gemini-flash": "google/gemini-3-flash-preview"
|
|
171
|
+
};
|
|
172
|
+
const DEFAULT_MODEL_COST = {
|
|
173
|
+
input: 0,
|
|
174
|
+
output: 0,
|
|
175
|
+
cacheRead: 0,
|
|
176
|
+
cacheWrite: 0
|
|
177
|
+
};
|
|
178
|
+
const DEFAULT_MODEL_INPUT = ["text"];
|
|
179
|
+
const DEFAULT_MODEL_MAX_TOKENS = 8192;
|
|
180
|
+
function isPositiveNumber(value) {
|
|
181
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
182
|
+
}
|
|
183
|
+
function resolveModelCost(raw) {
|
|
184
|
+
return {
|
|
185
|
+
input: typeof raw?.input === "number" ? raw.input : DEFAULT_MODEL_COST.input,
|
|
186
|
+
output: typeof raw?.output === "number" ? raw.output : DEFAULT_MODEL_COST.output,
|
|
187
|
+
cacheRead: typeof raw?.cacheRead === "number" ? raw.cacheRead : DEFAULT_MODEL_COST.cacheRead,
|
|
188
|
+
cacheWrite: typeof raw?.cacheWrite === "number" ? raw.cacheWrite : DEFAULT_MODEL_COST.cacheWrite
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function resolveAnthropicDefaultAuthMode(cfg) {
|
|
192
|
+
const profiles = cfg.auth?.profiles ?? {};
|
|
193
|
+
const anthropicProfiles = Object.entries(profiles).filter(([, profile]) => profile?.provider === "anthropic");
|
|
194
|
+
const order = cfg.auth?.order?.anthropic ?? [];
|
|
195
|
+
for (const profileId of order) {
|
|
196
|
+
const entry = profiles[profileId];
|
|
197
|
+
if (!entry || entry.provider !== "anthropic") continue;
|
|
198
|
+
if (entry.mode === "api_key") return "api_key";
|
|
199
|
+
if (entry.mode === "oauth" || entry.mode === "token") return "oauth";
|
|
200
|
+
}
|
|
201
|
+
const hasApiKey = anthropicProfiles.some(([, profile]) => profile?.mode === "api_key");
|
|
202
|
+
const hasOauth = anthropicProfiles.some(([, profile]) => profile?.mode === "oauth" || profile?.mode === "token");
|
|
203
|
+
if (hasApiKey && !hasOauth) return "api_key";
|
|
204
|
+
if (hasOauth && !hasApiKey) return "oauth";
|
|
205
|
+
if (process.env.ANTHROPIC_OAUTH_TOKEN?.trim()) return "oauth";
|
|
206
|
+
if (process.env.ANTHROPIC_API_KEY?.trim()) return "api_key";
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
function resolvePrimaryModelRef(raw) {
|
|
210
|
+
if (!raw || typeof raw !== "string") return null;
|
|
211
|
+
const trimmed = raw.trim();
|
|
212
|
+
if (!trimmed) return null;
|
|
213
|
+
return DEFAULT_MODEL_ALIASES[trimmed.toLowerCase()] ?? trimmed;
|
|
214
|
+
}
|
|
215
|
+
function applyMessageDefaults(cfg) {
|
|
216
|
+
const messages = cfg.messages;
|
|
217
|
+
if (messages?.ackReactionScope !== void 0) return cfg;
|
|
218
|
+
const nextMessages = messages ? { ...messages } : {};
|
|
219
|
+
nextMessages.ackReactionScope = "group-mentions";
|
|
220
|
+
return {
|
|
221
|
+
...cfg,
|
|
222
|
+
messages: nextMessages
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function applySessionDefaults(cfg, options = {}) {
|
|
226
|
+
const session = cfg.session;
|
|
227
|
+
if (!session || session.mainKey === void 0) return cfg;
|
|
228
|
+
const trimmed = session.mainKey.trim();
|
|
229
|
+
const warn = options.warn ?? console.warn;
|
|
230
|
+
const warnState = options.warnState ?? defaultWarnState;
|
|
231
|
+
const next = {
|
|
232
|
+
...cfg,
|
|
233
|
+
session: {
|
|
234
|
+
...session,
|
|
235
|
+
mainKey: "main"
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
if (trimmed && trimmed !== "main" && !warnState.warned) {
|
|
239
|
+
warnState.warned = true;
|
|
240
|
+
warn("session.mainKey is ignored; main session is always \"main\".");
|
|
241
|
+
}
|
|
242
|
+
return next;
|
|
243
|
+
}
|
|
244
|
+
function applyTalkApiKey(config) {
|
|
245
|
+
const resolved = resolveTalkApiKey();
|
|
246
|
+
if (!resolved) return config;
|
|
247
|
+
if (config.talk?.apiKey?.trim()) return config;
|
|
248
|
+
return {
|
|
249
|
+
...config,
|
|
250
|
+
talk: {
|
|
251
|
+
...config.talk,
|
|
252
|
+
apiKey: resolved
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function applyModelDefaults(cfg) {
|
|
257
|
+
let mutated = false;
|
|
258
|
+
let nextCfg = cfg;
|
|
259
|
+
const providerConfig = nextCfg.models?.providers;
|
|
260
|
+
if (providerConfig) {
|
|
261
|
+
const nextProviders = { ...providerConfig };
|
|
262
|
+
for (const [providerId, provider] of Object.entries(providerConfig)) {
|
|
263
|
+
const models = provider.models;
|
|
264
|
+
if (!Array.isArray(models) || models.length === 0) continue;
|
|
265
|
+
let providerMutated = false;
|
|
266
|
+
const nextModels = models.map((model) => {
|
|
267
|
+
const raw = model;
|
|
268
|
+
let modelMutated = false;
|
|
269
|
+
const reasoning = typeof raw.reasoning === "boolean" ? raw.reasoning : false;
|
|
270
|
+
if (raw.reasoning !== reasoning) modelMutated = true;
|
|
271
|
+
const input = raw.input ?? [...DEFAULT_MODEL_INPUT];
|
|
272
|
+
if (raw.input === void 0) modelMutated = true;
|
|
273
|
+
const cost = resolveModelCost(raw.cost);
|
|
274
|
+
if (!raw.cost || raw.cost.input !== cost.input || raw.cost.output !== cost.output || raw.cost.cacheRead !== cost.cacheRead || raw.cost.cacheWrite !== cost.cacheWrite) modelMutated = true;
|
|
275
|
+
const contextWindow = isPositiveNumber(raw.contextWindow) ? raw.contextWindow : DEFAULT_CONTEXT_TOKENS;
|
|
276
|
+
if (raw.contextWindow !== contextWindow) modelMutated = true;
|
|
277
|
+
const defaultMaxTokens = Math.min(DEFAULT_MODEL_MAX_TOKENS, contextWindow);
|
|
278
|
+
const maxTokens = isPositiveNumber(raw.maxTokens) ? raw.maxTokens : defaultMaxTokens;
|
|
279
|
+
if (raw.maxTokens !== maxTokens) modelMutated = true;
|
|
280
|
+
if (!modelMutated) return model;
|
|
281
|
+
providerMutated = true;
|
|
282
|
+
return {
|
|
283
|
+
...raw,
|
|
284
|
+
reasoning,
|
|
285
|
+
input,
|
|
286
|
+
cost,
|
|
287
|
+
contextWindow,
|
|
288
|
+
maxTokens
|
|
289
|
+
};
|
|
290
|
+
});
|
|
291
|
+
if (!providerMutated) continue;
|
|
292
|
+
nextProviders[providerId] = {
|
|
293
|
+
...provider,
|
|
294
|
+
models: nextModels
|
|
295
|
+
};
|
|
296
|
+
mutated = true;
|
|
297
|
+
}
|
|
298
|
+
if (mutated) nextCfg = {
|
|
299
|
+
...nextCfg,
|
|
300
|
+
models: {
|
|
301
|
+
...nextCfg.models,
|
|
302
|
+
providers: nextProviders
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
const existingAgent = nextCfg.agents?.defaults;
|
|
307
|
+
if (!existingAgent) return mutated ? nextCfg : cfg;
|
|
308
|
+
const existingModels = existingAgent.models ?? {};
|
|
309
|
+
if (Object.keys(existingModels).length === 0) return mutated ? nextCfg : cfg;
|
|
310
|
+
const nextModels = { ...existingModels };
|
|
311
|
+
for (const [alias, target] of Object.entries(DEFAULT_MODEL_ALIASES)) {
|
|
312
|
+
const entry = nextModels[target];
|
|
313
|
+
if (!entry) continue;
|
|
314
|
+
if (entry.alias !== void 0) continue;
|
|
315
|
+
nextModels[target] = {
|
|
316
|
+
...entry,
|
|
317
|
+
alias
|
|
318
|
+
};
|
|
319
|
+
mutated = true;
|
|
320
|
+
}
|
|
321
|
+
if (!mutated) return cfg;
|
|
322
|
+
return {
|
|
323
|
+
...nextCfg,
|
|
324
|
+
agents: {
|
|
325
|
+
...nextCfg.agents,
|
|
326
|
+
defaults: {
|
|
327
|
+
...existingAgent,
|
|
328
|
+
models: nextModels
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function applyAgentDefaults(cfg) {
|
|
334
|
+
const agents = cfg.agents;
|
|
335
|
+
const defaults = agents?.defaults;
|
|
336
|
+
const hasMax = typeof defaults?.maxConcurrent === "number" && Number.isFinite(defaults.maxConcurrent);
|
|
337
|
+
const hasSubMax = typeof defaults?.subagents?.maxConcurrent === "number" && Number.isFinite(defaults.subagents.maxConcurrent);
|
|
338
|
+
if (hasMax && hasSubMax) return cfg;
|
|
339
|
+
let mutated = false;
|
|
340
|
+
const nextDefaults = defaults ? { ...defaults } : {};
|
|
341
|
+
if (!hasMax) {
|
|
342
|
+
nextDefaults.maxConcurrent = DEFAULT_AGENT_MAX_CONCURRENT;
|
|
343
|
+
mutated = true;
|
|
344
|
+
}
|
|
345
|
+
const nextSubagents = defaults?.subagents ? { ...defaults.subagents } : {};
|
|
346
|
+
if (!hasSubMax) {
|
|
347
|
+
nextSubagents.maxConcurrent = DEFAULT_SUBAGENT_MAX_CONCURRENT;
|
|
348
|
+
mutated = true;
|
|
349
|
+
}
|
|
350
|
+
if (!mutated) return cfg;
|
|
351
|
+
return {
|
|
352
|
+
...cfg,
|
|
353
|
+
agents: {
|
|
354
|
+
...agents,
|
|
355
|
+
defaults: {
|
|
356
|
+
...nextDefaults,
|
|
357
|
+
subagents: nextSubagents
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function applyLoggingDefaults(cfg) {
|
|
363
|
+
const logging = cfg.logging;
|
|
364
|
+
if (!logging) return cfg;
|
|
365
|
+
if (logging.redactSensitive) return cfg;
|
|
366
|
+
return {
|
|
367
|
+
...cfg,
|
|
368
|
+
logging: {
|
|
369
|
+
...logging,
|
|
370
|
+
redactSensitive: "tools"
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function applyContextPruningDefaults(cfg) {
|
|
375
|
+
const defaults = cfg.agents?.defaults;
|
|
376
|
+
if (!defaults) return cfg;
|
|
377
|
+
const authMode = resolveAnthropicDefaultAuthMode(cfg);
|
|
378
|
+
if (!authMode) return cfg;
|
|
379
|
+
let mutated = false;
|
|
380
|
+
const nextDefaults = { ...defaults };
|
|
381
|
+
const contextPruning = defaults.contextPruning ?? {};
|
|
382
|
+
const heartbeat = defaults.heartbeat ?? {};
|
|
383
|
+
if (defaults.contextPruning?.mode === void 0) {
|
|
384
|
+
nextDefaults.contextPruning = {
|
|
385
|
+
...contextPruning,
|
|
386
|
+
mode: "cache-ttl",
|
|
387
|
+
ttl: defaults.contextPruning?.ttl ?? "1h"
|
|
388
|
+
};
|
|
389
|
+
mutated = true;
|
|
390
|
+
}
|
|
391
|
+
if (defaults.heartbeat?.every === void 0) {
|
|
392
|
+
nextDefaults.heartbeat = {
|
|
393
|
+
...heartbeat,
|
|
394
|
+
every: authMode === "oauth" ? "1h" : "30m"
|
|
395
|
+
};
|
|
396
|
+
mutated = true;
|
|
397
|
+
}
|
|
398
|
+
if (authMode === "api_key") {
|
|
399
|
+
const nextModels = defaults.models ? { ...defaults.models } : {};
|
|
400
|
+
let modelsMutated = false;
|
|
401
|
+
for (const [key, entry] of Object.entries(nextModels)) {
|
|
402
|
+
const parsed = parseModelRef(key, "anthropic");
|
|
403
|
+
if (!parsed || parsed.provider !== "anthropic") continue;
|
|
404
|
+
const current = entry ?? {};
|
|
405
|
+
const params = current.params ?? {};
|
|
406
|
+
if (typeof params.cacheRetention === "string") continue;
|
|
407
|
+
nextModels[key] = {
|
|
408
|
+
...current,
|
|
409
|
+
params: {
|
|
410
|
+
...params,
|
|
411
|
+
cacheRetention: "short"
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
modelsMutated = true;
|
|
415
|
+
}
|
|
416
|
+
const primary = resolvePrimaryModelRef(defaults.model?.primary ?? void 0);
|
|
417
|
+
if (primary) {
|
|
418
|
+
const parsedPrimary = parseModelRef(primary, "anthropic");
|
|
419
|
+
if (parsedPrimary?.provider === "anthropic") {
|
|
420
|
+
const key = `${parsedPrimary.provider}/${parsedPrimary.model}`;
|
|
421
|
+
const current = nextModels[key] ?? {};
|
|
422
|
+
const params = current.params ?? {};
|
|
423
|
+
if (typeof params.cacheRetention !== "string") {
|
|
424
|
+
nextModels[key] = {
|
|
425
|
+
...current,
|
|
426
|
+
params: {
|
|
427
|
+
...params,
|
|
428
|
+
cacheRetention: "short"
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
modelsMutated = true;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (modelsMutated) {
|
|
436
|
+
nextDefaults.models = nextModels;
|
|
437
|
+
mutated = true;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (!mutated) return cfg;
|
|
441
|
+
return {
|
|
442
|
+
...cfg,
|
|
443
|
+
agents: {
|
|
444
|
+
...cfg.agents,
|
|
445
|
+
defaults: nextDefaults
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function applyCompactionDefaults(cfg) {
|
|
450
|
+
const defaults = cfg.agents?.defaults;
|
|
451
|
+
if (!defaults) return cfg;
|
|
452
|
+
const compaction = defaults?.compaction;
|
|
453
|
+
if (compaction?.mode) return cfg;
|
|
454
|
+
return {
|
|
455
|
+
...cfg,
|
|
456
|
+
agents: {
|
|
457
|
+
...cfg.agents,
|
|
458
|
+
defaults: {
|
|
459
|
+
...defaults,
|
|
460
|
+
compaction: {
|
|
461
|
+
...compaction,
|
|
462
|
+
mode: "safeguard"
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
//#endregion
|
|
470
|
+
//#region src/config/env-substitution.ts
|
|
471
|
+
/**
|
|
472
|
+
* Environment variable substitution for config values.
|
|
473
|
+
*
|
|
474
|
+
* Supports `${VAR_NAME}` syntax in string values, substituted at config load time.
|
|
475
|
+
* - Only uppercase env vars are matched: `[A-Z_][A-Z0-9_]*`
|
|
476
|
+
* - Escape with `$${}` to output literal `${}`
|
|
477
|
+
* - Missing env vars throw `MissingEnvVarError` with context
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* ```json5
|
|
481
|
+
* {
|
|
482
|
+
* models: {
|
|
483
|
+
* providers: {
|
|
484
|
+
* "vercel-gateway": {
|
|
485
|
+
* apiKey: "${VERCEL_GATEWAY_API_KEY}"
|
|
486
|
+
* }
|
|
487
|
+
* }
|
|
488
|
+
* }
|
|
489
|
+
* }
|
|
490
|
+
* ```
|
|
491
|
+
*/
|
|
492
|
+
const ENV_VAR_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
|
|
493
|
+
var MissingEnvVarError = class extends Error {
|
|
494
|
+
constructor(varName, configPath) {
|
|
495
|
+
super(`Missing env var "${varName}" referenced at config path: ${configPath}`);
|
|
496
|
+
this.varName = varName;
|
|
497
|
+
this.configPath = configPath;
|
|
498
|
+
this.name = "MissingEnvVarError";
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
function isPlainObject$4(value) {
|
|
502
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
|
|
503
|
+
}
|
|
504
|
+
function substituteString(value, env, configPath) {
|
|
505
|
+
if (!value.includes("$")) return value;
|
|
506
|
+
const chunks = [];
|
|
507
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
508
|
+
const char = value[i];
|
|
509
|
+
if (char !== "$") {
|
|
510
|
+
chunks.push(char);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
const next = value[i + 1];
|
|
514
|
+
const afterNext = value[i + 2];
|
|
515
|
+
if (next === "$" && afterNext === "{") {
|
|
516
|
+
const start = i + 3;
|
|
517
|
+
const end = value.indexOf("}", start);
|
|
518
|
+
if (end !== -1) {
|
|
519
|
+
const name = value.slice(start, end);
|
|
520
|
+
if (ENV_VAR_NAME_PATTERN.test(name)) {
|
|
521
|
+
chunks.push(`\${${name}}`);
|
|
522
|
+
i = end;
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (next === "{") {
|
|
528
|
+
const start = i + 2;
|
|
529
|
+
const end = value.indexOf("}", start);
|
|
530
|
+
if (end !== -1) {
|
|
531
|
+
const name = value.slice(start, end);
|
|
532
|
+
if (ENV_VAR_NAME_PATTERN.test(name)) {
|
|
533
|
+
const envValue = env[name];
|
|
534
|
+
if (envValue === void 0 || envValue === "") throw new MissingEnvVarError(name, configPath);
|
|
535
|
+
chunks.push(envValue);
|
|
536
|
+
i = end;
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
chunks.push(char);
|
|
542
|
+
}
|
|
543
|
+
return chunks.join("");
|
|
544
|
+
}
|
|
545
|
+
function substituteAny(value, env, path) {
|
|
546
|
+
if (typeof value === "string") return substituteString(value, env, path);
|
|
547
|
+
if (Array.isArray(value)) return value.map((item, index) => substituteAny(item, env, `${path}[${index}]`));
|
|
548
|
+
if (isPlainObject$4(value)) {
|
|
549
|
+
const result = {};
|
|
550
|
+
for (const [key, val] of Object.entries(value)) result[key] = substituteAny(val, env, path ? `${path}.${key}` : key);
|
|
551
|
+
return result;
|
|
552
|
+
}
|
|
553
|
+
return value;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Resolves `${VAR_NAME}` environment variable references in config values.
|
|
557
|
+
*
|
|
558
|
+
* @param obj - The parsed config object (after JSON5 parse and $include resolution)
|
|
559
|
+
* @param env - Environment variables to use for substitution (defaults to process.env)
|
|
560
|
+
* @returns The config object with env vars substituted
|
|
561
|
+
* @throws {MissingEnvVarError} If a referenced env var is not set or empty
|
|
562
|
+
*/
|
|
563
|
+
function resolveConfigEnvVars(obj, env = process.env) {
|
|
564
|
+
return substituteAny(obj, env, "");
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
//#endregion
|
|
568
|
+
//#region src/config/env-vars.ts
|
|
569
|
+
function collectConfigEnvVars(cfg) {
|
|
570
|
+
const envConfig = cfg?.env;
|
|
571
|
+
if (!envConfig) return {};
|
|
572
|
+
const entries = {};
|
|
573
|
+
if (envConfig.vars) for (const [key, value] of Object.entries(envConfig.vars)) {
|
|
574
|
+
if (!value) continue;
|
|
575
|
+
entries[key] = value;
|
|
576
|
+
}
|
|
577
|
+
for (const [key, value] of Object.entries(envConfig)) {
|
|
578
|
+
if (key === "shellEnv" || key === "vars") continue;
|
|
579
|
+
if (typeof value !== "string" || !value.trim()) continue;
|
|
580
|
+
entries[key] = value;
|
|
581
|
+
}
|
|
582
|
+
return entries;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
//#endregion
|
|
586
|
+
//#region src/config/includes.ts
|
|
587
|
+
/**
|
|
588
|
+
* Config includes: $include directive for modular configs
|
|
589
|
+
*
|
|
590
|
+
* @example
|
|
591
|
+
* ```json5
|
|
592
|
+
* {
|
|
593
|
+
* "$include": "./base.json5", // single file
|
|
594
|
+
* "$include": ["./a.json5", "./b.json5"] // merge multiple
|
|
595
|
+
* }
|
|
596
|
+
* ```
|
|
597
|
+
*/
|
|
598
|
+
const INCLUDE_KEY = "$include";
|
|
599
|
+
const MAX_INCLUDE_DEPTH = 10;
|
|
600
|
+
var ConfigIncludeError = class extends Error {
|
|
601
|
+
constructor(message, includePath, cause) {
|
|
602
|
+
super(message);
|
|
603
|
+
this.includePath = includePath;
|
|
604
|
+
this.cause = cause;
|
|
605
|
+
this.name = "ConfigIncludeError";
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
var CircularIncludeError = class extends ConfigIncludeError {
|
|
609
|
+
constructor(chain) {
|
|
610
|
+
super(`Circular include detected: ${chain.join(" -> ")}`, chain[chain.length - 1]);
|
|
611
|
+
this.chain = chain;
|
|
612
|
+
this.name = "CircularIncludeError";
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
function isPlainObject$3(value) {
|
|
616
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
|
|
617
|
+
}
|
|
618
|
+
/** Deep merge: arrays concatenate, objects merge recursively, primitives: source wins */
|
|
619
|
+
function deepMerge(target, source) {
|
|
620
|
+
if (Array.isArray(target) && Array.isArray(source)) return [...target, ...source];
|
|
621
|
+
if (isPlainObject$3(target) && isPlainObject$3(source)) {
|
|
622
|
+
const result = { ...target };
|
|
623
|
+
for (const key of Object.keys(source)) result[key] = key in result ? deepMerge(result[key], source[key]) : source[key];
|
|
624
|
+
return result;
|
|
625
|
+
}
|
|
626
|
+
return source;
|
|
627
|
+
}
|
|
628
|
+
var IncludeProcessor = class IncludeProcessor {
|
|
629
|
+
constructor(basePath, resolver) {
|
|
630
|
+
this.basePath = basePath;
|
|
631
|
+
this.resolver = resolver;
|
|
632
|
+
this.visited = /* @__PURE__ */ new Set();
|
|
633
|
+
this.depth = 0;
|
|
634
|
+
this.visited.add(path.normalize(basePath));
|
|
635
|
+
}
|
|
636
|
+
process(obj) {
|
|
637
|
+
if (Array.isArray(obj)) return obj.map((item) => this.process(item));
|
|
638
|
+
if (!isPlainObject$3(obj)) return obj;
|
|
639
|
+
if (!(INCLUDE_KEY in obj)) return this.processObject(obj);
|
|
640
|
+
return this.processInclude(obj);
|
|
641
|
+
}
|
|
642
|
+
processObject(obj) {
|
|
643
|
+
const result = {};
|
|
644
|
+
for (const [key, value] of Object.entries(obj)) result[key] = this.process(value);
|
|
645
|
+
return result;
|
|
646
|
+
}
|
|
647
|
+
processInclude(obj) {
|
|
648
|
+
const includeValue = obj[INCLUDE_KEY];
|
|
649
|
+
const otherKeys = Object.keys(obj).filter((k) => k !== INCLUDE_KEY);
|
|
650
|
+
const included = this.resolveInclude(includeValue);
|
|
651
|
+
if (otherKeys.length === 0) return included;
|
|
652
|
+
if (!isPlainObject$3(included)) throw new ConfigIncludeError("Sibling keys require included content to be an object", typeof includeValue === "string" ? includeValue : INCLUDE_KEY);
|
|
653
|
+
const rest = {};
|
|
654
|
+
for (const key of otherKeys) rest[key] = this.process(obj[key]);
|
|
655
|
+
return deepMerge(included, rest);
|
|
656
|
+
}
|
|
657
|
+
resolveInclude(value) {
|
|
658
|
+
if (typeof value === "string") return this.loadFile(value);
|
|
659
|
+
if (Array.isArray(value)) return value.reduce((merged, item) => {
|
|
660
|
+
if (typeof item !== "string") throw new ConfigIncludeError(`Invalid $include array item: expected string, got ${typeof item}`, String(item));
|
|
661
|
+
return deepMerge(merged, this.loadFile(item));
|
|
662
|
+
}, {});
|
|
663
|
+
throw new ConfigIncludeError(`Invalid $include value: expected string or array of strings, got ${typeof value}`, String(value));
|
|
664
|
+
}
|
|
665
|
+
loadFile(includePath) {
|
|
666
|
+
const resolvedPath = this.resolvePath(includePath);
|
|
667
|
+
this.checkCircular(resolvedPath);
|
|
668
|
+
this.checkDepth(includePath);
|
|
669
|
+
const raw = this.readFile(includePath, resolvedPath);
|
|
670
|
+
const parsed = this.parseFile(includePath, resolvedPath, raw);
|
|
671
|
+
return this.processNested(resolvedPath, parsed);
|
|
672
|
+
}
|
|
673
|
+
resolvePath(includePath) {
|
|
674
|
+
const resolved = path.isAbsolute(includePath) ? includePath : path.resolve(path.dirname(this.basePath), includePath);
|
|
675
|
+
return path.normalize(resolved);
|
|
676
|
+
}
|
|
677
|
+
checkCircular(resolvedPath) {
|
|
678
|
+
if (this.visited.has(resolvedPath)) throw new CircularIncludeError([...this.visited, resolvedPath]);
|
|
679
|
+
}
|
|
680
|
+
checkDepth(includePath) {
|
|
681
|
+
if (this.depth >= MAX_INCLUDE_DEPTH) throw new ConfigIncludeError(`Maximum include depth (${MAX_INCLUDE_DEPTH}) exceeded at: ${includePath}`, includePath);
|
|
682
|
+
}
|
|
683
|
+
readFile(includePath, resolvedPath) {
|
|
684
|
+
try {
|
|
685
|
+
return this.resolver.readFile(resolvedPath);
|
|
686
|
+
} catch (err) {
|
|
687
|
+
throw new ConfigIncludeError(`Failed to read include file: ${includePath} (resolved: ${resolvedPath})`, includePath, err instanceof Error ? err : void 0);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
parseFile(includePath, resolvedPath, raw) {
|
|
691
|
+
try {
|
|
692
|
+
return this.resolver.parseJson(raw);
|
|
693
|
+
} catch (err) {
|
|
694
|
+
throw new ConfigIncludeError(`Failed to parse include file: ${includePath} (resolved: ${resolvedPath})`, includePath, err instanceof Error ? err : void 0);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
processNested(resolvedPath, parsed) {
|
|
698
|
+
const nested = new IncludeProcessor(resolvedPath, this.resolver);
|
|
699
|
+
nested.visited = new Set([...this.visited, resolvedPath]);
|
|
700
|
+
nested.depth = this.depth + 1;
|
|
701
|
+
return nested.process(parsed);
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
const defaultResolver = {
|
|
705
|
+
readFile: (p) => fs.readFileSync(p, "utf-8"),
|
|
706
|
+
parseJson: (raw) => JSON5.parse(raw)
|
|
707
|
+
};
|
|
708
|
+
/**
|
|
709
|
+
* Resolves all $include directives in a parsed config object.
|
|
710
|
+
*/
|
|
711
|
+
function resolveConfigIncludes(obj, configPath, resolver = defaultResolver) {
|
|
712
|
+
return new IncludeProcessor(configPath, resolver).process(obj);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
//#endregion
|
|
716
|
+
//#region src/config/legacy.shared.ts
|
|
717
|
+
const isRecord$1 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
718
|
+
const getRecord = (value) => isRecord$1(value) ? value : null;
|
|
719
|
+
const ensureRecord = (root, key) => {
|
|
720
|
+
const existing = root[key];
|
|
721
|
+
if (isRecord$1(existing)) return existing;
|
|
722
|
+
const next = {};
|
|
723
|
+
root[key] = next;
|
|
724
|
+
return next;
|
|
725
|
+
};
|
|
726
|
+
const mergeMissing = (target, source) => {
|
|
727
|
+
for (const [key, value] of Object.entries(source)) {
|
|
728
|
+
if (value === void 0) continue;
|
|
729
|
+
const existing = target[key];
|
|
730
|
+
if (existing === void 0) {
|
|
731
|
+
target[key] = value;
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
if (isRecord$1(existing) && isRecord$1(value)) mergeMissing(existing, value);
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
const AUDIO_TRANSCRIPTION_CLI_ALLOWLIST = new Set(["whisper"]);
|
|
738
|
+
const mapLegacyAudioTranscription = (value) => {
|
|
739
|
+
const transcriber = getRecord(value);
|
|
740
|
+
const command = Array.isArray(transcriber?.command) ? transcriber?.command : null;
|
|
741
|
+
if (!command || command.length === 0) return null;
|
|
742
|
+
const rawExecutable = String(command[0] ?? "").trim();
|
|
743
|
+
if (!rawExecutable) return null;
|
|
744
|
+
const executableName = rawExecutable.split(/[\\/]/).pop() ?? rawExecutable;
|
|
745
|
+
if (!AUDIO_TRANSCRIPTION_CLI_ALLOWLIST.has(executableName)) return null;
|
|
746
|
+
const args = command.slice(1).map((part) => String(part));
|
|
747
|
+
const timeoutSeconds = typeof transcriber?.timeoutSeconds === "number" ? transcriber?.timeoutSeconds : void 0;
|
|
748
|
+
const result = {
|
|
749
|
+
command: rawExecutable,
|
|
750
|
+
type: "cli"
|
|
751
|
+
};
|
|
752
|
+
if (args.length > 0) result.args = args;
|
|
753
|
+
if (timeoutSeconds !== void 0) result.timeoutSeconds = timeoutSeconds;
|
|
754
|
+
return result;
|
|
755
|
+
};
|
|
756
|
+
const getAgentsList = (agents) => {
|
|
757
|
+
const list = agents?.list;
|
|
758
|
+
return Array.isArray(list) ? list : [];
|
|
759
|
+
};
|
|
760
|
+
const resolveDefaultAgentIdFromRaw = (raw) => {
|
|
761
|
+
const list = getAgentsList(getRecord(raw.agents));
|
|
762
|
+
const defaultEntry = list.find((entry) => isRecord$1(entry) && entry.default === true && typeof entry.id === "string" && entry.id.trim() !== "");
|
|
763
|
+
if (defaultEntry) return defaultEntry.id.trim();
|
|
764
|
+
const routing = getRecord(raw.routing);
|
|
765
|
+
const routingDefault = typeof routing?.defaultAgentId === "string" ? routing.defaultAgentId.trim() : "";
|
|
766
|
+
if (routingDefault) return routingDefault;
|
|
767
|
+
const firstEntry = list.find((entry) => isRecord$1(entry) && typeof entry.id === "string" && entry.id.trim() !== "");
|
|
768
|
+
if (firstEntry) return firstEntry.id.trim();
|
|
769
|
+
return "main";
|
|
770
|
+
};
|
|
771
|
+
const ensureAgentEntry = (list, id) => {
|
|
772
|
+
const normalized = id.trim();
|
|
773
|
+
const existing = list.find((entry) => isRecord$1(entry) && typeof entry.id === "string" && entry.id.trim() === normalized);
|
|
774
|
+
if (existing) return existing;
|
|
775
|
+
const created = { id: normalized };
|
|
776
|
+
list.push(created);
|
|
777
|
+
return created;
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
//#endregion
|
|
781
|
+
//#region src/config/legacy.migrations.part-1.ts
|
|
782
|
+
const LEGACY_CONFIG_MIGRATIONS_PART_1 = [
|
|
783
|
+
{
|
|
784
|
+
id: "bindings.match.provider->bindings.match.channel",
|
|
785
|
+
describe: "Move bindings[].match.provider to bindings[].match.channel",
|
|
786
|
+
apply: (raw, changes) => {
|
|
787
|
+
const bindings = Array.isArray(raw.bindings) ? raw.bindings : null;
|
|
788
|
+
if (!bindings) return;
|
|
789
|
+
let touched = false;
|
|
790
|
+
for (const entry of bindings) {
|
|
791
|
+
if (!isRecord$1(entry)) continue;
|
|
792
|
+
const match = getRecord(entry.match);
|
|
793
|
+
if (!match) continue;
|
|
794
|
+
if (typeof match.channel === "string" && match.channel.trim()) continue;
|
|
795
|
+
const provider = typeof match.provider === "string" ? match.provider.trim() : "";
|
|
796
|
+
if (!provider) continue;
|
|
797
|
+
match.channel = provider;
|
|
798
|
+
delete match.provider;
|
|
799
|
+
entry.match = match;
|
|
800
|
+
touched = true;
|
|
801
|
+
}
|
|
802
|
+
if (touched) {
|
|
803
|
+
raw.bindings = bindings;
|
|
804
|
+
changes.push("Moved bindings[].match.provider → bindings[].match.channel.");
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
id: "bindings.match.accountID->bindings.match.accountId",
|
|
810
|
+
describe: "Move bindings[].match.accountID to bindings[].match.accountId",
|
|
811
|
+
apply: (raw, changes) => {
|
|
812
|
+
const bindings = Array.isArray(raw.bindings) ? raw.bindings : null;
|
|
813
|
+
if (!bindings) return;
|
|
814
|
+
let touched = false;
|
|
815
|
+
for (const entry of bindings) {
|
|
816
|
+
if (!isRecord$1(entry)) continue;
|
|
817
|
+
const match = getRecord(entry.match);
|
|
818
|
+
if (!match) continue;
|
|
819
|
+
if (match.accountId !== void 0) continue;
|
|
820
|
+
const accountID = typeof match.accountID === "string" ? match.accountID.trim() : match.accountID;
|
|
821
|
+
if (!accountID) continue;
|
|
822
|
+
match.accountId = accountID;
|
|
823
|
+
delete match.accountID;
|
|
824
|
+
entry.match = match;
|
|
825
|
+
touched = true;
|
|
826
|
+
}
|
|
827
|
+
if (touched) {
|
|
828
|
+
raw.bindings = bindings;
|
|
829
|
+
changes.push("Moved bindings[].match.accountID → bindings[].match.accountId.");
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
id: "session.sendPolicy.rules.match.provider->match.channel",
|
|
835
|
+
describe: "Move session.sendPolicy.rules[].match.provider to match.channel",
|
|
836
|
+
apply: (raw, changes) => {
|
|
837
|
+
const session = getRecord(raw.session);
|
|
838
|
+
if (!session) return;
|
|
839
|
+
const sendPolicy = getRecord(session.sendPolicy);
|
|
840
|
+
if (!sendPolicy) return;
|
|
841
|
+
const rules = Array.isArray(sendPolicy.rules) ? sendPolicy.rules : null;
|
|
842
|
+
if (!rules) return;
|
|
843
|
+
let touched = false;
|
|
844
|
+
for (const rule of rules) {
|
|
845
|
+
if (!isRecord$1(rule)) continue;
|
|
846
|
+
const match = getRecord(rule.match);
|
|
847
|
+
if (!match) continue;
|
|
848
|
+
if (typeof match.channel === "string" && match.channel.trim()) continue;
|
|
849
|
+
const provider = typeof match.provider === "string" ? match.provider.trim() : "";
|
|
850
|
+
if (!provider) continue;
|
|
851
|
+
match.channel = provider;
|
|
852
|
+
delete match.provider;
|
|
853
|
+
rule.match = match;
|
|
854
|
+
touched = true;
|
|
855
|
+
}
|
|
856
|
+
if (touched) {
|
|
857
|
+
sendPolicy.rules = rules;
|
|
858
|
+
session.sendPolicy = sendPolicy;
|
|
859
|
+
raw.session = session;
|
|
860
|
+
changes.push("Moved session.sendPolicy.rules[].match.provider → match.channel.");
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
id: "messages.queue.byProvider->byChannel",
|
|
866
|
+
describe: "Move messages.queue.byProvider to messages.queue.byChannel",
|
|
867
|
+
apply: (raw, changes) => {
|
|
868
|
+
const messages = getRecord(raw.messages);
|
|
869
|
+
if (!messages) return;
|
|
870
|
+
const queue = getRecord(messages.queue);
|
|
871
|
+
if (!queue) return;
|
|
872
|
+
if (queue.byProvider === void 0) return;
|
|
873
|
+
if (queue.byChannel === void 0) {
|
|
874
|
+
queue.byChannel = queue.byProvider;
|
|
875
|
+
changes.push("Moved messages.queue.byProvider → messages.queue.byChannel.");
|
|
876
|
+
} else changes.push("Removed messages.queue.byProvider (messages.queue.byChannel already set).");
|
|
877
|
+
delete queue.byProvider;
|
|
878
|
+
messages.queue = queue;
|
|
879
|
+
raw.messages = messages;
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
id: "providers->channels",
|
|
884
|
+
describe: "Move provider config sections to channels.*",
|
|
885
|
+
apply: (raw, changes) => {
|
|
886
|
+
const legacyEntries = [
|
|
887
|
+
"whatsapp",
|
|
888
|
+
"telegram",
|
|
889
|
+
"discord",
|
|
890
|
+
"slack",
|
|
891
|
+
"signal",
|
|
892
|
+
"imessage",
|
|
893
|
+
"msteams"
|
|
894
|
+
].filter((key) => isRecord$1(raw[key]));
|
|
895
|
+
if (legacyEntries.length === 0) return;
|
|
896
|
+
const channels = ensureRecord(raw, "channels");
|
|
897
|
+
for (const key of legacyEntries) {
|
|
898
|
+
const legacy = getRecord(raw[key]);
|
|
899
|
+
if (!legacy) continue;
|
|
900
|
+
const channelEntry = ensureRecord(channels, key);
|
|
901
|
+
const hadEntries = Object.keys(channelEntry).length > 0;
|
|
902
|
+
mergeMissing(channelEntry, legacy);
|
|
903
|
+
channels[key] = channelEntry;
|
|
904
|
+
delete raw[key];
|
|
905
|
+
changes.push(hadEntries ? `Merged ${key} → channels.${key}.` : `Moved ${key} → channels.${key}.`);
|
|
906
|
+
}
|
|
907
|
+
raw.channels = channels;
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
id: "routing.allowFrom->channels.whatsapp.allowFrom",
|
|
912
|
+
describe: "Move routing.allowFrom to channels.whatsapp.allowFrom",
|
|
913
|
+
apply: (raw, changes) => {
|
|
914
|
+
const routing = raw.routing;
|
|
915
|
+
if (!routing || typeof routing !== "object") return;
|
|
916
|
+
const allowFrom = routing.allowFrom;
|
|
917
|
+
if (allowFrom === void 0) return;
|
|
918
|
+
const channels = getRecord(raw.channels);
|
|
919
|
+
const whatsapp = channels ? getRecord(channels.whatsapp) : null;
|
|
920
|
+
if (!whatsapp) {
|
|
921
|
+
delete routing.allowFrom;
|
|
922
|
+
if (Object.keys(routing).length === 0) delete raw.routing;
|
|
923
|
+
changes.push("Removed routing.allowFrom (channels.whatsapp not configured).");
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (whatsapp.allowFrom === void 0) {
|
|
927
|
+
whatsapp.allowFrom = allowFrom;
|
|
928
|
+
changes.push("Moved routing.allowFrom → channels.whatsapp.allowFrom.");
|
|
929
|
+
} else changes.push("Removed routing.allowFrom (channels.whatsapp.allowFrom already set).");
|
|
930
|
+
delete routing.allowFrom;
|
|
931
|
+
if (Object.keys(routing).length === 0) delete raw.routing;
|
|
932
|
+
channels.whatsapp = whatsapp;
|
|
933
|
+
raw.channels = channels;
|
|
934
|
+
}
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
id: "routing.groupChat.requireMention->groups.*.requireMention",
|
|
938
|
+
describe: "Move routing.groupChat.requireMention to channels.whatsapp/telegram/imessage groups",
|
|
939
|
+
apply: (raw, changes) => {
|
|
940
|
+
const routing = raw.routing;
|
|
941
|
+
if (!routing || typeof routing !== "object") return;
|
|
942
|
+
const groupChat = routing.groupChat && typeof routing.groupChat === "object" ? routing.groupChat : null;
|
|
943
|
+
if (!groupChat) return;
|
|
944
|
+
const requireMention = groupChat.requireMention;
|
|
945
|
+
if (requireMention === void 0) return;
|
|
946
|
+
const channels = ensureRecord(raw, "channels");
|
|
947
|
+
const applyTo = (key, options) => {
|
|
948
|
+
if (options?.requireExisting && !isRecord$1(channels[key])) return;
|
|
949
|
+
const section = channels[key] && typeof channels[key] === "object" ? channels[key] : {};
|
|
950
|
+
const groups = section.groups && typeof section.groups === "object" ? section.groups : {};
|
|
951
|
+
const defaultKey = "*";
|
|
952
|
+
const entry = groups[defaultKey] && typeof groups[defaultKey] === "object" ? groups[defaultKey] : {};
|
|
953
|
+
if (entry.requireMention === void 0) {
|
|
954
|
+
entry.requireMention = requireMention;
|
|
955
|
+
groups[defaultKey] = entry;
|
|
956
|
+
section.groups = groups;
|
|
957
|
+
channels[key] = section;
|
|
958
|
+
changes.push(`Moved routing.groupChat.requireMention → channels.${key}.groups."*".requireMention.`);
|
|
959
|
+
} else changes.push(`Removed routing.groupChat.requireMention (channels.${key}.groups."*" already set).`);
|
|
960
|
+
};
|
|
961
|
+
applyTo("whatsapp", { requireExisting: true });
|
|
962
|
+
applyTo("telegram");
|
|
963
|
+
applyTo("imessage");
|
|
964
|
+
delete groupChat.requireMention;
|
|
965
|
+
if (Object.keys(groupChat).length === 0) delete routing.groupChat;
|
|
966
|
+
if (Object.keys(routing).length === 0) delete raw.routing;
|
|
967
|
+
raw.channels = channels;
|
|
968
|
+
}
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
id: "gateway.token->gateway.auth.token",
|
|
972
|
+
describe: "Move gateway.token to gateway.auth.token",
|
|
973
|
+
apply: (raw, changes) => {
|
|
974
|
+
const gateway = raw.gateway;
|
|
975
|
+
if (!gateway || typeof gateway !== "object") return;
|
|
976
|
+
const token = gateway.token;
|
|
977
|
+
if (token === void 0) return;
|
|
978
|
+
const gatewayObj = gateway;
|
|
979
|
+
const auth = gatewayObj.auth && typeof gatewayObj.auth === "object" ? gatewayObj.auth : {};
|
|
980
|
+
if (auth.token === void 0) {
|
|
981
|
+
auth.token = token;
|
|
982
|
+
if (!auth.mode) auth.mode = "token";
|
|
983
|
+
changes.push("Moved gateway.token → gateway.auth.token.");
|
|
984
|
+
} else changes.push("Removed gateway.token (gateway.auth.token already set).");
|
|
985
|
+
delete gatewayObj.token;
|
|
986
|
+
if (Object.keys(auth).length > 0) gatewayObj.auth = auth;
|
|
987
|
+
raw.gateway = gatewayObj;
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
id: "telegram.requireMention->channels.telegram.groups.*.requireMention",
|
|
992
|
+
describe: "Move telegram.requireMention to channels.telegram.groups.*.requireMention",
|
|
993
|
+
apply: (raw, changes) => {
|
|
994
|
+
const channels = ensureRecord(raw, "channels");
|
|
995
|
+
const telegram = channels.telegram;
|
|
996
|
+
if (!telegram || typeof telegram !== "object") return;
|
|
997
|
+
const requireMention = telegram.requireMention;
|
|
998
|
+
if (requireMention === void 0) return;
|
|
999
|
+
const groups = telegram.groups && typeof telegram.groups === "object" ? telegram.groups : {};
|
|
1000
|
+
const defaultKey = "*";
|
|
1001
|
+
const entry = groups[defaultKey] && typeof groups[defaultKey] === "object" ? groups[defaultKey] : {};
|
|
1002
|
+
if (entry.requireMention === void 0) {
|
|
1003
|
+
entry.requireMention = requireMention;
|
|
1004
|
+
groups[defaultKey] = entry;
|
|
1005
|
+
telegram.groups = groups;
|
|
1006
|
+
changes.push("Moved telegram.requireMention → channels.telegram.groups.\"*\".requireMention.");
|
|
1007
|
+
} else changes.push("Removed telegram.requireMention (channels.telegram.groups.\"*\" already set).");
|
|
1008
|
+
delete telegram.requireMention;
|
|
1009
|
+
channels.telegram = telegram;
|
|
1010
|
+
raw.channels = channels;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
];
|
|
1014
|
+
|
|
1015
|
+
//#endregion
|
|
1016
|
+
//#region src/config/legacy.migrations.part-2.ts
|
|
1017
|
+
const LEGACY_CONFIG_MIGRATIONS_PART_2 = [
|
|
1018
|
+
{
|
|
1019
|
+
id: "agent.model-config-v2",
|
|
1020
|
+
describe: "Migrate legacy agent.model/allowedModels/modelAliases/modelFallbacks/imageModelFallbacks to agent.models + model lists",
|
|
1021
|
+
apply: (raw, changes) => {
|
|
1022
|
+
const agentRoot = getRecord(raw.agent);
|
|
1023
|
+
const defaults = getRecord(getRecord(raw.agents)?.defaults);
|
|
1024
|
+
const agent = agentRoot ?? defaults;
|
|
1025
|
+
if (!agent) return;
|
|
1026
|
+
const label = agentRoot ? "agent" : "agents.defaults";
|
|
1027
|
+
const legacyModel = typeof agent.model === "string" ? String(agent.model) : void 0;
|
|
1028
|
+
const legacyImageModel = typeof agent.imageModel === "string" ? String(agent.imageModel) : void 0;
|
|
1029
|
+
const legacyAllowed = Array.isArray(agent.allowedModels) ? agent.allowedModels.map(String) : [];
|
|
1030
|
+
const legacyModelFallbacks = Array.isArray(agent.modelFallbacks) ? agent.modelFallbacks.map(String) : [];
|
|
1031
|
+
const legacyImageModelFallbacks = Array.isArray(agent.imageModelFallbacks) ? agent.imageModelFallbacks.map(String) : [];
|
|
1032
|
+
const legacyAliases = agent.modelAliases && typeof agent.modelAliases === "object" ? agent.modelAliases : {};
|
|
1033
|
+
if (!(legacyModel || legacyImageModel || legacyAllowed.length > 0 || legacyModelFallbacks.length > 0 || legacyImageModelFallbacks.length > 0 || Object.keys(legacyAliases).length > 0)) return;
|
|
1034
|
+
const models = agent.models && typeof agent.models === "object" ? agent.models : {};
|
|
1035
|
+
const ensureModel = (rawKey) => {
|
|
1036
|
+
if (typeof rawKey !== "string") return;
|
|
1037
|
+
const key = rawKey.trim();
|
|
1038
|
+
if (!key) return;
|
|
1039
|
+
if (!models[key]) models[key] = {};
|
|
1040
|
+
};
|
|
1041
|
+
ensureModel(legacyModel);
|
|
1042
|
+
ensureModel(legacyImageModel);
|
|
1043
|
+
for (const key of legacyAllowed) ensureModel(key);
|
|
1044
|
+
for (const key of legacyModelFallbacks) ensureModel(key);
|
|
1045
|
+
for (const key of legacyImageModelFallbacks) ensureModel(key);
|
|
1046
|
+
for (const target of Object.values(legacyAliases)) {
|
|
1047
|
+
if (typeof target !== "string") continue;
|
|
1048
|
+
ensureModel(target);
|
|
1049
|
+
}
|
|
1050
|
+
for (const [alias, targetRaw] of Object.entries(legacyAliases)) {
|
|
1051
|
+
if (typeof targetRaw !== "string") continue;
|
|
1052
|
+
const target = targetRaw.trim();
|
|
1053
|
+
if (!target) continue;
|
|
1054
|
+
const entry = models[target] && typeof models[target] === "object" ? models[target] : {};
|
|
1055
|
+
if (!("alias" in entry)) {
|
|
1056
|
+
entry.alias = alias;
|
|
1057
|
+
models[target] = entry;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
const currentModel = agent.model && typeof agent.model === "object" ? agent.model : null;
|
|
1061
|
+
if (currentModel) {
|
|
1062
|
+
if (!currentModel.primary && legacyModel) currentModel.primary = legacyModel;
|
|
1063
|
+
if (legacyModelFallbacks.length > 0 && (!Array.isArray(currentModel.fallbacks) || currentModel.fallbacks.length === 0)) currentModel.fallbacks = legacyModelFallbacks;
|
|
1064
|
+
agent.model = currentModel;
|
|
1065
|
+
} else if (legacyModel || legacyModelFallbacks.length > 0) agent.model = {
|
|
1066
|
+
primary: legacyModel,
|
|
1067
|
+
fallbacks: legacyModelFallbacks.length ? legacyModelFallbacks : []
|
|
1068
|
+
};
|
|
1069
|
+
const currentImageModel = agent.imageModel && typeof agent.imageModel === "object" ? agent.imageModel : null;
|
|
1070
|
+
if (currentImageModel) {
|
|
1071
|
+
if (!currentImageModel.primary && legacyImageModel) currentImageModel.primary = legacyImageModel;
|
|
1072
|
+
if (legacyImageModelFallbacks.length > 0 && (!Array.isArray(currentImageModel.fallbacks) || currentImageModel.fallbacks.length === 0)) currentImageModel.fallbacks = legacyImageModelFallbacks;
|
|
1073
|
+
agent.imageModel = currentImageModel;
|
|
1074
|
+
} else if (legacyImageModel || legacyImageModelFallbacks.length > 0) agent.imageModel = {
|
|
1075
|
+
primary: legacyImageModel,
|
|
1076
|
+
fallbacks: legacyImageModelFallbacks.length ? legacyImageModelFallbacks : []
|
|
1077
|
+
};
|
|
1078
|
+
agent.models = models;
|
|
1079
|
+
if (legacyModel !== void 0) changes.push(`Migrated ${label}.model string → ${label}.model.primary.`);
|
|
1080
|
+
if (legacyModelFallbacks.length > 0) changes.push(`Migrated ${label}.modelFallbacks → ${label}.model.fallbacks.`);
|
|
1081
|
+
if (legacyImageModel !== void 0) changes.push(`Migrated ${label}.imageModel string → ${label}.imageModel.primary.`);
|
|
1082
|
+
if (legacyImageModelFallbacks.length > 0) changes.push(`Migrated ${label}.imageModelFallbacks → ${label}.imageModel.fallbacks.`);
|
|
1083
|
+
if (legacyAllowed.length > 0) changes.push(`Migrated ${label}.allowedModels → ${label}.models.`);
|
|
1084
|
+
if (Object.keys(legacyAliases).length > 0) changes.push(`Migrated ${label}.modelAliases → ${label}.models.*.alias.`);
|
|
1085
|
+
delete agent.allowedModels;
|
|
1086
|
+
delete agent.modelAliases;
|
|
1087
|
+
delete agent.modelFallbacks;
|
|
1088
|
+
delete agent.imageModelFallbacks;
|
|
1089
|
+
}
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
id: "routing.agents-v2",
|
|
1093
|
+
describe: "Move routing.agents/defaultAgentId to agents.list",
|
|
1094
|
+
apply: (raw, changes) => {
|
|
1095
|
+
const routing = getRecord(raw.routing);
|
|
1096
|
+
if (!routing) return;
|
|
1097
|
+
const routingAgents = getRecord(routing.agents);
|
|
1098
|
+
const agents = ensureRecord(raw, "agents");
|
|
1099
|
+
const list = getAgentsList(agents);
|
|
1100
|
+
if (routingAgents) {
|
|
1101
|
+
for (const [rawId, entryRaw] of Object.entries(routingAgents)) {
|
|
1102
|
+
const agentId = String(rawId ?? "").trim();
|
|
1103
|
+
const entry = getRecord(entryRaw);
|
|
1104
|
+
if (!agentId || !entry) continue;
|
|
1105
|
+
const target = ensureAgentEntry(list, agentId);
|
|
1106
|
+
const entryCopy = { ...entry };
|
|
1107
|
+
if ("mentionPatterns" in entryCopy) {
|
|
1108
|
+
const mentionPatterns = entryCopy.mentionPatterns;
|
|
1109
|
+
const groupChat = ensureRecord(target, "groupChat");
|
|
1110
|
+
if (groupChat.mentionPatterns === void 0) {
|
|
1111
|
+
groupChat.mentionPatterns = mentionPatterns;
|
|
1112
|
+
changes.push(`Moved routing.agents.${agentId}.mentionPatterns → agents.list (id "${agentId}").groupChat.mentionPatterns.`);
|
|
1113
|
+
} else changes.push(`Removed routing.agents.${agentId}.mentionPatterns (agents.list groupChat mentionPatterns already set).`);
|
|
1114
|
+
delete entryCopy.mentionPatterns;
|
|
1115
|
+
}
|
|
1116
|
+
const legacyGroupChat = getRecord(entryCopy.groupChat);
|
|
1117
|
+
if (legacyGroupChat) {
|
|
1118
|
+
mergeMissing(ensureRecord(target, "groupChat"), legacyGroupChat);
|
|
1119
|
+
delete entryCopy.groupChat;
|
|
1120
|
+
}
|
|
1121
|
+
const legacySandbox = getRecord(entryCopy.sandbox);
|
|
1122
|
+
if (legacySandbox) {
|
|
1123
|
+
const sandboxTools = getRecord(legacySandbox.tools);
|
|
1124
|
+
if (sandboxTools) {
|
|
1125
|
+
mergeMissing(ensureRecord(ensureRecord(ensureRecord(target, "tools"), "sandbox"), "tools"), sandboxTools);
|
|
1126
|
+
delete legacySandbox.tools;
|
|
1127
|
+
changes.push(`Moved routing.agents.${agentId}.sandbox.tools → agents.list (id "${agentId}").tools.sandbox.tools.`);
|
|
1128
|
+
}
|
|
1129
|
+
entryCopy.sandbox = legacySandbox;
|
|
1130
|
+
}
|
|
1131
|
+
mergeMissing(target, entryCopy);
|
|
1132
|
+
}
|
|
1133
|
+
delete routing.agents;
|
|
1134
|
+
changes.push("Moved routing.agents → agents.list.");
|
|
1135
|
+
}
|
|
1136
|
+
const defaultAgentId = typeof routing.defaultAgentId === "string" ? routing.defaultAgentId.trim() : "";
|
|
1137
|
+
if (defaultAgentId) {
|
|
1138
|
+
if (!list.some((entry) => isRecord$1(entry) && entry.default === true)) {
|
|
1139
|
+
const entry = ensureAgentEntry(list, defaultAgentId);
|
|
1140
|
+
entry.default = true;
|
|
1141
|
+
changes.push(`Moved routing.defaultAgentId → agents.list (id "${defaultAgentId}").default.`);
|
|
1142
|
+
} else changes.push("Removed routing.defaultAgentId (agents.list default already set).");
|
|
1143
|
+
delete routing.defaultAgentId;
|
|
1144
|
+
}
|
|
1145
|
+
if (list.length > 0) agents.list = list;
|
|
1146
|
+
if (Object.keys(routing).length === 0) delete raw.routing;
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
id: "routing.config-v2",
|
|
1151
|
+
describe: "Move routing bindings/groupChat/queue/agentToAgent/transcribeAudio",
|
|
1152
|
+
apply: (raw, changes) => {
|
|
1153
|
+
const routing = getRecord(raw.routing);
|
|
1154
|
+
if (!routing) return;
|
|
1155
|
+
if (routing.bindings !== void 0) {
|
|
1156
|
+
if (raw.bindings === void 0) {
|
|
1157
|
+
raw.bindings = routing.bindings;
|
|
1158
|
+
changes.push("Moved routing.bindings → bindings.");
|
|
1159
|
+
} else changes.push("Removed routing.bindings (bindings already set).");
|
|
1160
|
+
delete routing.bindings;
|
|
1161
|
+
}
|
|
1162
|
+
if (routing.agentToAgent !== void 0) {
|
|
1163
|
+
const tools = ensureRecord(raw, "tools");
|
|
1164
|
+
if (tools.agentToAgent === void 0) {
|
|
1165
|
+
tools.agentToAgent = routing.agentToAgent;
|
|
1166
|
+
changes.push("Moved routing.agentToAgent → tools.agentToAgent.");
|
|
1167
|
+
} else changes.push("Removed routing.agentToAgent (tools.agentToAgent already set).");
|
|
1168
|
+
delete routing.agentToAgent;
|
|
1169
|
+
}
|
|
1170
|
+
if (routing.queue !== void 0) {
|
|
1171
|
+
const messages = ensureRecord(raw, "messages");
|
|
1172
|
+
if (messages.queue === void 0) {
|
|
1173
|
+
messages.queue = routing.queue;
|
|
1174
|
+
changes.push("Moved routing.queue → messages.queue.");
|
|
1175
|
+
} else changes.push("Removed routing.queue (messages.queue already set).");
|
|
1176
|
+
delete routing.queue;
|
|
1177
|
+
}
|
|
1178
|
+
const groupChat = getRecord(routing.groupChat);
|
|
1179
|
+
if (groupChat) {
|
|
1180
|
+
const historyLimit = groupChat.historyLimit;
|
|
1181
|
+
if (historyLimit !== void 0) {
|
|
1182
|
+
const messagesGroup = ensureRecord(ensureRecord(raw, "messages"), "groupChat");
|
|
1183
|
+
if (messagesGroup.historyLimit === void 0) {
|
|
1184
|
+
messagesGroup.historyLimit = historyLimit;
|
|
1185
|
+
changes.push("Moved routing.groupChat.historyLimit → messages.groupChat.historyLimit.");
|
|
1186
|
+
} else changes.push("Removed routing.groupChat.historyLimit (messages.groupChat.historyLimit already set).");
|
|
1187
|
+
delete groupChat.historyLimit;
|
|
1188
|
+
}
|
|
1189
|
+
const mentionPatterns = groupChat.mentionPatterns;
|
|
1190
|
+
if (mentionPatterns !== void 0) {
|
|
1191
|
+
const messagesGroup = ensureRecord(ensureRecord(raw, "messages"), "groupChat");
|
|
1192
|
+
if (messagesGroup.mentionPatterns === void 0) {
|
|
1193
|
+
messagesGroup.mentionPatterns = mentionPatterns;
|
|
1194
|
+
changes.push("Moved routing.groupChat.mentionPatterns → messages.groupChat.mentionPatterns.");
|
|
1195
|
+
} else changes.push("Removed routing.groupChat.mentionPatterns (messages.groupChat.mentionPatterns already set).");
|
|
1196
|
+
delete groupChat.mentionPatterns;
|
|
1197
|
+
}
|
|
1198
|
+
if (Object.keys(groupChat).length === 0) delete routing.groupChat;
|
|
1199
|
+
else routing.groupChat = groupChat;
|
|
1200
|
+
}
|
|
1201
|
+
if (routing.transcribeAudio !== void 0) {
|
|
1202
|
+
const mapped = mapLegacyAudioTranscription(routing.transcribeAudio);
|
|
1203
|
+
if (mapped) {
|
|
1204
|
+
const mediaAudio = ensureRecord(ensureRecord(ensureRecord(raw, "tools"), "media"), "audio");
|
|
1205
|
+
if ((Array.isArray(mediaAudio.models) ? mediaAudio.models : []).length === 0) {
|
|
1206
|
+
mediaAudio.enabled = true;
|
|
1207
|
+
mediaAudio.models = [mapped];
|
|
1208
|
+
changes.push("Moved routing.transcribeAudio → tools.media.audio.models.");
|
|
1209
|
+
} else changes.push("Removed routing.transcribeAudio (tools.media.audio.models already set).");
|
|
1210
|
+
} else changes.push("Removed routing.transcribeAudio (unsupported transcription CLI).");
|
|
1211
|
+
delete routing.transcribeAudio;
|
|
1212
|
+
}
|
|
1213
|
+
const audio = getRecord(raw.audio);
|
|
1214
|
+
if (audio?.transcription !== void 0) {
|
|
1215
|
+
const mapped = mapLegacyAudioTranscription(audio.transcription);
|
|
1216
|
+
if (mapped) {
|
|
1217
|
+
const mediaAudio = ensureRecord(ensureRecord(ensureRecord(raw, "tools"), "media"), "audio");
|
|
1218
|
+
if ((Array.isArray(mediaAudio.models) ? mediaAudio.models : []).length === 0) {
|
|
1219
|
+
mediaAudio.enabled = true;
|
|
1220
|
+
mediaAudio.models = [mapped];
|
|
1221
|
+
changes.push("Moved audio.transcription → tools.media.audio.models.");
|
|
1222
|
+
} else changes.push("Removed audio.transcription (tools.media.audio.models already set).");
|
|
1223
|
+
delete audio.transcription;
|
|
1224
|
+
if (Object.keys(audio).length === 0) delete raw.audio;
|
|
1225
|
+
else raw.audio = audio;
|
|
1226
|
+
} else {
|
|
1227
|
+
delete audio.transcription;
|
|
1228
|
+
changes.push("Removed audio.transcription (unsupported transcription CLI).");
|
|
1229
|
+
if (Object.keys(audio).length === 0) delete raw.audio;
|
|
1230
|
+
else raw.audio = audio;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
if (Object.keys(routing).length === 0) delete raw.routing;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
];
|
|
1237
|
+
|
|
1238
|
+
//#endregion
|
|
1239
|
+
//#region src/config/legacy.migrations.part-3.ts
|
|
1240
|
+
const LEGACY_CONFIG_MIGRATIONS_PART_3 = [
|
|
1241
|
+
{
|
|
1242
|
+
id: "auth.anthropic-claude-cli-mode-oauth",
|
|
1243
|
+
describe: "Switch anthropic:claude-cli auth profile mode to oauth",
|
|
1244
|
+
apply: (raw, changes) => {
|
|
1245
|
+
const profiles = getRecord(getRecord(raw.auth)?.profiles);
|
|
1246
|
+
if (!profiles) return;
|
|
1247
|
+
const claudeCli = getRecord(profiles["anthropic:claude-cli"]);
|
|
1248
|
+
if (!claudeCli) return;
|
|
1249
|
+
if (claudeCli.mode !== "token") return;
|
|
1250
|
+
claudeCli.mode = "oauth";
|
|
1251
|
+
changes.push("Updated auth.profiles[\"anthropic:claude-cli\"].mode → \"oauth\".");
|
|
1252
|
+
}
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
id: "tools.bash->tools.exec",
|
|
1256
|
+
describe: "Move tools.bash to tools.exec",
|
|
1257
|
+
apply: (raw, changes) => {
|
|
1258
|
+
const tools = ensureRecord(raw, "tools");
|
|
1259
|
+
const bash = getRecord(tools.bash);
|
|
1260
|
+
if (!bash) return;
|
|
1261
|
+
if (tools.exec === void 0) {
|
|
1262
|
+
tools.exec = bash;
|
|
1263
|
+
changes.push("Moved tools.bash → tools.exec.");
|
|
1264
|
+
} else changes.push("Removed tools.bash (tools.exec already set).");
|
|
1265
|
+
delete tools.bash;
|
|
1266
|
+
}
|
|
1267
|
+
},
|
|
1268
|
+
{
|
|
1269
|
+
id: "messages.tts.enabled->auto",
|
|
1270
|
+
describe: "Move messages.tts.enabled to messages.tts.auto",
|
|
1271
|
+
apply: (raw, changes) => {
|
|
1272
|
+
const tts = getRecord(getRecord(raw.messages)?.tts);
|
|
1273
|
+
if (!tts) return;
|
|
1274
|
+
if (tts.auto !== void 0) {
|
|
1275
|
+
if ("enabled" in tts) {
|
|
1276
|
+
delete tts.enabled;
|
|
1277
|
+
changes.push("Removed messages.tts.enabled (messages.tts.auto already set).");
|
|
1278
|
+
}
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
if (typeof tts.enabled !== "boolean") return;
|
|
1282
|
+
tts.auto = tts.enabled ? "always" : "off";
|
|
1283
|
+
delete tts.enabled;
|
|
1284
|
+
changes.push(`Moved messages.tts.enabled → messages.tts.auto (${String(tts.auto)}).`);
|
|
1285
|
+
}
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
id: "agent.defaults-v2",
|
|
1289
|
+
describe: "Move agent config to agents.defaults and tools",
|
|
1290
|
+
apply: (raw, changes) => {
|
|
1291
|
+
const agent = getRecord(raw.agent);
|
|
1292
|
+
if (!agent) return;
|
|
1293
|
+
const agents = ensureRecord(raw, "agents");
|
|
1294
|
+
const defaults = getRecord(agents.defaults) ?? {};
|
|
1295
|
+
const tools = ensureRecord(raw, "tools");
|
|
1296
|
+
const agentTools = getRecord(agent.tools);
|
|
1297
|
+
if (agentTools) {
|
|
1298
|
+
if (tools.allow === void 0 && agentTools.allow !== void 0) {
|
|
1299
|
+
tools.allow = agentTools.allow;
|
|
1300
|
+
changes.push("Moved agent.tools.allow → tools.allow.");
|
|
1301
|
+
}
|
|
1302
|
+
if (tools.deny === void 0 && agentTools.deny !== void 0) {
|
|
1303
|
+
tools.deny = agentTools.deny;
|
|
1304
|
+
changes.push("Moved agent.tools.deny → tools.deny.");
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
const elevated = getRecord(agent.elevated);
|
|
1308
|
+
if (elevated) if (tools.elevated === void 0) {
|
|
1309
|
+
tools.elevated = elevated;
|
|
1310
|
+
changes.push("Moved agent.elevated → tools.elevated.");
|
|
1311
|
+
} else changes.push("Removed agent.elevated (tools.elevated already set).");
|
|
1312
|
+
const bash = getRecord(agent.bash);
|
|
1313
|
+
if (bash) if (tools.exec === void 0) {
|
|
1314
|
+
tools.exec = bash;
|
|
1315
|
+
changes.push("Moved agent.bash → tools.exec.");
|
|
1316
|
+
} else changes.push("Removed agent.bash (tools.exec already set).");
|
|
1317
|
+
const sandbox = getRecord(agent.sandbox);
|
|
1318
|
+
if (sandbox) {
|
|
1319
|
+
const sandboxTools = getRecord(sandbox.tools);
|
|
1320
|
+
if (sandboxTools) {
|
|
1321
|
+
mergeMissing(ensureRecord(ensureRecord(tools, "sandbox"), "tools"), sandboxTools);
|
|
1322
|
+
delete sandbox.tools;
|
|
1323
|
+
changes.push("Moved agent.sandbox.tools → tools.sandbox.tools.");
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
const subagents = getRecord(agent.subagents);
|
|
1327
|
+
if (subagents) {
|
|
1328
|
+
const subagentTools = getRecord(subagents.tools);
|
|
1329
|
+
if (subagentTools) {
|
|
1330
|
+
mergeMissing(ensureRecord(ensureRecord(tools, "subagents"), "tools"), subagentTools);
|
|
1331
|
+
delete subagents.tools;
|
|
1332
|
+
changes.push("Moved agent.subagents.tools → tools.subagents.tools.");
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
const agentCopy = structuredClone(agent);
|
|
1336
|
+
delete agentCopy.tools;
|
|
1337
|
+
delete agentCopy.elevated;
|
|
1338
|
+
delete agentCopy.bash;
|
|
1339
|
+
if (isRecord$1(agentCopy.sandbox)) delete agentCopy.sandbox.tools;
|
|
1340
|
+
if (isRecord$1(agentCopy.subagents)) delete agentCopy.subagents.tools;
|
|
1341
|
+
mergeMissing(defaults, agentCopy);
|
|
1342
|
+
agents.defaults = defaults;
|
|
1343
|
+
raw.agents = agents;
|
|
1344
|
+
delete raw.agent;
|
|
1345
|
+
changes.push("Moved agent → agents.defaults.");
|
|
1346
|
+
}
|
|
1347
|
+
},
|
|
1348
|
+
{
|
|
1349
|
+
id: "identity->agents.list",
|
|
1350
|
+
describe: "Move identity to agents.list[].identity",
|
|
1351
|
+
apply: (raw, changes) => {
|
|
1352
|
+
const identity = getRecord(raw.identity);
|
|
1353
|
+
if (!identity) return;
|
|
1354
|
+
const agents = ensureRecord(raw, "agents");
|
|
1355
|
+
const list = getAgentsList(agents);
|
|
1356
|
+
const defaultId = resolveDefaultAgentIdFromRaw(raw);
|
|
1357
|
+
const entry = ensureAgentEntry(list, defaultId);
|
|
1358
|
+
if (entry.identity === void 0) {
|
|
1359
|
+
entry.identity = identity;
|
|
1360
|
+
changes.push(`Moved identity → agents.list (id "${defaultId}").identity.`);
|
|
1361
|
+
} else changes.push("Removed identity (agents.list identity already set).");
|
|
1362
|
+
agents.list = list;
|
|
1363
|
+
raw.agents = agents;
|
|
1364
|
+
delete raw.identity;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
];
|
|
1368
|
+
|
|
1369
|
+
//#endregion
|
|
1370
|
+
//#region src/config/legacy.migrations.ts
|
|
1371
|
+
const LEGACY_CONFIG_MIGRATIONS = [
|
|
1372
|
+
...LEGACY_CONFIG_MIGRATIONS_PART_1,
|
|
1373
|
+
...LEGACY_CONFIG_MIGRATIONS_PART_2,
|
|
1374
|
+
...LEGACY_CONFIG_MIGRATIONS_PART_3
|
|
1375
|
+
];
|
|
1376
|
+
|
|
1377
|
+
//#endregion
|
|
1378
|
+
//#region src/config/legacy.rules.ts
|
|
1379
|
+
const LEGACY_CONFIG_RULES = [
|
|
1380
|
+
{
|
|
1381
|
+
path: ["whatsapp"],
|
|
1382
|
+
message: "whatsapp config moved to channels.whatsapp (auto-migrated on load)."
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
path: ["telegram"],
|
|
1386
|
+
message: "telegram config moved to channels.telegram (auto-migrated on load)."
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
path: ["discord"],
|
|
1390
|
+
message: "discord config moved to channels.discord (auto-migrated on load)."
|
|
1391
|
+
},
|
|
1392
|
+
{
|
|
1393
|
+
path: ["slack"],
|
|
1394
|
+
message: "slack config moved to channels.slack (auto-migrated on load)."
|
|
1395
|
+
},
|
|
1396
|
+
{
|
|
1397
|
+
path: ["signal"],
|
|
1398
|
+
message: "signal config moved to channels.signal (auto-migrated on load)."
|
|
1399
|
+
},
|
|
1400
|
+
{
|
|
1401
|
+
path: ["imessage"],
|
|
1402
|
+
message: "imessage config moved to channels.imessage (auto-migrated on load)."
|
|
1403
|
+
},
|
|
1404
|
+
{
|
|
1405
|
+
path: ["msteams"],
|
|
1406
|
+
message: "msteams config moved to channels.msteams (auto-migrated on load)."
|
|
1407
|
+
},
|
|
1408
|
+
{
|
|
1409
|
+
path: ["routing", "allowFrom"],
|
|
1410
|
+
message: "routing.allowFrom was removed; use channels.whatsapp.allowFrom instead (auto-migrated on load)."
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
path: ["routing", "bindings"],
|
|
1414
|
+
message: "routing.bindings was moved; use top-level bindings instead (auto-migrated on load)."
|
|
1415
|
+
},
|
|
1416
|
+
{
|
|
1417
|
+
path: ["routing", "agents"],
|
|
1418
|
+
message: "routing.agents was moved; use agents.list instead (auto-migrated on load)."
|
|
1419
|
+
},
|
|
1420
|
+
{
|
|
1421
|
+
path: ["routing", "defaultAgentId"],
|
|
1422
|
+
message: "routing.defaultAgentId was moved; use agents.list[].default instead (auto-migrated on load)."
|
|
1423
|
+
},
|
|
1424
|
+
{
|
|
1425
|
+
path: ["routing", "agentToAgent"],
|
|
1426
|
+
message: "routing.agentToAgent was moved; use tools.agentToAgent instead (auto-migrated on load)."
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
path: [
|
|
1430
|
+
"routing",
|
|
1431
|
+
"groupChat",
|
|
1432
|
+
"requireMention"
|
|
1433
|
+
],
|
|
1434
|
+
message: "routing.groupChat.requireMention was removed; use channels.whatsapp/telegram/imessage groups defaults (e.g. channels.whatsapp.groups.\"*\".requireMention) instead (auto-migrated on load)."
|
|
1435
|
+
},
|
|
1436
|
+
{
|
|
1437
|
+
path: [
|
|
1438
|
+
"routing",
|
|
1439
|
+
"groupChat",
|
|
1440
|
+
"mentionPatterns"
|
|
1441
|
+
],
|
|
1442
|
+
message: "routing.groupChat.mentionPatterns was moved; use agents.list[].groupChat.mentionPatterns or messages.groupChat.mentionPatterns instead (auto-migrated on load)."
|
|
1443
|
+
},
|
|
1444
|
+
{
|
|
1445
|
+
path: ["routing", "queue"],
|
|
1446
|
+
message: "routing.queue was moved; use messages.queue instead (auto-migrated on load)."
|
|
1447
|
+
},
|
|
1448
|
+
{
|
|
1449
|
+
path: ["routing", "transcribeAudio"],
|
|
1450
|
+
message: "routing.transcribeAudio was moved; use tools.media.audio.models instead (auto-migrated on load)."
|
|
1451
|
+
},
|
|
1452
|
+
{
|
|
1453
|
+
path: ["telegram", "requireMention"],
|
|
1454
|
+
message: "telegram.requireMention was removed; use channels.telegram.groups.\"*\".requireMention instead (auto-migrated on load)."
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
path: ["identity"],
|
|
1458
|
+
message: "identity was moved; use agents.list[].identity instead (auto-migrated on load)."
|
|
1459
|
+
},
|
|
1460
|
+
{
|
|
1461
|
+
path: ["agent"],
|
|
1462
|
+
message: "agent.* was moved; use agents.defaults (and tools.* for tool/elevated/exec settings) instead (auto-migrated on load)."
|
|
1463
|
+
},
|
|
1464
|
+
{
|
|
1465
|
+
path: ["tools", "bash"],
|
|
1466
|
+
message: "tools.bash was removed; use tools.exec instead (auto-migrated on load)."
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
path: ["agent", "model"],
|
|
1470
|
+
message: "agent.model string was replaced by agents.defaults.model.primary/fallbacks and agents.defaults.models (auto-migrated on load).",
|
|
1471
|
+
match: (value) => typeof value === "string"
|
|
1472
|
+
},
|
|
1473
|
+
{
|
|
1474
|
+
path: ["agent", "imageModel"],
|
|
1475
|
+
message: "agent.imageModel string was replaced by agents.defaults.imageModel.primary/fallbacks (auto-migrated on load).",
|
|
1476
|
+
match: (value) => typeof value === "string"
|
|
1477
|
+
},
|
|
1478
|
+
{
|
|
1479
|
+
path: ["agent", "allowedModels"],
|
|
1480
|
+
message: "agent.allowedModels was replaced by agents.defaults.models (auto-migrated on load)."
|
|
1481
|
+
},
|
|
1482
|
+
{
|
|
1483
|
+
path: ["agent", "modelAliases"],
|
|
1484
|
+
message: "agent.modelAliases was replaced by agents.defaults.models.*.alias (auto-migrated on load)."
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
path: ["agent", "modelFallbacks"],
|
|
1488
|
+
message: "agent.modelFallbacks was replaced by agents.defaults.model.fallbacks (auto-migrated on load)."
|
|
1489
|
+
},
|
|
1490
|
+
{
|
|
1491
|
+
path: ["agent", "imageModelFallbacks"],
|
|
1492
|
+
message: "agent.imageModelFallbacks was replaced by agents.defaults.imageModel.fallbacks (auto-migrated on load)."
|
|
1493
|
+
},
|
|
1494
|
+
{
|
|
1495
|
+
path: [
|
|
1496
|
+
"messages",
|
|
1497
|
+
"tts",
|
|
1498
|
+
"enabled"
|
|
1499
|
+
],
|
|
1500
|
+
message: "messages.tts.enabled was replaced by messages.tts.auto (auto-migrated on load)."
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
path: ["gateway", "token"],
|
|
1504
|
+
message: "gateway.token is ignored; use gateway.auth.token instead (auto-migrated on load)."
|
|
1505
|
+
}
|
|
1506
|
+
];
|
|
1507
|
+
|
|
1508
|
+
//#endregion
|
|
1509
|
+
//#region src/config/legacy.ts
|
|
1510
|
+
function findLegacyConfigIssues(raw) {
|
|
1511
|
+
if (!raw || typeof raw !== "object") return [];
|
|
1512
|
+
const root = raw;
|
|
1513
|
+
const issues = [];
|
|
1514
|
+
for (const rule of LEGACY_CONFIG_RULES) {
|
|
1515
|
+
let cursor = root;
|
|
1516
|
+
for (const key of rule.path) {
|
|
1517
|
+
if (!cursor || typeof cursor !== "object") {
|
|
1518
|
+
cursor = void 0;
|
|
1519
|
+
break;
|
|
1520
|
+
}
|
|
1521
|
+
cursor = cursor[key];
|
|
1522
|
+
}
|
|
1523
|
+
if (cursor !== void 0 && (!rule.match || rule.match(cursor, root))) issues.push({
|
|
1524
|
+
path: rule.path.join("."),
|
|
1525
|
+
message: rule.message
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
return issues;
|
|
1529
|
+
}
|
|
1530
|
+
function applyLegacyMigrations(raw) {
|
|
1531
|
+
if (!raw || typeof raw !== "object") return {
|
|
1532
|
+
next: null,
|
|
1533
|
+
changes: []
|
|
1534
|
+
};
|
|
1535
|
+
const next = structuredClone(raw);
|
|
1536
|
+
const changes = [];
|
|
1537
|
+
for (const migration of LEGACY_CONFIG_MIGRATIONS) migration.apply(next, changes);
|
|
1538
|
+
if (changes.length === 0) return {
|
|
1539
|
+
next: null,
|
|
1540
|
+
changes: []
|
|
1541
|
+
};
|
|
1542
|
+
return {
|
|
1543
|
+
next,
|
|
1544
|
+
changes
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
//#endregion
|
|
1549
|
+
//#region src/config/normalize-paths.ts
|
|
1550
|
+
const PATH_VALUE_RE = /^~(?=$|[\\/])/;
|
|
1551
|
+
const PATH_KEY_RE = /(dir|path|paths|file|root|workspace)$/i;
|
|
1552
|
+
const PATH_LIST_KEYS = new Set(["paths", "pathPrepend"]);
|
|
1553
|
+
function isPlainObject$2(value) {
|
|
1554
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1555
|
+
}
|
|
1556
|
+
function normalizeStringValue(key, value) {
|
|
1557
|
+
if (!PATH_VALUE_RE.test(value.trim())) return value;
|
|
1558
|
+
if (!key) return value;
|
|
1559
|
+
if (PATH_KEY_RE.test(key) || PATH_LIST_KEYS.has(key)) return resolveUserPath(value);
|
|
1560
|
+
return value;
|
|
1561
|
+
}
|
|
1562
|
+
function normalizeAny(key, value) {
|
|
1563
|
+
if (typeof value === "string") return normalizeStringValue(key, value);
|
|
1564
|
+
if (Array.isArray(value)) {
|
|
1565
|
+
const normalizeChildren = Boolean(key && PATH_LIST_KEYS.has(key));
|
|
1566
|
+
return value.map((entry) => {
|
|
1567
|
+
if (typeof entry === "string") return normalizeChildren ? normalizeStringValue(key, entry) : entry;
|
|
1568
|
+
if (Array.isArray(entry)) return normalizeAny(void 0, entry);
|
|
1569
|
+
if (isPlainObject$2(entry)) return normalizeAny(void 0, entry);
|
|
1570
|
+
return entry;
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
if (!isPlainObject$2(value)) return value;
|
|
1574
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
1575
|
+
const next = normalizeAny(childKey, childValue);
|
|
1576
|
+
if (next !== childValue) value[childKey] = next;
|
|
1577
|
+
}
|
|
1578
|
+
return value;
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Normalize "~" paths in path-ish config fields.
|
|
1582
|
+
*
|
|
1583
|
+
* Goal: accept `~/...` consistently across config file + env overrides, while
|
|
1584
|
+
* keeping the surface area small and predictable.
|
|
1585
|
+
*/
|
|
1586
|
+
function normalizeConfigPaths(cfg) {
|
|
1587
|
+
if (!cfg || typeof cfg !== "object") return cfg;
|
|
1588
|
+
normalizeAny(void 0, cfg);
|
|
1589
|
+
return cfg;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
//#endregion
|
|
1593
|
+
//#region src/config/config-paths.ts
|
|
1594
|
+
const BLOCKED_KEYS = new Set([
|
|
1595
|
+
"__proto__",
|
|
1596
|
+
"prototype",
|
|
1597
|
+
"constructor"
|
|
1598
|
+
]);
|
|
1599
|
+
function parseConfigPath(raw) {
|
|
1600
|
+
const trimmed = raw.trim();
|
|
1601
|
+
if (!trimmed) return {
|
|
1602
|
+
ok: false,
|
|
1603
|
+
error: "Invalid path. Use dot notation (e.g. foo.bar)."
|
|
1604
|
+
};
|
|
1605
|
+
const parts = trimmed.split(".").map((part) => part.trim());
|
|
1606
|
+
if (parts.some((part) => !part)) return {
|
|
1607
|
+
ok: false,
|
|
1608
|
+
error: "Invalid path. Use dot notation (e.g. foo.bar)."
|
|
1609
|
+
};
|
|
1610
|
+
if (parts.some((part) => BLOCKED_KEYS.has(part))) return {
|
|
1611
|
+
ok: false,
|
|
1612
|
+
error: "Invalid path segment."
|
|
1613
|
+
};
|
|
1614
|
+
return {
|
|
1615
|
+
ok: true,
|
|
1616
|
+
path: parts
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
function setConfigValueAtPath(root, path, value) {
|
|
1620
|
+
let cursor = root;
|
|
1621
|
+
for (let idx = 0; idx < path.length - 1; idx += 1) {
|
|
1622
|
+
const key = path[idx];
|
|
1623
|
+
const next = cursor[key];
|
|
1624
|
+
if (!isPlainObject$1(next)) cursor[key] = {};
|
|
1625
|
+
cursor = cursor[key];
|
|
1626
|
+
}
|
|
1627
|
+
cursor[path[path.length - 1]] = value;
|
|
1628
|
+
}
|
|
1629
|
+
function unsetConfigValueAtPath(root, path) {
|
|
1630
|
+
const stack = [];
|
|
1631
|
+
let cursor = root;
|
|
1632
|
+
for (let idx = 0; idx < path.length - 1; idx += 1) {
|
|
1633
|
+
const key = path[idx];
|
|
1634
|
+
const next = cursor[key];
|
|
1635
|
+
if (!isPlainObject$1(next)) return false;
|
|
1636
|
+
stack.push({
|
|
1637
|
+
node: cursor,
|
|
1638
|
+
key
|
|
1639
|
+
});
|
|
1640
|
+
cursor = next;
|
|
1641
|
+
}
|
|
1642
|
+
const leafKey = path[path.length - 1];
|
|
1643
|
+
if (!(leafKey in cursor)) return false;
|
|
1644
|
+
delete cursor[leafKey];
|
|
1645
|
+
for (let idx = stack.length - 1; idx >= 0; idx -= 1) {
|
|
1646
|
+
const { node, key } = stack[idx];
|
|
1647
|
+
const child = node[key];
|
|
1648
|
+
if (isPlainObject$1(child) && Object.keys(child).length === 0) delete node[key];
|
|
1649
|
+
else break;
|
|
1650
|
+
}
|
|
1651
|
+
return true;
|
|
1652
|
+
}
|
|
1653
|
+
function getConfigValueAtPath(root, path) {
|
|
1654
|
+
let cursor = root;
|
|
1655
|
+
for (const key of path) {
|
|
1656
|
+
if (!isPlainObject$1(cursor)) return;
|
|
1657
|
+
cursor = cursor[key];
|
|
1658
|
+
}
|
|
1659
|
+
return cursor;
|
|
1660
|
+
}
|
|
1661
|
+
function isPlainObject$1(value) {
|
|
1662
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
//#endregion
|
|
1666
|
+
//#region src/config/runtime-overrides.ts
|
|
1667
|
+
let overrides = {};
|
|
1668
|
+
function mergeOverrides(base, override) {
|
|
1669
|
+
if (!isPlainObject(base) || !isPlainObject(override)) return override;
|
|
1670
|
+
const next = { ...base };
|
|
1671
|
+
for (const [key, value] of Object.entries(override)) {
|
|
1672
|
+
if (value === void 0) continue;
|
|
1673
|
+
next[key] = mergeOverrides(base[key], value);
|
|
1674
|
+
}
|
|
1675
|
+
return next;
|
|
1676
|
+
}
|
|
1677
|
+
function isPlainObject(value) {
|
|
1678
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
|
|
1679
|
+
}
|
|
1680
|
+
function getConfigOverrides() {
|
|
1681
|
+
return overrides;
|
|
1682
|
+
}
|
|
1683
|
+
function resetConfigOverrides() {
|
|
1684
|
+
overrides = {};
|
|
1685
|
+
}
|
|
1686
|
+
function setConfigOverride(pathRaw, value) {
|
|
1687
|
+
const parsed = parseConfigPath(pathRaw);
|
|
1688
|
+
if (!parsed.ok || !parsed.path) return {
|
|
1689
|
+
ok: false,
|
|
1690
|
+
error: parsed.error ?? "Invalid path."
|
|
1691
|
+
};
|
|
1692
|
+
setConfigValueAtPath(overrides, parsed.path, value);
|
|
1693
|
+
return { ok: true };
|
|
1694
|
+
}
|
|
1695
|
+
function unsetConfigOverride(pathRaw) {
|
|
1696
|
+
const parsed = parseConfigPath(pathRaw);
|
|
1697
|
+
if (!parsed.ok || !parsed.path) return {
|
|
1698
|
+
ok: false,
|
|
1699
|
+
removed: false,
|
|
1700
|
+
error: parsed.error ?? "Invalid path."
|
|
1701
|
+
};
|
|
1702
|
+
return {
|
|
1703
|
+
ok: true,
|
|
1704
|
+
removed: unsetConfigValueAtPath(overrides, parsed.path)
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
function applyConfigOverrides(cfg) {
|
|
1708
|
+
if (!overrides || Object.keys(overrides).length === 0) return cfg;
|
|
1709
|
+
return mergeOverrides(cfg, overrides);
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
//#endregion
|
|
1713
|
+
//#region src/plugins/schema-validator.ts
|
|
1714
|
+
const ajv = new AjvPkg({
|
|
1715
|
+
allErrors: true,
|
|
1716
|
+
strict: false,
|
|
1717
|
+
removeAdditional: false
|
|
1718
|
+
});
|
|
1719
|
+
const schemaCache = /* @__PURE__ */ new Map();
|
|
1720
|
+
function formatAjvErrors(errors) {
|
|
1721
|
+
if (!errors || errors.length === 0) return ["invalid config"];
|
|
1722
|
+
return errors.map((error) => {
|
|
1723
|
+
return `${error.instancePath?.replace(/^\//, "").replace(/\//g, ".") || "<root>"}: ${error.message ?? "invalid"}`;
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
function validateJsonSchemaValue(params) {
|
|
1727
|
+
let cached = schemaCache.get(params.cacheKey);
|
|
1728
|
+
if (!cached || cached.schema !== params.schema) {
|
|
1729
|
+
cached = {
|
|
1730
|
+
validate: ajv.compile(params.schema),
|
|
1731
|
+
schema: params.schema
|
|
1732
|
+
};
|
|
1733
|
+
schemaCache.set(params.cacheKey, cached);
|
|
1734
|
+
}
|
|
1735
|
+
if (cached.validate(params.value)) return { ok: true };
|
|
1736
|
+
return {
|
|
1737
|
+
ok: false,
|
|
1738
|
+
errors: formatAjvErrors(cached.validate.errors)
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
//#endregion
|
|
1743
|
+
//#region src/cli/parse-duration.ts
|
|
1744
|
+
function parseDurationMs(raw, opts) {
|
|
1745
|
+
const trimmed = String(raw ?? "").trim().toLowerCase();
|
|
1746
|
+
if (!trimmed) throw new Error("invalid duration (empty)");
|
|
1747
|
+
const m = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)?$/.exec(trimmed);
|
|
1748
|
+
if (!m) throw new Error(`invalid duration: ${raw}`);
|
|
1749
|
+
const value = Number(m[1]);
|
|
1750
|
+
if (!Number.isFinite(value) || value < 0) throw new Error(`invalid duration: ${raw}`);
|
|
1751
|
+
const unit = m[2] ?? opts?.defaultUnit ?? "ms";
|
|
1752
|
+
const multiplier = unit === "ms" ? 1 : unit === "s" ? 1e3 : unit === "m" ? 6e4 : unit === "h" ? 36e5 : 864e5;
|
|
1753
|
+
const ms = Math.round(value * multiplier);
|
|
1754
|
+
if (!Number.isFinite(ms)) throw new Error(`invalid duration: ${raw}`);
|
|
1755
|
+
return ms;
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
//#endregion
|
|
1759
|
+
//#region src/infra/exec-safety.ts
|
|
1760
|
+
const SHELL_METACHARS = /[;&|`$<>]/;
|
|
1761
|
+
const CONTROL_CHARS = /[\r\n]/;
|
|
1762
|
+
const QUOTE_CHARS = /["']/;
|
|
1763
|
+
const BARE_NAME_PATTERN = /^[A-Za-z0-9._+-]+$/;
|
|
1764
|
+
function isLikelyPath(value) {
|
|
1765
|
+
if (value.startsWith(".") || value.startsWith("~")) return true;
|
|
1766
|
+
if (value.includes("/") || value.includes("\\")) return true;
|
|
1767
|
+
return /^[A-Za-z]:[\\/]/.test(value);
|
|
1768
|
+
}
|
|
1769
|
+
function isSafeExecutableValue(value) {
|
|
1770
|
+
if (!value) return false;
|
|
1771
|
+
const trimmed = value.trim();
|
|
1772
|
+
if (!trimmed) return false;
|
|
1773
|
+
if (trimmed.includes("\0")) return false;
|
|
1774
|
+
if (CONTROL_CHARS.test(trimmed)) return false;
|
|
1775
|
+
if (SHELL_METACHARS.test(trimmed)) return false;
|
|
1776
|
+
if (QUOTE_CHARS.test(trimmed)) return false;
|
|
1777
|
+
if (isLikelyPath(trimmed)) return true;
|
|
1778
|
+
if (trimmed.startsWith("-")) return false;
|
|
1779
|
+
return BARE_NAME_PATTERN.test(trimmed);
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
//#endregion
|
|
1783
|
+
//#region src/config/zod-schema.core.ts
|
|
1784
|
+
const ModelApiSchema = z.union([
|
|
1785
|
+
z.literal("openai-completions"),
|
|
1786
|
+
z.literal("openai-responses"),
|
|
1787
|
+
z.literal("anthropic-messages"),
|
|
1788
|
+
z.literal("google-generative-ai"),
|
|
1789
|
+
z.literal("github-copilot"),
|
|
1790
|
+
z.literal("bedrock-converse-stream")
|
|
1791
|
+
]);
|
|
1792
|
+
const ModelCompatSchema = z.object({
|
|
1793
|
+
supportsStore: z.boolean().optional(),
|
|
1794
|
+
supportsDeveloperRole: z.boolean().optional(),
|
|
1795
|
+
supportsReasoningEffort: z.boolean().optional(),
|
|
1796
|
+
maxTokensField: z.union([z.literal("max_completion_tokens"), z.literal("max_tokens")]).optional()
|
|
1797
|
+
}).strict().optional();
|
|
1798
|
+
const ModelDefinitionSchema = z.object({
|
|
1799
|
+
id: z.string().min(1),
|
|
1800
|
+
name: z.string().min(1),
|
|
1801
|
+
api: ModelApiSchema.optional(),
|
|
1802
|
+
reasoning: z.boolean().optional(),
|
|
1803
|
+
input: z.array(z.union([z.literal("text"), z.literal("image")])).optional(),
|
|
1804
|
+
cost: z.object({
|
|
1805
|
+
input: z.number().optional(),
|
|
1806
|
+
output: z.number().optional(),
|
|
1807
|
+
cacheRead: z.number().optional(),
|
|
1808
|
+
cacheWrite: z.number().optional()
|
|
1809
|
+
}).strict().optional(),
|
|
1810
|
+
contextWindow: z.number().positive().optional(),
|
|
1811
|
+
maxTokens: z.number().positive().optional(),
|
|
1812
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
1813
|
+
compat: ModelCompatSchema
|
|
1814
|
+
}).strict();
|
|
1815
|
+
const ModelProviderSchema = z.object({
|
|
1816
|
+
baseUrl: z.string().min(1),
|
|
1817
|
+
apiKey: z.string().optional(),
|
|
1818
|
+
auth: z.union([
|
|
1819
|
+
z.literal("api-key"),
|
|
1820
|
+
z.literal("aws-sdk"),
|
|
1821
|
+
z.literal("oauth"),
|
|
1822
|
+
z.literal("token")
|
|
1823
|
+
]).optional(),
|
|
1824
|
+
api: ModelApiSchema.optional(),
|
|
1825
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
1826
|
+
authHeader: z.boolean().optional(),
|
|
1827
|
+
models: z.array(ModelDefinitionSchema)
|
|
1828
|
+
}).strict();
|
|
1829
|
+
const BedrockDiscoverySchema = z.object({
|
|
1830
|
+
enabled: z.boolean().optional(),
|
|
1831
|
+
region: z.string().optional(),
|
|
1832
|
+
providerFilter: z.array(z.string()).optional(),
|
|
1833
|
+
refreshInterval: z.number().int().nonnegative().optional(),
|
|
1834
|
+
defaultContextWindow: z.number().int().positive().optional(),
|
|
1835
|
+
defaultMaxTokens: z.number().int().positive().optional()
|
|
1836
|
+
}).strict().optional();
|
|
1837
|
+
const ModelsConfigSchema = z.object({
|
|
1838
|
+
mode: z.union([z.literal("merge"), z.literal("replace")]).optional(),
|
|
1839
|
+
providers: z.record(z.string(), ModelProviderSchema).optional(),
|
|
1840
|
+
bedrockDiscovery: BedrockDiscoverySchema
|
|
1841
|
+
}).strict().optional();
|
|
1842
|
+
const GroupChatSchema = z.object({
|
|
1843
|
+
mentionPatterns: z.array(z.string()).optional(),
|
|
1844
|
+
historyLimit: z.number().int().positive().optional()
|
|
1845
|
+
}).strict().optional();
|
|
1846
|
+
const DmConfigSchema = z.object({ historyLimit: z.number().int().min(0).optional() }).strict();
|
|
1847
|
+
const IdentitySchema = z.object({
|
|
1848
|
+
name: z.string().optional(),
|
|
1849
|
+
theme: z.string().optional(),
|
|
1850
|
+
emoji: z.string().optional(),
|
|
1851
|
+
avatar: z.string().optional()
|
|
1852
|
+
}).strict().optional();
|
|
1853
|
+
const QueueModeSchema = z.union([
|
|
1854
|
+
z.literal("steer"),
|
|
1855
|
+
z.literal("followup"),
|
|
1856
|
+
z.literal("collect"),
|
|
1857
|
+
z.literal("steer-backlog"),
|
|
1858
|
+
z.literal("steer+backlog"),
|
|
1859
|
+
z.literal("queue"),
|
|
1860
|
+
z.literal("interrupt")
|
|
1861
|
+
]);
|
|
1862
|
+
const QueueDropSchema = z.union([
|
|
1863
|
+
z.literal("old"),
|
|
1864
|
+
z.literal("new"),
|
|
1865
|
+
z.literal("summarize")
|
|
1866
|
+
]);
|
|
1867
|
+
const ReplyToModeSchema = z.union([
|
|
1868
|
+
z.literal("off"),
|
|
1869
|
+
z.literal("first"),
|
|
1870
|
+
z.literal("all")
|
|
1871
|
+
]);
|
|
1872
|
+
const GroupPolicySchema = z.enum([
|
|
1873
|
+
"open",
|
|
1874
|
+
"disabled",
|
|
1875
|
+
"allowlist"
|
|
1876
|
+
]);
|
|
1877
|
+
const DmPolicySchema = z.enum([
|
|
1878
|
+
"pairing",
|
|
1879
|
+
"allowlist",
|
|
1880
|
+
"open",
|
|
1881
|
+
"disabled"
|
|
1882
|
+
]);
|
|
1883
|
+
const BlockStreamingCoalesceSchema = z.object({
|
|
1884
|
+
minChars: z.number().int().positive().optional(),
|
|
1885
|
+
maxChars: z.number().int().positive().optional(),
|
|
1886
|
+
idleMs: z.number().int().nonnegative().optional()
|
|
1887
|
+
}).strict();
|
|
1888
|
+
const BlockStreamingChunkSchema = z.object({
|
|
1889
|
+
minChars: z.number().int().positive().optional(),
|
|
1890
|
+
maxChars: z.number().int().positive().optional(),
|
|
1891
|
+
breakPreference: z.union([
|
|
1892
|
+
z.literal("paragraph"),
|
|
1893
|
+
z.literal("newline"),
|
|
1894
|
+
z.literal("sentence")
|
|
1895
|
+
]).optional()
|
|
1896
|
+
}).strict();
|
|
1897
|
+
const MarkdownTableModeSchema = z.enum([
|
|
1898
|
+
"off",
|
|
1899
|
+
"bullets",
|
|
1900
|
+
"code"
|
|
1901
|
+
]);
|
|
1902
|
+
const MarkdownConfigSchema = z.object({ tables: MarkdownTableModeSchema.optional() }).strict().optional();
|
|
1903
|
+
const TtsProviderSchema = z.enum([
|
|
1904
|
+
"elevenlabs",
|
|
1905
|
+
"openai",
|
|
1906
|
+
"edge"
|
|
1907
|
+
]);
|
|
1908
|
+
const TtsModeSchema = z.enum(["final", "all"]);
|
|
1909
|
+
const TtsAutoSchema = z.enum([
|
|
1910
|
+
"off",
|
|
1911
|
+
"always",
|
|
1912
|
+
"inbound",
|
|
1913
|
+
"tagged"
|
|
1914
|
+
]);
|
|
1915
|
+
const TtsConfigSchema = z.object({
|
|
1916
|
+
auto: TtsAutoSchema.optional(),
|
|
1917
|
+
enabled: z.boolean().optional(),
|
|
1918
|
+
mode: TtsModeSchema.optional(),
|
|
1919
|
+
provider: TtsProviderSchema.optional(),
|
|
1920
|
+
summaryModel: z.string().optional(),
|
|
1921
|
+
modelOverrides: z.object({
|
|
1922
|
+
enabled: z.boolean().optional(),
|
|
1923
|
+
allowText: z.boolean().optional(),
|
|
1924
|
+
allowProvider: z.boolean().optional(),
|
|
1925
|
+
allowVoice: z.boolean().optional(),
|
|
1926
|
+
allowModelId: z.boolean().optional(),
|
|
1927
|
+
allowVoiceSettings: z.boolean().optional(),
|
|
1928
|
+
allowNormalization: z.boolean().optional(),
|
|
1929
|
+
allowSeed: z.boolean().optional()
|
|
1930
|
+
}).strict().optional(),
|
|
1931
|
+
elevenlabs: z.object({
|
|
1932
|
+
apiKey: z.string().optional(),
|
|
1933
|
+
baseUrl: z.string().optional(),
|
|
1934
|
+
voiceId: z.string().optional(),
|
|
1935
|
+
modelId: z.string().optional(),
|
|
1936
|
+
seed: z.number().int().min(0).max(4294967295).optional(),
|
|
1937
|
+
applyTextNormalization: z.enum([
|
|
1938
|
+
"auto",
|
|
1939
|
+
"on",
|
|
1940
|
+
"off"
|
|
1941
|
+
]).optional(),
|
|
1942
|
+
languageCode: z.string().optional(),
|
|
1943
|
+
voiceSettings: z.object({
|
|
1944
|
+
stability: z.number().min(0).max(1).optional(),
|
|
1945
|
+
similarityBoost: z.number().min(0).max(1).optional(),
|
|
1946
|
+
style: z.number().min(0).max(1).optional(),
|
|
1947
|
+
useSpeakerBoost: z.boolean().optional(),
|
|
1948
|
+
speed: z.number().min(.5).max(2).optional()
|
|
1949
|
+
}).strict().optional()
|
|
1950
|
+
}).strict().optional(),
|
|
1951
|
+
openai: z.object({
|
|
1952
|
+
apiKey: z.string().optional(),
|
|
1953
|
+
model: z.string().optional(),
|
|
1954
|
+
voice: z.string().optional()
|
|
1955
|
+
}).strict().optional(),
|
|
1956
|
+
edge: z.object({
|
|
1957
|
+
enabled: z.boolean().optional(),
|
|
1958
|
+
voice: z.string().optional(),
|
|
1959
|
+
lang: z.string().optional(),
|
|
1960
|
+
outputFormat: z.string().optional(),
|
|
1961
|
+
pitch: z.string().optional(),
|
|
1962
|
+
rate: z.string().optional(),
|
|
1963
|
+
volume: z.string().optional(),
|
|
1964
|
+
saveSubtitles: z.boolean().optional(),
|
|
1965
|
+
proxy: z.string().optional(),
|
|
1966
|
+
timeoutMs: z.number().int().min(1e3).max(12e4).optional()
|
|
1967
|
+
}).strict().optional(),
|
|
1968
|
+
prefsPath: z.string().optional(),
|
|
1969
|
+
maxTextLength: z.number().int().min(1).optional(),
|
|
1970
|
+
timeoutMs: z.number().int().min(1e3).max(12e4).optional()
|
|
1971
|
+
}).strict().optional();
|
|
1972
|
+
const HumanDelaySchema = z.object({
|
|
1973
|
+
mode: z.union([
|
|
1974
|
+
z.literal("off"),
|
|
1975
|
+
z.literal("natural"),
|
|
1976
|
+
z.literal("custom")
|
|
1977
|
+
]).optional(),
|
|
1978
|
+
minMs: z.number().int().nonnegative().optional(),
|
|
1979
|
+
maxMs: z.number().int().nonnegative().optional()
|
|
1980
|
+
}).strict();
|
|
1981
|
+
const CliBackendSchema = z.object({
|
|
1982
|
+
command: z.string(),
|
|
1983
|
+
args: z.array(z.string()).optional(),
|
|
1984
|
+
output: z.union([
|
|
1985
|
+
z.literal("json"),
|
|
1986
|
+
z.literal("text"),
|
|
1987
|
+
z.literal("jsonl")
|
|
1988
|
+
]).optional(),
|
|
1989
|
+
resumeOutput: z.union([
|
|
1990
|
+
z.literal("json"),
|
|
1991
|
+
z.literal("text"),
|
|
1992
|
+
z.literal("jsonl")
|
|
1993
|
+
]).optional(),
|
|
1994
|
+
input: z.union([z.literal("arg"), z.literal("stdin")]).optional(),
|
|
1995
|
+
maxPromptArgChars: z.number().int().positive().optional(),
|
|
1996
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
1997
|
+
clearEnv: z.array(z.string()).optional(),
|
|
1998
|
+
modelArg: z.string().optional(),
|
|
1999
|
+
modelAliases: z.record(z.string(), z.string()).optional(),
|
|
2000
|
+
sessionArg: z.string().optional(),
|
|
2001
|
+
sessionArgs: z.array(z.string()).optional(),
|
|
2002
|
+
resumeArgs: z.array(z.string()).optional(),
|
|
2003
|
+
sessionMode: z.union([
|
|
2004
|
+
z.literal("always"),
|
|
2005
|
+
z.literal("existing"),
|
|
2006
|
+
z.literal("none")
|
|
2007
|
+
]).optional(),
|
|
2008
|
+
sessionIdFields: z.array(z.string()).optional(),
|
|
2009
|
+
systemPromptArg: z.string().optional(),
|
|
2010
|
+
systemPromptMode: z.union([z.literal("append"), z.literal("replace")]).optional(),
|
|
2011
|
+
systemPromptWhen: z.union([
|
|
2012
|
+
z.literal("first"),
|
|
2013
|
+
z.literal("always"),
|
|
2014
|
+
z.literal("never")
|
|
2015
|
+
]).optional(),
|
|
2016
|
+
imageArg: z.string().optional(),
|
|
2017
|
+
imageMode: z.union([z.literal("repeat"), z.literal("list")]).optional(),
|
|
2018
|
+
serialize: z.boolean().optional()
|
|
2019
|
+
}).strict();
|
|
2020
|
+
const normalizeAllowFrom = (values) => (values ?? []).map((v) => String(v).trim()).filter(Boolean);
|
|
2021
|
+
const requireOpenAllowFrom = (params) => {
|
|
2022
|
+
if (params.policy !== "open") return;
|
|
2023
|
+
if (normalizeAllowFrom(params.allowFrom).includes("*")) return;
|
|
2024
|
+
params.ctx.addIssue({
|
|
2025
|
+
code: z.ZodIssueCode.custom,
|
|
2026
|
+
path: params.path,
|
|
2027
|
+
message: params.message
|
|
2028
|
+
});
|
|
2029
|
+
};
|
|
2030
|
+
const MSTeamsReplyStyleSchema = z.enum(["thread", "top-level"]);
|
|
2031
|
+
const RetryConfigSchema = z.object({
|
|
2032
|
+
attempts: z.number().int().min(1).optional(),
|
|
2033
|
+
minDelayMs: z.number().int().min(0).optional(),
|
|
2034
|
+
maxDelayMs: z.number().int().min(0).optional(),
|
|
2035
|
+
jitter: z.number().min(0).max(1).optional()
|
|
2036
|
+
}).strict().optional();
|
|
2037
|
+
const QueueModeBySurfaceSchema = z.object({
|
|
2038
|
+
whatsapp: QueueModeSchema.optional(),
|
|
2039
|
+
telegram: QueueModeSchema.optional(),
|
|
2040
|
+
discord: QueueModeSchema.optional(),
|
|
2041
|
+
slack: QueueModeSchema.optional(),
|
|
2042
|
+
mattermost: QueueModeSchema.optional(),
|
|
2043
|
+
signal: QueueModeSchema.optional(),
|
|
2044
|
+
imessage: QueueModeSchema.optional(),
|
|
2045
|
+
msteams: QueueModeSchema.optional(),
|
|
2046
|
+
webchat: QueueModeSchema.optional()
|
|
2047
|
+
}).strict().optional();
|
|
2048
|
+
const DebounceMsBySurfaceSchema = z.record(z.string(), z.number().int().nonnegative()).optional();
|
|
2049
|
+
const QueueSchema = z.object({
|
|
2050
|
+
mode: QueueModeSchema.optional(),
|
|
2051
|
+
byChannel: QueueModeBySurfaceSchema,
|
|
2052
|
+
debounceMs: z.number().int().nonnegative().optional(),
|
|
2053
|
+
debounceMsByChannel: DebounceMsBySurfaceSchema,
|
|
2054
|
+
cap: z.number().int().positive().optional(),
|
|
2055
|
+
drop: QueueDropSchema.optional()
|
|
2056
|
+
}).strict().optional();
|
|
2057
|
+
const InboundDebounceSchema = z.object({
|
|
2058
|
+
debounceMs: z.number().int().nonnegative().optional(),
|
|
2059
|
+
byChannel: DebounceMsBySurfaceSchema
|
|
2060
|
+
}).strict().optional();
|
|
2061
|
+
const TranscribeAudioSchema = z.object({
|
|
2062
|
+
command: z.array(z.string()).superRefine((value, ctx) => {
|
|
2063
|
+
const executable = value[0];
|
|
2064
|
+
if (!isSafeExecutableValue(executable)) ctx.addIssue({
|
|
2065
|
+
code: z.ZodIssueCode.custom,
|
|
2066
|
+
path: [0],
|
|
2067
|
+
message: "expected safe executable name or path"
|
|
2068
|
+
});
|
|
2069
|
+
}),
|
|
2070
|
+
timeoutSeconds: z.number().int().positive().optional()
|
|
2071
|
+
}).strict().optional();
|
|
2072
|
+
const HexColorSchema = z.string().regex(/^#?[0-9a-fA-F]{6}$/, "expected hex color (RRGGBB)");
|
|
2073
|
+
const ExecutableTokenSchema = z.string().refine(isSafeExecutableValue, "expected safe executable name or path");
|
|
2074
|
+
const MediaUnderstandingScopeSchema = z.object({
|
|
2075
|
+
default: z.union([z.literal("allow"), z.literal("deny")]).optional(),
|
|
2076
|
+
rules: z.array(z.object({
|
|
2077
|
+
action: z.union([z.literal("allow"), z.literal("deny")]),
|
|
2078
|
+
match: z.object({
|
|
2079
|
+
channel: z.string().optional(),
|
|
2080
|
+
chatType: z.union([
|
|
2081
|
+
z.literal("direct"),
|
|
2082
|
+
z.literal("group"),
|
|
2083
|
+
z.literal("channel")
|
|
2084
|
+
]).optional(),
|
|
2085
|
+
keyPrefix: z.string().optional()
|
|
2086
|
+
}).strict().optional()
|
|
2087
|
+
}).strict()).optional()
|
|
2088
|
+
}).strict().optional();
|
|
2089
|
+
const MediaUnderstandingCapabilitiesSchema = z.array(z.union([
|
|
2090
|
+
z.literal("image"),
|
|
2091
|
+
z.literal("audio"),
|
|
2092
|
+
z.literal("video")
|
|
2093
|
+
])).optional();
|
|
2094
|
+
const MediaUnderstandingAttachmentsSchema = z.object({
|
|
2095
|
+
mode: z.union([z.literal("first"), z.literal("all")]).optional(),
|
|
2096
|
+
maxAttachments: z.number().int().positive().optional(),
|
|
2097
|
+
prefer: z.union([
|
|
2098
|
+
z.literal("first"),
|
|
2099
|
+
z.literal("last"),
|
|
2100
|
+
z.literal("path"),
|
|
2101
|
+
z.literal("url")
|
|
2102
|
+
]).optional()
|
|
2103
|
+
}).strict().optional();
|
|
2104
|
+
const DeepgramAudioSchema = z.object({
|
|
2105
|
+
detectLanguage: z.boolean().optional(),
|
|
2106
|
+
punctuate: z.boolean().optional(),
|
|
2107
|
+
smartFormat: z.boolean().optional()
|
|
2108
|
+
}).strict().optional();
|
|
2109
|
+
const ProviderOptionValueSchema = z.union([
|
|
2110
|
+
z.string(),
|
|
2111
|
+
z.number(),
|
|
2112
|
+
z.boolean()
|
|
2113
|
+
]);
|
|
2114
|
+
const ProviderOptionsSchema = z.record(z.string(), z.record(z.string(), ProviderOptionValueSchema)).optional();
|
|
2115
|
+
const MediaUnderstandingModelSchema = z.object({
|
|
2116
|
+
provider: z.string().optional(),
|
|
2117
|
+
model: z.string().optional(),
|
|
2118
|
+
capabilities: MediaUnderstandingCapabilitiesSchema,
|
|
2119
|
+
type: z.union([z.literal("provider"), z.literal("cli")]).optional(),
|
|
2120
|
+
command: z.string().optional(),
|
|
2121
|
+
args: z.array(z.string()).optional(),
|
|
2122
|
+
prompt: z.string().optional(),
|
|
2123
|
+
maxChars: z.number().int().positive().optional(),
|
|
2124
|
+
maxBytes: z.number().int().positive().optional(),
|
|
2125
|
+
timeoutSeconds: z.number().int().positive().optional(),
|
|
2126
|
+
language: z.string().optional(),
|
|
2127
|
+
providerOptions: ProviderOptionsSchema,
|
|
2128
|
+
deepgram: DeepgramAudioSchema,
|
|
2129
|
+
baseUrl: z.string().optional(),
|
|
2130
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
2131
|
+
profile: z.string().optional(),
|
|
2132
|
+
preferredProfile: z.string().optional()
|
|
2133
|
+
}).strict().optional();
|
|
2134
|
+
const ToolsMediaUnderstandingSchema = z.object({
|
|
2135
|
+
enabled: z.boolean().optional(),
|
|
2136
|
+
scope: MediaUnderstandingScopeSchema,
|
|
2137
|
+
maxBytes: z.number().int().positive().optional(),
|
|
2138
|
+
maxChars: z.number().int().positive().optional(),
|
|
2139
|
+
prompt: z.string().optional(),
|
|
2140
|
+
timeoutSeconds: z.number().int().positive().optional(),
|
|
2141
|
+
language: z.string().optional(),
|
|
2142
|
+
providerOptions: ProviderOptionsSchema,
|
|
2143
|
+
deepgram: DeepgramAudioSchema,
|
|
2144
|
+
baseUrl: z.string().optional(),
|
|
2145
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
2146
|
+
attachments: MediaUnderstandingAttachmentsSchema,
|
|
2147
|
+
models: z.array(MediaUnderstandingModelSchema).optional()
|
|
2148
|
+
}).strict().optional();
|
|
2149
|
+
const ToolsMediaSchema = z.object({
|
|
2150
|
+
models: z.array(MediaUnderstandingModelSchema).optional(),
|
|
2151
|
+
concurrency: z.number().int().positive().optional(),
|
|
2152
|
+
image: ToolsMediaUnderstandingSchema.optional(),
|
|
2153
|
+
audio: ToolsMediaUnderstandingSchema.optional(),
|
|
2154
|
+
video: ToolsMediaUnderstandingSchema.optional()
|
|
2155
|
+
}).strict().optional();
|
|
2156
|
+
const LinkModelSchema = z.object({
|
|
2157
|
+
type: z.literal("cli").optional(),
|
|
2158
|
+
command: z.string().min(1),
|
|
2159
|
+
args: z.array(z.string()).optional(),
|
|
2160
|
+
timeoutSeconds: z.number().int().positive().optional()
|
|
2161
|
+
}).strict();
|
|
2162
|
+
const ToolsLinksSchema = z.object({
|
|
2163
|
+
enabled: z.boolean().optional(),
|
|
2164
|
+
scope: MediaUnderstandingScopeSchema,
|
|
2165
|
+
maxLinks: z.number().int().positive().optional(),
|
|
2166
|
+
timeoutSeconds: z.number().int().positive().optional(),
|
|
2167
|
+
models: z.array(LinkModelSchema).optional()
|
|
2168
|
+
}).strict().optional();
|
|
2169
|
+
const NativeCommandsSettingSchema = z.union([z.boolean(), z.literal("auto")]);
|
|
2170
|
+
const ProviderCommandsSchema = z.object({
|
|
2171
|
+
native: NativeCommandsSettingSchema.optional(),
|
|
2172
|
+
nativeSkills: NativeCommandsSettingSchema.optional()
|
|
2173
|
+
}).strict().optional();
|
|
2174
|
+
|
|
2175
|
+
//#endregion
|
|
2176
|
+
//#region src/config/zod-schema.agent-runtime.ts
|
|
2177
|
+
const HeartbeatSchema = z.object({
|
|
2178
|
+
every: z.string().optional(),
|
|
2179
|
+
activeHours: z.object({
|
|
2180
|
+
start: z.string().optional(),
|
|
2181
|
+
end: z.string().optional(),
|
|
2182
|
+
timezone: z.string().optional()
|
|
2183
|
+
}).strict().optional(),
|
|
2184
|
+
model: z.string().optional(),
|
|
2185
|
+
session: z.string().optional(),
|
|
2186
|
+
includeReasoning: z.boolean().optional(),
|
|
2187
|
+
target: z.string().optional(),
|
|
2188
|
+
to: z.string().optional(),
|
|
2189
|
+
accountId: z.string().optional(),
|
|
2190
|
+
prompt: z.string().optional(),
|
|
2191
|
+
ackMaxChars: z.number().int().nonnegative().optional()
|
|
2192
|
+
}).strict().superRefine((val, ctx) => {
|
|
2193
|
+
if (!val.every) return;
|
|
2194
|
+
try {
|
|
2195
|
+
parseDurationMs(val.every, { defaultUnit: "m" });
|
|
2196
|
+
} catch {
|
|
2197
|
+
ctx.addIssue({
|
|
2198
|
+
code: z.ZodIssueCode.custom,
|
|
2199
|
+
path: ["every"],
|
|
2200
|
+
message: "invalid duration (use ms, s, m, h)"
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
const active = val.activeHours;
|
|
2204
|
+
if (!active) return;
|
|
2205
|
+
const timePattern = /^([01]\d|2[0-3]|24):([0-5]\d)$/;
|
|
2206
|
+
const validateTime = (raw, opts, path) => {
|
|
2207
|
+
if (!raw) return;
|
|
2208
|
+
if (!timePattern.test(raw)) {
|
|
2209
|
+
ctx.addIssue({
|
|
2210
|
+
code: z.ZodIssueCode.custom,
|
|
2211
|
+
path: ["activeHours", path],
|
|
2212
|
+
message: "invalid time (use \"HH:MM\" 24h format)"
|
|
2213
|
+
});
|
|
2214
|
+
return;
|
|
2215
|
+
}
|
|
2216
|
+
const [hourStr, minuteStr] = raw.split(":");
|
|
2217
|
+
const hour = Number(hourStr);
|
|
2218
|
+
if (hour === 24 && Number(minuteStr) !== 0) {
|
|
2219
|
+
ctx.addIssue({
|
|
2220
|
+
code: z.ZodIssueCode.custom,
|
|
2221
|
+
path: ["activeHours", path],
|
|
2222
|
+
message: "invalid time (24:00 is the only allowed 24:xx value)"
|
|
2223
|
+
});
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
if (hour === 24 && !opts.allow24) ctx.addIssue({
|
|
2227
|
+
code: z.ZodIssueCode.custom,
|
|
2228
|
+
path: ["activeHours", path],
|
|
2229
|
+
message: "invalid time (start cannot be 24:00)"
|
|
2230
|
+
});
|
|
2231
|
+
};
|
|
2232
|
+
validateTime(active.start, { allow24: false }, "start");
|
|
2233
|
+
validateTime(active.end, { allow24: true }, "end");
|
|
2234
|
+
}).optional();
|
|
2235
|
+
const SandboxDockerSchema = z.object({
|
|
2236
|
+
image: z.string().optional(),
|
|
2237
|
+
containerPrefix: z.string().optional(),
|
|
2238
|
+
workdir: z.string().optional(),
|
|
2239
|
+
readOnlyRoot: z.boolean().optional(),
|
|
2240
|
+
tmpfs: z.array(z.string()).optional(),
|
|
2241
|
+
network: z.string().optional(),
|
|
2242
|
+
user: z.string().optional(),
|
|
2243
|
+
capDrop: z.array(z.string()).optional(),
|
|
2244
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
2245
|
+
setupCommand: z.string().optional(),
|
|
2246
|
+
pidsLimit: z.number().int().positive().optional(),
|
|
2247
|
+
memory: z.union([z.string(), z.number()]).optional(),
|
|
2248
|
+
memorySwap: z.union([z.string(), z.number()]).optional(),
|
|
2249
|
+
cpus: z.number().positive().optional(),
|
|
2250
|
+
ulimits: z.record(z.string(), z.union([
|
|
2251
|
+
z.string(),
|
|
2252
|
+
z.number(),
|
|
2253
|
+
z.object({
|
|
2254
|
+
soft: z.number().int().nonnegative().optional(),
|
|
2255
|
+
hard: z.number().int().nonnegative().optional()
|
|
2256
|
+
}).strict()
|
|
2257
|
+
])).optional(),
|
|
2258
|
+
seccompProfile: z.string().optional(),
|
|
2259
|
+
apparmorProfile: z.string().optional(),
|
|
2260
|
+
dns: z.array(z.string()).optional(),
|
|
2261
|
+
extraHosts: z.array(z.string()).optional(),
|
|
2262
|
+
binds: z.array(z.string()).optional()
|
|
2263
|
+
}).strict().optional();
|
|
2264
|
+
const SandboxBrowserSchema = z.object({
|
|
2265
|
+
enabled: z.boolean().optional(),
|
|
2266
|
+
image: z.string().optional(),
|
|
2267
|
+
containerPrefix: z.string().optional(),
|
|
2268
|
+
cdpPort: z.number().int().positive().optional(),
|
|
2269
|
+
vncPort: z.number().int().positive().optional(),
|
|
2270
|
+
noVncPort: z.number().int().positive().optional(),
|
|
2271
|
+
headless: z.boolean().optional(),
|
|
2272
|
+
enableNoVnc: z.boolean().optional(),
|
|
2273
|
+
allowHostControl: z.boolean().optional(),
|
|
2274
|
+
autoStart: z.boolean().optional(),
|
|
2275
|
+
autoStartTimeoutMs: z.number().int().positive().optional()
|
|
2276
|
+
}).strict().optional();
|
|
2277
|
+
const SandboxPruneSchema = z.object({
|
|
2278
|
+
idleHours: z.number().int().nonnegative().optional(),
|
|
2279
|
+
maxAgeDays: z.number().int().nonnegative().optional()
|
|
2280
|
+
}).strict().optional();
|
|
2281
|
+
const ToolPolicyBaseSchema = z.object({
|
|
2282
|
+
allow: z.array(z.string()).optional(),
|
|
2283
|
+
alsoAllow: z.array(z.string()).optional(),
|
|
2284
|
+
deny: z.array(z.string()).optional()
|
|
2285
|
+
}).strict();
|
|
2286
|
+
const ToolPolicySchema = ToolPolicyBaseSchema.superRefine((value, ctx) => {
|
|
2287
|
+
if (value.allow && value.allow.length > 0 && value.alsoAllow && value.alsoAllow.length > 0) ctx.addIssue({
|
|
2288
|
+
code: z.ZodIssueCode.custom,
|
|
2289
|
+
message: "tools policy cannot set both allow and alsoAllow in the same scope (merge alsoAllow into allow, or remove allow and use profile + alsoAllow)"
|
|
2290
|
+
});
|
|
2291
|
+
}).optional();
|
|
2292
|
+
const ToolsWebSearchSchema = z.object({
|
|
2293
|
+
enabled: z.boolean().optional(),
|
|
2294
|
+
provider: z.union([z.literal("brave"), z.literal("perplexity")]).optional(),
|
|
2295
|
+
apiKey: z.string().optional(),
|
|
2296
|
+
maxResults: z.number().int().positive().optional(),
|
|
2297
|
+
timeoutSeconds: z.number().int().positive().optional(),
|
|
2298
|
+
cacheTtlMinutes: z.number().nonnegative().optional(),
|
|
2299
|
+
perplexity: z.object({
|
|
2300
|
+
apiKey: z.string().optional(),
|
|
2301
|
+
baseUrl: z.string().optional(),
|
|
2302
|
+
model: z.string().optional()
|
|
2303
|
+
}).strict().optional()
|
|
2304
|
+
}).strict().optional();
|
|
2305
|
+
const ToolsWebFetchSchema = z.object({
|
|
2306
|
+
enabled: z.boolean().optional(),
|
|
2307
|
+
maxChars: z.number().int().positive().optional(),
|
|
2308
|
+
maxCharsCap: z.number().int().positive().optional(),
|
|
2309
|
+
timeoutSeconds: z.number().int().positive().optional(),
|
|
2310
|
+
cacheTtlMinutes: z.number().nonnegative().optional(),
|
|
2311
|
+
maxRedirects: z.number().int().nonnegative().optional(),
|
|
2312
|
+
userAgent: z.string().optional()
|
|
2313
|
+
}).strict().optional();
|
|
2314
|
+
const ToolsWebSchema = z.object({
|
|
2315
|
+
search: ToolsWebSearchSchema,
|
|
2316
|
+
fetch: ToolsWebFetchSchema
|
|
2317
|
+
}).strict().optional();
|
|
2318
|
+
const ToolProfileSchema = z.union([
|
|
2319
|
+
z.literal("minimal"),
|
|
2320
|
+
z.literal("coding"),
|
|
2321
|
+
z.literal("messaging"),
|
|
2322
|
+
z.literal("full")
|
|
2323
|
+
]).optional();
|
|
2324
|
+
const ToolPolicyWithProfileSchema = z.object({
|
|
2325
|
+
allow: z.array(z.string()).optional(),
|
|
2326
|
+
alsoAllow: z.array(z.string()).optional(),
|
|
2327
|
+
deny: z.array(z.string()).optional(),
|
|
2328
|
+
profile: ToolProfileSchema
|
|
2329
|
+
}).strict().superRefine((value, ctx) => {
|
|
2330
|
+
if (value.allow && value.allow.length > 0 && value.alsoAllow && value.alsoAllow.length > 0) ctx.addIssue({
|
|
2331
|
+
code: z.ZodIssueCode.custom,
|
|
2332
|
+
message: "tools.byProvider policy cannot set both allow and alsoAllow in the same scope (merge alsoAllow into allow, or remove allow and use profile + alsoAllow)"
|
|
2333
|
+
});
|
|
2334
|
+
});
|
|
2335
|
+
const ElevatedAllowFromSchema = z.record(z.string(), z.array(z.union([z.string(), z.number()]))).optional();
|
|
2336
|
+
const AgentSandboxSchema = z.object({
|
|
2337
|
+
mode: z.union([
|
|
2338
|
+
z.literal("off"),
|
|
2339
|
+
z.literal("non-main"),
|
|
2340
|
+
z.literal("all")
|
|
2341
|
+
]).optional(),
|
|
2342
|
+
workspaceAccess: z.union([
|
|
2343
|
+
z.literal("none"),
|
|
2344
|
+
z.literal("ro"),
|
|
2345
|
+
z.literal("rw")
|
|
2346
|
+
]).optional(),
|
|
2347
|
+
sessionToolsVisibility: z.union([z.literal("spawned"), z.literal("all")]).optional(),
|
|
2348
|
+
scope: z.union([
|
|
2349
|
+
z.literal("session"),
|
|
2350
|
+
z.literal("agent"),
|
|
2351
|
+
z.literal("shared")
|
|
2352
|
+
]).optional(),
|
|
2353
|
+
perSession: z.boolean().optional(),
|
|
2354
|
+
workspaceRoot: z.string().optional(),
|
|
2355
|
+
docker: SandboxDockerSchema,
|
|
2356
|
+
browser: SandboxBrowserSchema,
|
|
2357
|
+
prune: SandboxPruneSchema
|
|
2358
|
+
}).strict().optional();
|
|
2359
|
+
const AgentToolsSchema = z.object({
|
|
2360
|
+
profile: ToolProfileSchema,
|
|
2361
|
+
allow: z.array(z.string()).optional(),
|
|
2362
|
+
alsoAllow: z.array(z.string()).optional(),
|
|
2363
|
+
deny: z.array(z.string()).optional(),
|
|
2364
|
+
byProvider: z.record(z.string(), ToolPolicyWithProfileSchema).optional(),
|
|
2365
|
+
elevated: z.object({
|
|
2366
|
+
enabled: z.boolean().optional(),
|
|
2367
|
+
allowFrom: ElevatedAllowFromSchema
|
|
2368
|
+
}).strict().optional(),
|
|
2369
|
+
exec: z.object({
|
|
2370
|
+
host: z.enum([
|
|
2371
|
+
"sandbox",
|
|
2372
|
+
"gateway",
|
|
2373
|
+
"node"
|
|
2374
|
+
]).optional(),
|
|
2375
|
+
security: z.enum([
|
|
2376
|
+
"deny",
|
|
2377
|
+
"allowlist",
|
|
2378
|
+
"full"
|
|
2379
|
+
]).optional(),
|
|
2380
|
+
ask: z.enum([
|
|
2381
|
+
"off",
|
|
2382
|
+
"on-miss",
|
|
2383
|
+
"always"
|
|
2384
|
+
]).optional(),
|
|
2385
|
+
node: z.string().optional(),
|
|
2386
|
+
pathPrepend: z.array(z.string()).optional(),
|
|
2387
|
+
safeBins: z.array(z.string()).optional(),
|
|
2388
|
+
backgroundMs: z.number().int().positive().optional(),
|
|
2389
|
+
timeoutSec: z.number().int().positive().optional(),
|
|
2390
|
+
approvalRunningNoticeMs: z.number().int().nonnegative().optional(),
|
|
2391
|
+
cleanupMs: z.number().int().positive().optional(),
|
|
2392
|
+
notifyOnExit: z.boolean().optional(),
|
|
2393
|
+
applyPatch: z.object({
|
|
2394
|
+
enabled: z.boolean().optional(),
|
|
2395
|
+
allowModels: z.array(z.string()).optional()
|
|
2396
|
+
}).strict().optional()
|
|
2397
|
+
}).strict().optional(),
|
|
2398
|
+
sandbox: z.object({ tools: ToolPolicySchema }).strict().optional()
|
|
2399
|
+
}).strict().superRefine((value, ctx) => {
|
|
2400
|
+
if (value.allow && value.allow.length > 0 && value.alsoAllow && value.alsoAllow.length > 0) ctx.addIssue({
|
|
2401
|
+
code: z.ZodIssueCode.custom,
|
|
2402
|
+
message: "agent tools cannot set both allow and alsoAllow in the same scope (merge alsoAllow into allow, or remove allow and use profile + alsoAllow)"
|
|
2403
|
+
});
|
|
2404
|
+
}).optional();
|
|
2405
|
+
const MemorySearchSchema = z.object({
|
|
2406
|
+
enabled: z.boolean().optional(),
|
|
2407
|
+
sources: z.array(z.union([z.literal("memory"), z.literal("sessions")])).optional(),
|
|
2408
|
+
extraPaths: z.array(z.string()).optional(),
|
|
2409
|
+
experimental: z.object({ sessionMemory: z.boolean().optional() }).strict().optional(),
|
|
2410
|
+
provider: z.union([
|
|
2411
|
+
z.literal("openai"),
|
|
2412
|
+
z.literal("local"),
|
|
2413
|
+
z.literal("gemini")
|
|
2414
|
+
]).optional(),
|
|
2415
|
+
remote: z.object({
|
|
2416
|
+
baseUrl: z.string().optional(),
|
|
2417
|
+
apiKey: z.string().optional(),
|
|
2418
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
2419
|
+
batch: z.object({
|
|
2420
|
+
enabled: z.boolean().optional(),
|
|
2421
|
+
wait: z.boolean().optional(),
|
|
2422
|
+
concurrency: z.number().int().positive().optional(),
|
|
2423
|
+
pollIntervalMs: z.number().int().nonnegative().optional(),
|
|
2424
|
+
timeoutMinutes: z.number().int().positive().optional()
|
|
2425
|
+
}).strict().optional()
|
|
2426
|
+
}).strict().optional(),
|
|
2427
|
+
fallback: z.union([
|
|
2428
|
+
z.literal("openai"),
|
|
2429
|
+
z.literal("gemini"),
|
|
2430
|
+
z.literal("local"),
|
|
2431
|
+
z.literal("none")
|
|
2432
|
+
]).optional(),
|
|
2433
|
+
model: z.string().optional(),
|
|
2434
|
+
local: z.object({
|
|
2435
|
+
modelPath: z.string().optional(),
|
|
2436
|
+
modelCacheDir: z.string().optional()
|
|
2437
|
+
}).strict().optional(),
|
|
2438
|
+
store: z.object({
|
|
2439
|
+
driver: z.literal("sqlite").optional(),
|
|
2440
|
+
path: z.string().optional(),
|
|
2441
|
+
vector: z.object({
|
|
2442
|
+
enabled: z.boolean().optional(),
|
|
2443
|
+
extensionPath: z.string().optional()
|
|
2444
|
+
}).strict().optional()
|
|
2445
|
+
}).strict().optional(),
|
|
2446
|
+
chunking: z.object({
|
|
2447
|
+
tokens: z.number().int().positive().optional(),
|
|
2448
|
+
overlap: z.number().int().nonnegative().optional()
|
|
2449
|
+
}).strict().optional(),
|
|
2450
|
+
sync: z.object({
|
|
2451
|
+
onSessionStart: z.boolean().optional(),
|
|
2452
|
+
onSearch: z.boolean().optional(),
|
|
2453
|
+
watch: z.boolean().optional(),
|
|
2454
|
+
watchDebounceMs: z.number().int().nonnegative().optional(),
|
|
2455
|
+
intervalMinutes: z.number().int().nonnegative().optional(),
|
|
2456
|
+
sessions: z.object({
|
|
2457
|
+
deltaBytes: z.number().int().nonnegative().optional(),
|
|
2458
|
+
deltaMessages: z.number().int().nonnegative().optional()
|
|
2459
|
+
}).strict().optional()
|
|
2460
|
+
}).strict().optional(),
|
|
2461
|
+
query: z.object({
|
|
2462
|
+
maxResults: z.number().int().positive().optional(),
|
|
2463
|
+
minScore: z.number().min(0).max(1).optional(),
|
|
2464
|
+
hybrid: z.object({
|
|
2465
|
+
enabled: z.boolean().optional(),
|
|
2466
|
+
vectorWeight: z.number().min(0).max(1).optional(),
|
|
2467
|
+
textWeight: z.number().min(0).max(1).optional(),
|
|
2468
|
+
candidateMultiplier: z.number().int().positive().optional()
|
|
2469
|
+
}).strict().optional()
|
|
2470
|
+
}).strict().optional(),
|
|
2471
|
+
cache: z.object({
|
|
2472
|
+
enabled: z.boolean().optional(),
|
|
2473
|
+
maxEntries: z.number().int().positive().optional()
|
|
2474
|
+
}).strict().optional()
|
|
2475
|
+
}).strict().optional();
|
|
2476
|
+
const AgentModelSchema = z.union([z.string(), z.object({
|
|
2477
|
+
primary: z.string().optional(),
|
|
2478
|
+
fallbacks: z.array(z.string()).optional()
|
|
2479
|
+
}).strict()]);
|
|
2480
|
+
const AgentEntrySchema = z.object({
|
|
2481
|
+
id: z.string(),
|
|
2482
|
+
default: z.boolean().optional(),
|
|
2483
|
+
name: z.string().optional(),
|
|
2484
|
+
workspace: z.string().optional(),
|
|
2485
|
+
agentDir: z.string().optional(),
|
|
2486
|
+
model: AgentModelSchema.optional(),
|
|
2487
|
+
skills: z.array(z.string()).optional(),
|
|
2488
|
+
memorySearch: MemorySearchSchema,
|
|
2489
|
+
humanDelay: HumanDelaySchema.optional(),
|
|
2490
|
+
heartbeat: HeartbeatSchema,
|
|
2491
|
+
identity: IdentitySchema,
|
|
2492
|
+
groupChat: GroupChatSchema,
|
|
2493
|
+
subagents: z.object({
|
|
2494
|
+
allowAgents: z.array(z.string()).optional(),
|
|
2495
|
+
model: z.union([z.string(), z.object({
|
|
2496
|
+
primary: z.string().optional(),
|
|
2497
|
+
fallbacks: z.array(z.string()).optional()
|
|
2498
|
+
}).strict()]).optional(),
|
|
2499
|
+
thinking: z.string().optional()
|
|
2500
|
+
}).strict().optional(),
|
|
2501
|
+
sandbox: AgentSandboxSchema,
|
|
2502
|
+
tools: AgentToolsSchema
|
|
2503
|
+
}).strict();
|
|
2504
|
+
const ToolsSchema = z.object({
|
|
2505
|
+
profile: ToolProfileSchema,
|
|
2506
|
+
allow: z.array(z.string()).optional(),
|
|
2507
|
+
alsoAllow: z.array(z.string()).optional(),
|
|
2508
|
+
deny: z.array(z.string()).optional(),
|
|
2509
|
+
byProvider: z.record(z.string(), ToolPolicyWithProfileSchema).optional(),
|
|
2510
|
+
web: ToolsWebSchema,
|
|
2511
|
+
media: ToolsMediaSchema,
|
|
2512
|
+
links: ToolsLinksSchema,
|
|
2513
|
+
message: z.object({
|
|
2514
|
+
allowCrossContextSend: z.boolean().optional(),
|
|
2515
|
+
crossContext: z.object({
|
|
2516
|
+
allowWithinProvider: z.boolean().optional(),
|
|
2517
|
+
allowAcrossProviders: z.boolean().optional(),
|
|
2518
|
+
marker: z.object({
|
|
2519
|
+
enabled: z.boolean().optional(),
|
|
2520
|
+
prefix: z.string().optional(),
|
|
2521
|
+
suffix: z.string().optional()
|
|
2522
|
+
}).strict().optional()
|
|
2523
|
+
}).strict().optional(),
|
|
2524
|
+
broadcast: z.object({ enabled: z.boolean().optional() }).strict().optional()
|
|
2525
|
+
}).strict().optional(),
|
|
2526
|
+
agentToAgent: z.object({
|
|
2527
|
+
enabled: z.boolean().optional(),
|
|
2528
|
+
allow: z.array(z.string()).optional()
|
|
2529
|
+
}).strict().optional(),
|
|
2530
|
+
elevated: z.object({
|
|
2531
|
+
enabled: z.boolean().optional(),
|
|
2532
|
+
allowFrom: ElevatedAllowFromSchema
|
|
2533
|
+
}).strict().optional(),
|
|
2534
|
+
exec: z.object({
|
|
2535
|
+
host: z.enum([
|
|
2536
|
+
"sandbox",
|
|
2537
|
+
"gateway",
|
|
2538
|
+
"node"
|
|
2539
|
+
]).optional(),
|
|
2540
|
+
security: z.enum([
|
|
2541
|
+
"deny",
|
|
2542
|
+
"allowlist",
|
|
2543
|
+
"full"
|
|
2544
|
+
]).optional(),
|
|
2545
|
+
ask: z.enum([
|
|
2546
|
+
"off",
|
|
2547
|
+
"on-miss",
|
|
2548
|
+
"always"
|
|
2549
|
+
]).optional(),
|
|
2550
|
+
node: z.string().optional(),
|
|
2551
|
+
pathPrepend: z.array(z.string()).optional(),
|
|
2552
|
+
safeBins: z.array(z.string()).optional(),
|
|
2553
|
+
backgroundMs: z.number().int().positive().optional(),
|
|
2554
|
+
timeoutSec: z.number().int().positive().optional(),
|
|
2555
|
+
cleanupMs: z.number().int().positive().optional(),
|
|
2556
|
+
notifyOnExit: z.boolean().optional(),
|
|
2557
|
+
applyPatch: z.object({
|
|
2558
|
+
enabled: z.boolean().optional(),
|
|
2559
|
+
allowModels: z.array(z.string()).optional()
|
|
2560
|
+
}).strict().optional()
|
|
2561
|
+
}).strict().optional(),
|
|
2562
|
+
subagents: z.object({ tools: ToolPolicySchema }).strict().optional(),
|
|
2563
|
+
sandbox: z.object({ tools: ToolPolicySchema }).strict().optional()
|
|
2564
|
+
}).strict().superRefine((value, ctx) => {
|
|
2565
|
+
if (value.allow && value.allow.length > 0 && value.alsoAllow && value.alsoAllow.length > 0) ctx.addIssue({
|
|
2566
|
+
code: z.ZodIssueCode.custom,
|
|
2567
|
+
message: "tools cannot set both allow and alsoAllow in the same scope (merge alsoAllow into allow, or remove allow and use profile + alsoAllow)"
|
|
2568
|
+
});
|
|
2569
|
+
}).optional();
|
|
2570
|
+
|
|
2571
|
+
//#endregion
|
|
2572
|
+
//#region src/config/zod-schema.agent-defaults.ts
|
|
2573
|
+
const AgentDefaultsSchema = z.object({
|
|
2574
|
+
model: z.object({
|
|
2575
|
+
primary: z.string().optional(),
|
|
2576
|
+
fallbacks: z.array(z.string()).optional()
|
|
2577
|
+
}).strict().optional(),
|
|
2578
|
+
imageModel: z.object({
|
|
2579
|
+
primary: z.string().optional(),
|
|
2580
|
+
fallbacks: z.array(z.string()).optional()
|
|
2581
|
+
}).strict().optional(),
|
|
2582
|
+
models: z.record(z.string(), z.object({
|
|
2583
|
+
alias: z.string().optional(),
|
|
2584
|
+
params: z.record(z.string(), z.unknown()).optional(),
|
|
2585
|
+
streaming: z.boolean().optional()
|
|
2586
|
+
}).strict()).optional(),
|
|
2587
|
+
workspace: z.string().optional(),
|
|
2588
|
+
repoRoot: z.string().optional(),
|
|
2589
|
+
skipBootstrap: z.boolean().optional(),
|
|
2590
|
+
bootstrapMaxChars: z.number().int().positive().optional(),
|
|
2591
|
+
userTimezone: z.string().optional(),
|
|
2592
|
+
timeFormat: z.union([
|
|
2593
|
+
z.literal("auto"),
|
|
2594
|
+
z.literal("12"),
|
|
2595
|
+
z.literal("24")
|
|
2596
|
+
]).optional(),
|
|
2597
|
+
envelopeTimezone: z.string().optional(),
|
|
2598
|
+
envelopeTimestamp: z.union([z.literal("on"), z.literal("off")]).optional(),
|
|
2599
|
+
envelopeElapsed: z.union([z.literal("on"), z.literal("off")]).optional(),
|
|
2600
|
+
contextTokens: z.number().int().positive().optional(),
|
|
2601
|
+
cliBackends: z.record(z.string(), CliBackendSchema).optional(),
|
|
2602
|
+
memorySearch: MemorySearchSchema,
|
|
2603
|
+
contextPruning: z.object({
|
|
2604
|
+
mode: z.union([z.literal("off"), z.literal("cache-ttl")]).optional(),
|
|
2605
|
+
ttl: z.string().optional(),
|
|
2606
|
+
keepLastAssistants: z.number().int().nonnegative().optional(),
|
|
2607
|
+
softTrimRatio: z.number().min(0).max(1).optional(),
|
|
2608
|
+
hardClearRatio: z.number().min(0).max(1).optional(),
|
|
2609
|
+
minPrunableToolChars: z.number().int().nonnegative().optional(),
|
|
2610
|
+
tools: z.object({
|
|
2611
|
+
allow: z.array(z.string()).optional(),
|
|
2612
|
+
deny: z.array(z.string()).optional()
|
|
2613
|
+
}).strict().optional(),
|
|
2614
|
+
softTrim: z.object({
|
|
2615
|
+
maxChars: z.number().int().nonnegative().optional(),
|
|
2616
|
+
headChars: z.number().int().nonnegative().optional(),
|
|
2617
|
+
tailChars: z.number().int().nonnegative().optional()
|
|
2618
|
+
}).strict().optional(),
|
|
2619
|
+
hardClear: z.object({
|
|
2620
|
+
enabled: z.boolean().optional(),
|
|
2621
|
+
placeholder: z.string().optional()
|
|
2622
|
+
}).strict().optional()
|
|
2623
|
+
}).strict().optional(),
|
|
2624
|
+
compaction: z.object({
|
|
2625
|
+
mode: z.union([z.literal("default"), z.literal("safeguard")]).optional(),
|
|
2626
|
+
reserveTokensFloor: z.number().int().nonnegative().optional(),
|
|
2627
|
+
maxHistoryShare: z.number().min(.1).max(.9).optional(),
|
|
2628
|
+
memoryFlush: z.object({
|
|
2629
|
+
enabled: z.boolean().optional(),
|
|
2630
|
+
softThresholdTokens: z.number().int().nonnegative().optional(),
|
|
2631
|
+
prompt: z.string().optional(),
|
|
2632
|
+
systemPrompt: z.string().optional()
|
|
2633
|
+
}).strict().optional()
|
|
2634
|
+
}).strict().optional(),
|
|
2635
|
+
thinkingDefault: z.union([
|
|
2636
|
+
z.literal("off"),
|
|
2637
|
+
z.literal("minimal"),
|
|
2638
|
+
z.literal("low"),
|
|
2639
|
+
z.literal("medium"),
|
|
2640
|
+
z.literal("high"),
|
|
2641
|
+
z.literal("xhigh")
|
|
2642
|
+
]).optional(),
|
|
2643
|
+
verboseDefault: z.union([
|
|
2644
|
+
z.literal("off"),
|
|
2645
|
+
z.literal("on"),
|
|
2646
|
+
z.literal("full")
|
|
2647
|
+
]).optional(),
|
|
2648
|
+
elevatedDefault: z.union([
|
|
2649
|
+
z.literal("off"),
|
|
2650
|
+
z.literal("on"),
|
|
2651
|
+
z.literal("ask"),
|
|
2652
|
+
z.literal("full")
|
|
2653
|
+
]).optional(),
|
|
2654
|
+
blockStreamingDefault: z.union([z.literal("off"), z.literal("on")]).optional(),
|
|
2655
|
+
blockStreamingBreak: z.union([z.literal("text_end"), z.literal("message_end")]).optional(),
|
|
2656
|
+
blockStreamingChunk: BlockStreamingChunkSchema.optional(),
|
|
2657
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
2658
|
+
humanDelay: HumanDelaySchema.optional(),
|
|
2659
|
+
timeoutSeconds: z.number().int().positive().optional(),
|
|
2660
|
+
mediaMaxMb: z.number().positive().optional(),
|
|
2661
|
+
typingIntervalSeconds: z.number().int().positive().optional(),
|
|
2662
|
+
typingMode: z.union([
|
|
2663
|
+
z.literal("never"),
|
|
2664
|
+
z.literal("instant"),
|
|
2665
|
+
z.literal("thinking"),
|
|
2666
|
+
z.literal("message")
|
|
2667
|
+
]).optional(),
|
|
2668
|
+
heartbeat: HeartbeatSchema,
|
|
2669
|
+
maxConcurrent: z.number().int().positive().optional(),
|
|
2670
|
+
subagents: z.object({
|
|
2671
|
+
maxConcurrent: z.number().int().positive().optional(),
|
|
2672
|
+
archiveAfterMinutes: z.number().int().positive().optional(),
|
|
2673
|
+
model: z.union([z.string(), z.object({
|
|
2674
|
+
primary: z.string().optional(),
|
|
2675
|
+
fallbacks: z.array(z.string()).optional()
|
|
2676
|
+
}).strict()]).optional(),
|
|
2677
|
+
thinking: z.string().optional()
|
|
2678
|
+
}).strict().optional(),
|
|
2679
|
+
sandbox: z.object({
|
|
2680
|
+
mode: z.union([
|
|
2681
|
+
z.literal("off"),
|
|
2682
|
+
z.literal("non-main"),
|
|
2683
|
+
z.literal("all")
|
|
2684
|
+
]).optional(),
|
|
2685
|
+
workspaceAccess: z.union([
|
|
2686
|
+
z.literal("none"),
|
|
2687
|
+
z.literal("ro"),
|
|
2688
|
+
z.literal("rw")
|
|
2689
|
+
]).optional(),
|
|
2690
|
+
sessionToolsVisibility: z.union([z.literal("spawned"), z.literal("all")]).optional(),
|
|
2691
|
+
scope: z.union([
|
|
2692
|
+
z.literal("session"),
|
|
2693
|
+
z.literal("agent"),
|
|
2694
|
+
z.literal("shared")
|
|
2695
|
+
]).optional(),
|
|
2696
|
+
perSession: z.boolean().optional(),
|
|
2697
|
+
workspaceRoot: z.string().optional(),
|
|
2698
|
+
docker: SandboxDockerSchema,
|
|
2699
|
+
browser: SandboxBrowserSchema,
|
|
2700
|
+
prune: SandboxPruneSchema
|
|
2701
|
+
}).strict().optional()
|
|
2702
|
+
}).strict().optional();
|
|
2703
|
+
|
|
2704
|
+
//#endregion
|
|
2705
|
+
//#region src/config/zod-schema.agents.ts
|
|
2706
|
+
const AgentsSchema = z.object({
|
|
2707
|
+
defaults: z.lazy(() => AgentDefaultsSchema).optional(),
|
|
2708
|
+
list: z.array(AgentEntrySchema).optional()
|
|
2709
|
+
}).strict().optional();
|
|
2710
|
+
const BindingsSchema = z.array(z.object({
|
|
2711
|
+
agentId: z.string(),
|
|
2712
|
+
match: z.object({
|
|
2713
|
+
channel: z.string(),
|
|
2714
|
+
accountId: z.string().optional(),
|
|
2715
|
+
peer: z.object({
|
|
2716
|
+
kind: z.union([
|
|
2717
|
+
z.literal("dm"),
|
|
2718
|
+
z.literal("group"),
|
|
2719
|
+
z.literal("channel")
|
|
2720
|
+
]),
|
|
2721
|
+
id: z.string()
|
|
2722
|
+
}).strict().optional(),
|
|
2723
|
+
guildId: z.string().optional(),
|
|
2724
|
+
teamId: z.string().optional()
|
|
2725
|
+
}).strict()
|
|
2726
|
+
}).strict()).optional();
|
|
2727
|
+
const BroadcastStrategySchema = z.enum(["parallel", "sequential"]);
|
|
2728
|
+
const BroadcastSchema = z.object({ strategy: BroadcastStrategySchema.optional() }).catchall(z.array(z.string())).optional();
|
|
2729
|
+
const AudioSchema = z.object({ transcription: TranscribeAudioSchema }).strict().optional();
|
|
2730
|
+
|
|
2731
|
+
//#endregion
|
|
2732
|
+
//#region src/config/zod-schema.approvals.ts
|
|
2733
|
+
const ExecApprovalForwardTargetSchema = z.object({
|
|
2734
|
+
channel: z.string().min(1),
|
|
2735
|
+
to: z.string().min(1),
|
|
2736
|
+
accountId: z.string().optional(),
|
|
2737
|
+
threadId: z.union([z.string(), z.number()]).optional()
|
|
2738
|
+
}).strict();
|
|
2739
|
+
const ExecApprovalForwardingSchema = z.object({
|
|
2740
|
+
enabled: z.boolean().optional(),
|
|
2741
|
+
mode: z.union([
|
|
2742
|
+
z.literal("session"),
|
|
2743
|
+
z.literal("targets"),
|
|
2744
|
+
z.literal("both")
|
|
2745
|
+
]).optional(),
|
|
2746
|
+
agentFilter: z.array(z.string()).optional(),
|
|
2747
|
+
sessionFilter: z.array(z.string()).optional(),
|
|
2748
|
+
targets: z.array(ExecApprovalForwardTargetSchema).optional()
|
|
2749
|
+
}).strict().optional();
|
|
2750
|
+
const ApprovalsSchema = z.object({ exec: ExecApprovalForwardingSchema }).strict().optional();
|
|
2751
|
+
|
|
2752
|
+
//#endregion
|
|
2753
|
+
//#region src/config/zod-schema.hooks.ts
|
|
2754
|
+
const HookMappingSchema = z.object({
|
|
2755
|
+
id: z.string().optional(),
|
|
2756
|
+
match: z.object({
|
|
2757
|
+
path: z.string().optional(),
|
|
2758
|
+
source: z.string().optional()
|
|
2759
|
+
}).optional(),
|
|
2760
|
+
action: z.union([z.literal("wake"), z.literal("agent")]).optional(),
|
|
2761
|
+
wakeMode: z.union([z.literal("now"), z.literal("next-heartbeat")]).optional(),
|
|
2762
|
+
name: z.string().optional(),
|
|
2763
|
+
sessionKey: z.string().optional(),
|
|
2764
|
+
messageTemplate: z.string().optional(),
|
|
2765
|
+
textTemplate: z.string().optional(),
|
|
2766
|
+
deliver: z.boolean().optional(),
|
|
2767
|
+
allowUnsafeExternalContent: z.boolean().optional(),
|
|
2768
|
+
channel: z.union([
|
|
2769
|
+
z.literal("last"),
|
|
2770
|
+
z.literal("whatsapp"),
|
|
2771
|
+
z.literal("telegram"),
|
|
2772
|
+
z.literal("discord"),
|
|
2773
|
+
z.literal("slack"),
|
|
2774
|
+
z.literal("signal"),
|
|
2775
|
+
z.literal("imessage"),
|
|
2776
|
+
z.literal("msteams")
|
|
2777
|
+
]).optional(),
|
|
2778
|
+
to: z.string().optional(),
|
|
2779
|
+
model: z.string().optional(),
|
|
2780
|
+
thinking: z.string().optional(),
|
|
2781
|
+
timeoutSeconds: z.number().int().positive().optional(),
|
|
2782
|
+
transform: z.object({
|
|
2783
|
+
module: z.string(),
|
|
2784
|
+
export: z.string().optional()
|
|
2785
|
+
}).strict().optional()
|
|
2786
|
+
}).strict().optional();
|
|
2787
|
+
const InternalHookHandlerSchema = z.object({
|
|
2788
|
+
event: z.string(),
|
|
2789
|
+
module: z.string(),
|
|
2790
|
+
export: z.string().optional()
|
|
2791
|
+
}).strict();
|
|
2792
|
+
const HookConfigSchema = z.object({
|
|
2793
|
+
enabled: z.boolean().optional(),
|
|
2794
|
+
env: z.record(z.string(), z.string()).optional()
|
|
2795
|
+
}).strict();
|
|
2796
|
+
const HookInstallRecordSchema = z.object({
|
|
2797
|
+
source: z.union([
|
|
2798
|
+
z.literal("npm"),
|
|
2799
|
+
z.literal("archive"),
|
|
2800
|
+
z.literal("path")
|
|
2801
|
+
]),
|
|
2802
|
+
spec: z.string().optional(),
|
|
2803
|
+
sourcePath: z.string().optional(),
|
|
2804
|
+
installPath: z.string().optional(),
|
|
2805
|
+
version: z.string().optional(),
|
|
2806
|
+
installedAt: z.string().optional(),
|
|
2807
|
+
hooks: z.array(z.string()).optional()
|
|
2808
|
+
}).strict();
|
|
2809
|
+
const InternalHooksSchema = z.object({
|
|
2810
|
+
enabled: z.boolean().optional(),
|
|
2811
|
+
handlers: z.array(InternalHookHandlerSchema).optional(),
|
|
2812
|
+
entries: z.record(z.string(), HookConfigSchema).optional(),
|
|
2813
|
+
load: z.object({ extraDirs: z.array(z.string()).optional() }).strict().optional(),
|
|
2814
|
+
installs: z.record(z.string(), HookInstallRecordSchema).optional()
|
|
2815
|
+
}).strict().optional();
|
|
2816
|
+
const HooksGmailSchema = z.object({
|
|
2817
|
+
account: z.string().optional(),
|
|
2818
|
+
label: z.string().optional(),
|
|
2819
|
+
topic: z.string().optional(),
|
|
2820
|
+
subscription: z.string().optional(),
|
|
2821
|
+
pushToken: z.string().optional(),
|
|
2822
|
+
hookUrl: z.string().optional(),
|
|
2823
|
+
includeBody: z.boolean().optional(),
|
|
2824
|
+
maxBytes: z.number().int().positive().optional(),
|
|
2825
|
+
renewEveryMinutes: z.number().int().positive().optional(),
|
|
2826
|
+
allowUnsafeExternalContent: z.boolean().optional(),
|
|
2827
|
+
serve: z.object({
|
|
2828
|
+
bind: z.string().optional(),
|
|
2829
|
+
port: z.number().int().positive().optional(),
|
|
2830
|
+
path: z.string().optional()
|
|
2831
|
+
}).strict().optional(),
|
|
2832
|
+
tailscale: z.object({
|
|
2833
|
+
mode: z.union([
|
|
2834
|
+
z.literal("off"),
|
|
2835
|
+
z.literal("serve"),
|
|
2836
|
+
z.literal("funnel")
|
|
2837
|
+
]).optional(),
|
|
2838
|
+
path: z.string().optional(),
|
|
2839
|
+
target: z.string().optional()
|
|
2840
|
+
}).strict().optional(),
|
|
2841
|
+
model: z.string().optional(),
|
|
2842
|
+
thinking: z.union([
|
|
2843
|
+
z.literal("off"),
|
|
2844
|
+
z.literal("minimal"),
|
|
2845
|
+
z.literal("low"),
|
|
2846
|
+
z.literal("medium"),
|
|
2847
|
+
z.literal("high")
|
|
2848
|
+
]).optional()
|
|
2849
|
+
}).strict().optional();
|
|
2850
|
+
|
|
2851
|
+
//#endregion
|
|
2852
|
+
//#region src/config/zod-schema.channels.ts
|
|
2853
|
+
const ChannelHeartbeatVisibilitySchema = z.object({
|
|
2854
|
+
showOk: z.boolean().optional(),
|
|
2855
|
+
showAlerts: z.boolean().optional(),
|
|
2856
|
+
useIndicator: z.boolean().optional()
|
|
2857
|
+
}).strict().optional();
|
|
2858
|
+
|
|
2859
|
+
//#endregion
|
|
2860
|
+
//#region src/config/telegram-custom-commands.ts
|
|
2861
|
+
const TELEGRAM_COMMAND_NAME_PATTERN = /^[a-z0-9_]{1,32}$/;
|
|
2862
|
+
function normalizeTelegramCommandName(value) {
|
|
2863
|
+
const trimmed = value.trim();
|
|
2864
|
+
if (!trimmed) return "";
|
|
2865
|
+
return (trimmed.startsWith("/") ? trimmed.slice(1) : trimmed).trim().toLowerCase();
|
|
2866
|
+
}
|
|
2867
|
+
function normalizeTelegramCommandDescription(value) {
|
|
2868
|
+
return value.trim();
|
|
2869
|
+
}
|
|
2870
|
+
function resolveTelegramCustomCommands(params) {
|
|
2871
|
+
const entries = Array.isArray(params.commands) ? params.commands : [];
|
|
2872
|
+
const reserved = params.reservedCommands ?? /* @__PURE__ */ new Set();
|
|
2873
|
+
const checkReserved = params.checkReserved !== false;
|
|
2874
|
+
const checkDuplicates = params.checkDuplicates !== false;
|
|
2875
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2876
|
+
const resolved = [];
|
|
2877
|
+
const issues = [];
|
|
2878
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
2879
|
+
const entry = entries[index];
|
|
2880
|
+
const normalized = normalizeTelegramCommandName(String(entry?.command ?? ""));
|
|
2881
|
+
if (!normalized) {
|
|
2882
|
+
issues.push({
|
|
2883
|
+
index,
|
|
2884
|
+
field: "command",
|
|
2885
|
+
message: "Telegram custom command is missing a command name."
|
|
2886
|
+
});
|
|
2887
|
+
continue;
|
|
2888
|
+
}
|
|
2889
|
+
if (!TELEGRAM_COMMAND_NAME_PATTERN.test(normalized)) {
|
|
2890
|
+
issues.push({
|
|
2891
|
+
index,
|
|
2892
|
+
field: "command",
|
|
2893
|
+
message: `Telegram custom command "/${normalized}" is invalid (use a-z, 0-9, underscore; max 32 chars).`
|
|
2894
|
+
});
|
|
2895
|
+
continue;
|
|
2896
|
+
}
|
|
2897
|
+
if (checkReserved && reserved.has(normalized)) {
|
|
2898
|
+
issues.push({
|
|
2899
|
+
index,
|
|
2900
|
+
field: "command",
|
|
2901
|
+
message: `Telegram custom command "/${normalized}" conflicts with a native command.`
|
|
2902
|
+
});
|
|
2903
|
+
continue;
|
|
2904
|
+
}
|
|
2905
|
+
if (checkDuplicates && seen.has(normalized)) {
|
|
2906
|
+
issues.push({
|
|
2907
|
+
index,
|
|
2908
|
+
field: "command",
|
|
2909
|
+
message: `Telegram custom command "/${normalized}" is duplicated.`
|
|
2910
|
+
});
|
|
2911
|
+
continue;
|
|
2912
|
+
}
|
|
2913
|
+
const description = normalizeTelegramCommandDescription(String(entry?.description ?? ""));
|
|
2914
|
+
if (!description) {
|
|
2915
|
+
issues.push({
|
|
2916
|
+
index,
|
|
2917
|
+
field: "description",
|
|
2918
|
+
message: `Telegram custom command "/${normalized}" is missing a description.`
|
|
2919
|
+
});
|
|
2920
|
+
continue;
|
|
2921
|
+
}
|
|
2922
|
+
if (checkDuplicates) seen.add(normalized);
|
|
2923
|
+
resolved.push({
|
|
2924
|
+
command: normalized,
|
|
2925
|
+
description
|
|
2926
|
+
});
|
|
2927
|
+
}
|
|
2928
|
+
return {
|
|
2929
|
+
commands: resolved,
|
|
2930
|
+
issues
|
|
2931
|
+
};
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
//#endregion
|
|
2935
|
+
//#region src/config/zod-schema.providers-core.ts
|
|
2936
|
+
const ToolPolicyBySenderSchema$1 = z.record(z.string(), ToolPolicySchema).optional();
|
|
2937
|
+
const TelegramInlineButtonsScopeSchema = z.enum([
|
|
2938
|
+
"off",
|
|
2939
|
+
"dm",
|
|
2940
|
+
"group",
|
|
2941
|
+
"all",
|
|
2942
|
+
"allowlist"
|
|
2943
|
+
]);
|
|
2944
|
+
const TelegramCapabilitiesSchema = z.union([z.array(z.string()), z.object({ inlineButtons: TelegramInlineButtonsScopeSchema.optional() }).strict()]);
|
|
2945
|
+
const TelegramTopicSchema = z.object({
|
|
2946
|
+
requireMention: z.boolean().optional(),
|
|
2947
|
+
groupPolicy: GroupPolicySchema.optional(),
|
|
2948
|
+
skills: z.array(z.string()).optional(),
|
|
2949
|
+
enabled: z.boolean().optional(),
|
|
2950
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
2951
|
+
systemPrompt: z.string().optional()
|
|
2952
|
+
}).strict();
|
|
2953
|
+
const TelegramGroupSchema = z.object({
|
|
2954
|
+
requireMention: z.boolean().optional(),
|
|
2955
|
+
groupPolicy: GroupPolicySchema.optional(),
|
|
2956
|
+
tools: ToolPolicySchema,
|
|
2957
|
+
toolsBySender: ToolPolicyBySenderSchema$1,
|
|
2958
|
+
skills: z.array(z.string()).optional(),
|
|
2959
|
+
enabled: z.boolean().optional(),
|
|
2960
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
2961
|
+
systemPrompt: z.string().optional(),
|
|
2962
|
+
topics: z.record(z.string(), TelegramTopicSchema.optional()).optional()
|
|
2963
|
+
}).strict();
|
|
2964
|
+
const TelegramCustomCommandSchema = z.object({
|
|
2965
|
+
command: z.string().transform(normalizeTelegramCommandName),
|
|
2966
|
+
description: z.string().transform(normalizeTelegramCommandDescription)
|
|
2967
|
+
}).strict();
|
|
2968
|
+
const validateTelegramCustomCommands = (value, ctx) => {
|
|
2969
|
+
if (!value.customCommands || value.customCommands.length === 0) return;
|
|
2970
|
+
const { issues } = resolveTelegramCustomCommands({
|
|
2971
|
+
commands: value.customCommands,
|
|
2972
|
+
checkReserved: false,
|
|
2973
|
+
checkDuplicates: false
|
|
2974
|
+
});
|
|
2975
|
+
for (const issue of issues) ctx.addIssue({
|
|
2976
|
+
code: z.ZodIssueCode.custom,
|
|
2977
|
+
path: [
|
|
2978
|
+
"customCommands",
|
|
2979
|
+
issue.index,
|
|
2980
|
+
issue.field
|
|
2981
|
+
],
|
|
2982
|
+
message: issue.message
|
|
2983
|
+
});
|
|
2984
|
+
};
|
|
2985
|
+
const TelegramAccountSchemaBase = z.object({
|
|
2986
|
+
name: z.string().optional(),
|
|
2987
|
+
capabilities: TelegramCapabilitiesSchema.optional(),
|
|
2988
|
+
markdown: MarkdownConfigSchema,
|
|
2989
|
+
enabled: z.boolean().optional(),
|
|
2990
|
+
commands: ProviderCommandsSchema,
|
|
2991
|
+
customCommands: z.array(TelegramCustomCommandSchema).optional(),
|
|
2992
|
+
configWrites: z.boolean().optional(),
|
|
2993
|
+
dmPolicy: DmPolicySchema.optional().default("pairing"),
|
|
2994
|
+
botToken: z.string().optional(),
|
|
2995
|
+
tokenFile: z.string().optional(),
|
|
2996
|
+
replyToMode: ReplyToModeSchema.optional(),
|
|
2997
|
+
groups: z.record(z.string(), TelegramGroupSchema.optional()).optional(),
|
|
2998
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
2999
|
+
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3000
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
3001
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
3002
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
3003
|
+
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
|
3004
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
3005
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
3006
|
+
blockStreaming: z.boolean().optional(),
|
|
3007
|
+
draftChunk: BlockStreamingChunkSchema.optional(),
|
|
3008
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
3009
|
+
streamMode: z.enum([
|
|
3010
|
+
"off",
|
|
3011
|
+
"partial",
|
|
3012
|
+
"block"
|
|
3013
|
+
]).optional().default("partial"),
|
|
3014
|
+
mediaMaxMb: z.number().positive().optional(),
|
|
3015
|
+
timeoutSeconds: z.number().int().positive().optional(),
|
|
3016
|
+
retry: RetryConfigSchema,
|
|
3017
|
+
network: z.object({ autoSelectFamily: z.boolean().optional() }).strict().optional(),
|
|
3018
|
+
proxy: z.string().optional(),
|
|
3019
|
+
webhookUrl: z.string().optional(),
|
|
3020
|
+
webhookSecret: z.string().optional(),
|
|
3021
|
+
webhookPath: z.string().optional(),
|
|
3022
|
+
actions: z.object({
|
|
3023
|
+
reactions: z.boolean().optional(),
|
|
3024
|
+
sendMessage: z.boolean().optional(),
|
|
3025
|
+
deleteMessage: z.boolean().optional(),
|
|
3026
|
+
sticker: z.boolean().optional()
|
|
3027
|
+
}).strict().optional(),
|
|
3028
|
+
reactionNotifications: z.enum([
|
|
3029
|
+
"off",
|
|
3030
|
+
"own",
|
|
3031
|
+
"all"
|
|
3032
|
+
]).optional(),
|
|
3033
|
+
reactionLevel: z.enum([
|
|
3034
|
+
"off",
|
|
3035
|
+
"ack",
|
|
3036
|
+
"minimal",
|
|
3037
|
+
"extensive"
|
|
3038
|
+
]).optional(),
|
|
3039
|
+
heartbeat: ChannelHeartbeatVisibilitySchema,
|
|
3040
|
+
linkPreview: z.boolean().optional(),
|
|
3041
|
+
responsePrefix: z.string().optional()
|
|
3042
|
+
}).strict();
|
|
3043
|
+
const TelegramAccountSchema = TelegramAccountSchemaBase.superRefine((value, ctx) => {
|
|
3044
|
+
requireOpenAllowFrom({
|
|
3045
|
+
policy: value.dmPolicy,
|
|
3046
|
+
allowFrom: value.allowFrom,
|
|
3047
|
+
ctx,
|
|
3048
|
+
path: ["allowFrom"],
|
|
3049
|
+
message: "channels.telegram.dmPolicy=\"open\" requires channels.telegram.allowFrom to include \"*\""
|
|
3050
|
+
});
|
|
3051
|
+
validateTelegramCustomCommands(value, ctx);
|
|
3052
|
+
});
|
|
3053
|
+
const TelegramConfigSchema = TelegramAccountSchemaBase.extend({ accounts: z.record(z.string(), TelegramAccountSchema.optional()).optional() }).superRefine((value, ctx) => {
|
|
3054
|
+
requireOpenAllowFrom({
|
|
3055
|
+
policy: value.dmPolicy,
|
|
3056
|
+
allowFrom: value.allowFrom,
|
|
3057
|
+
ctx,
|
|
3058
|
+
path: ["allowFrom"],
|
|
3059
|
+
message: "channels.telegram.dmPolicy=\"open\" requires channels.telegram.allowFrom to include \"*\""
|
|
3060
|
+
});
|
|
3061
|
+
validateTelegramCustomCommands(value, ctx);
|
|
3062
|
+
const baseWebhookUrl = typeof value.webhookUrl === "string" ? value.webhookUrl.trim() : "";
|
|
3063
|
+
const baseWebhookSecret = typeof value.webhookSecret === "string" ? value.webhookSecret.trim() : "";
|
|
3064
|
+
if (baseWebhookUrl && !baseWebhookSecret) ctx.addIssue({
|
|
3065
|
+
code: z.ZodIssueCode.custom,
|
|
3066
|
+
message: "channels.telegram.webhookUrl requires channels.telegram.webhookSecret",
|
|
3067
|
+
path: ["webhookSecret"]
|
|
3068
|
+
});
|
|
3069
|
+
if (!value.accounts) return;
|
|
3070
|
+
for (const [accountId, account] of Object.entries(value.accounts)) {
|
|
3071
|
+
if (!account) continue;
|
|
3072
|
+
if (account.enabled === false) continue;
|
|
3073
|
+
if (!(typeof account.webhookUrl === "string" ? account.webhookUrl.trim() : "")) continue;
|
|
3074
|
+
if (!(typeof account.webhookSecret === "string" ? account.webhookSecret.trim() : "") && !baseWebhookSecret) ctx.addIssue({
|
|
3075
|
+
code: z.ZodIssueCode.custom,
|
|
3076
|
+
message: "channels.telegram.accounts.*.webhookUrl requires channels.telegram.webhookSecret or channels.telegram.accounts.*.webhookSecret",
|
|
3077
|
+
path: [
|
|
3078
|
+
"accounts",
|
|
3079
|
+
accountId,
|
|
3080
|
+
"webhookSecret"
|
|
3081
|
+
]
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
});
|
|
3085
|
+
const DiscordDmSchema = z.object({
|
|
3086
|
+
enabled: z.boolean().optional(),
|
|
3087
|
+
policy: DmPolicySchema.optional().default("pairing"),
|
|
3088
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3089
|
+
groupEnabled: z.boolean().optional(),
|
|
3090
|
+
groupChannels: z.array(z.union([z.string(), z.number()])).optional()
|
|
3091
|
+
}).strict().superRefine((value, ctx) => {
|
|
3092
|
+
requireOpenAllowFrom({
|
|
3093
|
+
policy: value.policy,
|
|
3094
|
+
allowFrom: value.allowFrom,
|
|
3095
|
+
ctx,
|
|
3096
|
+
path: ["allowFrom"],
|
|
3097
|
+
message: "channels.discord.dm.policy=\"open\" requires channels.discord.dm.allowFrom to include \"*\""
|
|
3098
|
+
});
|
|
3099
|
+
});
|
|
3100
|
+
const DiscordGuildChannelSchema = z.object({
|
|
3101
|
+
allow: z.boolean().optional(),
|
|
3102
|
+
requireMention: z.boolean().optional(),
|
|
3103
|
+
tools: ToolPolicySchema,
|
|
3104
|
+
toolsBySender: ToolPolicyBySenderSchema$1,
|
|
3105
|
+
skills: z.array(z.string()).optional(),
|
|
3106
|
+
enabled: z.boolean().optional(),
|
|
3107
|
+
users: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3108
|
+
systemPrompt: z.string().optional(),
|
|
3109
|
+
includeThreadStarter: z.boolean().optional(),
|
|
3110
|
+
autoThread: z.boolean().optional()
|
|
3111
|
+
}).strict();
|
|
3112
|
+
const DiscordGuildSchema = z.object({
|
|
3113
|
+
slug: z.string().optional(),
|
|
3114
|
+
requireMention: z.boolean().optional(),
|
|
3115
|
+
tools: ToolPolicySchema,
|
|
3116
|
+
toolsBySender: ToolPolicyBySenderSchema$1,
|
|
3117
|
+
reactionNotifications: z.enum([
|
|
3118
|
+
"off",
|
|
3119
|
+
"own",
|
|
3120
|
+
"all",
|
|
3121
|
+
"allowlist"
|
|
3122
|
+
]).optional(),
|
|
3123
|
+
users: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3124
|
+
channels: z.record(z.string(), DiscordGuildChannelSchema.optional()).optional()
|
|
3125
|
+
}).strict();
|
|
3126
|
+
const DiscordAccountSchema = z.object({
|
|
3127
|
+
name: z.string().optional(),
|
|
3128
|
+
capabilities: z.array(z.string()).optional(),
|
|
3129
|
+
markdown: MarkdownConfigSchema,
|
|
3130
|
+
enabled: z.boolean().optional(),
|
|
3131
|
+
commands: ProviderCommandsSchema,
|
|
3132
|
+
configWrites: z.boolean().optional(),
|
|
3133
|
+
token: z.string().optional(),
|
|
3134
|
+
allowBots: z.boolean().optional(),
|
|
3135
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
3136
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
3137
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
3138
|
+
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
|
3139
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
3140
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
3141
|
+
blockStreaming: z.boolean().optional(),
|
|
3142
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
3143
|
+
maxLinesPerMessage: z.number().int().positive().optional(),
|
|
3144
|
+
mediaMaxMb: z.number().positive().optional(),
|
|
3145
|
+
retry: RetryConfigSchema,
|
|
3146
|
+
actions: z.object({
|
|
3147
|
+
reactions: z.boolean().optional(),
|
|
3148
|
+
stickers: z.boolean().optional(),
|
|
3149
|
+
emojiUploads: z.boolean().optional(),
|
|
3150
|
+
stickerUploads: z.boolean().optional(),
|
|
3151
|
+
polls: z.boolean().optional(),
|
|
3152
|
+
permissions: z.boolean().optional(),
|
|
3153
|
+
messages: z.boolean().optional(),
|
|
3154
|
+
threads: z.boolean().optional(),
|
|
3155
|
+
pins: z.boolean().optional(),
|
|
3156
|
+
search: z.boolean().optional(),
|
|
3157
|
+
memberInfo: z.boolean().optional(),
|
|
3158
|
+
roleInfo: z.boolean().optional(),
|
|
3159
|
+
roles: z.boolean().optional(),
|
|
3160
|
+
channelInfo: z.boolean().optional(),
|
|
3161
|
+
voiceStatus: z.boolean().optional(),
|
|
3162
|
+
events: z.boolean().optional(),
|
|
3163
|
+
moderation: z.boolean().optional(),
|
|
3164
|
+
channels: z.boolean().optional(),
|
|
3165
|
+
presence: z.boolean().optional()
|
|
3166
|
+
}).strict().optional(),
|
|
3167
|
+
replyToMode: ReplyToModeSchema.optional(),
|
|
3168
|
+
dm: DiscordDmSchema.optional(),
|
|
3169
|
+
guilds: z.record(z.string(), DiscordGuildSchema.optional()).optional(),
|
|
3170
|
+
heartbeat: ChannelHeartbeatVisibilitySchema,
|
|
3171
|
+
execApprovals: z.object({
|
|
3172
|
+
enabled: z.boolean().optional(),
|
|
3173
|
+
approvers: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3174
|
+
agentFilter: z.array(z.string()).optional(),
|
|
3175
|
+
sessionFilter: z.array(z.string()).optional()
|
|
3176
|
+
}).strict().optional(),
|
|
3177
|
+
intents: z.object({
|
|
3178
|
+
presence: z.boolean().optional(),
|
|
3179
|
+
guildMembers: z.boolean().optional()
|
|
3180
|
+
}).strict().optional(),
|
|
3181
|
+
pluralkit: z.object({
|
|
3182
|
+
enabled: z.boolean().optional(),
|
|
3183
|
+
token: z.string().optional()
|
|
3184
|
+
}).strict().optional(),
|
|
3185
|
+
responsePrefix: z.string().optional()
|
|
3186
|
+
}).strict();
|
|
3187
|
+
const DiscordConfigSchema = DiscordAccountSchema.extend({ accounts: z.record(z.string(), DiscordAccountSchema.optional()).optional() });
|
|
3188
|
+
const GoogleChatDmSchema = z.object({
|
|
3189
|
+
enabled: z.boolean().optional(),
|
|
3190
|
+
policy: DmPolicySchema.optional().default("pairing"),
|
|
3191
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional()
|
|
3192
|
+
}).strict().superRefine((value, ctx) => {
|
|
3193
|
+
requireOpenAllowFrom({
|
|
3194
|
+
policy: value.policy,
|
|
3195
|
+
allowFrom: value.allowFrom,
|
|
3196
|
+
ctx,
|
|
3197
|
+
path: ["allowFrom"],
|
|
3198
|
+
message: "channels.googlechat.dm.policy=\"open\" requires channels.googlechat.dm.allowFrom to include \"*\""
|
|
3199
|
+
});
|
|
3200
|
+
});
|
|
3201
|
+
const GoogleChatGroupSchema = z.object({
|
|
3202
|
+
enabled: z.boolean().optional(),
|
|
3203
|
+
allow: z.boolean().optional(),
|
|
3204
|
+
requireMention: z.boolean().optional(),
|
|
3205
|
+
users: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3206
|
+
systemPrompt: z.string().optional()
|
|
3207
|
+
}).strict();
|
|
3208
|
+
const GoogleChatAccountSchema = z.object({
|
|
3209
|
+
name: z.string().optional(),
|
|
3210
|
+
capabilities: z.array(z.string()).optional(),
|
|
3211
|
+
enabled: z.boolean().optional(),
|
|
3212
|
+
configWrites: z.boolean().optional(),
|
|
3213
|
+
allowBots: z.boolean().optional(),
|
|
3214
|
+
requireMention: z.boolean().optional(),
|
|
3215
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
3216
|
+
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3217
|
+
groups: z.record(z.string(), GoogleChatGroupSchema.optional()).optional(),
|
|
3218
|
+
serviceAccount: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
|
|
3219
|
+
serviceAccountFile: z.string().optional(),
|
|
3220
|
+
audienceType: z.enum(["app-url", "project-number"]).optional(),
|
|
3221
|
+
audience: z.string().optional(),
|
|
3222
|
+
webhookPath: z.string().optional(),
|
|
3223
|
+
webhookUrl: z.string().optional(),
|
|
3224
|
+
botUser: z.string().optional(),
|
|
3225
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
3226
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
3227
|
+
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
|
3228
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
3229
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
3230
|
+
blockStreaming: z.boolean().optional(),
|
|
3231
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
3232
|
+
mediaMaxMb: z.number().positive().optional(),
|
|
3233
|
+
replyToMode: ReplyToModeSchema.optional(),
|
|
3234
|
+
actions: z.object({ reactions: z.boolean().optional() }).strict().optional(),
|
|
3235
|
+
dm: GoogleChatDmSchema.optional(),
|
|
3236
|
+
typingIndicator: z.enum([
|
|
3237
|
+
"none",
|
|
3238
|
+
"message",
|
|
3239
|
+
"reaction"
|
|
3240
|
+
]).optional(),
|
|
3241
|
+
responsePrefix: z.string().optional()
|
|
3242
|
+
}).strict();
|
|
3243
|
+
const GoogleChatConfigSchema = GoogleChatAccountSchema.extend({
|
|
3244
|
+
accounts: z.record(z.string(), GoogleChatAccountSchema.optional()).optional(),
|
|
3245
|
+
defaultAccount: z.string().optional()
|
|
3246
|
+
});
|
|
3247
|
+
const SlackDmSchema = z.object({
|
|
3248
|
+
enabled: z.boolean().optional(),
|
|
3249
|
+
policy: DmPolicySchema.optional().default("pairing"),
|
|
3250
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3251
|
+
groupEnabled: z.boolean().optional(),
|
|
3252
|
+
groupChannels: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3253
|
+
replyToMode: ReplyToModeSchema.optional()
|
|
3254
|
+
}).strict().superRefine((value, ctx) => {
|
|
3255
|
+
requireOpenAllowFrom({
|
|
3256
|
+
policy: value.policy,
|
|
3257
|
+
allowFrom: value.allowFrom,
|
|
3258
|
+
ctx,
|
|
3259
|
+
path: ["allowFrom"],
|
|
3260
|
+
message: "channels.slack.dm.policy=\"open\" requires channels.slack.dm.allowFrom to include \"*\""
|
|
3261
|
+
});
|
|
3262
|
+
});
|
|
3263
|
+
const SlackChannelSchema = z.object({
|
|
3264
|
+
enabled: z.boolean().optional(),
|
|
3265
|
+
allow: z.boolean().optional(),
|
|
3266
|
+
requireMention: z.boolean().optional(),
|
|
3267
|
+
tools: ToolPolicySchema,
|
|
3268
|
+
toolsBySender: ToolPolicyBySenderSchema$1,
|
|
3269
|
+
allowBots: z.boolean().optional(),
|
|
3270
|
+
users: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3271
|
+
skills: z.array(z.string()).optional(),
|
|
3272
|
+
systemPrompt: z.string().optional()
|
|
3273
|
+
}).strict();
|
|
3274
|
+
const SlackThreadSchema = z.object({
|
|
3275
|
+
historyScope: z.enum(["thread", "channel"]).optional(),
|
|
3276
|
+
inheritParent: z.boolean().optional()
|
|
3277
|
+
}).strict();
|
|
3278
|
+
const SlackReplyToModeByChatTypeSchema = z.object({
|
|
3279
|
+
direct: ReplyToModeSchema.optional(),
|
|
3280
|
+
group: ReplyToModeSchema.optional(),
|
|
3281
|
+
channel: ReplyToModeSchema.optional()
|
|
3282
|
+
}).strict();
|
|
3283
|
+
const SlackAccountSchema = z.object({
|
|
3284
|
+
name: z.string().optional(),
|
|
3285
|
+
mode: z.enum(["socket", "http"]).optional(),
|
|
3286
|
+
signingSecret: z.string().optional(),
|
|
3287
|
+
webhookPath: z.string().optional(),
|
|
3288
|
+
capabilities: z.array(z.string()).optional(),
|
|
3289
|
+
markdown: MarkdownConfigSchema,
|
|
3290
|
+
enabled: z.boolean().optional(),
|
|
3291
|
+
commands: ProviderCommandsSchema,
|
|
3292
|
+
configWrites: z.boolean().optional(),
|
|
3293
|
+
botToken: z.string().optional(),
|
|
3294
|
+
appToken: z.string().optional(),
|
|
3295
|
+
userToken: z.string().optional(),
|
|
3296
|
+
userTokenReadOnly: z.boolean().optional().default(true),
|
|
3297
|
+
allowBots: z.boolean().optional(),
|
|
3298
|
+
requireMention: z.boolean().optional(),
|
|
3299
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
3300
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
3301
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
3302
|
+
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
|
3303
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
3304
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
3305
|
+
blockStreaming: z.boolean().optional(),
|
|
3306
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
3307
|
+
mediaMaxMb: z.number().positive().optional(),
|
|
3308
|
+
reactionNotifications: z.enum([
|
|
3309
|
+
"off",
|
|
3310
|
+
"own",
|
|
3311
|
+
"all",
|
|
3312
|
+
"allowlist"
|
|
3313
|
+
]).optional(),
|
|
3314
|
+
reactionAllowlist: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3315
|
+
replyToMode: ReplyToModeSchema.optional(),
|
|
3316
|
+
replyToModeByChatType: SlackReplyToModeByChatTypeSchema.optional(),
|
|
3317
|
+
thread: SlackThreadSchema.optional(),
|
|
3318
|
+
actions: z.object({
|
|
3319
|
+
reactions: z.boolean().optional(),
|
|
3320
|
+
messages: z.boolean().optional(),
|
|
3321
|
+
pins: z.boolean().optional(),
|
|
3322
|
+
search: z.boolean().optional(),
|
|
3323
|
+
permissions: z.boolean().optional(),
|
|
3324
|
+
memberInfo: z.boolean().optional(),
|
|
3325
|
+
channelInfo: z.boolean().optional(),
|
|
3326
|
+
emojiList: z.boolean().optional()
|
|
3327
|
+
}).strict().optional(),
|
|
3328
|
+
slashCommand: z.object({
|
|
3329
|
+
enabled: z.boolean().optional(),
|
|
3330
|
+
name: z.string().optional(),
|
|
3331
|
+
sessionPrefix: z.string().optional(),
|
|
3332
|
+
ephemeral: z.boolean().optional()
|
|
3333
|
+
}).strict().optional(),
|
|
3334
|
+
dm: SlackDmSchema.optional(),
|
|
3335
|
+
channels: z.record(z.string(), SlackChannelSchema.optional()).optional(),
|
|
3336
|
+
heartbeat: ChannelHeartbeatVisibilitySchema,
|
|
3337
|
+
responsePrefix: z.string().optional()
|
|
3338
|
+
}).strict();
|
|
3339
|
+
const SlackConfigSchema = SlackAccountSchema.extend({
|
|
3340
|
+
mode: z.enum(["socket", "http"]).optional().default("socket"),
|
|
3341
|
+
signingSecret: z.string().optional(),
|
|
3342
|
+
webhookPath: z.string().optional().default("/slack/events"),
|
|
3343
|
+
accounts: z.record(z.string(), SlackAccountSchema.optional()).optional()
|
|
3344
|
+
}).superRefine((value, ctx) => {
|
|
3345
|
+
const baseMode = value.mode ?? "socket";
|
|
3346
|
+
if (baseMode === "http" && !value.signingSecret) ctx.addIssue({
|
|
3347
|
+
code: z.ZodIssueCode.custom,
|
|
3348
|
+
message: "channels.slack.mode=\"http\" requires channels.slack.signingSecret",
|
|
3349
|
+
path: ["signingSecret"]
|
|
3350
|
+
});
|
|
3351
|
+
if (!value.accounts) return;
|
|
3352
|
+
for (const [accountId, account] of Object.entries(value.accounts)) {
|
|
3353
|
+
if (!account) continue;
|
|
3354
|
+
if (account.enabled === false) continue;
|
|
3355
|
+
if ((account.mode ?? baseMode) !== "http") continue;
|
|
3356
|
+
if (!(account.signingSecret ?? value.signingSecret)) ctx.addIssue({
|
|
3357
|
+
code: z.ZodIssueCode.custom,
|
|
3358
|
+
message: "channels.slack.accounts.*.mode=\"http\" requires channels.slack.signingSecret or channels.slack.accounts.*.signingSecret",
|
|
3359
|
+
path: [
|
|
3360
|
+
"accounts",
|
|
3361
|
+
accountId,
|
|
3362
|
+
"signingSecret"
|
|
3363
|
+
]
|
|
3364
|
+
});
|
|
3365
|
+
}
|
|
3366
|
+
});
|
|
3367
|
+
const SignalAccountSchemaBase = z.object({
|
|
3368
|
+
name: z.string().optional(),
|
|
3369
|
+
capabilities: z.array(z.string()).optional(),
|
|
3370
|
+
markdown: MarkdownConfigSchema,
|
|
3371
|
+
enabled: z.boolean().optional(),
|
|
3372
|
+
configWrites: z.boolean().optional(),
|
|
3373
|
+
account: z.string().optional(),
|
|
3374
|
+
httpUrl: z.string().optional(),
|
|
3375
|
+
httpHost: z.string().optional(),
|
|
3376
|
+
httpPort: z.number().int().positive().optional(),
|
|
3377
|
+
cliPath: ExecutableTokenSchema.optional(),
|
|
3378
|
+
autoStart: z.boolean().optional(),
|
|
3379
|
+
startupTimeoutMs: z.number().int().min(1e3).max(12e4).optional(),
|
|
3380
|
+
receiveMode: z.union([z.literal("on-start"), z.literal("manual")]).optional(),
|
|
3381
|
+
ignoreAttachments: z.boolean().optional(),
|
|
3382
|
+
ignoreStories: z.boolean().optional(),
|
|
3383
|
+
sendReadReceipts: z.boolean().optional(),
|
|
3384
|
+
dmPolicy: DmPolicySchema.optional().default("pairing"),
|
|
3385
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3386
|
+
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3387
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
3388
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
3389
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
3390
|
+
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
|
3391
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
3392
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
3393
|
+
blockStreaming: z.boolean().optional(),
|
|
3394
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
3395
|
+
mediaMaxMb: z.number().int().positive().optional(),
|
|
3396
|
+
reactionNotifications: z.enum([
|
|
3397
|
+
"off",
|
|
3398
|
+
"own",
|
|
3399
|
+
"all",
|
|
3400
|
+
"allowlist"
|
|
3401
|
+
]).optional(),
|
|
3402
|
+
reactionAllowlist: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3403
|
+
actions: z.object({ reactions: z.boolean().optional() }).strict().optional(),
|
|
3404
|
+
reactionLevel: z.enum([
|
|
3405
|
+
"off",
|
|
3406
|
+
"ack",
|
|
3407
|
+
"minimal",
|
|
3408
|
+
"extensive"
|
|
3409
|
+
]).optional(),
|
|
3410
|
+
heartbeat: ChannelHeartbeatVisibilitySchema,
|
|
3411
|
+
responsePrefix: z.string().optional()
|
|
3412
|
+
}).strict();
|
|
3413
|
+
const SignalAccountSchema = SignalAccountSchemaBase.superRefine((value, ctx) => {
|
|
3414
|
+
requireOpenAllowFrom({
|
|
3415
|
+
policy: value.dmPolicy,
|
|
3416
|
+
allowFrom: value.allowFrom,
|
|
3417
|
+
ctx,
|
|
3418
|
+
path: ["allowFrom"],
|
|
3419
|
+
message: "channels.signal.dmPolicy=\"open\" requires channels.signal.allowFrom to include \"*\""
|
|
3420
|
+
});
|
|
3421
|
+
});
|
|
3422
|
+
const SignalConfigSchema = SignalAccountSchemaBase.extend({ accounts: z.record(z.string(), SignalAccountSchema.optional()).optional() }).superRefine((value, ctx) => {
|
|
3423
|
+
requireOpenAllowFrom({
|
|
3424
|
+
policy: value.dmPolicy,
|
|
3425
|
+
allowFrom: value.allowFrom,
|
|
3426
|
+
ctx,
|
|
3427
|
+
path: ["allowFrom"],
|
|
3428
|
+
message: "channels.signal.dmPolicy=\"open\" requires channels.signal.allowFrom to include \"*\""
|
|
3429
|
+
});
|
|
3430
|
+
});
|
|
3431
|
+
const IMessageAccountSchemaBase = z.object({
|
|
3432
|
+
name: z.string().optional(),
|
|
3433
|
+
capabilities: z.array(z.string()).optional(),
|
|
3434
|
+
markdown: MarkdownConfigSchema,
|
|
3435
|
+
enabled: z.boolean().optional(),
|
|
3436
|
+
configWrites: z.boolean().optional(),
|
|
3437
|
+
cliPath: ExecutableTokenSchema.optional(),
|
|
3438
|
+
dbPath: z.string().optional(),
|
|
3439
|
+
remoteHost: z.string().optional(),
|
|
3440
|
+
service: z.union([
|
|
3441
|
+
z.literal("imessage"),
|
|
3442
|
+
z.literal("sms"),
|
|
3443
|
+
z.literal("auto")
|
|
3444
|
+
]).optional(),
|
|
3445
|
+
region: z.string().optional(),
|
|
3446
|
+
dmPolicy: DmPolicySchema.optional().default("pairing"),
|
|
3447
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3448
|
+
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
3449
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
3450
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
3451
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
3452
|
+
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
|
3453
|
+
includeAttachments: z.boolean().optional(),
|
|
3454
|
+
mediaMaxMb: z.number().int().positive().optional(),
|
|
3455
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
3456
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
3457
|
+
blockStreaming: z.boolean().optional(),
|
|
3458
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
3459
|
+
groups: z.record(z.string(), z.object({
|
|
3460
|
+
requireMention: z.boolean().optional(),
|
|
3461
|
+
tools: ToolPolicySchema,
|
|
3462
|
+
toolsBySender: ToolPolicyBySenderSchema$1
|
|
3463
|
+
}).strict().optional()).optional(),
|
|
3464
|
+
heartbeat: ChannelHeartbeatVisibilitySchema,
|
|
3465
|
+
responsePrefix: z.string().optional()
|
|
3466
|
+
}).strict();
|
|
3467
|
+
const IMessageAccountSchema = IMessageAccountSchemaBase.superRefine((value, ctx) => {
|
|
3468
|
+
requireOpenAllowFrom({
|
|
3469
|
+
policy: value.dmPolicy,
|
|
3470
|
+
allowFrom: value.allowFrom,
|
|
3471
|
+
ctx,
|
|
3472
|
+
path: ["allowFrom"],
|
|
3473
|
+
message: "channels.imessage.dmPolicy=\"open\" requires channels.imessage.allowFrom to include \"*\""
|
|
3474
|
+
});
|
|
3475
|
+
});
|
|
3476
|
+
const IMessageConfigSchema = IMessageAccountSchemaBase.extend({ accounts: z.record(z.string(), IMessageAccountSchema.optional()).optional() }).superRefine((value, ctx) => {
|
|
3477
|
+
requireOpenAllowFrom({
|
|
3478
|
+
policy: value.dmPolicy,
|
|
3479
|
+
allowFrom: value.allowFrom,
|
|
3480
|
+
ctx,
|
|
3481
|
+
path: ["allowFrom"],
|
|
3482
|
+
message: "channels.imessage.dmPolicy=\"open\" requires channels.imessage.allowFrom to include \"*\""
|
|
3483
|
+
});
|
|
3484
|
+
});
|
|
3485
|
+
const BlueBubblesAllowFromEntry = z.union([z.string(), z.number()]);
|
|
3486
|
+
const BlueBubblesActionSchema = z.object({
|
|
3487
|
+
reactions: z.boolean().optional(),
|
|
3488
|
+
edit: z.boolean().optional(),
|
|
3489
|
+
unsend: z.boolean().optional(),
|
|
3490
|
+
reply: z.boolean().optional(),
|
|
3491
|
+
sendWithEffect: z.boolean().optional(),
|
|
3492
|
+
renameGroup: z.boolean().optional(),
|
|
3493
|
+
setGroupIcon: z.boolean().optional(),
|
|
3494
|
+
addParticipant: z.boolean().optional(),
|
|
3495
|
+
removeParticipant: z.boolean().optional(),
|
|
3496
|
+
leaveGroup: z.boolean().optional(),
|
|
3497
|
+
sendAttachment: z.boolean().optional()
|
|
3498
|
+
}).strict().optional();
|
|
3499
|
+
const BlueBubblesGroupConfigSchema = z.object({
|
|
3500
|
+
requireMention: z.boolean().optional(),
|
|
3501
|
+
tools: ToolPolicySchema,
|
|
3502
|
+
toolsBySender: ToolPolicyBySenderSchema$1
|
|
3503
|
+
}).strict();
|
|
3504
|
+
const BlueBubblesAccountSchemaBase = z.object({
|
|
3505
|
+
name: z.string().optional(),
|
|
3506
|
+
capabilities: z.array(z.string()).optional(),
|
|
3507
|
+
markdown: MarkdownConfigSchema,
|
|
3508
|
+
configWrites: z.boolean().optional(),
|
|
3509
|
+
enabled: z.boolean().optional(),
|
|
3510
|
+
serverUrl: z.string().optional(),
|
|
3511
|
+
password: z.string().optional(),
|
|
3512
|
+
webhookPath: z.string().optional(),
|
|
3513
|
+
dmPolicy: DmPolicySchema.optional().default("pairing"),
|
|
3514
|
+
allowFrom: z.array(BlueBubblesAllowFromEntry).optional(),
|
|
3515
|
+
groupAllowFrom: z.array(BlueBubblesAllowFromEntry).optional(),
|
|
3516
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
3517
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
3518
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
3519
|
+
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
|
3520
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
3521
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
3522
|
+
mediaMaxMb: z.number().int().positive().optional(),
|
|
3523
|
+
sendReadReceipts: z.boolean().optional(),
|
|
3524
|
+
blockStreaming: z.boolean().optional(),
|
|
3525
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
3526
|
+
groups: z.record(z.string(), BlueBubblesGroupConfigSchema.optional()).optional(),
|
|
3527
|
+
heartbeat: ChannelHeartbeatVisibilitySchema,
|
|
3528
|
+
responsePrefix: z.string().optional()
|
|
3529
|
+
}).strict();
|
|
3530
|
+
const BlueBubblesAccountSchema = BlueBubblesAccountSchemaBase.superRefine((value, ctx) => {
|
|
3531
|
+
requireOpenAllowFrom({
|
|
3532
|
+
policy: value.dmPolicy,
|
|
3533
|
+
allowFrom: value.allowFrom,
|
|
3534
|
+
ctx,
|
|
3535
|
+
path: ["allowFrom"],
|
|
3536
|
+
message: "channels.bluebubbles.accounts.*.dmPolicy=\"open\" requires allowFrom to include \"*\""
|
|
3537
|
+
});
|
|
3538
|
+
});
|
|
3539
|
+
const BlueBubblesConfigSchema = BlueBubblesAccountSchemaBase.extend({
|
|
3540
|
+
accounts: z.record(z.string(), BlueBubblesAccountSchema.optional()).optional(),
|
|
3541
|
+
actions: BlueBubblesActionSchema
|
|
3542
|
+
}).superRefine((value, ctx) => {
|
|
3543
|
+
requireOpenAllowFrom({
|
|
3544
|
+
policy: value.dmPolicy,
|
|
3545
|
+
allowFrom: value.allowFrom,
|
|
3546
|
+
ctx,
|
|
3547
|
+
path: ["allowFrom"],
|
|
3548
|
+
message: "channels.bluebubbles.dmPolicy=\"open\" requires channels.bluebubbles.allowFrom to include \"*\""
|
|
3549
|
+
});
|
|
3550
|
+
});
|
|
3551
|
+
const MSTeamsChannelSchema = z.object({
|
|
3552
|
+
requireMention: z.boolean().optional(),
|
|
3553
|
+
tools: ToolPolicySchema,
|
|
3554
|
+
toolsBySender: ToolPolicyBySenderSchema$1,
|
|
3555
|
+
replyStyle: MSTeamsReplyStyleSchema.optional()
|
|
3556
|
+
}).strict();
|
|
3557
|
+
const MSTeamsTeamSchema = z.object({
|
|
3558
|
+
requireMention: z.boolean().optional(),
|
|
3559
|
+
tools: ToolPolicySchema,
|
|
3560
|
+
toolsBySender: ToolPolicyBySenderSchema$1,
|
|
3561
|
+
replyStyle: MSTeamsReplyStyleSchema.optional(),
|
|
3562
|
+
channels: z.record(z.string(), MSTeamsChannelSchema.optional()).optional()
|
|
3563
|
+
}).strict();
|
|
3564
|
+
const MSTeamsConfigSchema = z.object({
|
|
3565
|
+
enabled: z.boolean().optional(),
|
|
3566
|
+
capabilities: z.array(z.string()).optional(),
|
|
3567
|
+
markdown: MarkdownConfigSchema,
|
|
3568
|
+
configWrites: z.boolean().optional(),
|
|
3569
|
+
appId: z.string().optional(),
|
|
3570
|
+
appPassword: z.string().optional(),
|
|
3571
|
+
tenantId: z.string().optional(),
|
|
3572
|
+
webhook: z.object({
|
|
3573
|
+
port: z.number().int().positive().optional(),
|
|
3574
|
+
path: z.string().optional()
|
|
3575
|
+
}).strict().optional(),
|
|
3576
|
+
dmPolicy: DmPolicySchema.optional().default("pairing"),
|
|
3577
|
+
allowFrom: z.array(z.string()).optional(),
|
|
3578
|
+
groupAllowFrom: z.array(z.string()).optional(),
|
|
3579
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
3580
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
3581
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
3582
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
3583
|
+
mediaAllowHosts: z.array(z.string()).optional(),
|
|
3584
|
+
mediaAuthAllowHosts: z.array(z.string()).optional(),
|
|
3585
|
+
requireMention: z.boolean().optional(),
|
|
3586
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
3587
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
3588
|
+
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
|
3589
|
+
replyStyle: MSTeamsReplyStyleSchema.optional(),
|
|
3590
|
+
teams: z.record(z.string(), MSTeamsTeamSchema.optional()).optional(),
|
|
3591
|
+
mediaMaxMb: z.number().positive().optional(),
|
|
3592
|
+
sharePointSiteId: z.string().optional(),
|
|
3593
|
+
heartbeat: ChannelHeartbeatVisibilitySchema,
|
|
3594
|
+
responsePrefix: z.string().optional()
|
|
3595
|
+
}).strict().superRefine((value, ctx) => {
|
|
3596
|
+
requireOpenAllowFrom({
|
|
3597
|
+
policy: value.dmPolicy,
|
|
3598
|
+
allowFrom: value.allowFrom,
|
|
3599
|
+
ctx,
|
|
3600
|
+
path: ["allowFrom"],
|
|
3601
|
+
message: "channels.msteams.dmPolicy=\"open\" requires channels.msteams.allowFrom to include \"*\""
|
|
3602
|
+
});
|
|
3603
|
+
});
|
|
3604
|
+
|
|
3605
|
+
//#endregion
|
|
3606
|
+
//#region src/config/zod-schema.providers-whatsapp.ts
|
|
3607
|
+
const ToolPolicyBySenderSchema = z.record(z.string(), ToolPolicySchema).optional();
|
|
3608
|
+
const WhatsAppAccountSchema = z.object({
|
|
3609
|
+
name: z.string().optional(),
|
|
3610
|
+
capabilities: z.array(z.string()).optional(),
|
|
3611
|
+
markdown: MarkdownConfigSchema,
|
|
3612
|
+
configWrites: z.boolean().optional(),
|
|
3613
|
+
enabled: z.boolean().optional(),
|
|
3614
|
+
sendReadReceipts: z.boolean().optional(),
|
|
3615
|
+
messagePrefix: z.string().optional(),
|
|
3616
|
+
responsePrefix: z.string().optional(),
|
|
3617
|
+
authDir: z.string().optional(),
|
|
3618
|
+
dmPolicy: DmPolicySchema.optional().default("pairing"),
|
|
3619
|
+
selfChatMode: z.boolean().optional(),
|
|
3620
|
+
allowFrom: z.array(z.string()).optional(),
|
|
3621
|
+
groupAllowFrom: z.array(z.string()).optional(),
|
|
3622
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
3623
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
3624
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
3625
|
+
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
|
3626
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
3627
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
3628
|
+
mediaMaxMb: z.number().int().positive().optional(),
|
|
3629
|
+
blockStreaming: z.boolean().optional(),
|
|
3630
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
3631
|
+
groups: z.record(z.string(), z.object({
|
|
3632
|
+
requireMention: z.boolean().optional(),
|
|
3633
|
+
tools: ToolPolicySchema,
|
|
3634
|
+
toolsBySender: ToolPolicyBySenderSchema
|
|
3635
|
+
}).strict().optional()).optional(),
|
|
3636
|
+
ackReaction: z.object({
|
|
3637
|
+
emoji: z.string().optional(),
|
|
3638
|
+
direct: z.boolean().optional().default(true),
|
|
3639
|
+
group: z.enum([
|
|
3640
|
+
"always",
|
|
3641
|
+
"mentions",
|
|
3642
|
+
"never"
|
|
3643
|
+
]).optional().default("mentions")
|
|
3644
|
+
}).strict().optional(),
|
|
3645
|
+
debounceMs: z.number().int().nonnegative().optional().default(0),
|
|
3646
|
+
heartbeat: ChannelHeartbeatVisibilitySchema
|
|
3647
|
+
}).strict().superRefine((value, ctx) => {
|
|
3648
|
+
if (value.dmPolicy !== "open") return;
|
|
3649
|
+
if ((value.allowFrom ?? []).map((v) => String(v).trim()).filter(Boolean).includes("*")) return;
|
|
3650
|
+
ctx.addIssue({
|
|
3651
|
+
code: z.ZodIssueCode.custom,
|
|
3652
|
+
path: ["allowFrom"],
|
|
3653
|
+
message: "channels.whatsapp.accounts.*.dmPolicy=\"open\" requires allowFrom to include \"*\""
|
|
3654
|
+
});
|
|
3655
|
+
});
|
|
3656
|
+
const WhatsAppConfigSchema = z.object({
|
|
3657
|
+
accounts: z.record(z.string(), WhatsAppAccountSchema.optional()).optional(),
|
|
3658
|
+
capabilities: z.array(z.string()).optional(),
|
|
3659
|
+
markdown: MarkdownConfigSchema,
|
|
3660
|
+
configWrites: z.boolean().optional(),
|
|
3661
|
+
sendReadReceipts: z.boolean().optional(),
|
|
3662
|
+
dmPolicy: DmPolicySchema.optional().default("pairing"),
|
|
3663
|
+
messagePrefix: z.string().optional(),
|
|
3664
|
+
responsePrefix: z.string().optional(),
|
|
3665
|
+
selfChatMode: z.boolean().optional(),
|
|
3666
|
+
allowFrom: z.array(z.string()).optional(),
|
|
3667
|
+
groupAllowFrom: z.array(z.string()).optional(),
|
|
3668
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
3669
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
3670
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
3671
|
+
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
|
3672
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
3673
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
3674
|
+
mediaMaxMb: z.number().int().positive().optional().default(50),
|
|
3675
|
+
blockStreaming: z.boolean().optional(),
|
|
3676
|
+
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
|
3677
|
+
actions: z.object({
|
|
3678
|
+
reactions: z.boolean().optional(),
|
|
3679
|
+
sendMessage: z.boolean().optional(),
|
|
3680
|
+
polls: z.boolean().optional()
|
|
3681
|
+
}).strict().optional(),
|
|
3682
|
+
groups: z.record(z.string(), z.object({
|
|
3683
|
+
requireMention: z.boolean().optional(),
|
|
3684
|
+
tools: ToolPolicySchema,
|
|
3685
|
+
toolsBySender: ToolPolicyBySenderSchema
|
|
3686
|
+
}).strict().optional()).optional(),
|
|
3687
|
+
ackReaction: z.object({
|
|
3688
|
+
emoji: z.string().optional(),
|
|
3689
|
+
direct: z.boolean().optional().default(true),
|
|
3690
|
+
group: z.enum([
|
|
3691
|
+
"always",
|
|
3692
|
+
"mentions",
|
|
3693
|
+
"never"
|
|
3694
|
+
]).optional().default("mentions")
|
|
3695
|
+
}).strict().optional(),
|
|
3696
|
+
debounceMs: z.number().int().nonnegative().optional().default(0),
|
|
3697
|
+
heartbeat: ChannelHeartbeatVisibilitySchema
|
|
3698
|
+
}).strict().superRefine((value, ctx) => {
|
|
3699
|
+
if (value.dmPolicy !== "open") return;
|
|
3700
|
+
if ((value.allowFrom ?? []).map((v) => String(v).trim()).filter(Boolean).includes("*")) return;
|
|
3701
|
+
ctx.addIssue({
|
|
3702
|
+
code: z.ZodIssueCode.custom,
|
|
3703
|
+
path: ["allowFrom"],
|
|
3704
|
+
message: "channels.whatsapp.dmPolicy=\"open\" requires channels.whatsapp.allowFrom to include \"*\""
|
|
3705
|
+
});
|
|
3706
|
+
});
|
|
3707
|
+
|
|
3708
|
+
//#endregion
|
|
3709
|
+
//#region src/config/zod-schema.providers.ts
|
|
3710
|
+
const ChannelsSchema = z.object({
|
|
3711
|
+
defaults: z.object({
|
|
3712
|
+
groupPolicy: GroupPolicySchema.optional(),
|
|
3713
|
+
heartbeat: ChannelHeartbeatVisibilitySchema
|
|
3714
|
+
}).strict().optional(),
|
|
3715
|
+
whatsapp: WhatsAppConfigSchema.optional(),
|
|
3716
|
+
telegram: TelegramConfigSchema.optional(),
|
|
3717
|
+
discord: DiscordConfigSchema.optional(),
|
|
3718
|
+
googlechat: GoogleChatConfigSchema.optional(),
|
|
3719
|
+
slack: SlackConfigSchema.optional(),
|
|
3720
|
+
signal: SignalConfigSchema.optional(),
|
|
3721
|
+
imessage: IMessageConfigSchema.optional(),
|
|
3722
|
+
bluebubbles: BlueBubblesConfigSchema.optional(),
|
|
3723
|
+
msteams: MSTeamsConfigSchema.optional()
|
|
3724
|
+
}).passthrough().optional();
|
|
3725
|
+
|
|
3726
|
+
//#endregion
|
|
3727
|
+
//#region src/config/zod-schema.session.ts
|
|
3728
|
+
const SessionResetConfigSchema = z.object({
|
|
3729
|
+
mode: z.union([z.literal("daily"), z.literal("idle")]).optional(),
|
|
3730
|
+
atHour: z.number().int().min(0).max(23).optional(),
|
|
3731
|
+
idleMinutes: z.number().int().positive().optional()
|
|
3732
|
+
}).strict();
|
|
3733
|
+
const SessionSendPolicySchema = z.object({
|
|
3734
|
+
default: z.union([z.literal("allow"), z.literal("deny")]).optional(),
|
|
3735
|
+
rules: z.array(z.object({
|
|
3736
|
+
action: z.union([z.literal("allow"), z.literal("deny")]),
|
|
3737
|
+
match: z.object({
|
|
3738
|
+
channel: z.string().optional(),
|
|
3739
|
+
chatType: z.union([
|
|
3740
|
+
z.literal("direct"),
|
|
3741
|
+
z.literal("group"),
|
|
3742
|
+
z.literal("channel")
|
|
3743
|
+
]).optional(),
|
|
3744
|
+
keyPrefix: z.string().optional()
|
|
3745
|
+
}).strict().optional()
|
|
3746
|
+
}).strict()).optional()
|
|
3747
|
+
}).strict();
|
|
3748
|
+
const SessionSchema = z.object({
|
|
3749
|
+
scope: z.union([z.literal("per-sender"), z.literal("global")]).optional(),
|
|
3750
|
+
dmScope: z.union([
|
|
3751
|
+
z.literal("main"),
|
|
3752
|
+
z.literal("per-peer"),
|
|
3753
|
+
z.literal("per-channel-peer"),
|
|
3754
|
+
z.literal("per-account-channel-peer")
|
|
3755
|
+
]).optional(),
|
|
3756
|
+
identityLinks: z.record(z.string(), z.array(z.string())).optional(),
|
|
3757
|
+
resetTriggers: z.array(z.string()).optional(),
|
|
3758
|
+
idleMinutes: z.number().int().positive().optional(),
|
|
3759
|
+
reset: SessionResetConfigSchema.optional(),
|
|
3760
|
+
resetByType: z.object({
|
|
3761
|
+
dm: SessionResetConfigSchema.optional(),
|
|
3762
|
+
group: SessionResetConfigSchema.optional(),
|
|
3763
|
+
thread: SessionResetConfigSchema.optional()
|
|
3764
|
+
}).strict().optional(),
|
|
3765
|
+
resetByChannel: z.record(z.string(), SessionResetConfigSchema).optional(),
|
|
3766
|
+
store: z.string().optional(),
|
|
3767
|
+
typingIntervalSeconds: z.number().int().positive().optional(),
|
|
3768
|
+
typingMode: z.union([
|
|
3769
|
+
z.literal("never"),
|
|
3770
|
+
z.literal("instant"),
|
|
3771
|
+
z.literal("thinking"),
|
|
3772
|
+
z.literal("message")
|
|
3773
|
+
]).optional(),
|
|
3774
|
+
mainKey: z.string().optional(),
|
|
3775
|
+
sendPolicy: SessionSendPolicySchema.optional(),
|
|
3776
|
+
agentToAgent: z.object({ maxPingPongTurns: z.number().int().min(0).max(5).optional() }).strict().optional()
|
|
3777
|
+
}).strict().optional();
|
|
3778
|
+
const MessagesSchema = z.object({
|
|
3779
|
+
messagePrefix: z.string().optional(),
|
|
3780
|
+
responsePrefix: z.string().optional(),
|
|
3781
|
+
groupChat: GroupChatSchema,
|
|
3782
|
+
queue: QueueSchema,
|
|
3783
|
+
inbound: InboundDebounceSchema,
|
|
3784
|
+
ackReaction: z.string().optional(),
|
|
3785
|
+
ackReactionScope: z.enum([
|
|
3786
|
+
"group-mentions",
|
|
3787
|
+
"group-all",
|
|
3788
|
+
"direct",
|
|
3789
|
+
"all"
|
|
3790
|
+
]).optional(),
|
|
3791
|
+
removeAckAfterReply: z.boolean().optional(),
|
|
3792
|
+
tts: TtsConfigSchema
|
|
3793
|
+
}).strict().optional();
|
|
3794
|
+
const CommandsSchema = z.object({
|
|
3795
|
+
native: NativeCommandsSettingSchema.optional().default("auto"),
|
|
3796
|
+
nativeSkills: NativeCommandsSettingSchema.optional().default("auto"),
|
|
3797
|
+
text: z.boolean().optional(),
|
|
3798
|
+
bash: z.boolean().optional(),
|
|
3799
|
+
bashForegroundMs: z.number().int().min(0).max(3e4).optional(),
|
|
3800
|
+
config: z.boolean().optional(),
|
|
3801
|
+
debug: z.boolean().optional(),
|
|
3802
|
+
restart: z.boolean().optional(),
|
|
3803
|
+
useAccessGroups: z.boolean().optional(),
|
|
3804
|
+
ownerAllowFrom: z.array(z.union([z.string(), z.number()])).optional()
|
|
3805
|
+
}).strict().optional().default({
|
|
3806
|
+
native: "auto",
|
|
3807
|
+
nativeSkills: "auto"
|
|
3808
|
+
});
|
|
3809
|
+
|
|
3810
|
+
//#endregion
|
|
3811
|
+
//#region src/config/zod-schema.ts
|
|
3812
|
+
const BrowserSnapshotDefaultsSchema = z.object({ mode: z.literal("efficient").optional() }).strict().optional();
|
|
3813
|
+
const NodeHostSchema = z.object({ browserProxy: z.object({
|
|
3814
|
+
enabled: z.boolean().optional(),
|
|
3815
|
+
allowProfiles: z.array(z.string()).optional()
|
|
3816
|
+
}).strict().optional() }).strict().optional();
|
|
3817
|
+
const MemoryQmdPathSchema = z.object({
|
|
3818
|
+
path: z.string(),
|
|
3819
|
+
name: z.string().optional(),
|
|
3820
|
+
pattern: z.string().optional()
|
|
3821
|
+
}).strict();
|
|
3822
|
+
const MemoryQmdSessionSchema = z.object({
|
|
3823
|
+
enabled: z.boolean().optional(),
|
|
3824
|
+
exportDir: z.string().optional(),
|
|
3825
|
+
retentionDays: z.number().int().nonnegative().optional()
|
|
3826
|
+
}).strict();
|
|
3827
|
+
const MemoryQmdUpdateSchema = z.object({
|
|
3828
|
+
interval: z.string().optional(),
|
|
3829
|
+
debounceMs: z.number().int().nonnegative().optional(),
|
|
3830
|
+
onBoot: z.boolean().optional(),
|
|
3831
|
+
embedInterval: z.string().optional()
|
|
3832
|
+
}).strict();
|
|
3833
|
+
const MemoryQmdLimitsSchema = z.object({
|
|
3834
|
+
maxResults: z.number().int().positive().optional(),
|
|
3835
|
+
maxSnippetChars: z.number().int().positive().optional(),
|
|
3836
|
+
maxInjectedChars: z.number().int().positive().optional(),
|
|
3837
|
+
timeoutMs: z.number().int().nonnegative().optional()
|
|
3838
|
+
}).strict();
|
|
3839
|
+
const MemoryQmdSchema = z.object({
|
|
3840
|
+
command: z.string().optional(),
|
|
3841
|
+
includeDefaultMemory: z.boolean().optional(),
|
|
3842
|
+
paths: z.array(MemoryQmdPathSchema).optional(),
|
|
3843
|
+
sessions: MemoryQmdSessionSchema.optional(),
|
|
3844
|
+
update: MemoryQmdUpdateSchema.optional(),
|
|
3845
|
+
limits: MemoryQmdLimitsSchema.optional(),
|
|
3846
|
+
scope: SessionSendPolicySchema.optional()
|
|
3847
|
+
}).strict();
|
|
3848
|
+
const MemorySchema = z.object({
|
|
3849
|
+
backend: z.union([z.literal("builtin"), z.literal("qmd")]).optional(),
|
|
3850
|
+
citations: z.union([
|
|
3851
|
+
z.literal("auto"),
|
|
3852
|
+
z.literal("on"),
|
|
3853
|
+
z.literal("off")
|
|
3854
|
+
]).optional(),
|
|
3855
|
+
qmd: MemoryQmdSchema.optional()
|
|
3856
|
+
}).strict().optional();
|
|
3857
|
+
const OpenClawSchema = z.object({
|
|
3858
|
+
meta: z.object({
|
|
3859
|
+
lastTouchedVersion: z.string().optional(),
|
|
3860
|
+
lastTouchedAt: z.string().optional()
|
|
3861
|
+
}).strict().optional(),
|
|
3862
|
+
env: z.object({
|
|
3863
|
+
shellEnv: z.object({
|
|
3864
|
+
enabled: z.boolean().optional(),
|
|
3865
|
+
timeoutMs: z.number().int().nonnegative().optional()
|
|
3866
|
+
}).strict().optional(),
|
|
3867
|
+
vars: z.record(z.string(), z.string()).optional()
|
|
3868
|
+
}).catchall(z.string()).optional(),
|
|
3869
|
+
wizard: z.object({
|
|
3870
|
+
lastRunAt: z.string().optional(),
|
|
3871
|
+
lastRunVersion: z.string().optional(),
|
|
3872
|
+
lastRunCommit: z.string().optional(),
|
|
3873
|
+
lastRunCommand: z.string().optional(),
|
|
3874
|
+
lastRunMode: z.union([z.literal("local"), z.literal("remote")]).optional()
|
|
3875
|
+
}).strict().optional(),
|
|
3876
|
+
diagnostics: z.object({
|
|
3877
|
+
enabled: z.boolean().optional(),
|
|
3878
|
+
flags: z.array(z.string()).optional(),
|
|
3879
|
+
otel: z.object({
|
|
3880
|
+
enabled: z.boolean().optional(),
|
|
3881
|
+
endpoint: z.string().optional(),
|
|
3882
|
+
protocol: z.union([z.literal("http/protobuf"), z.literal("grpc")]).optional(),
|
|
3883
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
3884
|
+
serviceName: z.string().optional(),
|
|
3885
|
+
traces: z.boolean().optional(),
|
|
3886
|
+
metrics: z.boolean().optional(),
|
|
3887
|
+
logs: z.boolean().optional(),
|
|
3888
|
+
sampleRate: z.number().min(0).max(1).optional(),
|
|
3889
|
+
flushIntervalMs: z.number().int().nonnegative().optional()
|
|
3890
|
+
}).strict().optional(),
|
|
3891
|
+
cacheTrace: z.object({
|
|
3892
|
+
enabled: z.boolean().optional(),
|
|
3893
|
+
filePath: z.string().optional(),
|
|
3894
|
+
includeMessages: z.boolean().optional(),
|
|
3895
|
+
includePrompt: z.boolean().optional(),
|
|
3896
|
+
includeSystem: z.boolean().optional()
|
|
3897
|
+
}).strict().optional()
|
|
3898
|
+
}).strict().optional(),
|
|
3899
|
+
logging: z.object({
|
|
3900
|
+
level: z.union([
|
|
3901
|
+
z.literal("silent"),
|
|
3902
|
+
z.literal("fatal"),
|
|
3903
|
+
z.literal("error"),
|
|
3904
|
+
z.literal("warn"),
|
|
3905
|
+
z.literal("info"),
|
|
3906
|
+
z.literal("debug"),
|
|
3907
|
+
z.literal("trace")
|
|
3908
|
+
]).optional(),
|
|
3909
|
+
file: z.string().optional(),
|
|
3910
|
+
consoleLevel: z.union([
|
|
3911
|
+
z.literal("silent"),
|
|
3912
|
+
z.literal("fatal"),
|
|
3913
|
+
z.literal("error"),
|
|
3914
|
+
z.literal("warn"),
|
|
3915
|
+
z.literal("info"),
|
|
3916
|
+
z.literal("debug"),
|
|
3917
|
+
z.literal("trace")
|
|
3918
|
+
]).optional(),
|
|
3919
|
+
consoleStyle: z.union([
|
|
3920
|
+
z.literal("pretty"),
|
|
3921
|
+
z.literal("compact"),
|
|
3922
|
+
z.literal("json")
|
|
3923
|
+
]).optional(),
|
|
3924
|
+
redactSensitive: z.union([z.literal("off"), z.literal("tools")]).optional(),
|
|
3925
|
+
redactPatterns: z.array(z.string()).optional()
|
|
3926
|
+
}).strict().optional(),
|
|
3927
|
+
update: z.object({
|
|
3928
|
+
channel: z.union([
|
|
3929
|
+
z.literal("stable"),
|
|
3930
|
+
z.literal("beta"),
|
|
3931
|
+
z.literal("dev")
|
|
3932
|
+
]).optional(),
|
|
3933
|
+
checkOnStart: z.boolean().optional()
|
|
3934
|
+
}).strict().optional(),
|
|
3935
|
+
browser: z.object({
|
|
3936
|
+
enabled: z.boolean().optional(),
|
|
3937
|
+
evaluateEnabled: z.boolean().optional(),
|
|
3938
|
+
cdpUrl: z.string().optional(),
|
|
3939
|
+
remoteCdpTimeoutMs: z.number().int().nonnegative().optional(),
|
|
3940
|
+
remoteCdpHandshakeTimeoutMs: z.number().int().nonnegative().optional(),
|
|
3941
|
+
color: z.string().optional(),
|
|
3942
|
+
executablePath: z.string().optional(),
|
|
3943
|
+
headless: z.boolean().optional(),
|
|
3944
|
+
noSandbox: z.boolean().optional(),
|
|
3945
|
+
attachOnly: z.boolean().optional(),
|
|
3946
|
+
defaultProfile: z.string().optional(),
|
|
3947
|
+
snapshotDefaults: BrowserSnapshotDefaultsSchema,
|
|
3948
|
+
profiles: z.record(z.string().regex(/^[a-z0-9-]+$/, "Profile names must be alphanumeric with hyphens only"), z.object({
|
|
3949
|
+
cdpPort: z.number().int().min(1).max(65535).optional(),
|
|
3950
|
+
cdpUrl: z.string().optional(),
|
|
3951
|
+
driver: z.union([z.literal("clawd"), z.literal("extension")]).optional(),
|
|
3952
|
+
color: HexColorSchema
|
|
3953
|
+
}).strict().refine((value) => value.cdpPort || value.cdpUrl, { message: "Profile must set cdpPort or cdpUrl" })).optional()
|
|
3954
|
+
}).strict().optional(),
|
|
3955
|
+
ui: z.object({
|
|
3956
|
+
seamColor: HexColorSchema.optional(),
|
|
3957
|
+
assistant: z.object({
|
|
3958
|
+
name: z.string().max(50).optional(),
|
|
3959
|
+
avatar: z.string().max(200).optional()
|
|
3960
|
+
}).strict().optional()
|
|
3961
|
+
}).strict().optional(),
|
|
3962
|
+
auth: z.object({
|
|
3963
|
+
profiles: z.record(z.string(), z.object({
|
|
3964
|
+
provider: z.string(),
|
|
3965
|
+
mode: z.union([
|
|
3966
|
+
z.literal("api_key"),
|
|
3967
|
+
z.literal("oauth"),
|
|
3968
|
+
z.literal("token")
|
|
3969
|
+
]),
|
|
3970
|
+
email: z.string().optional()
|
|
3971
|
+
}).strict()).optional(),
|
|
3972
|
+
order: z.record(z.string(), z.array(z.string())).optional(),
|
|
3973
|
+
cooldowns: z.object({
|
|
3974
|
+
billingBackoffHours: z.number().positive().optional(),
|
|
3975
|
+
billingBackoffHoursByProvider: z.record(z.string(), z.number().positive()).optional(),
|
|
3976
|
+
billingMaxHours: z.number().positive().optional(),
|
|
3977
|
+
failureWindowHours: z.number().positive().optional()
|
|
3978
|
+
}).strict().optional()
|
|
3979
|
+
}).strict().optional(),
|
|
3980
|
+
models: ModelsConfigSchema,
|
|
3981
|
+
nodeHost: NodeHostSchema,
|
|
3982
|
+
agents: AgentsSchema,
|
|
3983
|
+
tools: ToolsSchema,
|
|
3984
|
+
bindings: BindingsSchema,
|
|
3985
|
+
broadcast: BroadcastSchema,
|
|
3986
|
+
audio: AudioSchema,
|
|
3987
|
+
media: z.object({ preserveFilenames: z.boolean().optional() }).strict().optional(),
|
|
3988
|
+
messages: MessagesSchema,
|
|
3989
|
+
commands: CommandsSchema,
|
|
3990
|
+
approvals: ApprovalsSchema,
|
|
3991
|
+
session: SessionSchema,
|
|
3992
|
+
cron: z.object({
|
|
3993
|
+
enabled: z.boolean().optional(),
|
|
3994
|
+
store: z.string().optional(),
|
|
3995
|
+
maxConcurrentRuns: z.number().int().positive().optional()
|
|
3996
|
+
}).strict().optional(),
|
|
3997
|
+
hooks: z.object({
|
|
3998
|
+
enabled: z.boolean().optional(),
|
|
3999
|
+
path: z.string().optional(),
|
|
4000
|
+
token: z.string().optional(),
|
|
4001
|
+
maxBodyBytes: z.number().int().positive().optional(),
|
|
4002
|
+
presets: z.array(z.string()).optional(),
|
|
4003
|
+
transformsDir: z.string().optional(),
|
|
4004
|
+
mappings: z.array(HookMappingSchema).optional(),
|
|
4005
|
+
gmail: HooksGmailSchema,
|
|
4006
|
+
internal: InternalHooksSchema
|
|
4007
|
+
}).strict().optional(),
|
|
4008
|
+
web: z.object({
|
|
4009
|
+
enabled: z.boolean().optional(),
|
|
4010
|
+
heartbeatSeconds: z.number().int().positive().optional(),
|
|
4011
|
+
reconnect: z.object({
|
|
4012
|
+
initialMs: z.number().positive().optional(),
|
|
4013
|
+
maxMs: z.number().positive().optional(),
|
|
4014
|
+
factor: z.number().positive().optional(),
|
|
4015
|
+
jitter: z.number().min(0).max(1).optional(),
|
|
4016
|
+
maxAttempts: z.number().int().min(0).optional()
|
|
4017
|
+
}).strict().optional()
|
|
4018
|
+
}).strict().optional(),
|
|
4019
|
+
channels: ChannelsSchema,
|
|
4020
|
+
discovery: z.object({
|
|
4021
|
+
wideArea: z.object({ enabled: z.boolean().optional() }).strict().optional(),
|
|
4022
|
+
mdns: z.object({ mode: z.enum([
|
|
4023
|
+
"off",
|
|
4024
|
+
"minimal",
|
|
4025
|
+
"full"
|
|
4026
|
+
]).optional() }).strict().optional()
|
|
4027
|
+
}).strict().optional(),
|
|
4028
|
+
canvasHost: z.object({
|
|
4029
|
+
enabled: z.boolean().optional(),
|
|
4030
|
+
root: z.string().optional(),
|
|
4031
|
+
port: z.number().int().positive().optional(),
|
|
4032
|
+
liveReload: z.boolean().optional()
|
|
4033
|
+
}).strict().optional(),
|
|
4034
|
+
talk: z.object({
|
|
4035
|
+
voiceId: z.string().optional(),
|
|
4036
|
+
voiceAliases: z.record(z.string(), z.string()).optional(),
|
|
4037
|
+
modelId: z.string().optional(),
|
|
4038
|
+
outputFormat: z.string().optional(),
|
|
4039
|
+
apiKey: z.string().optional(),
|
|
4040
|
+
interruptOnSpeech: z.boolean().optional()
|
|
4041
|
+
}).strict().optional(),
|
|
4042
|
+
gateway: z.object({
|
|
4043
|
+
port: z.number().int().positive().optional(),
|
|
4044
|
+
mode: z.union([z.literal("local"), z.literal("remote")]).optional(),
|
|
4045
|
+
bind: z.union([
|
|
4046
|
+
z.literal("auto"),
|
|
4047
|
+
z.literal("lan"),
|
|
4048
|
+
z.literal("loopback"),
|
|
4049
|
+
z.literal("custom"),
|
|
4050
|
+
z.literal("tailnet")
|
|
4051
|
+
]).optional(),
|
|
4052
|
+
controlUi: z.object({
|
|
4053
|
+
enabled: z.boolean().optional(),
|
|
4054
|
+
basePath: z.string().optional(),
|
|
4055
|
+
root: z.string().optional(),
|
|
4056
|
+
allowedOrigins: z.array(z.string()).optional(),
|
|
4057
|
+
allowInsecureAuth: z.boolean().optional(),
|
|
4058
|
+
dangerouslyDisableDeviceAuth: z.boolean().optional()
|
|
4059
|
+
}).strict().optional(),
|
|
4060
|
+
auth: z.object({
|
|
4061
|
+
mode: z.union([z.literal("token"), z.literal("password")]).optional(),
|
|
4062
|
+
token: z.string().optional(),
|
|
4063
|
+
password: z.string().optional(),
|
|
4064
|
+
allowTailscale: z.boolean().optional()
|
|
4065
|
+
}).strict().optional(),
|
|
4066
|
+
trustedProxies: z.array(z.string()).optional(),
|
|
4067
|
+
tailscale: z.object({
|
|
4068
|
+
mode: z.union([
|
|
4069
|
+
z.literal("off"),
|
|
4070
|
+
z.literal("serve"),
|
|
4071
|
+
z.literal("funnel")
|
|
4072
|
+
]).optional(),
|
|
4073
|
+
resetOnExit: z.boolean().optional()
|
|
4074
|
+
}).strict().optional(),
|
|
4075
|
+
remote: z.object({
|
|
4076
|
+
url: z.string().optional(),
|
|
4077
|
+
transport: z.union([z.literal("ssh"), z.literal("direct")]).optional(),
|
|
4078
|
+
token: z.string().optional(),
|
|
4079
|
+
password: z.string().optional(),
|
|
4080
|
+
tlsFingerprint: z.string().optional(),
|
|
4081
|
+
sshTarget: z.string().optional(),
|
|
4082
|
+
sshIdentity: z.string().optional()
|
|
4083
|
+
}).strict().optional(),
|
|
4084
|
+
reload: z.object({
|
|
4085
|
+
mode: z.union([
|
|
4086
|
+
z.literal("off"),
|
|
4087
|
+
z.literal("restart"),
|
|
4088
|
+
z.literal("hot"),
|
|
4089
|
+
z.literal("hybrid")
|
|
4090
|
+
]).optional(),
|
|
4091
|
+
debounceMs: z.number().int().min(0).optional()
|
|
4092
|
+
}).strict().optional(),
|
|
4093
|
+
tls: z.object({
|
|
4094
|
+
enabled: z.boolean().optional(),
|
|
4095
|
+
autoGenerate: z.boolean().optional(),
|
|
4096
|
+
certPath: z.string().optional(),
|
|
4097
|
+
keyPath: z.string().optional(),
|
|
4098
|
+
caPath: z.string().optional()
|
|
4099
|
+
}).optional(),
|
|
4100
|
+
http: z.object({ endpoints: z.object({
|
|
4101
|
+
chatCompletions: z.object({ enabled: z.boolean().optional() }).strict().optional(),
|
|
4102
|
+
responses: z.object({
|
|
4103
|
+
enabled: z.boolean().optional(),
|
|
4104
|
+
maxBodyBytes: z.number().int().positive().optional(),
|
|
4105
|
+
files: z.object({
|
|
4106
|
+
allowUrl: z.boolean().optional(),
|
|
4107
|
+
allowedMimes: z.array(z.string()).optional(),
|
|
4108
|
+
maxBytes: z.number().int().positive().optional(),
|
|
4109
|
+
maxChars: z.number().int().positive().optional(),
|
|
4110
|
+
maxRedirects: z.number().int().nonnegative().optional(),
|
|
4111
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
4112
|
+
pdf: z.object({
|
|
4113
|
+
maxPages: z.number().int().positive().optional(),
|
|
4114
|
+
maxPixels: z.number().int().positive().optional(),
|
|
4115
|
+
minTextChars: z.number().int().nonnegative().optional()
|
|
4116
|
+
}).strict().optional()
|
|
4117
|
+
}).strict().optional(),
|
|
4118
|
+
images: z.object({
|
|
4119
|
+
allowUrl: z.boolean().optional(),
|
|
4120
|
+
allowedMimes: z.array(z.string()).optional(),
|
|
4121
|
+
maxBytes: z.number().int().positive().optional(),
|
|
4122
|
+
maxRedirects: z.number().int().nonnegative().optional(),
|
|
4123
|
+
timeoutMs: z.number().int().positive().optional()
|
|
4124
|
+
}).strict().optional()
|
|
4125
|
+
}).strict().optional()
|
|
4126
|
+
}).strict().optional() }).strict().optional(),
|
|
4127
|
+
nodes: z.object({
|
|
4128
|
+
browser: z.object({
|
|
4129
|
+
mode: z.union([
|
|
4130
|
+
z.literal("auto"),
|
|
4131
|
+
z.literal("manual"),
|
|
4132
|
+
z.literal("off")
|
|
4133
|
+
]).optional(),
|
|
4134
|
+
node: z.string().optional()
|
|
4135
|
+
}).strict().optional(),
|
|
4136
|
+
allowCommands: z.array(z.string()).optional(),
|
|
4137
|
+
denyCommands: z.array(z.string()).optional()
|
|
4138
|
+
}).strict().optional()
|
|
4139
|
+
}).strict().optional(),
|
|
4140
|
+
memory: MemorySchema,
|
|
4141
|
+
skills: z.object({
|
|
4142
|
+
allowBundled: z.array(z.string()).optional(),
|
|
4143
|
+
load: z.object({
|
|
4144
|
+
extraDirs: z.array(z.string()).optional(),
|
|
4145
|
+
watch: z.boolean().optional(),
|
|
4146
|
+
watchDebounceMs: z.number().int().min(0).optional()
|
|
4147
|
+
}).strict().optional(),
|
|
4148
|
+
install: z.object({
|
|
4149
|
+
preferBrew: z.boolean().optional(),
|
|
4150
|
+
nodeManager: z.union([
|
|
4151
|
+
z.literal("npm"),
|
|
4152
|
+
z.literal("pnpm"),
|
|
4153
|
+
z.literal("yarn"),
|
|
4154
|
+
z.literal("bun")
|
|
4155
|
+
]).optional()
|
|
4156
|
+
}).strict().optional(),
|
|
4157
|
+
entries: z.record(z.string(), z.object({
|
|
4158
|
+
enabled: z.boolean().optional(),
|
|
4159
|
+
apiKey: z.string().optional(),
|
|
4160
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
4161
|
+
config: z.record(z.string(), z.unknown()).optional()
|
|
4162
|
+
}).strict()).optional()
|
|
4163
|
+
}).strict().optional(),
|
|
4164
|
+
plugins: z.object({
|
|
4165
|
+
enabled: z.boolean().optional(),
|
|
4166
|
+
allow: z.array(z.string()).optional(),
|
|
4167
|
+
deny: z.array(z.string()).optional(),
|
|
4168
|
+
load: z.object({ paths: z.array(z.string()).optional() }).strict().optional(),
|
|
4169
|
+
slots: z.object({ memory: z.string().optional() }).strict().optional(),
|
|
4170
|
+
entries: z.record(z.string(), z.object({
|
|
4171
|
+
enabled: z.boolean().optional(),
|
|
4172
|
+
config: z.record(z.string(), z.unknown()).optional()
|
|
4173
|
+
}).strict()).optional(),
|
|
4174
|
+
installs: z.record(z.string(), z.object({
|
|
4175
|
+
source: z.union([
|
|
4176
|
+
z.literal("npm"),
|
|
4177
|
+
z.literal("archive"),
|
|
4178
|
+
z.literal("path")
|
|
4179
|
+
]),
|
|
4180
|
+
spec: z.string().optional(),
|
|
4181
|
+
sourcePath: z.string().optional(),
|
|
4182
|
+
installPath: z.string().optional(),
|
|
4183
|
+
version: z.string().optional(),
|
|
4184
|
+
installedAt: z.string().optional()
|
|
4185
|
+
}).strict()).optional()
|
|
4186
|
+
}).strict().optional()
|
|
4187
|
+
}).strict().superRefine((cfg, ctx) => {
|
|
4188
|
+
const agents = cfg.agents?.list ?? [];
|
|
4189
|
+
if (agents.length === 0) return;
|
|
4190
|
+
const agentIds = new Set(agents.map((agent) => agent.id));
|
|
4191
|
+
const broadcast = cfg.broadcast;
|
|
4192
|
+
if (!broadcast) return;
|
|
4193
|
+
for (const [peerId, ids] of Object.entries(broadcast)) {
|
|
4194
|
+
if (peerId === "strategy") continue;
|
|
4195
|
+
if (!Array.isArray(ids)) continue;
|
|
4196
|
+
for (let idx = 0; idx < ids.length; idx += 1) {
|
|
4197
|
+
const agentId = ids[idx];
|
|
4198
|
+
if (!agentIds.has(agentId)) ctx.addIssue({
|
|
4199
|
+
code: z.ZodIssueCode.custom,
|
|
4200
|
+
path: [
|
|
4201
|
+
"broadcast",
|
|
4202
|
+
peerId,
|
|
4203
|
+
idx
|
|
4204
|
+
],
|
|
4205
|
+
message: `Unknown agent id "${agentId}" (not in agents.list).`
|
|
4206
|
+
});
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
});
|
|
4210
|
+
|
|
4211
|
+
//#endregion
|
|
4212
|
+
//#region src/config/validation.ts
|
|
4213
|
+
const AVATAR_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
|
|
4214
|
+
const AVATAR_DATA_RE = /^data:/i;
|
|
4215
|
+
const AVATAR_HTTP_RE = /^https?:\/\//i;
|
|
4216
|
+
const WINDOWS_ABS_RE = /^[a-zA-Z]:[\\/]/;
|
|
4217
|
+
function isWorkspaceAvatarPath(value, workspaceDir) {
|
|
4218
|
+
const workspaceRoot = path.resolve(workspaceDir);
|
|
4219
|
+
const resolved = path.resolve(workspaceRoot, value);
|
|
4220
|
+
const relative = path.relative(workspaceRoot, resolved);
|
|
4221
|
+
if (relative === "") return true;
|
|
4222
|
+
if (relative.startsWith("..")) return false;
|
|
4223
|
+
return !path.isAbsolute(relative);
|
|
4224
|
+
}
|
|
4225
|
+
function validateIdentityAvatar(config) {
|
|
4226
|
+
const agents = config.agents?.list;
|
|
4227
|
+
if (!Array.isArray(agents) || agents.length === 0) return [];
|
|
4228
|
+
const issues = [];
|
|
4229
|
+
for (const [index, entry] of agents.entries()) {
|
|
4230
|
+
if (!entry || typeof entry !== "object") continue;
|
|
4231
|
+
const avatarRaw = entry.identity?.avatar;
|
|
4232
|
+
if (typeof avatarRaw !== "string") continue;
|
|
4233
|
+
const avatar = avatarRaw.trim();
|
|
4234
|
+
if (!avatar) continue;
|
|
4235
|
+
if (AVATAR_DATA_RE.test(avatar) || AVATAR_HTTP_RE.test(avatar)) continue;
|
|
4236
|
+
if (avatar.startsWith("~")) {
|
|
4237
|
+
issues.push({
|
|
4238
|
+
path: `agents.list.${index}.identity.avatar`,
|
|
4239
|
+
message: "identity.avatar must be a workspace-relative path, http(s) URL, or data URI."
|
|
4240
|
+
});
|
|
4241
|
+
continue;
|
|
4242
|
+
}
|
|
4243
|
+
if (AVATAR_SCHEME_RE.test(avatar) && !WINDOWS_ABS_RE.test(avatar)) {
|
|
4244
|
+
issues.push({
|
|
4245
|
+
path: `agents.list.${index}.identity.avatar`,
|
|
4246
|
+
message: "identity.avatar must be a workspace-relative path, http(s) URL, or data URI."
|
|
4247
|
+
});
|
|
4248
|
+
continue;
|
|
4249
|
+
}
|
|
4250
|
+
if (!isWorkspaceAvatarPath(avatar, resolveAgentWorkspaceDir(config, entry.id ?? resolveDefaultAgentId(config)))) issues.push({
|
|
4251
|
+
path: `agents.list.${index}.identity.avatar`,
|
|
4252
|
+
message: "identity.avatar must stay within the agent workspace."
|
|
4253
|
+
});
|
|
4254
|
+
}
|
|
4255
|
+
return issues;
|
|
4256
|
+
}
|
|
4257
|
+
function validateConfigObject(raw) {
|
|
4258
|
+
const legacyIssues = findLegacyConfigIssues(raw);
|
|
4259
|
+
if (legacyIssues.length > 0) return {
|
|
4260
|
+
ok: false,
|
|
4261
|
+
issues: legacyIssues.map((iss) => ({
|
|
4262
|
+
path: iss.path,
|
|
4263
|
+
message: iss.message
|
|
4264
|
+
}))
|
|
4265
|
+
};
|
|
4266
|
+
const validated = OpenClawSchema.safeParse(raw);
|
|
4267
|
+
if (!validated.success) return {
|
|
4268
|
+
ok: false,
|
|
4269
|
+
issues: validated.error.issues.map((iss) => ({
|
|
4270
|
+
path: iss.path.join("."),
|
|
4271
|
+
message: iss.message
|
|
4272
|
+
}))
|
|
4273
|
+
};
|
|
4274
|
+
const duplicates = findDuplicateAgentDirs(validated.data);
|
|
4275
|
+
if (duplicates.length > 0) return {
|
|
4276
|
+
ok: false,
|
|
4277
|
+
issues: [{
|
|
4278
|
+
path: "agents.list",
|
|
4279
|
+
message: formatDuplicateAgentDirError(duplicates)
|
|
4280
|
+
}]
|
|
4281
|
+
};
|
|
4282
|
+
const avatarIssues = validateIdentityAvatar(validated.data);
|
|
4283
|
+
if (avatarIssues.length > 0) return {
|
|
4284
|
+
ok: false,
|
|
4285
|
+
issues: avatarIssues
|
|
4286
|
+
};
|
|
4287
|
+
return {
|
|
4288
|
+
ok: true,
|
|
4289
|
+
config: applyModelDefaults(applyAgentDefaults(applySessionDefaults(validated.data)))
|
|
4290
|
+
};
|
|
4291
|
+
}
|
|
4292
|
+
function isRecord(value) {
|
|
4293
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
4294
|
+
}
|
|
4295
|
+
function validateConfigObjectWithPlugins(raw) {
|
|
4296
|
+
const base = validateConfigObject(raw);
|
|
4297
|
+
if (!base.ok) return {
|
|
4298
|
+
ok: false,
|
|
4299
|
+
issues: base.issues,
|
|
4300
|
+
warnings: []
|
|
4301
|
+
};
|
|
4302
|
+
const config = base.config;
|
|
4303
|
+
const issues = [];
|
|
4304
|
+
const warnings = [];
|
|
4305
|
+
const pluginsConfig = config.plugins;
|
|
4306
|
+
const normalizedPlugins = normalizePluginsConfig(pluginsConfig);
|
|
4307
|
+
const registry = loadPluginManifestRegistry({
|
|
4308
|
+
config,
|
|
4309
|
+
workspaceDir: resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config)) ?? void 0
|
|
4310
|
+
});
|
|
4311
|
+
const knownIds = new Set(registry.plugins.map((record) => record.id));
|
|
4312
|
+
for (const diag of registry.diagnostics) {
|
|
4313
|
+
let path = diag.pluginId ? `plugins.entries.${diag.pluginId}` : "plugins";
|
|
4314
|
+
if (!diag.pluginId && diag.message.includes("plugin path not found")) path = "plugins.load.paths";
|
|
4315
|
+
const message = `${diag.pluginId ? `plugin ${diag.pluginId}` : "plugin"}: ${diag.message}`;
|
|
4316
|
+
if (diag.level === "error") issues.push({
|
|
4317
|
+
path,
|
|
4318
|
+
message
|
|
4319
|
+
});
|
|
4320
|
+
else warnings.push({
|
|
4321
|
+
path,
|
|
4322
|
+
message
|
|
4323
|
+
});
|
|
4324
|
+
}
|
|
4325
|
+
const entries = pluginsConfig?.entries;
|
|
4326
|
+
if (entries && isRecord(entries)) {
|
|
4327
|
+
for (const pluginId of Object.keys(entries)) if (!knownIds.has(pluginId)) issues.push({
|
|
4328
|
+
path: `plugins.entries.${pluginId}`,
|
|
4329
|
+
message: `plugin not found: ${pluginId}`
|
|
4330
|
+
});
|
|
4331
|
+
}
|
|
4332
|
+
const allow = pluginsConfig?.allow ?? [];
|
|
4333
|
+
for (const pluginId of allow) {
|
|
4334
|
+
if (typeof pluginId !== "string" || !pluginId.trim()) continue;
|
|
4335
|
+
if (!knownIds.has(pluginId)) issues.push({
|
|
4336
|
+
path: "plugins.allow",
|
|
4337
|
+
message: `plugin not found: ${pluginId}`
|
|
4338
|
+
});
|
|
4339
|
+
}
|
|
4340
|
+
const deny = pluginsConfig?.deny ?? [];
|
|
4341
|
+
for (const pluginId of deny) {
|
|
4342
|
+
if (typeof pluginId !== "string" || !pluginId.trim()) continue;
|
|
4343
|
+
if (!knownIds.has(pluginId)) issues.push({
|
|
4344
|
+
path: "plugins.deny",
|
|
4345
|
+
message: `plugin not found: ${pluginId}`
|
|
4346
|
+
});
|
|
4347
|
+
}
|
|
4348
|
+
const memorySlot = normalizedPlugins.slots.memory;
|
|
4349
|
+
if (typeof memorySlot === "string" && memorySlot.trim() && !knownIds.has(memorySlot)) issues.push({
|
|
4350
|
+
path: "plugins.slots.memory",
|
|
4351
|
+
message: `plugin not found: ${memorySlot}`
|
|
4352
|
+
});
|
|
4353
|
+
const allowedChannels = new Set(["defaults", ...CHANNEL_IDS]);
|
|
4354
|
+
for (const record of registry.plugins) for (const channelId of record.channels) allowedChannels.add(channelId);
|
|
4355
|
+
if (config.channels && isRecord(config.channels)) for (const key of Object.keys(config.channels)) {
|
|
4356
|
+
const trimmed = key.trim();
|
|
4357
|
+
if (!trimmed) continue;
|
|
4358
|
+
if (!allowedChannels.has(trimmed)) issues.push({
|
|
4359
|
+
path: `channels.${trimmed}`,
|
|
4360
|
+
message: `unknown channel id: ${trimmed}`
|
|
4361
|
+
});
|
|
4362
|
+
}
|
|
4363
|
+
const heartbeatChannelIds = /* @__PURE__ */ new Set();
|
|
4364
|
+
for (const channelId of CHANNEL_IDS) heartbeatChannelIds.add(channelId.toLowerCase());
|
|
4365
|
+
for (const record of registry.plugins) for (const channelId of record.channels) {
|
|
4366
|
+
const trimmed = channelId.trim();
|
|
4367
|
+
if (trimmed) heartbeatChannelIds.add(trimmed.toLowerCase());
|
|
4368
|
+
}
|
|
4369
|
+
const validateHeartbeatTarget = (target, path) => {
|
|
4370
|
+
if (typeof target !== "string") return;
|
|
4371
|
+
const trimmed = target.trim();
|
|
4372
|
+
if (!trimmed) {
|
|
4373
|
+
issues.push({
|
|
4374
|
+
path,
|
|
4375
|
+
message: "heartbeat target must not be empty"
|
|
4376
|
+
});
|
|
4377
|
+
return;
|
|
4378
|
+
}
|
|
4379
|
+
const normalized = trimmed.toLowerCase();
|
|
4380
|
+
if (normalized === "last" || normalized === "none") return;
|
|
4381
|
+
if (normalizeChatChannelId(trimmed)) return;
|
|
4382
|
+
if (heartbeatChannelIds.has(normalized)) return;
|
|
4383
|
+
issues.push({
|
|
4384
|
+
path,
|
|
4385
|
+
message: `unknown heartbeat target: ${target}`
|
|
4386
|
+
});
|
|
4387
|
+
};
|
|
4388
|
+
validateHeartbeatTarget(config.agents?.defaults?.heartbeat?.target, "agents.defaults.heartbeat.target");
|
|
4389
|
+
if (Array.isArray(config.agents?.list)) for (const [index, entry] of config.agents.list.entries()) validateHeartbeatTarget(entry?.heartbeat?.target, `agents.list.${index}.heartbeat.target`);
|
|
4390
|
+
let selectedMemoryPluginId = null;
|
|
4391
|
+
const seenPlugins = /* @__PURE__ */ new Set();
|
|
4392
|
+
for (const record of registry.plugins) {
|
|
4393
|
+
const pluginId = record.id;
|
|
4394
|
+
if (seenPlugins.has(pluginId)) continue;
|
|
4395
|
+
seenPlugins.add(pluginId);
|
|
4396
|
+
const entry = normalizedPlugins.entries[pluginId];
|
|
4397
|
+
const entryHasConfig = Boolean(entry?.config);
|
|
4398
|
+
const enableState = resolveEnableState(pluginId, record.origin, normalizedPlugins);
|
|
4399
|
+
let enabled = enableState.enabled;
|
|
4400
|
+
let reason = enableState.reason;
|
|
4401
|
+
if (enabled) {
|
|
4402
|
+
const memoryDecision = resolveMemorySlotDecision({
|
|
4403
|
+
id: pluginId,
|
|
4404
|
+
kind: record.kind,
|
|
4405
|
+
slot: memorySlot,
|
|
4406
|
+
selectedId: selectedMemoryPluginId
|
|
4407
|
+
});
|
|
4408
|
+
if (!memoryDecision.enabled) {
|
|
4409
|
+
enabled = false;
|
|
4410
|
+
reason = memoryDecision.reason;
|
|
4411
|
+
}
|
|
4412
|
+
if (memoryDecision.selected && record.kind === "memory") selectedMemoryPluginId = pluginId;
|
|
4413
|
+
}
|
|
4414
|
+
if (enabled || entryHasConfig) if (record.configSchema) {
|
|
4415
|
+
const res = validateJsonSchemaValue({
|
|
4416
|
+
schema: record.configSchema,
|
|
4417
|
+
cacheKey: record.schemaCacheKey ?? record.manifestPath ?? pluginId,
|
|
4418
|
+
value: entry?.config ?? {}
|
|
4419
|
+
});
|
|
4420
|
+
if (!res.ok) for (const error of res.errors) issues.push({
|
|
4421
|
+
path: `plugins.entries.${pluginId}.config`,
|
|
4422
|
+
message: `invalid config: ${error}`
|
|
4423
|
+
});
|
|
4424
|
+
} else issues.push({
|
|
4425
|
+
path: `plugins.entries.${pluginId}`,
|
|
4426
|
+
message: `plugin schema missing for ${pluginId}`
|
|
4427
|
+
});
|
|
4428
|
+
if (!enabled && entryHasConfig) warnings.push({
|
|
4429
|
+
path: `plugins.entries.${pluginId}`,
|
|
4430
|
+
message: `plugin disabled (${reason ?? "disabled"}) but config is present`
|
|
4431
|
+
});
|
|
4432
|
+
}
|
|
4433
|
+
if (issues.length > 0) return {
|
|
4434
|
+
ok: false,
|
|
4435
|
+
issues,
|
|
4436
|
+
warnings
|
|
4437
|
+
};
|
|
4438
|
+
return {
|
|
4439
|
+
ok: true,
|
|
4440
|
+
config,
|
|
4441
|
+
warnings
|
|
4442
|
+
};
|
|
4443
|
+
}
|
|
4444
|
+
|
|
4445
|
+
//#endregion
|
|
4446
|
+
//#region src/config/version.ts
|
|
4447
|
+
const VERSION_RE = /^v?(\d+)\.(\d+)\.(\d+)(?:-(\d+))?/;
|
|
4448
|
+
function parseOpenClawVersion(raw) {
|
|
4449
|
+
if (!raw) return null;
|
|
4450
|
+
const match = raw.trim().match(VERSION_RE);
|
|
4451
|
+
if (!match) return null;
|
|
4452
|
+
const [, major, minor, patch, revision] = match;
|
|
4453
|
+
return {
|
|
4454
|
+
major: Number.parseInt(major, 10),
|
|
4455
|
+
minor: Number.parseInt(minor, 10),
|
|
4456
|
+
patch: Number.parseInt(patch, 10),
|
|
4457
|
+
revision: revision ? Number.parseInt(revision, 10) : 0
|
|
4458
|
+
};
|
|
4459
|
+
}
|
|
4460
|
+
function compareOpenClawVersions(a, b) {
|
|
4461
|
+
const parsedA = parseOpenClawVersion(a);
|
|
4462
|
+
const parsedB = parseOpenClawVersion(b);
|
|
4463
|
+
if (!parsedA || !parsedB) return null;
|
|
4464
|
+
if (parsedA.major !== parsedB.major) return parsedA.major < parsedB.major ? -1 : 1;
|
|
4465
|
+
if (parsedA.minor !== parsedB.minor) return parsedA.minor < parsedB.minor ? -1 : 1;
|
|
4466
|
+
if (parsedA.patch !== parsedB.patch) return parsedA.patch < parsedB.patch ? -1 : 1;
|
|
4467
|
+
if (parsedA.revision !== parsedB.revision) return parsedA.revision < parsedB.revision ? -1 : 1;
|
|
4468
|
+
return 0;
|
|
4469
|
+
}
|
|
4470
|
+
|
|
4471
|
+
//#endregion
|
|
4472
|
+
//#region src/config/io.ts
|
|
4473
|
+
const SHELL_ENV_EXPECTED_KEYS = [
|
|
4474
|
+
"OPENAI_API_KEY",
|
|
4475
|
+
"ANTHROPIC_API_KEY",
|
|
4476
|
+
"ANTHROPIC_OAUTH_TOKEN",
|
|
4477
|
+
"GEMINI_API_KEY",
|
|
4478
|
+
"ZAI_API_KEY",
|
|
4479
|
+
"OPENROUTER_API_KEY",
|
|
4480
|
+
"AI_GATEWAY_API_KEY",
|
|
4481
|
+
"MINIMAX_API_KEY",
|
|
4482
|
+
"SYNTHETIC_API_KEY",
|
|
4483
|
+
"ELEVENLABS_API_KEY",
|
|
4484
|
+
"TELEGRAM_BOT_TOKEN",
|
|
4485
|
+
"DISCORD_BOT_TOKEN",
|
|
4486
|
+
"SLACK_BOT_TOKEN",
|
|
4487
|
+
"SLACK_APP_TOKEN",
|
|
4488
|
+
"CRYPTOCLAW_GATEWAY_TOKEN",
|
|
4489
|
+
"CRYPTOCLAW_GATEWAY_PASSWORD"
|
|
4490
|
+
];
|
|
4491
|
+
const CONFIG_BACKUP_COUNT = 5;
|
|
4492
|
+
const loggedInvalidConfigs = /* @__PURE__ */ new Set();
|
|
4493
|
+
function hashConfigRaw(raw) {
|
|
4494
|
+
return crypto.createHash("sha256").update(raw ?? "").digest("hex");
|
|
4495
|
+
}
|
|
4496
|
+
function resolveConfigSnapshotHash(snapshot) {
|
|
4497
|
+
if (typeof snapshot.hash === "string") {
|
|
4498
|
+
const trimmed = snapshot.hash.trim();
|
|
4499
|
+
if (trimmed) return trimmed;
|
|
4500
|
+
}
|
|
4501
|
+
if (typeof snapshot.raw !== "string") return null;
|
|
4502
|
+
return hashConfigRaw(snapshot.raw);
|
|
4503
|
+
}
|
|
4504
|
+
function coerceConfig(value) {
|
|
4505
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
4506
|
+
return value;
|
|
4507
|
+
}
|
|
4508
|
+
async function rotateConfigBackups(configPath, ioFs) {
|
|
4509
|
+
if (CONFIG_BACKUP_COUNT <= 1) return;
|
|
4510
|
+
const backupBase = `${configPath}.bak`;
|
|
4511
|
+
const maxIndex = CONFIG_BACKUP_COUNT - 1;
|
|
4512
|
+
await ioFs.unlink(`${backupBase}.${maxIndex}`).catch(() => {});
|
|
4513
|
+
for (let index = maxIndex - 1; index >= 1; index -= 1) await ioFs.rename(`${backupBase}.${index}`, `${backupBase}.${index + 1}`).catch(() => {});
|
|
4514
|
+
await ioFs.rename(backupBase, `${backupBase}.1`).catch(() => {});
|
|
4515
|
+
}
|
|
4516
|
+
function warnOnConfigMiskeys(raw, logger) {
|
|
4517
|
+
if (!raw || typeof raw !== "object") return;
|
|
4518
|
+
const gateway = raw.gateway;
|
|
4519
|
+
if (!gateway || typeof gateway !== "object") return;
|
|
4520
|
+
if ("token" in gateway) logger.warn("Config uses \"gateway.token\". This key is ignored; use \"gateway.auth.token\" instead.");
|
|
4521
|
+
}
|
|
4522
|
+
function stampConfigVersion(cfg) {
|
|
4523
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4524
|
+
return {
|
|
4525
|
+
...cfg,
|
|
4526
|
+
meta: {
|
|
4527
|
+
...cfg.meta,
|
|
4528
|
+
lastTouchedVersion: VERSION,
|
|
4529
|
+
lastTouchedAt: now
|
|
4530
|
+
}
|
|
4531
|
+
};
|
|
4532
|
+
}
|
|
4533
|
+
function warnIfConfigFromFuture(cfg, logger) {
|
|
4534
|
+
const touched = cfg.meta?.lastTouchedVersion;
|
|
4535
|
+
if (!touched) return;
|
|
4536
|
+
const cmp = compareOpenClawVersions(VERSION, touched);
|
|
4537
|
+
if (cmp === null) return;
|
|
4538
|
+
if (cmp < 0) logger.warn(`Config was last written by a newer CryptoClaw (${touched}); current version is ${VERSION}.`);
|
|
4539
|
+
}
|
|
4540
|
+
function applyConfigEnv(cfg, env) {
|
|
4541
|
+
const entries = collectConfigEnvVars(cfg);
|
|
4542
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
4543
|
+
if (env[key]?.trim()) continue;
|
|
4544
|
+
env[key] = value;
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
function resolveConfigPathForDeps(deps) {
|
|
4548
|
+
if (deps.configPath) return deps.configPath;
|
|
4549
|
+
return resolveConfigPath(deps.env, resolveStateDir(deps.env, deps.homedir));
|
|
4550
|
+
}
|
|
4551
|
+
function normalizeDeps(overrides = {}) {
|
|
4552
|
+
return {
|
|
4553
|
+
fs: overrides.fs ?? fs,
|
|
4554
|
+
json5: overrides.json5 ?? JSON5,
|
|
4555
|
+
env: overrides.env ?? process.env,
|
|
4556
|
+
homedir: overrides.homedir ?? os.homedir,
|
|
4557
|
+
configPath: overrides.configPath ?? "",
|
|
4558
|
+
logger: overrides.logger ?? console
|
|
4559
|
+
};
|
|
4560
|
+
}
|
|
4561
|
+
function parseConfigJson5(raw, json5 = JSON5) {
|
|
4562
|
+
try {
|
|
4563
|
+
return {
|
|
4564
|
+
ok: true,
|
|
4565
|
+
parsed: json5.parse(raw)
|
|
4566
|
+
};
|
|
4567
|
+
} catch (err) {
|
|
4568
|
+
return {
|
|
4569
|
+
ok: false,
|
|
4570
|
+
error: String(err)
|
|
4571
|
+
};
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4574
|
+
function createConfigIO(overrides = {}) {
|
|
4575
|
+
const deps = normalizeDeps(overrides);
|
|
4576
|
+
const requestedConfigPath = resolveConfigPathForDeps(deps);
|
|
4577
|
+
const configPath = (deps.configPath ? [requestedConfigPath] : resolveDefaultConfigCandidates(deps.env, deps.homedir)).find((candidate) => deps.fs.existsSync(candidate)) ?? requestedConfigPath;
|
|
4578
|
+
function loadConfig() {
|
|
4579
|
+
try {
|
|
4580
|
+
if (!deps.fs.existsSync(configPath)) {
|
|
4581
|
+
if (shouldEnableShellEnvFallback(deps.env) && !shouldDeferShellEnvFallback(deps.env)) loadShellEnvFallback({
|
|
4582
|
+
enabled: true,
|
|
4583
|
+
env: deps.env,
|
|
4584
|
+
expectedKeys: SHELL_ENV_EXPECTED_KEYS,
|
|
4585
|
+
logger: deps.logger,
|
|
4586
|
+
timeoutMs: resolveShellEnvFallbackTimeoutMs(deps.env)
|
|
4587
|
+
});
|
|
4588
|
+
return {};
|
|
4589
|
+
}
|
|
4590
|
+
const raw = deps.fs.readFileSync(configPath, "utf-8");
|
|
4591
|
+
const resolved = resolveConfigIncludes(deps.json5.parse(raw), configPath, {
|
|
4592
|
+
readFile: (p) => deps.fs.readFileSync(p, "utf-8"),
|
|
4593
|
+
parseJson: (raw) => deps.json5.parse(raw)
|
|
4594
|
+
});
|
|
4595
|
+
if (resolved && typeof resolved === "object" && "env" in resolved) applyConfigEnv(resolved, deps.env);
|
|
4596
|
+
const resolvedConfig = resolveConfigEnvVars(resolved, deps.env);
|
|
4597
|
+
warnOnConfigMiskeys(resolvedConfig, deps.logger);
|
|
4598
|
+
if (typeof resolvedConfig !== "object" || resolvedConfig === null) return {};
|
|
4599
|
+
const preValidationDuplicates = findDuplicateAgentDirs(resolvedConfig, {
|
|
4600
|
+
env: deps.env,
|
|
4601
|
+
homedir: deps.homedir
|
|
4602
|
+
});
|
|
4603
|
+
if (preValidationDuplicates.length > 0) throw new DuplicateAgentDirError(preValidationDuplicates);
|
|
4604
|
+
const validated = validateConfigObjectWithPlugins(resolvedConfig);
|
|
4605
|
+
if (!validated.ok) {
|
|
4606
|
+
const details = validated.issues.map((iss) => `- ${iss.path || "<root>"}: ${iss.message}`).join("\n");
|
|
4607
|
+
if (!loggedInvalidConfigs.has(configPath)) {
|
|
4608
|
+
loggedInvalidConfigs.add(configPath);
|
|
4609
|
+
deps.logger.error(`Invalid config at ${configPath}:\\n${details}`);
|
|
4610
|
+
}
|
|
4611
|
+
const error = /* @__PURE__ */ new Error("Invalid config");
|
|
4612
|
+
error.code = "INVALID_CONFIG";
|
|
4613
|
+
error.details = details;
|
|
4614
|
+
throw error;
|
|
4615
|
+
}
|
|
4616
|
+
if (validated.warnings.length > 0) {
|
|
4617
|
+
const details = validated.warnings.map((iss) => `- ${iss.path || "<root>"}: ${iss.message}`).join("\n");
|
|
4618
|
+
deps.logger.warn(`Config warnings:\\n${details}`);
|
|
4619
|
+
}
|
|
4620
|
+
warnIfConfigFromFuture(validated.config, deps.logger);
|
|
4621
|
+
const cfg = applyModelDefaults(applyCompactionDefaults(applyContextPruningDefaults(applyAgentDefaults(applySessionDefaults(applyLoggingDefaults(applyMessageDefaults(validated.config)))))));
|
|
4622
|
+
normalizeConfigPaths(cfg);
|
|
4623
|
+
const duplicates = findDuplicateAgentDirs(cfg, {
|
|
4624
|
+
env: deps.env,
|
|
4625
|
+
homedir: deps.homedir
|
|
4626
|
+
});
|
|
4627
|
+
if (duplicates.length > 0) throw new DuplicateAgentDirError(duplicates);
|
|
4628
|
+
applyConfigEnv(cfg, deps.env);
|
|
4629
|
+
if ((shouldEnableShellEnvFallback(deps.env) || cfg.env?.shellEnv?.enabled === true) && !shouldDeferShellEnvFallback(deps.env)) loadShellEnvFallback({
|
|
4630
|
+
enabled: true,
|
|
4631
|
+
env: deps.env,
|
|
4632
|
+
expectedKeys: SHELL_ENV_EXPECTED_KEYS,
|
|
4633
|
+
logger: deps.logger,
|
|
4634
|
+
timeoutMs: cfg.env?.shellEnv?.timeoutMs ?? resolveShellEnvFallbackTimeoutMs(deps.env)
|
|
4635
|
+
});
|
|
4636
|
+
return applyConfigOverrides(cfg);
|
|
4637
|
+
} catch (err) {
|
|
4638
|
+
if (err instanceof DuplicateAgentDirError) {
|
|
4639
|
+
deps.logger.error(err.message);
|
|
4640
|
+
throw err;
|
|
4641
|
+
}
|
|
4642
|
+
if (err?.code === "INVALID_CONFIG") return {};
|
|
4643
|
+
deps.logger.error(`Failed to read config at ${configPath}`, err);
|
|
4644
|
+
return {};
|
|
4645
|
+
}
|
|
4646
|
+
}
|
|
4647
|
+
async function readConfigFileSnapshot() {
|
|
4648
|
+
if (!deps.fs.existsSync(configPath)) {
|
|
4649
|
+
const hash = hashConfigRaw(null);
|
|
4650
|
+
return {
|
|
4651
|
+
path: configPath,
|
|
4652
|
+
exists: false,
|
|
4653
|
+
raw: null,
|
|
4654
|
+
parsed: {},
|
|
4655
|
+
valid: true,
|
|
4656
|
+
config: applyTalkApiKey(applyModelDefaults(applyCompactionDefaults(applyContextPruningDefaults(applyAgentDefaults(applySessionDefaults(applyMessageDefaults({}))))))),
|
|
4657
|
+
hash,
|
|
4658
|
+
issues: [],
|
|
4659
|
+
warnings: [],
|
|
4660
|
+
legacyIssues: []
|
|
4661
|
+
};
|
|
4662
|
+
}
|
|
4663
|
+
try {
|
|
4664
|
+
const raw = deps.fs.readFileSync(configPath, "utf-8");
|
|
4665
|
+
const hash = hashConfigRaw(raw);
|
|
4666
|
+
const parsedRes = parseConfigJson5(raw, deps.json5);
|
|
4667
|
+
if (!parsedRes.ok) return {
|
|
4668
|
+
path: configPath,
|
|
4669
|
+
exists: true,
|
|
4670
|
+
raw,
|
|
4671
|
+
parsed: {},
|
|
4672
|
+
valid: false,
|
|
4673
|
+
config: {},
|
|
4674
|
+
hash,
|
|
4675
|
+
issues: [{
|
|
4676
|
+
path: "",
|
|
4677
|
+
message: `JSON5 parse failed: ${parsedRes.error}`
|
|
4678
|
+
}],
|
|
4679
|
+
warnings: [],
|
|
4680
|
+
legacyIssues: []
|
|
4681
|
+
};
|
|
4682
|
+
let resolved;
|
|
4683
|
+
try {
|
|
4684
|
+
resolved = resolveConfigIncludes(parsedRes.parsed, configPath, {
|
|
4685
|
+
readFile: (p) => deps.fs.readFileSync(p, "utf-8"),
|
|
4686
|
+
parseJson: (raw) => deps.json5.parse(raw)
|
|
4687
|
+
});
|
|
4688
|
+
} catch (err) {
|
|
4689
|
+
const message = err instanceof ConfigIncludeError ? err.message : `Include resolution failed: ${String(err)}`;
|
|
4690
|
+
return {
|
|
4691
|
+
path: configPath,
|
|
4692
|
+
exists: true,
|
|
4693
|
+
raw,
|
|
4694
|
+
parsed: parsedRes.parsed,
|
|
4695
|
+
valid: false,
|
|
4696
|
+
config: coerceConfig(parsedRes.parsed),
|
|
4697
|
+
hash,
|
|
4698
|
+
issues: [{
|
|
4699
|
+
path: "",
|
|
4700
|
+
message
|
|
4701
|
+
}],
|
|
4702
|
+
warnings: [],
|
|
4703
|
+
legacyIssues: []
|
|
4704
|
+
};
|
|
4705
|
+
}
|
|
4706
|
+
if (resolved && typeof resolved === "object" && "env" in resolved) applyConfigEnv(resolved, deps.env);
|
|
4707
|
+
let substituted;
|
|
4708
|
+
try {
|
|
4709
|
+
substituted = resolveConfigEnvVars(resolved, deps.env);
|
|
4710
|
+
} catch (err) {
|
|
4711
|
+
const message = err instanceof MissingEnvVarError ? err.message : `Env var substitution failed: ${String(err)}`;
|
|
4712
|
+
return {
|
|
4713
|
+
path: configPath,
|
|
4714
|
+
exists: true,
|
|
4715
|
+
raw,
|
|
4716
|
+
parsed: parsedRes.parsed,
|
|
4717
|
+
valid: false,
|
|
4718
|
+
config: coerceConfig(resolved),
|
|
4719
|
+
hash,
|
|
4720
|
+
issues: [{
|
|
4721
|
+
path: "",
|
|
4722
|
+
message
|
|
4723
|
+
}],
|
|
4724
|
+
warnings: [],
|
|
4725
|
+
legacyIssues: []
|
|
4726
|
+
};
|
|
4727
|
+
}
|
|
4728
|
+
const resolvedConfigRaw = substituted;
|
|
4729
|
+
const legacyIssues = findLegacyConfigIssues(resolvedConfigRaw);
|
|
4730
|
+
const validated = validateConfigObjectWithPlugins(resolvedConfigRaw);
|
|
4731
|
+
if (!validated.ok) return {
|
|
4732
|
+
path: configPath,
|
|
4733
|
+
exists: true,
|
|
4734
|
+
raw,
|
|
4735
|
+
parsed: parsedRes.parsed,
|
|
4736
|
+
valid: false,
|
|
4737
|
+
config: coerceConfig(resolvedConfigRaw),
|
|
4738
|
+
hash,
|
|
4739
|
+
issues: validated.issues,
|
|
4740
|
+
warnings: validated.warnings,
|
|
4741
|
+
legacyIssues
|
|
4742
|
+
};
|
|
4743
|
+
warnIfConfigFromFuture(validated.config, deps.logger);
|
|
4744
|
+
return {
|
|
4745
|
+
path: configPath,
|
|
4746
|
+
exists: true,
|
|
4747
|
+
raw,
|
|
4748
|
+
parsed: parsedRes.parsed,
|
|
4749
|
+
valid: true,
|
|
4750
|
+
config: normalizeConfigPaths(applyTalkApiKey(applyModelDefaults(applyAgentDefaults(applySessionDefaults(applyLoggingDefaults(applyMessageDefaults(validated.config))))))),
|
|
4751
|
+
hash,
|
|
4752
|
+
issues: [],
|
|
4753
|
+
warnings: validated.warnings,
|
|
4754
|
+
legacyIssues
|
|
4755
|
+
};
|
|
4756
|
+
} catch (err) {
|
|
4757
|
+
return {
|
|
4758
|
+
path: configPath,
|
|
4759
|
+
exists: true,
|
|
4760
|
+
raw: null,
|
|
4761
|
+
parsed: {},
|
|
4762
|
+
valid: false,
|
|
4763
|
+
config: {},
|
|
4764
|
+
hash: hashConfigRaw(null),
|
|
4765
|
+
issues: [{
|
|
4766
|
+
path: "",
|
|
4767
|
+
message: `read failed: ${String(err)}`
|
|
4768
|
+
}],
|
|
4769
|
+
warnings: [],
|
|
4770
|
+
legacyIssues: []
|
|
4771
|
+
};
|
|
4772
|
+
}
|
|
4773
|
+
}
|
|
4774
|
+
async function writeConfigFile(cfg) {
|
|
4775
|
+
clearConfigCache();
|
|
4776
|
+
const validated = validateConfigObjectWithPlugins(cfg);
|
|
4777
|
+
if (!validated.ok) {
|
|
4778
|
+
const issue = validated.issues[0];
|
|
4779
|
+
const pathLabel = issue?.path ? issue.path : "<root>";
|
|
4780
|
+
throw new Error(`Config validation failed: ${pathLabel}: ${issue?.message ?? "invalid"}`);
|
|
4781
|
+
}
|
|
4782
|
+
if (validated.warnings.length > 0) {
|
|
4783
|
+
const details = validated.warnings.map((warning) => `- ${warning.path}: ${warning.message}`).join("\n");
|
|
4784
|
+
deps.logger.warn(`Config warnings:\n${details}`);
|
|
4785
|
+
}
|
|
4786
|
+
const dir = path.dirname(configPath);
|
|
4787
|
+
await deps.fs.promises.mkdir(dir, {
|
|
4788
|
+
recursive: true,
|
|
4789
|
+
mode: 448
|
|
4790
|
+
});
|
|
4791
|
+
const json = JSON.stringify(applyModelDefaults(stampConfigVersion(cfg)), null, 2).trimEnd().concat("\n");
|
|
4792
|
+
const tmp = path.join(dir, `${path.basename(configPath)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
4793
|
+
await deps.fs.promises.writeFile(tmp, json, {
|
|
4794
|
+
encoding: "utf-8",
|
|
4795
|
+
mode: 384
|
|
4796
|
+
});
|
|
4797
|
+
if (deps.fs.existsSync(configPath)) {
|
|
4798
|
+
await rotateConfigBackups(configPath, deps.fs.promises);
|
|
4799
|
+
await deps.fs.promises.copyFile(configPath, `${configPath}.bak`).catch(() => {});
|
|
4800
|
+
}
|
|
4801
|
+
try {
|
|
4802
|
+
await deps.fs.promises.rename(tmp, configPath);
|
|
4803
|
+
} catch (err) {
|
|
4804
|
+
const code = err.code;
|
|
4805
|
+
if (code === "EPERM" || code === "EEXIST") {
|
|
4806
|
+
await deps.fs.promises.copyFile(tmp, configPath);
|
|
4807
|
+
await deps.fs.promises.chmod(configPath, 384).catch(() => {});
|
|
4808
|
+
await deps.fs.promises.unlink(tmp).catch(() => {});
|
|
4809
|
+
return;
|
|
4810
|
+
}
|
|
4811
|
+
await deps.fs.promises.unlink(tmp).catch(() => {});
|
|
4812
|
+
throw err;
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4815
|
+
return {
|
|
4816
|
+
configPath,
|
|
4817
|
+
loadConfig,
|
|
4818
|
+
readConfigFileSnapshot,
|
|
4819
|
+
writeConfigFile
|
|
4820
|
+
};
|
|
4821
|
+
}
|
|
4822
|
+
const DEFAULT_CONFIG_CACHE_MS = 200;
|
|
4823
|
+
let configCache = null;
|
|
4824
|
+
function resolveConfigCacheMs(env) {
|
|
4825
|
+
const raw = env.CRYPTOCLAW_CONFIG_CACHE_MS?.trim();
|
|
4826
|
+
if (raw === "" || raw === "0") return 0;
|
|
4827
|
+
if (!raw) return DEFAULT_CONFIG_CACHE_MS;
|
|
4828
|
+
const parsed = Number.parseInt(raw, 10);
|
|
4829
|
+
if (!Number.isFinite(parsed)) return DEFAULT_CONFIG_CACHE_MS;
|
|
4830
|
+
return Math.max(0, parsed);
|
|
4831
|
+
}
|
|
4832
|
+
function shouldUseConfigCache(env) {
|
|
4833
|
+
if (env.CRYPTOCLAW_DISABLE_CONFIG_CACHE?.trim()) return false;
|
|
4834
|
+
return resolveConfigCacheMs(env) > 0;
|
|
4835
|
+
}
|
|
4836
|
+
function clearConfigCache() {
|
|
4837
|
+
configCache = null;
|
|
4838
|
+
}
|
|
4839
|
+
function loadConfig() {
|
|
4840
|
+
const io = createConfigIO();
|
|
4841
|
+
const configPath = io.configPath;
|
|
4842
|
+
const now = Date.now();
|
|
4843
|
+
if (shouldUseConfigCache(process.env)) {
|
|
4844
|
+
const cached = configCache;
|
|
4845
|
+
if (cached && cached.configPath === configPath && cached.expiresAt > now) return cached.config;
|
|
4846
|
+
}
|
|
4847
|
+
const config = io.loadConfig();
|
|
4848
|
+
if (shouldUseConfigCache(process.env)) {
|
|
4849
|
+
const cacheMs = resolveConfigCacheMs(process.env);
|
|
4850
|
+
if (cacheMs > 0) configCache = {
|
|
4851
|
+
configPath,
|
|
4852
|
+
expiresAt: now + cacheMs,
|
|
4853
|
+
config
|
|
4854
|
+
};
|
|
4855
|
+
}
|
|
4856
|
+
return config;
|
|
4857
|
+
}
|
|
4858
|
+
async function readConfigFileSnapshot() {
|
|
4859
|
+
return await createConfigIO().readConfigFileSnapshot();
|
|
4860
|
+
}
|
|
4861
|
+
async function writeConfigFile(cfg) {
|
|
4862
|
+
clearConfigCache();
|
|
4863
|
+
await createConfigIO().writeConfigFile(cfg);
|
|
4864
|
+
}
|
|
4865
|
+
|
|
4866
|
+
//#endregion
|
|
4867
|
+
//#region src/config/legacy-migrate.ts
|
|
4868
|
+
function migrateLegacyConfig(raw) {
|
|
4869
|
+
const { next, changes } = applyLegacyMigrations(raw);
|
|
4870
|
+
if (!next) return {
|
|
4871
|
+
config: null,
|
|
4872
|
+
changes: []
|
|
4873
|
+
};
|
|
4874
|
+
const validated = validateConfigObjectWithPlugins(next);
|
|
4875
|
+
if (!validated.ok) {
|
|
4876
|
+
changes.push("Migration applied, but config still invalid; fix remaining issues manually.");
|
|
4877
|
+
return {
|
|
4878
|
+
config: null,
|
|
4879
|
+
changes
|
|
4880
|
+
};
|
|
4881
|
+
}
|
|
4882
|
+
return {
|
|
4883
|
+
config: validated.config,
|
|
4884
|
+
changes
|
|
4885
|
+
};
|
|
4886
|
+
}
|
|
4887
|
+
|
|
4888
|
+
//#endregion
|
|
4889
|
+
//#region src/config/config.ts
|
|
4890
|
+
var config_exports = /* @__PURE__ */ __exportAll({
|
|
4891
|
+
CONFIG_PATH: () => CONFIG_PATH,
|
|
4892
|
+
DEFAULT_GATEWAY_PORT: () => DEFAULT_GATEWAY_PORT,
|
|
4893
|
+
STATE_DIR: () => STATE_DIR,
|
|
4894
|
+
applyConfigOverrides: () => applyConfigOverrides,
|
|
4895
|
+
getConfigOverrides: () => getConfigOverrides,
|
|
4896
|
+
isNixMode: () => isNixMode,
|
|
4897
|
+
loadConfig: () => loadConfig,
|
|
4898
|
+
resetConfigOverrides: () => resetConfigOverrides,
|
|
4899
|
+
resolveCanonicalConfigPath: () => resolveCanonicalConfigPath,
|
|
4900
|
+
resolveConfigPath: () => resolveConfigPath,
|
|
4901
|
+
resolveConfigPathCandidate: () => resolveConfigPathCandidate,
|
|
4902
|
+
resolveDefaultConfigCandidates: () => resolveDefaultConfigCandidates,
|
|
4903
|
+
resolveGatewayLockDir: () => resolveGatewayLockDir,
|
|
4904
|
+
resolveGatewayPort: () => resolveGatewayPort,
|
|
4905
|
+
resolveIsNixMode: () => resolveIsNixMode,
|
|
4906
|
+
resolveLegacyStateDirs: () => resolveLegacyStateDirs,
|
|
4907
|
+
resolveNewStateDir: () => resolveNewStateDir,
|
|
4908
|
+
resolveOAuthDir: () => resolveOAuthDir,
|
|
4909
|
+
resolveOAuthPath: () => resolveOAuthPath,
|
|
4910
|
+
resolveStateDir: () => resolveStateDir,
|
|
4911
|
+
setConfigOverride: () => setConfigOverride,
|
|
4912
|
+
unsetConfigOverride: () => unsetConfigOverride
|
|
4913
|
+
});
|
|
4914
|
+
|
|
4915
|
+
//#endregion
|
|
4916
|
+
export { resolveSubagentMaxConcurrent as A, setConfigValueAtPath as C, MAX_INCLUDE_DEPTH as D, INCLUDE_KEY as E, collectConfigEnvVars as O, parseConfigPath as S, applyLegacyMigrations as T, getConfigOverrides as _, parseConfigJson5 as a, unsetConfigOverride as b, writeConfigFile as c, TELEGRAM_COMMAND_NAME_PATTERN as d, normalizeTelegramCommandName as f, validateJsonSchemaValue as g, parseDurationMs as h, loadConfig as i, VERSION as j, resolveAgentMaxConcurrent as k, validateConfigObjectWithPlugins as l, isSafeExecutableValue as m, migrateLegacyConfig as n, readConfigFileSnapshot as o, resolveTelegramCustomCommands as p, createConfigIO as r, resolveConfigSnapshotHash as s, config_exports as t, OpenClawSchema as u, resetConfigOverrides as v, unsetConfigValueAtPath as w, getConfigValueAtPath as x, setConfigOverride as y };
|