@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,85 +1,28 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { resolveStateDir } from "../config/paths.js";
2
+ import { normalizeDeviceAuthScopes } from "../shared/device-auth.js";
3
+ import { roleScopesAllow } from "../shared/operator-scope-compat.js";
4
+ import { createAsyncLock, pruneExpiredPending, readJsonFile, resolvePairingPaths, writeJsonAtomic, } from "./pairing-files.js";
5
+ import { generatePairingToken, verifyPairingToken } from "./pairing-token.js";
5
6
  const PENDING_TTL_MS = 5 * 60 * 1000;
6
- function resolvePaths(baseDir) {
7
- const root = baseDir ?? resolveStateDir();
8
- const dir = path.join(root, "devices");
9
- return {
10
- dir,
11
- pendingPath: path.join(dir, "pending.json"),
12
- pairedPath: path.join(dir, "paired.json"),
13
- };
14
- }
15
- async function readJSON(filePath) {
16
- try {
17
- const raw = await fs.readFile(filePath, "utf8");
18
- return JSON.parse(raw);
19
- }
20
- catch {
21
- return null;
22
- }
23
- }
24
- async function writeJSONAtomic(filePath, value) {
25
- const dir = path.dirname(filePath);
26
- await fs.mkdir(dir, { recursive: true });
27
- const tmp = `${filePath}.${randomUUID()}.tmp`;
28
- await fs.writeFile(tmp, JSON.stringify(value, null, 2), "utf8");
29
- try {
30
- await fs.chmod(tmp, 0o600);
31
- }
32
- catch {
33
- // best-effort
34
- }
35
- await fs.rename(tmp, filePath);
36
- try {
37
- await fs.chmod(filePath, 0o600);
38
- }
39
- catch {
40
- // best-effort
41
- }
42
- }
43
- function pruneExpiredPending(pendingById, nowMs) {
44
- for (const [id, req] of Object.entries(pendingById)) {
45
- if (nowMs - req.ts > PENDING_TTL_MS) {
46
- delete pendingById[id];
47
- }
48
- }
49
- }
50
- let lock = Promise.resolve();
51
- async function withLock(fn) {
52
- const prev = lock;
53
- let release;
54
- lock = new Promise((resolve) => {
55
- release = resolve;
56
- });
57
- await prev;
58
- try {
59
- return await fn();
60
- }
61
- finally {
62
- release?.();
63
- }
64
- }
7
+ const withLock = createAsyncLock();
65
8
  async function loadState(baseDir) {
66
- const { pendingPath, pairedPath } = resolvePaths(baseDir);
9
+ const { pendingPath, pairedPath } = resolvePairingPaths(baseDir, "devices");
67
10
  const [pending, paired] = await Promise.all([
68
- readJSON(pendingPath),
69
- readJSON(pairedPath),
11
+ readJsonFile(pendingPath),
12
+ readJsonFile(pairedPath),
70
13
  ]);
71
14
  const state = {
72
15
  pendingById: pending ?? {},
73
16
  pairedByDeviceId: paired ?? {},
74
17
  };
75
- pruneExpiredPending(state.pendingById, Date.now());
18
+ pruneExpiredPending(state.pendingById, Date.now(), PENDING_TTL_MS);
76
19
  return state;
77
20
  }
78
21
  async function persistState(state, baseDir) {
79
- const { pendingPath, pairedPath } = resolvePaths(baseDir);
22
+ const { pendingPath, pairedPath } = resolvePairingPaths(baseDir, "devices");
80
23
  await Promise.all([
81
- writeJSONAtomic(pendingPath, state.pendingById),
82
- writeJSONAtomic(pairedPath, state.pairedByDeviceId),
24
+ writeJsonAtomic(pendingPath, state.pendingById),
25
+ writeJsonAtomic(pairedPath, state.pairedByDeviceId),
83
26
  ]);
84
27
  }
