@poolzin/pool-bot 2026.2.24 → 2026.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (646) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/acp/client.js +207 -18
  3. package/dist/acp/event-mapper.js +87 -22
  4. package/dist/acp/meta.js +12 -6
  5. package/dist/acp/secret-file.js +22 -0
  6. package/dist/agents/agent-paths.js +8 -9
  7. package/dist/agents/agent-scope.js +17 -5
  8. package/dist/agents/auth-profiles/oauth.js +148 -64
  9. package/dist/agents/auth-profiles/session-override.js +13 -7
  10. package/dist/agents/bash-process-registry.test-helpers.js +29 -0
  11. package/dist/agents/bash-tools.exec-approval-request.js +20 -0
  12. package/dist/agents/bash-tools.exec-host-gateway.js +240 -0
  13. package/dist/agents/bash-tools.exec-host-node.js +235 -0
  14. package/dist/agents/bash-tools.exec-runtime.js +2 -25
  15. package/dist/agents/bash-tools.exec-types.js +1 -0
  16. package/dist/agents/bash-tools.process.js +224 -218
  17. package/dist/agents/bedrock-discovery.js +3 -1
  18. package/dist/agents/byteplus-models.js +97 -0
  19. package/dist/agents/chutes-oauth.js +1 -0
  20. package/dist/agents/cli-runner/helpers.js +4 -0
  21. package/dist/agents/compaction.js +41 -14
  22. package/dist/agents/content-blocks.js +16 -0
  23. package/dist/agents/doubao-models.js +121 -0
  24. package/dist/agents/failover-error.js +2 -0
  25. package/dist/agents/huggingface-models.js +5 -3
  26. package/dist/agents/live-model-filter.js +5 -0
  27. package/dist/agents/minimax-vlm.js +10 -8
  28. package/dist/agents/model-auth.js +6 -0
  29. package/dist/agents/model-catalog.js +3 -1
  30. package/dist/agents/model-fallback.js +96 -101
  31. package/dist/agents/model-selection.js +7 -1
  32. package/dist/agents/models-config.providers.js +364 -165
  33. package/dist/agents/ollama-stream.js +117 -4
  34. package/dist/agents/opencode-zen-models.js +22 -11
  35. package/dist/agents/pi-embedded-helpers/errors.js +55 -33
  36. package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
  37. package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
  38. package/dist/agents/pi-embedded-helpers.js +1 -1
  39. package/dist/agents/pi-embedded-payloads.js +1 -0
  40. package/dist/agents/pi-embedded-runner/compact.js +29 -7
  41. package/dist/agents/pi-embedded-runner/extensions.js +28 -26
  42. package/dist/agents/pi-embedded-runner/google.js +20 -8
  43. package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
  44. package/dist/agents/pi-embedded-runner/run.js +71 -12
  45. package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
  46. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
  47. package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
  48. package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
  49. package/dist/agents/pi-embedded-runner/thinking.js +42 -0
  50. package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
  51. package/dist/agents/pi-embedded-runner/utils.js +7 -10
  52. package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
  53. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
  54. package/dist/agents/pi-embedded-subscribe.js +9 -4
  55. package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
  56. package/dist/agents/pi-embedded-utils.js +3 -0
  57. package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
  58. package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
  59. package/dist/agents/pi-settings.js +40 -0
  60. package/dist/agents/pi-tools.policy.js +2 -1
  61. package/dist/agents/provider/config-loader.js +1 -1
  62. package/dist/agents/sandbox/browser.js +170 -33
  63. package/dist/agents/sandbox/config-hash.js +14 -27
  64. package/dist/agents/sandbox/config.js +21 -2
  65. package/dist/agents/sandbox/constants.js +2 -0
  66. package/dist/agents/sandbox/docker.js +16 -2
  67. package/dist/agents/sandbox/novnc-auth.js +62 -0
  68. package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
  69. package/dist/agents/sandbox/shared.js +10 -6
  70. package/dist/agents/sandbox-paths.js +24 -11
  71. package/dist/agents/schema/clean-for-gemini.js +132 -85
  72. package/dist/agents/session-slug.js +10 -5
  73. package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
  74. package/dist/agents/session-tool-result-guard.js +3 -1
  75. package/dist/agents/session-transcript-repair.js +40 -6
  76. package/dist/agents/skills/bundled-dir.js +19 -5
  77. package/dist/agents/skills/env-overrides.js +124 -43
  78. package/dist/agents/skills/frontmatter.js +6 -6
  79. package/dist/agents/skills/plugin-skills.js +14 -7
  80. package/dist/agents/skills/workspace.js +1 -0
  81. package/dist/agents/skills.test-helpers.js +13 -0
  82. package/dist/agents/stable-stringify.js +12 -0
  83. package/dist/agents/subagent-announce.js +251 -49
  84. package/dist/agents/subagent-lifecycle-events.js +19 -0
  85. package/dist/agents/subagent-registry-cleanup.js +31 -0
  86. package/dist/agents/subagent-registry-completion.js +68 -0
  87. package/dist/agents/subagent-registry-queries.js +117 -0
  88. package/dist/agents/subagent-registry-state.js +46 -0
  89. package/dist/agents/subagent-registry.js +252 -221
  90. package/dist/agents/subagent-registry.mocks.shared.js +12 -0
  91. package/dist/agents/subagent-registry.store.js +1 -0
  92. package/dist/agents/subagent-registry.types.js +1 -0
  93. package/dist/agents/subagent-spawn.js +195 -7
  94. package/dist/agents/system-prompt.js +22 -6
  95. package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
  96. package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
  97. package/dist/agents/test-helpers/fast-core-tools.js +1 -17
  98. package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
  99. package/dist/agents/timeout.js +18 -6
  100. package/dist/agents/tool-call-id.js +1 -1
  101. package/dist/agents/tool-display-common.js +162 -29
  102. package/dist/agents/tool-images.js +82 -9
  103. package/dist/agents/tool-policy-shared.js +108 -0
  104. package/dist/agents/tool-policy.js +51 -26
  105. package/dist/agents/tools/browser-tool.js +160 -54
  106. package/dist/agents/tools/canvas-tool.js +27 -1
  107. package/dist/agents/tools/common.js +45 -0
  108. package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
  109. package/dist/agents/tools/discord-actions-guild.js +4 -1
  110. package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
  111. package/dist/agents/tools/gateway-tool.js +3 -1
  112. package/dist/agents/tools/image-tool.js +214 -99
  113. package/dist/agents/tools/nodes-utils.js +1 -10
  114. package/dist/agents/tools/sessions-history-tool.js +140 -108
  115. package/dist/agents/tools/sessions-send-helpers.js +12 -6
  116. package/dist/agents/tools/sessions-spawn-tool.js +8 -2
  117. package/dist/agents/tools/subagents-tool.js +2 -1
  118. package/dist/agents/tools/whatsapp-actions.js +10 -2
  119. package/dist/agents/tools/whatsapp-target-auth.js +18 -0
  120. package/dist/agents/transcript-policy.js +22 -8
  121. package/dist/agents/venice-models.js +11 -3
  122. package/dist/agents/workspace.js +222 -46
  123. package/dist/auto-reply/commands-registry.data.js +51 -0
  124. package/dist/auto-reply/commands-registry.js +19 -21
  125. package/dist/auto-reply/fallback-state.js +114 -0
  126. package/dist/auto-reply/group-activation.js +10 -5
  127. package/dist/auto-reply/inbound-debounce.js +10 -5
  128. package/dist/auto-reply/model-runtime.js +68 -0
  129. package/dist/auto-reply/reply/abort.js +1 -1
  130. package/dist/auto-reply/reply/agent-runner-execution.js +40 -5
  131. package/dist/auto-reply/reply/agent-runner.js +165 -39
  132. package/dist/auto-reply/reply/bash-command.js +41 -39
  133. package/dist/auto-reply/reply/command-gates.js +25 -0
  134. package/dist/auto-reply/reply/commands-allowlist.js +111 -72
  135. package/dist/auto-reply/reply/commands-bash.js +6 -5
  136. package/dist/auto-reply/reply/commands-config.js +30 -28
  137. package/dist/auto-reply/reply/commands-core.js +2 -1
  138. package/dist/auto-reply/reply/commands-info.js +1 -0
  139. package/dist/auto-reply/reply/commands-models.js +65 -14
  140. package/dist/auto-reply/reply/commands-session.js +237 -82
  141. package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
  142. package/dist/auto-reply/reply/commands-setunset.js +45 -0
  143. package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
  144. package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
  145. package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
  146. package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
  147. package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
  148. package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
  149. package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
  150. package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
  151. package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
  152. package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
  153. package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
  154. package/dist/auto-reply/reply/commands-subagents.js +51 -587
  155. package/dist/auto-reply/reply/commands-tts.js +10 -5
  156. package/dist/auto-reply/reply/config-value.js +10 -5
  157. package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
  158. package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
  159. package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
  160. package/dist/auto-reply/reply/followup-runner.js +1 -0
  161. package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
  162. package/dist/auto-reply/reply/get-reply-directives.js +17 -28
  163. package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
  164. package/dist/auto-reply/reply/get-reply.js +71 -12
  165. package/dist/auto-reply/reply/model-selection.js +80 -39
  166. package/dist/auto-reply/reply/queue/enqueue.js +10 -5
  167. package/dist/auto-reply/reply/queue/state.js +13 -12
  168. package/dist/auto-reply/reply/reply-payloads.js +67 -36
  169. package/dist/auto-reply/reply/reply-reference.js +9 -8
  170. package/dist/auto-reply/reply/route-reply.js +15 -8
  171. package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
  172. package/dist/auto-reply/reply/session.js +22 -6
  173. package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
  174. package/dist/auto-reply/reply/subagents-utils.js +56 -30
  175. package/dist/auto-reply/reply/typing.js +46 -21
  176. package/dist/auto-reply/send-policy.js +14 -7
  177. package/dist/auto-reply/status.js +140 -16
  178. package/dist/auto-reply/templating.js +10 -5
  179. package/dist/auto-reply/thinking.js +7 -16
  180. package/dist/auto-reply/tokens.js +21 -5
  181. package/dist/browser/bridge-server.js +36 -20
  182. package/dist/browser/cdp.helpers.js +7 -14
  183. package/dist/browser/cdp.js +35 -15
  184. package/dist/browser/chrome.profile-decoration.js +7 -4
  185. package/dist/browser/config.js +30 -0
  186. package/dist/browser/extension-relay-auth.js +55 -0
  187. package/dist/browser/extension-relay.js +74 -29
  188. package/dist/browser/navigation-guard.js +39 -0
  189. package/dist/browser/paths.js +77 -0
  190. package/dist/browser/profiles.js +13 -8
  191. package/dist/browser/pw-ai-module.js +10 -5
  192. package/dist/browser/pw-session.js +76 -39
  193. package/dist/browser/pw-tools-core.interactions.js +14 -7
  194. package/dist/browser/pw-tools-core.state.js +12 -6
  195. package/dist/browser/routes/agent.act.js +431 -424
  196. package/dist/browser/routes/agent.shared.js +47 -3
  197. package/dist/browser/routes/agent.snapshot.js +122 -116
  198. package/dist/browser/routes/agent.storage.js +303 -297
  199. package/dist/browser/routes/tabs.js +154 -100
  200. package/dist/browser/server-context.js +7 -0
  201. package/dist/browser/server-lifecycle.js +37 -0
  202. package/dist/build-info.json +3 -3
  203. package/dist/channels/allow-from.js +26 -0
  204. package/dist/channels/allowlists/resolve-utils.js +43 -19
  205. package/dist/channels/channel-config.js +14 -7
  206. package/dist/channels/draft-stream-loop.js +7 -0
  207. package/dist/channels/model-overrides.js +82 -0
  208. package/dist/channels/plugins/account-action-gate.js +13 -0
  209. package/dist/channels/plugins/message-actions.js +10 -0
  210. package/dist/channels/plugins/normalize/imessage.js +14 -7
  211. package/dist/channels/plugins/normalize/slack.js +10 -5
  212. package/dist/channels/plugins/normalize/telegram.js +14 -7
  213. package/dist/channels/plugins/outbound/discord.js +80 -8
  214. package/dist/channels/plugins/outbound/signal.js +11 -11
  215. package/dist/channels/plugins/setup-helpers.js +10 -5
  216. package/dist/channels/sender-label.js +14 -7
  217. package/dist/channels/session.js +4 -2
  218. package/dist/channels/status-reactions.js +297 -0
  219. package/dist/channels/telegram/api.js +18 -0
  220. package/dist/cli/argv.js +84 -21
  221. package/dist/cli/banner.js +3 -2
  222. package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
  223. package/dist/cli/cli-name.js +11 -11
  224. package/dist/cli/cli-utils.js +13 -3
  225. package/dist/cli/command-format.js +1 -1
  226. package/dist/cli/config-cli.js +1 -1
  227. package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
  228. package/dist/cli/daemon-cli/lifecycle.js +64 -2
  229. package/dist/cli/daemon-cli/restart-health.js +126 -0
  230. package/dist/cli/daemon-cli/status.gather.js +9 -13
  231. package/dist/cli/daemon-cli/status.print.js +2 -10
  232. package/dist/cli/deps.js +27 -22
  233. package/dist/cli/exec-approvals-cli.js +92 -124
  234. package/dist/cli/gateway-cli/run-loop.js +23 -5
  235. package/dist/cli/memory-cli.js +158 -61
  236. package/dist/cli/node-cli/register.js +14 -5
  237. package/dist/cli/nodes-cli/register.push.js +63 -0
  238. package/dist/cli/nodes-media-utils.js +26 -0
  239. package/dist/cli/outbound-send-deps.js +2 -9
  240. package/dist/cli/outbound-send-mapping.js +11 -0
  241. package/dist/cli/pairing-cli.js +40 -14
  242. package/dist/cli/plugins-cli.js +250 -73
  243. package/dist/cli/ports.js +11 -10
  244. package/dist/cli/program/build-program.js +3 -1
  245. package/dist/cli/program/command-registry.js +214 -136
  246. package/dist/cli/program/command-tree.js +16 -0
  247. package/dist/cli/program/help.js +43 -12
  248. package/dist/cli/program/preaction.js +13 -9
  249. package/dist/cli/program/register.configure.js +3 -18
  250. package/dist/cli/program/register.maintenance.js +2 -2
  251. package/dist/cli/program/register.onboard.js +2 -0
  252. package/dist/cli/program/register.status-health-sessions.js +16 -17
  253. package/dist/cli/program/register.subclis.js +93 -52
  254. package/dist/cli/route.js +12 -8
  255. package/dist/cli/system-cli.js +36 -46
  256. package/dist/cli/test-runtime-capture.js +24 -0
  257. package/dist/cli/update-cli/shared.js +22 -9
  258. package/dist/cli/update-cli/update-command.js +89 -14
  259. package/dist/cli/update-cli/wizard.js +6 -12
  260. package/dist/commands/agent/run-context.js +18 -5
  261. package/dist/commands/agent/session-store.js +17 -4
  262. package/dist/commands/agent.js +185 -89
  263. package/dist/commands/agents.bindings.js +14 -7
  264. package/dist/commands/agents.commands.add.js +13 -9
  265. package/dist/commands/agents.commands.identity.js +12 -6
  266. package/dist/commands/agents.commands.list.js +11 -6
  267. package/dist/commands/agents.config.js +8 -10
  268. package/dist/commands/agents.providers.js +12 -6
  269. package/dist/commands/auth-choice-options.js +103 -75
  270. package/dist/commands/auth-choice.apply.byteplus.js +55 -0
  271. package/dist/commands/auth-choice.apply.js +4 -0
  272. package/dist/commands/auth-choice.apply.minimax.js +61 -13
  273. package/dist/commands/auth-choice.apply.openai.js +3 -1
  274. package/dist/commands/auth-choice.apply.volcengine.js +55 -0
  275. package/dist/commands/auth-choice.preferred-provider.js +2 -0
  276. package/dist/commands/channels/remove.js +13 -6
  277. package/dist/commands/channels/shared.js +4 -14
  278. package/dist/commands/channels.mock-harness.js +23 -0
  279. package/dist/commands/configure.commands.js +14 -0
  280. package/dist/commands/configure.gateway.js +2 -4
  281. package/dist/commands/configure.js +1 -1
  282. package/dist/commands/configure.shared.js +11 -0
  283. package/dist/commands/daemon-install-helpers.js +2 -2
  284. package/dist/commands/daemon-install-runtime-warning.js +11 -0
  285. package/dist/commands/dashboard.js +12 -10
  286. package/dist/commands/docs.js +14 -8
  287. package/dist/commands/doctor-config-flow.js +11 -9
  288. package/dist/commands/doctor-legacy-config.js +281 -0
  289. package/dist/commands/doctor-state-integrity.js +99 -23
  290. package/dist/commands/doctor-update.js +12 -9
  291. package/dist/commands/models/list.list-command.js +7 -5
  292. package/dist/commands/models/set-image.js +2 -21
  293. package/dist/commands/node-daemon-install-helpers.js +10 -8
  294. package/dist/commands/onboard-auth.config-minimax.js +54 -80
  295. package/dist/commands/onboard-auth.config-opencode.js +2 -18
  296. package/dist/commands/onboard-auth.credentials.js +90 -13
  297. package/dist/commands/onboard-auth.js +1 -1
  298. package/dist/commands/onboard-auth.models.js +6 -5
  299. package/dist/commands/onboard-hooks.js +1 -1
  300. package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
  301. package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
  302. package/dist/commands/onboard-provider-auth-flags.js +14 -0
  303. package/dist/commands/onboard-remote.js +14 -7
  304. package/dist/commands/onboard.js +11 -13
  305. package/dist/commands/sandbox-display.js +6 -5
  306. package/dist/commands/sessions.test-helpers.js +61 -0
  307. package/dist/commands/status-all/diagnosis.js +14 -10
  308. package/dist/commands/status-all/format.js +1 -0
  309. package/dist/commands/status.gateway-probe.js +1 -16
  310. package/dist/commands/systemd-linger.js +12 -6
  311. package/dist/config/agent-limits.js +2 -0
  312. package/dist/config/commands.js +32 -15
  313. package/dist/config/config-paths.js +9 -11
  314. package/dist/config/config.js +1 -1
  315. package/dist/config/defaults.js +22 -2
  316. package/dist/config/discord-preview-streaming.js +104 -0
  317. package/dist/config/env-substitution.js +62 -34
  318. package/dist/config/env-vars.js +45 -7
  319. package/dist/config/includes.js +4 -0
  320. package/dist/config/io.js +656 -171
  321. package/dist/config/legacy.migrations.part-1.js +189 -78
  322. package/dist/config/legacy.shared.js +3 -1
  323. package/dist/config/merge-patch.js +54 -4
  324. package/dist/config/prototype-keys.js +4 -0
  325. package/dist/config/redact-snapshot.js +404 -76
  326. package/dist/config/schema.help.js +44 -7
  327. package/dist/config/schema.js +58 -570
  328. package/dist/config/schema.labels.js +38 -6
  329. package/dist/config/sessions/delivery-info.js +10 -3
  330. package/dist/config/sessions/main-session.js +10 -5
  331. package/dist/config/sessions/session-file.js +33 -0
  332. package/dist/config/sessions/session-key.js +10 -5
  333. package/dist/config/sessions/store.js +1 -1
  334. package/dist/config/sessions.js +1 -0
  335. package/dist/config/validation.js +140 -85
  336. package/dist/config/zod-schema.agent-runtime.js +11 -0
  337. package/dist/config/zod-schema.hooks.js +40 -11
  338. package/dist/config/zod-schema.installs.js +20 -0
  339. package/dist/config/zod-schema.js +156 -20
  340. package/dist/config/zod-schema.providers-core.js +78 -4
  341. package/dist/config/zod-schema.providers.js +6 -1
  342. package/dist/config/zod-schema.session.js +41 -2
  343. package/dist/cron/run-log.js +3 -0
  344. package/dist/cron/schedule.js +21 -10
  345. package/dist/cron/service/ops.js +35 -21
  346. package/dist/cron/service/timer.js +116 -16
  347. package/dist/cron/stagger.js +3 -1
  348. package/dist/daemon/cmd-argv.js +21 -0
  349. package/dist/daemon/cmd-set.js +58 -0
  350. package/dist/daemon/service-types.js +1 -0
  351. package/dist/discord/api.js +12 -6
  352. package/dist/discord/draft-chunking.js +22 -0
  353. package/dist/discord/draft-stream.js +124 -0
  354. package/dist/discord/monitor/agent-components.js +1 -1
  355. package/dist/discord/monitor/commands.js +5 -0
  356. package/dist/discord/monitor/exec-approvals.js +357 -162
  357. package/dist/discord/monitor/gateway-plugin.js +2 -1
  358. package/dist/discord/monitor/listeners.js +37 -27
  359. package/dist/discord/monitor/message-handler.js +4 -1
  360. package/dist/discord/monitor/message-handler.preflight.js +65 -8
  361. package/dist/discord/monitor/message-handler.process.js +246 -217
  362. package/dist/discord/monitor/message-utils.js +143 -6
  363. package/dist/discord/monitor/model-picker-preferences.js +143 -0
  364. package/dist/discord/monitor/model-picker.js +651 -0
  365. package/dist/discord/monitor/native-command.js +573 -16
  366. package/dist/discord/monitor/provider.allowlist.js +223 -0
  367. package/dist/discord/monitor/provider.js +275 -347
  368. package/dist/discord/monitor/provider.lifecycle.js +100 -0
  369. package/dist/discord/monitor/reply-delivery.js +123 -16
  370. package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
  371. package/dist/discord/monitor/thread-bindings.js +4 -0
  372. package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
  373. package/dist/discord/monitor/thread-bindings.manager.js +423 -0
  374. package/dist/discord/monitor/thread-bindings.messages.js +55 -0
  375. package/dist/discord/monitor/thread-bindings.state.js +358 -0
  376. package/dist/discord/monitor/thread-bindings.types.js +6 -0
  377. package/dist/discord/resolve-users.js +33 -21
  378. package/dist/discord/send.channels.js +15 -0
  379. package/dist/discord/send.js +3 -2
  380. package/dist/discord/send.outbound.js +82 -26
  381. package/dist/discord/send.permissions.js +83 -30
  382. package/dist/discord/send.reactions.js +8 -4
  383. package/dist/discord/token.js +10 -5
  384. package/dist/discord/voice/command.js +263 -0
  385. package/dist/discord/voice/manager.js +531 -0
  386. package/dist/gateway/auth.js +72 -13
  387. package/dist/gateway/call.js +152 -83
  388. package/dist/gateway/canvas-capability.js +75 -0
  389. package/dist/gateway/client.js +28 -4
  390. package/dist/gateway/config-reload.js +3 -4
  391. package/dist/gateway/control-plane-audit.js +28 -0
  392. package/dist/gateway/control-plane-rate-limit.js +53 -0
  393. package/dist/gateway/control-ui.js +219 -96
  394. package/dist/gateway/events.js +1 -0
  395. package/dist/gateway/hooks-mapping.js +88 -38
  396. package/dist/gateway/hooks.js +109 -54
  397. package/dist/gateway/http-auth-helpers.js +3 -2
  398. package/dist/gateway/http-common.js +22 -0
  399. package/dist/gateway/http-endpoint-helpers.js +1 -0
  400. package/dist/gateway/method-scopes.js +169 -0
  401. package/dist/gateway/net.js +74 -9
  402. package/dist/gateway/node-invoke-system-run-approval.js +14 -35
  403. package/dist/gateway/node-registry.js +10 -5
  404. package/dist/gateway/openai-http.js +1 -0
  405. package/dist/gateway/openresponses-http.js +121 -110
  406. package/dist/gateway/origin-check.js +1 -18
  407. package/dist/gateway/probe-auth.js +2 -0
  408. package/dist/gateway/protocol/index.js +4 -2
  409. package/dist/gateway/protocol/schema/cron.js +1 -0
  410. package/dist/gateway/protocol/schema/devices.js +1 -0
  411. package/dist/gateway/protocol/schema/protocol-schemas.js +4 -1
  412. package/dist/gateway/protocol/schema/push.js +18 -0
  413. package/dist/gateway/protocol/schema/sessions.js +6 -0
  414. package/dist/gateway/protocol/schema.js +1 -0
  415. package/dist/gateway/role-policy.js +17 -0
  416. package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
  417. package/dist/gateway/server/ws-connection/message-handler.js +175 -148
  418. package/dist/gateway/server-chat.js +83 -25
  419. package/dist/gateway/server-constants.js +10 -9
  420. package/dist/gateway/server-cron.js +1 -0
  421. package/dist/gateway/server-http.js +247 -54
  422. package/dist/gateway/server-maintenance.js +20 -5
  423. package/dist/gateway/server-methods/agent.js +162 -24
  424. package/dist/gateway/server-methods/chat.js +465 -130
  425. package/dist/gateway/server-methods/config.js +193 -152
  426. package/dist/gateway/server-methods/devices.js +17 -3
  427. package/dist/gateway/server-methods/models.js +11 -1
  428. package/dist/gateway/server-methods/nodes.helpers.js +12 -0
  429. package/dist/gateway/server-methods/nodes.js +251 -69
  430. package/dist/gateway/server-methods/push.js +53 -0
  431. package/dist/gateway/server-methods/sessions.js +64 -8
  432. package/dist/gateway/server-methods/usage.js +162 -75
  433. package/dist/gateway/server-node-events.js +29 -0
  434. package/dist/gateway/server-reload-handlers.js +2 -3
  435. package/dist/gateway/server-runtime-config.js +39 -13
  436. package/dist/gateway/server-runtime-state.js +2 -0
  437. package/dist/gateway/server-startup-memory.js +17 -11
  438. package/dist/gateway/server-ws-runtime.js +1 -0
  439. package/dist/gateway/server.impl.js +296 -139
  440. package/dist/gateway/session-preview.test-helpers.js +11 -0
  441. package/dist/gateway/session-utils.fs.js +32 -34
  442. package/dist/gateway/sessions-resolve.js +17 -5
  443. package/dist/gateway/startup-auth.js +126 -0
  444. package/dist/gateway/test-helpers.agent-results.js +15 -0
  445. package/dist/gateway/test-helpers.mocks.js +37 -14
  446. package/dist/gateway/test-helpers.openai-mock.js +14 -7
  447. package/dist/gateway/test-helpers.server.js +161 -77
  448. package/dist/gateway/tools-invoke-http.js +21 -10
  449. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
  450. package/dist/hooks/bundled/command-logger/handler.js +7 -2
  451. package/dist/hooks/bundled/session-memory/handler.js +170 -38
  452. package/dist/hooks/frontmatter.js +6 -6
  453. package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
  454. package/dist/hooks/gmail-watcher.js +11 -6
  455. package/dist/hooks/internal-hooks.js +11 -1
  456. package/dist/hooks/llm-slug-generator.js +4 -1
  457. package/dist/hooks/workspace.js +47 -17
  458. package/dist/imessage/accounts.js +9 -20
  459. package/dist/imessage/monitor/inbound-processing.js +2 -1
  460. package/dist/infra/archive-path.js +49 -0
  461. package/dist/infra/archive.js +174 -73
  462. package/dist/infra/control-ui-assets.js +14 -6
  463. package/dist/infra/device-pairing.js +204 -144
  464. package/dist/infra/env.js +10 -5
  465. package/dist/infra/exec-approvals-allowlist.js +141 -70
  466. package/dist/infra/exec-approvals-analysis.js +78 -20
  467. package/dist/infra/exec-approvals.js +5 -17
  468. package/dist/infra/exec-safe-bin-policy.js +277 -0
  469. package/dist/infra/fixed-window-rate-limit.js +33 -0
  470. package/dist/infra/fs-safe.js +71 -39
  471. package/dist/infra/gateway-lock.js +6 -2
  472. package/dist/infra/git-root.js +61 -0
  473. package/dist/infra/heartbeat-active-hours.js +2 -2
  474. package/dist/infra/heartbeat-reason.js +40 -0
  475. package/dist/infra/heartbeat-runner.js +72 -32
  476. package/dist/infra/heartbeat-wake.js +6 -12
  477. package/dist/infra/host-env-security-policy.json +19 -0
  478. package/dist/infra/host-env-security.js +66 -0
  479. package/dist/infra/install-source-utils.js +91 -7
  480. package/dist/infra/net/ssrf.js +131 -38
  481. package/dist/infra/node-pairing.js +50 -105
  482. package/dist/infra/npm-integrity.js +45 -0
  483. package/dist/infra/npm-pack-install.js +40 -0
  484. package/dist/infra/outbound/bound-delivery-router.js +88 -0
  485. package/dist/infra/outbound/channel-adapters.js +20 -7
  486. package/dist/infra/outbound/channel-selection.js +12 -6
  487. package/dist/infra/outbound/envelope.js +1 -1
  488. package/dist/infra/outbound/format.js +12 -6
  489. package/dist/infra/outbound/message-action-runner.js +107 -327
  490. package/dist/infra/outbound/message.js +59 -36
  491. package/dist/infra/outbound/outbound-policy.js +52 -25
  492. package/dist/infra/outbound/outbound-send-service.js +58 -71
  493. package/dist/infra/outbound/payloads.js +14 -7
  494. package/dist/infra/outbound/session-binding-service.js +123 -0
  495. package/dist/infra/pairing-files.js +10 -0
  496. package/dist/infra/path-guards.js +25 -0
  497. package/dist/infra/plain-object.js +9 -0
  498. package/dist/infra/provider-usage.fetch.codex.js +7 -15
  499. package/dist/infra/provider-usage.fetch.gemini.js +14 -11
  500. package/dist/infra/provider-usage.fetch.shared.js +30 -1
  501. package/dist/infra/provider-usage.fetch.zai.js +10 -9
  502. package/dist/infra/push-apns.js +365 -0
  503. package/dist/infra/restart-sentinel.js +16 -1
  504. package/dist/infra/restart.js +229 -26
  505. package/dist/infra/retry-policy.js +4 -2
  506. package/dist/infra/retry.js +9 -5
  507. package/dist/infra/scp-host.js +54 -0
  508. package/dist/infra/session-cost-usage.js +107 -59
  509. package/dist/infra/session-maintenance-warning.js +3 -1
  510. package/dist/infra/shell-env.js +98 -34
  511. package/dist/infra/ssh-config.js +12 -6
  512. package/dist/infra/system-run-command.js +49 -4
  513. package/dist/infra/update-channels.js +10 -5
  514. package/dist/infra/update-startup.js +86 -9
  515. package/dist/line/accounts.js +5 -7
  516. package/dist/line/bot-access.js +8 -20
  517. package/dist/line/bot-handlers.js +3 -1
  518. package/dist/link-understanding/detect.js +15 -7
  519. package/dist/media/constants.js +15 -6
  520. package/dist/media/image-ops.js +7 -0
  521. package/dist/media/inbound-path-policy.js +114 -0
  522. package/dist/media/input-files.js +16 -0
  523. package/dist/media/local-roots.js +3 -2
  524. package/dist/media-understanding/apply.js +4 -1
  525. package/dist/media-understanding/concurrency.js +8 -20
  526. package/dist/memory/backend-config.js +45 -6
  527. package/dist/memory/embeddings.js +10 -4
  528. package/dist/memory/fs-utils.js +23 -0
  529. package/dist/memory/manager-search.js +12 -6
  530. package/dist/memory/manager-sync-ops.js +12 -2
  531. package/dist/memory/qmd-manager.js +466 -53
  532. package/dist/memory/query-expansion.js +167 -3
  533. package/dist/memory/status-format.js +10 -5
  534. package/dist/memory/sync-memory-files.js +1 -1
  535. package/dist/memory/test-manager.js +8 -0
  536. package/dist/node-host/invoke-system-run.js +281 -0
  537. package/dist/node-host/invoke.js +55 -337
  538. package/dist/pairing/pairing-store.js +22 -0
  539. package/dist/plugin-sdk/allow-from.js +1 -1
  540. package/dist/plugin-sdk/command-auth.js +3 -1
  541. package/dist/plugin-sdk/index.js +6 -3
  542. package/dist/plugin-sdk/temp-path.js +47 -0
  543. package/dist/plugin-sdk/webhook-targets.js +32 -0
  544. package/dist/plugins/bundled-dir.js +9 -6
  545. package/dist/plugins/discovery.js +217 -23
  546. package/dist/plugins/hook-runner-global.js +16 -0
  547. package/dist/plugins/hooks.js +50 -0
  548. package/dist/plugins/install.js +28 -16
  549. package/dist/plugins/loader.js +192 -26
  550. package/dist/plugins/logger.js +8 -0
  551. package/dist/plugins/manifest-registry.js +3 -0
  552. package/dist/plugins/path-safety.js +34 -0
  553. package/dist/plugins/registry.js +5 -2
  554. package/dist/plugins/runtime/index.js +271 -206
  555. package/dist/plugins/runtime.js +3 -17
  556. package/dist/plugins/update.js +78 -12
  557. package/dist/process/spawn-utils.js +14 -7
  558. package/dist/providers/github-copilot-models.js +4 -1
  559. package/dist/providers/github-copilot-token.js +11 -6
  560. package/dist/providers/qwen-portal-oauth.js +14 -6
  561. package/dist/routing/account-id.js +30 -0
  562. package/dist/routing/resolve-route.js +3 -7
  563. package/dist/routing/session-key.js +2 -16
  564. package/dist/security/audit-channel.js +100 -20
  565. package/dist/security/audit-extra.async.js +505 -179
  566. package/dist/security/audit-extra.js +12 -2
  567. package/dist/security/audit-extra.sync.js +421 -35
  568. package/dist/security/audit-fs.js +31 -13
  569. package/dist/security/audit.js +180 -370
  570. package/dist/security/dm-policy-shared.js +68 -0
  571. package/dist/security/external-content.js +46 -14
  572. package/dist/security/fix.js +49 -85
  573. package/dist/security/scan-paths.js +20 -0
  574. package/dist/security/secret-equal.js +3 -7
  575. package/dist/security/windows-acl.js +30 -15
  576. package/dist/shared/entry-status.js +6 -0
  577. package/dist/shared/frontmatter.js +5 -5
  578. package/dist/shared/node-list-parse.js +13 -0
  579. package/dist/shared/node-match.js +11 -4
  580. package/dist/shared/operator-scope-compat.js +42 -0
  581. package/dist/shared/text-chunking.js +29 -0
  582. package/dist/signal/accounts.js +7 -20
  583. package/dist/signal/monitor/event-handler.js +3 -1
  584. package/dist/slack/accounts.js +6 -19
  585. package/dist/slack/actions.js +11 -3
  586. package/dist/slack/blocks.test-helpers.js +31 -0
  587. package/dist/slack/monitor/auth.js +1 -1
  588. package/dist/slack/monitor/message-handler/dispatch.js +50 -29
  589. package/dist/slack/monitor/mrkdwn.js +8 -0
  590. package/dist/slack/monitor/replies.js +15 -7
  591. package/dist/slack/monitor/slash.js +22 -13
  592. package/dist/slack/resolve-channels.js +10 -5
  593. package/dist/slack/send.js +102 -12
  594. package/dist/slack/stream-mode.js +10 -0
  595. package/dist/slack/streaming.js +4 -2
  596. package/dist/telegram/accounts.js +19 -14
  597. package/dist/telegram/bot/helpers.js +3 -5
  598. package/dist/telegram/bot-access.js +35 -36
  599. package/dist/telegram/bot-handlers.js +120 -148
  600. package/dist/telegram/bot-message-context.js +68 -9
  601. package/dist/telegram/bot-message-dispatch.js +477 -210
  602. package/dist/telegram/bot-native-commands.js +16 -0
  603. package/dist/telegram/draft-stream.js +44 -8
  604. package/dist/telegram/inline-buttons.js +5 -15
  605. package/dist/telegram/monitor.js +11 -7
  606. package/dist/telegram/network-config.js +19 -7
  607. package/dist/telegram/reasoning-lane-coordinator.js +128 -0
  608. package/dist/telegram/send.js +3 -2
  609. package/dist/telegram/sent-message-cache.js +5 -6
  610. package/dist/telegram/status-reaction-variants.js +208 -0
  611. package/dist/telegram/sticker-cache.js +11 -9
  612. package/dist/terminal/prompt-select-styled.js +9 -0
  613. package/dist/terminal/theme.js +12 -12
  614. package/dist/test-utils/command-runner.js +6 -0
  615. package/dist/test-utils/internal-hook-event-payload.js +10 -0
  616. package/dist/test-utils/model-auth-mock.js +12 -0
  617. package/dist/test-utils/provider-usage-fetch.js +14 -0
  618. package/dist/test-utils/temp-home.js +33 -0
  619. package/dist/tts/tts.js +80 -567
  620. package/dist/tui/components/chat-log.js +50 -8
  621. package/dist/tui/theme/theme.js +10 -12
  622. package/dist/tui/tui-command-handlers.js +36 -27
  623. package/dist/tui/tui-event-handlers.js +122 -32
  624. package/dist/tui/tui-local-shell.js +16 -6
  625. package/dist/tui/tui.js +236 -48
  626. package/dist/utils/account-id.js +2 -4
  627. package/dist/utils/boolean.js +10 -5
  628. package/dist/utils/directive-tags.js +11 -0
  629. package/dist/utils/mask-api-key.js +10 -0
  630. package/dist/utils/queue-helpers.js +67 -12
  631. package/dist/utils/run-with-concurrency.js +39 -0
  632. package/dist/web/auto-reply/deliver-reply.js +8 -4
  633. package/dist/web/auto-reply/mentions.js +10 -5
  634. package/dist/web/auto-reply/monitor/group-members.js +14 -7
  635. package/dist/web/auto-reply/monitor/process-message.js +45 -24
  636. package/dist/web/inbound/access-control.js +5 -2
  637. package/dist/web/login-qr.js +12 -6
  638. package/dist/web/media.js +126 -15
  639. package/docs/tools/slash-commands.md +5 -1
  640. package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
  641. package/extensions/bluebubbles/src/monitor.ts +208 -1950
  642. package/extensions/feishu/src/external-keys.ts +19 -0
  643. package/extensions/lobster/src/windows-spawn.ts +193 -0
  644. package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
  645. package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
  646. package/package.json +1 -1
