@poolzin/pool-bot 2026.2.21 → 2026.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (369) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/agents/api-key-rotation.js +47 -0
  3. package/dist/agents/apply-patch-update.js +19 -9
  4. package/dist/agents/apply-patch.js +72 -47
  5. package/dist/agents/bash-tools.exec.js +141 -559
  6. package/dist/agents/cli-backends.js +49 -6
  7. package/dist/agents/cli-runner/helpers.js +69 -152
  8. package/dist/agents/cli-runner.js +70 -19
  9. package/dist/agents/identity.js +20 -1
  10. package/dist/agents/image-sanitization.js +9 -0
  11. package/dist/agents/live-auth-keys.js +123 -26
  12. package/dist/agents/live-model-filter.js +13 -4
  13. package/dist/agents/model-catalog.js +40 -9
  14. package/dist/agents/model-forward-compat.js +60 -23
  15. package/dist/agents/model-selection.js +134 -41
  16. package/dist/agents/pi-auth-json.js +2 -2
  17. package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
  18. package/dist/agents/pi-embedded-helpers/errors.js +140 -15
  19. package/dist/agents/pi-embedded-helpers/images.js +22 -12
  20. package/dist/agents/pi-embedded-helpers.js +2 -2
  21. package/dist/agents/pi-embedded-runner/abort.js +10 -3
  22. package/dist/agents/pi-embedded-runner/compact.js +230 -32
  23. package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
  24. package/dist/agents/pi-embedded-runner/google.js +109 -19
  25. package/dist/agents/pi-embedded-runner/history.js +35 -17
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
  27. package/dist/agents/pi-embedded-runner/run/images.js +81 -55
  28. package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
  29. package/dist/agents/pi-embedded-runner/run.js +193 -25
  30. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
  31. package/dist/agents/pi-embedded-runner/runs.js +17 -8
  32. package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
  33. package/dist/agents/pi-embedded-runner.js +1 -1
  34. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
  35. package/dist/agents/pi-embedded-subscribe.js +37 -0
  36. package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
  37. package/dist/agents/pi-model-discovery.js +9 -2
  38. package/dist/agents/pi-tool-definition-adapter.js +60 -8
  39. package/dist/agents/pi-tools.before-tool-call.js +1 -1
  40. package/dist/agents/pi-tools.js +113 -94
  41. package/dist/agents/pi-tools.read.js +337 -38
  42. package/dist/agents/poolbot-tools.js +14 -5
  43. package/dist/agents/sandbox/docker.js +10 -5
  44. package/dist/agents/sandbox/registry.js +96 -46
  45. package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
  46. package/dist/agents/sandbox-paths.js +43 -10
  47. package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
  48. package/dist/agents/session-tool-result-guard.js +39 -39
  49. package/dist/agents/session-transcript-repair.js +36 -33
  50. package/dist/agents/session-write-lock.js +62 -44
  51. package/dist/agents/skills/frontmatter.js +49 -88
  52. package/dist/agents/skills/workspace.js +335 -28
  53. package/dist/agents/subagent-announce.js +508 -174
  54. package/dist/agents/subagent-registry.js +45 -4
  55. package/dist/agents/subagent-spawn.js +16 -33
  56. package/dist/agents/system-prompt-report.js +27 -10
  57. package/dist/agents/system-prompt.js +26 -32
  58. package/dist/agents/tool-call-id.js +69 -17
  59. package/dist/agents/tool-display-common.js +1 -1
  60. package/dist/agents/tool-images.js +64 -31
  61. package/dist/agents/tools/canvas-tool.js +17 -11
  62. package/dist/agents/tools/common.js +37 -19
  63. package/dist/agents/tools/cron-tool.js +40 -38
  64. package/dist/agents/tools/gateway.js +70 -2
  65. package/dist/agents/tools/message-tool.js +181 -40
  66. package/dist/agents/tools/nodes-tool.js +128 -36
  67. package/dist/agents/tools/nodes-utils.js +12 -38
  68. package/dist/agents/tools/session-status-tool.js +24 -71
  69. package/dist/agents/tools/sessions-helpers.js +38 -210
  70. package/dist/agents/tools/sessions-spawn-tool.js +28 -198
  71. package/dist/agents/tools/telegram-actions.js +58 -7
  72. package/dist/agents/tools/web-fetch-utils.js +112 -7
  73. package/dist/agents/tools/web-fetch.js +279 -175
  74. package/dist/agents/tools/web-shared.js +71 -8
  75. package/dist/agents/usage.js +25 -16
  76. package/dist/auto-reply/commands-registry.data.js +85 -11
  77. package/dist/auto-reply/dispatch.js +40 -21
  78. package/dist/auto-reply/reply/abort.js +102 -33
  79. package/dist/auto-reply/reply/commands-core.js +82 -33
  80. package/dist/auto-reply/reply/commands-export-session.js +1 -1
  81. package/dist/auto-reply/reply/commands-info.js +41 -12
  82. package/dist/auto-reply/reply/commands-subagents.js +352 -100
  83. package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
  84. package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
  85. package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
  86. package/dist/auto-reply/reply/inbound-meta.js +12 -1
  87. package/dist/auto-reply/reply/mentions.js +18 -11
  88. package/dist/auto-reply/reply/normalize-reply.js +17 -8
  89. package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
  90. package/dist/auto-reply/reply/session.js +102 -21
  91. package/dist/auto-reply/reply/streaming-directives.js +16 -5
  92. package/dist/auto-reply/status.js +73 -50
  93. package/dist/browser/extension-relay.js +3 -3
  94. package/dist/browser/http-auth.js +1 -1
  95. package/dist/browser/paths.js +2 -2
  96. package/dist/build-info.json +3 -3
  97. package/dist/channels/allowlist-match.js +20 -0
  98. package/dist/channels/allowlists/resolve-utils.js +65 -2
  99. package/dist/channels/chat-type.js +8 -4
  100. package/dist/channels/dock.js +127 -35
  101. package/dist/channels/draft-stream-loop.js +6 -2
  102. package/dist/channels/plugins/actions/telegram.js +42 -18
  103. package/dist/channels/plugins/allowlist-match.js +1 -1
  104. package/dist/channels/plugins/group-mentions.js +51 -41
  105. package/dist/channels/plugins/message-action-names.js +2 -0
  106. package/dist/channels/plugins/message-actions.js +24 -5
  107. package/dist/channels/plugins/normalize/discord.js +26 -4
  108. package/dist/channels/plugins/normalize/signal.js +35 -22
  109. package/dist/channels/plugins/onboarding/helpers.js +8 -26
  110. package/dist/channels/plugins/outbound/imessage.js +15 -14
  111. package/dist/channels/registry.js +20 -7
  112. package/dist/cli/acp-cli.js +7 -5
  113. package/dist/cli/browser-cli-extension.js +25 -12
  114. package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
  115. package/dist/cli/browser-cli-state.js +101 -145
  116. package/dist/cli/command-options.js +28 -0
  117. package/dist/cli/completion-cli.js +6 -6
  118. package/dist/cli/cron-cli/register.cron-add.js +25 -1
  119. package/dist/cli/cron-cli/register.cron-edit.js +44 -0
  120. package/dist/cli/cron-cli/shared.js +7 -1
  121. package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
  122. package/dist/cli/daemon-cli/lifecycle.js +23 -247
  123. package/dist/cli/daemon-cli/register-service-commands.js +25 -4
  124. package/dist/cli/daemon-cli.js +1 -0
  125. package/dist/cli/devices-cli.js +33 -20
  126. package/dist/cli/gateway-cli/register.js +37 -105
  127. package/dist/cli/gateway-cli/run.js +49 -11
  128. package/dist/cli/nodes-camera.js +59 -4
  129. package/dist/cli/nodes-cli/register.camera.js +27 -24
  130. package/dist/cli/nodes-cli/rpc.js +21 -38
  131. package/dist/cli/qr-cli.js +2 -2
  132. package/dist/cli/skills-cli.format.js +2 -2
  133. package/dist/cli/update-cli/progress.js +2 -2
  134. package/dist/cli/update-cli/restart-helper.js +28 -7
  135. package/dist/cli/update-cli/shared.js +7 -7
  136. package/dist/cli/update-cli/status.js +1 -1
  137. package/dist/cli/update-cli/update-command.js +14 -8
  138. package/dist/cli/update-cli/wizard.js +2 -2
  139. package/dist/cli/update-cli.js +21 -1027
  140. package/dist/commands/auth-choice.apply.anthropic.js +10 -2
  141. package/dist/commands/channels/add-mutators.js +3 -35
  142. package/dist/commands/channels/add.js +39 -51
  143. package/dist/commands/config-validation.js +1 -1
  144. package/dist/commands/configure.gateway-auth.js +52 -15
  145. package/dist/commands/configure.gateway.js +84 -40
  146. package/dist/commands/doctor-completion.js +3 -3
  147. package/dist/commands/doctor-config-flow.js +536 -16
  148. package/dist/commands/doctor-gateway-services.js +103 -79
  149. package/dist/commands/doctor-memory-search.js +9 -9
  150. package/dist/commands/doctor-platform-notes.js +57 -30
  151. package/dist/commands/doctor-prompter.js +26 -15
  152. package/dist/commands/doctor-session-locks.js +1 -1
  153. package/dist/commands/doctor.js +21 -9
  154. package/dist/commands/model-picker.js +120 -95
  155. package/dist/commands/models/set.js +2 -21
  156. package/dist/commands/models/shared.js +65 -37
  157. package/dist/commands/onboard-helpers.js +81 -39
  158. package/dist/commands/openai-codex-oauth.js +1 -1
  159. package/dist/commands/sessions.js +52 -53
  160. package/dist/commands/status.summary.js +52 -34
  161. package/dist/commands/test-wizard-helpers.js +2 -2
  162. package/dist/config/defaults.js +79 -42
  163. package/dist/config/group-policy.js +50 -18
  164. package/dist/config/includes.js +37 -10
  165. package/dist/config/schema.help.js +5 -4
  166. package/dist/config/schema.hints.js +2 -2
  167. package/dist/config/schema.labels.js +1 -0
  168. package/dist/config/sessions/group.js +12 -11
  169. package/dist/config/sessions/paths.js +137 -11
  170. package/dist/config/sessions/store.js +185 -65
  171. package/dist/config/sessions/types.js +15 -1
  172. package/dist/config/sessions.js +1 -0
  173. package/dist/config/telegram-custom-commands.js +3 -2
  174. package/dist/config/types.js +2 -0
  175. package/dist/config/zod-schema.agent-defaults.js +6 -27
  176. package/dist/config/zod-schema.agent-runtime.js +171 -79
  177. package/dist/config/zod-schema.providers-core.js +138 -65
  178. package/dist/config/zod-schema.session.js +49 -22
  179. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  180. package/dist/cron/isolated-agent/run.js +224 -57
  181. package/dist/cron/normalize.js +48 -45
  182. package/dist/cron/run-log.js +14 -0
  183. package/dist/cron/service/jobs.js +190 -28
  184. package/dist/cron/service/normalize.js +29 -11
  185. package/dist/cron/service/store.js +30 -44
  186. package/dist/cron/service/timer.js +182 -96
  187. package/dist/cron/service.js +3 -0
  188. package/dist/cron/stagger.js +37 -0
  189. package/dist/daemon/inspect.js +132 -92
  190. package/dist/daemon/runtime-paths.js +25 -4
  191. package/dist/daemon/service-audit.js +47 -16
  192. package/dist/discord/accounts.js +23 -20
  193. package/dist/discord/monitor/agent-components.js +1115 -219
  194. package/dist/discord/monitor/allow-list.js +114 -34
  195. package/dist/discord/monitor/listeners.js +204 -97
  196. package/dist/discord/monitor/message-handler.js +21 -10
  197. package/dist/discord/monitor/message-handler.preflight.js +195 -101
  198. package/dist/discord/monitor/message-handler.process.js +384 -123
  199. package/dist/discord/monitor/message-utils.js +86 -23
  200. package/dist/discord/monitor/native-command.js +77 -57
  201. package/dist/discord/monitor/provider.js +122 -117
  202. package/dist/discord/monitor/reply-context.js +20 -16
  203. package/dist/discord/monitor/reply-delivery.js +40 -8
  204. package/dist/discord/monitor/rest-fetch.js +22 -0
  205. package/dist/discord/monitor/threading.js +117 -24
  206. package/dist/discord/send.js +2 -1
  207. package/dist/discord/send.outbound.js +124 -11
  208. package/dist/discord/send.shared.js +112 -72
  209. package/dist/discord/voice-message.js +3 -3
  210. package/dist/gateway/auth.js +119 -44
  211. package/dist/gateway/call.js +76 -34
  212. package/dist/gateway/channel-health-monitor.js +57 -50
  213. package/dist/gateway/client.js +63 -29
  214. package/dist/gateway/control-ui-contract.js +1 -1
  215. package/dist/gateway/gateway-config-prompts.shared.js +2 -2
  216. package/dist/gateway/net.js +109 -1
  217. package/dist/gateway/protocol/index.js +5 -8
  218. package/dist/gateway/protocol/schema/agent.js +19 -1
  219. package/dist/gateway/protocol/schema/channels.js +21 -0
  220. package/dist/gateway/protocol/schema/cron.js +43 -30
  221. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
  222. package/dist/gateway/protocol/schema/sessions.js +5 -1
  223. package/dist/gateway/protocol/schema.js +0 -1
  224. package/dist/gateway/server/presence-events.js +12 -0
  225. package/dist/gateway/server/ws-connection/message-handler.js +203 -212
  226. package/dist/gateway/server/ws-connection.js +58 -21
  227. package/dist/gateway/server-broadcast.js +18 -13
  228. package/dist/gateway/server-cron.js +177 -10
  229. package/dist/gateway/server-methods/agent-job.js +131 -38
  230. package/dist/gateway/server-methods/send.js +60 -14
  231. package/dist/gateway/server-methods/sessions.js +160 -96
  232. package/dist/gateway/server-methods/system.js +5 -7
  233. package/dist/gateway/server-methods-list.js +8 -0
  234. package/dist/gateway/server-methods.js +24 -8
  235. package/dist/gateway/server-node-events.js +278 -68
  236. package/dist/gateway/session-utils.fs.js +316 -75
  237. package/dist/gateway/session-utils.js +224 -70
  238. package/dist/gateway/sessions-patch.js +63 -20
  239. package/dist/gateway/test-temp-config.js +1 -1
  240. package/dist/gateway/tools-invoke-http.js +118 -70
  241. package/dist/gateway/ws-log.js +135 -107
  242. package/dist/hooks/frontmatter.js +36 -82
  243. package/dist/hooks/install.js +149 -139
  244. package/dist/hooks/internal-hooks.js +29 -4
  245. package/dist/hooks/plugin-hooks.js +2 -1
  246. package/dist/imessage/monitor/deliver.js +10 -4
  247. package/dist/imessage/monitor/monitor-provider.js +138 -375
  248. package/dist/imessage/monitor/runtime.js +4 -8
  249. package/dist/imessage/send.js +65 -19
  250. package/dist/infra/exec-approvals-allowlist.js +7 -0
  251. package/dist/infra/exec-approvals.js +35 -920
  252. package/dist/infra/exec-safe-bin-trust.js +64 -0
  253. package/dist/infra/heartbeat-runner.js +207 -134
  254. package/dist/infra/heartbeat-wake.js +183 -22
  255. package/dist/infra/install-source-utils.js +47 -0
  256. package/dist/infra/net/ssrf.js +170 -36
  257. package/dist/infra/outbound/deliver.js +224 -58
  258. package/dist/infra/outbound/message-action-spec.js +12 -5
  259. package/dist/infra/outbound/outbound-session.js +27 -25
  260. package/dist/infra/poolbot-root.js +32 -22
  261. package/dist/infra/ports.js +14 -11
  262. package/dist/infra/skills-remote.js +48 -37
  263. package/dist/infra/system-events.js +25 -11
  264. package/dist/infra/system-presence.js +26 -33
  265. package/dist/infra/tmp-poolbot-dir.js +81 -2
  266. package/dist/infra/wsl.js +37 -1
  267. package/dist/line/bot-message-context.js +163 -191
  268. package/dist/logging/subsystem.js +59 -22
  269. package/dist/markdown/ir.js +124 -50
  270. package/dist/media/store.js +1 -1
  271. package/dist/media-understanding/runner.entries.js +42 -25
  272. package/dist/media-understanding/runner.js +53 -488
  273. package/dist/memory/embeddings-gemini.js +53 -38
  274. package/dist/memory/manager-embedding-ops.js +48 -69
  275. package/dist/pairing/pairing-store.js +178 -119
  276. package/dist/plugin-sdk/index.js +34 -6
  277. package/dist/plugins/hooks.js +135 -14
  278. package/dist/plugins/install.js +190 -152
  279. package/dist/polls.js +11 -0
  280. package/dist/routing/resolve-route.js +190 -56
  281. package/dist/routing/session-key.js +38 -22
  282. package/dist/runtime.js +35 -9
  283. package/dist/security/audit-channel.js +1 -1
  284. package/dist/sessions/session-key-utils.js +29 -11
  285. package/dist/shared/frontmatter.js +5 -5
  286. package/dist/shared/node-list-types.js +1 -0
  287. package/dist/shared/string-normalization.js +15 -0
  288. package/dist/signal/monitor/event-handler.js +68 -36
  289. package/dist/signal/send.js +29 -37
  290. package/dist/slack/monitor/allow-list.js +10 -11
  291. package/dist/slack/monitor/commands.js +14 -3
  292. package/dist/slack/monitor/events/interactions.js +4 -4
  293. package/dist/slack/monitor/media.js +224 -16
  294. package/dist/slack/monitor/message-handler/dispatch.js +247 -13
  295. package/dist/slack/monitor/message-handler/prepare.js +128 -45
  296. package/dist/slack/monitor/slash.js +357 -144
  297. package/dist/slack/streaming.js +77 -0
  298. package/dist/telegram/accounts.js +40 -13
  299. package/dist/telegram/allowed-updates.js +3 -0
  300. package/dist/telegram/bot/delivery.js +129 -66
  301. package/dist/telegram/bot/helpers.js +136 -122
  302. package/dist/telegram/bot-handlers.js +600 -339
  303. package/dist/telegram/bot-message-context.js +115 -73
  304. package/dist/telegram/bot-message-dispatch.js +235 -104
  305. package/dist/telegram/bot-native-command-menu.js +3 -1
  306. package/dist/telegram/bot-native-commands.js +213 -193
  307. package/dist/telegram/bot.js +24 -132
  308. package/dist/telegram/draft-stream.js +84 -75
  309. package/dist/telegram/format.js +150 -6
  310. package/dist/telegram/send.js +415 -255
  311. package/dist/telegram/targets.js +21 -2
  312. package/dist/telegram/update-offset-store.js +19 -3
  313. package/dist/terminal/restore.js +5 -2
  314. package/dist/test-utils/fetch-mock.js +5 -0
  315. package/dist/version.js +18 -5
  316. package/dist/web/auto-reply/monitor/broadcast.js +7 -3
  317. package/dist/web/auto-reply/monitor/on-message.js +6 -3
  318. package/dist/web/inbound/media.js +34 -8
  319. package/dist/web/inbound/monitor.js +34 -17
  320. package/dist/web/inbound/send-api.js +18 -17
  321. package/dist/web/outbound.js +12 -5
  322. package/dist/wizard/clack-prompter.js +40 -7
  323. package/extensions/bluebubbles/package.json +1 -1
  324. package/extensions/copilot-proxy/package.json +1 -1
  325. package/extensions/diagnostics-otel/package.json +1 -1
  326. package/extensions/discord/package.json +1 -1
  327. package/extensions/feishu/package.json +1 -1
  328. package/extensions/google-antigravity-auth/package.json +1 -1
  329. package/extensions/google-gemini-cli-auth/package.json +1 -1
  330. package/extensions/googlechat/package.json +1 -1
  331. package/extensions/imessage/package.json +1 -1
  332. package/extensions/irc/package.json +1 -1
  333. package/extensions/line/package.json +1 -1
  334. package/extensions/llm-task/package.json +1 -1
  335. package/extensions/lobster/package.json +1 -1
  336. package/extensions/matrix/CHANGELOG.md +5 -0
  337. package/extensions/matrix/package.json +1 -1
  338. package/extensions/mattermost/package.json +1 -1
  339. package/extensions/memory-core/package.json +1 -1
  340. package/extensions/memory-lancedb/package.json +1 -1
  341. package/extensions/minimax-portal-auth/package.json +1 -1
  342. package/extensions/msteams/CHANGELOG.md +5 -0
  343. package/extensions/msteams/package.json +1 -1
  344. package/extensions/nextcloud-talk/package.json +1 -1
  345. package/extensions/nostr/CHANGELOG.md +5 -0
  346. package/extensions/nostr/package.json +1 -1
  347. package/extensions/open-prose/package.json +1 -1
  348. package/extensions/openai-codex-auth/package.json +1 -1
  349. package/extensions/signal/package.json +1 -1
  350. package/extensions/slack/package.json +1 -1
  351. package/extensions/telegram/package.json +1 -1
  352. package/extensions/tlon/package.json +1 -1
  353. package/extensions/twitch/CHANGELOG.md +5 -0
  354. package/extensions/twitch/package.json +1 -1
  355. package/extensions/voice-call/CHANGELOG.md +5 -0
  356. package/extensions/voice-call/package.json +1 -1
  357. package/extensions/whatsapp/package.json +1 -1
  358. package/extensions/zalo/CHANGELOG.md +5 -0
  359. package/extensions/zalo/package.json +1 -1
  360. package/extensions/zalouser/CHANGELOG.md +5 -0
  361. package/extensions/zalouser/package.json +1 -1
  362. package/package.json +1 -1
  363. package/skills/apple-reminders/SKILL.md +100 -49
  364. package/skills/coding-agent/SKILL.md +34 -28
  365. package/skills/github/SKILL.md +131 -16
  366. package/skills/imsg/SKILL.md +112 -15
  367. package/skills/openhue/SKILL.md +101 -19
  368. package/skills/tmux/SKILL.md +111 -79
  369. package/skills/weather/SKILL.md +88 -25
