@poolzin/pool-bot 2026.2.25 → 2026.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/dist/acp/event-mapper.js +87 -22
  2. package/dist/acp/meta.js +12 -6
  3. package/dist/agents/agent-paths.js +8 -9
  4. package/dist/agents/agent-scope.js +7 -5
  5. package/dist/agents/auth-profiles/oauth.js +148 -64
  6. package/dist/agents/auth-profiles/session-override.js +13 -7
  7. package/dist/agents/bash-tools.exec-host-gateway.js +14 -4
  8. package/dist/agents/bash-tools.exec-runtime.js +2 -25
  9. package/dist/agents/bedrock-discovery.js +3 -1
  10. package/dist/agents/byteplus-models.js +97 -0
  11. package/dist/agents/chutes-oauth.js +1 -0
  12. package/dist/agents/cli-runner/helpers.js +4 -0
  13. package/dist/agents/compaction.js +41 -14
  14. package/dist/agents/doubao-models.js +121 -0
  15. package/dist/agents/failover-error.js +2 -0
  16. package/dist/agents/huggingface-models.js +5 -3
  17. package/dist/agents/live-model-filter.js +5 -0
  18. package/dist/agents/minimax-vlm.js +10 -8
  19. package/dist/agents/model-auth.js +6 -0
  20. package/dist/agents/model-catalog.js +3 -1
  21. package/dist/agents/model-selection.js +7 -1
  22. package/dist/agents/models-config.providers.js +93 -11
  23. package/dist/agents/ollama-stream.js +117 -4
  24. package/dist/agents/opencode-zen-models.js +22 -11
  25. package/dist/agents/pi-embedded-helpers/errors.js +55 -33
  26. package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
  27. package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
  28. package/dist/agents/pi-embedded-helpers.js +1 -1
  29. package/dist/agents/pi-embedded-runner/compact.js +29 -7
  30. package/dist/agents/pi-embedded-runner/extensions.js +28 -26
  31. package/dist/agents/pi-embedded-runner/google.js +20 -8
  32. package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
  33. package/dist/agents/pi-embedded-runner/run.js +71 -12
  34. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
  35. package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
  36. package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
  37. package/dist/agents/pi-embedded-runner/thinking.js +42 -0
  38. package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
  39. package/dist/agents/pi-embedded-runner/utils.js +7 -10
  40. package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
  41. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
  42. package/dist/agents/pi-embedded-subscribe.js +9 -4
  43. package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
  44. package/dist/agents/pi-embedded-utils.js +3 -0
  45. package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
  46. package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
  47. package/dist/agents/pi-settings.js +40 -0
  48. package/dist/agents/pi-tools.policy.js +2 -1
  49. package/dist/agents/provider/config-loader.js +1 -1
  50. package/dist/agents/sandbox/browser.js +170 -33
  51. package/dist/agents/sandbox/config-hash.js +14 -27
  52. package/dist/agents/sandbox/config.js +21 -2
  53. package/dist/agents/sandbox/constants.js +2 -0
  54. package/dist/agents/sandbox/docker.js +16 -2
  55. package/dist/agents/sandbox/novnc-auth.js +62 -0
  56. package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
  57. package/dist/agents/sandbox/shared.js +10 -6
  58. package/dist/agents/sandbox-paths.js +24 -11
  59. package/dist/agents/schema/clean-for-gemini.js +132 -85
  60. package/dist/agents/session-slug.js +10 -5
  61. package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
  62. package/dist/agents/session-tool-result-guard.js +3 -1
  63. package/dist/agents/session-transcript-repair.js +40 -6
  64. package/dist/agents/skills/bundled-dir.js +19 -5
  65. package/dist/agents/skills/env-overrides.js +124 -43
  66. package/dist/agents/skills/frontmatter.js +6 -6
  67. package/dist/agents/skills/plugin-skills.js +14 -7
  68. package/dist/agents/skills/workspace.js +1 -0
  69. package/dist/agents/subagent-announce.js +251 -49
  70. package/dist/agents/subagent-lifecycle-events.js +19 -0
  71. package/dist/agents/subagent-registry-cleanup.js +31 -0
  72. package/dist/agents/subagent-registry-completion.js +68 -0
  73. package/dist/agents/subagent-registry-queries.js +117 -0
  74. package/dist/agents/subagent-registry-state.js +46 -0
  75. package/dist/agents/subagent-registry.js +252 -221
  76. package/dist/agents/subagent-registry.store.js +1 -0
  77. package/dist/agents/subagent-registry.types.js +1 -0
  78. package/dist/agents/subagent-spawn.js +195 -7
  79. package/dist/agents/system-prompt.js +22 -6
  80. package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
  81. package/dist/agents/test-helpers/fast-core-tools.js +1 -17
  82. package/dist/agents/timeout.js +18 -6
  83. package/dist/agents/tool-call-id.js +1 -1
  84. package/dist/agents/tool-display-common.js +162 -29
  85. package/dist/agents/tool-images.js +82 -9
  86. package/dist/agents/tool-policy.js +51 -26
  87. package/dist/agents/tools/browser-tool.js +2 -2
  88. package/dist/agents/tools/canvas-tool.js +27 -1
  89. package/dist/agents/tools/common.js +45 -0
  90. package/dist/agents/tools/discord-actions-guild.js +4 -1
  91. package/dist/agents/tools/gateway-tool.js +3 -1
  92. package/dist/agents/tools/nodes-utils.js +1 -10
  93. package/dist/agents/tools/sessions-send-helpers.js +12 -6
  94. package/dist/agents/tools/sessions-spawn-tool.js +8 -2
  95. package/dist/agents/tools/subagents-tool.js +2 -1
  96. package/dist/agents/tools/whatsapp-actions.js +10 -2
  97. package/dist/agents/tools/whatsapp-target-auth.js +18 -0
  98. package/dist/agents/transcript-policy.js +22 -8
  99. package/dist/agents/venice-models.js +11 -3
  100. package/dist/auto-reply/commands-registry.data.js +51 -0
  101. package/dist/auto-reply/commands-registry.js +4 -3
  102. package/dist/auto-reply/group-activation.js +10 -5
  103. package/dist/auto-reply/inbound-debounce.js +10 -5
  104. package/dist/auto-reply/reply/abort.js +1 -1
  105. package/dist/auto-reply/reply/agent-runner-execution.js +4 -1
  106. package/dist/auto-reply/reply/bash-command.js +41 -39
  107. package/dist/auto-reply/reply/command-gates.js +25 -0
  108. package/dist/auto-reply/reply/commands-allowlist.js +111 -72
  109. package/dist/auto-reply/reply/commands-bash.js +6 -5
  110. package/dist/auto-reply/reply/commands-config.js +30 -28
  111. package/dist/auto-reply/reply/commands-core.js +2 -1
  112. package/dist/auto-reply/reply/commands-info.js +1 -0
  113. package/dist/auto-reply/reply/commands-models.js +65 -14
  114. package/dist/auto-reply/reply/commands-session.js +237 -82
  115. package/dist/auto-reply/reply/commands-setunset.js +45 -0
  116. package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
  117. package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
  118. package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
  119. package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
  120. package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
  121. package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
  122. package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
  123. package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
  124. package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
  125. package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
  126. package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
  127. package/dist/auto-reply/reply/commands-subagents.js +51 -587
  128. package/dist/auto-reply/reply/commands-tts.js +10 -5
  129. package/dist/auto-reply/reply/config-value.js +10 -5
  130. package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
  131. package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
  132. package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
  133. package/dist/auto-reply/reply/followup-runner.js +1 -0
  134. package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
  135. package/dist/auto-reply/reply/get-reply-directives.js +17 -28
  136. package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
  137. package/dist/auto-reply/reply/get-reply.js +71 -12
  138. package/dist/auto-reply/reply/model-selection.js +80 -39
  139. package/dist/auto-reply/reply/queue/enqueue.js +10 -5
  140. package/dist/auto-reply/reply/queue/state.js +13 -12
  141. package/dist/auto-reply/reply/reply-payloads.js +67 -36
  142. package/dist/auto-reply/reply/reply-reference.js +9 -8
  143. package/dist/auto-reply/reply/route-reply.js +15 -8
  144. package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
  145. package/dist/auto-reply/reply/session.js +22 -6
  146. package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
  147. package/dist/auto-reply/reply/subagents-utils.js +56 -30
  148. package/dist/auto-reply/reply/typing.js +46 -21
  149. package/dist/auto-reply/send-policy.js +14 -7
  150. package/dist/auto-reply/status.js +140 -16
  151. package/dist/auto-reply/templating.js +10 -5
  152. package/dist/auto-reply/thinking.js +7 -16
  153. package/dist/auto-reply/tokens.js +21 -5
  154. package/dist/browser/bridge-server.js +36 -20
  155. package/dist/browser/cdp.helpers.js +7 -14
  156. package/dist/browser/cdp.js +35 -15
  157. package/dist/browser/chrome.profile-decoration.js +7 -4
  158. package/dist/browser/config.js +4 -0
  159. package/dist/browser/extension-relay-auth.js +55 -0
  160. package/dist/browser/extension-relay.js +74 -29
  161. package/dist/browser/navigation-guard.js +9 -1
  162. package/dist/browser/paths.js +77 -0
  163. package/dist/browser/profiles.js +13 -8
  164. package/dist/browser/pw-ai-module.js +10 -5
  165. package/dist/browser/pw-session.js +76 -39
  166. package/dist/browser/pw-tools-core.interactions.js +14 -7
  167. package/dist/browser/pw-tools-core.state.js +12 -6
  168. package/dist/browser/routes/agent.act.js +2 -2
  169. package/dist/browser/server-context.js +7 -0
  170. package/dist/build-info.json +3 -3
  171. package/dist/channels/allow-from.js +2 -1
  172. package/dist/channels/allowlists/resolve-utils.js +43 -19
  173. package/dist/channels/channel-config.js +14 -7
  174. package/dist/channels/draft-stream-loop.js +7 -0
  175. package/dist/channels/model-overrides.js +82 -0
  176. package/dist/channels/plugins/normalize/imessage.js +14 -7
  177. package/dist/channels/plugins/normalize/slack.js +10 -5
  178. package/dist/channels/plugins/normalize/telegram.js +14 -7
  179. package/dist/channels/plugins/outbound/discord.js +80 -8
  180. package/dist/channels/plugins/outbound/signal.js +11 -11
  181. package/dist/channels/plugins/setup-helpers.js +10 -5
  182. package/dist/channels/sender-label.js +14 -7
  183. package/dist/channels/session.js +4 -2
  184. package/dist/channels/status-reactions.js +297 -0
  185. package/dist/cli/banner.js +1 -1
  186. package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
  187. package/dist/cli/cli-name.js +11 -11
  188. package/dist/cli/cli-utils.js +13 -3
  189. package/dist/cli/command-format.js +1 -1
  190. package/dist/cli/config-cli.js +1 -1
  191. package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
  192. package/dist/cli/daemon-cli/lifecycle.js +64 -2
  193. package/dist/cli/daemon-cli/restart-health.js +126 -0
  194. package/dist/cli/daemon-cli/status.gather.js +9 -13
  195. package/dist/cli/daemon-cli/status.print.js +2 -10
  196. package/dist/cli/deps.js +27 -22
  197. package/dist/cli/gateway-cli/run-loop.js +23 -5
  198. package/dist/cli/node-cli/register.js +14 -5
  199. package/dist/cli/nodes-media-utils.js +7 -2
  200. package/dist/cli/outbound-send-deps.js +2 -9
  201. package/dist/cli/outbound-send-mapping.js +11 -0
  202. package/dist/cli/pairing-cli.js +40 -14
  203. package/dist/cli/plugins-cli.js +34 -41
  204. package/dist/cli/ports.js +11 -10
  205. package/dist/cli/program/command-registry.js +2 -11
  206. package/dist/cli/program/command-tree.js +16 -0
  207. package/dist/cli/program/preaction.js +13 -9
  208. package/dist/cli/program/register.configure.js +3 -18
  209. package/dist/cli/program/register.maintenance.js +2 -2
  210. package/dist/cli/program/register.onboard.js +2 -0
  211. package/dist/cli/program/register.status-health-sessions.js +16 -17
  212. package/dist/cli/program/register.subclis.js +93 -52
  213. package/dist/cli/route.js +11 -7
  214. package/dist/cli/system-cli.js +36 -46
  215. package/dist/cli/update-cli/shared.js +22 -9
  216. package/dist/cli/update-cli/update-command.js +89 -14
  217. package/dist/cli/update-cli/wizard.js +6 -12
  218. package/dist/commands/agent/run-context.js +18 -5
  219. package/dist/commands/agent/session-store.js +17 -4
  220. package/dist/commands/agent.js +22 -2
  221. package/dist/commands/agents.bindings.js +14 -7
  222. package/dist/commands/agents.commands.add.js +13 -9
  223. package/dist/commands/agents.commands.identity.js +12 -6
  224. package/dist/commands/agents.commands.list.js +11 -6
  225. package/dist/commands/agents.config.js +8 -10
  226. package/dist/commands/agents.providers.js +12 -6
  227. package/dist/commands/auth-choice-options.js +103 -75
  228. package/dist/commands/auth-choice.apply.byteplus.js +55 -0
  229. package/dist/commands/auth-choice.apply.js +4 -0
  230. package/dist/commands/auth-choice.apply.minimax.js +61 -13
  231. package/dist/commands/auth-choice.apply.openai.js +3 -1
  232. package/dist/commands/auth-choice.apply.volcengine.js +55 -0
  233. package/dist/commands/auth-choice.preferred-provider.js +2 -0
  234. package/dist/commands/channels/remove.js +13 -6
  235. package/dist/commands/channels/shared.js +4 -14
  236. package/dist/commands/configure.commands.js +14 -0
  237. package/dist/commands/configure.gateway.js +2 -4
  238. package/dist/commands/configure.js +1 -1
  239. package/dist/commands/configure.shared.js +11 -0
  240. package/dist/commands/daemon-install-helpers.js +2 -2
  241. package/dist/commands/dashboard.js +12 -10
  242. package/dist/commands/docs.js +14 -8
  243. package/dist/commands/doctor-config-flow.js +11 -9
  244. package/dist/commands/doctor-legacy-config.js +281 -0
  245. package/dist/commands/doctor-state-integrity.js +99 -23
  246. package/dist/commands/doctor-update.js +12 -9
  247. package/dist/commands/models/list.list-command.js +7 -5
  248. package/dist/commands/models/set-image.js +2 -21
  249. package/dist/commands/node-daemon-install-helpers.js +10 -8
  250. package/dist/commands/onboard-auth.config-minimax.js +54 -80
  251. package/dist/commands/onboard-auth.config-opencode.js +2 -18
  252. package/dist/commands/onboard-auth.credentials.js +90 -13
  253. package/dist/commands/onboard-auth.js +1 -1
  254. package/dist/commands/onboard-auth.models.js +6 -5
  255. package/dist/commands/onboard-hooks.js +1 -1
  256. package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
  257. package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
  258. package/dist/commands/onboard-provider-auth-flags.js +14 -0
  259. package/dist/commands/onboard-remote.js +14 -7
  260. package/dist/commands/onboard.js +11 -13
  261. package/dist/commands/sandbox-display.js +6 -5
  262. package/dist/commands/status-all/diagnosis.js +14 -10
  263. package/dist/commands/status-all/format.js +1 -0
  264. package/dist/commands/status.gateway-probe.js +1 -16
  265. package/dist/commands/systemd-linger.js +12 -6
  266. package/dist/config/agent-limits.js +2 -0
  267. package/dist/config/commands.js +30 -16
  268. package/dist/config/config-paths.js +9 -11
  269. package/dist/config/defaults.js +22 -2
  270. package/dist/config/discord-preview-streaming.js +104 -0
  271. package/dist/config/env-vars.js +37 -8
  272. package/dist/config/includes.js +4 -0
  273. package/dist/config/io.js +97 -12
  274. package/dist/config/legacy.migrations.part-1.js +189 -78
  275. package/dist/config/legacy.shared.js +3 -1
  276. package/dist/config/merge-patch.js +4 -0
  277. package/dist/config/prototype-keys.js +4 -0
  278. package/dist/config/schema.help.js +44 -7
  279. package/dist/config/schema.labels.js +38 -6
  280. package/dist/config/sessions/delivery-info.js +10 -3
  281. package/dist/config/sessions/main-session.js +10 -5
  282. package/dist/config/sessions/session-file.js +33 -0
  283. package/dist/config/sessions/session-key.js +10 -5
  284. package/dist/config/sessions/store.js +1 -1
  285. package/dist/config/sessions.js +1 -0
  286. package/dist/config/zod-schema.agent-runtime.js +11 -0
  287. package/dist/config/zod-schema.js +148 -13
  288. package/dist/config/zod-schema.providers-core.js +78 -4
  289. package/dist/config/zod-schema.providers.js +6 -1
  290. package/dist/config/zod-schema.session.js +41 -2
  291. package/dist/cron/run-log.js +3 -0
  292. package/dist/cron/schedule.js +21 -10
  293. package/dist/cron/service/ops.js +35 -21
  294. package/dist/cron/service/timer.js +116 -16
  295. package/dist/cron/stagger.js +3 -1
  296. package/dist/discord/api.js +12 -6
  297. package/dist/discord/draft-chunking.js +22 -0
  298. package/dist/discord/draft-stream.js +124 -0
  299. package/dist/discord/monitor/agent-components.js +1 -1
  300. package/dist/discord/monitor/commands.js +5 -0
  301. package/dist/discord/monitor/gateway-plugin.js +2 -1
  302. package/dist/discord/monitor/listeners.js +37 -27
  303. package/dist/discord/monitor/message-handler.js +4 -1
  304. package/dist/discord/monitor/message-handler.preflight.js +65 -8
  305. package/dist/discord/monitor/message-handler.process.js +246 -217
  306. package/dist/discord/monitor/message-utils.js +143 -6
  307. package/dist/discord/monitor/model-picker-preferences.js +143 -0
  308. package/dist/discord/monitor/model-picker.js +651 -0
  309. package/dist/discord/monitor/native-command.js +573 -16
  310. package/dist/discord/monitor/provider.allowlist.js +223 -0
  311. package/dist/discord/monitor/provider.js +275 -347
  312. package/dist/discord/monitor/provider.lifecycle.js +100 -0
  313. package/dist/discord/monitor/reply-delivery.js +123 -16
  314. package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
  315. package/dist/discord/monitor/thread-bindings.js +4 -0
  316. package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
  317. package/dist/discord/monitor/thread-bindings.manager.js +423 -0
  318. package/dist/discord/monitor/thread-bindings.messages.js +55 -0
  319. package/dist/discord/monitor/thread-bindings.state.js +358 -0
  320. package/dist/discord/monitor/thread-bindings.types.js +6 -0
  321. package/dist/discord/resolve-users.js +33 -21
  322. package/dist/discord/send.channels.js +15 -0
  323. package/dist/discord/send.js +3 -2
  324. package/dist/discord/send.outbound.js +82 -26
  325. package/dist/discord/send.permissions.js +83 -30
  326. package/dist/discord/send.reactions.js +8 -4
  327. package/dist/discord/token.js +10 -5
  328. package/dist/discord/voice/command.js +263 -0
  329. package/dist/discord/voice/manager.js +531 -0
  330. package/dist/gateway/auth.js +34 -10
  331. package/dist/gateway/call.js +4 -16
  332. package/dist/gateway/client.js +28 -4
  333. package/dist/gateway/config-reload.js +3 -4
  334. package/dist/gateway/control-ui.js +219 -96
  335. package/dist/gateway/hooks-mapping.js +88 -38
  336. package/dist/gateway/http-auth-helpers.js +3 -2
  337. package/dist/gateway/http-endpoint-helpers.js +1 -0
  338. package/dist/gateway/net.js +54 -12
  339. package/dist/gateway/node-invoke-system-run-approval.js +14 -35
  340. package/dist/gateway/node-registry.js +10 -5
  341. package/dist/gateway/openai-http.js +1 -0
  342. package/dist/gateway/openresponses-http.js +1 -0
  343. package/dist/gateway/origin-check.js +1 -18
  344. package/dist/gateway/protocol/index.js +4 -3
  345. package/dist/gateway/protocol/schema/cron.js +1 -0
  346. package/dist/gateway/protocol/schema/devices.js +1 -0
  347. package/dist/gateway/protocol/schema/protocol-schemas.js +2 -1
  348. package/dist/gateway/protocol/schema/sessions.js +6 -0
  349. package/dist/gateway/role-policy.js +17 -0
  350. package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
  351. package/dist/gateway/server/ws-connection/message-handler.js +175 -148
  352. package/dist/gateway/server-chat.js +83 -25
  353. package/dist/gateway/server-constants.js +10 -9
  354. package/dist/gateway/server-cron.js +1 -0
  355. package/dist/gateway/server-http.js +16 -7
  356. package/dist/gateway/server-maintenance.js +20 -5
  357. package/dist/gateway/server-methods/chat.js +10 -6
  358. package/dist/gateway/server-methods/config.js +12 -14
  359. package/dist/gateway/server-methods/devices.js +17 -3
  360. package/dist/gateway/server-methods/models.js +11 -1
  361. package/dist/gateway/server-methods/sessions.js +64 -8
  362. package/dist/gateway/server-methods/usage.js +162 -75
  363. package/dist/gateway/server-node-events.js +29 -0
  364. package/dist/gateway/server-runtime-config.js +34 -13
  365. package/dist/gateway/server-startup-memory.js +17 -11
  366. package/dist/gateway/session-utils.fs.js +32 -34
  367. package/dist/gateway/sessions-resolve.js +17 -5
  368. package/dist/gateway/test-helpers.openai-mock.js +14 -7
  369. package/dist/gateway/tools-invoke-http.js +21 -10
  370. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
  371. package/dist/hooks/bundled/command-logger/handler.js +7 -2
  372. package/dist/hooks/bundled/session-memory/handler.js +6 -5
  373. package/dist/hooks/frontmatter.js +6 -6
  374. package/dist/hooks/gmail-watcher.js +11 -6
  375. package/dist/hooks/internal-hooks.js +11 -1
  376. package/dist/hooks/llm-slug-generator.js +4 -1
  377. package/dist/hooks/workspace.js +47 -17
  378. package/dist/imessage/accounts.js +9 -20
  379. package/dist/imessage/monitor/inbound-processing.js +2 -1
  380. package/dist/infra/archive.js +174 -73
  381. package/dist/infra/control-ui-assets.js +14 -6
  382. package/dist/infra/device-pairing.js +108 -29
  383. package/dist/infra/env.js +10 -5
  384. package/dist/infra/exec-approvals-allowlist.js +122 -0
  385. package/dist/infra/exec-approvals-analysis.js +34 -3
  386. package/dist/infra/exec-approvals.js +5 -17
  387. package/dist/infra/exec-safe-bin-policy.js +53 -45
  388. package/dist/infra/fs-safe.js +71 -39
  389. package/dist/infra/gateway-lock.js +6 -2
  390. package/dist/infra/heartbeat-wake.js +6 -12
  391. package/dist/infra/host-env-security-policy.json +19 -0
  392. package/dist/infra/host-env-security.js +66 -0
  393. package/dist/infra/net/ssrf.js +131 -38
  394. package/dist/infra/outbound/bound-delivery-router.js +88 -0
  395. package/dist/infra/outbound/channel-selection.js +12 -6
  396. package/dist/infra/outbound/envelope.js +1 -1
  397. package/dist/infra/outbound/format.js +12 -6
  398. package/dist/infra/outbound/payloads.js +14 -7
  399. package/dist/infra/outbound/session-binding-service.js +123 -0
  400. package/dist/infra/path-guards.js +25 -0
  401. package/dist/infra/provider-usage.fetch.codex.js +7 -15
  402. package/dist/infra/provider-usage.fetch.gemini.js +14 -11
  403. package/dist/infra/provider-usage.fetch.shared.js +30 -1
  404. package/dist/infra/provider-usage.fetch.zai.js +10 -9
  405. package/dist/infra/retry-policy.js +4 -2
  406. package/dist/infra/retry.js +9 -5
  407. package/dist/infra/session-cost-usage.js +107 -59
  408. package/dist/infra/session-maintenance-warning.js +3 -1
  409. package/dist/infra/shell-env.js +98 -34
  410. package/dist/infra/ssh-config.js +12 -6
  411. package/dist/infra/system-run-command.js +49 -4
  412. package/dist/infra/update-channels.js +10 -5
  413. package/dist/line/accounts.js +5 -7
  414. package/dist/line/bot-access.js +8 -20
  415. package/dist/line/bot-handlers.js +3 -1
  416. package/dist/link-understanding/detect.js +15 -7
  417. package/dist/media/constants.js +15 -6
  418. package/dist/media/image-ops.js +7 -0
  419. package/dist/media/local-roots.js +3 -2
  420. package/dist/media-understanding/apply.js +4 -1
  421. package/dist/media-understanding/concurrency.js +8 -20
  422. package/dist/memory/backend-config.js +45 -6
  423. package/dist/memory/embeddings.js +10 -4
  424. package/dist/memory/fs-utils.js +23 -0
  425. package/dist/memory/manager-search.js +12 -6
  426. package/dist/memory/manager-sync-ops.js +12 -2
  427. package/dist/memory/qmd-manager.js +466 -53
  428. package/dist/memory/query-expansion.js +167 -3
  429. package/dist/memory/status-format.js +10 -5
  430. package/dist/memory/sync-memory-files.js +1 -1
  431. package/dist/node-host/invoke-system-run.js +281 -0
  432. package/dist/node-host/invoke.js +55 -337
  433. package/dist/pairing/pairing-store.js +22 -0
  434. package/dist/plugin-sdk/allow-from.js +1 -1
  435. package/dist/plugin-sdk/command-auth.js +3 -1
  436. package/dist/plugin-sdk/index.js +6 -3
  437. package/dist/plugin-sdk/webhook-targets.js +32 -0
  438. package/dist/plugins/bundled-dir.js +9 -6
  439. package/dist/plugins/hooks.js +50 -0
  440. package/dist/plugins/install.js +28 -16
  441. package/dist/plugins/runtime.js +3 -17
  442. package/dist/plugins/update.js +78 -12
  443. package/dist/process/spawn-utils.js +14 -7
  444. package/dist/providers/github-copilot-token.js +11 -6
  445. package/dist/providers/qwen-portal-oauth.js +14 -6
  446. package/dist/routing/account-id.js +30 -0
  447. package/dist/routing/resolve-route.js +3 -7
  448. package/dist/routing/session-key.js +2 -16
  449. package/dist/security/audit-channel.js +93 -2
  450. package/dist/security/audit-extra.async.js +159 -5
  451. package/dist/security/audit-extra.js +1 -1
  452. package/dist/security/audit-extra.sync.js +85 -6
  453. package/dist/security/audit.js +40 -4
  454. package/dist/security/dm-policy-shared.js +44 -0
  455. package/dist/security/external-content.js +26 -6
  456. package/dist/shared/entry-status.js +6 -0
  457. package/dist/shared/frontmatter.js +5 -5
  458. package/dist/shared/node-match.js +11 -4
  459. package/dist/shared/operator-scope-compat.js +8 -3
  460. package/dist/signal/accounts.js +7 -20
  461. package/dist/signal/monitor/event-handler.js +3 -1
  462. package/dist/slack/accounts.js +6 -19
  463. package/dist/slack/actions.js +11 -3
  464. package/dist/slack/monitor/auth.js +1 -1
  465. package/dist/slack/monitor/message-handler/dispatch.js +50 -29
  466. package/dist/slack/monitor/replies.js +15 -7
  467. package/dist/slack/monitor/slash.js +22 -13
  468. package/dist/slack/resolve-channels.js +10 -5
  469. package/dist/slack/send.js +102 -12
  470. package/dist/slack/stream-mode.js +10 -0
  471. package/dist/slack/streaming.js +4 -2
  472. package/dist/telegram/accounts.js +19 -14
  473. package/dist/telegram/bot/helpers.js +3 -5
  474. package/dist/telegram/bot-access.js +35 -36
  475. package/dist/telegram/bot-handlers.js +120 -148
  476. package/dist/telegram/bot-message-context.js +68 -9
  477. package/dist/telegram/bot-message-dispatch.js +155 -90
  478. package/dist/telegram/bot-native-commands.js +16 -0
  479. package/dist/telegram/draft-stream.js +14 -1
  480. package/dist/telegram/inline-buttons.js +5 -15
  481. package/dist/telegram/monitor.js +11 -7
  482. package/dist/telegram/network-config.js +19 -7
  483. package/dist/telegram/send.js +3 -2
  484. package/dist/telegram/sent-message-cache.js +5 -6
  485. package/dist/telegram/status-reaction-variants.js +208 -0
  486. package/dist/telegram/sticker-cache.js +11 -9
  487. package/dist/terminal/theme.js +12 -12
  488. package/dist/tts/tts.js +80 -567
  489. package/dist/tui/components/chat-log.js +41 -8
  490. package/dist/tui/theme/theme.js +10 -12
  491. package/dist/tui/tui-local-shell.js +16 -6
  492. package/dist/tui/tui.js +58 -6
  493. package/dist/utils/account-id.js +2 -4
  494. package/dist/utils/boolean.js +10 -5
  495. package/dist/utils/directive-tags.js +11 -0
  496. package/dist/utils/queue-helpers.js +67 -12
  497. package/dist/web/auto-reply/deliver-reply.js +8 -4
  498. package/dist/web/auto-reply/mentions.js +10 -5
  499. package/dist/web/auto-reply/monitor/group-members.js +14 -7
  500. package/dist/web/auto-reply/monitor/process-message.js +45 -24
  501. package/dist/web/inbound/access-control.js +5 -2
  502. package/dist/web/login-qr.js +12 -6
  503. package/dist/web/media.js +123 -16
  504. package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
  505. package/extensions/bluebubbles/src/monitor.ts +208 -1950
  506. package/package.json +1 -1
