@poolzin/pool-bot 2026.2.21 → 2026.2.23

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 (378) hide show
  1. package/CHANGELOG.md +25 -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/device-pair/index.ts +2 -2
  326. package/extensions/diagnostics-otel/package.json +1 -1
  327. package/extensions/discord/package.json +1 -1
  328. package/extensions/feishu/package.json +1 -1
  329. package/extensions/google-antigravity-auth/package.json +1 -1
  330. package/extensions/google-gemini-cli-auth/package.json +1 -1
  331. package/extensions/googlechat/package.json +1 -1
  332. package/extensions/imessage/package.json +1 -1
  333. package/extensions/irc/package.json +1 -1
  334. package/extensions/irc/src/accounts.ts +1 -1
  335. package/extensions/irc/src/onboarding.ts +4 -4
  336. package/extensions/line/package.json +1 -1
  337. package/extensions/llm-task/package.json +1 -1
  338. package/extensions/lobster/package.json +1 -1
  339. package/extensions/matrix/CHANGELOG.md +10 -0
  340. package/extensions/matrix/package.json +1 -1
  341. package/extensions/mattermost/package.json +1 -1
  342. package/extensions/memory-core/package.json +1 -1
  343. package/extensions/memory-lancedb/package.json +1 -1
  344. package/extensions/minimax-portal-auth/package.json +1 -1
  345. package/extensions/msteams/CHANGELOG.md +10 -0
  346. package/extensions/msteams/package.json +1 -1
  347. package/extensions/nextcloud-talk/package.json +1 -1
  348. package/extensions/nostr/CHANGELOG.md +10 -0
  349. package/extensions/nostr/package.json +1 -1
  350. package/extensions/open-prose/package.json +1 -1
  351. package/extensions/openai-codex-auth/package.json +1 -1
  352. package/extensions/signal/package.json +1 -1
  353. package/extensions/slack/package.json +1 -1
  354. package/extensions/telegram/package.json +1 -1
  355. package/extensions/tlon/package.json +1 -1
  356. package/extensions/twitch/CHANGELOG.md +10 -0
  357. package/extensions/twitch/package.json +1 -1
  358. package/extensions/voice-call/CHANGELOG.md +10 -0
  359. package/extensions/voice-call/package.json +1 -1
  360. package/extensions/whatsapp/package.json +1 -1
  361. package/extensions/zalo/CHANGELOG.md +10 -0
  362. package/extensions/zalo/package.json +1 -1
  363. package/extensions/zalouser/CHANGELOG.md +10 -0
  364. package/extensions/zalouser/package.json +1 -1
  365. package/package.json +1 -1
  366. package/skills/apple-reminders/SKILL.md +100 -49
  367. package/skills/coding-agent/SKILL.md +34 -28
  368. package/skills/github/SKILL.md +131 -16
  369. package/skills/imsg/SKILL.md +112 -15
  370. package/skills/openhue/SKILL.md +101 -19
  371. package/skills/tmux/SKILL.md +111 -79
  372. package/skills/weather/SKILL.md +88 -25
  373. package/dist/agents/openclaw-tools.js +0 -151
  374. package/dist/agents/tool-security.js +0 -96
  375. package/dist/gateway/url-validation.js +0 -94
  376. package/dist/infra/openclaw-root.js +0 -109
  377. package/dist/infra/tmp-openclaw-dir.js +0 -81
  378. package/dist/media/path-sanitization.js +0 -78
@@ -4,100 +4,97 @@ import { finalizeInboundContext } from "../auto-reply/reply/inbound-context.js";
4
4
  import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/provider-dispatcher.js";
5
5
  import { listSkillCommandsForAgents } from "../auto-reply/skill-commands.js";
6
6
  import { resolveCommandAuthorizedFromAuthorizers } from "../channels/command-gating.js";