@@ -1,24 +1,33 @@
1
- import { Button, ChannelType, Command, Row, } from "@buape/carbon";
1
+ import { Button, ChannelType, Command, Container, Row, StringSelectMenu, TextDisplay, } from "@buape/carbon";
2
2
  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
6
  import { finalizeInboundContext } from "../../auto-reply/reply/inbound-context.js";
7
+ import { resolveStoredModelOverride } from "../../auto-reply/reply/model-selection.js";
7
8
  import { dispatchReplyWithDispatcher } from "../../auto-reply/reply/provider-dispatcher.js";
8
9
  import { resolveCommandAuthorizedFromAuthorizers } from "../../channels/command-gating.js";
9
10
  import { createReplyPrefixOptions } from "../../channels/reply-prefix.js";
11
+ import { loadSessionStore, resolveStorePath } from "../../config/sessions.js";
12
+ import { logVerbose } from "../../globals.js";
13
+ import { createSubsystemLogger } from "../../logging/subsystem.js";
10
14
  import { getAgentScopedMediaLocalRoots } from "../../media/local-roots.js";
11
15
  import { buildPairingReply } from "../../pairing/pairing-messages.js";
12
16
  import { readChannelAllowFromStore, upsertChannelPairingRequest, } from "../../pairing/pairing-store.js";
