@poolzin/pool-bot 2026.2.25 → 2026.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/dist/acp/event-mapper.js +87 -22
  2. package/dist/acp/meta.js +12 -6
  3. package/dist/agents/agent-paths.js +8 -9
  4. package/dist/agents/agent-scope.js +7 -5
  5. package/dist/agents/auth-profiles/oauth.js +148 -64
  6. package/dist/agents/auth-profiles/session-override.js +13 -7
  7. package/dist/agents/bash-tools.exec-host-gateway.js +14 -4
  8. package/dist/agents/bash-tools.exec-runtime.js +2 -25
  9. package/dist/agents/bedrock-discovery.js +3 -1
  10. package/dist/agents/byteplus-models.js +97 -0
  11. package/dist/agents/chutes-oauth.js +1 -0
  12. package/dist/agents/cli-runner/helpers.js +4 -0
  13. package/dist/agents/compaction.js +41 -14
  14. package/dist/agents/doubao-models.js +121 -0
  15. package/dist/agents/failover-error.js +2 -0
  16. package/dist/agents/huggingface-models.js +5 -3
  17. package/dist/agents/live-model-filter.js +5 -0
  18. package/dist/agents/minimax-vlm.js +10 -8
  19. package/dist/agents/model-auth.js +6 -0
  20. package/dist/agents/model-catalog.js +3 -1
  21. package/dist/agents/model-selection.js +7 -1
  22. package/dist/agents/models-config.providers.js +93 -11
  23. package/dist/agents/ollama-stream.js +117 -4
  24. package/dist/agents/opencode-zen-models.js +22 -11
  25. package/dist/agents/pi-embedded-helpers/errors.js +55 -33
  26. package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
  27. package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
  28. package/dist/agents/pi-embedded-helpers.js +1 -1
  29. package/dist/agents/pi-embedded-runner/compact.js +29 -7
  30. package/dist/agents/pi-embedded-runner/extensions.js +28 -26
  31. package/dist/agents/pi-embedded-runner/google.js +20 -8
  32. package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
  33. package/dist/agents/pi-embedded-runner/run.js +71 -12
  34. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
  35. package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
  36. package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
  37. package/dist/agents/pi-embedded-runner/thinking.js +42 -0
  38. package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
  39. package/dist/agents/pi-embedded-runner/utils.js +7 -10
  40. package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
  41. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
  42. package/dist/agents/pi-embedded-subscribe.js +9 -4
  43. package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
  44. package/dist/agents/pi-embedded-utils.js +3 -0
  45. package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
  46. package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
  47. package/dist/agents/pi-settings.js +40 -0
  48. package/dist/agents/pi-tools.policy.js +2 -1
  49. package/dist/agents/provider/config-loader.js +1 -1
  50. package/dist/agents/sandbox/browser.js +170 -33
  51. package/dist/agents/sandbox/config-hash.js +14 -27
  52. package/dist/agents/sandbox/config.js +21 -2
  53. package/dist/agents/sandbox/constants.js +2 -0
  54. package/dist/agents/sandbox/docker.js +16 -2
  55. package/dist/agents/sandbox/novnc-auth.js +62 -0
  56. package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
  57. package/dist/agents/sandbox/shared.js +10 -6
  58. package/dist/agents/sandbox-paths.js +24 -11
  59. package/dist/agents/schema/clean-for-gemini.js +132 -85
  60. package/dist/agents/session-slug.js +10 -5
  61. package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
  62. package/dist/agents/session-tool-result-guard.js +3 -1
  63. package/dist/agents/session-transcript-repair.js +40 -6
  64. package/dist/agents/skills/bundled-dir.js +19 -5
  65. package/dist/agents/skills/env-overrides.js +124 -43
  66. package/dist/agents/skills/frontmatter.js +6 -6
  67. package/dist/agents/skills/plugin-skills.js +14 -7
  68. package/dist/agents/skills/workspace.js +1 -0
  69. package/dist/agents/subagent-announce.js +251 -49
  70. package/dist/agents/subagent-lifecycle-events.js +19 -0
  71. package/dist/agents/subagent-registry-cleanup.js +31 -0
  72. package/dist/agents/subagent-registry-completion.js +68 -0
  73. package/dist/agents/subagent-registry-queries.js +117 -0
  74. package/dist/agents/subagent-registry-state.js +46 -0
  75. package/dist/agents/subagent-registry.js +252 -221
  76. package/dist/agents/subagent-registry.store.js +1 -0
  77. package/dist/agents/subagent-registry.types.js +1 -0
  78. package/dist/agents/subagent-spawn.js +195 -7
  79. package/dist/agents/system-prompt.js +22 -6
  80. package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
  81. package/dist/agents/test-helpers/fast-core-tools.js +1 -17
  82. package/dist/agents/timeout.js +18 -6
  83. package/dist/agents/tool-call-id.js +1 -1
  84. package/dist/agents/tool-display-common.js +162 -29
  85. package/dist/agents/tool-images.js +82 -9
  86. package/dist/agents/tool-policy.js +51 -26
  87. package/dist/agents/tools/browser-tool.js +2 -2
  88. package/dist/agents/tools/canvas-tool.js +27 -1
  89. package/dist/agents/tools/common.js +45 -0
  90. package/dist/agents/tools/discord-actions-guild.js +4 -1
  91. package/dist/agents/tools/gateway-tool.js +3 -1
  92. package/dist/agents/tools/nodes-utils.js +1 -10
  93. package/dist/agents/tools/sessions-send-helpers.js +12 -6
  94. package/dist/agents/tools/sessions-spawn-tool.js +8 -2
  95. package/dist/agents/tools/subagents-tool.js +2 -1
  96. package/dist/agents/tools/whatsapp-actions.js +10 -2
  97. package/dist/agents/tools/whatsapp-target-auth.js +18 -0
  98. package/dist/agents/transcript-policy.js +22 -8
  99. package/dist/agents/venice-models.js +11 -3
  100. package/dist/auto-reply/commands-registry.data.js +51 -0
  101. package/dist/auto-reply/commands-registry.js +4 -3
  102. package/dist/auto-reply/group-activation.js +10 -5
  103. package/dist/auto-reply/inbound-debounce.js +10 -5
  104. package/dist/auto-reply/reply/abort.js +1 -1
  105. package/dist/auto-reply/reply/agent-runner-execution.js +4 -1
  106. package/dist/auto-reply/reply/bash-command.js +41 -39
  107. package/dist/auto-reply/reply/command-gates.js +25 -0
  108. package/dist/auto-reply/reply/commands-allowlist.js +111 -72
  109. package/dist/auto-reply/reply/commands-bash.js +6 -5
  110. package/dist/auto-reply/reply/commands-config.js +30 -28
  111. package/dist/auto-reply/reply/commands-core.js +2 -1
  112. package/dist/auto-reply/reply/commands-info.js +1 -0
  113. package/dist/auto-reply/reply/commands-models.js +65 -14
  114. package/dist/auto-reply/reply/commands-session.js +237 -82
  115. package/dist/auto-reply/reply/commands-setunset.js +45 -0
  116. package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
  117. package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
  118. package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
  119. package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
  120. package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
  121. package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
  122. package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
  123. package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
  124. package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
  125. package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
  126. package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
  127. package/dist/auto-reply/reply/commands-subagents.js +51 -587
  128. package/dist/auto-reply/reply/commands-tts.js +10 -5
  129. package/dist/auto-reply/reply/config-value.js +10 -5
  130. package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
  131. package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
  132. package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
  133. package/dist/auto-reply/reply/followup-runner.js +1 -0
  134. package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
  135. package/dist/auto-reply/reply/get-reply-directives.js +17 -28
  136. package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
  137. package/dist/auto-reply/reply/get-reply.js +71 -12
  138. package/dist/auto-reply/reply/model-selection.js +80 -39
  139. package/dist/auto-reply/reply/queue/enqueue.js +10 -5
  140. package/dist/auto-reply/reply/queue/state.js +13 -12
  141. package/dist/auto-reply/reply/reply-payloads.js +67 -36
  142. package/dist/auto-reply/reply/reply-reference.js +9 -8
  143. package/dist/auto-reply/reply/route-reply.js +15 -8
  144. package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
  145. package/dist/auto-reply/reply/session.js +22 -6
  146. package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
  147. package/dist/auto-reply/reply/subagents-utils.js +56 -30
  148. package/dist/auto-reply/reply/typing.js +46 -21
  149. package/dist/auto-reply/send-policy.js +14 -7
  150. package/dist/auto-reply/status.js +140 -16
  151. package/dist/auto-reply/templating.js +10 -5
  152. package/dist/auto-reply/thinking.js +7 -16
  153. package/dist/auto-reply/tokens.js +21 -5
  154. package/dist/browser/bridge-server.js +36 -20
  155. package/dist/browser/cdp.helpers.js +7 -14
  156. package/dist/browser/cdp.js +35 -15
  157. package/dist/browser/chrome.profile-decoration.js +7 -4
  158. package/dist/browser/config.js +4 -0
  159. package/dist/browser/extension-relay-auth.js +55 -0
  160. package/dist/browser/extension-relay.js +74 -29
  161. package/dist/browser/navigation-guard.js +9 -1
  162. package/dist/browser/paths.js +77 -0
  163. package/dist/browser/profiles.js +13 -8
  164. package/dist/browser/pw-ai-module.js +10 -5
  165. package/dist/browser/pw-session.js +76 -39
  166. package/dist/browser/pw-tools-core.interactions.js +14 -7
  167. package/dist/browser/pw-tools-core.state.js +12 -6
  168. package/dist/browser/routes/agent.act.js +2 -2
  169. package/dist/browser/server-context.js +7 -0
  170. package/dist/build-info.json +3 -3
  171. package/dist/channels/allow-from.js +2 -1
  172. package/dist/channels/allowlists/resolve-utils.js +43 -19
  173. package/dist/channels/channel-config.js +14 -7
  174. package/dist/channels/draft-stream-loop.js +7 -0
  175. package/dist/channels/model-overrides.js +82 -0
  176. package/dist/channels/plugins/normalize/imessage.js +14 -7
  177. package/dist/channels/plugins/normalize/slack.js +10 -5
  178. package/dist/channels/plugins/normalize/telegram.js +14 -7
  179. package/dist/channels/plugins/outbound/discord.js +80 -8
  180. package/dist/channels/plugins/outbound/signal.js +11 -11
  181. package/dist/channels/plugins/setup-helpers.js +10 -5
  182. package/dist/channels/sender-label.js +14 -7
  183. package/dist/channels/session.js +4 -2
  184. package/dist/channels/status-reactions.js +297 -0
  185. package/dist/cli/banner.js +1 -1
  186. package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
  187. package/dist/cli/cli-name.js +11 -11
  188. package/dist/cli/cli-utils.js +13 -3
  189. package/dist/cli/command-format.js +1 -1
  190. package/dist/cli/config-cli.js +1 -1
  191. package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
  192. package/dist/cli/daemon-cli/lifecycle.js +64 -2
  193. package/dist/cli/daemon-cli/restart-health.js +126 -0
  194. package/dist/cli/daemon-cli/status.gather.js +9 -13
  195. package/dist/cli/daemon-cli/status.print.js +2 -10
  196. package/dist/cli/deps.js +27 -22
  197. package/dist/cli/gateway-cli/run-loop.js +23 -5
  198. package/dist/cli/node-cli/register.js +14 -5
  199. package/dist/cli/nodes-media-utils.js +7 -2
  200. package/dist/cli/outbound-send-deps.js +2 -9
  201. package/dist/cli/outbound-send-mapping.js +11 -0
  202. package/dist/cli/pairing-cli.js +40 -14
  203. package/dist/cli/plugins-cli.js +34 -41
  204. package/dist/cli/ports.js +11 -10
  205. package/dist/cli/program/command-registry.js +2 -11
  206. package/dist/cli/program/command-tree.js +16 -0
  207. package/dist/cli/program/preaction.js +13 -9
  208. package/dist/cli/program/register.configure.js +3 -18
  209. package/dist/cli/program/register.maintenance.js +2 -2
  210. package/dist/cli/program/register.onboard.js +2 -0
  211. package/dist/cli/program/register.status-health-sessions.js +16 -17
  212. package/dist/cli/program/register.subclis.js +93 -52
  213. package/dist/cli/route.js +11 -7
  214. package/dist/cli/system-cli.js +36 -46
  215. package/dist/cli/update-cli/shared.js +22 -9
  216. package/dist/cli/update-cli/update-command.js +89 -14
  217. package/dist/cli/update-cli/wizard.js +6 -12
  218. package/dist/commands/agent/run-context.js +18 -5
  219. package/dist/commands/agent/session-store.js +17 -4
  220. package/dist/commands/agent.js +22 -2
  221. package/dist/commands/agents.bindings.js +14 -7
  222. package/dist/commands/agents.commands.add.js +13 -9
  223. package/dist/commands/agents.commands.identity.js +12 -6
  224. package/dist/commands/agents.commands.list.js +11 -6
  225. package/dist/commands/agents.config.js +8 -10
  226. package/dist/commands/agents.providers.js +12 -6
  227. package/dist/commands/auth-choice-options.js +103 -75
  228. package/dist/commands/auth-choice.apply.byteplus.js +55 -0
  229. package/dist/commands/auth-choice.apply.js +4 -0
  230. package/dist/commands/auth-choice.apply.minimax.js +61 -13
  231. package/dist/commands/auth-choice.apply.openai.js +3 -1
  232. package/dist/commands/auth-choice.apply.volcengine.js +55 -0
  233. package/dist/commands/auth-choice.preferred-provider.js +2 -0
  234. package/dist/commands/channels/remove.js +13 -6
  235. package/dist/commands/channels/shared.js +4 -14
  236. package/dist/commands/configure.commands.js +14 -0
  237. package/dist/commands/configure.gateway.js +2 -4
  238. package/dist/commands/configure.js +1 -1
  239. package/dist/commands/configure.shared.js +11 -0
  240. package/dist/commands/daemon-install-helpers.js +2 -2
  241. package/dist/commands/dashboard.js +12 -10
  242. package/dist/commands/docs.js +14 -8
  243. package/dist/commands/doctor-config-flow.js +11 -9
  244. package/dist/commands/doctor-legacy-config.js +281 -0
  245. package/dist/commands/doctor-state-integrity.js +99 -23
  246. package/dist/commands/doctor-update.js +12 -9
  247. package/dist/commands/models/list.list-command.js +7 -5
  248. package/dist/commands/models/set-image.js +2 -21
  249. package/dist/commands/node-daemon-install-helpers.js +10 -8
  250. package/dist/commands/onboard-auth.config-minimax.js +54 -80
  251. package/dist/commands/onboard-auth.config-opencode.js +2 -18
  252. package/dist/commands/onboard-auth.credentials.js +90 -13
  253. package/dist/commands/onboard-auth.js +1 -1
  254. package/dist/commands/onboard-auth.models.js +6 -5
  255. package/dist/commands/onboard-hooks.js +1 -1
  256. package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
  257. package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
  258. package/dist/commands/onboard-provider-auth-flags.js +14 -0
  259. package/dist/commands/onboard-remote.js +14 -7
  260. package/dist/commands/onboard.js +11 -13
  261. package/dist/commands/sandbox-display.js +6 -5
  262. package/dist/commands/status-all/diagnosis.js +14 -10
  263. package/dist/commands/status-all/format.js +1 -0
  264. package/dist/commands/status.gateway-probe.js +1 -16
  265. package/dist/commands/systemd-linger.js +12 -6
  266. package/dist/config/agent-limits.js +2 -0
  267. package/dist/config/commands.js +30 -16
  268. package/dist/config/config-paths.js +9 -11
  269. package/dist/config/defaults.js +22 -2
  270. package/dist/config/discord-preview-streaming.js +104 -0
  271. package/dist/config/env-vars.js +37 -8
  272. package/dist/config/includes.js +4 -0
  273. package/dist/config/io.js +97 -12
  274. package/dist/config/legacy.migrations.part-1.js +189 -78
  275. package/dist/config/legacy.shared.js +3 -1
  276. package/dist/config/merge-patch.js +4 -0
  277. package/dist/config/prototype-keys.js +4 -0
  278. package/dist/config/schema.help.js +44 -7
  279. package/dist/config/schema.labels.js +38 -6
  280. package/dist/config/sessions/delivery-info.js +10 -3
  281. package/dist/config/sessions/main-session.js +10 -5
  282. package/dist/config/sessions/session-file.js +33 -0
  283. package/dist/config/sessions/session-key.js +10 -5
  284. package/dist/config/sessions/store.js +1 -1
  285. package/dist/config/sessions.js +1 -0
  286. package/dist/config/zod-schema.agent-runtime.js +11 -0
  287. package/dist/config/zod-schema.js +148 -13
  288. package/dist/config/zod-schema.providers-core.js +78 -4
  289. package/dist/config/zod-schema.providers.js +6 -1
  290. package/dist/config/zod-schema.session.js +41 -2
  291. package/dist/cron/run-log.js +3 -0
  292. package/dist/cron/schedule.js +21 -10
  293. package/dist/cron/service/ops.js +35 -21
  294. package/dist/cron/service/timer.js +116 -16
  295. package/dist/cron/stagger.js +3 -1
  296. package/dist/discord/api.js +12 -6
  297. package/dist/discord/draft-chunking.js +22 -0
  298. package/dist/discord/draft-stream.js +124 -0
  299. package/dist/discord/monitor/agent-components.js +1 -1
  300. package/dist/discord/monitor/commands.js +5 -0
  301. package/dist/discord/monitor/gateway-plugin.js +2 -1
  302. package/dist/discord/monitor/listeners.js +37 -27
  303. package/dist/discord/monitor/message-handler.js +4 -1
  304. package/dist/discord/monitor/message-handler.preflight.js +65 -8
  305. package/dist/discord/monitor/message-handler.process.js +246 -217
  306. package/dist/discord/monitor/message-utils.js +143 -6
  307. package/dist/discord/monitor/model-picker-preferences.js +143 -0
  308. package/dist/discord/monitor/model-picker.js +651 -0
  309. package/dist/discord/monitor/native-command.js +573 -16
  310. package/dist/discord/monitor/provider.allowlist.js +223 -0
  311. package/dist/discord/monitor/provider.js +275 -347
  312. package/dist/discord/monitor/provider.lifecycle.js +100 -0
  313. package/dist/discord/monitor/reply-delivery.js +123 -16
  314. package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
  315. package/dist/discord/monitor/thread-bindings.js +4 -0
  316. package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
  317. package/dist/discord/monitor/thread-bindings.manager.js +423 -0
  318. package/dist/discord/monitor/thread-bindings.messages.js +55 -0
  319. package/dist/discord/monitor/thread-bindings.state.js +358 -0
  320. package/dist/discord/monitor/thread-bindings.types.js +6 -0
  321. package/dist/discord/resolve-users.js +33 -21
  322. package/dist/discord/send.channels.js +15 -0
  323. package/dist/discord/send.js +3 -2
  324. package/dist/discord/send.outbound.js +82 -26
  325. package/dist/discord/send.permissions.js +83 -30
  326. package/dist/discord/send.reactions.js +8 -4
  327. package/dist/discord/token.js +10 -5
  328. package/dist/discord/voice/command.js +263 -0
  329. package/dist/discord/voice/manager.js +531 -0
  330. package/dist/gateway/auth.js +34 -10
  331. package/dist/gateway/call.js +4 -16
  332. package/dist/gateway/client.js +28 -4
  333. package/dist/gateway/config-reload.js +3 -4
  334. package/dist/gateway/control-ui.js +219 -96
  335. package/dist/gateway/hooks-mapping.js +88 -38
  336. package/dist/gateway/http-auth-helpers.js +3 -2
  337. package/dist/gateway/http-endpoint-helpers.js +1 -0
  338. package/dist/gateway/net.js +54 -12
  339. package/dist/gateway/node-invoke-system-run-approval.js +14 -35
  340. package/dist/gateway/node-registry.js +10 -5
  341. package/dist/gateway/openai-http.js +1 -0
  342. package/dist/gateway/openresponses-http.js +1 -0
  343. package/dist/gateway/origin-check.js +1 -18
  344. package/dist/gateway/protocol/index.js +4 -3
  345. package/dist/gateway/protocol/schema/cron.js +1 -0
  346. package/dist/gateway/protocol/schema/devices.js +1 -0
  347. package/dist/gateway/protocol/schema/protocol-schemas.js +2 -1
  348. package/dist/gateway/protocol/schema/sessions.js +6 -0
  349. package/dist/gateway/role-policy.js +17 -0
  350. package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
  351. package/dist/gateway/server/ws-connection/message-handler.js +175 -148
  352. package/dist/gateway/server-chat.js +83 -25
  353. package/dist/gateway/server-constants.js +10 -9
  354. package/dist/gateway/server-cron.js +1 -0
  355. package/dist/gateway/server-http.js +16 -7
  356. package/dist/gateway/server-maintenance.js +20 -5
  357. package/dist/gateway/server-methods/chat.js +10 -6
  358. package/dist/gateway/server-methods/config.js +12 -14
  359. package/dist/gateway/server-methods/devices.js +17 -3
  360. package/dist/gateway/server-methods/models.js +11 -1
  361. package/dist/gateway/server-methods/sessions.js +64 -8
  362. package/dist/gateway/server-methods/usage.js +162 -75
  363. package/dist/gateway/server-node-events.js +29 -0
  364. package/dist/gateway/server-runtime-config.js +34 -13
  365. package/dist/gateway/server-startup-memory.js +17 -11
  366. package/dist/gateway/session-utils.fs.js +32 -34
  367. package/dist/gateway/sessions-resolve.js +17 -5
  368. package/dist/gateway/test-helpers.openai-mock.js +14 -7
  369. package/dist/gateway/tools-invoke-http.js +21 -10
  370. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
  371. package/dist/hooks/bundled/command-logger/handler.js +7 -2
  372. package/dist/hooks/bundled/session-memory/handler.js +6 -5
  373. package/dist/hooks/frontmatter.js +6 -6
  374. package/dist/hooks/gmail-watcher.js +11 -6
  375. package/dist/hooks/internal-hooks.js +11 -1
  376. package/dist/hooks/llm-slug-generator.js +4 -1
  377. package/dist/hooks/workspace.js +47 -17
  378. package/dist/imessage/accounts.js +9 -20
  379. package/dist/imessage/monitor/inbound-processing.js +2 -1
  380. package/dist/infra/archive.js +174 -73
  381. package/dist/infra/control-ui-assets.js +14 -6
  382. package/dist/infra/device-pairing.js +108 -29
  383. package/dist/infra/env.js +10 -5
  384. package/dist/infra/exec-approvals-allowlist.js +122 -0
  385. package/dist/infra/exec-approvals-analysis.js +34 -3
  386. package/dist/infra/exec-approvals.js +5 -17
  387. package/dist/infra/exec-safe-bin-policy.js +53 -45
  388. package/dist/infra/fs-safe.js +71 -39
  389. package/dist/infra/gateway-lock.js +6 -2
  390. package/dist/infra/heartbeat-wake.js +6 -12
  391. package/dist/infra/host-env-security-policy.json +19 -0
  392. package/dist/infra/host-env-security.js +66 -0
  393. package/dist/infra/net/ssrf.js +131 -38
  394. package/dist/infra/outbound/bound-delivery-router.js +88 -0
  395. package/dist/infra/outbound/channel-selection.js +12 -6
  396. package/dist/infra/outbound/envelope.js +1 -1
  397. package/dist/infra/outbound/format.js +12 -6
  398. package/dist/infra/outbound/payloads.js +14 -7
  399. package/dist/infra/outbound/session-binding-service.js +123 -0
  400. package/dist/infra/path-guards.js +25 -0
  401. package/dist/infra/provider-usage.fetch.codex.js +7 -15
  402. package/dist/infra/provider-usage.fetch.gemini.js +14 -11
  403. package/dist/infra/provider-usage.fetch.shared.js +30 -1
  404. package/dist/infra/provider-usage.fetch.zai.js +10 -9
  405. package/dist/infra/retry-policy.js +4 -2
  406. package/dist/infra/retry.js +9 -5
  407. package/dist/infra/session-cost-usage.js +107 -59
  408. package/dist/infra/session-maintenance-warning.js +3 -1
  409. package/dist/infra/shell-env.js +98 -34
  410. package/dist/infra/ssh-config.js +12 -6
  411. package/dist/infra/system-run-command.js +49 -4
  412. package/dist/infra/update-channels.js +10 -5
  413. package/dist/line/accounts.js +5 -7
  414. package/dist/line/bot-access.js +8 -20
  415. package/dist/line/bot-handlers.js +3 -1
  416. package/dist/link-understanding/detect.js +15 -7
  417. package/dist/media/constants.js +15 -6
  418. package/dist/media/image-ops.js +7 -0
  419. package/dist/media/local-roots.js +3 -2
  420. package/dist/media-understanding/apply.js +4 -1
  421. package/dist/media-understanding/concurrency.js +8 -20
  422. package/dist/memory/backend-config.js +45 -6
  423. package/dist/memory/embeddings.js +10 -4
  424. package/dist/memory/fs-utils.js +23 -0
  425. package/dist/memory/manager-search.js +12 -6
  426. package/dist/memory/manager-sync-ops.js +12 -2
  427. package/dist/memory/qmd-manager.js +466 -53
  428. package/dist/memory/query-expansion.js +167 -3
  429. package/dist/memory/status-format.js +10 -5
  430. package/dist/memory/sync-memory-files.js +1 -1
  431. package/dist/node-host/invoke-system-run.js +281 -0
  432. package/dist/node-host/invoke.js +55 -337
  433. package/dist/pairing/pairing-store.js +22 -0
  434. package/dist/plugin-sdk/allow-from.js +1 -1
  435. package/dist/plugin-sdk/command-auth.js +3 -1
  436. package/dist/plugin-sdk/index.js +6 -3
  437. package/dist/plugin-sdk/webhook-targets.js +32 -0
  438. package/dist/plugins/bundled-dir.js +9 -6
  439. package/dist/plugins/hooks.js +50 -0
  440. package/dist/plugins/install.js +28 -16
  441. package/dist/plugins/runtime.js +3 -17
  442. package/dist/plugins/update.js +78 -12
  443. package/dist/process/spawn-utils.js +14 -7
  444. package/dist/providers/github-copilot-token.js +11 -6
  445. package/dist/providers/qwen-portal-oauth.js +14 -6
  446. package/dist/routing/account-id.js +30 -0
  447. package/dist/routing/resolve-route.js +3 -7
  448. package/dist/routing/session-key.js +2 -16
  449. package/dist/security/audit-channel.js +93 -2
  450. package/dist/security/audit-extra.async.js +159 -5
  451. package/dist/security/audit-extra.js +1 -1
  452. package/dist/security/audit-extra.sync.js +85 -6
  453. package/dist/security/audit.js +40 -4
  454. package/dist/security/dm-policy-shared.js +44 -0
  455. package/dist/security/external-content.js +26 -6
  456. package/dist/shared/entry-status.js +6 -0
  457. package/dist/shared/frontmatter.js +5 -5
  458. package/dist/shared/node-match.js +11 -4
  459. package/dist/shared/operator-scope-compat.js +8 -3
  460. package/dist/signal/accounts.js +7 -20
  461. package/dist/signal/monitor/event-handler.js +3 -1
  462. package/dist/slack/accounts.js +6 -19
  463. package/dist/slack/actions.js +11 -3
  464. package/dist/slack/monitor/auth.js +1 -1
  465. package/dist/slack/monitor/message-handler/dispatch.js +50 -29
  466. package/dist/slack/monitor/replies.js +15 -7
  467. package/dist/slack/monitor/slash.js +22 -13
  468. package/dist/slack/resolve-channels.js +10 -5
  469. package/dist/slack/send.js +102 -12
  470. package/dist/slack/stream-mode.js +10 -0
  471. package/dist/slack/streaming.js +4 -2
  472. package/dist/telegram/accounts.js +19 -14
  473. package/dist/telegram/bot/helpers.js +3 -5
  474. package/dist/telegram/bot-access.js +35 -36
  475. package/dist/telegram/bot-handlers.js +120 -148
  476. package/dist/telegram/bot-message-context.js +68 -9
  477. package/dist/telegram/bot-message-dispatch.js +155 -90
  478. package/dist/telegram/bot-native-commands.js +16 -0
  479. package/dist/telegram/draft-stream.js +14 -1
  480. package/dist/telegram/inline-buttons.js +5 -15
  481. package/dist/telegram/monitor.js +11 -7
  482. package/dist/telegram/network-config.js +19 -7
  483. package/dist/telegram/send.js +3 -2
  484. package/dist/telegram/sent-message-cache.js +5 -6
  485. package/dist/telegram/status-reaction-variants.js +208 -0
  486. package/dist/telegram/sticker-cache.js +11 -9
  487. package/dist/terminal/theme.js +12 -12
  488. package/dist/tts/tts.js +80 -567
  489. package/dist/tui/components/chat-log.js +41 -8
  490. package/dist/tui/theme/theme.js +10 -12
  491. package/dist/tui/tui-local-shell.js +16 -6
  492. package/dist/tui/tui.js +58 -6
  493. package/dist/utils/account-id.js +2 -4
  494. package/dist/utils/boolean.js +10 -5
  495. package/dist/utils/directive-tags.js +11 -0
  496. package/dist/utils/queue-helpers.js +67 -12
  497. package/dist/web/auto-reply/deliver-reply.js +8 -4
  498. package/dist/web/auto-reply/mentions.js +10 -5
  499. package/dist/web/auto-reply/monitor/group-members.js +14 -7
  500. package/dist/web/auto-reply/monitor/process-message.js +45 -24
  501. package/dist/web/inbound/access-control.js +5 -2
  502. package/dist/web/login-qr.js +12 -6
  503. package/dist/web/media.js +123 -16
  504. package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
  505. package/extensions/bluebubbles/src/monitor.ts +208 -1950
  506. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { normalizeDeviceAuthScopes } from "../shared/device-auth.js";
