@poolzin/pool-bot 2026.2.21 → 2026.2.22

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 (369) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/agents/api-key-rotation.js +47 -0
  3. package/dist/agents/apply-patch-update.js +19 -9
  4. package/dist/agents/apply-patch.js +72 -47
  5. package/dist/agents/bash-tools.exec.js +141 -559
  6. package/dist/agents/cli-backends.js +49 -6
  7. package/dist/agents/cli-runner/helpers.js +69 -152
  8. package/dist/agents/cli-runner.js +70 -19
  9. package/dist/agents/identity.js +20 -1
  10. package/dist/agents/image-sanitization.js +9 -0
  11. package/dist/agents/live-auth-keys.js +123 -26
  12. package/dist/agents/live-model-filter.js +13 -4
  13. package/dist/agents/model-catalog.js +40 -9
  14. package/dist/agents/model-forward-compat.js +60 -23
  15. package/dist/agents/model-selection.js +134 -41
  16. package/dist/agents/pi-auth-json.js +2 -2
  17. package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
  18. package/dist/agents/pi-embedded-helpers/errors.js +140 -15
  19. package/dist/agents/pi-embedded-helpers/images.js +22 -12
  20. package/dist/agents/pi-embedded-helpers.js +2 -2
  21. package/dist/agents/pi-embedded-runner/abort.js +10 -3
  22. package/dist/agents/pi-embedded-runner/compact.js +230 -32
  23. package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
  24. package/dist/agents/pi-embedded-runner/google.js +109 -19
  25. package/dist/agents/pi-embedded-runner/history.js +35 -17
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
  27. package/dist/agents/pi-embedded-runner/run/images.js +81 -55
  28. package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
  29. package/dist/agents/pi-embedded-runner/run.js +193 -25
  30. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
  31. package/dist/agents/pi-embedded-runner/runs.js +17 -8
  32. package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
  33. package/dist/agents/pi-embedded-runner.js +1 -1
  34. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
  35. package/dist/agents/pi-embedded-subscribe.js +37 -0
  36. package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
  37. package/dist/agents/pi-model-discovery.js +9 -2
  38. package/dist/agents/pi-tool-definition-adapter.js +60 -8
  39. package/dist/agents/pi-tools.before-tool-call.js +1 -1
  40. package/dist/agents/pi-tools.js +113 -94
  41. package/dist/agents/pi-tools.read.js +337 -38
  42. package/dist/agents/poolbot-tools.js +14 -5
  43. package/dist/agents/sandbox/docker.js +10 -5
  44. package/dist/agents/sandbox/registry.js +96 -46
  45. package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
  46. package/dist/agents/sandbox-paths.js +43 -10
  47. package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
  48. package/dist/agents/session-tool-result-guard.js +39 -39
  49. package/dist/agents/session-transcript-repair.js +36 -33
  50. package/dist/agents/session-write-lock.js +62 -44
  51. package/dist/agents/skills/frontmatter.js +49 -88
  52. package/dist/agents/skills/workspace.js +335 -28
  53. package/dist/agents/subagent-announce.js +508 -174
  54. package/dist/agents/subagent-registry.js +45 -4
  55. package/dist/agents/subagent-spawn.js +16 -33
  56. package/dist/agents/system-prompt-report.js +27 -10
  57. package/dist/agents/system-prompt.js +26 -32
  58. package/dist/agents/tool-call-id.js +69 -17
  59. package/dist/agents/tool-display-common.js +1 -1
  60. package/dist/agents/tool-images.js +64 -31
  61. package/dist/agents/tools/canvas-tool.js +17 -11
  62. package/dist/agents/tools/common.js +37 -19
  63. package/dist/agents/tools/cron-tool.js +40 -38
  64. package/dist/agents/tools/gateway.js +70 -2
  65. package/dist/agents/tools/message-tool.js +181 -40
  66. package/dist/agents/tools/nodes-tool.js +128 -36
  67. package/dist/agents/tools/nodes-utils.js +12 -38
  68. package/dist/agents/tools/session-status-tool.js +24 -71
  69. package/dist/agents/tools/sessions-helpers.js +38 -210
  70. package/dist/agents/tools/sessions-spawn-tool.js +28 -198
  71. package/dist/agents/tools/telegram-actions.js +58 -7
  72. package/dist/agents/tools/web-fetch-utils.js +112 -7
  73. package/dist/agents/tools/web-fetch.js +279 -175
  74. package/dist/agents/tools/web-shared.js +71 -8
  75. package/dist/agents/usage.js +25 -16
  76. package/dist/auto-reply/commands-registry.data.js +85 -11
  77. package/dist/auto-reply/dispatch.js +40 -21
  78. package/dist/auto-reply/reply/abort.js +102 -33
  79. package/dist/auto-reply/reply/commands-core.js +82 -33
  80. package/dist/auto-reply/reply/commands-export-session.js +1 -1
  81. package/dist/auto-reply/reply/commands-info.js +41 -12
  82. package/dist/auto-reply/reply/commands-subagents.js +352 -100
  83. package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
  84. package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
  85. package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
  86. package/dist/auto-reply/reply/inbound-meta.js +12 -1
  87. package/dist/auto-reply/reply/mentions.js +18 -11
  88. package/dist/auto-reply/reply/normalize-reply.js +17 -8
  89. package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
  90. package/dist/auto-reply/reply/session.js +102 -21
  91. package/dist/auto-reply/reply/streaming-directives.js +16 -5
  92. package/dist/auto-reply/status.js +73 -50
  93. package/dist/browser/extension-relay.js +3 -3
  94. package/dist/browser/http-auth.js +1 -1
  95. package/dist/browser/paths.js +2 -2
  96. package/dist/build-info.json +3 -3
  97. package/dist/channels/allowlist-match.js +20 -0
  98. package/dist/channels/allowlists/resolve-utils.js +65 -2
  99. package/dist/channels/chat-type.js +8 -4
  100. package/dist/channels/dock.js +127 -35
  101. package/dist/channels/draft-stream-loop.js +6 -2
  102. package/dist/channels/plugins/actions/telegram.js +42 -18
  103. package/dist/channels/plugins/allowlist-match.js +1 -1
  104. package/dist/channels/plugins/group-mentions.js +51 -41
  105. package/dist/channels/plugins/message-action-names.js +2 -0
  106. package/dist/channels/plugins/message-actions.js +24 -5
  107. package/dist/channels/plugins/normalize/discord.js +26 -4
  108. package/dist/channels/plugins/normalize/signal.js +35 -22
  109. package/dist/channels/plugins/onboarding/helpers.js +8 -26
  110. package/dist/channels/plugins/outbound/imessage.js +15 -14
  111. package/dist/channels/registry.js +20 -7
  112. package/dist/cli/acp-cli.js +7 -5
  113. package/dist/cli/browser-cli-extension.js +25 -12
  114. package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
  115. package/dist/cli/browser-cli-state.js +101 -145
  116. package/dist/cli/command-options.js +28 -0
  117. package/dist/cli/completion-cli.js +6 -6
  118. package/dist/cli/cron-cli/register.cron-add.js +25 -1
  119. package/dist/cli/cron-cli/register.cron-edit.js +44 -0
  120. package/dist/cli/cron-cli/shared.js +7 -1
  121. package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
  122. package/dist/cli/daemon-cli/lifecycle.js +23 -247
  123. package/dist/cli/daemon-cli/register-service-commands.js +25 -4
  124. package/dist/cli/daemon-cli.js +1 -0
  125. package/dist/cli/devices-cli.js +33 -20
  126. package/dist/cli/gateway-cli/register.js +37 -105
  127. package/dist/cli/gateway-cli/run.js +49 -11
  128. package/dist/cli/nodes-camera.js +59 -4
  129. package/dist/cli/nodes-cli/register.camera.js +27 -24
  130. package/dist/cli/nodes-cli/rpc.js +21 -38
  131. package/dist/cli/qr-cli.js +2 -2
  132. package/dist/cli/skills-cli.format.js +2 -2
  133. package/dist/cli/update-cli/progress.js +2 -2
  134. package/dist/cli/update-cli/restart-helper.js +28 -7
  135. package/dist/cli/update-cli/shared.js +7 -7
  136. package/dist/cli/update-cli/status.js +1 -1
  137. package/dist/cli/update-cli/update-command.js +14 -8
  138. package/dist/cli/update-cli/wizard.js +2 -2
  139. package/dist/cli/update-cli.js +21 -1027
  140. package/dist/commands/auth-choice.apply.anthropic.js +10 -2
  141. package/dist/commands/channels/add-mutators.js +3 -35
  142. package/dist/commands/channels/add.js +39 -51
  143. package/dist/commands/config-validation.js +1 -1
  144. package/dist/commands/configure.gateway-auth.js +52 -15
  145. package/dist/commands/configure.gateway.js +84 -40
  146. package/dist/commands/doctor-completion.js +3 -3
  147. package/dist/commands/doctor-config-flow.js +536 -16
  148. package/dist/commands/doctor-gateway-services.js +103 -79
  149. package/dist/commands/doctor-memory-search.js +9 -9
  150. package/dist/commands/doctor-platform-notes.js +57 -30
  151. package/dist/commands/doctor-prompter.js +26 -15
  152. package/dist/commands/doctor-session-locks.js +1 -1
  153. package/dist/commands/doctor.js +21 -9
  154. package/dist/commands/model-picker.js +120 -95
  155. package/dist/commands/models/set.js +2 -21
  156. package/dist/commands/models/shared.js +65 -37
  157. package/dist/commands/onboard-helpers.js +81 -39
  158. package/dist/commands/openai-codex-oauth.js +1 -1
  159. package/dist/commands/sessions.js +52 -53
  160. package/dist/commands/status.summary.js +52 -34
  161. package/dist/commands/test-wizard-helpers.js +2 -2
  162. package/dist/config/defaults.js +79 -42
  163. package/dist/config/group-policy.js +50 -18
  164. package/dist/config/includes.js +37 -10
  165. package/dist/config/schema.help.js +5 -4
  166. package/dist/config/schema.hints.js +2 -2
  167. package/dist/config/schema.labels.js +1 -0
  168. package/dist/config/sessions/group.js +12 -11
  169. package/dist/config/sessions/paths.js +137 -11
  170. package/dist/config/sessions/store.js +185 -65
  171. package/dist/config/sessions/types.js +15 -1
  172. package/dist/config/sessions.js +1 -0
  173. package/dist/config/telegram-custom-commands.js +3 -2
  174. package/dist/config/types.js +2 -0
  175. package/dist/config/zod-schema.agent-defaults.js +6 -27
  176. package/dist/config/zod-schema.agent-runtime.js +171 -79
  177. package/dist/config/zod-schema.providers-core.js +138 -65
  178. package/dist/config/zod-schema.session.js +49 -22
  179. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  180. package/dist/cron/isolated-agent/run.js +224 -57
  181. package/dist/cron/normalize.js +48 -45
  182. package/dist/cron/run-log.js +14 -0
  183. package/dist/cron/service/jobs.js +190 -28
  184. package/dist/cron/service/normalize.js +29 -11
  185. package/dist/cron/service/store.js +30 -44
  186. package/dist/cron/service/timer.js +182 -96
  187. package/dist/cron/service.js +3 -0
  188. package/dist/cron/stagger.js +37 -0
  189. package/dist/daemon/inspect.js +132 -92
  190. package/dist/daemon/runtime-paths.js +25 -4
  191. package/dist/daemon/service-audit.js +47 -16
  192. package/dist/discord/accounts.js +23 -20
  193. package/dist/discord/monitor/agent-components.js +1115 -219
  194. package/dist/discord/monitor/allow-list.js +114 -34
  195. package/dist/discord/monitor/listeners.js +204 -97
  196. package/dist/discord/monitor/message-handler.js +21 -10
  197. package/dist/discord/monitor/message-handler.preflight.js +195 -101
  198. package/dist/discord/monitor/message-handler.process.js +384 -123
  199. package/dist/discord/monitor/message-utils.js +86 -23
  200. package/dist/discord/monitor/native-command.js +77 -57
  201. package/dist/discord/monitor/provider.js +122 -117
  202. package/dist/discord/monitor/reply-context.js +20 -16
  203. package/dist/discord/monitor/reply-delivery.js +40 -8
  204. package/dist/discord/monitor/rest-fetch.js +22 -0
  205. package/dist/discord/monitor/threading.js +117 -24
  206. package/dist/discord/send.js +2 -1
  207. package/dist/discord/send.outbound.js +124 -11
  208. package/dist/discord/send.shared.js +112 -72
  209. package/dist/discord/voice-message.js +3 -3
  210. package/dist/gateway/auth.js +119 -44
  211. package/dist/gateway/call.js +76 -34
  212. package/dist/gateway/channel-health-monitor.js +57 -50
  213. package/dist/gateway/client.js +63 -29
  214. package/dist/gateway/control-ui-contract.js +1 -1
  215. package/dist/gateway/gateway-config-prompts.shared.js +2 -2
  216. package/dist/gateway/net.js +109 -1
  217. package/dist/gateway/protocol/index.js +5 -8
  218. package/dist/gateway/protocol/schema/agent.js +19 -1
  219. package/dist/gateway/protocol/schema/channels.js +21 -0
  220. package/dist/gateway/protocol/schema/cron.js +43 -30
  221. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
  222. package/dist/gateway/protocol/schema/sessions.js +5 -1
  223. package/dist/gateway/protocol/schema.js +0 -1
  224. package/dist/gateway/server/presence-events.js +12 -0
  225. package/dist/gateway/server/ws-connection/message-handler.js +203 -212
  226. package/dist/gateway/server/ws-connection.js +58 -21
  227. package/dist/gateway/server-broadcast.js +18 -13
  228. package/dist/gateway/server-cron.js +177 -10
  229. package/dist/gateway/server-methods/agent-job.js +131 -38
  230. package/dist/gateway/server-methods/send.js +60 -14
  231. package/dist/gateway/server-methods/sessions.js +160 -96
  232. package/dist/gateway/server-methods/system.js +5 -7
  233. package/dist/gateway/server-methods-list.js +8 -0
  234. package/dist/gateway/server-methods.js +24 -8
  235. package/dist/gateway/server-node-events.js +278 -68
  236. package/dist/gateway/session-utils.fs.js +316 -75
  237. package/dist/gateway/session-utils.js +224 -70
  238. package/dist/gateway/sessions-patch.js +63 -20
  239. package/dist/gateway/test-temp-config.js +1 -1
  240. package/dist/gateway/tools-invoke-http.js +118 -70
  241. package/dist/gateway/ws-log.js +135 -107
  242. package/dist/hooks/frontmatter.js +36 -82
  243. package/dist/hooks/install.js +149 -139
  244. package/dist/hooks/internal-hooks.js +29 -4
  245. package/dist/hooks/plugin-hooks.js +2 -1
  246. package/dist/imessage/monitor/deliver.js +10 -4
  247. package/dist/imessage/monitor/monitor-provider.js +138 -375
  248. package/dist/imessage/monitor/runtime.js +4 -8
  249. package/dist/imessage/send.js +65 -19
  250. package/dist/infra/exec-approvals-allowlist.js +7 -0
  251. package/dist/infra/exec-approvals.js +35 -920
  252. package/dist/infra/exec-safe-bin-trust.js +64 -0
  253. package/dist/infra/heartbeat-runner.js +207 -134
  254. package/dist/infra/heartbeat-wake.js +183 -22
  255. package/dist/infra/install-source-utils.js +47 -0
  256. package/dist/infra/net/ssrf.js +170 -36
  257. package/dist/infra/outbound/deliver.js +224 -58
  258. package/dist/infra/outbound/message-action-spec.js +12 -5
  259. package/dist/infra/outbound/outbound-session.js +27 -25
  260. package/dist/infra/poolbot-root.js +32 -22
  261. package/dist/infra/ports.js +14 -11
  262. package/dist/infra/skills-remote.js +48 -37
  263. package/dist/infra/system-events.js +25 -11
  264. package/dist/infra/system-presence.js +26 -33
  265. package/dist/infra/tmp-poolbot-dir.js +81 -2
  266. package/dist/infra/wsl.js +37 -1
  267. package/dist/line/bot-message-context.js +163 -191
  268. package/dist/logging/subsystem.js +59 -22
  269. package/dist/markdown/ir.js +124 -50
  270. package/dist/media/store.js +1 -1
  271. package/dist/media-understanding/runner.entries.js +42 -25
  272. package/dist/media-understanding/runner.js +53 -488
  273. package/dist/memory/embeddings-gemini.js +53 -38
  274. package/dist/memory/manager-embedding-ops.js +48 -69
  275. package/dist/pairing/pairing-store.js +178 -119
  276. package/dist/plugin-sdk/index.js +34 -6
  277. package/dist/plugins/hooks.js +135 -14
  278. package/dist/plugins/install.js +190 -152
  279. package/dist/polls.js +11 -0
  280. package/dist/routing/resolve-route.js +190 -56
  281. package/dist/routing/session-key.js +38 -22
  282. package/dist/runtime.js +35 -9
  283. package/dist/security/audit-channel.js +1 -1
  284. package/dist/sessions/session-key-utils.js +29 -11
  285. package/dist/shared/frontmatter.js +5 -5
  286. package/dist/shared/node-list-types.js +1 -0
  287. package/dist/shared/string-normalization.js +15 -0
  288. package/dist/signal/monitor/event-handler.js +68 -36
  289. package/dist/signal/send.js +29 -37
  290. package/dist/slack/monitor/allow-list.js +10 -11
  291. package/dist/slack/monitor/commands.js +14 -3
  292. package/dist/slack/monitor/events/interactions.js +4 -4
  293. package/dist/slack/monitor/media.js +224 -16
  294. package/dist/slack/monitor/message-handler/dispatch.js +247 -13
  295. package/dist/slack/monitor/message-handler/prepare.js +128 -45
  296. package/dist/slack/monitor/slash.js +357 -144
  297. package/dist/slack/streaming.js +77 -0
  298. package/dist/telegram/accounts.js +40 -13
  299. package/dist/telegram/allowed-updates.js +3 -0
  300. package/dist/telegram/bot/delivery.js +129 -66
  301. package/dist/telegram/bot/helpers.js +136 -122
  302. package/dist/telegram/bot-handlers.js +600 -339
  303. package/dist/telegram/bot-message-context.js +115 -73
  304. package/dist/telegram/bot-message-dispatch.js +235 -104
  305. package/dist/telegram/bot-native-command-menu.js +3 -1
  306. package/dist/telegram/bot-native-commands.js +213 -193
  307. package/dist/telegram/bot.js +24 -132
  308. package/dist/telegram/draft-stream.js +84 -75
  309. package/dist/telegram/format.js +150 -6
  310. package/dist/telegram/send.js +415 -255
  311. package/dist/telegram/targets.js +21 -2
  312. package/dist/telegram/update-offset-store.js +19 -3
  313. package/dist/terminal/restore.js +5 -2
  314. package/dist/test-utils/fetch-mock.js +5 -0
  315. package/dist/version.js +18 -5
  316. package/dist/web/auto-reply/monitor/broadcast.js +7 -3
  317. package/dist/web/auto-reply/monitor/on-message.js +6 -3
  318. package/dist/web/inbound/media.js +34 -8
  319. package/dist/web/inbound/monitor.js +34 -17
  320. package/dist/web/inbound/send-api.js +18 -17
  321. package/dist/web/outbound.js +12 -5
  322. package/dist/wizard/clack-prompter.js +40 -7
  323. package/extensions/bluebubbles/package.json +1 -1
  324. package/extensions/copilot-proxy/package.json +1 -1
  325. package/extensions/diagnostics-otel/package.json +1 -1
  326. package/extensions/discord/package.json +1 -1
  327. package/extensions/feishu/package.json +1 -1
  328. package/extensions/google-antigravity-auth/package.json +1 -1
  329. package/extensions/google-gemini-cli-auth/package.json +1 -1
  330. package/extensions/googlechat/package.json +1 -1
  331. package/extensions/imessage/package.json +1 -1
  332. package/extensions/irc/package.json +1 -1
  333. package/extensions/line/package.json +1 -1
  334. package/extensions/llm-task/package.json +1 -1
  335. package/extensions/lobster/package.json +1 -1
  336. package/extensions/matrix/CHANGELOG.md +5 -0
  337. package/extensions/matrix/package.json +1 -1
  338. package/extensions/mattermost/package.json +1 -1
  339. package/extensions/memory-core/package.json +1 -1
  340. package/extensions/memory-lancedb/package.json +1 -1
  341. package/extensions/minimax-portal-auth/package.json +1 -1
  342. package/extensions/msteams/CHANGELOG.md +5 -0
  343. package/extensions/msteams/package.json +1 -1
  344. package/extensions/nextcloud-talk/package.json +1 -1
  345. package/extensions/nostr/CHANGELOG.md +5 -0
  346. package/extensions/nostr/package.json +1 -1
  347. package/extensions/open-prose/package.json +1 -1
  348. package/extensions/openai-codex-auth/package.json +1 -1
  349. package/extensions/signal/package.json +1 -1
  350. package/extensions/slack/package.json +1 -1
  351. package/extensions/telegram/package.json +1 -1
  352. package/extensions/tlon/package.json +1 -1
  353. package/extensions/twitch/CHANGELOG.md +5 -0
  354. package/extensions/twitch/package.json +1 -1
  355. package/extensions/voice-call/CHANGELOG.md +5 -0
  356. package/extensions/voice-call/package.json +1 -1
  357. package/extensions/whatsapp/package.json +1 -1
  358. package/extensions/zalo/CHANGELOG.md +5 -0
  359. package/extensions/zalo/package.json +1 -1
  360. package/extensions/zalouser/CHANGELOG.md +5 -0
  361. package/extensions/zalouser/package.json +1 -1
  362. package/package.json +1 -1
  363. package/skills/apple-reminders/SKILL.md +100 -49
  364. package/skills/coding-agent/SKILL.md +34 -28
  365. package/skills/github/SKILL.md +131 -16
  366. package/skills/imsg/SKILL.md +112 -15
  367. package/skills/openhue/SKILL.md +101 -19
  368. package/skills/tmux/SKILL.md +111 -79
  369. package/skills/weather/SKILL.md +88 -25