13
17
  import { resolveAgentRoute } from "../../routing/resolve-route.js";
18
+ import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
14
19
  import { buildUntrustedChannelMetadata } from "../../security/channel-metadata.js";
15
20
  import { chunkItems } from "../../utils/chunk-items.js";
21
+ import { withTimeout } from "../../utils/with-timeout.js";
16
22
  import { loadWebMedia } from "../../web/media.js";
17
23
  import { chunkDiscordTextWithMode } from "../chunk.js";
18
24
  import { allowListMatches, isDiscordGroupAllowedByPolicy, normalizeDiscordAllowList, normalizeDiscordSlug, resolveDiscordChannelConfigWithFallback, resolveDiscordGuildEntry, resolveDiscordMemberAccessState, resolveDiscordOwnerAllowFrom, } from "./allow-list.js";
19
25
  import { resolveDiscordChannelInfo } from "./message-utils.js";
26
+ import { readDiscordModelPickerRecentModels, recordDiscordModelPickerRecentModel, } from "./model-picker-preferences.js";
27
+ import { DISCORD_MODEL_PICKER_CUSTOM_ID_KEY, loadDiscordModelPickerData, parseDiscordModelPickerData, renderDiscordModelPickerModelsView, renderDiscordModelPickerProvidersView, renderDiscordModelPickerRecentsView, toDiscordModelPickerMessagePayload, } from "./model-picker.js";
20
28
  import { resolveDiscordSenderIdentity } from "./sender-identity.js";