3
3
  import { roleScopesAllow } from "../shared/operator-scope-compat.js";
4
- import { createAsyncLock, pruneExpiredPending, readJsonFile, resolvePairingPaths, upsertPendingPairingRequest, writeJsonAtomic, } from "./pairing-files.js";
4
+ import { createAsyncLock, pruneExpiredPending, readJsonFile, resolvePairingPaths, writeJsonAtomic, } from "./pairing-files.js";
5
5
  import { generatePairingToken, verifyPairingToken } from "./pairing-token.js";
6
6
  const PENDING_TTL_MS = 5 * 60 * 1000;
7
7
  const withLock = createAsyncLock();
@@ -76,6 +76,59 @@ function mergeScopes(...items) {
76
76
  }
77
77
  return [...scopes];
78
78
  }
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
+ };
97
+ }
98
+ function scopesAllow(requested, allowed) {
99
+ if (requested.length === 0) {
100
+ return true;
101
+ }
102
+ if (allowed.length === 0) {
103
+ return false;
104
+ }
105
+ const allowedSet = new Set(allowed);
106
+ return requested.every((scope) => allowedSet.has(scope));
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
+ }
79
132
  function newToken() {
80
133
  return generatePairingToken();
81
134
  }
@@ -113,28 +166,33 @@ export async function requestDevicePairing(req, baseDir) {
113
166
  if (!deviceId) {
114
167
  throw new Error("deviceId required");
115
168
  }
116
- return await upsertPendingPairingRequest({
117
- pendingById: state.pendingById,
118
- isExisting: (pending) => pending.deviceId === deviceId,
119
- isRepair: Boolean(state.pairedByDeviceId[deviceId]),
120
- createRequest: (isRepair) => ({
121
- requestId: randomUUID(),
122
- deviceId,
123
- publicKey: req.publicKey,
124
- displayName: req.displayName,
125
- platform: req.platform,
126
- clientId: req.clientId,
127
- clientMode: req.clientMode,
128
- role: req.role,
129
- roles: req.role ? [req.role] : undefined,
130
- scopes: req.scopes,
131
- remoteIp: req.remoteIp,
132
- silent: req.silent,
133
- isRepair,
134
- ts: Date.now(),
135
- }),
136
- persist: async () => await persistState(state, baseDir),
137
- });
169
+ const isRepair = Boolean(state.pairedByDeviceId[deviceId]);
170
+ const existing = Object.values(state.pendingById).find((pending) => pending.deviceId === deviceId);
171
+ if (existing) {
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 };
176
+ }
177
+ const request = {
178
+ requestId: randomUUID(),
179
+ deviceId,
180
+ publicKey: req.publicKey,
181
+ displayName: req.displayName,
182
+ platform: req.platform,
183
+ clientId: req.clientId,
184
+ clientMode: req.clientMode,
185
+ role: req.role,
186
+ roles: req.role ? [req.role] : undefined,
187
+ scopes: req.scopes,
188
+ remoteIp: req.remoteIp,
189
+ silent: req.silent,
190
+ isRepair,
191
+ ts: Date.now(),
192
+ };
193
+ state.pendingById[request.requestId] = request;
194
+ await persistState(state, baseDir);
195
+ return { status: "pending", request, created: true };
138
196
  });
