silentlake 2026.3.24
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 +4587 -0
- package/LICENSE +21 -0
- package/README.md +248 -0
- package/assets/avatar-placeholder.svg +19 -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/dmg-background-small.png +0 -0
- package/assets/dmg-background.png +0 -0
- package/assets/silentlake-banner.png +0 -0
- package/dist/APEv2Parser-BZv_dP9t.js +269 -0
- package/dist/APEv2Parser-CPzxFNBB.js +5 -0
- package/dist/AbstractID3Parser-mvDFcjYV.js +47 -0
- package/dist/AiffParser-BXQ9SRZk.js +145 -0
- package/dist/AsfParser-CmBDUlZE.js +631 -0
- package/dist/BasicParser-DhmXREDo.js +853 -0
- package/dist/DsdiffParser-CTKKGyZg.js +150 -0
- package/dist/DsfParser-Ds-YQe4Z.js +101 -0
- package/dist/FlacParser-B1XVPgXF.js +5 -0
- package/dist/FlacParser-DMPyL1y4.js +367 -0
- package/dist/ID3v1Parser-BICWWVDG.js +289 -0
- package/dist/ID3v2Parser-BmZHSUqs.js +650 -0
- package/dist/ID3v2Token-DeJf4tYQ.js +145 -0
- package/dist/MP4Parser-Cjf-Zs8T.js +1061 -0
- package/dist/MatroskaParser-DgBzBe8t.js +909 -0
- package/dist/MpegParser-D5swTpA1.js +744 -0
- package/dist/MusepackParser-D8EQXnpK.js +285 -0
- package/dist/OggParser-BcIYPHwP.js +390 -0
- package/dist/Util-D_zGsr97.js +170 -0
- package/dist/WavPackParser-hosU8gfo.js +166 -0
- package/dist/WaveParser-CA00FZrC.js +273 -0
- package/dist/abort-cutoff-CERmtgZI.js +56 -0
- package/dist/abort-cutoff.runtime-DZkGKKzv.js +61 -0
- package/dist/abort-signal-CsrBEr94.js +13 -0
- package/dist/account-helpers-D3c_eI7c.js +37 -0
- package/dist/account-helpers-ru3jdZSV.js +12 -0
- package/dist/account-id-DZnNZg8x.js +1 -0
- package/dist/account-id-ZCrgXl7Z.js +44 -0
- package/dist/account-lookup-nkoa-foB.js +10 -0
- package/dist/account-snapshot-fields-Cvq7803C.js +116 -0
- package/dist/account-summary-B5Xzvntm.js +36 -0
- package/dist/accounts-43SvCDEA.js +212 -0
- package/dist/accounts-CYgFhv2o.js +105 -0
- package/dist/accounts-ChlyF7cx.js +112 -0
- package/dist/ack-reactions-CNVwfOBj.js +43 -0
- package/dist/acp-cli-B4Rv7-xU.js +2033 -0
- package/dist/acp-runtime-BdLdT-QY.js +1 -0
- package/dist/actions.runtime-CqnQssoB.js +217 -0
- package/dist/actions.runtime-FLmCvVRd.js +236 -0
- package/dist/agent-CBOdzEvR.js +1 -0
- package/dist/agent-scope-BLhzf-o0.js +17 -0
- package/dist/agent-scope-DPP4Z_UU.js +193 -0
- package/dist/agents-D8pBK0II.js +855 -0
- package/dist/agents-Dihz1Ihx.js +323 -0
- package/dist/agents.config-C_lrnc9J.js +18 -0
- package/dist/agents.config-D1VqC78r.js +121 -0
- package/dist/allow-from-BPSBITdd.js +9 -0
- package/dist/allow-from-BwTLpvhp.js +20 -0
- package/dist/allow-from-C4iBpqFI.js +62 -0
- package/dist/allowlist-config-edit-CKbnMmwS.js +279 -0
- package/dist/allowlist-match-CYmPgg1K.js +63 -0
- package/dist/ansi-BEJF8NKS.js +54 -0
- package/dist/anthropic-vertex-provider-Dd5agCN9.js +60 -0
- package/dist/apply.runtime-DhKxNSJE.js +370 -0
- package/dist/apply.runtime-ghlh-P6X.js +211 -0
- package/dist/archive-Tr0wIUO-.js +532 -0
- package/dist/arg-split-Dtda0YDl.js +38 -0
- package/dist/artifacts-C_4LekPC.js +39 -0
- package/dist/audit-BOPSQQtd.js +54 -0
- package/dist/audit-C5kdrCi_.js +788 -0
- package/dist/audit-channel.allow-from.runtime-B7BHNblL.js +17 -0
- package/dist/audit-channel.collect.runtime-CjAbXFBV.js +521 -0
- package/dist/audit-channel.discord.runtime-BBY6S9lg.js +5 -0
- package/dist/audit-channel.telegram.runtime-SJnxOJH2.js +8 -0
- package/dist/audit-channel.zalouser.runtime-SGRWvHxT.js +5 -0
- package/dist/audit-extra.async-DY8v7LXH.js +817 -0
- package/dist/audit-fs-oDMUa5N_.js +375 -0
- package/dist/audit-membership-runtime-BPjFryEx.js +261 -0
- package/dist/audit.deep.runtime-WFf-TpsD.js +31 -0
- package/dist/audit.nondeep.runtime-B4BaEaRU.js +842 -0
- package/dist/audit.runtime-rFjCrods.js +74 -0
- package/dist/auth-O6LQFLHJ.js +416 -0
- package/dist/auth-choice-DIBaxmAQ.js +219 -0
- package/dist/auth-choice-PrbpIjyg.js +610 -0
- package/dist/auth-choice-legacy-Clyw2lVc.js +17 -0
- package/dist/auth-choice-options-ohUw8QR-.js +127 -0
- package/dist/auth-choice-prompt-Cm0s-9Du.js +215 -0
- package/dist/auth-choice-prompt-DI-Xl1Nv.js +36 -0
- package/dist/auth-choice-rKBOd02a.js +64 -0
- package/dist/auth-choice.apply-helpers-BibBSEl9.js +66 -0
- package/dist/auth-choice.plugin-providers.runtime-gqF9NO7_.js +219 -0
- package/dist/auth-health-TWboMYA5.js +166 -0
- package/dist/auth-mode-policy-DywddkT-.js +18 -0
- package/dist/auth-profiles-CWEIQV77.js +1047 -0
- package/dist/auth-profiles.runtime-B98lwopF.js +48 -0
- package/dist/avatar-policy-Ds9e6uHI.js +67 -0
- package/dist/axios-xDDnM0KG.js +12831 -0
- package/dist/backup-create-B6JAR6jJ.js +461 -0
- package/dist/banner-bez5CpOK.js +351 -0
- package/dist/base-session-key-Cf2rkwag.js +14 -0
- package/dist/bindings-BV4AtNSY.js +21 -0
- package/dist/bindings-kjwuC11Q.js +69 -0
- package/dist/bluebubbles-C1M3Geg0.js +87 -0
- package/dist/bluebubbles-DRW3JdOY.js +603 -0
- package/dist/bluebubbles-dEl4QpYz.js +37 -0
- package/dist/bonjour-discovery-C2oY96BG.js +376 -0
- package/dist/boolean-DKtCJu_W.js +29 -0
- package/dist/boolean-param-xAGXUSSN.js +13 -0
- package/dist/boundary-file-read-BP6VMpqH.js +106 -0
- package/dist/boundary-path-B3FFLYNx.js +557 -0
- package/dist/brave-CkimJe4j.js +405 -0
- package/dist/brew-DSwWqzLd.js +44 -0
- package/dist/browser-cli-D3kBUBNc.js +1502 -0
- package/dist/bundled/boot-md/handler.js +381 -0
- package/dist/bundled/bootstrap-extra-files/handler.js +56 -0
- package/dist/bundled/command-logger/handler.js +62 -0
- package/dist/bundled/session-memory/handler.js +401 -0
- package/dist/call-BDvaXe4i.js +44 -0
- package/dist/call-BmLt3xO1.js +639 -0
- package/dist/catalog-BwAYUfL7.js +240 -0
- package/dist/channel-BBCuV5OT.js +4945 -0
- package/dist/channel-BDBXuqeg.js +321 -0
- package/dist/channel-C8h1Irxm.js +1284 -0
- package/dist/channel-CbGpFzo4.js +1602 -0
- package/dist/channel-Ci3K8fI9.js +1006 -0
- package/dist/channel-DGT5N1v7.js +1077 -0
- package/dist/channel-account-context-Bwa-YH_o.js +104 -0
- package/dist/channel-actions-DU2CR3xW.js +37 -0
- package/dist/channel-activity-B8aReQoE.js +35 -0
- package/dist/channel-config-3Uv6ve2_.js +115 -0
- package/dist/channel-config-helpers-CieQWILI.js +377 -0
- package/dist/channel-config-schema-DEVsCZpj.js +1 -0
- package/dist/channel-feedback-G6zh8efr.js +245 -0
- package/dist/channel-inbound-DwzVf2PK.js +395 -0
- package/dist/channel-lifecycle-CpU1dRbh.js +354 -0
- package/dist/channel-options-DJaIP4Dv.js +38 -0
- package/dist/channel-pairing-D54mn51y.js +66 -0
- package/dist/channel-plugin-common-BhTxCE5t.js +1 -0
- package/dist/channel-plugin-ids-CFeS3qir.js +26 -0
- package/dist/channel-plugin-resolution-DUngfdFj.js +112 -0
- package/dist/channel-policy-C4GKHvhz.js +1 -0
- package/dist/channel-reply-pipeline-CPTuaW8n.js +15 -0
- package/dist/channel-send-result-By8EpCPw.js +40 -0
- package/dist/channel-setup-Ck35g7zI.js +49 -0
- package/dist/channel-shared-LkXtTPXk.js +308 -0
- package/dist/channel-summary-CdYLGMVt.js +137 -0
- package/dist/channel-summary-D33z52ft.js +41 -0
- package/dist/channel-targets-DfnKGXez.js +87 -0
- package/dist/channel.runtime-DAyBR2A5.js +324 -0
- package/dist/channel.runtime-DVq5tC2D.js +35 -0
- package/dist/channel.runtime-DaLTDGtF.js +288 -0
- package/dist/channel.runtime-FKfTev2g.js +512 -0
- package/dist/channel.runtime-owqedh1t.js +268 -0
- package/dist/channel.runtime-wOTeiifp.js +230 -0
- package/dist/channels-CIHgkPea.js +408 -0
- package/dist/channels-ZXK6Jiuk.js +1393 -0
- package/dist/channels-cli-Bj6qSlkE.js +412 -0
- package/dist/channels-status-issues-C_U44M8Y.js +16 -0
- package/dist/chat-type-C-n03mQY.js +10 -0
- package/dist/clack-prompter-DuzDnaLi.js +112 -0
- package/dist/clawbot-cli-YNPuwmTB.js +218 -0
- package/dist/cli/daemon-cli.js +88 -0
- package/dist/cli-CW46WAZn.js +254 -0
- package/dist/cli-name-Daok7A7-.js +25 -0
- package/dist/cli-runtime-aAVwbEYy.js +7 -0
- package/dist/cli-utils-Np4NAAtt.js +39 -0
- package/dist/command-format-CYK9XiUC.js +16 -0
- package/dist/command-format-g8YUHNir.js +2 -0
- package/dist/command-gating-BQXGSqc9.js +40 -0
- package/dist/command-options-5coRiipK.js +25 -0
- package/dist/command-poll-backoff-CpkSns-6.js +56 -0
- package/dist/command-poll-backoff.runtime-YT6EGcLN.js +7 -0
- package/dist/command-registry-BI2MOs89.js +242 -0
- package/dist/command-registry-BMsxnuoC.js +14 -0
- package/dist/command-secret-gateway-ChXyZwos.js +211 -0
- package/dist/command-secret-targets-COcwhn-D.js +88 -0
- package/dist/command-secret-targets-CQJT3viO.js +3 -0
- package/dist/commands-Bb9xUwz9.js +42 -0
- package/dist/commands-core-C1usZXC2.js +4923 -0
- package/dist/commands-core.runtime-OTZivlO2.js +232 -0
- package/dist/commands-registry-ChCep1KJ.js +295 -0
- package/dist/commands-registry.data-XyUTELK9.js +904 -0
- package/dist/commands-registry.runtime-m5WTxFtv.js +25 -0
- package/dist/commands-status.runtime-C8_hpNgj.js +211 -0
- package/dist/commands.runtime-BZPnQKcW.js +232 -0
- package/dist/common-CUBlLRXB.js +457 -0
- package/dist/compact.runtime-Djmzpbn6.js +216 -0
- package/dist/completion-cli-D8tLgE5W.js +445 -0
- package/dist/completion-cli-Dz89naVA.js +17 -0
- package/dist/config-6sZwvXJD.js +88 -0
- package/dist/config-B7tPwoHZ.js +38 -0
- package/dist/config-DdDLrP_v.js +273 -0
- package/dist/config-cli-C10R8azD.js +945 -0
- package/dist/config-guard-B1c73BYQ.js +126 -0
- package/dist/config-helpers-3u5wfLBu.js +117 -0
- package/dist/config-pn7LKJdW.js +23 -0
- package/dist/config-presence-BmUF_5K9.js +79 -0
- package/dist/config-regex-CvZFnWkO.js +39 -0
- package/dist/config-runtime-CstET7fq.js +142 -0
- package/dist/config-schema-5YkIW1xw.js +270 -0
- package/dist/config-schema-B1UGMwZ8.js +31 -0
- package/dist/config-schema-DzlnsY3D.js +33 -0
- package/dist/config-state-CE0CGjey.js +288 -0
- package/dist/config-validation-CkVqgkHr.js +272 -0
- package/dist/config-value-DgJrpclm.js +25 -0
- package/dist/configure-DMkp7Sr4.js +1126 -0
- package/dist/configure-DOrQthLy.js +344 -0
- package/dist/connection-auth-BSQJeDOU.js +30 -0
- package/dist/constants-C_Scc680.js +71 -0
- package/dist/control-ui-assets-DjqeIg6A.js +232 -0
- package/dist/control-ui-shared-DP000Pxd.js +29 -0
- package/dist/conversation-runtime-1O0Aaolb.js +1458 -0
- package/dist/core-C7aHA4Aq.js +187 -0
- package/dist/core-command-descriptors-DCUYAEZd.js +96 -0
- package/dist/credentials-BPwBlm1X.js +265 -0
- package/dist/cron-cli-N2Hw_02d.js +579 -0
- package/dist/daemon-cli-DgfaF9xx.js +354 -0
- package/dist/daemon-install-CbclJo5M.js +134 -0
- package/dist/daemon-install-plan.shared-DK6BHlWI.js +222 -0
- package/dist/daemon-runtime-CbClrCwc.js +12 -0
- package/dist/dangerous-config-flags-BJtLWIk7.js +15 -0
- package/dist/dangerous-name-matching-DZa_t0RM.js +44 -0
- package/dist/dangerous-tools-yGPDFTHh.js +27 -0
- package/dist/date-time-DCAyaBop.js +118 -0
- package/dist/dedupe-Cgnk5BbX.js +55 -0
- package/dist/defaults-CEdZhIIb.js +6 -0
- package/dist/delegate-D4ql5N70.js +43 -0
- package/dist/deliver-B004w1Mv.js +212 -0
- package/dist/deliver-runtime-IYvc0giI.js +211 -0
- package/dist/delivery-queue-B19wDCjT.js +3 -0
- package/dist/delivery-queue-DrrqB4Hi.js +299 -0
- package/dist/device-auth-GEXe9vqR.js +15 -0
- package/dist/device-bootstrap-CwwokLEY.js +96 -0
- package/dist/device-bootstrap-Dbhe6oe8.js +1 -0
- package/dist/device-metadata-normalization-BDSQ_eA7.js +21 -0
- package/dist/device-pairing-cFWbBray.js +553 -0
- package/dist/devices-cli-DzycjFzS.js +366 -0
- package/dist/diagnostic-DqJXx_4Q.js +310 -0
- package/dist/diagnostic-events-ktCoG8Br.js +48 -0
- package/dist/diagnostics-CMhyGsPu.js +33 -0
- package/dist/diagnostics-DpLHpQ9c.js +14 -0
- package/dist/directive-handling.fast-lane-toP_ri_H.js +273 -0
- package/dist/directive-handling.impl-BRARyrsT.js +638 -0
- package/dist/directive-handling.impl-CUB4MOnK.js +214 -0
- package/dist/directive-handling.levels-8vnMeuGX.js +2 -0
- package/dist/directive-handling.levels-CoruY1AA.js +13 -0
- package/dist/directive-handling.persist.runtime-78Du6PgL.js +170 -0
- package/dist/directive-handling.shared-DCGUCHjn.js +147 -0
- package/dist/directory-cli-DVsDcgIU.js +437 -0
- package/dist/directory-config-helpers-CURJ8mj7.js +129 -0
- package/dist/directory-runtime-DhC8QkMq.js +19 -0
- package/dist/directory.static-DQaG9ohH.js +44 -0
- package/dist/discord-CYj8s73O.js +214 -0
- package/dist/discord-L9zvSHVn.js +635 -0
- package/dist/discord-core-5tkl-BzP.js +1 -0
- package/dist/dm-policy-shared-6bCJzHOS.js +188 -0
- package/dist/dns-cli-BMvHy265.js +223 -0
- package/dist/docker-XFNiArwM.js +1254 -0
- package/dist/docs-cli-BTaH94wD.js +176 -0
- package/dist/doctor-completion-DKx5m2UC.js +90 -0
- package/dist/doctor-config-preflight-BzQgc3_t.js +40 -0
- package/dist/doctor-config-preflight-DxVCut8L.js +150 -0
- package/dist/doctor-state-migrations-CTF66iAy.js +732 -0
- package/dist/doctor-state-migrations-D0VP4dUh.js +212 -0
- package/dist/entry-status-B2OWAf0s.js +172 -0
- package/dist/entry.js +210 -0
- package/dist/env-BP70DGuy.js +30 -0
- package/dist/env-overrides-JneV60sd.js +434 -0
- package/dist/env-overrides.runtime-DLrwions.js +18 -0
- package/dist/env-substitution-D6t_sLS_.js +136 -0
- package/dist/errors-BxyFnvP3.js +58 -0
- package/dist/exec-Dmex2w_d.js +310 -0
- package/dist/exec-approvals-BJhuySBz.js +386 -0
- package/dist/exec-approvals-allowlist-B_wPddCb.js +384 -0
- package/dist/exec-approvals-cli-C2dwhSkX.js +427 -0
- package/dist/exec-safe-bin-runtime-policy-BZkObC8r.js +89 -0
- package/dist/exec-safety-CaaBy-Zw.js +24 -0
- package/dist/extension-shared-5txN7IXK.js +74 -0
- package/dist/extensionAPI.js +218 -0
- package/dist/extensions/amazon-bedrock/index.js +231 -0
- package/dist/extensions/anthropic/index.js +330 -0
- package/dist/extensions/bluebubbles/index.js +224 -0
- package/dist/extensions/bluebubbles/setup-entry.js +289 -0
- package/dist/extensions/brave/index.js +23 -0
- package/dist/extensions/byteplus/index.js +112 -0
- package/dist/extensions/chutes/index.js +221 -0
- package/dist/extensions/cloudflare-ai-gateway/index.js +218 -0
- package/dist/extensions/copilot-proxy/index.js +125 -0
- package/dist/extensions/device-pair/index.js +1040 -0
- package/dist/extensions/discord/index.js +215 -0
- package/dist/extensions/discord/setup-entry.js +215 -0
- package/dist/extensions/elevenlabs/index.js +223 -0
- package/dist/extensions/fal/index.js +112 -0
- package/dist/extensions/feishu/index.js +227 -0
- package/dist/extensions/feishu/setup-entry.js +112 -0
- package/dist/extensions/firecrawl/index.js +211 -0
- package/dist/extensions/github-copilot/index.js +490 -0
- package/dist/extensions/google/index.js +211 -0
- package/dist/extensions/huggingface/index.js +108 -0
- package/dist/extensions/imessage/index.js +223 -0
- package/dist/extensions/imessage/setup-entry.js +220 -0
- package/dist/extensions/irc/index.js +220 -0
- package/dist/extensions/irc/setup-entry.js +222 -0
- package/dist/extensions/kilocode/index.js +282 -0
- package/dist/extensions/kimi-coding/index.js +148 -0
- package/dist/extensions/line/index.js +57 -0
- package/dist/extensions/line/setup-entry.js +49 -0
- package/dist/extensions/llm-task/index.js +157 -0
- package/dist/extensions/lobster/index.js +261 -0
- package/dist/extensions/mattermost/index.js +220 -0
- package/dist/extensions/mattermost/setup-entry.js +222 -0
- package/dist/extensions/memory-core/index.js +36 -0
- package/dist/extensions/microsoft/index.js +223 -0
- package/dist/extensions/minimax/index.js +437 -0
- package/dist/extensions/mistral/index.js +149 -0
- package/dist/extensions/modelstudio/index.js +144 -0
- package/dist/extensions/moonshot/index.js +211 -0
- package/dist/extensions/nextcloud-talk/index.js +221 -0
- package/dist/extensions/nextcloud-talk/setup-entry.js +223 -0
- package/dist/extensions/nvidia/index.js +29 -0
- package/dist/extensions/ollama/index.js +118 -0
- package/dist/extensions/open-prose/index.js +10 -0
- package/dist/extensions/openai/index.js +677 -0
- package/dist/extensions/opencode/index.js +116 -0
- package/dist/extensions/opencode-go/index.js +114 -0
- package/dist/extensions/openrouter/index.js +398 -0
- package/dist/extensions/openshell/index.js +923 -0
- package/dist/extensions/perplexity/index.js +23 -0
- package/dist/extensions/phone-control/index.js +276 -0
- package/dist/extensions/qianfan/index.js +114 -0
- package/dist/extensions/qwen-portal-auth/index.js +350 -0
- package/dist/extensions/sglang/index.js +285 -0
- package/dist/extensions/signal/index.js +218 -0
- package/dist/extensions/signal/setup-entry.js +218 -0
- package/dist/extensions/slack/index.js +222 -0
- package/dist/extensions/slack/setup-entry.js +220 -0
- package/dist/extensions/synology-chat/index.js +56 -0
- package/dist/extensions/synology-chat/setup-entry.js +58 -0
- package/dist/extensions/synthetic/index.js +112 -0
- package/dist/extensions/talk-voice/index.js +197 -0
- package/dist/extensions/tavily/index.js +211 -0
- package/dist/extensions/telegram/index.js +221 -0
- package/dist/extensions/telegram/setup-entry.js +221 -0
- package/dist/extensions/thread-ownership/index.js +70 -0
- package/dist/extensions/together/index.js +113 -0
- package/dist/extensions/venice/index.js +132 -0
- package/dist/extensions/vercel-ai-gateway/index.js +86 -0
- package/dist/extensions/vllm/index.js +285 -0
- package/dist/extensions/voice-call/index.js +5715 -0
- package/dist/extensions/volcengine/index.js +112 -0
- package/dist/extensions/xai/index.js +211 -0
- package/dist/extensions/xiaomi/index.js +115 -0
- package/dist/extensions/zai/index.js +559 -0
- package/dist/extensions/zalo/index.js +225 -0
- package/dist/extensions/zalo/setup-entry.js +226 -0
- package/dist/external-content-BUdUOqkv.js +238 -0
- package/dist/feishu-CgbwAF0e.js +2664 -0
- package/dist/feishu-Dh5fEbh5.js +59127 -0
- package/dist/fetch-guard-DIyN1HW5.js +165 -0
- package/dist/fetch-timeout-C5xpMuGd.js +36 -0
- package/dist/file-identity-Cw0fQxYY.js +11 -0
- package/dist/file-lock-WbEmczmY.js +107 -0
- package/dist/filter-oMGaNOM1.js +20 -0
- package/dist/format-DH8ysi7s.js +19 -0
- package/dist/format-datetime-BGS6tLDE.js +73 -0
- package/dist/format-duration-CO0BGWB0.js +57 -0
- package/dist/format-relative-C3nDxnXz.js +54 -0
- package/dist/frontmatter-S5vS-I4a.js +309 -0
- package/dist/fs-safe-D3qzH-ab.js +731 -0
- package/dist/gateway-cli-PQNp7o0j.js +28378 -0
- package/dist/gateway-install-token-DV5KjD4F.js +164 -0
- package/dist/gateway-rpc-C0Ey-rik.js +26 -0
- package/dist/gateway-runtime-ih2e7a2K.js +42 -0
- package/dist/gaxios-fetch-compat-KX6bsqFm.js +165 -0
- package/dist/gemini-auth-B5ljg7jr.js +29 -0
- package/dist/git-commit-BIdLubm5.js +2 -0
- package/dist/git-commit-OvUvjri2.js +177 -0
- package/dist/github-copilot-auth-Ccm-cBwy.js +104 -0
- package/dist/global-singleton-4KwY5RvX.js +13 -0
- package/dist/globals-41sdSaKv.js +38 -0
- package/dist/gmail-setup-utils-BX68dZla.js +419 -0
- package/dist/group-access-DJZrYPx1.js +113 -0
- package/dist/group-keys-BD_IYSMs.js +44 -0
- package/dist/group-policy-CWFxv3iB.js +201 -0
- package/dist/group-policy-warnings-Ddu6lBkh.js +175 -0
- package/dist/health-Bu1sbyYy.js +573 -0
- package/dist/health-Cbxc9Bn3.js +59 -0
- package/dist/health-format-B5XfOTuJ.js +26 -0
- package/dist/heartbeat-7aHh0m3d.js +169 -0
- package/dist/heartbeat-summary-Das49TYq.js +57 -0
- package/dist/help-CcbF7-ha.js +81 -0
- package/dist/help-format-Dv45FpYu.js +15 -0
- package/dist/helpers-DJ-5HEbE.js +24 -0
- package/dist/helpers-MxyaLZUk.js +32 -0
- package/dist/history-CHjo8B5W.js +102 -0
- package/dist/hook-runtime-BnNBi_q4.js +1 -0
- package/dist/hooks-cli-DUYK4RM1.js +1102 -0
- package/dist/hooks-policy-BL6HDLUn.js +20 -0
- package/dist/hooks-status-gzNmo3li.js +78 -0
- package/dist/host-env-security-BogNN146.js +223 -0
- package/dist/http-body-CCiSfloA.js +237 -0
- package/dist/http-registry-WFFbLYRd.js +153 -0
- package/dist/identity-DovQV4zD.js +112 -0
- package/dist/identity-cyBYcoXS.js +84 -0
- package/dist/identity-file-EndG1nfc.js +60 -0
- package/dist/image-generation-CNKc-mFK.js +441 -0
- package/dist/image-kJ7Tbov4.js +211 -0
- package/dist/image-ops-j01UkxEv.js +371 -0
- package/dist/imessage-B5pSMT47.js +219 -0
- package/dist/imessage-CoIuY1Ro.js +1451 -0
- package/dist/imessage-Cqjsq4VW.js +190 -0
- package/dist/imessage-core-CsYJuaRZ.js +1 -0
- package/dist/inbound-envelope-4P3IIJc3.js +61 -0
- package/dist/inbound-reply-dispatch-i2Vekqyy.js +72 -0
- package/dist/includes-7XyL3p1c.js +188 -0
- package/dist/includes-scan-y-rS6tTw.js +55 -0
- package/dist/index.js +57 -0
- package/dist/infra/warning-filter.js +2 -0
- package/dist/inspect-CcxlJ1ba.js +279 -0
- package/dist/install-safe-path-Rwbw1XCZ.js +62 -0
- package/dist/installs-iHi2aSjM.js +532 -0
- package/dist/interactive-F7iY0yED.js +8 -0
- package/dist/interactive-runtime-OweOj_Vv.js +90 -0
- package/dist/internal-hooks-0uipqzRY.js +156 -0
- package/dist/io-BX49DsSJ.js +35 -0
- package/dist/io-jOnQRia2.js +7178 -0
- package/dist/ip-C8vmzVu0.js +203 -0
- package/dist/ipv4-DAmsJVOV.js +82 -0
- package/dist/irc-AZ-Ec8be.js +12 -0
- package/dist/irc-CCSRuEC2.js +660 -0
- package/dist/is-main-YViS6wOn.js +27 -0
- package/dist/issue-format-CBEXVico.js +31 -0
- package/dist/issue-format-D3HehoKZ.js +4 -0
- package/dist/json-file-C2zjA0Gv.js +23 -0
- package/dist/json-files-WW-H_psG.js +60 -0
- package/dist/json-pointer-f9dEnBoR.js +43 -0
- package/dist/json-store-O1LwpnBH.js +37 -0
- package/dist/kb-cli-DMZs6PCu.js +65 -0
- package/dist/keyed-async-queue-CPUWV5Pm.js +32 -0
- package/dist/kill-tree-CbjXBw3z.js +149 -0
- package/dist/kilocode-shared-DS7_0IMs.js +29 -0
- package/dist/launchd-tyqGVx9U.js +491 -0
- package/dist/lazy-runtime-BcXbyAaC.js +1 -0
- package/dist/lazy-runtime-bWkd2cs3.js +29 -0
- package/dist/legacy-names-CUNZ4vHN.js +7 -0
- package/dist/legacy-web-search-BgZjNG2h.js +222 -0
- package/dist/lib-CERS7N4b.js +503 -0
- package/dist/lib-PPICrHv1.js +1938 -0
- package/dist/library-CZ461krl.js +211 -0
- package/dist/lifecycle-core-Dnxnw0oy.js +382 -0
- package/dist/line/accounts.js +10 -0
- package/dist/line/send.js +39 -0
- package/dist/line/template-messages.js +2 -0
- package/dist/line-CJSvwApm.js +1 -0
- package/dist/line-N9vL-2JB.js +688 -0
- package/dist/line-core-BOIxkjgu.js +1 -0
- package/dist/links-Bilm-v0z.js +13 -0
- package/dist/llm-slug-generator-BuAuQ5Ft.js +68 -0
- package/dist/llm-slug-generator.js +212 -0
- package/dist/llm-task-Dx8ymRFr.js +1 -0
- package/dist/local-roots-DAzCjWbC.js +34 -0
- package/dist/location-DefAH9WS.js +42 -0
- package/dist/logger-CoEtkjhn.js +550 -0
- package/dist/logger-Cqy7-Maj.js +70 -0
- package/dist/logging-B2wMcpWV.js +13 -0
- package/dist/logging-Bz1qZDPg.js +16 -0
- package/dist/logging-CArEWRgI.js +36 -0
- package/dist/logging-CbTTfADU.js +1 -0
- package/dist/logs-cli-BSjKwaur.js +261 -0
- package/dist/magic-string.es-DJPWMt-n.js +1011 -0
- package/dist/main-session-DKr0lBVk.js +36 -0
- package/dist/manager-ChTGDe87.js +2005 -0
- package/dist/manager-DuwFn87U.js +4226 -0
- package/dist/manager-runtime-E16jsvRe.js +59 -0
- package/dist/manager.runtime-F9F1eFiB.js +827 -0
- package/dist/manifest-registry-B90TyTWl.js +1350 -0
- package/dist/map-size-CMTQVKUV.js +15 -0
- package/dist/markdown-to-line-BWwaRx5F.js +640 -0
- package/dist/mask-api-key-CprzEe7l.js +10 -0
- package/dist/matrix-DzvdUw97.js +228 -0
- package/dist/matrix-migration-snapshot-adoDbNii.js +702 -0
- package/dist/mattermost-DO0BCfF3.js +1 -0
- package/dist/mattermost-SjOt4QDb.js +15 -0
- package/dist/mcp-cli-BC_VPl_o.js +94 -0
- package/dist/mcp-config-Coky4zS4.js +108 -0
- package/dist/media-limits-Cuvmmhop.js +14 -0
- package/dist/media-understanding-DD2uMjK8.js +48 -0
- package/dist/media-understanding.runtime-Kbb2bRmk.js +216 -0
- package/dist/memory-DBjQ0TPd.js +1 -0
- package/dist/memory-cli-Cm4Df0hJ.js +215 -0
- package/dist/memory-cli-yzqneSF8.js +541 -0
- package/dist/memory-search-Das1tiuB.js +204 -0
- package/dist/memory-search-DxmSTjHq.js +18 -0
- package/dist/mention-gating-B_q-EHFx.js +25 -0
- package/dist/mentions-Bxys_va0.js +154 -0
- package/dist/message-channel-Cy-gN4K2.js +106 -0
- package/dist/message-hook-mappers-BBTV3JRQ.js +249 -0
- package/dist/method-scopes-DgypDW23.js +2649 -0
- package/dist/mime-C4vVTBso.js +150 -0
- package/dist/minimal-C5yUxtHy.js +2120 -0
- package/dist/model-auth-B__TJTPw.js +309 -0
- package/dist/model-auth-env-CF9ts7Th.js +111 -0
- package/dist/model-catalog.runtime-CWh17vcc.js +211 -0
- package/dist/model-id-normalization-Y-MIsyK_.js +16 -0
- package/dist/model-input-BB2wSAHb.js +20 -0
- package/dist/model-overrides-sIzKU2wo.js +84 -0
- package/dist/model-param-b-DIFEhICm.js +15 -0
- package/dist/model-picker-CAPjetT3.js +400 -0
- package/dist/model-picker-DEw9viWc.js +215 -0
- package/dist/model-picker.runtime-ixYl7lB5.js +224 -0
- package/dist/model-selection-BTpJnslv.js +437 -0
- package/dist/model-selection-Ci9cPkL2.js +765 -0
- package/dist/model-suppression.runtime-D8cIb6Y5.js +216 -0
- package/dist/models-BQtc3khN.js +226 -0
- package/dist/models-CQgBV5dW.js +2536 -0
- package/dist/models-cli-DbQ-QpQk.js +418 -0
- package/dist/models-config-D2xK-G6c.js +211 -0
- package/dist/models-config.providers.discovery-BaIk1NKL.js +141 -0
- package/dist/models-config.runtime-Cf7q9uAQ.js +211 -0
- package/dist/monitor-B5QmKaD7.js +3272 -0
- package/dist/monitor-CL5OYLih.js +878 -0
- package/dist/monitor-CNZxrM4d.js +3145 -0
- package/dist/monitor-CyQVZdDh.js +223 -0
- package/dist/multimodal-DC43jYNv.js +75 -0
- package/dist/mutable-allowlist-detectors-C6EAzWYE.js +62 -0
- package/dist/net-DlJFp95v.js +270 -0
- package/dist/network-mode-DOgvmom4.js +17 -0
- package/dist/nextcloud-talk-ChMP88s-.js +12 -0
- package/dist/nextcloud-talk-CwnkUy8E.js +1 -0
- package/dist/node-cli-BZDC7rXg.js +2484 -0
- package/dist/node-command-policy-Bg2g6Xjp.js +192 -0
- package/dist/node-commands-B6W6Eo0b.js +11 -0
- package/dist/node-require-BgDD9bTi.js +14 -0
- package/dist/node-resolve-BunMro3f.js +69 -0
- package/dist/node-service-CEZZaqba.js +65 -0
- package/dist/node-startup-env-Gz8ZQniA.js +50 -0
- package/dist/nodes-cli-B4Jr9vct.js +1330 -0
- package/dist/nodes-screen-CQ7IvP62.js +401 -0
- package/dist/normalize-secret-input-_PgpexOG.js +32 -0
- package/dist/note-dfjacCV8.js +109 -0
- package/dist/npm-pack-install-CYNRv-vM.js +574 -0
- package/dist/npm-resolution-Ml2aA6Nu.js +60 -0
- package/dist/oauth.runtime-DA_48MPQ.js +687 -0
- package/dist/oauth.runtime-DS1ry5__.js +318 -0
- package/dist/oauth.runtime-qCkidk8J.js +180 -0
- package/dist/ollama-defaults-asNuGW4_.js +4 -0
- package/dist/onboard-BM6gO6Uw.js +589 -0
- package/dist/onboard-D9IU-7uw.js +48 -0
- package/dist/onboard-DQaHGPRm.js +25 -0
- package/dist/onboard-channels-BdQtLjYb.js +300 -0
- package/dist/onboard-channels-DIVUygs5.js +1257 -0
- package/dist/onboard-config-DFKb-0sE.js +29 -0
- package/dist/onboard-config-DYykzJhx.js +2 -0
- package/dist/onboard-custom-CDP4w1AT.js +216 -0
- package/dist/onboard-custom-DhJN13UV.js +644 -0
- package/dist/onboard-helpers-B7XTd4Pw.js +335 -0
- package/dist/onboard-helpers-BUKtx5Bq.js +54 -0
- package/dist/onboard-hooks-BHSSLAhI.js +73 -0
- package/dist/onboard-remote-BOzEPdHA.js +59 -0
- package/dist/onboard-remote-DVza19_k.js +182 -0
- package/dist/onboard-search-CrS-n9_3.js +446 -0
- package/dist/onboard-skills-BojzIPvk.js +133 -0
- package/dist/onboard-skills-D7HyCVjz.js +69 -0
- package/dist/openai-codex-provider.runtime-BFsopDHI.js +2 -0
- package/dist/openai-defaults-B7FUywsh.js +10 -0
- package/dist/openclaw-exec-env-AcZ9we1N.js +14 -0
- package/dist/openclaw-root-TUHYdr9B.js +88 -0
- package/dist/openclaw-tools.runtime-draZJo5r.js +211 -0
- package/dist/outbound-media-69yrWRDt.js +11 -0
- package/dist/outbound-runtime-ic_7ulJJ.js +1 -0
- package/dist/pairing-challenge-CNrPmmi9.js +48 -0
- package/dist/pairing-cli-BohXW2BK.js +150 -0
- package/dist/pairing-labels-CNKCSmBK.js +7 -0
- package/dist/pairing-message-CBv2njJT.js +4 -0
- package/dist/pairing-store-C4lsd4pO.js +590 -0
- package/dist/pairing-token-gKj4SNFJ.js +55 -0
- package/dist/parse-duration-BBGYkY0S.js +41 -0
- package/dist/parse-finite-number-CP4MQF_w.js +30 -0
- package/dist/parse-log-line-CVh9zu3Q.js +43 -0
- package/dist/parse-port-COyt3COn.js +8 -0
- package/dist/path-alias-guards-ZTKqurNH.js +40 -0
- package/dist/path-env-CPkz6U0Y.js +87 -0
- package/dist/paths-CTjJI9l0.js +179 -0
- package/dist/paths-GHJ97ebE.js +268 -0
- package/dist/paths-nCHyK08H.js +56 -0
- package/dist/perplexity-Beshd9zu.js +422 -0
- package/dist/persistent-dedupe-bjKjVI5u.js +116 -0
- package/dist/pi-embedded-CSQySvOV.js +168518 -0
- package/dist/pi-model-discovery-CuX5CDyZ.js +125 -0
- package/dist/pi-model-discovery-runtime-DNsMrX1n.js +44 -0
- package/dist/pi-tools.before-tool-call.runtime-DxVqzMVf.js +387 -0
- package/dist/platform-launcher-CqGy6UhP.js +83 -0
- package/dist/plugin-entry-CwuwM1jC.js +17 -0
- package/dist/plugin-install-JJwfOXtg.js +216 -0
- package/dist/plugin-install-plan-cixz1_W4.js +49 -0
- package/dist/plugin-install-vkpI1UNd.js +184 -0
- package/dist/plugin-registry-C3j_DUnj.js +51 -0
- package/dist/plugin-registry-DB_yxabS.js +213 -0
- package/dist/plugin-sdk/account-helpers.js +3 -0
- package/dist/plugin-sdk/account-id.js +2 -0
- package/dist/plugin-sdk/account-resolution.js +216 -0
- package/dist/plugin-sdk/acp-runtime.js +46 -0
- package/dist/plugin-sdk/agent-runtime.js +215 -0
- package/dist/plugin-sdk/allow-from.js +17 -0
- package/dist/plugin-sdk/allowlist-config-edit.js +2 -0
- package/dist/plugin-sdk/bluebubbles.js +232 -0
- package/dist/plugin-sdk/boolean-param.js +2 -0
- package/dist/plugin-sdk/channel-actions.js +16 -0
- package/dist/plugin-sdk/channel-config-helpers.js +15 -0
- package/dist/plugin-sdk/channel-config-schema.js +5 -0
- package/dist/plugin-sdk/channel-contract.js +1 -0
- package/dist/plugin-sdk/channel-feedback.js +4 -0
- package/dist/plugin-sdk/channel-inbound.js +53 -0
- package/dist/plugin-sdk/channel-lifecycle.js +2 -0
- package/dist/plugin-sdk/channel-pairing.js +2 -0
- package/dist/plugin-sdk/channel-policy.js +19 -0
- package/dist/plugin-sdk/channel-reply-pipeline.js +23 -0
- package/dist/plugin-sdk/channel-runtime.js +33 -0
- package/dist/plugin-sdk/channel-send-result.js +2 -0
- package/dist/plugin-sdk/channel-setup.js +24 -0
- package/dist/plugin-sdk/channel-targets.js +3 -0
- package/dist/plugin-sdk/cli-runtime.js +5 -0
- package/dist/plugin-sdk/command-auth.js +212 -0
- package/dist/plugin-sdk/compat.js +59 -0
- package/dist/plugin-sdk/config-runtime.js +51 -0
- package/dist/plugin-sdk/conversation-runtime.js +55 -0
- package/dist/plugin-sdk/core.js +40 -0
- package/dist/plugin-sdk/device-bootstrap.js +6 -0
- package/dist/plugin-sdk/diagnostics-otel.js +7 -0
- package/dist/plugin-sdk/diffs.js +3 -0
- package/dist/plugin-sdk/directory-runtime.js +5 -0
- package/dist/plugin-sdk/discord-core.js +26 -0
- package/dist/plugin-sdk/discord.js +219 -0
- package/dist/plugin-sdk/extension-shared.js +15 -0
- package/dist/plugin-sdk/feishu.js +101 -0
- package/dist/plugin-sdk/gateway-runtime.js +46 -0
- package/dist/plugin-sdk/googlechat.js +93 -0
- package/dist/plugin-sdk/group-access.js +2 -0
- package/dist/plugin-sdk/hook-runtime.js +12 -0
- package/dist/plugin-sdk/image-generation.js +51 -0
- package/dist/plugin-sdk/imessage-core.js +214 -0
- package/dist/plugin-sdk/imessage.js +223 -0
- package/dist/plugin-sdk/index.js +55 -0
- package/dist/plugin-sdk/infra-runtime.js +223 -0
- package/dist/plugin-sdk/interactive-runtime.js +3 -0
- package/dist/plugin-sdk/irc.js +232 -0
- package/dist/plugin-sdk/json-store.js +9 -0
- package/dist/plugin-sdk/keyed-async-queue.js +2 -0
- package/dist/plugin-sdk/lazy-runtime.js +2 -0
- package/dist/plugin-sdk/line-core.js +29 -0
- package/dist/plugin-sdk/line.js +22 -0
- package/dist/plugin-sdk/llm-task.js +7 -0
- package/dist/plugin-sdk/matrix-runtime-heavy.js +223 -0
- package/dist/plugin-sdk/matrix-runtime-shared.js +2 -0
- package/dist/plugin-sdk/matrix.js +84 -0
- package/dist/plugin-sdk/mattermost.js +233 -0
- package/dist/plugin-sdk/media-runtime.js +212 -0
- package/dist/plugin-sdk/media-understanding-runtime.js +211 -0
- package/dist/plugin-sdk/media-understanding.js +15 -0
- package/dist/plugin-sdk/memory-core.js +2 -0
- package/dist/plugin-sdk/memory-lancedb.js +2 -0
- package/dist/plugin-sdk/msteams.js +244 -0
- package/dist/plugin-sdk/nextcloud-talk.js +231 -0
- package/dist/plugin-sdk/nostr.js +50 -0
- package/dist/plugin-sdk/ollama-setup.js +33 -0
- package/dist/plugin-sdk/outbound-runtime.js +23 -0
- package/dist/plugin-sdk/plugin-entry.js +3 -0
- package/dist/plugin-sdk/plugin-runtime.js +211 -0
- package/dist/plugin-sdk/process-runtime.js +12 -0
- package/dist/plugin-sdk/provider-auth-api-key.js +53 -0
- package/dist/plugin-sdk/provider-auth-login.js +2 -0
- package/dist/plugin-sdk/provider-auth.js +37 -0
- package/dist/plugin-sdk/provider-catalog.js +2 -0
- package/dist/plugin-sdk/provider-env-vars.js +2 -0
- package/dist/plugin-sdk/provider-google.js +3 -0
- package/dist/plugin-sdk/provider-models.js +27 -0
- package/dist/plugin-sdk/provider-onboard.js +20 -0
- package/dist/plugin-sdk/provider-setup.js +58 -0
- package/dist/plugin-sdk/provider-stream.js +211 -0
- package/dist/plugin-sdk/provider-usage.js +18 -0
- package/dist/plugin-sdk/provider-web-search.js +23 -0
- package/dist/plugin-sdk/provider-zai-endpoint.js +3 -0
- package/dist/plugin-sdk/reply-history.js +23 -0
- package/dist/plugin-sdk/reply-payload.js +2 -0
- package/dist/plugin-sdk/reply-runtime.js +211 -0
- package/dist/plugin-sdk/request-url.js +2 -0
- package/dist/plugin-sdk/routing.js +25 -0
- package/dist/plugin-sdk/runtime-env.js +15 -0
- package/dist/plugin-sdk/runtime-store.js +2 -0
- package/dist/plugin-sdk/runtime.js +15 -0
- package/dist/plugin-sdk/sandbox.js +66 -0
- package/dist/plugin-sdk/secret-input.js +3 -0
- package/dist/plugin-sdk/security-runtime.js +18 -0
- package/dist/plugin-sdk/self-hosted-provider-setup.js +33 -0
- package/dist/plugin-sdk/setup-adapter-runtime.js +2 -0
- package/dist/plugin-sdk/setup-runtime.js +17 -0
- package/dist/plugin-sdk/setup-tools.js +21 -0
- package/dist/plugin-sdk/setup.js +26 -0
- package/dist/plugin-sdk/signal.js +220 -0
- package/dist/plugin-sdk/slack-core.js +22 -0
- package/dist/plugin-sdk/slack.js +223 -0
- package/dist/plugin-sdk/speech-runtime.js +213 -0
- package/dist/plugin-sdk/speech.js +212 -0
- package/dist/plugin-sdk/ssrf-runtime.js +5 -0
- package/dist/plugin-sdk/state-paths.js +3 -0
- package/dist/plugin-sdk/status-helpers.js +9 -0
- package/dist/plugin-sdk/telegram-core.js +23 -0
- package/dist/plugin-sdk/telegram.js +224 -0
- package/dist/plugin-sdk/testing.js +13174 -0
- package/dist/plugin-sdk/text-runtime.js +45 -0
- package/dist/plugin-sdk/thread-bindings-runtime.js +2 -0
- package/dist/plugin-sdk/thread-ownership.js +2 -0
- package/dist/plugin-sdk/tlon.js +57 -0
- package/dist/plugin-sdk/tool-send.js +2 -0
- package/dist/plugin-sdk/twitch.js +46 -0
- package/dist/plugin-sdk/voice-call.js +213 -0
- package/dist/plugin-sdk/web-media.js +28 -0
- package/dist/plugin-sdk/webhook-ingress.js +7 -0
- package/dist/plugin-sdk/webhook-path.js +2 -0
- package/dist/plugin-sdk/whatsapp-core.js +219 -0
- package/dist/plugin-sdk/whatsapp-shared.js +18 -0
- package/dist/plugin-sdk/windows-spawn.js +2 -0
- package/dist/plugin-sdk/zalo.js +239 -0
- package/dist/plugin-sdk/zalouser.js +240 -0
- package/dist/plugins/build-smoke-entry.js +211 -0
- package/dist/plugins/runtime/index.js +229 -0
- package/dist/plugins-1Z50ecJ6.js +1 -0
- package/dist/plugins-C6fKmNuA.js +7 -0
- package/dist/plugins-cli-BkgQkGaU.js +1192 -0
- package/dist/policy-CpkbSAfm.js +60 -0
- package/dist/polls-B2VH7SN9.js +35 -0
- package/dist/ports-BjWuIIQw.js +262 -0
- package/dist/ports-DFiK_Jc-.js +385 -0
- package/dist/ports-lsof-DtJqhFOr.js +25 -0
- package/dist/ports-probe-BQqp8l8E.js +14 -0
- package/dist/preflight-audio.runtime-Fi9mofpp.js +216 -0
- package/dist/probe-BM9sbCgS.js +20 -0
- package/dist/probe-DLBOZftS.js +134 -0
- package/dist/probe-auth-Bjp3G4CI.js +48 -0
- package/dist/probe-auth-DMSPTRRk.js +45 -0
- package/dist/process-runtime-C7el-Ri4.js +1 -0
- package/dist/process-scoped-map-C4gOa-gv.js +61 -0
- package/dist/profile-utils-BcMYGFPT.js +15 -0
- package/dist/profiles-D17eMKQZ.js +683 -0
- package/dist/program-Ch-76sgl.js +155 -0
- package/dist/program-context-BMWNUfqL.js +10 -0
- package/dist/program-context-CD_RvRYh.js +2 -0
- package/dist/progress-D1r9bZU1.js +132 -0
- package/dist/prompt-select-styled-NUKYS9QR.js +4879 -0
- package/dist/prompt-style-BvciNCqy.js +7 -0
- package/dist/prompts-NtuylUyl.js +9 -0
- package/dist/prototype-keys-Cm_8mWvq.js +11 -0
- package/dist/provider-api-key-auth-BE0taXiB.js +108 -0
- package/dist/provider-api-key-auth.runtime-jDZZUAMX.js +34 -0
- package/dist/provider-auth-Bw8x1a3o.js +58 -0
- package/dist/provider-auth-api-key-BrQYvdxi.js +1 -0
- package/dist/provider-auth-choice-BYbPq0eC.js +128 -0
- package/dist/provider-auth-choice-helpers-Bj1GkOSn.js +48 -0
- package/dist/provider-auth-choice-preference-tKq5gaJL.js +192 -0
- package/dist/provider-auth-choice.runtime-DegPpvRJ.js +223 -0
- package/dist/provider-auth-choices-QSilukI1.js +58 -0
- package/dist/provider-auth-guidance-gninjjq8.js +34 -0
- package/dist/provider-auth-helpers-B0dS-1WK.js +86 -0
- package/dist/provider-auth-input-BftBdgvW.js +112 -0
- package/dist/provider-auth-login-D0n0lMuc.js +8 -0
- package/dist/provider-auth-login.runtime-LvuBkQrc.js +243 -0
- package/dist/provider-auth-mode-sTdccIKL.js +20 -0
- package/dist/provider-auth-ref-BS3gwrNr.js +168 -0
- package/dist/provider-auth-ref-BmEcEN7K.js +3 -0
- package/dist/provider-catalog--18-pW5t.js +11 -0
- package/dist/provider-catalog-2P2hel74.js +48 -0
- package/dist/provider-catalog-B0FqWSwe.js +48 -0
- package/dist/provider-catalog-BvORKzzD.js +91 -0
- package/dist/provider-catalog-C34j1_or.js +26 -0
- package/dist/provider-catalog-C5vmXjmb.js +11 -0
- package/dist/provider-catalog-CBufm2Dr.js +36 -0
- package/dist/provider-catalog-D7QvsUXS.js +12 -0
- package/dist/provider-catalog-DKy_dzQZ.js +41 -0
- package/dist/provider-env-vars-CsQlY7bF.js +110 -0
- package/dist/provider-id-BpXo5t6v.js +31 -0
- package/dist/provider-model-allowlist-4HSOnlX-.js +24 -0
- package/dist/provider-model-primary-NJ-xlhec.js +53 -0
- package/dist/provider-models-C2EjYMwW.js +2416 -0
- package/dist/provider-oauth-flow-BQN6F6EC.js +33 -0
- package/dist/provider-ollama-setup-DhQvDwAj.js +309 -0
- package/dist/provider-onboard-CjOfyeQG.js +1 -0
- package/dist/provider-onboarding-config-DOZ3pFA6.js +165 -0
- package/dist/provider-openai-codex-oauth-tls-Bo8U4D3E.js +101 -0
- package/dist/provider-runtime.runtime-DnP2jpoM.js +211 -0
- package/dist/provider-self-hosted-setup-CUrmsugW.js +182 -0
- package/dist/provider-usage-ClDVmkhl.js +633 -0
- package/dist/provider-usage-DIC6cn-3.js +211 -0
- package/dist/provider-web-search-NzK8ep1E.js +507 -0
- package/dist/provider-wizard-C6jCuyQe.js +236 -0
- package/dist/provider-zai-endpoint-DeDABzT4.js +106 -0
- package/dist/proxy-H5O2p6AP.js +121 -0
- package/dist/proxy-env-DG2u55RW.js +40 -0
- package/dist/push-apns-D4zD2tmP.js +1050 -0
- package/dist/pw-ai-BuPUVeUK.js +1876 -0
- package/dist/qmd-manager-BpygGMW9.js +1571 -0
- package/dist/qr-cli-DnWHXcxh.js +370 -0
- package/dist/qr-cli-yaZ0FZ6z.js +213 -0
- package/dist/query-expansion-Do45hILP.js +1114 -0
- package/dist/reactions-BcC_XZqD.js +281 -0
- package/dist/read-only-account-inspect-DPJzadPo.js +42 -0
- package/dist/read-only-account-inspect.discord.runtime-CW9DDKH8.js +216 -0
- package/dist/read-only-account-inspect.slack.runtime-BcXBPyh3.js +216 -0
- package/dist/read-only-account-inspect.telegram.runtime-Y7h0Jbdj.js +216 -0
- package/dist/redact-BDinS1q9.js +102 -0
- package/dist/redact-identifier-FUiWQxv5.js +13 -0
- package/dist/redact-snapshot-DBPmeYy2.js +2654 -0
- package/dist/ref-contract-CCBBbf1r.js +53 -0
- package/dist/register-CppP7Ddc.js +43 -0
- package/dist/register.agent-BOD5ROGQ.js +546 -0
- package/dist/register.backup-Y2VGqcRu.js +269 -0
- package/dist/register.configure-1qiTINph.js +354 -0
- package/dist/register.maintenance-shn-zigv.js +694 -0
- package/dist/register.message-mR4CLSoo.js +812 -0
- package/dist/register.onboard-CkDryVid.js +298 -0
- package/dist/register.setup-B0xW5olD.js +318 -0
- package/dist/register.status-health-sessions-CuhWc03j.js +604 -0
- package/dist/register.subclis-BazXM5TW.js +315 -0
- package/dist/register.subclis-C2d8UDhH.js +13 -0
- package/dist/registry-C3q59Qj0.js +55 -0
- package/dist/registry-CPsHw6xU.js +219 -0
- package/dist/registry-CxgtJ09C.js +28 -0
- package/dist/registry-rgYi7KoO.js +160 -0
- package/dist/repair-qXnOAvDy.js +105 -0
- package/dist/replies-EiwmmZ_W.js +122 -0
- package/dist/reply-history-CVCD5oE9.js +1 -0
- package/dist/reply-payload-DBGc074f.js +232 -0
- package/dist/report-cli-DB1jQx32.js +42 -0
- package/dist/request-url-BKfWAQx8.js +10 -0
- package/dist/resolve-Ckjd8TAk.js +14 -0
- package/dist/resolve-T2q_0ARF.js +619 -0
- package/dist/resolve-route-vEY3ONZ2.js +466 -0
- package/dist/resolve-utils-CbqJY2bs.js +102 -0
- package/dist/response-generator-VdoCcQ3y.js +153 -0
- package/dist/restart-stale-pids-CLGiqU2E.js +187 -0
- package/dist/retry-D15TD1S3.js +168 -0
- package/dist/root-help-B9Aou4ho.js +32 -0
- package/dist/routes-TpLEcKO8.js +7084 -0
- package/dist/routing-Y3m0o-kB.js +26 -0
- package/dist/rpc-C6MN-nVc.js +67 -0
- package/dist/run-command-DRKv5Lj6.js +32 -0
- package/dist/run-main-YZSMdx0B.js +424 -0
- package/dist/run-with-concurrency-BrSjWzpg.js +41 -0
- package/dist/runtime-B66W9flm.js +43 -0
- package/dist/runtime-C9VaVKYZ.js +2338 -0
- package/dist/runtime-CT2LIJZu.js +91 -0
- package/dist/runtime-CqDQ81eY.js +143 -0
- package/dist/runtime-CuvWMN7E.js +89 -0
- package/dist/runtime-D4_OpzA1.js +5 -0
- package/dist/runtime-DP-4DZja.js +5 -0
- package/dist/runtime-Dl17x_cV.js +1 -0
- package/dist/runtime-Z35JoYPC.js +30 -0
- package/dist/runtime-api-D79M0lQN.js +1 -0
- package/dist/runtime-api-y3zfnQGK.js +39 -0
- package/dist/runtime-discord-ops.runtime-Bg5h5v9-.js +234 -0
- package/dist/runtime-env-a_iwdJIv.js +1 -0
- package/dist/runtime-forwarders-DtMc8rBP.js +44 -0
- package/dist/runtime-group-policy-B7irU4eu.js +59 -0
- package/dist/runtime-guard-y62lPDGY.js +58 -0
- package/dist/runtime-parse-CeqXmZHJ.js +84 -0
- package/dist/runtime-paths-CstaCCMi.js +334 -0
- package/dist/runtime-slack-ops.runtime-BumgKDhS.js +226 -0
- package/dist/runtime-status-CgL02wYX.js +15 -0
- package/dist/runtime-store-Bt3Sdbrn.js +22 -0
- package/dist/runtime-telegram-ops.runtime-rSLQ3KrE.js +233 -0
- package/dist/runtime-whatsapp-boundary-xZem0NyQ.js +364 -0
- package/dist/safe-open-sync-Bt9R1Mnf.js +83 -0
- package/dist/safe-regex-tLlDZYfM.js +244 -0
- package/dist/safe-text-B_CQuica.js +16 -0
- package/dist/sandbox-CUUouiKs.js +2795 -0
- package/dist/sandbox-cli-BN8y0Get.js +499 -0
- package/dist/sandbox-paths-fqp_TZdO.js +144 -0
- package/dist/sandbox-qSs4h3sk.js +1 -0
- package/dist/sanitize-env-vars-vNSNqm0y.js +74 -0
- package/dist/scan-paths-BJmvUZ1E.js +28 -0
- package/dist/search-manager-DWhFgwyp.js +17 -0
- package/dist/search-manager-r8Cw4ZRv.js +392 -0
- package/dist/secret-equal-ObQfyZGa.js +9 -0
- package/dist/secret-file-Ch0yuOXR.js +11 -0
- package/dist/secret-file-DYJtH6kf.js +92 -0
- package/dist/secret-input-4REZ4sHo.js +35 -0
- package/dist/secrets-cli-D1df8b0o.js +2304 -0
- package/dist/secure-random-Cs8tw_HQ.js +10 -0
- package/dist/security-cli-V66ESmdT.js +676 -0
- package/dist/security-runtime-BuEhpJVE.js +23 -0
- package/dist/send-3tabvle6.js +100 -0
- package/dist/send-CC5J3tyW.js +1026 -0
- package/dist/send-deps-CrFMNvqO.js +19 -0
- package/dist/send-i2-mdtiE.js +250 -0
- package/dist/server-BTOjmlyi.js +116 -0
- package/dist/server-middleware-CCqKhKUb.js +106 -0
- package/dist/server-node-events-D6y22Tt8.js +611 -0
- package/dist/server-startup-matrix-migration-DHWSoS73.js +1595 -0
- package/dist/service-Bxc9uL2e.js +774 -0
- package/dist/service-CBLajPZL.js +21 -0
- package/dist/session-cost-usage-BmbaBvk4.js +212 -0
- package/dist/session-cost-usage-C30Jl2SI.js +615 -0
- package/dist/session-fork.runtime-BZfcC1Nc.js +51 -0
- package/dist/session-key-gFFk3uv9.js +216 -0
- package/dist/session-write-lock-DNKvpjKf.js +324 -0
- package/dist/sessions-BIH_j_XS.js +222 -0
- package/dist/sessions-D5dWcxC_.js +212 -0
- package/dist/sessions-DaSBVNwD.js +669 -0
- package/dist/setup-C2XF1YH3.js +397 -0
- package/dist/setup-CN-teRpz.js +8 -0
- package/dist/setup-adapter-runtime-Bjv2adwG.js +1 -0
- package/dist/setup-binary-BOJA7zdN.js +30 -0
- package/dist/setup-browser-BhNPCUtK.js +71 -0
- package/dist/setup-core-BsG09DZH.js +149 -0
- package/dist/setup-core-D-O1GQax.js +162 -0
- package/dist/setup-core-Dtm54Rcq.js +510 -0
- package/dist/setup-entry-B1mTa7bU.js +10 -0
- package/dist/setup-entry-CTMgw-K5.js +13 -0
- package/dist/setup-entry-Cmd_cufO.js +13 -0
- package/dist/setup-entry-CybgA3zP.js +12 -0
- package/dist/setup-entry-DED_hL6i.js +12 -0
- package/dist/setup-entry-WCq9VMWx.js +14 -0
- package/dist/setup-group-access-BtPApRvE.js +70 -0
- package/dist/setup-helpers-B62Ecg9r.js +362 -0
- package/dist/setup-surface-B7A7qowY.js +452 -0
- package/dist/setup-surface-BBYJVRXc.js +380 -0
- package/dist/setup-surface-CFUz_BJi.js +298 -0
- package/dist/setup-tools-BPiMjAN7.js +1 -0
- package/dist/setup-wizard-helpers-COZ1UAdX.js +770 -0
- package/dist/setup-wizard-proxy-Slwi-1gX.js +116 -0
- package/dist/setup.finalize-B8O01nge.js +633 -0
- package/dist/setup.gateway-config-BPDIFk__.js +288 -0
- package/dist/setup.secret-input-BL-bqJpt.js +25 -0
- package/dist/shared-AygSbeCK.js +50 -0
- package/dist/shared-BHqDLkMG.js +127 -0
- package/dist/shared-BPtG8PgB.js +70 -0
- package/dist/shared-BU0QgVMZ.js +36 -0
- package/dist/shared-Bzr2UyEm.js +351 -0
- package/dist/shared-C_XXbGIF.js +87 -0
- package/dist/shared-Diw3KzwZ.js +82 -0
- package/dist/shared-DngjQumT.js +196 -0
- package/dist/shared-DzH3zmAy.js +64 -0
- package/dist/shared-LeP8iUTz.js +54 -0
- package/dist/shell-argv-DWV43Vya.js +72 -0
- package/dist/shell-env-cD92jEyV.js +181 -0
- package/dist/signal-BQd9f9dF.js +315 -0
- package/dist/signal-Ca7y47bM.js +46 -0
- package/dist/signal-Did9U_fa.js +214 -0
- package/dist/signal-cli-install-DxoL8CgF.js +188 -0
- package/dist/skill-commands-CiSwTFBQ.js +652 -0
- package/dist/skill-commands.runtime-CEwlWT4j.js +34 -0
- package/dist/skill-scanner-DG7MT7pu.js +354 -0
- package/dist/skills-BC8GJ9Rp.js +22 -0
- package/dist/skills-CCgKs_NJ.js +863 -0
- package/dist/skills-cli-dhYXJCuL.js +339 -0
- package/dist/skills-install-DiriUXJd.js +763 -0
- package/dist/skills-status-BmQTn4jL.js +23 -0
- package/dist/skills-status-a9b899Y3.js +169 -0
- package/dist/slack-CXgv7nu7.js +730 -0
- package/dist/slack-CcSByPzI.js +217 -0
- package/dist/slack-CtcCh0Lj.js +24537 -0
- package/dist/slack-core-DcsbATUs.js +1 -0
- package/dist/slash-commands.runtime-kO8EUKYW.js +228 -0
- package/dist/slash-dispatch.runtime-Bu2yMeFy.js +238 -0
- package/dist/slash-skill-commands.runtime-wwX3tF84.js +216 -0
- package/dist/speech-bSreRuDH.js +1 -0
- package/dist/speech-runtime-y1FcnGVA.js +1 -0
- package/dist/src-CmXHIz5f.js +846 -0
- package/dist/ssh-config-ChqR6ijV.js +77 -0
- package/dist/ssh-tunnel-Cz51VBAt.js +159 -0
- package/dist/ssh-tunnel-DWze2IQS.js +16 -0
- package/dist/ssrf-Dk9XaoKN.js +220 -0
- package/dist/ssrf-policy-Dk6oMa20.js +69 -0
- package/dist/ssrf-runtime-C-mAQLVA.js +1 -0
- package/dist/stagger-DU7FjHYo.js +54 -0
- package/dist/state-paths-DJIGEFq_.js +1 -0
- package/dist/status-69r8-Zey.js +75 -0
- package/dist/status-BdLTvZOL.js +44 -0
- package/dist/status-Bt7DQmRI.js +1665 -0
- package/dist/status-CoUFSBgt.js +202 -0
- package/dist/status-DbI3Kbh5.js +235 -0
- package/dist/status-DeKlzu_o.js +212 -0
- package/dist/status-Haie42Fc.js +606 -0
- package/dist/status-helpers-Cda-rGLX.js +101 -0
- package/dist/status-json-DS1M_MWJ.js +322 -0
- package/dist/status.link-channel-Cb8bZ_Od.js +40 -0
- package/dist/status.scan.deps.runtime-7_6VUs50.js +77 -0
- package/dist/status.scan.runtime-DtR8BIE9.js +14 -0
- package/dist/status.summary-l_Bi1buR.js +600 -0
- package/dist/status.summary.runtime-Cng6MzRU.js +151 -0
- package/dist/status.update-DtbnnPKx.js +79 -0
- package/dist/store-CvL8MPei.js +1446 -0
- package/dist/store.runtime-hgnvmZgO.js +43 -0
- package/dist/string-normalization-CvzuCAZv.js +19 -0
- package/dist/string-sample-BOLqzr4Y.js +11 -0
- package/dist/subagent-orphan-recovery-si1z2iBu.js +407 -0
- package/dist/subagent-registry-runtime-CXUDI8gL.js +211 -0
- package/dist/subcli-descriptors-CY_nHzpZ.js +151 -0
- package/dist/subsystem-CUp-6QQf.js +421 -0
- package/dist/synology-chat-CdejNfs0.js +12 -0
- package/dist/system-cli-DkOaXHkQ.js +99 -0
- package/dist/system-events-mAu6Ap6K.js +75 -0
- package/dist/system-message-DA9eUYzB.js +16 -0
- package/dist/system-run-command-Cxq2F1MB.js +258 -0
- package/dist/systemd-CrxZBFae.js +557 -0
- package/dist/systemd-hints-y-zJ9aTm.js +315 -0
- package/dist/systemd-linger-BdklDcLg.js +16 -0
- package/dist/systemd-linger-DLrbG9_d.js +68 -0
- package/dist/table-DFMOhmNZ.js +305 -0
- package/dist/tailnet-ofqBrXzu.js +38 -0
- package/dist/tailscale-Cbsx-2HB.js +254 -0
- package/dist/target-errors-ksphhzJg.js +26 -0
- package/dist/target-registry-krAVlXi_.js +1321 -0
- package/dist/telegram/audit.js +2 -0
- package/dist/telegram/token.js +211 -0
- package/dist/telegram-BjDUP22F.js +10910 -0
- package/dist/telegram-DMiNSGAJ.js +575 -0
- package/dist/telegram-Dt11B3JL.js +218 -0
- package/dist/telegram-core-B4Jo-uko.js +1 -0
- package/dist/template-messages-kh7VfgOb.js +214 -0
- package/dist/text-chunking-CUf5WgqG.js +19 -0
- package/dist/text-format-sFXlJfHH.js +8 -0
- package/dist/text-runtime-C_Roi_Je.js +1418 -0
- package/dist/theme-B5HDbQfl.js +2 -0
- package/dist/theme-CdOoMzRk.js +34 -0
- package/dist/thinking-BBD_0HSp.js +68 -0
- package/dist/thinking.shared-CncvRHts.js +246 -0
- package/dist/thread-bindings-messages-Cdo8jSa9.js +229 -0
- package/dist/thread-bindings-policy-DMjOaNyR.js +119 -0
- package/dist/thread-bindings-runtime-Ckwk3Uuz.js +1 -0
- package/dist/threading-helpers-Cq55SUtb.js +14 -0
- package/dist/timeouts-BwR1sGom.js +72 -0
- package/dist/tmp-openclaw-dir-idKIOMmb.js +102 -0
- package/dist/token-Bgv8XEsC.js +50 -0
- package/dist/tool-catalog-BV6FcEWS.js +337 -0
- package/dist/tool-policy-match-CHqTCSdK.js +46 -0
- package/dist/tool-send-9LXKcrda.js +16 -0
- package/dist/topology-cli-BhUXVViF.js +43 -0
- package/dist/transcript-events-B1V6z5ct.js +29 -0
- package/dist/tui-DDJMGCFK.js +3838 -0
- package/dist/tui-cli-ByN-ZH6y.js +237 -0
- package/dist/typebox-D0SHDJST.js +175 -0
- package/dist/types-BCKGVVld.js +83 -0
- package/dist/types-CtpUGsDP.js +30 -0
- package/dist/types.secrets-BWSeXrF4.js +80 -0
- package/dist/types.tools-BBO8HCi6.js +22 -0
- package/dist/typing-DG_YqWJ7.js +224 -0
- package/dist/unhandled-rejections-CDJ8dOVP.js +170 -0
- package/dist/unhandled-rejections-O6cVOz2D.js +4 -0
- package/dist/update-Br8U-txJ.js +1039 -0
- package/dist/update-check-C3TeQaWg.js +464 -0
- package/dist/update-cli-XHfIntD0.js +1625 -0
- package/dist/update-offset-store-36vzzZXw.js +211 -0
- package/dist/upsert-with-lock-Bb96JHpb.js +34 -0
- package/dist/url-userinfo-Db63ng4y.js +14 -0
- package/dist/utils-Bxk6BLTg.js +236 -0
- package/dist/utils-vDeUf98G.js +7 -0
- package/dist/version-DCY9_obP.js +64 -0
- package/dist/version-DRF-wKTV.js +2 -0
- package/dist/voice-call-D4fgwZNO.js +1 -0
- package/dist/warning-filter-CgvLQB4Y.js +56 -0
- package/dist/web-media-BfBb8i48.js +1 -0
- package/dist/web-media-CtU6jM5V.js +498 -0
- package/dist/webhook-ingress-CupqYpKM.js +338 -0
- package/dist/webhook-memory-guards-BHrFZ4yq.js +129 -0
- package/dist/webhook-path-BGFZ55ML.js +22 -0
- package/dist/webhook-shared-Cvk3b0ac.js +349 -0
- package/dist/webhooks-cli-vOAoBF9b.js +357 -0
- package/dist/whatsapp-D5nD0rGG.js +58 -0
- package/dist/whatsapp-DXbWlm3A.js +82 -0
- package/dist/whatsapp-core-C2WGMsaY.js +89451 -0
- package/dist/whatsapp-heartbeat-CSWnPQ7q.js +84 -0
- package/dist/whatsapp-shared-BmHKqTtR.js +95 -0
- package/dist/widearea-dns-CXimgJzu.js +125 -0
- package/dist/windows-argv-IXrdWrJj.js +145 -0
- package/dist/windows-spawn-vMJGZo89.js +154 -0
- package/dist/with-timeout-2AKTISee.js +58 -0
- package/dist/workspace-BH7CXmrr.js +479 -0
- package/dist/workspace-dirs-_O4V3xCR.js +13 -0
- package/dist/workspace-v5XppK5M.js +302 -0
- package/dist/ws-By-QcLjg.js +11 -0
- package/dist/wsl-BV3Cb66X.js +57 -0
- package/dist/zalo-CHQzsLhE.js +301 -0
- package/dist/zalo-CcJ3J9f2.js +13 -0
- package/dist/zod-schema.agent-runtime-T_EC_6fg.js +600 -0
- package/dist/zod-schema.core-BdgRr-F1.js +545 -0
- package/dist/zod-schema.providers-core-Dgq7MTqU.js +1613 -0
- package/docs/.i18n/README.md +31 -0
- package/docs/.i18n/glossary.ja-JP.json +14 -0
- package/docs/.i18n/glossary.zh-CN.json +242 -0
- package/docs/.i18n/ja-JP.tm.jsonl +0 -0
- package/docs/assets/install-script.svg +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-dark.svg +418 -0
- package/docs/assets/openclaw-logo-text.png +0 -0
- package/docs/assets/openclaw-logo-text.svg +418 -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/assets/sponsors/blacksmith.svg +14 -0
- package/docs/assets/sponsors/convex.svg +16 -0
- package/docs/assets/sponsors/openai.svg +3 -0
- package/docs/assets/sponsors/vercel.svg +5 -0
- package/docs/auth-credential-semantics.md +53 -0
- package/docs/automation/auth-monitoring.md +44 -0
- package/docs/automation/cron-jobs.md +727 -0
- package/docs/automation/cron-vs-heartbeat.md +286 -0
- package/docs/automation/gmail-pubsub.md +256 -0
- package/docs/automation/hooks.md +1049 -0
- package/docs/automation/poll.md +86 -0
- package/docs/automation/standing-orders.md +251 -0
- package/docs/automation/troubleshooting.md +122 -0
- package/docs/automation/webhook.md +217 -0
- package/docs/brave-search.md +93 -0
- package/docs/channels/bluebubbles.md +347 -0
- package/docs/channels/broadcast-groups.md +442 -0
- package/docs/channels/channel-routing.md +139 -0
- package/docs/channels/discord.md +1229 -0
- package/docs/channels/feishu.md +747 -0
- package/docs/channels/googlechat.md +261 -0
- package/docs/channels/group-messages.md +84 -0
- package/docs/channels/groups.md +379 -0
- package/docs/channels/imessage.md +367 -0
- package/docs/channels/index.md +47 -0
- package/docs/channels/irc.md +242 -0
- package/docs/channels/line.md +194 -0
- package/docs/channels/location.md +56 -0
- package/docs/channels/matrix.md +677 -0
- package/docs/channels/mattermost.md +427 -0
- package/docs/channels/msteams.md +780 -0
- package/docs/channels/nextcloud-talk.md +138 -0
- package/docs/channels/nostr.md +242 -0
- package/docs/channels/pairing.md +114 -0
- package/docs/channels/signal.md +329 -0
- package/docs/channels/slack.md +603 -0
- package/docs/channels/synology-chat.md +132 -0
- package/docs/channels/telegram.md +987 -0
- package/docs/channels/tlon.md +276 -0
- package/docs/channels/troubleshooting.md +118 -0
- package/docs/channels/twitch.md +379 -0
- package/docs/channels/whatsapp.md +460 -0
- package/docs/channels/zalo.md +243 -0
- package/docs/channels/zalouser.md +181 -0
- package/docs/ci.md +55 -0
- package/docs/cli/acp.md +288 -0
- package/docs/cli/agent.md +29 -0
- package/docs/cli/agents.md +123 -0
- package/docs/cli/approvals.md +50 -0
- package/docs/cli/backup.md +76 -0
- package/docs/cli/browser.md +106 -0
- package/docs/cli/channels.md +102 -0
- package/docs/cli/clawbot.md +21 -0
- package/docs/cli/completion.md +35 -0
- package/docs/cli/config.md +295 -0
- package/docs/cli/configure.md +36 -0
- package/docs/cli/cron.md +77 -0
- package/docs/cli/daemon.md +53 -0
- package/docs/cli/dashboard.md +22 -0
- package/docs/cli/devices.md +139 -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 +48 -0
- package/docs/cli/gateway.md +235 -0
- package/docs/cli/health.md +21 -0
- package/docs/cli/hooks.md +318 -0
- package/docs/cli/index.md +1147 -0
- package/docs/cli/logs.md +28 -0
- package/docs/cli/memory.md +66 -0
- package/docs/cli/message.md +278 -0
- package/docs/cli/models.md +81 -0
- package/docs/cli/node.md +127 -0
- package/docs/cli/nodes.md +75 -0
- package/docs/cli/onboard.md +157 -0
- package/docs/cli/pairing.md +32 -0
- package/docs/cli/plugins.md +186 -0
- package/docs/cli/qr.md +46 -0
- package/docs/cli/reset.md +20 -0
- package/docs/cli/sandbox.md +197 -0
- package/docs/cli/secrets.md +188 -0
- package/docs/cli/security.md +79 -0
- package/docs/cli/sessions.md +110 -0
- package/docs/cli/setup.md +29 -0
- package/docs/cli/skills.md +26 -0
- package/docs/cli/status.md +30 -0
- package/docs/cli/system.md +60 -0
- package/docs/cli/tui.md +30 -0
- package/docs/cli/uninstall.md +20 -0
- package/docs/cli/update.md +103 -0
- package/docs/cli/voicecall.md +34 -0
- package/docs/cli/webhooks.md +25 -0
- package/docs/concepts/agent-loop.md +148 -0
- package/docs/concepts/agent-workspace.md +236 -0
- package/docs/concepts/agent.md +122 -0
- package/docs/concepts/architecture.md +137 -0
- package/docs/concepts/compaction.md +123 -0
- package/docs/concepts/context-engine.md +268 -0
- package/docs/concepts/context.md +172 -0
- package/docs/concepts/delegate-architecture.md +296 -0
- package/docs/concepts/features.md +73 -0
- package/docs/concepts/markdown-formatting.md +130 -0
- package/docs/concepts/memory.md +108 -0
- package/docs/concepts/messages.md +154 -0
- package/docs/concepts/model-failover.md +152 -0
- package/docs/concepts/model-providers.md +607 -0
- package/docs/concepts/models.md +225 -0
- package/docs/concepts/multi-agent.md +552 -0
- package/docs/concepts/oauth.md +158 -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 +121 -0
- package/docs/concepts/session-tool.md +242 -0
- package/docs/concepts/session.md +310 -0
- package/docs/concepts/streaming.md +155 -0
- package/docs/concepts/system-prompt.md +132 -0
- package/docs/concepts/timezone.md +91 -0
- package/docs/concepts/typebox.md +291 -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 +85 -0
- package/docs/diagnostics/flags.md +91 -0
- package/docs/docs.json +2061 -0
- package/docs/gateway/authentication.md +179 -0
- package/docs/gateway/background-process.md +97 -0
- package/docs/gateway/bonjour.md +177 -0
- package/docs/gateway/bridge-protocol.md +91 -0
- package/docs/gateway/cli-backends.md +225 -0
- package/docs/gateway/configuration-examples.md +651 -0
- package/docs/gateway/configuration-reference.md +3123 -0
- package/docs/gateway/configuration.md +633 -0
- package/docs/gateway/discovery.md +123 -0
- package/docs/gateway/doctor.md +362 -0
- package/docs/gateway/gateway-lock.md +34 -0
- package/docs/gateway/health.md +44 -0
- package/docs/gateway/heartbeat.md +393 -0
- package/docs/gateway/index.md +261 -0
- package/docs/gateway/local-models.md +152 -0
- package/docs/gateway/logging.md +113 -0
- package/docs/gateway/multiple-gateways.md +112 -0
- package/docs/gateway/network-model.md +22 -0
- package/docs/gateway/openai-http-api.md +132 -0
- package/docs/gateway/openresponses-http-api.md +295 -0
- package/docs/gateway/openshell.md +307 -0
- package/docs/gateway/pairing.md +99 -0
- package/docs/gateway/protocol.md +267 -0
- package/docs/gateway/remote-gateway-readme.md +158 -0
- package/docs/gateway/remote.md +153 -0
- package/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md +134 -0
- package/docs/gateway/sandboxing.md +469 -0
- package/docs/gateway/secrets-plan-contract.md +116 -0
- package/docs/gateway/secrets.md +503 -0
- package/docs/gateway/security/index.md +1213 -0
- package/docs/gateway/tailscale.md +132 -0
- package/docs/gateway/tools-invoke-http-api.md +110 -0
- package/docs/gateway/troubleshooting.md +378 -0
- package/docs/gateway/trusted-proxy-auth.md +330 -0
- package/docs/help/debugging.md +168 -0
- package/docs/help/environment.md +163 -0
- package/docs/help/faq.md +2999 -0
- package/docs/help/index.md +28 -0
- package/docs/help/scripts.md +28 -0
- package/docs/help/testing.md +524 -0
- package/docs/help/troubleshooting.md +297 -0
- package/docs/images/configure-model-picker-unsearchable.png +0 -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/feishu-verification-token.png +0 -0
- package/docs/images/groups-flow.svg +52 -0
- package/docs/images/mobile-ui-screenshot.png +0 -0
- package/docs/index.md +196 -0
- package/docs/install/ansible.md +230 -0
- package/docs/install/azure.md +311 -0
- package/docs/install/bun.md +55 -0
- package/docs/install/development-channels.md +120 -0
- package/docs/install/digitalocean.md +129 -0
- package/docs/install/docker-vm-runtime.md +142 -0
- package/docs/install/docker.md +375 -0
- package/docs/install/exe-dev.md +126 -0
- package/docs/install/fly.md +501 -0
- package/docs/install/gcp.md +402 -0
- package/docs/install/hetzner.md +251 -0
- package/docs/install/index.md +183 -0
- package/docs/install/installer.md +415 -0
- package/docs/install/kubernetes.md +191 -0
- package/docs/install/macos-vm.md +281 -0
- package/docs/install/migrating-matrix.md +346 -0
- package/docs/install/migrating.md +110 -0
- package/docs/install/nix.md +89 -0
- package/docs/install/node.md +138 -0
- package/docs/install/northflank.mdx +54 -0
- package/docs/install/oracle.md +156 -0
- package/docs/install/podman.md +133 -0
- package/docs/install/railway.mdx +100 -0
- package/docs/install/raspberry-pi.md +159 -0
- package/docs/install/render.mdx +169 -0
- package/docs/install/uninstall.md +128 -0
- package/docs/install/updating.md +128 -0
- package/docs/ja-JP/index.md +186 -0
- package/docs/ja-JP/start/getting-started.md +125 -0
- package/docs/ja-JP/start/wizard.md +77 -0
- package/docs/logging.md +352 -0
- package/docs/nav-tabs-underline.js +100 -0
- package/docs/network.md +54 -0
- package/docs/nodes/audio.md +187 -0
- package/docs/nodes/camera.md +162 -0
- package/docs/nodes/images.md +72 -0
- package/docs/nodes/index.md +393 -0
- package/docs/nodes/location-command.md +98 -0
- package/docs/nodes/media-understanding.md +394 -0
- package/docs/nodes/talk.md +92 -0
- package/docs/nodes/troubleshooting.md +114 -0
- package/docs/nodes/voicewake.md +66 -0
- package/docs/perplexity.md +174 -0
- package/docs/pi-dev.md +80 -0
- package/docs/pi.md +567 -0
- package/docs/platforms/android.md +168 -0
- package/docs/platforms/digitalocean.md +266 -0
- package/docs/platforms/index.md +54 -0
- package/docs/platforms/ios.md +220 -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 +104 -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 +50 -0
- package/docs/platforms/mac/remote.md +84 -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 +43 -0
- package/docs/platforms/mac/xpc.md +61 -0
- package/docs/platforms/macos.md +226 -0
- package/docs/platforms/oracle.md +303 -0
- package/docs/platforms/raspberry-pi.md +412 -0
- package/docs/platforms/windows.md +241 -0
- package/docs/plugins/agent-tools.md +10 -0
- package/docs/plugins/architecture.md +1363 -0
- package/docs/plugins/building-extensions.md +10 -0
- package/docs/plugins/building-plugins.md +376 -0
- package/docs/plugins/bundles.md +181 -0
- package/docs/plugins/community.md +141 -0
- package/docs/plugins/manifest.md +145 -0
- package/docs/plugins/sdk-migration.md +169 -0
- package/docs/plugins/voice-call.md +380 -0
- package/docs/plugins/zalouser.md +77 -0
- package/docs/prose.md +134 -0
- package/docs/providers/anthropic.md +259 -0
- package/docs/providers/bedrock.md +176 -0
- package/docs/providers/claude-max-api-proxy.md +154 -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 +43 -0
- package/docs/providers/google.md +78 -0
- package/docs/providers/groq.md +96 -0
- package/docs/providers/huggingface.md +209 -0
- package/docs/providers/index.md +69 -0
- package/docs/providers/kilocode.md +74 -0
- package/docs/providers/litellm.md +154 -0
- package/docs/providers/minimax.md +224 -0
- package/docs/providers/mistral.md +54 -0
- package/docs/providers/models.md +45 -0
- package/docs/providers/modelstudio.md +66 -0
- package/docs/providers/moonshot.md +175 -0
- package/docs/providers/nvidia.md +55 -0
- package/docs/providers/ollama.md +352 -0
- package/docs/providers/openai.md +303 -0
- package/docs/providers/opencode-go.md +45 -0
- package/docs/providers/opencode.md +64 -0
- package/docs/providers/openrouter.md +37 -0
- package/docs/providers/perplexity-provider.md +62 -0
- package/docs/providers/qianfan.md +38 -0
- package/docs/providers/qwen.md +53 -0
- package/docs/providers/sglang.md +104 -0
- package/docs/providers/synthetic.md +99 -0
- package/docs/providers/together.md +66 -0
- package/docs/providers/venice.md +282 -0
- package/docs/providers/vercel-ai-gateway.md +60 -0
- package/docs/providers/vllm.md +92 -0
- package/docs/providers/volcengine.md +74 -0
- package/docs/providers/xai.md +60 -0
- package/docs/providers/xiaomi.md +86 -0
- package/docs/providers/zai.md +46 -0
- package/docs/reference/AGENTS.default.md +126 -0
- package/docs/reference/RELEASING.md +42 -0
- package/docs/reference/api-usage-costs.md +144 -0
- package/docs/reference/credits.md +30 -0
- package/docs/reference/device-models.md +47 -0
- package/docs/reference/memory-config.md +711 -0
- package/docs/reference/prompt-caching.md +185 -0
- package/docs/reference/rpc.md +43 -0
- package/docs/reference/secretref-credential-surface.md +140 -0
- package/docs/reference/secretref-user-supplied-credentials-matrix.json +563 -0
- package/docs/reference/session-management-compaction.md +324 -0
- package/docs/reference/templates/AGENTS.dev.md +83 -0
- package/docs/reference/templates/AGENTS.md +219 -0
- package/docs/reference/templates/BOOT.md +11 -0
- package/docs/reference/templates/BOOTSTRAP.md +62 -0
- package/docs/reference/templates/HEARTBEAT.md +14 -0
- package/docs/reference/templates/IDENTITY.dev.md +47 -0
- package/docs/reference/templates/IDENTITY.md +29 -0
- package/docs/reference/templates/SOUL.dev.md +76 -0
- package/docs/reference/templates/SOUL.md +43 -0
- package/docs/reference/templates/TOOLS.dev.md +24 -0
- package/docs/reference/templates/TOOLS.md +47 -0
- package/docs/reference/templates/USER.dev.md +18 -0
- package/docs/reference/templates/USER.md +23 -0
- package/docs/reference/test.md +90 -0
- package/docs/reference/token-use.md +175 -0
- package/docs/reference/transcript-hygiene.md +151 -0
- package/docs/reference/wizard.md +235 -0
- package/docs/security/CONTRIBUTING-THREAT-MODEL.md +98 -0
- package/docs/security/THREAT-MODEL-ATLAS.md +611 -0
- package/docs/security/formal-verification.md +167 -0
- package/docs/start/bootstrapping.md +41 -0
- package/docs/start/docs-directory.md +66 -0
- package/docs/start/getting-started.md +116 -0
- package/docs/start/hubs.md +198 -0
- package/docs/start/lore.md +219 -0
- package/docs/start/onboarding-overview.md +67 -0
- package/docs/start/onboarding.md +91 -0
- package/docs/start/openclaw.md +216 -0
- package/docs/start/quickstart.md +22 -0
- package/docs/start/setup.md +164 -0
- package/docs/start/showcase.md +418 -0
- package/docs/start/wizard-cli-automation.md +215 -0
- package/docs/start/wizard-cli-reference.md +299 -0
- package/docs/start/wizard.md +125 -0
- package/docs/style.css +37 -0
- package/docs/tools/acp-agents.md +623 -0
- package/docs/tools/agent-send.md +100 -0
- package/docs/tools/apply-patch.md +51 -0
- package/docs/tools/brave-search.md +93 -0
- package/docs/tools/browser-linux-troubleshooting.md +138 -0
- package/docs/tools/browser-login.md +73 -0
- package/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md +211 -0
- package/docs/tools/browser.md +731 -0
- package/docs/tools/btw.md +142 -0
- package/docs/tools/capability-cookbook.md +119 -0
- package/docs/tools/clawhub.md +257 -0
- package/docs/tools/creating-skills.md +117 -0
- package/docs/tools/diffs.md +386 -0
- package/docs/tools/elevated.md +114 -0
- package/docs/tools/exec-approvals.md +400 -0
- package/docs/tools/exec.md +204 -0
- package/docs/tools/firecrawl.md +140 -0
- package/docs/tools/index.md +137 -0
- package/docs/tools/llm-task.md +119 -0
- package/docs/tools/lobster.md +340 -0
- package/docs/tools/loop-detection.md +100 -0
- package/docs/tools/multi-agent-sandbox-tools.md +364 -0
- package/docs/tools/pdf.md +156 -0
- package/docs/tools/perplexity-search.md +174 -0
- package/docs/tools/plugin.md +251 -0
- package/docs/tools/reactions.md +64 -0
- package/docs/tools/skills-config.md +86 -0
- package/docs/tools/skills.md +306 -0
- package/docs/tools/slash-commands.md +294 -0
- package/docs/tools/subagents.md +295 -0
- package/docs/tools/tavily.md +125 -0
- package/docs/tools/thinking.md +96 -0
- package/docs/tools/tts.md +406 -0
- package/docs/tools/web.md +516 -0
- package/docs/tts.md +406 -0
- package/docs/vps.md +112 -0
- package/docs/web/control-ui.md +275 -0
- package/docs/web/dashboard.md +54 -0
- package/docs/web/index.md +120 -0
- package/docs/web/tui.md +170 -0
- package/docs/web/webchat.md +61 -0
- package/docs/whatsapp-openclaw-ai-zh.jpg +0 -0
- package/docs/whatsapp-openclaw.jpg +0 -0
- package/docs/zh-CN/AGENTS.md +61 -0
- package/docs/zh-CN/automation/auth-monitoring.md +47 -0
- package/docs/zh-CN/automation/cron-jobs.md +435 -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/hooks.md +1051 -0
- package/docs/zh-CN/automation/poll.md +76 -0
- package/docs/zh-CN/automation/troubleshooting.md +8 -0
- package/docs/zh-CN/automation/webhook.md +163 -0
- package/docs/zh-CN/brave-search.md +60 -0
- package/docs/zh-CN/channels/bluebubbles.md +354 -0
- package/docs/zh-CN/channels/broadcast-groups.md +449 -0
- package/docs/zh-CN/channels/channel-routing.md +117 -0
- package/docs/zh-CN/channels/discord.md +468 -0
- package/docs/zh-CN/channels/feishu.md +728 -0
- package/docs/zh-CN/channels/googlechat.md +257 -0
- package/docs/zh-CN/channels/grammy.md +38 -0
- package/docs/zh-CN/channels/group-messages.md +91 -0
- package/docs/zh-CN/channels/groups.md +379 -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 +249 -0
- package/docs/zh-CN/channels/pairing.md +89 -0
- package/docs/zh-CN/channels/signal.md +209 -0
- package/docs/zh-CN/channels/slack.md +531 -0
- package/docs/zh-CN/channels/synology-chat.md +138 -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 +298 -0
- package/docs/zh-CN/cli/index.md +1143 -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 +164 -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/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/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 +606 -0
- package/docs/zh-CN/concepts/models.md +225 -0
- package/docs/zh-CN/concepts/multi-agent.md +372 -0
- package/docs/zh-CN/concepts/oauth.md +164 -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/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/diagnostics/flags.md +98 -0
- package/docs/zh-CN/gateway/authentication.md +184 -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-reference.md +3103 -0
- package/docs/zh-CN/gateway/configuration.md +640 -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 +159 -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/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/debugging.md +160 -0
- package/docs/zh-CN/help/environment.md +88 -0
- package/docs/zh-CN/help/faq.md +2640 -0
- package/docs/zh-CN/help/index.md +28 -0
- package/docs/zh-CN/help/scripts.md +35 -0
- package/docs/zh-CN/help/testing.md +375 -0
- package/docs/zh-CN/help/troubleshooting.md +104 -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 +133 -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 +235 -0
- package/docs/zh-CN/install/installer.md +422 -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 +8 -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/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/troubleshooting.md +8 -0
- package/docs/zh-CN/nodes/voicewake.md +72 -0
- package/docs/zh-CN/perplexity.md +102 -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 +273 -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 +100 -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/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 +416 -0
- package/docs/zh-CN/platforms/windows.md +247 -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 +265 -0
- package/docs/zh-CN/providers/bedrock.md +170 -0
- package/docs/zh-CN/providers/claude-max-api-proxy.md +155 -0
- package/docs/zh-CN/providers/cloudflare-ai-gateway.md +78 -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 +50 -0
- package/docs/zh-CN/providers/huggingface.md +216 -0
- package/docs/zh-CN/providers/index.md +69 -0
- package/docs/zh-CN/providers/kilocode.md +80 -0
- package/docs/zh-CN/providers/litellm.md +160 -0
- package/docs/zh-CN/providers/minimax.md +222 -0
- package/docs/zh-CN/providers/mistral.md +61 -0
- package/docs/zh-CN/providers/models.md +51 -0
- package/docs/zh-CN/providers/moonshot.md +182 -0
- package/docs/zh-CN/providers/nvidia.md +62 -0
- package/docs/zh-CN/providers/ollama.md +359 -0
- package/docs/zh-CN/providers/openai.md +308 -0
- package/docs/zh-CN/providers/opencode-go.md +52 -0
- package/docs/zh-CN/providers/opencode.md +71 -0
- package/docs/zh-CN/providers/openrouter.md +44 -0
- package/docs/zh-CN/providers/qianfan.md +45 -0
- package/docs/zh-CN/providers/qwen.md +55 -0
- package/docs/zh-CN/providers/sglang.md +111 -0
- package/docs/zh-CN/providers/synthetic.md +106 -0
- package/docs/zh-CN/providers/together.md +72 -0
- package/docs/zh-CN/providers/venice.md +289 -0
- package/docs/zh-CN/providers/vercel-ai-gateway.md +66 -0
- package/docs/zh-CN/providers/xiaomi.md +93 -0
- package/docs/zh-CN/providers/zai.md +53 -0
- package/docs/zh-CN/reference/AGENTS.default.md +131 -0
- package/docs/zh-CN/reference/RELEASING.md +48 -0
- package/docs/zh-CN/reference/api-usage-costs.md +141 -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 +36 -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/token-use.md +119 -0
- package/docs/zh-CN/reference/transcript-hygiene.md +109 -0
- package/docs/zh-CN/reference/wizard.md +242 -0
- package/docs/zh-CN/security/formal-verification.md +171 -0
- package/docs/zh-CN/start/bootstrapping.md +9 -0
- package/docs/zh-CN/start/docs-directory.md +70 -0
- package/docs/zh-CN/start/getting-started.md +143 -0
- package/docs/zh-CN/start/hubs.md +194 -0
- package/docs/zh-CN/start/lore.md +226 -0
- package/docs/zh-CN/start/onboarding-overview.md +58 -0
- package/docs/zh-CN/start/onboarding.md +105 -0
- package/docs/zh-CN/start/openclaw.md +248 -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-cli-automation.md +222 -0
- package/docs/zh-CN/start/wizard-cli-reference.md +306 -0
- package/docs/zh-CN/start/wizard.md +132 -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/multi-agent-sandbox-tools.md +401 -0
- package/docs/zh-CN/tools/plugin.md +1612 -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 +167 -0
- package/docs/zh-CN/tools/thinking.md +80 -0
- package/docs/zh-CN/tools/web.md +289 -0
- package/docs/zh-CN/tts.md +375 -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/tui.md +166 -0
- package/docs/zh-CN/web/webchat.md +56 -0
- package/openclaw.mjs +135 -0
- package/package.json +835 -0
- package/skills/1password/SKILL.md +70 -0
- package/skills/1password/references/cli-examples.md +29 -0
- package/skills/1password/references/get-started.md +17 -0
- package/skills/apple-notes/SKILL.md +77 -0
- package/skills/apple-reminders/SKILL.md +118 -0
- package/skills/bear-notes/SKILL.md +107 -0
- package/skills/blogwatcher/SKILL.md +69 -0
- package/skills/blucli/SKILL.md +47 -0
- package/skills/bluebubbles/SKILL.md +131 -0
- package/skills/camsnap/SKILL.md +45 -0
- package/skills/canvas/SKILL.md +198 -0
- package/skills/clawhub/SKILL.md +77 -0
- package/skills/coding-agent/SKILL.md +295 -0
- package/skills/discord/SKILL.md +197 -0
- package/skills/doubao-code/SKILL.md +43 -0
- package/skills/eightctl/SKILL.md +50 -0
- package/skills/gemini/SKILL.md +43 -0
- package/skills/gh-issues/SKILL.md +865 -0
- package/skills/gifgrep/SKILL.md +79 -0
- package/skills/github/SKILL.md +163 -0
- package/skills/gog/SKILL.md +116 -0
- package/skills/goplaces/SKILL.md +52 -0
- package/skills/healthcheck/SKILL.md +245 -0
- package/skills/himalaya/SKILL.md +257 -0
- package/skills/himalaya/references/configuration.md +184 -0
- package/skills/himalaya/references/message-composition.md +199 -0
- package/skills/imsg/SKILL.md +122 -0
- package/skills/kimi-code/SKILL.md +42 -0
- package/skills/mcporter/SKILL.md +61 -0
- package/skills/model-usage/SKILL.md +69 -0
- package/skills/model-usage/references/codexbar-cli.md +33 -0
- package/skills/model-usage/scripts/model_usage.py +320 -0
- package/skills/model-usage/scripts/test_model_usage.py +40 -0
- package/skills/nano-pdf/SKILL.md +38 -0
- package/skills/node-connect/SKILL.md +142 -0
- package/skills/notion/SKILL.md +174 -0
- package/skills/obsidian/SKILL.md +81 -0
- package/skills/openai-image-gen/SKILL.md +92 -0
- package/skills/openai-image-gen/scripts/gen.py +328 -0
- package/skills/openai-image-gen/scripts/test_gen.py +140 -0
- package/skills/openai-whisper/SKILL.md +38 -0
- package/skills/openai-whisper-api/SKILL.md +52 -0
- package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
- package/skills/openhue/SKILL.md +112 -0
- package/skills/oracle/SKILL.md +125 -0
- package/skills/ordercli/SKILL.md +78 -0
- package/skills/peekaboo/SKILL.md +190 -0
- package/skills/sag/SKILL.md +87 -0
- package/skills/session-logs/SKILL.md +115 -0
- package/skills/sherpa-onnx-tts/SKILL.md +103 -0
- package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
- package/skills/skill-creator/SKILL.md +372 -0
- package/skills/skill-creator/license.txt +202 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/package_skill.py +139 -0
- package/skills/skill-creator/scripts/quick_validate.py +159 -0
- package/skills/skill-creator/scripts/test_package_skill.py +160 -0
- package/skills/skill-creator/scripts/test_quick_validate.py +72 -0
- package/skills/slack/SKILL.md +144 -0
- package/skills/songsee/SKILL.md +49 -0
- package/skills/sonoscli/SKILL.md +65 -0
- package/skills/spotify-player/SKILL.md +64 -0
- package/skills/summarize/SKILL.md +87 -0
- package/skills/things-mac/SKILL.md +86 -0
- package/skills/tmux/SKILL.md +153 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/trello/SKILL.md +95 -0
- package/skills/video-frames/SKILL.md +46 -0
- package/skills/video-frames/scripts/frame.sh +81 -0
- package/skills/voice-call/SKILL.md +45 -0
- package/skills/wacli/SKILL.md +72 -0
- package/skills/weather/SKILL.md +112 -0
- package/skills/xurl/SKILL.md +461 -0
|
@@ -0,0 +1,4923 @@
|
|
|
1
|
+
import { a as logVerbose } from "./globals-41sdSaKv.js";
|
|
2
|
+
import { i as clampInt } from "./utils-Bxk6BLTg.js";
|
|
3
|
+
import { d as normalizeUsageDisplay, p as resolveResponseUsageMode, s as normalizeFastMode } from "./thinking.shared-CncvRHts.js";
|
|
4
|
+
import { S as parseAgentSessionKey, c as normalizeAgentId, u as resolveAgentIdFromSessionKey, v as isAcpSessionKey, x as isSubagentSessionKey } from "./session-key-gFFk3uv9.js";
|
|
5
|
+
import { n as normalizeAccountId, r as normalizeOptionalAccountId } from "./account-id-ZCrgXl7Z.js";
|
|
6
|
+
import { r as normalizeStringEntries } from "./string-normalization-CvzuCAZv.js";
|
|
7
|
+
import { v as resolveSessionAgentId } from "./agent-scope-DPP4Z_UU.js";
|
|
8
|
+
import { A as getConfigOverrides, F as parseConfigPath, I as setConfigValueAtPath, L as unsetConfigValueAtPath, M as setConfigOverride, N as unsetConfigOverride, P as getConfigValueAtPath, b as validateConfigObjectWithPlugins, d as readConfigFileSnapshot, g as writeConfigFile, j as resetConfigOverrides } from "./io-jOnQRia2.js";
|
|
9
|
+
import { o as normalizeChannelId } from "./registry-rgYi7KoO.js";
|
|
10
|
+
import { t as parseDurationMs } from "./parse-duration-BBGYkY0S.js";
|
|
11
|
+
import { a as isInternalMessageChannel, h as GATEWAY_CLIENT_NAMES, m as GATEWAY_CLIENT_MODES } from "./message-channel-Cy-gN4K2.js";
|
|
12
|
+
import { l as updateSessionStore, n as loadSessionStore } from "./store-CvL8MPei.js";
|
|
13
|
+
import { t as getChannelPlugin } from "./registry-C3q59Qj0.js";
|
|
14
|
+
import { i as resolveSessionFilePathOptions, l as resolveStorePath, r as resolveSessionFilePath } from "./paths-CTjJI9l0.js";
|
|
15
|
+
import { a as resolveFreshSessionTotalTokens } from "./types-BCKGVVld.js";
|
|
16
|
+
import { n as callGateway } from "./call-BmLt3xO1.js";
|
|
17
|
+
import { r as isRestartEnabled, t as isCommandFlagEnabled } from "./commands-Bb9xUwz9.js";
|
|
18
|
+
import { $d as setTtsEnabled, $r as extractBtwQuestion, Aa as buildDisabledCommandReply, Al as normalizeConversationText, Ap as resolveAcpThreadSessionDetailLines, Ba as handleStatusCommand, Bd as isTtsEnabled, Cf as killProcessTree, Cl as looksLikeSessionId, Ct as isTelegramExecApprovalClientEnabled, Dl as resolveEffectiveResetTargetSessionKey, El as parseDiscordParentChannelFromSessionKey, Fl as incrementCompactionCount, Fs as getFinishedSession, Gd as resolveTtsApiKey, Gm as parseExplicitTargetForChannel, Ia as handleCommandsListCommand, Id as getLastTtsAttempt, Il as stripToolResultDetails, Is as getSession, Jd as resolveTtsPrefsPath, La as handleContextCommand, Ld as getTtsMaxLength, Ls as markExited, Ma as rejectUnauthorizedCommand, Mp as resolveAcpAgentPolicyError, Na as requireCommandFlagEnabled, Nf as resolveSendPolicy, Np as resolveAcpDispatchPolicyError, Oa as handleModelsCommand, Ol as resolveConversationIdFromTargets, Op as resolveAcpSessionCwd, Pa as requireGatewayClientScopeForInternalChannel, Pp as resolveAcpDispatchPolicyMessage, Ps as createExecTool, Ra as handleExportSessionCommand, Rd as getTtsProvider, Rl as clearSessionQueues, Rm as executePluginCommand, Sl as SESSION_ID_RE, St as isTelegramExecApprovalApprover, Us as formatContextUsageShort, Va as handleWhoamiCommand, Vd as isTtsProviderConfigured, Vm as matchPluginCommand, Ws as formatTokenCount, Xd as setLastTtsAttempt, Yl as formatElevatedUnavailableMessage, Zd as setSummarizationEnabled, Zl as parseActivationCommand, a as isEmbeddedPiRunActive, af as listSpeechProviders, am as EmbeddedBlockChunker, as as triggerOpenClawRestart, bl as resolveInternalSessionKey, c as compactEmbeddedPiSession, cd as formatUsd, co as formatDurationCompact, do as resolveSubagentTargetFromRuns, ef as setTtsMaxLength, el as formatAcpRuntimeErrorText, fl as listSubagentRunsForController, fm as ensureOpenClawModelsJson, fo as sortSubagentRuns, go as cleanupFailedAcpSpawn, ho as resolveAcpSpawnRuntimePolicyError, i as getActiveEmbeddedRunSnapshot, il as stopSubagentsForRequester, im as extractTextFromChatContent, ja as rejectNonOwnerCommand, jl as parseTelegramChatIdFromTarget, jp as isAcpEnabledByPolicy, kl as buildTelegramTopicConversationId, km as getGlobalHookRunner, kp as resolveAcpSessionIdentifierLinesFromIdentity, lo as formatRunLabel, mo as resolveStoredSubagentCapabilities, n as abortEmbeddedPiRun, nl as formatAbortReplyText, no as killAllControlledSubagentRuns, ns as scheduleGatewaySigusr1Restart, of as normalizeSpeechProviderId, ol as countPendingDescendantRuns, oo as sendControlledSubagentMessage, po as spawnSubagentDirect, qd as resolveTtsConfig, rf as textToSpeech, rl as resolveSessionEntryForKey, ro as killControlledSubagentRun, s as waitForEmbeddedPiRunEnd, sd as formatTokenCount$1, so as steerControlledSubagentRun, tf as setTtsProvider, tl as toAcpRuntimeErrorText, to as buildSubagentList, u as mapThinkingLevel, uo as formatRunStatus, vf as resolveModelWithRegistry, vl as sanitizeTextContent, xl as resolveMainSessionAlias, xp as resolveFastModeState, yl as stripToolMessages, za as handleHelpCommand, zd as isSummarizationEnabled, zl as routeReply } from "./pi-embedded-CSQySvOV.js";
|
|
19
|
+
import { n as getApiKeyForModel, o as requireApiKey } from "./model-auth-B__TJTPw.js";
|
|
20
|
+
import { t as diag } from "./diagnostic-DqJXx_4Q.js";
|
|
21
|
+
import { i as resolveThreadBindingThreadName, o as getSessionBindingService, r as resolveThreadBindingIntroText, t as formatThreadBindingDurationLabel } from "./thread-bindings-messages-Cdo8jSa9.js";
|
|
22
|
+
import { c as resetConfiguredBindingTargetInPlace } from "./conversation-runtime-1O0Aaolb.js";
|
|
23
|
+
import { d as triggerInternalHook, n as createInternalHookEvent } from "./internal-hooks-0uipqzRY.js";
|
|
24
|
+
import { P as resolveSandboxRuntimeStatus } from "./sandbox-CUUouiKs.js";
|
|
25
|
+
import { r as setPluginEnabledInConfig } from "./provider-web-search-NzK8ep1E.js";
|
|
26
|
+
import { b as toAcpRuntimeError, c as parseRuntimeTimeoutSecondsInput, d as validateRuntimeModeInput, f as validateRuntimeModelInput, h as resolveAcpSessionResolutionError, i as getAcpRuntimeBackend, l as validateRuntimeConfigOptionInput, n as readAcpSessionEntry, o as requireAcpRuntimeBackend, p as validateRuntimePermissionProfileInput, r as resolveSessionStorePathForAcp, t as getAcpSessionManager, u as validateRuntimeCwdInput } from "./manager-ChTGDe87.js";
|
|
27
|
+
import { _ as authorizeConfigWrite, v as canBypassConfigWritePolicy, x as resolveConfigWriteTargetFromPath, y as formatConfigWriteDeniedMessage } from "./channel-config-helpers-CieQWILI.js";
|
|
28
|
+
import { a as readChannelAllowFromStore, l as removeChannelAllowFromStoreEntry, t as addChannelAllowFromStoreEntry } from "./pairing-store-C4lsd4pO.js";
|
|
29
|
+
import { d as resolveSessionAuthProfileOverride } from "./model-selection-Ci9cPkL2.js";
|
|
30
|
+
import { r as enqueueSystemEvent } from "./system-events-mAu6Ap6K.js";
|
|
31
|
+
import { i as discoverModels, r as discoverAuthStorage } from "./pi-model-discovery-CuX5CDyZ.js";
|
|
32
|
+
import { o as stripMentions, s as stripStructuralPrefixes } from "./mentions-Bxys_va0.js";
|
|
33
|
+
import { a as shouldPersistAbortCutoff, i as resolveAbortCutoffFromContext, t as applyAbortCutoffToSessionEntry } from "./abort-cutoff-CERmtgZI.js";
|
|
34
|
+
import { p as normalizeCommandBody, y as shouldHandleTextCommands } from "./commands-registry-ChCep1KJ.js";
|
|
35
|
+
import { v as isAbortTrigger, y as setAbortMemory } from "./channel-inbound-DwzVf2PK.js";
|
|
36
|
+
import { n as formatTimeAgo } from "./format-relative-C3nDxnXz.js";
|
|
37
|
+
import { d as resolveThreadBindingSpawnPolicy, i as formatThreadBindingSpawnDisabledError, r as formatThreadBindingDisabledError, s as resolveThreadBindingIdleTimeoutMsForChannel, u as resolveThreadBindingMaxAgeMsForChannel } from "./thread-bindings-policy-DMjOaNyR.js";
|
|
38
|
+
import { i as unsetConfiguredMcpServer, r as setConfiguredMcpServer, t as listConfiguredMcpServers } from "./mcp-config-Coky4zS4.js";
|
|
39
|
+
import { n as createPluginRuntime } from "./runtime-C9VaVKYZ.js";
|
|
40
|
+
import { t as parseConfigValue } from "./config-value-DgJrpclm.js";
|
|
41
|
+
import { a as buildPluginStatusReport, i as buildPluginInspectReport, o as formatPluginCompatibilityNotice, t as buildAllPluginInspectReports } from "./status-CoUFSBgt.js";
|
|
42
|
+
import { n as loadCostUsageSummary, r as loadSessionCostSummary } from "./session-cost-usage-C30Jl2SI.js";
|
|
43
|
+
import { existsSync } from "node:fs";
|
|
44
|
+
import path from "node:path";
|
|
45
|
+
import fs$1 from "node:fs/promises";
|
|
46
|
+
import { randomUUID } from "node:crypto";
|
|
47
|
+
import { SessionManager } from "@mariozechner/pi-coding-agent";
|
|
48
|
+
import { streamSimple } from "@mariozechner/pi-ai";
|
|
49
|
+
//#region src/auto-reply/reply/matrix-context.ts
|
|
50
|
+
function normalizeMatrixTarget(value) {
|
|
51
|
+
return typeof value === "string" ? value.trim() : "";
|
|
52
|
+
}
|
|
53
|
+
function resolveMatrixRoomIdFromTarget(raw) {
|
|
54
|
+
let target = normalizeMatrixTarget(raw);
|
|
55
|
+
if (!target) return;
|
|
56
|
+
if (target.toLowerCase().startsWith("matrix:")) target = target.slice(7).trim();
|
|
57
|
+
if (/^(room|channel):/i.test(target)) return target.replace(/^(room|channel):/i, "").trim() || void 0;
|
|
58
|
+
if (target.startsWith("!") || target.startsWith("#")) return target;
|
|
59
|
+
}
|
|
60
|
+
function resolveMatrixParentConversationId(params) {
|
|
61
|
+
const targets = [
|
|
62
|
+
params.ctx.OriginatingTo,
|
|
63
|
+
params.command.to,
|
|
64
|
+
params.ctx.To
|
|
65
|
+
];
|
|
66
|
+
for (const candidate of targets) {
|
|
67
|
+
const roomId = resolveMatrixRoomIdFromTarget(candidate ?? "");
|
|
68
|
+
if (roomId) return roomId;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function resolveMatrixConversationId(params) {
|
|
72
|
+
const threadId = params.ctx.MessageThreadId != null ? String(params.ctx.MessageThreadId).trim() : "";
|
|
73
|
+
if (threadId) return threadId;
|
|
74
|
+
return resolveMatrixParentConversationId(params);
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/auto-reply/reply/telegram-context.ts
|
|
78
|
+
function resolveTelegramConversationId(params) {
|
|
79
|
+
const threadId = (params.ctx.MessageThreadId != null ? String(params.ctx.MessageThreadId).trim() : "") || void 0;
|
|
80
|
+
const chatId = [
|
|
81
|
+
typeof params.ctx.OriginatingTo === "string" ? params.ctx.OriginatingTo : "",
|
|
82
|
+
typeof params.command.to === "string" ? params.command.to : "",
|
|
83
|
+
typeof params.ctx.To === "string" ? params.ctx.To : ""
|
|
84
|
+
].map((value) => value.trim()).filter(Boolean).map((candidate) => parseExplicitTargetForChannel("telegram", candidate)?.to.trim() ?? "").find((candidate) => candidate.length > 0);
|
|
85
|
+
if (!chatId) return;
|
|
86
|
+
if (threadId) return `${chatId}:topic:${threadId}`;
|
|
87
|
+
if (chatId.startsWith("-")) return;
|
|
88
|
+
return chatId;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/auto-reply/reply/commands-acp/context.ts
|
|
92
|
+
function buildFeishuConversationId(params) {
|
|
93
|
+
const chatId = normalizeConversationText(params.chatId) ?? "unknown";
|
|
94
|
+
const senderOpenId = normalizeConversationText(params.senderOpenId);
|
|
95
|
+
const topicId = normalizeConversationText(params.topicId);
|
|
96
|
+
switch (params.scope) {
|
|
97
|
+
case "group_sender": return senderOpenId ? `${chatId}:sender:${senderOpenId}` : chatId;
|
|
98
|
+
case "group_topic": return topicId ? `${chatId}:topic:${topicId}` : chatId;
|
|
99
|
+
case "group_topic_sender":
|
|
100
|
+
if (topicId && senderOpenId) return `${chatId}:topic:${topicId}:sender:${senderOpenId}`;
|
|
101
|
+
if (topicId) return `${chatId}:topic:${topicId}`;
|
|
102
|
+
return senderOpenId ? `${chatId}:sender:${senderOpenId}` : chatId;
|
|
103
|
+
default: return chatId;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function parseFeishuTargetId(raw) {
|
|
107
|
+
const target = normalizeConversationText(raw);
|
|
108
|
+
if (!target) return;
|
|
109
|
+
const withoutProvider = target.replace(/^(feishu|lark):/i, "").trim();
|
|
110
|
+
if (!withoutProvider) return;
|
|
111
|
+
const lowered = withoutProvider.toLowerCase();
|
|
112
|
+
for (const prefix of [
|
|
113
|
+
"chat:",
|
|
114
|
+
"group:",
|
|
115
|
+
"channel:",
|
|
116
|
+
"user:",
|
|
117
|
+
"dm:",
|
|
118
|
+
"open_id:"
|
|
119
|
+
]) if (lowered.startsWith(prefix)) return normalizeConversationText(withoutProvider.slice(prefix.length));
|
|
120
|
+
return withoutProvider;
|
|
121
|
+
}
|
|
122
|
+
function parseFeishuDirectConversationId(raw) {
|
|
123
|
+
const target = normalizeConversationText(raw);
|
|
124
|
+
if (!target) return;
|
|
125
|
+
const withoutProvider = target.replace(/^(feishu|lark):/i, "").trim();
|
|
126
|
+
if (!withoutProvider) return;
|
|
127
|
+
const lowered = withoutProvider.toLowerCase();
|
|
128
|
+
for (const prefix of [
|
|
129
|
+
"user:",
|
|
130
|
+
"dm:",
|
|
131
|
+
"open_id:"
|
|
132
|
+
]) if (lowered.startsWith(prefix)) return normalizeConversationText(withoutProvider.slice(prefix.length));
|
|
133
|
+
const id = parseFeishuTargetId(target);
|
|
134
|
+
if (!id) return;
|
|
135
|
+
if (id.startsWith("ou_") || id.startsWith("on_")) return id;
|
|
136
|
+
}
|
|
137
|
+
function resolveFeishuSenderScopedConversationId(params) {
|
|
138
|
+
const parentConversationId = normalizeConversationText(params.parentConversationId);
|
|
139
|
+
const threadId = normalizeConversationText(params.threadId);
|
|
140
|
+
const senderId = normalizeConversationText(params.senderId);
|
|
141
|
+
const expectedScopePrefix = `feishu:group:${parentConversationId?.toLowerCase()}:topic:${threadId?.toLowerCase()}:sender:`;
|
|
142
|
+
const isSenderScopedSession = [params.sessionKey, params.parentSessionKey].some((candidate) => {
|
|
143
|
+
const scopedRest = parseAgentSessionKey(candidate)?.rest?.trim().toLowerCase() ?? "";
|
|
144
|
+
return Boolean(scopedRest && expectedScopePrefix && scopedRest.startsWith(expectedScopePrefix));
|
|
145
|
+
});
|
|
146
|
+
if (!parentConversationId || !threadId || !senderId) return;
|
|
147
|
+
if (!isSenderScopedSession && params.sessionKey?.trim()) {
|
|
148
|
+
const boundConversation = getSessionBindingService().listBySession(params.sessionKey).find((binding) => {
|
|
149
|
+
if (binding.conversation.channel !== "feishu" || binding.conversation.accountId !== params.accountId) return false;
|
|
150
|
+
return binding.conversation.conversationId === buildFeishuConversationId({
|
|
151
|
+
chatId: parentConversationId,
|
|
152
|
+
scope: "group_topic_sender",
|
|
153
|
+
topicId: threadId,
|
|
154
|
+
senderOpenId: senderId
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
if (boundConversation) return boundConversation.conversation.conversationId;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
return buildFeishuConversationId({
|
|
161
|
+
chatId: parentConversationId,
|
|
162
|
+
scope: "group_topic_sender",
|
|
163
|
+
topicId: threadId,
|
|
164
|
+
senderOpenId: senderId
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function resolveAcpCommandChannel(params) {
|
|
168
|
+
return normalizeConversationText(params.ctx.OriginatingChannel ?? params.command.channel ?? params.ctx.Surface ?? params.ctx.Provider).toLowerCase();
|
|
169
|
+
}
|
|
170
|
+
function resolveAcpCommandAccountId(params) {
|
|
171
|
+
return normalizeConversationText(params.ctx.AccountId) || "default";
|
|
172
|
+
}
|
|
173
|
+
function resolveAcpCommandThreadId(params) {
|
|
174
|
+
return (params.ctx.MessageThreadId != null ? normalizeConversationText(String(params.ctx.MessageThreadId)) : "") || void 0;
|
|
175
|
+
}
|
|
176
|
+
function resolveAcpCommandConversationId(params) {
|
|
177
|
+
const channel = resolveAcpCommandChannel(params);
|
|
178
|
+
if (channel === "matrix") return resolveMatrixConversationId({
|
|
179
|
+
ctx: {
|
|
180
|
+
MessageThreadId: params.ctx.MessageThreadId,
|
|
181
|
+
OriginatingTo: params.ctx.OriginatingTo,
|
|
182
|
+
To: params.ctx.To
|
|
183
|
+
},
|
|
184
|
+
command: { to: params.command.to }
|
|
185
|
+
});
|
|
186
|
+
if (channel === "telegram") {
|
|
187
|
+
const telegramConversationId = resolveTelegramConversationId({
|
|
188
|
+
ctx: {
|
|
189
|
+
MessageThreadId: params.ctx.MessageThreadId,
|
|
190
|
+
OriginatingTo: params.ctx.OriginatingTo,
|
|
191
|
+
To: params.ctx.To
|
|
192
|
+
},
|
|
193
|
+
command: { to: params.command.to }
|
|
194
|
+
});
|
|
195
|
+
if (telegramConversationId) return telegramConversationId;
|
|
196
|
+
const threadId = resolveAcpCommandThreadId(params);
|
|
197
|
+
const parentConversationId = resolveAcpCommandParentConversationId(params);
|
|
198
|
+
if (threadId && parentConversationId) return buildTelegramTopicConversationId({
|
|
199
|
+
chatId: parentConversationId,
|
|
200
|
+
topicId: threadId
|
|
201
|
+
}) ?? threadId;
|
|
202
|
+
}
|
|
203
|
+
if (channel === "feishu") {
|
|
204
|
+
const threadId = resolveAcpCommandThreadId(params);
|
|
205
|
+
const parentConversationId = resolveAcpCommandParentConversationId(params);
|
|
206
|
+
if (threadId && parentConversationId) return resolveFeishuSenderScopedConversationId({
|
|
207
|
+
accountId: resolveAcpCommandAccountId(params),
|
|
208
|
+
parentConversationId,
|
|
209
|
+
threadId,
|
|
210
|
+
senderId: params.command.senderId ?? params.ctx.SenderId,
|
|
211
|
+
sessionKey: params.sessionKey,
|
|
212
|
+
parentSessionKey: params.ctx.ParentSessionKey
|
|
213
|
+
}) ?? buildFeishuConversationId({
|
|
214
|
+
chatId: parentConversationId,
|
|
215
|
+
scope: "group_topic",
|
|
216
|
+
topicId: threadId
|
|
217
|
+
});
|
|
218
|
+
return parseFeishuDirectConversationId(params.ctx.OriginatingTo) ?? parseFeishuDirectConversationId(params.command.to) ?? parseFeishuDirectConversationId(params.ctx.To);
|
|
219
|
+
}
|
|
220
|
+
return resolveConversationIdFromTargets({
|
|
221
|
+
threadId: params.ctx.MessageThreadId,
|
|
222
|
+
targets: [
|
|
223
|
+
params.ctx.OriginatingTo,
|
|
224
|
+
params.command.to,
|
|
225
|
+
params.ctx.To
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
function parseDiscordParentChannelFromContext(raw) {
|
|
230
|
+
const parentId = normalizeConversationText(raw);
|
|
231
|
+
if (!parentId) return;
|
|
232
|
+
return parentId;
|
|
233
|
+
}
|
|
234
|
+
function resolveAcpCommandParentConversationId(params) {
|
|
235
|
+
const channel = resolveAcpCommandChannel(params);
|
|
236
|
+
if (channel === "matrix") return resolveMatrixParentConversationId({
|
|
237
|
+
ctx: {
|
|
238
|
+
MessageThreadId: params.ctx.MessageThreadId,
|
|
239
|
+
OriginatingTo: params.ctx.OriginatingTo,
|
|
240
|
+
To: params.ctx.To
|
|
241
|
+
},
|
|
242
|
+
command: { to: params.command.to }
|
|
243
|
+
});
|
|
244
|
+
if (channel === "telegram") return parseTelegramChatIdFromTarget(params.ctx.OriginatingTo) ?? parseTelegramChatIdFromTarget(params.command.to) ?? parseTelegramChatIdFromTarget(params.ctx.To);
|
|
245
|
+
if (channel === "feishu") {
|
|
246
|
+
if (!resolveAcpCommandThreadId(params)) return;
|
|
247
|
+
return parseFeishuTargetId(params.ctx.OriginatingTo) ?? parseFeishuTargetId(params.command.to) ?? parseFeishuTargetId(params.ctx.To);
|
|
248
|
+
}
|
|
249
|
+
if (channel === "discord") {
|
|
250
|
+
const threadId = resolveAcpCommandThreadId(params);
|
|
251
|
+
if (!threadId) return;
|
|
252
|
+
const fromContext = parseDiscordParentChannelFromContext(params.ctx.ThreadParentId);
|
|
253
|
+
if (fromContext && fromContext !== threadId) return fromContext;
|
|
254
|
+
const fromParentSession = parseDiscordParentChannelFromSessionKey(params.ctx.ParentSessionKey);
|
|
255
|
+
if (fromParentSession && fromParentSession !== threadId) return fromParentSession;
|
|
256
|
+
const fromTargets = resolveConversationIdFromTargets({ targets: [
|
|
257
|
+
params.ctx.OriginatingTo,
|
|
258
|
+
params.command.to,
|
|
259
|
+
params.ctx.To
|
|
260
|
+
] });
|
|
261
|
+
if (fromTargets && fromTargets !== threadId) return fromTargets;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function resolveAcpCommandBindingContext(params) {
|
|
265
|
+
const parentConversationId = resolveAcpCommandParentConversationId(params);
|
|
266
|
+
return {
|
|
267
|
+
channel: resolveAcpCommandChannel(params),
|
|
268
|
+
accountId: resolveAcpCommandAccountId(params),
|
|
269
|
+
threadId: resolveAcpCommandThreadId(params),
|
|
270
|
+
conversationId: resolveAcpCommandConversationId(params),
|
|
271
|
+
...parentConversationId ? { parentConversationId } : {}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
//#endregion
|
|
275
|
+
//#region src/auto-reply/reply/commands-acp/install-hints.ts
|
|
276
|
+
function resolveConfiguredAcpBackendId(cfg) {
|
|
277
|
+
return cfg.acp?.backend?.trim() || "acpx";
|
|
278
|
+
}
|
|
279
|
+
function resolveAcpInstallCommandHint(cfg) {
|
|
280
|
+
const configured = cfg.acp?.runtime?.installCommand?.trim();
|
|
281
|
+
if (configured) return configured;
|
|
282
|
+
const backendId = resolveConfiguredAcpBackendId(cfg).toLowerCase();
|
|
283
|
+
if (backendId === "acpx") {
|
|
284
|
+
const localPath = path.resolve(process.cwd(), "extensions/acpx");
|
|
285
|
+
if (existsSync(localPath)) return `openclaw plugins install ${localPath}`;
|
|
286
|
+
return "openclaw plugins install acpx";
|
|
287
|
+
}
|
|
288
|
+
return `Install and enable the plugin that provides ACP backend "${backendId}".`;
|
|
289
|
+
}
|
|
290
|
+
const ACP_SPAWN_USAGE = "Usage: /acp spawn [harness-id] [--mode persistent|oneshot] [--thread auto|here|off] [--cwd <path>] [--label <label>].";
|
|
291
|
+
const ACP_STEER_USAGE = "Usage: /acp steer [--session <session-key|session-id|session-label>] <instruction>";
|
|
292
|
+
const ACP_SET_MODE_USAGE = "Usage: /acp set-mode <mode> [session-key|session-id|session-label]";
|
|
293
|
+
const ACP_SET_USAGE = "Usage: /acp set <key> <value> [session-key|session-id|session-label]";
|
|
294
|
+
const ACP_CWD_USAGE = "Usage: /acp cwd <path> [session-key|session-id|session-label]";
|
|
295
|
+
const ACP_PERMISSIONS_USAGE = "Usage: /acp permissions <profile> [session-key|session-id|session-label]";
|
|
296
|
+
const ACP_TIMEOUT_USAGE = "Usage: /acp timeout <seconds> [session-key|session-id|session-label]";
|
|
297
|
+
const ACP_MODEL_USAGE = "Usage: /acp model <model-id> [session-key|session-id|session-label]";
|
|
298
|
+
const ACP_RESET_OPTIONS_USAGE = "Usage: /acp reset-options [session-key|session-id|session-label]";
|
|
299
|
+
const ACP_STATUS_USAGE = "Usage: /acp status [session-key|session-id|session-label]";
|
|
300
|
+
const ACP_INSTALL_USAGE = "Usage: /acp install";
|
|
301
|
+
const ACP_DOCTOR_USAGE = "Usage: /acp doctor";
|
|
302
|
+
const ACP_SESSIONS_USAGE = "Usage: /acp sessions";
|
|
303
|
+
const ACP_UNICODE_DASH_PREFIX_RE = /^[\u2010\u2011\u2012\u2013\u2014\u2015\u2212\uFE58\uFE63\uFF0D]+/;
|
|
304
|
+
function stopWithText$1(text) {
|
|
305
|
+
return {
|
|
306
|
+
shouldContinue: false,
|
|
307
|
+
reply: { text }
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function resolveAcpAction(tokens) {
|
|
311
|
+
const action = tokens[0]?.trim().toLowerCase();
|
|
312
|
+
if (action === "spawn" || action === "cancel" || action === "steer" || action === "close" || action === "sessions" || action === "status" || action === "set-mode" || action === "set" || action === "cwd" || action === "permissions" || action === "timeout" || action === "model" || action === "reset-options" || action === "doctor" || action === "install" || action === "help") {
|
|
313
|
+
tokens.shift();
|
|
314
|
+
return action;
|
|
315
|
+
}
|
|
316
|
+
return "help";
|
|
317
|
+
}
|
|
318
|
+
function readOptionValue(params) {
|
|
319
|
+
const token = normalizeAcpOptionToken(params.tokens[params.index] ?? "");
|
|
320
|
+
if (token === params.flag) {
|
|
321
|
+
const nextValue = normalizeAcpOptionToken(params.tokens[params.index + 1] ?? "");
|
|
322
|
+
if (!nextValue || nextValue.startsWith("--")) return {
|
|
323
|
+
matched: true,
|
|
324
|
+
nextIndex: params.index + 1,
|
|
325
|
+
error: `${params.flag} requires a value`
|
|
326
|
+
};
|
|
327
|
+
return {
|
|
328
|
+
matched: true,
|
|
329
|
+
value: nextValue,
|
|
330
|
+
nextIndex: params.index + 2
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
if (token.startsWith(`${params.flag}=`)) {
|
|
334
|
+
const value = token.slice(`${params.flag}=`.length).trim();
|
|
335
|
+
if (!value) return {
|
|
336
|
+
matched: true,
|
|
337
|
+
nextIndex: params.index + 1,
|
|
338
|
+
error: `${params.flag} requires a value`
|
|
339
|
+
};
|
|
340
|
+
return {
|
|
341
|
+
matched: true,
|
|
342
|
+
value,
|
|
343
|
+
nextIndex: params.index + 1
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return { matched: false };
|
|
347
|
+
}
|
|
348
|
+
function normalizeAcpOptionToken(raw) {
|
|
349
|
+
const token = raw.trim();
|
|
350
|
+
if (!token || token.startsWith("--")) return token;
|
|
351
|
+
const dashPrefix = token.match(ACP_UNICODE_DASH_PREFIX_RE)?.[0];
|
|
352
|
+
if (!dashPrefix) return token;
|
|
353
|
+
return `--${token.slice(dashPrefix.length)}`;
|
|
354
|
+
}
|
|
355
|
+
function resolveDefaultSpawnThreadMode(params) {
|
|
356
|
+
const channel = resolveAcpCommandChannel(params);
|
|
357
|
+
if (channel !== "discord" && channel !== "matrix") return "off";
|
|
358
|
+
return resolveAcpCommandThreadId(params) ? "here" : "auto";
|
|
359
|
+
}
|
|
360
|
+
function parseSpawnInput(params, tokens) {
|
|
361
|
+
const normalizedTokens = tokens.map((token) => normalizeAcpOptionToken(token));
|
|
362
|
+
let mode = "persistent";
|
|
363
|
+
let thread = resolveDefaultSpawnThreadMode(params);
|
|
364
|
+
let cwd;
|
|
365
|
+
let label;
|
|
366
|
+
let rawAgentId;
|
|
367
|
+
for (let i = 0; i < normalizedTokens.length;) {
|
|
368
|
+
const token = normalizedTokens[i] ?? "";
|
|
369
|
+
const modeOption = readOptionValue({
|
|
370
|
+
tokens: normalizedTokens,
|
|
371
|
+
index: i,
|
|
372
|
+
flag: "--mode"
|
|
373
|
+
});
|
|
374
|
+
if (modeOption.matched) {
|
|
375
|
+
if (modeOption.error) return {
|
|
376
|
+
ok: false,
|
|
377
|
+
error: `${modeOption.error}. ${ACP_SPAWN_USAGE}`
|
|
378
|
+
};
|
|
379
|
+
const raw = modeOption.value?.trim().toLowerCase();
|
|
380
|
+
if (raw !== "persistent" && raw !== "oneshot") return {
|
|
381
|
+
ok: false,
|
|
382
|
+
error: `Invalid --mode value "${modeOption.value}". Use persistent or oneshot.`
|
|
383
|
+
};
|
|
384
|
+
mode = raw;
|
|
385
|
+
i = modeOption.nextIndex;
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
const threadOption = readOptionValue({
|
|
389
|
+
tokens: normalizedTokens,
|
|
390
|
+
index: i,
|
|
391
|
+
flag: "--thread"
|
|
392
|
+
});
|
|
393
|
+
if (threadOption.matched) {
|
|
394
|
+
if (threadOption.error) return {
|
|
395
|
+
ok: false,
|
|
396
|
+
error: `${threadOption.error}. ${ACP_SPAWN_USAGE}`
|
|
397
|
+
};
|
|
398
|
+
const raw = threadOption.value?.trim().toLowerCase();
|
|
399
|
+
if (raw !== "auto" && raw !== "here" && raw !== "off") return {
|
|
400
|
+
ok: false,
|
|
401
|
+
error: `Invalid --thread value "${threadOption.value}". Use auto, here, or off.`
|
|
402
|
+
};
|
|
403
|
+
thread = raw;
|
|
404
|
+
i = threadOption.nextIndex;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const cwdOption = readOptionValue({
|
|
408
|
+
tokens: normalizedTokens,
|
|
409
|
+
index: i,
|
|
410
|
+
flag: "--cwd"
|
|
411
|
+
});
|
|
412
|
+
if (cwdOption.matched) {
|
|
413
|
+
if (cwdOption.error) return {
|
|
414
|
+
ok: false,
|
|
415
|
+
error: `${cwdOption.error}. ${ACP_SPAWN_USAGE}`
|
|
416
|
+
};
|
|
417
|
+
cwd = cwdOption.value?.trim();
|
|
418
|
+
i = cwdOption.nextIndex;
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
const labelOption = readOptionValue({
|
|
422
|
+
tokens: normalizedTokens,
|
|
423
|
+
index: i,
|
|
424
|
+
flag: "--label"
|
|
425
|
+
});
|
|
426
|
+
if (labelOption.matched) {
|
|
427
|
+
if (labelOption.error) return {
|
|
428
|
+
ok: false,
|
|
429
|
+
error: `${labelOption.error}. ${ACP_SPAWN_USAGE}`
|
|
430
|
+
};
|
|
431
|
+
label = labelOption.value?.trim();
|
|
432
|
+
i = labelOption.nextIndex;
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
if (token.startsWith("--")) return {
|
|
436
|
+
ok: false,
|
|
437
|
+
error: `Unknown option: ${token}. ${ACP_SPAWN_USAGE}`
|
|
438
|
+
};
|
|
439
|
+
if (!rawAgentId) {
|
|
440
|
+
rawAgentId = token.trim();
|
|
441
|
+
i += 1;
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
ok: false,
|
|
446
|
+
error: `Unexpected argument: ${token}. ${ACP_SPAWN_USAGE}`
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
const fallbackAgent = params.cfg.acp?.defaultAgent?.trim() || "";
|
|
450
|
+
const selectedAgent = (rawAgentId?.trim() || fallbackAgent).trim();
|
|
451
|
+
if (!selectedAgent) return {
|
|
452
|
+
ok: false,
|
|
453
|
+
error: `ACP target harness id is required. Pass an ACP harness id (for example codex) or configure acp.defaultAgent. ${ACP_SPAWN_USAGE}`
|
|
454
|
+
};
|
|
455
|
+
return {
|
|
456
|
+
ok: true,
|
|
457
|
+
value: {
|
|
458
|
+
agentId: normalizeAgentId(selectedAgent),
|
|
459
|
+
mode,
|
|
460
|
+
thread,
|
|
461
|
+
cwd,
|
|
462
|
+
label: label || void 0
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function parseSteerInput(tokens) {
|
|
467
|
+
const normalizedTokens = tokens.map((token) => normalizeAcpOptionToken(token));
|
|
468
|
+
let sessionToken;
|
|
469
|
+
const instructionTokens = [];
|
|
470
|
+
for (let i = 0; i < normalizedTokens.length;) {
|
|
471
|
+
const sessionOption = readOptionValue({
|
|
472
|
+
tokens: normalizedTokens,
|
|
473
|
+
index: i,
|
|
474
|
+
flag: "--session"
|
|
475
|
+
});
|
|
476
|
+
if (sessionOption.matched) {
|
|
477
|
+
if (sessionOption.error) return {
|
|
478
|
+
ok: false,
|
|
479
|
+
error: `${sessionOption.error}. ${ACP_STEER_USAGE}`
|
|
480
|
+
};
|
|
481
|
+
sessionToken = sessionOption.value?.trim() || void 0;
|
|
482
|
+
i = sessionOption.nextIndex;
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
instructionTokens.push(tokens[i] ?? "");
|
|
486
|
+
i += 1;
|
|
487
|
+
}
|
|
488
|
+
const instruction = instructionTokens.join(" ").trim();
|
|
489
|
+
if (!instruction) return {
|
|
490
|
+
ok: false,
|
|
491
|
+
error: ACP_STEER_USAGE
|
|
492
|
+
};
|
|
493
|
+
return {
|
|
494
|
+
ok: true,
|
|
495
|
+
value: {
|
|
496
|
+
sessionToken,
|
|
497
|
+
instruction
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function parseSingleValueCommandInput(tokens, usage) {
|
|
502
|
+
const value = tokens[0]?.trim() || "";
|
|
503
|
+
if (!value) return {
|
|
504
|
+
ok: false,
|
|
505
|
+
error: usage
|
|
506
|
+
};
|
|
507
|
+
if (tokens.length > 2) return {
|
|
508
|
+
ok: false,
|
|
509
|
+
error: usage
|
|
510
|
+
};
|
|
511
|
+
return {
|
|
512
|
+
ok: true,
|
|
513
|
+
value: {
|
|
514
|
+
value,
|
|
515
|
+
sessionToken: tokens[1]?.trim() || void 0
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function parseSetCommandInput(tokens) {
|
|
520
|
+
const key = tokens[0]?.trim() || "";
|
|
521
|
+
const value = tokens[1]?.trim() || "";
|
|
522
|
+
if (!key || !value) return {
|
|
523
|
+
ok: false,
|
|
524
|
+
error: ACP_SET_USAGE
|
|
525
|
+
};
|
|
526
|
+
if (tokens.length > 3) return {
|
|
527
|
+
ok: false,
|
|
528
|
+
error: ACP_SET_USAGE
|
|
529
|
+
};
|
|
530
|
+
return {
|
|
531
|
+
ok: true,
|
|
532
|
+
value: {
|
|
533
|
+
key,
|
|
534
|
+
value,
|
|
535
|
+
sessionToken: tokens[2]?.trim() || void 0
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function parseOptionalSingleTarget(tokens, usage) {
|
|
540
|
+
if (tokens.length > 1) return {
|
|
541
|
+
ok: false,
|
|
542
|
+
error: usage
|
|
543
|
+
};
|
|
544
|
+
const token = tokens[0]?.trim() || "";
|
|
545
|
+
return {
|
|
546
|
+
ok: true,
|
|
547
|
+
...token ? { sessionToken: token } : {}
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function resolveAcpHelpText() {
|
|
551
|
+
return [
|
|
552
|
+
"ACP commands:",
|
|
553
|
+
"-----",
|
|
554
|
+
"/acp spawn [harness-id] [--mode persistent|oneshot] [--thread auto|here|off] [--cwd <path>] [--label <label>]",
|
|
555
|
+
"/acp cancel [session-key|session-id|session-label]",
|
|
556
|
+
"/acp steer [--session <session-key|session-id|session-label>] <instruction>",
|
|
557
|
+
"/acp close [session-key|session-id|session-label]",
|
|
558
|
+
"/acp status [session-key|session-id|session-label]",
|
|
559
|
+
"/acp set-mode <mode> [session-key|session-id|session-label]",
|
|
560
|
+
"/acp set <key> <value> [session-key|session-id|session-label]",
|
|
561
|
+
"/acp cwd <path> [session-key|session-id|session-label]",
|
|
562
|
+
"/acp permissions <profile> [session-key|session-id|session-label]",
|
|
563
|
+
"/acp timeout <seconds> [session-key|session-id|session-label]",
|
|
564
|
+
"/acp model <model-id> [session-key|session-id|session-label]",
|
|
565
|
+
"/acp reset-options [session-key|session-id|session-label]",
|
|
566
|
+
"/acp doctor",
|
|
567
|
+
"/acp install",
|
|
568
|
+
"/acp sessions",
|
|
569
|
+
"",
|
|
570
|
+
"Notes:",
|
|
571
|
+
"- /acp spawn harness-id is an ACP runtime harness alias (for example codex), not an OpenClaw agents.list id.",
|
|
572
|
+
"- /focus and /unfocus also work with ACP session keys.",
|
|
573
|
+
"- ACP dispatch of normal thread messages is controlled by acp.dispatch.enabled."
|
|
574
|
+
].join("\n");
|
|
575
|
+
}
|
|
576
|
+
function formatRuntimeOptionsText(options) {
|
|
577
|
+
const extras = options.backendExtras ? Object.entries(options.backendExtras).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${key}=${value}`).join(", ") : "";
|
|
578
|
+
const parts = [
|
|
579
|
+
options.runtimeMode ? `runtimeMode=${options.runtimeMode}` : null,
|
|
580
|
+
options.model ? `model=${options.model}` : null,
|
|
581
|
+
options.cwd ? `cwd=${options.cwd}` : null,
|
|
582
|
+
options.permissionProfile ? `permissionProfile=${options.permissionProfile}` : null,
|
|
583
|
+
typeof options.timeoutSeconds === "number" ? `timeoutSeconds=${options.timeoutSeconds}` : null,
|
|
584
|
+
extras ? `extras={${extras}}` : null
|
|
585
|
+
].filter(Boolean);
|
|
586
|
+
if (parts.length === 0) return "(none)";
|
|
587
|
+
return parts.join(", ");
|
|
588
|
+
}
|
|
589
|
+
function formatAcpCapabilitiesText(controls) {
|
|
590
|
+
if (controls.length === 0) return "(none)";
|
|
591
|
+
return controls.toSorted().join(", ");
|
|
592
|
+
}
|
|
593
|
+
function resolveCommandRequestId(params) {
|
|
594
|
+
const value = params.ctx.MessageSidFull ?? params.ctx.MessageSid ?? params.ctx.MessageSidFirst ?? params.ctx.MessageSidLast;
|
|
595
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
596
|
+
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
597
|
+
return randomUUID();
|
|
598
|
+
}
|
|
599
|
+
function collectAcpErrorText(params) {
|
|
600
|
+
return toAcpRuntimeErrorText({
|
|
601
|
+
error: params.error,
|
|
602
|
+
fallbackCode: params.fallbackCode,
|
|
603
|
+
fallbackMessage: params.fallbackMessage
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
async function withAcpCommandErrorBoundary(params) {
|
|
607
|
+
try {
|
|
608
|
+
const result = await params.run();
|
|
609
|
+
return params.onSuccess(result);
|
|
610
|
+
} catch (error) {
|
|
611
|
+
return stopWithText$1(collectAcpErrorText({
|
|
612
|
+
error,
|
|
613
|
+
fallbackCode: params.fallbackCode,
|
|
614
|
+
fallbackMessage: params.fallbackMessage
|
|
615
|
+
}));
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
//#endregion
|
|
619
|
+
//#region src/auto-reply/reply/channel-context.ts
|
|
620
|
+
function isDiscordSurface(params) {
|
|
621
|
+
return resolveCommandSurfaceChannel(params) === "discord";
|
|
622
|
+
}
|
|
623
|
+
function isTelegramSurface(params) {
|
|
624
|
+
return resolveCommandSurfaceChannel(params) === "telegram";
|
|
625
|
+
}
|
|
626
|
+
function isMatrixSurface(params) {
|
|
627
|
+
return resolveCommandSurfaceChannel(params) === "matrix";
|
|
628
|
+
}
|
|
629
|
+
function resolveCommandSurfaceChannel(params) {
|
|
630
|
+
const channel = params.ctx.OriginatingChannel ?? params.command.channel ?? params.ctx.Surface ?? params.ctx.Provider;
|
|
631
|
+
return String(channel ?? "").trim().toLowerCase();
|
|
632
|
+
}
|
|
633
|
+
function resolveChannelAccountId(params) {
|
|
634
|
+
return (typeof params.ctx.AccountId === "string" ? params.ctx.AccountId.trim() : "") || "default";
|
|
635
|
+
}
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region src/auto-reply/reply/commands-subagents/shared.ts
|
|
638
|
+
const COMMAND = "/subagents";
|
|
639
|
+
const COMMAND_KILL = "/kill";
|
|
640
|
+
const COMMAND_STEER = "/steer";
|
|
641
|
+
const COMMAND_TELL = "/tell";
|
|
642
|
+
const COMMAND_FOCUS = "/focus";
|
|
643
|
+
const COMMAND_UNFOCUS = "/unfocus";
|
|
644
|
+
const COMMAND_AGENTS = "/agents";
|
|
645
|
+
const ACTIONS$1 = new Set([
|
|
646
|
+
"list",
|
|
647
|
+
"kill",
|
|
648
|
+
"log",
|
|
649
|
+
"send",
|
|
650
|
+
"steer",
|
|
651
|
+
"info",
|
|
652
|
+
"spawn",
|
|
653
|
+
"focus",
|
|
654
|
+
"unfocus",
|
|
655
|
+
"agents",
|
|
656
|
+
"help"
|
|
657
|
+
]);
|
|
658
|
+
function resolveDisplayStatus(entry, options) {
|
|
659
|
+
const pendingDescendants = Math.max(0, options?.pendingDescendants ?? 0);
|
|
660
|
+
if (pendingDescendants > 0) return `active (waiting on ${pendingDescendants} ${pendingDescendants === 1 ? "child" : "children"})`;
|
|
661
|
+
const status = formatRunStatus(entry);
|
|
662
|
+
return status === "error" ? "failed" : status;
|
|
663
|
+
}
|
|
664
|
+
function formatTimestamp(valueMs) {
|
|
665
|
+
if (!valueMs || !Number.isFinite(valueMs) || valueMs <= 0) return "n/a";
|
|
666
|
+
return new Date(valueMs).toISOString();
|
|
667
|
+
}
|
|
668
|
+
function formatTimestampWithAge(valueMs) {
|
|
669
|
+
if (!valueMs || !Number.isFinite(valueMs) || valueMs <= 0) return "n/a";
|
|
670
|
+
return `${formatTimestamp(valueMs)} (${formatTimeAgo(Date.now() - valueMs, { fallback: "n/a" })})`;
|
|
671
|
+
}
|
|
672
|
+
function stopWithText(text) {
|
|
673
|
+
return {
|
|
674
|
+
shouldContinue: false,
|
|
675
|
+
reply: { text }
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
function stopWithUnknownTargetError(error) {
|
|
679
|
+
return stopWithText(`⚠️ ${error ?? "Unknown subagent."}`);
|
|
680
|
+
}
|
|
681
|
+
function resolveSubagentTarget(runs, token) {
|
|
682
|
+
return resolveSubagentTargetFromRuns({
|
|
683
|
+
runs,
|
|
684
|
+
token,
|
|
685
|
+
recentWindowMinutes: 30,
|
|
686
|
+
label: (entry) => formatRunLabel(entry),
|
|
687
|
+
isActive: (entry) => !entry.endedAt || Math.max(0, countPendingDescendantRuns(entry.childSessionKey)) > 0,
|
|
688
|
+
errors: {
|
|
689
|
+
missingTarget: "Missing subagent id.",
|
|
690
|
+
invalidIndex: (value) => `Invalid subagent index: ${value}`,
|
|
691
|
+
unknownSession: (value) => `Unknown subagent session: ${value}`,
|
|
692
|
+
ambiguousLabel: (value) => `Ambiguous subagent label: ${value}`,
|
|
693
|
+
ambiguousLabelPrefix: (value) => `Ambiguous subagent label prefix: ${value}`,
|
|
694
|
+
ambiguousRunIdPrefix: (value) => `Ambiguous run id prefix: ${value}`,
|
|
695
|
+
unknownTarget: (value) => `Unknown subagent id: ${value}`
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
function resolveSubagentEntryForToken(runs, token) {
|
|
700
|
+
const resolved = resolveSubagentTarget(runs, token);
|
|
701
|
+
if (!resolved.entry) return { reply: stopWithUnknownTargetError(resolved.error) };
|
|
702
|
+
return { entry: resolved.entry };
|
|
703
|
+
}
|
|
704
|
+
function resolveRequesterSessionKey(params, opts) {
|
|
705
|
+
const commandTarget = params.ctx.CommandTargetSessionKey?.trim();
|
|
706
|
+
const commandSession = params.sessionKey?.trim();
|
|
707
|
+
const raw = opts?.preferCommandTarget ?? params.ctx.CommandSource === "native" ? commandTarget || commandSession : commandSession || commandTarget;
|
|
708
|
+
if (!raw) return;
|
|
709
|
+
const { mainKey, alias } = resolveMainSessionAlias(params.cfg);
|
|
710
|
+
return resolveInternalSessionKey({
|
|
711
|
+
key: raw,
|
|
712
|
+
alias,
|
|
713
|
+
mainKey
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
function resolveCommandSubagentController(params, requesterKey) {
|
|
717
|
+
if (!isSubagentSessionKey(requesterKey)) return {
|
|
718
|
+
controllerSessionKey: requesterKey,
|
|
719
|
+
callerSessionKey: requesterKey,
|
|
720
|
+
callerIsSubagent: false,
|
|
721
|
+
controlScope: "children"
|
|
722
|
+
};
|
|
723
|
+
return {
|
|
724
|
+
controllerSessionKey: requesterKey,
|
|
725
|
+
callerSessionKey: requesterKey,
|
|
726
|
+
callerIsSubagent: true,
|
|
727
|
+
controlScope: resolveStoredSubagentCapabilities(requesterKey, { cfg: params.cfg }).controlScope
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
function resolveHandledPrefix(normalized) {
|
|
731
|
+
return normalized.startsWith("/subagents") ? COMMAND : normalized.startsWith("/kill") ? COMMAND_KILL : normalized.startsWith("/steer") ? COMMAND_STEER : normalized.startsWith("/tell") ? COMMAND_TELL : normalized.startsWith("/focus") ? COMMAND_FOCUS : normalized.startsWith("/unfocus") ? COMMAND_UNFOCUS : normalized.startsWith("/agents") ? COMMAND_AGENTS : null;
|
|
732
|
+
}
|
|
733
|
+
function resolveSubagentsAction(params) {
|
|
734
|
+
if (params.handledPrefix === "/subagents") {
|
|
735
|
+
const [actionRaw] = params.restTokens;
|
|
736
|
+
const action = actionRaw?.toLowerCase() || "list";
|
|
737
|
+
if (!ACTIONS$1.has(action)) return null;
|
|
738
|
+
params.restTokens.splice(0, 1);
|
|
739
|
+
return action;
|
|
740
|
+
}
|
|
741
|
+
if (params.handledPrefix === "/kill") return "kill";
|
|
742
|
+
if (params.handledPrefix === "/focus") return "focus";
|
|
743
|
+
if (params.handledPrefix === "/unfocus") return "unfocus";
|
|
744
|
+
if (params.handledPrefix === "/agents") return "agents";
|
|
745
|
+
return "steer";
|
|
746
|
+
}
|
|
747
|
+
function resolveDiscordChannelIdForFocus(params) {
|
|
748
|
+
const toCandidates = [
|
|
749
|
+
typeof params.ctx.OriginatingTo === "string" ? params.ctx.OriginatingTo.trim() : "",
|
|
750
|
+
typeof params.command.to === "string" ? params.command.to.trim() : "",
|
|
751
|
+
typeof params.ctx.To === "string" ? params.ctx.To.trim() : ""
|
|
752
|
+
].filter(Boolean);
|
|
753
|
+
for (const candidate of toCandidates) {
|
|
754
|
+
const target = parseExplicitTargetForChannel("discord", candidate);
|
|
755
|
+
if (target?.chatType === "channel" && target.to) return target.to;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
async function resolveFocusTargetSession(params) {
|
|
759
|
+
const subagentMatch = resolveSubagentTarget(params.runs, params.token);
|
|
760
|
+
if (subagentMatch.entry) {
|
|
761
|
+
const key = subagentMatch.entry.childSessionKey;
|
|
762
|
+
return {
|
|
763
|
+
targetKind: "subagent",
|
|
764
|
+
targetSessionKey: key,
|
|
765
|
+
agentId: parseAgentSessionKey(key)?.agentId ?? "main",
|
|
766
|
+
label: formatRunLabel(subagentMatch.entry)
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
const token = params.token.trim();
|
|
770
|
+
if (!token) return null;
|
|
771
|
+
const attempts = [];
|
|
772
|
+
attempts.push({ key: token });
|
|
773
|
+
if (looksLikeSessionId(token)) attempts.push({ sessionId: token });
|
|
774
|
+
attempts.push({ label: token });
|
|
775
|
+
for (const attempt of attempts) try {
|
|
776
|
+
const resolved = await callGateway({
|
|
777
|
+
method: "sessions.resolve",
|
|
778
|
+
params: attempt
|
|
779
|
+
});
|
|
780
|
+
const key = typeof resolved?.key === "string" ? resolved.key.trim() : "";
|
|
781
|
+
if (!key) continue;
|
|
782
|
+
const parsed = parseAgentSessionKey(key);
|
|
783
|
+
return {
|
|
784
|
+
targetKind: key.includes(":subagent:") ? "subagent" : "acp",
|
|
785
|
+
targetSessionKey: key,
|
|
786
|
+
agentId: parsed?.agentId ?? "main",
|
|
787
|
+
label: token
|
|
788
|
+
};
|
|
789
|
+
} catch {}
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
792
|
+
function buildSubagentsHelp() {
|
|
793
|
+
return [
|
|
794
|
+
"Subagents",
|
|
795
|
+
"Usage:",
|
|
796
|
+
"- /subagents list",
|
|
797
|
+
"- /subagents kill <id|#|all>",
|
|
798
|
+
"- /subagents log <id|#> [limit] [tools]",
|
|
799
|
+
"- /subagents info <id|#>",
|
|
800
|
+
"- /subagents send <id|#> <message>",
|
|
801
|
+
"- /subagents steer <id|#> <message>",
|
|
802
|
+
"- /subagents spawn <agentId> <task> [--model <model>] [--thinking <level>]",
|
|
803
|
+
"- /focus <subagent-label|session-key|session-id|session-label>",
|
|
804
|
+
"- /unfocus",
|
|
805
|
+
"- /agents",
|
|
806
|
+
"- /session idle <duration|off>",
|
|
807
|
+
"- /session max-age <duration|off>",
|
|
808
|
+
"- /kill <id|#|all>",
|
|
809
|
+
"- /steer <id|#> <message>",
|
|
810
|
+
"- /tell <id|#> <message>",
|
|
811
|
+
"",
|
|
812
|
+
"Ids: use the list index (#), runId/session prefix, label, or full session key."
|
|
813
|
+
].join("\n");
|
|
814
|
+
}
|
|
815
|
+
function extractMessageText(message) {
|
|
816
|
+
const role = typeof message.role === "string" ? message.role : "";
|
|
817
|
+
const shouldSanitize = role === "assistant";
|
|
818
|
+
const text = extractTextFromChatContent(message.content, { sanitizeText: shouldSanitize ? sanitizeTextContent : void 0 });
|
|
819
|
+
return text ? {
|
|
820
|
+
role,
|
|
821
|
+
text
|
|
822
|
+
} : null;
|
|
823
|
+
}
|
|
824
|
+
function formatLogLines(messages) {
|
|
825
|
+
const lines = [];
|
|
826
|
+
for (const msg of messages) {
|
|
827
|
+
const extracted = extractMessageText(msg);
|
|
828
|
+
if (!extracted) continue;
|
|
829
|
+
const label = extracted.role === "assistant" ? "Assistant" : "User";
|
|
830
|
+
lines.push(`${label}: ${extracted.text}`);
|
|
831
|
+
}
|
|
832
|
+
return lines;
|
|
833
|
+
}
|
|
834
|
+
function loadSubagentSessionEntry(params, childKey, loaders, storeCache) {
|
|
835
|
+
const parsed = parseAgentSessionKey(childKey);
|
|
836
|
+
const storePath = loaders.resolveStorePath(params.cfg.session?.store, { agentId: parsed?.agentId });
|
|
837
|
+
let store = storeCache?.get(storePath);
|
|
838
|
+
if (!store) {
|
|
839
|
+
store = loaders.loadSessionStore(storePath);
|
|
840
|
+
storeCache?.set(storePath, store);
|
|
841
|
+
}
|
|
842
|
+
return {
|
|
843
|
+
storePath,
|
|
844
|
+
store,
|
|
845
|
+
entry: store[childKey]
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
//#endregion
|
|
849
|
+
//#region src/auto-reply/reply/commands-acp/targets.ts
|
|
850
|
+
async function resolveSessionKeyByToken(token) {
|
|
851
|
+
const trimmed = token.trim();
|
|
852
|
+
if (!trimmed) return null;
|
|
853
|
+
const attempts = [{ key: trimmed }];
|
|
854
|
+
if (SESSION_ID_RE.test(trimmed)) attempts.push({ sessionId: trimmed });
|
|
855
|
+
attempts.push({ label: trimmed });
|
|
856
|
+
for (const params of attempts) try {
|
|
857
|
+
const resolved = await callGateway({
|
|
858
|
+
method: "sessions.resolve",
|
|
859
|
+
params,
|
|
860
|
+
timeoutMs: 8e3
|
|
861
|
+
});
|
|
862
|
+
const key = typeof resolved?.key === "string" ? resolved.key.trim() : "";
|
|
863
|
+
if (key) return key;
|
|
864
|
+
} catch {}
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
function resolveBoundAcpThreadSessionKey(params) {
|
|
868
|
+
const activeSessionKey = (typeof params.ctx.CommandTargetSessionKey === "string" ? params.ctx.CommandTargetSessionKey.trim() : "") || params.sessionKey.trim();
|
|
869
|
+
const bindingContext = resolveAcpCommandBindingContext(params);
|
|
870
|
+
return resolveEffectiveResetTargetSessionKey({
|
|
871
|
+
cfg: params.cfg,
|
|
872
|
+
channel: bindingContext.channel,
|
|
873
|
+
accountId: bindingContext.accountId,
|
|
874
|
+
conversationId: bindingContext.conversationId,
|
|
875
|
+
parentConversationId: bindingContext.parentConversationId,
|
|
876
|
+
activeSessionKey,
|
|
877
|
+
allowNonAcpBindingSessionKey: true,
|
|
878
|
+
skipConfiguredFallbackWhenActiveSessionNonAcp: false
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
async function resolveAcpTargetSessionKey(params) {
|
|
882
|
+
const token = params.token?.trim() || "";
|
|
883
|
+
if (token) {
|
|
884
|
+
const resolved = await resolveSessionKeyByToken(token);
|
|
885
|
+
if (!resolved) return {
|
|
886
|
+
ok: false,
|
|
887
|
+
error: `Unable to resolve session target: ${token}`
|
|
888
|
+
};
|
|
889
|
+
return {
|
|
890
|
+
ok: true,
|
|
891
|
+
sessionKey: resolved
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
const threadBound = resolveBoundAcpThreadSessionKey(params.commandParams);
|
|
895
|
+
if (threadBound) return {
|
|
896
|
+
ok: true,
|
|
897
|
+
sessionKey: threadBound
|
|
898
|
+
};
|
|
899
|
+
const fallback = resolveRequesterSessionKey(params.commandParams, { preferCommandTarget: true });
|
|
900
|
+
if (!fallback) return {
|
|
901
|
+
ok: false,
|
|
902
|
+
error: "Missing session key."
|
|
903
|
+
};
|
|
904
|
+
return {
|
|
905
|
+
ok: true,
|
|
906
|
+
sessionKey: fallback
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
//#endregion
|
|
910
|
+
//#region src/auto-reply/reply/commands-acp/diagnostics.ts
|
|
911
|
+
async function handleAcpDoctorAction(params, restTokens) {
|
|
912
|
+
if (restTokens.length > 0) return stopWithText$1(`⚠️ ${ACP_DOCTOR_USAGE}`);
|
|
913
|
+
const backendId = resolveConfiguredAcpBackendId(params.cfg);
|
|
914
|
+
const installHint = resolveAcpInstallCommandHint(params.cfg);
|
|
915
|
+
const registeredBackend = getAcpRuntimeBackend(backendId);
|
|
916
|
+
const managerSnapshot = getAcpSessionManager().getObservabilitySnapshot(params.cfg);
|
|
917
|
+
const lines = [
|
|
918
|
+
"ACP doctor:",
|
|
919
|
+
"-----",
|
|
920
|
+
`configuredBackend: ${backendId}`
|
|
921
|
+
];
|
|
922
|
+
lines.push(`activeRuntimeSessions: ${managerSnapshot.runtimeCache.activeSessions}`);
|
|
923
|
+
lines.push(`runtimeIdleTtlMs: ${managerSnapshot.runtimeCache.idleTtlMs}`);
|
|
924
|
+
lines.push(`evictedIdleRuntimes: ${managerSnapshot.runtimeCache.evictedTotal}`);
|
|
925
|
+
lines.push(`activeTurns: ${managerSnapshot.turns.active}`);
|
|
926
|
+
lines.push(`queueDepth: ${managerSnapshot.turns.queueDepth}`);
|
|
927
|
+
lines.push(`turnLatencyMs: avg=${managerSnapshot.turns.averageLatencyMs}, max=${managerSnapshot.turns.maxLatencyMs}`);
|
|
928
|
+
lines.push(`turnCounts: completed=${managerSnapshot.turns.completed}, failed=${managerSnapshot.turns.failed}`);
|
|
929
|
+
const errorStatsText = Object.entries(managerSnapshot.errorsByCode).map(([code, count]) => `${code}=${count}`).join(", ") || "(none)";
|
|
930
|
+
lines.push(`errorCodes: ${errorStatsText}`);
|
|
931
|
+
if (registeredBackend) lines.push(`registeredBackend: ${registeredBackend.id}`);
|
|
932
|
+
else lines.push("registeredBackend: (none)");
|
|
933
|
+
if (registeredBackend?.runtime.doctor) try {
|
|
934
|
+
const report = await registeredBackend.runtime.doctor();
|
|
935
|
+
lines.push(`runtimeDoctor: ${report.ok ? "ok" : "error"} (${report.message})`);
|
|
936
|
+
if (report.code) lines.push(`runtimeDoctorCode: ${report.code}`);
|
|
937
|
+
if (report.installCommand) lines.push(`runtimeDoctorInstall: ${report.installCommand}`);
|
|
938
|
+
for (const detail of report.details ?? []) lines.push(`runtimeDoctorDetail: ${detail}`);
|
|
939
|
+
} catch (error) {
|
|
940
|
+
lines.push(`runtimeDoctor: error (${toAcpRuntimeError({
|
|
941
|
+
error,
|
|
942
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
943
|
+
fallbackMessage: "Runtime doctor failed."
|
|
944
|
+
}).message})`);
|
|
945
|
+
}
|
|
946
|
+
try {
|
|
947
|
+
const backend = requireAcpRuntimeBackend(backendId);
|
|
948
|
+
const capabilities = backend.runtime.getCapabilities ? await backend.runtime.getCapabilities({}) : {
|
|
949
|
+
controls: [],
|
|
950
|
+
configOptionKeys: []
|
|
951
|
+
};
|
|
952
|
+
lines.push("healthy: yes");
|
|
953
|
+
lines.push(`capabilities: ${formatAcpCapabilitiesText(capabilities.controls ?? [])}`);
|
|
954
|
+
if ((capabilities.configOptionKeys?.length ?? 0) > 0) lines.push(`configKeys: ${capabilities.configOptionKeys?.join(", ")}`);
|
|
955
|
+
return stopWithText$1(lines.join("\n"));
|
|
956
|
+
} catch (error) {
|
|
957
|
+
const acpError = toAcpRuntimeError({
|
|
958
|
+
error,
|
|
959
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
960
|
+
fallbackMessage: "ACP backend doctor failed."
|
|
961
|
+
});
|
|
962
|
+
lines.push("healthy: no");
|
|
963
|
+
lines.push(formatAcpRuntimeErrorText(acpError));
|
|
964
|
+
lines.push(`next: ${installHint}`);
|
|
965
|
+
lines.push(`next: openclaw config set plugins.entries.${backendId}.enabled true`);
|
|
966
|
+
if (backendId.toLowerCase() === "acpx") lines.push("next: verify acpx is installed (`acpx --help`).");
|
|
967
|
+
return stopWithText$1(lines.join("\n"));
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function handleAcpInstallAction(params, restTokens) {
|
|
971
|
+
if (restTokens.length > 0) return stopWithText$1(`⚠️ ${ACP_INSTALL_USAGE}`);
|
|
972
|
+
const backendId = resolveConfiguredAcpBackendId(params.cfg);
|
|
973
|
+
const installHint = resolveAcpInstallCommandHint(params.cfg);
|
|
974
|
+
return stopWithText$1([
|
|
975
|
+
"ACP install:",
|
|
976
|
+
"-----",
|
|
977
|
+
`configuredBackend: ${backendId}`,
|
|
978
|
+
`run: ${installHint}`,
|
|
979
|
+
`then: openclaw config set plugins.entries.${backendId}.enabled true`,
|
|
980
|
+
"then: /acp doctor"
|
|
981
|
+
].join("\n"));
|
|
982
|
+
}
|
|
983
|
+
function formatAcpSessionLine(params) {
|
|
984
|
+
const acp = params.entry.acp;
|
|
985
|
+
if (!acp) return "";
|
|
986
|
+
const marker = params.currentSessionKey === params.key ? "*" : " ";
|
|
987
|
+
const label = params.entry.label?.trim() || acp.agent;
|
|
988
|
+
const threadText = params.threadId ? `, thread:${params.threadId}` : "";
|
|
989
|
+
return `${marker} ${label} (${acp.mode}, ${acp.state}, backend:${acp.backend}${threadText}) -> ${params.key}`;
|
|
990
|
+
}
|
|
991
|
+
function handleAcpSessionsAction(params, restTokens) {
|
|
992
|
+
if (restTokens.length > 0) return stopWithText$1(ACP_SESSIONS_USAGE);
|
|
993
|
+
const currentSessionKey = resolveBoundAcpThreadSessionKey(params) || params.sessionKey;
|
|
994
|
+
if (!currentSessionKey) return stopWithText$1("⚠️ Missing session key.");
|
|
995
|
+
const { storePath } = resolveSessionStorePathForAcp({
|
|
996
|
+
cfg: params.cfg,
|
|
997
|
+
sessionKey: currentSessionKey
|
|
998
|
+
});
|
|
999
|
+
let store;
|
|
1000
|
+
try {
|
|
1001
|
+
store = loadSessionStore(storePath);
|
|
1002
|
+
} catch {
|
|
1003
|
+
store = {};
|
|
1004
|
+
}
|
|
1005
|
+
const bindingContext = resolveAcpCommandBindingContext(params);
|
|
1006
|
+
const normalizedChannel = bindingContext.channel;
|
|
1007
|
+
const normalizedAccountId = bindingContext.accountId || void 0;
|
|
1008
|
+
const bindingService = getSessionBindingService();
|
|
1009
|
+
const rows = Object.entries(store).filter(([, entry]) => Boolean(entry?.acp)).toSorted(([, a], [, b]) => (b?.updatedAt ?? 0) - (a?.updatedAt ?? 0)).slice(0, 20).map(([key, entry]) => {
|
|
1010
|
+
const bindingThreadId = bindingService.listBySession(key).find((binding) => (!normalizedChannel || binding.conversation.channel === normalizedChannel) && (!normalizedAccountId || binding.conversation.accountId === normalizedAccountId))?.conversation.conversationId;
|
|
1011
|
+
return formatAcpSessionLine({
|
|
1012
|
+
key,
|
|
1013
|
+
entry,
|
|
1014
|
+
currentSessionKey,
|
|
1015
|
+
threadId: bindingThreadId
|
|
1016
|
+
});
|
|
1017
|
+
}).filter(Boolean);
|
|
1018
|
+
if (rows.length === 0) return stopWithText$1("ACP sessions:\n-----\n(none)");
|
|
1019
|
+
return stopWithText$1([
|
|
1020
|
+
"ACP sessions:",
|
|
1021
|
+
"-----",
|
|
1022
|
+
...rows
|
|
1023
|
+
].join("\n"));
|
|
1024
|
+
}
|
|
1025
|
+
//#endregion
|
|
1026
|
+
//#region src/auto-reply/reply/commands-acp/lifecycle.ts
|
|
1027
|
+
async function bindSpawnedAcpSessionToThread(params) {
|
|
1028
|
+
const { commandParams, threadMode } = params;
|
|
1029
|
+
if (threadMode === "off") return {
|
|
1030
|
+
ok: false,
|
|
1031
|
+
error: "internal: thread binding is disabled for this spawn"
|
|
1032
|
+
};
|
|
1033
|
+
const bindingContext = resolveAcpCommandBindingContext(commandParams);
|
|
1034
|
+
const channel = bindingContext.channel;
|
|
1035
|
+
if (!channel) return {
|
|
1036
|
+
ok: false,
|
|
1037
|
+
error: "ACP thread binding requires a channel context."
|
|
1038
|
+
};
|
|
1039
|
+
const accountId = resolveAcpCommandAccountId(commandParams);
|
|
1040
|
+
const spawnPolicy = resolveThreadBindingSpawnPolicy({
|
|
1041
|
+
cfg: commandParams.cfg,
|
|
1042
|
+
channel,
|
|
1043
|
+
accountId,
|
|
1044
|
+
kind: "acp"
|
|
1045
|
+
});
|
|
1046
|
+
if (!spawnPolicy.enabled) return {
|
|
1047
|
+
ok: false,
|
|
1048
|
+
error: formatThreadBindingDisabledError({
|
|
1049
|
+
channel: spawnPolicy.channel,
|
|
1050
|
+
accountId: spawnPolicy.accountId,
|
|
1051
|
+
kind: "acp"
|
|
1052
|
+
})
|
|
1053
|
+
};
|
|
1054
|
+
if (!spawnPolicy.spawnEnabled) return {
|
|
1055
|
+
ok: false,
|
|
1056
|
+
error: formatThreadBindingSpawnDisabledError({
|
|
1057
|
+
channel: spawnPolicy.channel,
|
|
1058
|
+
accountId: spawnPolicy.accountId,
|
|
1059
|
+
kind: "acp"
|
|
1060
|
+
})
|
|
1061
|
+
};
|
|
1062
|
+
const bindingService = getSessionBindingService();
|
|
1063
|
+
const capabilities = bindingService.getCapabilities({
|
|
1064
|
+
channel: spawnPolicy.channel,
|
|
1065
|
+
accountId: spawnPolicy.accountId
|
|
1066
|
+
});
|
|
1067
|
+
if (!capabilities.adapterAvailable) return {
|
|
1068
|
+
ok: false,
|
|
1069
|
+
error: `Thread bindings are unavailable for ${channel}.`
|
|
1070
|
+
};
|
|
1071
|
+
if (!capabilities.bindSupported) return {
|
|
1072
|
+
ok: false,
|
|
1073
|
+
error: `Thread bindings are unavailable for ${channel}.`
|
|
1074
|
+
};
|
|
1075
|
+
const currentThreadId = bindingContext.threadId ?? "";
|
|
1076
|
+
const currentConversationId = bindingContext.conversationId?.trim() || "";
|
|
1077
|
+
const requiresThreadIdForHere = channel !== "telegram" && channel !== "feishu";
|
|
1078
|
+
if (threadMode === "here" && (requiresThreadIdForHere && !currentThreadId || !requiresThreadIdForHere && !currentConversationId)) return {
|
|
1079
|
+
ok: false,
|
|
1080
|
+
error: `--thread here requires running /acp spawn inside an active ${channel} thread/conversation.`
|
|
1081
|
+
};
|
|
1082
|
+
const placement = channel === "telegram" || channel === "feishu" ? "current" : currentThreadId ? "current" : "child";
|
|
1083
|
+
if (!capabilities.placements.includes(placement)) return {
|
|
1084
|
+
ok: false,
|
|
1085
|
+
error: `Thread bindings do not support ${placement} placement for ${channel}.`
|
|
1086
|
+
};
|
|
1087
|
+
if (!currentConversationId) return {
|
|
1088
|
+
ok: false,
|
|
1089
|
+
error: `Could not resolve a ${channel} conversation for ACP thread spawn.`
|
|
1090
|
+
};
|
|
1091
|
+
const senderId = commandParams.command.senderId?.trim() || "";
|
|
1092
|
+
const parentConversationId = bindingContext.parentConversationId?.trim() || void 0;
|
|
1093
|
+
const conversationRef = {
|
|
1094
|
+
channel: spawnPolicy.channel,
|
|
1095
|
+
accountId: spawnPolicy.accountId,
|
|
1096
|
+
conversationId: currentConversationId,
|
|
1097
|
+
...parentConversationId && parentConversationId !== currentConversationId ? { parentConversationId } : {}
|
|
1098
|
+
};
|
|
1099
|
+
if (placement === "current") {
|
|
1100
|
+
const existingBinding = bindingService.resolveByConversation(conversationRef);
|
|
1101
|
+
const boundBy = typeof existingBinding?.metadata?.boundBy === "string" ? existingBinding.metadata.boundBy.trim() : "";
|
|
1102
|
+
if (existingBinding && boundBy && boundBy !== "system" && senderId && senderId !== boundBy) return {
|
|
1103
|
+
ok: false,
|
|
1104
|
+
error: `Only ${boundBy} can rebind this ${channel === "telegram" ? "conversation" : "thread"}.`
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
const label = params.label || params.agentId;
|
|
1108
|
+
try {
|
|
1109
|
+
return {
|
|
1110
|
+
ok: true,
|
|
1111
|
+
binding: await bindingService.bind({
|
|
1112
|
+
targetSessionKey: params.sessionKey,
|
|
1113
|
+
targetKind: "session",
|
|
1114
|
+
conversation: conversationRef,
|
|
1115
|
+
placement,
|
|
1116
|
+
metadata: {
|
|
1117
|
+
threadName: resolveThreadBindingThreadName({
|
|
1118
|
+
agentId: params.agentId,
|
|
1119
|
+
label
|
|
1120
|
+
}),
|
|
1121
|
+
agentId: params.agentId,
|
|
1122
|
+
label,
|
|
1123
|
+
boundBy: senderId || "unknown",
|
|
1124
|
+
introText: resolveThreadBindingIntroText({
|
|
1125
|
+
agentId: params.agentId,
|
|
1126
|
+
label,
|
|
1127
|
+
idleTimeoutMs: resolveThreadBindingIdleTimeoutMsForChannel({
|
|
1128
|
+
cfg: commandParams.cfg,
|
|
1129
|
+
channel: spawnPolicy.channel,
|
|
1130
|
+
accountId: spawnPolicy.accountId
|
|
1131
|
+
}),
|
|
1132
|
+
maxAgeMs: resolveThreadBindingMaxAgeMsForChannel({
|
|
1133
|
+
cfg: commandParams.cfg,
|
|
1134
|
+
channel: spawnPolicy.channel,
|
|
1135
|
+
accountId: spawnPolicy.accountId
|
|
1136
|
+
}),
|
|
1137
|
+
sessionCwd: resolveAcpSessionCwd(params.sessionMeta),
|
|
1138
|
+
sessionDetails: resolveAcpThreadSessionDetailLines({
|
|
1139
|
+
sessionKey: params.sessionKey,
|
|
1140
|
+
meta: params.sessionMeta
|
|
1141
|
+
})
|
|
1142
|
+
})
|
|
1143
|
+
}
|
|
1144
|
+
})
|
|
1145
|
+
};
|
|
1146
|
+
} catch (error) {
|
|
1147
|
+
return {
|
|
1148
|
+
ok: false,
|
|
1149
|
+
error: (error instanceof Error ? error.message : String(error)) || `Failed to bind a ${channel} thread/conversation to the new ACP session.`
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
async function cleanupFailedSpawn(params) {
|
|
1154
|
+
await cleanupFailedAcpSpawn({
|
|
1155
|
+
cfg: params.cfg,
|
|
1156
|
+
sessionKey: params.sessionKey,
|
|
1157
|
+
shouldDeleteSession: params.shouldDeleteSession,
|
|
1158
|
+
deleteTranscript: false,
|
|
1159
|
+
runtimeCloseHandle: params.initializedRuntime
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
async function handleAcpSpawnAction(params, restTokens) {
|
|
1163
|
+
if (!isAcpEnabledByPolicy(params.cfg)) return stopWithText$1("ACP is disabled by policy (`acp.enabled=false`).");
|
|
1164
|
+
const parsed = parseSpawnInput(params, restTokens);
|
|
1165
|
+
if (!parsed.ok) return stopWithText$1(`⚠️ ${parsed.error}`);
|
|
1166
|
+
const spawn = parsed.value;
|
|
1167
|
+
const runtimePolicyError = resolveAcpSpawnRuntimePolicyError({
|
|
1168
|
+
cfg: params.cfg,
|
|
1169
|
+
requesterSessionKey: params.sessionKey
|
|
1170
|
+
});
|
|
1171
|
+
if (runtimePolicyError) return stopWithText$1(`⚠️ ${runtimePolicyError}`);
|
|
1172
|
+
const agentPolicyError = resolveAcpAgentPolicyError(params.cfg, spawn.agentId);
|
|
1173
|
+
if (agentPolicyError) return stopWithText$1(collectAcpErrorText({
|
|
1174
|
+
error: agentPolicyError,
|
|
1175
|
+
fallbackCode: "ACP_SESSION_INIT_FAILED",
|
|
1176
|
+
fallbackMessage: "ACP target agent is not allowed by policy."
|
|
1177
|
+
}));
|
|
1178
|
+
const acpManager = getAcpSessionManager();
|
|
1179
|
+
const sessionKey = `agent:${spawn.agentId}:acp:${randomUUID()}`;
|
|
1180
|
+
let initializedBackend = "";
|
|
1181
|
+
let initializedMeta;
|
|
1182
|
+
let initializedRuntime;
|
|
1183
|
+
try {
|
|
1184
|
+
const initialized = await acpManager.initializeSession({
|
|
1185
|
+
cfg: params.cfg,
|
|
1186
|
+
sessionKey,
|
|
1187
|
+
agent: spawn.agentId,
|
|
1188
|
+
mode: spawn.mode,
|
|
1189
|
+
cwd: spawn.cwd
|
|
1190
|
+
});
|
|
1191
|
+
initializedRuntime = {
|
|
1192
|
+
runtime: initialized.runtime,
|
|
1193
|
+
handle: initialized.handle
|
|
1194
|
+
};
|
|
1195
|
+
initializedBackend = initialized.handle.backend || initialized.meta.backend;
|
|
1196
|
+
initializedMeta = initialized.meta;
|
|
1197
|
+
} catch (err) {
|
|
1198
|
+
return stopWithText$1(collectAcpErrorText({
|
|
1199
|
+
error: err,
|
|
1200
|
+
fallbackCode: "ACP_SESSION_INIT_FAILED",
|
|
1201
|
+
fallbackMessage: "Could not initialize ACP session runtime."
|
|
1202
|
+
}));
|
|
1203
|
+
}
|
|
1204
|
+
let binding = null;
|
|
1205
|
+
if (spawn.thread !== "off") {
|
|
1206
|
+
const bound = await bindSpawnedAcpSessionToThread({
|
|
1207
|
+
commandParams: params,
|
|
1208
|
+
sessionKey,
|
|
1209
|
+
agentId: spawn.agentId,
|
|
1210
|
+
label: spawn.label,
|
|
1211
|
+
threadMode: spawn.thread,
|
|
1212
|
+
sessionMeta: initializedMeta
|
|
1213
|
+
});
|
|
1214
|
+
if (!bound.ok) {
|
|
1215
|
+
await cleanupFailedSpawn({
|
|
1216
|
+
cfg: params.cfg,
|
|
1217
|
+
sessionKey,
|
|
1218
|
+
shouldDeleteSession: true,
|
|
1219
|
+
initializedRuntime
|
|
1220
|
+
});
|
|
1221
|
+
return stopWithText$1(`⚠️ ${bound.error}`);
|
|
1222
|
+
}
|
|
1223
|
+
binding = bound.binding;
|
|
1224
|
+
}
|
|
1225
|
+
try {
|
|
1226
|
+
await callGateway({
|
|
1227
|
+
method: "sessions.patch",
|
|
1228
|
+
params: {
|
|
1229
|
+
key: sessionKey,
|
|
1230
|
+
...spawn.label ? { label: spawn.label } : {}
|
|
1231
|
+
},
|
|
1232
|
+
timeoutMs: 1e4
|
|
1233
|
+
});
|
|
1234
|
+
} catch (err) {
|
|
1235
|
+
await cleanupFailedSpawn({
|
|
1236
|
+
cfg: params.cfg,
|
|
1237
|
+
sessionKey,
|
|
1238
|
+
shouldDeleteSession: true,
|
|
1239
|
+
initializedRuntime
|
|
1240
|
+
});
|
|
1241
|
+
return stopWithText$1(`⚠️ ACP spawn failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1242
|
+
}
|
|
1243
|
+
const parts = [`✅ Spawned ACP session ${sessionKey} (${spawn.mode}, backend ${initializedBackend}).`];
|
|
1244
|
+
if (binding) {
|
|
1245
|
+
const currentConversationId = resolveAcpCommandConversationId(params)?.trim() || "";
|
|
1246
|
+
const boundConversationId = binding.conversation.conversationId.trim();
|
|
1247
|
+
const placementLabel = binding.conversation.channel === "telegram" ? "conversation" : "thread";
|
|
1248
|
+
if (currentConversationId && boundConversationId === currentConversationId) parts.push(`Bound this ${placementLabel} to ${sessionKey}.`);
|
|
1249
|
+
else parts.push(`Created ${placementLabel} ${boundConversationId} and bound it to ${sessionKey}.`);
|
|
1250
|
+
} else parts.push("Session is unbound (use /focus <session-key> to bind this thread/conversation).");
|
|
1251
|
+
const dispatchNote = resolveAcpDispatchPolicyMessage(params.cfg);
|
|
1252
|
+
if (dispatchNote) parts.push(`ℹ️ ${dispatchNote}`);
|
|
1253
|
+
if (binding?.conversation.channel === "telegram" && binding.conversation.conversationId.includes(":topic:")) return {
|
|
1254
|
+
shouldContinue: false,
|
|
1255
|
+
reply: {
|
|
1256
|
+
text: parts.join(" "),
|
|
1257
|
+
channelData: { telegram: { pin: true } }
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
return stopWithText$1(parts.join(" "));
|
|
1261
|
+
}
|
|
1262
|
+
function resolveAcpSessionForCommandOrStop(params) {
|
|
1263
|
+
const error = resolveAcpSessionResolutionError(params.acpManager.resolveSession({
|
|
1264
|
+
cfg: params.cfg,
|
|
1265
|
+
sessionKey: params.sessionKey
|
|
1266
|
+
}));
|
|
1267
|
+
if (error) return stopWithText$1(collectAcpErrorText({
|
|
1268
|
+
error,
|
|
1269
|
+
fallbackCode: "ACP_SESSION_INIT_FAILED",
|
|
1270
|
+
fallbackMessage: error.message
|
|
1271
|
+
}));
|
|
1272
|
+
return null;
|
|
1273
|
+
}
|
|
1274
|
+
async function resolveAcpTokenTargetSessionKeyOrStop(params) {
|
|
1275
|
+
const token = params.restTokens.join(" ").trim() || void 0;
|
|
1276
|
+
const target = await resolveAcpTargetSessionKey({
|
|
1277
|
+
commandParams: params.commandParams,
|
|
1278
|
+
token
|
|
1279
|
+
});
|
|
1280
|
+
if (!target.ok) return stopWithText$1(`⚠️ ${target.error}`);
|
|
1281
|
+
return target.sessionKey;
|
|
1282
|
+
}
|
|
1283
|
+
async function withResolvedAcpSessionTarget(params) {
|
|
1284
|
+
const acpManager = getAcpSessionManager();
|
|
1285
|
+
const targetSessionKey = await resolveAcpTokenTargetSessionKeyOrStop({
|
|
1286
|
+
commandParams: params.commandParams,
|
|
1287
|
+
restTokens: params.restTokens
|
|
1288
|
+
});
|
|
1289
|
+
if (typeof targetSessionKey !== "string") return targetSessionKey;
|
|
1290
|
+
const guardFailure = resolveAcpSessionForCommandOrStop({
|
|
1291
|
+
acpManager,
|
|
1292
|
+
cfg: params.commandParams.cfg,
|
|
1293
|
+
sessionKey: targetSessionKey
|
|
1294
|
+
});
|
|
1295
|
+
if (guardFailure) return guardFailure;
|
|
1296
|
+
return await params.run({
|
|
1297
|
+
acpManager,
|
|
1298
|
+
sessionKey: targetSessionKey
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
async function handleAcpCancelAction(params, restTokens) {
|
|
1302
|
+
return await withResolvedAcpSessionTarget({
|
|
1303
|
+
commandParams: params,
|
|
1304
|
+
restTokens,
|
|
1305
|
+
run: async ({ acpManager, sessionKey }) => await withAcpCommandErrorBoundary({
|
|
1306
|
+
run: async () => await acpManager.cancelSession({
|
|
1307
|
+
cfg: params.cfg,
|
|
1308
|
+
sessionKey,
|
|
1309
|
+
reason: "manual-cancel"
|
|
1310
|
+
}),
|
|
1311
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1312
|
+
fallbackMessage: "ACP cancel failed before completion.",
|
|
1313
|
+
onSuccess: () => stopWithText$1(`✅ Cancel requested for ACP session ${sessionKey}.`)
|
|
1314
|
+
})
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
async function runAcpSteer(params) {
|
|
1318
|
+
const acpManager = getAcpSessionManager();
|
|
1319
|
+
let output = "";
|
|
1320
|
+
await acpManager.runTurn({
|
|
1321
|
+
cfg: params.cfg,
|
|
1322
|
+
sessionKey: params.sessionKey,
|
|
1323
|
+
text: params.instruction,
|
|
1324
|
+
mode: "steer",
|
|
1325
|
+
requestId: params.requestId,
|
|
1326
|
+
onEvent: (event) => {
|
|
1327
|
+
if (event.type !== "text_delta") return;
|
|
1328
|
+
if (event.stream && event.stream !== "output") return;
|
|
1329
|
+
if (event.text) {
|
|
1330
|
+
output += event.text;
|
|
1331
|
+
if (output.length > 800) output = `${output.slice(0, 800)}…`;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
return output.trim();
|
|
1336
|
+
}
|
|
1337
|
+
async function handleAcpSteerAction(params, restTokens) {
|
|
1338
|
+
const dispatchPolicyError = resolveAcpDispatchPolicyError(params.cfg);
|
|
1339
|
+
if (dispatchPolicyError) return stopWithText$1(collectAcpErrorText({
|
|
1340
|
+
error: dispatchPolicyError,
|
|
1341
|
+
fallbackCode: "ACP_DISPATCH_DISABLED",
|
|
1342
|
+
fallbackMessage: dispatchPolicyError.message
|
|
1343
|
+
}));
|
|
1344
|
+
const parsed = parseSteerInput(restTokens);
|
|
1345
|
+
if (!parsed.ok) return stopWithText$1(`⚠️ ${parsed.error}`);
|
|
1346
|
+
const acpManager = getAcpSessionManager();
|
|
1347
|
+
const target = await resolveAcpTargetSessionKey({
|
|
1348
|
+
commandParams: params,
|
|
1349
|
+
token: parsed.value.sessionToken
|
|
1350
|
+
});
|
|
1351
|
+
if (!target.ok) return stopWithText$1(`⚠️ ${target.error}`);
|
|
1352
|
+
const guardFailure = resolveAcpSessionForCommandOrStop({
|
|
1353
|
+
acpManager,
|
|
1354
|
+
cfg: params.cfg,
|
|
1355
|
+
sessionKey: target.sessionKey
|
|
1356
|
+
});
|
|
1357
|
+
if (guardFailure) return guardFailure;
|
|
1358
|
+
return await withAcpCommandErrorBoundary({
|
|
1359
|
+
run: async () => await runAcpSteer({
|
|
1360
|
+
cfg: params.cfg,
|
|
1361
|
+
sessionKey: target.sessionKey,
|
|
1362
|
+
instruction: parsed.value.instruction,
|
|
1363
|
+
requestId: `${resolveCommandRequestId(params)}:steer`
|
|
1364
|
+
}),
|
|
1365
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1366
|
+
fallbackMessage: "ACP steer failed before completion.",
|
|
1367
|
+
onSuccess: (steerOutput) => {
|
|
1368
|
+
if (!steerOutput) return stopWithText$1(`✅ ACP steer sent to ${target.sessionKey}.`);
|
|
1369
|
+
return stopWithText$1(`✅ ACP steer sent to ${target.sessionKey}.\n${steerOutput}`);
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
async function handleAcpCloseAction(params, restTokens) {
|
|
1374
|
+
return await withResolvedAcpSessionTarget({
|
|
1375
|
+
commandParams: params,
|
|
1376
|
+
restTokens,
|
|
1377
|
+
run: async ({ acpManager, sessionKey }) => {
|
|
1378
|
+
let runtimeNotice = "";
|
|
1379
|
+
try {
|
|
1380
|
+
const closed = await acpManager.closeSession({
|
|
1381
|
+
cfg: params.cfg,
|
|
1382
|
+
sessionKey,
|
|
1383
|
+
reason: "manual-close",
|
|
1384
|
+
allowBackendUnavailable: true,
|
|
1385
|
+
clearMeta: true
|
|
1386
|
+
});
|
|
1387
|
+
runtimeNotice = closed.runtimeNotice ? ` (${closed.runtimeNotice})` : "";
|
|
1388
|
+
} catch (error) {
|
|
1389
|
+
return stopWithText$1(collectAcpErrorText({
|
|
1390
|
+
error,
|
|
1391
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1392
|
+
fallbackMessage: "ACP close failed before completion."
|
|
1393
|
+
}));
|
|
1394
|
+
}
|
|
1395
|
+
const removedBindings = await getSessionBindingService().unbind({
|
|
1396
|
+
targetSessionKey: sessionKey,
|
|
1397
|
+
reason: "manual"
|
|
1398
|
+
});
|
|
1399
|
+
return stopWithText$1(`✅ Closed ACP session ${sessionKey}${runtimeNotice}. Removed ${removedBindings.length} binding${removedBindings.length === 1 ? "" : "s"}.`);
|
|
1400
|
+
}
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
//#endregion
|
|
1404
|
+
//#region src/auto-reply/reply/commands-acp/runtime-options.ts
|
|
1405
|
+
async function resolveTargetSessionKeyOrStop(params) {
|
|
1406
|
+
const target = await resolveAcpTargetSessionKey({
|
|
1407
|
+
commandParams: params.commandParams,
|
|
1408
|
+
token: params.token
|
|
1409
|
+
});
|
|
1410
|
+
if (!target.ok) return stopWithText$1(`⚠️ ${target.error}`);
|
|
1411
|
+
return target.sessionKey;
|
|
1412
|
+
}
|
|
1413
|
+
async function resolveOptionalSingleTargetOrStop(params) {
|
|
1414
|
+
const parsed = parseOptionalSingleTarget(params.restTokens, params.usage);
|
|
1415
|
+
if (!parsed.ok) return stopWithText$1(`⚠️ ${parsed.error}`);
|
|
1416
|
+
return await resolveTargetSessionKeyOrStop({
|
|
1417
|
+
commandParams: params.commandParams,
|
|
1418
|
+
token: parsed.sessionToken
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
async function resolveSingleTargetValueOrStop(params) {
|
|
1422
|
+
const parsed = parseSingleValueCommandInput(params.restTokens, params.usage);
|
|
1423
|
+
if (!parsed.ok) return stopWithText$1(`⚠️ ${parsed.error}`);
|
|
1424
|
+
const targetSessionKey = await resolveTargetSessionKeyOrStop({
|
|
1425
|
+
commandParams: params.commandParams,
|
|
1426
|
+
token: parsed.value.sessionToken
|
|
1427
|
+
});
|
|
1428
|
+
if (typeof targetSessionKey !== "string") return targetSessionKey;
|
|
1429
|
+
return {
|
|
1430
|
+
targetSessionKey,
|
|
1431
|
+
value: parsed.value.value
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
async function withSingleTargetValue(params) {
|
|
1435
|
+
const resolved = await resolveSingleTargetValueOrStop({
|
|
1436
|
+
commandParams: params.commandParams,
|
|
1437
|
+
restTokens: params.restTokens,
|
|
1438
|
+
usage: params.usage
|
|
1439
|
+
});
|
|
1440
|
+
if (!("targetSessionKey" in resolved)) return resolved;
|
|
1441
|
+
return await params.run(resolved);
|
|
1442
|
+
}
|
|
1443
|
+
async function handleAcpStatusAction(params, restTokens) {
|
|
1444
|
+
const targetSessionKey = await resolveOptionalSingleTargetOrStop({
|
|
1445
|
+
commandParams: params,
|
|
1446
|
+
restTokens,
|
|
1447
|
+
usage: ACP_STATUS_USAGE
|
|
1448
|
+
});
|
|
1449
|
+
if (typeof targetSessionKey !== "string") return targetSessionKey;
|
|
1450
|
+
return await withAcpCommandErrorBoundary({
|
|
1451
|
+
run: async () => await getAcpSessionManager().getSessionStatus({
|
|
1452
|
+
cfg: params.cfg,
|
|
1453
|
+
sessionKey: targetSessionKey
|
|
1454
|
+
}),
|
|
1455
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1456
|
+
fallbackMessage: "Could not read ACP session status.",
|
|
1457
|
+
onSuccess: (status) => {
|
|
1458
|
+
const sessionIdentifierLines = resolveAcpSessionIdentifierLinesFromIdentity({
|
|
1459
|
+
backend: status.backend,
|
|
1460
|
+
identity: status.identity
|
|
1461
|
+
});
|
|
1462
|
+
return stopWithText$1([
|
|
1463
|
+
"ACP status:",
|
|
1464
|
+
"-----",
|
|
1465
|
+
`session: ${status.sessionKey}`,
|
|
1466
|
+
`backend: ${status.backend}`,
|
|
1467
|
+
`agent: ${status.agent}`,
|
|
1468
|
+
...sessionIdentifierLines,
|
|
1469
|
+
`sessionMode: ${status.mode}`,
|
|
1470
|
+
`state: ${status.state}`,
|
|
1471
|
+
`runtimeOptions: ${formatRuntimeOptionsText(status.runtimeOptions)}`,
|
|
1472
|
+
`capabilities: ${formatAcpCapabilitiesText(status.capabilities.controls)}`,
|
|
1473
|
+
`lastActivityAt: ${new Date(status.lastActivityAt).toISOString()}`,
|
|
1474
|
+
...status.lastError ? [`lastError: ${status.lastError}`] : [],
|
|
1475
|
+
...status.runtimeStatus?.summary ? [`runtime: ${status.runtimeStatus.summary}`] : [],
|
|
1476
|
+
...status.runtimeStatus?.details ? [`runtimeDetails: ${JSON.stringify(status.runtimeStatus.details)}`] : []
|
|
1477
|
+
].join("\n"));
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
async function handleAcpSetModeAction(params, restTokens) {
|
|
1482
|
+
return await withSingleTargetValue({
|
|
1483
|
+
commandParams: params,
|
|
1484
|
+
restTokens,
|
|
1485
|
+
usage: ACP_SET_MODE_USAGE,
|
|
1486
|
+
run: async ({ targetSessionKey, value }) => await withAcpCommandErrorBoundary({
|
|
1487
|
+
run: async () => {
|
|
1488
|
+
const runtimeMode = validateRuntimeModeInput(value);
|
|
1489
|
+
return {
|
|
1490
|
+
runtimeMode,
|
|
1491
|
+
options: await getAcpSessionManager().setSessionRuntimeMode({
|
|
1492
|
+
cfg: params.cfg,
|
|
1493
|
+
sessionKey: targetSessionKey,
|
|
1494
|
+
runtimeMode
|
|
1495
|
+
})
|
|
1496
|
+
};
|
|
1497
|
+
},
|
|
1498
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1499
|
+
fallbackMessage: "Could not update ACP runtime mode.",
|
|
1500
|
+
onSuccess: ({ runtimeMode, options }) => stopWithText$1(`✅ Updated ACP runtime mode for ${targetSessionKey}: ${runtimeMode}. Effective options: ${formatRuntimeOptionsText(options)}`)
|
|
1501
|
+
})
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
async function handleAcpSetAction(params, restTokens) {
|
|
1505
|
+
const parsed = parseSetCommandInput(restTokens);
|
|
1506
|
+
if (!parsed.ok) return stopWithText$1(`⚠️ ${parsed.error}`);
|
|
1507
|
+
const target = await resolveAcpTargetSessionKey({
|
|
1508
|
+
commandParams: params,
|
|
1509
|
+
token: parsed.value.sessionToken
|
|
1510
|
+
});
|
|
1511
|
+
if (!target.ok) return stopWithText$1(`⚠️ ${target.error}`);
|
|
1512
|
+
const key = parsed.value.key.trim();
|
|
1513
|
+
const value = parsed.value.value.trim();
|
|
1514
|
+
return await withAcpCommandErrorBoundary({
|
|
1515
|
+
run: async () => {
|
|
1516
|
+
if (key.toLowerCase() === "cwd") {
|
|
1517
|
+
const cwd = validateRuntimeCwdInput(value);
|
|
1518
|
+
const options = await getAcpSessionManager().updateSessionRuntimeOptions({
|
|
1519
|
+
cfg: params.cfg,
|
|
1520
|
+
sessionKey: target.sessionKey,
|
|
1521
|
+
patch: { cwd }
|
|
1522
|
+
});
|
|
1523
|
+
return { text: `✅ Updated ACP cwd for ${target.sessionKey}: ${cwd}. Effective options: ${formatRuntimeOptionsText(options)}` };
|
|
1524
|
+
}
|
|
1525
|
+
const validated = validateRuntimeConfigOptionInput(key, value);
|
|
1526
|
+
const options = await getAcpSessionManager().setSessionConfigOption({
|
|
1527
|
+
cfg: params.cfg,
|
|
1528
|
+
sessionKey: target.sessionKey,
|
|
1529
|
+
key: validated.key,
|
|
1530
|
+
value: validated.value
|
|
1531
|
+
});
|
|
1532
|
+
return { text: `✅ Updated ACP config option for ${target.sessionKey}: ${validated.key}=${validated.value}. Effective options: ${formatRuntimeOptionsText(options)}` };
|
|
1533
|
+
},
|
|
1534
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1535
|
+
fallbackMessage: "Could not update ACP config option.",
|
|
1536
|
+
onSuccess: ({ text }) => stopWithText$1(text)
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
async function handleAcpCwdAction(params, restTokens) {
|
|
1540
|
+
return await withSingleTargetValue({
|
|
1541
|
+
commandParams: params,
|
|
1542
|
+
restTokens,
|
|
1543
|
+
usage: ACP_CWD_USAGE,
|
|
1544
|
+
run: async ({ targetSessionKey, value }) => await withAcpCommandErrorBoundary({
|
|
1545
|
+
run: async () => {
|
|
1546
|
+
const cwd = validateRuntimeCwdInput(value);
|
|
1547
|
+
return {
|
|
1548
|
+
cwd,
|
|
1549
|
+
options: await getAcpSessionManager().updateSessionRuntimeOptions({
|
|
1550
|
+
cfg: params.cfg,
|
|
1551
|
+
sessionKey: targetSessionKey,
|
|
1552
|
+
patch: { cwd }
|
|
1553
|
+
})
|
|
1554
|
+
};
|
|
1555
|
+
},
|
|
1556
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1557
|
+
fallbackMessage: "Could not update ACP cwd.",
|
|
1558
|
+
onSuccess: ({ cwd, options }) => stopWithText$1(`✅ Updated ACP cwd for ${targetSessionKey}: ${cwd}. Effective options: ${formatRuntimeOptionsText(options)}`)
|
|
1559
|
+
})
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
async function handleAcpPermissionsAction(params, restTokens) {
|
|
1563
|
+
return await withSingleTargetValue({
|
|
1564
|
+
commandParams: params,
|
|
1565
|
+
restTokens,
|
|
1566
|
+
usage: ACP_PERMISSIONS_USAGE,
|
|
1567
|
+
run: async ({ targetSessionKey, value }) => await withAcpCommandErrorBoundary({
|
|
1568
|
+
run: async () => {
|
|
1569
|
+
const permissionProfile = validateRuntimePermissionProfileInput(value);
|
|
1570
|
+
return {
|
|
1571
|
+
permissionProfile,
|
|
1572
|
+
options: await getAcpSessionManager().setSessionConfigOption({
|
|
1573
|
+
cfg: params.cfg,
|
|
1574
|
+
sessionKey: targetSessionKey,
|
|
1575
|
+
key: "approval_policy",
|
|
1576
|
+
value: permissionProfile
|
|
1577
|
+
})
|
|
1578
|
+
};
|
|
1579
|
+
},
|
|
1580
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1581
|
+
fallbackMessage: "Could not update ACP permissions profile.",
|
|
1582
|
+
onSuccess: ({ permissionProfile, options }) => stopWithText$1(`✅ Updated ACP permissions profile for ${targetSessionKey}: ${permissionProfile}. Effective options: ${formatRuntimeOptionsText(options)}`)
|
|
1583
|
+
})
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
async function handleAcpTimeoutAction(params, restTokens) {
|
|
1587
|
+
return await withSingleTargetValue({
|
|
1588
|
+
commandParams: params,
|
|
1589
|
+
restTokens,
|
|
1590
|
+
usage: ACP_TIMEOUT_USAGE,
|
|
1591
|
+
run: async ({ targetSessionKey, value }) => await withAcpCommandErrorBoundary({
|
|
1592
|
+
run: async () => {
|
|
1593
|
+
const timeoutSeconds = parseRuntimeTimeoutSecondsInput(value);
|
|
1594
|
+
return {
|
|
1595
|
+
timeoutSeconds,
|
|
1596
|
+
options: await getAcpSessionManager().setSessionConfigOption({
|
|
1597
|
+
cfg: params.cfg,
|
|
1598
|
+
sessionKey: targetSessionKey,
|
|
1599
|
+
key: "timeout",
|
|
1600
|
+
value: String(timeoutSeconds)
|
|
1601
|
+
})
|
|
1602
|
+
};
|
|
1603
|
+
},
|
|
1604
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1605
|
+
fallbackMessage: "Could not update ACP timeout.",
|
|
1606
|
+
onSuccess: ({ timeoutSeconds, options }) => stopWithText$1(`✅ Updated ACP timeout for ${targetSessionKey}: ${timeoutSeconds}s. Effective options: ${formatRuntimeOptionsText(options)}`)
|
|
1607
|
+
})
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
async function handleAcpModelAction(params, restTokens) {
|
|
1611
|
+
return await withSingleTargetValue({
|
|
1612
|
+
commandParams: params,
|
|
1613
|
+
restTokens,
|
|
1614
|
+
usage: ACP_MODEL_USAGE,
|
|
1615
|
+
run: async ({ targetSessionKey, value }) => await withAcpCommandErrorBoundary({
|
|
1616
|
+
run: async () => {
|
|
1617
|
+
const model = validateRuntimeModelInput(value);
|
|
1618
|
+
return {
|
|
1619
|
+
model,
|
|
1620
|
+
options: await getAcpSessionManager().setSessionConfigOption({
|
|
1621
|
+
cfg: params.cfg,
|
|
1622
|
+
sessionKey: targetSessionKey,
|
|
1623
|
+
key: "model",
|
|
1624
|
+
value: model
|
|
1625
|
+
})
|
|
1626
|
+
};
|
|
1627
|
+
},
|
|
1628
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1629
|
+
fallbackMessage: "Could not update ACP model.",
|
|
1630
|
+
onSuccess: ({ model, options }) => stopWithText$1(`✅ Updated ACP model for ${targetSessionKey}: ${model}. Effective options: ${formatRuntimeOptionsText(options)}`)
|
|
1631
|
+
})
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
async function handleAcpResetOptionsAction(params, restTokens) {
|
|
1635
|
+
const targetSessionKey = await resolveOptionalSingleTargetOrStop({
|
|
1636
|
+
commandParams: params,
|
|
1637
|
+
restTokens,
|
|
1638
|
+
usage: ACP_RESET_OPTIONS_USAGE
|
|
1639
|
+
});
|
|
1640
|
+
if (typeof targetSessionKey !== "string") return targetSessionKey;
|
|
1641
|
+
return await withAcpCommandErrorBoundary({
|
|
1642
|
+
run: async () => await getAcpSessionManager().resetSessionRuntimeOptions({
|
|
1643
|
+
cfg: params.cfg,
|
|
1644
|
+
sessionKey: targetSessionKey
|
|
1645
|
+
}),
|
|
1646
|
+
fallbackCode: "ACP_TURN_FAILED",
|
|
1647
|
+
fallbackMessage: "Could not reset ACP runtime options.",
|
|
1648
|
+
onSuccess: () => stopWithText$1(`✅ Reset ACP runtime options for ${targetSessionKey}.`)
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1651
|
+
//#endregion
|
|
1652
|
+
//#region src/auto-reply/reply/commands-acp.ts
|
|
1653
|
+
const ACP_ACTION_HANDLERS = {
|
|
1654
|
+
spawn: handleAcpSpawnAction,
|
|
1655
|
+
cancel: handleAcpCancelAction,
|
|
1656
|
+
steer: handleAcpSteerAction,
|
|
1657
|
+
close: handleAcpCloseAction,
|
|
1658
|
+
status: handleAcpStatusAction,
|
|
1659
|
+
"set-mode": handleAcpSetModeAction,
|
|
1660
|
+
set: handleAcpSetAction,
|
|
1661
|
+
cwd: handleAcpCwdAction,
|
|
1662
|
+
permissions: handleAcpPermissionsAction,
|
|
1663
|
+
timeout: handleAcpTimeoutAction,
|
|
1664
|
+
model: handleAcpModelAction,
|
|
1665
|
+
"reset-options": handleAcpResetOptionsAction,
|
|
1666
|
+
doctor: handleAcpDoctorAction,
|
|
1667
|
+
install: async (params, tokens) => handleAcpInstallAction(params, tokens),
|
|
1668
|
+
sessions: async (params, tokens) => handleAcpSessionsAction(params, tokens)
|
|
1669
|
+
};
|
|
1670
|
+
const ACP_MUTATING_ACTIONS = new Set([
|
|
1671
|
+
"spawn",
|
|
1672
|
+
"cancel",
|
|
1673
|
+
"steer",
|
|
1674
|
+
"close",
|
|
1675
|
+
"status",
|
|
1676
|
+
"set-mode",
|
|
1677
|
+
"set",
|
|
1678
|
+
"cwd",
|
|
1679
|
+
"permissions",
|
|
1680
|
+
"timeout",
|
|
1681
|
+
"model",
|
|
1682
|
+
"reset-options"
|
|
1683
|
+
]);
|
|
1684
|
+
const handleAcpCommand = async (params, allowTextCommands) => {
|
|
1685
|
+
if (!allowTextCommands) return null;
|
|
1686
|
+
const normalized = params.command.commandBodyNormalized;
|
|
1687
|
+
if (!normalized.startsWith("/acp")) return null;
|
|
1688
|
+
if (!params.command.isAuthorizedSender) {
|
|
1689
|
+
logVerbose(`Ignoring /acp from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
1690
|
+
return { shouldContinue: false };
|
|
1691
|
+
}
|
|
1692
|
+
const tokens = normalized.slice(4).trim().split(/\s+/).filter(Boolean);
|
|
1693
|
+
const action = resolveAcpAction(tokens);
|
|
1694
|
+
if (action === "help") return stopWithText$1(resolveAcpHelpText());
|
|
1695
|
+
if (ACP_MUTATING_ACTIONS.has(action)) {
|
|
1696
|
+
const scopeBlock = requireGatewayClientScopeForInternalChannel(params, {
|
|
1697
|
+
label: "/acp",
|
|
1698
|
+
allowedScopes: ["operator.admin"],
|
|
1699
|
+
missingText: "This /acp action requires operator.admin on the internal channel."
|
|
1700
|
+
});
|
|
1701
|
+
if (scopeBlock) return scopeBlock;
|
|
1702
|
+
}
|
|
1703
|
+
const handler = ACP_ACTION_HANDLERS[action];
|
|
1704
|
+
return handler ? await handler(params, tokens) : stopWithText$1(resolveAcpHelpText());
|
|
1705
|
+
};
|
|
1706
|
+
//#endregion
|
|
1707
|
+
//#region src/auto-reply/reply/config-write-authorization.ts
|
|
1708
|
+
function resolveConfigWriteDeniedText(params) {
|
|
1709
|
+
const writeAuth = authorizeConfigWrite({
|
|
1710
|
+
cfg: params.cfg,
|
|
1711
|
+
origin: {
|
|
1712
|
+
channelId: params.channelId,
|
|
1713
|
+
accountId: params.accountId
|
|
1714
|
+
},
|
|
1715
|
+
target: params.target,
|
|
1716
|
+
allowBypass: canBypassConfigWritePolicy({
|
|
1717
|
+
channel: params.channel ?? "",
|
|
1718
|
+
gatewayClientScopes: params.gatewayClientScopes
|
|
1719
|
+
})
|
|
1720
|
+
});
|
|
1721
|
+
if (writeAuth.allowed) return null;
|
|
1722
|
+
return formatConfigWriteDeniedMessage({
|
|
1723
|
+
result: writeAuth,
|
|
1724
|
+
fallbackChannelId: params.channelId
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
//#endregion
|
|
1728
|
+
//#region src/auto-reply/reply/commands-allowlist.ts
|
|
1729
|
+
const ACTIONS = new Set([
|
|
1730
|
+
"list",
|
|
1731
|
+
"add",
|
|
1732
|
+
"remove"
|
|
1733
|
+
]);
|
|
1734
|
+
const SCOPES = new Set([
|
|
1735
|
+
"dm",
|
|
1736
|
+
"group",
|
|
1737
|
+
"all"
|
|
1738
|
+
]);
|
|
1739
|
+
function parseAllowlistCommand(raw) {
|
|
1740
|
+
const trimmed = raw.trim();
|
|
1741
|
+
if (!trimmed.toLowerCase().startsWith("/allowlist")) return null;
|
|
1742
|
+
const rest = trimmed.slice(10).trim();
|
|
1743
|
+
if (!rest) return {
|
|
1744
|
+
action: "list",
|
|
1745
|
+
scope: "dm"
|
|
1746
|
+
};
|
|
1747
|
+
const tokens = rest.split(/\s+/);
|
|
1748
|
+
let action = "list";
|
|
1749
|
+
let scope = "dm";
|
|
1750
|
+
let resolve = false;
|
|
1751
|
+
let target = "both";
|
|
1752
|
+
let channel;
|
|
1753
|
+
let account;
|
|
1754
|
+
const entryTokens = [];
|
|
1755
|
+
let i = 0;
|
|
1756
|
+
if (tokens[i] && ACTIONS.has(tokens[i].toLowerCase())) {
|
|
1757
|
+
action = tokens[i].toLowerCase();
|
|
1758
|
+
i += 1;
|
|
1759
|
+
}
|
|
1760
|
+
if (tokens[i] && SCOPES.has(tokens[i].toLowerCase())) {
|
|
1761
|
+
scope = tokens[i].toLowerCase();
|
|
1762
|
+
i += 1;
|
|
1763
|
+
}
|
|
1764
|
+
for (; i < tokens.length; i += 1) {
|
|
1765
|
+
const token = tokens[i];
|
|
1766
|
+
const lowered = token.toLowerCase();
|
|
1767
|
+
if (lowered === "--resolve" || lowered === "resolve") {
|
|
1768
|
+
resolve = true;
|
|
1769
|
+
continue;
|
|
1770
|
+
}
|
|
1771
|
+
if (lowered === "--config" || lowered === "config") {
|
|
1772
|
+
target = "config";
|
|
1773
|
+
continue;
|
|
1774
|
+
}
|
|
1775
|
+
if (lowered === "--store" || lowered === "store") {
|
|
1776
|
+
target = "store";
|
|
1777
|
+
continue;
|
|
1778
|
+
}
|
|
1779
|
+
if (lowered === "--channel" && tokens[i + 1]) {
|
|
1780
|
+
channel = tokens[i + 1];
|
|
1781
|
+
i += 1;
|
|
1782
|
+
continue;
|
|
1783
|
+
}
|
|
1784
|
+
if (lowered === "--account" && tokens[i + 1]) {
|
|
1785
|
+
account = tokens[i + 1];
|
|
1786
|
+
i += 1;
|
|
1787
|
+
continue;
|
|
1788
|
+
}
|
|
1789
|
+
const kv = token.split("=");
|
|
1790
|
+
if (kv.length === 2) {
|
|
1791
|
+
const key = kv[0]?.trim().toLowerCase();
|
|
1792
|
+
const value = kv[1]?.trim();
|
|
1793
|
+
if (key === "channel") {
|
|
1794
|
+
if (value) channel = value;
|
|
1795
|
+
continue;
|
|
1796
|
+
}
|
|
1797
|
+
if (key === "account") {
|
|
1798
|
+
if (value) account = value;
|
|
1799
|
+
continue;
|
|
1800
|
+
}
|
|
1801
|
+
if (key === "scope" && value && SCOPES.has(value.toLowerCase())) {
|
|
1802
|
+
scope = value.toLowerCase();
|
|
1803
|
+
continue;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
entryTokens.push(token);
|
|
1807
|
+
}
|
|
1808
|
+
if (action === "add" || action === "remove") {
|
|
1809
|
+
const entry = entryTokens.join(" ").trim();
|
|
1810
|
+
if (!entry) return {
|
|
1811
|
+
action: "error",
|
|
1812
|
+
message: "Usage: /allowlist add|remove <entry>"
|
|
1813
|
+
};
|
|
1814
|
+
return {
|
|
1815
|
+
action,
|
|
1816
|
+
scope,
|
|
1817
|
+
entry,
|
|
1818
|
+
channel,
|
|
1819
|
+
account,
|
|
1820
|
+
resolve,
|
|
1821
|
+
target
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
return {
|
|
1825
|
+
action: "list",
|
|
1826
|
+
scope,
|
|
1827
|
+
channel,
|
|
1828
|
+
account,
|
|
1829
|
+
resolve
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
function normalizeAllowFrom(params) {
|
|
1833
|
+
const plugin = getChannelPlugin(params.channelId);
|
|
1834
|
+
if (plugin?.config.formatAllowFrom) return plugin.config.formatAllowFrom({
|
|
1835
|
+
cfg: params.cfg,
|
|
1836
|
+
accountId: params.accountId,
|
|
1837
|
+
allowFrom: params.values
|
|
1838
|
+
});
|
|
1839
|
+
return normalizeStringEntries(params.values);
|
|
1840
|
+
}
|
|
1841
|
+
function formatEntryList(entries, resolved) {
|
|
1842
|
+
if (entries.length === 0) return "(none)";
|
|
1843
|
+
return entries.map((entry) => {
|
|
1844
|
+
const name = resolved?.get(entry);
|
|
1845
|
+
return name ? `${entry} (${name})` : entry;
|
|
1846
|
+
}).join(", ");
|
|
1847
|
+
}
|
|
1848
|
+
async function updatePairingStoreAllowlist(params) {
|
|
1849
|
+
const storeEntry = {
|
|
1850
|
+
channel: params.channelId,
|
|
1851
|
+
entry: params.entry,
|
|
1852
|
+
accountId: params.accountId
|
|
1853
|
+
};
|
|
1854
|
+
if (params.action === "add") {
|
|
1855
|
+
await addChannelAllowFromStoreEntry(storeEntry);
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
await removeChannelAllowFromStoreEntry(storeEntry);
|
|
1859
|
+
if (params.accountId === "default") await removeChannelAllowFromStoreEntry({
|
|
1860
|
+
channel: params.channelId,
|
|
1861
|
+
entry: params.entry
|
|
1862
|
+
});
|
|
1863
|
+
}
|
|
1864
|
+
function mapResolvedAllowlistNames(entries) {
|
|
1865
|
+
const map = /* @__PURE__ */ new Map();
|
|
1866
|
+
for (const entry of entries) if (entry.resolved && entry.name) map.set(entry.input, entry.name);
|
|
1867
|
+
return map;
|
|
1868
|
+
}
|
|
1869
|
+
async function resolveAllowlistNames(params) {
|
|
1870
|
+
return mapResolvedAllowlistNames(await getChannelPlugin(params.channelId)?.allowlist?.resolveNames?.({
|
|
1871
|
+
cfg: params.cfg,
|
|
1872
|
+
accountId: params.accountId,
|
|
1873
|
+
scope: params.scope,
|
|
1874
|
+
entries: params.entries
|
|
1875
|
+
}) ?? []);
|
|
1876
|
+
}
|
|
1877
|
+
async function readAllowlistConfig(params) {
|
|
1878
|
+
return await getChannelPlugin(params.channelId)?.allowlist?.readConfig?.({
|
|
1879
|
+
cfg: params.cfg,
|
|
1880
|
+
accountId: params.accountId
|
|
1881
|
+
}) ?? {};
|
|
1882
|
+
}
|
|
1883
|
+
const handleAllowlistCommand = async (params, allowTextCommands) => {
|
|
1884
|
+
if (!allowTextCommands) return null;
|
|
1885
|
+
const parsed = parseAllowlistCommand(params.command.commandBodyNormalized);
|
|
1886
|
+
if (!parsed) return null;
|
|
1887
|
+
if (parsed.action === "error") return {
|
|
1888
|
+
shouldContinue: false,
|
|
1889
|
+
reply: { text: `⚠️ ${parsed.message}` }
|
|
1890
|
+
};
|
|
1891
|
+
const unauthorized = rejectUnauthorizedCommand(params, "/allowlist");
|
|
1892
|
+
if (unauthorized) return unauthorized;
|
|
1893
|
+
const channelId = normalizeChannelId(parsed.channel) ?? params.command.channelId ?? normalizeChannelId(params.command.channel);
|
|
1894
|
+
if (!channelId) return {
|
|
1895
|
+
shouldContinue: false,
|
|
1896
|
+
reply: { text: "⚠️ Unknown channel. Add channel=<id> to the command." }
|
|
1897
|
+
};
|
|
1898
|
+
if (parsed.account?.trim() && !normalizeOptionalAccountId(parsed.account)) return {
|
|
1899
|
+
shouldContinue: false,
|
|
1900
|
+
reply: { text: "⚠️ Invalid account id. Reserved keys (__proto__, constructor, prototype) are blocked." }
|
|
1901
|
+
};
|
|
1902
|
+
const accountId = normalizeAccountId(parsed.account ?? params.ctx.AccountId);
|
|
1903
|
+
const plugin = getChannelPlugin(channelId);
|
|
1904
|
+
if (parsed.action === "list") {
|
|
1905
|
+
const supportsStore = Boolean(plugin?.pairing);
|
|
1906
|
+
if (!plugin?.allowlist?.readConfig && !supportsStore) return {
|
|
1907
|
+
shouldContinue: false,
|
|
1908
|
+
reply: { text: `⚠️ ${channelId} does not expose allowlist configuration.` }
|
|
1909
|
+
};
|
|
1910
|
+
const storeAllowFrom = supportsStore ? await readChannelAllowFromStore(channelId, process.env, accountId).catch(() => []) : [];
|
|
1911
|
+
const configState = await readAllowlistConfig({
|
|
1912
|
+
cfg: params.cfg,
|
|
1913
|
+
channelId,
|
|
1914
|
+
accountId
|
|
1915
|
+
});
|
|
1916
|
+
const dmAllowFrom = (configState.dmAllowFrom ?? []).map(String);
|
|
1917
|
+
const groupAllowFrom = (configState.groupAllowFrom ?? []).map(String);
|
|
1918
|
+
const groupOverrides = (configState.groupOverrides ?? []).map((entry) => ({
|
|
1919
|
+
label: entry.label,
|
|
1920
|
+
entries: entry.entries.map(String).filter(Boolean)
|
|
1921
|
+
}));
|
|
1922
|
+
const dmDisplay = normalizeAllowFrom({
|
|
1923
|
+
cfg: params.cfg,
|
|
1924
|
+
channelId,
|
|
1925
|
+
accountId,
|
|
1926
|
+
values: dmAllowFrom
|
|
1927
|
+
});
|
|
1928
|
+
const groupDisplay = normalizeAllowFrom({
|
|
1929
|
+
cfg: params.cfg,
|
|
1930
|
+
channelId,
|
|
1931
|
+
accountId,
|
|
1932
|
+
values: groupAllowFrom
|
|
1933
|
+
});
|
|
1934
|
+
const groupOverrideEntries = groupOverrides.flatMap((entry) => entry.entries);
|
|
1935
|
+
const groupOverrideDisplay = normalizeAllowFrom({
|
|
1936
|
+
cfg: params.cfg,
|
|
1937
|
+
channelId,
|
|
1938
|
+
accountId,
|
|
1939
|
+
values: groupOverrideEntries
|
|
1940
|
+
});
|
|
1941
|
+
const resolvedDm = parsed.resolve && dmDisplay.length > 0 ? await resolveAllowlistNames({
|
|
1942
|
+
cfg: params.cfg,
|
|
1943
|
+
channelId,
|
|
1944
|
+
accountId,
|
|
1945
|
+
scope: "dm",
|
|
1946
|
+
entries: dmDisplay
|
|
1947
|
+
}) : void 0;
|
|
1948
|
+
const resolvedGroup = parsed.resolve && groupOverrideDisplay.length > 0 ? await resolveAllowlistNames({
|
|
1949
|
+
cfg: params.cfg,
|
|
1950
|
+
channelId,
|
|
1951
|
+
accountId,
|
|
1952
|
+
scope: "group",
|
|
1953
|
+
entries: groupOverrideDisplay
|
|
1954
|
+
}) : void 0;
|
|
1955
|
+
const lines = ["🧾 Allowlist"];
|
|
1956
|
+
lines.push(`Channel: ${channelId}${accountId ? ` (account ${accountId})` : ""}`);
|
|
1957
|
+
if (configState.dmPolicy) lines.push(`DM policy: ${configState.dmPolicy}`);
|
|
1958
|
+
if (configState.groupPolicy) lines.push(`Group policy: ${configState.groupPolicy}`);
|
|
1959
|
+
const showDm = parsed.scope === "dm" || parsed.scope === "all";
|
|
1960
|
+
const showGroup = parsed.scope === "group" || parsed.scope === "all";
|
|
1961
|
+
if (showDm) lines.push(`DM allowFrom (config): ${formatEntryList(dmDisplay, resolvedDm)}`);
|
|
1962
|
+
if (supportsStore && storeAllowFrom.length > 0) {
|
|
1963
|
+
const storeLabel = normalizeAllowFrom({
|
|
1964
|
+
cfg: params.cfg,
|
|
1965
|
+
channelId,
|
|
1966
|
+
accountId,
|
|
1967
|
+
values: storeAllowFrom
|
|
1968
|
+
});
|
|
1969
|
+
lines.push(`Paired allowFrom (store): ${formatEntryList(storeLabel)}`);
|
|
1970
|
+
}
|
|
1971
|
+
if (showGroup) {
|
|
1972
|
+
if (groupAllowFrom.length > 0) lines.push(`Group allowFrom (config): ${formatEntryList(groupDisplay, resolvedGroup)}`);
|
|
1973
|
+
if (groupOverrides.length > 0) {
|
|
1974
|
+
lines.push("Group overrides:");
|
|
1975
|
+
for (const entry of groupOverrides) {
|
|
1976
|
+
const normalized = normalizeAllowFrom({
|
|
1977
|
+
cfg: params.cfg,
|
|
1978
|
+
channelId,
|
|
1979
|
+
accountId,
|
|
1980
|
+
values: entry.entries
|
|
1981
|
+
});
|
|
1982
|
+
lines.push(`- ${entry.label}: ${formatEntryList(normalized, resolvedGroup)}`);
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
return {
|
|
1987
|
+
shouldContinue: false,
|
|
1988
|
+
reply: { text: lines.join("\n") }
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
const disabled = requireCommandFlagEnabled(params.cfg, {
|
|
1992
|
+
label: "/allowlist edits",
|
|
1993
|
+
configKey: "config",
|
|
1994
|
+
disabledVerb: "are"
|
|
1995
|
+
});
|
|
1996
|
+
if (disabled) return disabled;
|
|
1997
|
+
const shouldUpdateConfig = parsed.target !== "store";
|
|
1998
|
+
const shouldTouchStore = parsed.target !== "config" && Boolean(plugin?.pairing);
|
|
1999
|
+
if (shouldUpdateConfig) {
|
|
2000
|
+
if (parsed.scope === "all") return {
|
|
2001
|
+
shouldContinue: false,
|
|
2002
|
+
reply: { text: "⚠️ /allowlist add|remove requires scope dm or group." }
|
|
2003
|
+
};
|
|
2004
|
+
if (!plugin?.allowlist?.applyConfigEdit) return {
|
|
2005
|
+
shouldContinue: false,
|
|
2006
|
+
reply: { text: `⚠️ ${channelId} does not support ${parsed.scope} allowlist edits via /allowlist.` }
|
|
2007
|
+
};
|
|
2008
|
+
const snapshot = await readConfigFileSnapshot();
|
|
2009
|
+
if (!snapshot.valid || !snapshot.parsed || typeof snapshot.parsed !== "object") return {
|
|
2010
|
+
shouldContinue: false,
|
|
2011
|
+
reply: { text: "⚠️ Config file is invalid; fix it before using /allowlist." }
|
|
2012
|
+
};
|
|
2013
|
+
const parsedConfig = structuredClone(snapshot.parsed);
|
|
2014
|
+
const editResult = await plugin.allowlist.applyConfigEdit({
|
|
2015
|
+
cfg: params.cfg,
|
|
2016
|
+
parsedConfig,
|
|
2017
|
+
accountId,
|
|
2018
|
+
scope: parsed.scope,
|
|
2019
|
+
action: parsed.action,
|
|
2020
|
+
entry: parsed.entry
|
|
2021
|
+
});
|
|
2022
|
+
if (!editResult) return {
|
|
2023
|
+
shouldContinue: false,
|
|
2024
|
+
reply: { text: `⚠️ ${channelId} does not support ${parsed.scope} allowlist edits via /allowlist.` }
|
|
2025
|
+
};
|
|
2026
|
+
if (editResult.kind === "invalid-entry") return {
|
|
2027
|
+
shouldContinue: false,
|
|
2028
|
+
reply: { text: "⚠️ Invalid allowlist entry." }
|
|
2029
|
+
};
|
|
2030
|
+
const deniedText = resolveConfigWriteDeniedText({
|
|
2031
|
+
cfg: params.cfg,
|
|
2032
|
+
channel: params.command.channel,
|
|
2033
|
+
channelId,
|
|
2034
|
+
accountId: params.ctx.AccountId,
|
|
2035
|
+
gatewayClientScopes: params.ctx.GatewayClientScopes,
|
|
2036
|
+
target: editResult.writeTarget
|
|
2037
|
+
});
|
|
2038
|
+
if (deniedText) return {
|
|
2039
|
+
shouldContinue: false,
|
|
2040
|
+
reply: { text: deniedText }
|
|
2041
|
+
};
|
|
2042
|
+
const configChanged = editResult.changed;
|
|
2043
|
+
if (configChanged) {
|
|
2044
|
+
const validated = validateConfigObjectWithPlugins(parsedConfig);
|
|
2045
|
+
if (!validated.ok) {
|
|
2046
|
+
const issue = validated.issues[0];
|
|
2047
|
+
return {
|
|
2048
|
+
shouldContinue: false,
|
|
2049
|
+
reply: { text: `⚠️ Config invalid after update (${issue.path}: ${issue.message}).` }
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
await writeConfigFile(validated.config);
|
|
2053
|
+
}
|
|
2054
|
+
if (!configChanged && !shouldTouchStore) return {
|
|
2055
|
+
shouldContinue: false,
|
|
2056
|
+
reply: { text: parsed.action === "add" ? "✅ Already allowlisted." : "⚠️ Entry not found." }
|
|
2057
|
+
};
|
|
2058
|
+
if (shouldTouchStore) await updatePairingStoreAllowlist({
|
|
2059
|
+
action: parsed.action,
|
|
2060
|
+
channelId,
|
|
2061
|
+
accountId,
|
|
2062
|
+
entry: parsed.entry
|
|
2063
|
+
});
|
|
2064
|
+
const actionLabel = parsed.action === "add" ? "added" : "removed";
|
|
2065
|
+
const scopeLabel = parsed.scope === "dm" ? "DM" : "group";
|
|
2066
|
+
const locations = [];
|
|
2067
|
+
if (configChanged) locations.push(editResult.pathLabel);
|
|
2068
|
+
if (shouldTouchStore) locations.push("pairing store");
|
|
2069
|
+
return {
|
|
2070
|
+
shouldContinue: false,
|
|
2071
|
+
reply: { text: `✅ ${scopeLabel} allowlist ${actionLabel}: ${locations.length > 0 ? locations.join(" + ") : "no-op"}.` }
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
if (!shouldTouchStore) return {
|
|
2075
|
+
shouldContinue: false,
|
|
2076
|
+
reply: { text: "⚠️ This channel does not support allowlist storage." }
|
|
2077
|
+
};
|
|
2078
|
+
await updatePairingStoreAllowlist({
|
|
2079
|
+
action: parsed.action,
|
|
2080
|
+
channelId,
|
|
2081
|
+
accountId,
|
|
2082
|
+
entry: parsed.entry
|
|
2083
|
+
});
|
|
2084
|
+
const actionLabel = parsed.action === "add" ? "added" : "removed";
|
|
2085
|
+
return {
|
|
2086
|
+
shouldContinue: false,
|
|
2087
|
+
reply: { text: `✅ ${parsed.scope === "dm" ? "DM" : "group"} allowlist ${actionLabel} in pairing store.` }
|
|
2088
|
+
};
|
|
2089
|
+
};
|
|
2090
|
+
//#endregion
|
|
2091
|
+
//#region src/auto-reply/reply/commands-approve.ts
|
|
2092
|
+
const COMMAND_REGEX = /^\/approve(?:\s|$)/i;
|
|
2093
|
+
const FOREIGN_COMMAND_MENTION_REGEX = /^\/approve@([^\s]+)(?:\s|$)/i;
|
|
2094
|
+
const DECISION_ALIASES = {
|
|
2095
|
+
allow: "allow-once",
|
|
2096
|
+
once: "allow-once",
|
|
2097
|
+
"allow-once": "allow-once",
|
|
2098
|
+
allowonce: "allow-once",
|
|
2099
|
+
always: "allow-always",
|
|
2100
|
+
"allow-always": "allow-always",
|
|
2101
|
+
allowalways: "allow-always",
|
|
2102
|
+
deny: "deny",
|
|
2103
|
+
reject: "deny",
|
|
2104
|
+
block: "deny"
|
|
2105
|
+
};
|
|
2106
|
+
function parseApproveCommand(raw) {
|
|
2107
|
+
const trimmed = raw.trim();
|
|
2108
|
+
if (FOREIGN_COMMAND_MENTION_REGEX.test(trimmed)) return {
|
|
2109
|
+
ok: false,
|
|
2110
|
+
error: "❌ This /approve command targets a different Telegram bot."
|
|
2111
|
+
};
|
|
2112
|
+
const commandMatch = trimmed.match(COMMAND_REGEX);
|
|
2113
|
+
if (!commandMatch) return null;
|
|
2114
|
+
const rest = trimmed.slice(commandMatch[0].length).trim();
|
|
2115
|
+
if (!rest) return {
|
|
2116
|
+
ok: false,
|
|
2117
|
+
error: "Usage: /approve <id> allow-once|allow-always|deny"
|
|
2118
|
+
};
|
|
2119
|
+
const tokens = rest.split(/\s+/).filter(Boolean);
|
|
2120
|
+
if (tokens.length < 2) return {
|
|
2121
|
+
ok: false,
|
|
2122
|
+
error: "Usage: /approve <id> allow-once|allow-always|deny"
|
|
2123
|
+
};
|
|
2124
|
+
const first = tokens[0].toLowerCase();
|
|
2125
|
+
const second = tokens[1].toLowerCase();
|
|
2126
|
+
if (DECISION_ALIASES[first]) return {
|
|
2127
|
+
ok: true,
|
|
2128
|
+
decision: DECISION_ALIASES[first],
|
|
2129
|
+
id: tokens.slice(1).join(" ").trim()
|
|
2130
|
+
};
|
|
2131
|
+
if (DECISION_ALIASES[second]) return {
|
|
2132
|
+
ok: true,
|
|
2133
|
+
decision: DECISION_ALIASES[second],
|
|
2134
|
+
id: tokens[0]
|
|
2135
|
+
};
|
|
2136
|
+
return {
|
|
2137
|
+
ok: false,
|
|
2138
|
+
error: "Usage: /approve <id> allow-once|allow-always|deny"
|
|
2139
|
+
};
|
|
2140
|
+
}
|
|
2141
|
+
function buildResolvedByLabel(params) {
|
|
2142
|
+
return `${params.command.channel}:${params.command.senderId ?? "unknown"}`;
|
|
2143
|
+
}
|
|
2144
|
+
const handleApproveCommand = async (params, allowTextCommands) => {
|
|
2145
|
+
if (!allowTextCommands) return null;
|
|
2146
|
+
const normalized = params.command.commandBodyNormalized;
|
|
2147
|
+
const parsed = parseApproveCommand(normalized);
|
|
2148
|
+
if (!parsed) return null;
|
|
2149
|
+
if (!params.command.isAuthorizedSender) {
|
|
2150
|
+
logVerbose(`Ignoring /approve from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
2151
|
+
return { shouldContinue: false };
|
|
2152
|
+
}
|
|
2153
|
+
if (!parsed.ok) return {
|
|
2154
|
+
shouldContinue: false,
|
|
2155
|
+
reply: { text: parsed.error }
|
|
2156
|
+
};
|
|
2157
|
+
if (params.command.channel === "telegram") {
|
|
2158
|
+
if (!isTelegramExecApprovalClientEnabled({
|
|
2159
|
+
cfg: params.cfg,
|
|
2160
|
+
accountId: params.ctx.AccountId
|
|
2161
|
+
})) return {
|
|
2162
|
+
shouldContinue: false,
|
|
2163
|
+
reply: { text: "❌ Telegram exec approvals are not enabled for this bot account." }
|
|
2164
|
+
};
|
|
2165
|
+
if (!isTelegramExecApprovalApprover({
|
|
2166
|
+
cfg: params.cfg,
|
|
2167
|
+
accountId: params.ctx.AccountId,
|
|
2168
|
+
senderId: params.command.senderId
|
|
2169
|
+
})) return {
|
|
2170
|
+
shouldContinue: false,
|
|
2171
|
+
reply: { text: "❌ You are not authorized to approve exec requests on Telegram." }
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
const missingScope = requireGatewayClientScopeForInternalChannel(params, {
|
|
2175
|
+
label: "/approve",
|
|
2176
|
+
allowedScopes: ["operator.approvals", "operator.admin"],
|
|
2177
|
+
missingText: "❌ /approve requires operator.approvals for gateway clients."
|
|
2178
|
+
});
|
|
2179
|
+
if (missingScope) return missingScope;
|
|
2180
|
+
const resolvedBy = buildResolvedByLabel(params);
|
|
2181
|
+
try {
|
|
2182
|
+
await callGateway({
|
|
2183
|
+
method: "exec.approval.resolve",
|
|
2184
|
+
params: {
|
|
2185
|
+
id: parsed.id,
|
|
2186
|
+
decision: parsed.decision
|
|
2187
|
+
},
|
|
2188
|
+
clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
|
|
2189
|
+
clientDisplayName: `Chat approval (${resolvedBy})`,
|
|
2190
|
+
mode: GATEWAY_CLIENT_MODES.BACKEND
|
|
2191
|
+
});
|
|
2192
|
+
} catch (err) {
|
|
2193
|
+
return {
|
|
2194
|
+
shouldContinue: false,
|
|
2195
|
+
reply: { text: `❌ Failed to submit approval: ${String(err)}` }
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
return {
|
|
2199
|
+
shouldContinue: false,
|
|
2200
|
+
reply: { text: `✅ Exec approval ${parsed.decision} submitted for ${parsed.id}.` }
|
|
2201
|
+
};
|
|
2202
|
+
};
|
|
2203
|
+
//#endregion
|
|
2204
|
+
//#region src/auto-reply/reply/bash-command.ts
|
|
2205
|
+
const CHAT_BASH_SCOPE_KEY = "chat:bash";
|
|
2206
|
+
const DEFAULT_FOREGROUND_MS = 2e3;
|
|
2207
|
+
const MAX_FOREGROUND_MS = 3e4;
|
|
2208
|
+
let activeJob = null;
|
|
2209
|
+
function resolveForegroundMs(cfg) {
|
|
2210
|
+
const raw = cfg.commands?.bashForegroundMs;
|
|
2211
|
+
if (typeof raw !== "number" || Number.isNaN(raw)) return DEFAULT_FOREGROUND_MS;
|
|
2212
|
+
return clampInt(raw, 0, MAX_FOREGROUND_MS);
|
|
2213
|
+
}
|
|
2214
|
+
function formatSessionSnippet(sessionId) {
|
|
2215
|
+
const trimmed = sessionId.trim();
|
|
2216
|
+
if (trimmed.length <= 12) return trimmed;
|
|
2217
|
+
return `${trimmed.slice(0, 8)}…`;
|
|
2218
|
+
}
|
|
2219
|
+
function formatOutputBlock(text) {
|
|
2220
|
+
const trimmed = text.trim();
|
|
2221
|
+
if (!trimmed) return "(no output)";
|
|
2222
|
+
return `\`\`\`txt\n${trimmed}\n\`\`\``;
|
|
2223
|
+
}
|
|
2224
|
+
function parseBashRequest(raw) {
|
|
2225
|
+
const trimmed = raw.trimStart();
|
|
2226
|
+
let restSource = "";
|
|
2227
|
+
if (trimmed.toLowerCase().startsWith("/bash")) {
|
|
2228
|
+
const match = trimmed.match(/^\/bash(?:\s*:\s*|\s+|$)([\s\S]*)$/i);
|
|
2229
|
+
if (!match) return null;
|
|
2230
|
+
restSource = match[1] ?? "";
|
|
2231
|
+
} else if (trimmed.startsWith("!")) {
|
|
2232
|
+
restSource = trimmed.slice(1);
|
|
2233
|
+
if (restSource.trimStart().startsWith(":")) restSource = restSource.trimStart().slice(1);
|
|
2234
|
+
} else return null;
|
|
2235
|
+
const rest = restSource.trimStart();
|
|
2236
|
+
if (!rest) return { action: "help" };
|
|
2237
|
+
const tokenMatch = rest.match(/^(\S+)(?:\s+([\s\S]+))?$/);
|
|
2238
|
+
const token = tokenMatch?.[1]?.trim() ?? "";
|
|
2239
|
+
const remainder = tokenMatch?.[2]?.trim() ?? "";
|
|
2240
|
+
const lowered = token.toLowerCase();
|
|
2241
|
+
if (lowered === "poll") return {
|
|
2242
|
+
action: "poll",
|
|
2243
|
+
sessionId: remainder || void 0
|
|
2244
|
+
};
|
|
2245
|
+
if (lowered === "stop") return {
|
|
2246
|
+
action: "stop",
|
|
2247
|
+
sessionId: remainder || void 0
|
|
2248
|
+
};
|
|
2249
|
+
if (lowered === "help") return { action: "help" };
|
|
2250
|
+
return {
|
|
2251
|
+
action: "run",
|
|
2252
|
+
command: rest
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
function resolveRawCommandBody(params) {
|
|
2256
|
+
const stripped = stripStructuralPrefixes(params.ctx.CommandBody ?? params.ctx.RawBody ?? params.ctx.Body ?? "");
|
|
2257
|
+
return params.isGroup ? stripMentions(stripped, params.ctx, params.cfg, params.agentId) : stripped;
|
|
2258
|
+
}
|
|
2259
|
+
function getScopedSession(sessionId) {
|
|
2260
|
+
const running = getSession(sessionId);
|
|
2261
|
+
if (running && running.scopeKey === CHAT_BASH_SCOPE_KEY) return { running };
|
|
2262
|
+
const finished = getFinishedSession(sessionId);
|
|
2263
|
+
if (finished && finished.scopeKey === CHAT_BASH_SCOPE_KEY) return { finished };
|
|
2264
|
+
return {};
|
|
2265
|
+
}
|
|
2266
|
+
function ensureActiveJobState() {
|
|
2267
|
+
if (!activeJob) return null;
|
|
2268
|
+
if (activeJob.state === "starting") return activeJob;
|
|
2269
|
+
const { running, finished } = getScopedSession(activeJob.sessionId);
|
|
2270
|
+
if (running) return activeJob;
|
|
2271
|
+
if (finished) {
|
|
2272
|
+
activeJob = null;
|
|
2273
|
+
return null;
|
|
2274
|
+
}
|
|
2275
|
+
activeJob = null;
|
|
2276
|
+
return null;
|
|
2277
|
+
}
|
|
2278
|
+
function attachActiveWatcher(sessionId) {
|
|
2279
|
+
if (!activeJob || activeJob.state !== "running") return;
|
|
2280
|
+
if (activeJob.sessionId !== sessionId) return;
|
|
2281
|
+
if (activeJob.watcherAttached) return;
|
|
2282
|
+
const { running } = getScopedSession(sessionId);
|
|
2283
|
+
const child = running?.child;
|
|
2284
|
+
if (!child) return;
|
|
2285
|
+
activeJob.watcherAttached = true;
|
|
2286
|
+
child.once("close", () => {
|
|
2287
|
+
if (activeJob?.state === "running" && activeJob.sessionId === sessionId) activeJob = null;
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
function buildUsageReply() {
|
|
2291
|
+
return { text: [
|
|
2292
|
+
"⚙️ Usage:",
|
|
2293
|
+
"- ! <command>",
|
|
2294
|
+
"- !poll | ! poll",
|
|
2295
|
+
"- !stop | ! stop",
|
|
2296
|
+
"- /bash ... (alias; same subcommands as !)"
|
|
2297
|
+
].join("\n") };
|
|
2298
|
+
}
|
|
2299
|
+
async function handleBashChatCommand(params) {
|
|
2300
|
+
if (!isCommandFlagEnabled(params.cfg, "bash")) return buildDisabledCommandReply({
|
|
2301
|
+
label: "bash",
|
|
2302
|
+
configKey: "bash",
|
|
2303
|
+
docsUrl: "https://docs.openclaw.ai/tools/slash-commands#config"
|
|
2304
|
+
});
|
|
2305
|
+
const agentId = params.agentId ?? resolveSessionAgentId({
|
|
2306
|
+
sessionKey: params.sessionKey,
|
|
2307
|
+
config: params.cfg
|
|
2308
|
+
});
|
|
2309
|
+
if (!params.elevated.enabled || !params.elevated.allowed) {
|
|
2310
|
+
const runtimeSandboxed = resolveSandboxRuntimeStatus({
|
|
2311
|
+
cfg: params.cfg,
|
|
2312
|
+
sessionKey: params.ctx.SessionKey
|
|
2313
|
+
}).sandboxed;
|
|
2314
|
+
return { text: formatElevatedUnavailableMessage({
|
|
2315
|
+
runtimeSandboxed,
|
|
2316
|
+
failures: params.elevated.failures,
|
|
2317
|
+
sessionKey: params.ctx.SessionKey
|
|
2318
|
+
}) };
|
|
2319
|
+
}
|
|
2320
|
+
const request = parseBashRequest(resolveRawCommandBody({
|
|
2321
|
+
ctx: params.ctx,
|
|
2322
|
+
cfg: params.cfg,
|
|
2323
|
+
agentId,
|
|
2324
|
+
isGroup: params.isGroup
|
|
2325
|
+
}).trim());
|
|
2326
|
+
if (!request) return { text: "⚠️ Unrecognized bash request." };
|
|
2327
|
+
const liveJob = ensureActiveJobState();
|
|
2328
|
+
if (request.action === "help") return buildUsageReply();
|
|
2329
|
+
if (request.action === "poll") {
|
|
2330
|
+
const sessionId = request.sessionId?.trim() || (liveJob?.state === "running" ? liveJob.sessionId : "");
|
|
2331
|
+
if (!sessionId) return { text: "⚙️ No active bash job." };
|
|
2332
|
+
const { running, finished } = getScopedSession(sessionId);
|
|
2333
|
+
if (running) {
|
|
2334
|
+
attachActiveWatcher(sessionId);
|
|
2335
|
+
const runtimeSec = Math.max(0, Math.floor((Date.now() - running.startedAt) / 1e3));
|
|
2336
|
+
const tail = running.tail || "(no output yet)";
|
|
2337
|
+
return { text: [
|
|
2338
|
+
`⚙️ bash still running (session ${formatSessionSnippet(sessionId)}, ${runtimeSec}s).`,
|
|
2339
|
+
formatOutputBlock(tail),
|
|
2340
|
+
"Hint: !stop (or /bash stop)"
|
|
2341
|
+
].join("\n") };
|
|
2342
|
+
}
|
|
2343
|
+
if (finished) {
|
|
2344
|
+
if (activeJob?.state === "running" && activeJob.sessionId === sessionId) activeJob = null;
|
|
2345
|
+
const exitLabel = finished.exitSignal ? `signal ${String(finished.exitSignal)}` : `code ${String(finished.exitCode ?? 0)}`;
|
|
2346
|
+
return { text: [
|
|
2347
|
+
`${finished.status === "completed" ? "⚙️" : "⚠️"} bash finished (session ${formatSessionSnippet(sessionId)}).`,
|
|
2348
|
+
`Exit: ${exitLabel}`,
|
|
2349
|
+
formatOutputBlock(finished.aggregated || finished.tail)
|
|
2350
|
+
].join("\n") };
|
|
2351
|
+
}
|
|
2352
|
+
if (activeJob?.state === "running" && activeJob.sessionId === sessionId) activeJob = null;
|
|
2353
|
+
return { text: `⚙️ No bash session found for ${formatSessionSnippet(sessionId)}.` };
|
|
2354
|
+
}
|
|
2355
|
+
if (request.action === "stop") {
|
|
2356
|
+
const sessionId = request.sessionId?.trim() || (liveJob?.state === "running" ? liveJob.sessionId : "");
|
|
2357
|
+
if (!sessionId) return { text: "⚙️ No active bash job." };
|
|
2358
|
+
const { running } = getScopedSession(sessionId);
|
|
2359
|
+
if (!running) {
|
|
2360
|
+
if (activeJob?.state === "running" && activeJob.sessionId === sessionId) activeJob = null;
|
|
2361
|
+
return { text: `⚙️ No running bash job found for ${formatSessionSnippet(sessionId)}.` };
|
|
2362
|
+
}
|
|
2363
|
+
if (!running.backgrounded) return { text: `⚠️ Session ${formatSessionSnippet(sessionId)} is not backgrounded.` };
|
|
2364
|
+
const pid = running.pid ?? running.child?.pid;
|
|
2365
|
+
if (pid) killProcessTree(pid);
|
|
2366
|
+
markExited(running, null, "SIGKILL", "failed");
|
|
2367
|
+
if (activeJob?.state === "running" && activeJob.sessionId === sessionId) activeJob = null;
|
|
2368
|
+
return { text: `⚙️ bash stopped (session ${formatSessionSnippet(sessionId)}).` };
|
|
2369
|
+
}
|
|
2370
|
+
if (liveJob) return { text: `⚠️ A bash job is already running (${liveJob.state === "running" ? formatSessionSnippet(liveJob.sessionId) : "starting"}). Use !poll / !stop (or /bash poll / /bash stop).` };
|
|
2371
|
+
const commandText = request.command.trim();
|
|
2372
|
+
if (!commandText) return buildUsageReply();
|
|
2373
|
+
activeJob = {
|
|
2374
|
+
state: "starting",
|
|
2375
|
+
startedAt: Date.now(),
|
|
2376
|
+
command: commandText
|
|
2377
|
+
};
|
|
2378
|
+
try {
|
|
2379
|
+
const foregroundMs = resolveForegroundMs(params.cfg);
|
|
2380
|
+
const shouldBackgroundImmediately = foregroundMs <= 0;
|
|
2381
|
+
const timeoutSec = params.cfg.tools?.exec?.timeoutSec;
|
|
2382
|
+
const notifyOnExit = params.cfg.tools?.exec?.notifyOnExit;
|
|
2383
|
+
const notifyOnExitEmptySuccess = params.cfg.tools?.exec?.notifyOnExitEmptySuccess;
|
|
2384
|
+
const result = await createExecTool({
|
|
2385
|
+
scopeKey: CHAT_BASH_SCOPE_KEY,
|
|
2386
|
+
allowBackground: true,
|
|
2387
|
+
timeoutSec,
|
|
2388
|
+
sessionKey: params.sessionKey,
|
|
2389
|
+
notifyOnExit,
|
|
2390
|
+
notifyOnExitEmptySuccess,
|
|
2391
|
+
elevated: {
|
|
2392
|
+
enabled: params.elevated.enabled,
|
|
2393
|
+
allowed: params.elevated.allowed,
|
|
2394
|
+
defaultLevel: "on"
|
|
2395
|
+
}
|
|
2396
|
+
}).execute("chat-bash", {
|
|
2397
|
+
command: commandText,
|
|
2398
|
+
background: shouldBackgroundImmediately,
|
|
2399
|
+
yieldMs: shouldBackgroundImmediately ? void 0 : foregroundMs,
|
|
2400
|
+
timeout: timeoutSec,
|
|
2401
|
+
elevated: true
|
|
2402
|
+
});
|
|
2403
|
+
if (result.details?.status === "running") {
|
|
2404
|
+
const sessionId = result.details.sessionId;
|
|
2405
|
+
activeJob = {
|
|
2406
|
+
state: "running",
|
|
2407
|
+
sessionId,
|
|
2408
|
+
startedAt: result.details.startedAt,
|
|
2409
|
+
command: commandText,
|
|
2410
|
+
watcherAttached: false
|
|
2411
|
+
};
|
|
2412
|
+
attachActiveWatcher(sessionId);
|
|
2413
|
+
logVerbose(`Started bash session ${formatSessionSnippet(sessionId)}: ${commandText}`);
|
|
2414
|
+
return { text: `⚙️ bash started (session ${sessionId}). Still running; use !poll / !stop (or /bash poll / /bash stop).` };
|
|
2415
|
+
}
|
|
2416
|
+
activeJob = null;
|
|
2417
|
+
const exitCode = result.details?.status === "completed" ? result.details.exitCode : 0;
|
|
2418
|
+
const output = result.details?.status === "completed" ? result.details.aggregated : result.content.map((chunk) => chunk.type === "text" ? chunk.text : "").join("\n");
|
|
2419
|
+
return { text: [
|
|
2420
|
+
`⚙️ bash: ${commandText}`,
|
|
2421
|
+
`Exit: ${exitCode}`,
|
|
2422
|
+
formatOutputBlock(output || "(no output)")
|
|
2423
|
+
].join("\n") };
|
|
2424
|
+
} catch (err) {
|
|
2425
|
+
activeJob = null;
|
|
2426
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2427
|
+
return { text: [`⚠️ bash failed: ${commandText}`, formatOutputBlock(message)].join("\n") };
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
//#endregion
|
|
2431
|
+
//#region src/auto-reply/reply/commands-bash.ts
|
|
2432
|
+
const handleBashCommand = async (params, allowTextCommands) => {
|
|
2433
|
+
if (!allowTextCommands) return null;
|
|
2434
|
+
const { command } = params;
|
|
2435
|
+
const bashSlashRequested = command.commandBodyNormalized === "/bash" || command.commandBodyNormalized.startsWith("/bash ");
|
|
2436
|
+
const bashBangRequested = command.commandBodyNormalized.startsWith("!");
|
|
2437
|
+
if (!bashSlashRequested && !(bashBangRequested && command.isAuthorizedSender)) return null;
|
|
2438
|
+
const unauthorized = rejectUnauthorizedCommand(params, "/bash");
|
|
2439
|
+
if (unauthorized) return unauthorized;
|
|
2440
|
+
return {
|
|
2441
|
+
shouldContinue: false,
|
|
2442
|
+
reply: await handleBashChatCommand({
|
|
2443
|
+
ctx: params.ctx,
|
|
2444
|
+
cfg: params.cfg,
|
|
2445
|
+
agentId: params.agentId,
|
|
2446
|
+
sessionKey: params.sessionKey,
|
|
2447
|
+
isGroup: params.isGroup,
|
|
2448
|
+
elevated: params.elevated
|
|
2449
|
+
})
|
|
2450
|
+
};
|
|
2451
|
+
};
|
|
2452
|
+
//#endregion
|
|
2453
|
+
//#region src/agents/btw.ts
|
|
2454
|
+
function collectTextContent(content) {
|
|
2455
|
+
return content.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
2456
|
+
}
|
|
2457
|
+
function collectThinkingContent(content) {
|
|
2458
|
+
return content.filter((part) => part.type === "thinking").map((part) => part.thinking).join("");
|
|
2459
|
+
}
|
|
2460
|
+
function buildBtwSystemPrompt() {
|
|
2461
|
+
return [
|
|
2462
|
+
"You are answering an ephemeral /btw side question about the current conversation.",
|
|
2463
|
+
"Use the conversation only as background context.",
|
|
2464
|
+
"Answer only the side question in the last user message.",
|
|
2465
|
+
"Do not continue, resume, or complete any unfinished task from the conversation.",
|
|
2466
|
+
"Do not emit tool calls, pseudo-tool calls, shell commands, file writes, patches, or code unless the side question explicitly asks for them.",
|
|
2467
|
+
"Do not say you will continue the main task after answering.",
|
|
2468
|
+
"If the question can be answered briefly, answer briefly."
|
|
2469
|
+
].join("\n");
|
|
2470
|
+
}
|
|
2471
|
+
function buildBtwQuestionPrompt(question, inFlightPrompt) {
|
|
2472
|
+
const lines = ["Answer this side question only.", "Ignore any unfinished task in the conversation while answering it."];
|
|
2473
|
+
const trimmedPrompt = inFlightPrompt?.trim();
|
|
2474
|
+
if (trimmedPrompt) lines.push("", "Current in-flight main task request for background context only:", "<in_flight_main_task>", trimmedPrompt, "</in_flight_main_task>", "Do not continue or complete that task while answering the side question.");
|
|
2475
|
+
lines.push("", "<btw_side_question>", question.trim(), "</btw_side_question>");
|
|
2476
|
+
return lines.join("\n");
|
|
2477
|
+
}
|
|
2478
|
+
function toSimpleContextMessages(messages) {
|
|
2479
|
+
return stripToolResultDetails(messages.filter((message) => {
|
|
2480
|
+
if (!message || typeof message !== "object") return false;
|
|
2481
|
+
const role = message.role;
|
|
2482
|
+
return role === "user" || role === "assistant";
|
|
2483
|
+
}));
|
|
2484
|
+
}
|
|
2485
|
+
function resolveSimpleThinkingLevel(level) {
|
|
2486
|
+
if (!level || level === "off") return;
|
|
2487
|
+
return mapThinkingLevel(level);
|
|
2488
|
+
}
|
|
2489
|
+
function resolveSessionTranscriptPath(params) {
|
|
2490
|
+
try {
|
|
2491
|
+
const agentId = params.sessionKey?.split(":")[1];
|
|
2492
|
+
const pathOpts = resolveSessionFilePathOptions({
|
|
2493
|
+
agentId,
|
|
2494
|
+
storePath: params.storePath
|
|
2495
|
+
});
|
|
2496
|
+
return resolveSessionFilePath(params.sessionId, params.sessionEntry, pathOpts);
|
|
2497
|
+
} catch (error) {
|
|
2498
|
+
diag.debug(`resolveSessionTranscriptPath failed: sessionId=${params.sessionId} err=${String(error)}`);
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
async function resolveRuntimeModel(params) {
|
|
2503
|
+
await ensureOpenClawModelsJson(params.cfg, params.agentDir);
|
|
2504
|
+
const modelRegistry = discoverModels(discoverAuthStorage(params.agentDir), params.agentDir);
|
|
2505
|
+
const model = resolveModelWithRegistry({
|
|
2506
|
+
provider: params.provider,
|
|
2507
|
+
modelId: params.model,
|
|
2508
|
+
modelRegistry,
|
|
2509
|
+
cfg: params.cfg
|
|
2510
|
+
});
|
|
2511
|
+
if (!model) throw new Error(`Unknown model: ${params.provider}/${params.model}`);
|
|
2512
|
+
return {
|
|
2513
|
+
model,
|
|
2514
|
+
authProfileId: await resolveSessionAuthProfileOverride({
|
|
2515
|
+
cfg: params.cfg,
|
|
2516
|
+
provider: params.provider,
|
|
2517
|
+
agentDir: params.agentDir,
|
|
2518
|
+
sessionEntry: params.sessionEntry,
|
|
2519
|
+
sessionStore: params.sessionStore,
|
|
2520
|
+
sessionKey: params.sessionKey,
|
|
2521
|
+
storePath: params.storePath,
|
|
2522
|
+
isNewSession: params.isNewSession
|
|
2523
|
+
}),
|
|
2524
|
+
authProfileIdSource: params.sessionEntry?.authProfileOverrideSource
|
|
2525
|
+
};
|
|
2526
|
+
}
|
|
2527
|
+
async function runBtwSideQuestion(params) {
|
|
2528
|
+
const sessionId = params.sessionEntry.sessionId?.trim();
|
|
2529
|
+
if (!sessionId) throw new Error("No active session context.");
|
|
2530
|
+
const sessionFile = resolveSessionTranscriptPath({
|
|
2531
|
+
sessionId,
|
|
2532
|
+
sessionEntry: params.sessionEntry,
|
|
2533
|
+
sessionKey: params.sessionKey,
|
|
2534
|
+
storePath: params.storePath
|
|
2535
|
+
});
|
|
2536
|
+
if (!sessionFile) throw new Error("No active session transcript.");
|
|
2537
|
+
const sessionManager = SessionManager.open(sessionFile);
|
|
2538
|
+
const activeRunSnapshot = getActiveEmbeddedRunSnapshot(sessionId);
|
|
2539
|
+
let messages = [];
|
|
2540
|
+
let inFlightPrompt;
|
|
2541
|
+
if (Array.isArray(activeRunSnapshot?.messages) && activeRunSnapshot.messages.length > 0) {
|
|
2542
|
+
messages = toSimpleContextMessages(activeRunSnapshot.messages);
|
|
2543
|
+
inFlightPrompt = activeRunSnapshot.inFlightPrompt;
|
|
2544
|
+
} else if (activeRunSnapshot) {
|
|
2545
|
+
inFlightPrompt = activeRunSnapshot.inFlightPrompt;
|
|
2546
|
+
if (activeRunSnapshot.transcriptLeafId && sessionManager.branch) try {
|
|
2547
|
+
sessionManager.branch(activeRunSnapshot.transcriptLeafId);
|
|
2548
|
+
} catch (error) {
|
|
2549
|
+
diag.debug(`btw snapshot leaf unavailable: sessionId=${sessionId} leaf=${activeRunSnapshot.transcriptLeafId} err=${String(error)}`);
|
|
2550
|
+
sessionManager.resetLeaf?.();
|
|
2551
|
+
}
|
|
2552
|
+
else sessionManager.resetLeaf?.();
|
|
2553
|
+
} else {
|
|
2554
|
+
const leafEntry = sessionManager.getLeafEntry?.();
|
|
2555
|
+
if (leafEntry?.type === "message" && leafEntry.message?.role === "user") if (leafEntry.parentId && sessionManager.branch) sessionManager.branch(leafEntry.parentId);
|
|
2556
|
+
else sessionManager.resetLeaf?.();
|
|
2557
|
+
}
|
|
2558
|
+
if (messages.length === 0) {
|
|
2559
|
+
const sessionContext = sessionManager.buildSessionContext();
|
|
2560
|
+
messages = toSimpleContextMessages(Array.isArray(sessionContext.messages) ? sessionContext.messages : []);
|
|
2561
|
+
}
|
|
2562
|
+
if (messages.length === 0 && !inFlightPrompt?.trim()) throw new Error("No active session context.");
|
|
2563
|
+
const { model, authProfileId } = await resolveRuntimeModel({
|
|
2564
|
+
cfg: params.cfg,
|
|
2565
|
+
provider: params.provider,
|
|
2566
|
+
model: params.model,
|
|
2567
|
+
agentDir: params.agentDir,
|
|
2568
|
+
sessionEntry: params.sessionEntry,
|
|
2569
|
+
sessionStore: params.sessionStore,
|
|
2570
|
+
sessionKey: params.sessionKey,
|
|
2571
|
+
storePath: params.storePath,
|
|
2572
|
+
isNewSession: params.isNewSession
|
|
2573
|
+
});
|
|
2574
|
+
const apiKey = requireApiKey(await getApiKeyForModel({
|
|
2575
|
+
model,
|
|
2576
|
+
cfg: params.cfg,
|
|
2577
|
+
profileId: authProfileId,
|
|
2578
|
+
agentDir: params.agentDir
|
|
2579
|
+
}), model.provider);
|
|
2580
|
+
const chunker = params.opts?.onBlockReply && params.blockReplyChunking ? new EmbeddedBlockChunker(params.blockReplyChunking) : void 0;
|
|
2581
|
+
let emittedBlocks = 0;
|
|
2582
|
+
let blockEmitChain = Promise.resolve();
|
|
2583
|
+
let answerText = "";
|
|
2584
|
+
let reasoningText = "";
|
|
2585
|
+
let assistantStarted = false;
|
|
2586
|
+
let sawTextEvent = false;
|
|
2587
|
+
const emitBlockChunk = async (text) => {
|
|
2588
|
+
if (!text.trim() || !params.opts?.onBlockReply) return;
|
|
2589
|
+
emittedBlocks += 1;
|
|
2590
|
+
blockEmitChain = blockEmitChain.then(async () => {
|
|
2591
|
+
await params.opts?.onBlockReply?.({
|
|
2592
|
+
text,
|
|
2593
|
+
btw: { question: params.question }
|
|
2594
|
+
});
|
|
2595
|
+
});
|
|
2596
|
+
await blockEmitChain;
|
|
2597
|
+
};
|
|
2598
|
+
const stream = streamSimple(model, {
|
|
2599
|
+
systemPrompt: buildBtwSystemPrompt(),
|
|
2600
|
+
messages: [...messages, {
|
|
2601
|
+
role: "user",
|
|
2602
|
+
content: [{
|
|
2603
|
+
type: "text",
|
|
2604
|
+
text: buildBtwQuestionPrompt(params.question, inFlightPrompt)
|
|
2605
|
+
}],
|
|
2606
|
+
timestamp: Date.now()
|
|
2607
|
+
}]
|
|
2608
|
+
}, {
|
|
2609
|
+
apiKey,
|
|
2610
|
+
reasoning: resolveSimpleThinkingLevel(params.resolvedThinkLevel),
|
|
2611
|
+
signal: params.opts?.abortSignal
|
|
2612
|
+
});
|
|
2613
|
+
let finalEvent;
|
|
2614
|
+
for await (const event of stream) {
|
|
2615
|
+
finalEvent = event.type === "done" || event.type === "error" ? event : finalEvent;
|
|
2616
|
+
if (!assistantStarted && (event.type === "text_start" || event.type === "start")) {
|
|
2617
|
+
assistantStarted = true;
|
|
2618
|
+
await params.opts?.onAssistantMessageStart?.();
|
|
2619
|
+
}
|
|
2620
|
+
if (event.type === "text_delta") {
|
|
2621
|
+
sawTextEvent = true;
|
|
2622
|
+
answerText += event.delta;
|
|
2623
|
+
chunker?.append(event.delta);
|
|
2624
|
+
if (chunker && params.resolvedBlockStreamingBreak === "text_end") chunker.drain({
|
|
2625
|
+
force: false,
|
|
2626
|
+
emit: (chunk) => void emitBlockChunk(chunk)
|
|
2627
|
+
});
|
|
2628
|
+
continue;
|
|
2629
|
+
}
|
|
2630
|
+
if (event.type === "text_end" && chunker && params.resolvedBlockStreamingBreak === "text_end") {
|
|
2631
|
+
chunker.drain({
|
|
2632
|
+
force: true,
|
|
2633
|
+
emit: (chunk) => void emitBlockChunk(chunk)
|
|
2634
|
+
});
|
|
2635
|
+
continue;
|
|
2636
|
+
}
|
|
2637
|
+
if (event.type === "thinking_delta") {
|
|
2638
|
+
reasoningText += event.delta;
|
|
2639
|
+
if (params.resolvedReasoningLevel !== "off") await params.opts?.onReasoningStream?.({
|
|
2640
|
+
text: reasoningText,
|
|
2641
|
+
isReasoning: true
|
|
2642
|
+
});
|
|
2643
|
+
continue;
|
|
2644
|
+
}
|
|
2645
|
+
if (event.type === "thinking_end" && params.resolvedReasoningLevel !== "off") await params.opts?.onReasoningEnd?.();
|
|
2646
|
+
}
|
|
2647
|
+
if (chunker && params.resolvedBlockStreamingBreak !== "text_end" && chunker.hasBuffered()) chunker.drain({
|
|
2648
|
+
force: true,
|
|
2649
|
+
emit: (chunk) => void emitBlockChunk(chunk)
|
|
2650
|
+
});
|
|
2651
|
+
await blockEmitChain;
|
|
2652
|
+
if (finalEvent?.type === "error") {
|
|
2653
|
+
const message = collectTextContent(finalEvent.error.content);
|
|
2654
|
+
throw new Error(message || finalEvent.error.errorMessage || "BTW failed.");
|
|
2655
|
+
}
|
|
2656
|
+
const finalMessage = finalEvent?.type === "done" ? finalEvent.message : void 0;
|
|
2657
|
+
if (finalMessage) {
|
|
2658
|
+
if (!sawTextEvent) answerText = collectTextContent(finalMessage.content);
|
|
2659
|
+
if (!reasoningText) reasoningText = collectThinkingContent(finalMessage.content);
|
|
2660
|
+
}
|
|
2661
|
+
const answer = answerText.trim();
|
|
2662
|
+
if (!answer) throw new Error("No BTW response generated.");
|
|
2663
|
+
if (emittedBlocks > 0) return;
|
|
2664
|
+
return { text: answer };
|
|
2665
|
+
}
|
|
2666
|
+
//#endregion
|
|
2667
|
+
//#region src/auto-reply/reply/commands-btw.ts
|
|
2668
|
+
const BTW_USAGE = "Usage: /btw <side question>";
|
|
2669
|
+
const handleBtwCommand = async (params, allowTextCommands) => {
|
|
2670
|
+
if (!allowTextCommands) return null;
|
|
2671
|
+
const question = extractBtwQuestion(params.command.commandBodyNormalized);
|
|
2672
|
+
if (question === null) return null;
|
|
2673
|
+
const unauthorized = rejectUnauthorizedCommand(params, "/btw");
|
|
2674
|
+
if (unauthorized) return unauthorized;
|
|
2675
|
+
if (!question) return {
|
|
2676
|
+
shouldContinue: false,
|
|
2677
|
+
reply: { text: BTW_USAGE }
|
|
2678
|
+
};
|
|
2679
|
+
if (!params.sessionEntry?.sessionId) return {
|
|
2680
|
+
shouldContinue: false,
|
|
2681
|
+
reply: { text: "⚠️ /btw requires an active session with existing context." }
|
|
2682
|
+
};
|
|
2683
|
+
if (!params.agentDir) return {
|
|
2684
|
+
shouldContinue: false,
|
|
2685
|
+
reply: { text: "⚠️ /btw is unavailable because the active agent directory could not be resolved." }
|
|
2686
|
+
};
|
|
2687
|
+
try {
|
|
2688
|
+
await params.typing?.startTypingLoop();
|
|
2689
|
+
const reply = await runBtwSideQuestion({
|
|
2690
|
+
cfg: params.cfg,
|
|
2691
|
+
agentDir: params.agentDir,
|
|
2692
|
+
provider: params.provider,
|
|
2693
|
+
model: params.model,
|
|
2694
|
+
question,
|
|
2695
|
+
sessionEntry: params.sessionEntry,
|
|
2696
|
+
sessionStore: params.sessionStore,
|
|
2697
|
+
sessionKey: params.sessionKey,
|
|
2698
|
+
storePath: params.storePath,
|
|
2699
|
+
resolvedThinkLevel: "off",
|
|
2700
|
+
resolvedReasoningLevel: "off",
|
|
2701
|
+
blockReplyChunking: params.blockReplyChunking,
|
|
2702
|
+
resolvedBlockStreamingBreak: params.resolvedBlockStreamingBreak,
|
|
2703
|
+
opts: params.opts,
|
|
2704
|
+
isNewSession: false
|
|
2705
|
+
});
|
|
2706
|
+
return {
|
|
2707
|
+
shouldContinue: false,
|
|
2708
|
+
reply: reply ? {
|
|
2709
|
+
...reply,
|
|
2710
|
+
btw: { question }
|
|
2711
|
+
} : reply
|
|
2712
|
+
};
|
|
2713
|
+
} catch (error) {
|
|
2714
|
+
const message = error instanceof Error ? error.message.trim() : "";
|
|
2715
|
+
return {
|
|
2716
|
+
shouldContinue: false,
|
|
2717
|
+
reply: {
|
|
2718
|
+
text: `⚠️ /btw failed${message ? `: ${message}` : "."}`,
|
|
2719
|
+
btw: { question },
|
|
2720
|
+
isError: true
|
|
2721
|
+
}
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
};
|
|
2725
|
+
//#endregion
|
|
2726
|
+
//#region src/auto-reply/reply/commands-compact.ts
|
|
2727
|
+
function extractCompactInstructions(params) {
|
|
2728
|
+
const raw = stripStructuralPrefixes(params.rawBody ?? "");
|
|
2729
|
+
const trimmed = (params.isGroup ? stripMentions(raw, params.ctx, params.cfg, params.agentId) : raw).trim();
|
|
2730
|
+
if (!trimmed) return;
|
|
2731
|
+
const prefix = trimmed.toLowerCase().startsWith("/compact") ? "/compact" : null;
|
|
2732
|
+
if (!prefix) return;
|
|
2733
|
+
let rest = trimmed.slice(prefix.length).trimStart();
|
|
2734
|
+
if (rest.startsWith(":")) rest = rest.slice(1).trimStart();
|
|
2735
|
+
return rest.length ? rest : void 0;
|
|
2736
|
+
}
|
|
2737
|
+
const handleCompactCommand = async (params) => {
|
|
2738
|
+
if (!(params.command.commandBodyNormalized === "/compact" || params.command.commandBodyNormalized.startsWith("/compact "))) return null;
|
|
2739
|
+
if (!params.command.isAuthorizedSender) {
|
|
2740
|
+
logVerbose(`Ignoring /compact from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
2741
|
+
return { shouldContinue: false };
|
|
2742
|
+
}
|
|
2743
|
+
if (!params.sessionEntry?.sessionId) return {
|
|
2744
|
+
shouldContinue: false,
|
|
2745
|
+
reply: { text: "⚙️ Compaction unavailable (missing session id)." }
|
|
2746
|
+
};
|
|
2747
|
+
const sessionId = params.sessionEntry.sessionId;
|
|
2748
|
+
if (isEmbeddedPiRunActive(sessionId)) {
|
|
2749
|
+
abortEmbeddedPiRun(sessionId);
|
|
2750
|
+
await waitForEmbeddedPiRunEnd(sessionId, 15e3);
|
|
2751
|
+
}
|
|
2752
|
+
const customInstructions = extractCompactInstructions({
|
|
2753
|
+
rawBody: params.ctx.CommandBody ?? params.ctx.RawBody ?? params.ctx.Body,
|
|
2754
|
+
ctx: params.ctx,
|
|
2755
|
+
cfg: params.cfg,
|
|
2756
|
+
agentId: params.agentId,
|
|
2757
|
+
isGroup: params.isGroup
|
|
2758
|
+
});
|
|
2759
|
+
const result = await compactEmbeddedPiSession({
|
|
2760
|
+
sessionId,
|
|
2761
|
+
sessionKey: params.sessionKey,
|
|
2762
|
+
allowGatewaySubagentBinding: true,
|
|
2763
|
+
messageChannel: params.command.channel,
|
|
2764
|
+
groupId: params.sessionEntry.groupId,
|
|
2765
|
+
groupChannel: params.sessionEntry.groupChannel,
|
|
2766
|
+
groupSpace: params.sessionEntry.space,
|
|
2767
|
+
spawnedBy: params.sessionEntry.spawnedBy,
|
|
2768
|
+
sessionFile: resolveSessionFilePath(sessionId, params.sessionEntry, resolveSessionFilePathOptions({
|
|
2769
|
+
agentId: params.agentId,
|
|
2770
|
+
storePath: params.storePath
|
|
2771
|
+
})),
|
|
2772
|
+
workspaceDir: params.workspaceDir,
|
|
2773
|
+
agentDir: params.agentDir,
|
|
2774
|
+
config: params.cfg,
|
|
2775
|
+
skillsSnapshot: params.sessionEntry.skillsSnapshot,
|
|
2776
|
+
provider: params.provider,
|
|
2777
|
+
model: params.model,
|
|
2778
|
+
thinkLevel: params.resolvedThinkLevel ?? await params.resolveDefaultThinkingLevel(),
|
|
2779
|
+
bashElevated: {
|
|
2780
|
+
enabled: false,
|
|
2781
|
+
allowed: false,
|
|
2782
|
+
defaultLevel: "off"
|
|
2783
|
+
},
|
|
2784
|
+
customInstructions,
|
|
2785
|
+
trigger: "manual",
|
|
2786
|
+
senderIsOwner: params.command.senderIsOwner,
|
|
2787
|
+
ownerNumbers: params.command.ownerList.length > 0 ? params.command.ownerList : void 0
|
|
2788
|
+
});
|
|
2789
|
+
const compactLabel = result.ok ? result.compacted ? result.result?.tokensBefore != null && result.result?.tokensAfter != null ? `Compacted (${formatTokenCount(result.result.tokensBefore)} → ${formatTokenCount(result.result.tokensAfter)})` : result.result?.tokensBefore ? `Compacted (${formatTokenCount(result.result.tokensBefore)} before)` : "Compacted" : "Compaction skipped" : "Compaction failed";
|
|
2790
|
+
if (result.ok && result.compacted) await incrementCompactionCount({
|
|
2791
|
+
sessionEntry: params.sessionEntry,
|
|
2792
|
+
sessionStore: params.sessionStore,
|
|
2793
|
+
sessionKey: params.sessionKey,
|
|
2794
|
+
storePath: params.storePath,
|
|
2795
|
+
tokensAfter: result.result?.tokensAfter
|
|
2796
|
+
});
|
|
2797
|
+
const totalTokens = result.result?.tokensAfter ?? resolveFreshSessionTotalTokens(params.sessionEntry);
|
|
2798
|
+
const contextSummary = formatContextUsageShort(typeof totalTokens === "number" && totalTokens > 0 ? totalTokens : null, params.contextTokens ?? params.sessionEntry.contextTokens ?? null);
|
|
2799
|
+
const reason = result.reason?.trim();
|
|
2800
|
+
const line = reason ? `${compactLabel}: ${reason} • ${contextSummary}` : `${compactLabel} • ${contextSummary}`;
|
|
2801
|
+
enqueueSystemEvent(line, { sessionKey: params.sessionKey });
|
|
2802
|
+
return {
|
|
2803
|
+
shouldContinue: false,
|
|
2804
|
+
reply: { text: `⚙️ ${line}` }
|
|
2805
|
+
};
|
|
2806
|
+
};
|
|
2807
|
+
//#endregion
|
|
2808
|
+
//#region src/auto-reply/reply/commands-slash-parse.ts
|
|
2809
|
+
function parseSlashCommandActionArgs(raw, slash) {
|
|
2810
|
+
const trimmed = raw.trim();
|
|
2811
|
+
const slashLower = slash.toLowerCase();
|
|
2812
|
+
if (!trimmed.toLowerCase().startsWith(slashLower)) return { kind: "no-match" };
|
|
2813
|
+
const rest = trimmed.slice(slash.length).trim();
|
|
2814
|
+
if (!rest) return { kind: "empty" };
|
|
2815
|
+
const match = rest.match(/^(\S+)(?:\s+([\s\S]+))?$/);
|
|
2816
|
+
if (!match) return { kind: "invalid" };
|
|
2817
|
+
return {
|
|
2818
|
+
kind: "parsed",
|
|
2819
|
+
action: match[1]?.toLowerCase() ?? "",
|
|
2820
|
+
args: (match[2] ?? "").trim()
|
|
2821
|
+
};
|
|
2822
|
+
}
|
|
2823
|
+
function parseSlashCommandOrNull(raw, slash, opts) {
|
|
2824
|
+
const parsed = parseSlashCommandActionArgs(raw, slash);
|
|
2825
|
+
if (parsed.kind === "no-match") return null;
|
|
2826
|
+
if (parsed.kind === "invalid") return {
|
|
2827
|
+
ok: false,
|
|
2828
|
+
message: opts.invalidMessage
|
|
2829
|
+
};
|
|
2830
|
+
if (parsed.kind === "empty") return {
|
|
2831
|
+
ok: true,
|
|
2832
|
+
action: opts.defaultAction ?? "show",
|
|
2833
|
+
args: ""
|
|
2834
|
+
};
|
|
2835
|
+
return {
|
|
2836
|
+
ok: true,
|
|
2837
|
+
action: parsed.action,
|
|
2838
|
+
args: parsed.args
|
|
2839
|
+
};
|
|
2840
|
+
}
|
|
2841
|
+
//#endregion
|
|
2842
|
+
//#region src/auto-reply/reply/commands-setunset.ts
|
|
2843
|
+
function parseSetUnsetCommand(params) {
|
|
2844
|
+
const action = params.action;
|
|
2845
|
+
const args = params.args.trim();
|
|
2846
|
+
if (action === "unset") {
|
|
2847
|
+
if (!args) return {
|
|
2848
|
+
kind: "error",
|
|
2849
|
+
message: `Usage: ${params.slash} unset path`
|
|
2850
|
+
};
|
|
2851
|
+
return {
|
|
2852
|
+
kind: "unset",
|
|
2853
|
+
path: args
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
if (!args) return {
|
|
2857
|
+
kind: "error",
|
|
2858
|
+
message: `Usage: ${params.slash} set path=value`
|
|
2859
|
+
};
|
|
2860
|
+
const eqIndex = args.indexOf("=");
|
|
2861
|
+
if (eqIndex <= 0) return {
|
|
2862
|
+
kind: "error",
|
|
2863
|
+
message: `Usage: ${params.slash} set path=value`
|
|
2864
|
+
};
|
|
2865
|
+
const path = args.slice(0, eqIndex).trim();
|
|
2866
|
+
const rawValue = args.slice(eqIndex + 1);
|
|
2867
|
+
if (!path) return {
|
|
2868
|
+
kind: "error",
|
|
2869
|
+
message: `Usage: ${params.slash} set path=value`
|
|
2870
|
+
};
|
|
2871
|
+
const parsed = parseConfigValue(rawValue);
|
|
2872
|
+
if (parsed.error) return {
|
|
2873
|
+
kind: "error",
|
|
2874
|
+
message: parsed.error
|
|
2875
|
+
};
|
|
2876
|
+
return {
|
|
2877
|
+
kind: "set",
|
|
2878
|
+
path,
|
|
2879
|
+
value: parsed.value
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
function parseSetUnsetCommandAction(params) {
|
|
2883
|
+
if (params.action !== "set" && params.action !== "unset") return null;
|
|
2884
|
+
const parsed = parseSetUnsetCommand({
|
|
2885
|
+
slash: params.slash,
|
|
2886
|
+
action: params.action,
|
|
2887
|
+
args: params.args
|
|
2888
|
+
});
|
|
2889
|
+
if (parsed.kind === "error") return params.onError(parsed.message);
|
|
2890
|
+
return parsed.kind === "set" ? params.onSet(parsed.path, parsed.value) : params.onUnset(parsed.path);
|
|
2891
|
+
}
|
|
2892
|
+
function parseSlashCommandWithSetUnset(params) {
|
|
2893
|
+
const parsed = parseSlashCommandOrNull(params.raw, params.slash, { invalidMessage: params.invalidMessage });
|
|
2894
|
+
if (!parsed) return null;
|
|
2895
|
+
if (!parsed.ok) return params.onError(parsed.message);
|
|
2896
|
+
const { action, args } = parsed;
|
|
2897
|
+
const setUnset = parseSetUnsetCommandAction({
|
|
2898
|
+
slash: params.slash,
|
|
2899
|
+
action,
|
|
2900
|
+
args,
|
|
2901
|
+
onSet: params.onSet,
|
|
2902
|
+
onUnset: params.onUnset,
|
|
2903
|
+
onError: params.onError
|
|
2904
|
+
});
|
|
2905
|
+
if (setUnset) return setUnset;
|
|
2906
|
+
const knownAction = params.onKnownAction(action, args);
|
|
2907
|
+
if (knownAction) return knownAction;
|
|
2908
|
+
return params.onError(params.usageMessage);
|
|
2909
|
+
}
|
|
2910
|
+
//#endregion
|
|
2911
|
+
//#region src/auto-reply/reply/commands-setunset-standard.ts
|
|
2912
|
+
function parseStandardSetUnsetSlashCommand(params) {
|
|
2913
|
+
return parseSlashCommandWithSetUnset({
|
|
2914
|
+
raw: params.raw,
|
|
2915
|
+
slash: params.slash,
|
|
2916
|
+
invalidMessage: params.invalidMessage,
|
|
2917
|
+
usageMessage: params.usageMessage,
|
|
2918
|
+
onKnownAction: params.onKnownAction,
|
|
2919
|
+
onSet: params.onSet ?? ((path, value) => ({
|
|
2920
|
+
action: "set",
|
|
2921
|
+
path,
|
|
2922
|
+
value
|
|
2923
|
+
})),
|
|
2924
|
+
onUnset: params.onUnset ?? ((path) => ({
|
|
2925
|
+
action: "unset",
|
|
2926
|
+
path
|
|
2927
|
+
})),
|
|
2928
|
+
onError: params.onError ?? ((message) => ({
|
|
2929
|
+
action: "error",
|
|
2930
|
+
message
|
|
2931
|
+
}))
|
|
2932
|
+
});
|
|
2933
|
+
}
|
|
2934
|
+
//#endregion
|
|
2935
|
+
//#region src/auto-reply/reply/config-commands.ts
|
|
2936
|
+
function parseConfigCommand(raw) {
|
|
2937
|
+
return parseStandardSetUnsetSlashCommand({
|
|
2938
|
+
raw,
|
|
2939
|
+
slash: "/config",
|
|
2940
|
+
invalidMessage: "Invalid /config syntax.",
|
|
2941
|
+
usageMessage: "Usage: /config show|set|unset",
|
|
2942
|
+
onKnownAction: (action, args) => {
|
|
2943
|
+
if (action === "show" || action === "get") return {
|
|
2944
|
+
action: "show",
|
|
2945
|
+
path: args || void 0
|
|
2946
|
+
};
|
|
2947
|
+
}
|
|
2948
|
+
});
|
|
2949
|
+
}
|
|
2950
|
+
//#endregion
|
|
2951
|
+
//#region src/auto-reply/reply/debug-commands.ts
|
|
2952
|
+
function parseDebugCommand(raw) {
|
|
2953
|
+
return parseStandardSetUnsetSlashCommand({
|
|
2954
|
+
raw,
|
|
2955
|
+
slash: "/debug",
|
|
2956
|
+
invalidMessage: "Invalid /debug syntax.",
|
|
2957
|
+
usageMessage: "Usage: /debug show|set|unset|reset",
|
|
2958
|
+
onKnownAction: (action) => {
|
|
2959
|
+
if (action === "show") return { action: "show" };
|
|
2960
|
+
if (action === "reset") return { action: "reset" };
|
|
2961
|
+
}
|
|
2962
|
+
});
|
|
2963
|
+
}
|
|
2964
|
+
//#endregion
|
|
2965
|
+
//#region src/auto-reply/reply/commands-config.ts
|
|
2966
|
+
const handleConfigCommand = async (params, allowTextCommands) => {
|
|
2967
|
+
if (!allowTextCommands) return null;
|
|
2968
|
+
const configCommand = parseConfigCommand(params.command.commandBodyNormalized);
|
|
2969
|
+
if (!configCommand) return null;
|
|
2970
|
+
const unauthorized = rejectUnauthorizedCommand(params, "/config");
|
|
2971
|
+
if (unauthorized) return unauthorized;
|
|
2972
|
+
const nonOwner = configCommand.action === "show" && isInternalMessageChannel(params.command.channel) ? null : rejectNonOwnerCommand(params, "/config");
|
|
2973
|
+
if (nonOwner) return nonOwner;
|
|
2974
|
+
const disabled = requireCommandFlagEnabled(params.cfg, {
|
|
2975
|
+
label: "/config",
|
|
2976
|
+
configKey: "config"
|
|
2977
|
+
});
|
|
2978
|
+
if (disabled) return disabled;
|
|
2979
|
+
if (configCommand.action === "error") return {
|
|
2980
|
+
shouldContinue: false,
|
|
2981
|
+
reply: { text: `⚠️ ${configCommand.message}` }
|
|
2982
|
+
};
|
|
2983
|
+
let parsedWritePath;
|
|
2984
|
+
if (configCommand.action === "set" || configCommand.action === "unset") {
|
|
2985
|
+
const missingAdminScope = requireGatewayClientScopeForInternalChannel(params, {
|
|
2986
|
+
label: "/config write",
|
|
2987
|
+
allowedScopes: ["operator.admin"],
|
|
2988
|
+
missingText: "❌ /config set|unset requires operator.admin for gateway clients."
|
|
2989
|
+
});
|
|
2990
|
+
if (missingAdminScope) return missingAdminScope;
|
|
2991
|
+
const parsedPath = parseConfigPath(configCommand.path);
|
|
2992
|
+
if (!parsedPath.ok || !parsedPath.path) return {
|
|
2993
|
+
shouldContinue: false,
|
|
2994
|
+
reply: { text: `⚠️ ${parsedPath.error ?? "Invalid path."}` }
|
|
2995
|
+
};
|
|
2996
|
+
parsedWritePath = parsedPath.path;
|
|
2997
|
+
const channelId = params.command.channelId ?? normalizeChannelId(params.command.channel);
|
|
2998
|
+
const deniedText = resolveConfigWriteDeniedText({
|
|
2999
|
+
cfg: params.cfg,
|
|
3000
|
+
channel: params.command.channel,
|
|
3001
|
+
channelId,
|
|
3002
|
+
accountId: params.ctx.AccountId,
|
|
3003
|
+
gatewayClientScopes: params.ctx.GatewayClientScopes,
|
|
3004
|
+
target: resolveConfigWriteTargetFromPath(parsedWritePath)
|
|
3005
|
+
});
|
|
3006
|
+
if (deniedText) return {
|
|
3007
|
+
shouldContinue: false,
|
|
3008
|
+
reply: { text: deniedText }
|
|
3009
|
+
};
|
|
3010
|
+
}
|
|
3011
|
+
const snapshot = await readConfigFileSnapshot();
|
|
3012
|
+
if (!snapshot.valid || !snapshot.parsed || typeof snapshot.parsed !== "object") return {
|
|
3013
|
+
shouldContinue: false,
|
|
3014
|
+
reply: { text: "⚠️ Config file is invalid; fix it before using /config." }
|
|
3015
|
+
};
|
|
3016
|
+
const parsedBase = structuredClone(snapshot.parsed);
|
|
3017
|
+
if (configCommand.action === "show") {
|
|
3018
|
+
const pathRaw = configCommand.path?.trim();
|
|
3019
|
+
if (pathRaw) {
|
|
3020
|
+
const parsedPath = parseConfigPath(pathRaw);
|
|
3021
|
+
if (!parsedPath.ok || !parsedPath.path) return {
|
|
3022
|
+
shouldContinue: false,
|
|
3023
|
+
reply: { text: `⚠️ ${parsedPath.error ?? "Invalid path."}` }
|
|
3024
|
+
};
|
|
3025
|
+
const value = getConfigValueAtPath(parsedBase, parsedPath.path);
|
|
3026
|
+
return {
|
|
3027
|
+
shouldContinue: false,
|
|
3028
|
+
reply: { text: `⚙️ Config ${pathRaw}:\n\`\`\`json\n${JSON.stringify(value ?? null, null, 2)}\n\`\`\`` }
|
|
3029
|
+
};
|
|
3030
|
+
}
|
|
3031
|
+
return {
|
|
3032
|
+
shouldContinue: false,
|
|
3033
|
+
reply: { text: `⚙️ Config (raw):\n\`\`\`json\n${JSON.stringify(parsedBase, null, 2)}\n\`\`\`` }
|
|
3034
|
+
};
|
|
3035
|
+
}
|
|
3036
|
+
if (configCommand.action === "unset") {
|
|
3037
|
+
if (!unsetConfigValueAtPath(parsedBase, parsedWritePath ?? [])) return {
|
|
3038
|
+
shouldContinue: false,
|
|
3039
|
+
reply: { text: `⚙️ No config value found for ${configCommand.path}.` }
|
|
3040
|
+
};
|
|
3041
|
+
const validated = validateConfigObjectWithPlugins(parsedBase);
|
|
3042
|
+
if (!validated.ok) {
|
|
3043
|
+
const issue = validated.issues[0];
|
|
3044
|
+
return {
|
|
3045
|
+
shouldContinue: false,
|
|
3046
|
+
reply: { text: `⚠️ Config invalid after unset (${issue.path}: ${issue.message}).` }
|
|
3047
|
+
};
|
|
3048
|
+
}
|
|
3049
|
+
await writeConfigFile(validated.config);
|
|
3050
|
+
return {
|
|
3051
|
+
shouldContinue: false,
|
|
3052
|
+
reply: { text: `⚙️ Config updated: ${configCommand.path} removed.` }
|
|
3053
|
+
};
|
|
3054
|
+
}
|
|
3055
|
+
if (configCommand.action === "set") {
|
|
3056
|
+
setConfigValueAtPath(parsedBase, parsedWritePath ?? [], configCommand.value);
|
|
3057
|
+
const validated = validateConfigObjectWithPlugins(parsedBase);
|
|
3058
|
+
if (!validated.ok) {
|
|
3059
|
+
const issue = validated.issues[0];
|
|
3060
|
+
return {
|
|
3061
|
+
shouldContinue: false,
|
|
3062
|
+
reply: { text: `⚠️ Config invalid after set (${issue.path}: ${issue.message}).` }
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
await writeConfigFile(validated.config);
|
|
3066
|
+
const valueLabel = typeof configCommand.value === "string" ? `"${configCommand.value}"` : JSON.stringify(configCommand.value);
|
|
3067
|
+
return {
|
|
3068
|
+
shouldContinue: false,
|
|
3069
|
+
reply: { text: `⚙️ Config updated: ${configCommand.path}=${valueLabel ?? "null"}` }
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3072
|
+
return null;
|
|
3073
|
+
};
|
|
3074
|
+
const handleDebugCommand = async (params, allowTextCommands) => {
|
|
3075
|
+
if (!allowTextCommands) return null;
|
|
3076
|
+
const debugCommand = parseDebugCommand(params.command.commandBodyNormalized);
|
|
3077
|
+
if (!debugCommand) return null;
|
|
3078
|
+
const unauthorized = rejectUnauthorizedCommand(params, "/debug");
|
|
3079
|
+
if (unauthorized) return unauthorized;
|
|
3080
|
+
const nonOwner = rejectNonOwnerCommand(params, "/debug");
|
|
3081
|
+
if (nonOwner) return nonOwner;
|
|
3082
|
+
const disabled = requireCommandFlagEnabled(params.cfg, {
|
|
3083
|
+
label: "/debug",
|
|
3084
|
+
configKey: "debug"
|
|
3085
|
+
});
|
|
3086
|
+
if (disabled) return disabled;
|
|
3087
|
+
if (debugCommand.action === "error") return {
|
|
3088
|
+
shouldContinue: false,
|
|
3089
|
+
reply: { text: `⚠️ ${debugCommand.message}` }
|
|
3090
|
+
};
|
|
3091
|
+
if (debugCommand.action === "show") {
|
|
3092
|
+
const overrides = getConfigOverrides();
|
|
3093
|
+
if (!(Object.keys(overrides).length > 0)) return {
|
|
3094
|
+
shouldContinue: false,
|
|
3095
|
+
reply: { text: "⚙️ Debug overrides: (none)" }
|
|
3096
|
+
};
|
|
3097
|
+
return {
|
|
3098
|
+
shouldContinue: false,
|
|
3099
|
+
reply: { text: `⚙️ Debug overrides (memory-only):\n\`\`\`json\n${JSON.stringify(overrides, null, 2)}\n\`\`\`` }
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3102
|
+
if (debugCommand.action === "reset") {
|
|
3103
|
+
resetConfigOverrides();
|
|
3104
|
+
return {
|
|
3105
|
+
shouldContinue: false,
|
|
3106
|
+
reply: { text: "⚙️ Debug overrides cleared; using config on disk." }
|
|
3107
|
+
};
|
|
3108
|
+
}
|
|
3109
|
+
if (debugCommand.action === "unset") {
|
|
3110
|
+
const result = unsetConfigOverride(debugCommand.path);
|
|
3111
|
+
if (!result.ok) return {
|
|
3112
|
+
shouldContinue: false,
|
|
3113
|
+
reply: { text: `⚠️ ${result.error ?? "Invalid path."}` }
|
|
3114
|
+
};
|
|
3115
|
+
if (!result.removed) return {
|
|
3116
|
+
shouldContinue: false,
|
|
3117
|
+
reply: { text: `⚙️ No debug override found for ${debugCommand.path}.` }
|
|
3118
|
+
};
|
|
3119
|
+
return {
|
|
3120
|
+
shouldContinue: false,
|
|
3121
|
+
reply: { text: `⚙️ Debug override removed for ${debugCommand.path}.` }
|
|
3122
|
+
};
|
|
3123
|
+
}
|
|
3124
|
+
if (debugCommand.action === "set") {
|
|
3125
|
+
const result = setConfigOverride(debugCommand.path, debugCommand.value);
|
|
3126
|
+
if (!result.ok) return {
|
|
3127
|
+
shouldContinue: false,
|
|
3128
|
+
reply: { text: `⚠️ ${result.error ?? "Invalid override."}` }
|
|
3129
|
+
};
|
|
3130
|
+
const valueLabel = typeof debugCommand.value === "string" ? `"${debugCommand.value}"` : JSON.stringify(debugCommand.value);
|
|
3131
|
+
return {
|
|
3132
|
+
shouldContinue: false,
|
|
3133
|
+
reply: { text: `⚙️ Debug override set: ${debugCommand.path}=${valueLabel ?? "null"}` }
|
|
3134
|
+
};
|
|
3135
|
+
}
|
|
3136
|
+
return null;
|
|
3137
|
+
};
|
|
3138
|
+
//#endregion
|
|
3139
|
+
//#region src/auto-reply/reply/mcp-commands.ts
|
|
3140
|
+
function parseMcpCommand(raw) {
|
|
3141
|
+
return parseStandardSetUnsetSlashCommand({
|
|
3142
|
+
raw,
|
|
3143
|
+
slash: "/mcp",
|
|
3144
|
+
invalidMessage: "Invalid /mcp syntax.",
|
|
3145
|
+
usageMessage: "Usage: /mcp show|set|unset",
|
|
3146
|
+
onKnownAction: (action, args) => {
|
|
3147
|
+
if (action === "show" || action === "get") return {
|
|
3148
|
+
action: "show",
|
|
3149
|
+
name: args || void 0
|
|
3150
|
+
};
|
|
3151
|
+
},
|
|
3152
|
+
onSet: (name, value) => ({
|
|
3153
|
+
action: "set",
|
|
3154
|
+
name,
|
|
3155
|
+
value
|
|
3156
|
+
}),
|
|
3157
|
+
onUnset: (name) => ({
|
|
3158
|
+
action: "unset",
|
|
3159
|
+
name
|
|
3160
|
+
})
|
|
3161
|
+
});
|
|
3162
|
+
}
|
|
3163
|
+
//#endregion
|
|
3164
|
+
//#region src/auto-reply/reply/commands-mcp.ts
|
|
3165
|
+
function renderJsonBlock$1(label, value) {
|
|
3166
|
+
return `${label}\n\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\``;
|
|
3167
|
+
}
|
|
3168
|
+
const handleMcpCommand = async (params, allowTextCommands) => {
|
|
3169
|
+
if (!allowTextCommands) return null;
|
|
3170
|
+
const mcpCommand = parseMcpCommand(params.command.commandBodyNormalized);
|
|
3171
|
+
if (!mcpCommand) return null;
|
|
3172
|
+
const unauthorized = rejectUnauthorizedCommand(params, "/mcp");
|
|
3173
|
+
if (unauthorized) return unauthorized;
|
|
3174
|
+
const nonOwner = mcpCommand.action === "show" && isInternalMessageChannel(params.command.channel) ? null : rejectNonOwnerCommand(params, "/mcp");
|
|
3175
|
+
if (nonOwner) return nonOwner;
|
|
3176
|
+
const disabled = requireCommandFlagEnabled(params.cfg, {
|
|
3177
|
+
label: "/mcp",
|
|
3178
|
+
configKey: "mcp"
|
|
3179
|
+
});
|
|
3180
|
+
if (disabled) return disabled;
|
|
3181
|
+
if (mcpCommand.action === "error") return {
|
|
3182
|
+
shouldContinue: false,
|
|
3183
|
+
reply: { text: `⚠️ ${mcpCommand.message}` }
|
|
3184
|
+
};
|
|
3185
|
+
if (mcpCommand.action === "show") {
|
|
3186
|
+
const loaded = await listConfiguredMcpServers();
|
|
3187
|
+
if (!loaded.ok) return {
|
|
3188
|
+
shouldContinue: false,
|
|
3189
|
+
reply: { text: `⚠️ ${loaded.error}` }
|
|
3190
|
+
};
|
|
3191
|
+
if (mcpCommand.name) {
|
|
3192
|
+
const server = loaded.mcpServers[mcpCommand.name];
|
|
3193
|
+
if (!server) return {
|
|
3194
|
+
shouldContinue: false,
|
|
3195
|
+
reply: { text: `🔌 No MCP server named "${mcpCommand.name}" in ${loaded.path}.` }
|
|
3196
|
+
};
|
|
3197
|
+
return {
|
|
3198
|
+
shouldContinue: false,
|
|
3199
|
+
reply: { text: renderJsonBlock$1(`🔌 MCP server "${mcpCommand.name}" (${loaded.path})`, server) }
|
|
3200
|
+
};
|
|
3201
|
+
}
|
|
3202
|
+
if (Object.keys(loaded.mcpServers).length === 0) return {
|
|
3203
|
+
shouldContinue: false,
|
|
3204
|
+
reply: { text: `🔌 No MCP servers configured in ${loaded.path}.` }
|
|
3205
|
+
};
|
|
3206
|
+
return {
|
|
3207
|
+
shouldContinue: false,
|
|
3208
|
+
reply: { text: renderJsonBlock$1(`🔌 MCP servers (${loaded.path})`, loaded.mcpServers) }
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3211
|
+
const missingAdminScope = requireGatewayClientScopeForInternalChannel(params, {
|
|
3212
|
+
label: "/mcp write",
|
|
3213
|
+
allowedScopes: ["operator.admin"],
|
|
3214
|
+
missingText: "❌ /mcp set|unset requires operator.admin for gateway clients."
|
|
3215
|
+
});
|
|
3216
|
+
if (missingAdminScope) return missingAdminScope;
|
|
3217
|
+
if (mcpCommand.action === "set") {
|
|
3218
|
+
const result = await setConfiguredMcpServer({
|
|
3219
|
+
name: mcpCommand.name,
|
|
3220
|
+
server: mcpCommand.value
|
|
3221
|
+
});
|
|
3222
|
+
if (!result.ok) return {
|
|
3223
|
+
shouldContinue: false,
|
|
3224
|
+
reply: { text: `⚠️ ${result.error}` }
|
|
3225
|
+
};
|
|
3226
|
+
return {
|
|
3227
|
+
shouldContinue: false,
|
|
3228
|
+
reply: { text: `🔌 MCP server "${mcpCommand.name}" saved to ${result.path}.` }
|
|
3229
|
+
};
|
|
3230
|
+
}
|
|
3231
|
+
const result = await unsetConfiguredMcpServer({ name: mcpCommand.name });
|
|
3232
|
+
if (!result.ok) return {
|
|
3233
|
+
shouldContinue: false,
|
|
3234
|
+
reply: { text: `⚠️ ${result.error}` }
|
|
3235
|
+
};
|
|
3236
|
+
if (!result.removed) return {
|
|
3237
|
+
shouldContinue: false,
|
|
3238
|
+
reply: { text: `🔌 No MCP server named "${mcpCommand.name}" in ${result.path}.` }
|
|
3239
|
+
};
|
|
3240
|
+
return {
|
|
3241
|
+
shouldContinue: false,
|
|
3242
|
+
reply: { text: `🔌 MCP server "${mcpCommand.name}" removed from ${result.path}.` }
|
|
3243
|
+
};
|
|
3244
|
+
};
|
|
3245
|
+
//#endregion
|
|
3246
|
+
//#region src/auto-reply/reply/commands-plugin.ts
|
|
3247
|
+
/**
|
|
3248
|
+
* Plugin Command Handler
|
|
3249
|
+
*
|
|
3250
|
+
* Handles commands registered by plugins, bypassing the LLM agent.
|
|
3251
|
+
* This handler is called before built-in command handlers.
|
|
3252
|
+
*/
|
|
3253
|
+
/**
|
|
3254
|
+
* Handle plugin-registered commands.
|
|
3255
|
+
* Returns a result if a plugin command was matched and executed,
|
|
3256
|
+
* or null to continue to the next handler.
|
|
3257
|
+
*/
|
|
3258
|
+
const handlePluginCommand = async (params, allowTextCommands) => {
|
|
3259
|
+
const { command, cfg } = params;
|
|
3260
|
+
if (!allowTextCommands) return null;
|
|
3261
|
+
const match = matchPluginCommand(command.commandBodyNormalized);
|
|
3262
|
+
if (!match) return null;
|
|
3263
|
+
return {
|
|
3264
|
+
shouldContinue: false,
|
|
3265
|
+
reply: await executePluginCommand({
|
|
3266
|
+
command: match.command,
|
|
3267
|
+
args: match.args,
|
|
3268
|
+
senderId: command.senderId,
|
|
3269
|
+
channel: command.channel,
|
|
3270
|
+
channelId: command.channelId,
|
|
3271
|
+
isAuthorizedSender: command.isAuthorizedSender,
|
|
3272
|
+
commandBody: command.commandBodyNormalized,
|
|
3273
|
+
config: cfg,
|
|
3274
|
+
from: command.from,
|
|
3275
|
+
to: command.to,
|
|
3276
|
+
accountId: params.ctx.AccountId ?? void 0,
|
|
3277
|
+
messageThreadId: typeof params.ctx.MessageThreadId === "string" || typeof params.ctx.MessageThreadId === "number" ? params.ctx.MessageThreadId : void 0
|
|
3278
|
+
})
|
|
3279
|
+
};
|
|
3280
|
+
};
|
|
3281
|
+
//#endregion
|
|
3282
|
+
//#region src/auto-reply/reply/plugins-commands.ts
|
|
3283
|
+
function parsePluginsCommand(raw) {
|
|
3284
|
+
const match = raw.match(/^\/plugins?(?:\s+(.*))?$/i);
|
|
3285
|
+
if (!match) return null;
|
|
3286
|
+
const tail = match[1]?.trim() ?? "";
|
|
3287
|
+
if (!tail) return { action: "list" };
|
|
3288
|
+
const [rawAction, ...rest] = tail.split(/\s+/);
|
|
3289
|
+
const action = rawAction?.trim().toLowerCase();
|
|
3290
|
+
const name = rest.join(" ").trim();
|
|
3291
|
+
if (action === "list") return name ? {
|
|
3292
|
+
action: "error",
|
|
3293
|
+
message: "Usage: /plugins list|inspect|show|get|enable|disable [plugin]"
|
|
3294
|
+
} : { action: "list" };
|
|
3295
|
+
if (action === "inspect" || action === "show" || action === "get") return {
|
|
3296
|
+
action: "inspect",
|
|
3297
|
+
name: name || void 0
|
|
3298
|
+
};
|
|
3299
|
+
if (action === "enable" || action === "disable") {
|
|
3300
|
+
if (!name) return {
|
|
3301
|
+
action: "error",
|
|
3302
|
+
message: `Usage: /plugins ${action} <plugin-id-or-name>`
|
|
3303
|
+
};
|
|
3304
|
+
return {
|
|
3305
|
+
action,
|
|
3306
|
+
name
|
|
3307
|
+
};
|
|
3308
|
+
}
|
|
3309
|
+
return {
|
|
3310
|
+
action: "error",
|
|
3311
|
+
message: "Usage: /plugins list|inspect|show|get|enable|disable [plugin]"
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
//#endregion
|
|
3315
|
+
//#region src/auto-reply/reply/commands-plugins.ts
|
|
3316
|
+
function renderJsonBlock(label, value) {
|
|
3317
|
+
return `${label}\n\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\``;
|
|
3318
|
+
}
|
|
3319
|
+
function buildPluginInspectJson(params) {
|
|
3320
|
+
const inspect = buildPluginInspectReport({
|
|
3321
|
+
id: params.id,
|
|
3322
|
+
config: params.config,
|
|
3323
|
+
report: params.report
|
|
3324
|
+
});
|
|
3325
|
+
if (!inspect) return null;
|
|
3326
|
+
return {
|
|
3327
|
+
inspect,
|
|
3328
|
+
compatibilityWarnings: inspect.compatibility.map((warning) => ({
|
|
3329
|
+
code: warning.code,
|
|
3330
|
+
severity: warning.severity,
|
|
3331
|
+
message: formatPluginCompatibilityNotice(warning)
|
|
3332
|
+
})),
|
|
3333
|
+
install: params.config.plugins?.installs?.[inspect.plugin.id] ?? null
|
|
3334
|
+
};
|
|
3335
|
+
}
|
|
3336
|
+
function buildAllPluginInspectJson(params) {
|
|
3337
|
+
return buildAllPluginInspectReports({
|
|
3338
|
+
config: params.config,
|
|
3339
|
+
report: params.report
|
|
3340
|
+
}).map((inspect) => ({
|
|
3341
|
+
inspect,
|
|
3342
|
+
compatibilityWarnings: inspect.compatibility.map((warning) => ({
|
|
3343
|
+
code: warning.code,
|
|
3344
|
+
severity: warning.severity,
|
|
3345
|
+
message: formatPluginCompatibilityNotice(warning)
|
|
3346
|
+
})),
|
|
3347
|
+
install: params.config.plugins?.installs?.[inspect.plugin.id] ?? null
|
|
3348
|
+
}));
|
|
3349
|
+
}
|
|
3350
|
+
function formatPluginLabel(plugin) {
|
|
3351
|
+
if (!plugin.name || plugin.name === plugin.id) return plugin.id;
|
|
3352
|
+
return `${plugin.name} (${plugin.id})`;
|
|
3353
|
+
}
|
|
3354
|
+
function formatPluginsList(report) {
|
|
3355
|
+
if (report.plugins.length === 0) return `🔌 No plugins found for workspace ${report.workspaceDir ?? "(unknown workspace)"}.`;
|
|
3356
|
+
return [`🔌 Plugins (${report.plugins.filter((plugin) => plugin.status === "loaded").length}/${report.plugins.length} loaded)`, ...report.plugins.map((plugin) => {
|
|
3357
|
+
const format = plugin.bundleFormat ? `${plugin.format ?? "openclaw"}/${plugin.bundleFormat}` : plugin.format ?? "openclaw";
|
|
3358
|
+
return `- ${formatPluginLabel(plugin)} [${plugin.status}] ${format}`;
|
|
3359
|
+
})].join("\n");
|
|
3360
|
+
}
|
|
3361
|
+
function findPlugin(report, rawName) {
|
|
3362
|
+
const target = rawName.trim().toLowerCase();
|
|
3363
|
+
if (!target) return;
|
|
3364
|
+
return report.plugins.find((plugin) => plugin.id.toLowerCase() === target || plugin.name.toLowerCase() === target);
|
|
3365
|
+
}
|
|
3366
|
+
async function loadPluginCommandState(workspaceDir) {
|
|
3367
|
+
const snapshot = await readConfigFileSnapshot();
|
|
3368
|
+
if (!snapshot.valid) return {
|
|
3369
|
+
ok: false,
|
|
3370
|
+
path: snapshot.path,
|
|
3371
|
+
error: "Config file is invalid; fix it before using /plugins."
|
|
3372
|
+
};
|
|
3373
|
+
const config = structuredClone(snapshot.resolved);
|
|
3374
|
+
return {
|
|
3375
|
+
ok: true,
|
|
3376
|
+
path: snapshot.path,
|
|
3377
|
+
config,
|
|
3378
|
+
report: buildPluginStatusReport({
|
|
3379
|
+
config,
|
|
3380
|
+
workspaceDir
|
|
3381
|
+
})
|
|
3382
|
+
};
|
|
3383
|
+
}
|
|
3384
|
+
const handlePluginsCommand = async (params, allowTextCommands) => {
|
|
3385
|
+
if (!allowTextCommands) return null;
|
|
3386
|
+
const pluginsCommand = parsePluginsCommand(params.command.commandBodyNormalized);
|
|
3387
|
+
if (!pluginsCommand) return null;
|
|
3388
|
+
const unauthorized = rejectUnauthorizedCommand(params, "/plugins");
|
|
3389
|
+
if (unauthorized) return unauthorized;
|
|
3390
|
+
const nonOwner = (pluginsCommand.action === "list" || pluginsCommand.action === "inspect") && isInternalMessageChannel(params.command.channel) ? null : rejectNonOwnerCommand(params, "/plugins");
|
|
3391
|
+
if (nonOwner) return nonOwner;
|
|
3392
|
+
const disabled = requireCommandFlagEnabled(params.cfg, {
|
|
3393
|
+
label: "/plugins",
|
|
3394
|
+
configKey: "plugins"
|
|
3395
|
+
});
|
|
3396
|
+
if (disabled) return disabled;
|
|
3397
|
+
if (pluginsCommand.action === "error") return {
|
|
3398
|
+
shouldContinue: false,
|
|
3399
|
+
reply: { text: `⚠️ ${pluginsCommand.message}` }
|
|
3400
|
+
};
|
|
3401
|
+
const loaded = await loadPluginCommandState(params.workspaceDir);
|
|
3402
|
+
if (!loaded.ok) return {
|
|
3403
|
+
shouldContinue: false,
|
|
3404
|
+
reply: { text: `⚠️ ${loaded.error}` }
|
|
3405
|
+
};
|
|
3406
|
+
if (pluginsCommand.action === "list") return {
|
|
3407
|
+
shouldContinue: false,
|
|
3408
|
+
reply: { text: formatPluginsList(loaded.report) }
|
|
3409
|
+
};
|
|
3410
|
+
if (pluginsCommand.action === "inspect") {
|
|
3411
|
+
if (!pluginsCommand.name) return {
|
|
3412
|
+
shouldContinue: false,
|
|
3413
|
+
reply: { text: formatPluginsList(loaded.report) }
|
|
3414
|
+
};
|
|
3415
|
+
if (pluginsCommand.name.toLowerCase() === "all") return {
|
|
3416
|
+
shouldContinue: false,
|
|
3417
|
+
reply: { text: renderJsonBlock("🔌 Plugins", buildAllPluginInspectJson(loaded)) }
|
|
3418
|
+
};
|
|
3419
|
+
const payload = buildPluginInspectJson({
|
|
3420
|
+
id: pluginsCommand.name,
|
|
3421
|
+
config: loaded.config,
|
|
3422
|
+
report: loaded.report
|
|
3423
|
+
});
|
|
3424
|
+
if (!payload) return {
|
|
3425
|
+
shouldContinue: false,
|
|
3426
|
+
reply: { text: `🔌 No plugin named "${pluginsCommand.name}" found.` }
|
|
3427
|
+
};
|
|
3428
|
+
return {
|
|
3429
|
+
shouldContinue: false,
|
|
3430
|
+
reply: { text: renderJsonBlock(`🔌 Plugin "${payload.inspect.plugin.id}"`, {
|
|
3431
|
+
...payload.inspect,
|
|
3432
|
+
install: payload.install
|
|
3433
|
+
}) }
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3436
|
+
const missingAdminScope = requireGatewayClientScopeForInternalChannel(params, {
|
|
3437
|
+
label: "/plugins write",
|
|
3438
|
+
allowedScopes: ["operator.admin"],
|
|
3439
|
+
missingText: "❌ /plugins enable|disable requires operator.admin for gateway clients."
|
|
3440
|
+
});
|
|
3441
|
+
if (missingAdminScope) return missingAdminScope;
|
|
3442
|
+
const plugin = findPlugin(loaded.report, pluginsCommand.name);
|
|
3443
|
+
if (!plugin) return {
|
|
3444
|
+
shouldContinue: false,
|
|
3445
|
+
reply: { text: `🔌 No plugin named "${pluginsCommand.name}" found.` }
|
|
3446
|
+
};
|
|
3447
|
+
const validated = validateConfigObjectWithPlugins(setPluginEnabledInConfig(structuredClone(loaded.config), plugin.id, pluginsCommand.action === "enable"));
|
|
3448
|
+
if (!validated.ok) {
|
|
3449
|
+
const issue = validated.issues[0];
|
|
3450
|
+
return {
|
|
3451
|
+
shouldContinue: false,
|
|
3452
|
+
reply: { text: `⚠️ Config invalid after /plugins ${pluginsCommand.action} (${issue.path}: ${issue.message}).` }
|
|
3453
|
+
};
|
|
3454
|
+
}
|
|
3455
|
+
await writeConfigFile(validated.config);
|
|
3456
|
+
return {
|
|
3457
|
+
shouldContinue: false,
|
|
3458
|
+
reply: { text: `🔌 Plugin "${plugin.id}" ${pluginsCommand.action}d in ${loaded.path}. Restart the gateway to apply.` }
|
|
3459
|
+
};
|
|
3460
|
+
};
|
|
3461
|
+
//#endregion
|
|
3462
|
+
//#region src/auto-reply/send-policy.ts
|
|
3463
|
+
function normalizeSendPolicyOverride(raw) {
|
|
3464
|
+
const value = raw?.trim().toLowerCase();
|
|
3465
|
+
if (!value) return;
|
|
3466
|
+
if (value === "allow" || value === "on") return "allow";
|
|
3467
|
+
if (value === "deny" || value === "off") return "deny";
|
|
3468
|
+
}
|
|
3469
|
+
function parseSendPolicyCommand(raw) {
|
|
3470
|
+
if (!raw) return { hasCommand: false };
|
|
3471
|
+
const trimmed = raw.trim();
|
|
3472
|
+
if (!trimmed) return { hasCommand: false };
|
|
3473
|
+
const match = normalizeCommandBody(trimmed).match(/^\/send(?:\s+([a-zA-Z]+))?\s*$/i);
|
|
3474
|
+
if (!match) return { hasCommand: false };
|
|
3475
|
+
const token = match[1]?.trim().toLowerCase();
|
|
3476
|
+
if (!token) return { hasCommand: true };
|
|
3477
|
+
if (token === "inherit" || token === "default" || token === "reset") return {
|
|
3478
|
+
hasCommand: true,
|
|
3479
|
+
mode: "inherit"
|
|
3480
|
+
};
|
|
3481
|
+
return {
|
|
3482
|
+
hasCommand: true,
|
|
3483
|
+
mode: normalizeSendPolicyOverride(token)
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
//#endregion
|
|
3487
|
+
//#region src/auto-reply/reply/commands-session-store.ts
|
|
3488
|
+
async function persistSessionEntry(params) {
|
|
3489
|
+
if (!params.sessionEntry || !params.sessionStore || !params.sessionKey) return false;
|
|
3490
|
+
params.sessionEntry.updatedAt = Date.now();
|
|
3491
|
+
params.sessionStore[params.sessionKey] = params.sessionEntry;
|
|
3492
|
+
if (params.storePath) await updateSessionStore(params.storePath, (store) => {
|
|
3493
|
+
store[params.sessionKey] = params.sessionEntry;
|
|
3494
|
+
});
|
|
3495
|
+
return true;
|
|
3496
|
+
}
|
|
3497
|
+
async function persistAbortTargetEntry(params) {
|
|
3498
|
+
const { entry, key, sessionStore, storePath, abortCutoff } = params;
|
|
3499
|
+
if (!entry || !key || !sessionStore) return false;
|
|
3500
|
+
entry.abortedLastRun = true;
|
|
3501
|
+
applyAbortCutoffToSessionEntry(entry, abortCutoff);
|
|
3502
|
+
entry.updatedAt = Date.now();
|
|
3503
|
+
sessionStore[key] = entry;
|
|
3504
|
+
if (storePath) await updateSessionStore(storePath, (store) => {
|
|
3505
|
+
const nextEntry = store[key] ?? entry;
|
|
3506
|
+
if (!nextEntry) return;
|
|
3507
|
+
nextEntry.abortedLastRun = true;
|
|
3508
|
+
applyAbortCutoffToSessionEntry(nextEntry, abortCutoff);
|
|
3509
|
+
nextEntry.updatedAt = Date.now();
|
|
3510
|
+
store[key] = nextEntry;
|
|
3511
|
+
});
|
|
3512
|
+
return true;
|
|
3513
|
+
}
|
|
3514
|
+
//#endregion
|
|
3515
|
+
//#region src/auto-reply/reply/commands-session-abort.ts
|
|
3516
|
+
function resolveAbortTarget(params) {
|
|
3517
|
+
const targetSessionKey = params.ctx.CommandTargetSessionKey?.trim() || params.sessionKey;
|
|
3518
|
+
const { entry, key } = resolveSessionEntryForKey(params.sessionStore, targetSessionKey);
|
|
3519
|
+
if (entry && key) return {
|
|
3520
|
+
entry,
|
|
3521
|
+
key,
|
|
3522
|
+
sessionId: entry.sessionId
|
|
3523
|
+
};
|
|
3524
|
+
if (params.sessionEntry && params.sessionKey) return {
|
|
3525
|
+
entry: params.sessionEntry,
|
|
3526
|
+
key: params.sessionKey,
|
|
3527
|
+
sessionId: params.sessionEntry.sessionId
|
|
3528
|
+
};
|
|
3529
|
+
return {
|
|
3530
|
+
entry: void 0,
|
|
3531
|
+
key: targetSessionKey,
|
|
3532
|
+
sessionId: void 0
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3535
|
+
function resolveAbortCutoffForTarget(params) {
|
|
3536
|
+
if (!shouldPersistAbortCutoff({
|
|
3537
|
+
commandSessionKey: params.commandSessionKey,
|
|
3538
|
+
targetSessionKey: params.targetSessionKey
|
|
3539
|
+
})) return;
|
|
3540
|
+
return resolveAbortCutoffFromContext(params.ctx);
|
|
3541
|
+
}
|
|
3542
|
+
async function applyAbortTarget(params) {
|
|
3543
|
+
const { abortTarget } = params;
|
|
3544
|
+
if (abortTarget.sessionId) abortEmbeddedPiRun(abortTarget.sessionId);
|
|
3545
|
+
if (!await persistAbortTargetEntry({
|
|
3546
|
+
entry: abortTarget.entry,
|
|
3547
|
+
key: abortTarget.key,
|
|
3548
|
+
sessionStore: params.sessionStore,
|
|
3549
|
+
storePath: params.storePath,
|
|
3550
|
+
abortCutoff: params.abortCutoff
|
|
3551
|
+
}) && params.abortKey) setAbortMemory(params.abortKey, true);
|
|
3552
|
+
}
|
|
3553
|
+
function buildAbortTargetApplyParams(params, abortTarget) {
|
|
3554
|
+
return {
|
|
3555
|
+
abortTarget,
|
|
3556
|
+
sessionStore: params.sessionStore,
|
|
3557
|
+
storePath: params.storePath,
|
|
3558
|
+
abortKey: params.command.abortKey,
|
|
3559
|
+
abortCutoff: resolveAbortCutoffForTarget({
|
|
3560
|
+
ctx: params.ctx,
|
|
3561
|
+
commandSessionKey: params.sessionKey,
|
|
3562
|
+
targetSessionKey: abortTarget.key
|
|
3563
|
+
})
|
|
3564
|
+
};
|
|
3565
|
+
}
|
|
3566
|
+
const handleStopCommand = async (params, allowTextCommands) => {
|
|
3567
|
+
if (!allowTextCommands) return null;
|
|
3568
|
+
if (params.command.commandBodyNormalized !== "/stop") return null;
|
|
3569
|
+
const unauthorizedStop = rejectUnauthorizedCommand(params, "/stop");
|
|
3570
|
+
if (unauthorizedStop) return unauthorizedStop;
|
|
3571
|
+
const abortTarget = resolveAbortTarget({
|
|
3572
|
+
ctx: params.ctx,
|
|
3573
|
+
sessionKey: params.sessionKey,
|
|
3574
|
+
sessionEntry: params.sessionEntry,
|
|
3575
|
+
sessionStore: params.sessionStore
|
|
3576
|
+
});
|
|
3577
|
+
const cleared = clearSessionQueues([abortTarget.key, abortTarget.sessionId]);
|
|
3578
|
+
if (cleared.followupCleared > 0 || cleared.laneCleared > 0) logVerbose(`stop: cleared followups=${cleared.followupCleared} lane=${cleared.laneCleared} keys=${cleared.keys.join(",")}`);
|
|
3579
|
+
await applyAbortTarget(buildAbortTargetApplyParams(params, abortTarget));
|
|
3580
|
+
await triggerInternalHook(createInternalHookEvent("command", "stop", abortTarget.key ?? params.sessionKey ?? "", {
|
|
3581
|
+
sessionEntry: abortTarget.entry ?? params.sessionEntry,
|
|
3582
|
+
sessionId: abortTarget.sessionId,
|
|
3583
|
+
commandSource: params.command.surface,
|
|
3584
|
+
senderId: params.command.senderId
|
|
3585
|
+
}));
|
|
3586
|
+
const { stopped } = stopSubagentsForRequester({
|
|
3587
|
+
cfg: params.cfg,
|
|
3588
|
+
requesterSessionKey: abortTarget.key ?? params.sessionKey
|
|
3589
|
+
});
|
|
3590
|
+
return {
|
|
3591
|
+
shouldContinue: false,
|
|
3592
|
+
reply: { text: formatAbortReplyText(stopped) }
|
|
3593
|
+
};
|
|
3594
|
+
};
|
|
3595
|
+
const handleAbortTrigger = async (params, allowTextCommands) => {
|
|
3596
|
+
if (!allowTextCommands) return null;
|
|
3597
|
+
if (!isAbortTrigger(params.command.rawBodyNormalized)) return null;
|
|
3598
|
+
const unauthorizedAbortTrigger = rejectUnauthorizedCommand(params, "abort trigger");
|
|
3599
|
+
if (unauthorizedAbortTrigger) return unauthorizedAbortTrigger;
|
|
3600
|
+
await applyAbortTarget(buildAbortTargetApplyParams(params, resolveAbortTarget({
|
|
3601
|
+
ctx: params.ctx,
|
|
3602
|
+
sessionKey: params.sessionKey,
|
|
3603
|
+
sessionEntry: params.sessionEntry,
|
|
3604
|
+
sessionStore: params.sessionStore
|
|
3605
|
+
})));
|
|
3606
|
+
return {
|
|
3607
|
+
shouldContinue: false,
|
|
3608
|
+
reply: { text: "⚙️ Agent was aborted." }
|
|
3609
|
+
};
|
|
3610
|
+
};
|
|
3611
|
+
//#endregion
|
|
3612
|
+
//#region src/auto-reply/reply/commands-session.ts
|
|
3613
|
+
const SESSION_DURATION_OFF_VALUES = new Set([
|
|
3614
|
+
"off",
|
|
3615
|
+
"disable",
|
|
3616
|
+
"disabled",
|
|
3617
|
+
"none",
|
|
3618
|
+
"0"
|
|
3619
|
+
]);
|
|
3620
|
+
const SESSION_ACTION_IDLE = "idle";
|
|
3621
|
+
const SESSION_ACTION_MAX_AGE = "max-age";
|
|
3622
|
+
let cachedChannelRuntime;
|
|
3623
|
+
function getChannelRuntime() {
|
|
3624
|
+
cachedChannelRuntime ??= createPluginRuntime().channel;
|
|
3625
|
+
return cachedChannelRuntime;
|
|
3626
|
+
}
|
|
3627
|
+
function resolveSessionCommandUsage() {
|
|
3628
|
+
return "Usage: /session idle <duration|off> | /session max-age <duration|off> (example: /session idle 24h)";
|
|
3629
|
+
}
|
|
3630
|
+
function parseSessionDurationMs(raw) {
|
|
3631
|
+
const normalized = raw.trim().toLowerCase();
|
|
3632
|
+
if (!normalized) throw new Error("missing duration");
|
|
3633
|
+
if (SESSION_DURATION_OFF_VALUES.has(normalized)) return 0;
|
|
3634
|
+
if (/^\d+(?:\.\d+)?$/.test(normalized)) {
|
|
3635
|
+
const hours = Number(normalized);
|
|
3636
|
+
if (!Number.isFinite(hours) || hours < 0) throw new Error("invalid duration");
|
|
3637
|
+
return Math.round(hours * 60 * 60 * 1e3);
|
|
3638
|
+
}
|
|
3639
|
+
return parseDurationMs(normalized, { defaultUnit: "h" });
|
|
3640
|
+
}
|
|
3641
|
+
function formatSessionExpiry(expiresAt) {
|
|
3642
|
+
return new Date(expiresAt).toISOString();
|
|
3643
|
+
}
|
|
3644
|
+
function resolveSessionBindingDurationMs(binding, key, fallbackMs) {
|
|
3645
|
+
const raw = binding.metadata?.[key];
|
|
3646
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return fallbackMs;
|
|
3647
|
+
return Math.max(0, Math.floor(raw));
|
|
3648
|
+
}
|
|
3649
|
+
function resolveSessionBindingLastActivityAt(binding) {
|
|
3650
|
+
const raw = binding.metadata?.lastActivityAt;
|
|
3651
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return binding.boundAt;
|
|
3652
|
+
return Math.max(Math.floor(raw), binding.boundAt);
|
|
3653
|
+
}
|
|
3654
|
+
function resolveSessionBindingBoundBy(binding) {
|
|
3655
|
+
const raw = binding.metadata?.boundBy;
|
|
3656
|
+
return typeof raw === "string" ? raw.trim() : "";
|
|
3657
|
+
}
|
|
3658
|
+
function isSessionBindingRecord(binding) {
|
|
3659
|
+
return "bindingId" in binding;
|
|
3660
|
+
}
|
|
3661
|
+
function resolveUpdatedLifecycleDurationMs(binding, key) {
|
|
3662
|
+
if (!isSessionBindingRecord(binding)) {
|
|
3663
|
+
const raw = binding[key];
|
|
3664
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return Math.max(0, Math.floor(raw));
|
|
3665
|
+
}
|
|
3666
|
+
if (!isSessionBindingRecord(binding)) return;
|
|
3667
|
+
const raw = binding.metadata?.[key];
|
|
3668
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return;
|
|
3669
|
+
return Math.max(0, Math.floor(raw));
|
|
3670
|
+
}
|
|
3671
|
+
function toUpdatedLifecycleBinding(binding) {
|
|
3672
|
+
const lastActivityAt = isSessionBindingRecord(binding) ? resolveSessionBindingLastActivityAt(binding) : Math.max(Math.floor(binding.lastActivityAt), binding.boundAt);
|
|
3673
|
+
return {
|
|
3674
|
+
boundAt: binding.boundAt,
|
|
3675
|
+
lastActivityAt,
|
|
3676
|
+
idleTimeoutMs: resolveUpdatedLifecycleDurationMs(binding, "idleTimeoutMs"),
|
|
3677
|
+
maxAgeMs: resolveUpdatedLifecycleDurationMs(binding, "maxAgeMs")
|
|
3678
|
+
};
|
|
3679
|
+
}
|
|
3680
|
+
function resolveUpdatedBindingExpiry(params) {
|
|
3681
|
+
const expiries = params.bindings.map((binding) => {
|
|
3682
|
+
if (params.action === SESSION_ACTION_IDLE) {
|
|
3683
|
+
const idleTimeoutMs = typeof binding.idleTimeoutMs === "number" && Number.isFinite(binding.idleTimeoutMs) ? Math.max(0, Math.floor(binding.idleTimeoutMs)) : 0;
|
|
3684
|
+
if (idleTimeoutMs <= 0) return;
|
|
3685
|
+
return Math.max(binding.lastActivityAt, binding.boundAt) + idleTimeoutMs;
|
|
3686
|
+
}
|
|
3687
|
+
const maxAgeMs = typeof binding.maxAgeMs === "number" && Number.isFinite(binding.maxAgeMs) ? Math.max(0, Math.floor(binding.maxAgeMs)) : 0;
|
|
3688
|
+
if (maxAgeMs <= 0) return;
|
|
3689
|
+
return binding.boundAt + maxAgeMs;
|
|
3690
|
+
}).filter((expiresAt) => typeof expiresAt === "number");
|
|
3691
|
+
if (expiries.length === 0) return;
|
|
3692
|
+
return Math.min(...expiries);
|
|
3693
|
+
}
|
|
3694
|
+
const handleActivationCommand = async (params, allowTextCommands) => {
|
|
3695
|
+
if (!allowTextCommands) return null;
|
|
3696
|
+
const activationCommand = parseActivationCommand(params.command.commandBodyNormalized);
|
|
3697
|
+
if (!activationCommand.hasCommand) return null;
|
|
3698
|
+
if (!params.isGroup) return {
|
|
3699
|
+
shouldContinue: false,
|
|
3700
|
+
reply: { text: "⚙️ Group activation only applies to group chats." }
|
|
3701
|
+
};
|
|
3702
|
+
if (!params.command.isAuthorizedSender) {
|
|
3703
|
+
logVerbose(`Ignoring /activation from unauthorized sender in group: ${params.command.senderId || "<unknown>"}`);
|
|
3704
|
+
return { shouldContinue: false };
|
|
3705
|
+
}
|
|
3706
|
+
if (!activationCommand.mode) return {
|
|
3707
|
+
shouldContinue: false,
|
|
3708
|
+
reply: { text: "⚙️ Usage: /activation mention|always" }
|
|
3709
|
+
};
|
|
3710
|
+
if (params.sessionEntry && params.sessionStore && params.sessionKey) {
|
|
3711
|
+
params.sessionEntry.groupActivation = activationCommand.mode;
|
|
3712
|
+
params.sessionEntry.groupActivationNeedsSystemIntro = true;
|
|
3713
|
+
await persistSessionEntry(params);
|
|
3714
|
+
}
|
|
3715
|
+
return {
|
|
3716
|
+
shouldContinue: false,
|
|
3717
|
+
reply: { text: `⚙️ Group activation set to ${activationCommand.mode}.` }
|
|
3718
|
+
};
|
|
3719
|
+
};
|
|
3720
|
+
const handleSendPolicyCommand = async (params, allowTextCommands) => {
|
|
3721
|
+
if (!allowTextCommands) return null;
|
|
3722
|
+
const sendPolicyCommand = parseSendPolicyCommand(params.command.commandBodyNormalized);
|
|
3723
|
+
if (!sendPolicyCommand.hasCommand) return null;
|
|
3724
|
+
if (!params.command.isAuthorizedSender) {
|
|
3725
|
+
logVerbose(`Ignoring /send from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
3726
|
+
return { shouldContinue: false };
|
|
3727
|
+
}
|
|
3728
|
+
if (!sendPolicyCommand.mode) return {
|
|
3729
|
+
shouldContinue: false,
|
|
3730
|
+
reply: { text: "⚙️ Usage: /send on|off|inherit" }
|
|
3731
|
+
};
|
|
3732
|
+
if (params.sessionEntry && params.sessionStore && params.sessionKey) {
|
|
3733
|
+
if (sendPolicyCommand.mode === "inherit") delete params.sessionEntry.sendPolicy;
|
|
3734
|
+
else params.sessionEntry.sendPolicy = sendPolicyCommand.mode;
|
|
3735
|
+
await persistSessionEntry(params);
|
|
3736
|
+
}
|
|
3737
|
+
return {
|
|
3738
|
+
shouldContinue: false,
|
|
3739
|
+
reply: { text: `⚙️ Send policy set to ${sendPolicyCommand.mode === "inherit" ? "inherit" : sendPolicyCommand.mode === "allow" ? "on" : "off"}.` }
|
|
3740
|
+
};
|
|
3741
|
+
};
|
|
3742
|
+
const handleUsageCommand = async (params, allowTextCommands) => {
|
|
3743
|
+
if (!allowTextCommands) return null;
|
|
3744
|
+
const normalized = params.command.commandBodyNormalized;
|
|
3745
|
+
if (normalized !== "/usage" && !normalized.startsWith("/usage ")) return null;
|
|
3746
|
+
if (!params.command.isAuthorizedSender) {
|
|
3747
|
+
logVerbose(`Ignoring /usage from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
3748
|
+
return { shouldContinue: false };
|
|
3749
|
+
}
|
|
3750
|
+
const rawArgs = normalized === "/usage" ? "" : normalized.slice(6).trim();
|
|
3751
|
+
const requested = rawArgs ? normalizeUsageDisplay(rawArgs) : void 0;
|
|
3752
|
+
if (rawArgs.toLowerCase().startsWith("cost")) {
|
|
3753
|
+
const sessionSummary = await loadSessionCostSummary({
|
|
3754
|
+
sessionId: params.sessionEntry?.sessionId,
|
|
3755
|
+
sessionEntry: params.sessionEntry,
|
|
3756
|
+
sessionFile: params.sessionEntry?.sessionFile,
|
|
3757
|
+
config: params.cfg,
|
|
3758
|
+
agentId: params.agentId
|
|
3759
|
+
});
|
|
3760
|
+
const summary = await loadCostUsageSummary({
|
|
3761
|
+
days: 30,
|
|
3762
|
+
config: params.cfg
|
|
3763
|
+
});
|
|
3764
|
+
const sessionCost = formatUsd(sessionSummary?.totalCost);
|
|
3765
|
+
const sessionTokens = sessionSummary?.totalTokens ? formatTokenCount$1(sessionSummary.totalTokens) : void 0;
|
|
3766
|
+
const sessionSuffix = (sessionSummary?.missingCostEntries ?? 0) > 0 ? " (partial)" : "";
|
|
3767
|
+
const sessionLine = sessionCost || sessionTokens ? `Session ${sessionCost ?? "n/a"}${sessionSuffix}${sessionTokens ? ` · ${sessionTokens} tokens` : ""}` : "Session n/a";
|
|
3768
|
+
const todayKey = (/* @__PURE__ */ new Date()).toLocaleDateString("en-CA");
|
|
3769
|
+
const todayEntry = summary.daily.find((entry) => entry.date === todayKey);
|
|
3770
|
+
const todayCost = formatUsd(todayEntry?.totalCost);
|
|
3771
|
+
const todaySuffix = (todayEntry?.missingCostEntries ?? 0) > 0 ? " (partial)" : "";
|
|
3772
|
+
const todayLine = `Today ${todayCost ?? "n/a"}${todaySuffix}`;
|
|
3773
|
+
const last30Cost = formatUsd(summary.totals.totalCost);
|
|
3774
|
+
const last30Suffix = summary.totals.missingCostEntries > 0 ? " (partial)" : "";
|
|
3775
|
+
return {
|
|
3776
|
+
shouldContinue: false,
|
|
3777
|
+
reply: { text: `💸 Usage cost\n${sessionLine}\n${todayLine}\n${`Last 30d ${last30Cost ?? "n/a"}${last30Suffix}`}` }
|
|
3778
|
+
};
|
|
3779
|
+
}
|
|
3780
|
+
if (rawArgs && !requested) return {
|
|
3781
|
+
shouldContinue: false,
|
|
3782
|
+
reply: { text: "⚙️ Usage: /usage off|tokens|full|cost" }
|
|
3783
|
+
};
|
|
3784
|
+
const current = resolveResponseUsageMode(params.sessionEntry?.responseUsage ?? (params.sessionKey ? params.sessionStore?.[params.sessionKey]?.responseUsage : void 0));
|
|
3785
|
+
const next = requested ?? (current === "off" ? "tokens" : current === "tokens" ? "full" : "off");
|
|
3786
|
+
if (params.sessionEntry && params.sessionStore && params.sessionKey) {
|
|
3787
|
+
if (next === "off") delete params.sessionEntry.responseUsage;
|
|
3788
|
+
else params.sessionEntry.responseUsage = next;
|
|
3789
|
+
await persistSessionEntry(params);
|
|
3790
|
+
}
|
|
3791
|
+
return {
|
|
3792
|
+
shouldContinue: false,
|
|
3793
|
+
reply: { text: `⚙️ Usage footer: ${next}.` }
|
|
3794
|
+
};
|
|
3795
|
+
};
|
|
3796
|
+
const handleFastCommand = async (params, allowTextCommands) => {
|
|
3797
|
+
if (!allowTextCommands) return null;
|
|
3798
|
+
const normalized = params.command.commandBodyNormalized;
|
|
3799
|
+
if (normalized !== "/fast" && !normalized.startsWith("/fast ")) return null;
|
|
3800
|
+
if (!params.command.isAuthorizedSender) {
|
|
3801
|
+
logVerbose(`Ignoring /fast from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
3802
|
+
return { shouldContinue: false };
|
|
3803
|
+
}
|
|
3804
|
+
const rawMode = (normalized === "/fast" ? "" : normalized.slice(5).trim()).toLowerCase();
|
|
3805
|
+
if (!rawMode || rawMode === "status") {
|
|
3806
|
+
const state = resolveFastModeState({
|
|
3807
|
+
cfg: params.cfg,
|
|
3808
|
+
provider: params.provider,
|
|
3809
|
+
model: params.model,
|
|
3810
|
+
agentId: params.agentId,
|
|
3811
|
+
sessionEntry: params.sessionEntry
|
|
3812
|
+
});
|
|
3813
|
+
const suffix = state.source === "agent" ? " (agent)" : state.source === "config" ? " (config)" : state.source === "default" ? " (default)" : "";
|
|
3814
|
+
return {
|
|
3815
|
+
shouldContinue: false,
|
|
3816
|
+
reply: { text: `⚙️ Current fast mode: ${state.enabled ? "on" : "off"}${suffix}.` }
|
|
3817
|
+
};
|
|
3818
|
+
}
|
|
3819
|
+
const nextMode = normalizeFastMode(rawMode);
|
|
3820
|
+
if (nextMode === void 0) return {
|
|
3821
|
+
shouldContinue: false,
|
|
3822
|
+
reply: { text: "⚙️ Usage: /fast status|on|off" }
|
|
3823
|
+
};
|
|
3824
|
+
if (params.sessionEntry && params.sessionStore && params.sessionKey) {
|
|
3825
|
+
params.sessionEntry.fastMode = nextMode;
|
|
3826
|
+
await persistSessionEntry(params);
|
|
3827
|
+
}
|
|
3828
|
+
return {
|
|
3829
|
+
shouldContinue: false,
|
|
3830
|
+
reply: { text: `⚙️ Fast mode ${nextMode ? "enabled" : "disabled"}.` }
|
|
3831
|
+
};
|
|
3832
|
+
};
|
|
3833
|
+
const handleSessionCommand = async (params, allowTextCommands) => {
|
|
3834
|
+
if (!allowTextCommands) return null;
|
|
3835
|
+
const normalized = params.command.commandBodyNormalized;
|
|
3836
|
+
if (!/^\/session(?:\s|$)/.test(normalized)) return null;
|
|
3837
|
+
if (!params.command.isAuthorizedSender) {
|
|
3838
|
+
logVerbose(`Ignoring /session from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
3839
|
+
return { shouldContinue: false };
|
|
3840
|
+
}
|
|
3841
|
+
const tokens = normalized.slice(8).trim().split(/\s+/).filter(Boolean);
|
|
3842
|
+
const action = tokens[0]?.toLowerCase();
|
|
3843
|
+
if (action !== SESSION_ACTION_IDLE && action !== SESSION_ACTION_MAX_AGE) return {
|
|
3844
|
+
shouldContinue: false,
|
|
3845
|
+
reply: { text: resolveSessionCommandUsage() }
|
|
3846
|
+
};
|
|
3847
|
+
const onDiscord = isDiscordSurface(params);
|
|
3848
|
+
const onMatrix = isMatrixSurface(params);
|
|
3849
|
+
const onTelegram = isTelegramSurface(params);
|
|
3850
|
+
if (!onDiscord && !onMatrix && !onTelegram) return {
|
|
3851
|
+
shouldContinue: false,
|
|
3852
|
+
reply: { text: "⚠️ /session idle and /session max-age are currently available for Discord, Matrix, and Telegram bound sessions." }
|
|
3853
|
+
};
|
|
3854
|
+
const accountId = resolveChannelAccountId(params);
|
|
3855
|
+
const sessionBindingService = getSessionBindingService();
|
|
3856
|
+
const threadId = params.ctx.MessageThreadId != null ? String(params.ctx.MessageThreadId).trim() : "";
|
|
3857
|
+
const matrixConversationId = onMatrix ? resolveMatrixConversationId({
|
|
3858
|
+
ctx: {
|
|
3859
|
+
MessageThreadId: params.ctx.MessageThreadId,
|
|
3860
|
+
OriginatingTo: params.ctx.OriginatingTo,
|
|
3861
|
+
To: params.ctx.To
|
|
3862
|
+
},
|
|
3863
|
+
command: { to: params.command.to }
|
|
3864
|
+
}) : void 0;
|
|
3865
|
+
const matrixParentConversationId = onMatrix ? resolveMatrixParentConversationId({
|
|
3866
|
+
ctx: {
|
|
3867
|
+
MessageThreadId: params.ctx.MessageThreadId,
|
|
3868
|
+
OriginatingTo: params.ctx.OriginatingTo,
|
|
3869
|
+
To: params.ctx.To
|
|
3870
|
+
},
|
|
3871
|
+
command: { to: params.command.to }
|
|
3872
|
+
}) : void 0;
|
|
3873
|
+
const telegramConversationId = onTelegram ? resolveTelegramConversationId(params) : void 0;
|
|
3874
|
+
const channelRuntime = getChannelRuntime();
|
|
3875
|
+
const discordManager = onDiscord ? channelRuntime.discord.threadBindings.getManager(accountId) : null;
|
|
3876
|
+
if (onDiscord && !discordManager) return {
|
|
3877
|
+
shouldContinue: false,
|
|
3878
|
+
reply: { text: "⚠️ Discord thread bindings are unavailable for this account." }
|
|
3879
|
+
};
|
|
3880
|
+
const discordBinding = onDiscord && threadId ? discordManager?.getByThreadId(threadId) : void 0;
|
|
3881
|
+
const telegramBinding = onTelegram && telegramConversationId ? sessionBindingService.resolveByConversation({
|
|
3882
|
+
channel: "telegram",
|
|
3883
|
+
accountId,
|
|
3884
|
+
conversationId: telegramConversationId
|
|
3885
|
+
}) : null;
|
|
3886
|
+
const matrixBinding = onMatrix && matrixConversationId ? sessionBindingService.resolveByConversation({
|
|
3887
|
+
channel: "matrix",
|
|
3888
|
+
accountId,
|
|
3889
|
+
conversationId: matrixConversationId,
|
|
3890
|
+
...matrixParentConversationId && matrixParentConversationId !== matrixConversationId ? { parentConversationId: matrixParentConversationId } : {}
|
|
3891
|
+
}) : null;
|
|
3892
|
+
if (onDiscord && !discordBinding) {
|
|
3893
|
+
if (onDiscord && !threadId) return {
|
|
3894
|
+
shouldContinue: false,
|
|
3895
|
+
reply: { text: "⚠️ /session idle and /session max-age must be run inside a focused Discord thread." }
|
|
3896
|
+
};
|
|
3897
|
+
return {
|
|
3898
|
+
shouldContinue: false,
|
|
3899
|
+
reply: { text: "ℹ️ This thread is not currently focused." }
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
if (onMatrix && !matrixBinding) {
|
|
3903
|
+
if (!threadId) return {
|
|
3904
|
+
shouldContinue: false,
|
|
3905
|
+
reply: { text: "⚠️ /session idle and /session max-age must be run inside a focused Matrix thread." }
|
|
3906
|
+
};
|
|
3907
|
+
return {
|
|
3908
|
+
shouldContinue: false,
|
|
3909
|
+
reply: { text: "ℹ️ This thread is not currently focused." }
|
|
3910
|
+
};
|
|
3911
|
+
}
|
|
3912
|
+
if (onTelegram && !telegramBinding) {
|
|
3913
|
+
if (!telegramConversationId) return {
|
|
3914
|
+
shouldContinue: false,
|
|
3915
|
+
reply: { text: "⚠️ /session idle and /session max-age on Telegram require a topic context in groups, or a direct-message conversation." }
|
|
3916
|
+
};
|
|
3917
|
+
return {
|
|
3918
|
+
shouldContinue: false,
|
|
3919
|
+
reply: { text: "ℹ️ This conversation is not currently focused." }
|
|
3920
|
+
};
|
|
3921
|
+
}
|
|
3922
|
+
const idleTimeoutMs = onDiscord ? channelRuntime.discord.threadBindings.resolveIdleTimeoutMs({
|
|
3923
|
+
record: discordBinding,
|
|
3924
|
+
defaultIdleTimeoutMs: discordManager.getIdleTimeoutMs()
|
|
3925
|
+
}) : resolveSessionBindingDurationMs(onMatrix ? matrixBinding : telegramBinding, "idleTimeoutMs", 1440 * 60 * 1e3);
|
|
3926
|
+
const idleExpiresAt = onDiscord ? channelRuntime.discord.threadBindings.resolveInactivityExpiresAt({
|
|
3927
|
+
record: discordBinding,
|
|
3928
|
+
defaultIdleTimeoutMs: discordManager.getIdleTimeoutMs()
|
|
3929
|
+
}) : idleTimeoutMs > 0 ? resolveSessionBindingLastActivityAt(onMatrix ? matrixBinding : telegramBinding) + idleTimeoutMs : void 0;
|
|
3930
|
+
const maxAgeMs = onDiscord ? channelRuntime.discord.threadBindings.resolveMaxAgeMs({
|
|
3931
|
+
record: discordBinding,
|
|
3932
|
+
defaultMaxAgeMs: discordManager.getMaxAgeMs()
|
|
3933
|
+
}) : resolveSessionBindingDurationMs(onMatrix ? matrixBinding : telegramBinding, "maxAgeMs", 0);
|
|
3934
|
+
const maxAgeExpiresAt = onDiscord ? channelRuntime.discord.threadBindings.resolveMaxAgeExpiresAt({
|
|
3935
|
+
record: discordBinding,
|
|
3936
|
+
defaultMaxAgeMs: discordManager.getMaxAgeMs()
|
|
3937
|
+
}) : maxAgeMs > 0 ? (onMatrix ? matrixBinding : telegramBinding).boundAt + maxAgeMs : void 0;
|
|
3938
|
+
const durationArgRaw = tokens.slice(1).join("");
|
|
3939
|
+
if (!durationArgRaw) {
|
|
3940
|
+
if (action === SESSION_ACTION_IDLE) {
|
|
3941
|
+
if (typeof idleExpiresAt === "number" && Number.isFinite(idleExpiresAt) && idleExpiresAt > Date.now()) return {
|
|
3942
|
+
shouldContinue: false,
|
|
3943
|
+
reply: { text: `ℹ️ Idle timeout active (${formatThreadBindingDurationLabel(idleTimeoutMs)}, next auto-unfocus at ${formatSessionExpiry(idleExpiresAt)}).` }
|
|
3944
|
+
};
|
|
3945
|
+
return {
|
|
3946
|
+
shouldContinue: false,
|
|
3947
|
+
reply: { text: "ℹ️ Idle timeout is currently disabled for this focused session." }
|
|
3948
|
+
};
|
|
3949
|
+
}
|
|
3950
|
+
if (typeof maxAgeExpiresAt === "number" && Number.isFinite(maxAgeExpiresAt) && maxAgeExpiresAt > Date.now()) return {
|
|
3951
|
+
shouldContinue: false,
|
|
3952
|
+
reply: { text: `ℹ️ Max age active (${formatThreadBindingDurationLabel(maxAgeMs)}, hard auto-unfocus at ${formatSessionExpiry(maxAgeExpiresAt)}).` }
|
|
3953
|
+
};
|
|
3954
|
+
return {
|
|
3955
|
+
shouldContinue: false,
|
|
3956
|
+
reply: { text: "ℹ️ Max age is currently disabled for this focused session." }
|
|
3957
|
+
};
|
|
3958
|
+
}
|
|
3959
|
+
const senderId = params.command.senderId?.trim() || "";
|
|
3960
|
+
const boundBy = onDiscord ? discordBinding.boundBy : resolveSessionBindingBoundBy(onMatrix ? matrixBinding : telegramBinding);
|
|
3961
|
+
if (boundBy && boundBy !== "system" && senderId && senderId !== boundBy) return {
|
|
3962
|
+
shouldContinue: false,
|
|
3963
|
+
reply: { text: onDiscord ? `⚠️ Only ${boundBy} can update session lifecycle settings for this thread.` : onMatrix ? `⚠️ Only ${boundBy} can update session lifecycle settings for this thread.` : `⚠️ Only ${boundBy} can update session lifecycle settings for this conversation.` }
|
|
3964
|
+
};
|
|
3965
|
+
let durationMs;
|
|
3966
|
+
try {
|
|
3967
|
+
durationMs = parseSessionDurationMs(durationArgRaw);
|
|
3968
|
+
} catch {
|
|
3969
|
+
return {
|
|
3970
|
+
shouldContinue: false,
|
|
3971
|
+
reply: { text: resolveSessionCommandUsage() }
|
|
3972
|
+
};
|
|
3973
|
+
}
|
|
3974
|
+
const updatedBindings = (() => {
|
|
3975
|
+
if (onDiscord) return action === SESSION_ACTION_IDLE ? channelRuntime.discord.threadBindings.setIdleTimeoutBySessionKey({
|
|
3976
|
+
targetSessionKey: discordBinding.targetSessionKey,
|
|
3977
|
+
accountId,
|
|
3978
|
+
idleTimeoutMs: durationMs
|
|
3979
|
+
}) : channelRuntime.discord.threadBindings.setMaxAgeBySessionKey({
|
|
3980
|
+
targetSessionKey: discordBinding.targetSessionKey,
|
|
3981
|
+
accountId,
|
|
3982
|
+
maxAgeMs: durationMs
|
|
3983
|
+
});
|
|
3984
|
+
if (onMatrix) return action === SESSION_ACTION_IDLE ? channelRuntime.matrix.threadBindings.setIdleTimeoutBySessionKey({
|
|
3985
|
+
targetSessionKey: matrixBinding.targetSessionKey,
|
|
3986
|
+
accountId,
|
|
3987
|
+
idleTimeoutMs: durationMs
|
|
3988
|
+
}) : channelRuntime.matrix.threadBindings.setMaxAgeBySessionKey({
|
|
3989
|
+
targetSessionKey: matrixBinding.targetSessionKey,
|
|
3990
|
+
accountId,
|
|
3991
|
+
maxAgeMs: durationMs
|
|
3992
|
+
});
|
|
3993
|
+
return action === SESSION_ACTION_IDLE ? channelRuntime.telegram.threadBindings.setIdleTimeoutBySessionKey({
|
|
3994
|
+
targetSessionKey: telegramBinding.targetSessionKey,
|
|
3995
|
+
accountId,
|
|
3996
|
+
idleTimeoutMs: durationMs
|
|
3997
|
+
}) : channelRuntime.telegram.threadBindings.setMaxAgeBySessionKey({
|
|
3998
|
+
targetSessionKey: telegramBinding.targetSessionKey,
|
|
3999
|
+
accountId,
|
|
4000
|
+
maxAgeMs: durationMs
|
|
4001
|
+
});
|
|
4002
|
+
})();
|
|
4003
|
+
if (updatedBindings.length === 0) return {
|
|
4004
|
+
shouldContinue: false,
|
|
4005
|
+
reply: { text: action === SESSION_ACTION_IDLE ? "⚠️ Failed to update idle timeout for the current binding." : "⚠️ Failed to update max age for the current binding." }
|
|
4006
|
+
};
|
|
4007
|
+
if (durationMs <= 0) return {
|
|
4008
|
+
shouldContinue: false,
|
|
4009
|
+
reply: { text: action === SESSION_ACTION_IDLE ? `✅ Idle timeout disabled for ${updatedBindings.length} binding${updatedBindings.length === 1 ? "" : "s"}.` : `✅ Max age disabled for ${updatedBindings.length} binding${updatedBindings.length === 1 ? "" : "s"}.` }
|
|
4010
|
+
};
|
|
4011
|
+
const nextExpiry = resolveUpdatedBindingExpiry({
|
|
4012
|
+
action,
|
|
4013
|
+
bindings: updatedBindings.map((binding) => toUpdatedLifecycleBinding(binding))
|
|
4014
|
+
});
|
|
4015
|
+
const expiryLabel = typeof nextExpiry === "number" && Number.isFinite(nextExpiry) ? formatSessionExpiry(nextExpiry) : "n/a";
|
|
4016
|
+
return {
|
|
4017
|
+
shouldContinue: false,
|
|
4018
|
+
reply: { text: action === SESSION_ACTION_IDLE ? `✅ Idle timeout set to ${formatThreadBindingDurationLabel(durationMs)} for ${updatedBindings.length} binding${updatedBindings.length === 1 ? "" : "s"} (next auto-unfocus at ${expiryLabel}).` : `✅ Max age set to ${formatThreadBindingDurationLabel(durationMs)} for ${updatedBindings.length} binding${updatedBindings.length === 1 ? "" : "s"} (hard auto-unfocus at ${expiryLabel}).` }
|
|
4019
|
+
};
|
|
4020
|
+
};
|
|
4021
|
+
const handleRestartCommand = async (params, allowTextCommands) => {
|
|
4022
|
+
if (!allowTextCommands) return null;
|
|
4023
|
+
if (params.command.commandBodyNormalized !== "/restart") return null;
|
|
4024
|
+
if (!params.command.isAuthorizedSender) {
|
|
4025
|
+
logVerbose(`Ignoring /restart from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
4026
|
+
return { shouldContinue: false };
|
|
4027
|
+
}
|
|
4028
|
+
if (!isRestartEnabled(params.cfg)) return {
|
|
4029
|
+
shouldContinue: false,
|
|
4030
|
+
reply: { text: "⚠️ /restart is disabled (commands.restart=false)." }
|
|
4031
|
+
};
|
|
4032
|
+
if (process.listenerCount("SIGUSR1") > 0) {
|
|
4033
|
+
scheduleGatewaySigusr1Restart({ reason: "/restart" });
|
|
4034
|
+
return {
|
|
4035
|
+
shouldContinue: false,
|
|
4036
|
+
reply: { text: "⚙️ Restarting OpenClaw in-process (SIGUSR1); back in a few seconds." }
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
const restartMethod = triggerOpenClawRestart();
|
|
4040
|
+
if (!restartMethod.ok) {
|
|
4041
|
+
const detail = restartMethod.detail ? ` Details: ${restartMethod.detail}` : "";
|
|
4042
|
+
return {
|
|
4043
|
+
shouldContinue: false,
|
|
4044
|
+
reply: { text: `⚠️ Restart failed (${restartMethod.method}).${detail}` }
|
|
4045
|
+
};
|
|
4046
|
+
}
|
|
4047
|
+
return {
|
|
4048
|
+
shouldContinue: false,
|
|
4049
|
+
reply: { text: `⚙️ Restarting OpenClaw via ${restartMethod.method}; give me a few seconds to come back online.` }
|
|
4050
|
+
};
|
|
4051
|
+
};
|
|
4052
|
+
//#endregion
|
|
4053
|
+
//#region src/auto-reply/reply/commands-subagents/action-agents.ts
|
|
4054
|
+
function formatConversationBindingText(params) {
|
|
4055
|
+
if (params.channel === "discord") return `thread:${params.conversationId}`;
|
|
4056
|
+
if (params.channel === "telegram") return `conversation:${params.conversationId}`;
|
|
4057
|
+
return `binding:${params.conversationId}`;
|
|
4058
|
+
}
|
|
4059
|
+
function handleSubagentsAgentsAction(ctx) {
|
|
4060
|
+
const { params, requesterKey, runs } = ctx;
|
|
4061
|
+
const channel = resolveCommandSurfaceChannel(params);
|
|
4062
|
+
const accountId = resolveChannelAccountId(params);
|
|
4063
|
+
const bindingService = getSessionBindingService();
|
|
4064
|
+
const bindingsBySession = /* @__PURE__ */ new Map();
|
|
4065
|
+
const resolveSessionBindings = (sessionKey) => {
|
|
4066
|
+
const cached = bindingsBySession.get(sessionKey);
|
|
4067
|
+
if (cached) return cached;
|
|
4068
|
+
const resolved = bindingService.listBySession(sessionKey).filter((entry) => entry.status === "active" && entry.conversation.channel === channel && entry.conversation.accountId === accountId);
|
|
4069
|
+
bindingsBySession.set(sessionKey, resolved);
|
|
4070
|
+
return resolved;
|
|
4071
|
+
};
|
|
4072
|
+
const visibleRuns = sortSubagentRuns(runs).filter((entry) => {
|
|
4073
|
+
if (!entry.endedAt) return true;
|
|
4074
|
+
return resolveSessionBindings(entry.childSessionKey).length > 0;
|
|
4075
|
+
});
|
|
4076
|
+
const lines = ["agents:", "-----"];
|
|
4077
|
+
if (visibleRuns.length === 0) lines.push("(none)");
|
|
4078
|
+
else {
|
|
4079
|
+
let index = 1;
|
|
4080
|
+
for (const entry of visibleRuns) {
|
|
4081
|
+
const binding = resolveSessionBindings(entry.childSessionKey)[0];
|
|
4082
|
+
const bindingText = binding ? formatConversationBindingText({
|
|
4083
|
+
channel,
|
|
4084
|
+
conversationId: binding.conversation.conversationId
|
|
4085
|
+
}) : channel === "discord" || channel === "telegram" ? "unbound" : "bindings available on discord/telegram";
|
|
4086
|
+
lines.push(`${index}. ${formatRunLabel(entry)} (${bindingText})`);
|
|
4087
|
+
index += 1;
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
const requesterBindings = resolveSessionBindings(requesterKey).filter((entry) => entry.targetKind === "session");
|
|
4091
|
+
if (requesterBindings.length > 0) {
|
|
4092
|
+
lines.push("", "acp/session bindings:", "-----");
|
|
4093
|
+
for (const binding of requesterBindings) {
|
|
4094
|
+
const label = typeof binding.metadata?.label === "string" && binding.metadata.label.trim() ? binding.metadata.label.trim() : binding.targetSessionKey;
|
|
4095
|
+
lines.push(`- ${label} (${formatConversationBindingText({
|
|
4096
|
+
channel,
|
|
4097
|
+
conversationId: binding.conversation.conversationId
|
|
4098
|
+
})}, session:${binding.targetSessionKey})`);
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
return stopWithText(lines.join("\n"));
|
|
4102
|
+
}
|
|
4103
|
+
//#endregion
|
|
4104
|
+
//#region src/auto-reply/reply/commands-subagents/action-focus.ts
|
|
4105
|
+
function resolveFocusBindingContext(params) {
|
|
4106
|
+
if (isDiscordSurface(params)) {
|
|
4107
|
+
const currentThreadId = params.ctx.MessageThreadId != null ? String(params.ctx.MessageThreadId).trim() : "";
|
|
4108
|
+
const parentChannelId = currentThreadId ? void 0 : resolveDiscordChannelIdForFocus(params);
|
|
4109
|
+
const conversationId = currentThreadId || parentChannelId;
|
|
4110
|
+
if (!conversationId) return null;
|
|
4111
|
+
return {
|
|
4112
|
+
channel: "discord",
|
|
4113
|
+
accountId: resolveChannelAccountId(params),
|
|
4114
|
+
conversationId,
|
|
4115
|
+
placement: currentThreadId ? "current" : "child",
|
|
4116
|
+
labelNoun: "thread"
|
|
4117
|
+
};
|
|
4118
|
+
}
|
|
4119
|
+
if (isTelegramSurface(params)) {
|
|
4120
|
+
const conversationId = resolveTelegramConversationId(params);
|
|
4121
|
+
if (!conversationId) return null;
|
|
4122
|
+
return {
|
|
4123
|
+
channel: "telegram",
|
|
4124
|
+
accountId: resolveChannelAccountId(params),
|
|
4125
|
+
conversationId,
|
|
4126
|
+
placement: "current",
|
|
4127
|
+
labelNoun: "conversation"
|
|
4128
|
+
};
|
|
4129
|
+
}
|
|
4130
|
+
if (isMatrixSurface(params)) {
|
|
4131
|
+
const conversationId = resolveMatrixConversationId({
|
|
4132
|
+
ctx: {
|
|
4133
|
+
MessageThreadId: params.ctx.MessageThreadId,
|
|
4134
|
+
OriginatingTo: params.ctx.OriginatingTo,
|
|
4135
|
+
To: params.ctx.To
|
|
4136
|
+
},
|
|
4137
|
+
command: { to: params.command.to }
|
|
4138
|
+
});
|
|
4139
|
+
if (!conversationId) return null;
|
|
4140
|
+
const parentConversationId = resolveMatrixParentConversationId({
|
|
4141
|
+
ctx: {
|
|
4142
|
+
MessageThreadId: params.ctx.MessageThreadId,
|
|
4143
|
+
OriginatingTo: params.ctx.OriginatingTo,
|
|
4144
|
+
To: params.ctx.To
|
|
4145
|
+
},
|
|
4146
|
+
command: { to: params.command.to }
|
|
4147
|
+
});
|
|
4148
|
+
const currentThreadId = params.ctx.MessageThreadId != null ? String(params.ctx.MessageThreadId).trim() : "";
|
|
4149
|
+
return {
|
|
4150
|
+
channel: "matrix",
|
|
4151
|
+
accountId: resolveChannelAccountId(params),
|
|
4152
|
+
conversationId,
|
|
4153
|
+
...parentConversationId ? { parentConversationId } : {},
|
|
4154
|
+
placement: currentThreadId ? "current" : "child",
|
|
4155
|
+
labelNoun: "thread"
|
|
4156
|
+
};
|
|
4157
|
+
}
|
|
4158
|
+
return null;
|
|
4159
|
+
}
|
|
4160
|
+
async function handleSubagentsFocusAction(ctx) {
|
|
4161
|
+
const { params, runs, restTokens } = ctx;
|
|
4162
|
+
const channel = resolveCommandSurfaceChannel(params);
|
|
4163
|
+
if (channel !== "discord" && channel !== "matrix" && channel !== "telegram") return stopWithText("⚠️ /focus is only available on Discord, Matrix, and Telegram.");
|
|
4164
|
+
const token = restTokens.join(" ").trim();
|
|
4165
|
+
if (!token) return stopWithText("Usage: /focus <subagent-label|session-key|session-id|session-label>");
|
|
4166
|
+
const accountId = resolveChannelAccountId(params);
|
|
4167
|
+
const bindingService = getSessionBindingService();
|
|
4168
|
+
const capabilities = bindingService.getCapabilities({
|
|
4169
|
+
channel,
|
|
4170
|
+
accountId
|
|
4171
|
+
});
|
|
4172
|
+
if (!capabilities.adapterAvailable || !capabilities.bindSupported) return stopWithText(`⚠️ ${channel === "discord" ? "Discord thread" : channel === "matrix" ? "Matrix thread" : "Telegram conversation"} bindings are unavailable for this account.`);
|
|
4173
|
+
const focusTarget = await resolveFocusTargetSession({
|
|
4174
|
+
runs,
|
|
4175
|
+
token
|
|
4176
|
+
});
|
|
4177
|
+
if (!focusTarget) return stopWithText(`⚠️ Unable to resolve focus target: ${token}`);
|
|
4178
|
+
const bindingContext = resolveFocusBindingContext(params);
|
|
4179
|
+
if (!bindingContext) {
|
|
4180
|
+
if (channel === "telegram") return stopWithText("⚠️ /focus on Telegram requires a topic context in groups, or a direct-message conversation.");
|
|
4181
|
+
if (channel === "matrix") return stopWithText("⚠️ Could not resolve a Matrix room for /focus.");
|
|
4182
|
+
return stopWithText("⚠️ Could not resolve a Discord channel for /focus.");
|
|
4183
|
+
}
|
|
4184
|
+
if (channel === "matrix") {
|
|
4185
|
+
const spawnPolicy = resolveThreadBindingSpawnPolicy({
|
|
4186
|
+
cfg: params.cfg,
|
|
4187
|
+
channel,
|
|
4188
|
+
accountId: bindingContext.accountId,
|
|
4189
|
+
kind: "subagent"
|
|
4190
|
+
});
|
|
4191
|
+
if (!spawnPolicy.enabled) return stopWithText(`⚠️ ${formatThreadBindingDisabledError({
|
|
4192
|
+
channel: spawnPolicy.channel,
|
|
4193
|
+
accountId: spawnPolicy.accountId,
|
|
4194
|
+
kind: "subagent"
|
|
4195
|
+
})}`);
|
|
4196
|
+
if (bindingContext.placement === "child" && !spawnPolicy.spawnEnabled) return stopWithText(`⚠️ ${formatThreadBindingSpawnDisabledError({
|
|
4197
|
+
channel: spawnPolicy.channel,
|
|
4198
|
+
accountId: spawnPolicy.accountId,
|
|
4199
|
+
kind: "subagent"
|
|
4200
|
+
})}`);
|
|
4201
|
+
}
|
|
4202
|
+
const senderId = params.command.senderId?.trim() || "";
|
|
4203
|
+
const existingBinding = bindingService.resolveByConversation({
|
|
4204
|
+
channel: bindingContext.channel,
|
|
4205
|
+
accountId: bindingContext.accountId,
|
|
4206
|
+
conversationId: bindingContext.conversationId,
|
|
4207
|
+
...bindingContext.parentConversationId && bindingContext.parentConversationId !== bindingContext.conversationId ? { parentConversationId: bindingContext.parentConversationId } : {}
|
|
4208
|
+
});
|
|
4209
|
+
const boundBy = typeof existingBinding?.metadata?.boundBy === "string" ? existingBinding.metadata.boundBy.trim() : "";
|
|
4210
|
+
if (existingBinding && boundBy && boundBy !== "system" && senderId && senderId !== boundBy) return stopWithText(`⚠️ Only ${boundBy} can refocus this ${bindingContext.labelNoun}.`);
|
|
4211
|
+
const label = focusTarget.label || token;
|
|
4212
|
+
const acpMeta = focusTarget.targetKind === "acp" ? readAcpSessionEntry({
|
|
4213
|
+
cfg: params.cfg,
|
|
4214
|
+
sessionKey: focusTarget.targetSessionKey
|
|
4215
|
+
})?.acp : void 0;
|
|
4216
|
+
if (!capabilities.placements.includes(bindingContext.placement)) return stopWithText(`⚠️ ${channel} bindings are unavailable for this account.`);
|
|
4217
|
+
let binding;
|
|
4218
|
+
try {
|
|
4219
|
+
binding = await bindingService.bind({
|
|
4220
|
+
targetSessionKey: focusTarget.targetSessionKey,
|
|
4221
|
+
targetKind: focusTarget.targetKind === "acp" ? "session" : "subagent",
|
|
4222
|
+
conversation: {
|
|
4223
|
+
channel: bindingContext.channel,
|
|
4224
|
+
accountId: bindingContext.accountId,
|
|
4225
|
+
conversationId: bindingContext.conversationId,
|
|
4226
|
+
...bindingContext.parentConversationId && bindingContext.parentConversationId !== bindingContext.conversationId ? { parentConversationId: bindingContext.parentConversationId } : {}
|
|
4227
|
+
},
|
|
4228
|
+
placement: bindingContext.placement,
|
|
4229
|
+
metadata: {
|
|
4230
|
+
threadName: resolveThreadBindingThreadName({
|
|
4231
|
+
agentId: focusTarget.agentId,
|
|
4232
|
+
label
|
|
4233
|
+
}),
|
|
4234
|
+
agentId: focusTarget.agentId,
|
|
4235
|
+
label,
|
|
4236
|
+
boundBy: senderId || "unknown",
|
|
4237
|
+
introText: resolveThreadBindingIntroText({
|
|
4238
|
+
agentId: focusTarget.agentId,
|
|
4239
|
+
label,
|
|
4240
|
+
idleTimeoutMs: resolveThreadBindingIdleTimeoutMsForChannel({
|
|
4241
|
+
cfg: params.cfg,
|
|
4242
|
+
channel: bindingContext.channel,
|
|
4243
|
+
accountId
|
|
4244
|
+
}),
|
|
4245
|
+
maxAgeMs: resolveThreadBindingMaxAgeMsForChannel({
|
|
4246
|
+
cfg: params.cfg,
|
|
4247
|
+
channel: bindingContext.channel,
|
|
4248
|
+
accountId
|
|
4249
|
+
}),
|
|
4250
|
+
sessionCwd: focusTarget.targetKind === "acp" ? resolveAcpSessionCwd(acpMeta) : void 0,
|
|
4251
|
+
sessionDetails: focusTarget.targetKind === "acp" ? resolveAcpThreadSessionDetailLines({
|
|
4252
|
+
sessionKey: focusTarget.targetSessionKey,
|
|
4253
|
+
meta: acpMeta
|
|
4254
|
+
}) : []
|
|
4255
|
+
})
|
|
4256
|
+
}
|
|
4257
|
+
});
|
|
4258
|
+
} catch {
|
|
4259
|
+
return stopWithText(`⚠️ Failed to bind this ${bindingContext.labelNoun} to the target session.`);
|
|
4260
|
+
}
|
|
4261
|
+
return stopWithText(`✅ ${bindingContext.placement === "child" ? `created thread ${binding.conversation.conversationId} and bound it to ${binding.targetSessionKey}` : `bound this ${bindingContext.labelNoun} to ${binding.targetSessionKey}`} (${focusTarget.targetKind}).`);
|
|
4262
|
+
}
|
|
4263
|
+
//#endregion
|
|
4264
|
+
//#region src/auto-reply/reply/commands-subagents/action-help.ts
|
|
4265
|
+
function handleSubagentsHelpAction() {
|
|
4266
|
+
return stopWithText(buildSubagentsHelp());
|
|
4267
|
+
}
|
|
4268
|
+
//#endregion
|
|
4269
|
+
//#region src/auto-reply/reply/commands-subagents/action-info.ts
|
|
4270
|
+
function handleSubagentsInfoAction(ctx) {
|
|
4271
|
+
const { params, runs, restTokens } = ctx;
|
|
4272
|
+
const target = restTokens[0];
|
|
4273
|
+
if (!target) return stopWithText("ℹ️ Usage: /subagents info <id|#>");
|
|
4274
|
+
const targetResolution = resolveSubagentEntryForToken(runs, target);
|
|
4275
|
+
if ("reply" in targetResolution) return targetResolution.reply;
|
|
4276
|
+
const run = targetResolution.entry;
|
|
4277
|
+
const { entry: sessionEntry } = loadSubagentSessionEntry(params, run.childSessionKey, {
|
|
4278
|
+
loadSessionStore,
|
|
4279
|
+
resolveStorePath
|
|
4280
|
+
});
|
|
4281
|
+
const runtime = run.startedAt && Number.isFinite(run.startedAt) ? formatDurationCompact((run.endedAt ?? Date.now()) - run.startedAt) ?? "n/a" : "n/a";
|
|
4282
|
+
const outcome = run.outcome ? `${run.outcome.status}${run.outcome.error ? ` (${run.outcome.error})` : ""}` : "n/a";
|
|
4283
|
+
return stopWithText([
|
|
4284
|
+
"ℹ️ Subagent info",
|
|
4285
|
+
`Status: ${resolveDisplayStatus(run, { pendingDescendants: countPendingDescendantRuns(run.childSessionKey) })}`,
|
|
4286
|
+
`Label: ${formatRunLabel(run)}`,
|
|
4287
|
+
`Task: ${run.task}`,
|
|
4288
|
+
`Run: ${run.runId}`,
|
|
4289
|
+
`Session: ${run.childSessionKey}`,
|
|
4290
|
+
`SessionId: ${sessionEntry?.sessionId ?? "n/a"}`,
|
|
4291
|
+
`Transcript: ${sessionEntry?.sessionFile ?? "n/a"}`,
|
|
4292
|
+
`Runtime: ${runtime}`,
|
|
4293
|
+
`Created: ${formatTimestampWithAge(run.createdAt)}`,
|
|
4294
|
+
`Started: ${formatTimestampWithAge(run.startedAt)}`,
|
|
4295
|
+
`Ended: ${formatTimestampWithAge(run.endedAt)}`,
|
|
4296
|
+
`Cleanup: ${run.cleanup}`,
|
|
4297
|
+
run.archiveAtMs ? `Archive: ${formatTimestampWithAge(run.archiveAtMs)}` : void 0,
|
|
4298
|
+
run.cleanupHandled ? "Cleanup handled: yes" : void 0,
|
|
4299
|
+
`Outcome: ${outcome}`
|
|
4300
|
+
].filter(Boolean).join("\n"));
|
|
4301
|
+
}
|
|
4302
|
+
//#endregion
|
|
4303
|
+
//#region src/auto-reply/reply/commands-subagents/action-kill.ts
|
|
4304
|
+
async function handleSubagentsKillAction(ctx) {
|
|
4305
|
+
const { params, handledPrefix, requesterKey, runs, restTokens } = ctx;
|
|
4306
|
+
const target = restTokens[0];
|
|
4307
|
+
if (!target) return stopWithText(handledPrefix === "/subagents" ? "Usage: /subagents kill <id|#|all>" : "Usage: /kill <id|#|all>");
|
|
4308
|
+
if (target === "all" || target === "*") {
|
|
4309
|
+
const controller = resolveCommandSubagentController(params, requesterKey);
|
|
4310
|
+
const result = await killAllControlledSubagentRuns({
|
|
4311
|
+
cfg: params.cfg,
|
|
4312
|
+
controller,
|
|
4313
|
+
runs
|
|
4314
|
+
});
|
|
4315
|
+
if (result.status === "forbidden") return stopWithText(`⚠️ ${result.error}`);
|
|
4316
|
+
if (result.killed > 0) return { shouldContinue: false };
|
|
4317
|
+
return { shouldContinue: false };
|
|
4318
|
+
}
|
|
4319
|
+
const targetResolution = resolveSubagentEntryForToken(runs, target);
|
|
4320
|
+
if ("reply" in targetResolution) return targetResolution.reply;
|
|
4321
|
+
if (targetResolution.entry.endedAt) return stopWithText(`${formatRunLabel(targetResolution.entry)} is already finished.`);
|
|
4322
|
+
const controller = resolveCommandSubagentController(params, requesterKey);
|
|
4323
|
+
const result = await killControlledSubagentRun({
|
|
4324
|
+
cfg: params.cfg,
|
|
4325
|
+
controller,
|
|
4326
|
+
entry: targetResolution.entry
|
|
4327
|
+
});
|
|
4328
|
+
if (result.status === "forbidden") return stopWithText(`⚠️ ${result.error}`);
|
|
4329
|
+
if (result.status === "done") return stopWithText(result.text);
|
|
4330
|
+
return { shouldContinue: false };
|
|
4331
|
+
}
|
|
4332
|
+
//#endregion
|
|
4333
|
+
//#region src/auto-reply/reply/commands-subagents/action-list.ts
|
|
4334
|
+
function handleSubagentsListAction(ctx) {
|
|
4335
|
+
const { params, runs } = ctx;
|
|
4336
|
+
const list = buildSubagentList({
|
|
4337
|
+
cfg: params.cfg,
|
|
4338
|
+
runs,
|
|
4339
|
+
recentMinutes: 30,
|
|
4340
|
+
taskMaxChars: 110
|
|
4341
|
+
});
|
|
4342
|
+
const lines = ["active subagents:", "-----"];
|
|
4343
|
+
if (list.active.length === 0) lines.push("(none)");
|
|
4344
|
+
else lines.push(list.active.map((entry) => entry.line).join("\n"));
|
|
4345
|
+
lines.push("", `recent subagents (last 30m):`, "-----");
|
|
4346
|
+
if (list.recent.length === 0) lines.push("(none)");
|
|
4347
|
+
else lines.push(list.recent.map((entry) => entry.line).join("\n"));
|
|
4348
|
+
return stopWithText(lines.join("\n"));
|
|
4349
|
+
}
|
|
4350
|
+
//#endregion
|
|
4351
|
+
//#region src/auto-reply/reply/commands-subagents/action-log.ts
|
|
4352
|
+
async function handleSubagentsLogAction(ctx) {
|
|
4353
|
+
const { runs, restTokens } = ctx;
|
|
4354
|
+
const target = restTokens[0];
|
|
4355
|
+
if (!target) return stopWithText("📜 Usage: /subagents log <id|#> [limit]");
|
|
4356
|
+
const includeTools = restTokens.some((token) => token.toLowerCase() === "tools");
|
|
4357
|
+
const limitToken = restTokens.find((token) => /^\d+$/.test(token));
|
|
4358
|
+
const limit = limitToken ? Math.min(200, Math.max(1, Number.parseInt(limitToken, 10))) : 20;
|
|
4359
|
+
const targetResolution = resolveSubagentEntryForToken(runs, target);
|
|
4360
|
+
if ("reply" in targetResolution) return targetResolution.reply;
|
|
4361
|
+
const history = await callGateway({
|
|
4362
|
+
method: "chat.history",
|
|
4363
|
+
params: {
|
|
4364
|
+
sessionKey: targetResolution.entry.childSessionKey,
|
|
4365
|
+
limit
|
|
4366
|
+
}
|
|
4367
|
+
});
|
|
4368
|
+
const rawMessages = Array.isArray(history?.messages) ? history.messages : [];
|
|
4369
|
+
const lines = formatLogLines(includeTools ? rawMessages : stripToolMessages(rawMessages));
|
|
4370
|
+
const header = `📜 Subagent log: ${formatRunLabel(targetResolution.entry)}`;
|
|
4371
|
+
if (lines.length === 0) return stopWithText(`${header}\n(no messages)`);
|
|
4372
|
+
return stopWithText([header, ...lines].join("\n"));
|
|
4373
|
+
}
|
|
4374
|
+
//#endregion
|
|
4375
|
+
//#region src/auto-reply/reply/commands-subagents/action-send.ts
|
|
4376
|
+
async function handleSubagentsSendAction(ctx, steerRequested) {
|
|
4377
|
+
const { params, handledPrefix, runs, restTokens } = ctx;
|
|
4378
|
+
const target = restTokens[0];
|
|
4379
|
+
const message = restTokens.slice(1).join(" ").trim();
|
|
4380
|
+
if (!target || !message) return stopWithText(steerRequested ? handledPrefix === "/subagents" ? "Usage: /subagents steer <id|#> <message>" : `Usage: ${handledPrefix} <id|#> <message>` : "Usage: /subagents send <id|#> <message>");
|
|
4381
|
+
const targetResolution = resolveSubagentEntryForToken(runs, target);
|
|
4382
|
+
if ("reply" in targetResolution) return targetResolution.reply;
|
|
4383
|
+
if (steerRequested && targetResolution.entry.endedAt) return stopWithText(`${formatRunLabel(targetResolution.entry)} is already finished.`);
|
|
4384
|
+
const controller = resolveCommandSubagentController(params, ctx.requesterKey);
|
|
4385
|
+
if (steerRequested) {
|
|
4386
|
+
const result = await steerControlledSubagentRun({
|
|
4387
|
+
cfg: params.cfg,
|
|
4388
|
+
controller,
|
|
4389
|
+
entry: targetResolution.entry,
|
|
4390
|
+
message
|
|
4391
|
+
});
|
|
4392
|
+
if (result.status === "accepted") return stopWithText(`steered ${formatRunLabel(targetResolution.entry)} (run ${result.runId.slice(0, 8)}).`);
|
|
4393
|
+
if (result.status === "done" && result.text) return stopWithText(result.text);
|
|
4394
|
+
if (result.status === "error") return stopWithText(`send failed: ${result.error ?? "error"}`);
|
|
4395
|
+
return stopWithText(`⚠️ ${result.error ?? "send failed"}`);
|
|
4396
|
+
}
|
|
4397
|
+
const result = await sendControlledSubagentMessage({
|
|
4398
|
+
cfg: params.cfg,
|
|
4399
|
+
controller,
|
|
4400
|
+
entry: targetResolution.entry,
|
|
4401
|
+
message
|
|
4402
|
+
});
|
|
4403
|
+
if (result.status === "timeout") return stopWithText(`⏳ Subagent still running (run ${result.runId.slice(0, 8)}).`);
|
|
4404
|
+
if (result.status === "error") return stopWithText(`⚠️ Subagent error: ${result.error} (run ${result.runId.slice(0, 8)}).`);
|
|
4405
|
+
if (result.status === "forbidden") return stopWithText(`⚠️ ${result.error ?? "send failed"}`);
|
|
4406
|
+
return stopWithText(result.replyText ?? `✅ Sent to ${formatRunLabel(targetResolution.entry)} (run ${result.runId.slice(0, 8)}).`);
|
|
4407
|
+
}
|
|
4408
|
+
//#endregion
|
|
4409
|
+
//#region src/auto-reply/reply/commands-subagents/action-spawn.ts
|
|
4410
|
+
async function handleSubagentsSpawnAction(ctx) {
|
|
4411
|
+
const { params, requesterKey, restTokens } = ctx;
|
|
4412
|
+
const agentId = restTokens[0];
|
|
4413
|
+
const taskParts = [];
|
|
4414
|
+
let model;
|
|
4415
|
+
let thinking;
|
|
4416
|
+
for (let i = 1; i < restTokens.length; i++) if (restTokens[i] === "--model" && i + 1 < restTokens.length) {
|
|
4417
|
+
i += 1;
|
|
4418
|
+
model = restTokens[i];
|
|
4419
|
+
} else if (restTokens[i] === "--thinking" && i + 1 < restTokens.length) {
|
|
4420
|
+
i += 1;
|
|
4421
|
+
thinking = restTokens[i];
|
|
4422
|
+
} else taskParts.push(restTokens[i]);
|
|
4423
|
+
const task = taskParts.join(" ").trim();
|
|
4424
|
+
if (!agentId || !task) return stopWithText("Usage: /subagents spawn <agentId> <task> [--model <model>] [--thinking <level>]");
|
|
4425
|
+
const commandTo = typeof params.command.to === "string" ? params.command.to.trim() : "";
|
|
4426
|
+
const originatingTo = typeof params.ctx.OriginatingTo === "string" ? params.ctx.OriginatingTo.trim() : "";
|
|
4427
|
+
const fallbackTo = typeof params.ctx.To === "string" ? params.ctx.To.trim() : "";
|
|
4428
|
+
const normalizedTo = originatingTo || commandTo || fallbackTo || void 0;
|
|
4429
|
+
const result = await spawnSubagentDirect({
|
|
4430
|
+
task,
|
|
4431
|
+
agentId,
|
|
4432
|
+
model,
|
|
4433
|
+
thinking,
|
|
4434
|
+
mode: "run",
|
|
4435
|
+
cleanup: "keep",
|
|
4436
|
+
expectsCompletionMessage: true
|
|
4437
|
+
}, {
|
|
4438
|
+
agentSessionKey: requesterKey,
|
|
4439
|
+
agentChannel: params.ctx.OriginatingChannel ?? params.command.channel,
|
|
4440
|
+
agentAccountId: params.ctx.AccountId,
|
|
4441
|
+
agentTo: normalizedTo,
|
|
4442
|
+
agentThreadId: params.ctx.MessageThreadId,
|
|
4443
|
+
agentGroupId: params.sessionEntry?.groupId ?? null,
|
|
4444
|
+
agentGroupChannel: params.sessionEntry?.groupChannel ?? null,
|
|
4445
|
+
agentGroupSpace: params.sessionEntry?.space ?? null
|
|
4446
|
+
});
|
|
4447
|
+
if (result.status === "accepted") return stopWithText(`Spawned subagent ${agentId} (session ${result.childSessionKey}, run ${result.runId?.slice(0, 8)}).`);
|
|
4448
|
+
return stopWithText(`Spawn failed: ${result.error ?? result.status}`);
|
|
4449
|
+
}
|
|
4450
|
+
//#endregion
|
|
4451
|
+
//#region src/auto-reply/reply/commands-subagents/action-unfocus.ts
|
|
4452
|
+
async function handleSubagentsUnfocusAction(ctx) {
|
|
4453
|
+
const { params } = ctx;
|
|
4454
|
+
const channel = resolveCommandSurfaceChannel(params);
|
|
4455
|
+
if (channel !== "discord" && channel !== "matrix" && channel !== "telegram") return stopWithText("⚠️ /unfocus is only available on Discord, Matrix, and Telegram.");
|
|
4456
|
+
const accountId = resolveChannelAccountId(params);
|
|
4457
|
+
const bindingService = getSessionBindingService();
|
|
4458
|
+
const conversationId = (() => {
|
|
4459
|
+
if (isDiscordSurface(params)) return (params.ctx.MessageThreadId != null ? String(params.ctx.MessageThreadId) : "").trim() || void 0;
|
|
4460
|
+
if (isTelegramSurface(params)) return resolveTelegramConversationId(params);
|
|
4461
|
+
if (isMatrixSurface(params)) return resolveMatrixConversationId({
|
|
4462
|
+
ctx: {
|
|
4463
|
+
MessageThreadId: params.ctx.MessageThreadId,
|
|
4464
|
+
OriginatingTo: params.ctx.OriginatingTo,
|
|
4465
|
+
To: params.ctx.To
|
|
4466
|
+
},
|
|
4467
|
+
command: { to: params.command.to }
|
|
4468
|
+
});
|
|
4469
|
+
})();
|
|
4470
|
+
const parentConversationId = (() => {
|
|
4471
|
+
if (!isMatrixSurface(params)) return;
|
|
4472
|
+
return resolveMatrixParentConversationId({
|
|
4473
|
+
ctx: {
|
|
4474
|
+
MessageThreadId: params.ctx.MessageThreadId,
|
|
4475
|
+
OriginatingTo: params.ctx.OriginatingTo,
|
|
4476
|
+
To: params.ctx.To
|
|
4477
|
+
},
|
|
4478
|
+
command: { to: params.command.to }
|
|
4479
|
+
});
|
|
4480
|
+
})();
|
|
4481
|
+
if (!conversationId) {
|
|
4482
|
+
if (channel === "discord") return stopWithText("⚠️ /unfocus must be run inside a Discord thread.");
|
|
4483
|
+
if (channel === "matrix") return stopWithText("⚠️ /unfocus must be run inside a Matrix thread.");
|
|
4484
|
+
return stopWithText("⚠️ /unfocus on Telegram requires a topic context in groups, or a direct-message conversation.");
|
|
4485
|
+
}
|
|
4486
|
+
const binding = bindingService.resolveByConversation({
|
|
4487
|
+
channel,
|
|
4488
|
+
accountId,
|
|
4489
|
+
conversationId,
|
|
4490
|
+
...parentConversationId && parentConversationId !== conversationId ? { parentConversationId } : {}
|
|
4491
|
+
});
|
|
4492
|
+
if (!binding) return stopWithText(channel === "discord" ? "ℹ️ This thread is not currently focused." : channel === "matrix" ? "ℹ️ This thread is not currently focused." : "ℹ️ This conversation is not currently focused.");
|
|
4493
|
+
const senderId = params.command.senderId?.trim() || "";
|
|
4494
|
+
const boundBy = typeof binding.metadata?.boundBy === "string" ? binding.metadata.boundBy.trim() : "";
|
|
4495
|
+
if (boundBy && boundBy !== "system" && senderId && senderId !== boundBy) return stopWithText(channel === "discord" ? `⚠️ Only ${boundBy} can unfocus this thread.` : channel === "matrix" ? `⚠️ Only ${boundBy} can unfocus this thread.` : `⚠️ Only ${boundBy} can unfocus this conversation.`);
|
|
4496
|
+
await bindingService.unbind({
|
|
4497
|
+
bindingId: binding.bindingId,
|
|
4498
|
+
reason: "manual"
|
|
4499
|
+
});
|
|
4500
|
+
return stopWithText(channel === "discord" || channel === "matrix" ? "✅ Thread unfocused." : "✅ Conversation unfocused.");
|
|
4501
|
+
}
|
|
4502
|
+
//#endregion
|
|
4503
|
+
//#region src/auto-reply/reply/commands-subagents.ts
|
|
4504
|
+
const handleSubagentsCommand = async (params, allowTextCommands) => {
|
|
4505
|
+
if (!allowTextCommands) return null;
|
|
4506
|
+
const normalized = params.command.commandBodyNormalized;
|
|
4507
|
+
const handledPrefix = resolveHandledPrefix(normalized);
|
|
4508
|
+
if (!handledPrefix) return null;
|
|
4509
|
+
if (!params.command.isAuthorizedSender) {
|
|
4510
|
+
logVerbose(`Ignoring ${handledPrefix} from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
4511
|
+
return { shouldContinue: false };
|
|
4512
|
+
}
|
|
4513
|
+
const restTokens = normalized.slice(handledPrefix.length).trim().split(/\s+/).filter(Boolean);
|
|
4514
|
+
const action = resolveSubagentsAction({
|
|
4515
|
+
handledPrefix,
|
|
4516
|
+
restTokens
|
|
4517
|
+
});
|
|
4518
|
+
if (!action) return handleSubagentsHelpAction();
|
|
4519
|
+
const requesterKey = action === "spawn" ? resolveRequesterSessionKey(params, { preferCommandTarget: true }) : resolveRequesterSessionKey(params);
|
|
4520
|
+
if (!requesterKey) return stopWithText("⚠️ Missing session key.");
|
|
4521
|
+
const ctx = {
|
|
4522
|
+
params,
|
|
4523
|
+
handledPrefix,
|
|
4524
|
+
requesterKey,
|
|
4525
|
+
runs: listSubagentRunsForController(requesterKey),
|
|
4526
|
+
restTokens
|
|
4527
|
+
};
|
|
4528
|
+
switch (action) {
|
|
4529
|
+
case "help": return handleSubagentsHelpAction();
|
|
4530
|
+
case "agents": return handleSubagentsAgentsAction(ctx);
|
|
4531
|
+
case "focus": return await handleSubagentsFocusAction(ctx);
|
|
4532
|
+
case "unfocus": return await handleSubagentsUnfocusAction(ctx);
|
|
4533
|
+
case "list": return handleSubagentsListAction(ctx);
|
|
4534
|
+
case "kill": return await handleSubagentsKillAction(ctx);
|
|
4535
|
+
case "info": return handleSubagentsInfoAction(ctx);
|
|
4536
|
+
case "log": return await handleSubagentsLogAction(ctx);
|
|
4537
|
+
case "send": return await handleSubagentsSendAction(ctx, false);
|
|
4538
|
+
case "steer": return await handleSubagentsSendAction(ctx, true);
|
|
4539
|
+
case "spawn": return await handleSubagentsSpawnAction(ctx);
|
|
4540
|
+
default: return handleSubagentsHelpAction();
|
|
4541
|
+
}
|
|
4542
|
+
};
|
|
4543
|
+
//#endregion
|
|
4544
|
+
//#region src/auto-reply/reply/commands-tts.ts
|
|
4545
|
+
function parseTtsCommand(normalized) {
|
|
4546
|
+
if (normalized === "/tts") return {
|
|
4547
|
+
action: "status",
|
|
4548
|
+
args: ""
|
|
4549
|
+
};
|
|
4550
|
+
if (!normalized.startsWith("/tts ")) return null;
|
|
4551
|
+
const rest = normalized.slice(5).trim();
|
|
4552
|
+
if (!rest) return {
|
|
4553
|
+
action: "status",
|
|
4554
|
+
args: ""
|
|
4555
|
+
};
|
|
4556
|
+
const [action, ...tail] = rest.split(/\s+/);
|
|
4557
|
+
return {
|
|
4558
|
+
action: action.toLowerCase(),
|
|
4559
|
+
args: tail.join(" ").trim()
|
|
4560
|
+
};
|
|
4561
|
+
}
|
|
4562
|
+
function ttsUsage() {
|
|
4563
|
+
return { text: "🔊 **TTS (Text-to-Speech) Help**\n\n**Commands:**\n• /tts on — Enable automatic TTS for replies\n• /tts off — Disable TTS\n• /tts status — Show current settings\n• /tts provider [name] — View/change provider\n• /tts limit [number] — View/change text limit\n• /tts summary [on|off] — View/change auto-summary\n• /tts audio <text> — Generate audio from text\n\n**Providers:**\n• microsoft — Microsoft Edge-backed speech (default fallback)\n• openai — High quality (requires API key)\n• elevenlabs — Premium voices (requires API key)\n\n**Text Limit (default: 1500, max: 4096):**\nWhen text exceeds the limit:\n• Summary ON: AI summarizes, then generates audio\n• Summary OFF: Truncates text, then generates audio\n\n**Examples:**\n/tts provider microsoft\n/tts limit 2000\n/tts audio Hello, this is a test!" };
|
|
4564
|
+
}
|
|
4565
|
+
const handleTtsCommands = async (params, allowTextCommands) => {
|
|
4566
|
+
if (!allowTextCommands) return null;
|
|
4567
|
+
const parsed = parseTtsCommand(params.command.commandBodyNormalized);
|
|
4568
|
+
if (!parsed) return null;
|
|
4569
|
+
if (!params.command.isAuthorizedSender) {
|
|
4570
|
+
logVerbose(`Ignoring TTS command from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
4571
|
+
return { shouldContinue: false };
|
|
4572
|
+
}
|
|
4573
|
+
const config = resolveTtsConfig(params.cfg);
|
|
4574
|
+
const prefsPath = resolveTtsPrefsPath(config);
|
|
4575
|
+
const action = parsed.action;
|
|
4576
|
+
const args = parsed.args;
|
|
4577
|
+
if (action === "help") return {
|
|
4578
|
+
shouldContinue: false,
|
|
4579
|
+
reply: ttsUsage()
|
|
4580
|
+
};
|
|
4581
|
+
if (action === "on") {
|
|
4582
|
+
setTtsEnabled(prefsPath, true);
|
|
4583
|
+
return {
|
|
4584
|
+
shouldContinue: false,
|
|
4585
|
+
reply: { text: "🔊 TTS enabled." }
|
|
4586
|
+
};
|
|
4587
|
+
}
|
|
4588
|
+
if (action === "off") {
|
|
4589
|
+
setTtsEnabled(prefsPath, false);
|
|
4590
|
+
return {
|
|
4591
|
+
shouldContinue: false,
|
|
4592
|
+
reply: { text: "🔇 TTS disabled." }
|
|
4593
|
+
};
|
|
4594
|
+
}
|
|
4595
|
+
if (action === "audio") {
|
|
4596
|
+
if (!args.trim()) return {
|
|
4597
|
+
shouldContinue: false,
|
|
4598
|
+
reply: { text: "🎤 Generate audio from text.\n\nUsage: /tts audio <text>\nExample: /tts audio Hello, this is a test!" }
|
|
4599
|
+
};
|
|
4600
|
+
const start = Date.now();
|
|
4601
|
+
const result = await textToSpeech({
|
|
4602
|
+
text: args,
|
|
4603
|
+
cfg: params.cfg,
|
|
4604
|
+
channel: params.command.channel,
|
|
4605
|
+
prefsPath
|
|
4606
|
+
});
|
|
4607
|
+
if (result.success && result.audioPath) {
|
|
4608
|
+
setLastTtsAttempt({
|
|
4609
|
+
timestamp: Date.now(),
|
|
4610
|
+
success: true,
|
|
4611
|
+
textLength: args.length,
|
|
4612
|
+
summarized: false,
|
|
4613
|
+
provider: result.provider,
|
|
4614
|
+
latencyMs: result.latencyMs
|
|
4615
|
+
});
|
|
4616
|
+
return {
|
|
4617
|
+
shouldContinue: false,
|
|
4618
|
+
reply: {
|
|
4619
|
+
mediaUrl: result.audioPath,
|
|
4620
|
+
audioAsVoice: result.voiceCompatible === true
|
|
4621
|
+
}
|
|
4622
|
+
};
|
|
4623
|
+
}
|
|
4624
|
+
setLastTtsAttempt({
|
|
4625
|
+
timestamp: Date.now(),
|
|
4626
|
+
success: false,
|
|
4627
|
+
textLength: args.length,
|
|
4628
|
+
summarized: false,
|
|
4629
|
+
error: result.error,
|
|
4630
|
+
latencyMs: Date.now() - start
|
|
4631
|
+
});
|
|
4632
|
+
return {
|
|
4633
|
+
shouldContinue: false,
|
|
4634
|
+
reply: { text: `❌ Error generating audio: ${result.error ?? "unknown error"}` }
|
|
4635
|
+
};
|
|
4636
|
+
}
|
|
4637
|
+
if (action === "provider") {
|
|
4638
|
+
const currentProvider = getTtsProvider(config, prefsPath);
|
|
4639
|
+
if (!args.trim()) {
|
|
4640
|
+
const hasOpenAI = Boolean(resolveTtsApiKey(config, "openai"));
|
|
4641
|
+
const hasElevenLabs = Boolean(resolveTtsApiKey(config, "elevenlabs"));
|
|
4642
|
+
const hasMicrosoft = isTtsProviderConfigured(config, "microsoft", params.cfg);
|
|
4643
|
+
return {
|
|
4644
|
+
shouldContinue: false,
|
|
4645
|
+
reply: { text: `🎙️ TTS provider\nPrimary: ${currentProvider}\nOpenAI key: ${hasOpenAI ? "✅" : "❌"}\nElevenLabs key: ${hasElevenLabs ? "✅" : "❌"}\nMicrosoft enabled: ${hasMicrosoft ? "✅" : "❌"}\nUsage: /tts provider openai | elevenlabs | microsoft` }
|
|
4646
|
+
};
|
|
4647
|
+
}
|
|
4648
|
+
const requested = args.trim().toLowerCase();
|
|
4649
|
+
const knownProviders = new Set(listSpeechProviders(params.cfg).map((provider) => provider.id));
|
|
4650
|
+
if (requested !== "edge" && !knownProviders.has(requested)) return {
|
|
4651
|
+
shouldContinue: false,
|
|
4652
|
+
reply: ttsUsage()
|
|
4653
|
+
};
|
|
4654
|
+
const nextProvider = normalizeSpeechProviderId(requested) ?? requested;
|
|
4655
|
+
setTtsProvider(prefsPath, requested);
|
|
4656
|
+
return {
|
|
4657
|
+
shouldContinue: false,
|
|
4658
|
+
reply: { text: `✅ TTS provider set to ${nextProvider}.` }
|
|
4659
|
+
};
|
|
4660
|
+
}
|
|
4661
|
+
if (action === "limit") {
|
|
4662
|
+
if (!args.trim()) return {
|
|
4663
|
+
shouldContinue: false,
|
|
4664
|
+
reply: { text: `📏 TTS limit: ${getTtsMaxLength(prefsPath)} characters.\n\nText longer than this triggers summary (if enabled).\nRange: 100-4096 chars (Telegram max).\n\nTo change: /tts limit <number>\nExample: /tts limit 2000` }
|
|
4665
|
+
};
|
|
4666
|
+
const next = Number.parseInt(args.trim(), 10);
|
|
4667
|
+
if (!Number.isFinite(next) || next < 100 || next > 4096) return {
|
|
4668
|
+
shouldContinue: false,
|
|
4669
|
+
reply: { text: "❌ Limit must be between 100 and 4096 characters." }
|
|
4670
|
+
};
|
|
4671
|
+
setTtsMaxLength(prefsPath, next);
|
|
4672
|
+
return {
|
|
4673
|
+
shouldContinue: false,
|
|
4674
|
+
reply: { text: `✅ TTS limit set to ${next} characters.` }
|
|
4675
|
+
};
|
|
4676
|
+
}
|
|
4677
|
+
if (action === "summary") {
|
|
4678
|
+
if (!args.trim()) {
|
|
4679
|
+
const enabled = isSummarizationEnabled(prefsPath);
|
|
4680
|
+
const maxLen = getTtsMaxLength(prefsPath);
|
|
4681
|
+
return {
|
|
4682
|
+
shouldContinue: false,
|
|
4683
|
+
reply: { text: `📝 TTS auto-summary: ${enabled ? "on" : "off"}.\n\nWhen text exceeds ${maxLen} chars:\n• ON: summarizes text, then generates audio\n• OFF: truncates text, then generates audio\n\nTo change: /tts summary on | off` }
|
|
4684
|
+
};
|
|
4685
|
+
}
|
|
4686
|
+
const requested = args.trim().toLowerCase();
|
|
4687
|
+
if (requested !== "on" && requested !== "off") return {
|
|
4688
|
+
shouldContinue: false,
|
|
4689
|
+
reply: ttsUsage()
|
|
4690
|
+
};
|
|
4691
|
+
setSummarizationEnabled(prefsPath, requested === "on");
|
|
4692
|
+
return {
|
|
4693
|
+
shouldContinue: false,
|
|
4694
|
+
reply: { text: requested === "on" ? "✅ TTS auto-summary enabled." : "❌ TTS auto-summary disabled." }
|
|
4695
|
+
};
|
|
4696
|
+
}
|
|
4697
|
+
if (action === "status") {
|
|
4698
|
+
const enabled = isTtsEnabled(config, prefsPath);
|
|
4699
|
+
const provider = getTtsProvider(config, prefsPath);
|
|
4700
|
+
const hasKey = isTtsProviderConfigured(config, provider, params.cfg);
|
|
4701
|
+
const maxLength = getTtsMaxLength(prefsPath);
|
|
4702
|
+
const summarize = isSummarizationEnabled(prefsPath);
|
|
4703
|
+
const last = getLastTtsAttempt();
|
|
4704
|
+
const lines = [
|
|
4705
|
+
"📊 TTS status",
|
|
4706
|
+
`State: ${enabled ? "✅ enabled" : "❌ disabled"}`,
|
|
4707
|
+
`Provider: ${provider} (${hasKey ? "✅ configured" : "❌ not configured"})`,
|
|
4708
|
+
`Text limit: ${maxLength} chars`,
|
|
4709
|
+
`Auto-summary: ${summarize ? "on" : "off"}`
|
|
4710
|
+
];
|
|
4711
|
+
if (last) {
|
|
4712
|
+
const timeAgo = Math.round((Date.now() - last.timestamp) / 1e3);
|
|
4713
|
+
lines.push("");
|
|
4714
|
+
lines.push(`Last attempt (${timeAgo}s ago): ${last.success ? "✅" : "❌"}`);
|
|
4715
|
+
lines.push(`Text: ${last.textLength} chars${last.summarized ? " (summarized)" : ""}`);
|
|
4716
|
+
if (last.success) {
|
|
4717
|
+
lines.push(`Provider: ${last.provider ?? "unknown"}`);
|
|
4718
|
+
lines.push(`Latency: ${last.latencyMs ?? 0}ms`);
|
|
4719
|
+
} else if (last.error) lines.push(`Error: ${last.error}`);
|
|
4720
|
+
}
|
|
4721
|
+
return {
|
|
4722
|
+
shouldContinue: false,
|
|
4723
|
+
reply: { text: lines.join("\n") }
|
|
4724
|
+
};
|
|
4725
|
+
}
|
|
4726
|
+
return {
|
|
4727
|
+
shouldContinue: false,
|
|
4728
|
+
reply: ttsUsage()
|
|
4729
|
+
};
|
|
4730
|
+
};
|
|
4731
|
+
//#endregion
|
|
4732
|
+
//#region src/auto-reply/reply/commands-core.ts
|
|
4733
|
+
let HANDLERS = null;
|
|
4734
|
+
async function emitResetCommandHooks(params) {
|
|
4735
|
+
const hookEvent = createInternalHookEvent("command", params.action, params.sessionKey ?? "", {
|
|
4736
|
+
sessionEntry: params.sessionEntry,
|
|
4737
|
+
previousSessionEntry: params.previousSessionEntry,
|
|
4738
|
+
commandSource: params.command.surface,
|
|
4739
|
+
senderId: params.command.senderId,
|
|
4740
|
+
workspaceDir: params.workspaceDir,
|
|
4741
|
+
cfg: params.cfg
|
|
4742
|
+
});
|
|
4743
|
+
await triggerInternalHook(hookEvent);
|
|
4744
|
+
params.command.resetHookTriggered = true;
|
|
4745
|
+
if (hookEvent.messages.length > 0) {
|
|
4746
|
+
const channel = params.ctx.OriginatingChannel || params.command.channel;
|
|
4747
|
+
const to = params.ctx.OriginatingTo || params.command.from || params.command.to;
|
|
4748
|
+
if (channel && to) await routeReply({
|
|
4749
|
+
payload: { text: hookEvent.messages.join("\n\n") },
|
|
4750
|
+
channel,
|
|
4751
|
+
to,
|
|
4752
|
+
sessionKey: params.sessionKey,
|
|
4753
|
+
accountId: params.ctx.AccountId,
|
|
4754
|
+
threadId: params.ctx.MessageThreadId,
|
|
4755
|
+
cfg: params.cfg
|
|
4756
|
+
});
|
|
4757
|
+
}
|
|
4758
|
+
const hookRunner = getGlobalHookRunner();
|
|
4759
|
+
if (hookRunner?.hasHooks("before_reset")) {
|
|
4760
|
+
const prevEntry = params.previousSessionEntry;
|
|
4761
|
+
const sessionFile = prevEntry?.sessionFile;
|
|
4762
|
+
(async () => {
|
|
4763
|
+
try {
|
|
4764
|
+
const messages = [];
|
|
4765
|
+
if (sessionFile) {
|
|
4766
|
+
const content = await fs$1.readFile(sessionFile, "utf-8");
|
|
4767
|
+
for (const line of content.split("\n")) {
|
|
4768
|
+
if (!line.trim()) continue;
|
|
4769
|
+
try {
|
|
4770
|
+
const entry = JSON.parse(line);
|
|
4771
|
+
if (entry.type === "message" && entry.message) messages.push(entry.message);
|
|
4772
|
+
} catch {}
|
|
4773
|
+
}
|
|
4774
|
+
} else logVerbose("before_reset: no session file available, firing hook with empty messages");
|
|
4775
|
+
await hookRunner.runBeforeReset({
|
|
4776
|
+
sessionFile,
|
|
4777
|
+
messages,
|
|
4778
|
+
reason: params.action
|
|
4779
|
+
}, {
|
|
4780
|
+
agentId: resolveAgentIdFromSessionKey(params.sessionKey),
|
|
4781
|
+
sessionKey: params.sessionKey,
|
|
4782
|
+
sessionId: prevEntry?.sessionId,
|
|
4783
|
+
workspaceDir: params.workspaceDir
|
|
4784
|
+
});
|
|
4785
|
+
} catch (err) {
|
|
4786
|
+
logVerbose(`before_reset hook failed: ${String(err)}`);
|
|
4787
|
+
}
|
|
4788
|
+
})();
|
|
4789
|
+
}
|
|
4790
|
+
}
|
|
4791
|
+
function applyAcpResetTailContext(ctx, resetTail) {
|
|
4792
|
+
const mutableCtx = ctx;
|
|
4793
|
+
mutableCtx.Body = resetTail;
|
|
4794
|
+
mutableCtx.RawBody = resetTail;
|
|
4795
|
+
mutableCtx.CommandBody = resetTail;
|
|
4796
|
+
mutableCtx.BodyForCommands = resetTail;
|
|
4797
|
+
mutableCtx.BodyForAgent = resetTail;
|
|
4798
|
+
mutableCtx.BodyStripped = resetTail;
|
|
4799
|
+
mutableCtx.AcpDispatchTailAfterReset = true;
|
|
4800
|
+
}
|
|
4801
|
+
function resolveSessionEntryForHookSessionKey(sessionStore, sessionKey) {
|
|
4802
|
+
if (!sessionStore) return;
|
|
4803
|
+
const directEntry = sessionStore[sessionKey];
|
|
4804
|
+
if (directEntry) return directEntry;
|
|
4805
|
+
const normalizedTarget = sessionKey.trim().toLowerCase();
|
|
4806
|
+
if (!normalizedTarget) return;
|
|
4807
|
+
for (const [candidateKey, candidateEntry] of Object.entries(sessionStore)) if (candidateKey.trim().toLowerCase() === normalizedTarget) return candidateEntry;
|
|
4808
|
+
}
|
|
4809
|
+
async function handleCommands(params) {
|
|
4810
|
+
if (HANDLERS === null) HANDLERS = [
|
|
4811
|
+
handlePluginCommand,
|
|
4812
|
+
handleBtwCommand,
|
|
4813
|
+
handleBashCommand,
|
|
4814
|
+
handleActivationCommand,
|
|
4815
|
+
handleSendPolicyCommand,
|
|
4816
|
+
handleFastCommand,
|
|
4817
|
+
handleUsageCommand,
|
|
4818
|
+
handleSessionCommand,
|
|
4819
|
+
handleRestartCommand,
|
|
4820
|
+
handleTtsCommands,
|
|
4821
|
+
handleHelpCommand,
|
|
4822
|
+
handleCommandsListCommand,
|
|
4823
|
+
handleStatusCommand,
|
|
4824
|
+
handleAllowlistCommand,
|
|
4825
|
+
handleApproveCommand,
|
|
4826
|
+
handleContextCommand,
|
|
4827
|
+
handleExportSessionCommand,
|
|
4828
|
+
handleWhoamiCommand,
|
|
4829
|
+
handleSubagentsCommand,
|
|
4830
|
+
handleAcpCommand,
|
|
4831
|
+
handleMcpCommand,
|
|
4832
|
+
handlePluginsCommand,
|
|
4833
|
+
handleConfigCommand,
|
|
4834
|
+
handleDebugCommand,
|
|
4835
|
+
handleModelsCommand,
|
|
4836
|
+
handleStopCommand,
|
|
4837
|
+
handleCompactCommand,
|
|
4838
|
+
handleAbortTrigger
|
|
4839
|
+
];
|
|
4840
|
+
const resetMatch = params.command.commandBodyNormalized.match(/^\/(new|reset)(?:\s|$)/);
|
|
4841
|
+
const resetRequested = Boolean(resetMatch);
|
|
4842
|
+
if (resetRequested && !params.command.isAuthorizedSender) {
|
|
4843
|
+
logVerbose(`Ignoring /reset from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
4844
|
+
return { shouldContinue: false };
|
|
4845
|
+
}
|
|
4846
|
+
if (resetRequested && params.command.isAuthorizedSender) {
|
|
4847
|
+
const commandAction = resetMatch?.[1] === "reset" ? "reset" : "new";
|
|
4848
|
+
const resetTail = resetMatch != null ? params.command.commandBodyNormalized.slice(resetMatch[0].length).trimStart() : "";
|
|
4849
|
+
const boundAcpSessionKey = resolveBoundAcpThreadSessionKey(params);
|
|
4850
|
+
const boundAcpKey = boundAcpSessionKey && isAcpSessionKey(boundAcpSessionKey) ? boundAcpSessionKey.trim() : void 0;
|
|
4851
|
+
if (boundAcpKey) {
|
|
4852
|
+
const resetResult = await resetConfiguredBindingTargetInPlace({
|
|
4853
|
+
cfg: params.cfg,
|
|
4854
|
+
sessionKey: boundAcpKey,
|
|
4855
|
+
reason: commandAction
|
|
4856
|
+
});
|
|
4857
|
+
if (!resetResult.ok && !resetResult.skipped) logVerbose(`acp reset-in-place failed for ${boundAcpKey}: ${resetResult.error ?? "unknown error"}`);
|
|
4858
|
+
if (resetResult.ok) {
|
|
4859
|
+
const hookSessionEntry = boundAcpKey === params.sessionKey ? params.sessionEntry : resolveSessionEntryForHookSessionKey(params.sessionStore, boundAcpKey);
|
|
4860
|
+
const hookPreviousSessionEntry = boundAcpKey === params.sessionKey ? params.previousSessionEntry : resolveSessionEntryForHookSessionKey(params.sessionStore, boundAcpKey);
|
|
4861
|
+
await emitResetCommandHooks({
|
|
4862
|
+
action: commandAction,
|
|
4863
|
+
ctx: params.ctx,
|
|
4864
|
+
cfg: params.cfg,
|
|
4865
|
+
command: params.command,
|
|
4866
|
+
sessionKey: boundAcpKey,
|
|
4867
|
+
sessionEntry: hookSessionEntry,
|
|
4868
|
+
previousSessionEntry: hookPreviousSessionEntry,
|
|
4869
|
+
workspaceDir: params.workspaceDir
|
|
4870
|
+
});
|
|
4871
|
+
if (resetTail) {
|
|
4872
|
+
applyAcpResetTailContext(params.ctx, resetTail);
|
|
4873
|
+
if (params.rootCtx && params.rootCtx !== params.ctx) applyAcpResetTailContext(params.rootCtx, resetTail);
|
|
4874
|
+
return { shouldContinue: false };
|
|
4875
|
+
}
|
|
4876
|
+
return {
|
|
4877
|
+
shouldContinue: false,
|
|
4878
|
+
reply: { text: "✅ ACP session reset in place." }
|
|
4879
|
+
};
|
|
4880
|
+
}
|
|
4881
|
+
if (resetResult.skipped) return {
|
|
4882
|
+
shouldContinue: false,
|
|
4883
|
+
reply: { text: "⚠️ ACP session reset unavailable for this bound conversation. Rebind with /acp bind or /acp spawn." }
|
|
4884
|
+
};
|
|
4885
|
+
return {
|
|
4886
|
+
shouldContinue: false,
|
|
4887
|
+
reply: { text: "⚠️ ACP session reset failed. Check /acp status and try again." }
|
|
4888
|
+
};
|
|
4889
|
+
}
|
|
4890
|
+
await emitResetCommandHooks({
|
|
4891
|
+
action: commandAction,
|
|
4892
|
+
ctx: params.ctx,
|
|
4893
|
+
cfg: params.cfg,
|
|
4894
|
+
command: params.command,
|
|
4895
|
+
sessionKey: params.sessionKey,
|
|
4896
|
+
sessionEntry: params.sessionEntry,
|
|
4897
|
+
previousSessionEntry: params.previousSessionEntry,
|
|
4898
|
+
workspaceDir: params.workspaceDir
|
|
4899
|
+
});
|
|
4900
|
+
}
|
|
4901
|
+
const allowTextCommands = shouldHandleTextCommands({
|
|
4902
|
+
cfg: params.cfg,
|
|
4903
|
+
surface: params.command.surface,
|
|
4904
|
+
commandSource: params.ctx.CommandSource
|
|
4905
|
+
});
|
|
4906
|
+
for (const handler of HANDLERS) {
|
|
4907
|
+
const result = await handler(params, allowTextCommands);
|
|
4908
|
+
if (result) return result;
|
|
4909
|
+
}
|
|
4910
|
+
if (resolveSendPolicy({
|
|
4911
|
+
cfg: params.cfg,
|
|
4912
|
+
entry: params.sessionEntry,
|
|
4913
|
+
sessionKey: params.sessionKey,
|
|
4914
|
+
channel: params.sessionEntry?.channel ?? params.command.channel,
|
|
4915
|
+
chatType: params.sessionEntry?.chatType
|
|
4916
|
+
}) === "deny") {
|
|
4917
|
+
logVerbose(`Send blocked by policy for session ${params.sessionKey ?? "unknown"}`);
|
|
4918
|
+
return { shouldContinue: false };
|
|
4919
|
+
}
|
|
4920
|
+
return { shouldContinue: true };
|
|
4921
|
+
}
|
|
4922
|
+
//#endregion
|
|
4923
|
+
export { handleCommands as n, emitResetCommandHooks as t };
|