@poolzin/pool-bot 2026.2.24 → 2026.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (646) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/acp/client.js +207 -18
  3. package/dist/acp/event-mapper.js +87 -22
  4. package/dist/acp/meta.js +12 -6
  5. package/dist/acp/secret-file.js +22 -0
  6. package/dist/agents/agent-paths.js +8 -9
  7. package/dist/agents/agent-scope.js +17 -5
  8. package/dist/agents/auth-profiles/oauth.js +148 -64
  9. package/dist/agents/auth-profiles/session-override.js +13 -7
  10. package/dist/agents/bash-process-registry.test-helpers.js +29 -0
  11. package/dist/agents/bash-tools.exec-approval-request.js +20 -0
  12. package/dist/agents/bash-tools.exec-host-gateway.js +240 -0
  13. package/dist/agents/bash-tools.exec-host-node.js +235 -0
  14. package/dist/agents/bash-tools.exec-runtime.js +2 -25
  15. package/dist/agents/bash-tools.exec-types.js +1 -0
  16. package/dist/agents/bash-tools.process.js +224 -218
  17. package/dist/agents/bedrock-discovery.js +3 -1
  18. package/dist/agents/byteplus-models.js +97 -0
  19. package/dist/agents/chutes-oauth.js +1 -0
  20. package/dist/agents/cli-runner/helpers.js +4 -0
  21. package/dist/agents/compaction.js +41 -14
  22. package/dist/agents/content-blocks.js +16 -0
  23. package/dist/agents/doubao-models.js +121 -0
  24. package/dist/agents/failover-error.js +2 -0
  25. package/dist/agents/huggingface-models.js +5 -3
  26. package/dist/agents/live-model-filter.js +5 -0
  27. package/dist/agents/minimax-vlm.js +10 -8
  28. package/dist/agents/model-auth.js +6 -0
  29. package/dist/agents/model-catalog.js +3 -1
  30. package/dist/agents/model-fallback.js +96 -101
  31. package/dist/agents/model-selection.js +7 -1
  32. package/dist/agents/models-config.providers.js +364 -165
  33. package/dist/agents/ollama-stream.js +117 -4
  34. package/dist/agents/opencode-zen-models.js +22 -11
  35. package/dist/agents/pi-embedded-helpers/errors.js +55 -33
  36. package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
  37. package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
  38. package/dist/agents/pi-embedded-helpers.js +1 -1
  39. package/dist/agents/pi-embedded-payloads.js +1 -0
  40. package/dist/agents/pi-embedded-runner/compact.js +29 -7
  41. package/dist/agents/pi-embedded-runner/extensions.js +28 -26
  42. package/dist/agents/pi-embedded-runner/google.js +20 -8
  43. package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
  44. package/dist/agents/pi-embedded-runner/run.js +71 -12
  45. package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
  46. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
  47. package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
  48. package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
  49. package/dist/agents/pi-embedded-runner/thinking.js +42 -0
  50. package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
  51. package/dist/agents/pi-embedded-runner/utils.js +7 -10
  52. package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
  53. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
  54. package/dist/agents/pi-embedded-subscribe.js +9 -4
  55. package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
  56. package/dist/agents/pi-embedded-utils.js +3 -0
  57. package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
  58. package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
  59. package/dist/agents/pi-settings.js +40 -0
  60. package/dist/agents/pi-tools.policy.js +2 -1
  61. package/dist/agents/provider/config-loader.js +1 -1
  62. package/dist/agents/sandbox/browser.js +170 -33
  63. package/dist/agents/sandbox/config-hash.js +14 -27
  64. package/dist/agents/sandbox/config.js +21 -2
  65. package/dist/agents/sandbox/constants.js +2 -0
  66. package/dist/agents/sandbox/docker.js +16 -2
  67. package/dist/agents/sandbox/novnc-auth.js +62 -0
  68. package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
  69. package/dist/agents/sandbox/shared.js +10 -6
  70. package/dist/agents/sandbox-paths.js +24 -11
  71. package/dist/agents/schema/clean-for-gemini.js +132 -85
  72. package/dist/agents/session-slug.js +10 -5
  73. package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
  74. package/dist/agents/session-tool-result-guard.js +3 -1
  75. package/dist/agents/session-transcript-repair.js +40 -6
  76. package/dist/agents/skills/bundled-dir.js +19 -5
  77. package/dist/agents/skills/env-overrides.js +124 -43
  78. package/dist/agents/skills/frontmatter.js +6 -6
  79. package/dist/agents/skills/plugin-skills.js +14 -7
  80. package/dist/agents/skills/workspace.js +1 -0
  81. package/dist/agents/skills.test-helpers.js +13 -0
  82. package/dist/agents/stable-stringify.js +12 -0
  83. package/dist/agents/subagent-announce.js +251 -49
  84. package/dist/agents/subagent-lifecycle-events.js +19 -0
  85. package/dist/agents/subagent-registry-cleanup.js +31 -0
  86. package/dist/agents/subagent-registry-completion.js +68 -0
  87. package/dist/agents/subagent-registry-queries.js +117 -0
  88. package/dist/agents/subagent-registry-state.js +46 -0
  89. package/dist/agents/subagent-registry.js +252 -221
  90. package/dist/agents/subagent-registry.mocks.shared.js +12 -0
  91. package/dist/agents/subagent-registry.store.js +1 -0
  92. package/dist/agents/subagent-registry.types.js +1 -0
  93. package/dist/agents/subagent-spawn.js +195 -7
  94. package/dist/agents/system-prompt.js +22 -6
  95. package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
  96. package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
  97. package/dist/agents/test-helpers/fast-core-tools.js +1 -17
  98. package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
  99. package/dist/agents/timeout.js +18 -6
  100. package/dist/agents/tool-call-id.js +1 -1
  101. package/dist/agents/tool-display-common.js +162 -29
  102. package/dist/agents/tool-images.js +82 -9
  103. package/dist/agents/tool-policy-shared.js +108 -0
  104. package/dist/agents/tool-policy.js +51 -26
  105. package/dist/agents/tools/browser-tool.js +160 -54
  106. package/dist/agents/tools/canvas-tool.js +27 -1
  107. package/dist/agents/tools/common.js +45 -0
  108. package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
  109. package/dist/agents/tools/discord-actions-guild.js +4 -1
  110. package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
  111. package/dist/agents/tools/gateway-tool.js +3 -1
  112. package/dist/agents/tools/image-tool.js +214 -99
  113. package/dist/agents/tools/nodes-utils.js +1 -10
  114. package/dist/agents/tools/sessions-history-tool.js +140 -108
  115. package/dist/agents/tools/sessions-send-helpers.js +12 -6
  116. package/dist/agents/tools/sessions-spawn-tool.js +8 -2
  117. package/dist/agents/tools/subagents-tool.js +2 -1
  118. package/dist/agents/tools/whatsapp-actions.js +10 -2
  119. package/dist/agents/tools/whatsapp-target-auth.js +18 -0
  120. package/dist/agents/transcript-policy.js +22 -8
  121. package/dist/agents/venice-models.js +11 -3
  122. package/dist/agents/workspace.js +222 -46
  123. package/dist/auto-reply/commands-registry.data.js +51 -0
  124. package/dist/auto-reply/commands-registry.js +19 -21
  125. package/dist/auto-reply/fallback-state.js +114 -0
  126. package/dist/auto-reply/group-activation.js +10 -5
  127. package/dist/auto-reply/inbound-debounce.js +10 -5
  128. package/dist/auto-reply/model-runtime.js +68 -0
  129. package/dist/auto-reply/reply/abort.js +1 -1
  130. package/dist/auto-reply/reply/agent-runner-execution.js +40 -5
  131. package/dist/auto-reply/reply/agent-runner.js +165 -39
  132. package/dist/auto-reply/reply/bash-command.js +41 -39
  133. package/dist/auto-reply/reply/command-gates.js +25 -0
  134. package/dist/auto-reply/reply/commands-allowlist.js +111 -72
  135. package/dist/auto-reply/reply/commands-bash.js +6 -5
  136. package/dist/auto-reply/reply/commands-config.js +30 -28
  137. package/dist/auto-reply/reply/commands-core.js +2 -1
  138. package/dist/auto-reply/reply/commands-info.js +1 -0
  139. package/dist/auto-reply/reply/commands-models.js +65 -14
  140. package/dist/auto-reply/reply/commands-session.js +237 -82
  141. package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
  142. package/dist/auto-reply/reply/commands-setunset.js +45 -0
  143. package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
  144. package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
  145. package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
  146. package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
  147. package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
  148. package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
  149. package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
  150. package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
  151. package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
  152. package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
  153. package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
  154. package/dist/auto-reply/reply/commands-subagents.js +51 -587
  155. package/dist/auto-reply/reply/commands-tts.js +10 -5
  156. package/dist/auto-reply/reply/config-value.js +10 -5
  157. package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
  158. package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
  159. package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
  160. package/dist/auto-reply/reply/followup-runner.js +1 -0
  161. package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
  162. package/dist/auto-reply/reply/get-reply-directives.js +17 -28
  163. package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
  164. package/dist/auto-reply/reply/get-reply.js +71 -12
  165. package/dist/auto-reply/reply/model-selection.js +80 -39
  166. package/dist/auto-reply/reply/queue/enqueue.js +10 -5
  167. package/dist/auto-reply/reply/queue/state.js +13 -12
  168. package/dist/auto-reply/reply/reply-payloads.js +67 -36
  169. package/dist/auto-reply/reply/reply-reference.js +9 -8
  170. package/dist/auto-reply/reply/route-reply.js +15 -8
  171. package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
  172. package/dist/auto-reply/reply/session.js +22 -6
  173. package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
  174. package/dist/auto-reply/reply/subagents-utils.js +56 -30
  175. package/dist/auto-reply/reply/typing.js +46 -21
  176. package/dist/auto-reply/send-policy.js +14 -7
  177. package/dist/auto-reply/status.js +140 -16
  178. package/dist/auto-reply/templating.js +10 -5
  179. package/dist/auto-reply/thinking.js +7 -16
  180. package/dist/auto-reply/tokens.js +21 -5
  181. package/dist/browser/bridge-server.js +36 -20
  182. package/dist/browser/cdp.helpers.js +7 -14
  183. package/dist/browser/cdp.js +35 -15
  184. package/dist/browser/chrome.profile-decoration.js +7 -4
  185. package/dist/browser/config.js +30 -0
  186. package/dist/browser/extension-relay-auth.js +55 -0
  187. package/dist/browser/extension-relay.js +74 -29
  188. package/dist/browser/navigation-guard.js +39 -0
  189. package/dist/browser/paths.js +77 -0
  190. package/dist/browser/profiles.js +13 -8
  191. package/dist/browser/pw-ai-module.js +10 -5
  192. package/dist/browser/pw-session.js +76 -39
  193. package/dist/browser/pw-tools-core.interactions.js +14 -7
  194. package/dist/browser/pw-tools-core.state.js +12 -6
  195. package/dist/browser/routes/agent.act.js +431 -424
  196. package/dist/browser/routes/agent.shared.js +47 -3
  197. package/dist/browser/routes/agent.snapshot.js +122 -116
  198. package/dist/browser/routes/agent.storage.js +303 -297
  199. package/dist/browser/routes/tabs.js +154 -100
  200. package/dist/browser/server-context.js +7 -0
  201. package/dist/browser/server-lifecycle.js +37 -0
  202. package/dist/build-info.json +3 -3
  203. package/dist/channels/allow-from.js +26 -0
  204. package/dist/channels/allowlists/resolve-utils.js +43 -19
  205. package/dist/channels/channel-config.js +14 -7
  206. package/dist/channels/draft-stream-loop.js +7 -0
  207. package/dist/channels/model-overrides.js +82 -0
  208. package/dist/channels/plugins/account-action-gate.js +13 -0
  209. package/dist/channels/plugins/message-actions.js +10 -0
  210. package/dist/channels/plugins/normalize/imessage.js +14 -7
  211. package/dist/channels/plugins/normalize/slack.js +10 -5
  212. package/dist/channels/plugins/normalize/telegram.js +14 -7
  213. package/dist/channels/plugins/outbound/discord.js +80 -8
  214. package/dist/channels/plugins/outbound/signal.js +11 -11
  215. package/dist/channels/plugins/setup-helpers.js +10 -5
  216. package/dist/channels/sender-label.js +14 -7
  217. package/dist/channels/session.js +4 -2
  218. package/dist/channels/status-reactions.js +297 -0
  219. package/dist/channels/telegram/api.js +18 -0
  220. package/dist/cli/argv.js +84 -21
  221. package/dist/cli/banner.js +3 -2
  222. package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
  223. package/dist/cli/cli-name.js +11 -11
  224. package/dist/cli/cli-utils.js +13 -3
  225. package/dist/cli/command-format.js +1 -1
  226. package/dist/cli/config-cli.js +1 -1
  227. package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
  228. package/dist/cli/daemon-cli/lifecycle.js +64 -2
  229. package/dist/cli/daemon-cli/restart-health.js +126 -0
  230. package/dist/cli/daemon-cli/status.gather.js +9 -13
  231. package/dist/cli/daemon-cli/status.print.js +2 -10
  232. package/dist/cli/deps.js +27 -22
  233. package/dist/cli/exec-approvals-cli.js +92 -124
  234. package/dist/cli/gateway-cli/run-loop.js +23 -5
  235. package/dist/cli/memory-cli.js +158 -61
  236. package/dist/cli/node-cli/register.js +14 -5
  237. package/dist/cli/nodes-cli/register.push.js +63 -0
  238. package/dist/cli/nodes-media-utils.js +26 -0
  239. package/dist/cli/outbound-send-deps.js +2 -9
  240. package/dist/cli/outbound-send-mapping.js +11 -0
  241. package/dist/cli/pairing-cli.js +40 -14
  242. package/dist/cli/plugins-cli.js +250 -73
  243. package/dist/cli/ports.js +11 -10
  244. package/dist/cli/program/build-program.js +3 -1
  245. package/dist/cli/program/command-registry.js +214 -136
  246. package/dist/cli/program/command-tree.js +16 -0
  247. package/dist/cli/program/help.js +43 -12
  248. package/dist/cli/program/preaction.js +13 -9
  249. package/dist/cli/program/register.configure.js +3 -18
  250. package/dist/cli/program/register.maintenance.js +2 -2
  251. package/dist/cli/program/register.onboard.js +2 -0
  252. package/dist/cli/program/register.status-health-sessions.js +16 -17
  253. package/dist/cli/program/register.subclis.js +93 -52
  254. package/dist/cli/route.js +12 -8
  255. package/dist/cli/system-cli.js +36 -46
  256. package/dist/cli/test-runtime-capture.js +24 -0
  257. package/dist/cli/update-cli/shared.js +22 -9
  258. package/dist/cli/update-cli/update-command.js +89 -14
  259. package/dist/cli/update-cli/wizard.js +6 -12
  260. package/dist/commands/agent/run-context.js +18 -5
  261. package/dist/commands/agent/session-store.js +17 -4
  262. package/dist/commands/agent.js +185 -89
  263. package/dist/commands/agents.bindings.js +14 -7
  264. package/dist/commands/agents.commands.add.js +13 -9
  265. package/dist/commands/agents.commands.identity.js +12 -6
  266. package/dist/commands/agents.commands.list.js +11 -6
  267. package/dist/commands/agents.config.js +8 -10
  268. package/dist/commands/agents.providers.js +12 -6
  269. package/dist/commands/auth-choice-options.js +103 -75
  270. package/dist/commands/auth-choice.apply.byteplus.js +55 -0
  271. package/dist/commands/auth-choice.apply.js +4 -0
  272. package/dist/commands/auth-choice.apply.minimax.js +61 -13
  273. package/dist/commands/auth-choice.apply.openai.js +3 -1
  274. package/dist/commands/auth-choice.apply.volcengine.js +55 -0
  275. package/dist/commands/auth-choice.preferred-provider.js +2 -0
  276. package/dist/commands/channels/remove.js +13 -6
  277. package/dist/commands/channels/shared.js +4 -14
  278. package/dist/commands/channels.mock-harness.js +23 -0
  279. package/dist/commands/configure.commands.js +14 -0
  280. package/dist/commands/configure.gateway.js +2 -4
  281. package/dist/commands/configure.js +1 -1
  282. package/dist/commands/configure.shared.js +11 -0
  283. package/dist/commands/daemon-install-helpers.js +2 -2
  284. package/dist/commands/daemon-install-runtime-warning.js +11 -0
  285. package/dist/commands/dashboard.js +12 -10
  286. package/dist/commands/docs.js +14 -8
  287. package/dist/commands/doctor-config-flow.js +11 -9
  288. package/dist/commands/doctor-legacy-config.js +281 -0
  289. package/dist/commands/doctor-state-integrity.js +99 -23
  290. package/dist/commands/doctor-update.js +12 -9
  291. package/dist/commands/models/list.list-command.js +7 -5
  292. package/dist/commands/models/set-image.js +2 -21
  293. package/dist/commands/node-daemon-install-helpers.js +10 -8
  294. package/dist/commands/onboard-auth.config-minimax.js +54 -80
  295. package/dist/commands/onboard-auth.config-opencode.js +2 -18
  296. package/dist/commands/onboard-auth.credentials.js +90 -13
  297. package/dist/commands/onboard-auth.js +1 -1
  298. package/dist/commands/onboard-auth.models.js +6 -5
  299. package/dist/commands/onboard-hooks.js +1 -1
  300. package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
  301. package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
  302. package/dist/commands/onboard-provider-auth-flags.js +14 -0
  303. package/dist/commands/onboard-remote.js +14 -7
  304. package/dist/commands/onboard.js +11 -13
  305. package/dist/commands/sandbox-display.js +6 -5
  306. package/dist/commands/sessions.test-helpers.js +61 -0
  307. package/dist/commands/status-all/diagnosis.js +14 -10
  308. package/dist/commands/status-all/format.js +1 -0
  309. package/dist/commands/status.gateway-probe.js +1 -16
  310. package/dist/commands/systemd-linger.js +12 -6
  311. package/dist/config/agent-limits.js +2 -0
  312. package/dist/config/commands.js +32 -15
  313. package/dist/config/config-paths.js +9 -11
  314. package/dist/config/config.js +1 -1
  315. package/dist/config/defaults.js +22 -2
  316. package/dist/config/discord-preview-streaming.js +104 -0
  317. package/dist/config/env-substitution.js +62 -34
  318. package/dist/config/env-vars.js +45 -7
  319. package/dist/config/includes.js +4 -0
  320. package/dist/config/io.js +656 -171
  321. package/dist/config/legacy.migrations.part-1.js +189 -78
  322. package/dist/config/legacy.shared.js +3 -1
  323. package/dist/config/merge-patch.js +54 -4
  324. package/dist/config/prototype-keys.js +4 -0
  325. package/dist/config/redact-snapshot.js +404 -76
  326. package/dist/config/schema.help.js +44 -7
  327. package/dist/config/schema.js +58 -570
  328. package/dist/config/schema.labels.js +38 -6
  329. package/dist/config/sessions/delivery-info.js +10 -3
  330. package/dist/config/sessions/main-session.js +10 -5
  331. package/dist/config/sessions/session-file.js +33 -0
  332. package/dist/config/sessions/session-key.js +10 -5
  333. package/dist/config/sessions/store.js +1 -1
  334. package/dist/config/sessions.js +1 -0
  335. package/dist/config/validation.js +140 -85
  336. package/dist/config/zod-schema.agent-runtime.js +11 -0
  337. package/dist/config/zod-schema.hooks.js +40 -11
  338. package/dist/config/zod-schema.installs.js +20 -0
  339. package/dist/config/zod-schema.js +156 -20
  340. package/dist/config/zod-schema.providers-core.js +78 -4
  341. package/dist/config/zod-schema.providers.js +6 -1
  342. package/dist/config/zod-schema.session.js +41 -2
  343. package/dist/cron/run-log.js +3 -0
  344. package/dist/cron/schedule.js +21 -10
  345. package/dist/cron/service/ops.js +35 -21
  346. package/dist/cron/service/timer.js +116 -16
  347. package/dist/cron/stagger.js +3 -1
  348. package/dist/daemon/cmd-argv.js +21 -0
  349. package/dist/daemon/cmd-set.js +58 -0
  350. package/dist/daemon/service-types.js +1 -0
  351. package/dist/discord/api.js +12 -6
  352. package/dist/discord/draft-chunking.js +22 -0
  353. package/dist/discord/draft-stream.js +124 -0
  354. package/dist/discord/monitor/agent-components.js +1 -1
  355. package/dist/discord/monitor/commands.js +5 -0
  356. package/dist/discord/monitor/exec-approvals.js +357 -162
  357. package/dist/discord/monitor/gateway-plugin.js +2 -1
  358. package/dist/discord/monitor/listeners.js +37 -27
  359. package/dist/discord/monitor/message-handler.js +4 -1
  360. package/dist/discord/monitor/message-handler.preflight.js +65 -8
  361. package/dist/discord/monitor/message-handler.process.js +246 -217
  362. package/dist/discord/monitor/message-utils.js +143 -6
  363. package/dist/discord/monitor/model-picker-preferences.js +143 -0
  364. package/dist/discord/monitor/model-picker.js +651 -0
  365. package/dist/discord/monitor/native-command.js +573 -16
  366. package/dist/discord/monitor/provider.allowlist.js +223 -0
  367. package/dist/discord/monitor/provider.js +275 -347
  368. package/dist/discord/monitor/provider.lifecycle.js +100 -0
  369. package/dist/discord/monitor/reply-delivery.js +123 -16
  370. package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
  371. package/dist/discord/monitor/thread-bindings.js +4 -0
  372. package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
  373. package/dist/discord/monitor/thread-bindings.manager.js +423 -0
  374. package/dist/discord/monitor/thread-bindings.messages.js +55 -0
  375. package/dist/discord/monitor/thread-bindings.state.js +358 -0
  376. package/dist/discord/monitor/thread-bindings.types.js +6 -0
  377. package/dist/discord/resolve-users.js +33 -21
  378. package/dist/discord/send.channels.js +15 -0
  379. package/dist/discord/send.js +3 -2
  380. package/dist/discord/send.outbound.js +82 -26
  381. package/dist/discord/send.permissions.js +83 -30
  382. package/dist/discord/send.reactions.js +8 -4
  383. package/dist/discord/token.js +10 -5
  384. package/dist/discord/voice/command.js +263 -0
  385. package/dist/discord/voice/manager.js +531 -0
  386. package/dist/gateway/auth.js +72 -13
  387. package/dist/gateway/call.js +152 -83
  388. package/dist/gateway/canvas-capability.js +75 -0
  389. package/dist/gateway/client.js +28 -4
  390. package/dist/gateway/config-reload.js +3 -4
  391. package/dist/gateway/control-plane-audit.js +28 -0
  392. package/dist/gateway/control-plane-rate-limit.js +53 -0
  393. package/dist/gateway/control-ui.js +219 -96
  394. package/dist/gateway/events.js +1 -0
  395. package/dist/gateway/hooks-mapping.js +88 -38
  396. package/dist/gateway/hooks.js +109 -54
  397. package/dist/gateway/http-auth-helpers.js +3 -2
  398. package/dist/gateway/http-common.js +22 -0
  399. package/dist/gateway/http-endpoint-helpers.js +1 -0
  400. package/dist/gateway/method-scopes.js +169 -0
  401. package/dist/gateway/net.js +74 -9
  402. package/dist/gateway/node-invoke-system-run-approval.js +14 -35
  403. package/dist/gateway/node-registry.js +10 -5
  404. package/dist/gateway/openai-http.js +1 -0
  405. package/dist/gateway/openresponses-http.js +121 -110
  406. package/dist/gateway/origin-check.js +1 -18
  407. package/dist/gateway/probe-auth.js +2 -0
  408. package/dist/gateway/protocol/index.js +4 -2
  409. package/dist/gateway/protocol/schema/cron.js +1 -0
  410. package/dist/gateway/protocol/schema/devices.js +1 -0
  411. package/dist/gateway/protocol/schema/protocol-schemas.js +4 -1
  412. package/dist/gateway/protocol/schema/push.js +18 -0
  413. package/dist/gateway/protocol/schema/sessions.js +6 -0
  414. package/dist/gateway/protocol/schema.js +1 -0
  415. package/dist/gateway/role-policy.js +17 -0
  416. package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
  417. package/dist/gateway/server/ws-connection/message-handler.js +175 -148
  418. package/dist/gateway/server-chat.js +83 -25
  419. package/dist/gateway/server-constants.js +10 -9
  420. package/dist/gateway/server-cron.js +1 -0
  421. package/dist/gateway/server-http.js +247 -54
  422. package/dist/gateway/server-maintenance.js +20 -5
  423. package/dist/gateway/server-methods/agent.js +162 -24
  424. package/dist/gateway/server-methods/chat.js +465 -130
  425. package/dist/gateway/server-methods/config.js +193 -152
  426. package/dist/gateway/server-methods/devices.js +17 -3
  427. package/dist/gateway/server-methods/models.js +11 -1
  428. package/dist/gateway/server-methods/nodes.helpers.js +12 -0
  429. package/dist/gateway/server-methods/nodes.js +251 -69
  430. package/dist/gateway/server-methods/push.js +53 -0
  431. package/dist/gateway/server-methods/sessions.js +64 -8
  432. package/dist/gateway/server-methods/usage.js +162 -75
  433. package/dist/gateway/server-node-events.js +29 -0
  434. package/dist/gateway/server-reload-handlers.js +2 -3
  435. package/dist/gateway/server-runtime-config.js +39 -13
  436. package/dist/gateway/server-runtime-state.js +2 -0
  437. package/dist/gateway/server-startup-memory.js +17 -11
  438. package/dist/gateway/server-ws-runtime.js +1 -0
  439. package/dist/gateway/server.impl.js +296 -139
  440. package/dist/gateway/session-preview.test-helpers.js +11 -0
  441. package/dist/gateway/session-utils.fs.js +32 -34
  442. package/dist/gateway/sessions-resolve.js +17 -5
  443. package/dist/gateway/startup-auth.js +126 -0
  444. package/dist/gateway/test-helpers.agent-results.js +15 -0
  445. package/dist/gateway/test-helpers.mocks.js +37 -14
  446. package/dist/gateway/test-helpers.openai-mock.js +14 -7
  447. package/dist/gateway/test-helpers.server.js +161 -77
  448. package/dist/gateway/tools-invoke-http.js +21 -10
  449. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
  450. package/dist/hooks/bundled/command-logger/handler.js +7 -2
  451. package/dist/hooks/bundled/session-memory/handler.js +170 -38
  452. package/dist/hooks/frontmatter.js +6 -6
  453. package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
  454. package/dist/hooks/gmail-watcher.js +11 -6
  455. package/dist/hooks/internal-hooks.js +11 -1
  456. package/dist/hooks/llm-slug-generator.js +4 -1
  457. package/dist/hooks/workspace.js +47 -17
  458. package/dist/imessage/accounts.js +9 -20
  459. package/dist/imessage/monitor/inbound-processing.js +2 -1
  460. package/dist/infra/archive-path.js +49 -0
  461. package/dist/infra/archive.js +174 -73
  462. package/dist/infra/control-ui-assets.js +14 -6
  463. package/dist/infra/device-pairing.js +204 -144
  464. package/dist/infra/env.js +10 -5
  465. package/dist/infra/exec-approvals-allowlist.js +141 -70
  466. package/dist/infra/exec-approvals-analysis.js +78 -20
  467. package/dist/infra/exec-approvals.js +5 -17
  468. package/dist/infra/exec-safe-bin-policy.js +277 -0
  469. package/dist/infra/fixed-window-rate-limit.js +33 -0
  470. package/dist/infra/fs-safe.js +71 -39
  471. package/dist/infra/gateway-lock.js +6 -2
  472. package/dist/infra/git-root.js +61 -0
  473. package/dist/infra/heartbeat-active-hours.js +2 -2
  474. package/dist/infra/heartbeat-reason.js +40 -0
  475. package/dist/infra/heartbeat-runner.js +72 -32
  476. package/dist/infra/heartbeat-wake.js +6 -12
  477. package/dist/infra/host-env-security-policy.json +19 -0
  478. package/dist/infra/host-env-security.js +66 -0
  479. package/dist/infra/install-source-utils.js +91 -7
  480. package/dist/infra/net/ssrf.js +131 -38
  481. package/dist/infra/node-pairing.js +50 -105
  482. package/dist/infra/npm-integrity.js +45 -0
  483. package/dist/infra/npm-pack-install.js +40 -0
  484. package/dist/infra/outbound/bound-delivery-router.js +88 -0
  485. package/dist/infra/outbound/channel-adapters.js +20 -7
  486. package/dist/infra/outbound/channel-selection.js +12 -6
  487. package/dist/infra/outbound/envelope.js +1 -1
  488. package/dist/infra/outbound/format.js +12 -6
  489. package/dist/infra/outbound/message-action-runner.js +107 -327
  490. package/dist/infra/outbound/message.js +59 -36
  491. package/dist/infra/outbound/outbound-policy.js +52 -25
  492. package/dist/infra/outbound/outbound-send-service.js +58 -71
  493. package/dist/infra/outbound/payloads.js +14 -7
  494. package/dist/infra/outbound/session-binding-service.js +123 -0
  495. package/dist/infra/pairing-files.js +10 -0
  496. package/dist/infra/path-guards.js +25 -0
  497. package/dist/infra/plain-object.js +9 -0
  498. package/dist/infra/provider-usage.fetch.codex.js +7 -15
  499. package/dist/infra/provider-usage.fetch.gemini.js +14 -11
  500. package/dist/infra/provider-usage.fetch.shared.js +30 -1
  501. package/dist/infra/provider-usage.fetch.zai.js +10 -9
  502. package/dist/infra/push-apns.js +365 -0
  503. package/dist/infra/restart-sentinel.js +16 -1
  504. package/dist/infra/restart.js +229 -26
  505. package/dist/infra/retry-policy.js +4 -2
  506. package/dist/infra/retry.js +9 -5
  507. package/dist/infra/scp-host.js +54 -0
  508. package/dist/infra/session-cost-usage.js +107 -59
  509. package/dist/infra/session-maintenance-warning.js +3 -1
  510. package/dist/infra/shell-env.js +98 -34
  511. package/dist/infra/ssh-config.js +12 -6
  512. package/dist/infra/system-run-command.js +49 -4
  513. package/dist/infra/update-channels.js +10 -5
  514. package/dist/infra/update-startup.js +86 -9
  515. package/dist/line/accounts.js +5 -7
  516. package/dist/line/bot-access.js +8 -20
  517. package/dist/line/bot-handlers.js +3 -1
  518. package/dist/link-understanding/detect.js +15 -7
  519. package/dist/media/constants.js +15 -6
  520. package/dist/media/image-ops.js +7 -0
  521. package/dist/media/inbound-path-policy.js +114 -0
  522. package/dist/media/input-files.js +16 -0
  523. package/dist/media/local-roots.js +3 -2
  524. package/dist/media-understanding/apply.js +4 -1
  525. package/dist/media-understanding/concurrency.js +8 -20
  526. package/dist/memory/backend-config.js +45 -6
  527. package/dist/memory/embeddings.js +10 -4
  528. package/dist/memory/fs-utils.js +23 -0
  529. package/dist/memory/manager-search.js +12 -6
  530. package/dist/memory/manager-sync-ops.js +12 -2
  531. package/dist/memory/qmd-manager.js +466 -53
  532. package/dist/memory/query-expansion.js +167 -3
  533. package/dist/memory/status-format.js +10 -5
  534. package/dist/memory/sync-memory-files.js +1 -1
  535. package/dist/memory/test-manager.js +8 -0
  536. package/dist/node-host/invoke-system-run.js +281 -0
  537. package/dist/node-host/invoke.js +55 -337
  538. package/dist/pairing/pairing-store.js +22 -0
  539. package/dist/plugin-sdk/allow-from.js +1 -1
  540. package/dist/plugin-sdk/command-auth.js +3 -1
  541. package/dist/plugin-sdk/index.js +6 -3
  542. package/dist/plugin-sdk/temp-path.js +47 -0
  543. package/dist/plugin-sdk/webhook-targets.js +32 -0
  544. package/dist/plugins/bundled-dir.js +9 -6
  545. package/dist/plugins/discovery.js +217 -23
  546. package/dist/plugins/hook-runner-global.js +16 -0
  547. package/dist/plugins/hooks.js +50 -0
  548. package/dist/plugins/install.js +28 -16
  549. package/dist/plugins/loader.js +192 -26
  550. package/dist/plugins/logger.js +8 -0
  551. package/dist/plugins/manifest-registry.js +3 -0
  552. package/dist/plugins/path-safety.js +34 -0
  553. package/dist/plugins/registry.js +5 -2
  554. package/dist/plugins/runtime/index.js +271 -206
  555. package/dist/plugins/runtime.js +3 -17
  556. package/dist/plugins/update.js +78 -12
  557. package/dist/process/spawn-utils.js +14 -7
  558. package/dist/providers/github-copilot-models.js +4 -1
  559. package/dist/providers/github-copilot-token.js +11 -6
  560. package/dist/providers/qwen-portal-oauth.js +14 -6
  561. package/dist/routing/account-id.js +30 -0
  562. package/dist/routing/resolve-route.js +3 -7
  563. package/dist/routing/session-key.js +2 -16
  564. package/dist/security/audit-channel.js +100 -20
  565. package/dist/security/audit-extra.async.js +505 -179
  566. package/dist/security/audit-extra.js +12 -2
  567. package/dist/security/audit-extra.sync.js +421 -35
  568. package/dist/security/audit-fs.js +31 -13
  569. package/dist/security/audit.js +180 -370
  570. package/dist/security/dm-policy-shared.js +68 -0
  571. package/dist/security/external-content.js +46 -14
  572. package/dist/security/fix.js +49 -85
  573. package/dist/security/scan-paths.js +20 -0
  574. package/dist/security/secret-equal.js +3 -7
  575. package/dist/security/windows-acl.js +30 -15
  576. package/dist/shared/entry-status.js +6 -0
  577. package/dist/shared/frontmatter.js +5 -5
  578. package/dist/shared/node-list-parse.js +13 -0
  579. package/dist/shared/node-match.js +11 -4
  580. package/dist/shared/operator-scope-compat.js +42 -0
  581. package/dist/shared/text-chunking.js +29 -0
  582. package/dist/signal/accounts.js +7 -20
  583. package/dist/signal/monitor/event-handler.js +3 -1
  584. package/dist/slack/accounts.js +6 -19
  585. package/dist/slack/actions.js +11 -3
  586. package/dist/slack/blocks.test-helpers.js +31 -0
  587. package/dist/slack/monitor/auth.js +1 -1
  588. package/dist/slack/monitor/message-handler/dispatch.js +50 -29
  589. package/dist/slack/monitor/mrkdwn.js +8 -0
  590. package/dist/slack/monitor/replies.js +15 -7
  591. package/dist/slack/monitor/slash.js +22 -13
  592. package/dist/slack/resolve-channels.js +10 -5
  593. package/dist/slack/send.js +102 -12
  594. package/dist/slack/stream-mode.js +10 -0
  595. package/dist/slack/streaming.js +4 -2
  596. package/dist/telegram/accounts.js +19 -14
  597. package/dist/telegram/bot/helpers.js +3 -5
  598. package/dist/telegram/bot-access.js +35 -36
  599. package/dist/telegram/bot-handlers.js +120 -148
  600. package/dist/telegram/bot-message-context.js +68 -9
  601. package/dist/telegram/bot-message-dispatch.js +477 -210
  602. package/dist/telegram/bot-native-commands.js +16 -0
  603. package/dist/telegram/draft-stream.js +44 -8
  604. package/dist/telegram/inline-buttons.js +5 -15
  605. package/dist/telegram/monitor.js +11 -7
  606. package/dist/telegram/network-config.js +19 -7
  607. package/dist/telegram/reasoning-lane-coordinator.js +128 -0
  608. package/dist/telegram/send.js +3 -2
  609. package/dist/telegram/sent-message-cache.js +5 -6
  610. package/dist/telegram/status-reaction-variants.js +208 -0
  611. package/dist/telegram/sticker-cache.js +11 -9
  612. package/dist/terminal/prompt-select-styled.js +9 -0
  613. package/dist/terminal/theme.js +12 -12
  614. package/dist/test-utils/command-runner.js +6 -0
  615. package/dist/test-utils/internal-hook-event-payload.js +10 -0
  616. package/dist/test-utils/model-auth-mock.js +12 -0
  617. package/dist/test-utils/provider-usage-fetch.js +14 -0
  618. package/dist/test-utils/temp-home.js +33 -0
  619. package/dist/tts/tts.js +80 -567
  620. package/dist/tui/components/chat-log.js +50 -8
  621. package/dist/tui/theme/theme.js +10 -12
  622. package/dist/tui/tui-command-handlers.js +36 -27
  623. package/dist/tui/tui-event-handlers.js +122 -32
  624. package/dist/tui/tui-local-shell.js +16 -6
  625. package/dist/tui/tui.js +236 -48
  626. package/dist/utils/account-id.js +2 -4
  627. package/dist/utils/boolean.js +10 -5
  628. package/dist/utils/directive-tags.js +11 -0
  629. package/dist/utils/mask-api-key.js +10 -0
  630. package/dist/utils/queue-helpers.js +67 -12
  631. package/dist/utils/run-with-concurrency.js +39 -0
  632. package/dist/web/auto-reply/deliver-reply.js +8 -4
  633. package/dist/web/auto-reply/mentions.js +10 -5
  634. package/dist/web/auto-reply/monitor/group-members.js +14 -7
  635. package/dist/web/auto-reply/monitor/process-message.js +45 -24
  636. package/dist/web/inbound/access-control.js +5 -2
  637. package/dist/web/login-qr.js +12 -6
  638. package/dist/web/media.js +126 -15
  639. package/docs/tools/slash-commands.md +5 -1
  640. package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
  641. package/extensions/bluebubbles/src/monitor.ts +208 -1950
  642. package/extensions/feishu/src/external-keys.ts +19 -0
  643. package/extensions/lobster/src/windows-spawn.ts +193 -0
  644. package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
  645. package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
  646. package/package.json +1 -1