139
197
  }
140
198
  export async function approveDevicePairing(requestId, baseDir) {
@@ -147,12 +205,18 @@ export async function approveDevicePairing(requestId, baseDir) {
147
205
  const now = Date.now();
148
206
  const existing = state.pairedByDeviceId[pending.deviceId];
149
207
  const roles = mergeRoles(existing?.roles, existing?.role, pending.roles, pending.role);
150
- const scopes = mergeScopes(existing?.scopes, pending.scopes);
208
+ const approvedScopes = mergeScopes(existing?.approvedScopes ?? existing?.scopes, pending.scopes);
151
209
  const tokens = existing?.tokens ? { ...existing.tokens } : {};
152
210
  const roleForToken = normalizeRole(pending.role);
153
211
  if (roleForToken) {
154
- const nextScopes = normalizeDeviceAuthScopes(pending.scopes);
155
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);
156
220
  const now = Date.now();
157
221
  tokens[roleForToken] = {
158
222
  token: newToken(),
@@ -173,7 +237,8 @@ export async function approveDevicePairing(requestId, baseDir) {
173
237
  clientMode: pending.clientMode,
174
238
  role: pending.role,
175
239
  roles,
176
- scopes,
240
+ scopes: approvedScopes,
241
+ approvedScopes,
177
242
  remoteIp: pending.remoteIp,
178
243
  tokens,
179
244
  createdAtMs: existing?.createdAtMs ?? now,
@@ -224,6 +289,7 @@ export async function updatePairedDeviceMetadata(deviceId, patch, baseDir) {
224
289
  deviceId: existing.deviceId,
225
290
  createdAtMs: existing.createdAtMs,
226
291
  approvedAtMs: existing.approvedAtMs,
292
+ approvedScopes: existing.approvedScopes,
227
293
  role: patch.role ?? existing.role,
228
294
  roles,
229
295
  scopes,
@@ -339,6 +405,10 @@ export async function rotateDeviceToken(params) {
339
405
  }
340
406
  const { device, role, tokens, existing } = context;
341
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)) {
410
+ return null;
411
+ }
342
412
  const now = Date.now();
343
413
  const next = buildDeviceAuthToken({
344
414
  role,
@@ -349,9 +419,6 @@ export async function rotateDeviceToken(params) {
349
419
  });
350
420
  tokens[role] = next;
351
421
  device.tokens = tokens;
352
- if (params.scopes !== undefined) {
353
- device.scopes = requestedScopes;
354
- }
355
422
  state.pairedByDeviceId[device.deviceId] = device;
356
423
  await persistState(state, params.baseDir);
357
424
  return next;
@@ -380,3 +447,15 @@ export async function revokeDeviceToken(params) {
380
447
  return entry;
381
448
  });
382
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
  }
@@ -127,6 +127,128 @@ export function evaluateExecAllowlist(params) {
127
127
  segmentSatisfiedBy: result.segmentSatisfiedBy,
128
128
  };
129
129
  }
130
+ const SHELL_WRAPPER_EXECUTABLES = new Set([
131
+ "ash",
132
+ "bash",
133
+ "cmd",
134
+ "cmd.exe",
135
+ "dash",
136
+ "fish",
137
+ "ksh",
138
+ "powershell",
139
+ "powershell.exe",
140
+ "pwsh",
141
+ "pwsh.exe",
142
+ "sh",
143
+ "zsh",
144
+ ]);
145
+ function normalizeExecutableName(name) {
146
+ return (name ?? "").trim().toLowerCase();
147
+ }
148
+ function isShellWrapperSegment(segment) {
149
+ const candidates = [
150
+ normalizeExecutableName(segment.resolution?.executableName),
151
+ normalizeExecutableName(segment.resolution?.rawExecutable),
152
+ ];
153
+ for (const candidate of candidates) {
154
+ if (!candidate) {
155
+ continue;
156
+ }
157
+ if (SHELL_WRAPPER_EXECUTABLES.has(candidate)) {
158
+ return true;
159
+ }
160
+ const base = candidate.split(/[\\/]/).pop();
161
+ if (base && SHELL_WRAPPER_EXECUTABLES.has(base)) {
162
+ return true;
163
+ }
164
+ }
165
+ return false;
166
+ }
167
+ function extractShellInlineCommand(argv) {
168
+ for (let i = 1; i < argv.length; i += 1) {
169
+ const token = argv[i];
170
+ if (!token) {
171
+ continue;
172
+ }
173
+ const lower = token.toLowerCase();
174
+ if (lower === "--") {
175
+ break;
176
+ }
177
+ if (lower === "-c" ||
178
+ lower === "--command" ||
179
+ lower === "-command" ||
180
+ lower === "/c" ||
181
+ lower === "/k") {
182
+ const next = argv[i + 1]?.trim();
183
+ return next ? next : null;
184
+ }
185
+ if (/^-[^-]*c[^-]*$/i.test(token)) {
186
+ const commandIndex = lower.indexOf("c");
187
+ const inline = token.slice(commandIndex + 1).trim();
188
+ if (inline) {
189
+ return inline;
190
+ }
191
+ const next = argv[i + 1]?.trim();
192
+ return next ? next : null;
193
+ }
194
+ }
195
+ return null;
196
+ }
197
+ function collectAllowAlwaysPatterns(params) {
198
+ const candidatePath = resolveAllowlistCandidatePath(params.segment.resolution, params.cwd);
199
+ if (!candidatePath) {
200
+ return;
201
+ }
202
+ if (!isShellWrapperSegment(params.segment)) {
203
+ params.out.add(candidatePath);
204
+ return;
205
+ }
206
+ if (params.depth >= 3) {
207
+ return;
208
+ }
209
+ const inlineCommand = extractShellInlineCommand(params.segment.argv);
210
+ if (!inlineCommand) {
211
+ return;
212
+ }
213
+ const nested = analyzeShellCommand({
214
+ command: inlineCommand,
215
+ cwd: params.cwd,
216
+ env: params.env,
217
+ platform: params.platform,
218
+ });
219
+ if (!nested.ok) {
220
+ return;
221
+ }
222
+ for (const nestedSegment of nested.segments) {
223
+ collectAllowAlwaysPatterns({
224
+ segment: nestedSegment,
225
+ cwd: params.cwd,
226
+ env: params.env,
227
+ platform: params.platform,
228
+ depth: params.depth + 1,
229
+ out: params.out,
230
+ });
231
+ }
232
+ }
233
+ /**
234
+ * Derive persisted allowlist patterns for an "allow always" decision.
235
+ * When a command is wrapped in a shell (for example `zsh -lc "<cmd>"`),
236
+ * persist the inner executable(s) rather than the shell binary.
237
+ */
238
+ export function resolveAllowAlwaysPatterns(params) {
239
+ const patterns = new Set();
240
+ for (const segment of params.segments) {
241
+ collectAllowAlwaysPatterns({
242
+ segment,
243
+ cwd: params.cwd,
244
+ env: params.env,
245
+ platform: params.platform,
246
+ depth: 0,
247
+ out: patterns,
248
+ });
249
+ }
250
+ return Array.from(patterns);
251
+ }
130
252
  /**
131
253
  * Evaluates allowlist for shell commands (including &&, ||, ;) and returns analysis metadata.
132
254
  */
@@ -269,7 +269,7 @@ function splitShellPipeline(command) {
269
269
  continue;
270
270
  }
271
271
  if (ch === quote) {
272
- return { delimiter, end: i + 1 };
272
+ return { delimiter, end: i + 1, quoted: true };
273
273
  }
274
274
  delimiter += ch;
275
275
  i += 1;
@@ -288,7 +288,7 @@ function splitShellPipeline(command) {
288
288
  if (!delimiter) {
289
289
  return null;
290
290
  }
291
- return { delimiter, end: i };
291
+ return { delimiter, end: i, quoted: false };
292
292
  };
293
293
  const segments = [];
294
294
  let buf = "";
@@ -306,6 +306,28 @@ function splitShellPipeline(command) {
306
306
  }
307
307
  buf = "";
308
308
  };