@@ -7,11 +7,28 @@ const DISCORD_CHANNEL_INFO_CACHE = new Map();
7
7
  export function __resetDiscordChannelInfoCacheForTest() {
8
8
  DISCORD_CHANNEL_INFO_CACHE.clear();
9
9
  }
10
+ function normalizeDiscordChannelId(value) {
11
+ if (typeof value === "string") {
12
+ return value.trim();
13
+ }
14
+ if (typeof value === "number" || typeof value === "bigint") {
15
+ return String(value).trim();
16
+ }
17
+ return "";
18
+ }
19
+ export function resolveDiscordMessageChannelId(params) {
20
+ const message = params.message;
21
+ return (normalizeDiscordChannelId(message.channelId) ||
22
+ normalizeDiscordChannelId(message.channel_id) ||
23
+ normalizeDiscordChannelId(message.rawData?.channel_id) ||
24
+ normalizeDiscordChannelId(params.eventChannelId));
25
+ }
10
26
  export async function resolveDiscordChannelInfo(client, channelId) {
11
27
  const cached = DISCORD_CHANNEL_INFO_CACHE.get(channelId);
12
28
  if (cached) {
13
- if (cached.expiresAt > Date.now())
29
+ if (cached.expiresAt > Date.now()) {
14
30
  return cached.value;
31
+ }
15
32
  DISCORD_CHANNEL_INFO_CACHE.delete(channelId);
16
33
  }
17
34
  try {
@@ -51,17 +68,47 @@ export async function resolveDiscordChannelInfo(client, channelId) {
51
68
  }
52
69
  export async function resolveMediaList(message, maxBytes) {
53
70
  const attachments = message.attachments ?? [];
54
- if (attachments.length === 0)
71
+ if (attachments.length === 0) {
72
+ return [];
73
+ }
74
+ const out = [];
75
+ await appendResolvedMediaFromAttachments({
76
+ attachments,
77
+ maxBytes,
78
+ out,
79
+ errorPrefix: "discord: failed to download attachment",
80
+ });
81
+ return out;
82
+ }
83
+ export async function resolveForwardedMediaList(message, maxBytes) {
84
+ const snapshots = resolveDiscordMessageSnapshots(message);
85
+ if (snapshots.length === 0) {
55
86
  return [];
87
+ }
56
88
  const out = [];
89
+ for (const snapshot of snapshots) {
90
+ await appendResolvedMediaFromAttachments({
91
+ attachments: snapshot.message?.attachments,
92
+ maxBytes,
93
+ out,
94
+ errorPrefix: "discord: failed to download forwarded attachment",
95
+ });
96
+ }
97
+ return out;
98
+ }
99
+ async function appendResolvedMediaFromAttachments(params) {
100
+ const attachments = params.attachments;
101
+ if (!attachments || attachments.length === 0) {
102
+ return;
103
+ }
57
104
  for (const attachment of attachments) {
58
105
  try {
59
106
  const fetched = await fetchRemoteMedia({
60
107
  url: attachment.url,
61
108
  filePathHint: attachment.filename ?? attachment.url,
62
109
  });
63
- const saved = await saveMediaBuffer(fetched.buffer, fetched.contentType ?? attachment.content_type, "inbound", maxBytes);
64
- out.push({
110
+ const saved = await saveMediaBuffer(fetched.buffer, fetched.contentType ?? attachment.content_type, "inbound", params.maxBytes);
111
+ params.out.push({
65
112
  path: saved.path,
66
113
  contentType: saved.contentType,
67
114
  placeholder: inferPlaceholder(attachment),
@@ -69,33 +116,38 @@ export async function resolveMediaList(message, maxBytes) {
69
116
  }
70
117
  catch (err) {
71
118
  const id = attachment.id ?? attachment.url;
72
- logVerbose(`discord: failed to download attachment ${id}: ${String(err)}`);
119
+ logVerbose(`${params.errorPrefix} ${id}: ${String(err)}`);
73
120
  }
74
121
  }
75
- return out;
76
122
  }
77
123
  function inferPlaceholder(attachment) {
78
124
  const mime = attachment.content_type ?? "";
79
- if (mime.startsWith("image/"))
125
+ if (mime.startsWith("image/")) {
80
126
  return "<media:image>";
81
- if (mime.startsWith("video/"))
127
+ }
128
+ if (mime.startsWith("video/")) {
82
129
  return "<media:video>";
83
- if (mime.startsWith("audio/"))
130
+ }
131
+ if (mime.startsWith("audio/")) {
84
132
  return "<media:audio>";
133
+ }
85
134
  return "<media:document>";
86
135
  }
87
136
  function isImageAttachment(attachment) {
88
137
  const mime = attachment.content_type ?? "";
89
- if (mime.startsWith("image/"))
138
+ if (mime.startsWith("image/")) {
90
139
  return true;
140
+ }
91
141
  const name = attachment.filename?.toLowerCase() ?? "";
92
- if (!name)
142
+ if (!name) {
93
143
  return false;
144
+ }
94
145
  return /\.(avif|bmp|gif|heic|heif|jpe?g|png|tiff?|webp)$/.test(name);
95
146
  }
96
147
  function buildDiscordAttachmentPlaceholder(attachments) {
97
- if (!attachments || attachments.length === 0)
148
+ if (!attachments || attachments.length === 0) {
98
149
  return "";
150
+ }
99
151
  const count = attachments.length;
100
152
  const allImages = attachments.every(isImageAttachment);
101
153
  const label = allImages ? "image" : "file";
@@ -109,27 +161,33 @@ export function resolveDiscordMessageText(message, options) {
109
161
  message.embeds?.[0]?.description ||
110
162
  options?.fallbackText?.trim() ||
111
163
  "";
112
- if (!options?.includeForwarded)
164
+ if (!options?.includeForwarded) {
113
165
  return baseText;
166
+ }
114
167
  const forwardedText = resolveDiscordForwardedMessagesText(message);
115
- if (!forwardedText)
168
+ if (!forwardedText) {
116
169
  return baseText;
117
- if (!baseText)
170
+ }
171
+ if (!baseText) {
118
172
  return forwardedText;
173
+ }
119
174
  return `${baseText}\n${forwardedText}`;
120
175
  }
121
176
  function resolveDiscordForwardedMessagesText(message) {
122
177
  const snapshots = resolveDiscordMessageSnapshots(message);
123
- if (snapshots.length === 0)
178
+ if (snapshots.length === 0) {
124
179
  return "";
180
+ }
125
181
  const forwardedBlocks = snapshots
126
182
  .map((snapshot) => {
127
183
  const snapshotMessage = snapshot.message;
128
- if (!snapshotMessage)
184
+ if (!snapshotMessage) {
129
185
  return null;
186
+ }
130
187
  const text = resolveDiscordSnapshotMessageText(snapshotMessage);
131
- if (!text)
188
+ if (!text) {
132
189
  return null;
190
+ }
133
191
  const authorLabel = formatDiscordSnapshotAuthor(snapshotMessage.author);
134
192
  const heading = authorLabel
135
193
  ? `[Forwarded message from ${authorLabel}]`
@@ -137,8 +195,9 @@ function resolveDiscordForwardedMessagesText(message) {
137
195
  return `${heading}\n${text}`;
138
196
  })
139
197
  .filter((entry) => Boolean(entry));
140
- if (forwardedBlocks.length === 0)
198
+ if (forwardedBlocks.length === 0) {
141
199
  return "";
200
+ }
142
201
  return forwardedBlocks.join("\n\n");
143
202
  }
144
203
  function resolveDiscordMessageSnapshots(message) {
@@ -146,8 +205,9 @@ function resolveDiscordMessageSnapshots(message) {
146
205
  const snapshots = rawData?.message_snapshots ??
147
206
  message.message_snapshots ??
148
207
  message.messageSnapshots;
149
- if (!Array.isArray(snapshots))
208
+ if (!Array.isArray(snapshots)) {
150
209
  return [];
210
+ }
151
211
  return snapshots.filter((entry) => Boolean(entry) && typeof entry === "object");
152
212
  }
153
213
  function resolveDiscordSnapshotMessageText(snapshot) {
@@ -158,8 +218,9 @@ function resolveDiscordSnapshotMessageText(snapshot) {
158
218
  return content || attachmentText || embedText || "";
159
219
  }
160
220
  function formatDiscordSnapshotAuthor(author) {
161
- if (!author)
221
+ if (!author) {
162
222
  return undefined;
223
+ }
163
224
  const globalName = author.global_name ?? undefined;
164
225
  const username = author.username ?? undefined;
165
226
  const name = author.name ?? undefined;
@@ -168,10 +229,12 @@ function formatDiscordSnapshotAuthor(author) {
168
229
  if (username && discriminator && discriminator !== "0") {
169
230
  return `@${username}#${discriminator}`;
170
231
  }
171
- if (base)
232
+ if (base) {
172
233
  return `@${base}`;
173
- if (author.id)
234
+ }
235
+ if (author.id) {
174
236
  return `@${author.id}`;
237
+ }
175
238
  return undefined;
176
239
  }
177
240
  export function buildDiscordMediaPayload(mediaList) {
@@ -3,25 +3,28 @@ import { ApplicationCommandOptionType, ButtonStyle } from "discord-api-types/v10
3
3
  import { resolveHumanDelayConfig } from "../../agents/identity.js";
4
4
  import { resolveChunkMode, resolveTextChunkLimit } from "../../auto-reply/chunk.js";
5
5
  import { buildCommandTextFromArgs, findCommandByNativeName, listChatCommands, parseCommandArgs, resolveCommandArgChoices, resolveCommandArgMenu, serializeCommandArgs, } from "../../auto-reply/commands-registry.js";
6
- import { dispatchReplyWithDispatcher } from "../../auto-reply/reply/provider-dispatcher.js";
7
6
  import { finalizeInboundContext } from "../../auto-reply/reply/inbound-context.js";
7
+ import { dispatchReplyWithDispatcher } from "../../auto-reply/reply/provider-dispatcher.js";
8
+ import { resolveCommandAuthorizedFromAuthorizers } from "../../channels/command-gating.js";
9
+ import { createReplyPrefixOptions } from "../../channels/reply-prefix.js";
10
+ import { getAgentScopedMediaLocalRoots } from "../../media/local-roots.js";
8
11
  import { buildPairingReply } from "../../pairing/pairing-messages.js";
9
12
  import { readChannelAllowFromStore, upsertChannelPairingRequest, } from "../../pairing/pairing-store.js";
10
13
  import { resolveAgentRoute } from "../../routing/resolve-route.js";
14
+ import { buildUntrustedChannelMetadata } from "../../security/channel-metadata.js";
15
+ import { chunkItems } from "../../utils/chunk-items.js";
11
16
  import { loadWebMedia } from "../../web/media.js";
12
17
  import { chunkDiscordTextWithMode } from "../chunk.js";
13
- import { resolveCommandAuthorizedFromAuthorizers } from "../../channels/command-gating.js";
14
- import { createReplyPrefixContext } from "../../channels/reply-prefix.js";
15
- import { buildUntrustedChannelMetadata } from "../../security/channel-metadata.js";
16
- import { allowListMatches, isDiscordGroupAllowedByPolicy, normalizeDiscordAllowList, normalizeDiscordSlug, resolveDiscordChannelConfigWithFallback, resolveDiscordGuildEntry, resolveDiscordOwnerAllowFrom, resolveDiscordUserAllowed, } from "./allow-list.js";
17
- import { resolveDiscordSenderIdentity } from "./sender-identity.js";
18
+ import { allowListMatches, isDiscordGroupAllowedByPolicy, normalizeDiscordAllowList, normalizeDiscordSlug, resolveDiscordChannelConfigWithFallback, resolveDiscordGuildEntry, resolveDiscordMemberAccessState, resolveDiscordOwnerAllowFrom, } from "./allow-list.js";
18
19
  import { resolveDiscordChannelInfo } from "./message-utils.js";
20
+ import { resolveDiscordSenderIdentity } from "./sender-identity.js";
19
21
  import { resolveDiscordThreadParentInfo } from "./threading.js";
20
22
  function buildDiscordCommandOptions(params) {
21
23
  const { command, cfg } = params;
22
24
  const args = command.args;
23
- if (!args || args.length === 0)
25
+ if (!args || args.length === 0) {
24
26
  return undefined;
27
+ }
25
28
  return args.map((arg) => {
26
29
  const required = arg.required ?? false;
27
30
  if (arg.type === "number") {
@@ -70,8 +73,9 @@ function buildDiscordCommandOptions(params) {
70
73
  });
71
74
  }
72
75
  function readDiscordCommandArgs(interaction, definitions) {
73
- if (!definitions || definitions.length === 0)
76
+ if (!definitions || definitions.length === 0) {
74
77
  return undefined;
78
+ }
75
79
  const values = {};
76
80
  for (const definition of definitions) {
77
81
  let value;
@@ -90,15 +94,6 @@ function readDiscordCommandArgs(interaction, definitions) {
90
94
  }
91
95
  return Object.keys(values).length > 0 ? { values } : undefined;
92
96
  }
93
- function chunkItems(items, size) {
94
- if (size <= 0)
95
- return [items];
96
- const rows = [];
97
- for (let i = 0; i < items.length; i += size) {
98
- rows.push(items.slice(i, i + size));
99
- }
100
- return rows;
101
- }
102
97
  const DISCORD_COMMAND_ARG_CUSTOM_ID_KEY = "cmdarg";
103
98
  function createCommandArgsWithValue(params) {
104
99
  const values = { [params.argName]: params.value };
@@ -116,15 +111,19 @@ function decodeDiscordCommandArgValue(value) {
116
111
  }
117
112
  }
118
113
  function isDiscordUnknownInteraction(error) {
119
- if (!error || typeof error !== "object")
114
+ if (!error || typeof error !== "object") {
120
115
  return false;
116
+ }
121
117
  const err = error;
122
- if (err.discordCode === 10062 || err.rawBody?.code === 10062)
118
+ if (err.discordCode === 10062 || err.rawBody?.code === 10062) {
123
119
  return true;
124
- if (err.status === 404 && /Unknown interaction/i.test(err.message ?? ""))
120
+ }
121
+ if (err.status === 404 && /Unknown interaction/i.test(err.message ?? "")) {
125
122
  return true;
126
- if (/Unknown interaction/i.test(err.rawBody?.message ?? ""))
123
+ }
124
+ if (/Unknown interaction/i.test(err.rawBody?.message ?? "")) {
127
125
  return true;
126
+ }
128
127
  return false;
129
128
  }
130
129
  async function safeDiscordInteractionCall(label, fn) {
@@ -148,15 +147,17 @@ function buildDiscordCommandArgCustomId(params) {
148
147
  ].join(";");
149
148
  }
150
149
  function parseDiscordCommandArgData(data) {
151
- if (!data || typeof data !== "object")
150
+ if (!data || typeof data !== "object") {
152
151
  return null;
152
+ }
153
153
  const coerce = (value) => typeof value === "string" || typeof value === "number" ? String(value) : "";
154
154
  const rawCommand = coerce(data.command);
155
155
  const rawArg = coerce(data.arg);
156
156
  const rawValue = coerce(data.value);
157
157
  const rawUser = coerce(data.user);
158
- if (!rawCommand || !rawArg || !rawValue || !rawUser)
158
+ if (!rawCommand || !rawArg || !rawValue || !rawUser) {
159
159
  return null;
160
+ }
160
161
  return {
161
162
  command: decodeDiscordCommandArgValue(rawCommand),
162
163
  arg: decodeDiscordCommandArgValue(rawArg),
@@ -190,8 +191,9 @@ async function handleDiscordCommandArgInteraction(interaction, data, ctx) {
190
191
  content: `✅ Selected ${parsed.value}.`,
191
192
  components: [],
192
193
  }));
193
- if (!updated)
194
+ if (!updated) {
194
195
  return;
196
+ }
195
197
  const commandArgs = createCommandArgsWithValue({
196
198
  argName: parsed.arg,
197
199
  value: parsed.value,
@@ -357,8 +359,9 @@ async function dispatchDiscordCommandInteraction(params) {
357
359
  };
358
360
  const useAccessGroups = cfg.commands?.useAccessGroups !== false;
359
361
  const user = interaction.user;
360
- if (!user)
362
+ if (!user) {
361
363
  return;
364
+ }
362
365
  const sender = resolveDiscordSenderIdentity({ author: user, pluralkitInfo: null });
363
366
  const channel = interaction.channel;
364
367
  const channelType = channel?.type;
@@ -370,11 +373,10 @@ async function dispatchDiscordCommandInteraction(params) {
370
373
  const channelName = channel && "name" in channel ? channel.name : undefined;
371
374
  const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
372
375
  const rawChannelId = channel?.id ?? "";
373
- const ownerAllowList = normalizeDiscordAllowList(discordConfig?.dm?.allowFrom ?? [], [
374
- "discord:",
375
- "user:",
376
- "pk:",
377
- ]);
376
+ const memberRoleIds = Array.isArray(interaction.rawData.member?.roles)
377
+ ? interaction.rawData.member.roles.map((roleId) => String(roleId))
378
+ : [];
379
+ const ownerAllowList = normalizeDiscordAllowList(discordConfig?.allowFrom ?? discordConfig?.dm?.allowFrom ?? [], ["discord:", "user:", "pk:"]);
378
380
  const ownerOk = ownerAllowList && user
379
381
  ? allowListMatches(ownerAllowList, {
380
382
  id: sender.id,
@@ -441,7 +443,7 @@ async function dispatchDiscordCommandInteraction(params) {
441
443
  }
442
444
  }
443
445
  const dmEnabled = discordConfig?.dm?.enabled ?? true;
444
- const dmPolicy = discordConfig?.dm?.policy ?? "pairing";
446
+ const dmPolicy = discordConfig?.dmPolicy ?? discordConfig?.dm?.policy ?? "pairing";
445
447
  let commandAuthorized = true;
446
448
  if (isDirectMessage) {
447
449
  if (!dmEnabled || dmPolicy === "disabled") {
@@ -450,7 +452,10 @@ async function dispatchDiscordCommandInteraction(params) {
450
452
  }
451
453
  if (dmPolicy !== "open") {
452
454
  const storeAllowFrom = await readChannelAllowFromStore("discord").catch(() => []);
453
- const effectiveAllowFrom = [...(discordConfig?.dm?.allowFrom ?? []), ...storeAllowFrom];
455
+ const effectiveAllowFrom = [
456
+ ...(discordConfig?.allowFrom ?? discordConfig?.dm?.allowFrom ?? []),
457
+ ...storeAllowFrom,
458
+ ];
454
459
  const allowList = normalizeDiscordAllowList(effectiveAllowFrom, ["discord:", "user:", "pk:"]);
455
460
  const permitted = allowList
456
461
  ? allowListMatches(allowList, {
@@ -467,7 +472,7 @@ async function dispatchDiscordCommandInteraction(params) {
467
472
  id: user.id,
468
473
  meta: {
469
474
  tag: sender.tag,
470
- name: sender.name ?? undefined,
475
+ name: sender.name,
471
476
  },
472
477
  });
473
478
  if (created) {
@@ -487,22 +492,18 @@ async function dispatchDiscordCommandInteraction(params) {
487
492
  }
488
493
  }
489
494
  if (!isDirectMessage) {
490
- const channelUsers = channelConfig?.users ?? guildInfo?.users;
491
- const hasUserAllowlist = Array.isArray(channelUsers) && channelUsers.length > 0;
492
- const userOk = hasUserAllowlist
493
- ? resolveDiscordUserAllowed({
494
- allowList: channelUsers,
495
- userId: sender.id,
496
- userName: sender.name,
497
- userTag: sender.tag,
498
- })
499
- : false;
495
+ const { hasAccessRestrictions, memberAllowed } = resolveDiscordMemberAccessState({
496
+ channelConfig,
497
+ guildInfo,
498
+ memberRoleIds,
499
+ sender,
500
+ });
500
501
  const authorizers = useAccessGroups
501
502
  ? [
502
503
  { configured: ownerAllowList != null, allowed: ownerOk },
503
- { configured: hasUserAllowlist, allowed: userOk },
504
+ { configured: hasAccessRestrictions, allowed: memberAllowed },
504
505
  ]
505
- : [{ configured: hasUserAllowlist, allowed: userOk }];
506
+ : [{ configured: hasAccessRestrictions, allowed: memberAllowed }];
506
507
  commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
507
508
  useAccessGroups,
508
509
  authorizers,
@@ -555,8 +556,9 @@ async function dispatchDiscordCommandInteraction(params) {
555
556
  channel: "discord",
556
557
  accountId,
557
558
  guildId: interaction.guild?.id ?? undefined,
559
+ memberRoleIds,
558
560
  peer: {
559
- kind: isDirectMessage ? "dm" : isGroupDm ? "group" : "channel",
561
+ kind: isDirectMessage ? "direct" : isGroupDm ? "group" : "channel",
560
562
  id: isDirectMessage ? user.id : channelId,
561
563
  },
562
564
  parentPeer: threadParentId ? { kind: "channel", id: threadParentId } : undefined,
@@ -602,6 +604,7 @@ async function dispatchDiscordCommandInteraction(params) {
602
604
  return untrustedChannelMetadata ? [untrustedChannelMetadata] : undefined;
603
605
  })()
604
606
  : undefined,
607
+ OwnerAllowFrom: ownerAllowFrom,
605
608
  SenderName: user.globalName ?? user.username,
606
609
  SenderId: user.id,
607
610
  SenderUsername: user.username,
@@ -613,22 +616,32 @@ async function dispatchDiscordCommandInteraction(params) {
613
616
  Timestamp: Date.now(),
614
617
  CommandAuthorized: commandAuthorized,
615
618
  CommandSource: "native",
616
- OwnerAllowFrom: ownerAllowFrom,
619
+ // Native slash contexts use To=slash:<user> for interaction routing.
620
+ // For follow-up delivery (for example subagent completion announces),
621
+ // preserve the real Discord target separately.
622
+ OriginatingChannel: "discord",
623
+ OriginatingTo: isDirectMessage ? `user:${user.id}` : `channel:${channelId}`,
624
+ });
625
+ const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
626
+ cfg,
627
+ agentId: route.agentId,
628
+ channel: "discord",
629
+ accountId: route.accountId,
617
630
  });
618
- const prefixContext = createReplyPrefixContext({ cfg, agentId: route.agentId });
631
+ const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
619
632
  let didReply = false;
620
633
  await dispatchReplyWithDispatcher({
621
634
  ctx: ctxPayload,
622
635
  cfg,
623
636
  dispatcherOptions: {
624
- responsePrefix: prefixContext.responsePrefix,
625
- responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
637
+ ...prefixOptions,
626
638
  humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
627
639
  deliver: async (payload) => {
628
640
  try {
629
641
  await deliverDiscordInteractionReply({
630
642
  interaction,
631
643
  payload,
644
+ mediaLocalRoots,
632
645
  textLimit: resolveTextChunkLimit(cfg, "discord", accountId, {
633
646
  fallbackLimit: 2000,
634
647
  }),
@@ -655,7 +668,7 @@ async function dispatchDiscordCommandInteraction(params) {
655
668
  disableBlockStreaming: typeof discordConfig?.blockStreaming === "boolean"
656
669
  ? !discordConfig.blockStreaming
657
670
  : undefined,
658
- onModelSelected: prefixContext.onModelSelected,
671
+ onModelSelected,
659
672
  },
660
673
  });
661
674
  }
@@ -689,7 +702,9 @@ async function deliverDiscordInteractionReply(params) {
689
702
  };
690
703
  if (mediaList.length > 0) {
691
704
  const media = await Promise.all(mediaList.map(async (url) => {
692
- const loaded = await loadWebMedia(url);
705
+ const loaded = await loadWebMedia(url, {
706
+ localRoots: params.mediaLocalRoots,
707
+ });
693
708
  return {
694
709
  name: loaded.fileName ?? "upload",
695
710
  data: loaded.buffer,
@@ -700,29 +715,34 @@ async function deliverDiscordInteractionReply(params) {
700
715
  maxLines: maxLinesPerMessage,
701
716
  chunkMode,
702
717
  });
703
- if (!chunks.length && text)
718
+ if (!chunks.length && text) {
704
719
  chunks.push(text);
720
+ }
705
721
  const caption = chunks[0] ?? "";
706
722
  await sendMessage(caption, media);
707
723
  for (const chunk of chunks.slice(1)) {
708
- if (!chunk.trim())
724
+ if (!chunk.trim()) {
709
725
  continue;
726
+ }
710
727
  await interaction.followUp({ content: chunk });
711
728
  }
712
729
  return;
713
730
  }
714
- if (!text.trim())
731
+ if (!text.trim()) {
715
732
  return;
733
+ }
716
734
  const chunks = chunkDiscordTextWithMode(text, {
717
735
  maxChars: textLimit,
718
736
  maxLines: maxLinesPerMessage,
719
737
  chunkMode,
720
738
  });
721
- if (!chunks.length && text)
739
+ if (!chunks.length && text) {
722
740
  chunks.push(text);
741
+ }
723
742
  for (const chunk of chunks) {
724
- if (!chunk.trim())
743
+ if (!chunk.trim()) {
725
744
  continue;
745
+ }
726
746
  await sendMessage(chunk);
727
747
  }
728
748
  }