21
29
  import { resolveDiscordThreadParentInfo } from "./threading.js";
30
+ const log = createSubsystemLogger("discord/native-command");
22
31
  function buildDiscordCommandOptions(params) {
23
32
  const { command, cfg } = params;
24
33
  const args = command.args;
@@ -132,7 +141,7 @@ async function safeDiscordInteractionCall(label, fn) {
132
141
  }
133
142
  catch (error) {
134
143
  if (isDiscordUnknownInteraction(error)) {
135
- console.warn(`discord: ${label} skipped (interaction expired)`);
144
+ logVerbose(`discord: ${label} skipped (interaction expired)`);
136
145
  return null;
137
146
  }
138
147
  throw error;
@@ -165,6 +174,486 @@ function parseDiscordCommandArgData(data) {
165
174
  userId: decodeDiscordCommandArgValue(rawUser),
166
175
  };
167
176
  }
177
+ function resolveDiscordModelPickerCommandContext(command) {
178
+ const normalized = (command.nativeName ?? command.key).trim().toLowerCase();
179
+ if (normalized === "model" || normalized === "models") {
180
+ return normalized;
181
+ }
182
+ return null;
183
+ }
184
+ function resolveCommandArgStringValue(args, key) {
185
+ const value = args?.values?.[key];
186
+ if (typeof value !== "string") {
187
+ return "";
188
+ }
189
+ return value.trim();
190
+ }
191
+ function shouldOpenDiscordModelPickerFromCommand(params) {
192
+ const context = resolveDiscordModelPickerCommandContext(params.command);
193
+ if (!context) {
194
+ return null;
195
+ }
196
+ const serializedArgs = serializeCommandArgs(params.command, params.commandArgs)?.trim() ?? "";
197
+ if (context === "model") {
198
+ const modelValue = resolveCommandArgStringValue(params.commandArgs, "model");
199
+ return !modelValue && !serializedArgs ? context : null;
200
+ }
201
+ return serializedArgs ? null : context;
202
+ }
203
+ function buildDiscordModelPickerCurrentModel(defaultProvider, defaultModel) {
204
+ return `${defaultProvider}/${defaultModel}`;
205
+ }
206
+ function buildDiscordModelPickerAllowedModelRefs(data) {
207
+ const out = new Set();
208
+ for (const provider of data.providers) {
209
+ const models = data.byProvider.get(provider);
210
+ if (!models) {
211
+ continue;
212
+ }
213
+ for (const model of models) {
214
+ out.add(`${provider}/${model}`);
215
+ }
216
+ }
217
+ return out;
218
+ }
219
+ function resolveDiscordModelPickerPreferenceScope(params) {
220
+ return {
221
+ accountId: params.accountId,
222
+ guildId: params.interaction.guild?.id ?? undefined,
223
+ userId: params.userId,
224
+ };
225
+ }
226
+ function buildDiscordModelPickerNoticePayload(message) {
227
+ return {
228
+ components: [new Container([new TextDisplay(message)])],
229
+ };
230
+ }
231
+ async function resolveDiscordModelPickerRoute(params) {
232
+ const { interaction, cfg, accountId } = params;
233
+ const channel = interaction.channel;
234
+ const channelType = channel?.type;
235
+ const isDirectMessage = channelType === ChannelType.DM;
236
+ const isGroupDm = channelType === ChannelType.GroupDM;
237
+ const isThreadChannel = channelType === ChannelType.PublicThread ||
238
+ channelType === ChannelType.PrivateThread ||
239
+ channelType === ChannelType.AnnouncementThread;
240
+ const rawChannelId = channel?.id ?? "unknown";
241
+ const memberRoleIds = Array.isArray(interaction.rawData.member?.roles)
242
+ ? interaction.rawData.member.roles.map((roleId) => String(roleId))
243
+ : [];
244
+ let threadParentId;
245
+ if (interaction.guild && channel && isThreadChannel && rawChannelId) {
246
+ const channelInfo = await resolveDiscordChannelInfo(interaction.client, rawChannelId);
247
+ const parentInfo = await resolveDiscordThreadParentInfo({
248
+ client: interaction.client,
249
+ threadChannel: {
250
+ id: rawChannelId,
251
+ name: "name" in channel ? channel.name : undefined,
252
+ parentId: "parentId" in channel ? (channel.parentId ?? undefined) : undefined,
253
+ parent: undefined,
254
+ },
255
+ channelInfo,
256
+ });
257
+ threadParentId = parentInfo.id;
258
+ }
259
+ const route = resolveAgentRoute({
260
+ cfg,
261
+ channel: "discord",
262
+ accountId,
263
+ guildId: interaction.guild?.id ?? undefined,
264
+ memberRoleIds,
265
+ peer: {
266
+ kind: isDirectMessage ? "direct" : isGroupDm ? "group" : "channel",
267
+ id: isDirectMessage ? (interaction.user?.id ?? rawChannelId) : rawChannelId,
268
+ },
269
+ parentPeer: threadParentId ? { kind: "channel", id: threadParentId } : undefined,
270
+ });
271
+ const threadBinding = isThreadChannel
272
+ ? params.threadBindings.getByThreadId(rawChannelId)
273
+ : undefined;
274
+ const boundSessionKey = threadBinding?.targetSessionKey?.trim();
275
+ const boundAgentId = boundSessionKey ? resolveAgentIdFromSessionKey(boundSessionKey) : undefined;
276
+ return boundSessionKey
277
+ ? {
278
+ ...route,
279
+ sessionKey: boundSessionKey,
280
+ agentId: boundAgentId ?? route.agentId,
281
+ }
282
+ : route;
283
+ }
284
+ function resolveDiscordModelPickerCurrentModel(params) {
285
+ const fallback = buildDiscordModelPickerCurrentModel(params.data.resolvedDefault.provider, params.data.resolvedDefault.model);
286
+ try {
287
+ const storePath = resolveStorePath(params.cfg.session?.store, {
288
+ agentId: params.route.agentId,
289
+ });
290
+ const sessionStore = loadSessionStore(storePath, { skipCache: true });
291
+ const sessionEntry = sessionStore[params.route.sessionKey];
292
+ const override = resolveStoredModelOverride({
293
+ sessionEntry,
294
+ sessionStore,
295
+ sessionKey: params.route.sessionKey,
296
+ });
297
+ if (!override?.model) {
298
+ return fallback;
299
+ }
300
+ const provider = (override.provider || params.data.resolvedDefault.provider).trim();
301
+ if (!provider) {
302
+ return fallback;
303
+ }
304
+ return `${provider}/${override.model}`;
305
+ }
306
+ catch {
307
+ return fallback;
308
+ }
309
+ }
310
+ async function replyWithDiscordModelPickerProviders(params) {
311
+ const data = await loadDiscordModelPickerData(params.cfg);
312
+ const route = await resolveDiscordModelPickerRoute({
313
+ interaction: params.interaction,
314
+ cfg: params.cfg,
315
+ accountId: params.accountId,
316
+ threadBindings: params.threadBindings,
317
+ });
318
+ const currentModel = resolveDiscordModelPickerCurrentModel({
319
+ cfg: params.cfg,
320
+ route,
321
+ data,
322
+ });
323
+ const quickModels = await readDiscordModelPickerRecentModels({
324
+ scope: resolveDiscordModelPickerPreferenceScope({
325
+ interaction: params.interaction,
326
+ accountId: params.accountId,
327
+ userId: params.userId,
328
+ }),
329
+ allowedModelRefs: buildDiscordModelPickerAllowedModelRefs(data),
330
+ limit: 5,
331
+ });
332
+ const rendered = renderDiscordModelPickerModelsView({
333
+ command: params.command,
334
+ userId: params.userId,
335
+ data,
336
+ provider: splitDiscordModelRef(currentModel ?? "")?.provider ?? data.resolvedDefault.provider,
337
+ page: 1,
338
+ providerPage: 1,
339
+ currentModel,
340
+ quickModels,
341
+ });
342
+ const payload = {
343
+ ...toDiscordModelPickerMessagePayload(rendered),
344
+ ephemeral: true,
345
+ };
346
+ await safeDiscordInteractionCall("model picker reply", async () => {
347
+ if (params.preferFollowUp) {
348
+ await params.interaction.followUp(payload);
349
+ return;
350
+ }
351
+ await params.interaction.reply(payload);
352
+ });
353
+ }
354
+ function resolveModelPickerSelectionValue(interaction) {
355
+ const rawValues = interaction.values;
356
+ if (!Array.isArray(rawValues) || rawValues.length === 0) {
357
+ return null;
358
+ }
359
+ const first = rawValues[0];
360
+ if (typeof first !== "string") {
361
+ return null;
362
+ }
363
+ const trimmed = first.trim();
364
+ return trimmed || null;
365
+ }
366
+ function buildDiscordModelPickerSelectionCommand(params) {
367
+ const commandDefinition = findCommandByNativeName("model", "discord") ??
368
+ listChatCommands().find((entry) => entry.key === "model");
369
+ if (!commandDefinition) {
370
+ return null;
371
+ }
372
+ const commandArgs = {
373
+ values: {
374
+ model: params.modelRef,
375
+ },
376
+ raw: params.modelRef,
377
+ };
378
+ return {
379
+ command: commandDefinition,
380
+ args: commandArgs,
381
+ prompt: buildCommandTextFromArgs(commandDefinition, commandArgs),
382
+ };
383
+ }
384
+ function listDiscordModelPickerProviderModels(data, provider) {
385
+ const modelSet = data.byProvider.get(provider);
386
+ if (!modelSet) {
387
+ return [];
388
+ }
389
+ return [...modelSet].toSorted();
390
+ }
391
+ function resolveDiscordModelPickerModelIndex(params) {
392
+ const models = listDiscordModelPickerProviderModels(params.data, params.provider);
393
+ if (!models.length) {
394
+ return null;
395
+ }
396
+ const index = models.indexOf(params.model);
397
+ if (index < 0) {
398
+ return null;
399
+ }
400
+ return index + 1;
401
+ }
402
+ function resolveDiscordModelPickerModelByIndex(params) {
403
+ if (!params.modelIndex || params.modelIndex < 1) {
404
+ return null;
405
+ }
406
+ const models = listDiscordModelPickerProviderModels(params.data, params.provider);
407
+ if (!models.length) {
408
+ return null;
409
+ }
410
+ return models[params.modelIndex - 1] ?? null;
411
+ }
412
+ function splitDiscordModelRef(modelRef) {
413
+ const trimmed = modelRef.trim();
414
+ const slashIndex = trimmed.indexOf("/");
415
+ if (slashIndex <= 0 || slashIndex >= trimmed.length - 1) {
416
+ return null;
417
+ }
418
+ const provider = trimmed.slice(0, slashIndex).trim();
419
+ const model = trimmed.slice(slashIndex + 1).trim();
420
+ if (!provider || !model) {
421
+ return null;
422
+ }
423
+ return { provider, model };
424
+ }
425
+ async function handleDiscordModelPickerInteraction(interaction, data, ctx) {
426
+ const parsed = parseDiscordModelPickerData(data);
427
+ if (!parsed) {
428
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(buildDiscordModelPickerNoticePayload("Sorry, that model picker interaction is no longer available.")));
429
+ return;
430
+ }
431
+ if (interaction.user?.id && interaction.user.id !== parsed.userId) {
432
+ await safeDiscordInteractionCall("model picker ack", () => interaction.acknowledge());
433
+ return;
434
+ }
435
+ const pickerData = await loadDiscordModelPickerData(ctx.cfg);
436
+ const route = await resolveDiscordModelPickerRoute({
437
+ interaction,
438
+ cfg: ctx.cfg,
439
+ accountId: ctx.accountId,
440
+ threadBindings: ctx.threadBindings,
441
+ });
442
+ const currentModelRef = resolveDiscordModelPickerCurrentModel({
443
+ cfg: ctx.cfg,
444
+ route,
445
+ data: pickerData,
446
+ });
447
+ const allowedModelRefs = buildDiscordModelPickerAllowedModelRefs(pickerData);
448
+ const preferenceScope = resolveDiscordModelPickerPreferenceScope({
449
+ interaction,
450
+ accountId: ctx.accountId,
451
+ userId: parsed.userId,
452
+ });
453
+ const quickModels = await readDiscordModelPickerRecentModels({
454
+ scope: preferenceScope,
455
+ allowedModelRefs,
456
+ limit: 5,
457
+ });
458
+ if (parsed.action === "recents") {
459
+ const rendered = renderDiscordModelPickerRecentsView({
460
+ command: parsed.command,
461
+ userId: parsed.userId,
462
+ data: pickerData,
463
+ quickModels,
464
+ currentModel: currentModelRef,
465
+ provider: parsed.provider,
466
+ page: parsed.page,
467
+ providerPage: parsed.providerPage,
468
+ });
469
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(toDiscordModelPickerMessagePayload(rendered)));
470
+ return;
471
+ }
472
+ if (parsed.action === "back" && parsed.view === "providers") {
473
+ const rendered = renderDiscordModelPickerProvidersView({
474
+ command: parsed.command,
475
+ userId: parsed.userId,
476
+ data: pickerData,
477
+ page: parsed.page,
478
+ currentModel: currentModelRef,
479
+ });
480
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(toDiscordModelPickerMessagePayload(rendered)));
481
+ return;
482
+ }
483
+ if (parsed.action === "back" && parsed.view === "models") {
484
+ const provider = parsed.provider ??
485
+ splitDiscordModelRef(currentModelRef ?? "")?.provider ??
486
+ pickerData.resolvedDefault.provider;
487
+ const rendered = renderDiscordModelPickerModelsView({
488
+ command: parsed.command,
489
+ userId: parsed.userId,
490
+ data: pickerData,
491
+ provider,
492
+ page: parsed.page ?? 1,
493
+ providerPage: parsed.providerPage ?? 1,
494
+ currentModel: currentModelRef,
495
+ quickModels,
496
+ });
497
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(toDiscordModelPickerMessagePayload(rendered)));
498
+ return;
499
+ }
500
+ if (parsed.action === "provider") {
501
+ const selectedProvider = resolveModelPickerSelectionValue(interaction) ?? parsed.provider;
502
+ if (!selectedProvider || !pickerData.byProvider.has(selectedProvider)) {
503
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(buildDiscordModelPickerNoticePayload("Sorry, that provider isn't available anymore.")));
504
+ return;
505
+ }
506
+ const rendered = renderDiscordModelPickerModelsView({
507
+ command: parsed.command,
508
+ userId: parsed.userId,
509
+ data: pickerData,
510
+ provider: selectedProvider,
511
+ page: 1,
512
+ providerPage: parsed.providerPage ?? parsed.page,
513
+ currentModel: currentModelRef,
514
+ quickModels,
515
+ });
516
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(toDiscordModelPickerMessagePayload(rendered)));
517
+ return;
518
+ }
519
+ if (parsed.action === "model") {
520
+ const selectedModel = resolveModelPickerSelectionValue(interaction);
521
+ const provider = parsed.provider;
522
+ if (!provider || !selectedModel) {
523
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(buildDiscordModelPickerNoticePayload("Sorry, I couldn't read that model selection.")));
524
+ return;
525
+ }
526
+ const modelIndex = resolveDiscordModelPickerModelIndex({
527
+ data: pickerData,
528
+ provider,
529
+ model: selectedModel,
530
+ });
531
+ if (!modelIndex) {
532
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(buildDiscordModelPickerNoticePayload("Sorry, that model isn't available anymore.")));
533
+ return;
534
+ }
535
+ const modelRef = `${provider}/${selectedModel}`;
536
+ const rendered = renderDiscordModelPickerModelsView({
537
+ command: parsed.command,
538
+ userId: parsed.userId,
539
+ data: pickerData,
540
+ provider,
541
+ page: parsed.page,
542
+ providerPage: parsed.providerPage ?? 1,
543
+ currentModel: currentModelRef,
544
+ pendingModel: modelRef,
545
+ pendingModelIndex: modelIndex,
546
+ quickModels,
547
+ });
548
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(toDiscordModelPickerMessagePayload(rendered)));
549
+ return;
550
+ }
551
+ if (parsed.action === "submit" || parsed.action === "reset" || parsed.action === "quick") {
552
+ let modelRef = null;
553
+ if (parsed.action === "reset") {
554
+ modelRef = `${pickerData.resolvedDefault.provider}/${pickerData.resolvedDefault.model}`;
555
+ }
556
+ else if (parsed.action === "quick") {
557
+ const slot = parsed.recentSlot ?? 0;
558
+ modelRef = slot >= 1 ? (quickModels[slot - 1] ?? null) : null;
559
+ }
560
+ else if (parsed.view === "recents") {
561
+ const defaultModelRef = `${pickerData.resolvedDefault.provider}/${pickerData.resolvedDefault.model}`;
562
+ const dedupedRecents = quickModels.filter((ref) => ref !== defaultModelRef);
563
+ const slot = parsed.recentSlot ?? 0;
564
+ if (slot === 1) {
565
+ modelRef = defaultModelRef;
566
+ }
567
+ else if (slot >= 2) {
568
+ modelRef = dedupedRecents[slot - 2] ?? null;
569
+ }
570
+ }
571
+ else {
572
+ const provider = parsed.provider;
573
+ const selectedModel = resolveDiscordModelPickerModelByIndex({
574
+ data: pickerData,
575
+ provider: provider ?? "",
576
+ modelIndex: parsed.modelIndex,
577
+ });
578
+ modelRef = provider && selectedModel ? `${provider}/${selectedModel}` : null;
579
+ }
580
+ const parsedModelRef = modelRef ? splitDiscordModelRef(modelRef) : null;
581
+ if (!parsedModelRef ||
582
+ !pickerData.byProvider.get(parsedModelRef.provider)?.has(parsedModelRef.model)) {
583
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(buildDiscordModelPickerNoticePayload("That selection expired. Please choose a model again.")));
584
+ return;
585
+ }
586
+ const resolvedModelRef = `${parsedModelRef.provider}/${parsedModelRef.model}`;
587
+ const selectionCommand = buildDiscordModelPickerSelectionCommand({
588
+ modelRef: resolvedModelRef,
589
+ });
590
+ if (!selectionCommand) {
591
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(buildDiscordModelPickerNoticePayload("Sorry, /model is unavailable right now.")));
592
+ return;
593
+ }
594
+ const updateResult = await safeDiscordInteractionCall("model picker update", () => interaction.update(buildDiscordModelPickerNoticePayload(`Applying model change to ${resolvedModelRef}...`)));
595
+ if (updateResult === null) {
596
+ return;
597
+ }
598
+ try {
599
+ await withTimeout(dispatchDiscordCommandInteraction({
600
+ interaction,
601
+ prompt: selectionCommand.prompt,
602
+ command: selectionCommand.command,
603
+ commandArgs: selectionCommand.args,
604
+ cfg: ctx.cfg,
605
+ discordConfig: ctx.discordConfig,
606
+ accountId: ctx.accountId,
607
+ sessionPrefix: ctx.sessionPrefix,
608
+ preferFollowUp: true,
609
+ threadBindings: ctx.threadBindings,
610
+ suppressReplies: true,
611
+ }), 12000);
612
+ }
613
+ catch (error) {
614
+ if (error instanceof Error && error.message === "timeout") {
615
+ await safeDiscordInteractionCall("model picker follow-up", () => interaction.followUp({
616
+ ...buildDiscordModelPickerNoticePayload(`⏳ Model change to ${resolvedModelRef} is still processing. Check /status in a few seconds.`),
617
+ ephemeral: true,
618
+ }));
619
+ return;
620
+ }
621
+ await safeDiscordInteractionCall("model picker follow-up", () => interaction.followUp({
622
+ ...buildDiscordModelPickerNoticePayload(`❌ Failed to apply ${resolvedModelRef}. Try /model ${resolvedModelRef} directly.`),
623
+ ephemeral: true,
624
+ }));
625
+ return;
626
+ }
627
+ const effectiveModelRef = resolveDiscordModelPickerCurrentModel({
628
+ cfg: ctx.cfg,
629
+ route,
630
+ data: pickerData,
631
+ });
632
+ const persisted = effectiveModelRef === resolvedModelRef;
633
+ if (!persisted) {
634
+ logVerbose(`discord: model picker override mismatch — expected ${resolvedModelRef} but read ${effectiveModelRef} from session key ${route.sessionKey}`);
635
+ }
636
+ if (persisted) {
637
+ await recordDiscordModelPickerRecentModel({
638
+ scope: preferenceScope,
639
+ modelRef: resolvedModelRef,
640
+ limit: 5,
641
+ }).catch(() => undefined);
642
+ }
643
+ await safeDiscordInteractionCall("model picker follow-up", () => interaction.followUp({
644
+ ...buildDiscordModelPickerNoticePayload(persisted
645
+ ? `✅ Model set to ${resolvedModelRef}.`
646
+ : `⚠️ Tried to set ${resolvedModelRef}, but current model is ${effectiveModelRef}.`),
647
+ ephemeral: true,
648
+ }));
649
+ return;
650
+ }
651
+ if (parsed.action === "cancel") {
652
+ const displayModel = currentModelRef ?? "default";
653
+ await safeDiscordInteractionCall("model picker update", () => interaction.update(buildDiscordModelPickerNoticePayload(`ℹ️ Model kept as ${displayModel}.`)));
654
+ return;
655
+ }
656
+ }
168
657
  async function handleDiscordCommandArgInteraction(interaction, data, ctx) {
169
658
  const parsed = parseDiscordCommandArgData(data);
170
659
  if (!parsed) {
@@ -187,11 +676,11 @@ async function handleDiscordCommandArgInteraction(interaction, data, ctx) {
187
676
  }));
