@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
@@ -1,33 +1,88 @@
1
- import { resolveChunkMode } from "../../auto-reply/chunk.js";
2
- import { resolveEffectiveMessagesConfig } from "../../agents/identity.js";
3
- import { buildCommandTextFromArgs, findCommandByNativeName, listNativeCommandSpecsForConfig, parseCommandArgs, resolveCommandArgMenu, } from "../../auto-reply/commands-registry.js";
4
- import { listSkillCommandsForAgents } from "../../auto-reply/skill-commands.js";
5
- import { dispatchReplyWithDispatcher } from "../../auto-reply/reply/provider-dispatcher.js";
6
- import { finalizeInboundContext } from "../../auto-reply/reply/inbound-context.js";
1
+ import { formatAllowlistMatchMeta } from "../../channels/allowlist-match.js";
2
+ import { resolveCommandAuthorizedFromAuthorizers } from "../../channels/command-gating.js";
7
3
  import { resolveNativeCommandsEnabled, resolveNativeSkillsEnabled } from "../../config/commands.js";
8
- import { resolveMarkdownTableMode } from "../../config/markdown-tables.js";
9
4
  import { danger, logVerbose } from "../../globals.js";
10
5
  import { buildPairingReply } from "../../pairing/pairing-messages.js";
11
6
  import { readChannelAllowFromStore, upsertChannelPairingRequest, } from "../../pairing/pairing-store.js";
12
- import { resolveAgentRoute } from "../../routing/resolve-route.js";
13
- import { resolveConversationLabel } from "../../channels/conversation-label.js";
14
- import { resolveCommandAuthorizedFromAuthorizers } from "../../channels/command-gating.js";
15
- import { formatAllowlistMatchMeta } from "../../channels/allowlist-match.js";
7
+ import { chunkItems } from "../../utils/chunk-items.js";
16
8
  import { normalizeAllowList, normalizeAllowListLower, resolveSlackAllowListMatch, resolveSlackUserAllowed, } from "./allow-list.js";
17
9
  import { resolveSlackChannelConfig } from "./channel-config.js";
18
10
  import { buildSlackSlashCommandMatcher, resolveSlackSlashCommandConfig } from "./commands.js";
11
+ import { normalizeSlackChannelType } from "./context.js";
19
12
  import { isSlackChannelAllowedByPolicy } from "./policy.js";
20
- import { deliverSlackSlashReplies } from "./replies.js";
13
+ import { resolveSlackRoomContextHints } from "./room-context.js";
21
14
  const SLACK_COMMAND_ARG_ACTION_ID = "poolbot_cmdarg";
22
15
  const SLACK_COMMAND_ARG_VALUE_PREFIX = "cmdarg";
