@poolzin/pool-bot 2026.2.24 → 2026.2.26

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 (646) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/acp/client.js +207 -18
  3. package/dist/acp/event-mapper.js +87 -22
  4. package/dist/acp/meta.js +12 -6
  5. package/dist/acp/secret-file.js +22 -0
  6. package/dist/agents/agent-paths.js +8 -9
  7. package/dist/agents/agent-scope.js +17 -5
  8. package/dist/agents/auth-profiles/oauth.js +148 -64
  9. package/dist/agents/auth-profiles/session-override.js +13 -7
  10. package/dist/agents/bash-process-registry.test-helpers.js +29 -0
  11. package/dist/agents/bash-tools.exec-approval-request.js +20 -0
  12. package/dist/agents/bash-tools.exec-host-gateway.js +240 -0
  13. package/dist/agents/bash-tools.exec-host-node.js +235 -0
  14. package/dist/agents/bash-tools.exec-runtime.js +2 -25
  15. package/dist/agents/bash-tools.exec-types.js +1 -0
  16. package/dist/agents/bash-tools.process.js +224 -218
  17. package/dist/agents/bedrock-discovery.js +3 -1
  18. package/dist/agents/byteplus-models.js +97 -0
  19. package/dist/agents/chutes-oauth.js +1 -0
  20. package/dist/agents/cli-runner/helpers.js +4 -0
  21. package/dist/agents/compaction.js +41 -14
  22. package/dist/agents/content-blocks.js +16 -0
  23. package/dist/agents/doubao-models.js +121 -0
  24. package/dist/agents/failover-error.js +2 -0
  25. package/dist/agents/huggingface-models.js +5 -3
  26. package/dist/agents/live-model-filter.js +5 -0
  27. package/dist/agents/minimax-vlm.js +10 -8
  28. package/dist/agents/model-auth.js +6 -0
  29. package/dist/agents/model-catalog.js +3 -1
  30. package/dist/agents/model-fallback.js +96 -101
  31. package/dist/agents/model-selection.js +7 -1
  32. package/dist/agents/models-config.providers.js +364 -165
  33. package/dist/agents/ollama-stream.js +117 -4
  34. package/dist/agents/opencode-zen-models.js +22 -11
  35. package/dist/agents/pi-embedded-helpers/errors.js +55 -33
  36. package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
  37. package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
  38. package/dist/agents/pi-embedded-helpers.js +1 -1
  39. package/dist/agents/pi-embedded-payloads.js +1 -0
  40. package/dist/agents/pi-embedded-runner/compact.js +29 -7
  41. package/dist/agents/pi-embedded-runner/extensions.js +28 -26
  42. package/dist/agents/pi-embedded-runner/google.js +20 -8
  43. package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
  44. package/dist/agents/pi-embedded-runner/run.js +71 -12
  45. package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
  46. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
  47. package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
  48. package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
  49. package/dist/agents/pi-embedded-runner/thinking.js +42 -0
  50. package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
  51. package/dist/agents/pi-embedded-runner/utils.js +7 -10
  52. package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
  53. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
  54. package/dist/agents/pi-embedded-subscribe.js +9 -4
  55. package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
  56. package/dist/agents/pi-embedded-utils.js +3 -0
  57. package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
  58. package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
  59. package/dist/agents/pi-settings.js +40 -0
  60. package/dist/agents/pi-tools.policy.js +2 -1
  61. package/dist/agents/provider/config-loader.js +1 -1
  62. package/dist/agents/sandbox/browser.js +170 -33
  63. package/dist/agents/sandbox/config-hash.js +14 -27
  64. package/dist/agents/sandbox/config.js +21 -2
  65. package/dist/agents/sandbox/constants.js +2 -0
  66. package/dist/agents/sandbox/docker.js +16 -2
  67. package/dist/agents/sandbox/novnc-auth.js +62 -0
  68. package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
  69. package/dist/agents/sandbox/shared.js +10 -6
  70. package/dist/agents/sandbox-paths.js +24 -11
  71. package/dist/agents/schema/clean-for-gemini.js +132 -85
  72. package/dist/agents/session-slug.js +10 -5
  73. package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
  74. package/dist/agents/session-tool-result-guard.js +3 -1
  75. package/dist/agents/session-transcript-repair.js +40 -6
  76. package/dist/agents/skills/bundled-dir.js +19 -5
  77. package/dist/agents/skills/env-overrides.js +124 -43
  78. package/dist/agents/skills/frontmatter.js +6 -6
  79. package/dist/agents/skills/plugin-skills.js +14 -7
  80. package/dist/agents/skills/workspace.js +1 -0
  81. package/dist/agents/skills.test-helpers.js +13 -0
  82. package/dist/agents/stable-stringify.js +12 -0
  83. package/dist/agents/subagent-announce.js +251 -49
  84. package/dist/agents/subagent-lifecycle-events.js +19 -0
  85. package/dist/agents/subagent-registry-cleanup.js +31 -0
  86. package/dist/agents/subagent-registry-completion.js +68 -0
  87. package/dist/agents/subagent-registry-queries.js +117 -0
  88. package/dist/agents/subagent-registry-state.js +46 -0
  89. package/dist/agents/subagent-registry.js +252 -221
  90. package/dist/agents/subagent-registry.mocks.shared.js +12 -0
  91. package/dist/agents/subagent-registry.store.js +1 -0
  92. package/dist/agents/subagent-registry.types.js +1 -0
  93. package/dist/agents/subagent-spawn.js +195 -7
  94. package/dist/agents/system-prompt.js +22 -6
  95. package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
  96. package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
  97. package/dist/agents/test-helpers/fast-core-tools.js +1 -17
  98. package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
  99. package/dist/agents/timeout.js +18 -6
  100. package/dist/agents/tool-call-id.js +1 -1
  101. package/dist/agents/tool-display-common.js +162 -29
  102. package/dist/agents/tool-images.js +82 -9
  103. package/dist/agents/tool-policy-shared.js +108 -0
  104. package/dist/agents/tool-policy.js +51 -26
  105. package/dist/agents/tools/browser-tool.js +160 -54
  106. package/dist/agents/tools/canvas-tool.js +27 -1
  107. package/dist/agents/tools/common.js +45 -0
  108. package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
  109. package/dist/agents/tools/discord-actions-guild.js +4 -1
  110. package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
  111. package/dist/agents/tools/gateway-tool.js +3 -1
  112. package/dist/agents/tools/image-tool.js +214 -99
  113. package/dist/agents/tools/nodes-utils.js +1 -10
  114. package/dist/agents/tools/sessions-history-tool.js +140 -108
  115. package/dist/agents/tools/sessions-send-helpers.js +12 -6
  116. package/dist/agents/tools/sessions-spawn-tool.js +8 -2
  117. package/dist/agents/tools/subagents-tool.js +2 -1
  118. package/dist/agents/tools/whatsapp-actions.js +10 -2
  119. package/dist/agents/tools/whatsapp-target-auth.js +18 -0
  120. package/dist/agents/transcript-policy.js +22 -8
  121. package/dist/agents/venice-models.js +11 -3
  122. package/dist/agents/workspace.js +222 -46
  123. package/dist/auto-reply/commands-registry.data.js +51 -0
  124. package/dist/auto-reply/commands-registry.js +19 -21
  125. package/dist/auto-reply/fallback-state.js +114 -0
  126. package/dist/auto-reply/group-activation.js +10 -5
  127. package/dist/auto-reply/inbound-debounce.js +10 -5
  128. package/dist/auto-reply/model-runtime.js +68 -0
  129. package/dist/auto-reply/reply/abort.js +1 -1
  130. package/dist/auto-reply/reply/agent-runner-execution.js +40 -5
  131. package/dist/auto-reply/reply/agent-runner.js +165 -39
  132. package/dist/auto-reply/reply/bash-command.js +41 -39
  133. package/dist/auto-reply/reply/command-gates.js +25 -0
  134. package/dist/auto-reply/reply/commands-allowlist.js +111 -72
  135. package/dist/auto-reply/reply/commands-bash.js +6 -5
  136. package/dist/auto-reply/reply/commands-config.js +30 -28
  137. package/dist/auto-reply/reply/commands-core.js +2 -1
  138. package/dist/auto-reply/reply/commands-info.js +1 -0
  139. package/dist/auto-reply/reply/commands-models.js +65 -14
  140. package/dist/auto-reply/reply/commands-session.js +237 -82
  141. package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
  142. package/dist/auto-reply/reply/commands-setunset.js +45 -0
  143. package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
  144. package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
  145. package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
  146. package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
  147. package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
  148. package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
  149. package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
  150. package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
  151. package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
  152. package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
  153. package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
  154. package/dist/auto-reply/reply/commands-subagents.js +51 -587
  155. package/dist/auto-reply/reply/commands-tts.js +10 -5
  156. package/dist/auto-reply/reply/config-value.js +10 -5
  157. package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
  158. package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
  159. package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
  160. package/dist/auto-reply/reply/followup-runner.js +1 -0
  161. package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
  162. package/dist/auto-reply/reply/get-reply-directives.js +17 -28
  163. package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
  164. package/dist/auto-reply/reply/get-reply.js +71 -12
  165. package/dist/auto-reply/reply/model-selection.js +80 -39
  166. package/dist/auto-reply/reply/queue/enqueue.js +10 -5
  167. package/dist/auto-reply/reply/queue/state.js +13 -12
  168. package/dist/auto-reply/reply/reply-payloads.js +67 -36
  169. package/dist/auto-reply/reply/reply-reference.js +9 -8
  170. package/dist/auto-reply/reply/route-reply.js +15 -8
  171. package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
  172. package/dist/auto-reply/reply/session.js +22 -6
  173. package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
  174. package/dist/auto-reply/reply/subagents-utils.js +56 -30
  175. package/dist/auto-reply/reply/typing.js +46 -21
  176. package/dist/auto-reply/send-policy.js +14 -7
  177. package/dist/auto-reply/status.js +140 -16
  178. package/dist/auto-reply/templating.js +10 -5
  179. package/dist/auto-reply/thinking.js +7 -16
  180. package/dist/auto-reply/tokens.js +21 -5
  181. package/dist/browser/bridge-server.js +36 -20
  182. package/dist/browser/cdp.helpers.js +7 -14
  183. package/dist/browser/cdp.js +35 -15
  184. package/dist/browser/chrome.profile-decoration.js +7 -4
  185. package/dist/browser/config.js +30 -0
  186. package/dist/browser/extension-relay-auth.js +55 -0
  187. package/dist/browser/extension-relay.js +74 -29
  188. package/dist/browser/navigation-guard.js +39 -0
  189. package/dist/browser/paths.js +77 -0
  190. package/dist/browser/profiles.js +13 -8
  191. package/dist/browser/pw-ai-module.js +10 -5
  192. package/dist/browser/pw-session.js +76 -39
  193. package/dist/browser/pw-tools-core.interactions.js +14 -7
  194. package/dist/browser/pw-tools-core.state.js +12 -6
  195. package/dist/browser/routes/agent.act.js +431 -424
  196. package/dist/browser/routes/agent.shared.js +47 -3
  197. package/dist/browser/routes/agent.snapshot.js +122 -116
  198. package/dist/browser/routes/agent.storage.js +303 -297
  199. package/dist/browser/routes/tabs.js +154 -100
  200. package/dist/browser/server-context.js +7 -0
  201. package/dist/browser/server-lifecycle.js +37 -0
  202. package/dist/build-info.json +3 -3
  203. package/dist/channels/allow-from.js +26 -0
  204. package/dist/channels/allowlists/resolve-utils.js +43 -19
  205. package/dist/channels/channel-config.js +14 -7
  206. package/dist/channels/draft-stream-loop.js +7 -0
  207. package/dist/channels/model-overrides.js +82 -0
  208. package/dist/channels/plugins/account-action-gate.js +13 -0
  209. package/dist/channels/plugins/message-actions.js +10 -0
  210. package/dist/channels/plugins/normalize/imessage.js +14 -7
  211. package/dist/channels/plugins/normalize/slack.js +10 -5
  212. package/dist/channels/plugins/normalize/telegram.js +14 -7
  213. package/dist/channels/plugins/outbound/discord.js +80 -8
  214. package/dist/channels/plugins/outbound/signal.js +11 -11
  215. package/dist/channels/plugins/setup-helpers.js +10 -5
  216. package/dist/channels/sender-label.js +14 -7
  217. package/dist/channels/session.js +4 -2
  218. package/dist/channels/status-reactions.js +297 -0
  219. package/dist/channels/telegram/api.js +18 -0
  220. package/dist/cli/argv.js +84 -21
  221. package/dist/cli/banner.js +3 -2
  222. package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
  223. package/dist/cli/cli-name.js +11 -11
  224. package/dist/cli/cli-utils.js +13 -3
  225. package/dist/cli/command-format.js +1 -1
  226. package/dist/cli/config-cli.js +1 -1
  227. package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
  228. package/dist/cli/daemon-cli/lifecycle.js +64 -2
  229. package/dist/cli/daemon-cli/restart-health.js +126 -0
  230. package/dist/cli/daemon-cli/status.gather.js +9 -13
  231. package/dist/cli/daemon-cli/status.print.js +2 -10
  232. package/dist/cli/deps.js +27 -22
  233. package/dist/cli/exec-approvals-cli.js +92 -124
  234. package/dist/cli/gateway-cli/run-loop.js +23 -5
  235. package/dist/cli/memory-cli.js +158 -61
  236. package/dist/cli/node-cli/register.js +14 -5
  237. package/dist/cli/nodes-cli/register.push.js +63 -0
  238. package/dist/cli/nodes-media-utils.js +26 -0
  239. package/dist/cli/outbound-send-deps.js +2 -9
  240. package/dist/cli/outbound-send-mapping.js +11 -0
  241. package/dist/cli/pairing-cli.js +40 -14
  242. package/dist/cli/plugins-cli.js +250 -73
  243. package/dist/cli/ports.js +11 -10
  244. package/dist/cli/program/build-program.js +3 -1
  245. package/dist/cli/program/command-registry.js +214 -136
  246. package/dist/cli/program/command-tree.js +16 -0
  247. package/dist/cli/program/help.js +43 -12
  248. package/dist/cli/program/preaction.js +13 -9
  249. package/dist/cli/program/register.configure.js +3 -18
  250. package/dist/cli/program/register.maintenance.js +2 -2
  251. package/dist/cli/program/register.onboard.js +2 -0
  252. package/dist/cli/program/register.status-health-sessions.js +16 -17
  253. package/dist/cli/program/register.subclis.js +93 -52
  254. package/dist/cli/route.js +12 -8
  255. package/dist/cli/system-cli.js +36 -46
  256. package/dist/cli/test-runtime-capture.js +24 -0
  257. package/dist/cli/update-cli/shared.js +22 -9
  258. package/dist/cli/update-cli/update-command.js +89 -14
  259. package/dist/cli/update-cli/wizard.js +6 -12
  260. package/dist/commands/agent/run-context.js +18 -5
  261. package/dist/commands/agent/session-store.js +17 -4
  262. package/dist/commands/agent.js +185 -89
  263. package/dist/commands/agents.bindings.js +14 -7
  264. package/dist/commands/agents.commands.add.js +13 -9
  265. package/dist/commands/agents.commands.identity.js +12 -6
  266. package/dist/commands/agents.commands.list.js +11 -6
  267. package/dist/commands/agents.config.js +8 -10
  268. package/dist/commands/agents.providers.js +12 -6
  269. package/dist/commands/auth-choice-options.js +103 -75
  270. package/dist/commands/auth-choice.apply.byteplus.js +55 -0
  271. package/dist/commands/auth-choice.apply.js +4 -0
  272. package/dist/commands/auth-choice.apply.minimax.js +61 -13
  273. package/dist/commands/auth-choice.apply.openai.js +3 -1
  274. package/dist/commands/auth-choice.apply.volcengine.js +55 -0
  275. package/dist/commands/auth-choice.preferred-provider.js +2 -0
  276. package/dist/commands/channels/remove.js +13 -6
  277. package/dist/commands/channels/shared.js +4 -14
  278. package/dist/commands/channels.mock-harness.js +23 -0
  279. package/dist/commands/configure.commands.js +14 -0
  280. package/dist/commands/configure.gateway.js +2 -4
  281. package/dist/commands/configure.js +1 -1
  282. package/dist/commands/configure.shared.js +11 -0
  283. package/dist/commands/daemon-install-helpers.js +2 -2
  284. package/dist/commands/daemon-install-runtime-warning.js +11 -0
  285. package/dist/commands/dashboard.js +12 -10
  286. package/dist/commands/docs.js +14 -8
  287. package/dist/commands/doctor-config-flow.js +11 -9
  288. package/dist/commands/doctor-legacy-config.js +281 -0
  289. package/dist/commands/doctor-state-integrity.js +99 -23
  290. package/dist/commands/doctor-update.js +12 -9
  291. package/dist/commands/models/list.list-command.js +7 -5
  292. package/dist/commands/models/set-image.js +2 -21
  293. package/dist/commands/node-daemon-install-helpers.js +10 -8
  294. package/dist/commands/onboard-auth.config-minimax.js +54 -80
  295. package/dist/commands/onboard-auth.config-opencode.js +2 -18
  296. package/dist/commands/onboard-auth.credentials.js +90 -13
  297. package/dist/commands/onboard-auth.js +1 -1
  298. package/dist/commands/onboard-auth.models.js +6 -5
  299. package/dist/commands/onboard-hooks.js +1 -1
  300. package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
  301. package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
  302. package/dist/commands/onboard-provider-auth-flags.js +14 -0
  303. package/dist/commands/onboard-remote.js +14 -7
  304. package/dist/commands/onboard.js +11 -13
  305. package/dist/commands/sandbox-display.js +6 -5
  306. package/dist/commands/sessions.test-helpers.js +61 -0
  307. package/dist/commands/status-all/diagnosis.js +14 -10
  308. package/dist/commands/status-all/format.js +1 -0
  309. package/dist/commands/status.gateway-probe.js +1 -16
  310. package/dist/commands/systemd-linger.js +12 -6
  311. package/dist/config/agent-limits.js +2 -0
  312. package/dist/config/commands.js +32 -15
  313. package/dist/config/config-paths.js +9 -11
  314. package/dist/config/config.js +1 -1
  315. package/dist/config/defaults.js +22 -2
  316. package/dist/config/discord-preview-streaming.js +104 -0
  317. package/dist/config/env-substitution.js +62 -34
  318. package/dist/config/env-vars.js +45 -7
  319. package/dist/config/includes.js +4 -0
  320. package/dist/config/io.js +656 -171
  321. package/dist/config/legacy.migrations.part-1.js +189 -78
  322. package/dist/config/legacy.shared.js +3 -1
  323. package/dist/config/merge-patch.js +54 -4
  324. package/dist/config/prototype-keys.js +4 -0
  325. package/dist/config/redact-snapshot.js +404 -76
  326. package/dist/config/schema.help.js +44 -7
  327. package/dist/config/schema.js +58 -570
  328. package/dist/config/schema.labels.js +38 -6
  329. package/dist/config/sessions/delivery-info.js +10 -3
  330. package/dist/config/sessions/main-session.js +10 -5
  331. package/dist/config/sessions/session-file.js +33 -0
  332. package/dist/config/sessions/session-key.js +10 -5
  333. package/dist/config/sessions/store.js +1 -1
  334. package/dist/config/sessions.js +1 -0
  335. package/dist/config/validation.js +140 -85
  336. package/dist/config/zod-schema.agent-runtime.js +11 -0
  337. package/dist/config/zod-schema.hooks.js +40 -11
  338. package/dist/config/zod-schema.installs.js +20 -0
  339. package/dist/config/zod-schema.js +156 -20
  340. package/dist/config/zod-schema.providers-core.js +78 -4
  341. package/dist/config/zod-schema.providers.js +6 -1
  342. package/dist/config/zod-schema.session.js +41 -2
  343. package/dist/cron/run-log.js +3 -0
  344. package/dist/cron/schedule.js +21 -10
  345. package/dist/cron/service/ops.js +35 -21
  346. package/dist/cron/service/timer.js +116 -16
  347. package/dist/cron/stagger.js +3 -1
  348. package/dist/daemon/cmd-argv.js +21 -0
  349. package/dist/daemon/cmd-set.js +58 -0
  350. package/dist/daemon/service-types.js +1 -0
  351. package/dist/discord/api.js +12 -6
  352. package/dist/discord/draft-chunking.js +22 -0
  353. package/dist/discord/draft-stream.js +124 -0
  354. package/dist/discord/monitor/agent-components.js +1 -1
  355. package/dist/discord/monitor/commands.js +5 -0
  356. package/dist/discord/monitor/exec-approvals.js +357 -162
  357. package/dist/discord/monitor/gateway-plugin.js +2 -1
  358. package/dist/discord/monitor/listeners.js +37 -27
  359. package/dist/discord/monitor/message-handler.js +4 -1
  360. package/dist/discord/monitor/message-handler.preflight.js +65 -8
  361. package/dist/discord/monitor/message-handler.process.js +246 -217
  362. package/dist/discord/monitor/message-utils.js +143 -6
  363. package/dist/discord/monitor/model-picker-preferences.js +143 -0
  364. package/dist/discord/monitor/model-picker.js +651 -0
  365. package/dist/discord/monitor/native-command.js +573 -16
  366. package/dist/discord/monitor/provider.allowlist.js +223 -0
  367. package/dist/discord/monitor/provider.js +275 -347
  368. package/dist/discord/monitor/provider.lifecycle.js +100 -0
  369. package/dist/discord/monitor/reply-delivery.js +123 -16
  370. package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
  371. package/dist/discord/monitor/thread-bindings.js +4 -0
  372. package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
  373. package/dist/discord/monitor/thread-bindings.manager.js +423 -0
  374. package/dist/discord/monitor/thread-bindings.messages.js +55 -0
  375. package/dist/discord/monitor/thread-bindings.state.js +358 -0
  376. package/dist/discord/monitor/thread-bindings.types.js +6 -0
  377. package/dist/discord/resolve-users.js +33 -21
  378. package/dist/discord/send.channels.js +15 -0
  379. package/dist/discord/send.js +3 -2
  380. package/dist/discord/send.outbound.js +82 -26
  381. package/dist/discord/send.permissions.js +83 -30
  382. package/dist/discord/send.reactions.js +8 -4
  383. package/dist/discord/token.js +10 -5
  384. package/dist/discord/voice/command.js +263 -0
  385. package/dist/discord/voice/manager.js +531 -0
  386. package/dist/gateway/auth.js +72 -13
  387. package/dist/gateway/call.js +152 -83
  388. package/dist/gateway/canvas-capability.js +75 -0
  389. package/dist/gateway/client.js +28 -4
  390. package/dist/gateway/config-reload.js +3 -4
  391. package/dist/gateway/control-plane-audit.js +28 -0
  392. package/dist/gateway/control-plane-rate-limit.js +53 -0
  393. package/dist/gateway/control-ui.js +219 -96
  394. package/dist/gateway/events.js +1 -0
  395. package/dist/gateway/hooks-mapping.js +88 -38
  396. package/dist/gateway/hooks.js +109 -54
  397. package/dist/gateway/http-auth-helpers.js +3 -2
  398. package/dist/gateway/http-common.js +22 -0
  399. package/dist/gateway/http-endpoint-helpers.js +1 -0
  400. package/dist/gateway/method-scopes.js +169 -0
  401. package/dist/gateway/net.js +74 -9
  402. package/dist/gateway/node-invoke-system-run-approval.js +14 -35
  403. package/dist/gateway/node-registry.js +10 -5
  404. package/dist/gateway/openai-http.js +1 -0
  405. package/dist/gateway/openresponses-http.js +121 -110
  406. package/dist/gateway/origin-check.js +1 -18
  407. package/dist/gateway/probe-auth.js +2 -0
  408. package/dist/gateway/protocol/index.js +4 -2
  409. package/dist/gateway/protocol/schema/cron.js +1 -0
  410. package/dist/gateway/protocol/schema/devices.js +1 -0
  411. package/dist/gateway/protocol/schema/protocol-schemas.js +4 -1
  412. package/dist/gateway/protocol/schema/push.js +18 -0
  413. package/dist/gateway/protocol/schema/sessions.js +6 -0
  414. package/dist/gateway/protocol/schema.js +1 -0
  415. package/dist/gateway/role-policy.js +17 -0
  416. package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
  417. package/dist/gateway/server/ws-connection/message-handler.js +175 -148
  418. package/dist/gateway/server-chat.js +83 -25
  419. package/dist/gateway/server-constants.js +10 -9
  420. package/dist/gateway/server-cron.js +1 -0
  421. package/dist/gateway/server-http.js +247 -54
  422. package/dist/gateway/server-maintenance.js +20 -5
  423. package/dist/gateway/server-methods/agent.js +162 -24
  424. package/dist/gateway/server-methods/chat.js +465 -130
  425. package/dist/gateway/server-methods/config.js +193 -152
  426. package/dist/gateway/server-methods/devices.js +17 -3
  427. package/dist/gateway/server-methods/models.js +11 -1
  428. package/dist/gateway/server-methods/nodes.helpers.js +12 -0
  429. package/dist/gateway/server-methods/nodes.js +251 -69
  430. package/dist/gateway/server-methods/push.js +53 -0
  431. package/dist/gateway/server-methods/sessions.js +64 -8
  432. package/dist/gateway/server-methods/usage.js +162 -75
  433. package/dist/gateway/server-node-events.js +29 -0
  434. package/dist/gateway/server-reload-handlers.js +2 -3
  435. package/dist/gateway/server-runtime-config.js +39 -13
  436. package/dist/gateway/server-runtime-state.js +2 -0
  437. package/dist/gateway/server-startup-memory.js +17 -11
  438. package/dist/gateway/server-ws-runtime.js +1 -0
  439. package/dist/gateway/server.impl.js +296 -139
  440. package/dist/gateway/session-preview.test-helpers.js +11 -0
  441. package/dist/gateway/session-utils.fs.js +32 -34
  442. package/dist/gateway/sessions-resolve.js +17 -5
  443. package/dist/gateway/startup-auth.js +126 -0
  444. package/dist/gateway/test-helpers.agent-results.js +15 -0
  445. package/dist/gateway/test-helpers.mocks.js +37 -14
  446. package/dist/gateway/test-helpers.openai-mock.js +14 -7
  447. package/dist/gateway/test-helpers.server.js +161 -77
  448. package/dist/gateway/tools-invoke-http.js +21 -10
  449. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
  450. package/dist/hooks/bundled/command-logger/handler.js +7 -2
  451. package/dist/hooks/bundled/session-memory/handler.js +170 -38
  452. package/dist/hooks/frontmatter.js +6 -6
  453. package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
  454. package/dist/hooks/gmail-watcher.js +11 -6
  455. package/dist/hooks/internal-hooks.js +11 -1
  456. package/dist/hooks/llm-slug-generator.js +4 -1
  457. package/dist/hooks/workspace.js +47 -17
  458. package/dist/imessage/accounts.js +9 -20
  459. package/dist/imessage/monitor/inbound-processing.js +2 -1
  460. package/dist/infra/archive-path.js +49 -0
  461. package/dist/infra/archive.js +174 -73
  462. package/dist/infra/control-ui-assets.js +14 -6
  463. package/dist/infra/device-pairing.js +204 -144
  464. package/dist/infra/env.js +10 -5
  465. package/dist/infra/exec-approvals-allowlist.js +141 -70
  466. package/dist/infra/exec-approvals-analysis.js +78 -20
  467. package/dist/infra/exec-approvals.js +5 -17
  468. package/dist/infra/exec-safe-bin-policy.js +277 -0
  469. package/dist/infra/fixed-window-rate-limit.js +33 -0
  470. package/dist/infra/fs-safe.js +71 -39
  471. package/dist/infra/gateway-lock.js +6 -2
  472. package/dist/infra/git-root.js +61 -0
  473. package/dist/infra/heartbeat-active-hours.js +2 -2
  474. package/dist/infra/heartbeat-reason.js +40 -0
  475. package/dist/infra/heartbeat-runner.js +72 -32
  476. package/dist/infra/heartbeat-wake.js +6 -12
  477. package/dist/infra/host-env-security-policy.json +19 -0
  478. package/dist/infra/host-env-security.js +66 -0
  479. package/dist/infra/install-source-utils.js +91 -7
  480. package/dist/infra/net/ssrf.js +131 -38
  481. package/dist/infra/node-pairing.js +50 -105
  482. package/dist/infra/npm-integrity.js +45 -0
  483. package/dist/infra/npm-pack-install.js +40 -0
  484. package/dist/infra/outbound/bound-delivery-router.js +88 -0
  485. package/dist/infra/outbound/channel-adapters.js +20 -7
  486. package/dist/infra/outbound/channel-selection.js +12 -6
  487. package/dist/infra/outbound/envelope.js +1 -1
  488. package/dist/infra/outbound/format.js +12 -6
  489. package/dist/infra/outbound/message-action-runner.js +107 -327
  490. package/dist/infra/outbound/message.js +59 -36
  491. package/dist/infra/outbound/outbound-policy.js +52 -25
  492. package/dist/infra/outbound/outbound-send-service.js +58 -71
  493. package/dist/infra/outbound/payloads.js +14 -7
  494. package/dist/infra/outbound/session-binding-service.js +123 -0
  495. package/dist/infra/pairing-files.js +10 -0
  496. package/dist/infra/path-guards.js +25 -0
  497. package/dist/infra/plain-object.js +9 -0
  498. package/dist/infra/provider-usage.fetch.codex.js +7 -15
  499. package/dist/infra/provider-usage.fetch.gemini.js +14 -11
  500. package/dist/infra/provider-usage.fetch.shared.js +30 -1
  501. package/dist/infra/provider-usage.fetch.zai.js +10 -9
  502. package/dist/infra/push-apns.js +365 -0
  503. package/dist/infra/restart-sentinel.js +16 -1
  504. package/dist/infra/restart.js +229 -26
  505. package/dist/infra/retry-policy.js +4 -2
  506. package/dist/infra/retry.js +9 -5
  507. package/dist/infra/scp-host.js +54 -0
  508. package/dist/infra/session-cost-usage.js +107 -59
  509. package/dist/infra/session-maintenance-warning.js +3 -1
  510. package/dist/infra/shell-env.js +98 -34
  511. package/dist/infra/ssh-config.js +12 -6
  512. package/dist/infra/system-run-command.js +49 -4
  513. package/dist/infra/update-channels.js +10 -5
  514. package/dist/infra/update-startup.js +86 -9
  515. package/dist/line/accounts.js +5 -7
  516. package/dist/line/bot-access.js +8 -20
  517. package/dist/line/bot-handlers.js +3 -1
  518. package/dist/link-understanding/detect.js +15 -7
  519. package/dist/media/constants.js +15 -6
  520. package/dist/media/image-ops.js +7 -0
  521. package/dist/media/inbound-path-policy.js +114 -0
  522. package/dist/media/input-files.js +16 -0
  523. package/dist/media/local-roots.js +3 -2
  524. package/dist/media-understanding/apply.js +4 -1
  525. package/dist/media-understanding/concurrency.js +8 -20
  526. package/dist/memory/backend-config.js +45 -6
  527. package/dist/memory/embeddings.js +10 -4
  528. package/dist/memory/fs-utils.js +23 -0
  529. package/dist/memory/manager-search.js +12 -6
  530. package/dist/memory/manager-sync-ops.js +12 -2
  531. package/dist/memory/qmd-manager.js +466 -53
  532. package/dist/memory/query-expansion.js +167 -3
  533. package/dist/memory/status-format.js +10 -5
  534. package/dist/memory/sync-memory-files.js +1 -1
  535. package/dist/memory/test-manager.js +8 -0
  536. package/dist/node-host/invoke-system-run.js +281 -0
  537. package/dist/node-host/invoke.js +55 -337
  538. package/dist/pairing/pairing-store.js +22 -0
  539. package/dist/plugin-sdk/allow-from.js +1 -1
  540. package/dist/plugin-sdk/command-auth.js +3 -1
  541. package/dist/plugin-sdk/index.js +6 -3
  542. package/dist/plugin-sdk/temp-path.js +47 -0
  543. package/dist/plugin-sdk/webhook-targets.js +32 -0
  544. package/dist/plugins/bundled-dir.js +9 -6
  545. package/dist/plugins/discovery.js +217 -23
  546. package/dist/plugins/hook-runner-global.js +16 -0
  547. package/dist/plugins/hooks.js +50 -0
  548. package/dist/plugins/install.js +28 -16
  549. package/dist/plugins/loader.js +192 -26
  550. package/dist/plugins/logger.js +8 -0
  551. package/dist/plugins/manifest-registry.js +3 -0
  552. package/dist/plugins/path-safety.js +34 -0
  553. package/dist/plugins/registry.js +5 -2
  554. package/dist/plugins/runtime/index.js +271 -206
  555. package/dist/plugins/runtime.js +3 -17
  556. package/dist/plugins/update.js +78 -12
  557. package/dist/process/spawn-utils.js +14 -7
  558. package/dist/providers/github-copilot-models.js +4 -1
  559. package/dist/providers/github-copilot-token.js +11 -6
  560. package/dist/providers/qwen-portal-oauth.js +14 -6
  561. package/dist/routing/account-id.js +30 -0
  562. package/dist/routing/resolve-route.js +3 -7
  563. package/dist/routing/session-key.js +2 -16
  564. package/dist/security/audit-channel.js +100 -20
  565. package/dist/security/audit-extra.async.js +505 -179
  566. package/dist/security/audit-extra.js +12 -2
  567. package/dist/security/audit-extra.sync.js +421 -35
  568. package/dist/security/audit-fs.js +31 -13
  569. package/dist/security/audit.js +180 -370
  570. package/dist/security/dm-policy-shared.js +68 -0
  571. package/dist/security/external-content.js +46 -14
  572. package/dist/security/fix.js +49 -85
  573. package/dist/security/scan-paths.js +20 -0
  574. package/dist/security/secret-equal.js +3 -7
  575. package/dist/security/windows-acl.js +30 -15
  576. package/dist/shared/entry-status.js +6 -0
  577. package/dist/shared/frontmatter.js +5 -5
  578. package/dist/shared/node-list-parse.js +13 -0
  579. package/dist/shared/node-match.js +11 -4
  580. package/dist/shared/operator-scope-compat.js +42 -0
  581. package/dist/shared/text-chunking.js +29 -0
  582. package/dist/signal/accounts.js +7 -20
  583. package/dist/signal/monitor/event-handler.js +3 -1
  584. package/dist/slack/accounts.js +6 -19
  585. package/dist/slack/actions.js +11 -3
  586. package/dist/slack/blocks.test-helpers.js +31 -0
  587. package/dist/slack/monitor/auth.js +1 -1
  588. package/dist/slack/monitor/message-handler/dispatch.js +50 -29
  589. package/dist/slack/monitor/mrkdwn.js +8 -0
  590. package/dist/slack/monitor/replies.js +15 -7
  591. package/dist/slack/monitor/slash.js +22 -13
  592. package/dist/slack/resolve-channels.js +10 -5
  593. package/dist/slack/send.js +102 -12
  594. package/dist/slack/stream-mode.js +10 -0
  595. package/dist/slack/streaming.js +4 -2
  596. package/dist/telegram/accounts.js +19 -14
  597. package/dist/telegram/bot/helpers.js +3 -5
  598. package/dist/telegram/bot-access.js +35 -36
  599. package/dist/telegram/bot-handlers.js +120 -148
  600. package/dist/telegram/bot-message-context.js +68 -9
  601. package/dist/telegram/bot-message-dispatch.js +477 -210
  602. package/dist/telegram/bot-native-commands.js +16 -0
  603. package/dist/telegram/draft-stream.js +44 -8
  604. package/dist/telegram/inline-buttons.js +5 -15
  605. package/dist/telegram/monitor.js +11 -7
  606. package/dist/telegram/network-config.js +19 -7
  607. package/dist/telegram/reasoning-lane-coordinator.js +128 -0
  608. package/dist/telegram/send.js +3 -2
  609. package/dist/telegram/sent-message-cache.js +5 -6
  610. package/dist/telegram/status-reaction-variants.js +208 -0
  611. package/dist/telegram/sticker-cache.js +11 -9
  612. package/dist/terminal/prompt-select-styled.js +9 -0
  613. package/dist/terminal/theme.js +12 -12
  614. package/dist/test-utils/command-runner.js +6 -0
  615. package/dist/test-utils/internal-hook-event-payload.js +10 -0
  616. package/dist/test-utils/model-auth-mock.js +12 -0
  617. package/dist/test-utils/provider-usage-fetch.js +14 -0
  618. package/dist/test-utils/temp-home.js +33 -0
  619. package/dist/tts/tts.js +80 -567
  620. package/dist/tui/components/chat-log.js +50 -8
  621. package/dist/tui/theme/theme.js +10 -12
  622. package/dist/tui/tui-command-handlers.js +36 -27
  623. package/dist/tui/tui-event-handlers.js +122 -32
  624. package/dist/tui/tui-local-shell.js +16 -6
  625. package/dist/tui/tui.js +236 -48
  626. package/dist/utils/account-id.js +2 -4
  627. package/dist/utils/boolean.js +10 -5
  628. package/dist/utils/directive-tags.js +11 -0
  629. package/dist/utils/mask-api-key.js +10 -0
  630. package/dist/utils/queue-helpers.js +67 -12
  631. package/dist/utils/run-with-concurrency.js +39 -0
  632. package/dist/web/auto-reply/deliver-reply.js +8 -4
  633. package/dist/web/auto-reply/mentions.js +10 -5
  634. package/dist/web/auto-reply/monitor/group-members.js +14 -7
  635. package/dist/web/auto-reply/monitor/process-message.js +45 -24
  636. package/dist/web/inbound/access-control.js +5 -2
  637. package/dist/web/login-qr.js +12 -6
  638. package/dist/web/media.js +126 -15
  639. package/docs/tools/slash-commands.md +5 -1
  640. package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
  641. package/extensions/bluebubbles/src/monitor.ts +208 -1950
  642. package/extensions/feishu/src/external-keys.ts +19 -0
  643. package/extensions/lobster/src/windows-spawn.ts +193 -0
  644. package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
  645. package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
  646. package/package.json +1 -1