7
- import { createReplyPrefixContext } from "../channels/reply-prefix.js";
7
+ import { createReplyPrefixOptions } from "../channels/reply-prefix.js";
8
8
  import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
9
- import { resolveTelegramCustomCommands } from "../config/telegram-custom-commands.js";
10
- import { normalizeTelegramCommandName, TELEGRAM_COMMAND_NAME_PATTERN, } from "../config/telegram-custom-commands.js";
9
+ import { normalizeTelegramCommandName, resolveTelegramCustomCommands, TELEGRAM_COMMAND_NAME_PATTERN, } from "../config/telegram-custom-commands.js";
11
10
  import { danger, logVerbose } from "../globals.js";
12
- import { readChannelAllowFromStore } from "../pairing/pairing-store.js";
11
+ import { getAgentScopedMediaLocalRoots } from "../media/local-roots.js";
13
12
  import { executePluginCommand, getPluginCommandSpecs, matchPluginCommand, } from "../plugins/commands.js";
14
13
  import { resolveAgentRoute } from "../routing/resolve-route.js";
15
14
  import { resolveThreadSessionKeys } from "../routing/session-key.js";
16
15
  import { withTelegramApiErrorLogging } from "./api-logging.js";
17
16
  import { firstDefined, isSenderAllowed, normalizeAllowFromWithStore } from "./bot-access.js";
17
+ import { buildCappedTelegramMenuCommands, buildPluginTelegramMenuCommands, syncTelegramMenuCommands, } from "./bot-native-command-menu.js";
18
18
  import { deliverReplies } from "./bot/delivery.js";
19
- import { buildTelegramThreadParams, buildSenderName, buildTelegramGroupFrom, buildTelegramGroupPeerId, resolveTelegramForumThreadId, } from "./bot/helpers.js";
19
+ import { buildTelegramThreadParams, buildSenderName, buildTelegramGroupFrom, buildTelegramGroupPeerId, buildTelegramParentPeer, resolveTelegramGroupAllowFromContext, resolveTelegramThreadSpec, } from "./bot/helpers.js";
20
+ import { evaluateTelegramGroupBaseAccess, evaluateTelegramGroupPolicyAccess, } from "./group-access.js";
20
21
  import { buildInlineKeyboard } from "./send.js";