@@ -6,8 +6,10 @@ import { stopSubagentsForRequester } from "../../auto-reply/reply/abort.js";
6
6
  import { clearSessionQueues } from "../../auto-reply/reply/queue.js";
7
7
  import { loadConfig } from "../../config/config.js";
8
8
  import { loadSessionStore, snapshotSessionOrigin, resolveMainSessionKey, updateSessionStore, } from "../../config/sessions.js";
9
+ import { unbindThreadBindingsBySessionKey } from "../../discord/monitor/thread-bindings.js";
9
10
  import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js";
10
- import { normalizeAgentId, parseAgentSessionKey } from "../../routing/session-key.js";
11
+ import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
12
+ import { isSubagentSessionKey, normalizeAgentId, parseAgentSessionKey, } from "../../routing/session-key.js";
11
13
  import { ErrorCodes, errorShape, validateSessionsCompactParams, validateSessionsDeleteParams, validateSessionsListParams, validateSessionsPatchParams, validateSessionsPreviewParams, validateSessionsResetParams, validateSessionsResolveParams, } from "../protocol/index.js";
12
14
  import { archiveFileOnDisk, archiveSessionTranscripts, listSessionsFromStore, loadCombinedSessionStoreForGateway, loadSessionEntry, pruneLegacyStoreKeys, readSessionPreviewItemsFromTranscript, resolveGatewaySessionStoreTarget, resolveSessionModelRef, resolveSessionTranscriptCandidates, } from "../session-utils.js";