@@ -1,7 +1,6 @@
1
1
  import { resolveAgentDir } from "../agents/agent-scope.js";
2
2
  import { findModelInCatalog, loadModelCatalog, modelSupportsVision, } from "../agents/model-catalog.js";
3
3
  import { resolveDefaultModelForAgent } from "../agents/model-selection.js";
4
- import { EmbeddedBlockChunker } from "../agents/pi-embedded-block-chunker.js";
5
4
  import { resolveChunkMode } from "../auto-reply/chunk.js";
6
5
  import { clearHistoryEntriesIfEnabled } from "../auto-reply/reply/history.js";
7
6
  import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/provider-dispatcher.js";
@@ -10,11 +9,13 @@ import { logAckFailure, logTypingFailure } from "../channels/logging.js";
10
9
  import { createReplyPrefixOptions } from "../channels/reply-prefix.js";
11
10
  import { createTypingCallbacks } from "../channels/typing.js";
12
11
  import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
12
+ import { loadSessionStore, resolveStorePath } from "../config/sessions.js";
13
13
  import { danger, logVerbose } from "../globals.js";
14
14
  import { getAgentScopedMediaLocalRoots } from "../media/local-roots.js";
15
15
  import { deliverReplies } from "./bot/delivery.js";
16
- import { resolveTelegramDraftStreamingChunking } from "./draft-chunking.js";
17
16
  import { createTelegramDraftStream } from "./draft-stream.js";