@@ -7,43 +7,49 @@ import { clearHistoryEntriesIfEnabled } from "../auto-reply/reply/history.js";
7
7
  import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/provider-dispatcher.js";
8
8
  import { removeAckReactionAfterReply } from "../channels/ack-reactions.js";
9
9
  import { logAckFailure, logTypingFailure } from "../channels/logging.js";
10
- import { createReplyPrefixContext } from "../channels/reply-prefix.js";
10
+ import { createReplyPrefixOptions } from "../channels/reply-prefix.js";
11
11
  import { createTypingCallbacks } from "../channels/typing.js";
12
12
  import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
13
13
  import { danger, logVerbose } from "../globals.js";
14
+ import { getAgentScopedMediaLocalRoots } from "../media/local-roots.js";
14
15
  import { deliverReplies } from "./bot/delivery.js";
15
16
  import { resolveTelegramDraftStreamingChunking } from "./draft-chunking.js";
16
17
  import { createTelegramDraftStream } from "./draft-stream.js";
18
+ import { editMessageTelegram } from "./send.js";
17
19
  import { cacheSticker, describeStickerImage } from "./sticker-cache.js";
18
20
  const EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again.";
21
+ /** Minimum chars before sending first streaming message (improves push notification UX) */
22
+ const DRAFT_MIN_INITIAL_CHARS = 30;
19
23
  async function resolveStickerVisionSupport(cfg, agentId) {
20
24
  try {
21
25
  const catalog = await loadModelCatalog({ config: cfg });
22
26
  const defaultModel = resolveDefaultModelForAgent({ cfg, agentId });
23
27
  const entry = findModelInCatalog(catalog, defaultModel.provider, defaultModel.model);
24
- if (!entry)
28
+ if (!entry) {
25
29
  return false;
30
+ }
26
31
  return modelSupportsVision(entry);
27
32
  }
28
33
  catch {
29
34
  return false;
30
35
  }
31
36
  }
