@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,15 +1,109 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { resolveGatewayLaunchAgentLabel, resolveGatewaySystemdServiceName, } from "../daemon/constants.js";
3
+ import { createSubsystemLogger } from "../logging/subsystem.js";
3
4
  const SPAWN_TIMEOUT_MS = 2000;
4
5
  const SIGUSR1_AUTH_GRACE_MS = 5000;
6
+ const DEFAULT_DEFERRAL_POLL_MS = 500;
7
+ const DEFAULT_DEFERRAL_MAX_WAIT_MS = 30_000;
8
+ const RESTART_COOLDOWN_MS = 30_000;
9
+ const restartLog = createSubsystemLogger("restart");
5
10
  let sigusr1AuthorizedCount = 0;
6
11
  let sigusr1AuthorizedUntil = 0;
7
12
  let sigusr1ExternalAllowed = false;
13
+ let preRestartCheck = null;
14
+ let restartCycleToken = 0;
15
+ let emittedRestartToken = 0;
16
+ let consumedRestartToken = 0;
17
+ let lastRestartEmittedAt = 0;
18
+ let pendingRestartTimer = null;
19
+ let pendingRestartDueAt = 0;
20
+ let pendingRestartReason;
21
+ function hasUnconsumedRestartSignal() {
22
+ return emittedRestartToken > consumedRestartToken;
23
+ }
24
+ function clearPendingScheduledRestart() {
25
+ if (pendingRestartTimer) {
26
+ clearTimeout(pendingRestartTimer);
27
+ }
28
+ pendingRestartTimer = null;
29
+ pendingRestartDueAt = 0;
30
+ pendingRestartReason = undefined;
31
+ }
32
+ function summarizeChangedPaths(paths, maxPaths = 6) {
33
+ if (!Array.isArray(paths) || paths.length === 0) {
34
+ return null;
35
+ }
36
+ if (paths.length <= maxPaths) {
37
+ return paths.join(",");
38
+ }
39
+ const head = paths.slice(0, maxPaths).join(",");
40
+ return `${head},+${paths.length - maxPaths} more`;
41
+ }
42
+ function formatRestartAudit(audit) {
43
+ const actor = typeof audit?.actor === "string" && audit.actor.trim() ? audit.actor.trim() : null;
44
+ const deviceId = typeof audit?.deviceId === "string" && audit.deviceId.trim() ? audit.deviceId.trim() : null;
45
+ const clientIp = typeof audit?.clientIp === "string" && audit.clientIp.trim() ? audit.clientIp.trim() : null;
46
+ const changed = summarizeChangedPaths(audit?.changedPaths);
47
+ const fields = [];
48
+ if (actor) {
49
+ fields.push(`actor=${actor}`);
50
+ }
51
+ if (deviceId) {
52
+ fields.push(`device=${deviceId}`);
53
+ }
54
+ if (clientIp) {
55
+ fields.push(`ip=${clientIp}`);
56
+ }
57
+ if (changed) {
58
+ fields.push(`changedPaths=${changed}`);
59
+ }
60
+ return fields.length > 0 ? fields.join(" ") : "actor=<unknown>";
61
+ }
62
+ /**
63
+ * Register a callback that scheduleGatewaySigusr1Restart checks before emitting SIGUSR1.
64
+ * The callback should return the number of pending items (0 = safe to restart).
65
+ */
66
+ export function setPreRestartDeferralCheck(fn) {
67
+ preRestartCheck = fn;
68
+ }
69
+ /**
70
+ * Emit an authorized SIGUSR1 gateway restart, guarded against duplicate emissions.
71
+ * Returns true if SIGUSR1 was emitted, false if a restart was already emitted.
72
+ * Both scheduleGatewaySigusr1Restart and the config watcher should use this
73
+ * to ensure only one restart fires.
74
+ */
75
+ export function emitGatewayRestart() {
76
+ if (hasUnconsumedRestartSignal()) {
77
+ clearPendingScheduledRestart();
78
+ return false;
79
+ }
80
+ clearPendingScheduledRestart();
81
+ const cycleToken = ++restartCycleToken;
82
+ emittedRestartToken = cycleToken;
83
+ authorizeGatewaySigusr1Restart();
84
+ try {
85
+ if (process.listenerCount("SIGUSR1") > 0) {
86
+ process.emit("SIGUSR1");
87
+ }
88
+ else {
89
+ process.kill(process.pid, "SIGUSR1");
90
+ }
91
+ }
92
+ catch {
93
+ // Roll back the cycle marker so future restart requests can still proceed.
94
+ emittedRestartToken = consumedRestartToken;
95
+ return false;
96
+ }
97
+ lastRestartEmittedAt = Date.now();
98
+ return true;
99
+ }
8
100
  function resetSigusr1AuthorizationIfExpired(now = Date.now()) {
9
- if (sigusr1AuthorizedCount <= 0)
101
+ if (sigusr1AuthorizedCount <= 0) {
10
102
  return;
11
- if (now <= sigusr1AuthorizedUntil)
103
+ }
104
+ if (now <= sigusr1AuthorizedUntil) {
12
105
  return;
106
+ }
13
107
  sigusr1AuthorizedCount = 0;
14
108
  sigusr1AuthorizedUntil = 0;
15
109
  }