17
+ import { renderTelegramHtmlText } from "./format.js";
18
+ import { createTelegramReasoningStepState, splitTelegramReasoningText, } from "./reasoning-lane-coordinator.js";
18
19
  import { editMessageTelegram } from "./send.js";
19
20
  import { cacheSticker, describeStickerImage } from "./sticker-cache.js";
20
21
  const EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again.";
@@ -34,118 +35,163 @@ async function resolveStickerVisionSupport(cfg, agentId) {
34
35
  return false;
35
36
  }
36
37
  }
38
+ function resolveTelegramReasoningLevel(params) {
39
+ const { cfg, sessionKey, agentId } = params;
40
+ if (!sessionKey) {
41
+ return "off";
42
+ }
43
+ try {
44
+ const storePath = resolveStorePath(cfg.session?.store, { agentId });
45
+ const store = loadSessionStore(storePath, { skipCache: true });
46
+ const entry = store[sessionKey.toLowerCase()] ?? store[sessionKey];
47
+ const level = entry?.reasoningLevel;
48
+ if (level === "on" || level === "stream") {
49
+ return level;
50
+ }
51
+ }
52
+ catch {
53
+ // Fall through to default.
54
+ }
55
+ return "off";
56
+ }
37
57
  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;
58
+ const { ctxPayload, msg, chatId, isGroup, threadSpec, historyKey, historyLimit, groupHistories, route, skillFilter, sendTyping, sendRecordVoice, ackReactionPromise, reactionApi, removeAckAfterReply, statusReactionController, } = context;
39
59
  const draftMaxChars = Math.min(textLimit, 4096);
