@poolzin/pool-bot 2026.2.24 → 2026.2.26
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 +21 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/event-mapper.js +87 -22
- package/dist/acp/meta.js +12 -6
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-paths.js +8 -9
- package/dist/agents/agent-scope.js +17 -5
- package/dist/agents/auth-profiles/oauth.js +148 -64
- package/dist/agents/auth-profiles/session-override.js +13 -7
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +240 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-runtime.js +2 -25
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/bedrock-discovery.js +3 -1
- package/dist/agents/byteplus-models.js +97 -0
- package/dist/agents/chutes-oauth.js +1 -0
- package/dist/agents/cli-runner/helpers.js +4 -0
- package/dist/agents/compaction.js +41 -14
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/doubao-models.js +121 -0
- package/dist/agents/failover-error.js +2 -0
- package/dist/agents/huggingface-models.js +5 -3
- package/dist/agents/live-model-filter.js +5 -0
- package/dist/agents/minimax-vlm.js +10 -8
- package/dist/agents/model-auth.js +6 -0
- package/dist/agents/model-catalog.js +3 -1
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/model-selection.js +7 -1
- package/dist/agents/models-config.providers.js +364 -165
- package/dist/agents/ollama-stream.js +117 -4
- package/dist/agents/opencode-zen-models.js +22 -11
- package/dist/agents/pi-embedded-helpers/errors.js +55 -33
- package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
- package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/compact.js +29 -7
- package/dist/agents/pi-embedded-runner/extensions.js +28 -26
- package/dist/agents/pi-embedded-runner/google.js +20 -8
- package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
- package/dist/agents/pi-embedded-runner/run.js +71 -12
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
- package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
- package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
- package/dist/agents/pi-embedded-runner/thinking.js +42 -0
- package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
- package/dist/agents/pi-embedded-runner/utils.js +7 -10
- package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
- package/dist/agents/pi-embedded-subscribe.js +9 -4
- package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
- package/dist/agents/pi-embedded-utils.js +3 -0
- package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
- package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
- package/dist/agents/pi-settings.js +40 -0
- package/dist/agents/pi-tools.policy.js +2 -1
- package/dist/agents/provider/config-loader.js +1 -1
- package/dist/agents/sandbox/browser.js +170 -33
- package/dist/agents/sandbox/config-hash.js +14 -27
- package/dist/agents/sandbox/config.js +21 -2
- package/dist/agents/sandbox/constants.js +2 -0
- package/dist/agents/sandbox/docker.js +16 -2
- package/dist/agents/sandbox/novnc-auth.js +62 -0
- package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
- package/dist/agents/sandbox/shared.js +10 -6
- package/dist/agents/sandbox-paths.js +24 -11
- package/dist/agents/schema/clean-for-gemini.js +132 -85
- package/dist/agents/session-slug.js +10 -5
- package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
- package/dist/agents/session-tool-result-guard.js +3 -1
- package/dist/agents/session-transcript-repair.js +40 -6
- package/dist/agents/skills/bundled-dir.js +19 -5
- package/dist/agents/skills/env-overrides.js +124 -43
- package/dist/agents/skills/frontmatter.js +6 -6
- package/dist/agents/skills/plugin-skills.js +14 -7
- package/dist/agents/skills/workspace.js +1 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-announce.js +251 -49
- package/dist/agents/subagent-lifecycle-events.js +19 -0
- package/dist/agents/subagent-registry-cleanup.js +31 -0
- package/dist/agents/subagent-registry-completion.js +68 -0
- package/dist/agents/subagent-registry-queries.js +117 -0
- package/dist/agents/subagent-registry-state.js +46 -0
- package/dist/agents/subagent-registry.js +252 -221
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/subagent-registry.store.js +1 -0
- package/dist/agents/subagent-registry.types.js +1 -0
- package/dist/agents/subagent-spawn.js +195 -7
- package/dist/agents/system-prompt.js +22 -6
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
- package/dist/agents/test-helpers/fast-core-tools.js +1 -17
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/timeout.js +18 -6
- package/dist/agents/tool-call-id.js +1 -1
- package/dist/agents/tool-display-common.js +162 -29
- package/dist/agents/tool-images.js +82 -9
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tool-policy.js +51 -26
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/canvas-tool.js +27 -1
- package/dist/agents/tools/common.js +45 -0
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-guild.js +4 -1
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/gateway-tool.js +3 -1
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/nodes-utils.js +1 -10
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/tools/sessions-send-helpers.js +12 -6
- package/dist/agents/tools/sessions-spawn-tool.js +8 -2
- package/dist/agents/tools/subagents-tool.js +2 -1
- package/dist/agents/tools/whatsapp-actions.js +10 -2
- package/dist/agents/tools/whatsapp-target-auth.js +18 -0
- package/dist/agents/transcript-policy.js +22 -8
- package/dist/agents/venice-models.js +11 -3
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.data.js +51 -0
- package/dist/auto-reply/commands-registry.js +19 -21
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/group-activation.js +10 -5
- package/dist/auto-reply/inbound-debounce.js +10 -5
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/abort.js +1 -1
- package/dist/auto-reply/reply/agent-runner-execution.js +40 -5
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/bash-command.js +41 -39
- package/dist/auto-reply/reply/command-gates.js +25 -0
- package/dist/auto-reply/reply/commands-allowlist.js +111 -72
- package/dist/auto-reply/reply/commands-bash.js +6 -5
- package/dist/auto-reply/reply/commands-config.js +30 -28
- package/dist/auto-reply/reply/commands-core.js +2 -1
- package/dist/auto-reply/reply/commands-info.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +65 -14
- package/dist/auto-reply/reply/commands-session.js +237 -82
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/auto-reply/reply/commands-setunset.js +45 -0
- package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
- package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
- package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
- package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
- package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
- package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
- package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
- package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
- package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
- package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
- package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
- package/dist/auto-reply/reply/commands-subagents.js +51 -587
- package/dist/auto-reply/reply/commands-tts.js +10 -5
- package/dist/auto-reply/reply/config-value.js +10 -5
- package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
- package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
- package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
- package/dist/auto-reply/reply/followup-runner.js +1 -0
- package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
- package/dist/auto-reply/reply/get-reply-directives.js +17 -28
- package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
- package/dist/auto-reply/reply/get-reply.js +71 -12
- package/dist/auto-reply/reply/model-selection.js +80 -39
- package/dist/auto-reply/reply/queue/enqueue.js +10 -5
- package/dist/auto-reply/reply/queue/state.js +13 -12
- package/dist/auto-reply/reply/reply-payloads.js +67 -36
- package/dist/auto-reply/reply/reply-reference.js +9 -8
- package/dist/auto-reply/reply/route-reply.js +15 -8
- package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
- package/dist/auto-reply/reply/session.js +22 -6
- package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
- package/dist/auto-reply/reply/subagents-utils.js +56 -30
- package/dist/auto-reply/reply/typing.js +46 -21
- package/dist/auto-reply/send-policy.js +14 -7
- package/dist/auto-reply/status.js +140 -16
- package/dist/auto-reply/templating.js +10 -5
- package/dist/auto-reply/thinking.js +7 -16
- package/dist/auto-reply/tokens.js +21 -5
- package/dist/browser/bridge-server.js +36 -20
- package/dist/browser/cdp.helpers.js +7 -14
- package/dist/browser/cdp.js +35 -15
- package/dist/browser/chrome.profile-decoration.js +7 -4
- package/dist/browser/config.js +30 -0
- package/dist/browser/extension-relay-auth.js +55 -0
- package/dist/browser/extension-relay.js +74 -29
- package/dist/browser/navigation-guard.js +39 -0
- package/dist/browser/paths.js +77 -0
- package/dist/browser/profiles.js +13 -8
- package/dist/browser/pw-ai-module.js +10 -5
- package/dist/browser/pw-session.js +76 -39
- package/dist/browser/pw-tools-core.interactions.js +14 -7
- package/dist/browser/pw-tools-core.state.js +12 -6
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-context.js +7 -0
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +26 -0
- package/dist/channels/allowlists/resolve-utils.js +43 -19
- package/dist/channels/channel-config.js +14 -7
- package/dist/channels/draft-stream-loop.js +7 -0
- package/dist/channels/model-overrides.js +82 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/plugins/normalize/imessage.js +14 -7
- package/dist/channels/plugins/normalize/slack.js +10 -5
- package/dist/channels/plugins/normalize/telegram.js +14 -7
- package/dist/channels/plugins/outbound/discord.js +80 -8
- package/dist/channels/plugins/outbound/signal.js +11 -11
- package/dist/channels/plugins/setup-helpers.js +10 -5
- package/dist/channels/sender-label.js +14 -7
- package/dist/channels/session.js +4 -2
- package/dist/channels/status-reactions.js +297 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +3 -2
- package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
- package/dist/cli/cli-name.js +11 -11
- package/dist/cli/cli-utils.js +13 -3
- package/dist/cli/command-format.js +1 -1
- package/dist/cli/config-cli.js +1 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
- package/dist/cli/daemon-cli/lifecycle.js +64 -2
- package/dist/cli/daemon-cli/restart-health.js +126 -0
- package/dist/cli/daemon-cli/status.gather.js +9 -13
- package/dist/cli/daemon-cli/status.print.js +2 -10
- package/dist/cli/deps.js +27 -22
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/gateway-cli/run-loop.js +23 -5
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/node-cli/register.js +14 -5
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +26 -0
- package/dist/cli/outbound-send-deps.js +2 -9
- package/dist/cli/outbound-send-mapping.js +11 -0
- package/dist/cli/pairing-cli.js +40 -14
- package/dist/cli/plugins-cli.js +250 -73
- package/dist/cli/ports.js +11 -10
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +214 -136
- package/dist/cli/program/command-tree.js +16 -0
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/program/preaction.js +13 -9
- package/dist/cli/program/register.configure.js +3 -18
- package/dist/cli/program/register.maintenance.js +2 -2
- package/dist/cli/program/register.onboard.js +2 -0
- package/dist/cli/program/register.status-health-sessions.js +16 -17
- package/dist/cli/program/register.subclis.js +93 -52
- package/dist/cli/route.js +12 -8
- package/dist/cli/system-cli.js +36 -46
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/cli/update-cli/shared.js +22 -9
- package/dist/cli/update-cli/update-command.js +89 -14
- package/dist/cli/update-cli/wizard.js +6 -12
- package/dist/commands/agent/run-context.js +18 -5
- package/dist/commands/agent/session-store.js +17 -4
- package/dist/commands/agent.js +185 -89
- package/dist/commands/agents.bindings.js +14 -7
- package/dist/commands/agents.commands.add.js +13 -9
- package/dist/commands/agents.commands.identity.js +12 -6
- package/dist/commands/agents.commands.list.js +11 -6
- package/dist/commands/agents.config.js +8 -10
- package/dist/commands/agents.providers.js +12 -6
- package/dist/commands/auth-choice-options.js +103 -75
- package/dist/commands/auth-choice.apply.byteplus.js +55 -0
- package/dist/commands/auth-choice.apply.js +4 -0
- package/dist/commands/auth-choice.apply.minimax.js +61 -13
- package/dist/commands/auth-choice.apply.openai.js +3 -1
- package/dist/commands/auth-choice.apply.volcengine.js +55 -0
- package/dist/commands/auth-choice.preferred-provider.js +2 -0
- package/dist/commands/channels/remove.js +13 -6
- package/dist/commands/channels/shared.js +4 -14
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/configure.commands.js +14 -0
- package/dist/commands/configure.gateway.js +2 -4
- package/dist/commands/configure.js +1 -1
- package/dist/commands/configure.shared.js +11 -0
- package/dist/commands/daemon-install-helpers.js +2 -2
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/dashboard.js +12 -10
- package/dist/commands/docs.js +14 -8
- package/dist/commands/doctor-config-flow.js +11 -9
- package/dist/commands/doctor-legacy-config.js +281 -0
- package/dist/commands/doctor-state-integrity.js +99 -23
- package/dist/commands/doctor-update.js +12 -9
- package/dist/commands/models/list.list-command.js +7 -5
- package/dist/commands/models/set-image.js +2 -21
- package/dist/commands/node-daemon-install-helpers.js +10 -8
- package/dist/commands/onboard-auth.config-minimax.js +54 -80
- package/dist/commands/onboard-auth.config-opencode.js +2 -18
- package/dist/commands/onboard-auth.credentials.js +90 -13
- package/dist/commands/onboard-auth.js +1 -1
- package/dist/commands/onboard-auth.models.js +6 -5
- package/dist/commands/onboard-hooks.js +1 -1
- package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
- package/dist/commands/onboard-provider-auth-flags.js +14 -0
- package/dist/commands/onboard-remote.js +14 -7
- package/dist/commands/onboard.js +11 -13
- package/dist/commands/sandbox-display.js +6 -5
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/commands/status-all/diagnosis.js +14 -10
- package/dist/commands/status-all/format.js +1 -0
- package/dist/commands/status.gateway-probe.js +1 -16
- package/dist/commands/systemd-linger.js +12 -6
- package/dist/config/agent-limits.js +2 -0
- package/dist/config/commands.js +32 -15
- package/dist/config/config-paths.js +9 -11
- package/dist/config/config.js +1 -1
- package/dist/config/defaults.js +22 -2
- package/dist/config/discord-preview-streaming.js +104 -0
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +45 -7
- package/dist/config/includes.js +4 -0
- package/dist/config/io.js +656 -171
- package/dist/config/legacy.migrations.part-1.js +189 -78
- package/dist/config/legacy.shared.js +3 -1
- package/dist/config/merge-patch.js +54 -4
- package/dist/config/prototype-keys.js +4 -0
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.help.js +44 -7
- package/dist/config/schema.js +58 -570
- package/dist/config/schema.labels.js +38 -6
- package/dist/config/sessions/delivery-info.js +10 -3
- package/dist/config/sessions/main-session.js +10 -5
- package/dist/config/sessions/session-file.js +33 -0
- package/dist/config/sessions/session-key.js +10 -5
- package/dist/config/sessions/store.js +1 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.agent-runtime.js +11 -0
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +156 -20
- package/dist/config/zod-schema.providers-core.js +78 -4
- package/dist/config/zod-schema.providers.js +6 -1
- package/dist/config/zod-schema.session.js +41 -2
- package/dist/cron/run-log.js +3 -0
- package/dist/cron/schedule.js +21 -10
- package/dist/cron/service/ops.js +35 -21
- package/dist/cron/service/timer.js +116 -16
- package/dist/cron/stagger.js +3 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/api.js +12 -6
- package/dist/discord/draft-chunking.js +22 -0
- package/dist/discord/draft-stream.js +124 -0
- package/dist/discord/monitor/agent-components.js +1 -1
- package/dist/discord/monitor/commands.js +5 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/discord/monitor/gateway-plugin.js +2 -1
- package/dist/discord/monitor/listeners.js +37 -27
- package/dist/discord/monitor/message-handler.js +4 -1
- package/dist/discord/monitor/message-handler.preflight.js +65 -8
- package/dist/discord/monitor/message-handler.process.js +246 -217
- package/dist/discord/monitor/message-utils.js +143 -6
- package/dist/discord/monitor/model-picker-preferences.js +143 -0
- package/dist/discord/monitor/model-picker.js +651 -0
- package/dist/discord/monitor/native-command.js +573 -16
- package/dist/discord/monitor/provider.allowlist.js +223 -0
- package/dist/discord/monitor/provider.js +275 -347
- package/dist/discord/monitor/provider.lifecycle.js +100 -0
- package/dist/discord/monitor/reply-delivery.js +123 -16
- package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
- package/dist/discord/monitor/thread-bindings.js +4 -0
- package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
- package/dist/discord/monitor/thread-bindings.manager.js +423 -0
- package/dist/discord/monitor/thread-bindings.messages.js +55 -0
- package/dist/discord/monitor/thread-bindings.state.js +358 -0
- package/dist/discord/monitor/thread-bindings.types.js +6 -0
- package/dist/discord/resolve-users.js +33 -21
- package/dist/discord/send.channels.js +15 -0
- package/dist/discord/send.js +3 -2
- package/dist/discord/send.outbound.js +82 -26
- package/dist/discord/send.permissions.js +83 -30
- package/dist/discord/send.reactions.js +8 -4
- package/dist/discord/token.js +10 -5
- package/dist/discord/voice/command.js +263 -0
- package/dist/discord/voice/manager.js +531 -0
- package/dist/gateway/auth.js +72 -13
- package/dist/gateway/call.js +152 -83
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/client.js +28 -4
- package/dist/gateway/config-reload.js +3 -4
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/control-ui.js +219 -96
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks-mapping.js +88 -38
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-auth-helpers.js +3 -2
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/http-endpoint-helpers.js +1 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +74 -9
- package/dist/gateway/node-invoke-system-run-approval.js +14 -35
- package/dist/gateway/node-registry.js +10 -5
- package/dist/gateway/openai-http.js +1 -0
- package/dist/gateway/openresponses-http.js +121 -110
- package/dist/gateway/origin-check.js +1 -18
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +4 -2
- package/dist/gateway/protocol/schema/cron.js +1 -0
- package/dist/gateway/protocol/schema/devices.js +1 -0
- package/dist/gateway/protocol/schema/protocol-schemas.js +4 -1
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema/sessions.js +6 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/role-policy.js +17 -0
- package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
- package/dist/gateway/server/ws-connection/message-handler.js +175 -148
- package/dist/gateway/server-chat.js +83 -25
- package/dist/gateway/server-constants.js +10 -9
- package/dist/gateway/server-cron.js +1 -0
- package/dist/gateway/server-http.js +247 -54
- package/dist/gateway/server-maintenance.js +20 -5
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +465 -130
- package/dist/gateway/server-methods/config.js +193 -152
- package/dist/gateway/server-methods/devices.js +17 -3
- package/dist/gateway/server-methods/models.js +11 -1
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-methods/sessions.js +64 -8
- package/dist/gateway/server-methods/usage.js +162 -75
- package/dist/gateway/server-node-events.js +29 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +39 -13
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-startup-memory.js +17 -11
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/session-utils.fs.js +32 -34
- package/dist/gateway/sessions-resolve.js +17 -5
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.openai-mock.js +14 -7
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/gateway/tools-invoke-http.js +21 -10
- package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
- package/dist/hooks/bundled/command-logger/handler.js +7 -2
- package/dist/hooks/bundled/session-memory/handler.js +170 -38
- package/dist/hooks/frontmatter.js +6 -6
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/hooks/gmail-watcher.js +11 -6
- package/dist/hooks/internal-hooks.js +11 -1
- package/dist/hooks/llm-slug-generator.js +4 -1
- package/dist/hooks/workspace.js +47 -17
- package/dist/imessage/accounts.js +9 -20
- package/dist/imessage/monitor/inbound-processing.js +2 -1
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/archive.js +174 -73
- package/dist/infra/control-ui-assets.js +14 -6
- package/dist/infra/device-pairing.js +204 -144
- package/dist/infra/env.js +10 -5
- package/dist/infra/exec-approvals-allowlist.js +141 -70
- package/dist/infra/exec-approvals-analysis.js +78 -20
- package/dist/infra/exec-approvals.js +5 -17
- package/dist/infra/exec-safe-bin-policy.js +277 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/fs-safe.js +71 -39
- package/dist/infra/gateway-lock.js +6 -2
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/heartbeat-wake.js +6 -12
- package/dist/infra/host-env-security-policy.json +19 -0
- package/dist/infra/host-env-security.js +66 -0
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/net/ssrf.js +131 -38
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/bound-delivery-router.js +88 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/channel-selection.js +12 -6
- package/dist/infra/outbound/envelope.js +1 -1
- package/dist/infra/outbound/format.js +12 -6
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/outbound/payloads.js +14 -7
- package/dist/infra/outbound/session-binding-service.js +123 -0
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/path-guards.js +25 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/provider-usage.fetch.codex.js +7 -15
- package/dist/infra/provider-usage.fetch.gemini.js +14 -11
- package/dist/infra/provider-usage.fetch.shared.js +30 -1
- package/dist/infra/provider-usage.fetch.zai.js +10 -9
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/retry-policy.js +4 -2
- package/dist/infra/retry.js +9 -5
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/session-cost-usage.js +107 -59
- package/dist/infra/session-maintenance-warning.js +3 -1
- package/dist/infra/shell-env.js +98 -34
- package/dist/infra/ssh-config.js +12 -6
- package/dist/infra/system-run-command.js +49 -4
- package/dist/infra/update-channels.js +10 -5
- package/dist/infra/update-startup.js +86 -9
- package/dist/line/accounts.js +5 -7
- package/dist/line/bot-access.js +8 -20
- package/dist/line/bot-handlers.js +3 -1
- package/dist/link-understanding/detect.js +15 -7
- package/dist/media/constants.js +15 -6
- package/dist/media/image-ops.js +7 -0
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/media/local-roots.js +3 -2
- package/dist/media-understanding/apply.js +4 -1
- package/dist/media-understanding/concurrency.js +8 -20
- package/dist/memory/backend-config.js +45 -6
- package/dist/memory/embeddings.js +10 -4
- package/dist/memory/fs-utils.js +23 -0
- package/dist/memory/manager-search.js +12 -6
- package/dist/memory/manager-sync-ops.js +12 -2
- package/dist/memory/qmd-manager.js +466 -53
- package/dist/memory/query-expansion.js +167 -3
- package/dist/memory/status-format.js +10 -5
- package/dist/memory/sync-memory-files.js +1 -1
- package/dist/memory/test-manager.js +8 -0
- package/dist/node-host/invoke-system-run.js +281 -0
- package/dist/node-host/invoke.js +55 -337
- package/dist/pairing/pairing-store.js +22 -0
- package/dist/plugin-sdk/allow-from.js +1 -1
- package/dist/plugin-sdk/command-auth.js +3 -1
- package/dist/plugin-sdk/index.js +6 -3
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugin-sdk/webhook-targets.js +32 -0
- package/dist/plugins/bundled-dir.js +9 -6
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/hooks.js +50 -0
- package/dist/plugins/install.js +28 -16
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/plugins/runtime.js +3 -17
- package/dist/plugins/update.js +78 -12
- package/dist/process/spawn-utils.js +14 -7
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/providers/github-copilot-token.js +11 -6
- package/dist/providers/qwen-portal-oauth.js +14 -6
- package/dist/routing/account-id.js +30 -0
- package/dist/routing/resolve-route.js +3 -7
- package/dist/routing/session-key.js +2 -16
- package/dist/security/audit-channel.js +100 -20
- package/dist/security/audit-extra.async.js +505 -179
- package/dist/security/audit-extra.js +12 -2
- package/dist/security/audit-extra.sync.js +421 -35
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +180 -370
- package/dist/security/dm-policy-shared.js +68 -0
- package/dist/security/external-content.js +46 -14
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/entry-status.js +6 -0
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/node-match.js +11 -4
- package/dist/shared/operator-scope-compat.js +42 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/signal/accounts.js +7 -20
- package/dist/signal/monitor/event-handler.js +3 -1
- package/dist/slack/accounts.js +6 -19
- package/dist/slack/actions.js +11 -3
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/auth.js +1 -1
- package/dist/slack/monitor/message-handler/dispatch.js +50 -29
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/slack/monitor/replies.js +15 -7
- package/dist/slack/monitor/slash.js +22 -13
- package/dist/slack/resolve-channels.js +10 -5
- package/dist/slack/send.js +102 -12
- package/dist/slack/stream-mode.js +10 -0
- package/dist/slack/streaming.js +4 -2
- package/dist/telegram/accounts.js +19 -14
- package/dist/telegram/bot/helpers.js +3 -5
- package/dist/telegram/bot-access.js +35 -36
- package/dist/telegram/bot-handlers.js +120 -148
- package/dist/telegram/bot-message-context.js +68 -9
- package/dist/telegram/bot-message-dispatch.js +477 -210
- package/dist/telegram/bot-native-commands.js +16 -0
- package/dist/telegram/draft-stream.js +44 -8
- package/dist/telegram/inline-buttons.js +5 -15
- package/dist/telegram/monitor.js +11 -7
- package/dist/telegram/network-config.js +19 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/telegram/send.js +3 -2
- package/dist/telegram/sent-message-cache.js +5 -6
- package/dist/telegram/status-reaction-variants.js +208 -0
- package/dist/telegram/sticker-cache.js +11 -9
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/terminal/theme.js +12 -12
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tts/tts.js +80 -567
- package/dist/tui/components/chat-log.js +50 -8
- package/dist/tui/theme/theme.js +10 -12
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui-local-shell.js +16 -6
- package/dist/tui/tui.js +236 -48
- package/dist/utils/account-id.js +2 -4
- package/dist/utils/boolean.js +10 -5
- package/dist/utils/directive-tags.js +11 -0
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/queue-helpers.js +67 -12
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/auto-reply/deliver-reply.js +8 -4
- package/dist/web/auto-reply/mentions.js +10 -5
- package/dist/web/auto-reply/monitor/group-members.js +14 -7
- package/dist/web/auto-reply/monitor/process-message.js +45 -24
- package/dist/web/inbound/access-control.js +5 -2
- package/dist/web/login-qr.js +12 -6
- package/dist/web/media.js +126 -15
- package/docs/tools/slash-commands.md +5 -1
- package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
- package/extensions/bluebubbles/src/monitor.ts +208 -1950
- package/extensions/feishu/src/external-keys.ts +19 -0
- package/extensions/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- package/package.json +1 -1
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { resolveStateDir } from "../../config/paths.js";
|
|
4
|
+
import { loadJsonFile, saveJsonFile } from "../../infra/json-file.js";
|
|
5
|
+
import { normalizeAccountId, resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
|
|
6
|
+
import { DEFAULT_THREAD_BINDING_TTL_MS, RECENT_UNBOUND_WEBHOOK_ECHO_TTL_MS, THREAD_BINDINGS_VERSION, } from "./thread-bindings.types.js";
|
|
7
|
+
// Plugin hooks can load this module via Jiti while core imports it via ESM.
|
|
8
|
+
// Store mutable state on globalThis so both loader paths share one registry.
|
|
9
|
+
const THREAD_BINDINGS_STATE_KEY = "__poolbotDiscordThreadBindingsState";
|
|
10
|
+
function createThreadBindingsGlobalState() {
|
|
11
|
+
return {
|
|
12
|
+
managersByAccountId: new Map(),
|
|
13
|
+
bindingsByThreadId: new Map(),
|
|
14
|
+
bindingsBySessionKey: new Map(),
|
|
15
|
+
tokensByAccountId: new Map(),
|
|
16
|
+
recentUnboundWebhookEchoesByBindingKey: new Map(),
|
|
17
|
+
reusableWebhooksByAccountChannel: new Map(),
|
|
18
|
+
persistByAccountId: new Map(),
|
|
19
|
+
loadedBindings: false,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function resolveThreadBindingsGlobalState() {
|
|
23
|
+
const runtimeGlobal = globalThis;
|
|
24
|
+
if (!runtimeGlobal[THREAD_BINDINGS_STATE_KEY]) {
|
|
25
|
+
runtimeGlobal[THREAD_BINDINGS_STATE_KEY] = createThreadBindingsGlobalState();
|
|
26
|
+
}
|
|
27
|
+
return runtimeGlobal[THREAD_BINDINGS_STATE_KEY];
|
|
28
|
+
}
|
|
29
|
+
const THREAD_BINDINGS_STATE = resolveThreadBindingsGlobalState();
|
|
30
|
+
export const MANAGERS_BY_ACCOUNT_ID = THREAD_BINDINGS_STATE.managersByAccountId;
|
|
31
|
+
export const BINDINGS_BY_THREAD_ID = THREAD_BINDINGS_STATE.bindingsByThreadId;
|
|
32
|
+
export const BINDINGS_BY_SESSION_KEY = THREAD_BINDINGS_STATE.bindingsBySessionKey;
|
|
33
|
+
export const TOKENS_BY_ACCOUNT_ID = THREAD_BINDINGS_STATE.tokensByAccountId;
|
|
34
|
+
export const RECENT_UNBOUND_WEBHOOK_ECHOES_BY_BINDING_KEY = THREAD_BINDINGS_STATE.recentUnboundWebhookEchoesByBindingKey;
|
|
35
|
+
export const REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL = THREAD_BINDINGS_STATE.reusableWebhooksByAccountChannel;
|
|
36
|
+
export const PERSIST_BY_ACCOUNT_ID = THREAD_BINDINGS_STATE.persistByAccountId;
|
|
37
|
+
export function rememberThreadBindingToken(params) {
|
|
38
|
+
const normalizedAccountId = normalizeAccountId(params.accountId);
|
|
39
|
+
const token = params.token?.trim();
|
|
40
|
+
if (!token) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
TOKENS_BY_ACCOUNT_ID.set(normalizedAccountId, token);
|
|
44
|
+
}
|
|
45
|
+
export function forgetThreadBindingToken(accountId) {
|
|
46
|
+
TOKENS_BY_ACCOUNT_ID.delete(normalizeAccountId(accountId));
|
|
47
|
+
}
|
|
48
|
+
export function getThreadBindingToken(accountId) {
|
|
49
|
+
return TOKENS_BY_ACCOUNT_ID.get(normalizeAccountId(accountId));
|
|
50
|
+
}
|
|
51
|
+
export function shouldDefaultPersist() {
|
|
52
|
+
return !(process.env.VITEST || process.env.NODE_ENV === "test");
|
|
53
|
+
}
|
|
54
|
+
export function resolveThreadBindingsPath() {
|
|
55
|
+
return path.join(resolveStateDir(process.env), "discord", "thread-bindings.json");
|
|
56
|
+
}
|
|
57
|
+
export function normalizeTargetKind(raw, targetSessionKey) {
|
|
58
|
+
if (raw === "subagent" || raw === "acp") {
|
|
59
|
+
return raw;
|
|
60
|
+
}
|
|
61
|
+
return targetSessionKey.includes(":subagent:") ? "subagent" : "acp";
|
|
62
|
+
}
|
|
63
|
+
export function normalizeThreadId(raw) {
|
|
64
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
65
|
+
return String(Math.floor(raw));
|
|
66
|
+
}
|
|
67
|
+
if (typeof raw !== "string") {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const trimmed = raw.trim();
|
|
71
|
+
return trimmed ? trimmed : undefined;
|
|
72
|
+
}
|
|
73
|
+
export function toBindingRecordKey(params) {
|
|
74
|
+
return `${normalizeAccountId(params.accountId)}:${params.threadId.trim()}`;
|
|
75
|
+
}
|
|
76
|
+
export function resolveBindingRecordKey(params) {
|
|
77
|
+
const threadId = normalizeThreadId(params.threadId);
|
|
78
|
+
if (!threadId) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return toBindingRecordKey({
|
|
82
|
+
accountId: normalizeAccountId(params.accountId),
|
|
83
|
+
threadId,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function normalizePersistedBinding(threadIdKey, raw) {
|
|
87
|
+
if (!raw || typeof raw !== "object") {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const value = raw;
|
|
91
|
+
const threadId = normalizeThreadId(value.threadId ?? threadIdKey);
|
|
92
|
+
const channelId = typeof value.channelId === "string" ? value.channelId.trim() : "";
|
|
93
|
+
const targetSessionKey = typeof value.targetSessionKey === "string"
|
|
94
|
+
? value.targetSessionKey.trim()
|
|
95
|
+
: typeof value.sessionKey === "string"
|
|
96
|
+
? value.sessionKey.trim()
|
|
97
|
+
: "";
|
|
98
|
+
if (!threadId || !channelId || !targetSessionKey) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const accountId = normalizeAccountId(value.accountId);
|
|
102
|
+
const targetKind = normalizeTargetKind(value.targetKind, targetSessionKey);
|
|
103
|
+
const agentIdRaw = typeof value.agentId === "string" ? value.agentId.trim() : "";
|
|
104
|
+
const agentId = agentIdRaw || resolveAgentIdFromSessionKey(targetSessionKey);
|
|
105
|
+
const label = typeof value.label === "string" ? value.label.trim() || undefined : undefined;
|
|
106
|
+
const webhookId = typeof value.webhookId === "string" ? value.webhookId.trim() || undefined : undefined;
|
|
107
|
+
const webhookToken = typeof value.webhookToken === "string" ? value.webhookToken.trim() || undefined : undefined;
|
|
108
|
+
const boundBy = typeof value.boundBy === "string" ? value.boundBy.trim() || "system" : "system";
|
|
109
|
+
const boundAt = typeof value.boundAt === "number" && Number.isFinite(value.boundAt)
|
|
110
|
+
? Math.floor(value.boundAt)
|
|
111
|
+
: Date.now();
|
|
112
|
+
const expiresAt = typeof value.expiresAt === "number" && Number.isFinite(value.expiresAt)
|
|
113
|
+
? Math.max(0, Math.floor(value.expiresAt))
|
|
114
|
+
: undefined;
|
|
115
|
+
return {
|
|
116
|
+
accountId,
|
|
117
|
+
channelId,
|
|
118
|
+
threadId,
|
|
119
|
+
targetKind,
|
|
120
|
+
targetSessionKey,
|
|
121
|
+
agentId,
|
|
122
|
+
label,
|
|
123
|
+
webhookId,
|
|
124
|
+
webhookToken,
|
|
125
|
+
boundBy,
|
|
126
|
+
boundAt,
|
|
127
|
+
expiresAt,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function normalizeThreadBindingTtlMs(raw) {
|
|
131
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
132
|
+
return DEFAULT_THREAD_BINDING_TTL_MS;
|
|
133
|
+
}
|
|
134
|
+
const ttlMs = Math.floor(raw);
|
|
135
|
+
if (ttlMs < 0) {
|
|
136
|
+
return DEFAULT_THREAD_BINDING_TTL_MS;
|
|
137
|
+
}
|
|
138
|
+
return ttlMs;
|
|
139
|
+
}
|
|
140
|
+
export function resolveThreadBindingExpiresAt(params) {
|
|
141
|
+
if (typeof params.record.expiresAt === "number" && Number.isFinite(params.record.expiresAt)) {
|
|
142
|
+
const explicitExpiresAt = Math.floor(params.record.expiresAt);
|
|
143
|
+
if (explicitExpiresAt <= 0) {
|
|
144
|
+
// 0 is an explicit per-binding TTL disable sentinel.
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
return explicitExpiresAt;
|
|
148
|
+
}
|
|
149
|
+
if (params.sessionTtlMs <= 0) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
const boundAt = Math.floor(params.record.boundAt);
|
|
153
|
+
if (!Number.isFinite(boundAt) || boundAt <= 0) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
return boundAt + params.sessionTtlMs;
|
|
157
|
+
}
|
|
158
|
+
function linkSessionBinding(targetSessionKey, bindingKey) {
|
|
159
|
+
const key = targetSessionKey.trim();
|
|
160
|
+
if (!key) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const threads = BINDINGS_BY_SESSION_KEY.get(key) ?? new Set();
|
|
164
|
+
threads.add(bindingKey);
|
|
165
|
+
BINDINGS_BY_SESSION_KEY.set(key, threads);
|
|
166
|
+
}
|
|
167
|
+
function unlinkSessionBinding(targetSessionKey, bindingKey) {
|
|
168
|
+
const key = targetSessionKey.trim();
|
|
169
|
+
if (!key) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const threads = BINDINGS_BY_SESSION_KEY.get(key);
|
|
173
|
+
if (!threads) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
threads.delete(bindingKey);
|
|
177
|
+
if (threads.size === 0) {
|
|
178
|
+
BINDINGS_BY_SESSION_KEY.delete(key);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
export function toReusableWebhookKey(params) {
|
|
182
|
+
return `${params.accountId.trim().toLowerCase()}:${params.channelId.trim()}`;
|
|
183
|
+
}
|
|
184
|
+
export function rememberReusableWebhook(record) {
|
|
185
|
+
const webhookId = record.webhookId?.trim();
|
|
186
|
+
const webhookToken = record.webhookToken?.trim();
|
|
187
|
+
if (!webhookId || !webhookToken) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const key = toReusableWebhookKey({
|
|
191
|
+
accountId: record.accountId,
|
|
192
|
+
channelId: record.channelId,
|
|
193
|
+
});
|
|
194
|
+
REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL.set(key, { webhookId, webhookToken });
|
|
195
|
+
}
|
|
196
|
+
export function rememberRecentUnboundWebhookEcho(record) {
|
|
197
|
+
const webhookId = record.webhookId?.trim();
|
|
198
|
+
if (!webhookId) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const bindingKey = resolveBindingRecordKey({
|
|
202
|
+
accountId: record.accountId,
|
|
203
|
+
threadId: record.threadId,
|
|
204
|
+
});
|
|
205
|
+
if (!bindingKey) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
RECENT_UNBOUND_WEBHOOK_ECHOES_BY_BINDING_KEY.set(bindingKey, {
|
|
209
|
+
webhookId,
|
|
210
|
+
expiresAt: Date.now() + RECENT_UNBOUND_WEBHOOK_ECHO_TTL_MS,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
function clearRecentUnboundWebhookEcho(bindingKeyRaw) {
|
|
214
|
+
const key = bindingKeyRaw.trim();
|
|
215
|
+
if (!key) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
RECENT_UNBOUND_WEBHOOK_ECHOES_BY_BINDING_KEY.delete(key);
|
|
219
|
+
}
|
|
220
|
+
export function setBindingRecord(record) {
|
|
221
|
+
const bindingKey = toBindingRecordKey({
|
|
222
|
+
accountId: record.accountId,
|
|
223
|
+
threadId: record.threadId,
|
|
224
|
+
});
|
|
225
|
+
const existing = BINDINGS_BY_THREAD_ID.get(bindingKey);
|
|
226
|
+
if (existing) {
|
|
227
|
+
unlinkSessionBinding(existing.targetSessionKey, bindingKey);
|
|
228
|
+
}
|
|
229
|
+
BINDINGS_BY_THREAD_ID.set(bindingKey, record);
|
|
230
|
+
linkSessionBinding(record.targetSessionKey, bindingKey);
|
|
231
|
+
clearRecentUnboundWebhookEcho(bindingKey);
|
|
232
|
+
rememberReusableWebhook(record);
|
|
233
|
+
}
|
|
234
|
+
export function removeBindingRecord(bindingKeyRaw) {
|
|
235
|
+
const key = bindingKeyRaw.trim();
|
|
236
|
+
if (!key) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
const existing = BINDINGS_BY_THREAD_ID.get(key);
|
|
240
|
+
if (!existing) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
BINDINGS_BY_THREAD_ID.delete(key);
|
|
244
|
+
unlinkSessionBinding(existing.targetSessionKey, key);
|
|
245
|
+
return existing;
|
|
246
|
+
}
|
|
247
|
+
export function isRecentlyUnboundThreadWebhookMessage(params) {
|
|
248
|
+
const webhookId = params.webhookId?.trim() || "";
|
|
249
|
+
if (!webhookId) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
const bindingKey = resolveBindingRecordKey({
|
|
253
|
+
accountId: params.accountId,
|
|
254
|
+
threadId: params.threadId,
|
|
255
|
+
});
|
|
256
|
+
if (!bindingKey) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
const suppressed = RECENT_UNBOUND_WEBHOOK_ECHOES_BY_BINDING_KEY.get(bindingKey);
|
|
260
|
+
if (!suppressed) {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
if (suppressed.expiresAt <= Date.now()) {
|
|
264
|
+
RECENT_UNBOUND_WEBHOOK_ECHOES_BY_BINDING_KEY.delete(bindingKey);
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
return suppressed.webhookId === webhookId;
|
|
268
|
+
}
|
|
269
|
+
function shouldPersistAnyBindingState() {
|
|
270
|
+
for (const value of PERSIST_BY_ACCOUNT_ID.values()) {
|
|
271
|
+
if (value) {
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
export function shouldPersistBindingMutations() {
|
|
278
|
+
if (shouldPersistAnyBindingState()) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
return fs.existsSync(resolveThreadBindingsPath());
|
|
282
|
+
}
|
|
283
|
+
export function saveBindingsToDisk(params = {}) {
|
|
284
|
+
if (!params.force && !shouldPersistAnyBindingState()) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const bindings = {};
|
|
288
|
+
for (const [bindingKey, record] of BINDINGS_BY_THREAD_ID.entries()) {
|
|
289
|
+
bindings[bindingKey] = { ...record };
|
|
290
|
+
}
|
|
291
|
+
const payload = {
|
|
292
|
+
version: THREAD_BINDINGS_VERSION,
|
|
293
|
+
bindings,
|
|
294
|
+
};
|
|
295
|
+
saveJsonFile(resolveThreadBindingsPath(), payload);
|
|
296
|
+
}
|
|
297
|
+
export function ensureBindingsLoaded() {
|
|
298
|
+
if (THREAD_BINDINGS_STATE.loadedBindings) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
THREAD_BINDINGS_STATE.loadedBindings = true;
|
|
302
|
+
BINDINGS_BY_THREAD_ID.clear();
|
|
303
|
+
BINDINGS_BY_SESSION_KEY.clear();
|
|
304
|
+
REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL.clear();
|
|
305
|
+
const raw = loadJsonFile(resolveThreadBindingsPath());
|
|
306
|
+
if (!raw || typeof raw !== "object") {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const payload = raw;
|
|
310
|
+
if (payload.version !== 1 || !payload.bindings || typeof payload.bindings !== "object") {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
for (const [threadId, entry] of Object.entries(payload.bindings)) {
|
|
314
|
+
const normalized = normalizePersistedBinding(threadId, entry);
|
|
315
|
+
if (!normalized) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
setBindingRecord(normalized);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
export function resolveBindingIdsForSession(params) {
|
|
322
|
+
const key = params.targetSessionKey.trim();
|
|
323
|
+
if (!key) {
|
|
324
|
+
return [];
|
|
325
|
+
}
|
|
326
|
+
const ids = BINDINGS_BY_SESSION_KEY.get(key);
|
|
327
|
+
if (!ids) {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
const out = [];
|
|
331
|
+
for (const bindingKey of ids.values()) {
|
|
332
|
+
const record = BINDINGS_BY_THREAD_ID.get(bindingKey);
|
|
333
|
+
if (!record) {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (params.accountId && record.accountId !== params.accountId) {
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (params.targetKind && record.targetKind !== params.targetKind) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
out.push(bindingKey);
|
|
343
|
+
}
|
|
344
|
+
return out;
|
|
345
|
+
}
|
|
346
|
+
export function resetThreadBindingsForTests() {
|
|
347
|
+
for (const manager of MANAGERS_BY_ACCOUNT_ID.values()) {
|
|
348
|
+
manager.stop();
|
|
349
|
+
}
|
|
350
|
+
MANAGERS_BY_ACCOUNT_ID.clear();
|
|
351
|
+
BINDINGS_BY_THREAD_ID.clear();
|
|
352
|
+
BINDINGS_BY_SESSION_KEY.clear();
|
|
353
|
+
RECENT_UNBOUND_WEBHOOK_ECHOES_BY_BINDING_KEY.clear();
|
|
354
|
+
REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL.clear();
|
|
355
|
+
TOKENS_BY_ACCOUNT_ID.clear();
|
|
356
|
+
PERSIST_BY_ACCOUNT_ID.clear();
|
|
357
|
+
THREAD_BINDINGS_STATE.loadedBindings = false;
|
|
358
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const THREAD_BINDINGS_VERSION = 1;
|
|
2
|
+
export const THREAD_BINDINGS_SWEEP_INTERVAL_MS = 120_000;
|
|
3
|
+
export const DEFAULT_THREAD_BINDING_TTL_MS = 24 * 60 * 60 * 1000; // 24h
|
|
4
|
+
export const DEFAULT_FAREWELL_TEXT = "Session ended. Messages here will no longer be routed.";
|
|
5
|
+
export const DISCORD_UNKNOWN_CHANNEL_ERROR_CODE = 10_003;
|
|
6
|
+
export const RECENT_UNBOUND_WEBHOOK_ECHO_TTL_MS = 30_000;
|
|
@@ -1,34 +1,31 @@
|
|
|
1
1
|
import { fetchDiscord } from "./api.js";
|
|
2
|
+
import { listGuilds } from "./guilds.js";
|
|
2
3
|
import { normalizeDiscordSlug } from "./monitor/allow-list.js";
|
|
3
4
|
import { normalizeDiscordToken } from "./token.js";
|
|
4
5
|
function parseDiscordUserInput(raw) {
|
|
5
6
|
const trimmed = raw.trim();
|
|
6
|
-
if (!trimmed)
|
|
7
|
+
if (!trimmed) {
|
|
7
8
|
return {};
|
|
9
|
+
}
|
|
8
10
|
const mention = trimmed.match(/^<@!?(\d+)>$/);
|
|
9
|
-
if (mention)
|
|
11
|
+
if (mention) {
|
|
10
12
|
return { userId: mention[1] };
|
|
13
|
+
}
|
|
11
14
|
const prefixed = trimmed.match(/^(?:user:|discord:)?(\d+)$/i);
|
|
12
|
-
if (prefixed)
|
|
15
|
+
if (prefixed) {
|
|
13
16
|
return { userId: prefixed[1] };
|
|
17
|
+
}
|
|
14
18
|
const split = trimmed.includes("/") ? trimmed.split("/") : trimmed.split("#");
|
|
15
19
|
if (split.length >= 2) {
|
|
16
20
|
const guild = split[0]?.trim();
|
|
17
21
|
const user = split.slice(1).join("#").trim();
|
|
18
|
-
if (guild && /^\d+$/.test(guild))
|
|
22
|
+
if (guild && /^\d+$/.test(guild)) {
|
|
19
23
|
return { guildId: guild, userName: user };
|
|
24
|
+
}
|
|
20
25
|
return { guildName: guild, userName: user };
|
|
21
26
|
}
|
|
22
27
|
return { userName: trimmed.replace(/^@/, "") };
|
|
23
28
|
}
|
|
24
|
-
async function listGuilds(token, fetcher) {
|
|
25
|
-
const raw = await fetchDiscord("/users/@me/guilds", token, fetcher);
|
|
26
|
-
return raw.map((guild) => ({
|
|
27
|
-
id: guild.id,
|
|
28
|
-
name: guild.name,
|
|
29
|
-
slug: normalizeDiscordSlug(guild.name),
|
|
30
|
-
}));
|
|
31
|
-
}
|
|
32
29
|
function scoreDiscordMember(member, query) {
|
|
33
30
|
const q = query.toLowerCase();
|
|
34
31
|
const user = member.user;
|
|
@@ -36,23 +33,36 @@ function scoreDiscordMember(member, query) {
|
|
|
36
33
|
.map((value) => value?.toLowerCase())
|
|
37
34
|
.filter(Boolean);
|
|
38
35
|
let score = 0;
|
|
39
|
-
if (candidates.some((value) => value === q))
|
|
36
|
+
if (candidates.some((value) => value === q)) {
|
|
40
37
|
score += 3;
|
|
41
|
-
|
|
38
|
+
}
|
|
39
|
+
if (candidates.some((value) => value?.includes(q))) {
|
|
42
40
|
score += 1;
|
|
43
|
-
|
|
41
|
+
}
|
|
42
|
+
if (!user.bot) {
|
|
44
43
|
score += 1;
|
|
44
|
+
}
|
|
45
45
|
return score;
|
|
46
46
|
}
|
|
47
47
|
export async function resolveDiscordUserAllowlist(params) {
|
|
48
48
|
const token = normalizeDiscordToken(params.token);
|
|
49
|
-
if (!token)
|
|
49
|
+
if (!token) {
|
|
50
50
|
return params.entries.map((input) => ({
|
|
51
51
|
input,
|
|
52
52
|
resolved: false,
|
|
53
53
|
}));
|
|
54
|
+
}
|
|
54
55
|
const fetcher = params.fetcher ?? fetch;
|
|
55
|
-
|
|
56
|
+
// Lazy-load guilds: only fetch when an entry actually needs username search.
|
|
57
|
+
// This prevents listGuilds() failures (permissions, network) from blocking
|
|
58
|
+
// resolution of plain user-id entries that don't need guild data at all.
|
|
59
|
+
let guilds = null;
|
|
60
|
+
const getGuilds = async () => {
|
|
61
|
+
if (!guilds) {
|
|
62
|
+
guilds = await listGuilds(token, fetcher);
|
|
63
|
+
}
|
|
64
|
+
return guilds;
|
|
65
|
+
};
|
|
56
66
|
const results = [];
|
|
57
67
|
for (const input of params.entries) {
|
|
58
68
|
const parsed = parseDiscordUserInput(input);
|
|
@@ -70,11 +80,12 @@ export async function resolveDiscordUserAllowlist(params) {
|
|
|
70
80
|
continue;
|
|
71
81
|
}
|
|
72
82
|
const guildName = parsed.guildName?.trim();
|
|
83
|
+
const allGuilds = await getGuilds();
|
|
73
84
|
const guildList = parsed.guildId
|
|
74
|
-
?
|
|
85
|
+
? allGuilds.filter((g) => g.id === parsed.guildId)
|
|
75
86
|
: guildName
|
|
76
|
-
?
|
|
77
|
-
:
|
|
87
|
+
? allGuilds.filter((g) => g.slug === normalizeDiscordSlug(guildName))
|
|
88
|
+
: allGuilds;
|
|
78
89
|
let best = null;
|
|
79
90
|
let matches = 0;
|
|
80
91
|
for (const guild of guildList) {
|
|
@@ -85,8 +96,9 @@ export async function resolveDiscordUserAllowlist(params) {
|
|
|
85
96
|
const members = await fetchDiscord(`/guilds/${guild.id}/members/search?${paramsObj.toString()}`, token, fetcher);
|
|
86
97
|
for (const member of members) {
|
|
87
98
|
const score = scoreDiscordMember(member, query);
|
|
88
|
-
if (score === 0)
|
|
99
|
+
if (score === 0) {
|
|
89
100
|
continue;
|
|
101
|
+
}
|
|
90
102
|
matches += 1;
|
|
91
103
|
if (!best || score > best.score) {
|
|
92
104
|
best = { member, guild, score };
|
|
@@ -34,6 +34,21 @@ export async function editChannelDiscord(payload, opts = {}) {
|
|
|
34
34
|
body.nsfw = payload.nsfw;
|
|
35
35
|
if (payload.rateLimitPerUser !== undefined)
|
|
36
36
|
body.rate_limit_per_user = payload.rateLimitPerUser;
|
|
37
|
+
if (payload.archived !== undefined)
|
|
38
|
+
body.archived = payload.archived;
|
|
39
|
+
if (payload.locked !== undefined)
|
|
40
|
+
body.locked = payload.locked;
|
|
41
|
+
if (payload.autoArchiveDuration !== undefined)
|
|
42
|
+
body.auto_archive_duration = payload.autoArchiveDuration;
|
|
43
|
+
if (payload.availableTags !== undefined) {
|
|
44
|
+
body.available_tags = payload.availableTags.map((t) => ({
|
|
45
|
+
...(t.id !== undefined && { id: t.id }),
|
|
46
|
+
name: t.name,
|
|
47
|
+
...(t.moderated !== undefined && { moderated: t.moderated }),
|
|
48
|
+
...(t.emoji_id !== undefined && { emoji_id: t.emoji_id }),
|
|
49
|
+
...(t.emoji_name !== undefined && { emoji_name: t.emoji_name }),
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
37
52
|
return (await rest.patch(Routes.channel(payload.channelId), {
|
|
38
53
|
body,
|
|
39
54
|
}));
|
package/dist/discord/send.js
CHANGED
|
@@ -2,7 +2,8 @@ export { createChannelDiscord, deleteChannelDiscord, editChannelDiscord, moveCha
|
|
|
2
2
|
export { listGuildEmojisDiscord, uploadEmojiDiscord, uploadStickerDiscord, } from "./send.emojis-stickers.js";
|
|
3
3
|
export { addRoleDiscord, banMemberDiscord, createScheduledEventDiscord, fetchChannelInfoDiscord, fetchMemberInfoDiscord, fetchRoleInfoDiscord, fetchVoiceStatusDiscord, kickMemberDiscord, listGuildChannelsDiscord, listScheduledEventsDiscord, removeRoleDiscord, timeoutMemberDiscord, } from "./send.guild.js";
|
|
4
4
|
export { createThreadDiscord, deleteMessageDiscord, editMessageDiscord, fetchMessageDiscord, listPinsDiscord, listThreadsDiscord, pinMessageDiscord, readMessagesDiscord, searchMessagesDiscord, unpinMessageDiscord, } from "./send.messages.js";
|
|
5
|
-
export { sendMessageDiscord, sendPollDiscord, sendStickerDiscord, sendVoiceMessageDiscord, } from "./send.outbound.js";
|
|
5
|
+
export { sendMessageDiscord, sendPollDiscord, sendStickerDiscord, sendWebhookMessageDiscord, sendVoiceMessageDiscord, } from "./send.outbound.js";
|
|
6
6
|
export { sendDiscordComponentMessage } from "./send.components.js";
|
|
7
|
-
export { fetchChannelPermissionsDiscord,
|
|
7
|
+
export { fetchChannelPermissionsDiscord, hasAllGuildPermissionsDiscord, hasAnyGuildPermissionDiscord, fetchMemberGuildPermissionsDiscord, } from "./send.permissions.js";
|
|
8
|
+
export { fetchReactionsDiscord, reactMessageDiscord, removeOwnReactionsDiscord, removeReactionDiscord, } from "./send.reactions.js";
|
|
8
9
|
export { DiscordSendError } from "./send.types.js";
|
|
@@ -29,6 +29,19 @@ function deriveForumThreadName(text) {
|
|
|
29
29
|
function isForumLikeType(channelType) {
|
|
30
30
|
return channelType === ChannelType.GuildForum || channelType === ChannelType.GuildMedia;
|
|
31
31
|
}
|
|
32
|
+
function toDiscordSendResult(result, fallbackChannelId) {
|
|
33
|
+
return {
|
|
34
|
+
messageId: result.id ? String(result.id) : "unknown",
|
|
35
|
+
channelId: String(result.channel_id ?? fallbackChannelId),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async function resolveDiscordSendTarget(to, opts) {
|
|
39
|
+
const cfg = loadConfig();
|
|
40
|
+
const { rest, request } = createDiscordClient(opts, cfg);
|
|
41
|
+
const recipient = await parseAndResolveRecipient(to, opts.accountId);
|
|
42
|
+
const { channelId } = await resolveChannelId(rest, recipient, request);
|
|
43
|
+
return { rest, request, channelId };
|
|
44
|
+
}
|
|
32
45
|
export async function sendMessageDiscord(to, text, opts = {}) {
|
|
33
46
|
const cfg = loadConfig();
|
|
34
47
|
const accountInfo = resolveDiscordAccount({
|
|
@@ -122,10 +135,10 @@ export async function sendMessageDiscord(to, text, opts = {}) {
|
|
|
122
135
|
accountId: accountInfo.accountId,
|
|
123
136
|
direction: "outbound",
|
|
124
137
|
});
|
|
125
|
-
return {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
};
|
|
138
|
+
return toDiscordSendResult({
|
|
139
|
+
id: messageId,
|
|
140
|
+
channel_id: resultChannelId,
|
|
141
|
+
}, channelId);
|
|
129
142
|
}
|
|
130
143
|
let result;
|
|
131
144
|
try {
|
|
@@ -149,16 +162,71 @@ export async function sendMessageDiscord(to, text, opts = {}) {
|
|
|
149
162
|
accountId: accountInfo.accountId,
|
|
150
163
|
direction: "outbound",
|
|
151
164
|
});
|
|
165
|
+
return toDiscordSendResult(result, channelId);
|
|
166
|
+
}
|
|
167
|
+
function resolveWebhookExecutionUrl(params) {
|
|
168
|
+
const baseUrl = new URL(`https://discord.com/api/v10/webhooks/${encodeURIComponent(params.webhookId)}/${encodeURIComponent(params.webhookToken)}`);
|
|
169
|
+
baseUrl.searchParams.set("wait", params.wait === false ? "false" : "true");
|
|
170
|
+
if (params.threadId !== undefined && params.threadId !== null && params.threadId !== "") {
|
|
171
|
+
baseUrl.searchParams.set("thread_id", String(params.threadId));
|
|
172
|
+
}
|
|
173
|
+
return baseUrl.toString();
|
|
174
|
+
}
|
|
175
|
+
export async function sendWebhookMessageDiscord(text, opts) {
|
|
176
|
+
const webhookId = opts.webhookId.trim();
|
|
177
|
+
const webhookToken = opts.webhookToken.trim();
|
|
178
|
+
if (!webhookId || !webhookToken) {
|
|
179
|
+
throw new Error("Discord webhook id/token are required");
|
|
180
|
+
}
|
|
181
|
+
const replyTo = typeof opts.replyTo === "string" ? opts.replyTo.trim() : "";
|
|
182
|
+
const messageReference = replyTo ? { message_id: replyTo, fail_if_not_exists: false } : undefined;
|
|
183
|
+
const response = await fetch(resolveWebhookExecutionUrl({
|
|
184
|
+
webhookId,
|
|
185
|
+
webhookToken,
|
|
186
|
+
threadId: opts.threadId,
|
|
187
|
+
wait: opts.wait,
|
|
188
|
+
}), {
|
|
189
|
+
method: "POST",
|
|
190
|
+
headers: {
|
|
191
|
+
"content-type": "application/json",
|
|
192
|
+
},
|
|
193
|
+
body: JSON.stringify({
|
|
194
|
+
content: text,
|
|
195
|
+
username: opts.username?.trim() || undefined,
|
|
196
|
+
avatar_url: opts.avatarUrl?.trim() || undefined,
|
|
197
|
+
...(messageReference ? { message_reference: messageReference } : {}),
|
|
198
|
+
}),
|
|
199
|
+
});
|
|
200
|
+
if (!response.ok) {
|
|
201
|
+
const raw = await response.text().catch(() => "");
|
|
202
|
+
throw new Error(`Discord webhook send failed (${response.status}${raw ? `: ${raw.slice(0, 200)}` : ""})`);
|
|
203
|
+
}
|
|
204
|
+
const payload = (await response.json().catch(() => ({})));
|
|
205
|
+
try {
|
|
206
|
+
const account = resolveDiscordAccount({
|
|
207
|
+
cfg: loadConfig(),
|
|
208
|
+
accountId: opts.accountId,
|
|
209
|
+
});
|
|
210
|
+
recordChannelActivity({
|
|
211
|
+
channel: "discord",
|
|
212
|
+
accountId: account.accountId,
|
|
213
|
+
direction: "outbound",
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// Best-effort telemetry only.
|
|
218
|
+
}
|
|
152
219
|
return {
|
|
153
|
-
messageId:
|
|
154
|
-
channelId:
|
|
220
|
+
messageId: payload.id ? String(payload.id) : "unknown",
|
|
221
|
+
channelId: payload.channel_id
|
|
222
|
+
? String(payload.channel_id)
|
|
223
|
+
: opts.threadId
|
|
224
|
+
? String(opts.threadId)
|
|
225
|
+
: "",
|
|
155
226
|
};
|
|
156
227
|
}
|
|
157
228
|
export async function sendStickerDiscord(to, stickerIds, opts = {}) {
|
|
158
|
-
const
|
|
159
|
-
const { rest, request } = createDiscordClient(opts, cfg);
|
|
160
|
-
const recipient = await parseAndResolveRecipient(to, opts.accountId);
|
|
161
|
-
const { channelId } = await resolveChannelId(rest, recipient, request);
|
|
229
|
+
const { rest, request, channelId } = await resolveDiscordSendTarget(to, opts);
|
|
162
230
|
const content = opts.content?.trim();
|
|
163
231
|
const stickers = normalizeStickerIds(stickerIds);
|
|
164
232
|
const res = (await request(() => rest.post(Routes.channelMessages(channelId), {
|
|
@@ -167,16 +235,10 @@ export async function sendStickerDiscord(to, stickerIds, opts = {}) {
|
|
|
167
235
|
sticker_ids: stickers,
|
|
168
236
|
},
|
|
169
237
|
}), "sticker"));
|
|
170
|
-
return
|
|
171
|
-
messageId: res.id ? String(res.id) : "unknown",
|
|
172
|
-
channelId: String(res.channel_id ?? channelId),
|
|
173
|
-
};
|
|
238
|
+
return toDiscordSendResult(res, channelId);
|
|
174
239
|
}
|
|
175
240
|
export async function sendPollDiscord(to, poll, opts = {}) {
|
|
176
|
-
const
|
|
177
|
-
const { rest, request } = createDiscordClient(opts, cfg);
|
|
178
|
-
const recipient = await parseAndResolveRecipient(to, opts.accountId);
|
|
179
|
-
const { channelId } = await resolveChannelId(rest, recipient, request);
|
|
241
|
+
const { rest, request, channelId } = await resolveDiscordSendTarget(to, opts);
|
|
180
242
|
const content = opts.content?.trim();
|
|
181
243
|
if (poll.durationSeconds !== undefined) {
|
|
182
244
|
throw new Error("Discord polls do not support durationSeconds; use durationHours");
|
|
@@ -190,10 +252,7 @@ export async function sendPollDiscord(to, poll, opts = {}) {
|
|
|
190
252
|
...(flags ? { flags } : {}),
|
|
191
253
|
},
|
|
192
254
|
}), "poll"));
|
|
193
|
-
return
|
|
194
|
-
messageId: res.id ? String(res.id) : "unknown",
|
|
195
|
-
channelId: String(res.channel_id ?? channelId),
|
|
196
|
-
};
|
|
255
|
+
return toDiscordSendResult(res, channelId);
|
|
197
256
|
}
|
|
198
257
|
async function materializeVoiceMessageInput(mediaUrl) {
|
|
199
258
|
// Security: reuse the standard media loader so we apply SSRF guards + allowed-local-root checks.
|
|
@@ -251,10 +310,7 @@ export async function sendVoiceMessageDiscord(to, audioPath, opts = {}) {
|
|
|
251
310
|
accountId: accountInfo.accountId,
|
|
252
311
|
direction: "outbound",
|
|
253
312
|
});
|
|
254
|
-
return
|
|
255
|
-
messageId: result.id ? String(result.id) : "unknown",
|
|
256
|
-
channelId: String(result.channel_id ?? channelId),
|
|
257
|
-
};
|
|
313
|
+
return toDiscordSendResult(result, channelId);
|
|
258
314
|
}
|
|
259
315
|
catch (err) {
|
|
260
316
|
if (channelId && rest && token) {
|