@@ -19,7 +113,7 @@ export function setGatewaySigusr1RestartPolicy(opts) {
19
113
  export function isGatewaySigusr1RestartExternallyAllowed() {
20
114
  return sigusr1ExternalAllowed;
21
115
  }
22
- export function authorizeGatewaySigusr1Restart(delayMs = 0) {
116
+ function authorizeGatewaySigusr1Restart(delayMs = 0) {
23
117
  const delay = Math.max(0, Math.floor(delayMs));
24
118
  const expiresAt = Date.now() + delay + SIGUSR1_AUTH_GRACE_MS;
25
119
  sigusr1AuthorizedCount += 1;
@@ -29,24 +123,87 @@ export function authorizeGatewaySigusr1Restart(delayMs = 0) {
29
123
  }
30
124
  export function consumeGatewaySigusr1RestartAuthorization() {
31
125
  resetSigusr1AuthorizationIfExpired();
32
- if (sigusr1AuthorizedCount <= 0)
126
+ if (sigusr1AuthorizedCount <= 0) {
33
127
  return false;
128
+ }
34
129
  sigusr1AuthorizedCount -= 1;
35
130
  if (sigusr1AuthorizedCount <= 0) {
36
131
  sigusr1AuthorizedUntil = 0;
37
132
  }
38
133
  return true;
39
134
  }
135
+ /**
136
+ * Mark the currently emitted SIGUSR1 restart cycle as consumed by the run loop.
137
+ * This explicitly advances the cycle state instead of resetting emit guards inside
138
+ * consumeGatewaySigusr1RestartAuthorization().
139
+ */
140
+ export function markGatewaySigusr1RestartHandled() {
141
+ if (hasUnconsumedRestartSignal()) {
142
+ consumedRestartToken = emittedRestartToken;
143
+ }
144
+ }
145
+ /**
146
+ * Poll pending work until it drains (or times out), then emit one restart signal.
147
+ * Shared by both the direct RPC restart path and the config watcher path.
148
+ */
149
+ export function deferGatewayRestartUntilIdle(opts) {
150
+ const pollMsRaw = opts.pollMs ?? DEFAULT_DEFERRAL_POLL_MS;
151
+ const pollMs = Math.max(10, Math.floor(pollMsRaw));
152
+ const maxWaitMsRaw = opts.maxWaitMs ?? DEFAULT_DEFERRAL_MAX_WAIT_MS;
153
+ const maxWaitMs = Math.max(pollMs, Math.floor(maxWaitMsRaw));
154
+ let pending;
155
+ try {
156
+ pending = opts.getPendingCount();
157
+ }
158
+ catch (err) {
159
+ opts.hooks?.onCheckError?.(err);
160
+ emitGatewayRestart();
161
+ return;
162
+ }
163
+ if (pending <= 0) {
164
+ opts.hooks?.onReady?.();
165
+ emitGatewayRestart();
166
+ return;
167
+ }
168
+ opts.hooks?.onDeferring?.(pending);
169
+ const startedAt = Date.now();
170
+ const poll = setInterval(() => {
171
+ let current;
172
+ try {
173
+ current = opts.getPendingCount();
174
+ }
175
+ catch (err) {
176
+ clearInterval(poll);
177
+ opts.hooks?.onCheckError?.(err);
178
+ emitGatewayRestart();
179
+ return;
180
+ }
181
+ if (current <= 0) {
182
+ clearInterval(poll);
183
+ opts.hooks?.onReady?.();
184
+ emitGatewayRestart();
185
+ return;
186
+ }
187
+ const elapsedMs = Date.now() - startedAt;
188
+ if (elapsedMs >= maxWaitMs) {
189
+ clearInterval(poll);
190
+ opts.hooks?.onTimeout?.(current, elapsedMs);
191
+ emitGatewayRestart();
192
+ }
193
+ }, pollMs);
194
+ }
40
195
  function formatSpawnDetail(result) {
41
196
  const clean = (value) => {
42
197
  const text = typeof value === "string" ? value : value ? value.toString() : "";
43
198
  return text.replace(/\s+/g, " ").trim();
44
199
  };
45
200
  if (result.error) {
46
- if (result.error instanceof Error)
201
+ if (result.error instanceof Error) {
47
202
  return result.error.message;
48
- if (typeof result.error === "string")
203
+ }
204
+ if (typeof result.error === "string") {
49
205
  return result.error;
206
+ }
50
207
  try {
51
208
  return JSON.stringify(result.error);
52
209
  }
@@ -55,13 +212,16 @@ function formatSpawnDetail(result) {
55
212
  }
56
213
  }
57
214
  const stderr = clean(result.stderr);
58
- if (stderr)
215
+ if (stderr) {
59
216
  return stderr;
217
+ }
60
218
  const stdout = clean(result.stdout);
61
- if (stdout)
219
+ if (stdout) {
62
220
  return stdout;
63
- if (typeof result.status === "number")
221
+ }
222
+ if (typeof result.status === "number") {
64
223
  return `exit ${result.status}`;
224
+ }
65
225
  return "unknown error";
66
226
  }
67
227
  function normalizeSystemdUnit(raw, profile) {
@@ -138,29 +298,66 @@ export function scheduleGatewaySigusr1Restart(opts) {
138
298
  const reason = typeof opts?.reason === "string" && opts.reason.trim()
139
299
  ? opts.reason.trim().slice(0, 200)
140
300
  : undefined;
141
- authorizeGatewaySigusr1Restart(delayMs);
142
- const pid = process.pid;
143
- const hasListener = process.listenerCount("SIGUSR1") > 0;
144
- setTimeout(() => {
145
- try {
146
- if (hasListener) {
147
- process.emit("SIGUSR1");
148
- }
149
- else {
150
- process.kill(pid, "SIGUSR1");
151
- }
301
+ const mode = process.listenerCount("SIGUSR1") > 0 ? "emit" : "signal";
302
+ const nowMs = Date.now();
303
+ const cooldownMsApplied = Math.max(0, lastRestartEmittedAt + RESTART_COOLDOWN_MS - nowMs);
304
+ const requestedDueAt = nowMs + delayMs + cooldownMsApplied;
305
+ if (hasUnconsumedRestartSignal()) {
306
+ restartLog.warn(`restart request coalesced (already in-flight) reason=${reason ?? "unspecified"} ${formatRestartAudit(opts?.audit)}`);
307
+ return {
308
+ ok: true,
309
+ pid: process.pid,
310
+ signal: "SIGUSR1",
311
+ delayMs: 0,
312
+ reason,
313
+ mode,
314
+ coalesced: true,
315
+ cooldownMsApplied,
316
+ };
317
+ }
318
+ if (pendingRestartTimer) {
319
+ const remainingMs = Math.max(0, pendingRestartDueAt - nowMs);
320
+ const shouldPullEarlier = requestedDueAt < pendingRestartDueAt;
321
+ if (shouldPullEarlier) {
322
+ restartLog.warn(`restart request rescheduled earlier reason=${reason ?? "unspecified"} pendingReason=${pendingRestartReason ?? "unspecified"} oldDelayMs=${remainingMs} newDelayMs=${Math.max(0, requestedDueAt - nowMs)} ${formatRestartAudit(opts?.audit)}`);
323
+ clearPendingScheduledRestart();
152
324
  }
153
- catch {
154
- /* ignore */
325
+ else {
326
+ restartLog.warn(`restart request coalesced (already scheduled) reason=${reason ?? "unspecified"} pendingReason=${pendingRestartReason ?? "unspecified"} delayMs=${remainingMs} ${formatRestartAudit(opts?.audit)}`);
327
+ return {
328
+ ok: true,
329
+ pid: process.pid,
330
+ signal: "SIGUSR1",
331
+ delayMs: remainingMs,
332
+ reason,
333
+ mode,
334
+ coalesced: true,
335
+ cooldownMsApplied,
336
+ };
337
+ }
338
+ }
339
+ pendingRestartDueAt = requestedDueAt;
340
+ pendingRestartReason = reason;
341
+ pendingRestartTimer = setTimeout(() => {
342
+ pendingRestartTimer = null;
343
+ pendingRestartDueAt = 0;
344
+ pendingRestartReason = undefined;
345
+ const pendingCheck = preRestartCheck;
346
+ if (!pendingCheck) {
347
+ emitGatewayRestart();
348
+ return;
155
349
  }
156
- }, delayMs);
350
+ deferGatewayRestartUntilIdle({ getPendingCount: pendingCheck });
351
+ }, Math.max(0, requestedDueAt - nowMs));
157
352
  return {
158
353
  ok: true,
159
- pid,
354
+ pid: process.pid,
160
355
  signal: "SIGUSR1",
161
- delayMs,
356
+ delayMs: Math.max(0, requestedDueAt - nowMs),
162
357
  reason,
163
- mode: hasListener ? "emit" : "signal",
358
+ mode,
359
+ coalesced: false,
360
+ cooldownMsApplied,
164
361
  };
165
362
  }
166
363
  export const __testing = {
@@ -168,5 +365,11 @@ export const __testing = {
168
365
  sigusr1AuthorizedCount = 0;
169
366
  sigusr1AuthorizedUntil = 0;
170
367
  sigusr1ExternalAllowed = false;
368
+ preRestartCheck = null;
369
+ restartCycleToken = 0;
370
+ emittedRestartToken = 0;
371
+ consumedRestartToken = 0;
372
+ lastRestartEmittedAt = 0;
373
+ clearPendingScheduledRestart();
171
374
  },
172
375
  };
@@ -1,4 +1,5 @@
1
1
  import { RateLimitError } from "@buape/carbon";
2
+ import { createSubsystemLogger } from "../logging/subsystem.js";
2
3
  import { formatErrorMessage } from "./errors.js";
3
4
  import { resolveRetryConfig, retryAsync } from "./retry.js";
4
5
  export const DISCORD_RETRY_DEFAULTS = {
@@ -13,6 +14,7 @@ export const TELEGRAM_RETRY_DEFAULTS = {
13
14
  maxDelayMs: 30_000,
14
15
  jitter: 0.1,
15
16
  };
17
+ const log = createSubsystemLogger("retry-policy");
16
18
  const TELEGRAM_RETRY_RE = /429|timeout|connect|reset|closed|unavailable|temporarily/i;
17
19
  function getTelegramRetryAfterMs(err) {
18
20
  if (!err || typeof err !== "object")
@@ -43,7 +45,7 @@ export function createDiscordRetryRunner(params) {
43
45
  ? (info) => {
44
46
  const labelText = info.label ?? "request";
45
47
  const maxRetries = Math.max(1, info.maxAttempts - 1);
46
- console.warn(`discord ${labelText} rate limited, retry ${info.attempt}/${maxRetries} in ${info.delayMs}ms`);
48
+ log.warn(`discord ${labelText} rate limited, retry ${info.attempt}/${maxRetries} in ${info.delayMs}ms`);
47
49
  }
48
50
  : undefined,
49
51
  });
@@ -64,7 +66,7 @@ export function createTelegramRetryRunner(params) {
64
66
  onRetry: params.verbose
65
67
  ? (info) => {
66
68
  const maxRetries = Math.max(1, info.maxAttempts - 1);
67
- console.warn(`telegram send retry ${info.attempt}/${maxRetries} for ${info.label ?? label ?? "request"} in ${info.delayMs}ms: ${formatErrorMessage(info.err)}`);
69
+ log.warn(`telegram send retry ${info.attempt}/${maxRetries} for ${info.label ?? label ?? "request"} in ${info.delayMs}ms: ${formatErrorMessage(info.err)}`);
68
70
  }
69
71
  : undefined,
70
72
  });
@@ -1,15 +1,16 @@
1
+ import { sleep } from "../utils.js";
1
2
  const DEFAULT_RETRY_CONFIG = {
2
3
  attempts: 3,
3
4
  minDelayMs: 300,
4
5
  maxDelayMs: 30_000,
5
6
  jitter: 0,
6
7
  };
7
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
8
8
  const asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
9
9
  const clampNumber = (value, fallback, min, max) => {
10
10
  const next = asFiniteNumber(value);
11
- if (next === undefined)
11
+ if (next === undefined) {
12
12
  return fallback;
13
+ }
13
14
  const floor = typeof min === "number" ? min : Number.NEGATIVE_INFINITY;
14
15
  const ceiling = typeof max === "number" ? max : Number.POSITIVE_INFINITY;
15
16
  return Math.min(Math.max(next, floor), ceiling);
@@ -22,8 +23,9 @@ export function resolveRetryConfig(defaults = DEFAULT_RETRY_CONFIG, overrides) {
22
23
  return { attempts, minDelayMs, maxDelayMs, jitter };
23
24
  }
24
25
  function applyJitter(delayMs, jitter) {
25
- if (jitter <= 0)
26
+ if (jitter <= 0) {
26
27
  return delayMs;
28
+ }
27
29
  const offset = (Math.random() * 2 - 1) * jitter;
28
30
  return Math.max(0, Math.round(delayMs * (1 + offset)));
29
31
  }
@@ -37,8 +39,9 @@ export async function retryAsync(fn, attemptsOrOptions = 3, initialDelayMs = 300
37
39
  }
38
40
  catch (err) {
39
41
  lastErr = err;
40
- if (i === attempts - 1)
42
+ if (i === attempts - 1) {
41
43
  break;
44
+ }
42
45
  const delay = initialDelayMs * 2 ** i;
43
46
  await sleep(delay);
44
47
  }
@@ -61,8 +64,9 @@ export async function retryAsync(fn, attemptsOrOptions = 3, initialDelayMs = 300
61
64
  }
62
65
  catch (err) {
63
66
  lastErr = err;
64
- if (attempt >= maxAttempts || !shouldRetry(err, attempt))
67
+ if (attempt >= maxAttempts || !shouldRetry(err, attempt)) {
65
68
  break;
69
+ }
66
70
  const retryAfterMs = options.retryAfterMs?.(err);
67
71
  const hasRetryAfter = typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs);
68
72
  const baseDelay = hasRetryAfter
@@ -0,0 +1,54 @@
1
+ const SSH_TOKEN = /^[A-Za-z0-9._-]+$/;
2
+ const BRACKETED_IPV6 = /^\[[0-9A-Fa-f:.%]+\]$/;
3
+ const WHITESPACE = /\s/;
4
+ function hasControlOrWhitespace(value) {
5
+ for (const char of value) {
6
+ const code = char.charCodeAt(0);
7
+ if (code <= 0x1f || code === 0x7f || WHITESPACE.test(char)) {
8
+ return true;
9
+ }
10
+ }
11
+ return false;
12
+ }
13
+ export function normalizeScpRemoteHost(value) {
14
+ if (typeof value !== "string") {
15
+ return undefined;
16
+ }
17
+ const trimmed = value.trim();
18
+ if (!trimmed) {
19
+ return undefined;
20
+ }
21
+ if (hasControlOrWhitespace(trimmed)) {
22
+ return undefined;
23
+ }
24
+ if (trimmed.startsWith("-") || trimmed.includes("/") || trimmed.includes("\\")) {
25
+ return undefined;
26
+ }
27
+ const firstAt = trimmed.indexOf("@");
28
+ const lastAt = trimmed.lastIndexOf("@");
29
+ let user;
30
+ let host = trimmed;
31
+ if (firstAt !== -1) {
32
+ if (firstAt !== lastAt || firstAt === 0 || firstAt === trimmed.length - 1) {
33
+ return undefined;
34
+ }
35
+ user = trimmed.slice(0, firstAt);
36
+ host = trimmed.slice(firstAt + 1);
37
+ if (!SSH_TOKEN.test(user)) {
38
+ return undefined;
39
+ }
40
+ }
41
+ if (!host || host.startsWith("-") || host.includes("@")) {
42
+ return undefined;
43
+ }
44
+ if (host.includes(":") && !BRACKETED_IPV6.test(host)) {
45
+ return undefined;
46
+ }
47
+ if (!SSH_TOKEN.test(host) && !BRACKETED_IPV6.test(host)) {
48
+ return undefined;
49
+ }
50
+ return user ? `${user}@${host}` : host;
51
+ }
52
+ export function isSafeScpRemoteHost(value) {
53
+ return normalizeScpRemoteHost(value) !== undefined;
54
+ }