@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
package/dist/cron/store.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
import JSON5 from "json5";
|
|
1
2
|
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import
|
|
4
|
+
import { expandHomePrefix } from "../infra/home-dir.js";
|
|
5
5
|
import { CONFIG_DIR } from "../utils.js";
|
|
6
6
|
export const DEFAULT_CRON_DIR = path.join(CONFIG_DIR, "cron");
|
|
7
7
|
export const DEFAULT_CRON_STORE_PATH = path.join(DEFAULT_CRON_DIR, "jobs.json");
|
|
8
8
|
export function resolveCronStorePath(storePath) {
|
|
9
9
|
if (storePath?.trim()) {
|
|
10
10
|
const raw = storePath.trim();
|
|
11
|
-
if (raw.startsWith("~"))
|
|
12
|
-
return path.resolve(raw
|
|
11
|
+
if (raw.startsWith("~")) {
|
|
12
|
+
return path.resolve(expandHomePrefix(raw));
|
|
13
|
+
}
|
|
13
14
|
return path.resolve(raw);
|
|
14
15
|
}
|
|
15
16
|
return DEFAULT_CRON_STORE_PATH;
|
|
@@ -17,15 +18,29 @@ export function resolveCronStorePath(storePath) {
|
|
|
17
18
|
export async function loadCronStore(storePath) {
|
|
18
19
|
try {
|
|
19
20
|
const raw = await fs.promises.readFile(storePath, "utf-8");
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON5.parse(raw);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
throw new Error(`Failed to parse cron store at ${storePath}: ${String(err)}`, {
|
|
27
|
+
cause: err,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const parsedRecord = parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
31
|
+
? parsed
|
|
32
|
+
: {};
|
|
33
|
+
const jobs = Array.isArray(parsedRecord.jobs) ? parsedRecord.jobs : [];
|
|
22
34
|
return {
|
|
23
35
|
version: 1,
|
|
24
36
|
jobs: jobs.filter(Boolean),
|
|
25
37
|
};
|
|
26
38
|
}
|
|
27
|
-
catch {
|
|
28
|
-
|
|
39
|
+
catch (err) {
|
|
40
|
+
if (err?.code === "ENOENT") {
|
|
41
|
+
return { version: 1, jobs: [] };
|
|
42
|
+
}
|
|
43
|
+
throw err;
|
|
29
44
|
}
|
|
30
45
|
}
|
|
31
46
|
export async function saveCronStore(storePath, store) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { parseAbsoluteTimeMs } from "./parse.js";
|
|
2
|
+
const ONE_MINUTE_MS = 60 * 1000;
|
|
3
|
+
const TEN_YEARS_MS = 10 * 365.25 * 24 * 60 * 60 * 1000;
|
|
4
|
+
/**
|
|
5
|
+
* Validates at timestamps in cron schedules.
|
|
6
|
+
* Rejects timestamps that are:
|
|
7
|
+
* - More than 1 minute in the past
|
|
8
|
+
* - More than 10 years in the future
|
|
9
|
+
*/
|
|
10
|
+
export function validateScheduleTimestamp(schedule, nowMs = Date.now()) {
|
|
11
|
+
if (schedule.kind !== "at") {
|
|
12
|
+
return { ok: true };
|
|
13
|
+
}
|
|
14
|
+
const atRaw = typeof schedule.at === "string" ? schedule.at.trim() : "";
|
|
15
|
+
const atMs = atRaw ? parseAbsoluteTimeMs(atRaw) : null;
|
|
16
|
+
if (atMs === null || !Number.isFinite(atMs)) {
|
|
17
|
+
return {
|
|
18
|
+
ok: false,
|
|
19
|
+
message: `Invalid schedule.at: expected ISO-8601 timestamp (got ${String(schedule.at)})`,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const diffMs = atMs - nowMs;
|
|
23
|
+
// Check if timestamp is in the past (allow 1 minute grace period)
|
|
24
|
+
if (diffMs < -ONE_MINUTE_MS) {
|
|
25
|
+
const nowDate = new Date(nowMs).toISOString();
|
|
26
|
+
const atDate = new Date(atMs).toISOString();
|
|
27
|
+
const minutesAgo = Math.floor(-diffMs / ONE_MINUTE_MS);
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
message: `schedule.at is in the past: ${atDate} (${minutesAgo} minutes ago). Current time: ${nowDate}`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Check if timestamp is too far in the future
|
|
34
|
+
if (diffMs > TEN_YEARS_MS) {
|
|
35
|
+
const atDate = new Date(atMs).toISOString();
|
|
36
|
+
const yearsAhead = Math.floor(diffMs / (365.25 * 24 * 60 * 60 * 1000));
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
message: `schedule.at is too far in the future: ${atDate} (${yearsAhead} years ahead). Maximum allowed: 10 years`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return { ok: true };
|
|
43
|
+
}
|
package/dist/daemon/constants.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Default service labels (for backward compatibility and when no profile specified)
|
|
2
2
|
export const GATEWAY_LAUNCH_AGENT_LABEL = "com.poolbot.gateway";
|
|
3
3
|
export const GATEWAY_SYSTEMD_SERVICE_NAME = "poolbot-gateway";
|
|
4
|
-
export const GATEWAY_WINDOWS_TASK_NAME = "
|
|
4
|
+
export const GATEWAY_WINDOWS_TASK_NAME = "Poolbot Gateway";
|
|
5
5
|
export const GATEWAY_SERVICE_MARKER = "poolbot";
|
|
6
6
|
export const GATEWAY_SERVICE_KIND = "gateway";
|
|
7
7
|
export const NODE_LAUNCH_AGENT_LABEL = "com.poolbot.node";
|
|
8
8
|
export const NODE_SYSTEMD_SERVICE_NAME = "poolbot-node";
|
|
9
|
-
export const NODE_WINDOWS_TASK_NAME = "
|
|
9
|
+
export const NODE_WINDOWS_TASK_NAME = "Poolbot Node";
|
|
10
10
|
export const NODE_SERVICE_MARKER = "poolbot";
|
|
11
11
|
export const NODE_SERVICE_KIND = "node";
|
|
12
12
|
export const NODE_WINDOWS_TASK_SCRIPT_NAME = "node.cmd";
|
|
@@ -40,7 +40,7 @@ export function resolveGatewayWindowsTaskName(profile) {
|
|
|
40
40
|
const normalized = normalizeGatewayProfile(profile);
|
|
41
41
|
if (!normalized)
|
|
42
42
|
return GATEWAY_WINDOWS_TASK_NAME;
|
|
43
|
-
return `
|
|
43
|
+
return `Poolbot Gateway (${normalized})`;
|
|
44
44
|
}
|
|
45
45
|
export function formatGatewayServiceDescription(params) {
|
|
46
46
|
const profile = normalizeGatewayProfile(params?.profile);
|
|
@@ -51,8 +51,8 @@ export function formatGatewayServiceDescription(params) {
|
|
|
51
51
|
if (version)
|
|
52
52
|
parts.push(`v${version}`);
|
|
53
53
|
if (parts.length === 0)
|
|
54
|
-
return "
|
|
55
|
-
return `
|
|
54
|
+
return "Poolbot Gateway";
|
|
55
|
+
return `Poolbot Gateway (${parts.join(", ")})`;
|
|
56
56
|
}
|
|
57
57
|
export function resolveNodeLaunchAgentLabel() {
|
|
58
58
|
return NODE_LAUNCH_AGENT_LABEL;
|
|
@@ -66,6 +66,6 @@ export function resolveNodeWindowsTaskName() {
|
|
|
66
66
|
export function formatNodeServiceDescription(params) {
|
|
67
67
|
const version = params?.version?.trim();
|
|
68
68
|
if (!version)
|
|
69
|
-
return "
|
|
70
|
-
return `
|
|
69
|
+
return "Poolbot Node Host";
|
|
70
|
+
return `Poolbot Node Host (v${version})`;
|
|
71
71
|
}
|
package/dist/daemon/inspect.js
CHANGED
|
@@ -44,7 +44,7 @@ function hasGatewayServiceMarker(content) {
|
|
|
44
44
|
lower.includes("poolbot_service_kind") &&
|
|
45
45
|
lower.includes(GATEWAY_SERVICE_KIND.toLowerCase()));
|
|
46
46
|
}
|
|
47
|
-
function
|
|
47
|
+
function isPoolbotGatewayLaunchdService(label, contents) {
|
|
48
48
|
if (hasGatewayServiceMarker(contents))
|
|
49
49
|
return true;
|
|
50
50
|
const lowerContents = contents.toLowerCase();
|
|
@@ -52,14 +52,14 @@ function isMoltbotGatewayLaunchdService(label, contents) {
|
|
|
52
52
|
return false;
|
|
53
53
|
return label.startsWith("com.poolbot.");
|
|
54
54
|
}
|
|
55
|
-
function
|
|
55
|
+
function isPoolbotGatewaySystemdService(name, contents) {
|
|
56
56
|
if (hasGatewayServiceMarker(contents))
|
|
57
57
|
return true;
|
|
58
58
|
if (!name.startsWith("poolbot-gateway"))
|
|
59
59
|
return false;
|
|
60
60
|
return contents.toLowerCase().includes("gateway");
|
|
61
61
|
}
|
|
62
|
-
function
|
|
62
|
+
function isPoolbotGatewayTaskName(name) {
|
|
63
63
|
const normalized = name.trim().toLowerCase();
|
|
64
64
|
if (!normalized)
|
|
65
65
|
return false;
|
|
@@ -107,7 +107,7 @@ async function scanLaunchdDir(params) {
|
|
|
107
107
|
const label = tryExtractPlistLabel(contents) ?? labelFromName;
|
|
108
108
|
if (isIgnoredLaunchdLabel(label))
|
|
109
109
|
continue;
|
|
110
|
-
if (
|
|
110
|
+
if (isPoolbotGatewayLaunchdService(label, contents))
|
|
111
111
|
continue;
|
|
112
112
|
results.push({
|
|
113
113
|
platform: "darwin",
|
|
@@ -143,7 +143,7 @@ async function scanSystemdDir(params) {
|
|
|
143
143
|
}
|
|
144
144
|
if (!containsMarker(contents))
|
|
145
145
|
continue;
|
|
146
|
-
if (
|
|
146
|
+
if (isPoolbotGatewaySystemdService(name, contents))
|
|
147
147
|
continue;
|
|
148
148
|
results.push({
|
|
149
149
|
platform: "linux",
|
|
@@ -291,7 +291,7 @@ export async function findExtraGatewayServices(env, opts = {}) {
|
|
|
291
291
|
const name = task.name.trim();
|
|
292
292
|
if (!name)
|
|
293
293
|
continue;
|
|
294
|
-
if (
|
|
294
|
+
if (isPoolbotGatewayTaskName(name))
|
|
295
295
|
continue;
|
|
296
296
|
if (LEGACY_GATEWAY_WINDOWS_TASK_NAMES.includes(name))
|
|
297
297
|
continue;
|
|
@@ -13,7 +13,7 @@ function renderEnvLines(env) {
|
|
|
13
13
|
}
|
|
14
14
|
export function buildSystemdUnit({ description, programArguments, workingDirectory, environment, }) {
|
|
15
15
|
const execStart = programArguments.map(systemdEscapeArg).join(" ");
|
|
16
|
-
const descriptionLine = `Description=${description?.trim() || "
|
|
16
|
+
const descriptionLine = `Description=${description?.trim() || "Poolbot Gateway"}`;
|
|
17
17
|
const workingDirLine = workingDirectory
|
|
18
18
|
? `WorkingDirectory=${systemdEscapeArg(workingDirectory)}`
|
|
19
19
|
: null;
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { Button, StringSelectMenu, } from "@buape/carbon";
|
|
2
|
+
import { ButtonStyle, ChannelType } from "discord-api-types/v10";
|
|
3
|
+
import { logVerbose } from "../../globals.js";
|
|
4
|
+
import { enqueueSystemEvent } from "../../infra/system-events.js";
|
|
5
|
+
import { logDebug, logError } from "../../logger.js";
|
|
6
|
+
import { buildPairingReply } from "../../pairing/pairing-messages.js";
|
|
7
|
+
import { readChannelAllowFromStore, upsertChannelPairingRequest, } from "../../pairing/pairing-store.js";
|
|
8
|
+
import { resolveAgentRoute } from "../../routing/resolve-route.js";
|
|
9
|
+
import { normalizeDiscordAllowList, normalizeDiscordSlug, resolveDiscordAllowListMatch, resolveDiscordChannelConfigWithFallback, resolveDiscordGuildEntry, resolveDiscordUserAllowed, } from "./allow-list.js";
|
|
10
|
+
import { formatDiscordUserTag } from "./format.js";
|
|
11
|
+
const AGENT_BUTTON_KEY = "agent";
|
|
12
|
+
const AGENT_SELECT_KEY = "agentsel";
|
|
13
|
+
/**
|
|
14
|
+
* Build agent button custom ID: agent:componentId=<id>
|
|
15
|
+
* The channelId is NOT embedded in customId - we use interaction.rawData.channel_id instead
|
|
16
|
+
* to prevent channel spoofing attacks.
|
|
17
|
+
*
|
|
18
|
+
* Carbon's customIdParser parses "key:arg1=value1;arg2=value2" into { arg1: value1, arg2: value2 }
|
|
19
|
+
*/
|
|
20
|
+
export function buildAgentButtonCustomId(componentId) {
|
|
21
|
+
return `${AGENT_BUTTON_KEY}:componentId=${encodeURIComponent(componentId)}`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build agent select menu custom ID: agentsel:componentId=<id>
|
|
25
|
+
*/
|
|
26
|
+
export function buildAgentSelectCustomId(componentId) {
|
|
27
|
+
return `${AGENT_SELECT_KEY}:componentId=${encodeURIComponent(componentId)}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse agent component data from Carbon's parsed ComponentData
|
|
31
|
+
* Carbon parses "key:componentId=xxx" into { componentId: "xxx" }
|
|
32
|
+
*/
|
|
33
|
+
function parseAgentComponentData(data) {
|
|
34
|
+
if (!data || typeof data !== "object") {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const componentId = typeof data.componentId === "string"
|
|
38
|
+
? decodeURIComponent(data.componentId)
|
|
39
|
+
: typeof data.componentId === "number"
|
|
40
|
+
? String(data.componentId)
|
|
41
|
+
: null;
|
|
42
|
+
if (!componentId) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return { componentId };
|
|
46
|
+
}
|
|
47
|
+
function formatUsername(user) {
|
|
48
|
+
if (user.discriminator && user.discriminator !== "0") {
|
|
49
|
+
return `${user.username}#${user.discriminator}`;
|
|
50
|
+
}
|
|
51
|
+
return user.username;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if a channel type is a thread type
|
|
55
|
+
*/
|
|
56
|
+
function isThreadChannelType(channelType) {
|
|
57
|
+
return (channelType === ChannelType.PublicThread ||
|
|
58
|
+
channelType === ChannelType.PrivateThread ||
|
|
59
|
+
channelType === ChannelType.AnnouncementThread);
|
|
60
|
+
}
|
|
61
|
+
async function ensureDmComponentAuthorized(params) {
|
|
62
|
+
const { ctx, interaction, user, componentLabel } = params;
|
|
63
|
+
const dmPolicy = ctx.dmPolicy ?? "pairing";
|
|
64
|
+
if (dmPolicy === "disabled") {
|
|
65
|
+
logVerbose(`agent ${componentLabel}: blocked (DM policy disabled)`);
|
|
66
|
+
try {
|
|
67
|
+
await interaction.reply({
|
|
68
|
+
content: "DM interactions are disabled.",
|
|
69
|
+
ephemeral: true,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Interaction may have expired
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (dmPolicy === "open") {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
const storeAllowFrom = await readChannelAllowFromStore("discord").catch(() => []);
|
|
81
|
+
const effectiveAllowFrom = [...(ctx.allowFrom ?? []), ...storeAllowFrom];
|
|
82
|
+
const allowList = normalizeDiscordAllowList(effectiveAllowFrom, ["discord:", "user:", "pk:"]);
|
|
83
|
+
const allowMatch = allowList
|
|
84
|
+
? resolveDiscordAllowListMatch({
|
|
85
|
+
allowList,
|
|
86
|
+
candidate: {
|
|
87
|
+
id: user.id,
|
|
88
|
+
name: user.username,
|
|
89
|
+
tag: formatDiscordUserTag(user),
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
: { allowed: false };
|
|
93
|
+
if (allowMatch.allowed) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (dmPolicy === "pairing") {
|
|
97
|
+
const { code, created } = await upsertChannelPairingRequest({
|
|
98
|
+
channel: "discord",
|
|
99
|
+
id: user.id,
|
|
100
|
+
meta: {
|
|
101
|
+
tag: formatDiscordUserTag(user),
|
|
102
|
+
name: user.username,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
try {
|
|
106
|
+
await interaction.reply({
|
|
107
|
+
content: created
|
|
108
|
+
? buildPairingReply({
|
|
109
|
+
channel: "discord",
|
|
110
|
+
idLine: `Your Discord user id: ${user.id}`,
|
|
111
|
+
code,
|
|
112
|
+
})
|
|
113
|
+
: "Pairing already requested. Ask the bot owner to approve your code.",
|
|
114
|
+
ephemeral: true,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Interaction may have expired
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
logVerbose(`agent ${componentLabel}: blocked DM user ${user.id} (not in allowFrom)`);
|
|
123
|
+
try {
|
|
124
|
+
await interaction.reply({
|
|
125
|
+
content: `You are not authorized to use this ${componentLabel}.`,
|
|
126
|
+
ephemeral: true,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Interaction may have expired
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
export class AgentComponentButton extends Button {
|
|
135
|
+
label = AGENT_BUTTON_KEY;
|
|
136
|
+
customId = `${AGENT_BUTTON_KEY}:seed=1`;
|
|
137
|
+
style = ButtonStyle.Primary;
|
|
138
|
+
ctx;
|
|
139
|
+
constructor(ctx) {
|
|
140
|
+
super();
|
|
141
|
+
this.ctx = ctx;
|
|
142
|
+
}
|
|
143
|
+
async run(interaction, data) {
|
|
144
|
+
// Parse componentId from Carbon's parsed ComponentData
|
|
145
|
+
const parsed = parseAgentComponentData(data);
|
|
146
|
+
if (!parsed) {
|
|
147
|
+
logError("agent button: failed to parse component data");
|
|
148
|
+
try {
|
|
149
|
+
await interaction.reply({
|
|
150
|
+
content: "This button is no longer valid.",
|
|
151
|
+
ephemeral: true,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// Interaction may have expired
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const { componentId } = parsed;
|
|
160
|
+
// P1 FIX: Use interaction's actual channel_id instead of trusting customId
|
|
161
|
+
// This prevents channel ID spoofing attacks where an attacker crafts a button
|
|
162
|
+
// with a different channelId to inject events into other sessions
|
|
163
|
+
const channelId = interaction.rawData.channel_id;
|
|
164
|
+
if (!channelId) {
|
|
165
|
+
logError("agent button: missing channel_id in interaction");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const user = interaction.user;
|
|
169
|
+
if (!user) {
|
|
170
|
+
logError("agent button: missing user in interaction");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const username = formatUsername(user);
|
|
174
|
+
const userId = user.id;
|
|
175
|
+
// P1 FIX: Use rawData.guild_id as source of truth - interaction.guild can be null
|
|
176
|
+
// when guild is not cached even though guild_id is present in rawData
|
|
177
|
+
const rawGuildId = interaction.rawData.guild_id;
|
|
178
|
+
const isDirectMessage = !rawGuildId;
|
|
179
|
+
if (isDirectMessage) {
|
|
180
|
+
const authorized = await ensureDmComponentAuthorized({
|
|
181
|
+
ctx: this.ctx,
|
|
182
|
+
interaction,
|
|
183
|
+
user,
|
|
184
|
+
componentLabel: "button",
|
|
185
|
+
});
|
|
186
|
+
if (!authorized) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// P2 FIX: Check user allowlist before processing component interaction
|
|
191
|
+
// This prevents unauthorized users from injecting system events
|
|
192
|
+
const guild = interaction.guild;
|
|
193
|
+
const guildInfo = resolveDiscordGuildEntry({
|
|
194
|
+
guild: guild ?? undefined,
|
|
195
|
+
guildEntries: this.ctx.guildEntries,
|
|
196
|
+
});
|
|
197
|
+
// Resolve channel info for thread detection and allowlist inheritance
|
|
198
|
+
const channel = interaction.channel;
|
|
199
|
+
const channelName = channel && "name" in channel ? channel.name : undefined;
|
|
200
|
+
const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
|
|
201
|
+
const channelType = channel && "type" in channel ? channel.type : undefined;
|
|
202
|
+
const isThread = isThreadChannelType(channelType);
|
|
203
|
+
// Resolve thread parent for allowlist inheritance
|
|
204
|
+
// Note: We can get parentId from channel but cannot fetch parent name without a client.
|
|
205
|
+
// The parentId alone enables ID-based parent config matching. Name-based matching
|
|
206
|
+
// requires the channel cache to have parent info available.
|
|
207
|
+
let parentId;
|
|
208
|
+
let parentName;
|
|
209
|
+
let parentSlug = "";
|
|
210
|
+
if (isThread && channel && "parentId" in channel) {
|
|
211
|
+
parentId = channel.parentId ?? undefined;
|
|
212
|
+
// Try to get parent name from channel's parent if available
|
|
213
|
+
if ("parent" in channel) {
|
|
214
|
+
const parent = channel.parent;
|
|
215
|
+
if (parent?.name) {
|
|
216
|
+
parentName = parent.name;
|
|
217
|
+
parentSlug = normalizeDiscordSlug(parentName);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Only check guild allowlists if this is a guild interaction
|
|
222
|
+
if (rawGuildId) {
|
|
223
|
+
const channelConfig = resolveDiscordChannelConfigWithFallback({
|
|
224
|
+
guildInfo,
|
|
225
|
+
channelId,
|
|
226
|
+
channelName,
|
|
227
|
+
channelSlug,
|
|
228
|
+
parentId,
|
|
229
|
+
parentName,
|
|
230
|
+
parentSlug,
|
|
231
|
+
scope: isThread ? "thread" : "channel",
|
|
232
|
+
});
|
|
233
|
+
const channelUsers = channelConfig?.users ?? guildInfo?.users;
|
|
234
|
+
if (Array.isArray(channelUsers) && channelUsers.length > 0) {
|
|
235
|
+
const userOk = resolveDiscordUserAllowed({
|
|
236
|
+
allowList: channelUsers,
|
|
237
|
+
userId,
|
|
238
|
+
userName: user.username,
|
|
239
|
+
userTag: user.discriminator ? `${user.username}#${user.discriminator}` : undefined,
|
|
240
|
+
});
|
|
241
|
+
if (!userOk) {
|
|
242
|
+
logVerbose(`agent button: blocked user ${userId} (not in allowlist)`);
|
|
243
|
+
try {
|
|
244
|
+
await interaction.reply({
|
|
245
|
+
content: "You are not authorized to use this button.",
|
|
246
|
+
ephemeral: true,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
// Interaction may have expired
|
|
251
|
+
}
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Resolve route with full context (guildId, proper peer kind)
|
|
257
|
+
const route = resolveAgentRoute({
|
|
258
|
+
cfg: this.ctx.cfg,
|
|
259
|
+
channel: "discord",
|
|
260
|
+
accountId: this.ctx.accountId,
|
|
261
|
+
guildId: rawGuildId,
|
|
262
|
+
peer: {
|
|
263
|
+
kind: isDirectMessage ? "dm" : "channel",
|
|
264
|
+
id: isDirectMessage ? userId : channelId,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
const eventText = `[Discord component: ${componentId} clicked by ${username} (${userId})]`;
|
|
268
|
+
logDebug(`agent button: enqueuing event for channel ${channelId}: ${eventText}`);
|
|
269
|
+
enqueueSystemEvent(eventText, {
|
|
270
|
+
sessionKey: route.sessionKey,
|
|
271
|
+
contextKey: `discord:agent-button:${channelId}:${componentId}:${userId}`,
|
|
272
|
+
});
|
|
273
|
+
// Acknowledge the interaction
|
|
274
|
+
try {
|
|
275
|
+
await interaction.reply({
|
|
276
|
+
content: "✓",
|
|
277
|
+
ephemeral: true,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
logError(`agent button: failed to acknowledge interaction: ${String(err)}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
export class AgentSelectMenu extends StringSelectMenu {
|
|
286
|
+
customId = `${AGENT_SELECT_KEY}:seed=1`;
|
|
287
|
+
options = [];
|
|
288
|
+
ctx;
|
|
289
|
+
constructor(ctx) {
|
|
290
|
+
super();
|
|
291
|
+
this.ctx = ctx;
|
|
292
|
+
}
|
|
293
|
+
async run(interaction, data) {
|
|
294
|
+
// Parse componentId from Carbon's parsed ComponentData
|
|
295
|
+
const parsed = parseAgentComponentData(data);
|
|
296
|
+
if (!parsed) {
|
|
297
|
+
logError("agent select: failed to parse component data");
|
|
298
|
+
try {
|
|
299
|
+
await interaction.reply({
|
|
300
|
+
content: "This select menu is no longer valid.",
|
|
301
|
+
ephemeral: true,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
// Interaction may have expired
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const { componentId } = parsed;
|
|
310
|
+
// Use interaction's actual channel_id (trusted source from Discord)
|
|
311
|
+
// This prevents channel spoofing attacks
|
|
312
|
+
const channelId = interaction.rawData.channel_id;
|
|
313
|
+
if (!channelId) {
|
|
314
|
+
logError("agent select: missing channel_id in interaction");
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const user = interaction.user;
|
|
318
|
+
if (!user) {
|
|
319
|
+
logError("agent select: missing user in interaction");
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const username = formatUsername(user);
|
|
323
|
+
const userId = user.id;
|
|
324
|
+
// P1 FIX: Use rawData.guild_id as source of truth - interaction.guild can be null
|
|
325
|
+
// when guild is not cached even though guild_id is present in rawData
|
|
326
|
+
const rawGuildId = interaction.rawData.guild_id;
|
|
327
|
+
const isDirectMessage = !rawGuildId;
|
|
328
|
+
if (isDirectMessage) {
|
|
329
|
+
const authorized = await ensureDmComponentAuthorized({
|
|
330
|
+
ctx: this.ctx,
|
|
331
|
+
interaction,
|
|
332
|
+
user,
|
|
333
|
+
componentLabel: "select menu",
|
|
334
|
+
});
|
|
335
|
+
if (!authorized) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// Check user allowlist before processing component interaction
|
|
340
|
+
const guild = interaction.guild;
|
|
341
|
+
const guildInfo = resolveDiscordGuildEntry({
|
|
342
|
+
guild: guild ?? undefined,
|
|
343
|
+
guildEntries: this.ctx.guildEntries,
|
|
344
|
+
});
|
|
345
|
+
// Resolve channel info for thread detection and allowlist inheritance
|
|
346
|
+
const channel = interaction.channel;
|
|
347
|
+
const channelName = channel && "name" in channel ? channel.name : undefined;
|
|
348
|
+
const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
|
|
349
|
+
const channelType = channel && "type" in channel ? channel.type : undefined;
|
|
350
|
+
const isThread = isThreadChannelType(channelType);
|
|
351
|
+
// Resolve thread parent for allowlist inheritance
|
|
352
|
+
let parentId;
|
|
353
|
+
let parentName;
|
|
354
|
+
let parentSlug = "";
|
|
355
|
+
if (isThread && channel && "parentId" in channel) {
|
|
356
|
+
parentId = channel.parentId ?? undefined;
|
|
357
|
+
// Try to get parent name from channel's parent if available
|
|
358
|
+
if ("parent" in channel) {
|
|
359
|
+
const parent = channel.parent;
|
|
360
|
+
if (parent?.name) {
|
|
361
|
+
parentName = parent.name;
|
|
362
|
+
parentSlug = normalizeDiscordSlug(parentName);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// Only check guild allowlists if this is a guild interaction
|
|
367
|
+
if (rawGuildId) {
|
|
368
|
+
const channelConfig = resolveDiscordChannelConfigWithFallback({
|
|
369
|
+
guildInfo,
|
|
370
|
+
channelId,
|
|
371
|
+
channelName,
|
|
372
|
+
channelSlug,
|
|
373
|
+
parentId,
|
|
374
|
+
parentName,
|
|
375
|
+
parentSlug,
|
|
376
|
+
scope: isThread ? "thread" : "channel",
|
|
377
|
+
});
|
|
378
|
+
const channelUsers = channelConfig?.users ?? guildInfo?.users;
|
|
379
|
+
if (Array.isArray(channelUsers) && channelUsers.length > 0) {
|
|
380
|
+
const userOk = resolveDiscordUserAllowed({
|
|
381
|
+
allowList: channelUsers,
|
|
382
|
+
userId,
|
|
383
|
+
userName: user.username,
|
|
384
|
+
userTag: user.discriminator ? `${user.username}#${user.discriminator}` : undefined,
|
|
385
|
+
});
|
|
386
|
+
if (!userOk) {
|
|
387
|
+
logVerbose(`agent select: blocked user ${userId} (not in allowlist)`);
|
|
388
|
+
try {
|
|
389
|
+
await interaction.reply({
|
|
390
|
+
content: "You are not authorized to use this select menu.",
|
|
391
|
+
ephemeral: true,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
// Interaction may have expired
|
|
396
|
+
}
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// Extract selected values
|
|
402
|
+
const values = interaction.values ?? [];
|
|
403
|
+
const valuesText = values.length > 0 ? ` (selected: ${values.join(", ")})` : "";
|
|
404
|
+
// Resolve route with full context (guildId, proper peer kind)
|
|
405
|
+
const route = resolveAgentRoute({
|
|
406
|
+
cfg: this.ctx.cfg,
|
|
407
|
+
channel: "discord",
|
|
408
|
+
accountId: this.ctx.accountId,
|
|
409
|
+
guildId: rawGuildId,
|
|
410
|
+
peer: {
|
|
411
|
+
kind: isDirectMessage ? "dm" : "channel",
|
|
412
|
+
id: isDirectMessage ? userId : channelId,
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
const eventText = `[Discord select menu: ${componentId} interacted by ${username} (${userId})${valuesText}]`;
|
|
416
|
+
logDebug(`agent select: enqueuing event for channel ${channelId}: ${eventText}`);
|
|
417
|
+
enqueueSystemEvent(eventText, {
|
|
418
|
+
sessionKey: route.sessionKey,
|
|
419
|
+
contextKey: `discord:agent-select:${channelId}:${componentId}:${userId}`,
|
|
420
|
+
});
|
|
421
|
+
// Acknowledge the interaction
|
|
422
|
+
try {
|
|
423
|
+
await interaction.reply({
|
|
424
|
+
content: "✓",
|
|
425
|
+
ephemeral: true,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
catch (err) {
|
|
429
|
+
logError(`agent select: failed to acknowledge interaction: ${String(err)}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
export function createAgentComponentButton(ctx) {
|
|
434
|
+
return new AgentComponentButton(ctx);
|
|
435
|
+
}
|
|
436
|
+
export function createAgentSelectMenu(ctx) {
|
|
437
|
+
return new AgentSelectMenu(ctx);
|
|
438
|
+
}
|