13
15
  import { applySessionsPatchToStore } from "../sessions-patch.js";
@@ -33,6 +35,13 @@ function resolveGatewaySessionTargetFromKey(key) {
33
35
  const target = resolveGatewaySessionStoreTarget({ cfg, key });
34
36
  return { cfg, target, storePath: target.storePath };
35
37
  }
38
+ function rejectWebchatSessionMutation(params) {
39
+ if (!params.client?.connect || !params.isWebchatConnect(params.client.connect)) {
40
+ return false;
41
+ }
42
+ params.respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `webchat clients cannot ${params.action} sessions; use chat.send for session-scoped updates`));
43
+ return true;
44
+ }
36
45
  function migrateAndPruneSessionStoreKey(params) {
37
46
  const target = resolveGatewaySessionStoreTarget({
38
47
  cfg: params.cfg,
@@ -65,6 +74,31 @@ function archiveSessionTranscriptsForSession(params) {
65
74
  reason: params.reason,
66
75
  });
67
76
  }
77
+ async function emitSessionUnboundLifecycleEvent(params) {
78
+ const targetKind = isSubagentSessionKey(params.targetSessionKey) ? "subagent" : "acp";
79
+ unbindThreadBindingsBySessionKey({
80
+ targetSessionKey: params.targetSessionKey,
81
+ targetKind,
82
+ reason: params.reason,
83
+ sendFarewell: true,
84
+ });
85
+ if (params.emitHooks === false) {
86
+ return;
87
+ }
88
+ const hookRunner = getGlobalHookRunner();
89
+ if (!hookRunner?.hasHooks("subagent_ended")) {
90
+ return;
91
+ }
92
+ await hookRunner.runSubagentEnded({
93
+ targetSessionKey: params.targetSessionKey,
94
+ targetKind,
95
+ reason: params.reason,
96
+ sendFarewell: true,
97
+ outcome: params.reason === "session-reset" ? "reset" : "deleted",
98
+ }, {
99
+ childSessionKey: params.targetSessionKey,
100
+ });
101
+ }
68
102
  async function ensureSessionRuntimeCleanup(params) {
69
103
  const queueKeys = new Set(params.target.storeKeys);
70
104
  queueKeys.add(params.target.canonicalKey);
@@ -161,7 +195,7 @@ export const sessionsHandlers = {
161
195
  }
162
196
  respond(true, { ok: true, key: resolved.key }, undefined);
163
197
  },
164
- "sessions.patch": async ({ params, respond, context }) => {
198
+ "sessions.patch": async ({ params, respond, context, client, isWebchatConnect }) => {
165
199
  if (!assertValidParams(params, validateSessionsPatchParams, "sessions.patch", respond)) {
166
200
  return;
167
201
  }
@@ -170,6 +204,9 @@ export const sessionsHandlers = {
170
204
  if (!key) {
171
205
  return;
172
206
  }
207
+ if (rejectWebchatSessionMutation({ action: "patch", client, isWebchatConnect, respond })) {
208
+ return;
209
+ }
173
210
  const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
174
211
  const applied = await updateSessionStore(storePath, async (store) => {
175
212
  const { primaryKey } = migrateAndPruneSessionStoreKey({ cfg, key, store });
@@ -211,6 +248,7 @@ export const sessionsHandlers = {
211
248
  }
212
249
  const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
213
250
  const { entry } = loadSessionEntry(key);
251
+ const hadExistingEntry = Boolean(entry);
214
252
  const commandReason = p.reason === "new" ? "new" : "reset";
215
253
  const hookEvent = createInternalHookEvent("command", commandReason, target.canonicalKey ?? key, {
216
254
  sessionEntry: entry,
@@ -267,9 +305,15 @@ export const sessionsHandlers = {
267
305
  agentId: target.agentId,
268
306
  reason: "reset",
269
307
  });
308
+ if (hadExistingEntry) {
309
+ await emitSessionUnboundLifecycleEvent({
310
+ targetSessionKey: target.canonicalKey ?? key,
311
+ reason: "session-reset",
312
+ });
313
+ }
270
314
  respond(true, { ok: true, key: target.canonicalKey, entry: next }, undefined);
271
315
  },
272
- "sessions.delete": async ({ params, respond }) => {
316
+ "sessions.delete": async ({ params, respond, client, isWebchatConnect }) => {
273
317
  if (!assertValidParams(params, validateSessionsDeleteParams, "sessions.delete", respond)) {
274
318
  return;
275
319
  }
@@ -278,6 +322,9 @@ export const sessionsHandlers = {
278
322
  if (!key) {
279
323
  return;
280
324
  }
325
+ if (rejectWebchatSessionMutation({ action: "delete", client, isWebchatConnect, respond })) {
326
+ return;
327
+ }
281
328
  const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
282
329
  const mainKey = resolveMainSessionKey(cfg);
283
330
  if (target.canonicalKey === mainKey) {
@@ -287,19 +334,20 @@ export const sessionsHandlers = {
287
334
  const deleteTranscript = typeof p.deleteTranscript === "boolean" ? p.deleteTranscript : true;
288
335
  const { entry } = loadSessionEntry(key);
289
336
  const sessionId = entry?.sessionId;
290
- const existed = Boolean(entry);
291
337
  const cleanupError = await ensureSessionRuntimeCleanup({ cfg, key, target, sessionId });
292
338
  if (cleanupError) {
293
339
  respond(false, undefined, cleanupError);
294
340
  return;
295
341
  }
296
- await updateSessionStore(storePath, (store) => {
342
+ const deleted = await updateSessionStore(storePath, (store) => {
297
343
  const { primaryKey } = migrateAndPruneSessionStoreKey({ cfg, key, store });
298
- if (store[primaryKey]) {
344
+ const hadEntry = Boolean(store[primaryKey]);
345
+ if (hadEntry) {
299
346
  delete store[primaryKey];
300
347
  }
348
+ return hadEntry;
301
349
  });
302
- const archived = deleteTranscript
350
+ const archived = deleted && deleteTranscript
303
351
  ? archiveSessionTranscriptsForSession({
304
352
  sessionId,
305
353
  storePath,
@@ -308,7 +356,15 @@ export const sessionsHandlers = {
308
356
  reason: "deleted",
309
357
  })
310
358
  : [];
311
- respond(true, { ok: true, key: target.canonicalKey, deleted: existed, archived }, undefined);
359
+ if (deleted) {
360
+ const emitLifecycleHooks = p.emitLifecycleHooks !== false;
361
+ await emitSessionUnboundLifecycleEvent({
362
+ targetSessionKey: target.canonicalKey ?? key,
363
+ reason: "session-delete",
364
+ emitHooks: emitLifecycleHooks,
365
+ });
366
+ }
367
+ respond(true, { ok: true, key: target.canonicalKey, deleted, archived }, undefined);
312
368
  },
313
369
  "sessions.compact": async ({ params, respond }) => {
314
370
  if (!assertValidParams(params, validateSessionsCompactParams, "sessions.compact", respond)) {
@@ -1,32 +1,122 @@
1
1
  import fs from "node:fs";
2
2
  import { loadConfig } from "../../config/config.js";
3
- import { resolveSessionFilePath } from "../../config/sessions/paths.js";
3
+ import { resolveSessionFilePath, resolveSessionFilePathOptions, } from "../../config/sessions/paths.js";
4
4
  import { loadProviderUsageSummary } from "../../infra/provider-usage.js";
5
5
  import { loadCostUsageSummary, loadSessionCostSummary, loadSessionUsageTimeSeries, discoverAllSessions, } from "../../infra/session-cost-usage.js";
6
6
  import { parseAgentSessionKey } from "../../routing/session-key.js";
7
+ import { buildUsageAggregateTail } from "../../shared/usage-aggregates.js";
7
8
  import { ErrorCodes, errorShape, formatValidationErrors, validateSessionsUsageParams, } from "../protocol/index.js";
8
9
  import { listAgentsForGateway, loadCombinedSessionStoreForGateway, loadSessionEntry, } from "../session-utils.js";
9
10
  const COST_USAGE_CACHE_TTL_MS = 30_000;
11
+ const DAY_MS = 24 * 60 * 60 * 1000;
10
12
  const costUsageCache = new Map();
13
+ function resolveSessionUsageFileOrRespond(key, respond) {
14
+ const config = loadConfig();
15
+ const { entry, storePath } = loadSessionEntry(key);
16
+ // For discovered sessions (not in store), try using key as sessionId directly
17
+ const parsed = parseAgentSessionKey(key);
18
+ const agentId = parsed?.agentId;
19
+ const rawSessionId = parsed?.rest ?? key;
20
+ const sessionId = entry?.sessionId ?? rawSessionId;
21
+ let sessionFile;
22
+ try {
23
+ const pathOpts = resolveSessionFilePathOptions({ storePath, agentId });
24
+ sessionFile = resolveSessionFilePath(sessionId, entry, pathOpts);
25
+ }
26
+ catch {
27
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `Invalid session key: ${key}`));
28
+ return null;
29
+ }
30
+ return { config, entry, agentId, sessionId, sessionFile };
31
+ }
32
+ const parseDateParts = (raw) => {
33
+ if (typeof raw !== "string" || !raw.trim()) {
34
+ return undefined;
35
+ }
36
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());
37
+ if (!match) {
38
+ return undefined;
39
+ }
40
+ const [, yearStr, monthStr, dayStr] = match;
41
+ const year = Number(yearStr);
42
+ const monthIndex = Number(monthStr) - 1;
43
+ const day = Number(dayStr);
44
+ if (!Number.isFinite(year) || !Number.isFinite(monthIndex) || !Number.isFinite(day)) {
45
+ return undefined;
46
+ }
47
+ return { year, monthIndex, day };
48
+ };
11
49
  /**
12
- * Parse a date string (YYYY-MM-DD) to start of day timestamp in UTC.
13
- * Returns undefined if invalid.
50
+ * Parse a UTC offset string in the format UTC+H, UTC-H, UTC+HH, UTC-HH, UTC+H:MM, UTC-HH:MM.
51
+ * Returns the UTC offset in minutes (east-positive), or undefined if invalid.
14
52
  */
15
- const parseDateToMs = (raw) => {
53
+ const parseUtcOffsetToMinutes = (raw) => {
16
54
  if (typeof raw !== "string" || !raw.trim()) {
17
55
  return undefined;
18
56
  }
19
- const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());
57
+ const match = /^UTC([+-])(\d{1,2})(?::([0-5]\d))?$/.exec(raw.trim());
20
58
  if (!match) {
21
59
  return undefined;
22
60
  }
23
- const [, year, month, day] = match;
24
- // Use UTC to ensure consistent behavior across timezones
25
- const ms = Date.UTC(parseInt(year), parseInt(month) - 1, parseInt(day));
26
- if (Number.isNaN(ms)) {
61
+ const sign = match[1] === "+" ? 1 : -1;
62
+ const hours = Number(match[2]);
63
+ const minutes = Number(match[3] ?? "0");
64
+ if (!Number.isInteger(hours) || !Number.isInteger(minutes)) {
65
+ return undefined;
66
+ }
67
+ if (hours > 14 || (hours === 14 && minutes !== 0)) {
68
+ return undefined;
69
+ }
70
+ const totalMinutes = sign * (hours * 60 + minutes);
71
+ if (totalMinutes < -12 * 60 || totalMinutes > 14 * 60) {
72
+ return undefined;
73
+ }
74
+ return totalMinutes;
75
+ };
76
+ const resolveDateInterpretation = (params) => {
77
+ if (params.mode === "gateway") {
78
+ return { mode: "gateway" };
79
+ }
80
+ if (params.mode === "specific") {
81
+ const utcOffsetMinutes = parseUtcOffsetToMinutes(params.utcOffset);
82
+ if (utcOffsetMinutes !== undefined) {
83
+ return { mode: "specific", utcOffsetMinutes };
84
+ }
85
+ }
86
+ // Backward compatibility: when mode is missing (or invalid), keep current UTC interpretation.
87
+ return { mode: "utc" };
88
+ };
89
+ /**
90
+ * Parse a date string (YYYY-MM-DD) to start-of-day timestamp based on interpretation mode.
91
+ * Returns undefined if invalid.
92
+ */
93
+ const parseDateToMs = (raw, interpretation = { mode: "utc" }) => {
94
+ const parts = parseDateParts(raw);
95
+ if (!parts) {
27
96
  return undefined;
28
97
  }
29
- return ms;
98
+ const { year, monthIndex, day } = parts;
99
+ if (interpretation.mode === "gateway") {
100
+ const ms = new Date(year, monthIndex, day).getTime();
101
+ return Number.isNaN(ms) ? undefined : ms;
102
+ }
103
+ if (interpretation.mode === "specific") {
104
+ const ms = Date.UTC(year, monthIndex, day) - interpretation.utcOffsetMinutes * 60 * 1000;
105
+ return Number.isNaN(ms) ? undefined : ms;
106
+ }
107
+ const ms = Date.UTC(year, monthIndex, day);
108
+ return Number.isNaN(ms) ? undefined : ms;
109
+ };
110
+ const getTodayStartMs = (now, interpretation) => {
111
+ if (interpretation.mode === "gateway") {
112
+ return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
113
+ }
114
+ if (interpretation.mode === "specific") {
115
+ const shifted = new Date(now.getTime() + interpretation.utcOffsetMinutes * 60 * 1000);
116
+ return (Date.UTC(shifted.getUTCFullYear(), shifted.getUTCMonth(), shifted.getUTCDate()) -
117
+ interpretation.utcOffsetMinutes * 60 * 1000);
118
+ }
119
+ return Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
30
120
  };
31
121
  const parseDays = (raw) => {
32
122
  if (typeof raw === "number" && Number.isFinite(raw)) {
@@ -46,25 +136,34 @@ const parseDays = (raw) => {
46
136
  */
47
137
  const parseDateRange = (params) => {
48
138
  const now = new Date();
49
- // Use UTC for consistent date handling
50
- const todayStartMs = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
51
- const todayEndMs = todayStartMs + 24 * 60 * 60 * 1000 - 1;
52
- const startMs = parseDateToMs(params.startDate);
53
- const endMs = parseDateToMs(params.endDate);
139
+ const interpretation = resolveDateInterpretation(params);
140
+ const todayStartMs = getTodayStartMs(now, interpretation);
141
+ const todayEndMs = todayStartMs + DAY_MS - 1;
142
+ const startMs = parseDateToMs(params.startDate, interpretation);
143
+ const endMs = parseDateToMs(params.endDate, interpretation);
54
144
  if (startMs !== undefined && endMs !== undefined) {
55
145
  // endMs should be end of day
56
- return { startMs, endMs: endMs + 24 * 60 * 60 * 1000 - 1 };
146
+ return { startMs, endMs: endMs + DAY_MS - 1 };
57
147
  }
58
148
  const days = parseDays(params.days);
59
149
  if (days !== undefined) {
60
150
  const clampedDays = Math.max(1, days);
61
- const start = todayStartMs - (clampedDays - 1) * 24 * 60 * 60 * 1000;
151
+ const start = todayStartMs - (clampedDays - 1) * DAY_MS;
62
152
  return { startMs: start, endMs: todayEndMs };
63
153
  }
64
154
  // Default to last 30 days
65
- const defaultStartMs = todayStartMs - 29 * 24 * 60 * 60 * 1000;
155
+ const defaultStartMs = todayStartMs - 29 * DAY_MS;
66
156
  return { startMs: defaultStartMs, endMs: todayEndMs };
67
157
  };
158
+ function buildStoreBySessionId(store) {
159
+ const storeBySessionId = new Map();
160
+ for (const [key, entry] of Object.entries(store)) {
161
+ if (entry?.sessionId) {
162
+ storeBySessionId.set(entry.sessionId, { key, entry });
163
+ }
164
+ }
165
+ return storeBySessionId;
166
+ }
68
167
  async function discoverAllSessionsForUsage(params) {
69
168
  const agents = listAgentsForGateway(params.config).agents;
70
169
  const results = await Promise.all(agents.map(async (agent) => {
@@ -122,7 +221,11 @@ async function loadCostUsageSummaryCached(params) {
122
221
  }
123
222
  // Exposed for unit tests (kept as a single export to avoid widening the public API surface).
124
223
  export const __test = {
224
+ parseDateParts,
225
+ parseUtcOffsetToMinutes,
226
+ resolveDateInterpretation,
125
227
  parseDateToMs,
228
+ getTodayStartMs,
126
229
  parseDays,
127
230
  parseDateRange,
128
231
  discoverAllSessionsForUsage,
@@ -140,6 +243,8 @@ export const usageHandlers = {
140
243
  startDate: params?.startDate,
141
244
  endDate: params?.endDate,
142
245
  days: params?.days,
246
+ mode: params?.mode,
247
+ utcOffset: params?.utcOffset,
143
248
  });
144
249
  const summary = await loadCostUsageSummaryCached({ startMs, endMs, config });
145
250
  respond(true, summary, undefined);
@@ -154,12 +259,14 @@ export const usageHandlers = {
154
259
  const { startMs, endMs } = parseDateRange({
155
260
  startDate: p.startDate,
156
261
  endDate: p.endDate,
262
+ mode: p.mode,
263
+ utcOffset: p.utcOffset,
157
264
  });
158
265
  const limit = typeof p.limit === "number" && Number.isFinite(p.limit) ? p.limit : 50;
159
266
  const includeContextWeight = p.includeContextWeight ?? false;
160
267
  const specificKey = typeof p.key === "string" ? p.key.trim() : null;
161
268
  // Load session store for named sessions
162
- const { store } = loadCombinedSessionStoreForGateway(config);
269
+ const { storePath, store } = loadCombinedSessionStoreForGateway(config);
163
270
  const now = Date.now();
164
271
  const mergedEntries = [];
165
272
  // Optimization: If a specific key is requested, skip full directory scan
@@ -169,12 +276,7 @@ export const usageHandlers = {
169
276
  const keyRest = parsed?.rest ?? specificKey;
170
277
  // Prefer the store entry when available, even if the caller provides a discovered key
171
278
  // (`agent:<id>:<sessionId>`) for a session that now has a canonical store key.
172
- const storeBySessionId = new Map();
173
- for (const [key, entry] of Object.entries(store)) {
174
- if (entry?.sessionId) {
175
- storeBySessionId.set(entry.sessionId, { key, entry });
176
- }
177
- }
279
+ const storeBySessionId = buildStoreBySessionId(store);
178
280
  const storeMatch = store[specificKey]
179
281
  ? { key: specificKey, entry: store[specificKey] }
180
282
  : null;
@@ -183,9 +285,18 @@ export const usageHandlers = {
183
285
  const storeEntry = storeMatch?.entry ?? storeByIdMatch?.entry;
184
286
  const sessionId = storeEntry?.sessionId ?? keyRest;
185
287
  // Resolve the session file path
186
- const sessionFile = resolveSessionFilePath(sessionId, storeEntry, {
187
- agentId: agentIdFromKey,
188
- });
288
+ let sessionFile;
289
+ try {
290
+ const pathOpts = resolveSessionFilePathOptions({
291
+ storePath: storePath !== "(multiple)" ? storePath : undefined,
292
+ agentId: agentIdFromKey,
293
+ });
294
+ sessionFile = resolveSessionFilePath(sessionId, storeEntry, pathOpts);
295
+ }
296
+ catch {
297
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `Invalid session reference: ${specificKey}`));
298
+ return;
299
+ }
189
300
  try {
190
301
  const stats = fs.statSync(sessionFile);
191
302
  if (stats.isFile()) {
@@ -211,12 +322,7 @@ export const usageHandlers = {
211
322
  endMs,
212
323
  });
213
324
  // Build a map of sessionId -> store entry for quick lookup
214
- const storeBySessionId = new Map();
215
- for (const [key, entry] of Object.entries(store)) {
216
- if (entry?.sessionId) {
217
- storeBySessionId.set(entry.sessionId, { key, entry });
218
- }
219
- }
325
+ const storeBySessionId = buildStoreBySessionId(store);
220
326
  for (const discovered of discoveredSessions) {
221
327
  const storeMatch = storeBySessionId.get(discovered.sessionId);
222
328
  if (storeMatch) {
@@ -312,11 +418,13 @@ export const usageHandlers = {
312
418
  target.missingCostEntries += source.missingCostEntries;
313
419
  };
314
420
  for (const merged of limitedEntries) {
421
+ const agentId = parseAgentSessionKey(merged.key)?.agentId;
315
422
  const usage = await loadSessionCostSummary({
316
423
  sessionId: merged.sessionId,
317
424
  sessionEntry: merged.storeEntry,
318
425
  sessionFile: merged.sessionFile,
319
426
  config,
427
+ agentId,
320
428
  startMs,
321
429
  endMs,
322
430
  });
@@ -333,7 +441,6 @@ export const usageHandlers = {
333
441
  aggregateTotals.cacheWriteCost += usage.cacheWriteCost;
334
442
  aggregateTotals.missingCostEntries += usage.missingCostEntries;
335
443
  }
336
- const agentId = parseAgentSessionKey(merged.key)?.agentId;
337
444
  const channel = merged.storeEntry?.channel ?? merged.storeEntry?.origin?.provider;
338
445
  const chatType = merged.storeEntry?.chatType ?? merged.storeEntry?.origin?.chatType;
339
446
  if (usage) {
@@ -488,6 +595,13 @@ export const usageHandlers = {
488
595
  const d = new Date(ms);
489
596
  return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(d.getUTCDate()).padStart(2, "0")}`;
490
597
  };
598
+ const tail = buildUsageAggregateTail({
599
+ byChannelMap: byChannelMap,
600
+ latencyTotals,
601
+ dailyLatencyMap,
602
+ modelDailyMap,
603
+ dailyMap: dailyAggregateMap,
604
+ });
491
605
  const aggregates = {
492
606
  messages: aggregateMessages,
493
607
  tools: {
@@ -514,30 +628,7 @@ export const usageHandlers = {
514
628
  byAgent: Array.from(byAgentMap.entries())
515
629
  .map(([id, totals]) => ({ agentId: id, totals }))
516
630
  .toSorted((a, b) => b.totals.totalCost - a.totals.totalCost),
517
- byChannel: Array.from(byChannelMap.entries())
518
- .map(([name, totals]) => ({ channel: name, totals }))
519
- .toSorted((a, b) => b.totals.totalCost - a.totals.totalCost),
520
- latency: latencyTotals.count > 0
521
- ? {
522
- count: latencyTotals.count,
523
- avgMs: latencyTotals.sum / latencyTotals.count,
524
- minMs: latencyTotals.min === Number.POSITIVE_INFINITY ? 0 : latencyTotals.min,
525
- maxMs: latencyTotals.max,
526
- p95Ms: latencyTotals.p95Max,
527
- }
528
- : undefined,
529
- dailyLatency: Array.from(dailyLatencyMap.values())
530
- .map((entry) => ({
531
- date: entry.date,
532
- count: entry.count,
533
- avgMs: entry.count ? entry.sum / entry.count : 0,
534
- minMs: entry.min === Number.POSITIVE_INFINITY ? 0 : entry.min,
535
- maxMs: entry.max,
536
- p95Ms: entry.p95Max,
537
- }))
538
- .toSorted((a, b) => a.date.localeCompare(b.date)),
539
- modelDaily: Array.from(modelDailyMap.values()).toSorted((a, b) => a.date.localeCompare(b.date) || b.cost - a.cost),
540
- daily: Array.from(dailyAggregateMap.values()).toSorted((a, b) => a.date.localeCompare(b.date)),
631
+ ...tail,
541
632
  };
542
633
  const result = {
543
634
  updatedAt: now,
@@ -555,19 +646,17 @@ export const usageHandlers = {
555
646
  respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key is required for timeseries"));
556
647
  return;
557
648
  }
558
- const config = loadConfig();
559
- const { entry } = loadSessionEntry(key);
560
- // For discovered sessions (not in store), try using key as sessionId directly
561
- const parsed = parseAgentSessionKey(key);
562
- const agentId = parsed?.agentId;
563
- const rawSessionId = parsed?.rest ?? key;
564
- const sessionId = entry?.sessionId ?? rawSessionId;
565
- const sessionFile = entry?.sessionFile ?? resolveSessionFilePath(rawSessionId, entry, { agentId });
649
+ const resolved = resolveSessionUsageFileOrRespond(key, respond);
650
+ if (!resolved) {
651
+ return;
652
+ }
653
+ const { config, entry, agentId, sessionId, sessionFile } = resolved;
566
654
  const timeseries = await loadSessionUsageTimeSeries({
567
655
  sessionId,
568
656
  sessionEntry: entry,
569
657
  sessionFile,
570
658
  config,
659
+ agentId,
571
660
  maxPoints: 200,
572
661
  });
573
662
  if (!timeseries) {
@@ -585,20 +674,18 @@ export const usageHandlers = {
585
674
  const limit = typeof params?.limit === "number" && Number.isFinite(params.limit)
586
675
  ? Math.min(params.limit, 1000)
587
676
  : 200;
588
- const config = loadConfig();
589
- const { entry } = loadSessionEntry(key);
590
- // For discovered sessions (not in store), try using key as sessionId directly
591
- const parsed = parseAgentSessionKey(key);
592
- const agentId = parsed?.agentId;
593
- const rawSessionId = parsed?.rest ?? key;
594
- const sessionId = entry?.sessionId ?? rawSessionId;
595
- const sessionFile = entry?.sessionFile ?? resolveSessionFilePath(rawSessionId, entry, { agentId });
677
+ const resolved = resolveSessionUsageFileOrRespond(key, respond);
678
+ if (!resolved) {
679
+ return;
680
+ }
681
+ const { config, entry, agentId, sessionId, sessionFile } = resolved;
596
682
  const { loadSessionLogs } = await import("../../infra/session-cost-usage.js");
597
683
  const logs = await loadSessionLogs({
598
684
  sessionId,
599
685
  sessionEntry: entry,
600
686
  sessionFile,
601
687
  config,
688
+ agentId,
602
689
  limit,
603
690
  });
604
691
  respond(true, { logs: logs ?? [] }, undefined);
@@ -8,6 +8,7 @@ import { updateSessionStore } from "../config/sessions.js";
8
8
  import { requestHeartbeatNow } from "../infra/heartbeat-wake.js";
9
9
  import { deliverOutboundPayloads } from "../infra/outbound/deliver.js";
10
10
  import { resolveOutboundTarget } from "../infra/outbound/targets.js";
11
+ import { registerApnsToken } from "../infra/push-apns.js";
11
12
  import { enqueueSystemEvent } from "../infra/system-events.js";
12
13
  import { normalizeMainKey } from "../routing/session-key.js";
13
14
  import { defaultRuntime } from "../runtime.js";
@@ -410,6 +411,34 @@ export const handleNodeEvent = async (ctx, nodeId, evt) => {
410
411
  requestHeartbeatNow({ reason: "exec-event" });
411
412
  return;
412
413
  }
414
+ case "push.apns.register": {
415
+ if (!evt.payloadJSON) {
416
+ return;
417
+ }
418
+ let payload;
419
+ try {
420
+ payload = JSON.parse(evt.payloadJSON);
421
+ }
422
+ catch {
423
+ return;
424
+ }
425
+ const obj = typeof payload === "object" && payload !== null ? payload : {};
426
+ const token = typeof obj.token === "string" ? obj.token : "";
427
+ const topic = typeof obj.topic === "string" ? obj.topic : "";
428
+ const environment = obj.environment;
429
+ try {
430
+ await registerApnsToken({
431
+ nodeId,
432
+ token,
433
+ topic,
434
+ environment,
435
+ });
436
+ }
437
+ catch (err) {
438
+ ctx.logGateway.warn(`push apns register failed node=${nodeId}: ${formatForLog(err)}`);
439
+ }
440
+ return;
441
+ }
413
442
  default:
414
443
  return;
415
444
  }
@@ -1,11 +1,27 @@
1
1
  import { assertGatewayAuthConfigured, resolveGatewayAuth, } from "./auth.js";
2
2
  import { normalizeControlUiBasePath } from "./control-ui-shared.js";
3
3
  import { resolveHooksConfig } from "./hooks.js";
4
- import { isLoopbackHost, resolveGatewayBindHost } from "./net.js";
4
+ import { isLoopbackHost, isTrustedProxyAddress, isValidIPv4, resolveGatewayBindHost, } from "./net.js";
5
+ import { mergeGatewayTailscaleConfig } from "./startup-auth.js";
5
6
  export async function resolveGatewayRuntimeConfig(params) {
6
7
  const bindMode = params.bind ?? params.cfg.gateway?.bind ?? "loopback";
7
8
  const customBindHost = params.cfg.gateway?.customBindHost;
8
9
  const bindHost = params.host ?? (await resolveGatewayBindHost(bindMode, customBindHost));
10
+ if (bindMode === "loopback" && !isLoopbackHost(bindHost)) {
11
+ throw new Error(`gateway bind=loopback resolved to non-loopback host ${bindHost}; refusing fallback to a network bind`);
12
+ }
13
+ if (bindMode === "custom") {
14
+ const configuredCustomBindHost = customBindHost?.trim();
15
+ if (!configuredCustomBindHost) {
16
+ throw new Error("gateway.bind=custom requires gateway.customBindHost");
17
+ }
18
+ if (!isValidIPv4(configuredCustomBindHost)) {
19
+ throw new Error(`gateway.bind=custom requires a valid IPv4 customBindHost (got ${configuredCustomBindHost})`);
20
+ }
21
+ if (bindHost !== configuredCustomBindHost) {
22
+ throw new Error(`gateway bind=custom requested ${configuredCustomBindHost} but resolved ${bindHost}; refusing fallback`);
23
+ }
24
+ }
9
25
  const controlUiEnabled = params.controlUiEnabled ?? params.cfg.gateway?.controlUi?.enabled ?? true;
10
26
  const openAiChatCompletionsEnabled = params.openAiChatCompletionsEnabled ??
11
27
  params.cfg.gateway?.http?.endpoints?.chatCompletions?.enabled ??
@@ -17,21 +33,13 @@ export async function resolveGatewayRuntimeConfig(params) {
17
33
  const controlUiRoot = typeof controlUiRootRaw === "string" && controlUiRootRaw.trim().length > 0
18
34
  ? controlUiRootRaw.trim()
19
35
  : undefined;
20
- const authBase = params.cfg.gateway?.auth ?? {};
21
- const authOverrides = params.auth ?? {};
22
- const authConfig = {
23
- ...authBase,
24
- ...authOverrides,
25
- };
26
36
  const tailscaleBase = params.cfg.gateway?.tailscale ?? {};
27
37
  const tailscaleOverrides = params.tailscale ?? {};
28
- const tailscaleConfig = {
29
- ...tailscaleBase,
30
- ...tailscaleOverrides,
31
- };
38
+ const tailscaleConfig = mergeGatewayTailscaleConfig(tailscaleBase, tailscaleOverrides);
32
39
  const tailscaleMode = tailscaleConfig.mode ?? "off";
33
40
  const resolvedAuth = resolveGatewayAuth({
34
- authConfig,
41
+ authConfig: params.cfg.gateway?.auth,
42
+ authOverride: params.auth,
35
43
  env: process.env,
36
44
  tailscaleMode,
37
45
  });
@@ -41,6 +49,7 @@ export async function resolveGatewayRuntimeConfig(params) {
41
49
  const hasSharedSecret = (authMode === "token" && hasToken) || (authMode === "password" && hasPassword);
42
50
  const hooksConfig = resolveHooksConfig(params.cfg);
43
51
  const canvasHostEnabled = process.env.CLAWDBOT_SKIP_CANVAS_HOST !== "1" && params.cfg.canvasHost?.enabled !== false;
52
+ const trustedProxies = params.cfg.gateway?.trustedProxies ?? [];
44
53
  assertGatewayAuthConfigured(resolvedAuth);
45
54
  if (tailscaleMode === "funnel" && authMode !== "password") {
46
55
  throw new Error("tailscale funnel requires gateway auth mode=password (set gateway.auth.password or POOLBOT_GATEWAY_PASSWORD)");
@@ -48,9 +57,21 @@ export async function resolveGatewayRuntimeConfig(params) {
48
57
  if (tailscaleMode !== "off" && !isLoopbackHost(bindHost)) {
49
58
  throw new Error("tailscale serve/funnel requires gateway bind=loopback (127.0.0.1)");
50
59
  }
51
- if (!isLoopbackHost(bindHost) && !hasSharedSecret) {
60
+ if (!isLoopbackHost(bindHost) && !hasSharedSecret && authMode !== "trusted-proxy") {
52
61
  throw new Error(`refusing to bind gateway to ${bindHost}:${params.port} without auth (set gateway.auth.token/password, or set POOLBOT_GATEWAY_TOKEN/POOLBOT_GATEWAY_PASSWORD)`);
53
62
  }
63
+ if (authMode === "trusted-proxy") {
64
+ if (trustedProxies.length === 0) {
65
+ throw new Error("gateway auth mode=trusted-proxy requires gateway.trustedProxies to be configured with at least one proxy IP");
66
+ }
67
+ if (isLoopbackHost(bindHost)) {
68
+ const hasLoopbackTrustedProxy = isTrustedProxyAddress("127.0.0.1", trustedProxies) ||
69
+ isTrustedProxyAddress("::1", trustedProxies);
70
+ if (!hasLoopbackTrustedProxy) {
71
+ throw new Error("gateway auth mode=trusted-proxy with bind=loopback requires gateway.trustedProxies to include 127.0.0.1, ::1, or a loopback CIDR");
72
+ }
73
+ }
74
+ }
54
75
  return {
55
76
  bindHost,
56
77
  controlUiEnabled,