@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,10 +1,23 @@
1
- import { Button } from "@buape/carbon";
1
+ import { Button, Row, Separator, TextDisplay, serializePayload, } from "@buape/carbon";
2
2
  import { ButtonStyle, Routes } from "discord-api-types/v10";
3
+ import { loadSessionStore, resolveStorePath } from "../../config/sessions.js";
4
+ import { buildGatewayConnectionDetails } from "../../gateway/call.js";
3
5
  import { GatewayClient } from "../../gateway/client.js";
4
- import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../../utils/message-channel.js";
5
- import { createDiscordClient } from "../send.shared.js";
6
6
  import { logDebug, logError } from "../../logger.js";
7
+ import { normalizeAccountId, resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
8
+ import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES, normalizeMessageChannel, } from "../../utils/message-channel.js";
9
+ import { createDiscordClient, stripUndefinedFields } from "../send.shared.js";
10
+ import { DiscordUiContainer } from "../ui.js";
7
11
  const EXEC_APPROVAL_KEY = "execapproval";
12
+ /** Extract Discord channel ID from a session key like "agent:main:discord:channel:123456789" */
13
+ export function extractDiscordChannelId(sessionKey) {
14
+ if (!sessionKey) {
15
+ return null;
16
+ }
17
+ // Session key format: agent:<id>:discord:channel:<channelId> or agent:<id>:discord:group:<channelId>
18
+ const match = sessionKey.match(/discord:(?:channel|group):(\d+)/);
19
+ return match ? match[1] : null;
20
+ }
8
21
  function encodeCustomIdValue(value) {
9
22
  return encodeURIComponent(value);
10
23
  }
@@ -20,13 +33,15 @@ export function buildExecApprovalCustomId(approvalId, action) {
20
33
  return [`${EXEC_APPROVAL_KEY}:id=${encodeCustomIdValue(approvalId)}`, `action=${action}`].join(";");
21
34
  }