32
- export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, replyToMode, streamMode, textLimit, telegramCfg, opts, resolveBotTopicsEnabled, }) => {
33
- const { ctxPayload, primaryCtx, msg, chatId, isGroup, resolvedThreadId, historyKey, historyLimit, groupHistories, route, skillFilter, sendTyping, sendRecordVoice, ackReactionPromise, reactionApi, removeAckAfterReply, } = context;
34
- const isPrivateChat = msg.chat.type === "private";
37
+ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, replyToMode, streamMode, textLimit, telegramCfg, opts, }) => {
38
+ const { ctxPayload, msg, chatId, isGroup, threadSpec, historyKey, historyLimit, groupHistories, route, skillFilter, sendTyping, sendRecordVoice, ackReactionPromise, reactionApi, removeAckAfterReply, } = context;
35
39
  const draftMaxChars = Math.min(textLimit, 4096);
36
- const canStreamDraft = streamMode !== "off" &&
37
- isPrivateChat &&
38
- typeof resolvedThreadId === "number" &&
39
- (await resolveBotTopicsEnabled(primaryCtx));
40
+ const accountBlockStreamingEnabled = typeof telegramCfg.blockStreaming === "boolean"
41
+ ? telegramCfg.blockStreaming
42
+ : cfg.agents?.defaults?.blockStreamingDefault === "on";
43
+ const canStreamDraft = streamMode !== "off" && !accountBlockStreamingEnabled;
44
+ const draftReplyToMessageId = replyToMode !== "off" && typeof msg.message_id === "number" ? msg.message_id : undefined;
40
45
  const draftStream = canStreamDraft
41
46
  ? createTelegramDraftStream({
42
47
  api: bot.api,
43
48
  chatId,
44
- draftId: msg.message_id || Date.now(),
45
49
  maxChars: draftMaxChars,
46
- messageThreadId: resolvedThreadId,
50
+ thread: threadSpec,
51
+ replyToMessageId: draftReplyToMessageId,
52
+ minInitialChars: DRAFT_MIN_INITIAL_CHARS,
47
53
  log: logVerbose,
48
54
  warn: logVerbose,
49
55
  })
@@ -51,15 +57,30 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
51
57
  const draftChunking = draftStream && streamMode === "block"
52
58
  ? resolveTelegramDraftStreamingChunking(cfg, route.accountId)
53
59
  : undefined;
60
+ const shouldSplitPreviewMessages = streamMode === "block";
54
61
  const draftChunker = draftChunking ? new EmbeddedBlockChunker(draftChunking) : undefined;
62
+ const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
55
63
  let lastPartialText = "";
56
64
  let draftText = "";
65
+ let hasStreamedMessage = false;
57
66
  const updateDraftFromPartial = (text) => {
58
- if (!draftStream || !text)
67
+ if (!draftStream || !text) {
59
68
  return;
60
- if (text === lastPartialText)
69
+ }
70
+ if (text === lastPartialText) {
61
71
  return;
72
+ }
73
+ // Mark that we've received streaming content (for forceNewMessage decision).
74
+ hasStreamedMessage = true;
62
75
  if (streamMode === "partial") {
76
+ // Some providers briefly emit a shorter prefix snapshot (for example
77
+ // "Sure." -> "Sure" -> "Sure."). Keep the longer preview to avoid
78
+ // visible punctuation flicker.
79
+ if (lastPartialText &&
80
+ lastPartialText.startsWith(text) &&
81
+ text.length < lastPartialText.length) {
82
+ return;
83
+ }
63
84
  lastPartialText = text;
64
85
  draftStream.update(text);
65
86
  return;
@@ -74,8 +95,9 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
74
95
  draftText = "";
75
96
  }
76
97
  lastPartialText = text;
77
- if (!delta)
98
+ if (!delta) {
78
99
  return;
100
+ }
79
101
  if (!draftChunker) {
80
102
  draftText = text;
81
103
  draftStream.update(draftText);
@@ -91,8 +113,9 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
91
113
  });
92
114
  };
