@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,11 +1,11 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { loadConfig, resolveConfigPath, resolveGatewayPort, resolveStateDir, } from "../config/config.js";
3
3
  import { loadOrCreateDeviceIdentity } from "../infra/device-identity.js";
4
- import { pickPrimaryTailnetIPv4 } from "../infra/tailnet.js";
5
4
  import { loadGatewayTlsRuntime } from "../infra/tls/gateway.js";
6
5
  import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES, } from "../utils/message-channel.js";
7
6
  import { GatewayClient } from "./client.js";
8
- import { pickPrimaryLanIPv4 } from "./net.js";
7
+ import { CLI_DEFAULT_OPERATOR_SCOPES, resolveLeastPrivilegeOperatorScopesForMethod, } from "./method-scopes.js";
8
+ import { isSecureWebSocketUrl } from "./net.js";
9
9
  import { PROTOCOL_VERSION } from "./protocol/index.js";
10
10
  export function resolveExplicitGatewayAuth(opts) {
11
11
  const token = typeof opts?.token === "string" && opts.token.trim().length > 0 ? opts.token.trim() : undefined;
@@ -37,17 +37,10 @@ export function buildGatewayConnectionDetails(options = {}) {
37
37
  const remote = isRemoteMode ? config.gateway?.remote : undefined;
38
38
  const tlsEnabled = config.gateway?.tls?.enabled === true;
39
39
  const localPort = resolveGatewayPort(config);
40
- const tailnetIPv4 = pickPrimaryTailnetIPv4();
41
40
  const bindMode = config.gateway?.bind ?? "loopback";
42
- const preferTailnet = bindMode === "tailnet" && !!tailnetIPv4;
43
- const preferLan = bindMode === "lan";
44
- const lanIPv4 = preferLan ? pickPrimaryLanIPv4() : undefined;
45
41
  const scheme = tlsEnabled ? "wss" : "ws";
46
- const localUrl = preferTailnet && tailnetIPv4
47
- ? `${scheme}://${tailnetIPv4}:${localPort}`
48
- : preferLan && lanIPv4
49
- ? `${scheme}://${lanIPv4}:${localPort}`
50
- : `${scheme}://127.0.0.1:${localPort}`;
42
+ // Self-connections should always target loopback; bind mode only controls listener exposure.
43
+ const localUrl = `${scheme}://127.0.0.1:${localPort}`;
51
44
  const urlOverride = typeof options.url === "string" && options.url.trim().length > 0
52
45
  ? options.url.trim()
53
46
  : undefined;
@@ -60,15 +53,23 @@ export function buildGatewayConnectionDetails(options = {}) {
60
53
  ? "config gateway.remote.url"
61
54
  : remoteMisconfigured
62
55
  ? "missing gateway.remote.url (fallback local)"
63
- : preferTailnet && tailnetIPv4
64
- ? `local tailnet ${tailnetIPv4}`
65
- : preferLan && lanIPv4
66
- ? `local lan ${lanIPv4}`
67
- : "local loopback";
56
+ : "local loopback";
68
57
  const remoteFallbackNote = remoteMisconfigured
69
58
  ? "Warn: gateway.mode=remote but gateway.remote.url is missing; set gateway.remote.url or switch gateway.mode=local."
70
59
  : undefined;
71
60
  const bindDetail = !urlOverride && !remoteUrl ? `Bind: ${bindMode}` : undefined;
61
+ // Security check: block ALL insecure ws:// to non-loopback addresses (CWE-319, CVSS 9.8)
62
+ // This applies to the FINAL resolved URL, regardless of source (config, CLI override, etc).
63
+ // Both credentials and chat/conversation data must not be transmitted over plaintext to remote hosts.
64
+ if (!isSecureWebSocketUrl(url)) {
65
+ throw new Error([
66
+ `SECURITY ERROR: Gateway URL "${url}" uses plaintext ws:// to a non-loopback address.`,
67
+ "Both credentials and chat data would be exposed to network interception.",
68
+ `Source: ${urlSource}`,
69
+ `Config: ${configPath}`,
70
+ "Fix: Use wss:// for the gateway URL, or connect via SSH tunnel to localhost.",
71
+ ].join("\n"));
72
+ }
72
73
  const message = [
73
74
  `Gateway target: ${url}`,
74
75
  `Source: ${urlSource}`,
@@ -86,77 +87,89 @@ export function buildGatewayConnectionDetails(options = {}) {
86
87
  message,
87
88
  };
88
89
  }
89
- export async function callGateway(opts) {
90
- const timeoutMs = typeof opts.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : 10_000;
90
+ function trimToUndefined(value) {
91
+ if (typeof value !== "string") {
92
+ return undefined;
93
+ }
94
+ const trimmed = value.trim();
95
+ return trimmed.length > 0 ? trimmed : undefined;
96
+ }
97
+ function resolveGatewayCallTimeout(timeoutValue) {
98
+ const timeoutMs = typeof timeoutValue === "number" && Number.isFinite(timeoutValue) ? timeoutValue : 10_000;
91
99
  const safeTimerTimeoutMs = Math.max(1, Math.min(Math.floor(timeoutMs), 2_147_483_647));
100
+ return { timeoutMs, safeTimerTimeoutMs };
101
+ }
102
+ function resolveGatewayCallContext(opts) {
92
103
  const config = opts.config ?? loadConfig();
104
+ const configPath = opts.configPath ?? resolveConfigPath(process.env, resolveStateDir(process.env));
93
105
  const isRemoteMode = config.gateway?.mode === "remote";
94
- const remote = isRemoteMode ? config.gateway?.remote : undefined;
95
- const urlOverride = typeof opts.url === "string" && opts.url.trim().length > 0 ? opts.url.trim() : undefined;
106
+ const remote = isRemoteMode
107
+ ? config.gateway?.remote
108
+ : undefined;
109
+ const urlOverride = trimToUndefined(opts.url);
110
+ const remoteUrl = trimToUndefined(remote?.url);
96
111
  const explicitAuth = resolveExplicitGatewayAuth({ token: opts.token, password: opts.password });
97
- ensureExplicitGatewayAuth({
98
- urlOverride,
99
- auth: explicitAuth,
100
- errorHint: "Fix: pass --token or --password (or gatewayToken in tools).",
101
- configPath: opts.configPath ?? resolveConfigPath(process.env, resolveStateDir(process.env)),
102
- });
103
- const remoteUrl = typeof remote?.url === "string" && remote.url.trim().length > 0 ? remote.url.trim() : undefined;
104
- if (isRemoteMode && !urlOverride && !remoteUrl) {
105
- const configPath = opts.configPath ?? resolveConfigPath(process.env, resolveStateDir(process.env));
106
- throw new Error([
107
- "gateway remote mode misconfigured: gateway.remote.url missing",
108
- `Config: ${configPath}`,
109
- "Fix: set gateway.remote.url, or set gateway.mode=local.",
110
- ].join("\n"));
112
+ return { config, configPath, isRemoteMode, remote, urlOverride, remoteUrl, explicitAuth };
113
+ }
114
+ function ensureRemoteModeUrlConfigured(context) {
115
+ if (!context.isRemoteMode || context.urlOverride || context.remoteUrl) {
116
+ return;
111
117
  }
112
- const authToken = config.gateway?.auth?.token;
113
- const authPassword = config.gateway?.auth?.password;
114
- const connectionDetails = buildGatewayConnectionDetails({
115
- config,
116
- url: urlOverride,
117
- ...(opts.configPath ? { configPath: opts.configPath } : {}),
118
- });
119
- const url = connectionDetails.url;
120
- const useLocalTls = config.gateway?.tls?.enabled === true && !urlOverride && !remoteUrl && url.startsWith("wss://");
121
- const tlsRuntime = useLocalTls ? await loadGatewayTlsRuntime(config.gateway?.tls) : undefined;
122
- const remoteTlsFingerprint = isRemoteMode && !urlOverride && remoteUrl && typeof remote?.tlsFingerprint === "string"
123
- ? remote.tlsFingerprint.trim()
124
- : undefined;
125
- const overrideTlsFingerprint = typeof opts.tlsFingerprint === "string" ? opts.tlsFingerprint.trim() : undefined;
126
- const tlsFingerprint = overrideTlsFingerprint ||
127
- remoteTlsFingerprint ||
128
- (tlsRuntime?.enabled ? tlsRuntime.fingerprintSha256 : undefined);
129
- const token = explicitAuth.token ||
130
- (!urlOverride
131
- ? isRemoteMode
132
- ? typeof remote?.token === "string" && remote.token.trim().length > 0
133
- ? remote.token.trim()
134
- : undefined
135
- : process.env.POOLBOT_GATEWAY_TOKEN?.trim() ||
136
- process.env.CLAWDBOT_GATEWAY_TOKEN?.trim() ||
137
- (typeof authToken === "string" && authToken.trim().length > 0
138
- ? authToken.trim()
139
- : undefined)
118
+ throw new Error([
119
+ "gateway remote mode misconfigured: gateway.remote.url missing",
120
+ `Config: ${context.configPath}`,
121
+ "Fix: set gateway.remote.url, or set gateway.mode=local.",
122
+ ].join("\n"));
123
+ }
124
+ function resolveGatewayCredentials(context) {
125
+ const authToken = context.config.gateway?.auth?.token;
126
+ const authPassword = context.config.gateway?.auth?.password;
127
+ const token = context.explicitAuth.token ||
128
+ (!context.urlOverride
129
+ ? context.isRemoteMode
130
+ ? trimToUndefined(context.remote?.token)
131
+ : trimToUndefined(process.env.POOLBOT_GATEWAY_TOKEN) ||
132
+ trimToUndefined(process.env.CLAWDBOT_GATEWAY_TOKEN) ||
133
+ trimToUndefined(authToken)
140
134
  : undefined);
141
- const password = explicitAuth.password ||
142
- (!urlOverride
143
- ? process.env.POOLBOT_GATEWAY_PASSWORD?.trim() ||
144
- process.env.CLAWDBOT_GATEWAY_PASSWORD?.trim() ||
145
- (isRemoteMode
146
- ? typeof remote?.password === "string" && remote.password.trim().length > 0
147
- ? remote.password.trim()
148
- : undefined
149
- : typeof authPassword === "string" && authPassword.trim().length > 0
150
- ? authPassword.trim()
151
- : undefined)
135
+ const password = context.explicitAuth.password ||
136
+ (!context.urlOverride
137
+ ? trimToUndefined(process.env.POOLBOT_GATEWAY_PASSWORD) ||
138
+ trimToUndefined(process.env.CLAWDBOT_GATEWAY_PASSWORD) ||
139
+ (context.isRemoteMode
140
+ ? trimToUndefined(context.remote?.password)
141
+ : trimToUndefined(authPassword))
152
142
  : undefined);
153
- const formatCloseError = (code, reason) => {
154
- const reasonText = reason?.trim() || "no close reason";
155
- const hint = code === 1006 ? "abnormal closure (no close frame)" : code === 1000 ? "normal closure" : "";
156
- const suffix = hint ? ` ${hint}` : "";
157
- return `gateway closed (${code}${suffix}): ${reasonText}\n${connectionDetails.message}`;
158
- };
159
- const formatTimeoutError = () => `gateway timeout after ${timeoutMs}ms\n${connectionDetails.message}`;
143
+ return { token, password };
144
+ }
145
+ async function resolveGatewayTlsFingerprint(params) {
146
+ const { opts, context, url } = params;
147
+ const useLocalTls = context.config.gateway?.tls?.enabled === true &&
148
+ !context.urlOverride &&
149
+ !context.remoteUrl &&
150
+ url.startsWith("wss://");
151
+ const tlsRuntime = useLocalTls
152
+ ? await loadGatewayTlsRuntime(context.config.gateway?.tls)
153
+ : undefined;
154
+ const overrideTlsFingerprint = trimToUndefined(opts.tlsFingerprint);
155
+ const remoteTlsFingerprint = context.isRemoteMode && !context.urlOverride && context.remoteUrl
156
+ ? trimToUndefined(context.remote?.tlsFingerprint)
157
+ : undefined;
158
+ return (overrideTlsFingerprint ||
159
+ remoteTlsFingerprint ||
160
+ (tlsRuntime?.enabled ? tlsRuntime.fingerprintSha256 : undefined));
161
+ }
162
+ function formatGatewayCloseError(code, reason, connectionDetails) {
163
+ const reasonText = reason?.trim() || "no close reason";
164
+ const hint = code === 1006 ? "abnormal closure (no close frame)" : code === 1000 ? "normal closure" : "";
165
+ const suffix = hint ? ` ${hint}` : "";
166
+ return `gateway closed (${code}${suffix}): ${reasonText}\n${connectionDetails.message}`;
167
+ }
168
+ function formatGatewayTimeoutError(timeoutMs, connectionDetails) {
169
+ return `gateway timeout after ${timeoutMs}ms\n${connectionDetails.message}`;
170
+ }
171
+ async function executeGatewayRequestWithScopes(params) {
172
+ const { opts, scopes, url, token, password, tlsFingerprint, timeoutMs, safeTimerTimeoutMs } = params;
160
173
  return await new Promise((resolve, reject) => {
161
174
  let settled = false;
162
175
  let ignoreClose = false;
@@ -185,7 +198,7 @@ export async function callGateway(opts) {
185
198
  platform: opts.platform,
186
199
  mode: opts.mode ?? GATEWAY_CLIENT_MODES.CLI,
187
200
  role: "operator",
188
- scopes: ["operator.admin", "operator.approvals", "operator.pairing"],
201
+ scopes,
189
202
  deviceIdentity: loadOrCreateDeviceIdentity(),
190
203
  minProtocol: opts.minProtocol ?? PROTOCOL_VERSION,
191
204
  maxProtocol: opts.maxProtocol ?? PROTOCOL_VERSION,
@@ -210,17 +223,73 @@ export async function callGateway(opts) {
210
223
  }
211
224
  ignoreClose = true;
212
225
  client.stop();
213
- stop(new Error(formatCloseError(code, reason)));
226
+ stop(new Error(formatGatewayCloseError(code, reason, params.connectionDetails)));
214
227
  },
215
228
  });
216
229
  const timer = setTimeout(() => {
217
230
  ignoreClose = true;
218
231
  client.stop();
219
- stop(new Error(formatTimeoutError()));
232
+ stop(new Error(formatGatewayTimeoutError(timeoutMs, params.connectionDetails)));
220
233
  }, safeTimerTimeoutMs);
221
234
  client.start();
222
235
  });
223
236
  }
237
+ async function callGatewayWithScopes(opts, scopes) {
238
+ const { timeoutMs, safeTimerTimeoutMs } = resolveGatewayCallTimeout(opts.timeoutMs);
239
+ const context = resolveGatewayCallContext(opts);
240
+ ensureExplicitGatewayAuth({
241
+ urlOverride: context.urlOverride,
242
+ auth: context.explicitAuth,
243
+ errorHint: "Fix: pass --token or --password (or gatewayToken in tools).",
244
+ configPath: context.configPath,
245
+ });
246
+ ensureRemoteModeUrlConfigured(context);
247
+ const connectionDetails = buildGatewayConnectionDetails({
248
+ config: context.config,
249
+ url: context.urlOverride,
250
+ ...(opts.configPath ? { configPath: opts.configPath } : {}),
251
+ });
252
+ const url = connectionDetails.url;
253
+ const tlsFingerprint = await resolveGatewayTlsFingerprint({ opts, context, url });
254
+ const { token, password } = resolveGatewayCredentials(context);
255
+ return await executeGatewayRequestWithScopes({
256
+ opts,
257
+ scopes,
258
+ url,
259
+ token,
260
+ password,
261
+ tlsFingerprint,
262
+ timeoutMs,
263
+ safeTimerTimeoutMs,
264
+ connectionDetails,
265
+ });
266
+ }
267
+ export async function callGatewayScoped(opts) {
268
+ return await callGatewayWithScopes(opts, opts.scopes);
269
+ }
270
+ export async function callGatewayCli(opts) {
271
+ const scopes = Array.isArray(opts.scopes) ? opts.scopes : CLI_DEFAULT_OPERATOR_SCOPES;
272
+ return await callGatewayWithScopes(opts, scopes);
273
+ }
274
+ export async function callGatewayLeastPrivilege(opts) {
275
+ const scopes = resolveLeastPrivilegeOperatorScopesForMethod(opts.method);
276
+ return await callGatewayWithScopes(opts, scopes);
277
+ }
278
+ export async function callGateway(opts) {
279
+ if (Array.isArray(opts.scopes)) {
280
+ return await callGatewayWithScopes(opts, opts.scopes);
281
+ }
282
+ const callerMode = opts.mode ?? GATEWAY_CLIENT_MODES.BACKEND;
283
+ const callerName = opts.clientName ?? GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT;
284
+ if (callerMode === GATEWAY_CLIENT_MODES.CLI || callerName === GATEWAY_CLIENT_NAMES.CLI) {
285
+ return await callGatewayCli(opts);
286
+ }
287
+ return await callGatewayLeastPrivilege({
288
+ ...opts,
289
+ mode: callerMode,
290
+ clientName: callerName,
291
+ });
292
+ }
224
293
  export function randomIdempotencyKey() {
225
294
  return randomUUID();
226
295
  }
@@ -0,0 +1,75 @@
1
+ import { randomBytes } from "node:crypto";
2
+ export const CANVAS_CAPABILITY_PATH_PREFIX = "/__poolbot__/cap";
3
+ export const CANVAS_CAPABILITY_QUERY_PARAM = "oc_cap";
4
+ export const CANVAS_CAPABILITY_TTL_MS = 10 * 60_000;
5
+ function normalizeCapability(raw) {
6
+ const trimmed = raw?.trim();
7
+ return trimmed ? trimmed : undefined;
8
+ }
9
+ export function mintCanvasCapabilityToken() {
10
+ return randomBytes(18).toString("base64url");
11
+ }
12
+ export function buildCanvasScopedHostUrl(baseUrl, capability) {
13
+ const normalizedCapability = normalizeCapability(capability);
14
+ if (!normalizedCapability) {
15
+ return undefined;
16
+ }
17
+ try {
18
+ const url = new URL(baseUrl);
19
+ const trimmedPath = url.pathname.replace(/\/+$/, "");
20
+ const prefix = `${CANVAS_CAPABILITY_PATH_PREFIX}/${encodeURIComponent(normalizedCapability)}`;
21
+ url.pathname = `${trimmedPath}${prefix}`;
22
+ url.search = "";
23
+ url.hash = "";
24
+ return url.toString().replace(/\/$/, "");
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
30
+ export function normalizeCanvasScopedUrl(rawUrl) {
31
+ const url = new URL(rawUrl, "http://localhost");
32
+ const prefix = `${CANVAS_CAPABILITY_PATH_PREFIX}/`;
33
+ let scopedPath = false;
34
+ let malformedScopedPath = false;
35
+ let capabilityFromPath;
36
+ let rewrittenUrl;
37
+ if (url.pathname.startsWith(prefix)) {
38
+ scopedPath = true;
39
+ const remainder = url.pathname.slice(prefix.length);
40
+ const slashIndex = remainder.indexOf("/");
41
+ if (slashIndex <= 0) {
42
+ malformedScopedPath = true;
43
+ }
44
+ else {
45
+ const encodedCapability = remainder.slice(0, slashIndex);
46
+ const canonicalPath = remainder.slice(slashIndex) || "/";
47
+ let decoded;
48
+ try {
49
+ decoded = decodeURIComponent(encodedCapability);
50
+ }
51
+ catch {
52
+ malformedScopedPath = true;
53
+ }
54
+ capabilityFromPath = normalizeCapability(decoded);
55
+ if (!capabilityFromPath || !canonicalPath.startsWith("/")) {
56
+ malformedScopedPath = true;
57
+ }
58
+ else {
59
+ url.pathname = canonicalPath;
60
+ if (!url.searchParams.has(CANVAS_CAPABILITY_QUERY_PARAM)) {
61
+ url.searchParams.set(CANVAS_CAPABILITY_QUERY_PARAM, capabilityFromPath);
62
+ }
63
+ rewrittenUrl = `${url.pathname}${url.search}`;
64
+ }
65
+ }
66
+ }
67
+ const capability = capabilityFromPath ?? normalizeCapability(url.searchParams.get(CANVAS_CAPABILITY_QUERY_PARAM));
68
+ return {
69
+ pathname: url.pathname,
70
+ capability,
71
+ rewrittenUrl,
72
+ scopedPath,
73
+ malformedScopedPath,
74
+ };
75
+ }
@@ -2,11 +2,13 @@ import { randomUUID } from "node:crypto";
2
2
  import { WebSocket } from "ws";
3
3
  import { clearDeviceAuthToken, loadDeviceAuthToken, storeDeviceAuthToken, } from "../infra/device-auth-store.js";
4
4
  import { loadOrCreateDeviceIdentity, publicKeyRawBase64UrlFromPem, signDevicePayload, } from "../infra/device-identity.js";
5
+ import { clearDevicePairing } from "../infra/device-pairing.js";
5
6
  import { normalizeFingerprint } from "../infra/tls/fingerprint.js";
6
7
  import { rawDataToString } from "../infra/ws.js";
7
8
  import { logDebug, logError } from "../logger.js";
8
9
  import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES, } from "../utils/message-channel.js";
9
10
  import { buildDeviceAuthPayload } from "./device-auth.js";
11
+ import { isSecureWebSocketUrl } from "./net.js";
10
12
  import { PROTOCOL_VERSION, validateEventFrame, validateRequestFrame, validateResponseFrame, } from "./protocol/index.js";
11
13
  export const GATEWAY_CLOSE_CODE_HINTS = {
12
14
  1000: "normal closure",
@@ -46,6 +48,24 @@ export class GatewayClient {
46
48
  this.opts.onConnectError?.(new Error("gateway tls fingerprint requires wss:// gateway url"));
47
49
  return;
48
50
  }
51
+ // Security check: block ALL plaintext ws:// to non-loopback addresses (CWE-319, CVSS 9.8)
52
+ // This protects both credentials AND chat/conversation data from MITM attacks.
53
+ // Device tokens may be loaded later in sendConnect(), so we block regardless of hasCredentials.
54
+ if (!isSecureWebSocketUrl(url)) {
55
+ // Safe hostname extraction - avoid throwing on malformed URLs in error path
56
+ let displayHost = url;
57
+ try {
58
+ displayHost = new URL(url).hostname || url;
59
+ }
60
+ catch {
61
+ // Use raw URL if parsing fails
62
+ }
63
+ const error = new Error(`SECURITY ERROR: Cannot connect to "${displayHost}" over plaintext ws://. ` +
64
+ "Both credentials and chat data would be exposed to network interception. " +
65
+ "Use wss:// for the gateway URL, or connect via SSH tunnel to localhost.");
66
+ this.opts.onConnectError?.(error);
67
+ return;
68
+ }
49
69
  // Allow node screen snapshots and other large responses.
50
70
  const wsOptions = {
51
71
  maxPayload: 25 * 1024 * 1024,
@@ -87,17 +107,21 @@ export class GatewayClient {
87
107
  this.ws.on("close", (code, reason) => {
88
108
  const reasonText = rawDataToString(reason);
89
109
  this.ws = null;
90
- // If closed due to device token mismatch, clear the stored token so next attempt can get a fresh one
110
+ // If closed due to device token mismatch, clear the stored token and pairing so next attempt can get a fresh one
91
111
  if (code === 1008 &&
92
112
  reasonText.toLowerCase().includes("device token mismatch") &&
93
113
  this.opts.deviceIdentity) {
114
+ const deviceId = this.opts.deviceIdentity.deviceId;
94
115
  const role = this.opts.role ?? "operator";
95
116
  try {
96
- clearDeviceAuthToken({ deviceId: this.opts.deviceIdentity.deviceId, role });
97
- logDebug(`cleared stale device-auth token for device ${this.opts.deviceIdentity.deviceId}`);
117
+ clearDeviceAuthToken({ deviceId, role });
118
+ void clearDevicePairing(deviceId).catch((err) => {
119
+ logDebug(`failed clearing stale device pairing for device ${deviceId}: ${String(err)}`);
120
+ });
121
+ logDebug(`cleared stale device-auth token for device ${deviceId}`);
98
122
  }
99
123
  catch (err) {
100
- logDebug(`failed clearing stale device-auth token for device ${this.opts.deviceIdentity.deviceId}: ${String(err)}`);
124
+ logDebug(`failed clearing stale device-auth token for device ${deviceId}: ${String(err)}`);
101
125
  }
102
126
  }
103
127
  this.flushPendingErrors(new Error(`gateway closed (${code}): ${reasonText}`));
@@ -1,3 +1,4 @@
1
+ import { isDeepStrictEqual } from "node:util";
1
2
  import chokidar from "chokidar";
2
3
  import { listChannelPlugins } from "../channels/plugins/index.js";
3
4
  import { getActivePluginRegistry } from "../plugins/runtime.js";
@@ -102,10 +103,8 @@ export function diffConfigPaths(prev, next, prefix = "") {
102
103
  }
103
104
  return paths;
104
105
  }
105
- if (Array.isArray(prev) && Array.isArray(next)) {
106
- if (prev.length === next.length && prev.every((val, idx) => val === next[idx])) {
107
- return [];
108
- }
106
+ if (isDeepStrictEqual(prev, next)) {
107
+ return [];
109
108
  }
110
109
  return [prefix || "<root>"];
111
110
  }
@@ -0,0 +1,28 @@
1
+ function normalizePart(value, fallback) {
2
+ if (typeof value !== "string") {
3
+ return fallback;
4
+ }
5
+ const normalized = value.trim();
6
+ return normalized.length > 0 ? normalized : fallback;
7
+ }
8
+ export function resolveControlPlaneActor(client) {
9
+ return {
10
+ actor: normalizePart(client?.connect?.client?.id, "unknown-actor"),
11
+ deviceId: normalizePart(client?.connect?.device?.id, "unknown-device"),
12
+ clientIp: normalizePart(client?.clientIp, "unknown-ip"),
13
+ connId: normalizePart(client?.connId, "unknown-conn"),
14
+ };
15
+ }
16
+ export function formatControlPlaneActor(actor) {
17
+ return `actor=${actor.actor} device=${actor.deviceId} ip=${actor.clientIp} conn=${actor.connId}`;
18
+ }
19
+ export function summarizeChangedPaths(paths, maxPaths = 8) {
20
+ if (paths.length === 0) {
21
+ return "<none>";
22
+ }
23
+ if (paths.length <= maxPaths) {
24
+ return paths.join(",");
25
+ }
26
+ const head = paths.slice(0, maxPaths).join(",");
27
+ return `${head},+${paths.length - maxPaths} more`;
28
+ }
@@ -0,0 +1,53 @@
1
+ const CONTROL_PLANE_RATE_LIMIT_MAX_REQUESTS = 3;
2
+ const CONTROL_PLANE_RATE_LIMIT_WINDOW_MS = 60_000;
3
+ const controlPlaneBuckets = new Map();
4
+ function normalizePart(value, fallback) {
5
+ if (typeof value !== "string") {
6
+ return fallback;
7
+ }
8
+ const normalized = value.trim();
9
+ return normalized.length > 0 ? normalized : fallback;
10
+ }
11
+ export function resolveControlPlaneRateLimitKey(client) {
12
+ const deviceId = normalizePart(client?.connect?.device?.id, "unknown-device");
13
+ const clientIp = normalizePart(client?.clientIp, "unknown-ip");
14
+ return `${deviceId}|${clientIp}`;
15
+ }
16
+ export function consumeControlPlaneWriteBudget(params) {
17
+ const nowMs = params.nowMs ?? Date.now();
18
+ const key = resolveControlPlaneRateLimitKey(params.client);
19
+ const bucket = controlPlaneBuckets.get(key);
20
+ if (!bucket || nowMs - bucket.windowStartMs >= CONTROL_PLANE_RATE_LIMIT_WINDOW_MS) {
21
+ controlPlaneBuckets.set(key, {
22
+ count: 1,
23
+ windowStartMs: nowMs,
24
+ });
25
+ return {
26
+ allowed: true,
27
+ retryAfterMs: 0,
28
+ remaining: CONTROL_PLANE_RATE_LIMIT_MAX_REQUESTS - 1,
29
+ key,
30
+ };
31
+ }
32
+ if (bucket.count >= CONTROL_PLANE_RATE_LIMIT_MAX_REQUESTS) {
33
+ const retryAfterMs = Math.max(0, bucket.windowStartMs + CONTROL_PLANE_RATE_LIMIT_WINDOW_MS - nowMs);
34
+ return {
35
+ allowed: false,
36
+ retryAfterMs,
37
+ remaining: 0,
38
+ key,
39
+ };
40
+ }
41
+ bucket.count += 1;
42
+ return {
43
+ allowed: true,
44
+ retryAfterMs: 0,
45
+ remaining: Math.max(0, CONTROL_PLANE_RATE_LIMIT_MAX_REQUESTS - bucket.count),
46
+ key,
47
+ };
48
+ }
49
+ export const __testing = {
50
+ resetControlPlaneRateLimitState() {
51
+ controlPlaneBuckets.clear();
52
+ },
53
+ };