@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
@@ -17,10 +17,11 @@ import { getQueueSize } from "../process/command-queue.js";
17
17
  import { normalizeAgentId, toAgentStoreSessionKey } from "../routing/session-key.js";
18
18
  import { defaultRuntime } from "../runtime.js";
19
19
  import { escapeRegExp } from "../utils.js";
20
- import { formatErrorMessage } from "./errors.js";
20
+ import { formatErrorMessage, hasErrnoCode } from "./errors.js";
21
21
  import { isWithinActiveHours } from "./heartbeat-active-hours.js";
22
22
  import { buildCronEventPrompt, isCronSystemEvent, isExecCompletionEvent, } from "./heartbeat-events-filter.js";
23
23
  import { emitHeartbeatEvent, resolveIndicatorType } from "./heartbeat-events.js";
24
+ import { resolveHeartbeatReasonKind } from "./heartbeat-reason.js";
24
25
  import { resolveHeartbeatVisibility } from "./heartbeat-visibility.js";
25
26
  import { requestHeartbeatNow, setHeartbeatWakeHandler, } from "./heartbeat-wake.js";
26
27
  import { deliverOutboundPayloads } from "./outbound/deliver.js";
@@ -322,6 +323,56 @@ function normalizeHeartbeatReply(payload, responsePrefix, ackMaxChars) {
322
323
  }
323
324
  return { shouldSkip: false, text: finalText, hasMedia };
324
325
  }