22
+ const EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again.";
21
23
  async function resolveTelegramCommandAuth(params) {
22
- const { msg, bot, cfg, telegramCfg, allowFrom, groupAllowFrom, useAccessGroups, resolveGroupPolicy, resolveTelegramGroupConfig, requireAuth, } = params;
24
+ const { msg, bot, cfg, accountId, telegramCfg, allowFrom, groupAllowFrom, useAccessGroups, resolveGroupPolicy, resolveTelegramGroupConfig, requireAuth, } = params;
23
25
  const chatId = msg.chat.id;
24
26
  const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup";
25
27
  const messageThreadId = msg.message_thread_id;
26
28
  const isForum = msg.chat.is_forum === true;
27
- const resolvedThreadId = resolveTelegramForumThreadId({
29
+ const groupAllowContext = await resolveTelegramGroupAllowFromContext({
30
+ chatId,
31
+ accountId,
28
32
  isForum,
29
33
  messageThreadId,
34
+ groupAllowFrom,
35
+ resolveTelegramGroupConfig,
30
36
  });
31
- const storeAllowFrom = await readChannelAllowFromStore("telegram").catch(() => []);
32
- const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId);
33
- const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
34
- const effectiveGroupAllow = normalizeAllowFromWithStore({
35
- allowFrom: groupAllowOverride ?? groupAllowFrom,
36
- storeAllowFrom,
37
- });
38
- const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined";
39
- const senderIdRaw = msg.from?.id;
40
- const senderId = senderIdRaw ? String(senderIdRaw) : "";
37
+ const { resolvedThreadId, storeAllowFrom, groupConfig, topicConfig, effectiveGroupAllow, hasGroupAllowOverride, } = groupAllowContext;
38
+ const senderId = msg.from?.id ? String(msg.from.id) : "";
41
39
  const senderUsername = msg.from?.username ?? "";
42
- if (isGroup && groupConfig?.enabled === false) {
43
- await withTelegramApiErrorLogging({
44
- operation: "sendMessage",
45
- fn: () => bot.api.sendMessage(chatId, "This group is disabled."),
46
- });
47
- return null;
48
- }
49
- if (isGroup && topicConfig?.enabled === false) {
40
+ const sendAuthMessage = async (text) => {
50
41
  await withTelegramApiErrorLogging({
51
42
  operation: "sendMessage",
52
- fn: () => bot.api.sendMessage(chatId, "This topic is disabled."),
43
+ fn: () => bot.api.sendMessage(chatId, text),
53
44
  });
54
45
  return null;
55
- }
56
- if (requireAuth && isGroup && hasGroupAllowOverride) {
57
- if (senderIdRaw == null ||
58
- !isSenderAllowed({
59
- allow: effectiveGroupAllow,
60
- senderId: String(senderIdRaw),
61
- senderUsername,
62
- })) {
63
- await withTelegramApiErrorLogging({
64
- operation: "sendMessage",
65
- fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."),
66
- });
67
- return null;
46
+ };
47
+ const rejectNotAuthorized = async () => {
48
+ return await sendAuthMessage("You are not authorized to use this command.");
49
+ };
50
+ const baseAccess = evaluateTelegramGroupBaseAccess({
51
+ isGroup,
52
+ groupConfig,
53
+ topicConfig,
54
+ hasGroupAllowOverride,
55
+ effectiveGroupAllow,
56
+ senderId,
57
+ senderUsername,
58
+ enforceAllowOverride: requireAuth,
59
+ requireSenderForAllowOverride: true,
60
+ });
61
+ if (!baseAccess.allowed) {
62
+ if (baseAccess.reason === "group-disabled") {
63
+ return await sendAuthMessage("This group is disabled.");
64
+ }
65
+ if (baseAccess.reason === "topic-disabled") {
66
+ return await sendAuthMessage("This topic is disabled.");
68
67
  }
68
+ return await rejectNotAuthorized();
69
69
  }
70
- if (isGroup && useAccessGroups) {
71
- const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
72
- const groupPolicy = firstDefined(topicConfig?.groupPolicy, groupConfig?.groupPolicy, telegramCfg.groupPolicy, defaultGroupPolicy, "open");
73
- if (groupPolicy === "disabled") {
74
- await withTelegramApiErrorLogging({
75
- operation: "sendMessage",
76
- fn: () => bot.api.sendMessage(chatId, "Telegram group commands are disabled."),
77
- });
78
- return null;
70
+ const policyAccess = evaluateTelegramGroupPolicyAccess({
71
+ isGroup,
72
+ chatId,
73
+ cfg,
74
+ telegramCfg,
75
+ topicConfig,
76
+ groupConfig,
77
+ effectiveGroupAllow,
78
+ senderId,
79
+ senderUsername,
80
+ resolveGroupPolicy,
81
+ enforcePolicy: useAccessGroups,
82
+ useTopicAndGroupOverrides: false,
83
+ enforceAllowlistAuthorization: requireAuth,
84
+ allowEmptyAllowlistEntries: true,
85
+ requireSenderForAllowlistAuthorization: true,
86
+ checkChatAllowlist: useAccessGroups,
87
+ });
88
+ if (!policyAccess.allowed) {
89
+ if (policyAccess.reason === "group-policy-disabled") {
90
+ return await sendAuthMessage("Telegram group commands are disabled.");
79
91
  }
80
- if (groupPolicy === "allowlist" && requireAuth) {
81
- if (senderIdRaw == null ||
82
- !isSenderAllowed({
83
- allow: effectiveGroupAllow,
84
- senderId: String(senderIdRaw),
85
- senderUsername,
86
- })) {
87
- await withTelegramApiErrorLogging({
88
- operation: "sendMessage",
89
- fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."),
90
- });
91
- return null;
92
- }
92
+ if (policyAccess.reason === "group-policy-allowlist-no-sender" ||
93
+ policyAccess.reason === "group-policy-allowlist-unauthorized") {
94
+ return await rejectNotAuthorized();
93
95
  }
94
- const groupAllowlist = resolveGroupPolicy(chatId);
95
- if (groupAllowlist.allowlistEnabled && !groupAllowlist.allowed) {
96
- await withTelegramApiErrorLogging({
97
- operation: "sendMessage",
98
- fn: () => bot.api.sendMessage(chatId, "This group is not allowed."),
99
- });
100
- return null;
96
+ if (policyAccess.reason === "group-chat-not-allowed") {
97
+ return await sendAuthMessage("This group is not allowed.");
101
98
  }
102
99
  }
103
100
  const dmAllow = normalizeAllowFromWithStore({
@@ -115,11 +112,7 @@ async function resolveTelegramCommandAuth(params) {
115
112
  modeWhenAccessGroupsOff: "configured",
116
113
  });
117
114
  if (requireAuth && !commandAuthorized) {
118
- await withTelegramApiErrorLogging({
119
- operation: "sendMessage",
120
- fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."),
121
- });
122
- return null;
115
+ return await rejectNotAuthorized();
123
116
  }
124
117
  return {
125
118
  chatId,
@@ -137,14 +130,17 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
137
130
  const boundRoute = nativeEnabled && nativeSkillsEnabled
138
131
  ? resolveAgentRoute({ cfg, channel: "telegram", accountId })
139
132
  : null;
140
- const boundAgentIds = boundRoute && boundRoute.matchedBy.startsWith("binding.") ? [boundRoute.agentId] : null;
133
+ const boundAgentIds = boundRoute ? [boundRoute.agentId] : null;
141
134
  const skillCommands = nativeEnabled && nativeSkillsEnabled
142
135
  ? listSkillCommandsForAgents(boundAgentIds ? { cfg, agentIds: boundAgentIds } : { cfg })
143
136
  : [];
144
137
  const nativeCommands = nativeEnabled
145
- ? listNativeCommandSpecsForConfig(cfg, { skillCommands, provider: "telegram" })
138
+ ? listNativeCommandSpecsForConfig(cfg, {
139
+ skillCommands,
140
+ provider: "telegram",
141
+ })
146
142
  : [];
147
- const reservedCommands = new Set(listNativeCommandSpecs().map((command) => command.name.toLowerCase()));
143
+ const reservedCommands = new Set(listNativeCommandSpecs().map((command) => normalizeTelegramCommandName(command.name)));
148
144
  for (const command of skillCommands) {
149
145
  reservedCommands.add(command.name.toLowerCase());
150
146
  }
@@ -157,82 +153,95 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
157
153
  }
158
154
  const customCommands = customResolution.commands;
159
155
  const pluginCommandSpecs = getPluginCommandSpecs();
160
- const pluginCommands = [];
161
156
  const existingCommands = new Set([
162
- ...nativeCommands.map((command) => command.name),
157
+ ...nativeCommands.map((command) => normalizeTelegramCommandName(command.name)),
163
158
  ...customCommands.map((command) => command.command),
164
159
  ].map((command) => command.toLowerCase()));
165
- const pluginCommandNames = new Set();
166
- for (const spec of pluginCommandSpecs) {
167
- const normalized = normalizeTelegramCommandName(spec.name);
168
- if (!normalized || !TELEGRAM_COMMAND_NAME_PATTERN.test(normalized)) {
169
- runtime.error?.(danger(`Plugin command "/${spec.name}" is invalid for Telegram (use a-z, 0-9, underscore; max 32 chars).`));
170
- continue;
171
- }
172
- const description = spec.description.trim();
173
- if (!description) {
174
- runtime.error?.(danger(`Plugin command "/${normalized}" is missing a description.`));
175
- continue;
176
- }
177
- if (existingCommands.has(normalized)) {
178
- runtime.error?.(danger(`Plugin command "/${normalized}" conflicts with an existing Telegram command.`));
179
- continue;
180
- }
181
- if (pluginCommandNames.has(normalized)) {
182
- runtime.error?.(danger(`Plugin command "/${normalized}" is duplicated.`));
183
- continue;
184
- }
185
- pluginCommandNames.add(normalized);
186
- existingCommands.add(normalized);
187
- pluginCommands.push({ command: normalized, description });
160
+ const pluginCatalog = buildPluginTelegramMenuCommands({
161
+ specs: pluginCommandSpecs,
162
+ existingCommands,
163
+ });
164
+ for (const issue of pluginCatalog.issues) {
165
+ runtime.error?.(danger(issue));
188
166
  }
189
167
  const allCommandsFull = [
190
- ...nativeCommands.map((command) => ({
191
- command: command.name,
192
- description: command.description,
193
- })),
194
- ...pluginCommands,
168
+ ...nativeCommands
169
+ .map((command) => {
170
+ const normalized = normalizeTelegramCommandName(command.name);
171
+ if (!TELEGRAM_COMMAND_NAME_PATTERN.test(normalized)) {
172
+ runtime.error?.(danger(`Native command "${command.name}" is invalid for Telegram (resolved to "${normalized}"). Skipping.`));
173
+ return null;
174
+ }
175
+ return {
176
+ command: normalized,
177
+ description: command.description,
178
+ };
179
+ })
180
+ .filter((cmd) => cmd !== null),
181
+ ...(nativeEnabled ? pluginCatalog.commands : []),
195
182
  ...customCommands,
196
183
  ];
197
- // Telegram Bot API limits commands to 100 per scope.
198
- // Truncate with a warning rather than failing with BOT_COMMANDS_TOO_MUCH.
199
- const TELEGRAM_MAX_COMMANDS = 100;
200
- if (allCommandsFull.length > TELEGRAM_MAX_COMMANDS) {
201
- runtime.log?.(`telegram: truncating ${allCommandsFull.length} commands to ${TELEGRAM_MAX_COMMANDS} (Telegram Bot API limit)`);
184
+ const { commandsToRegister, totalCommands, maxCommands, overflowCount } = buildCappedTelegramMenuCommands({
185
+ allCommands: allCommandsFull,
186
+ });
187
+ if (overflowCount > 0) {
188
+ runtime.log?.(`Telegram limits bots to ${maxCommands} commands. ` +
189
+ `${totalCommands} configured; registering first ${maxCommands}. ` +
190
+ `Use channels.telegram.commands.native: false to disable, or reduce plugin/skill/custom commands.`);
202
191
  }
203
- const allCommands = allCommandsFull.slice(0, TELEGRAM_MAX_COMMANDS);
204
- // Clear stale commands before registering new ones to prevent
205
- // leftover commands from deleted skills persisting across restarts.
206
- // Chain delete set so a late-resolving delete cannot wipe newly registered commands.
207
- const registerCommands = () => {
208
- if (allCommands.length > 0) {
209
- withTelegramApiErrorLogging({
210
- operation: "setMyCommands",
211
- runtime,
212
- fn: () => bot.api.setMyCommands(allCommands),
213
- }).catch(() => { });
214
- }
192
+ // Telegram only limits the setMyCommands payload (menu entries).
193
+ // Keep hidden commands callable by registering handlers for the full catalog.
194
+ syncTelegramMenuCommands({ bot, runtime, commandsToRegister });
195
+ const resolveCommandRuntimeContext = (params) => {
196
+ const { msg, isGroup, isForum, resolvedThreadId } = params;
197
+ const chatId = msg.chat.id;
198
+ const messageThreadId = msg.message_thread_id;
199
+ const threadSpec = resolveTelegramThreadSpec({
200
+ isGroup,
201
+ isForum,
202
+ messageThreadId,
203
+ });
204
+ const parentPeer = buildTelegramParentPeer({ isGroup, resolvedThreadId, chatId });
205
+ const route = resolveAgentRoute({
206
+ cfg,
207
+ channel: "telegram",
208
+ accountId,
209
+ peer: {
210
+ kind: isGroup ? "group" : "direct",
211
+ id: isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId),
212
+ },
213
+ parentPeer,
214
+ });
215
+ const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
216
+ const tableMode = resolveMarkdownTableMode({
217
+ cfg,
218
+ channel: "telegram",
219
+ accountId: route.accountId,
220
+ });
221
+ const chunkMode = resolveChunkMode(cfg, "telegram", route.accountId);
222
+ return { chatId, threadSpec, route, mediaLocalRoots, tableMode, chunkMode };
215
223
  };
216
- if (typeof bot.api.deleteMyCommands === "function") {
217
- withTelegramApiErrorLogging({
218
- operation: "deleteMyCommands",
219
- runtime,
220
- fn: () => bot.api.deleteMyCommands(),
221
- })
222
- .catch(() => { })
223
- .then(registerCommands)
224
- .catch(() => { });
225
- }
226
- else {
227
- registerCommands();
228
- }
229
- if (allCommands.length > 0) {
224
+ const buildCommandDeliveryBaseOptions = (params) => ({
225
+ chatId: String(params.chatId),
226
+ token: opts.token,
227
+ runtime,
228
+ bot,
229
+ mediaLocalRoots: params.mediaLocalRoots,
230
+ replyToMode,
231
+ textLimit,
232
+ thread: params.threadSpec,
233
+ tableMode: params.tableMode,
234
+ chunkMode: params.chunkMode,
235
+ linkPreview: telegramCfg.linkPreview,
236
+ });
237
+ if (commandsToRegister.length > 0 || pluginCatalog.commands.length > 0) {
230
238
  if (typeof bot.command !== "function") {
231
239
  logVerbose("telegram: bot.command unavailable; skipping native handlers");
232
240
  }
233
241
  else {
234
242
  for (const command of nativeCommands) {
235
- bot.command(command.name, async (ctx) => {
243
+ const normalizedCommandName = normalizeTelegramCommandName(command.name);
244
+ bot.command(normalizedCommandName, async (ctx) => {
236
245
  const msg = ctx.message;
237
246
  if (!msg) {
238
247
  return;
@@ -244,6 +253,7 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
244
253
  msg,
245
254
  bot,
246
255
  cfg,
256
+ accountId,
247
257
  telegramCfg,
248
258
  allowFrom,
249
259
  groupAllowFrom,
@@ -256,8 +266,20 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
256
266
  return;
257
267
  }
258
268
  const { chatId, isGroup, isForum, resolvedThreadId, senderId, senderUsername, groupConfig, topicConfig, commandAuthorized, } = auth;
259
- const messageThreadId = msg.message_thread_id;
260
- const threadParams = buildTelegramThreadParams(resolvedThreadId) ?? {};
269
+ const { threadSpec, route, mediaLocalRoots, tableMode, chunkMode } = resolveCommandRuntimeContext({
270
+ msg,
271
+ isGroup,
272
+ isForum,
273
+ resolvedThreadId,
274
+ });
275
+ const deliveryBaseOptions = buildCommandDeliveryBaseOptions({
276
+ chatId,
277
+ mediaLocalRoots,
278
+ threadSpec,
279
+ tableMode,
280
+ chunkMode,
281
+ });
282
+ const threadParams = buildTelegramThreadParams(threadSpec) ?? {};
261
283
  const commandDefinition = findCommandByNativeName(command.name, "telegram");
262
284
  const rawText = ctx.match?.trim() ?? "";
263
285
  const commandArgs = commandDefinition
@@ -304,18 +326,9 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
304
326
  });
305
327
  return;
306
328
  }
307
- const route = resolveAgentRoute({
308
- cfg,
309
- channel: "telegram",
310
- accountId,
311
- peer: {
312
- kind: isGroup ? "group" : "dm",
313
- id: isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId),
314
- },
315
- });
316
329
  const baseSessionKey = route.sessionKey;
317
330
  // DMs: use raw messageThreadId for thread sessions (not resolvedThreadId which is for forums)
318
- const dmThreadId = !isGroup ? messageThreadId : undefined;
331
+ const dmThreadId = threadSpec.scope === "dm" ? threadSpec.id : undefined;
319
332
  const threadKeys = dmThreadId != null
320
333
  ? resolveThreadSessionKeys({
321
334
  baseSessionKey,
@@ -323,11 +336,6 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
323
336
  })
324
337
  : null;
325
338
  const sessionKey = threadKeys?.sessionKey ?? baseSessionKey;
326
- const tableMode = resolveMarkdownTableMode({
327
- cfg,
328
- channel: "telegram",
329
- accountId: route.accountId,
330
- });
331
339
  const skillFilter = firstDefined(topicConfig?.skills, groupConfig?.skills);
332
340
  const systemPromptParts = [
333
341
  groupConfig?.systemPrompt?.trim() || null,
@@ -363,7 +371,7 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
363
371
  SessionKey: `telegram:slash:${senderId || chatId}`,
364
372
  AccountId: route.accountId,
365
373
  CommandTargetSessionKey: sessionKey,
366
- MessageThreadId: resolvedThreadId,
374
+ MessageThreadId: threadSpec.id,
367
375
  IsForum: isForum,
368
376
  // Originating context for sub-agent announce routing
369
377
  OriginatingChannel: "telegram",
@@ -372,10 +380,15 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
372
380
  const disableBlockStreaming = typeof telegramCfg.blockStreaming === "boolean"
373
381
  ? !telegramCfg.blockStreaming
374
382
  : undefined;
375
- const chunkMode = resolveChunkMode(cfg, "telegram", route.accountId);
376
- const { onModelSelected, ...prefixOptions } = createReplyPrefixContext({
383
+ const deliveryState = {
384
+ delivered: false,
385
+ skippedNonSilent: 0,
386
+ };
387
+ const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
377
388
  cfg,
378
389
  agentId: route.agentId,
390
+ channel: "telegram",
391
+ accountId: route.accountId,
379
392
  });
380
393
  await dispatchReplyWithBufferedBlockDispatcher({
381
394
  ctx: ctxPayload,
@@ -383,19 +396,18 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
383
396
  dispatcherOptions: {
384
397
  ...prefixOptions,
385
398
  deliver: async (payload, _info) => {
386
- await deliverReplies({
399
+ const result = await deliverReplies({
387
400
  replies: [payload],
388
- chatId: String(chatId),
389
- token: opts.token,
390
- runtime,
391
- bot,
392
- replyToMode,
393
- textLimit,
394
- messageThreadId: resolvedThreadId,
395
- tableMode,
396
- chunkMode,
397
- linkPreview: telegramCfg.linkPreview,
401
+ ...deliveryBaseOptions,
398
402
  });
403
+ if (result.delivered) {
404
+ deliveryState.delivered = true;
405
+ }
406
+ },
407
+ onSkip: (_payload, info) => {
408
+ if (info.reason !== "silent") {
409
+ deliveryState.skippedNonSilent += 1;
410
+ }
399
411
  },
400
412
  onError: (err, info) => {
401
413
  runtime.error?.(danger(`telegram slash ${info.kind} reply failed: ${String(err)}`));
@@ -407,15 +419,23 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
407
419
  onModelSelected,
408
420
  },
409
421
  });
422
+ if (!deliveryState.delivered && deliveryState.skippedNonSilent > 0) {
423
+ await deliverReplies({
424
+ replies: [{ text: EMPTY_RESPONSE_FALLBACK }],
425
+ ...deliveryBaseOptions,
426
+ });
427
+ }
410
428
  });
411
429
  }
412
- for (const pluginCommand of pluginCommands) {
430
+ for (const pluginCommand of pluginCatalog.commands) {
413
431
  bot.command(pluginCommand.command, async (ctx) => {
414
432
  const msg = ctx.message;
415
- if (!msg)
433
+ if (!msg) {
416
434
  return;
417
- if (shouldSkipUpdate(ctx))
435
+ }
436
+ if (shouldSkipUpdate(ctx)) {
418
437
  return;
438
+ }
419
439
  const chatId = msg.chat.id;
420
440
  const rawText = ctx.match?.trim() ?? "";
421
441
  const commandBody = `/${pluginCommand.command}${rawText ? ` ${rawText}` : ""}`;
@@ -432,6 +452,7 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
432
452
  msg,
433
453
  bot,
434
454
  cfg,
455
+ accountId,
435
456
  telegramCfg,
436
457
  allowFrom,
437
458
  groupAllowFrom,
@@ -440,11 +461,25 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
440
461
  resolveTelegramGroupConfig,
441
462
  requireAuth: match.command.requireAuth !== false,
442
463
  });
443
- if (!auth)
464
+ if (!auth) {
444
465
  return;
445
- const { resolvedThreadId, senderId, commandAuthorized, isGroup } = auth;
466
+ }
467
+ const { senderId, commandAuthorized, isGroup, isForum, resolvedThreadId } = auth;
468
+ const { threadSpec, mediaLocalRoots, tableMode, chunkMode } = resolveCommandRuntimeContext({
469
+ msg,
470
+ isGroup,
471
+ isForum,
472
+ resolvedThreadId,
473
+ });
474
+ const deliveryBaseOptions = buildCommandDeliveryBaseOptions({
475
+ chatId,
476
+ mediaLocalRoots,
477
+ threadSpec,
478
+ tableMode,
479
+ chunkMode,
480
+ });
446
481
  const from = isGroup
447
- ? buildTelegramGroupFrom(chatId, resolvedThreadId)
482
+ ? buildTelegramGroupFrom(chatId, threadSpec.id)
448
483
  : `telegram:${chatId}`;
449
484
  const to = `telegram:${chatId}`;
450
485
  const result = await executePluginCommand({
@@ -458,33 +493,18 @@ export const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, t
458
493
  from,
459
494
  to,
460
495
  accountId,
461
- messageThreadId: resolvedThreadId,
496
+ messageThreadId: threadSpec.id,
462
497
  });
463
- const tableMode = resolveMarkdownTableMode({
464
- cfg,
465
- channel: "telegram",
466
- accountId,
467
- });
468
- const chunkMode = resolveChunkMode(cfg, "telegram", accountId);
469
498
  await deliverReplies({
470
499
  replies: [result],
471
- chatId: String(chatId),
472
- token: opts.token,
473
- runtime,
474
- bot,
475
- replyToMode,
476
- textLimit,
477
- messageThreadId: resolvedThreadId,
478
- tableMode,
479
- chunkMode,
480
- linkPreview: telegramCfg.linkPreview,
500
+ ...deliveryBaseOptions,
481
501
  });
482
502
  });
483
503
  }
484
504
  }
485
505
  }
486
506
  else if (nativeDisabledExplicit) {
487
- void withTelegramApiErrorLogging({
507
+ withTelegramApiErrorLogging({
488
508
  operation: "setMyCommands",
489
509
  runtime,
490
510
  fn: () => bot.api.setMyCommands([]),