93
115
  const flushDraft = async () => {
94
- if (!draftStream)
116
+ if (!draftStream) {
95
117
  return;
118
+ }
96
119
  if (draftChunker?.hasBuffered()) {
97
120
  draftChunker.drain({
98
121
  force: true,
@@ -101,14 +124,23 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
101
124
  },
102
125
  });
103
126
  draftChunker.reset();
104
- if (draftText)
127
+ if (draftText) {
105
128
  draftStream.update(draftText);
129
+ }
106
130
  }
107
131
  await draftStream.flush();
108
132
  };
109
- const disableBlockStreaming = Boolean(draftStream) ||
110
- (typeof telegramCfg.blockStreaming === "boolean" ? !telegramCfg.blockStreaming : undefined);
111
- const prefixContext = createReplyPrefixContext({ cfg, agentId: route.agentId });
133
+ const disableBlockStreaming = typeof telegramCfg.blockStreaming === "boolean"
134
+ ? !telegramCfg.blockStreaming
135
+ : draftStream || streamMode === "off"
136
+ ? true
137
+ : undefined;
138
+ const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
139
+ cfg,
140
+ agentId: route.agentId,
141
+ channel: "telegram",
142
+ accountId: route.accountId,
143
+ });
112
144
  const tableMode = resolveMarkdownTableMode({
113
145
  cfg,
114
146
  channel: "telegram",
@@ -167,99 +199,199 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
167
199
  }
168
200
  }
