@poolzin/pool-bot 2026.2.25 → 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 (506) hide show
  1. package/dist/acp/event-mapper.js +87 -22
  2. package/dist/acp/meta.js +12 -6
  3. package/dist/agents/agent-paths.js +8 -9
  4. package/dist/agents/agent-scope.js +7 -5
  5. package/dist/agents/auth-profiles/oauth.js +148 -64
  6. package/dist/agents/auth-profiles/session-override.js +13 -7
  7. package/dist/agents/bash-tools.exec-host-gateway.js +14 -4
  8. package/dist/agents/bash-tools.exec-runtime.js +2 -25
  9. package/dist/agents/bedrock-discovery.js +3 -1
  10. package/dist/agents/byteplus-models.js +97 -0
  11. package/dist/agents/chutes-oauth.js +1 -0
  12. package/dist/agents/cli-runner/helpers.js +4 -0
  13. package/dist/agents/compaction.js +41 -14
  14. package/dist/agents/doubao-models.js +121 -0
  15. package/dist/agents/failover-error.js +2 -0
  16. package/dist/agents/huggingface-models.js +5 -3
  17. package/dist/agents/live-model-filter.js +5 -0
  18. package/dist/agents/minimax-vlm.js +10 -8
  19. package/dist/agents/model-auth.js +6 -0
  20. package/dist/agents/model-catalog.js +3 -1
  21. package/dist/agents/model-selection.js +7 -1
  22. package/dist/agents/models-config.providers.js +93 -11
  23. package/dist/agents/ollama-stream.js +117 -4
  24. package/dist/agents/opencode-zen-models.js +22 -11
  25. package/dist/agents/pi-embedded-helpers/errors.js +55 -33
  26. package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
  27. package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
  28. package/dist/agents/pi-embedded-helpers.js +1 -1
  29. package/dist/agents/pi-embedded-runner/compact.js +29 -7
  30. package/dist/agents/pi-embedded-runner/extensions.js +28 -26
  31. package/dist/agents/pi-embedded-runner/google.js +20 -8
  32. package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
  33. package/dist/agents/pi-embedded-runner/run.js +71 -12
  34. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
  35. package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
  36. package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
  37. package/dist/agents/pi-embedded-runner/thinking.js +42 -0
  38. package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
  39. package/dist/agents/pi-embedded-runner/utils.js +7 -10
  40. package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
  41. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
  42. package/dist/agents/pi-embedded-subscribe.js +9 -4
  43. package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
  44. package/dist/agents/pi-embedded-utils.js +3 -0
  45. package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
  46. package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
  47. package/dist/agents/pi-settings.js +40 -0
  48. package/dist/agents/pi-tools.policy.js +2 -1
  49. package/dist/agents/provider/config-loader.js +1 -1
  50. package/dist/agents/sandbox/browser.js +170 -33
  51. package/dist/agents/sandbox/config-hash.js +14 -27
  52. package/dist/agents/sandbox/config.js +21 -2
  53. package/dist/agents/sandbox/constants.js +2 -0
  54. package/dist/agents/sandbox/docker.js +16 -2
  55. package/dist/agents/sandbox/novnc-auth.js +62 -0
  56. package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
  57. package/dist/agents/sandbox/shared.js +10 -6
  58. package/dist/agents/sandbox-paths.js +24 -11
  59. package/dist/agents/schema/clean-for-gemini.js +132 -85
  60. package/dist/agents/session-slug.js +10 -5
  61. package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
  62. package/dist/agents/session-tool-result-guard.js +3 -1
  63. package/dist/agents/session-transcript-repair.js +40 -6
  64. package/dist/agents/skills/bundled-dir.js +19 -5
  65. package/dist/agents/skills/env-overrides.js +124 -43
  66. package/dist/agents/skills/frontmatter.js +6 -6
  67. package/dist/agents/skills/plugin-skills.js +14 -7
  68. package/dist/agents/skills/workspace.js +1 -0
  69. package/dist/agents/subagent-announce.js +251 -49
  70. package/dist/agents/subagent-lifecycle-events.js +19 -0
  71. package/dist/agents/subagent-registry-cleanup.js +31 -0
  72. package/dist/agents/subagent-registry-completion.js +68 -0
  73. package/dist/agents/subagent-registry-queries.js +117 -0
  74. package/dist/agents/subagent-registry-state.js +46 -0
  75. package/dist/agents/subagent-registry.js +252 -221
  76. package/dist/agents/subagent-registry.store.js +1 -0
  77. package/dist/agents/subagent-registry.types.js +1 -0
  78. package/dist/agents/subagent-spawn.js +195 -7
  79. package/dist/agents/system-prompt.js +22 -6
  80. package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
  81. package/dist/agents/test-helpers/fast-core-tools.js +1 -17
  82. package/dist/agents/timeout.js +18 -6
  83. package/dist/agents/tool-call-id.js +1 -1
  84. package/dist/agents/tool-display-common.js +162 -29
  85. package/dist/agents/tool-images.js +82 -9
  86. package/dist/agents/tool-policy.js +51 -26
  87. package/dist/agents/tools/browser-tool.js +2 -2
  88. package/dist/agents/tools/canvas-tool.js +27 -1
  89. package/dist/agents/tools/common.js +45 -0
  90. package/dist/agents/tools/discord-actions-guild.js +4 -1
  91. package/dist/agents/tools/gateway-tool.js +3 -1
  92. package/dist/agents/tools/nodes-utils.js +1 -10
  93. package/dist/agents/tools/sessions-send-helpers.js +12 -6
  94. package/dist/agents/tools/sessions-spawn-tool.js +8 -2
  95. package/dist/agents/tools/subagents-tool.js +2 -1
  96. package/dist/agents/tools/whatsapp-actions.js +10 -2
  97. package/dist/agents/tools/whatsapp-target-auth.js +18 -0
  98. package/dist/agents/transcript-policy.js +22 -8
  99. package/dist/agents/venice-models.js +11 -3
  100. package/dist/auto-reply/commands-registry.data.js +51 -0
  101. package/dist/auto-reply/commands-registry.js +4 -3
  102. package/dist/auto-reply/group-activation.js +10 -5
  103. package/dist/auto-reply/inbound-debounce.js +10 -5
  104. package/dist/auto-reply/reply/abort.js +1 -1
  105. package/dist/auto-reply/reply/agent-runner-execution.js +4 -1
  106. package/dist/auto-reply/reply/bash-command.js +41 -39
  107. package/dist/auto-reply/reply/command-gates.js +25 -0
  108. package/dist/auto-reply/reply/commands-allowlist.js +111 -72
  109. package/dist/auto-reply/reply/commands-bash.js +6 -5
  110. package/dist/auto-reply/reply/commands-config.js +30 -28
  111. package/dist/auto-reply/reply/commands-core.js +2 -1
  112. package/dist/auto-reply/reply/commands-info.js +1 -0
  113. package/dist/auto-reply/reply/commands-models.js +65 -14
  114. package/dist/auto-reply/reply/commands-session.js +237 -82
  115. package/dist/auto-reply/reply/commands-setunset.js +45 -0
  116. package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
  117. package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
  118. package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
  119. package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
  120. package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
  121. package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
  122. package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
  123. package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
  124. package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
  125. package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
  126. package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
  127. package/dist/auto-reply/reply/commands-subagents.js +51 -587
  128. package/dist/auto-reply/reply/commands-tts.js +10 -5
  129. package/dist/auto-reply/reply/config-value.js +10 -5
  130. package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
  131. package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
  132. package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
  133. package/dist/auto-reply/reply/followup-runner.js +1 -0
  134. package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
  135. package/dist/auto-reply/reply/get-reply-directives.js +17 -28
  136. package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
  137. package/dist/auto-reply/reply/get-reply.js +71 -12
  138. package/dist/auto-reply/reply/model-selection.js +80 -39
  139. package/dist/auto-reply/reply/queue/enqueue.js +10 -5
  140. package/dist/auto-reply/reply/queue/state.js +13 -12
  141. package/dist/auto-reply/reply/reply-payloads.js +67 -36
  142. package/dist/auto-reply/reply/reply-reference.js +9 -8
  143. package/dist/auto-reply/reply/route-reply.js +15 -8
  144. package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
  145. package/dist/auto-reply/reply/session.js +22 -6
  146. package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
  147. package/dist/auto-reply/reply/subagents-utils.js +56 -30
  148. package/dist/auto-reply/reply/typing.js +46 -21
  149. package/dist/auto-reply/send-policy.js +14 -7
  150. package/dist/auto-reply/status.js +140 -16
  151. package/dist/auto-reply/templating.js +10 -5
  152. package/dist/auto-reply/thinking.js +7 -16
  153. package/dist/auto-reply/tokens.js +21 -5
  154. package/dist/browser/bridge-server.js +36 -20
  155. package/dist/browser/cdp.helpers.js +7 -14
  156. package/dist/browser/cdp.js +35 -15
  157. package/dist/browser/chrome.profile-decoration.js +7 -4
  158. package/dist/browser/config.js +4 -0
  159. package/dist/browser/extension-relay-auth.js +55 -0
  160. package/dist/browser/extension-relay.js +74 -29
  161. package/dist/browser/navigation-guard.js +9 -1
  162. package/dist/browser/paths.js +77 -0
  163. package/dist/browser/profiles.js +13 -8
  164. package/dist/browser/pw-ai-module.js +10 -5
  165. package/dist/browser/pw-session.js +76 -39
  166. package/dist/browser/pw-tools-core.interactions.js +14 -7
  167. package/dist/browser/pw-tools-core.state.js +12 -6
  168. package/dist/browser/routes/agent.act.js +2 -2
  169. package/dist/browser/server-context.js +7 -0
  170. package/dist/build-info.json +3 -3
  171. package/dist/channels/allow-from.js +2 -1
  172. package/dist/channels/allowlists/resolve-utils.js +43 -19
  173. package/dist/channels/channel-config.js +14 -7
  174. package/dist/channels/draft-stream-loop.js +7 -0
  175. package/dist/channels/model-overrides.js +82 -0
  176. package/dist/channels/plugins/normalize/imessage.js +14 -7
  177. package/dist/channels/plugins/normalize/slack.js +10 -5
  178. package/dist/channels/plugins/normalize/telegram.js +14 -7
  179. package/dist/channels/plugins/outbound/discord.js +80 -8
  180. package/dist/channels/plugins/outbound/signal.js +11 -11
  181. package/dist/channels/plugins/setup-helpers.js +10 -5
  182. package/dist/channels/sender-label.js +14 -7
  183. package/dist/channels/session.js +4 -2
  184. package/dist/channels/status-reactions.js +297 -0
  185. package/dist/cli/banner.js +1 -1
  186. package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
  187. package/dist/cli/cli-name.js +11 -11
  188. package/dist/cli/cli-utils.js +13 -3
  189. package/dist/cli/command-format.js +1 -1
  190. package/dist/cli/config-cli.js +1 -1
  191. package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
  192. package/dist/cli/daemon-cli/lifecycle.js +64 -2
  193. package/dist/cli/daemon-cli/restart-health.js +126 -0
  194. package/dist/cli/daemon-cli/status.gather.js +9 -13
  195. package/dist/cli/daemon-cli/status.print.js +2 -10
  196. package/dist/cli/deps.js +27 -22
  197. package/dist/cli/gateway-cli/run-loop.js +23 -5
  198. package/dist/cli/node-cli/register.js +14 -5
  199. package/dist/cli/nodes-media-utils.js +7 -2
  200. package/dist/cli/outbound-send-deps.js +2 -9
  201. package/dist/cli/outbound-send-mapping.js +11 -0
  202. package/dist/cli/pairing-cli.js +40 -14
  203. package/dist/cli/plugins-cli.js +34 -41
  204. package/dist/cli/ports.js +11 -10
  205. package/dist/cli/program/command-registry.js +2 -11
  206. package/dist/cli/program/command-tree.js +16 -0
  207. package/dist/cli/program/preaction.js +13 -9
  208. package/dist/cli/program/register.configure.js +3 -18
  209. package/dist/cli/program/register.maintenance.js +2 -2
  210. package/dist/cli/program/register.onboard.js +2 -0
  211. package/dist/cli/program/register.status-health-sessions.js +16 -17
  212. package/dist/cli/program/register.subclis.js +93 -52
  213. package/dist/cli/route.js +11 -7
  214. package/dist/cli/system-cli.js +36 -46
  215. package/dist/cli/update-cli/shared.js +22 -9
  216. package/dist/cli/update-cli/update-command.js +89 -14
  217. package/dist/cli/update-cli/wizard.js +6 -12
  218. package/dist/commands/agent/run-context.js +18 -5
  219. package/dist/commands/agent/session-store.js +17 -4
  220. package/dist/commands/agent.js +22 -2
  221. package/dist/commands/agents.bindings.js +14 -7
  222. package/dist/commands/agents.commands.add.js +13 -9
  223. package/dist/commands/agents.commands.identity.js +12 -6
  224. package/dist/commands/agents.commands.list.js +11 -6
  225. package/dist/commands/agents.config.js +8 -10
  226. package/dist/commands/agents.providers.js +12 -6
  227. package/dist/commands/auth-choice-options.js +103 -75
  228. package/dist/commands/auth-choice.apply.byteplus.js +55 -0
  229. package/dist/commands/auth-choice.apply.js +4 -0
  230. package/dist/commands/auth-choice.apply.minimax.js +61 -13
  231. package/dist/commands/auth-choice.apply.openai.js +3 -1
  232. package/dist/commands/auth-choice.apply.volcengine.js +55 -0
  233. package/dist/commands/auth-choice.preferred-provider.js +2 -0
  234. package/dist/commands/channels/remove.js +13 -6
  235. package/dist/commands/channels/shared.js +4 -14
  236. package/dist/commands/configure.commands.js +14 -0
  237. package/dist/commands/configure.gateway.js +2 -4
  238. package/dist/commands/configure.js +1 -1
  239. package/dist/commands/configure.shared.js +11 -0
  240. package/dist/commands/daemon-install-helpers.js +2 -2
  241. package/dist/commands/dashboard.js +12 -10
  242. package/dist/commands/docs.js +14 -8
  243. package/dist/commands/doctor-config-flow.js +11 -9
  244. package/dist/commands/doctor-legacy-config.js +281 -0
  245. package/dist/commands/doctor-state-integrity.js +99 -23
  246. package/dist/commands/doctor-update.js +12 -9
  247. package/dist/commands/models/list.list-command.js +7 -5
  248. package/dist/commands/models/set-image.js +2 -21
  249. package/dist/commands/node-daemon-install-helpers.js +10 -8
  250. package/dist/commands/onboard-auth.config-minimax.js +54 -80
  251. package/dist/commands/onboard-auth.config-opencode.js +2 -18
  252. package/dist/commands/onboard-auth.credentials.js +90 -13
  253. package/dist/commands/onboard-auth.js +1 -1
  254. package/dist/commands/onboard-auth.models.js +6 -5
  255. package/dist/commands/onboard-hooks.js +1 -1
  256. package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
  257. package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
  258. package/dist/commands/onboard-provider-auth-flags.js +14 -0
  259. package/dist/commands/onboard-remote.js +14 -7
  260. package/dist/commands/onboard.js +11 -13
  261. package/dist/commands/sandbox-display.js +6 -5
  262. package/dist/commands/status-all/diagnosis.js +14 -10
  263. package/dist/commands/status-all/format.js +1 -0
  264. package/dist/commands/status.gateway-probe.js +1 -16
  265. package/dist/commands/systemd-linger.js +12 -6
  266. package/dist/config/agent-limits.js +2 -0
  267. package/dist/config/commands.js +30 -16
  268. package/dist/config/config-paths.js +9 -11
  269. package/dist/config/defaults.js +22 -2
  270. package/dist/config/discord-preview-streaming.js +104 -0
  271. package/dist/config/env-vars.js +37 -8
  272. package/dist/config/includes.js +4 -0
  273. package/dist/config/io.js +97 -12
  274. package/dist/config/legacy.migrations.part-1.js +189 -78
  275. package/dist/config/legacy.shared.js +3 -1
  276. package/dist/config/merge-patch.js +4 -0
  277. package/dist/config/prototype-keys.js +4 -0
  278. package/dist/config/schema.help.js +44 -7
  279. package/dist/config/schema.labels.js +38 -6
  280. package/dist/config/sessions/delivery-info.js +10 -3
  281. package/dist/config/sessions/main-session.js +10 -5
  282. package/dist/config/sessions/session-file.js +33 -0
  283. package/dist/config/sessions/session-key.js +10 -5
  284. package/dist/config/sessions/store.js +1 -1
  285. package/dist/config/sessions.js +1 -0
  286. package/dist/config/zod-schema.agent-runtime.js +11 -0
  287. package/dist/config/zod-schema.js +148 -13
  288. package/dist/config/zod-schema.providers-core.js +78 -4
  289. package/dist/config/zod-schema.providers.js +6 -1
  290. package/dist/config/zod-schema.session.js +41 -2
  291. package/dist/cron/run-log.js +3 -0
  292. package/dist/cron/schedule.js +21 -10
  293. package/dist/cron/service/ops.js +35 -21
  294. package/dist/cron/service/timer.js +116 -16
  295. package/dist/cron/stagger.js +3 -1
  296. package/dist/discord/api.js +12 -6
  297. package/dist/discord/draft-chunking.js +22 -0
  298. package/dist/discord/draft-stream.js +124 -0
  299. package/dist/discord/monitor/agent-components.js +1 -1
  300. package/dist/discord/monitor/commands.js +5 -0
  301. package/dist/discord/monitor/gateway-plugin.js +2 -1
  302. package/dist/discord/monitor/listeners.js +37 -27
  303. package/dist/discord/monitor/message-handler.js +4 -1
  304. package/dist/discord/monitor/message-handler.preflight.js +65 -8
  305. package/dist/discord/monitor/message-handler.process.js +246 -217
  306. package/dist/discord/monitor/message-utils.js +143 -6
  307. package/dist/discord/monitor/model-picker-preferences.js +143 -0
  308. package/dist/discord/monitor/model-picker.js +651 -0
  309. package/dist/discord/monitor/native-command.js +573 -16
  310. package/dist/discord/monitor/provider.allowlist.js +223 -0
  311. package/dist/discord/monitor/provider.js +275 -347
  312. package/dist/discord/monitor/provider.lifecycle.js +100 -0
  313. package/dist/discord/monitor/reply-delivery.js +123 -16
  314. package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
  315. package/dist/discord/monitor/thread-bindings.js +4 -0
  316. package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
  317. package/dist/discord/monitor/thread-bindings.manager.js +423 -0
  318. package/dist/discord/monitor/thread-bindings.messages.js +55 -0
  319. package/dist/discord/monitor/thread-bindings.state.js +358 -0
  320. package/dist/discord/monitor/thread-bindings.types.js +6 -0
  321. package/dist/discord/resolve-users.js +33 -21
  322. package/dist/discord/send.channels.js +15 -0
  323. package/dist/discord/send.js +3 -2
  324. package/dist/discord/send.outbound.js +82 -26
  325. package/dist/discord/send.permissions.js +83 -30
  326. package/dist/discord/send.reactions.js +8 -4
  327. package/dist/discord/token.js +10 -5
  328. package/dist/discord/voice/command.js +263 -0
  329. package/dist/discord/voice/manager.js +531 -0
  330. package/dist/gateway/auth.js +34 -10
  331. package/dist/gateway/call.js +4 -16
  332. package/dist/gateway/client.js +28 -4
  333. package/dist/gateway/config-reload.js +3 -4
  334. package/dist/gateway/control-ui.js +219 -96
  335. package/dist/gateway/hooks-mapping.js +88 -38
  336. package/dist/gateway/http-auth-helpers.js +3 -2
  337. package/dist/gateway/http-endpoint-helpers.js +1 -0
  338. package/dist/gateway/net.js +54 -12
  339. package/dist/gateway/node-invoke-system-run-approval.js +14 -35
  340. package/dist/gateway/node-registry.js +10 -5
  341. package/dist/gateway/openai-http.js +1 -0
  342. package/dist/gateway/openresponses-http.js +1 -0
  343. package/dist/gateway/origin-check.js +1 -18
  344. package/dist/gateway/protocol/index.js +4 -3
  345. package/dist/gateway/protocol/schema/cron.js +1 -0
  346. package/dist/gateway/protocol/schema/devices.js +1 -0
  347. package/dist/gateway/protocol/schema/protocol-schemas.js +2 -1
  348. package/dist/gateway/protocol/schema/sessions.js +6 -0
  349. package/dist/gateway/role-policy.js +17 -0
  350. package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
  351. package/dist/gateway/server/ws-connection/message-handler.js +175 -148
  352. package/dist/gateway/server-chat.js +83 -25
  353. package/dist/gateway/server-constants.js +10 -9
  354. package/dist/gateway/server-cron.js +1 -0
  355. package/dist/gateway/server-http.js +16 -7
  356. package/dist/gateway/server-maintenance.js +20 -5
  357. package/dist/gateway/server-methods/chat.js +10 -6
  358. package/dist/gateway/server-methods/config.js +12 -14
  359. package/dist/gateway/server-methods/devices.js +17 -3
  360. package/dist/gateway/server-methods/models.js +11 -1
  361. package/dist/gateway/server-methods/sessions.js +64 -8
  362. package/dist/gateway/server-methods/usage.js +162 -75
  363. package/dist/gateway/server-node-events.js +29 -0
  364. package/dist/gateway/server-runtime-config.js +34 -13
  365. package/dist/gateway/server-startup-memory.js +17 -11
  366. package/dist/gateway/session-utils.fs.js +32 -34
  367. package/dist/gateway/sessions-resolve.js +17 -5
  368. package/dist/gateway/test-helpers.openai-mock.js +14 -7
  369. package/dist/gateway/tools-invoke-http.js +21 -10
  370. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
  371. package/dist/hooks/bundled/command-logger/handler.js +7 -2
  372. package/dist/hooks/bundled/session-memory/handler.js +6 -5
  373. package/dist/hooks/frontmatter.js +6 -6
  374. package/dist/hooks/gmail-watcher.js +11 -6
  375. package/dist/hooks/internal-hooks.js +11 -1
  376. package/dist/hooks/llm-slug-generator.js +4 -1
  377. package/dist/hooks/workspace.js +47 -17
  378. package/dist/imessage/accounts.js +9 -20
  379. package/dist/imessage/monitor/inbound-processing.js +2 -1
  380. package/dist/infra/archive.js +174 -73
  381. package/dist/infra/control-ui-assets.js +14 -6
  382. package/dist/infra/device-pairing.js +108 -29
  383. package/dist/infra/env.js +10 -5
  384. package/dist/infra/exec-approvals-allowlist.js +122 -0
  385. package/dist/infra/exec-approvals-analysis.js +34 -3
  386. package/dist/infra/exec-approvals.js +5 -17
  387. package/dist/infra/exec-safe-bin-policy.js +53 -45
  388. package/dist/infra/fs-safe.js +71 -39
  389. package/dist/infra/gateway-lock.js +6 -2
  390. package/dist/infra/heartbeat-wake.js +6 -12
  391. package/dist/infra/host-env-security-policy.json +19 -0
  392. package/dist/infra/host-env-security.js +66 -0
  393. package/dist/infra/net/ssrf.js +131 -38
  394. package/dist/infra/outbound/bound-delivery-router.js +88 -0
  395. package/dist/infra/outbound/channel-selection.js +12 -6
  396. package/dist/infra/outbound/envelope.js +1 -1
  397. package/dist/infra/outbound/format.js +12 -6
  398. package/dist/infra/outbound/payloads.js +14 -7
  399. package/dist/infra/outbound/session-binding-service.js +123 -0
  400. package/dist/infra/path-guards.js +25 -0
  401. package/dist/infra/provider-usage.fetch.codex.js +7 -15
  402. package/dist/infra/provider-usage.fetch.gemini.js +14 -11
  403. package/dist/infra/provider-usage.fetch.shared.js +30 -1
  404. package/dist/infra/provider-usage.fetch.zai.js +10 -9
  405. package/dist/infra/retry-policy.js +4 -2
  406. package/dist/infra/retry.js +9 -5
  407. package/dist/infra/session-cost-usage.js +107 -59
  408. package/dist/infra/session-maintenance-warning.js +3 -1
  409. package/dist/infra/shell-env.js +98 -34
  410. package/dist/infra/ssh-config.js +12 -6
  411. package/dist/infra/system-run-command.js +49 -4
  412. package/dist/infra/update-channels.js +10 -5
  413. package/dist/line/accounts.js +5 -7
  414. package/dist/line/bot-access.js +8 -20
  415. package/dist/line/bot-handlers.js +3 -1
  416. package/dist/link-understanding/detect.js +15 -7
  417. package/dist/media/constants.js +15 -6
  418. package/dist/media/image-ops.js +7 -0
  419. package/dist/media/local-roots.js +3 -2
  420. package/dist/media-understanding/apply.js +4 -1
  421. package/dist/media-understanding/concurrency.js +8 -20
  422. package/dist/memory/backend-config.js +45 -6
  423. package/dist/memory/embeddings.js +10 -4
  424. package/dist/memory/fs-utils.js +23 -0
  425. package/dist/memory/manager-search.js +12 -6
  426. package/dist/memory/manager-sync-ops.js +12 -2
  427. package/dist/memory/qmd-manager.js +466 -53
  428. package/dist/memory/query-expansion.js +167 -3
  429. package/dist/memory/status-format.js +10 -5
  430. package/dist/memory/sync-memory-files.js +1 -1
  431. package/dist/node-host/invoke-system-run.js +281 -0
  432. package/dist/node-host/invoke.js +55 -337
  433. package/dist/pairing/pairing-store.js +22 -0
  434. package/dist/plugin-sdk/allow-from.js +1 -1
  435. package/dist/plugin-sdk/command-auth.js +3 -1
  436. package/dist/plugin-sdk/index.js +6 -3
  437. package/dist/plugin-sdk/webhook-targets.js +32 -0
  438. package/dist/plugins/bundled-dir.js +9 -6
  439. package/dist/plugins/hooks.js +50 -0
  440. package/dist/plugins/install.js +28 -16
  441. package/dist/plugins/runtime.js +3 -17
  442. package/dist/plugins/update.js +78 -12
  443. package/dist/process/spawn-utils.js +14 -7
  444. package/dist/providers/github-copilot-token.js +11 -6
  445. package/dist/providers/qwen-portal-oauth.js +14 -6
  446. package/dist/routing/account-id.js +30 -0
  447. package/dist/routing/resolve-route.js +3 -7
  448. package/dist/routing/session-key.js +2 -16
  449. package/dist/security/audit-channel.js +93 -2
  450. package/dist/security/audit-extra.async.js +159 -5
  451. package/dist/security/audit-extra.js +1 -1
  452. package/dist/security/audit-extra.sync.js +85 -6
  453. package/dist/security/audit.js +40 -4
  454. package/dist/security/dm-policy-shared.js +44 -0
  455. package/dist/security/external-content.js +26 -6
  456. package/dist/shared/entry-status.js +6 -0
  457. package/dist/shared/frontmatter.js +5 -5
  458. package/dist/shared/node-match.js +11 -4
  459. package/dist/shared/operator-scope-compat.js +8 -3
  460. package/dist/signal/accounts.js +7 -20
  461. package/dist/signal/monitor/event-handler.js +3 -1
  462. package/dist/slack/accounts.js +6 -19
  463. package/dist/slack/actions.js +11 -3
  464. package/dist/slack/monitor/auth.js +1 -1
  465. package/dist/slack/monitor/message-handler/dispatch.js +50 -29
  466. package/dist/slack/monitor/replies.js +15 -7
  467. package/dist/slack/monitor/slash.js +22 -13
  468. package/dist/slack/resolve-channels.js +10 -5
  469. package/dist/slack/send.js +102 -12
  470. package/dist/slack/stream-mode.js +10 -0
  471. package/dist/slack/streaming.js +4 -2
  472. package/dist/telegram/accounts.js +19 -14
  473. package/dist/telegram/bot/helpers.js +3 -5
  474. package/dist/telegram/bot-access.js +35 -36
  475. package/dist/telegram/bot-handlers.js +120 -148
  476. package/dist/telegram/bot-message-context.js +68 -9
  477. package/dist/telegram/bot-message-dispatch.js +155 -90
  478. package/dist/telegram/bot-native-commands.js +16 -0
  479. package/dist/telegram/draft-stream.js +14 -1
  480. package/dist/telegram/inline-buttons.js +5 -15
  481. package/dist/telegram/monitor.js +11 -7
  482. package/dist/telegram/network-config.js +19 -7
  483. package/dist/telegram/send.js +3 -2
  484. package/dist/telegram/sent-message-cache.js +5 -6
  485. package/dist/telegram/status-reaction-variants.js +208 -0
  486. package/dist/telegram/sticker-cache.js +11 -9
  487. package/dist/terminal/theme.js +12 -12
  488. package/dist/tts/tts.js +80 -567
  489. package/dist/tui/components/chat-log.js +41 -8
  490. package/dist/tui/theme/theme.js +10 -12
  491. package/dist/tui/tui-local-shell.js +16 -6
  492. package/dist/tui/tui.js +58 -6
  493. package/dist/utils/account-id.js +2 -4
  494. package/dist/utils/boolean.js +10 -5
  495. package/dist/utils/directive-tags.js +11 -0
  496. package/dist/utils/queue-helpers.js +67 -12
  497. package/dist/web/auto-reply/deliver-reply.js +8 -4
  498. package/dist/web/auto-reply/mentions.js +10 -5
  499. package/dist/web/auto-reply/monitor/group-members.js +14 -7
  500. package/dist/web/auto-reply/monitor/process-message.js +45 -24
  501. package/dist/web/inbound/access-control.js +5 -2
  502. package/dist/web/login-qr.js +12 -6
  503. package/dist/web/media.js +123 -16
  504. package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
  505. package/extensions/bluebubbles/src/monitor.ts +208 -1950
  506. package/package.json +1 -1