188
677
  return;
189
678
  }
190
- const updated = await safeDiscordInteractionCall("command arg update", () => interaction.update({
679
+ const argUpdateResult = await safeDiscordInteractionCall("command arg update", () => interaction.update({
191
680
  content: `✅ Selected ${parsed.value}.`,
192
681
  components: [],
193
682
  }));
194
- if (!updated) {
683
+ if (argUpdateResult === null) {
195
684
  return;
196
685
  }
197
686
  const commandArgs = createCommandArgsWithValue({
@@ -213,6 +702,7 @@ async function handleDiscordCommandArgInteraction(interaction, data, ctx) {
213
702
  accountId: ctx.accountId,
214
703
  sessionPrefix: ctx.sessionPrefix,
215
704
  preferFollowUp: true,
705
+ threadBindings: ctx.threadBindings,
216
706
  });
217
707
  }
218
708
  class DiscordCommandArgButton extends Button {
@@ -223,6 +713,7 @@ class DiscordCommandArgButton extends Button {
223
713
  discordConfig;
224
714
  accountId;
225
715
  sessionPrefix;
716
+ threadBindings;
226
717
  constructor(params) {
227
718
  super();
228
719
  this.label = params.label;
@@ -231,6 +722,7 @@ class DiscordCommandArgButton extends Button {
231
722
  this.discordConfig = params.discordConfig;
232
723
  this.accountId = params.accountId;
233
724
  this.sessionPrefix = params.sessionPrefix;
725
+ this.threadBindings = params.threadBindings;
234
726
  }
235
727
  async run(interaction, data) {
236
728
  await handleDiscordCommandArgInteraction(interaction, data, {
@@ -238,6 +730,7 @@ class DiscordCommandArgButton extends Button {
238
730
  discordConfig: this.discordConfig,
239
731
  accountId: this.accountId,
240
732
  sessionPrefix: this.sessionPrefix,
733
+ threadBindings: this.threadBindings,
241
734
  });
242
735
  }
243
736
  }
@@ -256,6 +749,36 @@ class DiscordCommandArgFallbackButton extends Button {
256
749
  export function createDiscordCommandArgFallbackButton(params) {
257
750
  return new DiscordCommandArgFallbackButton(params);
258
751
  }
752
+ class DiscordModelPickerFallbackButton extends Button {
753
+ label = DISCORD_MODEL_PICKER_CUSTOM_ID_KEY;
754
+ customId = `${DISCORD_MODEL_PICKER_CUSTOM_ID_KEY}:seed=btn`;
755
+ ctx;
756
+ constructor(ctx) {
757
+ super();
758
+ this.ctx = ctx;
759
+ }
760
+ async run(interaction, data) {
761
+ await handleDiscordModelPickerInteraction(interaction, data, this.ctx);
762
+ }
763
+ }
764
+ class DiscordModelPickerFallbackSelect extends StringSelectMenu {
765
+ customId = `${DISCORD_MODEL_PICKER_CUSTOM_ID_KEY}:seed=sel`;
766
+ options = [];
767
+ ctx;
768
+ constructor(ctx) {
769
+ super();
770
+ this.ctx = ctx;
771
+ }
772
+ async run(interaction, data) {
773
+ await handleDiscordModelPickerInteraction(interaction, data, this.ctx);
774
+ }
775
+ }
776
+ export function createDiscordModelPickerFallbackButton(params) {
777
+ return new DiscordModelPickerFallbackButton(params);
778
+ }
779
+ export function createDiscordModelPickerFallbackSelect(params) {
780
+ return new DiscordModelPickerFallbackSelect(params);
781
+ }
259
782
  function buildDiscordCommandArgMenu(params) {
260
783
  const { command, menu, interaction } = params;
261
784
  const commandLabel = command.nativeName ?? command.key;
@@ -273,6 +796,7 @@ function buildDiscordCommandArgMenu(params) {
273
796
  discordConfig: params.discordConfig,
274
797
  accountId: params.accountId,
275
798
  sessionPrefix: params.sessionPrefix,
799
+ threadBindings: params.threadBindings,
276
800
  }));
277
801
  return new Row(buttons);
278
802
  });
@@ -280,7 +804,7 @@ function buildDiscordCommandArgMenu(params) {
280
804
  return { content, components: rows };
281
805
  }
282
806
  export function createDiscordNativeCommand(params) {
283
- const { command, cfg, discordConfig, accountId, sessionPrefix, ephemeralDefault } = params;
807
+ const { command, cfg, discordConfig, accountId, sessionPrefix, ephemeralDefault, threadBindings, } = params;
284
808
  const commandDefinition = findCommandByNativeName(command.name, "discord") ??
285
809
  {
286
810
  key: command.name,
@@ -338,12 +862,13 @@ export function createDiscordNativeCommand(params) {
338
862
  accountId,
339
863
  sessionPrefix,
340
864
  preferFollowUp: false,
865
+ threadBindings,
341
866
  });
342
867
  }
343
868
  })();
344
869
  }
345
870
  async function dispatchDiscordCommandInteraction(params) {
346
- const { interaction, prompt, command, commandArgs, cfg, discordConfig, accountId, sessionPrefix, preferFollowUp, } = params;
871
+ const { interaction, prompt, command, commandArgs, cfg, discordConfig, accountId, sessionPrefix, preferFollowUp, threadBindings, suppressReplies, } = params;
347
872
  const respond = async (content, options) => {
348
873
  const payload = {
349
874
  content,
@@ -451,7 +976,7 @@ async function dispatchDiscordCommandInteraction(params) {
451
976
  return;
452
977
  }
453
978
  if (dmPolicy !== "open") {
454
- const storeAllowFrom = await readChannelAllowFromStore("discord").catch(() => []);
979
+ const storeAllowFrom = dmPolicy === "allowlist" ? [] : await readChannelAllowFromStore("discord").catch(() => []);
455
980
  const effectiveAllowFrom = [
456
981
  ...(discordConfig?.allowFrom ?? discordConfig?.dm?.allowFrom ?? []),
457
982
  ...storeAllowFrom,
@@ -532,6 +1057,7 @@ async function dispatchDiscordCommandInteraction(params) {
532
1057
  discordConfig,
533
1058
  accountId,
534
1059
  sessionPrefix,
1060
+ threadBindings,
535
1061
  });
536
1062
  if (preferFollowUp) {
537
1063
  await safeDiscordInteractionCall("interaction follow-up", () => interaction.followUp({
@@ -548,6 +1074,22 @@ async function dispatchDiscordCommandInteraction(params) {
548
1074
  }));
549
1075
  return;
550
1076
  }
1077
+ const pickerCommandContext = shouldOpenDiscordModelPickerFromCommand({
1078
+ command,
1079
+ commandArgs,
1080
+ });
1081
+ if (pickerCommandContext) {
1082
+ await replyWithDiscordModelPickerProviders({
1083
+ interaction,
1084
+ cfg,
1085
+ command: pickerCommandContext,
1086
+ userId: user.id,
1087
+ accountId,
1088
+ threadBindings,
1089
+ preferFollowUp,
1090
+ });
1091
+ return;
1092
+ }
551
1093
  const isGuild = Boolean(interaction.guild);
552
1094
  const channelId = rawChannelId || "unknown";
553
1095
  const interactionId = interaction.rawData.id;
@@ -563,6 +1105,16 @@ async function dispatchDiscordCommandInteraction(params) {
563
1105
  },
564
1106
  parentPeer: threadParentId ? { kind: "channel", id: threadParentId } : undefined,
565
1107
  });
1108
+ const threadBinding = isThreadChannel ? threadBindings.getByThreadId(rawChannelId) : undefined;
1109
+ const boundSessionKey = threadBinding?.targetSessionKey?.trim();
1110
+ const boundAgentId = boundSessionKey ? resolveAgentIdFromSessionKey(boundSessionKey) : undefined;
1111
+ const effectiveRoute = boundSessionKey
1112
+ ? {
1113
+ ...route,
1114
+ sessionKey: boundSessionKey,
1115
+ agentId: boundAgentId ?? route.agentId,
1116
+ }
1117
+ : route;
566
1118
  const conversationLabel = isDirectMessage ? (user.globalName ?? user.username) : channelId;
567
1119
  const ownerAllowFrom = resolveDiscordOwnerAllowFrom({
568
1120
  channelConfig,
@@ -581,9 +1133,9 @@ async function dispatchDiscordCommandInteraction(params) {
581
1133
  ? `discord:group:${channelId}`
582
1134
  : `discord:channel:${channelId}`,
583
1135
  To: `slash:${user.id}`,
584
- SessionKey: `agent:${route.agentId}:${sessionPrefix}:${user.id}`,
585
- CommandTargetSessionKey: route.sessionKey,
586
- AccountId: route.accountId,
1136
+ SessionKey: boundSessionKey ?? `agent:${effectiveRoute.agentId}:${sessionPrefix}:${user.id}`,
1137
+ CommandTargetSessionKey: boundSessionKey ?? effectiveRoute.sessionKey,
1138
+ AccountId: effectiveRoute.accountId,
587
1139
  ChatType: isDirectMessage ? "direct" : isGroupDm ? "group" : "channel",
588
1140
  ConversationLabel: conversationLabel,
589
1141
  GroupSubject: isGuild ? interaction.guild?.name : undefined,
@@ -613,6 +1165,7 @@ async function dispatchDiscordCommandInteraction(params) {
613
1165
  Surface: "discord",
614
1166
  WasMentioned: true,
615
1167
  MessageSid: interactionId,
1168
+ MessageThreadId: isThreadChannel ? channelId : undefined,
616
1169
  Timestamp: Date.now(),
617
1170
  CommandAuthorized: commandAuthorized,
618
1171
  CommandSource: "native",
@@ -624,19 +1177,22 @@ async function dispatchDiscordCommandInteraction(params) {
624
1177
  });
625
1178
  const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
626
1179
  cfg,
627
- agentId: route.agentId,
1180
+ agentId: effectiveRoute.agentId,
628
1181
  channel: "discord",
629
- accountId: route.accountId,
1182
+ accountId: effectiveRoute.accountId,
630
1183
  });
631
- const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
1184
+ const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, effectiveRoute.agentId);
632
1185
  let didReply = false;
633
1186
  await dispatchReplyWithDispatcher({
634
1187
  ctx: ctxPayload,
635
1188
  cfg,
636
1189
  dispatcherOptions: {
637
1190
  ...prefixOptions,
638
- humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
1191
+ humanDelay: resolveHumanDelayConfig(cfg, effectiveRoute.agentId),
639
1192
  deliver: async (payload) => {
1193
+ if (suppressReplies) {
1194
+ return;
1195
+ }
640
1196
  try {
641
1197
  await deliverDiscordInteractionReply({
642
1198
  interaction,
@@ -652,7 +1208,7 @@ async function dispatchDiscordCommandInteraction(params) {
652
1208
  }
653
1209
  catch (error) {
654
1210
  if (isDiscordUnknownInteraction(error)) {
655
- console.warn("discord: interaction reply skipped (interaction expired)");
1211
+ logVerbose("discord: interaction reply skipped (interaction expired)");
656
1212
  return;
657
1213
  }
658
1214
  throw error;
@@ -660,7 +1216,8 @@ async function dispatchDiscordCommandInteraction(params) {
660
1216
  didReply = true;
661
1217
  },
662
1218
  onError: (err, info) => {
663
- console.error(`discord slash ${info.kind} reply failed`, err);
1219
+ const message = err instanceof Error ? (err.stack ?? err.message) : String(err);
1220
+ log.error(`discord slash ${info.kind} reply failed: ${message}`);
664
1221
  },
665
1222
  },
666
1223
  replyOptions: {