@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
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram inline button utilities for model selection.
|
|
3
|
+
*
|
|
4
|
+
* Callback data patterns (max 64 bytes for Telegram):
|
|
5
|
+
* - mdl_prov - show providers list
|
|
6
|
+
* - mdl_list_{prov}_{pg} - show models for provider (page N, 1-indexed)
|
|
7
|
+
* - mdl_sel_{provider/id} - select model
|
|
8
|
+
* - mdl_back - back to providers list
|
|
9
|
+
*/
|
|
10
|
+
const MODELS_PAGE_SIZE = 8;
|
|
11
|
+
const MAX_CALLBACK_DATA_BYTES = 64;
|
|
12
|
+
/**
|
|
13
|
+
* Parse a model callback_data string into a structured object.
|
|
14
|
+
* Returns null if the data doesn't match a known pattern.
|
|
15
|
+
*/
|
|
16
|
+
export function parseModelCallbackData(data) {
|
|
17
|
+
const trimmed = data.trim();
|
|
18
|
+
if (!trimmed.startsWith("mdl_")) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (trimmed === "mdl_prov" || trimmed === "mdl_back") {
|
|
22
|
+
return { type: trimmed === "mdl_prov" ? "providers" : "back" };
|
|
23
|
+
}
|
|
24
|
+
// mdl_list_{provider}_{page}
|
|
25
|
+
const listMatch = trimmed.match(/^mdl_list_([a-z0-9_-]+)_(\d+)$/i);
|
|
26
|
+
if (listMatch) {
|
|
27
|
+
const [, provider, pageStr] = listMatch;
|
|
28
|
+
const page = Number.parseInt(pageStr ?? "1", 10);
|
|
29
|
+
if (provider && Number.isFinite(page) && page >= 1) {
|
|
30
|
+
return { type: "list", provider, page };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// mdl_sel_{provider/model}
|
|
34
|
+
const selMatch = trimmed.match(/^mdl_sel_(.+)$/);
|
|
35
|
+
if (selMatch) {
|
|
36
|
+
const modelRef = selMatch[1];
|
|
37
|
+
if (modelRef) {
|
|
38
|
+
const slashIndex = modelRef.indexOf("/");
|
|
39
|
+
if (slashIndex > 0 && slashIndex < modelRef.length - 1) {
|
|
40
|
+
return {
|
|
41
|
+
type: "select",
|
|
42
|
+
provider: modelRef.slice(0, slashIndex),
|
|
43
|
+
model: modelRef.slice(slashIndex + 1),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build provider selection keyboard with 2 providers per row.
|
|
52
|
+
*/
|
|
53
|
+
export function buildProviderKeyboard(providers) {
|
|
54
|
+
if (providers.length === 0) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const rows = [];
|
|
58
|
+
let currentRow = [];
|
|
59
|
+
for (const provider of providers) {
|
|
60
|
+
const button = {
|
|
61
|
+
text: `${provider.id} (${provider.count})`,
|
|
62
|
+
callback_data: `mdl_list_${provider.id}_1`,
|
|
63
|
+
};
|
|
64
|
+
currentRow.push(button);
|
|
65
|
+
if (currentRow.length === 2) {
|
|
66
|
+
rows.push(currentRow);
|
|
67
|
+
currentRow = [];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Push any remaining button
|
|
71
|
+
if (currentRow.length > 0) {
|
|
72
|
+
rows.push(currentRow);
|
|
73
|
+
}
|
|
74
|
+
return rows;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build model list keyboard with pagination and back button.
|
|
78
|
+
*/
|
|
79
|
+
export function buildModelsKeyboard(params) {
|
|
80
|
+
const { provider, models, currentModel, currentPage, totalPages } = params;
|
|
81
|
+
const pageSize = params.pageSize ?? MODELS_PAGE_SIZE;
|
|
82
|
+
if (models.length === 0) {
|
|
83
|
+
return [[{ text: "<< Back", callback_data: "mdl_back" }]];
|
|
84
|
+
}
|
|
85
|
+
const rows = [];
|
|
86
|
+
// Calculate page slice
|
|
87
|
+
const startIndex = (currentPage - 1) * pageSize;
|
|
88
|
+
const endIndex = Math.min(startIndex + pageSize, models.length);
|
|
89
|
+
const pageModels = models.slice(startIndex, endIndex);
|
|
90
|
+
// Model buttons - one per row
|
|
91
|
+
const currentModelId = currentModel?.includes("/")
|
|
92
|
+
? currentModel.split("/").slice(1).join("/")
|
|
93
|
+
: currentModel;
|
|
94
|
+
for (const model of pageModels) {
|
|
95
|
+
const callbackData = `mdl_sel_${provider}/${model}`;
|
|
96
|
+
// Skip models that would exceed Telegram's callback_data limit
|
|
97
|
+
if (Buffer.byteLength(callbackData, "utf8") > MAX_CALLBACK_DATA_BYTES) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const isCurrentModel = model === currentModelId;
|
|
101
|
+
const displayText = truncateModelId(model, 38);
|
|
102
|
+
const text = isCurrentModel ? `${displayText} ✓` : displayText;
|
|
103
|
+
rows.push([
|
|
104
|
+
{
|
|
105
|
+
text,
|
|
106
|
+
callback_data: callbackData,
|
|
107
|
+
},
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
// Pagination row
|
|
111
|
+
if (totalPages > 1) {
|
|
112
|
+
const paginationRow = [];
|
|
113
|
+
if (currentPage > 1) {
|
|
114
|
+
paginationRow.push({
|
|
115
|
+
text: "◀ Prev",
|
|
116
|
+
callback_data: `mdl_list_${provider}_${currentPage - 1}`,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
paginationRow.push({
|
|
120
|
+
text: `${currentPage}/${totalPages}`,
|
|
121
|
+
callback_data: `mdl_list_${provider}_${currentPage}`, // noop
|
|
122
|
+
});
|
|
123
|
+
if (currentPage < totalPages) {
|
|
124
|
+
paginationRow.push({
|
|
125
|
+
text: "Next ▶",
|
|
126
|
+
callback_data: `mdl_list_${provider}_${currentPage + 1}`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
rows.push(paginationRow);
|
|
130
|
+
}
|
|
131
|
+
// Back button
|
|
132
|
+
rows.push([{ text: "<< Back", callback_data: "mdl_back" }]);
|
|
133
|
+
return rows;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Build "Browse providers" button for /model summary.
|
|
137
|
+
*/
|
|
138
|
+
export function buildBrowseProvidersButton() {
|
|
139
|
+
return [[{ text: "Browse providers", callback_data: "mdl_prov" }]];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Truncate model ID for display, preserving end if too long.
|
|
143
|
+
*/
|
|
144
|
+
function truncateModelId(modelId, maxLen) {
|
|
145
|
+
if (modelId.length <= maxLen) {
|
|
146
|
+
return modelId;
|
|
147
|
+
}
|
|
148
|
+
// Show last part with ellipsis prefix
|
|
149
|
+
return `…${modelId.slice(-(maxLen - 1))}`;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get page size for model list pagination.
|
|
153
|
+
*/
|
|
154
|
+
export function getModelsPageSize() {
|
|
155
|
+
return MODELS_PAGE_SIZE;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Calculate total pages for a model list.
|
|
159
|
+
*/
|
|
160
|
+
export function calculateTotalPages(totalModels, pageSize) {
|
|
161
|
+
const size = pageSize ?? MODELS_PAGE_SIZE;
|
|
162
|
+
return size > 0 ? Math.ceil(totalModels / size) : 1;
|
|
163
|
+
}
|
package/dist/telegram/monitor.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { run } from "@grammyjs/runner";
|
|
2
|
-
import { loadConfig } from "../config/config.js";
|
|
3
2
|
import { resolveAgentMaxConcurrent } from "../config/agent-limits.js";
|
|
3
|
+
import { loadConfig } from "../config/config.js";
|
|
4
4
|
import { computeBackoff, sleepWithAbort } from "../infra/backoff.js";
|
|
5
5
|
import { formatErrorMessage } from "../infra/errors.js";
|
|
6
|
-
import {
|
|
6
|
+
import { formatDurationPrecise } from "../infra/format-time/format-duration.js";
|
|
7
|
+
import { registerUnhandledRejectionHandler } from "../infra/unhandled-rejections.js";
|
|
7
8
|
import { resolveTelegramAccount } from "./accounts.js";
|
|
8
9
|
import { resolveTelegramAllowedUpdates } from "./allowed-updates.js";
|
|
9
10
|
import { createTelegramBot } from "./bot.js";
|
|
@@ -50,101 +51,125 @@ const isGetUpdatesConflict = (err) => {
|
|
|
50
51
|
.toLowerCase();
|
|
51
52
|
return haystack.includes("getupdates");
|
|
52
53
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
accountId: opts.accountId,
|
|
58
|
-
});
|
|
59
|
-
const token = opts.token?.trim() || account.token;
|
|
60
|
-
if (!token) {
|
|
61
|
-
throw new Error(`Telegram bot token missing for account "${account.accountId}" (set channels.telegram.accounts.${account.accountId}.botToken/tokenFile or TELEGRAM_BOT_TOKEN for default).`);
|
|
54
|
+
/** Check if error is a Grammy HttpError (used to scope unhandled rejection handling) */
|
|
55
|
+
const isGrammyHttpError = (err) => {
|
|
56
|
+
if (!err || typeof err !== "object") {
|
|
57
|
+
return false;
|
|
62
58
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
updateId,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
catch (err) {
|
|
79
|
-
(opts.runtime?.error ?? console.error)(`telegram: failed to persist update offset: ${String(err)}`);
|
|
59
|
+
return err.name === "HttpError";
|
|
60
|
+
};
|
|
61
|
+
export async function monitorTelegramProvider(opts = {}) {
|
|
62
|
+
const log = opts.runtime?.error ?? console.error;
|
|
63
|
+
// Register handler for Grammy HttpError unhandled rejections.
|
|
64
|
+
// This catches network errors that escape the polling loop's try-catch
|
|
65
|
+
// (e.g., from setMyCommands during bot setup).
|
|
66
|
+
// We gate on isGrammyHttpError to avoid suppressing non-Telegram errors.
|
|
67
|
+
const unregisterHandler = registerUnhandledRejectionHandler((err) => {
|
|
68
|
+
if (isGrammyHttpError(err) && isRecoverableTelegramNetworkError(err, { context: "polling" })) {
|
|
69
|
+
log(`[telegram] Suppressed network error: ${formatErrorMessage(err)}`);
|
|
70
|
+
return true; // handled - don't crash
|
|
80
71
|
}
|
|
81
|
-
|
|
82
|
-
const bot = createTelegramBot({
|
|
83
|
-
token,
|
|
84
|
-
runtime: opts.runtime,
|
|
85
|
-
proxyFetch,
|
|
86
|
-
config: cfg,
|
|
87
|
-
accountId: account.accountId,
|
|
88
|
-
updateOffset: {
|
|
89
|
-
lastUpdateId,
|
|
90
|
-
onUpdateId: persistUpdateId,
|
|
91
|
-
},
|
|
72
|
+
return false;
|
|
92
73
|
});
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
74
|
+
try {
|
|
75
|
+
const cfg = opts.config ?? loadConfig();
|
|
76
|
+
const account = resolveTelegramAccount({
|
|
77
|
+
cfg,
|
|
78
|
+
accountId: opts.accountId,
|
|
79
|
+
});
|
|
80
|
+
const token = opts.token?.trim() || account.token;
|
|
81
|
+
if (!token) {
|
|
82
|
+
throw new Error(`Telegram bot token missing for account "${account.accountId}" (set channels.telegram.accounts.${account.accountId}.botToken/tokenFile or TELEGRAM_BOT_TOKEN for default).`);
|
|
83
|
+
}
|
|
84
|
+
const proxyFetch = opts.proxyFetch ??
|
|
85
|
+
(account.config.proxy ? makeProxyFetch(account.config.proxy) : undefined);
|
|
86
|
+
let lastUpdateId = await readTelegramUpdateOffset({
|
|
96
87
|
accountId: account.accountId,
|
|
97
|
-
config: cfg,
|
|
98
|
-
path: opts.webhookPath,
|
|
99
|
-
port: opts.webhookPort,
|
|
100
|
-
secret: opts.webhookSecret,
|
|
101
|
-
runtime: opts.runtime,
|
|
102
|
-
fetch: proxyFetch,
|
|
103
|
-
abortSignal: opts.abortSignal,
|
|
104
|
-
publicUrl: opts.webhookUrl,
|
|
105
88
|
});
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
89
|
+
const persistUpdateId = async (updateId) => {
|
|
90
|
+
if (lastUpdateId !== null && updateId <= lastUpdateId)
|
|
91
|
+
return;
|
|
92
|
+
lastUpdateId = updateId;
|
|
93
|
+
try {
|
|
94
|
+
await writeTelegramUpdateOffset({
|
|
95
|
+
accountId: account.accountId,
|
|
96
|
+
updateId,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
(opts.runtime?.error ?? console.error)(`telegram: failed to persist update offset: ${String(err)}`);
|
|
115
101
|
}
|
|
116
102
|
};
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
103
|
+
const bot = createTelegramBot({
|
|
104
|
+
token,
|
|
105
|
+
runtime: opts.runtime,
|
|
106
|
+
proxyFetch,
|
|
107
|
+
config: cfg,
|
|
108
|
+
accountId: account.accountId,
|
|
109
|
+
updateOffset: {
|
|
110
|
+
lastUpdateId,
|
|
111
|
+
onUpdateId: persistUpdateId,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
if (opts.useWebhook) {
|
|
115
|
+
await startTelegramWebhook({
|
|
116
|
+
token,
|
|
117
|
+
accountId: account.accountId,
|
|
118
|
+
config: cfg,
|
|
119
|
+
path: opts.webhookPath,
|
|
120
|
+
port: opts.webhookPort,
|
|
121
|
+
secret: opts.webhookSecret,
|
|
122
|
+
runtime: opts.runtime,
|
|
123
|
+
fetch: proxyFetch,
|
|
124
|
+
abortSignal: opts.abortSignal,
|
|
125
|
+
publicUrl: opts.webhookUrl,
|
|
126
|
+
});
|
|
121
127
|
return;
|
|
122
128
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const delayMs = computeBackoff(TELEGRAM_POLL_RESTART_POLICY, restartAttempts);
|
|
134
|
-
const reason = isConflict ? "getUpdates conflict" : "network error";
|
|
135
|
-
const errMsg = formatErrorMessage(err);
|
|
136
|
-
(opts.runtime?.error ?? console.error)(`Telegram ${reason}: ${errMsg}; retrying in ${formatDurationMs(delayMs)}.`);
|
|
129
|
+
// Use grammyjs/runner for concurrent update processing
|
|
130
|
+
let restartAttempts = 0;
|
|
131
|
+
while (!opts.abortSignal?.aborted) {
|
|
132
|
+
const runner = run(bot, createTelegramRunnerOptions(cfg));
|
|
133
|
+
const stopOnAbort = () => {
|
|
134
|
+
if (opts.abortSignal?.aborted) {
|
|
135
|
+
void runner.stop();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
opts.abortSignal?.addEventListener("abort", stopOnAbort, { once: true });
|
|
137
139
|
try {
|
|
138
|
-
|
|
140
|
+
// runner.task() returns a promise that resolves when the runner stops
|
|
141
|
+
await runner.task();
|
|
142
|
+
return;
|
|
139
143
|
}
|
|
140
|
-
catch (
|
|
141
|
-
if (opts.abortSignal?.aborted)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
catch (err) {
|
|
145
|
+
if (opts.abortSignal?.aborted) {
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
const isConflict = isGetUpdatesConflict(err);
|
|
149
|
+
const isRecoverable = isRecoverableTelegramNetworkError(err, { context: "polling" });
|
|
150
|
+
if (!isConflict && !isRecoverable) {
|
|
151
|
+
throw err;
|
|
152
|
+
}
|
|
153
|
+
restartAttempts += 1;
|
|
154
|
+
const delayMs = computeBackoff(TELEGRAM_POLL_RESTART_POLICY, restartAttempts);
|
|
155
|
+
const reason = isConflict ? "getUpdates conflict" : "network error";
|
|
156
|
+
const errMsg = formatErrorMessage(err);
|
|
157
|
+
(opts.runtime?.error ?? console.error)(`Telegram ${reason}: ${errMsg}; retrying in ${formatDurationPrecise(delayMs)}.`);
|
|
158
|
+
try {
|
|
159
|
+
await sleepWithAbort(delayMs, opts.abortSignal);
|
|
160
|
+
}
|
|
161
|
+
catch (sleepErr) {
|
|
162
|
+
if (opts.abortSignal?.aborted)
|
|
163
|
+
return;
|
|
164
|
+
throw sleepErr;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
opts.abortSignal?.removeEventListener("abort", stopOnAbort);
|
|
144
169
|
}
|
|
145
170
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
unregisterHandler();
|
|
149
174
|
}
|
|
150
175
|
}
|
package/dist/telegram/send.js
CHANGED
|
@@ -23,6 +23,7 @@ import { parseTelegramTarget, stripTelegramInternalPrefixes } from "./targets.js
|
|
|
23
23
|
import { resolveTelegramVoiceSend } from "./voice.js";
|
|
24
24
|
import { buildTelegramThreadParams } from "./bot/helpers.js";
|
|
25
25
|
const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i;
|
|
26
|
+
const THREAD_NOT_FOUND_RE = /400:\s*Bad Request:\s*message thread not found/i;
|
|
26
27
|
const diagLogger = createSubsystemLogger("telegram/diagnostic");
|
|
27
28
|
function createTelegramHttpLogger(cfg) {
|
|
28
29
|
const enabled = isDiagnosticFlagEnabled("telegram.http", cfg);
|
|
@@ -101,6 +102,26 @@ function normalizeMessageId(raw) {
|
|
|
101
102
|
}
|
|
102
103
|
throw new Error("Message id is required for Telegram actions");
|
|
103
104
|
}
|
|
105
|
+
function isTelegramThreadNotFoundError(err) {
|
|
106
|
+
return THREAD_NOT_FOUND_RE.test(formatErrorMessage(err));
|
|
107
|
+
}
|
|
108
|
+
function hasMessageThreadIdParam(params) {
|
|
109
|
+
if (!params)
|
|
110
|
+
return false;
|
|
111
|
+
const value = params.message_thread_id;
|
|
112
|
+
if (typeof value === "number")
|
|
113
|
+
return Number.isFinite(value);
|
|
114
|
+
if (typeof value === "string")
|
|
115
|
+
return value.trim().length > 0;
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
function removeMessageThreadIdParam(params) {
|
|
119
|
+
if (!params || !hasMessageThreadIdParam(params))
|
|
120
|
+
return params;
|
|
121
|
+
const next = { ...params };
|
|
122
|
+
delete next.message_thread_id;
|
|
123
|
+
return Object.keys(next).length > 0 ? next : undefined;
|
|
124
|
+
}
|
|
104
125
|
export function buildInlineKeyboard(buttons) {
|
|
105
126
|
if (!buttons?.length)
|
|
106
127
|
return undefined;
|
|
@@ -136,8 +157,17 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
136
157
|
const messageThreadId = opts.messageThreadId != null ? opts.messageThreadId : target.messageThreadId;
|
|
137
158
|
const threadIdParams = buildTelegramThreadParams(messageThreadId);
|
|
138
159
|
const threadParams = threadIdParams ? { ...threadIdParams } : {};
|
|
160
|
+
const quoteText = opts.quoteText?.trim();
|
|
139
161
|
if (opts.replyToMessageId != null) {
|
|
140
|
-
|
|
162
|
+
if (quoteText) {
|
|
163
|
+
threadParams.reply_parameters = {
|
|
164
|
+
message_id: Math.trunc(opts.replyToMessageId),
|
|
165
|
+
quote: quoteText,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
threadParams.reply_to_message_id = Math.trunc(opts.replyToMessageId);
|
|
170
|
+
}
|
|
141
171
|
}
|
|
142
172
|
const hasThreadParams = Object.keys(threadParams).length > 0;
|
|
143
173
|
const request = createTelegramRetryRunner({
|
|
@@ -163,6 +193,21 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
163
193
|
`Input was: ${JSON.stringify(to)}.`,
|
|
164
194
|
].join(" "));
|
|
165
195
|
};
|
|
196
|
+
const sendWithThreadFallback = async (params, label, attempt) => {
|
|
197
|
+
try {
|
|
198
|
+
return await attempt(params, label);
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
if (!hasMessageThreadIdParam(params) || !isTelegramThreadNotFoundError(err)) {
|
|
202
|
+
throw err;
|
|
203
|
+
}
|
|
204
|
+
if (opts.verbose) {
|
|
205
|
+
console.warn(`telegram ${label} failed with message_thread_id, retrying without thread: ${formatErrorMessage(err)}`);
|
|
206
|
+
}
|
|
207
|
+
const retriedParams = removeMessageThreadIdParam(params);
|
|
208
|
+
return await attempt(retriedParams, `${label}-threadless`);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
166
211
|
const textMode = opts.textMode ?? "markdown";
|
|
167
212
|
const tableMode = resolveMarkdownTableMode({
|
|
168
213
|
cfg,
|
|
@@ -174,36 +219,40 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
174
219
|
const linkPreviewEnabled = account.config.linkPreview ?? true;
|
|
175
220
|
const linkPreviewOptions = linkPreviewEnabled ? undefined : { is_disabled: true };
|
|
176
221
|
const sendTelegramText = async (rawText, params, fallbackText) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const hasBaseParams = Object.keys(baseParams).length > 0;
|
|
183
|
-
const sendParams = {
|
|
184
|
-
parse_mode: "HTML",
|
|
185
|
-
...baseParams,
|
|
186
|
-
...(opts.silent === true ? { disable_notification: true } : {}),
|
|
187
|
-
};
|
|
188
|
-
const res = await requestWithDiag(() => api.sendMessage(chatId, htmlText, sendParams), "message").catch(async (err) => {
|
|
189
|
-
// Telegram rejects malformed HTML (e.g., unsupported tags or entities).
|
|
190
|
-
// When that happens, fall back to plain text so the message still delivers.
|
|
191
|
-
const errText = formatErrorMessage(err);
|
|
192
|
-
if (PARSE_ERR_RE.test(errText)) {
|
|
193
|
-
if (opts.verbose) {
|
|
194
|
-
console.warn(`telegram HTML parse failed, retrying as plain text: ${errText}`);
|
|
195
|
-
}
|
|
196
|
-
const fallback = fallbackText ?? rawText;
|
|
197
|
-
const plainParams = hasBaseParams ? baseParams : undefined;
|
|
198
|
-
return await requestWithDiag(() => plainParams
|
|
199
|
-
? api.sendMessage(chatId, fallback, plainParams)
|
|
200
|
-
: api.sendMessage(chatId, fallback), "message-plain").catch((err2) => {
|
|
201
|
-
throw wrapChatNotFound(err2);
|
|
202
|
-
});
|
|
222
|
+
return await sendWithThreadFallback(params, "message", async (effectiveParams, label) => {
|
|
223
|
+
const htmlText = renderHtmlText(rawText);
|
|
224
|
+
const baseParams = effectiveParams ? { ...effectiveParams } : {};
|
|
225
|
+
if (linkPreviewOptions) {
|
|
226
|
+
baseParams.link_preview_options = linkPreviewOptions;
|
|
203
227
|
}
|
|
204
|
-
|
|
228
|
+
const hasBaseParams = Object.keys(baseParams).length > 0;
|
|
229
|
+
const sendParams = {
|
|
230
|
+
parse_mode: "HTML",
|
|
231
|
+
...baseParams,
|
|
232
|
+
...(opts.silent === true ? { disable_notification: true } : {}),
|
|
233
|
+
};
|
|
234
|
+
const res = await requestWithDiag(() => api.sendMessage(chatId, htmlText, sendParams), label).catch(async (err) => {
|
|
235
|
+
// Telegram rejects malformed HTML (e.g., unsupported tags or entities).
|
|
236
|
+
// When that happens, fall back to plain text so the message still delivers.
|
|
237
|
+
const errText = formatErrorMessage(err);
|
|
238
|
+
if (PARSE_ERR_RE.test(errText)) {
|
|
239
|
+
if (opts.verbose) {
|
|
240
|
+
console.warn(`telegram HTML parse failed, retrying as plain text: ${errText}`);
|
|
241
|
+
}
|
|
242
|
+
const fallback = fallbackText ?? rawText;
|
|
243
|
+
const plainParams = hasBaseParams
|
|
244
|
+
? baseParams
|
|
245
|
+
: undefined;
|
|
246
|
+
return await requestWithDiag(() => plainParams
|
|
247
|
+
? api.sendMessage(chatId, fallback, plainParams)
|
|
248
|
+
: api.sendMessage(chatId, fallback), `${label}-plain`).catch((err2) => {
|
|
249
|
+
throw wrapChatNotFound(err2);
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
throw wrapChatNotFound(err);
|
|
253
|
+
});
|
|
254
|
+
return res;
|
|
205
255
|
});
|
|
206
|
-
return res;
|
|
207
256
|
};
|
|
208
257
|
if (mediaUrl) {
|
|
209
258
|
const media = await loadWebMedia(mediaUrl, opts.maxBytes);
|
|
@@ -212,9 +261,21 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
212
261
|
contentType: media.contentType,
|
|
213
262
|
fileName: media.fileName,
|
|
214
263
|
});
|
|
264
|
+
const isVideoNote = kind === "video" && opts.asVideoNote === true;
|
|
215
265
|
const fileName = media.fileName ?? (isGif ? "animation.gif" : inferFilename(kind)) ?? "file";
|
|
216
266
|
const file = new InputFile(media.buffer, fileName);
|
|
217
|
-
|
|
267
|
+
let caption;
|
|
268
|
+
let followUpText;
|
|
269
|
+
if (isVideoNote) {
|
|
270
|
+
// Video notes don't support captions; send any text as follow-up.
|
|
271
|
+
caption = undefined;
|
|
272
|
+
followUpText = text.trim() ? text : undefined;
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
const split = splitTelegramCaption(text);
|
|
276
|
+
caption = split.caption;
|
|
277
|
+
followUpText = split.followUpText;
|
|
278
|
+
}
|
|
218
279
|
const htmlCaption = caption ? renderHtmlText(caption) : undefined;
|
|
219
280
|
// If text exceeds Telegram's caption limit, send media without caption
|
|
220
281
|
// then send text as a separate follow-up message.
|
|
@@ -226,26 +287,32 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
226
287
|
...(!needsSeparateText && replyMarkup ? { reply_markup: replyMarkup } : {}),
|
|
227
288
|
};
|
|
228
289
|
const mediaParams = {
|
|
229
|
-
caption: htmlCaption,
|
|
230
|
-
...(htmlCaption ? { parse_mode: "HTML" } : {}),
|
|
290
|
+
...(htmlCaption ? { caption: htmlCaption, parse_mode: "HTML" } : {}),
|
|
231
291
|
...baseMediaParams,
|
|
232
292
|
...(opts.silent === true ? { disable_notification: true } : {}),
|
|
233
293
|
};
|
|
234
294
|
let result;
|
|
235
295
|
if (isGif) {
|
|
236
|
-
result = await requestWithDiag(() => api.sendAnimation(chatId, file,
|
|
296
|
+
result = await sendWithThreadFallback(mediaParams, "animation", async (effectiveParams, label) => requestWithDiag(() => api.sendAnimation(chatId, file, effectiveParams), label).catch((err) => {
|
|
237
297
|
throw wrapChatNotFound(err);
|
|
238
|
-
});
|
|
298
|
+
}));
|
|
239
299
|
}
|
|
240
300
|
else if (kind === "image") {
|
|
241
|
-
result = await requestWithDiag(() => api.sendPhoto(chatId, file,
|
|
301
|
+
result = await sendWithThreadFallback(mediaParams, "photo", async (effectiveParams, label) => requestWithDiag(() => api.sendPhoto(chatId, file, effectiveParams), label).catch((err) => {
|
|
242
302
|
throw wrapChatNotFound(err);
|
|
243
|
-
});
|
|
303
|
+
}));
|
|
244
304
|
}
|
|
245
305
|
else if (kind === "video") {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
306
|
+
if (isVideoNote) {
|
|
307
|
+
result = await sendWithThreadFallback(mediaParams, "video_note", async (effectiveParams, label) => requestWithDiag(() => api.sendVideoNote(chatId, file, effectiveParams), label).catch((err) => {
|
|
308
|
+
throw wrapChatNotFound(err);
|
|
309
|
+
}));
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
result = await sendWithThreadFallback(mediaParams, "video", async (effectiveParams, label) => requestWithDiag(() => api.sendVideo(chatId, file, effectiveParams), label).catch((err) => {
|
|
313
|
+
throw wrapChatNotFound(err);
|
|
314
|
+
}));
|
|
315
|
+
}
|
|
249
316
|
}
|
|
250
317
|
else if (kind === "audio") {
|
|
251
318
|
const { useVoice } = resolveTelegramVoiceSend({
|
|
@@ -255,20 +322,20 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
255
322
|
logFallback: logVerbose,
|
|
256
323
|
});
|
|
257
324
|
if (useVoice) {
|
|
258
|
-
result = await requestWithDiag(() => api.sendVoice(chatId, file,
|
|
325
|
+
result = await sendWithThreadFallback(mediaParams, "voice", async (effectiveParams, label) => requestWithDiag(() => api.sendVoice(chatId, file, effectiveParams), label).catch((err) => {
|
|
259
326
|
throw wrapChatNotFound(err);
|
|
260
|
-
});
|
|
327
|
+
}));
|
|
261
328
|
}
|
|
262
329
|
else {
|
|
263
|
-
result = await requestWithDiag(() => api.sendAudio(chatId, file,
|
|
330
|
+
result = await sendWithThreadFallback(mediaParams, "audio", async (effectiveParams, label) => requestWithDiag(() => api.sendAudio(chatId, file, effectiveParams), label).catch((err) => {
|
|
264
331
|
throw wrapChatNotFound(err);
|
|
265
|
-
});
|
|
332
|
+
}));
|
|
266
333
|
}
|
|
267
334
|
}
|
|
268
335
|
else {
|
|
269
|
-
result = await requestWithDiag(() => api.sendDocument(chatId, file,
|
|
336
|
+
result = await sendWithThreadFallback(mediaParams, "document", async (effectiveParams, label) => requestWithDiag(() => api.sendDocument(chatId, file, effectiveParams), label).catch((err) => {
|
|
270
337
|
throw wrapChatNotFound(err);
|
|
271
|
-
});
|
|
338
|
+
}));
|
|
272
339
|
}
|
|
273
340
|
const mediaMessageId = String(result?.message_id ?? "unknown");
|
|
274
341
|
const resolvedChatId = String(result?.chat?.id ?? chatId);
|
|
@@ -508,10 +575,25 @@ export async function sendStickerTelegram(to, fileId, opts = {}) {
|
|
|
508
575
|
`Input was: ${JSON.stringify(to)}.`,
|
|
509
576
|
].join(" "));
|
|
510
577
|
};
|
|
578
|
+
const sendWithStickerThreadFallback = async (params, label, attempt) => {
|
|
579
|
+
try {
|
|
580
|
+
return await attempt(params, label);
|
|
581
|
+
}
|
|
582
|
+
catch (err) {
|
|
583
|
+
if (!hasMessageThreadIdParam(params) || !isTelegramThreadNotFoundError(err)) {
|
|
584
|
+
throw err;
|
|
585
|
+
}
|
|
586
|
+
if (opts.verbose) {
|
|
587
|
+
console.warn(`telegram ${label} failed with message_thread_id, retrying without thread: ${formatErrorMessage(err)}`);
|
|
588
|
+
}
|
|
589
|
+
const retriedParams = removeMessageThreadIdParam(params);
|
|
590
|
+
return await attempt(retriedParams, `${label}-threadless`);
|
|
591
|
+
}
|
|
592
|
+
};
|
|
511
593
|
const stickerParams = hasThreadParams ? threadParams : undefined;
|
|
512
|
-
const result = await requestWithDiag(() => api.sendSticker(chatId, fileId.trim(),
|
|
594
|
+
const result = await sendWithStickerThreadFallback(stickerParams, "sticker", async (effectiveParams, label) => requestWithDiag(() => api.sendSticker(chatId, fileId.trim(), effectiveParams), label).catch((err) => {
|
|
513
595
|
throw wrapChatNotFound(err);
|
|
514
|
-
});
|
|
596
|
+
}));
|
|
515
597
|
const messageId = String(result?.message_id ?? "unknown");
|
|
516
598
|
const resolvedChatId = String(result?.chat?.id ?? chatId);
|
|
517
599
|
if (result?.message_id) {
|