@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
@@ -0,0 +1,531 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import { createRequire } from "node:module";
4
+ import path from "node:path";
5
+ import { ChannelType, ReadyListener } from "@buape/carbon";
6
+ import { AudioPlayerStatus, EndBehaviorType, VoiceConnectionStatus, createAudioPlayer, createAudioResource, entersState, joinVoiceChannel, } from "@discordjs/voice";
7
+ import { resolveAgentDir } from "../../agents/agent-scope.js";
8
+ import { agentCommand } from "../../commands/agent.js";
9
+ import { logVerbose, shouldLogVerbose } from "../../globals.js";
10
+ import { formatErrorMessage } from "../../infra/errors.js";
11
+ import { resolvePreferredPoolbotTmpDir } from "../../infra/tmp-poolbot-dir.js";
12
+ import { createSubsystemLogger } from "../../logging/subsystem.js";
13
+ import { buildProviderRegistry, createMediaAttachmentCache, normalizeMediaAttachments, runCapability, } from "../../media-understanding/runner.js";
14
+ import { resolveAgentRoute } from "../../routing/resolve-route.js";
15
+ import { parseTtsDirectives } from "../../tts/tts-core.js";
16
+ import { resolveTtsConfig, textToSpeech } from "../../tts/tts.js";
17
+ const require = createRequire(import.meta.url);
18
+ const SAMPLE_RATE = 48_000;
19
+ const CHANNELS = 2;
20
+ const BIT_DEPTH = 16;
21
+ const MIN_SEGMENT_SECONDS = 0.35;
22
+ const SILENCE_DURATION_MS = 1_000;
23
+ const PLAYBACK_READY_TIMEOUT_MS = 15_000;
24
+ const SPEAKING_READY_TIMEOUT_MS = 60_000;
25
+ const logger = createSubsystemLogger("discord/voice");
26
+ const logVoiceVerbose = (message) => {
27
+ logVerbose(`discord voice: ${message}`);
28
+ };
29
+ function mergeTtsConfig(base, override) {
30
+ if (!override) {
31
+ return base;
32
+ }
33
+ return {
34
+ ...base,
35
+ ...override,
36
+ modelOverrides: {
37
+ ...base.modelOverrides,
38
+ ...override.modelOverrides,
39
+ },
40
+ elevenlabs: {
41
+ ...base.elevenlabs,
42
+ ...override.elevenlabs,
43
+ voiceSettings: {
44
+ ...base.elevenlabs?.voiceSettings,
45
+ ...override.elevenlabs?.voiceSettings,
46
+ },
47
+ },
48
+ openai: {
49
+ ...base.openai,
50
+ ...override.openai,
51
+ },
52
+ edge: {
53
+ ...base.edge,
54
+ ...override.edge,
55
+ },
56
+ };
57
+ }
58
+ function resolveVoiceTtsConfig(params) {
59
+ if (!params.override) {
60
+ return { cfg: params.cfg, resolved: resolveTtsConfig(params.cfg) };
61
+ }
62
+ const base = params.cfg.messages?.tts ?? {};
63
+ const merged = mergeTtsConfig(base, params.override);
64
+ const messages = params.cfg.messages ?? {};
65
+ const cfg = {
66
+ ...params.cfg,
67
+ messages: {
68
+ ...messages,
69
+ tts: merged,
70
+ },
71
+ };
72
+ return { cfg, resolved: resolveTtsConfig(cfg) };
73
+ }
74
+ function buildWavBuffer(pcm) {
75
+ const blockAlign = (CHANNELS * BIT_DEPTH) / 8;
76
+ const byteRate = SAMPLE_RATE * blockAlign;
77
+ const header = Buffer.alloc(44);
78
+ header.write("RIFF", 0);
79
+ header.writeUInt32LE(36 + pcm.length, 4);
80
+ header.write("WAVE", 8);
81
+ header.write("fmt ", 12);
82
+ header.writeUInt32LE(16, 16);
83
+ header.writeUInt16LE(1, 20);
84
+ header.writeUInt16LE(CHANNELS, 22);
85
+ header.writeUInt32LE(SAMPLE_RATE, 24);
86
+ header.writeUInt32LE(byteRate, 28);
87
+ header.writeUInt16LE(blockAlign, 32);
88
+ header.writeUInt16LE(BIT_DEPTH, 34);
89
+ header.write("data", 36);
90
+ header.writeUInt32LE(pcm.length, 40);
91
+ return Buffer.concat([header, pcm]);
92
+ }
93
+ function createOpusDecoder() {
94
+ try {
95
+ const OpusScript = require("opusscript");
96
+ const decoder = new OpusScript(SAMPLE_RATE, CHANNELS, OpusScript.Application.AUDIO);
97
+ return { decoder, name: "opusscript" };
98
+ }
99
+ catch (err) {
100
+ logger.warn(`discord voice: opusscript init failed: ${formatErrorMessage(err)}`);
101
+ }
102
+ try {
103
+ const { OpusEncoder } = require("@discordjs/opus");
104
+ const decoder = new OpusEncoder(SAMPLE_RATE, CHANNELS);
105
+ return { decoder, name: "@discordjs/opus" };
106
+ }
107
+ catch (err) {
108
+ logger.warn(`discord voice: opus decoder init failed: ${formatErrorMessage(err)}`);
109
+ }
110
+ return null;
111
+ }
112
+ async function decodeOpusStream(stream) {
113
+ const selected = createOpusDecoder();
114
+ if (!selected) {
115
+ return Buffer.alloc(0);
116
+ }
117
+ logVoiceVerbose(`opus decoder: ${selected.name}`);
118
+ const chunks = [];
119
+ try {
120
+ for await (const chunk of stream) {
121
+ if (!chunk || !(chunk instanceof Buffer) || chunk.length === 0) {
122
+ continue;
123
+ }
124
+ const decoded = selected.decoder.decode(chunk);
125
+ if (decoded && decoded.length > 0) {
126
+ chunks.push(Buffer.from(decoded));
127
+ }
128
+ }
129
+ }
130
+ catch (err) {
131
+ if (shouldLogVerbose()) {
132
+ logVerbose(`discord voice: opus decode failed: ${formatErrorMessage(err)}`);
133
+ }
134
+ }
135
+ return chunks.length > 0 ? Buffer.concat(chunks) : Buffer.alloc(0);
136
+ }
137
+ function estimateDurationSeconds(pcm) {
138
+ const bytesPerSample = (BIT_DEPTH / 8) * CHANNELS;
139
+ if (bytesPerSample <= 0) {
140
+ return 0;
141
+ }
142
+ return pcm.length / (bytesPerSample * SAMPLE_RATE);
143
+ }
144
+ async function writeWavFile(pcm) {
145
+ const tempDir = await fs.mkdtemp(path.join(resolvePreferredPoolbotTmpDir(), "discord-voice-"));
146
+ const filePath = path.join(tempDir, `segment-${randomUUID()}.wav`);
147
+ const wav = buildWavBuffer(pcm);
148
+ await fs.writeFile(filePath, wav);
149
+ scheduleTempCleanup(tempDir);
150
+ return { path: filePath, durationSeconds: estimateDurationSeconds(pcm) };
151
+ }
152
+ function scheduleTempCleanup(tempDir, delayMs = 30 * 60 * 1000) {
153
+ const timer = setTimeout(() => {
154
+ fs.rm(tempDir, { recursive: true, force: true }).catch((err) => {
155
+ if (shouldLogVerbose()) {
156
+ logVerbose(`discord voice: temp cleanup failed for ${tempDir}: ${formatErrorMessage(err)}`);
157
+ }
158
+ });
159
+ }, delayMs);
160
+ timer.unref();
161
+ }
162
+ async function transcribeAudio(params) {
163
+ const ctx = {
164
+ MediaPath: params.filePath,
165
+ MediaType: "audio/wav",
166
+ };
167
+ const attachments = normalizeMediaAttachments(ctx);
168
+ if (attachments.length === 0) {
169
+ return undefined;
170
+ }
171
+ const cache = createMediaAttachmentCache(attachments);
172
+ const providerRegistry = buildProviderRegistry();
173
+ try {
174
+ const result = await runCapability({
175
+ capability: "audio",
176
+ cfg: params.cfg,
177
+ ctx,
178
+ attachments: cache,
179
+ media: attachments,
180
+ agentDir: resolveAgentDir(params.cfg, params.agentId),
181
+ providerRegistry,
182
+ config: params.cfg.tools?.media?.audio,
183
+ });
184
+ const output = result.outputs.find((entry) => entry.kind === "audio.transcription");
185
+ const text = output?.text?.trim();
186
+ return text || undefined;
187
+ }
188
+ finally {
189
+ await cache.cleanup();
190
+ }
191
+ }
192
+ export class DiscordVoiceManager {
193
+ params;
194
+ sessions = new Map();
195
+ botUserId;
196
+ voiceEnabled;
197
+ autoJoinTask = null;
198
+ constructor(params) {
199
+ this.params = params;
200
+ this.botUserId = params.botUserId;
201
+ this.voiceEnabled = params.discordConfig.voice?.enabled !== false;
202
+ }
203
+ setBotUserId(id) {
204
+ if (id) {
205
+ this.botUserId = id;
206
+ }
207
+ }
208
+ isEnabled() {
209
+ return this.voiceEnabled;
210
+ }
211
+ async autoJoin() {
212
+ if (!this.voiceEnabled) {
213
+ return;
214
+ }
215
+ if (this.autoJoinTask) {
216
+ return this.autoJoinTask;
217
+ }
218
+ this.autoJoinTask = (async () => {
219
+ const entries = this.params.discordConfig.voice?.autoJoin ?? [];
220
+ logVoiceVerbose(`autoJoin: ${entries.length} entries`);
221
+ const seenGuilds = new Set();
222
+ for (const entry of entries) {
223
+ const guildId = entry.guildId.trim();
224
+ if (!guildId) {
225
+ continue;
226
+ }
227
+ if (seenGuilds.has(guildId)) {
228
+ logger.warn(`discord voice: autoJoin has multiple entries for guild ${guildId}; skipping`);
229
+ continue;
230
+ }
231
+ seenGuilds.add(guildId);
232
+ logVoiceVerbose(`autoJoin: joining guild ${guildId} channel ${entry.channelId}`);
233
+ await this.join({
234
+ guildId: entry.guildId,
235
+ channelId: entry.channelId,
236
+ });
237
+ }
238
+ })().finally(() => {
239
+ this.autoJoinTask = null;
240
+ });
241
+ return this.autoJoinTask;
242
+ }
243
+ status() {
244
+ return Array.from(this.sessions.values()).map((session) => ({
245
+ ok: true,
246
+ message: `connected: guild ${session.guildId} channel ${session.channelId}`,
247
+ guildId: session.guildId,
248
+ channelId: session.channelId,
249
+ }));
250
+ }
251
+ async join(params) {
252
+ if (!this.voiceEnabled) {
253
+ return {
254
+ ok: false,
255
+ message: "Discord voice is disabled (channels.discord.voice.enabled).",
256
+ };
257
+ }
258
+ const guildId = params.guildId.trim();
259
+ const channelId = params.channelId.trim();
260
+ if (!guildId || !channelId) {
261
+ return { ok: false, message: "Missing guildId or channelId." };
262
+ }
263
+ logVoiceVerbose(`join requested: guild ${guildId} channel ${channelId}`);
264
+ const existing = this.sessions.get(guildId);
265
+ if (existing && existing.channelId === channelId) {
266
+ logVoiceVerbose(`join: already connected to guild ${guildId} channel ${channelId}`);
267
+ return { ok: true, message: `Already connected to <#${channelId}>.`, guildId, channelId };
268
+ }
269
+ if (existing) {
270
+ logVoiceVerbose(`join: replacing existing session for guild ${guildId}`);
271
+ await this.leave({ guildId });
272
+ }
273
+ const channelInfo = await this.params.client.fetchChannel(channelId).catch(() => null);
274
+ if (!channelInfo || ("type" in channelInfo && !isVoiceChannel(channelInfo.type))) {
275
+ return { ok: false, message: `Channel ${channelId} is not a voice channel.` };
276
+ }
277
+ const channelGuildId = "guildId" in channelInfo ? channelInfo.guildId : undefined;
278
+ if (channelGuildId && channelGuildId !== guildId) {
279
+ return { ok: false, message: "Voice channel is not in this guild." };
280
+ }
281
+ const voicePlugin = this.params.client.getPlugin("voice");
282
+ if (!voicePlugin) {
283
+ return { ok: false, message: "Discord voice plugin is not available." };
284
+ }
285
+ const adapterCreator = voicePlugin.getGatewayAdapterCreator(guildId);
286
+ const connection = joinVoiceChannel({
287
+ channelId,
288
+ guildId,
289
+ adapterCreator,
290
+ selfDeaf: false,
291
+ selfMute: false,
292
+ });
293
+ try {
294
+ await entersState(connection, VoiceConnectionStatus.Ready, PLAYBACK_READY_TIMEOUT_MS);
295
+ logVoiceVerbose(`join: connected to guild ${guildId} channel ${channelId}`);
296
+ }
297
+ catch (err) {
298
+ connection.destroy();
299
+ return { ok: false, message: `Failed to join voice channel: ${formatErrorMessage(err)}` };
300
+ }
301
+ const sessionChannelId = channelInfo?.id ?? channelId;
302
+ // Use the voice channel id as the session channel so text chat in the voice channel
303
+ // shares the same session as spoken audio.
304
+ if (sessionChannelId !== channelId) {
305
+ logVoiceVerbose(`join: using session channel ${sessionChannelId} for voice channel ${channelId}`);
306
+ }
307
+ const route = resolveAgentRoute({
308
+ cfg: this.params.cfg,
309
+ channel: "discord",
310
+ accountId: this.params.accountId,
311
+ guildId,
312
+ peer: { kind: "channel", id: sessionChannelId },
313
+ });
314
+ const player = createAudioPlayer();
315
+ connection.subscribe(player);
316
+ const entry = {
317
+ guildId,
318
+ channelId,
319
+ sessionChannelId,
320
+ route,
321
+ connection,
322
+ player,
323
+ playbackQueue: Promise.resolve(),
324
+ processingQueue: Promise.resolve(),
325
+ activeSpeakers: new Set(),
326
+ stop: () => {
327
+ player.stop();
328
+ connection.destroy();
329
+ },
330
+ };
331
+ const speakingHandler = (userId) => {
332
+ void this.handleSpeakingStart(entry, userId).catch((err) => {
333
+ logger.warn(`discord voice: capture failed: ${formatErrorMessage(err)}`);
334
+ });
335
+ };
336
+ connection.receiver.speaking.on("start", speakingHandler);
337
+ connection.on(VoiceConnectionStatus.Disconnected, async () => {
338
+ try {
339
+ await Promise.race([
340
+ entersState(connection, VoiceConnectionStatus.Signalling, 5_000),
341
+ entersState(connection, VoiceConnectionStatus.Connecting, 5_000),
342
+ ]);
343
+ }
344
+ catch {
345
+ this.sessions.delete(guildId);
346
+ connection.destroy();
347
+ }
348
+ });
349
+ connection.on(VoiceConnectionStatus.Destroyed, () => {
350
+ this.sessions.delete(guildId);
351
+ });
352
+ player.on("error", (err) => {
353
+ logger.warn(`discord voice: playback error: ${formatErrorMessage(err)}`);
354
+ });
355
+ this.sessions.set(guildId, entry);
356
+ return {
357
+ ok: true,
358
+ message: `Joined <#${channelId}>.`,
359
+ guildId,
360
+ channelId,
361
+ };
362
+ }
363
+ async leave(params) {
364
+ const guildId = params.guildId.trim();
365
+ logVoiceVerbose(`leave requested: guild ${guildId} channel ${params.channelId ?? "current"}`);
366
+ const entry = this.sessions.get(guildId);
367
+ if (!entry) {
368
+ return { ok: false, message: "Not connected to a voice channel." };
369
+ }
370
+ if (params.channelId && params.channelId !== entry.channelId) {
371
+ return { ok: false, message: "Not connected to that voice channel." };
372
+ }
373
+ entry.stop();
374
+ this.sessions.delete(guildId);
375
+ logVoiceVerbose(`leave: disconnected from guild ${guildId} channel ${entry.channelId}`);
376
+ return {
377
+ ok: true,
378
+ message: `Left <#${entry.channelId}>.`,
379
+ guildId,
380
+ channelId: entry.channelId,
381
+ };
382
+ }
383
+ async destroy() {
384
+ for (const entry of this.sessions.values()) {
385
+ entry.stop();
386
+ }
387
+ this.sessions.clear();
388
+ }
389
+ enqueueProcessing(entry, task) {
390
+ entry.processingQueue = entry.processingQueue
391
+ .then(task)
392
+ .catch((err) => logger.warn(`discord voice: processing failed: ${formatErrorMessage(err)}`));
393
+ }
394
+ enqueuePlayback(entry, task) {
395
+ entry.playbackQueue = entry.playbackQueue
396
+ .then(task)
397
+ .catch((err) => logger.warn(`discord voice: playback failed: ${formatErrorMessage(err)}`));
398
+ }
399
+ async handleSpeakingStart(entry, userId) {
400
+ if (!userId || entry.activeSpeakers.has(userId)) {
401
+ return;
402
+ }
403
+ if (this.botUserId && userId === this.botUserId) {
404
+ return;
405
+ }
406
+ entry.activeSpeakers.add(userId);
407
+ logVoiceVerbose(`capture start: guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
408
+ if (entry.player.state.status === AudioPlayerStatus.Playing) {
409
+ entry.player.stop(true);
410
+ }
411
+ const stream = entry.connection.receiver.subscribe(userId, {
412
+ end: {
413
+ behavior: EndBehaviorType.AfterSilence,
414
+ duration: SILENCE_DURATION_MS,
415
+ },
416
+ });
417
+ stream.on("error", (err) => {
418
+ logger.warn(`discord voice: receive error: ${formatErrorMessage(err)}`);
419
+ });
420
+ try {
421
+ const pcm = await decodeOpusStream(stream);
422
+ if (pcm.length === 0) {
423
+ logVoiceVerbose(`capture empty: guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
424
+ return;
425
+ }
426
+ const { path: wavPath, durationSeconds } = await writeWavFile(pcm);
427
+ if (durationSeconds < MIN_SEGMENT_SECONDS) {
428
+ logVoiceVerbose(`capture too short (${durationSeconds.toFixed(2)}s): guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
429
+ return;
430
+ }
431
+ logVoiceVerbose(`capture ready (${durationSeconds.toFixed(2)}s): guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
432
+ this.enqueueProcessing(entry, async () => {
433
+ await this.processSegment({ entry, wavPath, userId, durationSeconds });
434
+ });
435
+ }
436
+ finally {
437
+ entry.activeSpeakers.delete(userId);
438
+ }
439
+ }
440
+ async processSegment(params) {
441
+ const { entry, wavPath, userId, durationSeconds } = params;
442
+ logVoiceVerbose(`segment processing (${durationSeconds.toFixed(2)}s): guild ${entry.guildId} channel ${entry.channelId}`);
443
+ const transcript = await transcribeAudio({
444
+ cfg: this.params.cfg,
445
+ agentId: entry.route.agentId,
446
+ filePath: wavPath,
447
+ });
448
+ if (!transcript) {
449
+ logVoiceVerbose(`transcription empty: guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
450
+ return;
451
+ }
452
+ logVoiceVerbose(`transcription ok (${transcript.length} chars): guild ${entry.guildId} channel ${entry.channelId}`);
453
+ const speakerLabel = await this.resolveSpeakerLabel(entry.guildId, userId);
454
+ const prompt = speakerLabel ? `${speakerLabel}: ${transcript}` : transcript;
455
+ const result = await agentCommand({
456
+ message: prompt,
457
+ sessionKey: entry.route.sessionKey,
458
+ agentId: entry.route.agentId,
459
+ messageChannel: "discord",
460
+ deliver: false,
461
+ }, this.params.runtime);
462
+ const replyText = (result.payloads ?? [])
463
+ .map((payload) => payload.text)
464
+ .filter((text) => typeof text === "string" && text.trim())
465
+ .join("\n")
466
+ .trim();
467
+ if (!replyText) {
468
+ logVoiceVerbose(`reply empty: guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
469
+ return;
470
+ }
471
+ logVoiceVerbose(`reply ok (${replyText.length} chars): guild ${entry.guildId} channel ${entry.channelId}`);
472
+ const { cfg: ttsCfg, resolved: ttsConfig } = resolveVoiceTtsConfig({
473
+ cfg: this.params.cfg,
474
+ override: this.params.discordConfig.voice?.tts,
475
+ });
476
+ const directive = parseTtsDirectives(replyText, ttsConfig.modelOverrides);
477
+ const speakText = directive.overrides.ttsText ?? directive.cleanedText.trim();
478
+ if (!speakText) {
479
+ logVoiceVerbose(`tts skipped (empty): guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
480
+ return;
481
+ }
482
+ const ttsResult = await textToSpeech({
483
+ text: speakText,
484
+ cfg: ttsCfg,
485
+ channel: "discord",
486
+ overrides: directive.overrides,
487
+ });
488
+ if (!ttsResult.success || !ttsResult.audioPath) {
489
+ logger.warn(`discord voice: TTS failed: ${ttsResult.error ?? "unknown error"}`);
490
+ return;
491
+ }
492
+ const audioPath = ttsResult.audioPath;
493
+ logVoiceVerbose(`tts ok (${speakText.length} chars): guild ${entry.guildId} channel ${entry.channelId}`);
494
+ this.enqueuePlayback(entry, async () => {
495
+ logVoiceVerbose(`playback start: guild ${entry.guildId} channel ${entry.channelId} file ${path.basename(audioPath)}`);
496
+ const resource = createAudioResource(audioPath);
497
+ entry.player.play(resource);
498
+ await entersState(entry.player, AudioPlayerStatus.Playing, PLAYBACK_READY_TIMEOUT_MS).catch(() => undefined);
499
+ await entersState(entry.player, AudioPlayerStatus.Idle, SPEAKING_READY_TIMEOUT_MS).catch(() => undefined);
500
+ logVoiceVerbose(`playback done: guild ${entry.guildId} channel ${entry.channelId}`);
501
+ });
502
+ }
503
+ async resolveSpeakerLabel(guildId, userId) {
504
+ try {
505
+ const member = await this.params.client.fetchMember(guildId, userId);
506
+ return member.nickname ?? member.user?.globalName ?? member.user?.username ?? userId;
507
+ }
508
+ catch {
509
+ try {
510
+ const user = await this.params.client.fetchUser(userId);
511
+ return user.globalName ?? user.username ?? userId;
512
+ }
513
+ catch {
514
+ return userId;
515
+ }
516
+ }
517
+ }
518
+ }
519
+ export class DiscordVoiceReadyListener extends ReadyListener {
520
+ manager;
521
+ constructor(manager) {
522
+ super();
523
+ this.manager = manager;
524
+ }
525
+ async handle() {
526
+ await this.manager.autoJoin();
527
+ }
528
+ }
529
+ function isVoiceChannel(type) {
530
+ return type === ChannelType.GuildVoice || type === ChannelType.GuildStageVoice;
531
+ }
@@ -1,36 +1,41 @@
1
1
  import { readTailscaleWhoisIdentity } from "../infra/tailscale.js";
2
2
  import { safeEqualSecret } from "../security/secret-equal.js";
3
3
  import { AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET, } from "./auth-rate-limit.js";
4
- import { isLoopbackAddress, isTrustedProxyAddress, resolveHostName, parseForwardedForClientIp, resolveGatewayClientIp, } from "./net.js";
4
+ import { isLoopbackAddress, isTrustedProxyAddress, resolveHostName, resolveClientIp, } from "./net.js";
5
5
  function normalizeLogin(login) {
6
6
  return login.trim().toLowerCase();
7
7
  }
8
8
  function headerValue(value) {
9
9
  return Array.isArray(value) ? value[0] : value;
10
10
  }
11
+ const TAILSCALE_TRUSTED_PROXIES = ["127.0.0.1", "::1"];
11
12
  function resolveTailscaleClientIp(req) {
12
13
  if (!req) {
13
14
  return undefined;
14
15
  }
15
- const forwardedFor = headerValue(req.headers?.["x-forwarded-for"]);
16
- return forwardedFor ? parseForwardedForClientIp(forwardedFor) : undefined;
16
+ return resolveClientIp({
17
+ remoteAddr: req.socket?.remoteAddress ?? "",
18
+ forwardedFor: headerValue(req.headers?.["x-forwarded-for"]),
19
+ trustedProxies: [...TAILSCALE_TRUSTED_PROXIES],
20
+ });
17
21
  }
18
- function resolveRequestClientIp(req, trustedProxies) {
22
+ function resolveRequestClientIp(req, trustedProxies, allowRealIpFallback = false) {
19
23
  if (!req) {
20
24
  return undefined;
21
25
  }
22
- return resolveGatewayClientIp({
26
+ return resolveClientIp({
23
27
  remoteAddr: req.socket?.remoteAddress ?? "",
24
28
  forwardedFor: headerValue(req.headers?.["x-forwarded-for"]),
25
29
  realIp: headerValue(req.headers?.["x-real-ip"]),
26
30
  trustedProxies,
31
+ allowRealIpFallback,
27
32
  });
28
33
  }
29
- export function isLocalDirectRequest(req, trustedProxies) {
34
+ export function isLocalDirectRequest(req, trustedProxies, allowRealIpFallback = false) {
30
35
  if (!req) {
31
36
  return false;
32
37
  }
33
- const clientIp = resolveRequestClientIp(req, trustedProxies) ?? "";
38
+ const clientIp = resolveRequestClientIp(req, trustedProxies, allowRealIpFallback) ?? "";
34
39
  if (!isLoopbackAddress(clientIp)) {
35
40
  return false;
36
41
  }
@@ -104,28 +109,60 @@ async function resolveVerifiedTailscaleUser(params) {
104
109
  };
105
110
  }
106
111
  export function resolveGatewayAuth(params) {
107
- const authConfig = params.authConfig ?? {};
112
+ const baseAuthConfig = params.authConfig ?? {};
113
+ const authOverride = params.authOverride ?? undefined;
114
+ const authConfig = { ...baseAuthConfig };
115
+ if (authOverride) {
116
+ if (authOverride.mode !== undefined) {
117
+ authConfig.mode = authOverride.mode;
118
+ }
119
+ if (authOverride.token !== undefined) {
120
+ authConfig.token = authOverride.token;
121
+ }
122
+ if (authOverride.password !== undefined) {
123
+ authConfig.password = authOverride.password;
124
+ }
125
+ if (authOverride.allowTailscale !== undefined) {
126
+ authConfig.allowTailscale = authOverride.allowTailscale;
127
+ }
128
+ if (authOverride.rateLimit !== undefined) {
129
+ authConfig.rateLimit = authOverride.rateLimit;
130
+ }
131
+ if (authOverride.trustedProxy !== undefined) {
132
+ authConfig.trustedProxy = authOverride.trustedProxy;
133
+ }
134
+ }
108
135
  const env = params.env ?? process.env;
109
136
  const token = authConfig.token ?? env.POOLBOT_GATEWAY_TOKEN ?? undefined;
110
137
  const password = authConfig.password ?? env.POOLBOT_GATEWAY_PASSWORD ?? undefined;
111
138
  const trustedProxy = authConfig.trustedProxy;
112
139
  let mode;
113
- if (authConfig.mode) {
140
+ let modeSource;
141
+ if (authOverride?.mode !== undefined) {
142
+ mode = authOverride.mode;
143
+ modeSource = "override";
144
+ }
145
+ else if (authConfig.mode) {
114
146
  mode = authConfig.mode;
147
+ modeSource = "config";
115
148
  }
116
149
  else if (password) {
117
150
  mode = "password";
151
+ modeSource = "password";
118
152
  }
119
153
  else if (token) {
120
154
  mode = "token";
155
+ modeSource = "token";
121
156
  }
122
157
  else {
123
- mode = "none";
158
+ mode = "token";
159
+ modeSource = "default";
124
160
  }
125
161
  const allowTailscale = authConfig.allowTailscale ??
126
162
  (params.tailscaleMode === "serve" && mode !== "password" && mode !== "trusted-proxy");
127
163
  return {
128
164
  mode,
165
+ modeSource,
129
166
  token,
130
167
  password,
131
168
  allowTailscale,
@@ -182,10 +219,15 @@ function authorizeTrustedProxy(params) {
182
219
  }
183
220
  return { user };
184
221
  }
222
+ function shouldAllowTailscaleHeaderAuth(authSurface) {
223
+ return authSurface === "ws-control-ui";
224
+ }
185
225
  export async function authorizeGatewayConnect(params) {
186
226
  const { auth, connectAuth, req, trustedProxies } = params;
187
227
  const tailscaleWhois = params.tailscaleWhois ?? readTailscaleWhoisIdentity;
188
- const localDirect = isLocalDirectRequest(req, trustedProxies);
228
+ const authSurface = params.authSurface ?? "http";
229
+ const allowTailscaleHeaderAuth = shouldAllowTailscaleHeaderAuth(authSurface);
230
+ const localDirect = isLocalDirectRequest(req, trustedProxies, params.allowRealIpFallback === true);
189
231
  if (auth.mode === "trusted-proxy") {
190
232
  if (!auth.trustedProxy) {
191
233
  return { ok: false, reason: "trusted_proxy_config_missing" };
@@ -203,8 +245,13 @@ export async function authorizeGatewayConnect(params) {
203
245
  }
204
246
  return { ok: false, reason: result.reason };
205
247
  }
248
+ if (auth.mode === "none") {
249
+ return { ok: true, method: "none" };
250
+ }
206
251
  const limiter = params.rateLimiter;
207
- const ip = params.clientIp ?? resolveRequestClientIp(req, trustedProxies) ?? req?.socket?.remoteAddress;
252
+ const ip = params.clientIp ??
253
+ resolveRequestClientIp(req, trustedProxies, params.allowRealIpFallback === true) ??
254
+ req?.socket?.remoteAddress;
208
255
  const rateLimitScope = params.rateLimitScope ?? AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET;
209
256
  if (limiter) {
210
257
  const rlCheck = limiter.check(ip, rateLimitScope);
@@ -217,7 +264,7 @@ export async function authorizeGatewayConnect(params) {
217
264
  };
218
265
  }
219
266
  }
220
- if (auth.allowTailscale && !localDirect) {
267
+ if (allowTailscaleHeaderAuth && auth.allowTailscale && !localDirect) {
221
268
  const tailscaleCheck = await resolveVerifiedTailscaleUser({
222
269
  req,
223
270
  tailscaleWhois,
@@ -265,3 +312,15 @@ export async function authorizeGatewayConnect(params) {
265
312
  limiter?.recordFailure(ip, rateLimitScope);
266
313
  return { ok: false, reason: "unauthorized" };
267
314
  }
315
+ export async function authorizeHttpGatewayConnect(params) {
316
+ return authorizeGatewayConnect({
317
+ ...params,
318
+ authSurface: "http",
319
+ });
320
+ }
321
+ export async function authorizeWsControlUiGatewayConnect(params) {
322
+ return authorizeGatewayConnect({
323
+ ...params,
324
+ authSurface: "ws-control-ui",
325
+ });
326
+ }