@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.
Files changed (511) hide show
  1. package/assets/chrome-extension/README.md +3 -3
  2. package/assets/chrome-extension/background.js +5 -5
  3. package/assets/chrome-extension/manifest.json +3 -3
  4. package/assets/chrome-extension/options.html +4 -4
  5. package/assets/chrome-extension/options.js +1 -1
  6. package/dist/acp/client.js +3 -3
  7. package/dist/acp/types.js +1 -1
  8. package/dist/agents/agent-paths.js +3 -3
  9. package/dist/agents/auth-profiles/paths.js +3 -3
  10. package/dist/agents/bash-tools.exec.js +76 -25
  11. package/dist/agents/cli-runner/helpers.js +10 -12
  12. package/dist/agents/cli-runner.js +2 -2
  13. package/dist/agents/cloudflare-ai-gateway.js +31 -0
  14. package/dist/agents/compaction.js +16 -2
  15. package/dist/agents/context-window-guard.js +13 -10
  16. package/dist/agents/context.js +4 -4
  17. package/dist/agents/docs-path.js +1 -1
  18. package/dist/agents/identity.js +47 -7
  19. package/dist/agents/memory-search.js +25 -8
  20. package/dist/agents/minimax-vlm.js +1 -1
  21. package/dist/agents/model-auth.js +12 -1
  22. package/dist/agents/model-catalog.js +4 -4
  23. package/dist/agents/model-selection.js +31 -4
  24. package/dist/agents/models-config.js +3 -3
  25. package/dist/agents/models-config.providers.js +147 -39
  26. package/dist/agents/pi-embedded-block-chunker.js +117 -42
  27. package/dist/agents/pi-embedded-helpers/errors.js +183 -78
  28. package/dist/agents/pi-embedded-helpers/openai.js +1 -1
  29. package/dist/agents/pi-embedded-helpers.js +1 -1
  30. package/dist/agents/pi-embedded-runner/compact.js +9 -8
  31. package/dist/agents/pi-embedded-runner/model.js +63 -4
  32. package/dist/agents/pi-embedded-runner/run/attempt.js +27 -17
  33. package/dist/agents/pi-embedded-runner/run.js +203 -50
  34. package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
  35. package/dist/agents/pi-embedded-runner/tool-result-truncation.js +275 -0
  36. package/dist/agents/pi-embedded-runner/utils.js +1 -1
  37. package/dist/agents/pi-embedded-subscribe.js +118 -29
  38. package/dist/agents/pi-model-discovery.js +10 -0
  39. package/dist/agents/pi-tool-definition-adapter.js +50 -9
  40. package/dist/agents/pi-tools.before-tool-call.js +67 -0
  41. package/dist/agents/pi-tools.js +20 -10
  42. package/dist/agents/pi-tools.read.js +2 -2
  43. package/dist/agents/poolbot-tools.js +15 -10
  44. package/dist/agents/sandbox-paths.js +31 -0
  45. package/dist/agents/session-file-repair.js +83 -0
  46. package/dist/agents/session-tool-result-guard.js +94 -15
  47. package/dist/agents/session-transcript-repair.js +68 -0
  48. package/dist/agents/shell-utils.js +51 -0
  49. package/dist/agents/skills/bundled-context.js +23 -0
  50. package/dist/agents/skills/bundled-dir.js +41 -7
  51. package/dist/agents/skills/frontmatter.js +1 -1
  52. package/dist/agents/skills/workspace.js +2 -2
  53. package/dist/agents/skills-install.js +60 -23
  54. package/dist/agents/subagent-announce.js +79 -34
  55. package/dist/agents/system-prompt.js +28 -4
  56. package/dist/agents/together-models.js +127 -0
  57. package/dist/agents/tool-images.js +1 -1
  58. package/dist/agents/tool-policy.conformance.js +14 -0
  59. package/dist/agents/tool-policy.js +25 -1
  60. package/dist/agents/tools/browser-tool.js +3 -3
  61. package/dist/agents/tools/cron-tool.js +166 -19
  62. package/dist/agents/tools/discord-actions-presence.js +78 -0
  63. package/dist/agents/tools/image-tool.js +2 -2
  64. package/dist/agents/tools/memory-tool.js +93 -5
  65. package/dist/agents/tools/message-tool.js +56 -2
  66. package/dist/agents/tools/sessions-history-tool.js +69 -1
  67. package/dist/agents/tools/web-search.js +211 -42
  68. package/dist/agents/usage.js +23 -1
  69. package/dist/agents/workspace-run.js +67 -0
  70. package/dist/agents/workspace-templates.js +44 -0
  71. package/dist/auto-reply/command-auth.js +121 -6
  72. package/dist/auto-reply/commands-registry.data.js +1 -1
  73. package/dist/auto-reply/envelope.js +50 -72
  74. package/dist/auto-reply/reply/commands-compact.js +1 -0
  75. package/dist/auto-reply/reply/commands-context-report.js +3 -2
  76. package/dist/auto-reply/reply/commands-context.js +1 -0
  77. package/dist/auto-reply/reply/commands-models.js +107 -60
  78. package/dist/auto-reply/reply/commands-ptt.js +171 -0
  79. package/dist/auto-reply/reply/commands-session.js +2 -2
  80. package/dist/auto-reply/reply/get-reply-run.js +16 -5
  81. package/dist/auto-reply/reply/groups.js +1 -1
  82. package/dist/auto-reply/reply/inbound-context.js +9 -1
  83. package/dist/auto-reply/reply/inbound-meta.js +130 -0
  84. package/dist/auto-reply/reply/model-selection.js +3 -3
  85. package/dist/auto-reply/reply/untrusted-context.js +15 -0
  86. package/dist/auto-reply/status.js +1 -1
  87. package/dist/auto-reply/thinking.js +88 -43
  88. package/dist/browser/bridge-server.js +13 -0
  89. package/dist/browser/cdp.helpers.js +38 -24
  90. package/dist/browser/client-fetch.js +51 -8
  91. package/dist/browser/config.js +2 -11
  92. package/dist/browser/extension-relay.js +104 -43
  93. package/dist/browser/pw-ai.js +1 -1
  94. package/dist/browser/pw-session.js +143 -8
  95. package/dist/browser/pw-tools-core.interactions.js +125 -27
  96. package/dist/browser/pw-tools-core.responses.js +1 -1
  97. package/dist/browser/pw-tools-core.state.js +1 -1
  98. package/dist/browser/routes/agent.act.js +86 -41
  99. package/dist/browser/routes/dispatcher.js +4 -4
  100. package/dist/browser/screenshot.js +1 -1
  101. package/dist/browser/server-context.js +2 -2
  102. package/dist/browser/server.js +13 -0
  103. package/dist/build-info.json +3 -3
  104. package/dist/canvas-host/a2ui.js +3 -3
  105. package/dist/channels/plugins/catalog.js +2 -2
  106. package/dist/channels/plugins/onboarding/imessage.js +1 -1
  107. package/dist/channels/plugins/onboarding/signal.js +1 -1
  108. package/dist/channels/plugins/onboarding/slack.js +4 -4
  109. package/dist/channels/plugins/onboarding/whatsapp.js +3 -3
  110. package/dist/channels/plugins/pairing-message.js +1 -1
  111. package/dist/channels/reply-prefix.js +8 -1
  112. package/dist/cli/browser-cli-extension.js +2 -2
  113. package/dist/cli/cron-cli/register.cron-add.js +61 -40
  114. package/dist/cli/cron-cli/register.cron-edit.js +60 -34
  115. package/dist/cli/cron-cli/shared.js +56 -41
  116. package/dist/cli/dns-cli.js +26 -14
  117. package/dist/cli/docs-cli.js +1 -1
  118. package/dist/cli/gateway-cli/dev.js +1 -1
  119. package/dist/cli/gateway-cli/register.js +37 -19
  120. package/dist/cli/memory-cli.js +30 -20
  121. package/dist/cli/nodes-cli/register.canvas.js +1 -1
  122. package/dist/cli/parse-bytes.js +37 -0
  123. package/dist/cli/plugins-cli.js +1 -1
  124. package/dist/cli/run-main.js +2 -2
  125. package/dist/cli/security-cli.js +1 -1
  126. package/dist/cli/tagline.js +1 -1
  127. package/dist/cli/update-cli.js +173 -52
  128. package/dist/cli/webhooks-cli.js +5 -5
  129. package/dist/commands/agent.js +1 -0
  130. package/dist/commands/agents.commands.add.js +1 -1
  131. package/dist/commands/auth-choice.apply.api-providers.js +305 -17
  132. package/dist/commands/auth-choice.apply.js +4 -1
  133. package/dist/commands/auth-choice.apply.plugin-provider.js +2 -2
  134. package/dist/commands/auth-choice.apply.xai.js +63 -0
  135. package/dist/commands/auth-choice.preferred-provider.js +7 -1
  136. package/dist/commands/configure.wizard.js +1 -1
  137. package/dist/commands/dashboard.js +1 -1
  138. package/dist/commands/docs.js +1 -1
  139. package/dist/commands/doctor-config-flow.js +61 -5
  140. package/dist/commands/doctor-gateway-services.js +3 -3
  141. package/dist/commands/doctor-state-migrations.js +1 -1
  142. package/dist/commands/doctor-update.js +3 -3
  143. package/dist/commands/doctor.js +1 -1
  144. package/dist/commands/health.js +1 -1
  145. package/dist/commands/model-allowlist.js +29 -0
  146. package/dist/commands/model-picker.js +2 -1
  147. package/dist/commands/models/list.probe.js +2 -2
  148. package/dist/commands/models/list.registry.js +4 -4
  149. package/dist/commands/models/list.status-command.js +44 -24
  150. package/dist/commands/models/shared.js +15 -0
  151. package/dist/commands/onboard-auth.config-core.js +366 -28
  152. package/dist/commands/onboard-auth.credentials.js +71 -9
  153. package/dist/commands/onboard-auth.js +3 -3
  154. package/dist/commands/onboard-auth.models.js +26 -24
  155. package/dist/commands/onboard-custom.js +384 -0
  156. package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
  157. package/dist/commands/onboard-non-interactive/local/auth-choice.js +146 -9
  158. package/dist/commands/onboard-skills.js +63 -38
  159. package/dist/commands/openai-model-default.js +41 -0
  160. package/dist/commands/status-all/report-lines.js +1 -1
  161. package/dist/commands/status.command.js +1 -1
  162. package/dist/commands/uninstall.js +3 -3
  163. package/dist/compat/legacy-names.js +1 -1
  164. package/dist/config/defaults.js +3 -2
  165. package/dist/config/io.js +3 -3
  166. package/dist/config/paths.js +136 -35
  167. package/dist/config/plugin-auto-enable.js +21 -5
  168. package/dist/config/redact-snapshot.js +153 -0
  169. package/dist/config/schema.field-metadata.js +590 -0
  170. package/dist/config/schema.js +3 -3
  171. package/dist/config/sessions/store.js +291 -23
  172. package/dist/config/types.memory.js +1 -0
  173. package/dist/config/version.js +4 -4
  174. package/dist/config/zod-schema.agent-defaults.js +3 -0
  175. package/dist/config/zod-schema.agent-runtime.js +13 -2
  176. package/dist/config/zod-schema.providers-core.js +142 -0
  177. package/dist/config/zod-schema.session.js +3 -0
  178. package/dist/cron/delivery.js +57 -0
  179. package/dist/cron/isolated-agent/delivery-target.js +18 -3
  180. package/dist/cron/isolated-agent/helpers.js +22 -5
  181. package/dist/cron/isolated-agent/run.js +171 -63
  182. package/dist/cron/isolated-agent/session.js +2 -0
  183. package/dist/cron/normalize.js +356 -28
  184. package/dist/cron/parse.js +10 -5
  185. package/dist/cron/run-log.js +35 -10
  186. package/dist/cron/schedule.js +41 -6
  187. package/dist/cron/service/jobs.js +208 -35
  188. package/dist/cron/service/ops.js +72 -16
  189. package/dist/cron/service/state.js +2 -0
  190. package/dist/cron/service/store.js +386 -14
  191. package/dist/cron/service/timer.js +390 -147
  192. package/dist/cron/session-reaper.js +86 -0
  193. package/dist/cron/store.js +23 -8
  194. package/dist/cron/validate-timestamp.js +43 -0
  195. package/dist/daemon/constants.js +7 -7
  196. package/dist/daemon/inspect.js +6 -6
  197. package/dist/daemon/systemd-unit.js +1 -1
  198. package/dist/discord/monitor/agent-components.js +438 -0
  199. package/dist/discord/monitor/allow-list.js +28 -5
  200. package/dist/discord/monitor/gateway-registry.js +29 -0
  201. package/dist/discord/monitor/native-command.js +44 -23
  202. package/dist/discord/monitor/sender-identity.js +45 -0
  203. package/dist/discord/pluralkit.js +27 -0
  204. package/dist/discord/send.outbound.js +92 -5
  205. package/dist/discord/send.shared.js +60 -23
  206. package/dist/discord/targets.js +84 -1
  207. package/dist/entry.js +15 -9
  208. package/dist/extensionAPI.js +8 -0
  209. package/dist/gateway/control-ui.js +8 -1
  210. package/dist/gateway/hooks-mapping.js +3 -0
  211. package/dist/gateway/hooks.js +65 -0
  212. package/dist/gateway/live-image-probe.js +1 -66
  213. package/dist/gateway/net.js +96 -31
  214. package/dist/gateway/node-command-policy.js +50 -15
  215. package/dist/gateway/openai-http.js +2 -2
  216. package/dist/gateway/openresponses-http.js +4 -4
  217. package/dist/gateway/origin-check.js +56 -0
  218. package/dist/gateway/protocol/client-info.js +9 -0
  219. package/dist/gateway/protocol/index.js +9 -2
  220. package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
  221. package/dist/gateway/protocol/schema/cron.js +22 -10
  222. package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
  223. package/dist/gateway/protocol/schema/sessions.js +12 -0
  224. package/dist/gateway/server/hooks.js +1 -1
  225. package/dist/gateway/server-broadcast.js +26 -9
  226. package/dist/gateway/server-chat.js +112 -23
  227. package/dist/gateway/server-discovery-runtime.js +10 -2
  228. package/dist/gateway/server-discovery.js +2 -2
  229. package/dist/gateway/server-http.js +110 -12
  230. package/dist/gateway/server-methods/agent-timestamp.js +60 -0
  231. package/dist/gateway/server-methods/agents.js +321 -2
  232. package/dist/gateway/server-methods/usage.js +559 -16
  233. package/dist/gateway/server-runtime-state.js +22 -8
  234. package/dist/gateway/server-startup-memory.js +16 -0
  235. package/dist/gateway/server.impl.js +7 -3
  236. package/dist/gateway/session-utils.fs.js +23 -25
  237. package/dist/gateway/session-utils.js +20 -10
  238. package/dist/gateway/sessions-patch.js +7 -22
  239. package/dist/gateway/test-helpers.server.js +35 -2
  240. package/dist/hooks/frontmatter.js +1 -1
  241. package/dist/hooks/hooks-status.js +1 -1
  242. package/dist/hooks/install.js +2 -2
  243. package/dist/hooks/loader.js +1 -1
  244. package/dist/hooks/workspace.js +3 -3
  245. package/dist/imessage/constants.js +2 -0
  246. package/dist/imessage/monitor/deliver.js +4 -1
  247. package/dist/imessage/monitor/monitor-provider.js +51 -1
  248. package/dist/index.js +2 -2
  249. package/dist/infra/bonjour-discovery.js +131 -70
  250. package/dist/infra/bonjour.js +3 -3
  251. package/dist/infra/control-ui-assets.js +134 -12
  252. package/dist/infra/errors.js +12 -0
  253. package/dist/infra/exec-approvals.js +266 -57
  254. package/dist/infra/format-time/format-datetime.js +79 -0
  255. package/dist/infra/format-time/format-duration.js +81 -0
  256. package/dist/infra/format-time/format-relative.js +80 -0
  257. package/dist/infra/heartbeat-runner.js +140 -49
  258. package/dist/infra/home-dir.js +54 -0
  259. package/dist/infra/net/fetch-guard.js +122 -0
  260. package/dist/infra/net/ssrf.js +65 -29
  261. package/dist/infra/outbound/abort.js +14 -0
  262. package/dist/infra/outbound/message-action-runner.js +77 -13
  263. package/dist/infra/outbound/outbound-session.js +143 -37
  264. package/dist/infra/path-env.js +3 -3
  265. package/dist/infra/poolbot-root.js +43 -1
  266. package/dist/infra/provider-usage.fetch.minimax.js +1 -1
  267. package/dist/infra/restart.js +1 -1
  268. package/dist/infra/session-cost-usage.js +631 -41
  269. package/dist/infra/state-migrations.js +317 -47
  270. package/dist/infra/tailscale.js +1 -1
  271. package/dist/infra/update-global.js +35 -0
  272. package/dist/infra/update-runner.js +149 -43
  273. package/dist/infra/warning-filter.js +65 -0
  274. package/dist/infra/widearea-dns.js +30 -9
  275. package/dist/logging/redact-identifier.js +12 -0
  276. package/dist/macos/relay.js +2 -2
  277. package/dist/media/fetch.js +81 -58
  278. package/dist/media/input-files.js +1 -1
  279. package/dist/media/mime.js +4 -0
  280. package/dist/media/png-encode.js +74 -0
  281. package/dist/media-understanding/apply.js +403 -3
  282. package/dist/media-understanding/attachments.js +38 -27
  283. package/dist/media-understanding/defaults.js +16 -0
  284. package/dist/media-understanding/providers/deepgram/audio.js +22 -14
  285. package/dist/media-understanding/providers/google/audio.js +24 -17
  286. package/dist/media-understanding/providers/google/video.js +24 -17
  287. package/dist/media-understanding/providers/image.js +4 -4
  288. package/dist/media-understanding/providers/index.js +4 -1
  289. package/dist/media-understanding/providers/openai/audio.js +22 -14
  290. package/dist/media-understanding/providers/shared.js +16 -11
  291. package/dist/media-understanding/providers/zai/index.js +6 -0
  292. package/dist/media-understanding/runner.js +158 -90
  293. package/dist/memory/backend-config.js +207 -0
  294. package/dist/memory/batch-voyage.js +277 -0
  295. package/dist/memory/embeddings-voyage.js +75 -0
  296. package/dist/memory/embeddings.js +29 -17
  297. package/dist/memory/internal.js +101 -18
  298. package/dist/memory/manager.js +155 -48
  299. package/dist/memory/search-manager.js +173 -0
  300. package/dist/memory/session-files.js +9 -3
  301. package/dist/memory/types.js +1 -0
  302. package/dist/node-host/runner.js +36 -26
  303. package/dist/node-host/with-timeout.js +27 -0
  304. package/dist/pairing/pairing-messages.js +1 -1
  305. package/dist/plugins/commands.js +5 -1
  306. package/dist/plugins/config-state.js +86 -7
  307. package/dist/plugins/discovery.js +1 -1
  308. package/dist/plugins/install.js +2 -2
  309. package/dist/plugins/source-display.js +51 -0
  310. package/dist/plugins/update.js +1 -1
  311. package/dist/process/exec.js +20 -2
  312. package/dist/routing/resolve-route.js +12 -0
  313. package/dist/routing/session-key.js +15 -0
  314. package/dist/runtime.js +2 -0
  315. package/dist/security/audit-extra.async.js +601 -0
  316. package/dist/security/audit-extra.js +2 -830
  317. package/dist/security/audit-extra.sync.js +505 -0
  318. package/dist/security/audit.js +2 -2
  319. package/dist/security/channel-metadata.js +34 -0
  320. package/dist/security/external-content.js +88 -6
  321. package/dist/security/skill-scanner.js +330 -0
  322. package/dist/sessions/session-key-utils.js +7 -0
  323. package/dist/shared/text/reasoning-tags.js +52 -7
  324. package/dist/signal/monitor/event-handler.js +80 -1
  325. package/dist/slack/monitor/media.js +85 -15
  326. package/dist/tailscale/detect.js +145 -0
  327. package/dist/telegram/bot/helpers.js +109 -28
  328. package/dist/telegram/bot-handlers.js +144 -3
  329. package/dist/telegram/bot-message-context.js +38 -11
  330. package/dist/telegram/bot-message-dispatch.js +48 -15
  331. package/dist/telegram/bot-native-commands.js +86 -29
  332. package/dist/telegram/bot.js +30 -29
  333. package/dist/telegram/model-buttons.js +163 -0
  334. package/dist/telegram/monitor.js +110 -85
  335. package/dist/telegram/send.js +129 -47
  336. package/dist/terminal/restore.js +45 -0
  337. package/dist/test-helpers/state-dir-env.js +16 -0
  338. package/dist/test-helpers/workspace.js +11 -0
  339. package/dist/test-utils/channel-plugins.js +82 -0
  340. package/dist/test-utils/ports.js +73 -0
  341. package/dist/tts/tts.js +12 -6
  342. package/dist/tui/tui-session-actions.js +166 -54
  343. package/dist/utils/fetch-timeout.js +20 -0
  344. package/dist/utils/normalize-secret-input.js +19 -0
  345. package/dist/utils/shell-argv.js +61 -0
  346. package/dist/utils/transcript-tools.js +58 -0
  347. package/dist/utils.js +55 -14
  348. package/dist/version.js +42 -5
  349. package/dist/web/qr-image.js +1 -61
  350. package/dist/wizard/onboarding.finalize.js +7 -7
  351. package/dist/wizard/onboarding.js +3 -3
  352. package/docs/RELEASE_WORKFOTS_COMPARISON.md +3 -3
  353. package/docs/_config.yml +2 -2
  354. package/docs/_layouts/default.html +9 -9
  355. package/docs/concepts/typebox.md +1 -1
  356. package/docs/docs.json +1 -1
  357. package/docs/northflank.mdx +7 -7
  358. package/docs/railway.mdx +3 -3
  359. package/docs/render.mdx +5 -5
  360. package/docs/start/lore.md +2 -2
  361. package/extensions/bluebubbles/index.ts +2 -2
  362. package/extensions/bluebubbles/package.json +1 -1
  363. package/extensions/bluebubbles/src/accounts.ts +8 -8
  364. package/extensions/bluebubbles/src/actions.test.ts +22 -22
  365. package/extensions/bluebubbles/src/actions.ts +5 -5
  366. package/extensions/bluebubbles/src/attachments.ts +2 -2
  367. package/extensions/bluebubbles/src/channel.ts +16 -16
  368. package/extensions/bluebubbles/src/chat.ts +2 -2
  369. package/extensions/bluebubbles/src/media-send.ts +2 -2
  370. package/extensions/bluebubbles/src/monitor.test.ts +46 -46
  371. package/extensions/bluebubbles/src/monitor.ts +5 -5
  372. package/extensions/bluebubbles/src/onboarding.ts +7 -7
  373. package/extensions/bluebubbles/src/reactions.ts +2 -2
  374. package/extensions/bluebubbles/src/send.ts +2 -2
  375. package/extensions/copilot-proxy/README.md +1 -1
  376. package/extensions/copilot-proxy/package.json +1 -1
  377. package/extensions/diagnostics-otel/index.ts +2 -2
  378. package/extensions/diagnostics-otel/package.json +1 -1
  379. package/extensions/diagnostics-otel/src/service.ts +3 -3
  380. package/extensions/discord/index.ts +2 -2
  381. package/extensions/discord/package.json +1 -1
  382. package/extensions/google-antigravity-auth/README.md +1 -1
  383. package/extensions/google-antigravity-auth/index.ts +1 -1
  384. package/extensions/google-antigravity-auth/package.json +1 -1
  385. package/extensions/google-gemini-cli-auth/README.md +1 -1
  386. package/extensions/google-gemini-cli-auth/oauth.ts +1 -1
  387. package/extensions/google-gemini-cli-auth/package.json +1 -1
  388. package/extensions/googlechat/index.ts +3 -3
  389. package/extensions/googlechat/package.json +1 -1
  390. package/extensions/googlechat/src/accounts.ts +8 -8
  391. package/extensions/googlechat/src/actions.ts +6 -6
  392. package/extensions/googlechat/src/channel.ts +21 -21
  393. package/extensions/googlechat/src/monitor.ts +8 -8
  394. package/extensions/googlechat/src/onboarding.ts +10 -10
  395. package/extensions/imessage/index.ts +2 -2
  396. package/extensions/imessage/package.json +1 -1
  397. package/extensions/line/index.ts +2 -2
  398. package/extensions/line/package.json +1 -1
  399. package/extensions/line/src/card-command.ts +2 -2
  400. package/extensions/line/src/channel.logout.test.ts +4 -4
  401. package/extensions/line/src/channel.sendPayload.test.ts +8 -8
  402. package/extensions/line/src/channel.ts +3 -3
  403. package/extensions/llm-task/README.md +3 -3
  404. package/extensions/llm-task/index.ts +2 -2
  405. package/extensions/llm-task/package.json +1 -1
  406. package/extensions/llm-task/src/llm-task-tool.ts +4 -4
  407. package/extensions/lobster/README.md +6 -6
  408. package/extensions/lobster/index.ts +2 -2
  409. package/extensions/lobster/src/lobster-tool.test.ts +4 -4
  410. package/extensions/lobster/src/lobster-tool.ts +2 -2
  411. package/extensions/matrix/index.ts +2 -2
  412. package/extensions/matrix/package.json +1 -1
  413. package/extensions/matrix/src/matrix/client/config.ts +1 -1
  414. package/extensions/matrix/src/matrix/monitor/handler.ts +1 -1
  415. package/extensions/matrix/src/onboarding.ts +1 -1
  416. package/extensions/mattermost/index.ts +2 -2
  417. package/extensions/mattermost/package.json +1 -1
  418. package/extensions/mattermost/src/mattermost/accounts.ts +8 -8
  419. package/extensions/mattermost/src/mattermost/monitor-helpers.ts +5 -5
  420. package/extensions/mattermost/src/mattermost/monitor.ts +2 -2
  421. package/extensions/mattermost/src/onboarding-helpers.ts +3 -3
  422. package/extensions/mattermost/src/onboarding.ts +2 -2
  423. package/extensions/memory-core/index.ts +2 -2
  424. package/extensions/memory-core/package.json +1 -1
  425. package/extensions/memory-lancedb/index.ts +3 -3
  426. package/extensions/memory-lancedb/package.json +1 -1
  427. package/extensions/msteams/index.ts +2 -2
  428. package/extensions/msteams/package.json +1 -1
  429. package/extensions/msteams/src/channel.directory.test.ts +2 -2
  430. package/extensions/msteams/src/channel.ts +2 -2
  431. package/extensions/msteams/src/graph-upload.ts +4 -4
  432. package/extensions/msteams/src/monitor-handler.ts +2 -2
  433. package/extensions/msteams/src/monitor.ts +2 -2
  434. package/extensions/msteams/src/onboarding.ts +9 -9
  435. package/extensions/msteams/src/reply-dispatcher.ts +2 -2
  436. package/extensions/msteams/src/send-context.ts +2 -2
  437. package/extensions/msteams/src/send.ts +4 -4
  438. package/extensions/nextcloud-talk/index.ts +2 -2
  439. package/extensions/nextcloud-talk/package.json +1 -1
  440. package/extensions/nextcloud-talk/src/channel.ts +7 -7
  441. package/extensions/nextcloud-talk/src/inbound.ts +7 -7
  442. package/extensions/nextcloud-talk/src/onboarding.ts +1 -1
  443. package/extensions/nostr/README.md +2 -2
  444. package/extensions/nostr/index.ts +5 -5
  445. package/extensions/nostr/package.json +1 -1
  446. package/extensions/nostr/src/types.ts +4 -4
  447. package/extensions/open-prose/index.ts +2 -2
  448. package/extensions/qwen-portal-auth/README.md +1 -1
  449. package/extensions/signal/index.ts +2 -2
  450. package/extensions/signal/package.json +1 -1
  451. package/extensions/slack/index.ts +2 -2
  452. package/extensions/slack/package.json +1 -1
  453. package/extensions/telegram/index.ts +2 -2
  454. package/extensions/telegram/package.json +1 -1
  455. package/extensions/telegram/src/channel.ts +2 -2
  456. package/extensions/tlon/README.md +2 -2
  457. package/extensions/tlon/index.ts +2 -2
  458. package/extensions/tlon/package.json +1 -1
  459. package/extensions/tlon/src/channel.ts +13 -13
  460. package/extensions/tlon/src/monitor/index.ts +3 -3
  461. package/extensions/tlon/src/onboarding.ts +3 -3
  462. package/extensions/tlon/src/types.ts +3 -3
  463. package/extensions/twitch/README.md +1 -1
  464. package/extensions/twitch/index.ts +2 -2
  465. package/extensions/twitch/package.json +1 -1
  466. package/extensions/twitch/src/config.ts +3 -3
  467. package/extensions/twitch/src/monitor.ts +3 -3
  468. package/extensions/twitch/src/onboarding.ts +9 -9
  469. package/extensions/twitch/src/outbound.test.ts +2 -2
  470. package/extensions/twitch/src/plugin.test.ts +2 -2
  471. package/extensions/twitch/src/plugin.ts +8 -8
  472. package/extensions/twitch/src/send.test.ts +2 -2
  473. package/extensions/twitch/src/send.ts +4 -4
  474. package/extensions/twitch/src/token.test.ts +8 -8
  475. package/extensions/twitch/src/token.ts +3 -3
  476. package/extensions/twitch/src/twitch-client.ts +3 -3
  477. package/extensions/twitch/src/types.ts +3 -3
  478. package/extensions/twitch/src/utils/markdown.ts +1 -1
  479. package/extensions/voice-call/README.md +3 -3
  480. package/extensions/voice-call/package.json +1 -1
  481. package/extensions/voice-call/src/core-bridge.ts +2 -2
  482. package/extensions/voice-call/src/response-generator.ts +1 -1
  483. package/extensions/whatsapp/index.ts +2 -2
  484. package/extensions/whatsapp/package.json +1 -1
  485. package/extensions/zalo/README.md +1 -1
  486. package/extensions/zalo/index.ts +2 -2
  487. package/extensions/zalo/package.json +1 -1
  488. package/extensions/zalo/src/accounts.ts +8 -8
  489. package/extensions/zalo/src/actions.ts +4 -4
  490. package/extensions/zalo/src/channel.directory.test.ts +2 -2
  491. package/extensions/zalo/src/channel.ts +18 -18
  492. package/extensions/zalo/src/monitor.ts +9 -9
  493. package/extensions/zalo/src/monitor.webhook.test.ts +2 -2
  494. package/extensions/zalo/src/onboarding.ts +24 -24
  495. package/extensions/zalo/src/send.ts +2 -2
  496. package/extensions/zalouser/README.md +2 -2
  497. package/extensions/zalouser/index.ts +2 -2
  498. package/extensions/zalouser/package.json +1 -1
  499. package/extensions/zalouser/src/accounts.ts +9 -9
  500. package/extensions/zalouser/src/channel.ts +24 -24
  501. package/extensions/zalouser/src/monitor.ts +4 -4
  502. package/extensions/zalouser/src/onboarding.ts +28 -28
  503. package/package.json +13 -251
  504. package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
  505. package/skills/tmux/scripts/find-sessions.sh +1 -1
  506. package/CHANGELOG.md +0 -102
  507. package/README-header.png +0 -0
  508. package/git-hooks/pre-commit +0 -4
  509. package/scripts/format-staged.js +0 -148
  510. package/scripts/postinstall.js +0 -300
  511. 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