169
201
  }
170
- // TODO: pass replyQuoteText to deliverReplies once delivery.ts supports it
171
- const _replyQuoteText = ctxPayload.ReplyToIsQuote && ctxPayload.ReplyToBody
202
+ const replyQuoteText = ctxPayload.ReplyToIsQuote && ctxPayload.ReplyToBody
172
203
  ? ctxPayload.ReplyToBody.trim() || undefined
173
204
  : undefined;
174
- void _replyQuoteText;
175
205
  const deliveryState = {
176
206
  delivered: false,
207
+ skippedNonSilent: 0,
177
208
  };
178
- const { queuedFinal, counts } = await dispatchReplyWithBufferedBlockDispatcher({
179
- ctx: ctxPayload,
180
- cfg,
181
- dispatcherOptions: {
182
- responsePrefix: prefixContext.responsePrefix,
183
- responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
184
- deliver: async (payload, info) => {
185
- if (info.kind === "final") {
186
- await flushDraft();
187
- draftStream?.stop();
188
- }
189
- await deliverReplies({
190
- replies: [payload],
191
- chatId: String(chatId),
192
- token: opts.token,
193
- runtime,
194
- bot,
195
- replyToMode,
196
- textLimit,
197
- messageThreadId: resolvedThreadId,
198
- tableMode,
199
- chunkMode,
200
- onVoiceRecording: sendRecordVoice,
201
- linkPreview: telegramCfg.linkPreview,
202
- });
203
- deliveryState.delivered = true;
204
- },
205
- onError: (err, info) => {
206
- runtime.error?.(danger(`telegram ${info.kind} reply failed: ${String(err)}`));
207
- },
208
- onReplyStart: createTypingCallbacks({
209
- start: sendTyping,
210
- onStartError: (err) => {
211
- logTypingFailure({
212
- log: logVerbose,
213
- channel: "telegram",
214
- target: String(chatId),
215
- error: err,
209
+ let finalizedViaPreviewMessage = false;
210
+ const clearGroupHistory = () => {
211
+ if (isGroup && historyKey) {
212
+ clearHistoryEntriesIfEnabled({ historyMap: groupHistories, historyKey, limit: historyLimit });
213
+ }
214
+ };
215
+ const deliveryBaseOptions = {
216
+ chatId: String(chatId),
217
+ token: opts.token,
218
+ runtime,
219
+ bot,
220
+ mediaLocalRoots,
221
+ replyToMode,
222
+ textLimit,
223
+ thread: threadSpec,
224
+ tableMode,
225
+ chunkMode,
226
+ linkPreview: telegramCfg.linkPreview,
227
+ replyQuoteText,
228
+ };
229
+ let queuedFinal = false;
230
+ try {
231
+ ({ queuedFinal } = await dispatchReplyWithBufferedBlockDispatcher({
232
+ ctx: ctxPayload,
233
+ cfg,
234
+ dispatcherOptions: {
235
+ ...prefixOptions,
236
+ deliver: async (payload, info) => {
237
+ if (info.kind === "final") {
238
+ await flushDraft();
239
+ const hasMedia = Boolean(payload.mediaUrl) || (payload.mediaUrls?.length ?? 0) > 0;
240
+ const previewMessageId = draftStream?.messageId();
241
+ const finalText = payload.text;
242
+ const currentPreviewText = streamMode === "block" ? draftText : lastPartialText;
243
+ const previewButtons = payload.channelData?.telegram?.buttons;
244
+ let draftStoppedForPreviewEdit = false;
245
+ // Skip preview edit for error payloads to avoid overwriting previous content
246
+ const canFinalizeViaPreviewEdit = !finalizedViaPreviewMessage &&
247
+ !hasMedia &&
248
+ typeof finalText === "string" &&
249
+ finalText.length > 0 &&
250
+ typeof previewMessageId === "number" &&
251
+ finalText.length <= draftMaxChars &&
252
+ !payload.isError;
253
+ if (canFinalizeViaPreviewEdit) {
254
+ await draftStream?.stop();
255
+ draftStoppedForPreviewEdit = true;
256
+ if (currentPreviewText &&
257
+ currentPreviewText.startsWith(finalText) &&
258
+ finalText.length < currentPreviewText.length) {
259
+ // Ignore regressive final edits (e.g., "Okay." -> "Ok"), which
260
+ // can appear transiently in some provider streams.
261
+ return;
262
+ }
263
+ try {
264
+ await editMessageTelegram(chatId, previewMessageId, finalText, {
265
+ api: bot.api,
266
+ cfg,
267
+ accountId: route.accountId,
268
+ linkPreview: telegramCfg.linkPreview,
269
+ buttons: previewButtons,
270
+ });
271
+ finalizedViaPreviewMessage = true;
272
+ deliveryState.delivered = true;
273
+ return;
274
+ }
275
+ catch (err) {
276
+ logVerbose(`telegram: preview final edit failed; falling back to standard send (${String(err)})`);
277
+ }
278
+ }
279
+ if (!hasMedia &&
280
+ !payload.isError &&
281
+ typeof finalText === "string" &&
282
+ finalText.length > draftMaxChars) {
283
+ logVerbose(`telegram: preview final too long for edit (${finalText.length} > ${draftMaxChars}); falling back to standard send`);
284
+ }
285
+ if (!draftStoppedForPreviewEdit) {
286
+ await draftStream?.stop();
287
+ }
288
+ // Check if stop() sent a message (debounce released on isFinal)
289
+ // If so, edit that message instead of sending a new one
290
+ const messageIdAfterStop = draftStream?.messageId();
291
+ if (!finalizedViaPreviewMessage &&
292
+ typeof messageIdAfterStop === "number" &&
293
+ typeof finalText === "string" &&
294
+ finalText.length > 0 &&
295
+ finalText.length <= draftMaxChars &&
296
+ !hasMedia &&
297
+ !payload.isError) {
298
+ try {
299
+ await editMessageTelegram(chatId, messageIdAfterStop, finalText, {
300
+ api: bot.api,
301
+ cfg,
302
+ accountId: route.accountId,
303
+ linkPreview: telegramCfg.linkPreview,
304
+ buttons: previewButtons,
305
+ });
306
+ finalizedViaPreviewMessage = true;
307
+ deliveryState.delivered = true;
308
+ return;
309
+ }
310
+ catch (err) {
311
+ logVerbose(`telegram: post-stop preview edit failed; falling back to standard send (${String(err)})`);
312
+ }
313
+ }
314
+ }
315
+ const result = await deliverReplies({
316
+ ...deliveryBaseOptions,
317
+ replies: [payload],
318
+ onVoiceRecording: sendRecordVoice,
216
319
  });
320
+ if (result.delivered) {
321
+ deliveryState.delivered = true;
322
+ }
217
323
  },
218
- }).onReplyStart,
219
- },
220
- replyOptions: {
221
- skillFilter,
222
- onPartialReply: draftStream ? (payload) => updateDraftFromPartial(payload.text) : undefined,
223
- onReasoningStream: draftStream
224
- ? (payload) => {
225
- if (payload.text)
226
- draftStream.update(payload.text);
227
- }
228
- : undefined,
229
- disableBlockStreaming,
230
- onModelSelected: (ctx) => {
231
- prefixContext.onModelSelected(ctx);
324
+ onSkip: (_payload, info) => {
325
+ if (info.reason !== "silent") {
326
+ deliveryState.skippedNonSilent += 1;
327
+ }
328
+ },
329
+ onError: (err, info) => {
330
+ runtime.error?.(danger(`telegram ${info.kind} reply failed: ${String(err)}`));
331
+ },
332
+ onReplyStart: createTypingCallbacks({
333
+ start: sendTyping,
334
+ onStartError: (err) => {
335
+ logTypingFailure({
336
+ log: logVerbose,
337
+ channel: "telegram",
338
+ target: String(chatId),
339
+ error: err,
340
+ });
341
+ },
342
+ }).onReplyStart,
232
343
  },
233
- },
234
- });
235
- draftStream?.stop();
236
- let sentFallback = false;
237
- if (!deliveryState.delivered && counts.tool + counts.block + counts.final > 0) {
238
- try {
239
- await deliverReplies({
240
- replies: [{ text: EMPTY_RESPONSE_FALLBACK }],
241
- chatId: String(chatId),
242
- token: opts.token,
243
- runtime,
244
- bot,
245
- replyToMode,
246
- textLimit,
247
- messageThreadId: resolvedThreadId,
248
- tableMode,
249
- chunkMode,
250
- linkPreview: telegramCfg.linkPreview,
251
- });
252
- sentFallback = true;
253
- }
254
- catch {
255
- // Fallback delivery failed; proceed without it
344
+ replyOptions: {
345
+ skillFilter,
346
+ disableBlockStreaming,
347
+ onPartialReply: draftStream ? (payload) => updateDraftFromPartial(payload.text) : undefined,
348
+ onAssistantMessageStart: draftStream
349
+ ? () => {
350
+ // Only split preview bubbles in block mode. In partial mode, keep
351
+ // editing one preview message to avoid flooding the chat.
352
+ logVerbose(`telegram: onAssistantMessageStart called, hasStreamedMessage=${hasStreamedMessage}`);
353
+ if (shouldSplitPreviewMessages && hasStreamedMessage) {
354
+ logVerbose(`telegram: calling forceNewMessage()`);
355
+ draftStream.forceNewMessage();
356
+ }
357
+ lastPartialText = "";
358
+ draftText = "";
359
+ draftChunker?.reset();
360
+ }
361
+ : undefined,
362
+ onReasoningEnd: draftStream
363
+ ? () => {
364
+ // Same policy as assistant-message boundaries: split only in block mode.
365
+ if (shouldSplitPreviewMessages && hasStreamedMessage) {
366
+ draftStream.forceNewMessage();
367
+ }
368
+ lastPartialText = "";
369
+ draftText = "";
370
+ draftChunker?.reset();
371
+ }
372
+ : undefined,
373
+ onModelSelected,
374
+ },
375
+ }));
376
+ }
377
+ finally {
378
+ // Must stop() first to flush debounced content before clear() wipes state
379
+ await draftStream?.stop();
380
+ if (!finalizedViaPreviewMessage) {
381
+ await draftStream?.clear();
256
382
  }
257
383
  }
384
+ let sentFallback = false;
385
+ if (!deliveryState.delivered && deliveryState.skippedNonSilent > 0) {
386
+ const result = await deliverReplies({
387
+ replies: [{ text: EMPTY_RESPONSE_FALLBACK }],
388
+ ...deliveryBaseOptions,
389
+ });
390
+ sentFallback = result.delivered;
391
+ }
258
392
  const hasFinalResponse = queuedFinal || sentFallback;
259
393
  if (!hasFinalResponse) {
260
- if (isGroup && historyKey) {
261
- clearHistoryEntriesIfEnabled({ historyMap: groupHistories, historyKey, limit: historyLimit });
262
- }
394
+ clearGroupHistory();
263
395
  return;
264
396
  }
265
397
  removeAckReactionAfterReply({
@@ -268,8 +400,9 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
268
400
  ackReactionValue: ackReactionPromise ? "ack" : null,
269
401
  remove: () => reactionApi?.(chatId, msg.message_id ?? 0, []) ?? Promise.resolve(),
270
402
  onError: (err) => {
271
- if (!msg.message_id)
403
+ if (!msg.message_id) {
272
404
  return;
405
+ }
273
406
  logAckFailure({
274
407
  log: logVerbose,
275
408
  channel: "telegram",
@@ -278,7 +411,5 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
278
411
  });
279
412
  },
280
413
  });
281
- if (isGroup && historyKey) {
282
- clearHistoryEntriesIfEnabled({ historyMap: groupHistories, historyKey, limit: historyLimit });
283
- }
414
+ clearGroupHistory();
284
415
  };
@@ -60,5 +60,7 @@ export function syncTelegramMenuCommands(params) {
60
60
  fn: () => bot.api.setMyCommands(commandsToRegister),
61
61
  });
62
62
  };
63
- void sync().catch(() => { });
63
+ void sync().catch((err) => {
64
+ runtime.error?.(`Telegram command sync failed: ${String(err)}`);
65
+ });
64
66
  }