23
- function chunkItems(items, size) {
24
- if (size <= 0)
25
- return [items];
26
- const rows = [];
27
- for (let i = 0; i < items.length; i += size) {
28
- rows.push(items.slice(i, i + size));
16
+ const SLACK_COMMAND_ARG_BUTTON_ROW_SIZE = 5;
17
+ const SLACK_COMMAND_ARG_OVERFLOW_MIN = 3;
18
+ const SLACK_COMMAND_ARG_OVERFLOW_MAX = 5;
19
+ const SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX = 100;
20
+ const SLACK_COMMAND_ARG_SELECT_OPTION_VALUE_MAX = 75;
21
+ const SLACK_COMMAND_ARG_EXTERNAL_PREFIX = "poolbot_cmdarg_ext:";
22
+ const SLACK_COMMAND_ARG_EXTERNAL_TTL_MS = 10 * 60 * 1000;
23
+ const SLACK_HEADER_TEXT_MAX = 150;
24
+ const slackExternalArgMenuStore = new Map();
25
+ function truncatePlainText(value, max) {
26
+ const trimmed = value.trim();
27
+ if (trimmed.length <= max) {
28
+ return trimmed;
29
+ }
30
+ if (max <= 1) {
31
+ return trimmed.slice(0, max);
32
+ }
33
+ return `${trimmed.slice(0, max - 1)}…`;
34
+ }
35
+ function escapeSlackMrkdwn(value) {
36
+ return value
37
+ .replaceAll("\\", "\\\\")
38
+ .replaceAll("&", "&amp;")
39
+ .replaceAll("<", "&lt;")
40
+ .replaceAll(">", "&gt;")
41
+ .replace(/([*_`~])/g, "\\$1");
42
+ }
43
+ function buildSlackArgMenuConfirm(params) {
44
+ const command = escapeSlackMrkdwn(params.command);
45
+ const arg = escapeSlackMrkdwn(params.arg);
46
+ return {
47
+ title: { type: "plain_text", text: "Confirm selection" },
48
+ text: {
49
+ type: "mrkdwn",
50
+ text: `Run */${command}* with *${arg}* set to this value?`,
51
+ },
52
+ confirm: { type: "plain_text", text: "Run command" },
53
+ deny: { type: "plain_text", text: "Cancel" },
54
+ };
55
+ }
56
+ function pruneSlackExternalArgMenuStore(now = Date.now()) {
57
+ for (const [token, entry] of slackExternalArgMenuStore.entries()) {
58
+ if (entry.expiresAt <= now) {
59
+ slackExternalArgMenuStore.delete(token);
60
+ }
61
+ }
62
+ }
63
+ function storeSlackExternalArgMenu(params) {
64
+ pruneSlackExternalArgMenuStore();
65
+ const token = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
66
+ slackExternalArgMenuStore.set(token, {
67
+ choices: params.choices,
68
+ userId: params.userId,
69
+ expiresAt: Date.now() + SLACK_COMMAND_ARG_EXTERNAL_TTL_MS,
70
+ });
71
+ return token;
72
+ }
73
+ function readSlackExternalArgMenuToken(raw) {
74
+ if (typeof raw !== "string" || !raw.startsWith(SLACK_COMMAND_ARG_EXTERNAL_PREFIX)) {
75
+ return undefined;
76
+ }
77
+ const token = raw.slice(SLACK_COMMAND_ARG_EXTERNAL_PREFIX.length).trim();
78
+ return token.length > 0 ? token : undefined;
79
+ }
80
+ let commandsRegistry;
81
+ async function getCommandsRegistry() {
82
+ if (!commandsRegistry) {
83
+ commandsRegistry = await import("../../auto-reply/commands-registry.js");
29
84
  }
30
- return rows;
85
+ return commandsRegistry;
31
86
  }
32
87
  function encodeSlackCommandArgValue(parts) {
33
88
  return [
@@ -39,14 +94,17 @@ function encodeSlackCommandArgValue(parts) {
39
94
  ].join("|");
40
95
  }
41
96
  function parseSlackCommandArgValue(raw) {
42
- if (!raw)
97
+ if (!raw) {
43
98
  return null;
99
+ }
44
100
  const parts = raw.split("|");
45
- if (parts.length !== 5 || parts[0] !== SLACK_COMMAND_ARG_VALUE_PREFIX)
101
+ if (parts.length !== 5 || parts[0] !== SLACK_COMMAND_ARG_VALUE_PREFIX) {
46
102
  return null;
103
+ }
47
104
  const [, command, arg, value, userId] = parts;
48
- if (!command || !arg || !value || !userId)
105
+ if (!command || !arg || !value || !userId) {
49
106
  return null;
107
+ }
50
108
  const decode = (text) => {
51
109
  try {
52
110
  return decodeURIComponent(text);
@@ -59,8 +117,9 @@ function parseSlackCommandArgValue(raw) {
59
117
  const decodedArg = decode(arg);
60
118
  const decodedValue = decode(value);
61
119
  const decodedUserId = decode(userId);
62
- if (!decodedCommand || !decodedArg || !decodedValue || !decodedUserId)
120
+ if (!decodedCommand || !decodedArg || !decodedValue || !decodedUserId) {
63
121
  return null;
122
+ }
64
123
  return {
65
124
  command: decodedCommand,
66
125
  arg: decodedArg,
@@ -69,33 +128,112 @@ function parseSlackCommandArgValue(raw) {
69
128
  };
70
129
  }
71
130
  function buildSlackCommandArgMenuBlocks(params) {
72
- const rows = chunkItems(params.choices, 5).map((choices) => ({
73
- type: "actions",
74
- elements: choices.map((choice) => ({
75
- type: "button",
76
- action_id: SLACK_COMMAND_ARG_ACTION_ID,
77
- text: { type: "plain_text", text: choice.label },
78
- value: encodeSlackCommandArgValue({
79
- command: params.command,
80
- arg: params.arg,
81
- value: choice.value,
82
- userId: params.userId,
83
- }),
84
- })),
131
+ const encodedChoices = params.choices.map((choice) => ({
132
+ label: choice.label,
133
+ value: encodeSlackCommandArgValue({
134
+ command: params.command,
135
+ arg: params.arg,
136
+ value: choice.value,
137
+ userId: params.userId,
138
+ }),
85
139
  }));
140
+ const canUseStaticSelect = encodedChoices.every((choice) => choice.value.length <= SLACK_COMMAND_ARG_SELECT_OPTION_VALUE_MAX);
141
+ const canUseOverflow = canUseStaticSelect &&
142
+ encodedChoices.length >= SLACK_COMMAND_ARG_OVERFLOW_MIN &&
143
+ encodedChoices.length <= SLACK_COMMAND_ARG_OVERFLOW_MAX;
144
+ const canUseExternalSelect = params.supportsExternalSelect &&
145
+ canUseStaticSelect &&
146
+ encodedChoices.length > SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX;
147
+ const rows = canUseOverflow
148
+ ? [
149
+ {
150
+ type: "actions",
151
+ elements: [
152
+ {
153
+ type: "overflow",
154
+ action_id: SLACK_COMMAND_ARG_ACTION_ID,
155
+ confirm: buildSlackArgMenuConfirm({ command: params.command, arg: params.arg }),
156
+ options: encodedChoices.map((choice) => ({
157
+ text: { type: "plain_text", text: choice.label.slice(0, 75) },
158
+ value: choice.value,
159
+ })),
160
+ },
161
+ ],
162
+ },
163
+ ]
164
+ : canUseExternalSelect
165
+ ? [
166
+ {
167
+ type: "actions",
168
+ block_id: `${SLACK_COMMAND_ARG_EXTERNAL_PREFIX}${params.createExternalMenuToken(encodedChoices)}`,
169
+ elements: [
170
+ {
171
+ type: "external_select",
172
+ action_id: SLACK_COMMAND_ARG_ACTION_ID,
173
+ confirm: buildSlackArgMenuConfirm({ command: params.command, arg: params.arg }),
174
+ min_query_length: 0,
175
+ placeholder: {
176
+ type: "plain_text",
177
+ text: `Search ${params.arg}`,
178
+ },
179
+ },
180
+ ],
181
+ },
182
+ ]
183
+ : encodedChoices.length <= SLACK_COMMAND_ARG_BUTTON_ROW_SIZE || !canUseStaticSelect
184
+ ? chunkItems(encodedChoices, SLACK_COMMAND_ARG_BUTTON_ROW_SIZE).map((choices) => ({
185
+ type: "actions",
186
+ elements: choices.map((choice) => ({
187
+ type: "button",
188
+ action_id: SLACK_COMMAND_ARG_ACTION_ID,
189
+ text: { type: "plain_text", text: choice.label },
190
+ value: choice.value,
191
+ confirm: buildSlackArgMenuConfirm({ command: params.command, arg: params.arg }),
192
+ })),
193
+ }))
194
+ : chunkItems(encodedChoices, SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX).map((choices, index) => ({
195
+ type: "actions",
196
+ elements: [
197
+ {
198
+ type: "static_select",
199
+ action_id: SLACK_COMMAND_ARG_ACTION_ID,
200
+ confirm: buildSlackArgMenuConfirm({ command: params.command, arg: params.arg }),
201
+ placeholder: {
202
+ type: "plain_text",
203
+ text: index === 0 ? `Choose ${params.arg}` : `Choose ${params.arg} (${index + 1})`,
204
+ },
205
+ options: choices.map((choice) => ({
206
+ text: { type: "plain_text", text: choice.label.slice(0, 75) },
207
+ value: choice.value,
208
+ })),
209
+ },
210
+ ],
211
+ }));
212
+ const headerText = truncatePlainText(`/${params.command}: choose ${params.arg}`, SLACK_HEADER_TEXT_MAX);
213
+ const sectionText = truncatePlainText(params.title, 3000);
214
+ const contextText = truncatePlainText(`Select one option to continue /${params.command} (${params.arg})`, 3000);
86
215
  return [
216
+ {
217
+ type: "header",
218
+ text: { type: "plain_text", text: headerText },
219
+ },
87
220
  {
88
221
  type: "section",
89
- text: { type: "mrkdwn", text: params.title },
222
+ text: { type: "mrkdwn", text: sectionText },
223
+ },
224
+ {
225
+ type: "context",
226
+ elements: [{ type: "mrkdwn", text: contextText }],
90
227
  },
91
228
  ...rows,
92
229
  ];
93
230
  }
94
- export function registerSlackMonitorSlashCommands(params) {
231
+ export async function registerSlackMonitorSlashCommands(params) {
95
232
  const { ctx, account } = params;
96
233
  const cfg = ctx.cfg;
97
234
  const runtime = ctx.runtime;
98
235
  const supportsInteractiveArgMenus = typeof ctx.app.action === "function";
236
+ const supportsExternalArgMenus = typeof ctx.app.options === "function";
99
237
  const slashCommand = resolveSlackSlashCommandConfig(ctx.slashCommand ?? account.config.slashCommand);
100
238
  const handleSlashCommand = async (p) => {
101
239
  const { command, ack, respond, prompt, commandArgs, commandDefinition } = p;
@@ -108,10 +246,12 @@ export function registerSlackMonitorSlashCommands(params) {
108
246
  return;
109
247
  }
110
248
  await ack();
111
- if (ctx.botUserId && command.user_id === ctx.botUserId)
249
+ if (ctx.botUserId && command.user_id === ctx.botUserId) {
112
250
  return;
251
+ }
113
252
  const channelInfo = await ctx.resolveChannelName(command.channel_id);
114
- const channelType = channelInfo?.type ?? (command.channel_name === "directmessage" ? "im" : undefined);
253
+ const rawChannelType = channelInfo?.type ?? (command.channel_name === "directmessage" ? "im" : undefined);
254
+ const channelType = normalizeSlackChannelType(rawChannelType, command.channel_id);
115
255
  const isDirectMessage = channelType === "im";
116
256
  const isGroupDm = channelType === "mpim";
117
257
  const isRoom = channelType === "channel" || channelType === "group";
@@ -130,7 +270,9 @@ export function registerSlackMonitorSlashCommands(params) {
130
270
  const storeAllowFrom = await readChannelAllowFromStore("slack").catch(() => []);
131
271
  const effectiveAllowFrom = normalizeAllowList([...ctx.allowFrom, ...storeAllowFrom]);
132
272
  const effectiveAllowFromLower = normalizeAllowListLower(effectiveAllowFrom);
133
- let commandAuthorized = true;
273
+ // Privileged command surface: compute CommandAuthorized, don't assume true.
274
+ // Keep this aligned with the Slack message path (message-handler/prepare.ts).
275
+ let commandAuthorized = false;
134
276
  let channelConfig = null;
135
277
  if (isDirectMessage) {
136
278
  if (!ctx.dmEnabled || ctx.dmPolicy === "disabled") {
@@ -177,7 +319,6 @@ export function registerSlackMonitorSlashCommands(params) {
177
319
  }
178
320
  return;
179
321
  }
180
- commandAuthorized = true;
181
322
  }
182
323
  }
183
324
  if (isRoom) {
@@ -236,6 +377,13 @@ export function registerSlackMonitorSlashCommands(params) {
236
377
  id: command.user_id,
237
378
  name: senderName,
238
379
  }).allowed;
380
+ // DMs: allow chatting in dmPolicy=open, but keep privileged command gating intact by setting
381
+ // CommandAuthorized based on allowlists/access-groups (downstream decides which commands need it).
382
+ commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
383
+ useAccessGroups: ctx.useAccessGroups,
384
+ authorizers: [{ configured: effectiveAllowFromLower.length > 0, allowed: ownerAllowed }],
385
+ modeWhenAccessGroupsOff: "configured",
386
+ });
239
387
  if (isRoomish) {
240
388
  commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
241
389
  useAccessGroups: ctx.useAccessGroups,
@@ -243,6 +391,7 @@ export function registerSlackMonitorSlashCommands(params) {
243
391
  { configured: effectiveAllowFromLower.length > 0, allowed: ownerAllowed },
244
392
  { configured: channelUsersAllowlistConfigured, allowed: channelUserAllowed },
245
393
  ],
394
+ modeWhenAccessGroupsOff: "configured",
246
395
  });
247
396
  if (ctx.useAccessGroups && !commandAuthorized) {
248
397
  await respond({
@@ -253,7 +402,8 @@ export function registerSlackMonitorSlashCommands(params) {
253
402
  }
254
403
  }
255
404
  if (commandDefinition && supportsInteractiveArgMenus) {
256
- const menu = resolveCommandArgMenu({
405
+ const reg = await getCommandsRegistry();
406
+ const menu = reg.resolveCommandArgMenu({
257
407
  command: commandDefinition,
258
408
  args: commandArgs,
259
409
  cfg,
@@ -267,6 +417,8 @@ export function registerSlackMonitorSlashCommands(params) {
267
417
  arg: menu.arg.name,
268
418
  choices: menu.choices,
269
419
  userId: command.user_id,
420
+ supportsExternalSelect: supportsExternalArgMenus,
421
+ createExternalMenuToken: (choices) => storeSlackExternalArgMenu({ choices, userId: command.user_id }),
270
422
  });
271
423
  await respond({
272
424
  text: title,
@@ -278,28 +430,33 @@ export function registerSlackMonitorSlashCommands(params) {
278
430
  }
279
431
  const channelName = channelInfo?.name;
280
432
  const roomLabel = channelName ? `#${channelName}` : `#${command.channel_id}`;
433
+ const [{ resolveAgentRoute }, { finalizeInboundContext }, { dispatchReplyWithDispatcher }] = await Promise.all([
434
+ import("../../routing/resolve-route.js"),
435
+ import("../../auto-reply/reply/inbound-context.js"),
436
+ import("../../auto-reply/reply/provider-dispatcher.js"),
437
+ ]);
438
+ const [{ resolveConversationLabel }, { createReplyPrefixOptions }] = await Promise.all([
439
+ import("../../channels/conversation-label.js"),
440
+ import("../../channels/reply-prefix.js"),
441
+ ]);
281
442
  const route = resolveAgentRoute({
282
443
  cfg,
283
444
  channel: "slack",
284
445
  accountId: account.accountId,
285
446
  teamId: ctx.teamId || undefined,
286
447
  peer: {
287
- kind: isDirectMessage ? "dm" : isRoom ? "channel" : "group",
448
+ kind: isDirectMessage ? "direct" : isRoom ? "channel" : "group",
288
449
  id: isDirectMessage ? command.user_id : command.channel_id,
289
450
  },
290
451
  });
291
- const channelDescription = [channelInfo?.topic, channelInfo?.purpose]
292
- .map((entry) => entry?.trim())
293
- .filter((entry) => Boolean(entry))
294
- .filter((entry, index, list) => list.indexOf(entry) === index)
295
- .join("\n");
296
- const systemPromptParts = [
297
- channelDescription ? `Channel description: ${channelDescription}` : null,
298
- channelConfig?.systemPrompt?.trim() || null,
299
- ].filter((entry) => Boolean(entry));
300
- const groupSystemPrompt = systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : undefined;
452
+ const { untrustedChannelMetadata, groupSystemPrompt } = resolveSlackRoomContextHints({
453
+ isRoomish,
454
+ channelInfo,
455
+ channelConfig,
456
+ });
301
457
  const ctxPayload = finalizeInboundContext({
302
458
  Body: prompt,
459
+ BodyForAgent: prompt,
303
460
  RawBody: prompt,
304
461
  CommandBody: prompt,
305
462
  CommandArgs: commandArgs,
@@ -322,6 +479,7 @@ export function registerSlackMonitorSlashCommands(params) {
322
479
  }) ?? (isDirectMessage ? senderName : roomLabel),
323
480
  GroupSubject: isRoomish ? roomLabel : undefined,
324
481
  GroupSystemPrompt: isRoomish ? groupSystemPrompt : undefined,
482
+ UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : undefined,
325
483
  SenderName: senderName,
326
484
  SenderId: command.user_id,
327
485
  Provider: "slack",
@@ -337,34 +495,20 @@ export function registerSlackMonitorSlashCommands(params) {
337
495
  OriginatingChannel: "slack",
338
496
  OriginatingTo: `user:${command.user_id}`,
339
497
  });
340
- const { counts } = await dispatchReplyWithDispatcher({
341
- ctx: ctxPayload,
498
+ const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
342
499
  cfg,
343
- dispatcherOptions: {
344
- responsePrefix: resolveEffectiveMessagesConfig(cfg, route.agentId).responsePrefix,
345
- deliver: async (payload) => {
346
- await deliverSlackSlashReplies({
347
- replies: [payload],
348
- respond,
349
- ephemeral: slashCommand.ephemeral,
350
- textLimit: ctx.textLimit,
351
- chunkMode: resolveChunkMode(cfg, "slack", route.accountId),
352
- tableMode: resolveMarkdownTableMode({
353
- cfg,
354
- channel: "slack",
355
- accountId: route.accountId,
356
- }),
357
- });
358
- },
359
- onError: (err, info) => {
360
- runtime.error?.(danger(`slack slash ${info.kind} reply failed: ${String(err)}`));
361
- },
362
- },
363
- replyOptions: { skillFilter: channelConfig?.skills },
500
+ agentId: route.agentId,
501
+ channel: "slack",
502
+ accountId: route.accountId,
364
503
  });
365
- if (counts.final + counts.tool + counts.block === 0) {
504
+ const deliverSlashPayloads = async (replies) => {
505
+ const [{ deliverSlackSlashReplies }, { resolveChunkMode }, { resolveMarkdownTableMode }] = await Promise.all([
506
+ import("./replies.js"),
507
+ import("../../auto-reply/chunk.js"),
508
+ import("../../config/markdown-tables.js"),
509
+ ]);
366
510
  await deliverSlackSlashReplies({
367
- replies: [],
511
+ replies,
368
512
  respond,
369
513
  ephemeral: slashCommand.ephemeral,
370
514
  textLimit: ctx.textLimit,
@@ -375,6 +519,24 @@ export function registerSlackMonitorSlashCommands(params) {
375
519
  accountId: route.accountId,
376
520
  }),
377
521
  });
522
+ };
523
+ const { counts } = await dispatchReplyWithDispatcher({
524
+ ctx: ctxPayload,
525
+ cfg,
526
+ dispatcherOptions: {
527
+ ...prefixOptions,
528
+ deliver: async (payload) => deliverSlashPayloads([payload]),
529
+ onError: (err, info) => {
530
+ runtime.error?.(danger(`slack slash ${info.kind} reply failed: ${String(err)}`));
531
+ },
532
+ },
533
+ replyOptions: {
534
+ skillFilter: channelConfig?.skills,
535
+ onModelSelected,
536
+ },
537
+ });
538
+ if (counts.final + counts.tool + counts.block === 0) {
539
+ await deliverSlashPayloads([]);
378
540
  }
379
541
  }
380
542
  catch (err) {
@@ -395,22 +557,31 @@ export function registerSlackMonitorSlashCommands(params) {
395
557
  providerSetting: account.config.commands?.nativeSkills,
396
558
  globalSetting: cfg.commands?.nativeSkills,
397
559
  });
398
- const skillCommands = nativeEnabled && nativeSkillsEnabled ? listSkillCommandsForAgents({ cfg }) : [];
399
- const nativeCommands = nativeEnabled
400
- ? listNativeCommandSpecsForConfig(cfg, { skillCommands, provider: "slack" })
401
- : [];
560
+ let reg;
561
+ let nativeCommands = [];
562
+ if (nativeEnabled) {
563
+ reg = await getCommandsRegistry();
564
+ const skillCommands = nativeSkillsEnabled
565
+ ? (await import("../../auto-reply/skill-commands.js")).listSkillCommandsForAgents({ cfg })
566
+ : [];
567
+ nativeCommands = reg.listNativeCommandSpecsForConfig(cfg, { skillCommands, provider: "slack" });
568
+ }
402
569
  if (nativeCommands.length > 0) {
570
+ const registry = reg;
571
+ if (!registry) {
572
+ throw new Error("Missing commands registry for native Slack commands.");
573
+ }
403
574
  for (const command of nativeCommands) {
404
575
  ctx.app.command(`/${command.name}`, async ({ command: cmd, ack, respond }) => {
405
- const commandDefinition = findCommandByNativeName(command.name, "slack");
576
+ const commandDefinition = registry.findCommandByNativeName(command.name, "slack");
406
577
  const rawText = cmd.text?.trim() ?? "";
407
578
  const commandArgs = commandDefinition
408
- ? parseCommandArgs(commandDefinition, rawText)
579
+ ? registry.parseCommandArgs(commandDefinition, rawText)
409
580
  : rawText
410
581
  ? { raw: rawText }
411
582
  : undefined;
412
583
  const prompt = commandDefinition
413
- ? buildCommandTextFromArgs(commandDefinition, commandArgs)
584
+ ? registry.buildCommandTextFromArgs(commandDefinition, commandArgs)
414
585
  : rawText
415
586
  ? `/${command.name} ${rawText}`
416
587
  : `/${command.name}`;
@@ -438,67 +609,109 @@ export function registerSlackMonitorSlashCommands(params) {
438
609
  else {
439
610
  logVerbose("slack: slash commands disabled");
440
611
  }
441
- if (nativeCommands.length === 0 || !supportsInteractiveArgMenus)
612
+ if (nativeCommands.length === 0 || !supportsInteractiveArgMenus) {
442
613
  return;
443
- ctx.app.action(SLACK_COMMAND_ARG_ACTION_ID, async (args) => {
444
- const { ack, body, respond } = args;
445
- const action = args.action;
446
- await ack();
447
- const respondFn = respond ??
448
- (async (payload) => {
449
- if (!body.channel?.id || !body.user?.id)
450
- return;
451
- await ctx.app.client.chat.postEphemeral({
452
- token: ctx.botToken,
453
- channel: body.channel.id,
454
- user: body.user.id,
455
- text: payload.text,
456
- blocks: payload.blocks,
457
- });
458
- });
459
- const parsed = parseSlackCommandArgValue(action?.value);
460
- if (!parsed) {
461
- await respondFn({
462
- text: "Sorry, that button is no longer valid.",
463
- response_type: "ephemeral",
464
- });
614
+ }
615
+ const registerArgOptions = () => {
616
+ const optionsHandler = ctx.app.options;
617
+ if (typeof optionsHandler !== "function") {
465
618
  return;
466
619
  }
467
- if (body.user?.id && parsed.userId !== body.user.id) {
468
- await respondFn({
469
- text: "That menu is for another user.",
470
- response_type: "ephemeral",
620
+ optionsHandler(SLACK_COMMAND_ARG_ACTION_ID, async ({ ack, body }) => {
621
+ const typedBody = body;
622
+ pruneSlackExternalArgMenuStore();
623
+ const blockId = typedBody.actions?.[0]?.block_id ?? typedBody.block_id;
624
+ const token = readSlackExternalArgMenuToken(blockId);
625
+ if (!token) {
626
+ await ack({ options: [] });
627
+ return;
628
+ }
629
+ const entry = slackExternalArgMenuStore.get(token);
630
+ if (!entry) {
631
+ await ack({ options: [] });
632
+ return;
633
+ }
634
+ if (typedBody.user?.id && typedBody.user.id !== entry.userId) {
635
+ await ack({ options: [] });
636
+ return;
637
+ }
638
+ const query = typedBody.value?.trim().toLowerCase() ?? "";
639
+ const options = entry.choices
640
+ .filter((choice) => !query || choice.label.toLowerCase().includes(query))
641
+ .slice(0, SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX)
642
+ .map((choice) => ({
643
+ text: { type: "plain_text", text: choice.label.slice(0, 75) },
644
+ value: choice.value,
645
+ }));
646
+ await ack({ options });
647
+ });
648
+ };
649
+ registerArgOptions();
650
+ const registerArgAction = (actionId) => {
651
+ ctx.app.action(actionId, async (args) => {
652
+ const { ack, body, respond } = args;
653
+ const action = args.action;
654
+ await ack();
655
+ const respondFn = respond ??
656
+ (async (payload) => {
657
+ if (!body.channel?.id || !body.user?.id) {
658
+ return;
659
+ }
660
+ await ctx.app.client.chat.postEphemeral({
661
+ token: ctx.botToken,
662
+ channel: body.channel.id,
663
+ user: body.user.id,
664
+ text: payload.text,
665
+ blocks: payload.blocks,
666
+ });
667
+ });
668
+ const actionValue = action?.value ?? action?.selected_option?.value;
669
+ const parsed = parseSlackCommandArgValue(actionValue);
670
+ if (!parsed) {
671
+ await respondFn({
672
+ text: "Sorry, that button is no longer valid.",
673
+ response_type: "ephemeral",
674
+ });
675
+ return;
676
+ }
677
+ if (body.user?.id && parsed.userId !== body.user.id) {
678
+ await respondFn({
679
+ text: "That menu is for another user.",
680
+ response_type: "ephemeral",
681
+ });
682
+ return;
683
+ }
684
+ const reg = await getCommandsRegistry();
685
+ const commandDefinition = reg.findCommandByNativeName(parsed.command, "slack");
686
+ const commandArgs = {
687
+ values: { [parsed.arg]: parsed.value },
688
+ };
689
+ const prompt = commandDefinition
690
+ ? reg.buildCommandTextFromArgs(commandDefinition, commandArgs)
691
+ : `/${parsed.command} ${parsed.value}`;
692
+ const user = body.user;
693
+ const userName = user && "name" in user && user.name
694
+ ? user.name
695
+ : user && "username" in user && user.username
696
+ ? user.username
697
+ : (user?.id ?? "");
698
+ const triggerId = "trigger_id" in body ? body.trigger_id : undefined;
699
+ const commandPayload = {
700
+ user_id: user?.id ?? "",
701
+ user_name: userName,
702
+ channel_id: body.channel?.id ?? "",
703
+ channel_name: body.channel?.name ?? body.channel?.id ?? "",
704
+ trigger_id: triggerId ?? String(Date.now()),
705
+ };
706
+ await handleSlashCommand({
707
+ command: commandPayload,
708
+ ack: async () => { },
709
+ respond: respondFn,
710
+ prompt,
711
+ commandArgs,
712
+ commandDefinition: commandDefinition ?? undefined,
471
713
  });
472
- return;
473
- }
474
- const commandDefinition = findCommandByNativeName(parsed.command, "slack");
475
- const commandArgs = {
476
- values: { [parsed.arg]: parsed.value },
477
- };
478
- const prompt = commandDefinition
479
- ? buildCommandTextFromArgs(commandDefinition, commandArgs)
480
- : `/${parsed.command} ${parsed.value}`;
481
- const user = body.user;
482
- const userName = user && "name" in user && user.name
483
- ? user.name
484
- : user && "username" in user && user.username
485
- ? user.username
486
- : (user?.id ?? "");
487
- const triggerId = "trigger_id" in body ? body.trigger_id : undefined;
488
- const commandPayload = {
489
- user_id: user?.id ?? "",
490
- user_name: userName,
491
- channel_id: body.channel?.id ?? "",
492
- channel_name: body.channel?.name ?? body.channel?.id ?? "",
493
- trigger_id: triggerId ?? String(Date.now()),
494
- };
495
- await handleSlashCommand({
496
- command: commandPayload,
497
- ack: async () => { },
498
- respond: respondFn,
499
- prompt,
500
- commandArgs,
501
- commandDefinition: commandDefinition ?? undefined,
502
714
  });
503
- });
715
+ };
716
+ registerArgAction(SLACK_COMMAND_ARG_ACTION_ID);
504
717
  }