326
+ function resolveHeartbeatReasonFlags(reason) {
327
+ const reasonKind = resolveHeartbeatReasonKind(reason);
328
+ return {
329
+ isExecEventReason: reasonKind === "exec-event",
330
+ isCronEventReason: reasonKind === "cron",
331
+ isWakeReason: reasonKind === "wake" || reasonKind === "hook",
332
+ };
333
+ }
334
+ async function resolveHeartbeatPreflight(params) {
335
+ const reasonFlags = resolveHeartbeatReasonFlags(params.reason);
336
+ const session = resolveHeartbeatSession(params.cfg, params.agentId, params.heartbeat, params.forcedSessionKey);
337
+ const pendingEventEntries = peekSystemEventEntries(session.sessionKey);
338
+ const hasTaggedCronEvents = pendingEventEntries.some((event) => event.contextKey?.startsWith("cron:"));
339
+ const shouldInspectPendingEvents = reasonFlags.isExecEventReason || reasonFlags.isCronEventReason || hasTaggedCronEvents;
340
+ const shouldBypassFileGates = reasonFlags.isExecEventReason ||
341
+ reasonFlags.isCronEventReason ||
342
+ reasonFlags.isWakeReason ||
343
+ hasTaggedCronEvents;
344
+ const basePreflight = {
345
+ ...reasonFlags,
346
+ session,
347
+ pendingEventEntries,
348
+ hasTaggedCronEvents,
349
+ shouldInspectPendingEvents,
350
+ };
351
+ if (shouldBypassFileGates) {
352
+ return basePreflight;
353
+ }
354
+ const workspaceDir = resolveAgentWorkspaceDir(params.cfg, params.agentId);
355
+ const heartbeatFilePath = path.join(workspaceDir, DEFAULT_HEARTBEAT_FILENAME);
356
+ try {
357
+ const heartbeatFileContent = await fs.readFile(heartbeatFilePath, "utf-8");
358
+ if (isHeartbeatContentEffectivelyEmpty(heartbeatFileContent)) {
359
+ return {
360
+ ...basePreflight,
361
+ skipReason: "empty-heartbeat-file",
362
+ };
363
+ }
364
+ }
365
+ catch (err) {
366
+ if (hasErrnoCode(err, "ENOENT")) {
367
+ // Missing HEARTBEAT.md is intentional in some setups (for example, when
368
+ // heartbeat instructions live outside the file), so keep the run active.
369
+ // The heartbeat prompt already says "if it exists".
370
+ return basePreflight;
371
+ }
372
+ // For other read errors, proceed with heartbeat as before.
373
+ }
374
+ return basePreflight;
375
+ }
325
376
  export async function runHeartbeatOnce(opts) {
326
377
  const cfg = opts.cfg ?? loadConfig();
327
378
  const agentId = normalizeAgentId(opts.agentId ?? resolveDefaultAgentId(cfg));
@@ -343,34 +394,24 @@ export async function runHeartbeatOnce(opts) {
343
394
  if (queueSize > 0) {
344
395
  return { status: "skipped", reason: "requests-in-flight" };
345
396
  }
346
- // Skip heartbeat if HEARTBEAT.md exists but has no actionable content.
347
- // This saves API calls/costs when the file is effectively empty (only comments/headers).
348
- // EXCEPTION: Don't skip for exec events, cron events, or explicit wake requests -
349
- // they have pending system events to process regardless of HEARTBEAT.md content.
350
- const isExecEventReason = opts.reason === "exec-event";
351
- const isCronEventReason = Boolean(opts.reason?.startsWith("cron:"));
352
- const isWakeReason = opts.reason === "wake" || Boolean(opts.reason?.startsWith("hook:"));
353
- const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
354
- const heartbeatFilePath = path.join(workspaceDir, DEFAULT_HEARTBEAT_FILENAME);
355
- try {
356
- const heartbeatFileContent = await fs.readFile(heartbeatFilePath, "utf-8");
357
- if (isHeartbeatContentEffectivelyEmpty(heartbeatFileContent) &&
358
- !isExecEventReason &&
359
- !isCronEventReason &&
360
- !isWakeReason) {
361
- emitHeartbeatEvent({
362
- status: "skipped",
363
- reason: "empty-heartbeat-file",
364
- durationMs: Date.now() - startedAt,
365
- });
366
- return { status: "skipped", reason: "empty-heartbeat-file" };
367
- }
368
- }
369
- catch {
370
- // File doesn't exist or can't be read - proceed with heartbeat.
371
- // The LLM prompt says "if it exists" so this is expected behavior.
397
+ // Preflight centralizes trigger classification, event inspection, and HEARTBEAT.md gating.
398
+ const preflight = await resolveHeartbeatPreflight({
399
+ cfg,
400
+ agentId,
401
+ heartbeat,
402
+ forcedSessionKey: opts.sessionKey,
403
+ reason: opts.reason,
404
+ });
405
+ if (preflight.skipReason) {
406
+ emitHeartbeatEvent({
407
+ status: "skipped",
408
+ reason: preflight.skipReason,
409
+ durationMs: Date.now() - startedAt,
410
+ });
411
+ return { status: "skipped", reason: preflight.skipReason };
372
412
  }
373
- const { entry, sessionKey, storePath } = resolveHeartbeatSession(cfg, agentId, heartbeat, opts.sessionKey);
413
+ const { entry, sessionKey, storePath } = preflight.session;
414
+ const { isCronEventReason, pendingEventEntries } = preflight;
374
415
  const previousUpdatedAt = entry?.updatedAt;
375
416
  const delivery = resolveHeartbeatDeliveryTarget({ cfg, entry, heartbeat });
376
417
  const heartbeatAccountId = heartbeat?.accountId?.trim();
@@ -402,10 +443,7 @@ export async function runHeartbeatOnce(opts) {
402
443
  // Check if this is an exec event or cron event with pending system events.
403
444
  // If so, use a specialized prompt that instructs the model to relay the result
404
445
  // instead of the standard heartbeat prompt with "reply HEARTBEAT_OK".
405
- const isExecEvent = opts.reason === "exec-event";
406
- const pendingEventEntries = peekSystemEventEntries(sessionKey);
407
- const hasTaggedCronEvents = pendingEventEntries.some((event) => event.contextKey?.startsWith("cron:"));
408
- const shouldInspectPendingEvents = isExecEvent || isCronEventReason || hasTaggedCronEvents;
446
+ const shouldInspectPendingEvents = preflight.shouldInspectPendingEvents;
409
447
  const pendingEvents = shouldInspectPendingEvents
410
448
  ? pendingEventEntries.map((event) => event.text)
411
449
  : [];
@@ -459,6 +497,7 @@ export async function runHeartbeatOnce(opts) {
459
497
  channel: delivery.channel,
460
498
  to: delivery.to,
461
499
  accountId: delivery.accountId,
500
+ threadId: delivery.threadId,
462
501
  payloads: [{ text: heartbeatOkText }],
463
502
  agentId,
464
503
  deps: opts.deps,
@@ -635,6 +674,7 @@ export async function runHeartbeatOnce(opts) {
635
674
  to: delivery.to,
636
675
  accountId: deliveryAccountId,
637
676
  agentId,
677
+ threadId: delivery.threadId,
638
678
  payloads: [
639
679
  ...reasoningPayloads,
640
680
  ...(shouldSkipMain
@@ -1,3 +1,4 @@
1
+ import { isHeartbeatActionWakeReason, normalizeHeartbeatWakeReason, resolveHeartbeatReasonKind, } from "./heartbeat-reason.js";
1
2
  let handler = null;
2
3
  let handlerGeneration = 0;
3
4
  const pendingWakes = new Map();
@@ -8,34 +9,27 @@ let timerDueAt = null;
8
9
  let timerKind = null;
9
10
  const DEFAULT_COALESCE_MS = 250;
10
11
  const DEFAULT_RETRY_MS = 1_000;
11
- const HOOK_REASON_PREFIX = "hook:";
12
12
  const REASON_PRIORITY = {
13
13
  RETRY: 0,
14
14
  INTERVAL: 1,
15
15
  DEFAULT: 2,
16
16
  ACTION: 3,
17
17
  };
18
- function isActionWakeReason(reason) {
19
- return reason === "manual" || reason === "exec-event" || reason.startsWith(HOOK_REASON_PREFIX);
20
- }
21
18
  function resolveReasonPriority(reason) {
22
- if (reason === "retry") {
19
+ const kind = resolveHeartbeatReasonKind(reason);
20
+ if (kind === "retry") {
23
21
  return REASON_PRIORITY.RETRY;
24
22
  }
25
- if (reason === "interval") {
23
+ if (kind === "interval") {
26
24
  return REASON_PRIORITY.INTERVAL;
27
25
  }
28
- if (isActionWakeReason(reason)) {
26
+ if (isHeartbeatActionWakeReason(reason)) {
29
27
  return REASON_PRIORITY.ACTION;
30
28
  }
31
29
  return REASON_PRIORITY.DEFAULT;
32
30
  }
33
31
  function normalizeWakeReason(reason) {
34
- if (typeof reason !== "string") {
35
- return "requested";
36
- }
37
- const trimmed = reason.trim();
38
- return trimmed.length > 0 ? trimmed : "requested";
32
+ return normalizeHeartbeatWakeReason(reason);
39
33
  }
40
34
  function normalizeWakeTarget(value) {
41
35
  const trimmed = typeof value === "string" ? value.trim() : "";
@@ -0,0 +1,19 @@
1
+ {
2
+ "blockedKeys": [
3
+ "NODE_OPTIONS",
4
+ "NODE_PATH",
5
+ "PYTHONHOME",
6
+ "PYTHONPATH",
7
+ "PERL5LIB",
8
+ "PERL5OPT",
9
+ "RUBYLIB",
10
+ "RUBYOPT",
11
+ "BASH_ENV",
12
+ "ENV",
13
+ "SHELL",
14
+ "GCONV_PATH",
15
+ "IFS",
16
+ "SSLKEYLOGFILE"
17
+ ],
18
+ "blockedPrefixes": ["DYLD_", "LD_", "BASH_FUNC_"]
19
+ }
@@ -0,0 +1,66 @@
1
+ import HOST_ENV_SECURITY_POLICY_JSON from "./host-env-security-policy.json" with { type: "json" };
2
+ const PORTABLE_ENV_VAR_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
3
+ const HOST_ENV_SECURITY_POLICY = HOST_ENV_SECURITY_POLICY_JSON;
4
+ export const HOST_DANGEROUS_ENV_KEY_VALUES = Object.freeze(HOST_ENV_SECURITY_POLICY.blockedKeys.map((key) => key.toUpperCase()));
5
+ export const HOST_DANGEROUS_ENV_PREFIXES = Object.freeze(HOST_ENV_SECURITY_POLICY.blockedPrefixes.map((prefix) => prefix.toUpperCase()));
6
+ export const HOST_DANGEROUS_ENV_KEYS = new Set(HOST_DANGEROUS_ENV_KEY_VALUES);
7
+ export function normalizeEnvVarKey(rawKey, options) {
8
+ const key = rawKey.trim();
9
+ if (!key) {
10
+ return null;
11
+ }
12
+ if (options?.portable && !PORTABLE_ENV_VAR_KEY.test(key)) {
13
+ return null;
14
+ }
15
+ return key;
16
+ }
17
+ export function isDangerousHostEnvVarName(rawKey) {
18
+ const key = normalizeEnvVarKey(rawKey);
19
+ if (!key) {
20
+ return false;
21
+ }
22
+ const upper = key.toUpperCase();
23
+ if (HOST_DANGEROUS_ENV_KEYS.has(upper)) {
24
+ return true;
25
+ }
26
+ return HOST_DANGEROUS_ENV_PREFIXES.some((prefix) => upper.startsWith(prefix));
27
+ }
28
+ export function sanitizeHostExecEnv(params) {
29
+ const baseEnv = params?.baseEnv ?? process.env;
30
+ const overrides = params?.overrides ?? undefined;
31
+ const blockPathOverrides = params?.blockPathOverrides ?? true;
32
+ const merged = {};
33
+ for (const [rawKey, value] of Object.entries(baseEnv)) {
34
+ if (typeof value !== "string") {
35
+ continue;
36
+ }
37
+ const key = normalizeEnvVarKey(rawKey, { portable: true });
38
+ if (!key || isDangerousHostEnvVarName(key)) {
39
+ continue;
40
+ }
41
+ merged[key] = value;
42
+ }
43
+ if (!overrides) {
44
+ return merged;
45
+ }
46
+ for (const [rawKey, value] of Object.entries(overrides)) {
47
+ if (typeof value !== "string") {
48
+ continue;
49
+ }
50
+ const key = normalizeEnvVarKey(rawKey, { portable: true });
51
+ if (!key) {
52
+ continue;
53
+ }
54
+ const upper = key.toUpperCase();
55
+ // PATH is part of the security boundary (command resolution + safe-bin checks). Never allow
56
+ // request-scoped PATH overrides from agents/gateways.
57
+ if (blockPathOverrides && upper === "PATH") {
58
+ continue;
59
+ }
60
+ if (isDangerousHostEnvVarName(upper)) {
61
+ continue;
62
+ }
63
+ merged[key] = value;
64
+ }
65
+ return merged;
66
+ }
@@ -23,8 +23,86 @@ export async function resolveArchiveSourcePath(archivePath) {
23
23
  }
24
24
  return { ok: true, path: resolved };
25
25
  }
26
+ function toOptionalString(value) {
27
+ if (typeof value !== "string") {
28
+ return undefined;
29
+ }
30
+ const trimmed = value.trim();
31
+ return trimmed.length > 0 ? trimmed : undefined;
32
+ }
33
+ function parseResolvedSpecFromId(id) {
34
+ const at = id.lastIndexOf("@");
35
+ if (at <= 0 || at >= id.length - 1) {
36
+ return undefined;
37
+ }
38
+ const name = id.slice(0, at).trim();
39
+ const version = id.slice(at + 1).trim();
40
+ if (!name || !version) {
41
+ return undefined;
42
+ }
43
+ return `${name}@${version}`;
44
+ }
45
+ function normalizeNpmPackEntry(entry) {
46
+ if (!entry || typeof entry !== "object") {
47
+ return null;
48
+ }
49
+ const rec = entry;
50
+ const name = toOptionalString(rec.name);
51
+ const version = toOptionalString(rec.version);
52
+ const id = toOptionalString(rec.id);
53
+ const resolvedSpec = (name && version ? `${name}@${version}` : undefined) ??
54
+ (id ? parseResolvedSpecFromId(id) : undefined);
55
+ return {
56
+ filename: toOptionalString(rec.filename),
57
+ metadata: {
58
+ name,
59
+ version,
60
+ resolvedSpec,
61
+ integrity: toOptionalString(rec.integrity),
62
+ shasum: toOptionalString(rec.shasum),
63
+ },
64
+ };
65
+ }
66
+ function parseNpmPackJsonOutput(raw) {
67
+ const trimmed = raw.trim();
68
+ if (!trimmed) {
69
+ return null;
70
+ }
71
+ const candidates = [trimmed];
72
+ const arrayStart = trimmed.indexOf("[");
73
+ if (arrayStart > 0) {
74
+ candidates.push(trimmed.slice(arrayStart));
75
+ }
76
+ for (const candidate of candidates) {
77
+ let parsed;
78
+ try {
79
+ parsed = JSON.parse(candidate);
80
+ }
81
+ catch {
82
+ continue;
83
+ }
84
+ const entries = Array.isArray(parsed) ? parsed : [parsed];
85
+ let fallback = null;
86
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
87
+ const normalized = normalizeNpmPackEntry(entries[i]);
88
+ if (!normalized) {
89
+ continue;
90
+ }
91
+ if (!fallback) {
92
+ fallback = normalized;
93
+ }
94
+ if (normalized.filename) {
95
+ return normalized;
96
+ }
97
+ }
98
+ if (fallback) {
99
+ return fallback;
100
+ }
101
+ }
102
+ return null;
103
+ }
26
104
  export async function packNpmSpecToArchive(params) {
27
- const res = await runCommandWithTimeout(["npm", "pack", params.spec, "--ignore-scripts"], {
105
+ const res = await runCommandWithTimeout(["npm", "pack", params.spec, "--ignore-scripts", "--json"], {
28
106
  timeoutMs: Math.max(params.timeoutMs, 300_000),
29
107
  cwd: params.cwd,
30
108
  env: {
@@ -35,13 +113,19 @@ export async function packNpmSpecToArchive(params) {
35
113
  if (res.code !== 0) {
36
114
  return { ok: false, error: `npm pack failed: ${res.stderr.trim() || res.stdout.trim()}` };
37
115
  }
38
- const packed = (res.stdout || "")
39
- .split("\n")
40
- .map((line) => line.trim())
41
- .filter(Boolean)
42
- .pop();
116
+ const parsedJson = parseNpmPackJsonOutput(res.stdout || "");
117
+ const packed = parsedJson?.filename ??
118
+ (res.stdout || "")
119
+ .split("\n")
120
+ .map((line) => line.trim())
121
+ .filter(Boolean)
122
+ .pop();
43
123
  if (!packed) {
44
124
  return { ok: false, error: "npm pack produced no archive" };
45
125
  }
46
- return { ok: true, archivePath: path.join(params.cwd, packed) };
126
+ return {
127
+ ok: true,
128
+ archivePath: path.join(params.cwd, packed),
129
+ metadata: parsedJson?.metadata ?? {},
130
+ };
47
131
  }
@@ -8,7 +8,11 @@ export class SsrFBlockedError extends Error {
8
8
  this.name = "SsrFBlockedError";
9
9
  }
10
10
  }
11
- const BLOCKED_HOSTNAMES = new Set(["localhost", "metadata.google.internal"]);
11
+ const BLOCKED_HOSTNAMES = new Set([
12
+ "localhost",
13
+ "localhost.localdomain",
14
+ "metadata.google.internal",
15
+ ]);
12
16
  function normalizeHostnameSet(values) {
13
17
  if (!values || values.length === 0) {
14
18
  return new Set();
@@ -39,16 +43,72 @@ function matchesHostnameAllowlist(hostname, allowlist) {
39
43
  }
40
44
  return allowlist.some((pattern) => isHostnameAllowedByPattern(hostname, pattern));
41
45
  }
46
+ function parseStrictIpv4Octet(part) {
47
+ if (!/^[0-9]+$/.test(part)) {
48
+ return null;
49
+ }
50
+ const value = Number.parseInt(part, 10);
51
+ if (Number.isNaN(value) || value < 0 || value > 255) {
52
+ return null;
53
+ }
54
+ // Accept only canonical decimal octets (no leading zeros, no alternate radices).
55
+ if (part !== String(value)) {
56
+ return null;
57
+ }
58
+ return value;
59
+ }
42
60
  function parseIpv4(address) {
43
61
  const parts = address.split(".");
44
62
  if (parts.length !== 4) {
45
63
  return null;
46
64
  }
47
- const numbers = parts.map((part) => Number.parseInt(part, 10));
48
- if (numbers.some((value) => Number.isNaN(value) || value < 0 || value > 255)) {
49
- return null;
65
+ for (const part of parts) {
66
+ if (parseStrictIpv4Octet(part) === null) {
67
+ return null;
68
+ }
69
+ }
70
+ return parts.map((part) => Number.parseInt(part, 10));
71
+ }
72
+ function classifyIpv4Part(part) {
73
+ if (/^0x[0-9a-f]+$/i.test(part)) {
74
+ return "hex";
75
+ }
76
+ if (/^0x/i.test(part)) {
77
+ return "invalid-hex";
78
+ }
79
+ if (/^[0-9]+$/.test(part)) {
80
+ return "decimal";
81
+ }
82
+ return "non-numeric";
83
+ }
84
+ function isUnsupportedLegacyIpv4Literal(address) {
85
+ const parts = address.split(".");
86
+ if (parts.length === 0 || parts.length > 4) {
87
+ return false;
88
+ }
89
+ if (parts.some((part) => part.length === 0)) {
90
+ return true;
91
+ }
92
+ const partKinds = parts.map(classifyIpv4Part);
93
+ if (partKinds.some((kind) => kind === "non-numeric")) {
94
+ return false;
50
95
  }
51
- return numbers;
96
+ if (partKinds.some((kind) => kind === "invalid-hex")) {
97
+ return true;
98
+ }
99
+ if (parts.length !== 4) {
100
+ return true;
101
+ }
102
+ for (const part of parts) {
103
+ if (/^0x/i.test(part)) {
104
+ return true;
105
+ }
106
+ const value = Number.parseInt(part, 10);
107
+ if (Number.isNaN(value) || value > 255 || part !== String(value)) {
108
+ return true;
109
+ }
110
+ }
111
+ return false;
52
112
  }
53
113
  function stripIpv6ZoneId(address) {
54
114
  const index = address.indexOf("%");
@@ -148,6 +208,12 @@ const EMBEDDED_IPV4_RULES = [
148
208
  matches: (hextets) => hextets[0] === 0x2001 && hextets[1] === 0x0000,
149
209
  extract: (hextets) => [hextets[6] ^ 0xffff, hextets[7] ^ 0xffff],
150
210
  },
211
+ {
212
+ // ISATAP IID format: 000000ug00000000:5efe:w.x.y.z (RFC 5214 section 6.1).
213
+ // Match only the IID marker bits to avoid over-broad :5efe: detection.
214
+ matches: (hextets) => (hextets[4] & 0xfcff) === 0 && hextets[5] === 0x5efe,
215
+ extract: (hextets) => [hextets[6], hextets[7]],
216
+ },
151
217
  ];
152
218
  function extractIpv4FromEmbeddedIpv6(hextets) {
153
219
  for (const rule of EMBEDDED_IPV4_RULES) {
@@ -159,31 +225,49 @@ function extractIpv4FromEmbeddedIpv6(hextets) {
159
225
  }
160
226
  return null;
161
227
  }
162
- function isPrivateIpv4(parts) {
163
- const [octet1, octet2] = parts;
164
- if (octet1 === 0) {
165
- return true;
166
- }
167
- if (octet1 === 10) {
168
- return true;
169
- }
170
- if (octet1 === 127) {
171
- return true;
172
- }
173
- if (octet1 === 169 && octet2 === 254) {
174
- return true;
175
- }
176
- if (octet1 === 172 && octet2 >= 16 && octet2 <= 31) {
177
- return true;
178
- }
179
- if (octet1 === 192 && octet2 === 168) {
180
- return true;
228
+ function ipv4ToUint(parts) {
229
+ const [a, b, c, d] = parts;
230
+ return (((a << 24) >>> 0) | (b << 16) | (c << 8) | d) >>> 0;
231
+ }
232
+ function ipv4RangeFromCidr(cidr) {
233
+ const base = ipv4ToUint(cidr.base);
234
+ const hostBits = 32 - cidr.prefixLength;
235
+ const mask = cidr.prefixLength === 0 ? 0 : (0xffffffff << hostBits) >>> 0;
236
+ const start = (base & mask) >>> 0;
237
+ const end = (start | (~mask >>> 0)) >>> 0;
238
+ return [start, end];
239
+ }
240
+ const BLOCKED_IPV4_SPECIAL_USE_CIDRS = [
241
+ { base: [0, 0, 0, 0], prefixLength: 8 },
242
+ { base: [10, 0, 0, 0], prefixLength: 8 },
243
+ { base: [100, 64, 0, 0], prefixLength: 10 },
244
+ { base: [127, 0, 0, 0], prefixLength: 8 },
245
+ { base: [169, 254, 0, 0], prefixLength: 16 },
246
+ { base: [172, 16, 0, 0], prefixLength: 12 },
247
+ { base: [192, 0, 0, 0], prefixLength: 24 },
248
+ { base: [192, 0, 2, 0], prefixLength: 24 },
249
+ { base: [192, 88, 99, 0], prefixLength: 24 },
250
+ { base: [192, 168, 0, 0], prefixLength: 16 },
251
+ { base: [198, 18, 0, 0], prefixLength: 15 },
252
+ { base: [198, 51, 100, 0], prefixLength: 24 },
253
+ { base: [203, 0, 113, 0], prefixLength: 24 },
254
+ { base: [224, 0, 0, 0], prefixLength: 4 },
255
+ { base: [240, 0, 0, 0], prefixLength: 4 },
256
+ ];
257
+ const BLOCKED_IPV4_SPECIAL_USE_RANGES = BLOCKED_IPV4_SPECIAL_USE_CIDRS.map(ipv4RangeFromCidr);
258
+ function isBlockedIpv4SpecialUse(parts) {
259
+ if (parts.length !== 4) {
260
+ return false;
181
261
  }
182
- if (octet1 === 100 && octet2 >= 64 && octet2 <= 127) {
183
- return true;
262
+ const value = ipv4ToUint(parts);
263
+ for (const [start, end] of BLOCKED_IPV4_SPECIAL_USE_RANGES) {
264
+ if (value >= start && value <= end) {
265
+ return true;
266
+ }
184
267
  }
185
268
  return false;
186
269
  }
270
+ // Returns true for private/internal and special-use non-global addresses.
187
271
  export function isPrivateIpAddress(address) {
188
272
  let normalized = address.trim().toLowerCase();
189
273
  if (normalized.startsWith("[") && normalized.endsWith("]")) {
@@ -219,7 +303,7 @@ export function isPrivateIpAddress(address) {
219
303
  }
220
304
  const embeddedIpv4 = extractIpv4FromEmbeddedIpv6(hextets);
221
305
  if (embeddedIpv4) {
222
- return isPrivateIpv4(embeddedIpv4);
306
+ return isBlockedIpv4SpecialUse(embeddedIpv4);
223
307
  }
224
308
  // IPv6 private/internal ranges
225
309
  // - link-local: fe80::/10
@@ -238,16 +322,23 @@ export function isPrivateIpAddress(address) {
238
322
  return false;
239
323
  }
240
324
  const ipv4 = parseIpv4(normalized);
241
- if (!ipv4) {
242
- return false;
325
+ if (ipv4) {
326
+ return isBlockedIpv4SpecialUse(ipv4);
243
327
  }
244
- return isPrivateIpv4(ipv4);
328
+ // Reject non-canonical IPv4 literal forms (octal/hex/short/packed) by default.
329
+ if (isUnsupportedLegacyIpv4Literal(normalized)) {
330
+ return true;
331
+ }
332
+ return false;
245
333
  }
246
334
  export function isBlockedHostname(hostname) {
247
335
  const normalized = normalizeHostname(hostname);
248
336
  if (!normalized) {
249
337
  return false;
250
338
  }
339
+ return isBlockedHostnameNormalized(normalized);
340
+ }
341
+ function isBlockedHostnameNormalized(normalized) {
251
342
  if (BLOCKED_HOSTNAMES.has(normalized)) {
252
343
  return true;
253
344
  }
@@ -255,6 +346,13 @@ export function isBlockedHostname(hostname) {
255
346
  normalized.endsWith(".local") ||
256
347
  normalized.endsWith(".internal"));
257
348
  }
349
+ export function isBlockedHostnameOrIp(hostname) {
350
+ const normalized = normalizeHostname(hostname);
351
+ if (!normalized) {
352
+ return false;
353
+ }
354
+ return isBlockedHostnameNormalized(normalized) || isPrivateIpAddress(normalized);
355
+ }
258
356
  export function createPinnedLookup(params) {
259
357
  const normalizedHost = normalizeHostname(params.hostname);
260
358
  const fallback = params.fallback ?? dnsLookupCb;
@@ -306,13 +404,8 @@ export async function resolvePinnedHostnameWithPolicy(hostname, params = {}) {
306
404
  if (!matchesHostnameAllowlist(normalized, hostnameAllowlist)) {
307
405
  throw new SsrFBlockedError(`Blocked hostname (not in allowlist): ${hostname}`);
308
406
  }
309
- if (!allowPrivateNetwork && !isExplicitAllowed) {
310
- if (isBlockedHostname(normalized)) {
311
- throw new SsrFBlockedError(`Blocked hostname: ${hostname}`);
312
- }
313
- if (isPrivateIpAddress(normalized)) {
314
- throw new SsrFBlockedError("Blocked: private/internal IP address");
315
- }
407
+ if (!allowPrivateNetwork && !isExplicitAllowed && isBlockedHostnameOrIp(normalized)) {
408
+ throw new SsrFBlockedError("Blocked hostname or private/internal/special-use IP address");
316
409
  }
317
410
  const lookupFn = params.lookupFn ?? dnsLookup;
318
411
  const results = await lookupFn(normalized, { all: true });
@@ -322,7 +415,7 @@ export async function resolvePinnedHostnameWithPolicy(hostname, params = {}) {
322
415
  if (!allowPrivateNetwork && !isExplicitAllowed) {
323
416
  for (const entry of results) {
324
417
  if (isPrivateIpAddress(entry.address)) {
325
- throw new SsrFBlockedError("Blocked: resolves to private/internal IP address");
418
+ throw new SsrFBlockedError("Blocked: resolves to private/internal/special-use IP address");
326
419
  }
327
420
  }
328
421
  }