60
+ const tableMode = resolveMarkdownTableMode({
61
+ cfg,
62
+ channel: "telegram",
63
+ accountId: route.accountId,
64
+ });
65
+ const renderDraftPreview = (text) => ({
66
+ text: renderTelegramHtmlText(text, { tableMode }),
67
+ parseMode: "HTML",
68
+ });
40
69
  const accountBlockStreamingEnabled = typeof telegramCfg.blockStreaming === "boolean"
41
70
  ? telegramCfg.blockStreaming
42
71
  : cfg.agents?.defaults?.blockStreamingDefault === "on";
43
- const canStreamDraft = streamMode !== "off" && !accountBlockStreamingEnabled;
72
+ const resolvedReasoningLevel = resolveTelegramReasoningLevel({
73
+ cfg,
74
+ sessionKey: ctxPayload.SessionKey,
75
+ agentId: route.agentId,
76
+ });
77
+ const forceBlockStreamingForReasoning = resolvedReasoningLevel === "on";
78
+ const streamReasoningDraft = resolvedReasoningLevel === "stream";
79
+ const previewStreamingEnabled = streamMode !== "off";
80
+ const canStreamAnswerDraft = previewStreamingEnabled && !accountBlockStreamingEnabled && !forceBlockStreamingForReasoning;
81
+ const canStreamReasoningDraft = canStreamAnswerDraft || streamReasoningDraft;
44
82
  const draftReplyToMessageId = replyToMode !== "off" && typeof msg.message_id === "number" ? msg.message_id : undefined;
