@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
@@ -1,5 +1,5 @@
1
1
  import { createSubsystemLogger } from "../logging/subsystem.js";
2
- import { getImageMetadata, resizeToJpeg } from "../media/image-ops.js";
2
+ import { buildImageResizeSideGrid, getImageMetadata, IMAGE_REDUCE_QUALITY_STEPS, resizeToJpeg, } from "../media/image-ops.js";
3
3
  import { DEFAULT_IMAGE_MAX_BYTES, DEFAULT_IMAGE_MAX_DIMENSION_PX, } from "./image-sanitization.js";
4
4
  // Anthropic Messages API limitations (observed in Pool Bot sessions):
5
5
  // - Images over ~2000px per side can fail in multi-image requests.
@@ -49,6 +49,68 @@ function formatBytesShort(bytes) {
49
49
  }
50
50
  return `${(bytes / (1024 * 1024)).toFixed(2)}MB`;
51
51
  }
52
+ function parseMediaPathFromText(text) {
53
+ for (const line of text.split(/\r?\n/u)) {
54
+ const trimmed = line.trim();
55
+ if (!trimmed.startsWith("MEDIA:")) {
56
+ continue;
57
+ }
58
+ const raw = trimmed.slice("MEDIA:".length).trim();
59
+ if (!raw) {
60
+ continue;
61
+ }
62
+ const backtickWrapped = raw.match(/^`([^`]+)`$/u);
63
+ return (backtickWrapped?.[1] ?? raw).trim();
64
+ }
65
+ return undefined;
66
+ }
67
+ function fileNameFromPathLike(pathLike) {
68
+ const value = pathLike.trim();
69
+ if (!value) {
70
+ return undefined;
71
+ }
72
+ try {
73
+ const url = new URL(value);
74
+ const candidate = url.pathname.split("/").filter(Boolean).at(-1);
75
+ return candidate && candidate.length > 0 ? candidate : undefined;
76
+ }
77
+ catch {
78
+ // Not a URL; continue with path-like parsing.
79
+ }
80
+ const normalized = value.replaceAll("\\", "/");
81
+ const candidate = normalized.split("/").filter(Boolean).at(-1);
82
+ return candidate && candidate.length > 0 ? candidate : undefined;
83
+ }
84
+ function inferImageFileName(params) {
85
+ const rec = params.block;
86
+ const explicitKeys = ["fileName", "filename", "path", "url"];
87
+ for (const key of explicitKeys) {
88
+ const raw = rec[key];
89
+ if (typeof raw !== "string" || raw.trim().length === 0) {
90
+ continue;
91
+ }
92
+ const candidate = fileNameFromPathLike(raw);
93
+ if (candidate) {
94
+ return candidate;
95
+ }
96
+ }
97
+ if (typeof rec.name === "string" && rec.name.trim().length > 0) {
98
+ return rec.name.trim();
99
+ }
100
+ if (params.mediaPathHint) {
101
+ const candidate = fileNameFromPathLike(params.mediaPathHint);
102
+ if (candidate) {
103
+ return candidate;
104
+ }
105
+ }
106
+ if (typeof params.label === "string" && params.label.startsWith("read:")) {
107
+ const candidate = fileNameFromPathLike(params.label.slice("read:".length));
108
+ if (candidate) {
109
+ return candidate;
110
+ }
111
+ }
112
+ return undefined;
113
+ }
52
114
  async function resizeImageBase64IfNeeded(params) {
53
115
  const buf = Buffer.from(params.base64, "base64");
54
116
  const meta = await getImageMetadata(buf);
@@ -69,16 +131,12 @@ async function resizeImageBase64IfNeeded(params) {
69
131
  height,
70
132
  };
71
133
  }
72
- const qualities = [85, 75, 65, 55, 45, 35];
73
134
  const maxDim = hasDimensions ? Math.max(width ?? 0, height ?? 0) : params.maxDimensionPx;
74
135
  const sideStart = maxDim > 0 ? Math.min(params.maxDimensionPx, maxDim) : params.maxDimensionPx;
75
- const sideGrid = [sideStart, 1800, 1600, 1400, 1200, 1000, 800]
76
- .filter((v) => v > 0 && v <= params.maxDimensionPx)
77
- .filter((v, i, arr) => v > 0 && arr.indexOf(v) === i)
78
- .toSorted((a, b) => b - a);
136
+ const sideGrid = buildImageResizeSideGrid(params.maxDimensionPx, sideStart);
79
137
  let smallest = null;
80
138
  for (const side of sideGrid) {
81
- for (const quality of qualities) {
139
+ for (const quality of IMAGE_REDUCE_QUALITY_STEPS) {
82
140
  const out = await resizeToJpeg({
83
141
  buffer: buf,
84
142
  maxSide: side,
@@ -92,11 +150,15 @@ async function resizeImageBase64IfNeeded(params) {
92
150
  const sourcePixels = typeof width === "number" && typeof height === "number"
93
151
  ? `${width}x${height}px`
94
152
  : "unknown";
153
+ const sourceWithFile = params.fileName
154
+ ? `${params.fileName} ${sourcePixels}`
155
+ : sourcePixels;
95
156
  const byteReductionPct = buf.byteLength > 0
96
157
  ? Number((((buf.byteLength - out.byteLength) / buf.byteLength) * 100).toFixed(1))
97
158
  : 0;
98
- log.info(`Image resized to fit limits: ${sourcePixels} ${formatBytesShort(buf.byteLength)} -> ${formatBytesShort(out.byteLength)} (-${byteReductionPct}%)`, {
159
+ log.info(`Image resized to fit limits: ${sourceWithFile} ${formatBytesShort(buf.byteLength)} -> ${formatBytesShort(out.byteLength)} (-${byteReductionPct}%)`, {
99
160
  label: params.label,
161
+ fileName: params.fileName,
100
162
  sourceMimeType: params.mimeType,
101
163
  sourceWidth: width,
102
164
  sourceHeight: height,
@@ -125,8 +187,10 @@ async function resizeImageBase64IfNeeded(params) {
125
187
  const maxMb = (params.maxBytes / (1024 * 1024)).toFixed(0);
126
188
  const gotMb = (best.byteLength / (1024 * 1024)).toFixed(2);
127
189
  const sourcePixels = typeof width === "number" && typeof height === "number" ? `${width}x${height}px` : "unknown";
128
- log.warn(`Image resize failed to fit limits: ${sourcePixels} best=${formatBytesShort(best.byteLength)} limit=${formatBytesShort(params.maxBytes)}`, {
190
+ const sourceWithFile = params.fileName ? `${params.fileName} ${sourcePixels}` : sourcePixels;
191
+ log.warn(`Image resize failed to fit limits: ${sourceWithFile} best=${formatBytesShort(best.byteLength)} limit=${formatBytesShort(params.maxBytes)}`, {
129
192
  label: params.label,
193
+ fileName: params.fileName,
130
194
  sourceMimeType: params.mimeType,
131
195
  sourceWidth: width,
132
196
  sourceHeight: height,
@@ -143,7 +207,14 @@ export async function sanitizeContentBlocksImages(blocks, label, opts = {}) {
143
207
  const maxDimensionPx = Math.max(opts.maxDimensionPx ?? MAX_IMAGE_DIMENSION_PX, 1);
144
208
  const maxBytes = Math.max(opts.maxBytes ?? MAX_IMAGE_BYTES, 1);
145
209
  const out = [];
210
+ let mediaPathHint;
146
211
  for (const block of blocks) {
212
+ if (isTextBlock(block)) {
213
+ const mediaPath = parseMediaPathFromText(block.text);
214
+ if (mediaPath) {
215
+ mediaPathHint = mediaPath;
216
+ }
217
+ }
147
218
  if (!isImageBlock(block)) {
148
219
  out.push(block);
149
220
  continue;
@@ -159,12 +230,14 @@ export async function sanitizeContentBlocksImages(blocks, label, opts = {}) {
159
230
  try {
160
231
  const inferredMimeType = inferMimeTypeFromBase64(data);
161
232
  const mimeType = inferredMimeType ?? block.mimeType;
233
+ const fileName = inferImageFileName({ block, label, mediaPathHint });
162
234
  const resized = await resizeImageBase64IfNeeded({
163
235
  base64: data,
164
236
  mimeType,
165
237
  maxDimensionPx,
166
238
  maxBytes,
167
239
  label,
240
+ fileName,
168
241
  });
169
242
  out.push({
170
243
  ...block,
@@ -1,3 +1,15 @@
1
+ // Keep tool-policy browser-safe: do not import tools/common at runtime.
2
+ function wrapOwnerOnlyToolExecution(tool, senderIsOwner) {
3
+ if (tool.ownerOnly !== true || senderIsOwner || !tool.execute) {
4
+ return tool;
5
+ }
6
+ return {
7
+ ...tool,
8
+ execute: async () => {
9
+ throw new Error("Tool restricted to owner senders.");
10
+ },
11
+ };
12
+ }
1
13
  const TOOL_NAME_ALIASES = {
2
14
  bash: "exec",
3
15
  "apply-patch": "apply_patch",
@@ -16,6 +28,7 @@ export const TOOL_GROUPS = {
16
28
  "sessions_history",
17
29
  "sessions_send",
18
30
  "sessions_spawn",
31
+ "subagents",
19
32
  "session_status",
20
33
  ],
21
34
  // UI helpers
@@ -26,7 +39,7 @@ export const TOOL_GROUPS = {
26
39
  "group:messaging": ["message"],
27
40
  // Nodes + device tools
28
41
  "group:nodes": ["nodes"],
29
- // All Poolbot native tools (excludes provider plugins).
42
+ // All Pool Bot native tools (excludes provider plugins).
30
43
  "group:poolbot": [
31
44
  "browser",
32
45
  "canvas",
@@ -39,6 +52,7 @@ export const TOOL_GROUPS = {
39
52
  "sessions_history",
40
53
  "sessions_send",
41
54
  "sessions_spawn",
55
+ "subagents",
42
56
  "session_status",
43
57
  "memory_search",
44
58
  "memory_get",
@@ -47,7 +61,7 @@ export const TOOL_GROUPS = {
47
61
  "image",
48
62
  ],
49
63
  };
50
- const OWNER_ONLY_TOOL_NAMES = new Set(["whatsapp_login"]);
64
+ const OWNER_ONLY_TOOL_NAME_FALLBACKS = new Set(["whatsapp_login", "cron", "gateway"]);
51
65
  const TOOL_PROFILES = {
52
66
  minimal: {
53
67
  allow: ["session_status"],
@@ -71,31 +85,27 @@ export function normalizeToolName(name) {
71
85
  return TOOL_NAME_ALIASES[normalized] ?? normalized;
72
86
  }
73
87
  export function isOwnerOnlyToolName(name) {
74
- return OWNER_ONLY_TOOL_NAMES.has(normalizeToolName(name));
88
+ return OWNER_ONLY_TOOL_NAME_FALLBACKS.has(normalizeToolName(name));
89
+ }
90
+ function isOwnerOnlyTool(tool) {
91
+ return tool.ownerOnly === true || isOwnerOnlyToolName(tool.name);
75
92
  }
76
93
  export function applyOwnerOnlyToolPolicy(tools, senderIsOwner) {
77
94
  const withGuard = tools.map((tool) => {
78
- if (!isOwnerOnlyToolName(tool.name)) {
95
+ if (!isOwnerOnlyTool(tool)) {
79
96
  return tool;
80
97
  }
81
- if (senderIsOwner || !tool.execute) {
82
- return tool;
83
- }
84
- return {
85
- ...tool,
86
- execute: async () => {
87
- throw new Error("Tool restricted to owner senders.");
88
- },
89
- };
98
+ return wrapOwnerOnlyToolExecution(tool, senderIsOwner);
90
99
  });
91
100
  if (senderIsOwner) {
92
101
  return withGuard;
93
102
  }
94
- return withGuard.filter((tool) => !isOwnerOnlyToolName(tool.name));
103
+ return withGuard.filter((tool) => !isOwnerOnlyTool(tool));
95
104
  }
96
105
  export function normalizeToolList(list) {
97
- if (!list)
106
+ if (!list) {
98
107
  return [];
108
+ }
99
109
  return list.map(normalizeToolName).filter(Boolean);
100
110
  }
101
111
  export function expandToolGroups(list) {
@@ -114,14 +124,17 @@ export function expandToolGroups(list) {
114
124
  export function collectExplicitAllowlist(policies) {
115
125
  const entries = [];
116
126
  for (const policy of policies) {
117
- if (!policy?.allow)
127
+ if (!policy?.allow) {
118
128
  continue;
129
+ }
119
130
  for (const value of policy.allow) {
120
- if (typeof value !== "string")
131
+ if (typeof value !== "string") {
121
132
  continue;
133
+ }
122
134
  const trimmed = value.trim();
123
- if (trimmed)
135
+ if (trimmed) {
124
136
  entries.push(trimmed);
137
+ }
125
138
  }
126
139
  }
127
140
  return entries;
@@ -131,8 +144,9 @@ export function buildPluginToolGroups(params) {
131
144
  const byPlugin = new Map();
132
145
  for (const tool of params.tools) {
133
146
  const meta = params.toolMeta(tool);
134
- if (!meta)
147
+ if (!meta) {
135
148
  continue;
149
+ }
136
150
  const name = normalizeToolName(tool.name);
137
151
  all.push(name);
138
152
  const pluginId = meta.pluginId.toLowerCase();
@@ -143,8 +157,9 @@ export function buildPluginToolGroups(params) {
143
157
  return { all, byPlugin };
144
158
  }
145
159
  export function expandPluginGroups(list, groups) {
146
- if (!list || list.length === 0)
160
+ if (!list || list.length === 0) {
147
161
  return list;
162
+ }
148
163
  const expanded = [];
149
164
  for (const entry of list) {
150
165
  const normalized = normalizeToolName(entry);
@@ -167,8 +182,9 @@ export function expandPluginGroups(list, groups) {
167
182
  return Array.from(new Set(expanded));
168
183
  }
169
184
  export function expandPolicyWithPluginGroups(policy, groups) {
170
- if (!policy)
185
+ if (!policy) {
171
186
  return undefined;
187
+ }
172
188
  return {
173
189
  allow: expandPluginGroups(policy.allow, groups),
174
190
  deny: expandPluginGroups(policy.deny, groups),
@@ -187,13 +203,19 @@ export function stripPluginOnlyAllowlist(policy, groups, coreTools) {
187
203
  const unknownAllowlist = [];
188
204
  let hasCoreEntry = false;
189
205
  for (const entry of normalized) {
206
+ if (entry === "*") {
207
+ hasCoreEntry = true;
208
+ continue;
209
+ }
190
210
  const isPluginEntry = entry === "group:plugins" || pluginIds.has(entry) || pluginTools.has(entry);
191
211
  const expanded = expandToolGroups([entry]);
192
212
  const isCoreEntry = expanded.some((tool) => coreTools.has(tool));
193
- if (isCoreEntry)
213
+ if (isCoreEntry) {
194
214
  hasCoreEntry = true;
195
- if (!isCoreEntry && !isPluginEntry)
215
+ }
216
+ if (!isCoreEntry && !isPluginEntry) {
196
217
  unknownAllowlist.push(entry);
218
+ }
197
219
  }
198
220
  const strippedAllowlist = !hasCoreEntry;
199
221
  // When an allowlist contains only plugin tools, we strip it to avoid accidentally
@@ -209,13 +231,16 @@ export function stripPluginOnlyAllowlist(policy, groups, coreTools) {
209
231
  };
210
232
  }
211
233
  export function resolveToolProfilePolicy(profile) {
212
- if (!profile)
234
+ if (!profile) {
213
235
  return undefined;
236
+ }
214
237
  const resolved = TOOL_PROFILES[profile];
215
- if (!resolved)
238
+ if (!resolved) {
216
239
  return undefined;
217
- if (!resolved.allow && !resolved.deny)
240
+ }
241
+ if (!resolved.allow && !resolved.deny) {
218
242
  return undefined;
243
+ }
219
244
  return {
220
245
  allow: resolved.allow ? [...resolved.allow] : undefined,
221
246
  deny: resolved.deny ? [...resolved.deny] : undefined,
@@ -3,7 +3,7 @@ import { browserAct, browserArmDialog, browserArmFileChooser, browserConsoleMess
3
3
  import { browserCloseTab, browserFocusTab, browserOpenTab, browserProfiles, browserSnapshot, browserStart, browserStatus, browserStop, browserTabs, } from "../../browser/client.js";
4
4
  import { resolveBrowserConfig } from "../../browser/config.js";
5
5
  import { DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "../../browser/constants.js";
6
- import { DEFAULT_UPLOAD_DIR, resolvePathsWithinRoot } from "../../browser/paths.js";
6
+ import { DEFAULT_UPLOAD_DIR, resolveExistingPathsWithinRoot } from "../../browser/paths.js";
7
7
  import { applyBrowserProxyPaths, persistBrowserProxyFiles } from "../../browser/proxy-files.js";
8
8
  import { loadConfig } from "../../config/config.js";
9
9
  import { wrapExternalContent } from "../../security/external-content.js";
@@ -587,7 +587,7 @@ export function createBrowserTool(opts) {
587
587
  if (paths.length === 0) {
588
588
  throw new Error("paths required");
589
589
  }
590
- const uploadPathsResult = resolvePathsWithinRoot({
590
+ const uploadPathsResult = await resolveExistingPathsWithinRoot({
591
591
  rootDir: DEFAULT_UPLOAD_DIR,
592
592
  requestedPaths: paths,
593
593
  scopeLabel: `uploads directory (${DEFAULT_UPLOAD_DIR})`,
@@ -1,8 +1,12 @@
1
1
  import crypto from "node:crypto";
2
2
  import fs from "node:fs/promises";
3
+ import path from "node:path";
3
4
  import { Type } from "@sinclair/typebox";
4
5
  import { writeBase64ToFile } from "../../cli/nodes-camera.js";
5
6
  import { canvasSnapshotTempPath, parseCanvasSnapshotPayload } from "../../cli/nodes-canvas.js";
7
+ import { logVerbose, shouldLogVerbose } from "../../globals.js";
8
+ import { isInboundPathAllowed } from "../../media/inbound-path-policy.js";
9
+ import { getDefaultMediaLocalRoots } from "../../media/local-roots.js";
6
10
  import { imageMimeFromFormat } from "../../media/mime.js";
7
11
  import { resolveImageSanitizationLimits } from "../image-sanitization.js";
8
12
  import { optionalStringEnum, stringEnum } from "../schema/typebox.js";
@@ -19,6 +23,28 @@ const CANVAS_ACTIONS = [
19
23
  "a2ui_reset",
20
24
  ];
21
25
  const CANVAS_SNAPSHOT_FORMATS = ["png", "jpg", "jpeg"];
26
+ async function readJsonlFromPath(jsonlPath) {
27
+ const trimmed = jsonlPath.trim();
28
+ if (!trimmed) {
29
+ return "";
30
+ }
31
+ const resolved = path.resolve(trimmed);
32
+ const roots = getDefaultMediaLocalRoots();
33
+ if (!isInboundPathAllowed({ filePath: resolved, roots })) {
34
+ if (shouldLogVerbose()) {
35
+ logVerbose(`Blocked canvas jsonlPath outside allowed roots: ${resolved}`);
36
+ }
37
+ throw new Error("jsonlPath outside allowed roots");
38
+ }
39
+ const canonical = await fs.realpath(resolved).catch(() => resolved);
40
+ if (!isInboundPathAllowed({ filePath: canonical, roots })) {
41
+ if (shouldLogVerbose()) {
42
+ logVerbose(`Blocked canvas jsonlPath outside allowed roots: ${canonical}`);
43
+ }
44
+ throw new Error("jsonlPath outside allowed roots");
45
+ }
46
+ return await fs.readFile(canonical, "utf8");
47
+ }
22
48
  // Flattened schema: runtime validates per-action requirements.
23
49
  const CanvasToolSchema = Type.Object({
24
50
  action: stringEnum(CANVAS_ACTIONS),
@@ -145,7 +171,7 @@ export function createCanvasTool(options) {
145
171
  const jsonl = typeof params.jsonl === "string" && params.jsonl.trim()
146
172
  ? params.jsonl
147
173
  : typeof params.jsonlPath === "string" && params.jsonlPath.trim()
148
- ? await fs.readFile(params.jsonlPath.trim(), "utf8")
174
+ ? await readJsonlFromPath(params.jsonlPath)
149
175
  : "";
150
176
  if (!jsonl.trim()) {
151
177
  throw new Error("jsonl or jsonlPath required");
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs/promises";
2
2
  import { detectMime } from "../../media/mime.js";
3
3
  import { sanitizeToolResultImages } from "../tool-images.js";
4
+ export const OWNER_ONLY_TOOL_ERROR = "Tool restricted to owner senders.";
4
5
  export class ToolInputError extends Error {
5
6
  status = 400;
6
7
  constructor(message) {
@@ -8,6 +9,13 @@ export class ToolInputError extends Error {
8
9
  this.name = "ToolInputError";
9
10
  }
10
11
  }
12
+ export class ToolAuthorizationError extends ToolInputError {
13
+ status = 403;
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "ToolAuthorizationError";
17
+ }
18
+ }
11
19
  export function createActionGate(actions) {
12
20
  return (key, defaultValue = true) => {
13
21
  const value = actions?.[key];
@@ -131,6 +139,17 @@ export function jsonResult(payload) {
131
139
  details: payload,
132
140
  };
133
141
  }
142
+ export function wrapOwnerOnlyToolExecution(tool, senderIsOwner) {
143
+ if (tool.ownerOnly !== true || senderIsOwner || !tool.execute) {
144
+ return tool;
145
+ }
146
+ return {
147
+ ...tool,
148
+ execute: async () => {
149
+ throw new Error(OWNER_ONLY_TOOL_ERROR);
150
+ },
151
+ };
152
+ }
134
153
  export async function imageResult(params) {
135
154
  const content = [
136
155
  {
@@ -162,3 +181,29 @@ export async function imageResultFromFile(params) {
162
181
  imageSanitization: params.imageSanitization,
163
182
  });
164
183
  }
184
+ /**
185
+ * Validate and parse an `availableTags` parameter from untrusted input.
186
+ * Returns `undefined` when the value is missing or not an array.
187
+ * Entries that lack a string `name` are silently dropped.
188
+ */
189
+ export function parseAvailableTags(raw) {
190
+ if (raw === undefined || raw === null) {
191
+ return undefined;
192
+ }
193
+ if (!Array.isArray(raw)) {
194
+ return undefined;
195
+ }
196
+ const result = raw
197
+ .filter((t) => typeof t === "object" && t !== null && typeof t.name === "string")
198
+ .map((t) => ({
199
+ ...(t.id !== undefined && typeof t.id === "string" ? { id: t.id } : {}),
200
+ name: t.name,
201
+ ...(typeof t.moderated === "boolean" ? { moderated: t.moderated } : {}),
202
+ ...(t.emoji_id === null || typeof t.emoji_id === "string" ? { emoji_id: t.emoji_id } : {}),
203
+ ...(t.emoji_name === null || typeof t.emoji_name === "string"
204
+ ? { emoji_name: t.emoji_name }
205
+ : {}),
206
+ }));
207
+ // Return undefined instead of empty array to avoid accidentally clearing all tags
208
+ return result.length ? result : undefined;
209
+ }
@@ -1,6 +1,6 @@
1
1
  import { getPresence } from "../../discord/monitor/presence-cache.js";
2
2
  import { addRoleDiscord, createChannelDiscord, createScheduledEventDiscord, deleteChannelDiscord, editChannelDiscord, fetchChannelInfoDiscord, fetchMemberInfoDiscord, fetchRoleInfoDiscord, fetchVoiceStatusDiscord, listGuildChannelsDiscord, listGuildEmojisDiscord, listScheduledEventsDiscord, moveChannelDiscord, removeChannelPermissionDiscord, removeRoleDiscord, setChannelPermissionDiscord, uploadEmojiDiscord, uploadStickerDiscord, } from "../../discord/send.js";
3
- import { jsonResult, readNumberParam, readStringArrayParam, readStringParam, } from "./common.js";
3
+ import { jsonResult, readNumberParam, parseAvailableTags, readStringArrayParam, readStringParam, } from "./common.js";
4
4
  function readParentIdParam(params) {
5
5
  if (params.clearParent === true)
6
6
  return null;
@@ -280,6 +280,7 @@ export async function handleDiscordGuildAction(action, params, isActionEnabled)
280
280
  const rateLimitPerUser = readNumberParam(params, "rateLimitPerUser", {
281
281
  integer: true,
282
282
  });
283
+ const availableTags = parseAvailableTags(params.availableTags);
283
284
  const channel = accountId
284
285
  ? await editChannelDiscord({
285
286
  channelId,
@@ -289,6 +290,7 @@ export async function handleDiscordGuildAction(action, params, isActionEnabled)
289
290
  parentId,
290
291
  nsfw,
291
292
  rateLimitPerUser: rateLimitPerUser ?? undefined,
293
+ availableTags,
292
294
  }, { accountId })
293
295
  : await editChannelDiscord({
294
296
  channelId,
@@ -298,6 +300,7 @@ export async function handleDiscordGuildAction(action, params, isActionEnabled)
298
300
  parentId,
299
301
  nsfw,
300
302
  rateLimitPerUser: rateLimitPerUser ?? undefined,
303
+ availableTags,
301
304
  });
302
305
  return jsonResult({ ok: true, channel });
303
306
  }
@@ -6,6 +6,8 @@ import { formatDoctorNonInteractiveHint, writeRestartSentinel, } from "../../inf
6
6
  import { stringEnum } from "../schema/typebox.js";
7
7
  import { jsonResult, readStringParam } from "./common.js";
8
8
  import { callGatewayTool } from "./gateway.js";
9
+ import { createSubsystemLogger } from "../../logging/subsystem.js";
10
+ const log = createSubsystemLogger("gateway-tool");
9
11
  function resolveBaseHashFromSnapshot(snapshot) {
10
12
  if (!snapshot || typeof snapshot !== "object")
11
13
  return undefined;
@@ -121,7 +123,7 @@ export function createGatewayTool(opts) {
121
123
  catch {
122
124
  // ignore: sentinel is best-effort
123
125
  }
124
- console.info(`gateway tool: restart requested (delayMs=${delayMs ?? "default"}, reason=${reason ?? "none"})`);
126
+ log.info(`gateway tool: restart requested (delayMs=${delayMs ?? "default"}, reason=${reason ?? "none"})`);
125
127
  const scheduled = scheduleGatewaySigusr1Restart({
126
128
  delayMs,
127
129
  reason,
@@ -1,15 +1,6 @@
1
+ import { parseNodeList, parsePairingList } from "../../shared/node-list-parse.js";
1
2
  import { resolveNodeIdFromCandidates } from "../../shared/node-match.js";
2
3
  import { callGatewayTool } from "./gateway.js";
3
- function parseNodeList(value) {
4
- const obj = typeof value === "object" && value !== null ? value : {};
5
- return Array.isArray(obj.nodes) ? obj.nodes : [];
6
- }
7
- function parsePairingList(value) {
8
- const obj = typeof value === "object" && value !== null ? value : {};
9
- const pending = Array.isArray(obj.pending) ? obj.pending : [];
10
- const paired = Array.isArray(obj.paired) ? obj.paired : [];
11
- return { pending, paired };
12
- }
13
4
  async function loadNodes(opts) {
14
5
  try {
15
6
  const res = await callGatewayTool("node.list", opts, {});
@@ -7,11 +7,13 @@ const MAX_PING_PONG_TURNS = 5;
7
7
  export function resolveAnnounceTargetFromKey(sessionKey) {
8
8
  const rawParts = sessionKey.split(":").filter(Boolean);
9
9
  const parts = rawParts.length >= 3 && rawParts[0] === "agent" ? rawParts.slice(2) : rawParts;
10
- if (parts.length < 3)
10
+ if (parts.length < 3) {
11
11
  return null;
12
+ }
12
13
  const [channelRaw, kind, ...rest] = parts;
13
- if (kind !== "group" && kind !== "channel")
14
+ if (kind !== "group" && kind !== "channel") {
14
15
  return null;
16
+ }
15
17
  // Extract topic/thread ID from rest (supports both :topic: and :thread:)
16
18
  // Telegram uses :topic:, other platforms use :thread:
17
19
  let threadId;
@@ -24,15 +26,18 @@ export function resolveAnnounceTargetFromKey(sessionKey) {
24
26
  }
25
27
  // Remove :topic:N or :thread:N suffix from ID for target
26
28
  const id = match ? restJoined.replace(/:(topic|thread):\d+$/, "") : restJoined.trim();
27
- if (!id)
29
+ if (!id) {
28
30
  return null;
29
- if (!channelRaw)
31
+ }
32
+ if (!channelRaw) {
30
33
  return null;
34
+ }
31
35
  const normalizedChannel = normalizeAnyChannelId(channelRaw) ?? normalizeChatChannelId(channelRaw);
32
36
  const channel = normalizedChannel ?? channelRaw.toLowerCase();
33
37
  const kindTarget = (() => {
34
- if (!normalizedChannel)
38
+ if (!normalizedChannel) {
35
39
  return id;
40
+ }
36
41
  if (normalizedChannel === "discord" || normalizedChannel === "slack") {
37
42
  return `channel:${id}`;
38
43
  }
@@ -109,8 +114,9 @@ export function isReplySkip(text) {
109
114
  export function resolvePingPongTurns(cfg) {
110
115
  const raw = cfg?.session?.agentToAgent?.maxPingPongTurns;
111
116
  const fallback = DEFAULT_PING_PONG_TURNS;
112
- if (typeof raw !== "number" || !Number.isFinite(raw))
117
+ if (typeof raw !== "number" || !Number.isFinite(raw)) {
113
118
  return fallback;
119
+ }
114
120
  const rounded = Math.floor(raw);
115
121
  return Math.max(0, Math.min(MAX_PING_PONG_TURNS, rounded));
116
122
  }
@@ -1,6 +1,6 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { optionalStringEnum } from "../schema/typebox.js";
3
- import { spawnSubagentDirect } from "../subagent-spawn.js";
3
+ import { SUBAGENT_SPAWN_MODES, spawnSubagentDirect } from "../subagent-spawn.js";
4
4
  import { jsonResult, readStringParam } from "./common.js";
5
5
  const SessionsSpawnToolSchema = Type.Object({
6
6
  task: Type.String(),
@@ -11,13 +11,15 @@ const SessionsSpawnToolSchema = Type.Object({
11
11
  runTimeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
12
12
  // Back-compat: older callers used timeoutSeconds for this tool.
13
13
  timeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
14
+ thread: Type.Optional(Type.Boolean()),
15
+ mode: optionalStringEnum(SUBAGENT_SPAWN_MODES),
14
16
  cleanup: optionalStringEnum(["delete", "keep"]),
15
17
  });
16
18
  export function createSessionsSpawnTool(opts) {
17
19
  return {
18
20
  label: "Sessions",
19
21
  name: "sessions_spawn",
20
- description: "Spawn a background sub-agent run in an isolated session and announce the result back to the requester chat.",
22
+ description: 'Spawn a sub-agent in an isolated session (mode="run" one-shot or mode="session" persistent) and route results back to the requester chat/thread.',
21
23
  parameters: SessionsSpawnToolSchema,
22
24
  execute: async (_toolCallId, args) => {
23
25
  const params = args;
@@ -26,6 +28,7 @@ export function createSessionsSpawnTool(opts) {
26
28
  const requestedAgentId = readStringParam(params, "agentId");
27
29
  const modelOverride = readStringParam(params, "model");
28
30
  const thinkingOverrideRaw = readStringParam(params, "thinking");
31
+ const mode = params.mode === "run" || params.mode === "session" ? params.mode : undefined;
29
32
  const cleanup = params.cleanup === "keep" || params.cleanup === "delete" ? params.cleanup : "keep";
30
33
  // Back-compat: older callers used timeoutSeconds for this tool.
31
34
  const timeoutSecondsCandidate = typeof params.runTimeoutSeconds === "number"
@@ -36,6 +39,7 @@ export function createSessionsSpawnTool(opts) {
36
39
  const runTimeoutSeconds = typeof timeoutSecondsCandidate === "number" && Number.isFinite(timeoutSecondsCandidate)
37
40
  ? Math.max(0, Math.floor(timeoutSecondsCandidate))
38
41
  : undefined;
42
+ const thread = params.thread === true;
39
43
  const result = await spawnSubagentDirect({
40
44
  task,
41
45
  label: label || undefined,
@@ -43,6 +47,8 @@ export function createSessionsSpawnTool(opts) {
43
47
  model: modelOverride,
44
48
  thinking: thinkingOverrideRaw,
45
49
  runTimeoutSeconds,
50
+ thread,
51
+ mode,
46
52
  cleanup,
47
53
  expectsCompletionMessage: true,
48
54
  }, {
@@ -1,6 +1,7 @@
1
1
  import crypto from "node:crypto";
2
2
  import { Type } from "@sinclair/typebox";
3
3
  import { clearSessionQueues } from "../../auto-reply/reply/queue.js";
4
+ import { DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH } from "../../config/agent-limits.js";
4
5
  import { loadConfig } from "../../config/config.js";
5
6
  import { loadSessionStore, resolveStorePath, updateSessionStore } from "../../config/sessions.js";
6
7
  import { callGateway } from "../../gateway/call.js";
@@ -181,7 +182,7 @@ function resolveRequesterKey(params) {
181
182
  // Check if this sub-agent can spawn children (orchestrator).
182
183
  // If so, it should see its own children, not its parent's children.
183
184
  const callerDepth = getSubagentDepthFromSessionStore(callerSessionKey, { cfg: params.cfg });
184
- const maxSpawnDepth = params.cfg.agents?.defaults?.subagents?.maxSpawnDepth ?? 1;
185
+ const maxSpawnDepth = params.cfg.agents?.defaults?.subagents?.maxSpawnDepth ?? DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH;
185
186
  if (callerDepth < maxSpawnDepth) {
186
187
  // Orchestrator sub-agent: use its own session key as requester
187
188
  // so it sees children it spawned.