85
28
  function normalizeDeviceId(deviceId) {
@@ -92,66 +35,124 @@ function normalizeRole(role) {
92
35
  function mergeRoles(...items) {
93
36
  const roles = new Set();
94
37
  for (const item of items) {
95
- if (!item)
38
+ if (!item) {
96
39
  continue;
40
+ }
97
41
  if (Array.isArray(item)) {
98
42
  for (const role of item) {
99
43
  const trimmed = role.trim();
100
- if (trimmed)
44
+ if (trimmed) {
101
45
  roles.add(trimmed);
46
+ }
102
47
  }
103
48
  }
104
49
  else {
105
50
  const trimmed = item.trim();
106
- if (trimmed)
51
+ if (trimmed) {
107
52
  roles.add(trimmed);
53
+ }
108
54
  }
109
55
  }
110
- if (roles.size === 0)
56
+ if (roles.size === 0) {
111
57
  return undefined;
58
+ }
112
59
  return [...roles];
113
60
  }
114
61
  function mergeScopes(...items) {
115
62
  const scopes = new Set();
116
63
  for (const item of items) {
117
- if (!item)
64
+ if (!item) {
118
65
  continue;
66
+ }
119
67
  for (const scope of item) {
120
68
  const trimmed = scope.trim();
121
- if (trimmed)
69
+ if (trimmed) {
122
70
  scopes.add(trimmed);
71
+ }
123
72
  }
124
73
  }
125
- if (scopes.size === 0)
74
+ if (scopes.size === 0) {
126
75
  return undefined;
76
+ }
127
77
  return [...scopes];
128
78
  }
129
- function normalizeScopes(scopes) {
130
- if (!Array.isArray(scopes))
131
- return [];
132
- const out = new Set();
133
- for (const scope of scopes) {
134
- const trimmed = scope.trim();
135
- if (trimmed)
136
- out.add(trimmed);
137
- }
138
- return [...out].sort();
79
+ function mergePendingDevicePairingRequest(existing, incoming, isRepair) {
80
+ const existingRole = normalizeRole(existing.role);
81
+ const incomingRole = normalizeRole(incoming.role);
82
+ return {
83
+ ...existing,
84
+ displayName: incoming.displayName ?? existing.displayName,
85
+ platform: incoming.platform ?? existing.platform,
86
+ clientId: incoming.clientId ?? existing.clientId,
87
+ clientMode: incoming.clientMode ?? existing.clientMode,
88
+ role: existingRole ?? incomingRole ?? undefined,
89
+ roles: mergeRoles(existing.roles, existing.role, incoming.role),
90
+ scopes: mergeScopes(existing.scopes, incoming.scopes),
91
+ remoteIp: incoming.remoteIp ?? existing.remoteIp,
92
+ // If either request is interactive, keep the pending request visible for approval.
93
+ silent: Boolean(existing.silent && incoming.silent),
94
+ isRepair: existing.isRepair || isRepair,
95
+ ts: Date.now(),
96
+ };
139
97
  }
140
98
  function scopesAllow(requested, allowed) {
141
- if (requested.length === 0)
99
+ if (requested.length === 0) {
142
100
  return true;
143
- if (allowed.length === 0)
101
+ }
102
+ if (allowed.length === 0) {
144
103
  return false;
104
+ }
145
105
  const allowedSet = new Set(allowed);
146
106
  return requested.every((scope) => allowedSet.has(scope));
147
107
  }
108
+ const DEVICE_SCOPE_IMPLICATIONS = {
109
+ "operator.admin": ["operator.read", "operator.write", "operator.approvals", "operator.pairing"],
110
+ "operator.write": ["operator.read"],
111
+ };
112
+ function expandScopeImplications(scopes) {
113
+ const expanded = new Set(scopes);
114
+ const queue = [...scopes];
115
+ while (queue.length > 0) {
116
+ const scope = queue.pop();
117
+ if (!scope) {
118
+ continue;
119
+ }
120
+ for (const impliedScope of DEVICE_SCOPE_IMPLICATIONS[scope] ?? []) {
121
+ if (!expanded.has(impliedScope)) {
122
+ expanded.add(impliedScope);
123
+ queue.push(impliedScope);
124
+ }
125
+ }
126
+ }
127
+ return [...expanded];
128
+ }
129
+ function scopesAllowWithImplications(requested, allowed) {
130
+ return scopesAllow(expandScopeImplications(requested), expandScopeImplications(allowed));
131
+ }
148
132
  function newToken() {
149
- return randomUUID().replaceAll("-", "");
133
+ return generatePairingToken();
134
+ }
135
+ function getPairedDeviceFromState(state, deviceId) {
136
+ return state.pairedByDeviceId[normalizeDeviceId(deviceId)] ?? null;
137
+ }
138
+ function cloneDeviceTokens(device) {
139
+ return device.tokens ? { ...device.tokens } : {};
140
+ }
141
+ function buildDeviceAuthToken(params) {
142
+ return {
143
+ token: newToken(),
144
+ role: params.role,
145
+ scopes: params.scopes,
146
+ createdAtMs: params.existing?.createdAtMs ?? params.now,
147
+ rotatedAtMs: params.rotatedAtMs,
148
+ revokedAtMs: undefined,
149
+ lastUsedAtMs: params.existing?.lastUsedAtMs,
150
+ };
150
151
  }
151
152
  export async function listDevicePairing(baseDir) {
152
153
  const state = await loadState(baseDir);
153
- const pending = Object.values(state.pendingById).sort((a, b) => b.ts - a.ts);
154
- const paired = Object.values(state.pairedByDeviceId).sort((a, b) => b.approvedAtMs - a.approvedAtMs);
154
+ const pending = Object.values(state.pendingById).toSorted((a, b) => b.ts - a.ts);
155
+ const paired = Object.values(state.pairedByDeviceId).toSorted((a, b) => b.approvedAtMs - a.approvedAtMs);
155
156
  return { pending, paired };
156
157
  }
157
158
  export async function getPairedDevice(deviceId, baseDir) {
@@ -165,11 +166,14 @@ export async function requestDevicePairing(req, baseDir) {
165
166
  if (!deviceId) {
166
167
  throw new Error("deviceId required");
167
168
  }
168
- const existing = Object.values(state.pendingById).find((p) => p.deviceId === deviceId);
169
+ const isRepair = Boolean(state.pairedByDeviceId[deviceId]);
170
+ const existing = Object.values(state.pendingById).find((pending) => pending.deviceId === deviceId);
169
171
  if (existing) {
170
- return { status: "pending", request: existing, created: false };
172
+ const merged = mergePendingDevicePairingRequest(existing, req, isRepair);
173
+ state.pendingById[existing.requestId] = merged;
174
+ await persistState(state, baseDir);
175
+ return { status: "pending", request: merged, created: false };
171
176
  }
172
- const isRepair = Boolean(state.pairedByDeviceId[deviceId]);
173
177
  const request = {
174
178
  requestId: randomUUID(),
175
179
  deviceId,
@@ -195,17 +199,24 @@ export async function approveDevicePairing(requestId, baseDir) {
195
199
  return await withLock(async () => {
196
200
  const state = await loadState(baseDir);
197
201
  const pending = state.pendingById[requestId];
198
- if (!pending)
202
+ if (!pending) {
199
203
  return null;
204
+ }
200
205
  const now = Date.now();
201
206
  const existing = state.pairedByDeviceId[pending.deviceId];
202
207
  const roles = mergeRoles(existing?.roles, existing?.role, pending.roles, pending.role);
203
- const scopes = mergeScopes(existing?.scopes, pending.scopes);
208
+ const approvedScopes = mergeScopes(existing?.approvedScopes ?? existing?.scopes, pending.scopes);
204
209
  const tokens = existing?.tokens ? { ...existing.tokens } : {};
205
210
  const roleForToken = normalizeRole(pending.role);
206
211
  if (roleForToken) {
207
- const nextScopes = normalizeScopes(pending.scopes);
208
212
  const existingToken = tokens[roleForToken];
213
+ const requestedScopes = normalizeDeviceAuthScopes(pending.scopes);
214
+ const nextScopes = requestedScopes.length > 0
215
+ ? requestedScopes
216
+ : normalizeDeviceAuthScopes(existingToken?.scopes ??
217
+ approvedScopes ??
218
+ existing?.approvedScopes ??
219
+ existing?.scopes);
209
220
  const now = Date.now();
210
221
  tokens[roleForToken] = {
211
222
  token: newToken(),
@@ -226,7 +237,8 @@ export async function approveDevicePairing(requestId, baseDir) {
226
237
  clientMode: pending.clientMode,
227
238
  role: pending.role,
228
239
  roles,
229
- scopes,
240
+ scopes: approvedScopes,
241
+ approvedScopes,
230
242
  remoteIp: pending.remoteIp,
231
243
  tokens,
232
244
  createdAtMs: existing?.createdAtMs ?? now,
@@ -242,19 +254,33 @@ export async function rejectDevicePairing(requestId, baseDir) {
242
254
  return await withLock(async () => {
243
255
  const state = await loadState(baseDir);
244
256
  const pending = state.pendingById[requestId];
245
- if (!pending)
257
+ if (!pending) {
246
258
  return null;
259
+ }
247
260
  delete state.pendingById[requestId];
248
261
  await persistState(state, baseDir);
249
262
  return { requestId, deviceId: pending.deviceId };
250
263
  });
251
264
  }
265
+ export async function removePairedDevice(deviceId, baseDir) {
266
+ return await withLock(async () => {
267
+ const state = await loadState(baseDir);
268
+ const normalized = normalizeDeviceId(deviceId);
269
+ if (!normalized || !state.pairedByDeviceId[normalized]) {
270
+ return null;
271
+ }
272
+ delete state.pairedByDeviceId[normalized];
273
+ await persistState(state, baseDir);
274
+ return { deviceId: normalized };
275
+ });
276
+ }
252
277
  export async function updatePairedDeviceMetadata(deviceId, patch, baseDir) {
253
278
  return await withLock(async () => {
254
279
  const state = await loadState(baseDir);
255
280
  const existing = state.pairedByDeviceId[normalizeDeviceId(deviceId)];
256
- if (!existing)
281
+ if (!existing) {
257
282
  return;
283
+ }
258
284
  const roles = mergeRoles(existing.roles, existing.role, patch.role);
259
285
  const scopes = mergeScopes(existing.scopes, patch.scopes);
260
286
  state.pairedByDeviceId[deviceId] = {
@@ -263,6 +289,7 @@ export async function updatePairedDeviceMetadata(deviceId, patch, baseDir) {
263
289
  deviceId: existing.deviceId,
264
290
  createdAtMs: existing.createdAtMs,
265
291
  approvedAtMs: existing.approvedAtMs,
292
+ approvedScopes: existing.approvedScopes,
266
293
  role: patch.role ?? existing.role,
267
294
  roles,
268
295
  scopes,
@@ -271,8 +298,9 @@ export async function updatePairedDeviceMetadata(deviceId, patch, baseDir) {
271
298
  });
272
299
  }
273
300
  export function summarizeDeviceTokens(tokens) {
274
- if (!tokens)
301
+ if (!tokens) {
275
302
  return undefined;
303
+ }
276
304
  const summaries = Object.values(tokens)
277
305
  .map((token) => ({
278
306
  role: token.role,
@@ -282,27 +310,32 @@ export function summarizeDeviceTokens(tokens) {
282
310
  revokedAtMs: token.revokedAtMs,
283
311
  lastUsedAtMs: token.lastUsedAtMs,
284
312
  }))
285
- .sort((a, b) => a.role.localeCompare(b.role));
313
+ .toSorted((a, b) => a.role.localeCompare(b.role));
286
314
  return summaries.length > 0 ? summaries : undefined;
287
315
  }
288
316
  export async function verifyDeviceToken(params) {
289
317
  return await withLock(async () => {
290
318
  const state = await loadState(params.baseDir);
291
- const device = state.pairedByDeviceId[normalizeDeviceId(params.deviceId)];
292
- if (!device)
319
+ const device = getPairedDeviceFromState(state, params.deviceId);
320
+ if (!device) {
293
321
  return { ok: false, reason: "device-not-paired" };
322
+ }
294
323
  const role = normalizeRole(params.role);
295
- if (!role)
324
+ if (!role) {
296
325
  return { ok: false, reason: "role-missing" };
326
+ }
297
327
  const entry = device.tokens?.[role];
298
- if (!entry)
328
+ if (!entry) {
299
329
  return { ok: false, reason: "token-missing" };
300
- if (entry.revokedAtMs)
330
+ }
331
+ if (entry.revokedAtMs) {
301
332
  return { ok: false, reason: "token-revoked" };
302
- if (entry.token !== params.token)
333
+ }
334
+ if (!verifyPairingToken(params.token, entry.token)) {
303
335
  return { ok: false, reason: "token-mismatch" };
304
- const requestedScopes = normalizeScopes(params.scopes);
305
- if (!scopesAllow(requestedScopes, entry.scopes)) {
336
+ }
337
+ const requestedScopes = normalizeDeviceAuthScopes(params.scopes);
338
+ if (!roleScopesAllow({ role, requestedScopes, allowedScopes: entry.scopes })) {
306
339
  return { ok: false, reason: "scope-mismatch" };
307
340
  }
308
341
  entry.lastUsedAtMs = Date.now();
@@ -316,30 +349,29 @@ export async function verifyDeviceToken(params) {
316
349
  export async function ensureDeviceToken(params) {
317
350
  return await withLock(async () => {
318
351
  const state = await loadState(params.baseDir);
319
- const device = state.pairedByDeviceId[normalizeDeviceId(params.deviceId)];
320
- if (!device)
352
+ const requestedScopes = normalizeDeviceAuthScopes(params.scopes);
353
+ const context = resolveDeviceTokenUpdateContext({
354
+ state,
355
+ deviceId: params.deviceId,
356
+ role: params.role,
357
+ });
358
+ if (!context) {
321
359
  return null;
322
- const role = normalizeRole(params.role);
323
- if (!role)
324
- return null;
325
- const requestedScopes = normalizeScopes(params.scopes);
326
- const tokens = device.tokens ? { ...device.tokens } : {};
327
- const existing = tokens[role];
360
+ }
361
+ const { device, role, tokens, existing } = context;
328
362
  if (existing && !existing.revokedAtMs) {
329
- if (scopesAllow(requestedScopes, existing.scopes)) {
363
+ if (roleScopesAllow({ role, requestedScopes, allowedScopes: existing.scopes })) {
330
364
  return existing;
331
365
  }
332
366
  }
333
367
  const now = Date.now();
334
- const next = {
335
- token: newToken(),
368
+ const next = buildDeviceAuthToken({
336
369
  role,
337
370
  scopes: requestedScopes,
338
- createdAtMs: existing?.createdAtMs ?? now,
371
+ existing,
372
+ now,
339
373
  rotatedAtMs: existing ? now : undefined,
340
- revokedAtMs: undefined,
341
- lastUsedAtMs: existing?.lastUsedAtMs,
342
- };
374
+ });
343
375
  tokens[role] = next;
344
376
  device.tokens = tokens;
345
377
  state.pairedByDeviceId[device.deviceId] = device;
@@ -347,33 +379,46 @@ export async function ensureDeviceToken(params) {
347
379
  return next;
348
380
  });
349
381
  }
382
+ function resolveDeviceTokenUpdateContext(params) {
383
+ const device = getPairedDeviceFromState(params.state, params.deviceId);
384
+ if (!device) {
385
+ return null;
386
+ }
387
+ const role = normalizeRole(params.role);
388
+ if (!role) {
389
+ return null;
390
+ }
391
+ const tokens = cloneDeviceTokens(device);
392
+ const existing = tokens[role];
393
+ return { device, role, tokens, existing };
394
+ }
350
395
  export async function rotateDeviceToken(params) {
351
396
  return await withLock(async () => {
352
397
  const state = await loadState(params.baseDir);
353
- const device = state.pairedByDeviceId[normalizeDeviceId(params.deviceId)];
354
- if (!device)
398
+ const context = resolveDeviceTokenUpdateContext({
399
+ state,
400
+ deviceId: params.deviceId,
401
+ role: params.role,
402
+ });
403
+ if (!context) {
355
404
  return null;
356
- const role = normalizeRole(params.role);
357
- if (!role)
405
+ }
406
+ const { device, role, tokens, existing } = context;
407
+ const requestedScopes = normalizeDeviceAuthScopes(params.scopes ?? existing?.scopes ?? device.scopes);
408
+ const approvedScopes = normalizeDeviceAuthScopes(device.approvedScopes ?? device.scopes ?? existing?.scopes);
409
+ if (!scopesAllowWithImplications(requestedScopes, approvedScopes)) {
358
410
  return null;
359
- const tokens = device.tokens ? { ...device.tokens } : {};
360
- const existing = tokens[role];
361
- const requestedScopes = normalizeScopes(params.scopes ?? existing?.scopes ?? device.scopes);
411
+ }
362
412
  const now = Date.now();
363
- const next = {
364
- token: newToken(),
413
+ const next = buildDeviceAuthToken({
365
414
  role,
366
415
  scopes: requestedScopes,
367
- createdAtMs: existing?.createdAtMs ?? now,
416
+ existing,
417
+ now,
368
418
  rotatedAtMs: now,
369
- revokedAtMs: undefined,
370
- lastUsedAtMs: existing?.lastUsedAtMs,
371
- };
419
+ });
372
420
  tokens[role] = next;
373
421
  device.tokens = tokens;
374
- if (params.scopes !== undefined) {
375
- device.scopes = requestedScopes;
376
- }
377
422
  state.pairedByDeviceId[device.deviceId] = device;
378
423
  await persistState(state, params.baseDir);
379
424
  return next;
@@ -383,13 +428,16 @@ export async function revokeDeviceToken(params) {
383
428
  return await withLock(async () => {
384
429
  const state = await loadState(params.baseDir);
385
430
  const device = state.pairedByDeviceId[normalizeDeviceId(params.deviceId)];
386
- if (!device)
431
+ if (!device) {
387
432
  return null;
433
+ }
388
434
  const role = normalizeRole(params.role);
389
- if (!role)
435
+ if (!role) {
390
436
  return null;
391
- if (!device.tokens?.[role])
437
+ }
438
+ if (!device.tokens?.[role]) {
392
439
  return null;
440
+ }
393
441
  const tokens = { ...device.tokens };
394
442
  const entry = { ...tokens[role], revokedAtMs: Date.now() };
395
443
  tokens[role] = entry;
@@ -399,3 +447,15 @@ export async function revokeDeviceToken(params) {
399
447
  return entry;
400
448
  });
401
449
  }
450
+ export async function clearDevicePairing(deviceId, baseDir) {
451
+ return await withLock(async () => {
452
+ const state = await loadState(baseDir);
453
+ const normalizedId = normalizeDeviceId(deviceId);
454
+ if (!state.pairedByDeviceId[normalizedId]) {
455
+ return false;
456
+ }
457
+ delete state.pairedByDeviceId[normalizedId];
458
+ await persistState(state, baseDir);
459
+ return true;
460
+ });
461
+ }
package/dist/infra/env.js CHANGED
@@ -3,21 +3,26 @@ import { parseBooleanValue } from "../utils/boolean.js";
3
3
  const log = createSubsystemLogger("env");
4
4
  const loggedEnv = new Set();
5
5
  function formatEnvValue(value, redact) {
6
- if (redact)
6
+ if (redact) {
7
7
  return "<redacted>";
8
+ }
8
9
  const singleLine = value.replace(/\s+/g, " ").trim();
9
- if (singleLine.length <= 160)
10
+ if (singleLine.length <= 160) {
10
11
  return singleLine;
12
+ }
11
13
  return `${singleLine.slice(0, 160)}…`;
12
14
  }
13
15
  export function logAcceptedEnvOption(option) {
14
- if (process.env.VITEST || process.env.NODE_ENV === "test")
16
+ if (process.env.VITEST || process.env.NODE_ENV === "test") {
15
17
  return;
16
- if (loggedEnv.has(option.key))
18
+ }
19
+ if (loggedEnv.has(option.key)) {
17
20
  return;
21
+ }
18
22
  const rawValue = option.value ?? process.env[option.key];
19
- if (!rawValue || !rawValue.trim())
23
+ if (!rawValue || !rawValue.trim()) {
20
24
  return;
25
+ }
21
26
  loggedEnv.add(option.key);
22
27
  log.info(`env: ${option.key}=${formatEnvValue(rawValue, option.redact)} (${option.description})`);
23
28
  }