@@ -8,6 +8,40 @@ import { resolveDmAllowState } from "./dm-policy-shared.js";
8
8
  function normalizeAllowFromList(list) {
9
9
  return normalizeStringEntries(Array.isArray(list) ? list : undefined);
10
10
  }
11
+ const DISCORD_ALLOWLIST_ID_PREFIXES = ["discord:", "user:", "pk:"];
12
+ function isDiscordNameBasedAllowEntry(raw) {
13
+ const text = String(raw).trim();
14
+ if (!text || text === "*") {
15
+ return false;
16
+ }
17
+ const maybeId = text.replace(/^<@!?/, "").replace(/>$/, "");
18
+ if (/^\d+$/.test(maybeId)) {
19
+ return false;
20
+ }
21
+ const prefixed = DISCORD_ALLOWLIST_ID_PREFIXES.find((prefix) => text.startsWith(prefix));
22
+ if (prefixed) {
23
+ const candidate = text.slice(prefixed.length);
24
+ if (candidate) {
25
+ return false;
26
+ }
27
+ }
28
+ return true;
29
+ }
30
+ function addDiscordNameBasedEntries(params) {
31
+ if (!Array.isArray(params.values)) {
32
+ return;
33
+ }
34
+ for (const value of params.values) {
35
+ if (!isDiscordNameBasedAllowEntry(value)) {
36
+ continue;
37
+ }
38
+ const text = String(value).trim();
39
+ if (!text) {
40
+ continue;
41
+ }
42
+ params.target.add(`${params.source}:${text}`);
43
+ }
44
+ }
11
45
  function classifyChannelWarningSeverity(message) {
12
46
  const s = message.toLowerCase();
13
47
  if (s.includes("dms: open") ||
@@ -108,6 +142,64 @@ export async function collectChannelSecurityFindings(params) {
108
142
  if (plugin.id === "discord") {
109
143
  const discordCfg = account?.config ??
110
144
  {};
145
+ const storeAllowFrom = await readChannelAllowFromStore("discord").catch(() => []);
146
+ const discordNameBasedAllowEntries = new Set();
147
+ addDiscordNameBasedEntries({
148
+ target: discordNameBasedAllowEntries,
149
+ values: discordCfg.allowFrom,
150
+ source: "channels.discord.allowFrom",
151
+ });
152
+ addDiscordNameBasedEntries({
153
+ target: discordNameBasedAllowEntries,
154
+ values: discordCfg.dm?.allowFrom,
155
+ source: "channels.discord.dm.allowFrom",
156
+ });
157
+ addDiscordNameBasedEntries({
158
+ target: discordNameBasedAllowEntries,
159
+ values: storeAllowFrom,
160
+ source: "~/.poolbot/credentials/discord-allowFrom.json",
161
+ });
162
+ const discordGuildEntries = discordCfg.guilds ?? {};
163
+ for (const [guildKey, guildValue] of Object.entries(discordGuildEntries)) {
164
+ if (!guildValue || typeof guildValue !== "object") {
165
+ continue;
166
+ }
167
+ const guild = guildValue;
168
+ addDiscordNameBasedEntries({
169
+ target: discordNameBasedAllowEntries,
170
+ values: guild.users,
171
+ source: `channels.discord.guilds.${guildKey}.users`,
172
+ });
173
+ const channels = guild.channels;
174
+ if (!channels || typeof channels !== "object") {
175
+ continue;
176
+ }
177
+ for (const [channelKey, channelValue] of Object.entries(channels)) {
178
+ if (!channelValue || typeof channelValue !== "object") {
179
+ continue;
180
+ }
181
+ const channel = channelValue;
182
+ addDiscordNameBasedEntries({
183
+ target: discordNameBasedAllowEntries,
184
+ values: channel.users,
185
+ source: `channels.discord.guilds.${guildKey}.channels.${channelKey}.users`,
186
+ });
187
+ }
188
+ }
189
+ if (discordNameBasedAllowEntries.size > 0) {
190
+ const examples = Array.from(discordNameBasedAllowEntries).slice(0, 5);
191
+ const more = discordNameBasedAllowEntries.size > examples.length
192
+ ? ` (+${discordNameBasedAllowEntries.size - examples.length} more)`
193
+ : "";
194
+ findings.push({
195
+ checkId: "channels.discord.allowFrom.name_based_entries",
196
+ severity: "warn",
197
+ title: "Discord allowlist contains name or tag entries",
198
+ detail: "Discord name/tag allowlist matching uses normalized slugs and can collide across users. " +
199
+ `Found: ${examples.join(", ")}${more}.`,
200
+ remediation: "Prefer stable Discord IDs (or <@id>/user:<id>/pk:<id>) in channels.discord.allowFrom and channels.discord.guilds.*.users.",
201
+ });
202
+ }
111
203
  const nativeEnabled = resolveNativeCommandsEnabled({
112
204
  providerId: "discord",
113
205
  providerSetting: coerceNativeSetting(discordCfg.commands?.native),
@@ -122,7 +214,7 @@ export async function collectChannelSecurityFindings(params) {
122
214
  if (slashEnabled) {
123
215
  const defaultGroupPolicy = params.cfg.channels?.defaults?.groupPolicy;
124
216
  const groupPolicy = discordCfg.groupPolicy ?? defaultGroupPolicy ?? "allowlist";
125
- const guildEntries = discordCfg.guilds ?? {};
217
+ const guildEntries = discordGuildEntries;
126
218
  const guildsConfigured = Object.keys(guildEntries).length > 0;
127
219
  const hasAnyUserAllowlist = Object.values(guildEntries).some((guild) => {
128
220
  if (!guild || typeof guild !== "object") {
@@ -146,7 +238,6 @@ export async function collectChannelSecurityFindings(params) {
146
238
  });
147
239
  const dmAllowFromRaw = discordCfg.dm?.allowFrom;
148
240
  const dmAllowFrom = Array.isArray(dmAllowFromRaw) ? dmAllowFromRaw : [];
149
- const storeAllowFrom = await readChannelAllowFromStore("discord").catch(() => []);
150
241
  const ownerAllowFromConfigured = normalizeAllowFromList([...dmAllowFrom, ...storeAllowFrom]).length > 0;
151
242
  const useAccessGroups = params.cfg.commands?.useAccessGroups !== false;
152
243
  if (!useAccessGroups &&
@@ -8,9 +8,12 @@ import path from "node:path";
8
8
  import { resolveDefaultAgentId } from "../agents/agent-scope.js";
9
9
  import { isToolAllowedByPolicies } from "../agents/pi-tools.policy.js";
10
10
  import { resolveSandboxConfigForAgent, resolveSandboxToolPolicyForAgent, } from "../agents/sandbox.js";
11
+ import { SANDBOX_BROWSER_SECURITY_HASH_EPOCH } from "../agents/sandbox/constants.js";
12
+ import { execDockerRaw } from "../agents/sandbox/docker.js";
11
13
  import { loadWorkspaceSkillEntries } from "../agents/skills.js";
12
14
  import { resolveToolProfilePolicy } from "../agents/tool-policy.js";
13
15
  import { listAgentWorkspaceDirs } from "../agents/workspace-dirs.js";
16
+ import { formatCliCommand } from "../cli/command-format.js";
14
17
  import { MANIFEST_KEY } from "../compat/legacy-names.js";
15
18
  import { resolveNativeSkillsEnabled } from "../config/commands.js";
16
19
  import { createConfigIO } from "../config/config.js";
@@ -180,6 +183,157 @@ async function readInstalledPackageVersion(dir) {
180
183
  // --------------------------------------------------------------------------
181
184
  // Exported collectors
182
185
  // --------------------------------------------------------------------------
186
+ function normalizeDockerLabelValue(raw) {
187
+ const trimmed = raw?.trim() ?? "";
188
+ if (!trimmed || trimmed === "<no value>") {
189
+ return null;
190
+ }
191
+ return trimmed;
192
+ }
193
+ async function listSandboxBrowserContainers(execDockerRawFn) {
194
+ try {
195
+ const result = await execDockerRawFn(["ps", "-a", "--filter", "label=poolbot.sandboxBrowser=1", "--format", "{{.Names}}"], { allowFailure: true });
196
+ if (result.code !== 0) {
197
+ return null;
198
+ }
199
+ return result.stdout
200
+ .toString("utf8")
201
+ .split(/\r?\n/)
202
+ .map((entry) => entry.trim())
203
+ .filter(Boolean);
204
+ }
205
+ catch {
206
+ return null;
207
+ }
208
+ }
209
+ async function readSandboxBrowserHashLabels(params) {
210
+ try {
211
+ const result = await params.execDockerRawFn([
212
+ "inspect",
213
+ "-f",
214
+ '{{ index .Config.Labels "poolbot.configHash" }}\t{{ index .Config.Labels "poolbot.browserConfigEpoch" }}',
215
+ params.containerName,
216
+ ], { allowFailure: true });
217
+ if (result.code !== 0) {
218
+ return null;
219
+ }
220
+ const [hashRaw, epochRaw] = result.stdout.toString("utf8").split("\t");
221
+ return {
222
+ configHash: normalizeDockerLabelValue(hashRaw),
223
+ epoch: normalizeDockerLabelValue(epochRaw),
224
+ };
225
+ }
226
+ catch {
227
+ return null;
228
+ }
229
+ }
230
+ function parsePublishedHostFromDockerPortLine(line) {
231
+ const trimmed = line.trim();
232
+ const rhs = trimmed.includes("->") ? (trimmed.split("->").at(-1)?.trim() ?? "") : trimmed;
233
+ if (!rhs) {
234
+ return null;
235
+ }
236
+ const bracketHost = rhs.match(/^\[([^\]]+)\]:\d+$/);
237
+ if (bracketHost?.[1]) {
238
+ return bracketHost[1];
239
+ }
240
+ const hostPort = rhs.match(/^([^:]+):\d+$/);
241
+ if (hostPort?.[1]) {
242
+ return hostPort[1];
243
+ }
244
+ return null;
245
+ }
246
+ function isLoopbackPublishHost(host) {
247
+ const normalized = host.trim().toLowerCase();
248
+ return normalized === "127.0.0.1" || normalized === "::1" || normalized === "localhost";
249
+ }
250
+ async function readSandboxBrowserPortMappings(params) {
251
+ try {
252
+ const result = await params.execDockerRawFn(["port", params.containerName], {
253
+ allowFailure: true,
254
+ });
255
+ if (result.code !== 0) {
256
+ return null;
257
+ }
258
+ return result.stdout
259
+ .toString("utf8")
260
+ .split(/\r?\n/)
261
+ .map((entry) => entry.trim())
262
+ .filter(Boolean);
263
+ }
264
+ catch {
265
+ return null;
266
+ }
267
+ }
268
+ export async function collectSandboxBrowserHashLabelFindings(params) {
269
+ const findings = [];
270
+ const execFn = params?.execDockerRawFn ?? execDockerRaw;
271
+ const containers = await listSandboxBrowserContainers(execFn);
272
+ if (!containers || containers.length === 0) {
273
+ return findings;
274
+ }
275
+ const missingHash = [];
276
+ const staleEpoch = [];
277
+ const nonLoopbackPublished = [];
278
+ for (const containerName of containers) {
279
+ const labels = await readSandboxBrowserHashLabels({ containerName, execDockerRawFn: execFn });
280
+ if (!labels) {
281
+ continue;
282
+ }
283
+ if (!labels.configHash) {
284
+ missingHash.push(containerName);
285
+ }
286
+ if (labels.epoch !== SANDBOX_BROWSER_SECURITY_HASH_EPOCH) {
287
+ staleEpoch.push(containerName);
288
+ }
289
+ const portMappings = await readSandboxBrowserPortMappings({
290
+ containerName,
291
+ execDockerRawFn: execFn,
292
+ });
293
+ if (!portMappings?.length) {
294
+ continue;
295
+ }
296
+ const exposedMappings = portMappings.filter((line) => {
297
+ const host = parsePublishedHostFromDockerPortLine(line);
298
+ return Boolean(host && !isLoopbackPublishHost(host));
299
+ });
300
+ if (exposedMappings.length > 0) {
301
+ nonLoopbackPublished.push(`${containerName} (${exposedMappings.join("; ")})`);
302
+ }
303
+ }
304
+ if (missingHash.length > 0) {
305
+ findings.push({
306
+ checkId: "sandbox.browser_container.hash_label_missing",
307
+ severity: "warn",
308
+ title: "Sandbox browser container missing config hash label",
309
+ detail: `Containers: ${missingHash.join(", ")}. ` +
310
+ "These browser containers predate hash-based drift checks and may miss security remediations until recreated.",
311
+ remediation: `${formatCliCommand("poolbot sandbox recreate --browser --all")} (add --force to skip prompt).`,
312
+ });
313
+ }
314
+ if (staleEpoch.length > 0) {
315
+ findings.push({
316
+ checkId: "sandbox.browser_container.hash_epoch_stale",
317
+ severity: "warn",
318
+ title: "Sandbox browser container hash epoch is stale",
319
+ detail: `Containers: ${staleEpoch.join(", ")}. ` +
320
+ `Expected poolbot.browserConfigEpoch=${SANDBOX_BROWSER_SECURITY_HASH_EPOCH}.`,
321
+ remediation: `${formatCliCommand("poolbot sandbox recreate --browser --all")} (add --force to skip prompt).`,
322
+ });
323
+ }
324
+ if (nonLoopbackPublished.length > 0) {
325
+ findings.push({
326
+ checkId: "sandbox.browser_container.non_loopback_publish",
327
+ severity: "critical",
328
+ title: "Sandbox browser container publishes ports on non-loopback interfaces",
329
+ detail: `Containers: ${nonLoopbackPublished.join(", ")}. ` +
330
+ "Sandbox browser observer/control ports should stay loopback-only to avoid unintended remote access.",
331
+ remediation: `${formatCliCommand("poolbot sandbox recreate --browser --all")} (add --force to skip prompt), ` +
332
+ "then verify published ports are bound to 127.0.0.1.",
333
+ });
334
+ }
335
+ return findings;
336
+ }
183
337
  export async function collectPluginsTrustFindings(params) {
184
338
  const findings = [];
185
339
  const { extensionsDir, pluginDirs } = await listInstalledPluginDirs({
@@ -265,7 +419,7 @@ export async function collectPluginsTrustFindings(params) {
265
419
  sandboxMode,
266
420
  agentId: context.agentId,
267
421
  });
268
- const broadPolicy = isToolAllowedByPolicies("__openclaw_plugin_probe__", policies);
422
+ const broadPolicy = isToolAllowedByPolicies("__poolbot_plugin_probe__", policies);
269
423
  const explicitPluginAllow = !restrictiveProfile &&
270
424
  (hasExplicitPluginAllow({
271
425
  allowEntries: collectAllowEntries(params.cfg.tools),
@@ -346,7 +500,7 @@ export async function collectPluginsTrustFindings(params) {
346
500
  severity: "warn",
347
501
  title: "Plugin install records drift from installed package versions",
348
502
  detail: `Detected plugin install metadata drift:\n${pluginVersionDrift.map((entry) => `- ${entry}`).join("\n")}`,
349
- remediation: "Run `openclaw plugins update --all` (or reinstall affected plugins) to refresh install metadata.",
503
+ remediation: "Run `poolbot plugins update --all` (or reinstall affected plugins) to refresh install metadata.",
350
504
  });
351
505
  }
352
506
  }
@@ -397,7 +551,7 @@ export async function collectPluginsTrustFindings(params) {
397
551
  severity: "warn",
398
552
  title: "Hook install records drift from installed package versions",
399
553
  detail: `Detected hook install metadata drift:\n${hookVersionDrift.map((entry) => `- ${entry}`).join("\n")}`,
400
- remediation: "Run `openclaw hooks update --all` (or reinstall affected hooks) to refresh install metadata.",
554
+ remediation: "Run `poolbot hooks update --all` (or reinstall affected hooks) to refresh install metadata.",
401
555
  });
402
556
  }
403
557
  }
@@ -666,7 +820,7 @@ export async function collectPluginsCodeSafetyFindings(params) {
666
820
  severity: "critical",
667
821
  title: `Plugin "${pluginName}" has extension entry path traversal`,
668
822
  detail: `Found extension entries that escape the plugin directory:\n${escapedEntries.map((entry) => ` - ${entry}`).join("\n")}`,
669
- remediation: "Update the plugin manifest so all openclaw.extensions entries stay inside the plugin directory.",
823
+ remediation: "Update the plugin manifest so all poolbot.extensions entries stay inside the plugin directory.",
670
824
  });
671
825
  }
672
826
  const summary = await skillScanner
@@ -694,7 +848,7 @@ export async function collectPluginsCodeSafetyFindings(params) {
694
848
  severity: "critical",
695
849
  title: `Plugin "${pluginName}" contains dangerous code patterns`,
696
850
  detail: `Found ${summary.critical} critical issue(s) in ${summary.scannedFiles} scanned file(s):\n${details}`,
697
- remediation: "Review the plugin source code carefully before use. If untrusted, remove the plugin from your OpenClaw extensions state directory.",
851
+ remediation: "Review the plugin source code carefully before use. If untrusted, remove the plugin from your Pool Bot extensions state directory.",
698
852
  });
699
853
  }
700
854
  else if (summary.warn > 0) {
@@ -9,4 +9,4 @@
9
9
  // Sync collectors
10
10
  export { collectAttackSurfaceSummaryFindings, collectExposureMatrixFindings, collectGatewayHttpNoAuthFindings, collectGatewayHttpSessionKeyOverrideFindings, collectHooksHardeningFindings, collectMinimalProfileOverrideFindings, collectModelHygieneFindings, collectNodeDenyCommandPatternFindings, collectSandboxDangerousConfigFindings, collectSandboxDockerNoopFindings, collectSecretsInConfigFindings, collectSmallModelRiskFindings, collectSyncedFolderFindings, } from "./audit-extra.sync.js";
11
11
  // Async collectors
12
- export { collectIncludeFilePermFindings, collectInstalledSkillsCodeSafetyFindings, collectPluginsCodeSafetyFindings, collectPluginsTrustFindings, collectStateDeepFilesystemFindings, readConfigSnapshotForAudit, } from "./audit-extra.async.js";
12
+ export { collectSandboxBrowserHashLabelFindings, collectIncludeFilePermFindings, collectInstalledSkillsCodeSafetyFindings, collectPluginsCodeSafetyFindings, collectPluginsTrustFindings, collectStateDeepFilesystemFindings, readConfigSnapshotForAudit, } from "./audit-extra.async.js";
@@ -290,7 +290,7 @@ export function collectSyncedFolderFindings(params) {
290
290
  severity: "warn",
291
291
  title: "State/config path looks like a synced folder",
292
292
  detail: `stateDir=${params.stateDir}, configPath=${params.configPath}. Synced folders (iCloud/Dropbox/OneDrive/Google Drive) can leak tokens and transcripts onto other devices.`,
293
- remediation: `Keep CLAWDBOT_STATE_DIR on a local-only volume and re-run "${formatCliCommand("poolbot security audit --fix")}".`,
293
+ remediation: `Keep POOLBOT_STATE_DIR on a local-only volume and re-run "${formatCliCommand("poolbot security audit --fix")}".`,
294
294
  });
295
295
  }
296
296
  return findings;
@@ -337,20 +337,20 @@ export function collectHooksHardeningFindings(cfg, env = process.env) {
337
337
  tailscaleMode: cfg.gateway?.tailscale?.mode ?? "off",
338
338
  env,
339
339
  });
340
- const envGatewayToken = typeof env.CLAWDBOT_GATEWAY_TOKEN === "string" && env.CLAWDBOT_GATEWAY_TOKEN.trim()
341
- ? env.CLAWDBOT_GATEWAY_TOKEN.trim()
340
+ const poolbotGatewayToken = typeof env.POOLBOT_GATEWAY_TOKEN === "string" && env.POOLBOT_GATEWAY_TOKEN.trim()
341
+ ? env.POOLBOT_GATEWAY_TOKEN.trim()
342
342
  : null;
343
343
  const gatewayToken = gatewayAuth.mode === "token" &&
344
344
  typeof gatewayAuth.token === "string" &&
345
345
  gatewayAuth.token.trim()
346
346
  ? gatewayAuth.token.trim()
347
- : envGatewayToken
348
- ? envGatewayToken
347
+ : poolbotGatewayToken
348
+ ? poolbotGatewayToken
349
349
  : null;
350
350
  if (token && gatewayToken && token === gatewayToken) {
351
351
  findings.push({
352
352
  checkId: "hooks.token_reuse_gateway_token",
353
- severity: "warn",
353
+ severity: "critical",
354
354
  title: "Hooks token reuses the Gateway token",
355
355
  detail: "hooks.token matches gateway.auth token; compromise of hooks expands blast radius to the Gateway API.",
356
356
  remediation: "Use a separate hooks.token dedicated to hook ingress.",
@@ -573,6 +573,40 @@ export function collectSandboxDangerousConfigFindings(cfg) {
573
573
  });
574
574
  }
575
575
  }
576
+ const browserExposurePaths = [];
577
+ const defaultBrowser = resolveSandboxConfigForAgent(cfg).browser;
578
+ if (defaultBrowser.enabled &&
579
+ defaultBrowser.network.trim().toLowerCase() === "bridge" &&
580
+ !defaultBrowser.cdpSourceRange?.trim()) {
581
+ browserExposurePaths.push("agents.defaults.sandbox.browser");
582
+ }
583
+ for (const entry of agents) {
584
+ if (!entry || typeof entry !== "object" || typeof entry.id !== "string") {
585
+ continue;
586
+ }
587
+ const browser = resolveSandboxConfigForAgent(cfg, entry.id).browser;
588
+ if (!browser.enabled) {
589
+ continue;
590
+ }
591
+ if (browser.network.trim().toLowerCase() !== "bridge") {
592
+ continue;
593
+ }
594
+ if (browser.cdpSourceRange?.trim()) {
595
+ continue;
596
+ }
597
+ browserExposurePaths.push(`agents.list.${entry.id}.sandbox.browser`);
598
+ }
599
+ if (browserExposurePaths.length > 0) {
600
+ findings.push({
601
+ checkId: "sandbox.browser_cdp_bridge_unrestricted",
602
+ severity: "warn",
603
+ title: "Sandbox browser CDP may be reachable by peer containers",
604
+ detail: "These sandbox browser configs use Docker bridge networking with no CDP source restriction:\n" +
605
+ browserExposurePaths.map((entry) => `- ${entry}`).join("\n"),
606
+ remediation: "Set sandbox.browser.network to a dedicated bridge network (recommended default: poolbot-sandbox-browser), " +
607
+ "or set sandbox.browser.cdpSourceRange (for example 172.21.0.1/32) to restrict container-edge CDP ingress.",
608
+ });
609
+ }
576
610
  return findings;
577
611
  }
578
612
  export function collectNodeDenyCommandPatternFindings(cfg) {
@@ -808,5 +842,50 @@ export function collectExposureMatrixFindings(cfg) {
808
842
  remediation: `Set groupPolicy="allowlist" and keep elevated allowlists extremely tight.`,
809
843
  });
810
844
  }
845
+ const contexts = [{ label: "agents.defaults" }];
846
+ for (const agent of cfg.agents?.list ?? []) {
847
+ if (!agent || typeof agent !== "object" || typeof agent.id !== "string") {
848
+ continue;
849
+ }
850
+ contexts.push({
851
+ label: `agents.list.${agent.id}`,
852
+ agentId: agent.id,
853
+ tools: agent.tools,
854
+ });
855
+ }
856
+ const riskyContexts = [];
857
+ let hasRuntimeRisk = false;
858
+ for (const context of contexts) {
859
+ const sandboxMode = resolveSandboxConfigForAgent(cfg, context.agentId).mode;
860
+ const policies = resolveToolPolicies({
861
+ cfg,
862
+ agentTools: context.tools,
863
+ sandboxMode,
864
+ agentId: context.agentId ?? null,
865
+ });
866
+ const runtimeTools = ["exec", "process"].filter((tool) => isToolAllowedByPolicies(tool, policies));
867
+ const fsTools = ["read", "write", "edit", "apply_patch"].filter((tool) => isToolAllowedByPolicies(tool, policies));
868
+ const fsWorkspaceOnly = context.tools?.fs?.workspaceOnly ?? cfg.tools?.fs?.workspaceOnly;
869
+ const runtimeUnguarded = runtimeTools.length > 0 && sandboxMode !== "all";
870
+ const fsUnguarded = fsTools.length > 0 && sandboxMode !== "all" && fsWorkspaceOnly !== true;
871
+ if (!runtimeUnguarded && !fsUnguarded) {
872
+ continue;
873
+ }
874
+ if (runtimeUnguarded) {
875
+ hasRuntimeRisk = true;
876
+ }
877
+ riskyContexts.push(`${context.label} (sandbox=${sandboxMode}; runtime=[${runtimeTools.join(", ") || "off"}]; fs=[${fsTools.join(", ") || "off"}]; fs.workspaceOnly=${fsWorkspaceOnly === true ? "true" : "false"})`);
878
+ }
879
+ if (riskyContexts.length > 0) {
880
+ findings.push({
881
+ checkId: "security.exposure.open_groups_with_runtime_or_fs",
882
+ severity: hasRuntimeRisk ? "critical" : "warn",
883
+ title: "Open groupPolicy with runtime/filesystem tools exposed",
884
+ detail: `Found groupPolicy="open" at:\n${openGroups.map((p) => `- ${p}`).join("\n")}\n` +
885
+ `Risky tool exposure contexts:\n${riskyContexts.map((line) => `- ${line}`).join("\n")}\n` +
886
+ "Prompt injection in open groups can trigger command/file actions in these contexts.",
887
+ remediation: 'For open groups, prefer tools.profile="messaging" (or deny group:runtime/group:fs), set tools.fs.workspaceOnly=true, and use agents.defaults.sandbox.mode="all" for exposed agents.',
888
+ });
889
+ }
811
890
  return findings;
812
891
  }
@@ -9,7 +9,7 @@ import { buildGatewayConnectionDetails } from "../gateway/call.js";
9
9
  import { resolveGatewayProbeAuth } from "../gateway/probe-auth.js";
10
10
  import { probeGateway } from "../gateway/probe.js";
11
11
  import { collectChannelSecurityFindings } from "./audit-channel.js";
12
- import { collectAttackSurfaceSummaryFindings, collectExposureMatrixFindings, collectGatewayHttpNoAuthFindings, collectGatewayHttpSessionKeyOverrideFindings, collectHooksHardeningFindings, collectIncludeFilePermFindings, collectInstalledSkillsCodeSafetyFindings, collectMinimalProfileOverrideFindings, collectModelHygieneFindings, collectNodeDenyCommandPatternFindings, collectSmallModelRiskFindings, collectSandboxDangerousConfigFindings, collectSandboxDockerNoopFindings, collectPluginsTrustFindings, collectSecretsInConfigFindings, collectPluginsCodeSafetyFindings, collectStateDeepFilesystemFindings, collectSyncedFolderFindings, readConfigSnapshotForAudit, } from "./audit-extra.js";
12
+ import { collectAttackSurfaceSummaryFindings, collectExposureMatrixFindings, collectGatewayHttpNoAuthFindings, collectGatewayHttpSessionKeyOverrideFindings, collectHooksHardeningFindings, collectIncludeFilePermFindings, collectInstalledSkillsCodeSafetyFindings, collectSandboxBrowserHashLabelFindings, collectMinimalProfileOverrideFindings, collectModelHygieneFindings, collectNodeDenyCommandPatternFindings, collectSmallModelRiskFindings, collectSandboxDangerousConfigFindings, collectSandboxDockerNoopFindings, collectPluginsTrustFindings, collectSecretsInConfigFindings, collectPluginsCodeSafetyFindings, collectStateDeepFilesystemFindings, collectSyncedFolderFindings, readConfigSnapshotForAudit, } from "./audit-extra.js";
13
13
  import { formatPermissionDetail, formatPermissionRemediation, inspectPathPermissions, } from "./audit-fs.js";
14
14
  import { DEFAULT_GATEWAY_HTTP_TOOL_DENY } from "./dangerous-tools.js";
15
15
  function countBySeverity(findings) {
@@ -35,6 +35,29 @@ function normalizeAllowFromList(list) {
35
35
  }
36
36
  return list.map((v) => String(v).trim()).filter(Boolean);
37
37
  }
38
+ function collectEnabledInsecureOrDangerousFlags(cfg) {
39
+ const enabledFlags = [];
40
+ if (cfg.gateway?.controlUi?.allowInsecureAuth === true) {
41
+ enabledFlags.push("gateway.controlUi.allowInsecureAuth=true");
42
+ }
43
+ if (cfg.gateway?.controlUi?.dangerouslyDisableDeviceAuth === true) {
44
+ enabledFlags.push("gateway.controlUi.dangerouslyDisableDeviceAuth=true");
45
+ }
46
+ if (cfg.hooks?.gmail?.allowUnsafeExternalContent === true) {
47
+ enabledFlags.push("hooks.gmail.allowUnsafeExternalContent=true");
48
+ }
49
+ if (Array.isArray(cfg.hooks?.mappings)) {
50
+ for (const [index, mapping] of cfg.hooks.mappings.entries()) {
51
+ if (mapping?.allowUnsafeExternalContent === true) {
52
+ enabledFlags.push(`hooks.mappings[${index}].allowUnsafeExternalContent=true`);
53
+ }
54
+ }
55
+ }
56
+ if (cfg.tools?.exec?.applyPatch?.workspaceOnly === false) {
57
+ enabledFlags.push("tools.exec.applyPatch.workspaceOnly=false");
58
+ }
59
+ return enabledFlags;
60
+ }
38
61
  async function collectFilesystemFindings(params) {
39
62
  const findings = [];
40
63
  const stateDirPerms = await inspectPathPermissions(params.stateDir, {
@@ -245,9 +268,9 @@ function collectGatewayConfigFindings(cfg, env) {
245
268
  if (cfg.gateway?.controlUi?.allowInsecureAuth === true) {
246
269
  findings.push({
247
270
  checkId: "gateway.control_ui.insecure_auth",
248
- severity: "critical",
249
- title: "Control UI allows insecure HTTP auth",
250
- detail: "gateway.controlUi.allowInsecureAuth=true allows token-only auth over HTTP and skips device identity.",
271
+ severity: "warn",
272
+ title: "Control UI insecure auth toggle enabled",
273
+ detail: "gateway.controlUi.allowInsecureAuth=true does not bypass secure context or device identity checks; only dangerouslyDisableDeviceAuth disables Control UI device identity checks.",
251
274
  remediation: "Disable it or switch to HTTPS (Tailscale Serve) or localhost.",
252
275
  });
253
276
  }
@@ -260,6 +283,16 @@ function collectGatewayConfigFindings(cfg, env) {
260
283
  remediation: "Disable it unless you are in a short-lived break-glass scenario.",
261
284
  });
262
285
  }
286
+ const enabledDangerousFlags = collectEnabledInsecureOrDangerousFlags(cfg);
287
+ if (enabledDangerousFlags.length > 0) {
288
+ findings.push({
289
+ checkId: "config.insecure_or_dangerous_flags",
290
+ severity: "warn",
291
+ title: "Insecure or dangerous config flags enabled",
292
+ detail: `Detected ${enabledDangerousFlags.length} enabled flag(s): ${enabledDangerousFlags.join(", ")}.`,
293
+ remediation: "Disable these flags when not actively debugging, or keep deployment scoped to trusted/local-only networks.",
294
+ });
295
+ }
263
296
  const token = typeof auth.token === "string" && auth.token.trim().length > 0 ? auth.token.trim() : null;
264
297
  if (auth.mode === "token" && token && token.length < 24) {
265
298
  findings.push({
@@ -536,6 +569,9 @@ export async function runSecurityAudit(opts) {
536
569
  findings.push(...(await collectIncludeFilePermFindings({ configSnapshot, env, platform, execIcacls })));
537
570
  }
538
571
  findings.push(...(await collectStateDeepFilesystemFindings({ cfg, env, stateDir, platform, execIcacls })));
572
+ findings.push(...(await collectSandboxBrowserHashLabelFindings({
573
+ execDockerRawFn: opts.execDockerRawFn,
574
+ })));
539
575
  findings.push(...(await collectPluginsTrustFindings({ cfg, stateDir })));
540
576
  if (opts.deep === true) {
541
577
  findings.push(...(await collectPluginsCodeSafetyFindings({ stateDir })));
@@ -1,5 +1,49 @@
1
1
  import { readChannelAllowFromStore } from "../pairing/pairing-store.js";
2
2
  import { normalizeStringEntries } from "../shared/string-normalization.js";
3
+ export function resolveEffectiveAllowFromLists(params) {
4
+ const configAllowFrom = normalizeStringEntries(Array.isArray(params.allowFrom) ? params.allowFrom : undefined);
5
+ const configGroupAllowFrom = normalizeStringEntries(Array.isArray(params.groupAllowFrom) ? params.groupAllowFrom : undefined);
6
+ const storeAllowFrom = params.dmPolicy === "allowlist"
7
+ ? []
8
+ : normalizeStringEntries(Array.isArray(params.storeAllowFrom) ? params.storeAllowFrom : undefined);
9
+ const effectiveAllowFrom = normalizeStringEntries([...configAllowFrom, ...storeAllowFrom]);
10
+ const groupBase = configGroupAllowFrom.length > 0 ? configGroupAllowFrom : configAllowFrom;
11
+ const effectiveGroupAllowFrom = normalizeStringEntries([...groupBase, ...storeAllowFrom]);
12
+ return { effectiveAllowFrom, effectiveGroupAllowFrom };
13
+ }
14
+ export function resolveDmGroupAccessDecision(params) {
15
+ const dmPolicy = params.dmPolicy ?? "pairing";
16
+ const groupPolicy = params.groupPolicy ?? "allowlist";
17
+ const effectiveAllowFrom = normalizeStringEntries(params.effectiveAllowFrom);
18
+ const effectiveGroupAllowFrom = normalizeStringEntries(params.effectiveGroupAllowFrom);
19
+ if (params.isGroup) {
20
+ if (groupPolicy === "disabled") {
21
+ return { decision: "block", reason: "groupPolicy=disabled" };
22
+ }
23
+ if (groupPolicy === "allowlist") {
24
+ if (effectiveGroupAllowFrom.length === 0) {
25
+ return { decision: "block", reason: "groupPolicy=allowlist (empty allowlist)" };
26
+ }
27
+ if (!params.isSenderAllowed(effectiveGroupAllowFrom)) {
28
+ return { decision: "block", reason: "groupPolicy=allowlist (not allowlisted)" };
29
+ }
30
+ }
31
+ return { decision: "allow", reason: `groupPolicy=${groupPolicy}` };
32
+ }
33
+ if (dmPolicy === "disabled") {
34
+ return { decision: "block", reason: "dmPolicy=disabled" };
35
+ }
36
+ if (dmPolicy === "open") {
37
+ return { decision: "allow", reason: "dmPolicy=open" };
38
+ }
39
+ if (params.isSenderAllowed(effectiveAllowFrom)) {
40
+ return { decision: "allow", reason: `dmPolicy=${dmPolicy} (allowlisted)` };
41
+ }
42
+ if (dmPolicy === "pairing") {
43
+ return { decision: "pairing", reason: "dmPolicy=pairing (not allowlisted)" };
44
+ }
45
+ return { decision: "block", reason: `dmPolicy=${dmPolicy} (not allowlisted)` };
46
+ }
3
47
  export async function resolveDmAllowState(params) {
4
48
  const configAllowFrom = normalizeStringEntries(Array.isArray(params.allowFrom) ? params.allowFrom : undefined);
5
49
  const hasWildcard = configAllowFrom.includes("*");
@@ -1,3 +1,4 @@
1
+ import { randomBytes } from "node:crypto";
1
2
  /**
2
3
  * Security utilities for handling untrusted external content.
3
4
  *
@@ -40,9 +41,20 @@ export function detectSuspiciousPatterns(content) {
40
41
  /**
41
42
  * Unique boundary markers for external content.
42
43
  * Using XML-style tags that are unlikely to appear in legitimate content.
44
+ * Each wrapper gets a unique random ID to prevent spoofing attacks where
45
+ * malicious content injects fake boundary markers.
43
46
  */
44
- const EXTERNAL_CONTENT_START = "<<<EXTERNAL_UNTRUSTED_CONTENT>>>";
45
- const EXTERNAL_CONTENT_END = "<<<END_EXTERNAL_UNTRUSTED_CONTENT>>>";
47
+ const EXTERNAL_CONTENT_START_NAME = "EXTERNAL_UNTRUSTED_CONTENT";
48
+ const EXTERNAL_CONTENT_END_NAME = "END_EXTERNAL_UNTRUSTED_CONTENT";
49
+ function createExternalContentMarkerId() {
50
+ return randomBytes(8).toString("hex");
51
+ }
52
+ function createExternalContentStartMarker(id) {
53
+ return `<<<${EXTERNAL_CONTENT_START_NAME} id="${id}">>>`;
54
+ }
55
+ function createExternalContentEndMarker(id) {
56
+ return `<<<${EXTERNAL_CONTENT_END_NAME} id="${id}">>>`;
57
+ }
46
58
  /**
47
59
  * Security warning prepended to external content.
48
60
  */
@@ -107,9 +119,16 @@ function replaceMarkers(content) {
107
119
  return content;
108
120
  }
109
121
  const replacements = [];
122
+ // Match markers with or without id attribute (handles both legacy and spoofed markers)
110
123
  const patterns = [
111
- { regex: /<<<EXTERNAL_UNTRUSTED_CONTENT>>>/gi, value: "[[MARKER_SANITIZED]]" },
112
- { regex: /<<<END_EXTERNAL_UNTRUSTED_CONTENT>>>/gi, value: "[[END_MARKER_SANITIZED]]" },
124
+ {
125
+ regex: /<<<EXTERNAL_UNTRUSTED_CONTENT(?:\s+id="[^"]{1,128}")?\s*>>>/gi,
126
+ value: "[[MARKER_SANITIZED]]",
127
+ },
128
+ {
129
+ regex: /<<<END_EXTERNAL_UNTRUSTED_CONTENT(?:\s+id="[^"]{1,128}")?\s*>>>/gi,
130
+ value: "[[END_MARKER_SANITIZED]]",
131
+ },
113
132
  ];
114
133
  for (const pattern of patterns) {
115
134
  pattern.regex.lastIndex = 0;
@@ -168,13 +187,14 @@ export function wrapExternalContent(content, options) {
168
187
  }
169
188
  const metadata = metadataLines.join("\n");
170
189
  const warningBlock = includeWarning ? `${EXTERNAL_CONTENT_WARNING}\n\n` : "";
190
+ const markerId = createExternalContentMarkerId();
171
191
  return [
172
192
  warningBlock,
173
- EXTERNAL_CONTENT_START,
193
+ createExternalContentStartMarker(markerId),
174
194
  metadata,
175
195
  "---",
176
196
  sanitized,
177
- EXTERNAL_CONTENT_END,
197
+ createExternalContentEndMarker(markerId),
178
198
  ].join("\n");
179
199
  }
180
200
  /**