22
35
  export function parseExecApprovalData(data) {
23
- if (!data || typeof data !== "object")
36
+ if (!data || typeof data !== "object") {
24
37
  return null;
38
+ }
25
39
  const coerce = (value) => typeof value === "string" || typeof value === "number" ? String(value) : "";
26
40
  const rawId = coerce(data.id);
27
41
  const rawAction = coerce(data.action);
28
- if (!rawId || !rawAction)
42
+ if (!rawId || !rawAction) {
29
43
  return null;
44
+ }
30
45
  const action = rawAction;
31
46
  if (action !== "allow-once" && action !== "allow-always" && action !== "deny") {
32
47
  return null;
@@ -36,88 +51,161 @@ export function parseExecApprovalData(data) {
36
51
  action,
37
52
  };
38
53
  }
39
- function formatExecApprovalEmbed(request) {
40
- const commandText = request.request.command;
41
- const commandPreview = commandText.length > 1000 ? `${commandText.slice(0, 1000)}...` : commandText;
42
- const expiresIn = Math.max(0, Math.round((request.expiresAtMs - Date.now()) / 1000));
43
- const fields = [
44
- {
45
- name: "Command",
46
- value: `\`\`\`\n${commandPreview}\n\`\`\``,
47
- inline: false,
48
- },
49
- ];
50
- if (request.request.cwd) {
51
- fields.push({
52
- name: "Working Directory",
53
- value: request.request.cwd,
54
- inline: true,
54
+ class ExecApprovalContainer extends DiscordUiContainer {
55
+ constructor(params) {
56
+ const components = [
57
+ new TextDisplay(`## ${params.title}`),
58
+ ];
59
+ if (params.description) {
60
+ components.push(new TextDisplay(params.description));
61
+ }
62
+ components.push(new Separator({ divider: true, spacing: "small" }));
63
+ components.push(new TextDisplay(`### Command\n\`\`\`\n${params.commandPreview}\n\`\`\``));
64
+ if (params.metadataLines?.length) {
65
+ components.push(new TextDisplay(params.metadataLines.join("\n")));
66
+ }
67
+ if (params.actionRow) {
68
+ components.push(params.actionRow);
69
+ }
70
+ if (params.footer) {
71
+ components.push(new Separator({ divider: false, spacing: "small" }));
72
+ components.push(new TextDisplay(`-# ${params.footer}`));
73
+ }
74
+ super({
75
+ cfg: params.cfg,
76
+ accountId: params.accountId,
77
+ components,
78
+ accentColor: params.accentColor,
55
79
  });
56
80
  }
81
+ }
82
+ class ExecApprovalActionButton extends Button {
83
+ customId;
84
+ label;
85
+ style;
86
+ constructor(params) {
87
+ super();
88
+ this.customId = buildExecApprovalCustomId(params.approvalId, params.action);
89
+ this.label = params.label;
90
+ this.style = params.style;
91
+ }
92
+ }
93
+ class ExecApprovalActionRow extends Row {
94
+ constructor(approvalId) {
95
+ super([
96
+ new ExecApprovalActionButton({
97
+ approvalId,
98
+ action: "allow-once",
99
+ label: "Allow once",
100
+ style: ButtonStyle.Success,
101
+ }),
102
+ new ExecApprovalActionButton({
103
+ approvalId,
104
+ action: "allow-always",
105
+ label: "Always allow",
106
+ style: ButtonStyle.Primary,
107
+ }),
108
+ new ExecApprovalActionButton({
109
+ approvalId,
110
+ action: "deny",
111
+ label: "Deny",
112
+ style: ButtonStyle.Danger,
113
+ }),
114
+ ]);
115
+ }
116
+ }
117
+ function resolveExecApprovalAccountId(params) {
118
+ const sessionKey = params.request.request.sessionKey?.trim();
119
+ if (!sessionKey) {
120
+ return null;
121
+ }
122
+ try {
123
+ const agentId = resolveAgentIdFromSessionKey(sessionKey);
124
+ const storePath = resolveStorePath(params.cfg.session?.store, { agentId });
125
+ const store = loadSessionStore(storePath);
126
+ const entry = store[sessionKey];
127
+ const channel = normalizeMessageChannel(entry?.origin?.provider ?? entry?.lastChannel);
128
+ if (channel && channel !== "discord") {
129
+ return null;
130
+ }
131
+ const accountId = entry?.origin?.accountId ?? entry?.lastAccountId;
132
+ return accountId?.trim() || null;
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ }
138
+ function buildExecApprovalMetadataLines(request) {
139
+ const lines = [];
140
+ if (request.request.cwd) {
141
+ lines.push(`- Working Directory: ${request.request.cwd}`);
142
+ }
57
143
  if (request.request.host) {
58
- fields.push({
59
- name: "Host",
60
- value: request.request.host,
61
- inline: true,
62
- });
144
+ lines.push(`- Host: ${request.request.host}`);
63
145
  }
64
146
  if (request.request.agentId) {
65
- fields.push({
66
- name: "Agent",
67
- value: request.request.agentId,
68
- inline: true,
69
- });
147
+ lines.push(`- Agent: ${request.request.agentId}`);
70
148
  }
71
- return {
149
+ return lines;
150
+ }
151
+ function buildExecApprovalPayload(container) {
152
+ const components = [container];
153
+ return { components };
154
+ }
155
+ function createExecApprovalRequestContainer(params) {
156
+ const commandText = params.request.request.command;
157
+ const commandRaw = commandText.length > 1000 ? `${commandText.slice(0, 1000)}...` : commandText;
158
+ const commandPreview = commandRaw.replace(/`/g, "\u200b`");
159
+ const expiresAtSeconds = Math.max(0, Math.floor(params.request.expiresAtMs / 1000));
160
+ return new ExecApprovalContainer({
161
+ cfg: params.cfg,
162
+ accountId: params.accountId,
72
163
  title: "Exec Approval Required",
73
164
  description: "A command needs your approval.",
74
- color: 0xffa500, // Orange
75
- fields,
76
- footer: { text: `Expires in ${expiresIn}s | ID: ${request.id}` },
77
- timestamp: new Date().toISOString(),
78
- };
165
+ commandPreview,
166
+ metadataLines: buildExecApprovalMetadataLines(params.request),
167
+ actionRow: params.actionRow,
168
+ footer: `Expires <t:${expiresAtSeconds}:R> · ID: ${params.request.id}`,
169
+ accentColor: "#FFA500",
170
+ });
79
171
  }
80
- function formatResolvedEmbed(request, decision, resolvedBy) {
81
- const commandText = request.request.command;
82
- const commandPreview = commandText.length > 500 ? `${commandText.slice(0, 500)}...` : commandText;
83
- const decisionLabel = decision === "allow-once"
172
+ function createResolvedContainer(params) {
173
+ const commandText = params.request.request.command;
174
+ const commandRaw = commandText.length > 500 ? `${commandText.slice(0, 500)}...` : commandText;
175
+ const commandPreview = commandRaw.replace(/`/g, "\u200b`");
176
+ const decisionLabel = params.decision === "allow-once"
84
177
  ? "Allowed (once)"
85
- : decision === "allow-always"
178
+ : params.decision === "allow-always"
86
179
  ? "Allowed (always)"
87
180
  : "Denied";
88
- const color = decision === "deny" ? 0xed4245 : decision === "allow-always" ? 0x5865f2 : 0x57f287;
89
- return {
181
+ const accentColor = params.decision === "deny"
182
+ ? "#ED4245"
183
+ : params.decision === "allow-always"
184
+ ? "#5865F2"
185
+ : "#57F287";
186
+ return new ExecApprovalContainer({
187
+ cfg: params.cfg,
188
+ accountId: params.accountId,
90
189
  title: `Exec Approval: ${decisionLabel}`,
91
- description: resolvedBy ? `Resolved by ${resolvedBy}` : "Resolved",
92
- color,
93
- fields: [
94
- {
95
- name: "Command",
96
- value: `\`\`\`\n${commandPreview}\n\`\`\``,
97
- inline: false,
98
- },
99
- ],
100
- footer: { text: `ID: ${request.id}` },
101
- timestamp: new Date().toISOString(),
102
- };
190
+ description: params.resolvedBy ? `Resolved by ${params.resolvedBy}` : "Resolved",
191
+ commandPreview,
192
+ footer: `ID: ${params.request.id}`,
193
+ accentColor,
194
+ });
103
195
  }
104
- function formatExpiredEmbed(request) {
105
- const commandText = request.request.command;
106
- const commandPreview = commandText.length > 500 ? `${commandText.slice(0, 500)}...` : commandText;
107
- return {
196
+ function createExpiredContainer(params) {
197
+ const commandText = params.request.request.command;
198
+ const commandRaw = commandText.length > 500 ? `${commandText.slice(0, 500)}...` : commandText;
199
+ const commandPreview = commandRaw.replace(/`/g, "\u200b`");
200
+ return new ExecApprovalContainer({
201
+ cfg: params.cfg,
202
+ accountId: params.accountId,
108
203
  title: "Exec Approval: Expired",
109
204
  description: "This approval request has expired.",
110
- color: 0x99aab5, // Gray
111
- fields: [
112
- {
113
- name: "Command",
114
- value: `\`\`\`\n${commandPreview}\n\`\`\``,
115
- inline: false,
116
- },
117
- ],
118
- footer: { text: `ID: ${request.id}` },
119
- timestamp: new Date().toISOString(),
120
- };
205
+ commandPreview,
206
+ footer: `ID: ${params.request.id}`,
207
+ accentColor: "#99AAB5",
208
+ });
121
209
  }
122
210
  export class DiscordExecApprovalHandler {
123
211
  gatewayClient = null;
@@ -130,22 +218,37 @@ export class DiscordExecApprovalHandler {
130
218
  }
131
219
  shouldHandle(request) {
132
220
  const config = this.opts.config;
133
- if (!config.enabled)
221
+ if (!config.enabled) {
134
222
  return false;
135
- if (!config.approvers || config.approvers.length === 0)
223
+ }
224
+ if (!config.approvers || config.approvers.length === 0) {
136
225
  return false;
226
+ }
227
+ const requestAccountId = resolveExecApprovalAccountId({
228
+ cfg: this.opts.cfg,
229
+ request,
230
+ });
231
+ if (requestAccountId) {
232
+ const handlerAccountId = normalizeAccountId(this.opts.accountId);
233
+ if (normalizeAccountId(requestAccountId) !== handlerAccountId) {
234
+ return false;
235
+ }
236
+ }
137
237
  // Check agent filter
138
238
  if (config.agentFilter?.length) {
139
- if (!request.request.agentId)
239
+ if (!request.request.agentId) {
140
240
  return false;
141
- if (!config.agentFilter.includes(request.request.agentId))
241
+ }
242
+ if (!config.agentFilter.includes(request.request.agentId)) {
142
243
  return false;
244
+ }
143
245
  }
144
246
  // Check session filter (substring match)
145
247
  if (config.sessionFilter?.length) {
146
248
  const session = request.request.sessionKey;
147
- if (!session)
249
+ if (!session) {
148
250
  return false;
251
+ }
149
252
  const matches = config.sessionFilter.some((p) => {
150
253
  try {
151
254
  return session.includes(p) || new RegExp(p).test(session);
@@ -154,14 +257,16 @@ export class DiscordExecApprovalHandler {
154
257
  return session.includes(p);
155
258
  }
156
259
  });
157
- if (!matches)
260
+ if (!matches) {
158
261
  return false;
262
+ }
159
263
  }
160
264
  return true;
161
265
  }
162
266
  async start() {
163
- if (this.started)
267
+ if (this.started) {
164
268
  return;
269
+ }
165
270
  this.started = true;
166
271
  const config = this.opts.config;
167
272
  if (!config.enabled) {
@@ -173,8 +278,12 @@ export class DiscordExecApprovalHandler {
173
278
  return;
174
279
  }
175
280
  logDebug("discord exec approvals: starting handler");
281
+ const { url: gatewayUrl } = buildGatewayConnectionDetails({
282
+ config: this.opts.cfg,
283
+ url: this.opts.gatewayUrl,
284
+ });
176
285
  this.gatewayClient = new GatewayClient({
177
- url: this.opts.gatewayUrl ?? "ws://127.0.0.1:18789",
286
+ url: gatewayUrl,
178
287
  clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
179
288
  clientDisplayName: "Discord Exec Approvals",
180
289
  mode: GATEWAY_CLIENT_MODES.BACKEND,
@@ -193,8 +302,9 @@ export class DiscordExecApprovalHandler {
193
302
  this.gatewayClient.start();
194
303
  }
195
304
  async stop() {
196
- if (!this.started)
305
+ if (!this.started) {
197
306
  return;
307
+ }
198
308
  this.started = false;
199
309
  // Clear all pending timeouts
200
310
  for (const pending of this.pending.values()) {
@@ -217,111 +327,177 @@ export class DiscordExecApprovalHandler {
217
327
  }
218
328
  }
219
329
  async handleApprovalRequested(request) {
220
- if (!this.shouldHandle(request))
330
+ if (!this.shouldHandle(request)) {
221
331
  return;
332
+ }
222
333
  logDebug(`discord exec approvals: received request ${request.id}`);
223
334
  this.requestCache.set(request.id, request);
224
335
  const { rest, request: discordRequest } = createDiscordClient({ token: this.opts.token, accountId: this.opts.accountId }, this.opts.cfg);
225
- const embed = formatExecApprovalEmbed(request);
226
- // Build action rows with buttons
227
- const components = [
228
- {
229
- type: 1, // ACTION_ROW
230
- components: [
231
- {
232
- type: 2, // BUTTON
233
- style: ButtonStyle.Success,
234
- label: "Allow once",
235
- custom_id: buildExecApprovalCustomId(request.id, "allow-once"),
236
- },
237
- {
238
- type: 2, // BUTTON
239
- style: ButtonStyle.Primary,
240
- label: "Always allow",
241
- custom_id: buildExecApprovalCustomId(request.id, "allow-always"),
242
- },
243
- {
244
- type: 2, // BUTTON
245
- style: ButtonStyle.Danger,
246
- label: "Deny",
247
- custom_id: buildExecApprovalCustomId(request.id, "deny"),
248
- },
249
- ],
250
- },
251
- ];
252
- const approvers = this.opts.config.approvers ?? [];
253
- for (const approver of approvers) {
254
- const userId = String(approver);
255
- try {
256
- // Create DM channel
257
- const dmChannel = (await discordRequest(() => rest.post(Routes.userChannels(), {
258
- body: { recipient_id: userId },
259
- }), "dm-channel"));
260
- if (!dmChannel?.id) {
261
- logError(`discord exec approvals: failed to create DM for user ${userId}`);
262
- continue;
336
+ const actionRow = new ExecApprovalActionRow(request.id);
337
+ const container = createExecApprovalRequestContainer({
338
+ request,
339
+ cfg: this.opts.cfg,
340
+ accountId: this.opts.accountId,
341
+ actionRow,
342
+ });
343
+ const payload = buildExecApprovalPayload(container);
344
+ const body = stripUndefinedFields(serializePayload(payload));
345
+ const target = this.opts.config.target ?? "dm";
346
+ const sendToDm = target === "dm" || target === "both";
347
+ const sendToChannel = target === "channel" || target === "both";
348
+ let fallbackToDm = false;
349
+ // Send to originating channel if configured
350
+ if (sendToChannel) {
351
+ const channelId = extractDiscordChannelId(request.request.sessionKey);
352
+ if (channelId) {
353
+ try {
354
+ const message = (await discordRequest(() => rest.post(Routes.channelMessages(channelId), {
355
+ body,
356
+ }), "send-approval-channel"));
357
+ if (message?.id) {
358
+ const timeoutMs = Math.max(0, request.expiresAtMs - Date.now());
359
+ const timeoutId = setTimeout(() => {
360
+ void this.handleApprovalTimeout(request.id, "channel");
361
+ }, timeoutMs);
362
+ this.pending.set(`${request.id}:channel`, {
363
+ discordMessageId: message.id,
364
+ discordChannelId: channelId,
365
+ timeoutId,
366
+ });
367
+ logDebug(`discord exec approvals: sent approval ${request.id} to channel ${channelId}`);
368
+ }
263
369
  }
264
- // Send message with embed and buttons
265
- const message = (await discordRequest(() => rest.post(Routes.channelMessages(dmChannel.id), {
266
- body: {
267
- embeds: [embed],
268
- components,
269
- },
270
- }), "send-approval"));
271
- if (!message?.id) {
272
- logError(`discord exec approvals: failed to send message to user ${userId}`);
273
- continue;
370
+ catch (err) {
371
+ logError(`discord exec approvals: failed to send to channel: ${String(err)}`);
372
+ }
373
+ }
374
+ else {
375
+ if (!sendToDm) {
376
+ logError(`discord exec approvals: target is "channel" but could not extract channel id from session key "${request.request.sessionKey ?? "(none)"}" — falling back to DM delivery for approval ${request.id}`);
377
+ fallbackToDm = true;
378
+ }
379
+ else {
380
+ logDebug("discord exec approvals: could not extract channel id from session key");
274
381
  }
275
- // Set up timeout
276
- const timeoutMs = Math.max(0, request.expiresAtMs - Date.now());
277
- const timeoutId = setTimeout(() => {
278
- void this.handleApprovalTimeout(request.id);
279
- }, timeoutMs);
280
- this.pending.set(request.id, {
281
- discordMessageId: message.id,
282
- discordChannelId: dmChannel.id,
283
- timeoutId,
284
- });
285
- logDebug(`discord exec approvals: sent approval ${request.id} to user ${userId}`);
286
382
  }
287
- catch (err) {
288
- logError(`discord exec approvals: failed to notify user ${userId}: ${String(err)}`);
383
+ }
384
+ // Send to approver DMs if configured (or as fallback when channel extraction fails)
385
+ if (sendToDm || fallbackToDm) {
386
+ const approvers = this.opts.config.approvers ?? [];
387
+ for (const approver of approvers) {
388
+ const userId = String(approver);
389
+ try {
390
+ // Create DM channel
391
+ const dmChannel = (await discordRequest(() => rest.post(Routes.userChannels(), {
392
+ body: { recipient_id: userId },
393
+ }), "dm-channel"));
394
+ if (!dmChannel?.id) {
395
+ logError(`discord exec approvals: failed to create DM for user ${userId}`);
396
+ continue;
397
+ }
398
+ // Send message with components v2 + buttons
399
+ const message = (await discordRequest(() => rest.post(Routes.channelMessages(dmChannel.id), {
400
+ body,
401
+ }), "send-approval"));
402
+ if (!message?.id) {
403
+ logError(`discord exec approvals: failed to send message to user ${userId}`);
404
+ continue;
405
+ }
406
+ // Clear any existing pending DM entry to avoid timeout leaks
407
+ const existingDm = this.pending.get(`${request.id}:dm`);
408
+ if (existingDm) {
409
+ clearTimeout(existingDm.timeoutId);
410
+ }
411
+ // Set up timeout
412
+ const timeoutMs = Math.max(0, request.expiresAtMs - Date.now());
413
+ const timeoutId = setTimeout(() => {
414
+ void this.handleApprovalTimeout(request.id, "dm");
415
+ }, timeoutMs);
416
+ this.pending.set(`${request.id}:dm`, {
417
+ discordMessageId: message.id,
418
+ discordChannelId: dmChannel.id,
419
+ timeoutId,
420
+ });
421
+ logDebug(`discord exec approvals: sent approval ${request.id} to user ${userId}`);
422
+ }
423
+ catch (err) {
424
+ logError(`discord exec approvals: failed to notify user ${userId}: ${String(err)}`);
425
+ }
289
426
  }
290
427
  }
291
428
  }
292
429
  async handleApprovalResolved(resolved) {
293
- const pending = this.pending.get(resolved.id);
294
- if (!pending)
295
- return;
296
- clearTimeout(pending.timeoutId);
297
- this.pending.delete(resolved.id);
430
+ // Clean up all pending entries for this approval (channel + dm)
298
431
  const request = this.requestCache.get(resolved.id);
299
432
  this.requestCache.delete(resolved.id);
300
- if (!request)
433
+ if (!request) {
301
434
  return;
435
+ }
302
436
  logDebug(`discord exec approvals: resolved ${resolved.id} with ${resolved.decision}`);
303
- await this.updateMessage(pending.discordChannelId, pending.discordMessageId, formatResolvedEmbed(request, resolved.decision, resolved.resolvedBy));
437
+ const container = createResolvedContainer({
438
+ request,
439
+ decision: resolved.decision,
440
+ resolvedBy: resolved.resolvedBy,
441
+ cfg: this.opts.cfg,
442
+ accountId: this.opts.accountId,
443
+ });
444
+ for (const suffix of [":channel", ":dm", ""]) {
445
+ const key = `${resolved.id}${suffix}`;
446
+ const pending = this.pending.get(key);
447
+ if (!pending) {
448
+ continue;
449
+ }
450
+ clearTimeout(pending.timeoutId);
451
+ this.pending.delete(key);
452
+ await this.finalizeMessage(pending.discordChannelId, pending.discordMessageId, container);
453
+ }
304
454
  }
305
- async handleApprovalTimeout(approvalId) {
306
- const pending = this.pending.get(approvalId);
307
- if (!pending)
455
+ async handleApprovalTimeout(approvalId, source) {
456
+ const key = source ? `${approvalId}:${source}` : approvalId;
457
+ const pending = this.pending.get(key);
458
+ if (!pending) {
308
459
  return;
309
- this.pending.delete(approvalId);
460
+ }
461
+ this.pending.delete(key);
310
462
  const request = this.requestCache.get(approvalId);
311
- this.requestCache.delete(approvalId);
312
- if (!request)
463
+ // Only clean up requestCache if no other pending entries exist for this approval
464
+ const hasOtherPending = this.pending.has(`${approvalId}:channel`) ||
465
+ this.pending.has(`${approvalId}:dm`) ||
466
+ this.pending.has(approvalId);
467
+ if (!hasOtherPending) {
468
+ this.requestCache.delete(approvalId);
469
+ }
470
+ if (!request) {
471
+ return;
472
+ }
473
+ logDebug(`discord exec approvals: timeout for ${approvalId} (${source ?? "default"})`);
474
+ const container = createExpiredContainer({
475
+ request,
476
+ cfg: this.opts.cfg,
477
+ accountId: this.opts.accountId,
478
+ });
479
+ await this.finalizeMessage(pending.discordChannelId, pending.discordMessageId, container);
480
+ }
481
+ async finalizeMessage(channelId, messageId, container) {
482
+ if (!this.opts.config.cleanupAfterResolve) {
483
+ await this.updateMessage(channelId, messageId, container);
313
484
  return;
314
- logDebug(`discord exec approvals: timeout for ${approvalId}`);
315
- await this.updateMessage(pending.discordChannelId, pending.discordMessageId, formatExpiredEmbed(request));
485
+ }
486
+ try {
487
+ const { rest, request: discordRequest } = createDiscordClient({ token: this.opts.token, accountId: this.opts.accountId }, this.opts.cfg);
488
+ await discordRequest(() => rest.delete(Routes.channelMessage(channelId, messageId)), "delete-approval");
489
+ }
490
+ catch (err) {
491
+ logError(`discord exec approvals: failed to delete message: ${String(err)}`);
492
+ await this.updateMessage(channelId, messageId, container);
493
+ }
316
494
  }
317
- async updateMessage(channelId, messageId, embed) {
495
+ async updateMessage(channelId, messageId, container) {
318
496
  try {
319
497
  const { rest, request: discordRequest } = createDiscordClient({ token: this.opts.token, accountId: this.opts.accountId }, this.opts.cfg);
498
+ const payload = buildExecApprovalPayload(container);
320
499
  await discordRequest(() => rest.patch(Routes.channelMessage(channelId, messageId), {
321
- body: {
322
- embeds: [embed],
323
- components: [], // Remove buttons
324
- },
500
+ body: stripUndefinedFields(serializePayload(payload)),
325
501
  }), "update-approval");
326
502
  }
327
503
  catch (err) {
@@ -347,6 +523,10 @@ export class DiscordExecApprovalHandler {
347
523
  return false;
348
524
  }
349
525
  }
526
+ /** Return the list of configured approver IDs. */
527
+ getApprovers() {
528
+ return this.opts.config.approvers ?? [];
529
+ }
350
530
  }
351
531
  export class ExecApprovalButton extends Button {
352
532
  label = "execapproval";
@@ -371,6 +551,21 @@ export class ExecApprovalButton extends Button {
371
551
  }
372
552
  return;
373
553
  }
554
+ // Verify the user is an authorized approver
555
+ const approvers = this.ctx.handler.getApprovers();
556
+ const userId = interaction.userId;
557
+ if (!approvers.some((id) => String(id) === userId)) {
558
+ try {
559
+ await interaction.reply({
560
+ content: "⛔ You are not authorized to approve exec requests.",
561
+ ephemeral: true,
562
+ });
563
+ }
564
+ catch {
565
+ // Interaction may have expired
566
+ }
567
+ return;
568
+ }
374
569
  const decisionLabel = parsed.action === "allow-once"
375
570
  ? "Allowed (once)"
376
571
  : parsed.action === "allow-always"
@@ -8,7 +8,8 @@ export function resolveDiscordGatewayIntents(intentsConfig) {
8
8
  GatewayIntents.MessageContent |
9
9
  GatewayIntents.DirectMessages |
10
10
  GatewayIntents.GuildMessageReactions |
11
- GatewayIntents.DirectMessageReactions;
11
+ GatewayIntents.DirectMessageReactions |
12
+ GatewayIntents.GuildVoiceStates;
12
13
  if (intentsConfig?.presence) {
13
14
  intents |= GatewayIntents.GuildPresences;
14
15
  }