309
+ const isEscapedInHeredocLine = (line, index) => {
310
+ let slashes = 0;
311
+ for (let i = index - 1; i >= 0 && line[i] === "\\"; i -= 1) {
312
+ slashes += 1;
313
+ }
314
+ return slashes % 2 === 1;
315
+ };
316
+ const hasUnquotedHeredocExpansionToken = (line) => {
317
+ for (let i = 0; i < line.length; i += 1) {
318
+ const ch = line[i];
319
+ if (ch === "`" && !isEscapedInHeredocLine(line, i)) {
320
+ return true;
321
+ }
322
+ if (ch === "$" && !isEscapedInHeredocLine(line, i)) {
323
+ const next = line[i + 1];
324
+ if (next === "(" || next === "{") {
325
+ return true;
326
+ }
327
+ }
328
+ }
329
+ return false;
330
+ };
309
331
  for (let i = 0; i < command.length; i += 1) {
310
332
  const ch = command[i];
311
333
  const next = command[i + 1];
@@ -317,6 +339,9 @@ function splitShellPipeline(command) {
317
339
  if (line === current.delimiter) {
318
340
  pendingHeredocs.shift();
319
341
  }
342
+ else if (!current.quoted && hasUnquotedHeredocExpansionToken(heredocLine)) {
343
+ return { ok: false, reason: "command substitution in unquoted heredoc", segments: [] };
344
+ }
320
345
  }
321
346
  heredocLine = "";
322
347
  if (pendingHeredocs.length === 0) {
@@ -422,7 +447,7 @@ function splitShellPipeline(command) {
422
447
  }
423
448
  const parsed = parseHeredocDelimiter(command, scanIndex);
424
449
  if (parsed) {
425
- pendingHeredocs.push({ delimiter: parsed.delimiter, stripTabs });
450
+ pendingHeredocs.push({ delimiter: parsed.delimiter, stripTabs, quoted: parsed.quoted });
426
451
  buf += command.slice(scanIndex, parsed.end);
427
452
  i = parsed.end - 1;
428
453
  }
@@ -442,8 +467,14 @@ function splitShellPipeline(command) {
442
467
  const line = current.stripTabs ? heredocLine.replace(/^\t+/, "") : heredocLine;
443
468
  if (line === current.delimiter) {
444
469
  pendingHeredocs.shift();
470
+ if (pendingHeredocs.length === 0) {
471
+ inHeredocBody = false;
472
+ }
445
473
  }
446
474
  }
475
+ if (pendingHeredocs.length > 0 || inHeredocBody) {
476
+ return { ok: false, reason: "unterminated heredoc", segments: [] };
477
+ }
447
478
  if (escaped || inSingle || inDouble) {
448
479
  return { ok: false, reason: "unterminated shell quote/escape", segments: [] };
449
480
  }
@@ -1,8 +1,8 @@
1
1
  import crypto from "node:crypto";
2
2
  import fs from "node:fs";
3
- import os from "node:os";
4
3
  import path from "node:path";
5
4
  import { DEFAULT_AGENT_ID } from "../routing/session-key.js";
5
+ import { expandHomePrefix } from "./home-dir.js";
6
6
  import { requestJsonlSocket } from "./jsonl-socket.js";
7
7
  export * from "./exec-approvals-analysis.js";
8
8
  export * from "./exec-approvals-allowlist.js";
@@ -20,23 +20,11 @@ function hashExecApprovalsRaw(raw) {
20
20
  .update(raw ?? "")
21
21
  .digest("hex");
22
22
  }
23
- function expandHome(value) {
24
- if (!value) {
25
- return value;
26
- }
27
- if (value === "~") {
28
- return os.homedir();
29
- }
30
- if (value.startsWith("~/")) {
31
- return path.join(os.homedir(), value.slice(2));
32
- }
33
- return value;
34
- }
35
23
  export function resolveExecApprovalsPath() {
36
- return expandHome(DEFAULT_FILE);
24
+ return expandHomePrefix(DEFAULT_FILE);
37
25
  }
38
26
  export function resolveExecApprovalsSocketPath() {
39
- return expandHome(DEFAULT_SOCKET);
27
+ return expandHomePrefix(DEFAULT_SOCKET);
40
28
  }
41
29
  function normalizeAllowlistPattern(value) {
42
30
  const trimmed = value?.trim() ?? "";
@@ -261,7 +249,7 @@ export function resolveExecApprovals(agentId, overrides) {
261
249
  agentId,
262
250
  overrides,
263
251
  path: resolveExecApprovalsPath(),
264
- socketPath: expandHome(file.socket?.path ?? resolveExecApprovalsSocketPath()),
252
+ socketPath: expandHomePrefix(file.socket?.path ?? resolveExecApprovalsSocketPath()),
265
253
  token: file.socket?.token ?? "",
266
254
  });
267
255
  }
@@ -293,7 +281,7 @@ export function resolveExecApprovalsFromFile(params) {
293
281
  ];
294
282
  return {
295
283
  path: params.path ?? resolveExecApprovalsPath(),
296
- socketPath: expandHome(params.socketPath ?? file.socket?.path ?? resolveExecApprovalsSocketPath()),
284
+ socketPath: expandHomePrefix(params.socketPath ?? file.socket?.path ?? resolveExecApprovalsSocketPath()),
297
285
  token: params.token ?? file.socket?.token ?? "",
298
286
  defaults: resolvedDefaults,
299
287
  agent: resolvedAgent,