@poolzin/pool-bot 2026.1.39 → 2026.2.1
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/assets/chrome-extension/README.md +3 -3
- package/assets/chrome-extension/background.js +5 -5
- package/assets/chrome-extension/manifest.json +3 -3
- package/assets/chrome-extension/options.html +4 -4
- package/assets/chrome-extension/options.js +1 -1
- package/dist/acp/client.js +3 -3
- package/dist/acp/types.js +1 -1
- package/dist/agents/agent-paths.js +3 -3
- package/dist/agents/auth-profiles/paths.js +3 -3
- package/dist/agents/bash-tools.exec.js +76 -25
- package/dist/agents/cli-runner/helpers.js +10 -12
- package/dist/agents/cli-runner.js +2 -2
- package/dist/agents/cloudflare-ai-gateway.js +31 -0
- package/dist/agents/compaction.js +16 -2
- package/dist/agents/context-window-guard.js +13 -10
- package/dist/agents/context.js +4 -4
- package/dist/agents/docs-path.js +1 -1
- package/dist/agents/identity.js +47 -7
- package/dist/agents/memory-search.js +25 -8
- package/dist/agents/minimax-vlm.js +1 -1
- package/dist/agents/model-auth.js +12 -1
- package/dist/agents/model-catalog.js +4 -4
- package/dist/agents/model-selection.js +31 -4
- package/dist/agents/models-config.js +3 -3
- package/dist/agents/models-config.providers.js +147 -39
- package/dist/agents/pi-embedded-block-chunker.js +117 -42
- package/dist/agents/pi-embedded-helpers/errors.js +183 -78
- package/dist/agents/pi-embedded-helpers/openai.js +1 -1
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-runner/compact.js +9 -8
- package/dist/agents/pi-embedded-runner/model.js +63 -4
- package/dist/agents/pi-embedded-runner/run/attempt.js +27 -17
- package/dist/agents/pi-embedded-runner/run.js +203 -50
- package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
- package/dist/agents/pi-embedded-runner/tool-result-truncation.js +275 -0
- package/dist/agents/pi-embedded-runner/utils.js +1 -1
- package/dist/agents/pi-embedded-subscribe.js +118 -29
- package/dist/agents/pi-model-discovery.js +10 -0
- package/dist/agents/pi-tool-definition-adapter.js +50 -9
- package/dist/agents/pi-tools.before-tool-call.js +67 -0
- package/dist/agents/pi-tools.js +20 -10
- package/dist/agents/pi-tools.read.js +2 -2
- package/dist/agents/poolbot-tools.js +15 -10
- package/dist/agents/sandbox-paths.js +31 -0
- package/dist/agents/session-file-repair.js +83 -0
- package/dist/agents/session-tool-result-guard.js +94 -15
- package/dist/agents/session-transcript-repair.js +68 -0
- package/dist/agents/shell-utils.js +51 -0
- package/dist/agents/skills/bundled-context.js +23 -0
- package/dist/agents/skills/bundled-dir.js +41 -7
- package/dist/agents/skills/frontmatter.js +1 -1
- package/dist/agents/skills/workspace.js +2 -2
- package/dist/agents/skills-install.js +60 -23
- package/dist/agents/subagent-announce.js +79 -34
- package/dist/agents/system-prompt.js +28 -4
- package/dist/agents/together-models.js +127 -0
- package/dist/agents/tool-images.js +1 -1
- package/dist/agents/tool-policy.conformance.js +14 -0
- package/dist/agents/tool-policy.js +25 -1
- package/dist/agents/tools/browser-tool.js +3 -3
- package/dist/agents/tools/cron-tool.js +166 -19
- package/dist/agents/tools/discord-actions-presence.js +78 -0
- package/dist/agents/tools/image-tool.js +2 -2
- package/dist/agents/tools/memory-tool.js +93 -5
- package/dist/agents/tools/message-tool.js +56 -2
- package/dist/agents/tools/sessions-history-tool.js +69 -1
- package/dist/agents/tools/web-search.js +211 -42
- package/dist/agents/usage.js +23 -1
- package/dist/agents/workspace-run.js +67 -0
- package/dist/agents/workspace-templates.js +44 -0
- package/dist/auto-reply/command-auth.js +121 -6
- package/dist/auto-reply/commands-registry.data.js +1 -1
- package/dist/auto-reply/envelope.js +50 -72
- package/dist/auto-reply/reply/commands-compact.js +1 -0
- package/dist/auto-reply/reply/commands-context-report.js +3 -2
- package/dist/auto-reply/reply/commands-context.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +107 -60
- package/dist/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/reply/commands-session.js +2 -2
- package/dist/auto-reply/reply/get-reply-run.js +16 -5
- package/dist/auto-reply/reply/groups.js +1 -1
- package/dist/auto-reply/reply/inbound-context.js +9 -1
- package/dist/auto-reply/reply/inbound-meta.js +130 -0
- package/dist/auto-reply/reply/model-selection.js +3 -3
- package/dist/auto-reply/reply/untrusted-context.js +15 -0
- package/dist/auto-reply/status.js +1 -1
- package/dist/auto-reply/thinking.js +88 -43
- package/dist/browser/bridge-server.js +13 -0
- package/dist/browser/cdp.helpers.js +38 -24
- package/dist/browser/client-fetch.js +51 -8
- package/dist/browser/config.js +2 -11
- package/dist/browser/extension-relay.js +104 -43
- package/dist/browser/pw-ai.js +1 -1
- package/dist/browser/pw-session.js +143 -8
- package/dist/browser/pw-tools-core.interactions.js +125 -27
- package/dist/browser/pw-tools-core.responses.js +1 -1
- package/dist/browser/pw-tools-core.state.js +1 -1
- package/dist/browser/routes/agent.act.js +86 -41
- package/dist/browser/routes/dispatcher.js +4 -4
- package/dist/browser/screenshot.js +1 -1
- package/dist/browser/server-context.js +2 -2
- package/dist/browser/server.js +13 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui.js +3 -3
- package/dist/channels/plugins/catalog.js +2 -2
- package/dist/channels/plugins/onboarding/imessage.js +1 -1
- package/dist/channels/plugins/onboarding/signal.js +1 -1
- package/dist/channels/plugins/onboarding/slack.js +4 -4
- package/dist/channels/plugins/onboarding/whatsapp.js +3 -3
- package/dist/channels/plugins/pairing-message.js +1 -1
- package/dist/channels/reply-prefix.js +8 -1
- package/dist/cli/browser-cli-extension.js +2 -2
- package/dist/cli/cron-cli/register.cron-add.js +61 -40
- package/dist/cli/cron-cli/register.cron-edit.js +60 -34
- package/dist/cli/cron-cli/shared.js +56 -41
- package/dist/cli/dns-cli.js +26 -14
- package/dist/cli/docs-cli.js +1 -1
- package/dist/cli/gateway-cli/dev.js +1 -1
- package/dist/cli/gateway-cli/register.js +37 -19
- package/dist/cli/memory-cli.js +30 -20
- package/dist/cli/nodes-cli/register.canvas.js +1 -1
- package/dist/cli/parse-bytes.js +37 -0
- package/dist/cli/plugins-cli.js +1 -1
- package/dist/cli/run-main.js +2 -2
- package/dist/cli/security-cli.js +1 -1
- package/dist/cli/tagline.js +1 -1
- package/dist/cli/update-cli.js +173 -52
- package/dist/cli/webhooks-cli.js +5 -5
- package/dist/commands/agent.js +1 -0
- package/dist/commands/agents.commands.add.js +1 -1
- package/dist/commands/auth-choice.apply.api-providers.js +305 -17
- package/dist/commands/auth-choice.apply.js +4 -1
- package/dist/commands/auth-choice.apply.plugin-provider.js +2 -2
- package/dist/commands/auth-choice.apply.xai.js +63 -0
- package/dist/commands/auth-choice.preferred-provider.js +7 -1
- package/dist/commands/configure.wizard.js +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/docs.js +1 -1
- package/dist/commands/doctor-config-flow.js +61 -5
- package/dist/commands/doctor-gateway-services.js +3 -3
- package/dist/commands/doctor-state-migrations.js +1 -1
- package/dist/commands/doctor-update.js +3 -3
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/health.js +1 -1
- package/dist/commands/model-allowlist.js +29 -0
- package/dist/commands/model-picker.js +2 -1
- package/dist/commands/models/list.probe.js +2 -2
- package/dist/commands/models/list.registry.js +4 -4
- package/dist/commands/models/list.status-command.js +44 -24
- package/dist/commands/models/shared.js +15 -0
- package/dist/commands/onboard-auth.config-core.js +366 -28
- package/dist/commands/onboard-auth.credentials.js +71 -9
- package/dist/commands/onboard-auth.js +3 -3
- package/dist/commands/onboard-auth.models.js +26 -24
- package/dist/commands/onboard-custom.js +384 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +146 -9
- package/dist/commands/onboard-skills.js +63 -38
- package/dist/commands/openai-model-default.js +41 -0
- package/dist/commands/status-all/report-lines.js +1 -1
- package/dist/commands/status.command.js +1 -1
- package/dist/commands/uninstall.js +3 -3
- package/dist/compat/legacy-names.js +1 -1
- package/dist/config/defaults.js +3 -2
- package/dist/config/io.js +3 -3
- package/dist/config/paths.js +136 -35
- package/dist/config/plugin-auto-enable.js +21 -5
- package/dist/config/redact-snapshot.js +153 -0
- package/dist/config/schema.field-metadata.js +590 -0
- package/dist/config/schema.js +3 -3
- package/dist/config/sessions/store.js +291 -23
- package/dist/config/types.memory.js +1 -0
- package/dist/config/version.js +4 -4
- package/dist/config/zod-schema.agent-defaults.js +3 -0
- package/dist/config/zod-schema.agent-runtime.js +13 -2
- package/dist/config/zod-schema.providers-core.js +142 -0
- package/dist/config/zod-schema.session.js +3 -0
- package/dist/cron/delivery.js +57 -0
- package/dist/cron/isolated-agent/delivery-target.js +18 -3
- package/dist/cron/isolated-agent/helpers.js +22 -5
- package/dist/cron/isolated-agent/run.js +171 -63
- package/dist/cron/isolated-agent/session.js +2 -0
- package/dist/cron/normalize.js +356 -28
- package/dist/cron/parse.js +10 -5
- package/dist/cron/run-log.js +35 -10
- package/dist/cron/schedule.js +41 -6
- package/dist/cron/service/jobs.js +208 -35
- package/dist/cron/service/ops.js +72 -16
- package/dist/cron/service/state.js +2 -0
- package/dist/cron/service/store.js +386 -14
- package/dist/cron/service/timer.js +390 -147
- package/dist/cron/session-reaper.js +86 -0
- package/dist/cron/store.js +23 -8
- package/dist/cron/validate-timestamp.js +43 -0
- package/dist/daemon/constants.js +7 -7
- package/dist/daemon/inspect.js +6 -6
- package/dist/daemon/systemd-unit.js +1 -1
- package/dist/discord/monitor/agent-components.js +438 -0
- package/dist/discord/monitor/allow-list.js +28 -5
- package/dist/discord/monitor/gateway-registry.js +29 -0
- package/dist/discord/monitor/native-command.js +44 -23
- package/dist/discord/monitor/sender-identity.js +45 -0
- package/dist/discord/pluralkit.js +27 -0
- package/dist/discord/send.outbound.js +92 -5
- package/dist/discord/send.shared.js +60 -23
- package/dist/discord/targets.js +84 -1
- package/dist/entry.js +15 -9
- package/dist/extensionAPI.js +8 -0
- package/dist/gateway/control-ui.js +8 -1
- package/dist/gateway/hooks-mapping.js +3 -0
- package/dist/gateway/hooks.js +65 -0
- package/dist/gateway/live-image-probe.js +1 -66
- package/dist/gateway/net.js +96 -31
- package/dist/gateway/node-command-policy.js +50 -15
- package/dist/gateway/openai-http.js +2 -2
- package/dist/gateway/openresponses-http.js +4 -4
- package/dist/gateway/origin-check.js +56 -0
- package/dist/gateway/protocol/client-info.js +9 -0
- package/dist/gateway/protocol/index.js +9 -2
- package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
- package/dist/gateway/protocol/schema/cron.js +22 -10
- package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
- package/dist/gateway/protocol/schema/sessions.js +12 -0
- package/dist/gateway/server/hooks.js +1 -1
- package/dist/gateway/server-broadcast.js +26 -9
- package/dist/gateway/server-chat.js +112 -23
- package/dist/gateway/server-discovery-runtime.js +10 -2
- package/dist/gateway/server-discovery.js +2 -2
- package/dist/gateway/server-http.js +110 -12
- package/dist/gateway/server-methods/agent-timestamp.js +60 -0
- package/dist/gateway/server-methods/agents.js +321 -2
- package/dist/gateway/server-methods/usage.js +559 -16
- package/dist/gateway/server-runtime-state.js +22 -8
- package/dist/gateway/server-startup-memory.js +16 -0
- package/dist/gateway/server.impl.js +7 -3
- package/dist/gateway/session-utils.fs.js +23 -25
- package/dist/gateway/session-utils.js +20 -10
- package/dist/gateway/sessions-patch.js +7 -22
- package/dist/gateway/test-helpers.server.js +35 -2
- package/dist/hooks/frontmatter.js +1 -1
- package/dist/hooks/hooks-status.js +1 -1
- package/dist/hooks/install.js +2 -2
- package/dist/hooks/loader.js +1 -1
- package/dist/hooks/workspace.js +3 -3
- package/dist/imessage/constants.js +2 -0
- package/dist/imessage/monitor/deliver.js +4 -1
- package/dist/imessage/monitor/monitor-provider.js +51 -1
- package/dist/index.js +2 -2
- package/dist/infra/bonjour-discovery.js +131 -70
- package/dist/infra/bonjour.js +3 -3
- package/dist/infra/control-ui-assets.js +134 -12
- package/dist/infra/errors.js +12 -0
- package/dist/infra/exec-approvals.js +266 -57
- package/dist/infra/format-time/format-datetime.js +79 -0
- package/dist/infra/format-time/format-duration.js +81 -0
- package/dist/infra/format-time/format-relative.js +80 -0
- package/dist/infra/heartbeat-runner.js +140 -49
- package/dist/infra/home-dir.js +54 -0
- package/dist/infra/net/fetch-guard.js +122 -0
- package/dist/infra/net/ssrf.js +65 -29
- package/dist/infra/outbound/abort.js +14 -0
- package/dist/infra/outbound/message-action-runner.js +77 -13
- package/dist/infra/outbound/outbound-session.js +143 -37
- package/dist/infra/path-env.js +3 -3
- package/dist/infra/poolbot-root.js +43 -1
- package/dist/infra/provider-usage.fetch.minimax.js +1 -1
- package/dist/infra/restart.js +1 -1
- package/dist/infra/session-cost-usage.js +631 -41
- package/dist/infra/state-migrations.js +317 -47
- package/dist/infra/tailscale.js +1 -1
- package/dist/infra/update-global.js +35 -0
- package/dist/infra/update-runner.js +149 -43
- package/dist/infra/warning-filter.js +65 -0
- package/dist/infra/widearea-dns.js +30 -9
- package/dist/logging/redact-identifier.js +12 -0
- package/dist/macos/relay.js +2 -2
- package/dist/media/fetch.js +81 -58
- package/dist/media/input-files.js +1 -1
- package/dist/media/mime.js +4 -0
- package/dist/media/png-encode.js +74 -0
- package/dist/media-understanding/apply.js +403 -3
- package/dist/media-understanding/attachments.js +38 -27
- package/dist/media-understanding/defaults.js +16 -0
- package/dist/media-understanding/providers/deepgram/audio.js +22 -14
- package/dist/media-understanding/providers/google/audio.js +24 -17
- package/dist/media-understanding/providers/google/video.js +24 -17
- package/dist/media-understanding/providers/image.js +4 -4
- package/dist/media-understanding/providers/index.js +4 -1
- package/dist/media-understanding/providers/openai/audio.js +22 -14
- package/dist/media-understanding/providers/shared.js +16 -11
- package/dist/media-understanding/providers/zai/index.js +6 -0
- package/dist/media-understanding/runner.js +158 -90
- package/dist/memory/backend-config.js +207 -0
- package/dist/memory/batch-voyage.js +277 -0
- package/dist/memory/embeddings-voyage.js +75 -0
- package/dist/memory/embeddings.js +29 -17
- package/dist/memory/internal.js +101 -18
- package/dist/memory/manager.js +155 -48
- package/dist/memory/search-manager.js +173 -0
- package/dist/memory/session-files.js +9 -3
- package/dist/memory/types.js +1 -0
- package/dist/node-host/runner.js +36 -26
- package/dist/node-host/with-timeout.js +27 -0
- package/dist/pairing/pairing-messages.js +1 -1
- package/dist/plugins/commands.js +5 -1
- package/dist/plugins/config-state.js +86 -7
- package/dist/plugins/discovery.js +1 -1
- package/dist/plugins/install.js +2 -2
- package/dist/plugins/source-display.js +51 -0
- package/dist/plugins/update.js +1 -1
- package/dist/process/exec.js +20 -2
- package/dist/routing/resolve-route.js +12 -0
- package/dist/routing/session-key.js +15 -0
- package/dist/runtime.js +2 -0
- package/dist/security/audit-extra.async.js +601 -0
- package/dist/security/audit-extra.js +2 -830
- package/dist/security/audit-extra.sync.js +505 -0
- package/dist/security/audit.js +2 -2
- package/dist/security/channel-metadata.js +34 -0
- package/dist/security/external-content.js +88 -6
- package/dist/security/skill-scanner.js +330 -0
- package/dist/sessions/session-key-utils.js +7 -0
- package/dist/shared/text/reasoning-tags.js +52 -7
- package/dist/signal/monitor/event-handler.js +80 -1
- package/dist/slack/monitor/media.js +85 -15
- package/dist/tailscale/detect.js +145 -0
- package/dist/telegram/bot/helpers.js +109 -28
- package/dist/telegram/bot-handlers.js +144 -3
- package/dist/telegram/bot-message-context.js +38 -11
- package/dist/telegram/bot-message-dispatch.js +48 -15
- package/dist/telegram/bot-native-commands.js +86 -29
- package/dist/telegram/bot.js +30 -29
- package/dist/telegram/model-buttons.js +163 -0
- package/dist/telegram/monitor.js +110 -85
- package/dist/telegram/send.js +129 -47
- package/dist/terminal/restore.js +45 -0
- package/dist/test-helpers/state-dir-env.js +16 -0
- package/dist/test-helpers/workspace.js +11 -0
- package/dist/test-utils/channel-plugins.js +82 -0
- package/dist/test-utils/ports.js +73 -0
- package/dist/tts/tts.js +12 -6
- package/dist/tui/tui-session-actions.js +166 -54
- package/dist/utils/fetch-timeout.js +20 -0
- package/dist/utils/normalize-secret-input.js +19 -0
- package/dist/utils/shell-argv.js +61 -0
- package/dist/utils/transcript-tools.js +58 -0
- package/dist/utils.js +55 -14
- package/dist/version.js +42 -5
- package/dist/web/qr-image.js +1 -61
- package/dist/wizard/onboarding.finalize.js +7 -7
- package/dist/wizard/onboarding.js +3 -3
- package/docs/RELEASE_WORKFOTS_COMPARISON.md +3 -3
- package/docs/_config.yml +2 -2
- package/docs/_layouts/default.html +9 -9
- package/docs/concepts/typebox.md +1 -1
- package/docs/docs.json +1 -1
- package/docs/northflank.mdx +7 -7
- package/docs/railway.mdx +3 -3
- package/docs/render.mdx +5 -5
- package/docs/start/lore.md +2 -2
- package/extensions/bluebubbles/index.ts +2 -2
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/bluebubbles/src/accounts.ts +8 -8
- package/extensions/bluebubbles/src/actions.test.ts +22 -22
- package/extensions/bluebubbles/src/actions.ts +5 -5
- package/extensions/bluebubbles/src/attachments.ts +2 -2
- package/extensions/bluebubbles/src/channel.ts +16 -16
- package/extensions/bluebubbles/src/chat.ts +2 -2
- package/extensions/bluebubbles/src/media-send.ts +2 -2
- package/extensions/bluebubbles/src/monitor.test.ts +46 -46
- package/extensions/bluebubbles/src/monitor.ts +5 -5
- package/extensions/bluebubbles/src/onboarding.ts +7 -7
- package/extensions/bluebubbles/src/reactions.ts +2 -2
- package/extensions/bluebubbles/src/send.ts +2 -2
- package/extensions/copilot-proxy/README.md +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/index.ts +2 -2
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/diagnostics-otel/src/service.ts +3 -3
- package/extensions/discord/index.ts +2 -2
- package/extensions/discord/package.json +1 -1
- package/extensions/google-antigravity-auth/README.md +1 -1
- package/extensions/google-antigravity-auth/index.ts +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/README.md +1 -1
- package/extensions/google-gemini-cli-auth/oauth.ts +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/index.ts +3 -3
- package/extensions/googlechat/package.json +1 -1
- package/extensions/googlechat/src/accounts.ts +8 -8
- package/extensions/googlechat/src/actions.ts +6 -6
- package/extensions/googlechat/src/channel.ts +21 -21
- package/extensions/googlechat/src/monitor.ts +8 -8
- package/extensions/googlechat/src/onboarding.ts +10 -10
- package/extensions/imessage/index.ts +2 -2
- package/extensions/imessage/package.json +1 -1
- package/extensions/line/index.ts +2 -2
- package/extensions/line/package.json +1 -1
- package/extensions/line/src/card-command.ts +2 -2
- package/extensions/line/src/channel.logout.test.ts +4 -4
- package/extensions/line/src/channel.sendPayload.test.ts +8 -8
- package/extensions/line/src/channel.ts +3 -3
- package/extensions/llm-task/README.md +3 -3
- package/extensions/llm-task/index.ts +2 -2
- package/extensions/llm-task/package.json +1 -1
- package/extensions/llm-task/src/llm-task-tool.ts +4 -4
- package/extensions/lobster/README.md +6 -6
- package/extensions/lobster/index.ts +2 -2
- package/extensions/lobster/src/lobster-tool.test.ts +4 -4
- package/extensions/lobster/src/lobster-tool.ts +2 -2
- package/extensions/matrix/index.ts +2 -2
- package/extensions/matrix/package.json +1 -1
- package/extensions/matrix/src/matrix/client/config.ts +1 -1
- package/extensions/matrix/src/matrix/monitor/handler.ts +1 -1
- package/extensions/matrix/src/onboarding.ts +1 -1
- package/extensions/mattermost/index.ts +2 -2
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/accounts.ts +8 -8
- package/extensions/mattermost/src/mattermost/monitor-helpers.ts +5 -5
- package/extensions/mattermost/src/mattermost/monitor.ts +2 -2
- package/extensions/mattermost/src/onboarding-helpers.ts +3 -3
- package/extensions/mattermost/src/onboarding.ts +2 -2
- package/extensions/memory-core/index.ts +2 -2
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/index.ts +3 -3
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/msteams/index.ts +2 -2
- package/extensions/msteams/package.json +1 -1
- package/extensions/msteams/src/channel.directory.test.ts +2 -2
- package/extensions/msteams/src/channel.ts +2 -2
- package/extensions/msteams/src/graph-upload.ts +4 -4
- package/extensions/msteams/src/monitor-handler.ts +2 -2
- package/extensions/msteams/src/monitor.ts +2 -2
- package/extensions/msteams/src/onboarding.ts +9 -9
- package/extensions/msteams/src/reply-dispatcher.ts +2 -2
- package/extensions/msteams/src/send-context.ts +2 -2
- package/extensions/msteams/src/send.ts +4 -4
- package/extensions/nextcloud-talk/index.ts +2 -2
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nextcloud-talk/src/channel.ts +7 -7
- package/extensions/nextcloud-talk/src/inbound.ts +7 -7
- package/extensions/nextcloud-talk/src/onboarding.ts +1 -1
- package/extensions/nostr/README.md +2 -2
- package/extensions/nostr/index.ts +5 -5
- package/extensions/nostr/package.json +1 -1
- package/extensions/nostr/src/types.ts +4 -4
- package/extensions/open-prose/index.ts +2 -2
- package/extensions/qwen-portal-auth/README.md +1 -1
- package/extensions/signal/index.ts +2 -2
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/index.ts +2 -2
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/index.ts +2 -2
- package/extensions/telegram/package.json +1 -1
- package/extensions/telegram/src/channel.ts +2 -2
- package/extensions/tlon/README.md +2 -2
- package/extensions/tlon/index.ts +2 -2
- package/extensions/tlon/package.json +1 -1
- package/extensions/tlon/src/channel.ts +13 -13
- package/extensions/tlon/src/monitor/index.ts +3 -3
- package/extensions/tlon/src/onboarding.ts +3 -3
- package/extensions/tlon/src/types.ts +3 -3
- package/extensions/twitch/README.md +1 -1
- package/extensions/twitch/index.ts +2 -2
- package/extensions/twitch/package.json +1 -1
- package/extensions/twitch/src/config.ts +3 -3
- package/extensions/twitch/src/monitor.ts +3 -3
- package/extensions/twitch/src/onboarding.ts +9 -9
- package/extensions/twitch/src/outbound.test.ts +2 -2
- package/extensions/twitch/src/plugin.test.ts +2 -2
- package/extensions/twitch/src/plugin.ts +8 -8
- package/extensions/twitch/src/send.test.ts +2 -2
- package/extensions/twitch/src/send.ts +4 -4
- package/extensions/twitch/src/token.test.ts +8 -8
- package/extensions/twitch/src/token.ts +3 -3
- package/extensions/twitch/src/twitch-client.ts +3 -3
- package/extensions/twitch/src/types.ts +3 -3
- package/extensions/twitch/src/utils/markdown.ts +1 -1
- package/extensions/voice-call/README.md +3 -3
- package/extensions/voice-call/package.json +1 -1
- package/extensions/voice-call/src/core-bridge.ts +2 -2
- package/extensions/voice-call/src/response-generator.ts +1 -1
- package/extensions/whatsapp/index.ts +2 -2
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/README.md +1 -1
- package/extensions/zalo/index.ts +2 -2
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalo/src/accounts.ts +8 -8
- package/extensions/zalo/src/actions.ts +4 -4
- package/extensions/zalo/src/channel.directory.test.ts +2 -2
- package/extensions/zalo/src/channel.ts +18 -18
- package/extensions/zalo/src/monitor.ts +9 -9
- package/extensions/zalo/src/monitor.webhook.test.ts +2 -2
- package/extensions/zalo/src/onboarding.ts +24 -24
- package/extensions/zalo/src/send.ts +2 -2
- package/extensions/zalouser/README.md +2 -2
- package/extensions/zalouser/index.ts +2 -2
- package/extensions/zalouser/package.json +1 -1
- package/extensions/zalouser/src/accounts.ts +9 -9
- package/extensions/zalouser/src/channel.ts +24 -24
- package/extensions/zalouser/src/monitor.ts +4 -4
- package/extensions/zalouser/src/onboarding.ts +28 -28
- package/package.json +13 -251
- package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
- package/skills/tmux/scripts/find-sessions.sh +1 -1
- package/CHANGELOG.md +0 -102
- package/README-header.png +0 -0
- package/git-hooks/pre-commit +0 -4
- package/scripts/format-staged.js +0 -148
- package/scripts/postinstall.js +0 -300
- package/scripts/setup-git-hooks.js +0 -96
|
@@ -68,7 +68,7 @@ export function resolveDiscordAllowListMatch(params) {
|
|
|
68
68
|
return { allowed: false };
|
|
69
69
|
}
|
|
70
70
|
export function resolveDiscordUserAllowed(params) {
|
|
71
|
-
const allowList = normalizeDiscordAllowList(params.allowList, ["discord:", "user:"]);
|
|
71
|
+
const allowList = normalizeDiscordAllowList(params.allowList, ["discord:", "user:", "pk:"]);
|
|
72
72
|
if (!allowList)
|
|
73
73
|
return true;
|
|
74
74
|
return allowListMatches(allowList, {
|
|
@@ -80,7 +80,7 @@ export function resolveDiscordUserAllowed(params) {
|
|
|
80
80
|
export function resolveDiscordCommandAuthorized(params) {
|
|
81
81
|
if (!params.isDirectMessage)
|
|
82
82
|
return true;
|
|
83
|
-
const allowList = normalizeDiscordAllowList(params.allowFrom, ["discord:", "user:"]);
|
|
83
|
+
const allowList = normalizeDiscordAllowList(params.allowFrom, ["discord:", "user:", "pk:"]);
|
|
84
84
|
if (!allowList)
|
|
85
85
|
return true;
|
|
86
86
|
return allowListMatches(allowList, {
|
|
@@ -89,6 +89,28 @@ export function resolveDiscordCommandAuthorized(params) {
|
|
|
89
89
|
tag: formatDiscordUserTag(params.author),
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
|
+
export function resolveDiscordOwnerAllowFrom(params) {
|
|
93
|
+
const rawAllowList = params.channelConfig?.users ?? params.guildInfo?.users;
|
|
94
|
+
if (!Array.isArray(rawAllowList) || rawAllowList.length === 0) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
const allowList = normalizeDiscordAllowList(rawAllowList, ["discord:", "user:", "pk:"]);
|
|
98
|
+
if (!allowList) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
const match = resolveDiscordAllowListMatch({
|
|
102
|
+
allowList,
|
|
103
|
+
candidate: {
|
|
104
|
+
id: params.sender.id,
|
|
105
|
+
name: params.sender.name,
|
|
106
|
+
tag: params.sender.tag,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
if (!match.allowed || !match.matchKey || match.matchKey === "*") {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
return [match.matchKey];
|
|
113
|
+
}
|
|
92
114
|
export function resolveDiscordGuildEntry(params) {
|
|
93
115
|
const guild = params.guild;
|
|
94
116
|
const entries = params.guildEntries;
|
|
@@ -128,6 +150,7 @@ function resolveDiscordChannelConfigEntry(entry) {
|
|
|
128
150
|
enabled: entry.enabled,
|
|
129
151
|
users: entry.users,
|
|
130
152
|
systemPrompt: entry.systemPrompt,
|
|
153
|
+
includeThreadStarter: entry.includeThreadStarter,
|
|
131
154
|
autoThread: entry.autoThread,
|
|
132
155
|
};
|
|
133
156
|
return resolved;
|
|
@@ -199,13 +222,13 @@ export function resolveGroupDmAllow(params) {
|
|
|
199
222
|
const { channels, channelId, channelName, channelSlug } = params;
|
|
200
223
|
if (!channels || channels.length === 0)
|
|
201
224
|
return true;
|
|
202
|
-
const allowList = channels.map((entry) => normalizeDiscordSlug(String(entry)));
|
|
225
|
+
const allowList = new Set(channels.map((entry) => normalizeDiscordSlug(String(entry))));
|
|
203
226
|
const candidates = [
|
|
204
227
|
normalizeDiscordSlug(channelId),
|
|
205
228
|
channelSlug,
|
|
206
229
|
channelName ? normalizeDiscordSlug(channelName) : "",
|
|
207
230
|
].filter(Boolean);
|
|
208
|
-
return allowList.
|
|
231
|
+
return allowList.has("*") || candidates.some((candidate) => allowList.has(candidate));
|
|
209
232
|
}
|
|
210
233
|
export function shouldEmitDiscordReactionNotification(params) {
|
|
211
234
|
const mode = params.mode ?? "own";
|
|
@@ -217,7 +240,7 @@ export function shouldEmitDiscordReactionNotification(params) {
|
|
|
217
240
|
return Boolean(params.botId && params.messageAuthorId === params.botId);
|
|
218
241
|
}
|
|
219
242
|
if (mode === "allowlist") {
|
|
220
|
-
const list = normalizeDiscordAllowList(params.allowlist, ["discord:", "user:"]);
|
|
243
|
+
const list = normalizeDiscordAllowList(params.allowlist, ["discord:", "user:", "pk:"]);
|
|
221
244
|
if (!list)
|
|
222
245
|
return false;
|
|
223
246
|
return allowListMatches(list, {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level registry of active Discord GatewayPlugin instances.
|
|
3
|
+
* Bridges the gap between agent tool handlers (which only have REST access)
|
|
4
|
+
* and the gateway WebSocket (needed for operations like updatePresence).
|
|
5
|
+
* Follows the same pattern as presence-cache.ts.
|
|
6
|
+
*/
|
|
7
|
+
const gatewayRegistry = new Map();
|
|
8
|
+
// Sentinel key for the default (unnamed) account. Uses a prefix that cannot
|
|
9
|
+
// collide with user-configured account IDs.
|
|
10
|
+
const DEFAULT_ACCOUNT_KEY = "\0__default__";
|
|
11
|
+
function resolveAccountKey(accountId) {
|
|
12
|
+
return accountId ?? DEFAULT_ACCOUNT_KEY;
|
|
13
|
+
}
|
|
14
|
+
/** Register a GatewayPlugin instance for an account. */
|
|
15
|
+
export function registerGateway(accountId, gateway) {
|
|
16
|
+
gatewayRegistry.set(resolveAccountKey(accountId), gateway);
|
|
17
|
+
}
|
|
18
|
+
/** Unregister a GatewayPlugin instance for an account. */
|
|
19
|
+
export function unregisterGateway(accountId) {
|
|
20
|
+
gatewayRegistry.delete(resolveAccountKey(accountId));
|
|
21
|
+
}
|
|
22
|
+
/** Get the GatewayPlugin for an account. Returns undefined if not registered. */
|
|
23
|
+
export function getGateway(accountId) {
|
|
24
|
+
return gatewayRegistry.get(resolveAccountKey(accountId));
|
|
25
|
+
}
|
|
26
|
+
/** Clear all registered gateways (for testing). */
|
|
27
|
+
export function clearGateways() {
|
|
28
|
+
gatewayRegistry.clear();
|
|
29
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Button, ChannelType, Command, Row, } from "@buape/carbon";
|
|
2
2
|
import { ApplicationCommandOptionType, ButtonStyle } from "discord-api-types/v10";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveHumanDelayConfig } from "../../agents/identity.js";
|
|
4
4
|
import { resolveChunkMode, resolveTextChunkLimit } from "../../auto-reply/chunk.js";
|
|
5
5
|
import { buildCommandTextFromArgs, findCommandByNativeName, listChatCommands, parseCommandArgs, resolveCommandArgChoices, resolveCommandArgMenu, serializeCommandArgs, } from "../../auto-reply/commands-registry.js";
|
|
6
6
|
import { dispatchReplyWithDispatcher } from "../../auto-reply/reply/provider-dispatcher.js";
|
|
@@ -11,8 +11,10 @@ import { resolveAgentRoute } from "../../routing/resolve-route.js";
|
|
|
11
11
|
import { loadWebMedia } from "../../web/media.js";
|
|
12
12
|
import { chunkDiscordTextWithMode } from "../chunk.js";
|
|
13
13
|
import { resolveCommandAuthorizedFromAuthorizers } from "../../channels/command-gating.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { createReplyPrefixContext } from "../../channels/reply-prefix.js";
|
|
15
|
+
import { buildUntrustedChannelMetadata } from "../../security/channel-metadata.js";
|
|
16
|
+
import { allowListMatches, isDiscordGroupAllowedByPolicy, normalizeDiscordAllowList, normalizeDiscordSlug, resolveDiscordChannelConfigWithFallback, resolveDiscordGuildEntry, resolveDiscordOwnerAllowFrom, resolveDiscordUserAllowed, } from "./allow-list.js";
|
|
17
|
+
import { resolveDiscordSenderIdentity } from "./sender-identity.js";
|
|
16
18
|
import { resolveDiscordChannelInfo } from "./message-utils.js";
|
|
17
19
|
import { resolveDiscordThreadParentInfo } from "./threading.js";
|
|
18
20
|
function buildDiscordCommandOptions(params) {
|
|
@@ -357,6 +359,7 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
357
359
|
const user = interaction.user;
|
|
358
360
|
if (!user)
|
|
359
361
|
return;
|
|
362
|
+
const sender = resolveDiscordSenderIdentity({ author: user, pluralkitInfo: null });
|
|
360
363
|
const channel = interaction.channel;
|
|
361
364
|
const channelType = channel?.type;
|
|
362
365
|
const isDirectMessage = channelType === ChannelType.DM;
|
|
@@ -370,12 +373,13 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
370
373
|
const ownerAllowList = normalizeDiscordAllowList(discordConfig?.dm?.allowFrom ?? [], [
|
|
371
374
|
"discord:",
|
|
372
375
|
"user:",
|
|
376
|
+
"pk:",
|
|
373
377
|
]);
|
|
374
378
|
const ownerOk = ownerAllowList && user
|
|
375
379
|
? allowListMatches(ownerAllowList, {
|
|
376
|
-
id:
|
|
377
|
-
name:
|
|
378
|
-
tag:
|
|
380
|
+
id: sender.id,
|
|
381
|
+
name: sender.name,
|
|
382
|
+
tag: sender.tag,
|
|
379
383
|
})
|
|
380
384
|
: false;
|
|
381
385
|
const guildInfo = resolveDiscordGuildEntry({
|
|
@@ -447,12 +451,12 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
447
451
|
if (dmPolicy !== "open") {
|
|
448
452
|
const storeAllowFrom = await readChannelAllowFromStore("discord").catch(() => []);
|
|
449
453
|
const effectiveAllowFrom = [...(discordConfig?.dm?.allowFrom ?? []), ...storeAllowFrom];
|
|
450
|
-
const allowList = normalizeDiscordAllowList(effectiveAllowFrom, ["discord:", "user:"]);
|
|
454
|
+
const allowList = normalizeDiscordAllowList(effectiveAllowFrom, ["discord:", "user:", "pk:"]);
|
|
451
455
|
const permitted = allowList
|
|
452
456
|
? allowListMatches(allowList, {
|
|
453
|
-
id:
|
|
454
|
-
name:
|
|
455
|
-
tag:
|
|
457
|
+
id: sender.id,
|
|
458
|
+
name: sender.name,
|
|
459
|
+
tag: sender.tag,
|
|
456
460
|
})
|
|
457
461
|
: false;
|
|
458
462
|
if (!permitted) {
|
|
@@ -462,8 +466,8 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
462
466
|
channel: "discord",
|
|
463
467
|
id: user.id,
|
|
464
468
|
meta: {
|
|
465
|
-
tag:
|
|
466
|
-
name:
|
|
469
|
+
tag: sender.tag,
|
|
470
|
+
name: sender.name ?? undefined,
|
|
467
471
|
},
|
|
468
472
|
});
|
|
469
473
|
if (created) {
|
|
@@ -488,9 +492,9 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
488
492
|
const userOk = hasUserAllowlist
|
|
489
493
|
? resolveDiscordUserAllowed({
|
|
490
494
|
allowList: channelUsers,
|
|
491
|
-
userId:
|
|
492
|
-
userName:
|
|
493
|
-
userTag:
|
|
495
|
+
userId: sender.id,
|
|
496
|
+
userName: sender.name,
|
|
497
|
+
userTag: sender.tag,
|
|
494
498
|
})
|
|
495
499
|
: false;
|
|
496
500
|
const authorizers = useAccessGroups
|
|
@@ -555,10 +559,17 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
555
559
|
kind: isDirectMessage ? "dm" : isGroupDm ? "group" : "channel",
|
|
556
560
|
id: isDirectMessage ? user.id : channelId,
|
|
557
561
|
},
|
|
562
|
+
parentPeer: threadParentId ? { kind: "channel", id: threadParentId } : undefined,
|
|
558
563
|
});
|
|
559
564
|
const conversationLabel = isDirectMessage ? (user.globalName ?? user.username) : channelId;
|
|
565
|
+
const ownerAllowFrom = resolveDiscordOwnerAllowFrom({
|
|
566
|
+
channelConfig,
|
|
567
|
+
guildInfo,
|
|
568
|
+
sender: { id: sender.id, name: sender.name, tag: sender.tag },
|
|
569
|
+
});
|
|
560
570
|
const ctxPayload = finalizeInboundContext({
|
|
561
571
|
Body: prompt,
|
|
572
|
+
BodyForAgent: prompt,
|
|
562
573
|
RawBody: prompt,
|
|
563
574
|
CommandBody: prompt,
|
|
564
575
|
CommandArgs: commandArgs,
|
|
@@ -576,19 +587,25 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
576
587
|
GroupSubject: isGuild ? interaction.guild?.name : undefined,
|
|
577
588
|
GroupSystemPrompt: isGuild
|
|
578
589
|
? (() => {
|
|
579
|
-
const
|
|
580
|
-
const channelDescription = channelTopic?.trim();
|
|
581
|
-
const systemPromptParts = [
|
|
582
|
-
channelDescription ? `Channel topic: ${channelDescription}` : null,
|
|
583
|
-
channelConfig?.systemPrompt?.trim() || null,
|
|
584
|
-
].filter((entry) => Boolean(entry));
|
|
590
|
+
const systemPromptParts = [channelConfig?.systemPrompt?.trim() || null].filter((entry) => Boolean(entry));
|
|
585
591
|
return systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : undefined;
|
|
586
592
|
})()
|
|
587
593
|
: undefined,
|
|
594
|
+
UntrustedContext: isGuild
|
|
595
|
+
? (() => {
|
|
596
|
+
const channelTopic = channel && "topic" in channel ? (channel.topic ?? undefined) : undefined;
|
|
597
|
+
const untrustedChannelMetadata = buildUntrustedChannelMetadata({
|
|
598
|
+
source: "discord",
|
|
599
|
+
label: "Discord channel topic",
|
|
600
|
+
entries: [channelTopic],
|
|
601
|
+
});
|
|
602
|
+
return untrustedChannelMetadata ? [untrustedChannelMetadata] : undefined;
|
|
603
|
+
})()
|
|
604
|
+
: undefined,
|
|
588
605
|
SenderName: user.globalName ?? user.username,
|
|
589
606
|
SenderId: user.id,
|
|
590
607
|
SenderUsername: user.username,
|
|
591
|
-
SenderTag:
|
|
608
|
+
SenderTag: sender.tag,
|
|
592
609
|
Provider: "discord",
|
|
593
610
|
Surface: "discord",
|
|
594
611
|
WasMentioned: true,
|
|
@@ -596,13 +613,16 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
596
613
|
Timestamp: Date.now(),
|
|
597
614
|
CommandAuthorized: commandAuthorized,
|
|
598
615
|
CommandSource: "native",
|
|
616
|
+
OwnerAllowFrom: ownerAllowFrom,
|
|
599
617
|
});
|
|
618
|
+
const prefixContext = createReplyPrefixContext({ cfg, agentId: route.agentId });
|
|
600
619
|
let didReply = false;
|
|
601
620
|
await dispatchReplyWithDispatcher({
|
|
602
621
|
ctx: ctxPayload,
|
|
603
622
|
cfg,
|
|
604
623
|
dispatcherOptions: {
|
|
605
|
-
responsePrefix:
|
|
624
|
+
responsePrefix: prefixContext.responsePrefix,
|
|
625
|
+
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
|
606
626
|
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
|
607
627
|
deliver: async (payload) => {
|
|
608
628
|
try {
|
|
@@ -635,6 +655,7 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
635
655
|
disableBlockStreaming: typeof discordConfig?.blockStreaming === "boolean"
|
|
636
656
|
? !discordConfig.blockStreaming
|
|
637
657
|
: undefined,
|
|
658
|
+
onModelSelected: prefixContext.onModelSelected,
|
|
638
659
|
},
|
|
639
660
|
});
|
|
640
661
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { formatDiscordUserTag } from "./format.js";
|
|
2
|
+
export function resolveDiscordWebhookId(message) {
|
|
3
|
+
const candidate = message.webhookId ?? message.webhook_id;
|
|
4
|
+
return typeof candidate === "string" && candidate.trim() ? candidate.trim() : null;
|
|
5
|
+
}
|
|
6
|
+
export function resolveDiscordSenderIdentity(params) {
|
|
7
|
+
const pkInfo = params.pluralkitInfo ?? null;
|
|
8
|
+
const pkMember = pkInfo?.member ?? undefined;
|
|
9
|
+
const pkSystem = pkInfo?.system ?? undefined;
|
|
10
|
+
const memberId = pkMember?.id?.trim();
|
|
11
|
+
const memberNameRaw = pkMember?.display_name ?? pkMember?.name ?? "";
|
|
12
|
+
const memberName = memberNameRaw?.trim();
|
|
13
|
+
if (memberId && memberName) {
|
|
14
|
+
const systemName = pkSystem?.name?.trim();
|
|
15
|
+
const label = systemName ? `${memberName} (PK:${systemName})` : `${memberName} (PK)`;
|
|
16
|
+
return {
|
|
17
|
+
id: memberId,
|
|
18
|
+
name: memberName,
|
|
19
|
+
tag: pkMember?.name?.trim() || undefined,
|
|
20
|
+
label,
|
|
21
|
+
isPluralKit: true,
|
|
22
|
+
pluralkit: {
|
|
23
|
+
memberId,
|
|
24
|
+
memberName,
|
|
25
|
+
systemId: pkSystem?.id?.trim() || undefined,
|
|
26
|
+
systemName,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const senderTag = formatDiscordUserTag(params.author);
|
|
31
|
+
const senderDisplay = params.member?.nickname ?? params.author.globalName ?? params.author.username;
|
|
32
|
+
const senderLabel = senderDisplay && senderTag && senderDisplay !== senderTag
|
|
33
|
+
? `${senderDisplay} (${senderTag})`
|
|
34
|
+
: (senderDisplay ?? senderTag ?? params.author.id);
|
|
35
|
+
return {
|
|
36
|
+
id: params.author.id,
|
|
37
|
+
name: params.author.username ?? undefined,
|
|
38
|
+
tag: senderTag,
|
|
39
|
+
label: senderLabel,
|
|
40
|
+
isPluralKit: false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function resolveDiscordSenderLabel(params) {
|
|
44
|
+
return resolveDiscordSenderIdentity(params).label;
|
|
45
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { resolveFetch } from "../infra/fetch.js";
|
|
2
|
+
const PLURALKIT_API_BASE = "https://api.pluralkit.me/v2";
|
|
3
|
+
export async function fetchPluralKitMessageInfo(params) {
|
|
4
|
+
if (!params.config?.enabled) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const fetchImpl = resolveFetch(params.fetcher);
|
|
8
|
+
if (!fetchImpl) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const headers = {};
|
|
12
|
+
if (params.config.token?.trim()) {
|
|
13
|
+
headers.Authorization = params.config.token.trim();
|
|
14
|
+
}
|
|
15
|
+
const res = await fetchImpl(`${PLURALKIT_API_BASE}/messages/${params.messageId}`, {
|
|
16
|
+
headers,
|
|
17
|
+
});
|
|
18
|
+
if (res.status === 404) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
const text = await res.text().catch(() => "");
|
|
23
|
+
const detail = text.trim() ? `: ${text.trim()}` : "";
|
|
24
|
+
throw new Error(`PluralKit API failed (${res.status})${detail}`);
|
|
25
|
+
}
|
|
26
|
+
return (await res.json());
|
|
27
|
+
}
|
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
import { Routes } from "discord-api-types/v10";
|
|
1
|
+
import { ChannelType, Routes } from "discord-api-types/v10";
|
|
2
2
|
import { resolveChunkMode } from "../auto-reply/chunk.js";
|
|
3
3
|
import { loadConfig } from "../config/config.js";
|
|
4
4
|
import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
|
|
5
5
|
import { recordChannelActivity } from "../infra/channel-activity.js";
|
|
6
6
|
import { convertMarkdownTables } from "../markdown/tables.js";
|
|
7
7
|
import { resolveDiscordAccount } from "./accounts.js";
|
|
8
|
-
import { buildDiscordSendError, createDiscordClient, normalizeDiscordPollInput, normalizeStickerIds,
|
|
8
|
+
import { buildDiscordSendError, buildDiscordTextChunks, createDiscordClient, normalizeDiscordPollInput, normalizeStickerIds, parseAndResolveRecipient, resolveChannelId, sendDiscordMedia, sendDiscordText, } from "./send.shared.js";
|
|
9
|
+
/** Discord thread names are capped at 100 characters. */
|
|
10
|
+
const DISCORD_THREAD_NAME_LIMIT = 100;
|
|
11
|
+
/** Derive a thread title from the first non-empty line of the message text. */
|
|
12
|
+
function deriveForumThreadName(text) {
|
|
13
|
+
const firstLine = text
|
|
14
|
+
.split("\n")
|
|
15
|
+
.find((l) => l.trim())
|
|
16
|
+
?.trim() ?? "";
|
|
17
|
+
return firstLine.slice(0, DISCORD_THREAD_NAME_LIMIT) || new Date().toISOString().slice(0, 16);
|
|
18
|
+
}
|
|
19
|
+
/** Forum/Media channels cannot receive regular messages; detect them here. */
|
|
20
|
+
function isForumLikeType(channelType) {
|
|
21
|
+
return channelType === ChannelType.GuildForum || channelType === ChannelType.GuildMedia;
|
|
22
|
+
}
|
|
9
23
|
export async function sendMessageDiscord(to, text, opts = {}) {
|
|
10
24
|
const cfg = loadConfig();
|
|
11
25
|
const accountInfo = resolveDiscordAccount({
|
|
@@ -20,8 +34,81 @@ export async function sendMessageDiscord(to, text, opts = {}) {
|
|
|
20
34
|
const chunkMode = resolveChunkMode(cfg, "discord", accountInfo.accountId);
|
|
21
35
|
const textWithTables = convertMarkdownTables(text ?? "", tableMode);
|
|
22
36
|
const { token, rest, request } = createDiscordClient(opts, cfg);
|
|
23
|
-
const recipient =
|
|
37
|
+
const recipient = await parseAndResolveRecipient(to, opts.accountId);
|
|
24
38
|
const { channelId } = await resolveChannelId(rest, recipient, request);
|
|
39
|
+
// Forum/Media channels reject POST /messages; auto-create a thread post instead.
|
|
40
|
+
let channelType;
|
|
41
|
+
try {
|
|
42
|
+
const channel = (await rest.get(Routes.channel(channelId)));
|
|
43
|
+
channelType = channel?.type;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// If we can't fetch the channel, fall through to the normal send path.
|
|
47
|
+
}
|
|
48
|
+
if (isForumLikeType(channelType)) {
|
|
49
|
+
const threadName = deriveForumThreadName(textWithTables);
|
|
50
|
+
const chunks = buildDiscordTextChunks(textWithTables, {
|
|
51
|
+
maxLinesPerMessage: accountInfo.config.maxLinesPerMessage,
|
|
52
|
+
chunkMode,
|
|
53
|
+
});
|
|
54
|
+
const starterContent = chunks[0]?.trim() ? chunks[0] : threadName;
|
|
55
|
+
const starterEmbeds = opts.embeds?.length ? opts.embeds : undefined;
|
|
56
|
+
let threadRes;
|
|
57
|
+
try {
|
|
58
|
+
threadRes = (await request(() => rest.post(Routes.threads(channelId), {
|
|
59
|
+
body: {
|
|
60
|
+
name: threadName,
|
|
61
|
+
message: {
|
|
62
|
+
content: starterContent,
|
|
63
|
+
...(starterEmbeds ? { embeds: starterEmbeds } : {}),
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
}), "forum-thread"));
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
throw await buildDiscordSendError(err, {
|
|
70
|
+
channelId,
|
|
71
|
+
rest,
|
|
72
|
+
token,
|
|
73
|
+
hasMedia: Boolean(opts.mediaUrl),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const threadId = threadRes.id;
|
|
77
|
+
const messageId = threadRes.message?.id ?? threadId;
|
|
78
|
+
const resultChannelId = threadRes.message?.channel_id ?? threadId;
|
|
79
|
+
const remainingChunks = chunks.slice(1);
|
|
80
|
+
try {
|
|
81
|
+
if (opts.mediaUrl) {
|
|
82
|
+
const [mediaCaption, ...afterMediaChunks] = remainingChunks;
|
|
83
|
+
await sendDiscordMedia(rest, threadId, mediaCaption ?? "", opts.mediaUrl, undefined, request, accountInfo.config.maxLinesPerMessage, undefined, chunkMode);
|
|
84
|
+
for (const chunk of afterMediaChunks) {
|
|
85
|
+
await sendDiscordText(rest, threadId, chunk, undefined, request, accountInfo.config.maxLinesPerMessage, undefined, chunkMode);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
for (const chunk of remainingChunks) {
|
|
90
|
+
await sendDiscordText(rest, threadId, chunk, undefined, request, accountInfo.config.maxLinesPerMessage, undefined, chunkMode);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
throw await buildDiscordSendError(err, {
|
|
96
|
+
channelId: threadId,
|
|
97
|
+
rest,
|
|
98
|
+
token,
|
|
99
|
+
hasMedia: Boolean(opts.mediaUrl),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
recordChannelActivity({
|
|
103
|
+
channel: "discord",
|
|
104
|
+
accountId: accountInfo.accountId,
|
|
105
|
+
direction: "outbound",
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
messageId: messageId ? String(messageId) : "unknown",
|
|
109
|
+
channelId: String(resultChannelId ?? channelId),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
25
112
|
let result;
|
|
26
113
|
try {
|
|
27
114
|
if (opts.mediaUrl) {
|
|
@@ -52,7 +139,7 @@ export async function sendMessageDiscord(to, text, opts = {}) {
|
|
|
52
139
|
export async function sendStickerDiscord(to, stickerIds, opts = {}) {
|
|
53
140
|
const cfg = loadConfig();
|
|
54
141
|
const { rest, request } = createDiscordClient(opts, cfg);
|
|
55
|
-
const recipient =
|
|
142
|
+
const recipient = await parseAndResolveRecipient(to, opts.accountId);
|
|
56
143
|
const { channelId } = await resolveChannelId(rest, recipient, request);
|
|
57
144
|
const content = opts.content?.trim();
|
|
58
145
|
const stickers = normalizeStickerIds(stickerIds);
|
|
@@ -70,7 +157,7 @@ export async function sendStickerDiscord(to, stickerIds, opts = {}) {
|
|
|
70
157
|
export async function sendPollDiscord(to, poll, opts = {}) {
|
|
71
158
|
const cfg = loadConfig();
|
|
72
159
|
const { rest, request } = createDiscordClient(opts, cfg);
|
|
73
|
-
const recipient =
|
|
160
|
+
const recipient = await parseAndResolveRecipient(to, opts.accountId);
|
|
74
161
|
const { channelId } = await resolveChannelId(rest, recipient, request);
|
|
75
162
|
const content = opts.content?.trim();
|
|
76
163
|
const payload = normalizeDiscordPollInput(poll);
|
|
@@ -9,7 +9,7 @@ import { resolveDiscordAccount } from "./accounts.js";
|
|
|
9
9
|
import { chunkDiscordTextWithMode } from "./chunk.js";
|
|
10
10
|
import { fetchChannelPermissionsDiscord, isThreadChannelType } from "./send.permissions.js";
|
|
11
11
|
import { DiscordSendError } from "./send.types.js";
|
|
12
|
-
import { parseDiscordTarget } from "./targets.js";
|
|
12
|
+
import { parseDiscordTarget, resolveDiscordTarget } from "./targets.js";
|
|
13
13
|
import { normalizeDiscordToken } from "./token.js";
|
|
14
14
|
const DISCORD_TEXT_LIMIT = 2000;
|
|
15
15
|
const DISCORD_MAX_STICKERS = 3;
|
|
@@ -19,8 +19,9 @@ const DISCORD_MISSING_PERMISSIONS = 50013;
|
|
|
19
19
|
const DISCORD_CANNOT_DM = 50007;
|
|
20
20
|
function resolveToken(params) {
|
|
21
21
|
const explicit = normalizeDiscordToken(params.explicit);
|
|
22
|
-
if (explicit)
|
|
22
|
+
if (explicit) {
|
|
23
23
|
return explicit;
|
|
24
|
+
}
|
|
24
25
|
const fallback = normalizeDiscordToken(params.fallbackToken);
|
|
25
26
|
if (!fallback) {
|
|
26
27
|
throw new Error(`Discord bot token missing for account "${params.accountId}" (set discord.accounts.${params.accountId}.token or DISCORD_BOT_TOKEN for default).`);
|
|
@@ -68,6 +69,37 @@ function parseRecipient(raw) {
|
|
|
68
69
|
}
|
|
69
70
|
return { kind: target.kind, id: target.id };
|
|
70
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Parse and resolve Discord recipient, including username lookup.
|
|
74
|
+
* This enables sending DMs by username (e.g., "john.doe") by querying
|
|
75
|
+
* the Discord directory to resolve usernames to user IDs.
|
|
76
|
+
*
|
|
77
|
+
* @param raw - The recipient string (username, ID, or known format)
|
|
78
|
+
* @param accountId - Discord account ID to use for directory lookup
|
|
79
|
+
* @returns Parsed DiscordRecipient with resolved user ID if applicable
|
|
80
|
+
*/
|
|
81
|
+
export async function parseAndResolveRecipient(raw, accountId) {
|
|
82
|
+
const cfg = loadConfig();
|
|
83
|
+
const accountInfo = resolveDiscordAccount({ cfg, accountId });
|
|
84
|
+
// First try to resolve using directory lookup (handles usernames)
|
|
85
|
+
const trimmed = raw.trim();
|
|
86
|
+
const parseOptions = {
|
|
87
|
+
ambiguousMessage: `Ambiguous Discord recipient "${trimmed}". Use "user:${trimmed}" for DMs or "channel:${trimmed}" for channel messages.`,
|
|
88
|
+
};
|
|
89
|
+
const resolved = await resolveDiscordTarget(raw, {
|
|
90
|
+
cfg,
|
|
91
|
+
accountId: accountInfo.accountId,
|
|
92
|
+
}, parseOptions);
|
|
93
|
+
if (resolved) {
|
|
94
|
+
return { kind: resolved.kind, id: resolved.id };
|
|
95
|
+
}
|
|
96
|
+
// Fallback to standard parsing (for channels, etc.)
|
|
97
|
+
const parsed = parseDiscordTarget(raw, parseOptions);
|
|
98
|
+
if (!parsed) {
|
|
99
|
+
throw new Error("Recipient is required for Discord sends");
|
|
100
|
+
}
|
|
101
|
+
return { kind: parsed.kind, id: parsed.id };
|
|
102
|
+
}
|
|
71
103
|
function normalizeStickerIds(raw) {
|
|
72
104
|
const ids = raw.map((entry) => entry.trim()).filter(Boolean);
|
|
73
105
|
if (ids.length === 0) {
|
|
@@ -102,29 +134,33 @@ function normalizeDiscordPollInput(input) {
|
|
|
102
134
|
};
|
|
103
135
|
}
|
|
104
136
|
function getDiscordErrorCode(err) {
|
|
105
|
-
if (!err || typeof err !== "object")
|
|
137
|
+
if (!err || typeof err !== "object") {
|
|
106
138
|
return undefined;
|
|
139
|
+
}
|
|
107
140
|
const candidate = "code" in err && err.code !== undefined
|
|
108
141
|
? err.code
|
|
109
142
|
: "rawError" in err && err.rawError && typeof err.rawError === "object"
|
|
110
143
|
? err.rawError.code
|
|
111
144
|
: undefined;
|
|
112
|
-
if (typeof candidate === "number")
|
|
145
|
+
if (typeof candidate === "number") {
|
|
113
146
|
return candidate;
|
|
147
|
+
}
|
|
114
148
|
if (typeof candidate === "string" && /^\d+$/.test(candidate)) {
|
|
115
149
|
return Number(candidate);
|
|
116
150
|
}
|
|
117
151
|
return undefined;
|
|
118
152
|
}
|
|
119
153
|
async function buildDiscordSendError(err, ctx) {
|
|
120
|
-
if (err instanceof DiscordSendError)
|
|
154
|
+
if (err instanceof DiscordSendError) {
|
|
121
155
|
return err;
|
|
156
|
+
}
|
|
122
157
|
const code = getDiscordErrorCode(err);
|
|
123
158
|
if (code === DISCORD_CANNOT_DM) {
|
|
124
159
|
return new DiscordSendError("discord dm failed: user blocks dms or privacy settings disallow it", { kind: "dm-blocked" });
|
|
125
160
|
}
|
|
126
|
-
if (code !== DISCORD_MISSING_PERMISSIONS)
|
|
161
|
+
if (code !== DISCORD_MISSING_PERMISSIONS) {
|
|
127
162
|
return err;
|
|
163
|
+
}
|
|
128
164
|
let missing = [];
|
|
129
165
|
try {
|
|
130
166
|
const permissions = await fetchChannelPermissionsDiscord(ctx.channelId, {
|
|
@@ -165,18 +201,26 @@ async function resolveChannelId(rest, recipient, request) {
|
|
|
165
201
|
}
|
|
166
202
|
return { channelId: dmChannel.id, dm: true };
|
|
167
203
|
}
|
|
204
|
+
export function buildDiscordTextChunks(text, opts = {}) {
|
|
205
|
+
if (!text) {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
const chunks = chunkDiscordTextWithMode(text, {
|
|
209
|
+
maxChars: opts.maxChars ?? DISCORD_TEXT_LIMIT,
|
|
210
|
+
maxLines: opts.maxLinesPerMessage,
|
|
211
|
+
chunkMode: opts.chunkMode,
|
|
212
|
+
});
|
|
213
|
+
if (!chunks.length && text) {
|
|
214
|
+
chunks.push(text);
|
|
215
|
+
}
|
|
216
|
+
return chunks;
|
|
217
|
+
}
|
|
168
218
|
async function sendDiscordText(rest, channelId, text, replyTo, request, maxLinesPerMessage, embeds, chunkMode) {
|
|
169
219
|
if (!text.trim()) {
|
|
170
220
|
throw new Error("Message must be non-empty for Discord sends");
|
|
171
221
|
}
|
|
172
222
|
const messageReference = replyTo ? { message_id: replyTo, fail_if_not_exists: false } : undefined;
|
|
173
|
-
const chunks =
|
|
174
|
-
maxChars: DISCORD_TEXT_LIMIT,
|
|
175
|
-
maxLines: maxLinesPerMessage,
|
|
176
|
-
chunkMode,
|
|
177
|
-
});
|
|
178
|
-
if (!chunks.length && text)
|
|
179
|
-
chunks.push(text);
|
|
223
|
+
const chunks = buildDiscordTextChunks(text, { maxLinesPerMessage, chunkMode });
|
|
180
224
|
if (chunks.length === 1) {
|
|
181
225
|
const res = (await request(() => rest.post(Routes.channelMessages(channelId), {
|
|
182
226
|
body: {
|
|
@@ -206,15 +250,7 @@ async function sendDiscordText(rest, channelId, text, replyTo, request, maxLines
|
|
|
206
250
|
}
|
|
207
251
|
async function sendDiscordMedia(rest, channelId, text, mediaUrl, replyTo, request, maxLinesPerMessage, embeds, chunkMode) {
|
|
208
252
|
const media = await loadWebMedia(mediaUrl);
|
|
209
|
-
const chunks = text
|
|
210
|
-
? chunkDiscordTextWithMode(text, {
|
|
211
|
-
maxChars: DISCORD_TEXT_LIMIT,
|
|
212
|
-
maxLines: maxLinesPerMessage,
|
|
213
|
-
chunkMode,
|
|
214
|
-
})
|
|
215
|
-
: [];
|
|
216
|
-
if (!chunks.length && text)
|
|
217
|
-
chunks.push(text);
|
|
253
|
+
const chunks = text ? buildDiscordTextChunks(text, { maxLinesPerMessage, chunkMode }) : [];
|
|
218
254
|
const caption = chunks[0] ?? "";
|
|
219
255
|
const messageReference = replyTo ? { message_id: replyTo, fail_if_not_exists: false } : undefined;
|
|
220
256
|
const res = (await request(() => rest.post(Routes.channelMessages(channelId), {
|
|
@@ -231,8 +267,9 @@ async function sendDiscordMedia(rest, channelId, text, mediaUrl, replyTo, reques
|
|
|
231
267
|
},
|
|
232
268
|
}), "media"));
|
|
233
269
|
for (const chunk of chunks.slice(1)) {
|
|
234
|
-
if (!chunk.trim())
|
|
270
|
+
if (!chunk.trim()) {
|
|
235
271
|
continue;
|
|
272
|
+
}
|
|
236
273
|
await sendDiscordText(rest, channelId, chunk, undefined, request, maxLinesPerMessage, undefined, chunkMode);
|
|
237
274
|
}
|
|
238
275
|
return res;
|