+ }
@@ -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 { formatDurationMs } from "../infra/format-duration.js";
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
- export async function monitorTelegramProvider(opts = {}) {
54
- const cfg = opts.config ?? loadConfig();
55
- const account = resolveTelegramAccount({
56
- cfg,
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
- const proxyFetch = opts.proxyFetch ??
64
- (account.config.proxy ? makeProxyFetch(account.config.proxy) : undefined);
65
- let lastUpdateId = await readTelegramUpdateOffset({
66
- accountId: account.accountId,
67
- });
68
- const persistUpdateId = async (updateId) => {
69
- if (lastUpdateId !== null && updateId <= lastUpdateId)
70
- return;
71
- lastUpdateId = updateId;
72
- try {
73
- await writeTelegramUpdateOffset({
74
- accountId: account.accountId,
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
- if (opts.useWebhook) {
94
- await startTelegramWebhook({
95
- token,
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
- return;
107
- }
108
- // Use grammyjs/runner for concurrent update processing
109
- let restartAttempts = 0;
110
- while (!opts.abortSignal?.aborted) {
111
- const runner = run(bot, createTelegramRunnerOptions(cfg));
112
- const stopOnAbort = () => {
113
- if (opts.abortSignal?.aborted) {
114
- void runner.stop();
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
- opts.abortSignal?.addEventListener("abort", stopOnAbort, { once: true });
118
- try {
119
- // runner.task() returns a promise that resolves when the runner stops
120
- await runner.task();
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
- catch (err) {
124
- if (opts.abortSignal?.aborted) {
125
- throw err;
126
- }
127
- const isConflict = isGetUpdatesConflict(err);
128
- const isRecoverable = isRecoverableTelegramNetworkError(err, { context: "polling" });
129
- if (!isConflict && !isRecoverable) {
130
- throw err;
131
- }
132
- restartAttempts += 1;
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
- await sleepWithAbort(delayMs, opts.abortSignal);
140
+ // runner.task() returns a promise that resolves when the runner stops
141
+ await runner.task();
142
+ return;
139
143
  }
140
- catch (sleepErr) {
141
- if (opts.abortSignal?.aborted)
142
- return;
143
- throw sleepErr;
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
- finally {
147
- opts.abortSignal?.removeEventListener("abort", stopOnAbort);
148
- }
171
+ }
172
+ finally {
173
+ unregisterHandler();
149
174
  }
150
175
  }
@@ -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
- threadParams.reply_to_message_id = Math.trunc(opts.replyToMessageId);
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
- const htmlText = renderHtmlText(rawText);
178
- const baseParams = params ? { ...params } : {};
179
- if (linkPreviewOptions) {
180
- baseParams.link_preview_options = linkPreviewOptions;
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
- throw wrapChatNotFound(err);
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
- const { caption, followUpText } = splitTelegramCaption(text);
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, mediaParams), "animation").catch((err) => {
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, mediaParams), "photo").catch((err) => {
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
- result = await requestWithDiag(() => api.sendVideo(chatId, file, mediaParams), "video").catch((err) => {
247
- throw wrapChatNotFound(err);
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, mediaParams), "voice").catch((err) => {
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, mediaParams), "audio").catch((err) => {
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, mediaParams), "document").catch((err) => {
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(), stickerParams), "sticker").catch((err) => {
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) {