@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,35 +1,12 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { fileURLToPath } from "node:url";
3
+ import { resolveControlUiRootSync } from "../infra/control-ui-assets.js";
4
+ import { isWithinDir } from "../infra/path-safety.js";
4
5
  import { DEFAULT_ASSISTANT_IDENTITY, resolveAssistantIdentity } from "./assistant-identity.js";
6
+ import { CONTROL_UI_BOOTSTRAP_CONFIG_PATH, } from "./control-ui-contract.js";
7
+ import { buildControlUiCspHeader } from "./control-ui-csp.js";
5
8
  import { buildControlUiAvatarUrl, CONTROL_UI_AVATAR_PREFIX, normalizeControlUiBasePath, resolveAssistantAvatarUrl, } from "./control-ui-shared.js";
6
9
  const ROOT_PREFIX = "/";
7
- function resolveControlUiRoot() {
8
- const here = path.dirname(fileURLToPath(import.meta.url));
9
- const execDir = (() => {
10
- try {
11
- return path.dirname(fs.realpathSync(process.execPath));
12
- }
13
- catch {
14
- return null;
15
- }
16
- })();
17
- const candidates = [
18
- // Packaged app: control-ui lives alongside the executable.
19
- execDir ? path.resolve(execDir, "control-ui") : null,
20
- // Running from dist: dist/gateway/control-ui.js -> dist/control-ui
21
- path.resolve(here, "../control-ui"),
22
- // Running from source: src/gateway/control-ui.ts -> dist/control-ui
23
- path.resolve(here, "../../dist/control-ui"),
24
- // Fallback to cwd (dev)
25
- path.resolve(process.cwd(), "dist", "control-ui"),
26
- ].filter((dir) => Boolean(dir));
27
- for (const dir of candidates) {
28
- if (fs.existsSync(path.join(dir, "index.html")))
29
- return dir;
30
- }
31
- return null;
32
- }
33
10
  function contentTypeForExt(ext) {
34
11
  switch (ext) {
35
12
  case ".html":
@@ -60,6 +37,33 @@ function contentTypeForExt(ext) {
60
37
  return "application/octet-stream";
61
38
  }
62
39
  }
40
+ /**
41
+ * Extensions recognised as static assets. Missing files with these extensions
42
+ * return 404 instead of the SPA index.html fallback. `.html` is intentionally
43
+ * excluded — actual HTML files on disk are served earlier, and missing `.html`
44
+ * paths should fall through to the SPA router (client-side routers may use
45
+ * `.html`-suffixed routes).
46
+ */
47
+ const STATIC_ASSET_EXTENSIONS = new Set([
48
+ ".js",
49
+ ".css",
50
+ ".json",
51
+ ".map",
52
+ ".svg",
53
+ ".png",
54
+ ".jpg",
55
+ ".jpeg",
56
+ ".gif",
57
+ ".webp",
58
+ ".ico",
59
+ ".txt",
60
+ ]);
61
+ function applyControlUiSecurityHeaders(res) {
62
+ res.setHeader("X-Frame-Options", "DENY");
63
+ res.setHeader("Content-Security-Policy", buildControlUiCspHeader());
64
+ res.setHeader("X-Content-Type-Options", "nosniff");
65
+ res.setHeader("Referrer-Policy", "no-referrer");
66
+ }
63
67
  function sendJson(res, status, body) {
64
68
  res.statusCode = status;
65
69
  res.setHeader("Content-Type", "application/json; charset=utf-8");
@@ -71,18 +75,22 @@ function isValidAgentId(agentId) {
71
75
  }
72
76
  export function handleControlUiAvatarRequest(req, res, opts) {
73
77
  const urlRaw = req.url;
74
- if (!urlRaw)
78
+ if (!urlRaw) {
75
79
  return false;
76
- if (req.method !== "GET" && req.method !== "HEAD")
80
+ }
81
+ if (req.method !== "GET" && req.method !== "HEAD") {
77
82
  return false;
83
+ }
78
84
  const url = new URL(urlRaw, "http://localhost");
79
85
  const basePath = normalizeControlUiBasePath(opts.basePath);
80
86
  const pathname = url.pathname;
81
87
  const pathWithBase = basePath
82
88
  ? `${basePath}${CONTROL_UI_AVATAR_PREFIX}/`
83
89
  : `${CONTROL_UI_AVATAR_PREFIX}/`;
84
- if (!pathname.startsWith(pathWithBase))
90
+ if (!pathname.startsWith(pathWithBase)) {
85
91
  return false;
92
+ }
93
+ applyControlUiSecurityHeaders(res);
86
94
  const agentIdParts = pathname.slice(pathWithBase.length).split("/").filter(Boolean);
87
95
  const agentId = agentIdParts[0] ?? "";
88
96
  if (agentIdParts.length !== 1 || !agentId || !isValidAgentId(agentId)) {
@@ -119,66 +127,93 @@ function respondNotFound(res) {
119
127
  res.setHeader("Content-Type", "text/plain; charset=utf-8");
120
128
  res.end("Not Found");
121
129
  }
122
- function serveFile(res, filePath) {
130
+ function setStaticFileHeaders(res, filePath) {
123
131
  const ext = path.extname(filePath).toLowerCase();
124
132
  res.setHeader("Content-Type", contentTypeForExt(ext));
125
133
  // Static UI should never be cached aggressively while iterating; allow the
126
134
  // browser to revalidate.
127
135
  res.setHeader("Cache-Control", "no-cache");
136
+ }
137
+ function serveFile(res, filePath) {
138
+ setStaticFileHeaders(res, filePath);
128
139
  res.end(fs.readFileSync(filePath));
129
140
  }
130
- function injectControlUiConfig(html, opts) {
131
- const { basePath, assistantName, assistantAvatar } = opts;
132
- const script = `<script>` +
133
- `window.__CLAWDBOT_CONTROL_UI_BASE_PATH__=${JSON.stringify(basePath)};` +
134
- `window.__CLAWDBOT_ASSISTANT_NAME__=${JSON.stringify(assistantName ?? DEFAULT_ASSISTANT_IDENTITY.name)};` +
135
- `window.__CLAWDBOT_ASSISTANT_AVATAR__=${JSON.stringify(assistantAvatar ?? DEFAULT_ASSISTANT_IDENTITY.avatar)};` +
136
- `</script>`;
137
- // Check if already injected
138
- if (html.includes("__CLAWDBOT_ASSISTANT_NAME__"))
139
- return html;
140
- const headClose = html.indexOf("</head>");
141
- if (headClose !== -1) {
142
- return `${html.slice(0, headClose)}${script}${html.slice(headClose)}`;
143
- }
144
- return `${script}${html}`;
141
+ function serveResolvedFile(res, filePath, body) {
142
+ setStaticFileHeaders(res, filePath);
143
+ res.end(body);
145
144
  }
146
- function serveIndexHtml(res, indexPath, opts) {
147
- const { basePath, config, agentId } = opts;
148
- const identity = config
149
- ? resolveAssistantIdentity({ cfg: config, agentId })
150
- : DEFAULT_ASSISTANT_IDENTITY;
151
- const resolvedAgentId = typeof identity.agentId === "string"
152
- ? identity.agentId
153
- : agentId;
154
- const avatarValue = resolveAssistantAvatarUrl({
155
- avatar: identity.avatar,
156
- agentId: resolvedAgentId,
157
- basePath,
158
- }) ?? identity.avatar;
145
+ function serveResolvedIndexHtml(res, body) {
159
146
  res.setHeader("Content-Type", "text/html; charset=utf-8");
160
147
  res.setHeader("Cache-Control", "no-cache");
161
- const raw = fs.readFileSync(indexPath, "utf8");
162
- res.end(injectControlUiConfig(raw, {
163
- basePath,
164
- assistantName: identity.name,
165
- assistantAvatar: avatarValue,
166
- }));
148
+ res.end(body);
149
+ }
150
+ function isContainedPath(baseDir, targetPath) {
151
+ const relative = path.relative(baseDir, targetPath);
152
+ return relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
153
+ }
154
+ function isExpectedSafePathError(error) {
155
+ const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
156
+ return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
157
+ }
158
+ function areSameFileIdentity(preOpen, opened) {
159
+ return preOpen.dev === opened.dev && preOpen.ino === opened.ino;
160
+ }
161
+ function resolveSafeControlUiFile(rootReal, filePath) {
162
+ let fd = null;
163
+ try {
164
+ const fileReal = fs.realpathSync(filePath);
165
+ if (!isContainedPath(rootReal, fileReal)) {
166
+ return null;
167
+ }
168
+ const preOpenStat = fs.lstatSync(fileReal);
169
+ if (!preOpenStat.isFile()) {
170
+ return null;
171
+ }
172
+ const openFlags = fs.constants.O_RDONLY |
173
+ (typeof fs.constants.O_NOFOLLOW === "number" ? fs.constants.O_NOFOLLOW : 0);
174
+ fd = fs.openSync(fileReal, openFlags);
175
+ const openedStat = fs.fstatSync(fd);
176
+ // Compare inode identity so swaps between validation and open are rejected.
177
+ if (!openedStat.isFile() || !areSameFileIdentity(preOpenStat, openedStat)) {
178
+ return null;
179
+ }
180
+ const resolved = { path: fileReal, fd };
181
+ fd = null;
182
+ return resolved;
183
+ }
184
+ catch (error) {
185
+ if (isExpectedSafePathError(error)) {
186
+ return null;
187
+ }
188
+ throw error;
189
+ }
190
+ finally {
191
+ if (fd !== null) {
192
+ fs.closeSync(fd);
193
+ }
194
+ }
167
195
  }
168
196
  function isSafeRelativePath(relPath) {
169
- if (!relPath)
197
+ if (!relPath) {
170
198
  return false;
199
+ }
171
200
  const normalized = path.posix.normalize(relPath);
172
- if (normalized.startsWith("../") || normalized === "..")
201
+ if (path.posix.isAbsolute(normalized) || path.win32.isAbsolute(normalized)) {
173
202
  return false;
174
- if (normalized.includes("\0"))
203
+ }
204
+ if (normalized.startsWith("../") || normalized === "..") {
205
+ return false;
206
+ }
207
+ if (normalized.includes("\0")) {
175
208
  return false;
209
+ }
176
210
  return true;
177
211
  }
178
212
  export function handleControlUiHttpRequest(req, res, opts) {
179
213
  const urlRaw = req.url;
180
- if (!urlRaw)
214
+ if (!urlRaw) {
181
215
  return false;
216
+ }
182
217
  if (req.method !== "GET" && req.method !== "HEAD") {
183
218
  res.statusCode = 405;
184
219
  res.setHeader("Content-Type", "text/plain; charset=utf-8");
@@ -190,41 +225,104 @@ export function handleControlUiHttpRequest(req, res, opts) {
190
225
  const pathname = url.pathname;
191
226
  if (!basePath) {
192
227
  if (pathname === "/ui" || pathname.startsWith("/ui/")) {
228
+ applyControlUiSecurityHeaders(res);
193
229
  respondNotFound(res);
194
230
  return true;
195
231
  }
196
232
  }
197
233
  if (basePath) {
198
234
  if (pathname === basePath) {
235
+ applyControlUiSecurityHeaders(res);
199
236
  res.statusCode = 302;
200
237
  res.setHeader("Location", `${basePath}/${url.search}`);
201
238
  res.end();
202
239
  return true;
203
240
  }
204
- if (!pathname.startsWith(`${basePath}/`))
241
+ if (!pathname.startsWith(`${basePath}/`)) {
205
242
  return false;
243
+ }
206
244
  }
207
- const root = (() => {
208
- if (opts?.root) {
209
- if (opts.root.kind === "resolved")
210
- return opts.root.path;
211
- return null;
245
+ applyControlUiSecurityHeaders(res);
246
+ const bootstrapConfigPath = basePath
247
+ ? `${basePath}${CONTROL_UI_BOOTSTRAP_CONFIG_PATH}`
248
+ : CONTROL_UI_BOOTSTRAP_CONFIG_PATH;
249
+ if (pathname === bootstrapConfigPath) {
250
+ const config = opts?.config;
251
+ const identity = config
252
+ ? resolveAssistantIdentity({ cfg: config, agentId: opts?.agentId })
253
+ : DEFAULT_ASSISTANT_IDENTITY;
254
+ const avatarValue = resolveAssistantAvatarUrl({
255
+ avatar: identity.avatar,
256
+ agentId: identity.agentId,
257
+ basePath,
258
+ });
259
+ if (req.method === "HEAD") {
260
+ res.statusCode = 200;
261
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
262
+ res.setHeader("Cache-Control", "no-cache");
263
+ res.end();
264
+ return true;
212
265
  }
213
- return resolveControlUiRoot();
214
- })();
266
+ sendJson(res, 200, {
267
+ basePath,
268
+ assistantName: identity.name,
269
+ assistantAvatar: avatarValue ?? identity.avatar,
270
+ assistantAgentId: identity.agentId,
271
+ });
272
+ return true;
273
+ }
274
+ const rootState = opts?.root;
275
+ if (rootState?.kind === "invalid") {
276
+ res.statusCode = 503;
277
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
278
+ res.end(`Control UI assets not found at ${rootState.path}. Build them with \`pnpm ui:build\` (auto-installs UI deps), or update gateway.controlUi.root.`);
279
+ return true;
280
+ }
281
+ if (rootState?.kind === "missing") {
282
+ res.statusCode = 503;
283
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
284
+ res.end("Control UI assets not found. Build them with `pnpm ui:build` (auto-installs UI deps), or run `pnpm ui:dev` during development.");
285
+ return true;
286
+ }
287
+ const root = rootState?.kind === "resolved"
288
+ ? rootState.path
289
+ : resolveControlUiRootSync({
290
+ moduleUrl: import.meta.url,
291
+ argv1: process.argv[1],
292
+ cwd: process.cwd(),
293
+ });
215
294
  if (!root) {
216
295
  res.statusCode = 503;
217
296
  res.setHeader("Content-Type", "text/plain; charset=utf-8");
218
297
  res.end("Control UI assets not found. Build them with `pnpm ui:build` (auto-installs UI deps), or run `pnpm ui:dev` during development.");
219
298
  return true;
220
299
  }
300
+ const rootReal = (() => {
301
+ try {
302
+ return fs.realpathSync(root);
303
+ }
304
+ catch (error) {
305
+ if (isExpectedSafePathError(error)) {
306
+ return null;
307
+ }
308
+ throw error;
309
+ }
310
+ })();
311
+ if (!rootReal) {
312
+ res.statusCode = 503;
313
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
314
+ res.end("Control UI assets not found. Build them with `pnpm ui:build` (auto-installs UI deps), or run `pnpm ui:dev` during development.");
315
+ return true;
316
+ }
221
317
  const uiPath = basePath && pathname.startsWith(`${basePath}/`) ? pathname.slice(basePath.length) : pathname;
222
318
  const rel = (() => {
223
- if (uiPath === ROOT_PREFIX)
319
+ if (uiPath === ROOT_PREFIX) {
224
320
  return "";
321
+ }
225
322
  const assetsIndex = uiPath.indexOf("/assets/");
226
- if (assetsIndex >= 0)
323
+ if (assetsIndex >= 0) {
227
324
  return uiPath.slice(assetsIndex + 1);
325
+ }
228
326
  return uiPath.slice(1);
229
327
  })();
230
328
  const requested = rel && !rel.endsWith("/") ? rel : `${rel}index.html`;
@@ -233,32 +331,57 @@ export function handleControlUiHttpRequest(req, res, opts) {
233
331
  respondNotFound(res);
234
332
  return true;
235
333
  }
236
- const filePath = path.join(root, fileRel);
237
- if (!filePath.startsWith(root)) {
334
+ const filePath = path.resolve(root, fileRel);
335
+ if (!isWithinDir(root, filePath)) {
238
336
  respondNotFound(res);
239
337
  return true;
240
338
  }
241
- if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
242
- if (path.basename(filePath) === "index.html") {
243
- serveIndexHtml(res, filePath, {
244
- basePath,
245
- config: opts?.config,
246
- agentId: opts?.agentId,
247
- });
339
+ const safeFile = resolveSafeControlUiFile(rootReal, filePath);
340
+ if (safeFile) {
341
+ try {
342
+ if (req.method === "HEAD") {
343
+ res.statusCode = 200;
344
+ setStaticFileHeaders(res, safeFile.path);
345
+ res.end();
346
+ return true;
347
+ }
348
+ if (path.basename(safeFile.path) === "index.html") {
349
+ serveResolvedIndexHtml(res, fs.readFileSync(safeFile.fd, "utf8"));
350
+ return true;
351
+ }
352
+ serveResolvedFile(res, safeFile.path, fs.readFileSync(safeFile.fd));
248
353
  return true;
249
354
  }
250
- serveFile(res, filePath);
355
+ finally {
356
+ fs.closeSync(safeFile.fd);
357
+ }
358
+ }
359
+ // If the requested path looks like a static asset (known extension), return
360
+ // 404 rather than falling through to the SPA index.html fallback. We check
361
+ // against the same set of extensions that contentTypeForExt() recognises so
362
+ // that dotted SPA routes (e.g. /user/jane.doe, /v2.0) still get the
363
+ // client-side router fallback.
364
+ if (STATIC_ASSET_EXTENSIONS.has(path.extname(fileRel).toLowerCase())) {
365
+ respondNotFound(res);
251
366
  return true;
252
367
  }
253
368
  // SPA fallback (client-side router): serve index.html for unknown paths.
254
369
  const indexPath = path.join(root, "index.html");
255
- if (fs.existsSync(indexPath)) {
256
- serveIndexHtml(res, indexPath, {
257
- basePath,
258
- config: opts?.config,
259
- agentId: opts?.agentId,
260
- });
261
- return true;
370
+ const safeIndex = resolveSafeControlUiFile(rootReal, indexPath);
371
+ if (safeIndex) {
372
+ try {
373
+ if (req.method === "HEAD") {
374
+ res.statusCode = 200;
375
+ setStaticFileHeaders(res, safeIndex.path);
376
+ res.end();
377
+ return true;
378
+ }
379
+ serveResolvedIndexHtml(res, fs.readFileSync(safeIndex.fd, "utf8"));
380
+ return true;
381
+ }
382
+ finally {
383
+ fs.closeSync(safeIndex.fd);
384
+ }
262
385
  }
263
386
  respondNotFound(res);
264
387
  return true;
@@ -0,0 +1 @@
1
+ export const GATEWAY_EVENT_UPDATE_AVAILABLE = "update.available";
@@ -15,16 +15,18 @@ const hookPresetMappings = {
15
15
  ],
16
16
  };
17
17
  const transformCache = new Map();
18
- export function resolveHookMappings(hooks) {
18
+ export function resolveHookMappings(hooks, opts) {
19
19
  const presets = hooks?.presets ?? [];
20
20
  const gmailAllowUnsafe = hooks?.gmail?.allowUnsafeExternalContent;
21
21
  const mappings = [];
22
- if (hooks?.mappings)
22
+ if (hooks?.mappings) {
23
23
  mappings.push(...hooks.mappings);
24
+ }
24
25
  for (const preset of presets) {
25
26
  const presetMappings = hookPresetMappings[preset];
26
- if (!presetMappings)
27
+ if (!presetMappings) {
27
28
  continue;
29
+ }
28
30
  if (preset === "gmail" && typeof gmailAllowUnsafe === "boolean") {
29
31
  mappings.push(...presetMappings.map((mapping) => ({
30
32
  ...mapping,
@@ -34,23 +36,26 @@ export function resolveHookMappings(hooks) {
34
36
  }
35
37
  mappings.push(...presetMappings);
36
38
  }
37
- if (mappings.length === 0)
39
+ if (mappings.length === 0) {
38
40
  return [];
39
- const configDir = path.dirname(CONFIG_PATH);
40
- const transformsDir = hooks?.transformsDir
41
- ? resolvePath(configDir, hooks.transformsDir)
42
- : configDir;
41
+ }
42
+ const configDir = path.resolve(opts?.configDir ?? path.dirname(CONFIG_PATH));
43
+ const transformsRootDir = path.join(configDir, "hooks", "transforms");
44
+ const transformsDir = resolveOptionalContainedPath(transformsRootDir, hooks?.transformsDir, "Hook transformsDir");
43
45
  return mappings.map((mapping, index) => normalizeHookMapping(mapping, index, transformsDir));
44
46
  }
45
47
  export async function applyHookMappings(mappings, ctx) {
46
- if (mappings.length === 0)
48
+ if (mappings.length === 0) {
47
49
  return null;
50
+ }
48
51
  for (const mapping of mappings) {
49
- if (!mappingMatches(mapping, ctx))
52
+ if (!mappingMatches(mapping, ctx)) {
50
53
  continue;
54
+ }
51
55
  const base = buildActionFromMapping(mapping, ctx);
52
- if (!base.ok)
56
+ if (!base.ok) {
53
57
  return base;
58
+ }
54
59
  let override = null;
55
60
  if (mapping.transform) {
56
61
  const transform = await loadTransform(mapping.transform);
@@ -59,11 +64,13 @@ export async function applyHookMappings(mappings, ctx) {
59
64
  return { ok: true, action: null, skipped: true };
60
65
  }
61
66
  }
62
- if (!base.action)
67
+ if (!base.action) {
63
68
  return { ok: true, action: null, skipped: true };
69
+ }
64
70
  const merged = mergeAction(base.action, override, mapping.action);
65
- if (!merged.ok)
71
+ if (!merged.ok) {
66
72
  return merged;
73
+ }
67
74
  return merged;
68
75
  }
69
76
  return null;
@@ -76,7 +83,7 @@ function normalizeHookMapping(mapping, index, transformsDir) {
76
83
  const wakeMode = mapping.wakeMode ?? "now";
77
84
  const transform = mapping.transform
78
85
  ? {
79
- modulePath: resolvePath(transformsDir, mapping.transform.module),
86
+ modulePath: resolveContainedPath(transformsDir, mapping.transform.module, "Hook transform"),
80
87
  exportName: mapping.transform.export?.trim() || undefined,
81
88
  }
82
89
  : undefined;
@@ -103,13 +110,15 @@ function normalizeHookMapping(mapping, index, transformsDir) {
103
110
  }
104
111
  function mappingMatches(mapping, ctx) {
105
112
  if (mapping.matchPath) {
106
- if (mapping.matchPath !== normalizeMatchPath(ctx.path))
113
+ if (mapping.matchPath !== normalizeMatchPath(ctx.path)) {
107
114
  return false;
115
+ }
108
116
  }
109
117
  if (mapping.matchSource) {
110
118
  const source = typeof ctx.payload.source === "string" ? ctx.payload.source : undefined;
111
- if (!source || source !== mapping.matchSource)
119
+ if (!source || source !== mapping.matchSource) {
112
120
  return false;
121
+ }
113
122
  }
114
123
  return true;
115
124
  }
@@ -149,7 +158,7 @@ function mergeAction(base, override, defaultAction) {
149
158
  if (!override) {
150
159
  return validateAction(base);
151
160
  }
152
- const kind = (override.kind ?? base.kind ?? defaultAction);
161
+ const kind = override.kind ?? base.kind ?? defaultAction;
153
162
  if (kind === "wake") {
154
163
  const baseWake = base.kind === "wake" ? base : undefined;
155
164
  const text = typeof override.text === "string" ? override.text : (baseWake?.text ?? "");
@@ -190,13 +199,15 @@ function validateAction(action) {
190
199
  return { ok: true, action };
191
200
  }
192
201
  async function loadTransform(transform) {
193
- const cached = transformCache.get(transform.modulePath);
194
- if (cached)
202
+ const cacheKey = `${transform.modulePath}::${transform.exportName ?? "default"}`;
203
+ const cached = transformCache.get(cacheKey);
204
+ if (cached) {
195
205
  return cached;
206
+ }
196
207
  const url = pathToFileURL(transform.modulePath).href;
197
208
  const mod = (await import(url));
198
209
  const fn = resolveTransformFn(mod, transform.exportName);
199
- transformCache.set(transform.modulePath, fn);
210
+ transformCache.set(cacheKey, fn);
200
211
  return fn;
201
212
  }
202
213
  function resolveTransformFn(mod, exportName) {
@@ -207,45 +218,73 @@ function resolveTransformFn(mod, exportName) {
207
218
  return candidate;
208
219
  }
209
220
  function resolvePath(baseDir, target) {
210
- if (!target)
211
- return baseDir;
212
- if (path.isAbsolute(target))
213
- return target;
214
- return path.join(baseDir, target);
221
+ if (!target) {
222
+ return path.resolve(baseDir);
223
+ }
224
+ return path.isAbsolute(target) ? path.resolve(target) : path.resolve(baseDir, target);
225
+ }
226
+ function resolveContainedPath(baseDir, target, label) {
227
+ const base = path.resolve(baseDir);
228
+ const trimmed = target?.trim();
229
+ if (!trimmed) {
230
+ throw new Error(`${label} module path is required`);
231
+ }
232
+ const resolved = resolvePath(base, trimmed);
233
+ const relative = path.relative(base, resolved);
234
+ if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
235
+ throw new Error(`${label} module path must be within ${base}: ${target}`);
236
+ }
237
+ return resolved;
238
+ }
239
+ function resolveOptionalContainedPath(baseDir, target, label) {
240
+ const trimmed = target?.trim();
241
+ if (!trimmed) {
242
+ return path.resolve(baseDir);
243
+ }
244
+ return resolveContainedPath(baseDir, trimmed, label);
215
245
  }
216
246
  function normalizeMatchPath(raw) {
217
- if (!raw)
247
+ if (!raw) {
218
248
  return undefined;
249
+ }
219
250
  const trimmed = raw.trim();
220
- if (!trimmed)
251
+ if (!trimmed) {
221
252
  return undefined;
253
+ }
222
254
  return trimmed.replace(/^\/+/, "").replace(/\/+$/, "");
223
255
  }
224
256
  function renderOptional(value, ctx) {
225
- if (!value)
257
+ if (!value) {
226
258
  return undefined;
259
+ }
227
260
  const rendered = renderTemplate(value, ctx).trim();
228
261
  return rendered ? rendered : undefined;
229
262
  }
230
263
  function renderTemplate(template, ctx) {
231
- if (!template)
264
+ if (!template) {
232
265
  return "";
266
+ }
233
267
  return template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (_, expr) => {
234
268
  const value = resolveTemplateExpr(expr.trim(), ctx);
235
- if (value === undefined || value === null)
269
+ if (value === undefined || value === null) {
236
270
  return "";
237
- if (typeof value === "string")
271
+ }
272
+ if (typeof value === "string") {
238
273
  return value;
239
- if (typeof value === "number" || typeof value === "boolean")
274
+ }
275
+ if (typeof value === "number" || typeof value === "boolean") {
240
276
  return String(value);
277
+ }
241
278
  return JSON.stringify(value);
242
279
  });
243
280
  }
244
281
  function resolveTemplateExpr(expr, ctx) {
245
- if (expr === "path")
282
+ if (expr === "path") {
246
283
  return ctx.path;
247
- if (expr === "now")
284
+ }
285
+ if (expr === "now") {
248
286
  return new Date().toISOString();
287
+ }
249
288
  if (expr.startsWith("headers.")) {
250
289
  return getByPath(ctx.headers, expr.slice("headers.".length));
251
290
  }
@@ -257,9 +296,14 @@ function resolveTemplateExpr(expr, ctx) {
257
296
  }
258
297
  return getByPath(ctx.payload, expr);
259
298
  }
299
+ // Block traversal into prototype-chain properties on attacker-controlled
300
+ // webhook payloads. Mirrors the same blocklist used by config-paths.ts
301
+ // for config path traversal.
302
+ const BLOCKED_PATH_KEYS = new Set(["__proto__", "prototype", "constructor"]);
260
303
  function getByPath(input, pathExpr) {
261
- if (!pathExpr)
304
+ if (!pathExpr) {
262
305
  return undefined;
306
+ }
263
307
  const parts = [];
264
308
  const re = /([^.[\]]+)|(\[(\d+)\])/g;
265
309
  let match = re.exec(pathExpr);
@@ -274,16 +318,22 @@ function getByPath(input, pathExpr) {
274
318
  }
275
319
  let current = input;
276
320
  for (const part of parts) {
277
- if (current === null || current === undefined)
321
+ if (current === null || current === undefined) {
278
322
  return undefined;
323
+ }
279
324
  if (typeof part === "number") {
280
- if (!Array.isArray(current))
325
+ if (!Array.isArray(current)) {
281
326
  return undefined;
327
+ }
282
328
  current = current[part];
283
329
  continue;
284
330
  }
285
- if (typeof current !== "object")
331
+ if (BLOCKED_PATH_KEYS.has(part)) {
332
+ return undefined;
333
+ }
334
+ if (typeof current !== "object") {
286
335
  return undefined;
336
+ }
287
337
  current = current[part];
288
338
  }
289
339
  return current;