@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
@@ -13,8 +13,9 @@ export function stripTelegramInternalPrefixes(to) {
13
13
  }
14
14
  return trimmed;
15
15
  })();
16
- if (next === trimmed)
16
+ if (next === trimmed) {
17
17
  return trimmed;
18
+ }
18
19
  trimmed = next;
19
20
  }
20
21
  }
@@ -26,6 +27,16 @@ export function stripTelegramInternalPrefixes(to) {
26
27
  * - `chatId:topicId` (numeric topic/thread ID)
27
28
  * - `chatId:topic:topicId` (explicit topic marker; preferred)
28
29
  */
30
+ function resolveTelegramChatType(chatId) {
31
+ const trimmed = chatId.trim();
32
+ if (!trimmed) {
33
+ return "unknown";
34
+ }
35
+ if (/^-?\d+$/.test(trimmed)) {
36
+ return trimmed.startsWith("-") ? "group" : "direct";
37
+ }
38
+ return "unknown";
39
+ }
29
40
  export function parseTelegramTarget(to) {
30
41
  const normalized = stripTelegramInternalPrefixes(to);
31
42
  const topicMatch = /^(.+?):topic:(\d+)$/.exec(normalized);
@@ -33,6 +44,7 @@ export function parseTelegramTarget(to) {
33
44
  return {
34
45
  chatId: topicMatch[1],
35
46
  messageThreadId: Number.parseInt(topicMatch[2], 10),
47
+ chatType: resolveTelegramChatType(topicMatch[1]),
36
48
  };
37
49
  }
38
50
  const colonMatch = /^(.+):(\d+)$/.exec(normalized);
@@ -40,7 +52,14 @@ export function parseTelegramTarget(to) {
40
52
  return {
41
53
  chatId: colonMatch[1],
42
54
  messageThreadId: Number.parseInt(colonMatch[2], 10),
55
+ chatType: resolveTelegramChatType(colonMatch[1]),
43
56
  };
44
57
  }
45
- return { chatId: normalized };
58
+ return {
59
+ chatId: normalized,
60
+ chatType: resolveTelegramChatType(normalized),
61
+ };
62
+ }
63
+ export function resolveTelegramTargetChatType(target) {
64
+ return parseTelegramTarget(target).chatType;
46
65
  }
@@ -6,8 +6,9 @@ import { resolveStateDir } from "../config/paths.js";
6
6
  const STORE_VERSION = 1;
7
7
  function normalizeAccountId(accountId) {
8
8
  const trimmed = accountId?.trim();
9
- if (!trimmed)
9
+ if (!trimmed) {
10
10
  return "default";
11
+ }
11
12
  return trimmed.replace(/[^a-z0-9._-]+/gi, "_");
12
13
  }
13
14
  function resolveTelegramUpdateOffsetPath(accountId, env = process.env) {
@@ -18,8 +19,9 @@ function resolveTelegramUpdateOffsetPath(accountId, env = process.env) {
18
19
  function safeParseState(raw) {
19
20
  try {
20
21
  const parsed = JSON.parse(raw);
21
- if (parsed?.version !== STORE_VERSION)
22
+ if (parsed?.version !== STORE_VERSION) {
22
23
  return null;
24
+ }
23
25
  if (parsed.lastUpdateId !== null && typeof parsed.lastUpdateId !== "number") {
24
26
  return null;
25
27
  }
@@ -38,8 +40,9 @@ export async function readTelegramUpdateOffset(params) {
38
40
  }
39
41
  catch (err) {
40
42
  const code = err.code;
41
- if (code === "ENOENT")
43
+ if (code === "ENOENT") {
42
44
  return null;
45
+ }
43
46
  return null;
44
47
  }
45
48
  }
@@ -58,3 +61,16 @@ export async function writeTelegramUpdateOffset(params) {
58
61
  await fs.chmod(tmp, 0o600);
59
62
  await fs.rename(tmp, filePath);
60
63
  }
64
+ export async function deleteTelegramUpdateOffset(params) {
65
+ const filePath = resolveTelegramUpdateOffsetPath(params.accountId, params.env);
66
+ try {
67
+ await fs.unlink(filePath);
68
+ }
69
+ catch (err) {
70
+ const code = err.code;
71
+ if (code === "ENOENT") {
72
+ return;
73
+ }
74
+ throw err;
75
+ }
76
+ }
@@ -10,7 +10,10 @@ function reportRestoreFailure(scope, err, reason) {
10
10
  console.error(`[terminal] restore reporting failed${suffix}: ${String(writeErr)}`);
11
11
  }
12
12
  }
13
- export function restoreTerminalState(reason) {
13
+ export function restoreTerminalState(reason, options = {}) {
14
+ // Docker TTY note: resuming stdin can keep a container process alive even
15
+ // after the wizard is "done" (stdin_open: true), making installers appear hung.
16
+ const resumeStdin = options.resumeStdinIfPaused ?? options.resumeStdin ?? false;
14
17
  try {
15
18
  clearActiveProgressLine();
16
19
  }
@@ -25,7 +28,7 @@ export function restoreTerminalState(reason) {
25
28
  catch (err) {
26
29
  reportRestoreFailure("raw mode", err, reason);
27
30
  }
28
- if (typeof stdin.isPaused === "function" && stdin.isPaused()) {
31
+ if (resumeStdin && typeof stdin.isPaused === "function" && stdin.isPaused()) {
29
32
  try {
30
33
  stdin.resume();
31
34
  }
@@ -0,0 +1,5 @@
1
+ export function withFetchPreconnect(fn) {
2
+ return Object.assign(fn, {
3
+ preconnect: (_url, _options) => { },
4
+ });
5
+ }
package/dist/version.js CHANGED
@@ -18,10 +18,12 @@ function readVersionFromJsonCandidates(moduleUrl, candidates, opts = {}) {
18
18
  try {
19
19
  const parsed = require(candidate);
20
20
  const version = parsed.version?.trim();
21
- if (!version)
21
+ if (!version) {
22
22
  continue;
23
- if (opts.requirePackageName && parsed.name !== CORE_PACKAGE_NAME)
23
+ }
24
+ if (opts.requirePackageName && parsed.name !== CORE_PACKAGE_NAME) {
24
25
  continue;
26
+ }
25
27
  return version;
26
28
  }
27
29
  catch {
@@ -34,6 +36,15 @@ function readVersionFromJsonCandidates(moduleUrl, candidates, opts = {}) {
34
36
  return null;
35
37
  }
36
38
  }
39
+ function firstNonEmpty(...values) {
40
+ for (const value of values) {
41
+ const trimmed = value?.trim();
42
+ if (trimmed) {
43
+ return trimmed;
44
+ }
45
+ }
46
+ return undefined;
47
+ }
37
48
  export function readVersionFromPackageJsonForModuleUrl(moduleUrl) {
38
49
  return readVersionFromJsonCandidates(moduleUrl, PACKAGE_JSON_CANDIDATES, {
39
50
  requirePackageName: true,
@@ -46,11 +57,13 @@ export function resolveVersionFromModuleUrl(moduleUrl) {
46
57
  return (readVersionFromPackageJsonForModuleUrl(moduleUrl) ||
47
58
  readVersionFromBuildInfoForModuleUrl(moduleUrl));
48
59
  }
49
- // Single source of truth for the current poolbot version.
60
+ export function resolveRuntimeServiceVersion(env = process.env, fallback = "dev") {
61
+ return (firstNonEmpty(env["POOLBOT_VERSION"], env["POOLBOT_SERVICE_VERSION"], env["npm_package_version"]) ?? fallback);
62
+ }
63
+ // Single source of truth for the current Pool Bot version.
50
64
  // - Embedded/bundled builds: injected define or env var.
51
65
  // - Dev/npm builds: package.json.
52
- export const VERSION = (typeof __CLAWDBOT_VERSION__ === "string" && __CLAWDBOT_VERSION__) ||
66
+ export const VERSION = (typeof __POOLBOT_VERSION__ === "string" && __POOLBOT_VERSION__) ||
53
67
  process.env.POOLBOT_BUNDLED_VERSION ||
54
- process.env.CLAWDBOT_BUNDLED_VERSION ||
55
68
  resolveVersionFromModuleUrl(import.meta.url) ||
56
69
  "0.0.0";
@@ -4,10 +4,12 @@ import { formatError } from "../../session.js";
4
4
  import { whatsappInboundLog } from "../loggers.js";
5
5
  export async function maybeBroadcastMessage(params) {
6
6
  const broadcastAgents = params.cfg.broadcast?.[params.peerId];
7
- if (!broadcastAgents || !Array.isArray(broadcastAgents))
7
+ if (!broadcastAgents || !Array.isArray(broadcastAgents)) {
8
8
  return false;
9
- if (broadcastAgents.length === 0)
9
+ }
10
+ if (broadcastAgents.length === 0) {
10
11
  return false;
12
+ }
11
13
  const strategy = params.cfg.broadcast?.strategy || "parallel";
12
14
  whatsappInboundLog.info(`Broadcasting message to ${broadcastAgents.length} agents (${strategy})`);
13
15
  const agentIds = params.cfg.agents?.list?.map((agent) => normalizeAgentId(agent.id));
@@ -27,11 +29,13 @@ export async function maybeBroadcastMessage(params) {
27
29
  sessionKey: buildAgentSessionKey({
28
30
  agentId: normalizedAgentId,
29
31
  channel: "whatsapp",
32
+ accountId: params.route.accountId,
30
33
  peer: {
31
- kind: params.msg.chatType === "group" ? "group" : "dm",
34
+ kind: params.msg.chatType === "group" ? "group" : "direct",
32
35
  id: params.peerId,
33
36
  },
34
37
  dmScope: params.cfg.session?.dmScope,
38
+ identityLinks: params.cfg.session?.identityLinks,
35
39
  }),
36
40
  mainSessionKey: buildAgentMainSessionKey({
37
41
  agentId: normalizedAgentId,
@@ -1,3 +1,4 @@
1
+ import { loadConfig } from "../../../config/config.js";
1
2
  import { logVerbose } from "../../../globals.js";
2
3
  import { resolveAgentRoute } from "../../../routing/resolve-route.js";
3
4
  import { buildGroupHistoryKey } from "../../../routing/session-key.js";
@@ -31,12 +32,13 @@ export function createWebOnMessageHandler(params) {
31
32
  return async (msg) => {
32
33
  const conversationId = msg.conversationId ?? msg.from;
33
34
  const peerId = resolvePeerId(msg);
35
+ // Fresh config for bindings lookup; other routing inputs are payload-derived.
34
36
  const route = resolveAgentRoute({
35
- cfg: params.cfg,
37
+ cfg: loadConfig(),
36
38
  channel: "whatsapp",
37
39
  accountId: msg.accountId,
38
40
  peer: {
39
- kind: msg.chatType === "group" ? "group" : "dm",
41
+ kind: msg.chatType === "group" ? "group" : "direct",
40
42
  id: peerId,
41
43
  },
42
44
  });
@@ -101,8 +103,9 @@ export function createWebOnMessageHandler(params) {
101
103
  logVerbose,
102
104
  replyLogger: params.replyLogger,
103
105
  });
104
- if (!gating.shouldProcess)
106
+ if (!gating.shouldProcess) {
105
107
  return;
108
+ }
106
109
  }
107
110
  else {
108
111
  // Ensure `peerId` for DMs is stable and stored as E.164 when possible.
@@ -4,16 +4,42 @@ function unwrapMessage(message) {
4
4
  const normalized = normalizeMessageContent(message);
5
5
  return normalized;
6
6
  }
7
- export async function downloadInboundMedia(msg, sock) {
8
- const message = unwrapMessage(msg.message);
9
- if (!message)
10
- return undefined;
11
- const mimetype = message.imageMessage?.mimetype ??
7
+ /**
8
+ * Resolve the MIME type for an inbound media message.
9
+ * Falls back to WhatsApp's standard formats when Baileys omits the MIME.
10
+ */
11
+ function resolveMediaMimetype(message) {
12
+ const explicit = message.imageMessage?.mimetype ??
12
13
  message.videoMessage?.mimetype ??
13
14
  message.documentMessage?.mimetype ??
14
15
  message.audioMessage?.mimetype ??
15
16
  message.stickerMessage?.mimetype ??
16
17
  undefined;
18
+ if (explicit) {
19
+ return explicit;
20
+ }
21
+ // WhatsApp voice messages (PTT) and audio use OGG Opus by default
22
+ if (message.audioMessage) {
23
+ return "audio/ogg; codecs=opus";
24
+ }
25
+ if (message.imageMessage) {
26
+ return "image/jpeg";
27
+ }
28
+ if (message.videoMessage) {
29
+ return "video/mp4";
30
+ }
31
+ if (message.stickerMessage) {
32
+ return "image/webp";
33
+ }
34
+ return undefined;
35
+ }
36
+ export async function downloadInboundMedia(msg, sock) {
37
+ const message = unwrapMessage(msg.message);
38
+ if (!message) {
39
+ return undefined;
40
+ }
41
+ const mimetype = resolveMediaMimetype(message);
42
+ const fileName = message.documentMessage?.fileName ?? undefined;
17
43
  if (!message.imageMessage &&
18
44
  !message.videoMessage &&
19
45
  !message.documentMessage &&
@@ -22,11 +48,11 @@ export async function downloadInboundMedia(msg, sock) {
22
48
  return undefined;
23
49
  }
24
50
  try {
25
- const buffer = (await downloadMediaMessage(msg, "buffer", {}, {
51
+ const buffer = await downloadMediaMessage(msg, "buffer", {}, {
26
52
  reuploadRequest: sock.updateMediaMessage,
27
53
  logger: sock.logger,
28
- }));
29
- return { buffer, mimetype };
54
+ });
55
+ return { buffer, mimetype, fileName };
30
56
  }
31
57
  catch (err) {
32
58
  logVerbose(`downloadMediaMessage failed: ${String(err)}`);
@@ -1,11 +1,11 @@
1
1
  import { DisconnectReason, isJidGroup } from "@whiskeysockets/baileys";
2
+ import { createInboundDebouncer } from "../../auto-reply/inbound-debounce.js";
2
3
  import { formatLocationText } from "../../channels/location.js";
3
4
  import { logVerbose, shouldLogVerbose } from "../../globals.js";
4
5
  import { recordChannelActivity } from "../../infra/channel-activity.js";
5
6
  import { getChildLogger } from "../../logging/logger.js";
6
7
  import { createSubsystemLogger } from "../../logging/subsystem.js";
7
8
  import { saveMediaBuffer } from "../../media/store.js";
8
- import { createInboundDebouncer } from "../../auto-reply/inbound-debounce.js";
9
9
  import { jidToE164, resolveJidToE164 } from "../../utils.js";
10
10
  import { createWaSocket, getStatusCode, waitForWaConnection } from "../session.js";
11
11
  import { checkInboundAccessControl } from "./access-control.js";
@@ -26,16 +26,18 @@ export async function monitorWebInbox(options) {
26
26
  onCloseResolve = resolve;
27
27
  });
28
28
  const resolveClose = (reason) => {
29
- if (!onCloseResolve)
29
+ if (!onCloseResolve) {
30
30
  return;
31
+ }
31
32
  const resolver = onCloseResolve;
32
33
  onCloseResolve = null;
33
34
  resolver(reason);
34
35
  };
35
36
  try {
36
37
  await sock.sendPresenceUpdate("available");
37
- if (shouldLogVerbose())
38
+ if (shouldLogVerbose()) {
38
39
  logVerbose("Sent global 'available' presence on connect");
40
+ }
39
41
  }
40
42
  catch (err) {
41
43
  logVerbose(`Failed to send 'available' presence on connect: ${String(err)}`);
@@ -48,24 +50,27 @@ export async function monitorWebInbox(options) {
48
50
  const senderKey = msg.chatType === "group"
49
51
  ? (msg.senderJid ?? msg.senderE164 ?? msg.senderName ?? msg.from)
50
52
  : msg.from;
51
- if (!senderKey)
53
+ if (!senderKey) {
52
54
  return null;
55
+ }
53
56
  const conversationKey = msg.chatType === "group" ? msg.chatId : msg.from;
54
57
  return `${msg.accountId}:${conversationKey}:${senderKey}`;
55
58
  },
56
59
  shouldDebounce: options.shouldDebounce,
57
60
  onFlush: async (entries) => {
58
61
  const last = entries.at(-1);
59
- if (!last)
62
+ if (!last) {
60
63
  return;
64
+ }
61
65
  if (entries.length === 1) {
62
66
  await options.onMessage(last);
63
67
  return;
64
68
  }
65
69
  const mentioned = new Set();
66
70
  for (const entry of entries) {
67
- for (const jid of entry.mentionedJids ?? [])
71
+ for (const jid of entry.mentionedJids ?? []) {
68
72
  mentioned.add(jid);
73
+ }
69
74
  }
70
75
  const combinedBody = entries
71
76
  .map((entry) => entry.body)
@@ -89,8 +94,9 @@ export async function monitorWebInbox(options) {
89
94
  const resolveInboundJid = async (jid) => resolveJidToE164(jid, { authDir: options.authDir, lidLookup });
90
95
  const getGroupMeta = async (jid) => {
91
96
  const cached = groupMetaCache.get(jid);
92
- if (cached && cached.expires > Date.now())
97
+ if (cached && cached.expires > Date.now()) {
93
98
  return cached;
99
+ }
94
100
  try {
95
101
  const meta = await sock.groupMetadata(jid);
96
102
  const participants = (await Promise.all(meta.participants?.map(async (p) => {
@@ -111,8 +117,9 @@ export async function monitorWebInbox(options) {
111
117
  }
112
118
  };
113
119
  const handleMessagesUpsert = async (upsert) => {
114
- if (upsert.type !== "notify" && upsert.type !== "append")
120
+ if (upsert.type !== "notify" && upsert.type !== "append") {
115
121
  return;
122
+ }
116
123
  for (const msg of upsert.messages ?? []) {
117
124
  recordChannelActivity({
118
125
  channel: "whatsapp",
@@ -121,20 +128,24 @@ export async function monitorWebInbox(options) {
121
128
  });
122
129
  const id = msg.key?.id ?? undefined;
123
130
  const remoteJid = msg.key?.remoteJid;
124
- if (!remoteJid)
131
+ if (!remoteJid) {
125
132
  continue;
126
- if (remoteJid.endsWith("@status") || remoteJid.endsWith("@broadcast"))
133
+ }
134
+ if (remoteJid.endsWith("@status") || remoteJid.endsWith("@broadcast")) {
127
135
  continue;
136
+ }
128
137
  const group = isJidGroup(remoteJid) === true;
129
138
  if (id) {
130
139
  const dedupeKey = `${options.accountId}:${remoteJid}:${id}`;
131
- if (isRecentInboundMessage(dedupeKey))
140
+ if (isRecentInboundMessage(dedupeKey)) {
132
141
  continue;
142
+ }
133
143
  }
134
144
  const participantJid = msg.key?.participant ?? undefined;
135
145
  const from = group ? remoteJid : await resolveInboundJid(remoteJid);
136
- if (!from)
146
+ if (!from) {
137
147
  continue;
148
+ }
138
149
  const senderE164 = group
139
150
  ? participantJid
140
151
  ? await resolveInboundJid(participantJid)
@@ -163,8 +174,9 @@ export async function monitorWebInbox(options) {
163
174
  sock: { sendMessage: (jid, content) => sock.sendMessage(jid, content) },
164
175
  remoteJid,
165
176
  });
166
- if (!access.allowed)
177
+ if (!access.allowed) {
167
178
  continue;
179
+ }
168
180
  if (id && !access.isSelfChat && options.sendReadReceipts !== false) {
169
181
  const participant = msg.key?.participant;
170
182
  try {
@@ -183,8 +195,9 @@ export async function monitorWebInbox(options) {
183
195
  logVerbose(`Self-chat mode: skipping read receipt for ${id}`);
184
196
  }
185
197
  // If this is history/offline catch-up, mark read above but skip auto-reply.
186
- if (upsert.type === "append")
198
+ if (upsert.type === "append") {
187
199
  continue;
200
+ }
188
201
  const location = extractLocationData(msg.message ?? undefined);
189
202
  const locationText = location ? formatLocationText(location) : undefined;
190
203
  let body = extractText(msg.message ?? undefined);
@@ -193,12 +206,14 @@ export async function monitorWebInbox(options) {
193
206
  }
194
207
  if (!body) {
195
208
  body = extractMediaPlaceholder(msg.message ?? undefined);
196
- if (!body)
209
+ if (!body) {
197
210
  continue;
211
+ }
198
212
  }
199
213
  const replyContext = describeReplyContext(msg.message);
200
214
  let mediaPath;
201
215
  let mediaType;
216
+ let mediaFileName;
202
217
  try {
203
218
  const inboundMedia = await downloadInboundMedia(msg, sock);
204
219
  if (inboundMedia) {
@@ -206,9 +221,10 @@ export async function monitorWebInbox(options) {
206
221
  ? options.mediaMaxMb
207
222
  : 50;
208
223
  const maxBytes = maxMb * 1024 * 1024;
209
- const saved = await saveMediaBuffer(inboundMedia.buffer, inboundMedia.mimetype, "inbound", maxBytes);
224
+ const saved = await saveMediaBuffer(inboundMedia.buffer, inboundMedia.mimetype, "inbound", maxBytes, inboundMedia.fileName);
210
225
  mediaPath = saved.path;
211
226
  mediaType = inboundMedia.mimetype;
227
+ mediaFileName = inboundMedia.fileName;
212
228
  }
213
229
  }
214
230
  catch (err) {
@@ -232,7 +248,7 @@ export async function monitorWebInbox(options) {
232
248
  const timestamp = messageTimestampMs;
233
249
  const mentionedJids = extractMentionedJids(msg.message);
234
250
  const senderName = msg.pushName ?? undefined;
235
- inboundLogger.info({ from, to: selfE164 ?? "me", body, mediaPath, mediaType, timestamp }, "inbound message");
251
+ inboundLogger.info({ from, to: selfE164 ?? "me", body, mediaPath, mediaType, mediaFileName, timestamp }, "inbound message");
236
252
  const inboundMessage = {
237
253
  id,
238
254
  from,
@@ -263,6 +279,7 @@ export async function monitorWebInbox(options) {
263
279
  sendMedia,
264
280
  mediaPath,
265
281
  mediaType,
282
+ mediaFileName,
266
283
  };
267
284
  try {
268
285
  const task = Promise.resolve(debouncer.enqueue(inboundMessage));
@@ -1,5 +1,17 @@
1
1
  import { recordChannelActivity } from "../../infra/channel-activity.js";
2
2
  import { toWhatsappJid } from "../../utils.js";
3
+ function recordWhatsAppOutbound(accountId) {
4
+ recordChannelActivity({
5
+ channel: "whatsapp",
6
+ accountId,
7
+ direction: "outbound",
8
+ });
9
+ }
10
+ function resolveOutboundMessageId(result) {
11
+ return typeof result === "object" && result && "key" in result
12
+ ? String(result.key?.id ?? "unknown")
13
+ : "unknown";
14
+ }
3
15
  export function createWebSendApi(params) {
4
16
  return {
5
17
  sendMessage: async (to, text, mediaBuffer, mediaType, sendOptions) => {
@@ -26,9 +38,10 @@ export function createWebSendApi(params) {
26
38
  };
27
39
  }
28
40
  else {
41
+ const fileName = sendOptions?.fileName?.trim() || "file";
29
42
  payload = {
30
43
  document: mediaBuffer,
31
- fileName: "file",
44
+ fileName,
32
45
  caption: text || undefined,
33
46
  mimetype: mediaType,
34
47
  };
@@ -39,14 +52,8 @@ export function createWebSendApi(params) {
39
52
  }
40
53
  const result = await params.sock.sendMessage(jid, payload);
41
54
  const accountId = sendOptions?.accountId ?? params.defaultAccountId;
42
- recordChannelActivity({
43
- channel: "whatsapp",
44
- accountId,
45
- direction: "outbound",
46
- });
47
- const messageId = typeof result === "object" && result && "key" in result
48
- ? String(result.key?.id ?? "unknown")
49
- : "unknown";
55
+ recordWhatsAppOutbound(accountId);
56
+ const messageId = resolveOutboundMessageId(result);
50
57
  return { messageId };
51
58
  },
52
59
  sendPoll: async (to, poll) => {
@@ -58,14 +65,8 @@ export function createWebSendApi(params) {
58
65
  selectableCount: poll.maxSelections ?? 1,
59
66
  },
60
67
  });
61
- recordChannelActivity({
62
- channel: "whatsapp",
63
- accountId: params.defaultAccountId,
64
- direction: "outbound",
65
- });
66
- const messageId = typeof result === "object" && result && "key" in result
67
- ? String(result.key?.id ?? "unknown")
68
- : "unknown";
68
+ recordWhatsAppOutbound(params.defaultAccountId);
69
+ const messageId = resolveOutboundMessageId(result);
69
70
  return { messageId };
70
71
  },
71
72
  sendReaction: async (chatJid, messageId, emoji, fromMe, participant) => {
@@ -1,11 +1,12 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { loadConfig } from "../config/config.js";
3
+ import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
2
4
  import { getChildLogger } from "../logging/logger.js";
3
5
  import { createSubsystemLogger } from "../logging/subsystem.js";
6
+ import { convertMarkdownTables } from "../markdown/tables.js";
7
+ import { markdownToWhatsApp } from "../markdown/whatsapp.js";
4
8
  import { normalizePollInput } from "../polls.js";
5
9
  import { toWhatsappJid } from "../utils.js";
6
- import { loadConfig } from "../config/config.js";
7
- import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
8
- import { convertMarkdownTables } from "../markdown/tables.js";
9
10
  import { requireActiveWebListener } from "./active-listener.js";
10
11
  import { loadWebMedia } from "./media.js";
11
12
  const outboundLog = createSubsystemLogger("gateway/channels/whatsapp").child("outbound");
@@ -21,6 +22,7 @@ export async function sendMessageWhatsApp(to, body, options) {
21
22
  accountId: resolvedAccountId ?? options.accountId,
22
23
  });
23
24
  text = convertMarkdownTables(text ?? "", tableMode);
25
+ text = markdownToWhatsApp(text);
24
26
  const logger = getChildLogger({
25
27
  module: "web-outbound",
26
28
  correlationId,
@@ -30,8 +32,11 @@ export async function sendMessageWhatsApp(to, body, options) {
30
32
  const jid = toWhatsappJid(to);
31
33
  let mediaBuffer;
32
34
  let mediaType;
35
+ let documentFileName;
33
36
  if (options.mediaUrl) {
34
- const media = await loadWebMedia(options.mediaUrl);
37
+ const media = await loadWebMedia(options.mediaUrl, {
38
+ localRoots: options.mediaLocalRoots,
39
+ });
35
40
  const caption = text || undefined;
36
41
  mediaBuffer = media.buffer;
37
42
  mediaType = media.contentType;
@@ -50,6 +55,7 @@ export async function sendMessageWhatsApp(to, body, options) {
50
55
  }
51
56
  else {
52
57
  text = caption ?? "";
58
+ documentFileName = media.fileName;
53
59
  }
54
60
  }
55
61
  outboundLog.info(`Sending message -> ${jid}${options.mediaUrl ? " (media)" : ""}`);
@@ -57,9 +63,10 @@ export async function sendMessageWhatsApp(to, body, options) {
57
63
  await active.sendComposingTo(to);
58
64
  const hasExplicitAccountId = Boolean(options.accountId?.trim());
59
65
  const accountId = hasExplicitAccountId ? resolvedAccountId : undefined;
60
- const sendOptions = options.gifPlayback || accountId
66
+ const sendOptions = options.gifPlayback || accountId || documentFileName
61
67
  ? {
62
68
  ...(options.gifPlayback ? { gifPlayback: true } : {}),
69
+ ...(documentFileName ? { fileName: documentFileName } : {}),
63
70
  accountId,
64
71
  }
65
72
  : undefined;
@@ -1,5 +1,6 @@
1
- import { cancel, confirm, intro, isCancel, multiselect, outro, select, spinner, text, } from "@clack/prompts";
1
+ import { autocompleteMultiselect, cancel, confirm, intro, isCancel, multiselect, outro, select, spinner, text, } from "@clack/prompts";
2
2
  import { createCliProgress } from "../cli/progress.js";
3
+ import { stripAnsi } from "../terminal/ansi.js";
3
4
  import { note as emitNote } from "../terminal/note.js";
4
5
  import { stylePromptHint, stylePromptMessage, stylePromptTitle } from "../terminal/prompt-style.js";
5
6
  import { theme } from "../terminal/theme.js";
@@ -11,6 +12,27 @@ function guardCancel(value) {
11
12
  }
12
13
  return value;
13
14
  }
15
+ function normalizeSearchTokens(search) {
16
+ return search
17
+ .toLowerCase()
18
+ .split(/\s+/)
19
+ .map((token) => token.trim())
20
+ .filter((token) => token.length > 0);
21
+ }
22
+ function buildOptionSearchText(option) {
23
+ const label = stripAnsi(option.label ?? "");
24
+ const hint = stripAnsi(option.hint ?? "");
25
+ const value = String(option.value ?? "");
26
+ return `${label} ${hint} ${value}`.toLowerCase();
27
+ }
28
+ export function tokenizedOptionFilter(search, option) {
29
+ const tokens = normalizeSearchTokens(search);
30
+ if (tokens.length === 0) {
31
+ return true;
32
+ }
33
+ const haystack = buildOptionSearchText(option);
34
+ return tokens.every((token) => haystack.includes(token));
35
+ }
14
36
  export function createClackPrompter() {
15
37
  return {
16
38
  intro: async (title) => {
@@ -30,14 +52,25 @@ export function createClackPrompter() {
30
52
  }),
31
53
  initialValue: params.initialValue,
32
54
  })),
33
- multiselect: async (params) => guardCancel(await multiselect({
34
- message: stylePromptMessage(params.message),
35
- options: params.options.map((opt) => {
55
+ multiselect: async (params) => {
56
+ const options = params.options.map((opt) => {
36
57
  const base = { value: opt.value, label: opt.label };
37
58
  return opt.hint === undefined ? base : { ...base, hint: stylePromptHint(opt.hint) };
38
- }),
39
- initialValues: params.initialValues,
40
- })),
59
+ });
60
+ if (params.searchable) {
61
+ return guardCancel(await autocompleteMultiselect({
62
+ message: stylePromptMessage(params.message),
63
+ options,
64
+ initialValues: params.initialValues,
65
+ filter: tokenizedOptionFilter,
66
+ }));
67
+ }
68
+ return guardCancel(await multiselect({
69
+ message: stylePromptMessage(params.message),
70
+ options,
71
+ initialValues: params.initialValues,
72
+ }));
73
+ },
41
74
  text: async (params) => {
42
75
  const validate = params.validate;
43
76
  return guardCancel(await text({