45
- const draftStream = canStreamDraft
46
- ? createTelegramDraftStream({
47
- api: bot.api,
48
- chatId,
49
- maxChars: draftMaxChars,
50
- thread: threadSpec,
51
- replyToMessageId: draftReplyToMessageId,
52
- minInitialChars: DRAFT_MIN_INITIAL_CHARS,
53
- log: logVerbose,
54
- warn: logVerbose,
55
- })
56
- : undefined;
57
- const draftChunking = draftStream && streamMode === "block"
58
- ? resolveTelegramDraftStreamingChunking(cfg, route.accountId)
59
- : undefined;
60
- const shouldSplitPreviewMessages = streamMode === "block";
61
- const draftChunker = draftChunking ? new EmbeddedBlockChunker(draftChunking) : undefined;
83
+ const draftMinInitialChars = DRAFT_MIN_INITIAL_CHARS;
62
84
  const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
63
- let lastPartialText = "";
64
- let draftText = "";
65
- let hasStreamedMessage = false;
66
- const updateDraftFromPartial = (text) => {
67
- if (!draftStream || !text) {
68
- return;
85
+ const archivedAnswerPreviews = [];
86
+ const archivedReasoningPreviewIds = [];
87
+ const createDraftLane = (laneName, enabled) => {
88
+ const stream = enabled
89
+ ? createTelegramDraftStream({
90
+ api: bot.api,
91
+ chatId,
92
+ maxChars: draftMaxChars,
93
+ thread: threadSpec,
94
+ replyToMessageId: draftReplyToMessageId,
95
+ minInitialChars: draftMinInitialChars,
96
+ renderText: renderDraftPreview,
97
+ onSupersededPreview: laneName === "answer" || laneName === "reasoning"
98
+ ? (preview) => {
99
+ if (laneName === "reasoning") {
100
+ if (!archivedReasoningPreviewIds.includes(preview.messageId)) {
101
+ archivedReasoningPreviewIds.push(preview.messageId);
102
+ }
103
+ return;
104
+ }
105
+ archivedAnswerPreviews.push({
106
+ messageId: preview.messageId,
107
+ textSnapshot: preview.textSnapshot,
108
+ });
109
+ }
110
+ : undefined,
111
+ log: logVerbose,
112
+ warn: logVerbose,
113
+ })
114
+ : undefined;
115
+ return {
116
+ stream,
117
+ lastPartialText: "",
118
+ hasStreamedMessage: false,
119
+ };
120
+ };
121
+ const lanes = {
122
+ answer: createDraftLane("answer", canStreamAnswerDraft),
123
+ reasoning: createDraftLane("reasoning", canStreamReasoningDraft),
124
+ };
125
+ const answerLane = lanes.answer;
126
+ const reasoningLane = lanes.reasoning;
127
+ let splitReasoningOnNextStream = false;
128
+ const reasoningStepState = createTelegramReasoningStepState();
129
+ const splitTextIntoLaneSegments = (text) => {
130
+ const split = splitTelegramReasoningText(text);
131
+ const segments = [];
132
+ if (split.reasoningText) {
133
+ segments.push({ lane: "reasoning", text: split.reasoningText });
69
134
  }
70
- if (text === lastPartialText) {
71
- return;
135
+ if (split.answerText) {
136
+ segments.push({ lane: "answer", text: split.answerText });
72
137
  }
73
- // Mark that we've received streaming content (for forceNewMessage decision).
74
- hasStreamedMessage = true;
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
- }
84
- lastPartialText = text;
85
- draftStream.update(text);
138
+ return segments;
139
+ };
140
+ const resetDraftLaneState = (lane) => {
141
+ lane.lastPartialText = "";
142
+ lane.hasStreamedMessage = false;
143
+ };
144
+ const updateDraftFromPartial = (lane, text) => {
145
+ const laneStream = lane.stream;
146
+ if (!laneStream || !text) {
86
147
  return;
87
148
  }
88
- let delta = text;
89
- if (text.startsWith(lastPartialText)) {
90
- delta = text.slice(lastPartialText.length);
91
- }
92
- else {
93
- // Streaming buffer reset (or non-monotonic stream). Start fresh.
94
- draftChunker?.reset();
95
- draftText = "";
96
- }
97
- lastPartialText = text;
98
- if (!delta) {
149
+ if (text === lane.lastPartialText) {
99
150
  return;
100
151
  }
101
- if (!draftChunker) {
102
- draftText = text;
103
- draftStream.update(draftText);
152
+ // Mark that we've received streaming content (for forceNewMessage decision).
153
+ lane.hasStreamedMessage = true;
154
+ // Some providers briefly emit a shorter prefix snapshot (for example
155
+ // "Sure." -> "Sure" -> "Sure."). Keep the longer preview to avoid
156
+ // visible punctuation flicker.
157
+ if (lane.lastPartialText &&
158
+ lane.lastPartialText.startsWith(text) &&
159
+ text.length < lane.lastPartialText.length) {
104
160
  return;
105
161
  }
106
- draftChunker.append(delta);
107
- draftChunker.drain({
108
- force: false,
109
- emit: (chunk) => {
110
- draftText += chunk;
111
- draftStream.update(draftText);
112
- },
113
- });
162
+ lane.lastPartialText = text;
163
+ laneStream.update(text);
114
164
  };
115
- const flushDraft = async () => {
116
- if (!draftStream) {
117
- return;
118
- }
119
- if (draftChunker?.hasBuffered()) {
120
- draftChunker.drain({
121
- force: true,
122
- emit: (chunk) => {
123
- draftText += chunk;
124
- },
125
- });
126
- draftChunker.reset();
127
- if (draftText) {
128
- draftStream.update(draftText);
165
+ const ingestDraftLaneSegments = (text) => {
166
+ for (const segment of splitTextIntoLaneSegments(text)) {
167
+ if (segment.lane === "reasoning") {
168
+ reasoningStepState.noteReasoningHint();
169
+ reasoningStepState.noteReasoningDelivered();
129
170
  }
171
+ updateDraftFromPartial(lanes[segment.lane], segment.text);
130
172
  }
131
- await draftStream.flush();
132
173
  };
133
- const disableBlockStreaming = typeof telegramCfg.blockStreaming === "boolean"
134
- ? !telegramCfg.blockStreaming
135
- : draftStream || streamMode === "off"
136
- ? true
137
- : undefined;
174
+ const flushDraftLane = async (lane) => {
175
+ if (!lane.stream) {
176
+ return;
177
+ }
178
+ await lane.stream.flush();
179
+ };
180
+ const disableBlockStreaming = !previewStreamingEnabled
181
+ ? true
182
+ : forceBlockStreamingForReasoning
183
+ ? false
184
+ : typeof telegramCfg.blockStreaming === "boolean"
185
+ ? !telegramCfg.blockStreaming
186
+ : canStreamAnswerDraft
187
+ ? true
188
+ : undefined;
138
189
  const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
139
190
  cfg,
140
191
  agentId: route.agentId,
141
192
  channel: "telegram",
142
193
  accountId: route.accountId,
143
194
  });
144
- const tableMode = resolveMarkdownTableMode({
145
- cfg,
146
- channel: "telegram",
147
- accountId: route.accountId,
148
- });
149
195
  const chunkMode = resolveChunkMode(cfg, "telegram", route.accountId);
150
196
  // Handle uncached stickers: get a dedicated vision description before dispatch
151
197
  // This ensures we cache a raw description rather than a conversational response
@@ -205,8 +251,12 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
205
251
  const deliveryState = {
206
252
  delivered: false,
207
253
  skippedNonSilent: 0,
254
+ failedNonSilent: 0,
255
+ };
256
+ const finalizedPreviewByLane = {
257
+ answer: false,
258
+ reasoning: false,
208
259
  };
209
- let finalizedViaPreviewMessage = false;
210
260
  const clearGroupHistory = () => {
211
261
  if (isGroup && historyKey) {
212
262
  clearHistoryEntriesIfEnabled({ historyMap: groupHistories, historyKey, limit: historyLimit });
@@ -226,7 +276,174 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
226
276
  linkPreview: telegramCfg.linkPreview,
227
277
  replyQuoteText,
228
278
  };
279
+ const getLanePreviewText = (lane) => lane.lastPartialText;
280
+ const tryUpdatePreviewForLane = async (params) => {
281
+ const { lane, laneName, text, previewButtons, stopBeforeEdit = false, updateLaneSnapshot = false, skipRegressive, context, previewMessageId: previewMessageIdOverride, previewTextSnapshot, } = params;
282
+ if (!lane.stream) {
283
+ return false;
284
+ }
285
+ const lanePreviewMessageId = lane.stream.messageId();
286
+ const hadPreviewMessage = typeof previewMessageIdOverride === "number" || typeof lanePreviewMessageId === "number";
287
+ if (stopBeforeEdit) {
288
+ await lane.stream.stop();
289
+ }
290
+ const previewMessageId = typeof previewMessageIdOverride === "number"
291
+ ? previewMessageIdOverride
292
+ : lane.stream.messageId();
293
+ if (typeof previewMessageId !== "number") {
294
+ return false;
295
+ }
296
+ const currentPreviewText = previewTextSnapshot ?? getLanePreviewText(lane);
297
+ const shouldSkipRegressive = Boolean(currentPreviewText) &&
298
+ currentPreviewText.startsWith(text) &&
299
+ text.length < currentPreviewText.length &&
300
+ (skipRegressive === "always" || hadPreviewMessage);
301
+ if (shouldSkipRegressive) {
302
+ // Avoid regressive punctuation/wording flicker from occasional shorter finals.
303
+ deliveryState.delivered = true;
304
+ return true;
305
+ }
306
+ try {
307
+ await editMessageTelegram(chatId, previewMessageId, text, {
308
+ api: bot.api,
309
+ cfg,
310
+ accountId: route.accountId,
311
+ linkPreview: telegramCfg.linkPreview,
312
+ buttons: previewButtons,
313
+ });
314
+ if (updateLaneSnapshot) {
315
+ lane.lastPartialText = text;
316
+ }
317
+ deliveryState.delivered = true;
318
+ return true;
319
+ }
320
+ catch (err) {
321
+ logVerbose(`telegram: ${laneName} preview ${context} edit failed; falling back to standard send (${String(err)})`);
322
+ return false;
323
+ }
324
+ };
325
+ const applyTextToPayload = (payload, text) => {
326
+ if (payload.text === text) {
327
+ return payload;
328
+ }
329
+ return { ...payload, text };
330
+ };
331
+ const sendPayload = async (payload) => {
332
+ const result = await deliverReplies({
333
+ ...deliveryBaseOptions,
334
+ replies: [payload],
335
+ onVoiceRecording: sendRecordVoice,
336
+ });
337
+ if (result.delivered) {
338
+ deliveryState.delivered = true;
339
+ }
340
+ return result.delivered;
341
+ };
342
+ const consumeArchivedAnswerPreviewForFinal = async (params) => {
343
+ const archivedPreview = archivedAnswerPreviews.shift();
344
+ if (!archivedPreview) {
345
+ return undefined;
346
+ }
347
+ if (params.canEditViaPreview) {
348
+ const finalized = await tryUpdatePreviewForLane({
349
+ lane: params.lane,
350
+ laneName: "answer",
351
+ text: params.text,
352
+ previewButtons: params.previewButtons,
353
+ stopBeforeEdit: false,
354
+ skipRegressive: "existingOnly",
355
+ context: "final",
356
+ previewMessageId: archivedPreview.messageId,
357
+ previewTextSnapshot: archivedPreview.textSnapshot,
358
+ });
359
+ if (finalized) {
360
+ return "preview-finalized";
361
+ }
362
+ }
363
+ try {
364
+ await bot.api.deleteMessage(chatId, archivedPreview.messageId);
365
+ }
366
+ catch (err) {
367
+ logVerbose(`telegram: archived answer preview cleanup failed (${archivedPreview.messageId}): ${String(err)}`);
368
+ }
369
+ const delivered = await sendPayload(applyTextToPayload(params.payload, params.text));
370
+ return delivered ? "sent" : "skipped";
371
+ };
372
+ const deliverLaneText = async (params) => {
373
+ const { laneName, text, payload, infoKind, previewButtons, allowPreviewUpdateForNonFinal = false, } = params;
374
+ const lane = lanes[laneName];
375
+ const hasMedia = Boolean(payload.mediaUrl) || (payload.mediaUrls?.length ?? 0) > 0;
376
+ const canEditViaPreview = !hasMedia && text.length > 0 && text.length <= draftMaxChars && !payload.isError;
377
+ if (infoKind === "final") {
378
+ if (laneName === "answer") {
379
+ const archivedResult = await consumeArchivedAnswerPreviewForFinal({
380
+ lane,
381
+ text,
382
+ payload,
383
+ previewButtons,
384
+ canEditViaPreview,
385
+ });
386
+ if (archivedResult) {
387
+ return archivedResult;
388
+ }
389
+ }
390
+ if (canEditViaPreview && !finalizedPreviewByLane[laneName]) {
391
+ await flushDraftLane(lane);
392
+ if (laneName === "answer") {
393
+ const archivedResultAfterFlush = await consumeArchivedAnswerPreviewForFinal({
394
+ lane,
395
+ text,
396
+ payload,
397
+ previewButtons,
398
+ canEditViaPreview,
399
+ });
400
+ if (archivedResultAfterFlush) {
401
+ return archivedResultAfterFlush;
402
+ }
403
+ }
404
+ const finalized = await tryUpdatePreviewForLane({
405
+ lane,
406
+ laneName,
407
+ text,
408
+ previewButtons,
409
+ stopBeforeEdit: true,
410
+ skipRegressive: "existingOnly",
411
+ context: "final",
412
+ });
413
+ if (finalized) {
414
+ finalizedPreviewByLane[laneName] = true;
415
+ return "preview-finalized";
416
+ }
417
+ }
418
+ else if (!hasMedia && !payload.isError && text.length > draftMaxChars) {
419
+ logVerbose(`telegram: preview final too long for edit (${text.length} > ${draftMaxChars}); falling back to standard send`);
420
+ }
421
+ await lane.stream?.stop();
422
+ const delivered = await sendPayload(applyTextToPayload(payload, text));
423
+ return delivered ? "sent" : "skipped";
424
+ }
425
+ if (allowPreviewUpdateForNonFinal && canEditViaPreview) {
426
+ const updated = await tryUpdatePreviewForLane({
427
+ lane,
428
+ laneName,
429
+ text,
430
+ previewButtons,
431
+ stopBeforeEdit: false,
432
+ updateLaneSnapshot: true,
433
+ skipRegressive: "always",
434
+ context: "update",
435
+ });
436
+ if (updated) {
437
+ return "preview-updated";
438
+ }
439
+ }
440
+ const delivered = await sendPayload(applyTextToPayload(payload, text));
441
+ return delivered ? "sent" : "skipped";
442
+ };
229
443
  let queuedFinal = false;
444
+ if (statusReactionController) {
445
+ void statusReactionController.setThinking();
446
+ }
230
447
  try {
231
448
  ({ queuedFinal } = await dispatchReplyWithBufferedBlockDispatcher({
232
449
  ctx: ctxPayload,
@@ -234,91 +451,74 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
234
451
  dispatcherOptions: {
235
452
  ...prefixOptions,
236
453
  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
- }
454
+ const previewButtons = payload.channelData?.telegram?.buttons;
455
+ const segments = splitTextIntoLaneSegments(payload.text);
456
+ const hasMedia = Boolean(payload.mediaUrl) || (payload.mediaUrls?.length ?? 0) > 0;
457
+ const flushBufferedFinalAnswer = async () => {
458
+ const buffered = reasoningStepState.takeBufferedFinalAnswer();
459
+ if (!buffered) {
460
+ return;
278
461
  }
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`);
462
+ const bufferedButtons = buffered.payload.channelData?.telegram?.buttons;
463
+ await deliverLaneText({
464
+ laneName: "answer",
465
+ text: buffered.text,
466
+ payload: buffered.payload,
467
+ infoKind: "final",
468
+ previewButtons: bufferedButtons,
469
+ });
470
+ reasoningStepState.resetForNextStep();
471
+ };
472
+ for (const segment of segments) {
473
+ if (segment.lane === "answer" &&
474
+ info.kind === "final" &&
475
+ reasoningStepState.shouldBufferFinalAnswer()) {
476
+ reasoningStepState.bufferFinalAnswer({ payload, text: segment.text });
477
+ continue;
284
478
  }
285
- if (!draftStoppedForPreviewEdit) {
286
- await draftStream?.stop();
479
+ if (segment.lane === "reasoning") {
480
+ reasoningStepState.noteReasoningHint();
287
481
  }
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;
482
+ const result = await deliverLaneText({
483
+ laneName: segment.lane,
484
+ text: segment.text,
485
+ payload,
486
+ infoKind: info.kind,
487
+ previewButtons,
488
+ allowPreviewUpdateForNonFinal: segment.lane === "reasoning",
489
+ });
490
+ if (segment.lane === "reasoning") {
491
+ if (result !== "skipped") {
492
+ reasoningStepState.noteReasoningDelivered();
493
+ await flushBufferedFinalAnswer();
309
494
  }
310
- catch (err) {
311
- logVerbose(`telegram: post-stop preview edit failed; falling back to standard send (${String(err)})`);
495
+ continue;
496
+ }
497
+ if (info.kind === "final") {
498
+ if (reasoningLane.hasStreamedMessage) {
499
+ finalizedPreviewByLane.reasoning = true;
312
500
  }
501
+ reasoningStepState.resetForNextStep();
313
502
  }
314
503
  }
315
- const result = await deliverReplies({
316
- ...deliveryBaseOptions,
317
- replies: [payload],
318
- onVoiceRecording: sendRecordVoice,
319
- });
320
- if (result.delivered) {
321
- deliveryState.delivered = true;
504
+ if (segments.length > 0) {
505
+ return;
506
+ }
507
+ if (info.kind === "final") {
508
+ await answerLane.stream?.stop();
509
+ await reasoningLane.stream?.stop();
510
+ reasoningStepState.resetForNextStep();
511
+ }
512
+ const canSendAsIs = hasMedia || typeof payload.text !== "string" || payload.text.length > 0;
513
+ if (!canSendAsIs) {
514
+ if (info.kind === "final") {
515
+ await flushBufferedFinalAnswer();
516
+ }
517
+ return;
518
+ }
519
+ await sendPayload(payload);
520
+ if (info.kind === "final") {
521
+ await flushBufferedFinalAnswer();
322
522
  }
323
523
  },
324
524
  onSkip: (_payload, info) => {
@@ -327,6 +527,7 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
327
527
  }
328
528
  },
329
529
  onError: (err, info) => {
530
+ deliveryState.failedNonSilent += 1;
330
531
  runtime.error?.(danger(`telegram ${info.kind} reply failed: ${String(err)}`));
331
532
  },
332
533
  onReplyStart: createTypingCallbacks({
@@ -344,30 +545,47 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
344
545
  replyOptions: {
345
546
  skillFilter,
346
547
  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();
548
+ onPartialReply: answerLane.stream || reasoningLane.stream
549
+ ? (payload) => ingestDraftLaneSegments(payload.text)
550
+ : undefined,
551
+ onReasoningStream: reasoningLane.stream
552
+ ? (payload) => {
553
+ // Split between reasoning blocks only when the next reasoning
554
+ // stream starts. Splitting at reasoning-end can orphan the active
555
+ // preview and cause duplicate reasoning sends on reasoning final.
556
+ if (splitReasoningOnNextStream) {
557
+ reasoningLane.stream?.forceNewMessage();
558
+ resetDraftLaneState(reasoningLane);
559
+ splitReasoningOnNextStream = false;
356
560
  }
357
- lastPartialText = "";
358
- draftText = "";
359
- draftChunker?.reset();
561
+ ingestDraftLaneSegments(payload.text);
360
562
  }
361
563
  : undefined,
362
- onReasoningEnd: draftStream
363
- ? () => {
364
- // Same policy as assistant-message boundaries: split only in block mode.
365
- if (shouldSplitPreviewMessages && hasStreamedMessage) {
366
- draftStream.forceNewMessage();
564
+ onAssistantMessageStart: answerLane.stream
565
+ ? async () => {
566
+ reasoningStepState.resetForNextStep();
567
+ if (answerLane.hasStreamedMessage) {
568
+ const previewMessageId = answerLane.stream?.messageId();
569
+ if (typeof previewMessageId === "number") {
570
+ archivedAnswerPreviews.push({
571
+ messageId: previewMessageId,
572
+ textSnapshot: answerLane.lastPartialText,
573
+ });
574
+ }
575
+ answerLane.stream?.forceNewMessage();
367
576
  }
368
- lastPartialText = "";
369
- draftText = "";
370
- draftChunker?.reset();
577
+ resetDraftLaneState(answerLane);
578
+ }
579
+ : undefined,
580
+ onReasoningEnd: reasoningLane.stream
581
+ ? () => {
582
+ // Split when/if a later reasoning block begins.
583
+ splitReasoningOnNextStream = reasoningLane.hasStreamedMessage;
584
+ }
585
+ : undefined,
586
+ onToolStart: statusReactionController
587
+ ? async (payload) => {
588
+ await statusReactionController.setTool(payload.name);
371
589
  }
372
590
  : undefined,
373
591
  onModelSelected,
@@ -375,14 +593,51 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
375
593
  }));
376
594
  }
377
595
  finally {
378
- // Must stop() first to flush debounced content before clear() wipes state
379
- await draftStream?.stop();
380
- if (!finalizedViaPreviewMessage) {
381
- await draftStream?.clear();
596
+ // Must stop() first to flush debounced content before clear() wipes state.
597
+ const streamCleanupStates = new Map();
598
+ const lanesToCleanup = [
599
+ { laneName: "answer", lane: answerLane },
600
+ { laneName: "reasoning", lane: reasoningLane },
601
+ ];
602
+ for (const laneState of lanesToCleanup) {
603
+ const stream = laneState.lane.stream;
604
+ if (!stream) {
605
+ continue;
606
+ }
607
+ const shouldClear = !finalizedPreviewByLane[laneState.laneName];
608
+ const existing = streamCleanupStates.get(stream);
609
+ if (!existing) {
610
+ streamCleanupStates.set(stream, { shouldClear });
611
+ continue;
612
+ }
613
+ existing.shouldClear = existing.shouldClear && shouldClear;
614
+ }
615
+ for (const [stream, cleanupState] of streamCleanupStates) {
616
+ await stream.stop();
617
+ if (cleanupState.shouldClear) {
618
+ await stream.clear();
619
+ }
620
+ }
621
+ for (const archivedPreview of archivedAnswerPreviews) {
622
+ try {
623
+ await bot.api.deleteMessage(chatId, archivedPreview.messageId);
624
+ }
625
+ catch (err) {
626
+ logVerbose(`telegram: archived answer preview cleanup failed (${archivedPreview.messageId}): ${String(err)}`);
627
+ }
628
+ }
629
+ for (const messageId of archivedReasoningPreviewIds) {
630
+ try {
631
+ await bot.api.deleteMessage(chatId, messageId);
632
+ }
633
+ catch (err) {
634
+ logVerbose(`telegram: archived reasoning preview cleanup failed (${messageId}): ${String(err)}`);
635
+ }
382
636
  }
383
637
  }
384
638
  let sentFallback = false;
385
- if (!deliveryState.delivered && deliveryState.skippedNonSilent > 0) {
639
+ if (!deliveryState.delivered &&
640
+ (deliveryState.skippedNonSilent > 0 || deliveryState.failedNonSilent > 0)) {
386
641
  const result = await deliverReplies({
387
642
  replies: [{ text: EMPTY_RESPONSE_FALLBACK }],
388
643
  ...deliveryBaseOptions,
@@ -390,26 +645,38 @@ export const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, repl
390
645
  sentFallback = result.delivered;
391
646
  }
392
647
  const hasFinalResponse = queuedFinal || sentFallback;
648
+ if (statusReactionController && !hasFinalResponse) {
649
+ void statusReactionController.setError().catch((err) => {
650
+ logVerbose(`telegram: status reaction error finalize failed: ${String(err)}`);
651
+ });
652
+ }
393
653
  if (!hasFinalResponse) {
394
654
  clearGroupHistory();
395
655
  return;
396
656
  }
397
- removeAckReactionAfterReply({
398
- removeAfterReply: removeAckAfterReply,
399
- ackReactionPromise,
400
- ackReactionValue: ackReactionPromise ? "ack" : null,
401
- remove: () => reactionApi?.(chatId, msg.message_id ?? 0, []) ?? Promise.resolve(),
402
- onError: (err) => {
403
- if (!msg.message_id) {
404
- return;
405
- }
406
- logAckFailure({
407
- log: logVerbose,
408
- channel: "telegram",
409
- target: `${chatId}/${msg.message_id}`,
410
- error: err,
411
- });
412
- },
413
- });
657
+ if (statusReactionController) {
658
+ void statusReactionController.setDone().catch((err) => {
659
+ logVerbose(`telegram: status reaction finalize failed: ${String(err)}`);
660
+ });
661
+ }
662
+ else {
663
+ removeAckReactionAfterReply({
664
+ removeAfterReply: removeAckAfterReply,
665
+ ackReactionPromise,
666
+ ackReactionValue: ackReactionPromise ? "ack" : null,
667
+ remove: () => reactionApi?.(chatId, msg.message_id ?? 0, []) ?? Promise.resolve(),
668
+ onError: (err) => {
669
+ if (!msg.message_id) {
670
+ return;
671
+ }
672
+ logAckFailure({
673
+ log: logVerbose,
674
+ channel: "telegram",
675
+ target: `${chatId}/${msg.message_id}`,
676
+ error: err,
677
+ });
678
+ },
679
+ });
680
+ }
414
681
  clearGroupHistory();
415
682
  };