@@ -1,14 +1,16 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { listAgentIds, resolveDefaultAgentId } from "../agents/agent-scope.js";
3
3
  import { listChannelPlugins } from "../channels/plugins/index.js";
4
+ import { readJsonBodyWithLimit, requestBodyErrorToText } from "../infra/http-body.js";
4
5
  import { normalizeAgentId } from "../routing/session-key.js";
5
6
  import { normalizeMessageChannel } from "../utils/message-channel.js";
6
7
  import { resolveHookMappings } from "./hooks-mapping.js";
7
8
  const DEFAULT_HOOKS_PATH = "/hooks";
8
9
  const DEFAULT_HOOKS_MAX_BODY_BYTES = 256 * 1024;
9
10
  export function resolveHooksConfig(cfg) {
10
- if (cfg.hooks?.enabled !== true)
11
+ if (cfg.hooks?.enabled !== true) {
11
12
  return null;
13
+ }
12
14
  const token = cfg.hooks?.token?.trim();
13
15
  if (!token) {
14
16
  throw new Error("hooks.enabled requires hooks.token");
@@ -26,6 +28,18 @@ export function resolveHooksConfig(cfg) {
26
28
  const defaultAgentId = resolveDefaultAgentId(cfg);
27
29
  const knownAgentIds = resolveKnownAgentIds(cfg, defaultAgentId);
28
30
  const allowedAgentIds = resolveAllowedAgentIds(cfg.hooks?.allowedAgentIds);
31
+ const defaultSessionKey = resolveSessionKey(cfg.hooks?.defaultSessionKey);
32
+ const allowedSessionKeyPrefixes = resolveAllowedSessionKeyPrefixes(cfg.hooks?.allowedSessionKeyPrefixes);
33
+ if (defaultSessionKey &&
34
+ allowedSessionKeyPrefixes &&
35
+ !isSessionKeyAllowedByPrefix(defaultSessionKey, allowedSessionKeyPrefixes)) {
36
+ throw new Error("hooks.defaultSessionKey must match hooks.allowedSessionKeyPrefixes");
37
+ }
38
+ if (!defaultSessionKey &&
39
+ allowedSessionKeyPrefixes &&
40
+ !isSessionKeyAllowedByPrefix("hook:example", allowedSessionKeyPrefixes)) {
41
+ throw new Error("hooks.allowedSessionKeyPrefixes must include 'hook:' when hooks.defaultSessionKey is unset");
42
+ }
29
43
  return {
30
44
  basePath: trimmed,
31
45
  token,
@@ -36,6 +50,11 @@ export function resolveHooksConfig(cfg) {
36
50
  knownAgentIds,
37
51
  allowedAgentIds,
38
52
  },
53
+ sessionPolicy: {
54
+ defaultSessionKey,
55
+ allowRequestSessionKey: cfg.hooks?.allowRequestSessionKey === true,
56
+ allowedSessionKeyPrefixes,
57
+ },
39
58
  };
40
59
  }
41
60
  function resolveKnownAgentIds(cfg, defaultAgentId) {
@@ -65,62 +84,68 @@ function resolveAllowedAgentIds(raw) {
65
84
  }
66
85
  return allowed;
67
86
  }
87
+ function resolveSessionKey(raw) {
88
+ const value = raw?.trim();
89
+ return value ? value : undefined;
90
+ }
91
+ function normalizeSessionKeyPrefix(raw) {
92
+ const value = raw.trim().toLowerCase();
93
+ return value ? value : undefined;
94
+ }
95
+ function resolveAllowedSessionKeyPrefixes(raw) {
96
+ if (!Array.isArray(raw)) {
97
+ return undefined;
98
+ }
99
+ const set = new Set();
100
+ for (const prefix of raw) {
101
+ const normalized = normalizeSessionKeyPrefix(prefix);
102
+ if (!normalized) {
103
+ continue;
104
+ }
105
+ set.add(normalized);
106
+ }
107
+ return set.size > 0 ? Array.from(set) : undefined;
108
+ }
109
+ function isSessionKeyAllowedByPrefix(sessionKey, prefixes) {
110
+ const normalized = sessionKey.trim().toLowerCase();
111
+ if (!normalized) {
112
+ return false;
113
+ }
114
+ return prefixes.some((prefix) => normalized.startsWith(prefix));
115
+ }
68
116
  export function extractHookToken(req, url) {
69
117
  const auth = typeof req.headers.authorization === "string" ? req.headers.authorization.trim() : "";
70
118
  if (auth.toLowerCase().startsWith("bearer ")) {
71
119
  const token = auth.slice(7).trim();
72
- if (token)
120
+ if (token) {
73
121
  return { token, fromQuery: false };
122
+ }
74
123
  }
75
124
  const headerToken = typeof req.headers["x-poolbot-token"] === "string" ? req.headers["x-poolbot-token"].trim() : "";
76
- if (headerToken)
125
+ if (headerToken) {
77
126
  return { token: headerToken, fromQuery: false };
78
- const queryToken = url.searchParams.get("token");
79
- if (queryToken)
80
- return { token: queryToken.trim(), fromQuery: true };
127
+ }
128
+ const queryToken = url?.searchParams.get("token")?.trim();
129
+ if (queryToken) {
130
+ return { token: queryToken, fromQuery: true };
131
+ }
81
132
  return { token: undefined, fromQuery: false };
82
133
  }
83
134
  export async function readJsonBody(req, maxBytes) {
84
- return await new Promise((resolve) => {
85
- let done = false;
86
- let total = 0;
87
- const chunks = [];
88
- req.on("data", (chunk) => {
89
- if (done)
90
- return;
91
- total += chunk.length;
92
- if (total > maxBytes) {
93
- done = true;
94
- resolve({ ok: false, error: "payload too large" });
95
- req.destroy();
96
- return;
97
- }
98
- chunks.push(chunk);
99
- });
100
- req.on("end", () => {
101
- if (done)
102
- return;
103
- done = true;
104
- const raw = Buffer.concat(chunks).toString("utf-8").trim();
105
- if (!raw) {
106
- resolve({ ok: true, value: {} });
107
- return;
108
- }
109
- try {
110
- const parsed = JSON.parse(raw);
111
- resolve({ ok: true, value: parsed });
112
- }
113
- catch (err) {
114
- resolve({ ok: false, error: String(err) });
115
- }
116
- });
117
- req.on("error", (err) => {
118
- if (done)
119
- return;
120
- done = true;
121
- resolve({ ok: false, error: String(err) });
122
- });
123
- });
135
+ const result = await readJsonBodyWithLimit(req, { maxBytes, emptyObjectOnEmpty: true });
136
+ if (result.ok) {
137
+ return result;
138
+ }
139
+ if (result.code === "PAYLOAD_TOO_LARGE") {
140
+ return { ok: false, error: "payload too large" };
141
+ }
142
+ if (result.code === "REQUEST_BODY_TIMEOUT") {
143
+ return { ok: false, error: "request body timeout" };
144
+ }
145
+ if (result.code === "CONNECTION_CLOSED") {
146
+ return { ok: false, error: requestBodyErrorToText("CONNECTION_CLOSED") };
147
+ }
148
+ return { ok: false, error: result.error };
124
149
  }
125
150
  export function normalizeHookHeaders(req) {
126
151
  const headers = {};
@@ -136,8 +161,9 @@ export function normalizeHookHeaders(req) {
136
161
  }
137
162
  export function normalizeWakePayload(payload) {
138
163
  const text = typeof payload.text === "string" ? payload.text.trim() : "";
139
- if (!text)
164
+ if (!text) {
140
165
  return { ok: false, error: "text required" };
166
+ }
141
167
  const mode = payload.mode === "next-heartbeat" ? "next-heartbeat" : "now";
142
168
  return { ok: true, value: { text, mode } };
143
169
  }
@@ -145,13 +171,16 @@ const listHookChannelValues = () => ["last", ...listChannelPlugins().map((plugin
145
171
  const getHookChannelSet = () => new Set(listHookChannelValues());
146
172
  export const getHookChannelError = () => `channel must be ${listHookChannelValues().join("|")}`;
147
173
  export function resolveHookChannel(raw) {
148
- if (raw === undefined)
174
+ if (raw === undefined) {
149
175
  return "last";
150
- if (typeof raw !== "string")
176
+ }
177
+ if (typeof raw !== "string") {
151
178
  return null;
179
+ }
152
180
  const normalized = normalizeMessageChannel(raw);
153
- if (!normalized || !getHookChannelSet().has(normalized))
181
+ if (!normalized || !getHookChannelSet().has(normalized)) {
154
182
  return null;
183
+ }
155
184
  return normalized;
156
185
  }
157
186
  export function resolveHookDeliver(raw) {
@@ -182,23 +211,49 @@ export function isHookAgentAllowed(hooksConfig, agentId) {
182
211
  return resolved ? allowed.has(resolved) : false;
183
212
  }
184
213
  export const getHookAgentPolicyError = () => "agentId is not allowed by hooks.allowedAgentIds";
214
+ export const getHookSessionKeyRequestPolicyError = () => "sessionKey is disabled for external /hooks/agent payloads; set hooks.allowRequestSessionKey=true to enable";
215
+ export const getHookSessionKeyPrefixError = (prefixes) => `sessionKey must start with one of: ${prefixes.join(", ")}`;
216
+ export function resolveHookSessionKey(params) {
217
+ const requested = resolveSessionKey(params.sessionKey);
218
+ if (requested) {
219
+ if (params.source === "request" && !params.hooksConfig.sessionPolicy.allowRequestSessionKey) {
220
+ return { ok: false, error: getHookSessionKeyRequestPolicyError() };
221
+ }
222
+ const allowedPrefixes = params.hooksConfig.sessionPolicy.allowedSessionKeyPrefixes;
223
+ if (allowedPrefixes && !isSessionKeyAllowedByPrefix(requested, allowedPrefixes)) {
224
+ return { ok: false, error: getHookSessionKeyPrefixError(allowedPrefixes) };
225
+ }
226
+ return { ok: true, value: requested };
227
+ }
228
+ const defaultSessionKey = params.hooksConfig.sessionPolicy.defaultSessionKey;
229
+ if (defaultSessionKey) {
230
+ return { ok: true, value: defaultSessionKey };
231
+ }
232
+ const generated = `hook:${(params.idFactory ?? randomUUID)()}`;
233
+ const allowedPrefixes = params.hooksConfig.sessionPolicy.allowedSessionKeyPrefixes;
234
+ if (allowedPrefixes && !isSessionKeyAllowedByPrefix(generated, allowedPrefixes)) {
235
+ return { ok: false, error: getHookSessionKeyPrefixError(allowedPrefixes) };
236
+ }
237
+ return { ok: true, value: generated };
238
+ }
185
239
  export function normalizeAgentPayload(payload, opts) {
186
240
  const message = typeof payload.message === "string" ? payload.message.trim() : "";
187
- if (!message)
241
+ if (!message) {
188
242
  return { ok: false, error: "message required" };
243
+ }
189
244
  const nameRaw = payload.name;
190
245
  const name = typeof nameRaw === "string" && nameRaw.trim() ? nameRaw.trim() : "Hook";
191
246
  const agentIdRaw = payload.agentId;
192
247
  const agentId = typeof agentIdRaw === "string" && agentIdRaw.trim() ? agentIdRaw.trim() : undefined;
193
248
  const wakeMode = payload.wakeMode === "next-heartbeat" ? "next-heartbeat" : "now";
194
249
  const sessionKeyRaw = payload.sessionKey;
195
- const idFactory = opts?.idFactory ?? randomUUID;
196
250
  const sessionKey = typeof sessionKeyRaw === "string" && sessionKeyRaw.trim()
197
251
  ? sessionKeyRaw.trim()
198
- : `hook:${idFactory()}`;
252
+ : `hook:${(opts?.idFactory ?? randomUUID)()}`;
199
253
  const channel = resolveHookChannel(payload.channel);
200
- if (!channel)
254
+ if (!channel) {
201
255
  return { ok: false, error: getHookChannelError() };
256
+ }
202
257
  const toRaw = payload.to;
203
258
  const to = typeof toRaw === "string" && toRaw.trim() ? toRaw.trim() : undefined;
204
259
  const modelRaw = payload.model;
@@ -1,13 +1,14 @@
1
- import { authorizeGatewayConnect } from "./auth.js";
1
+ import { authorizeHttpGatewayConnect } from "./auth.js";
2
2
  import { sendGatewayAuthFailure } from "./http-common.js";
3
3
  import { getBearerToken } from "./http-utils.js";
4
4
  export async function authorizeGatewayBearerRequestOrReply(params) {
5
5
  const token = getBearerToken(params.req);
6
- const authResult = await authorizeGatewayConnect({
6
+ const authResult = await authorizeHttpGatewayConnect({
7
7
  auth: params.auth,
8
8
  connectAuth: token ? { token, password: token } : null,
9
9
  req: params.req,
10
10
  trustedProxies: params.trustedProxies,
11
+ allowRealIpFallback: params.allowRealIpFallback,
11
12
  rateLimiter: params.rateLimiter,
12
13
  });
13
14
  if (!authResult.ok) {
@@ -1,4 +1,14 @@
1
1
  import { readJsonBody } from "./hooks.js";
2
+ /**
3
+ * Apply baseline security headers that are safe for all response types (API JSON,
4
+ * HTML pages, static assets, SSE streams). Headers that restrict framing or set a
5
+ * Content-Security-Policy are intentionally omitted here because some handlers
6
+ * (canvas host, A2UI) serve content that may be loaded inside frames.
7
+ */
8
+ export function setDefaultSecurityHeaders(res) {
9
+ res.setHeader("X-Content-Type-Options", "nosniff");
10
+ res.setHeader("Referrer-Policy", "no-referrer");
11
+ }
2
12
  export function sendJson(res, status, body) {
3
13
  res.statusCode = status;
4
14
  res.setHeader("Content-Type", "application/json; charset=utf-8");
@@ -44,6 +54,18 @@ export function sendInvalidRequest(res, message) {
44
54
  export async function readJsonBodyOrError(req, res, maxBytes) {
45
55
  const body = await readJsonBody(req, maxBytes);
46
56
  if (!body.ok) {
57
+ if (body.error === "payload too large") {
58
+ sendJson(res, 413, {
59
+ error: { message: "Payload too large", type: "invalid_request_error" },
60
+ });
61
+ return undefined;
62
+ }
63
+ if (body.error === "request body timeout") {
64
+ sendJson(res, 408, {
65
+ error: { message: "Request body timeout", type: "invalid_request_error" },
66
+ });
67
+ return undefined;
68
+ }
47
69
  sendInvalidRequest(res, body.error);
48
70
  return undefined;
49
71
  }
@@ -14,6 +14,7 @@ export async function handleGatewayPostJsonEndpoint(req, res, opts) {
14
14
  res,
15
15
  auth: opts.auth,
16
16
  trustedProxies: opts.trustedProxies,
17
+ allowRealIpFallback: opts.allowRealIpFallback,
17
18
  rateLimiter: opts.rateLimiter,
18
19
  });
19
20
  if (!authorized) {
@@ -0,0 +1,169 @@
1
+ export const ADMIN_SCOPE = "operator.admin";
2
+ export const READ_SCOPE = "operator.read";
3
+ export const WRITE_SCOPE = "operator.write";
4
+ export const APPROVALS_SCOPE = "operator.approvals";
5
+ export const PAIRING_SCOPE = "operator.pairing";
6
+ export const CLI_DEFAULT_OPERATOR_SCOPES = [
7
+ ADMIN_SCOPE,
8
+ APPROVALS_SCOPE,
9
+ PAIRING_SCOPE,
10
+ ];
11
+ const NODE_ROLE_METHODS = new Set(["node.invoke.result", "node.event", "skills.bins"]);
12
+ const METHOD_SCOPE_GROUPS = {
13
+ [APPROVALS_SCOPE]: [
14
+ "exec.approval.request",
15
+ "exec.approval.waitDecision",
16
+ "exec.approval.resolve",
17
+ ],
18
+ [PAIRING_SCOPE]: [
19
+ "node.pair.request",
20
+ "node.pair.list",
21
+ "node.pair.approve",
22
+ "node.pair.reject",
23
+ "node.pair.verify",
24
+ "device.pair.list",
25
+ "device.pair.approve",
26
+ "device.pair.reject",
27
+ "device.pair.remove",
28
+ "device.token.rotate",
29
+ "device.token.revoke",
30
+ "node.rename",
31
+ ],
32
+ [READ_SCOPE]: [
33
+ "health",
34
+ "logs.tail",
35
+ "channels.status",
36
+ "status",
37
+ "usage.status",
38
+ "usage.cost",
39
+ "tts.status",
40
+ "tts.providers",
41
+ "models.list",
42
+ "agents.list",
43
+ "agent.identity.get",
44
+ "skills.status",
45
+ "voicewake.get",
46
+ "sessions.list",
47
+ "sessions.preview",
48
+ "sessions.resolve",
49
+ "sessions.usage",
50
+ "sessions.usage.timeseries",
51
+ "sessions.usage.logs",
52
+ "cron.list",
53
+ "cron.status",
54
+ "cron.runs",
55
+ "system-presence",
56
+ "last-heartbeat",
57
+ "node.list",
58
+ "node.describe",
59
+ "chat.history",
60
+ "config.get",
61
+ "talk.config",
62
+ "agents.files.list",
63
+ "agents.files.get",
64
+ ],
65
+ [WRITE_SCOPE]: [
66
+ "send",
67
+ "poll",
68
+ "agent",
69
+ "agent.wait",
70
+ "wake",
71
+ "talk.mode",
72
+ "tts.enable",
73
+ "tts.disable",
74
+ "tts.convert",
75
+ "tts.setProvider",
76
+ "voicewake.set",
77
+ "node.invoke",
78
+ "chat.send",
79
+ "chat.abort",
80
+ "browser.request",
81
+ "push.test",
82
+ ],
83
+ [ADMIN_SCOPE]: [
84
+ "channels.logout",
85
+ "agents.create",
86
+ "agents.update",
87
+ "agents.delete",
88
+ "skills.install",
89
+ "skills.update",
90
+ "cron.add",
91
+ "cron.update",
92
+ "cron.remove",
93
+ "cron.run",
94
+ "sessions.patch",
95
+ "sessions.reset",
96
+ "sessions.delete",
97
+ "sessions.compact",
98
+ "connect",
99
+ "chat.inject",
100
+ "web.login.start",
101
+ "web.login.wait",
102
+ "set-heartbeats",
103
+ "system-event",
104
+ "agents.files.set",
105
+ ],
106
+ };
107
+ const ADMIN_METHOD_PREFIXES = ["exec.approvals.", "config.", "wizard.", "update."];
108
+ const METHOD_SCOPE_BY_NAME = new Map(Object.entries(METHOD_SCOPE_GROUPS).flatMap(([scope, methods]) => methods.map((method) => [method, scope])));
109
+ function resolveScopedMethod(method) {
110
+ const explicitScope = METHOD_SCOPE_BY_NAME.get(method);
111
+ if (explicitScope) {
112
+ return explicitScope;
113
+ }
114
+ if (ADMIN_METHOD_PREFIXES.some((prefix) => method.startsWith(prefix))) {
115
+ return ADMIN_SCOPE;
116
+ }
117
+ return undefined;
118
+ }
119
+ export function isApprovalMethod(method) {
120
+ return resolveScopedMethod(method) === APPROVALS_SCOPE;
121
+ }
122
+ export function isPairingMethod(method) {
123
+ return resolveScopedMethod(method) === PAIRING_SCOPE;
124
+ }
125
+ export function isReadMethod(method) {
126
+ return resolveScopedMethod(method) === READ_SCOPE;
127
+ }
128
+ export function isWriteMethod(method) {
129
+ return resolveScopedMethod(method) === WRITE_SCOPE;
130
+ }
131
+ export function isNodeRoleMethod(method) {
132
+ return NODE_ROLE_METHODS.has(method);
133
+ }
134
+ export function isAdminOnlyMethod(method) {
135
+ return resolveScopedMethod(method) === ADMIN_SCOPE;
136
+ }
137
+ export function resolveRequiredOperatorScopeForMethod(method) {
138
+ return resolveScopedMethod(method);
139
+ }
140
+ export function resolveLeastPrivilegeOperatorScopesForMethod(method) {
141
+ const requiredScope = resolveRequiredOperatorScopeForMethod(method);
142
+ if (requiredScope) {
143
+ return [requiredScope];
144
+ }
145
+ // Default-deny for unclassified methods.
146
+ return [];
147
+ }
148
+ export function authorizeOperatorScopesForMethod(method, scopes) {
149
+ if (scopes.includes(ADMIN_SCOPE)) {
150
+ return { allowed: true };
151
+ }
152
+ const requiredScope = resolveRequiredOperatorScopeForMethod(method) ?? ADMIN_SCOPE;
153
+ if (requiredScope === READ_SCOPE) {
154
+ if (scopes.includes(READ_SCOPE) || scopes.includes(WRITE_SCOPE)) {
155
+ return { allowed: true };
156
+ }
157
+ return { allowed: false, missingScope: READ_SCOPE };
158
+ }
159
+ if (scopes.includes(requiredScope)) {
160
+ return { allowed: true };
161
+ }
162
+ return { allowed: false, missingScope: requiredScope };
163
+ }
164
+ export function isGatewayMethodClassified(method) {
165
+ if (isNodeRoleMethod(method)) {
166
+ return true;
167
+ }
168
+ return resolveRequiredOperatorScopeForMethod(method) !== undefined;
169
+ }
@@ -135,19 +135,44 @@ function stripOptionalPort(ip) {
135
135
  }
136
136
  return ip;
137
137
  }
138
- export function parseForwardedForClientIp(forwardedFor) {
139
- const raw = forwardedFor?.split(",")[0]?.trim();
140
- if (!raw) {
138
+ function parseIpLiteral(raw) {
139
+ const trimmed = raw?.trim();
140
+ if (!trimmed) {
141
+ return undefined;
142
+ }
143
+ const stripped = stripOptionalPort(trimmed);
144
+ const normalized = normalizeIp(stripped);
145
+ if (!normalized || net.isIP(normalized) === 0) {
141
146
  return undefined;
142
147
  }
143
- return normalizeIp(stripOptionalPort(raw));
148
+ return normalized;
144
149
  }
145
150
  function parseRealIp(realIp) {
146
- const raw = realIp?.trim();
147
- if (!raw) {
151
+ return parseIpLiteral(realIp);
152
+ }
153
+ function resolveForwardedClientIp(params) {
154
+ const { forwardedFor, trustedProxies } = params;
155
+ if (!trustedProxies?.length) {
156
+ return undefined;
157
+ }
158
+ const forwardedChain = [];
159
+ for (const entry of forwardedFor?.split(",") ?? []) {
160
+ const normalized = parseIpLiteral(entry);
161
+ if (normalized) {
162
+ forwardedChain.push(normalized);
163
+ }
164
+ }
165
+ if (forwardedChain.length === 0) {
148
166
  return undefined;
149
167
  }
150
- return normalizeIp(stripOptionalPort(raw));
168
+ // Walk right-to-left and return the first untrusted hop.
169
+ for (let index = forwardedChain.length - 1; index >= 0; index -= 1) {
170
+ const hop = forwardedChain[index];
171
+ if (!isTrustedProxyAddress(hop, trustedProxies)) {
172
+ return hop;
173
+ }
174
+ }
175
+ return undefined;
151
176
  }
152
177
  /**
153
178
  * Check if an IP address matches a CIDR block.
@@ -202,7 +227,7 @@ export function isTrustedProxyAddress(ip, trustedProxies) {
202
227
  return normalizeIp(candidate) === normalized;
203
228
  });
204
229
  }
205
- export function resolveGatewayClientIp(params) {
230
+ export function resolveClientIp(params) {
206
231
  const remote = normalizeIp(params.remoteAddr);
207
232
  if (!remote) {
208
233
  return undefined;
@@ -210,7 +235,20 @@ export function resolveGatewayClientIp(params) {
210
235
  if (!isTrustedProxyAddress(remote, params.trustedProxies)) {
211
236
  return remote;
212
237
  }
213
- return parseForwardedForClientIp(params.forwardedFor) ?? parseRealIp(params.realIp) ?? remote;
238
+ // Fail closed when traffic comes from a trusted proxy but client-origin headers
239
+ // are missing or invalid. Falling back to the proxy's own IP can accidentally
240
+ // treat unrelated requests as local/trusted.
241
+ const forwardedIp = resolveForwardedClientIp({
242
+ forwardedFor: params.forwardedFor,
243
+ trustedProxies: params.trustedProxies,
244
+ });
245
+ if (forwardedIp) {
246
+ return forwardedIp;
247
+ }
248
+ if (params.allowRealIpFallback) {
249
+ return parseRealIp(params.realIp);
250
+ }
251
+ return undefined;
214
252
  }
215
253
  export function isLocalGatewayAddress(ip) {
216
254
  if (isLoopbackAddress(ip)) {
@@ -347,3 +385,30 @@ export function isLoopbackHost(host) {
347
385
  const unbracket = h.startsWith("[") && h.endsWith("]") ? h.slice(1, -1) : h;
348
386
  return isLoopbackAddress(unbracket);
349
387
  }
388
+ /**
389
+ * Security check for WebSocket URLs (CWE-319: Cleartext Transmission of Sensitive Information).
390
+ *
391
+ * Returns true if the URL is secure for transmitting data:
392
+ * - wss:// (TLS) is always secure
393
+ * - ws:// is only secure for loopback addresses (localhost, 127.x.x.x, ::1)
394
+ *
395
+ * All other ws:// URLs are considered insecure because both credentials
396
+ * AND chat/conversation data would be exposed to network interception.
397
+ */
398
+ export function isSecureWebSocketUrl(url) {
399
+ let parsed;
400
+ try {
401
+ parsed = new URL(url);
402
+ }
403
+ catch {
404
+ return false;
405
+ }
406
+ if (parsed.protocol === "wss:") {
407
+ return true;
408
+ }
409
+ if (parsed.protocol !== "ws:") {
410
+ return false;
411
+ }
412
+ // ws:// is only secure for loopback addresses
413
+ return isLoopbackHost(parsed.hostname);
414
+ }
@@ -1,4 +1,4 @@
1
- import { formatExecCommand, validateSystemRunCommandConsistency, } from "../infra/system-run-command.js";
1
+ import { resolveSystemRunCommand } from "../infra/system-run-command.js";
2
2
  function asRecord(value) {
3
3
  if (!value || typeof value !== "object" || Array.isArray(value)) {
4
4
  return null;
@@ -20,24 +20,10 @@ function clientHasApprovals(client) {
20
20
  const scopes = Array.isArray(client?.connect?.scopes) ? client?.connect?.scopes : [];
21
21
  return scopes.includes("operator.admin") || scopes.includes("operator.approvals");
22
22
  }
23
- function getCmdText(params) {
24
- const raw = normalizeString(params.rawCommand);
25
- if (raw) {
26
- return raw;
27
- }
28
- if (Array.isArray(params.command)) {
29
- const parts = params.command.map((v) => String(v));
30
- if (parts.length > 0) {
31
- return formatExecCommand(parts);
32
- }
33
- }
34
- return "";
35
- }
36
- function approvalMatchesRequest(params, record) {
23
+ function approvalMatchesRequest(cmdText, params, record) {
37
24
  if (record.request.host !== "node") {
38
25
  return false;
39
26
  }
40
- const cmdText = getCmdText(params);
41
27
  if (!cmdText || record.request.command !== cmdText) {
42
28
  return false;
43
29
  }
@@ -90,25 +76,18 @@ export function sanitizeSystemRunParamsForForwarding(opts) {
90
76
  return { ok: true, params: opts.rawParams };
91
77
  }
92
78
  const p = obj;
93
- const argv = Array.isArray(p.command) ? p.command.map((v) => String(v)) : [];
94
- const raw = normalizeString(p.rawCommand);
95
- if (raw) {
96
- if (!Array.isArray(p.command) || argv.length === 0) {
97
- return {
98
- ok: false,
99
- message: "rawCommand requires params.command",
100
- details: { code: "MISSING_COMMAND" },
101
- };
102
- }
103
- const validation = validateSystemRunCommandConsistency({ argv, rawCommand: raw });
104
- if (!validation.ok) {
105
- return {
106
- ok: false,
107
- message: validation.message,
108
- details: validation.details ?? { code: "RAW_COMMAND_MISMATCH" },
109
- };
110
- }
79
+ const cmdTextResolution = resolveSystemRunCommand({
80
+ command: p.command,
81
+ rawCommand: p.rawCommand,
82
+ });
83
+ if (!cmdTextResolution.ok) {
84
+ return {
85
+ ok: false,
86
+ message: cmdTextResolution.message,
87
+ details: cmdTextResolution.details,
88
+ };
111
89
  }
90
+ const cmdText = cmdTextResolution.cmdText;
112
91
  const approved = p.approved === true;
113
92
  const requestedDecision = normalizeApprovalDecision(p.approvalDecision);
114
93
  const wantsApprovalOverride = approved || requestedDecision !== null;
@@ -171,7 +150,7 @@ export function sanitizeSystemRunParamsForForwarding(opts) {
171
150
  details: { code: "APPROVAL_CLIENT_MISMATCH", runId },
172
151
  };
173
152
  }
174
- if (!approvalMatchesRequest(p, snapshot)) {
153
+ if (!approvalMatchesRequest(cmdText, p, snapshot)) {
175
154
  return {
176
155
  ok: false,
177
156
  message: "approval id does not match request",