@poolzin/pool-bot 2026.2.17 → 2026.2.18

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 (469) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/agents/agent-scope.js +4 -0
  3. package/dist/agents/announce-idempotency.js +14 -0
  4. package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +23 -0
  5. package/dist/agents/bash-tools.exec-runtime.js +438 -0
  6. package/dist/agents/bash-tools.shared.js +6 -0
  7. package/dist/agents/cli-runner/reliability.js +61 -0
  8. package/dist/agents/cli-watchdog-defaults.js +11 -0
  9. package/dist/agents/command-poll-backoff.js +63 -0
  10. package/dist/agents/current-time.js +16 -0
  11. package/dist/agents/model-alias-lines.js +18 -0
  12. package/dist/agents/model-auth-label.js +61 -0
  13. package/dist/agents/models-config.e2e-harness.js +115 -0
  14. package/dist/agents/ollama-stream.js +11 -3
  15. package/dist/agents/openclaw-tools.js +135 -0
  16. package/dist/agents/pi-auth-json.js +118 -0
  17. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +147 -0
  18. package/dist/agents/pi-embedded-subscribe.e2e-harness.js +90 -0
  19. package/dist/agents/pi-embedded-subscribe.handlers.compaction.js +63 -0
  20. package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +30 -0
  21. package/dist/agents/pi-extensions/session-manager-runtime-registry.js +23 -0
  22. package/dist/agents/pi-tools.js +2 -0
  23. package/dist/agents/queued-file-writer.js +22 -0
  24. package/dist/agents/sandbox/docker.js +133 -40
  25. package/dist/agents/sandbox/fs-bridge.js +146 -0
  26. package/dist/agents/sandbox/fs-paths.js +205 -0
  27. package/dist/agents/sandbox/hash.js +4 -0
  28. package/dist/agents/sandbox-paths.js +3 -0
  29. package/dist/agents/session-dirs.js +20 -0
  30. package/dist/agents/skills/filter.js +24 -0
  31. package/dist/agents/skills/tools-dir.js +9 -0
  32. package/dist/agents/skills-install-download.js +290 -0
  33. package/dist/agents/skills-install-output.js +30 -0
  34. package/dist/agents/skills-install.download-test-utils.js +36 -0
  35. package/dist/agents/skills.e2e-test-helpers.js +13 -0
  36. package/dist/agents/subagent-announce-queue.js +59 -15
  37. package/dist/agents/subagent-depth.js +137 -0
  38. package/dist/agents/subagent-registry.js +448 -96
  39. package/dist/agents/subagent-spawn.js +262 -0
  40. package/dist/agents/test-helpers/fast-tool-stubs.js +18 -0
  41. package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +74 -0
  42. package/dist/agents/tool-display-common.js +782 -0
  43. package/dist/agents/tools/image-tool.js +1 -1
  44. package/dist/agents/tools/sessions-access.js +178 -0
  45. package/dist/agents/tools/sessions-resolution.js +206 -0
  46. package/dist/agents/tools/subagents-tool.js +616 -0
  47. package/dist/agents/workspace-dir.js +18 -0
  48. package/dist/agents/workspace-dirs.js +14 -0
  49. package/dist/agents/workspace.js +70 -0
  50. package/dist/auto-reply/heartbeat-reply-payload.js +18 -0
  51. package/dist/auto-reply/reply/commands-export-session.js +163 -0
  52. package/dist/auto-reply/reply/commands-mesh.js +245 -0
  53. package/dist/auto-reply/reply/commands-setunset.js +28 -0
  54. package/dist/auto-reply/reply/commands-slash-parse.js +31 -0
  55. package/dist/auto-reply/reply/commands-system-prompt.js +117 -0
  56. package/dist/auto-reply/reply/directive-handling.levels.js +17 -0
  57. package/dist/auto-reply/reply/directive-handling.params.js +1 -0
  58. package/dist/auto-reply/reply/directive-parsing.js +36 -0
  59. package/dist/auto-reply/reply/dispatcher-registry.js +43 -0
  60. package/dist/auto-reply/reply/elevated-unavailable.js +20 -0
  61. package/dist/auto-reply/reply/reply-delivery.js +92 -0
  62. package/dist/auto-reply/reply/session-reset-prompt.js +1 -0
  63. package/dist/auto-reply/reply/session-run-accounting.js +33 -0
  64. package/dist/auto-reply/reply.directive.directive-behavior.e2e-harness.js +115 -0
  65. package/dist/auto-reply/reply.directive.directive-behavior.e2e-mocks.js +12 -0
  66. package/dist/browser/bridge-auth-registry.js +26 -0
  67. package/dist/browser/client-actions-url.js +10 -0
  68. package/dist/browser/control-auth.js +73 -0
  69. package/dist/browser/csrf.js +64 -0
  70. package/dist/browser/http-auth.js +52 -0
  71. package/dist/browser/paths.js +37 -0
  72. package/dist/browser/proxy-files.js +32 -0
  73. package/dist/browser/pw-ai-state.js +7 -0
  74. package/dist/browser/resolved-config-refresh.js +42 -0
  75. package/dist/browser/routes/path-output.js +1 -0
  76. package/dist/browser/server-context.chrome-test-harness.js +20 -0
  77. package/dist/browser/server-middleware.js +31 -0
  78. package/dist/browser/test-port.js +16 -0
  79. package/dist/build-info.json +3 -3
  80. package/dist/canvas-host/file-resolver.js +43 -0
  81. package/dist/channels/account-summary.js +19 -0
  82. package/dist/channels/draft-stream-loop.js +77 -0
  83. package/dist/channels/plugins/account-helpers.js +26 -0
  84. package/dist/channels/telegram/allow-from.js +10 -0
  85. package/dist/cli/browser-cli-resize.js +22 -0
  86. package/dist/cli/browser-cli-shared.js +8 -0
  87. package/dist/cli/clawbot-cli.js +5 -0
  88. package/dist/cli/completion-cli.js +566 -0
  89. package/dist/cli/config-cli.js +63 -5
  90. package/dist/cli/daemon-cli/lifecycle-core.js +256 -0
  91. package/dist/cli/daemon-cli/register-service-commands.js +60 -0
  92. package/dist/cli/daemon-cli-compat.js +80 -0
  93. package/dist/cli/nodes-cli/pairing-render.js +26 -0
  94. package/dist/cli/program/action-reparse.js +17 -0
  95. package/dist/cli/program/command-registry.js +17 -0
  96. package/dist/cli/program/program-context.js +8 -0
  97. package/dist/cli/program/register.subclis.js +7 -0
  98. package/dist/cli/program/routes.js +233 -0
  99. package/dist/cli/qr-cli.js +132 -0
  100. package/dist/cli/requirements-test-fixtures.js +17 -0
  101. package/dist/cli/respawn-policy.js +4 -0
  102. package/dist/cli/shared/parse-port.js +18 -0
  103. package/dist/cli/skills-cli.format.js +241 -0
  104. package/dist/cli/update-cli/progress.js +121 -0
  105. package/dist/cli/update-cli/restart-helper.js +108 -0
  106. package/dist/cli/update-cli/shared.js +196 -0
  107. package/dist/cli/update-cli/status.js +97 -0
  108. package/dist/cli/update-cli/suppress-deprecations.js +17 -0
  109. package/dist/cli/update-cli/update-command.js +506 -0
  110. package/dist/cli/update-cli/wizard.js +130 -0
  111. package/dist/cli/update-cli.js +3 -9
  112. package/dist/cli/windows-argv.js +69 -0
  113. package/dist/commands/auth-choice-legacy.js +20 -0
  114. package/dist/commands/auth-choice.apply-helpers.js +8 -0
  115. package/dist/commands/channel-test-helpers.js +19 -0
  116. package/dist/commands/cleanup-plan.js +10 -0
  117. package/dist/commands/cleanup-utils.js +7 -0
  118. package/dist/commands/config-validation.js +15 -0
  119. package/dist/commands/doctor-completion.js +112 -0
  120. package/dist/commands/doctor-memory-search.js +119 -0
  121. package/dist/commands/doctor-session-locks.js +73 -0
  122. package/dist/commands/doctor.e2e-harness.js +364 -0
  123. package/dist/commands/gateway-presence.js +19 -0
  124. package/dist/commands/model-default.js +35 -0
  125. package/dist/commands/models/fallbacks-shared.js +102 -0
  126. package/dist/commands/models/shared.js +24 -0
  127. package/dist/commands/onboard-auth.config-gateways.js +64 -0
  128. package/dist/commands/onboard-auth.config-litellm.js +45 -0
  129. package/dist/commands/onboard-auth.config-shared.js +116 -0
  130. package/dist/commands/onboard-config.js +16 -0
  131. package/dist/commands/onboard-non-interactive.test-helpers.js +31 -0
  132. package/dist/commands/onboard-provider-auth-flags.js +136 -0
  133. package/dist/commands/openai-codex-oauth.js +40 -0
  134. package/dist/commands/test-runtime-config-helpers.js +21 -0
  135. package/dist/commands/test-wizard-helpers.js +68 -0
  136. package/dist/commands/vllm-setup.js +66 -0
  137. package/dist/compat/legacy-names.js +2 -0
  138. package/dist/config/backup-rotation.js +19 -0
  139. package/dist/config/env-preserve.js +122 -0
  140. package/dist/config/includes-scan.js +78 -0
  141. package/dist/config/plugins-allowlist.js +13 -0
  142. package/dist/config/schema.help.js +256 -0
  143. package/dist/config/schema.hints.js +189 -0
  144. package/dist/config/schema.irc.js +20 -0
  145. package/dist/config/schema.labels.js +317 -0
  146. package/dist/config/sessions/delivery-info.js +40 -0
  147. package/dist/config/types.irc.js +1 -0
  148. package/dist/config/zod-schema.agent-model.js +10 -0
  149. package/dist/config/zod-schema.allowdeny.js +35 -0
  150. package/dist/config/zod-schema.sensitive.js +4 -0
  151. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  152. package/dist/cron/isolated-agent/skills-snapshot.js +26 -0
  153. package/dist/cron/isolated-agent/subagent-followup.js +127 -0
  154. package/dist/cron/isolated-agent.mocks.js +12 -0
  155. package/dist/cron/isolated-agent.test-setup.js +22 -0
  156. package/dist/cron/legacy-delivery.js +43 -0
  157. package/dist/cron/webhook-url.js +22 -0
  158. package/dist/daemon/arg-split.js +40 -0
  159. package/dist/daemon/exec-file.js +23 -0
  160. package/dist/daemon/output.js +6 -0
  161. package/dist/daemon/runtime-format.js +31 -0
  162. package/dist/daemon/schtasks-exec.js +4 -0
  163. package/dist/daemon/service-audit.js +22 -0
  164. package/dist/discord/client.js +41 -0
  165. package/dist/discord/components-registry.js +57 -0
  166. package/dist/discord/components.js +816 -0
  167. package/dist/discord/guilds.js +12 -0
  168. package/dist/discord/monitor/gateway-plugin.js +48 -0
  169. package/dist/discord/monitor/presence.js +30 -0
  170. package/dist/discord/send.components.js +115 -0
  171. package/dist/discord/send.shared.js +4 -0
  172. package/dist/discord/ui.js +26 -0
  173. package/dist/discord/voice-message.js +254 -0
  174. package/dist/gateway/agent-event-assistant-text.js +5 -0
  175. package/dist/gateway/agent-prompt.js +33 -0
  176. package/dist/gateway/auth-rate-limit.js +136 -0
  177. package/dist/gateway/channel-health-monitor.js +114 -0
  178. package/dist/gateway/control-ui-contract.js +1 -0
  179. package/dist/gateway/control-ui-csp.js +15 -0
  180. package/dist/gateway/gateway-config-prompts.shared.js +25 -0
  181. package/dist/gateway/http-auth-helpers.js +18 -0
  182. package/dist/gateway/http-common.js +18 -0
  183. package/dist/gateway/http-endpoint-helpers.js +27 -0
  184. package/dist/gateway/node-invoke-sanitize.js +11 -0
  185. package/dist/gateway/node-invoke-system-run-approval.js +205 -0
  186. package/dist/gateway/probe-auth.js +21 -0
  187. package/dist/gateway/protocol/index.js +7 -2
  188. package/dist/gateway/protocol/schema/mesh.js +54 -0
  189. package/dist/gateway/protocol/schema/protocol-schemas.js +7 -0
  190. package/dist/gateway/protocol/schema.js +1 -0
  191. package/dist/gateway/server/ws-connection/auth-messages.js +54 -0
  192. package/dist/gateway/server-channels.js +11 -0
  193. package/dist/gateway/server-methods/attachment-normalize.js +16 -0
  194. package/dist/gateway/server-methods/base-hash.js +8 -0
  195. package/dist/gateway/server-methods/mesh.js +700 -0
  196. package/dist/gateway/server-methods/nodes.handlers.invoke-result.js +55 -0
  197. package/dist/gateway/server-methods/restart-request.js +13 -0
  198. package/dist/gateway/server-methods/validation.js +8 -0
  199. package/dist/gateway/server.agent.gateway-server-agent.mocks.js +35 -0
  200. package/dist/gateway/server.e2e-registry-helpers.js +1 -0
  201. package/dist/gateway/server.e2e-ws-harness.js +20 -0
  202. package/dist/gateway/test-helpers.js +2 -0
  203. package/dist/gateway/test-helpers.server.js +3 -1
  204. package/dist/gateway/test-http-response.js +12 -0
  205. package/dist/gateway/test-openai-responses-model.js +20 -0
  206. package/dist/gateway/test-temp-config.js +30 -0
  207. package/dist/gateway/test-with-server.js +32 -0
  208. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +46 -0
  209. package/dist/imessage/monitor/abort-handler.js +23 -0
  210. package/dist/imessage/monitor/inbound-processing.js +346 -0
  211. package/dist/imessage/monitor/parse-notification.js +64 -0
  212. package/dist/imessage/target-parsing-helpers.js +92 -0
  213. package/dist/infra/archive.js +244 -20
  214. package/dist/infra/detect-package-manager.js +26 -0
  215. package/dist/infra/exec-approvals-allowlist.js +257 -0
  216. package/dist/infra/exec-approvals-analysis.js +770 -0
  217. package/dist/infra/exec-approvals.js +13 -0
  218. package/dist/infra/file-lock.js +1 -0
  219. package/dist/infra/gemini-auth.js +39 -0
  220. package/dist/infra/heartbeat-active-hours.js +85 -0
  221. package/dist/infra/heartbeat-events-filter.js +50 -0
  222. package/dist/infra/heartbeat-runner.test-utils.js +39 -0
  223. package/dist/infra/http-body.js +265 -0
  224. package/dist/infra/install-package-dir.js +50 -0
  225. package/dist/infra/install-safe-path.js +49 -0
  226. package/dist/infra/json-files.js +49 -0
  227. package/dist/infra/jsonl-socket.js +52 -0
  228. package/dist/infra/map-size.js +14 -0
  229. package/dist/infra/net/hostname.js +7 -0
  230. package/dist/infra/npm-registry-spec.js +39 -0
  231. package/dist/infra/openclaw-root.js +109 -0
  232. package/dist/infra/outbound/delivery-queue.js +214 -0
  233. package/dist/infra/outbound/identity.js +23 -0
  234. package/dist/infra/outbound/message-action-params.js +307 -0
  235. package/dist/infra/outbound/tool-payload.js +21 -0
  236. package/dist/infra/package-json.js +23 -0
  237. package/dist/infra/pairing-files.js +19 -0
  238. package/dist/infra/pairing-token.js +9 -0
  239. package/dist/infra/path-prepend.js +51 -0
  240. package/dist/infra/process-respawn.js +49 -0
  241. package/dist/infra/runtime-status.js +16 -0
  242. package/dist/infra/session-cost-usage.types.js +1 -0
  243. package/dist/infra/session-maintenance-warning.js +89 -0
  244. package/dist/infra/system-run-command.js +78 -0
  245. package/dist/infra/tmp-openclaw-dir.js +81 -0
  246. package/dist/infra/tmp-poolbot-dir.js +2 -0
  247. package/dist/infra/update-channels.js +19 -0
  248. package/dist/line/actions.js +45 -0
  249. package/dist/line/channel-access-token.js +9 -0
  250. package/dist/line/flex-templates/basic-cards.js +332 -0
  251. package/dist/line/flex-templates/common.js +18 -0
  252. package/dist/line/flex-templates/media-control-cards.js +453 -0
  253. package/dist/line/flex-templates/message.js +10 -0
  254. package/dist/line/flex-templates/schedule-cards.js +399 -0
  255. package/dist/line/flex-templates/types.js +1 -0
  256. package/dist/line/webhook-node.js +100 -0
  257. package/dist/line/webhook-utils.js +11 -0
  258. package/dist/logging/timestamps.js +14 -0
  259. package/dist/markdown/whatsapp.js +62 -0
  260. package/dist/media/base64.js +34 -0
  261. package/dist/media/local-roots.js +32 -0
  262. package/dist/media/outbound-attachment.js +10 -0
  263. package/dist/media/read-response-with-limit.js +41 -0
  264. package/dist/media/sniff-mime-from-base64.js +19 -0
  265. package/dist/media-understanding/audio-preflight.js +67 -0
  266. package/dist/media-understanding/fs.js +13 -0
  267. package/dist/media-understanding/output-extract.js +26 -0
  268. package/dist/media-understanding/providers/audio.test-helpers.js +34 -0
  269. package/dist/media-understanding/providers/google/inline-data.js +64 -0
  270. package/dist/media-understanding/providers/shared.js +7 -0
  271. package/dist/media-understanding/runner.entries.js +459 -0
  272. package/dist/memory/batch-error-utils.js +11 -0
  273. package/dist/memory/batch-http.js +27 -0
  274. package/dist/memory/batch-output.js +29 -0
  275. package/dist/memory/batch-runner.js +22 -0
  276. package/dist/memory/batch-upload.js +23 -0
  277. package/dist/memory/batch-utils.js +26 -0
  278. package/dist/memory/embeddings-debug.js +11 -0
  279. package/dist/memory/embeddings-remote-client.js +22 -0
  280. package/dist/memory/embeddings-remote-fetch.js +14 -0
  281. package/dist/memory/manager-embedding-ops.js +616 -0
  282. package/dist/memory/manager-sync-ops.js +953 -0
  283. package/dist/memory/qmd-manager.js +1061 -0
  284. package/dist/memory/qmd-query-parser.js +107 -0
  285. package/dist/memory/qmd-scope.js +93 -0
  286. package/dist/memory/search-manager.js +0 -1
  287. package/dist/memory/sync-index.js +21 -0
  288. package/dist/memory/sync-progress.js +22 -0
  289. package/dist/memory/sync-stale.js +30 -0
  290. package/dist/memory/test-embeddings-mock.js +16 -0
  291. package/dist/memory/test-manager-helpers.js +14 -0
  292. package/dist/memory/test-runtime-mocks.js +11 -0
  293. package/dist/node-host/invoke-browser.js +177 -0
  294. package/dist/node-host/invoke.js +685 -0
  295. package/dist/pairing/setup-code.js +285 -0
  296. package/dist/plugin-sdk/account-id.js +1 -0
  297. package/dist/plugin-sdk/agent-media-payload.js +13 -0
  298. package/dist/plugin-sdk/allow-from.js +47 -0
  299. package/dist/plugin-sdk/command-auth.js +23 -0
  300. package/dist/plugin-sdk/config-paths.js +9 -0
  301. package/dist/plugin-sdk/file-lock.js +116 -0
  302. package/dist/plugin-sdk/json-store.js +31 -0
  303. package/dist/plugin-sdk/onboarding.js +28 -0
  304. package/dist/plugin-sdk/provider-auth-result.js +29 -0
  305. package/dist/plugin-sdk/slack-message-actions.js +133 -0
  306. package/dist/plugin-sdk/status-helpers.js +35 -0
  307. package/dist/plugin-sdk/text-chunking.js +31 -0
  308. package/dist/plugin-sdk/tool-send.js +12 -0
  309. package/dist/plugin-sdk/webhook-path.js +27 -0
  310. package/dist/plugin-sdk/webhook-targets.js +34 -0
  311. package/dist/plugins/hooks.test-helpers.js +21 -0
  312. package/dist/plugins/uninstall.js +171 -0
  313. package/dist/process/supervisor/adapters/child.js +143 -0
  314. package/dist/process/supervisor/adapters/env.js +13 -0
  315. package/dist/process/supervisor/adapters/pty.js +148 -0
  316. package/dist/process/supervisor/index.js +10 -0
  317. package/dist/process/supervisor/registry.js +117 -0
  318. package/dist/process/supervisor/supervisor.js +244 -0
  319. package/dist/process/supervisor/types.js +1 -0
  320. package/dist/providers/google-shared.test-helpers.js +75 -0
  321. package/dist/security/audit-channel.js +419 -0
  322. package/dist/security/audit-tool-policy.js +1 -0
  323. package/dist/security/scan-paths.js +12 -0
  324. package/dist/sessions/input-provenance.js +55 -0
  325. package/dist/sessions/session-key-utils.js +7 -0
  326. package/dist/shared/chat-content.js +31 -0
  327. package/dist/shared/chat-envelope.js +45 -0
  328. package/dist/shared/config-eval.js +117 -0
  329. package/dist/shared/device-auth.js +16 -0
  330. package/dist/shared/entry-metadata.js +9 -0
  331. package/dist/shared/entry-status.js +25 -0
  332. package/dist/shared/frontmatter.js +98 -0
  333. package/dist/shared/model-param-b.js +19 -0
  334. package/dist/shared/net/ipv4.js +17 -0
  335. package/dist/shared/node-match.js +53 -0
  336. package/dist/shared/requirements.js +128 -0
  337. package/dist/shared/subagents-format.js +84 -0
  338. package/dist/shared/usage-aggregates.js +28 -0
  339. package/dist/signal/monitor/mentions.js +45 -0
  340. package/dist/signal/rpc-context.js +19 -0
  341. package/dist/slack/blocks-fallback.js +76 -0
  342. package/dist/slack/blocks-input.js +40 -0
  343. package/dist/slack/draft-stream.js +106 -0
  344. package/dist/slack/message-actions.js +51 -0
  345. package/dist/slack/modal-metadata.js +32 -0
  346. package/dist/slack/monitor/events/interactions.js +462 -0
  347. package/dist/slack/monitor/room-context.js +17 -0
  348. package/dist/slack/stream-mode.js +41 -0
  349. package/dist/telegram/bot-native-command-menu.js +64 -0
  350. package/dist/telegram/bot.media.e2e-harness.js +81 -0
  351. package/dist/telegram/button-types.js +1 -0
  352. package/dist/telegram/group-access.js +65 -0
  353. package/dist/telegram/outbound-params.js +21 -0
  354. package/dist/telegram/poll-vote-cache.js +21 -0
  355. package/dist/terminal/health-style.js +36 -0
  356. package/dist/test-utils/chunk-test-helpers.js +21 -0
  357. package/dist/test-utils/env.js +72 -0
  358. package/dist/test-utils/exec-assertions.js +12 -0
  359. package/dist/test-utils/imessage-test-plugin.js +54 -0
  360. package/dist/test-utils/mock-http-response.js +17 -0
  361. package/dist/test-utils/vitest-mock-fn.js +1 -0
  362. package/dist/tts/tts-core.js +550 -0
  363. package/dist/utils/chunk-items.js +10 -0
  364. package/dist/utils/reaction-level.js +52 -0
  365. package/dist/utils/safe-json.js +22 -0
  366. package/dist/utils/with-timeout.js +14 -0
  367. package/dist/web/media.js +17 -5
  368. package/dist/whatsapp/resolve-outbound-target.js +42 -0
  369. package/dist/wizard/onboarding.completion.js +74 -0
  370. package/extensions/bluebubbles/src/account-resolve.ts +29 -0
  371. package/extensions/bluebubbles/src/monitor-normalize.ts +796 -0
  372. package/extensions/bluebubbles/src/monitor-processing.ts +1007 -0
  373. package/extensions/bluebubbles/src/monitor-reply-cache.ts +185 -0
  374. package/extensions/bluebubbles/src/monitor-shared.ts +51 -0
  375. package/extensions/bluebubbles/src/multipart.ts +32 -0
  376. package/extensions/bluebubbles/src/send-helpers.ts +53 -0
  377. package/extensions/bluebubbles/src/test-harness.ts +50 -0
  378. package/extensions/bluebubbles/src/test-mocks.ts +11 -0
  379. package/extensions/device-pair/index.ts +554 -0
  380. package/extensions/discord/src/channel.js +366 -0
  381. package/extensions/discord/src/runtime.js +10 -0
  382. package/extensions/feishu/index.ts +63 -0
  383. package/extensions/feishu/src/accounts.ts +114 -0
  384. package/extensions/feishu/src/bitable.ts +739 -0
  385. package/extensions/feishu/src/bot.ts +965 -0
  386. package/extensions/feishu/src/channel.ts +351 -0
  387. package/extensions/feishu/src/client.ts +118 -0
  388. package/extensions/feishu/src/config-schema.ts +206 -0
  389. package/extensions/feishu/src/dedup.ts +33 -0
  390. package/extensions/feishu/src/directory.ts +177 -0
  391. package/extensions/feishu/src/doc-schema.ts +47 -0
  392. package/extensions/feishu/src/docx.ts +536 -0
  393. package/extensions/feishu/src/drive-schema.ts +46 -0
  394. package/extensions/feishu/src/drive.ts +227 -0
  395. package/extensions/feishu/src/dynamic-agent.ts +131 -0
  396. package/extensions/feishu/src/media.ts +449 -0
  397. package/extensions/feishu/src/mention.ts +126 -0
  398. package/extensions/feishu/src/monitor.ts +330 -0
  399. package/extensions/feishu/src/onboarding.ts +359 -0
  400. package/extensions/feishu/src/outbound.ts +55 -0
  401. package/extensions/feishu/src/perm-schema.ts +52 -0
  402. package/extensions/feishu/src/perm.ts +173 -0
  403. package/extensions/feishu/src/policy.ts +84 -0
  404. package/extensions/feishu/src/probe.ts +44 -0
  405. package/extensions/feishu/src/reactions.ts +160 -0
  406. package/extensions/feishu/src/reply-dispatcher.ts +239 -0
  407. package/extensions/feishu/src/runtime.ts +14 -0
  408. package/extensions/feishu/src/send-result.ts +29 -0
  409. package/extensions/feishu/src/send.ts +335 -0
  410. package/extensions/feishu/src/streaming-card.ts +223 -0
  411. package/extensions/feishu/src/targets.ts +78 -0
  412. package/extensions/feishu/src/tools-config.ts +21 -0
  413. package/extensions/feishu/src/types.ts +81 -0
  414. package/extensions/feishu/src/typing.ts +80 -0
  415. package/extensions/feishu/src/wiki-schema.ts +55 -0
  416. package/extensions/feishu/src/wiki.ts +232 -0
  417. package/extensions/imessage/src/channel.js +253 -0
  418. package/extensions/imessage/src/runtime.js +10 -0
  419. package/extensions/irc/index.ts +17 -0
  420. package/extensions/irc/src/accounts.ts +268 -0
  421. package/extensions/irc/src/channel.ts +367 -0
  422. package/extensions/irc/src/client.ts +439 -0
  423. package/extensions/irc/src/config-schema.ts +97 -0
  424. package/extensions/irc/src/connect-options.ts +30 -0
  425. package/extensions/irc/src/control-chars.ts +22 -0
  426. package/extensions/irc/src/inbound.ts +334 -0
  427. package/extensions/irc/src/monitor.ts +147 -0
  428. package/extensions/irc/src/normalize.ts +117 -0
  429. package/extensions/irc/src/onboarding.ts +479 -0
  430. package/extensions/irc/src/policy.ts +157 -0
  431. package/extensions/irc/src/probe.ts +53 -0
  432. package/extensions/irc/src/protocol.ts +169 -0
  433. package/extensions/irc/src/runtime.ts +14 -0
  434. package/extensions/irc/src/send.ts +88 -0
  435. package/extensions/irc/src/types.ts +93 -0
  436. package/extensions/matrix/src/matrix/client-bootstrap.ts +39 -0
  437. package/extensions/mattermost/src/mattermost/monitor-onchar.ts +25 -0
  438. package/extensions/mattermost/src/mattermost/monitor-websocket.ts +221 -0
  439. package/extensions/mattermost/src/mattermost/reactions.ts +130 -0
  440. package/extensions/mattermost/src/mattermost/reconnect.ts +103 -0
  441. package/extensions/minimax-portal-auth/index.ts +161 -0
  442. package/extensions/minimax-portal-auth/oauth.ts +247 -0
  443. package/extensions/msteams/src/file-lock.ts +1 -0
  444. package/extensions/msteams/src/graph.ts +92 -0
  445. package/extensions/msteams/src/mentions.ts +114 -0
  446. package/extensions/msteams/src/test-runtime.ts +16 -0
  447. package/extensions/openai-codex-auth/index.ts +177 -0
  448. package/extensions/phone-control/index.ts +421 -0
  449. package/extensions/shared/resolve-target-test-helpers.ts +66 -0
  450. package/extensions/signal/src/channel.js +273 -0
  451. package/extensions/signal/src/runtime.js +10 -0
  452. package/extensions/slack/src/channel.js +489 -0
  453. package/extensions/slack/src/runtime.js +10 -0
  454. package/extensions/talk-voice/index.ts +150 -0
  455. package/extensions/telegram/src/channel.js +424 -0
  456. package/extensions/telegram/src/runtime.js +10 -0
  457. package/extensions/thread-ownership/index.ts +133 -0
  458. package/extensions/tlon/src/account-fields.ts +25 -0
  459. package/extensions/tlon/src/urbit/base-url.ts +57 -0
  460. package/extensions/tlon/src/urbit/channel-client.ts +157 -0
  461. package/extensions/tlon/src/urbit/channel-ops.ts +164 -0
  462. package/extensions/tlon/src/urbit/context.ts +47 -0
  463. package/extensions/tlon/src/urbit/errors.ts +51 -0
  464. package/extensions/tlon/src/urbit/fetch.ts +39 -0
  465. package/extensions/twitch/src/test-fixtures.ts +30 -0
  466. package/extensions/voice-call/src/allowlist.ts +19 -0
  467. package/extensions/whatsapp/src/channel.js +429 -0
  468. package/extensions/whatsapp/src/runtime.js +10 -0
  469. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## v2026.2.18 (2026-02-17)
2
+
3
+ ### Features
4
+ - Upstream bulk port — 394 new production files + 52 modified files aligned with upstream:
5
+ - **Archive security hardening:** path traversal guards, symlink/hardlink blocking, entry count + byte budget limits, `preservePaths: false` + `strict: true` for tar extraction
6
+ - **Agent workspace:** `loadExtraBootstrapFiles()` with full security (path traversal guards, symlink resolution, basename allowlist)
7
+ - **Gateway channels:** `ChannelManager` restart tracking with `restartAttempts` map, `manuallyStopped` set, exponential backoff
8
+ - **Config CLI:** `runConfigGet`/`runConfigUnset` now accept injectable `RuntimeEnv`; `runConfigUnset` uses `snapshot.resolved` with `structuredClone` to prevent runtime defaults leaking into config file
9
+ - **Gateway auth:** rate limiting support (`rateLimited`, `retryAfterMs`), `"none" | "trusted-proxy"` auth modes
10
+ - **Mesh networking:** protocol schemas, validators, and server methods for mesh gateway federation
11
+ - **Subagent registry:** complete rewrite with announcement queue
12
+ - **Memory:** QMD search mode fallback, hybrid search backend config
13
+ - **Exec approvals:** normalized path signature for approval checks
14
+ - Plus 375+ new source files covering agents, CLI, config, cron, daemon, gateway, hooks, infra, media, routing, and more
15
+
16
+ ---
17
+
1
18
  ## v2026.2.17 (2026-02-17)
2
19
 
3
20
  ### Features
@@ -2,6 +2,7 @@ import os from "node:os";
2
2
  import path from "node:path";
3
3
  import { resolveStateDir } from "../config/paths.js";
4
4
  import { DEFAULT_AGENT_ID, normalizeAgentId, parseAgentSessionKey, } from "../routing/session-key.js";
5
+ import { normalizeSkillFilter } from "./skills/filter.js";
5
6
  import { resolveUserPath } from "../utils.js";
6
7
  import { DEFAULT_AGENT_WORKSPACE_DIR } from "./workspace.js";
7
8
  export { resolveAgentIdFromSessionKey } from "../routing/session-key.js";
@@ -76,6 +77,9 @@ export function resolveAgentConfig(cfg, agentId) {
76
77
  tools: entry.tools,
77
78
  };
78
79
  }
80
+ export function resolveAgentSkillsFilter(cfg, agentId) {
81
+ return normalizeSkillFilter(resolveAgentConfig(cfg, agentId)?.skills);
82
+ }
79
83
  export function resolveAgentModelPrimary(cfg, agentId) {
80
84
  const raw = resolveAgentConfig(cfg, agentId)?.model;
81
85
  if (!raw)
@@ -0,0 +1,14 @@
1
+ export function buildAnnounceIdFromChildRun(params) {
2
+ return `v1:${params.childSessionKey}:${params.childRunId}`;
3
+ }
4
+ export function buildAnnounceIdempotencyKey(announceId) {
5
+ return `announce:${announceId}`;
6
+ }
7
+ export function resolveQueueAnnounceId(params) {
8
+ const announceId = params.announceId?.trim();
9
+ if (announceId) {
10
+ return announceId;
11
+ }
12
+ // Backward-compatible fallback for queue items that predate announceId.
13
+ return `legacy:${params.sessionKey}:${params.enqueuedAt}`;
14
+ }
@@ -0,0 +1,23 @@
1
+ export const ANTHROPIC_STORE = {
2
+ version: 1,
3
+ profiles: {
4
+ "anthropic:default": {
5
+ type: "api_key",
6
+ provider: "anthropic",
7
+ key: "sk-default",
8
+ },
9
+ "anthropic:work": {
10
+ type: "api_key",
11
+ provider: "anthropic",
12
+ key: "sk-work",
13
+ },
14
+ },
15
+ };
16
+ export const ANTHROPIC_CFG = {
17
+ auth: {
18
+ profiles: {
19
+ "anthropic:default": { provider: "anthropic", mode: "api_key" },
20
+ "anthropic:work": { provider: "anthropic", mode: "api_key" },
21
+ },
22
+ },
23
+ };
@@ -0,0 +1,438 @@
1
+ import path from "node:path";
2
+ import { Type } from "@sinclair/typebox";
3
+ import { requestHeartbeatNow } from "../infra/heartbeat-wake.js";
4
+ import { mergePathPrepend } from "../infra/path-prepend.js";
5
+ import { enqueueSystemEvent } from "../infra/system-events.js";
6
+ export { applyPathPrepend, normalizePathPrepend } from "../infra/path-prepend.js";
7
+ import { logWarn } from "../logger.js";
8
+ import { getProcessSupervisor } from "../process/supervisor/index.js";
9
+ import { addSession, appendOutput, createSessionSlug, markExited, tail, } from "./bash-process-registry.js";
10
+ import { buildDockerExecArgs, chunkString, clampWithDefault, readEnvInt, } from "./bash-tools.shared.js";
11
+ import { buildCursorPositionResponse, stripDsrRequests } from "./pty-dsr.js";
12
+ import { getShellConfig, sanitizeBinaryOutput } from "./shell-utils.js";
13
+ // Security: Blocklist of environment variables that could alter execution flow
14
+ // or inject code when running on non-sandboxed hosts (Gateway/Node).
15
+ const DANGEROUS_HOST_ENV_VARS = new Set([
16
+ "LD_PRELOAD",
17
+ "LD_LIBRARY_PATH",
18
+ "LD_AUDIT",
19
+ "DYLD_INSERT_LIBRARIES",
20
+ "DYLD_LIBRARY_PATH",
21
+ "NODE_OPTIONS",
22
+ "NODE_PATH",
23
+ "PYTHONPATH",
24
+ "PYTHONHOME",
25
+ "RUBYLIB",
26
+ "PERL5LIB",
27
+ "BASH_ENV",
28
+ "ENV",
29
+ "GCONV_PATH",
30
+ "IFS",
31
+ "SSLKEYLOGFILE",
32
+ ]);
33
+ const DANGEROUS_HOST_ENV_PREFIXES = ["DYLD_", "LD_"];
34
+ // Centralized sanitization helper.
35
+ // Throws an error if dangerous variables or PATH modifications are detected on the host.
36
+ export function validateHostEnv(env) {
37
+ for (const key of Object.keys(env)) {
38
+ const upperKey = key.toUpperCase();
39
+ // 1. Block known dangerous variables (Fail Closed)
40
+ if (DANGEROUS_HOST_ENV_PREFIXES.some((prefix) => upperKey.startsWith(prefix))) {
41
+ throw new Error(`Security Violation: Environment variable '${key}' is forbidden during host execution.`);
42
+ }
43
+ if (DANGEROUS_HOST_ENV_VARS.has(upperKey)) {
44
+ throw new Error(`Security Violation: Environment variable '${key}' is forbidden during host execution.`);
45
+ }
46
+ // 2. Strictly block PATH modification on host
47
+ // Allowing custom PATH on the gateway/node can lead to binary hijacking.
48
+ if (upperKey === "PATH") {
49
+ throw new Error("Security Violation: Custom 'PATH' variable is forbidden during host execution.");
50
+ }
51
+ }
52
+ }
53
+ export const DEFAULT_MAX_OUTPUT = clampWithDefault(readEnvInt("PI_BASH_MAX_OUTPUT_CHARS"), 200_000, 1_000, 200_000);
54
+ export const DEFAULT_PENDING_MAX_OUTPUT = clampWithDefault(readEnvInt("POOLBOT_BASH_PENDING_MAX_OUTPUT_CHARS"), 30_000, 1_000, 200_000);
55
+ export const DEFAULT_PATH = process.env.PATH ?? "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
56
+ export const DEFAULT_NOTIFY_TAIL_CHARS = 400;
57
+ const DEFAULT_NOTIFY_SNIPPET_CHARS = 180;
58
+ export const DEFAULT_APPROVAL_TIMEOUT_MS = 120_000;
59
+ export const DEFAULT_APPROVAL_REQUEST_TIMEOUT_MS = 130_000;
60
+ const DEFAULT_APPROVAL_RUNNING_NOTICE_MS = 10_000;
61
+ const APPROVAL_SLUG_LENGTH = 8;
62
+ export const execSchema = Type.Object({
63
+ command: Type.String({ description: "Shell command to execute" }),
64
+ workdir: Type.Optional(Type.String({ description: "Working directory (defaults to cwd)" })),
65
+ env: Type.Optional(Type.Record(Type.String(), Type.String())),
66
+ yieldMs: Type.Optional(Type.Number({
67
+ description: "Milliseconds to wait before backgrounding (default 10000)",
68
+ })),
69
+ background: Type.Optional(Type.Boolean({ description: "Run in background immediately" })),
70
+ timeout: Type.Optional(Type.Number({
71
+ description: "Timeout in seconds (optional, kills process on expiry)",
72
+ })),
73
+ pty: Type.Optional(Type.Boolean({
74
+ description: "Run in a pseudo-terminal (PTY) when available (TTY-required CLIs, coding agents)",
75
+ })),
76
+ elevated: Type.Optional(Type.Boolean({
77
+ description: "Run on the host with elevated permissions (if allowed)",
78
+ })),
79
+ host: Type.Optional(Type.String({
80
+ description: "Exec host (sandbox|gateway|node).",
81
+ })),
82
+ security: Type.Optional(Type.String({
83
+ description: "Exec security mode (deny|allowlist|full).",
84
+ })),
85
+ ask: Type.Optional(Type.String({
86
+ description: "Exec ask mode (off|on-miss|always).",
87
+ })),
88
+ node: Type.Optional(Type.String({
89
+ description: "Node id/name for host=node.",
90
+ })),
91
+ });
92
+ export function normalizeExecHost(value) {
93
+ const normalized = value?.trim().toLowerCase();
94
+ if (normalized === "sandbox" || normalized === "gateway" || normalized === "node") {
95
+ return normalized;
96
+ }
97
+ return null;
98
+ }
99
+ export function normalizeExecSecurity(value) {
100
+ const normalized = value?.trim().toLowerCase();
101
+ if (normalized === "deny" || normalized === "allowlist" || normalized === "full") {
102
+ return normalized;
103
+ }
104
+ return null;
105
+ }
106
+ export function normalizeExecAsk(value) {
107
+ const normalized = value?.trim().toLowerCase();
108
+ if (normalized === "off" || normalized === "on-miss" || normalized === "always") {
109
+ return normalized;
110
+ }
111
+ return null;
112
+ }
113
+ export function renderExecHostLabel(host) {
114
+ return host === "sandbox" ? "sandbox" : host === "gateway" ? "gateway" : "node";
115
+ }
116
+ export function normalizeNotifyOutput(value) {
117
+ return value.replace(/\s+/g, " ").trim();
118
+ }
119
+ function compactNotifyOutput(value, maxChars = DEFAULT_NOTIFY_SNIPPET_CHARS) {
120
+ const normalized = normalizeNotifyOutput(value);
121
+ if (!normalized) {
122
+ return "";
123
+ }
124
+ if (normalized.length <= maxChars) {
125
+ return normalized;
126
+ }
127
+ const safe = Math.max(1, maxChars - 1);
128
+ return `${normalized.slice(0, safe)}…`;
129
+ }
130
+ export function applyShellPath(env, shellPath) {
131
+ if (!shellPath) {
132
+ return;
133
+ }
134
+ const entries = shellPath
135
+ .split(path.delimiter)
136
+ .map((part) => part.trim())
137
+ .filter(Boolean);
138
+ if (entries.length === 0) {
139
+ return;
140
+ }
141
+ const merged = mergePathPrepend(env.PATH, entries);
142
+ if (merged) {
143
+ env.PATH = merged;
144
+ }
145
+ }
146
+ function maybeNotifyOnExit(session, status) {
147
+ if (!session.backgrounded || !session.notifyOnExit || session.exitNotified) {
148
+ return;
149
+ }
150
+ const sessionKey = session.sessionKey?.trim();
151
+ if (!sessionKey) {
152
+ return;
153
+ }
154
+ session.exitNotified = true;
155
+ const exitLabel = session.exitSignal
156
+ ? `signal ${session.exitSignal}`
157
+ : `code ${session.exitCode ?? 0}`;
158
+ const output = compactNotifyOutput(tail(session.tail || session.aggregated || "", DEFAULT_NOTIFY_TAIL_CHARS));
159
+ if (status === "completed" && !output && session.notifyOnExitEmptySuccess !== true) {
160
+ return;
161
+ }
162
+ const summary = output
163
+ ? `Exec ${status} (${session.id.slice(0, 8)}, ${exitLabel}) :: ${output}`
164
+ : `Exec ${status} (${session.id.slice(0, 8)}, ${exitLabel})`;
165
+ enqueueSystemEvent(summary, { sessionKey });
166
+ requestHeartbeatNow({ reason: `exec:${session.id}:exit` });
167
+ }
168
+ export function createApprovalSlug(id) {
169
+ return id.slice(0, APPROVAL_SLUG_LENGTH);
170
+ }
171
+ export function resolveApprovalRunningNoticeMs(value) {
172
+ if (typeof value !== "number" || !Number.isFinite(value)) {
173
+ return DEFAULT_APPROVAL_RUNNING_NOTICE_MS;
174
+ }
175
+ if (value <= 0) {
176
+ return 0;
177
+ }
178
+ return Math.floor(value);
179
+ }
180
+ export function emitExecSystemEvent(text, opts) {
181
+ const sessionKey = opts.sessionKey?.trim();
182
+ if (!sessionKey) {
183
+ return;
184
+ }
185
+ enqueueSystemEvent(text, { sessionKey, contextKey: opts.contextKey });
186
+ requestHeartbeatNow({ reason: "exec-event" });
187
+ }
188
+ export async function runExecProcess(opts) {
189
+ const startedAt = Date.now();
190
+ const sessionId = createSessionSlug();
191
+ const execCommand = opts.execCommand ?? opts.command;
192
+ const supervisor = getProcessSupervisor();
193
+ const session = {
194
+ id: sessionId,
195
+ command: opts.command,
196
+ scopeKey: opts.scopeKey,
197
+ sessionKey: opts.sessionKey,
198
+ notifyOnExit: opts.notifyOnExit,
199
+ notifyOnExitEmptySuccess: opts.notifyOnExitEmptySuccess === true,
200
+ exitNotified: false,
201
+ child: undefined,
202
+ stdin: undefined,
203
+ pid: undefined,
204
+ startedAt,
205
+ cwd: opts.workdir,
206
+ maxOutputChars: opts.maxOutput,
207
+ pendingMaxOutputChars: opts.pendingMaxOutput,
208
+ totalOutputChars: 0,
209
+ pendingStdout: [],
210
+ pendingStderr: [],
211
+ pendingStdoutChars: 0,
212
+ pendingStderrChars: 0,
213
+ aggregated: "",
214
+ tail: "",
215
+ exited: false,
216
+ exitCode: undefined,
217
+ exitSignal: undefined,
218
+ truncated: false,
219
+ backgrounded: false,
220
+ };
221
+ addSession(session);
222
+ const emitUpdate = () => {
223
+ if (!opts.onUpdate) {
224
+ return;
225
+ }
226
+ const tailText = session.tail || session.aggregated;
227
+ const warningText = opts.warnings.length ? `${opts.warnings.join("\n")}\n\n` : "";
228
+ opts.onUpdate({
229
+ content: [{ type: "text", text: warningText + (tailText || "") }],
230
+ details: {
231
+ status: "running",
232
+ sessionId,
233
+ pid: session.pid ?? undefined,
234
+ startedAt,
235
+ cwd: session.cwd,
236
+ tail: session.tail,
237
+ },
238
+ });
239
+ };
240
+ const handleStdout = (data) => {
241
+ const str = sanitizeBinaryOutput(data.toString());
242
+ for (const chunk of chunkString(str)) {
243
+ appendOutput(session, "stdout", chunk);
244
+ emitUpdate();
245
+ }
246
+ };
247
+ const handleStderr = (data) => {
248
+ const str = sanitizeBinaryOutput(data.toString());
249
+ for (const chunk of chunkString(str)) {
250
+ appendOutput(session, "stderr", chunk);
251
+ emitUpdate();
252
+ }
253
+ };
254
+ const timeoutMs = typeof opts.timeoutSec === "number" && opts.timeoutSec > 0
255
+ ? Math.floor(opts.timeoutSec * 1000)
256
+ : undefined;
257
+ const spawnSpec = (() => {
258
+ if (opts.sandbox) {
259
+ return {
260
+ mode: "child",
261
+ argv: [
262
+ "docker",
263
+ ...buildDockerExecArgs({
264
+ containerName: opts.sandbox.containerName,
265
+ command: execCommand,
266
+ workdir: opts.containerWorkdir ?? opts.sandbox.containerWorkdir,
267
+ env: opts.env,
268
+ tty: opts.usePty,
269
+ }),
270
+ ],
271
+ env: process.env,
272
+ stdinMode: opts.usePty ? "pipe-open" : "pipe-closed",
273
+ };
274
+ }
275
+ const { shell, args: shellArgs } = getShellConfig();
276
+ const childArgv = [shell, ...shellArgs, execCommand];
277
+ if (opts.usePty) {
278
+ return {
279
+ mode: "pty",
280
+ ptyCommand: execCommand,
281
+ childFallbackArgv: childArgv,
282
+ env: opts.env,
283
+ stdinMode: "pipe-open",
284
+ };
285
+ }
286
+ return {
287
+ mode: "child",
288
+ argv: childArgv,
289
+ env: opts.env,
290
+ stdinMode: "pipe-closed",
291
+ };
292
+ })();
293
+ let managedRun = null;
294
+ let usingPty = spawnSpec.mode === "pty";
295
+ const cursorResponse = buildCursorPositionResponse();
296
+ const onSupervisorStdout = (chunk) => {
297
+ if (usingPty) {
298
+ const { cleaned, requests } = stripDsrRequests(chunk);
299
+ if (requests > 0 && managedRun?.stdin) {
300
+ for (let i = 0; i < requests; i += 1) {
301
+ managedRun.stdin.write(cursorResponse);
302
+ }
303
+ }
304
+ handleStdout(cleaned);
305
+ return;
306
+ }
307
+ handleStdout(chunk);
308
+ };
309
+ try {
310
+ const spawnBase = {
311
+ runId: sessionId,
312
+ sessionId: opts.sessionKey?.trim() || sessionId,
313
+ backendId: opts.sandbox ? "exec-sandbox" : "exec-host",
314
+ scopeKey: opts.scopeKey,
315
+ cwd: opts.workdir,
316
+ env: spawnSpec.env,
317
+ timeoutMs,
318
+ captureOutput: false,
319
+ onStdout: onSupervisorStdout,
320
+ onStderr: handleStderr,
321
+ };
322
+ managedRun =
323
+ spawnSpec.mode === "pty"
324
+ ? await supervisor.spawn({
325
+ ...spawnBase,
326
+ mode: "pty",
327
+ ptyCommand: spawnSpec.ptyCommand,
328
+ })
329
+ : await supervisor.spawn({
330
+ ...spawnBase,
331
+ mode: "child",
332
+ argv: spawnSpec.argv,
333
+ stdinMode: spawnSpec.stdinMode,
334
+ });
335
+ }
336
+ catch (err) {
337
+ if (spawnSpec.mode === "pty") {
338
+ const warning = `Warning: PTY spawn failed (${String(err)}); retrying without PTY for \`${opts.command}\`.`;
339
+ logWarn(`exec: PTY spawn failed (${String(err)}); retrying without PTY for "${opts.command}".`);
340
+ opts.warnings.push(warning);
341
+ usingPty = false;
342
+ try {
343
+ managedRun = await supervisor.spawn({
344
+ runId: sessionId,
345
+ sessionId: opts.sessionKey?.trim() || sessionId,
346
+ backendId: "exec-host",
347
+ scopeKey: opts.scopeKey,
348
+ mode: "child",
349
+ argv: spawnSpec.childFallbackArgv,
350
+ cwd: opts.workdir,
351
+ env: spawnSpec.env,
352
+ stdinMode: "pipe-open",
353
+ timeoutMs,
354
+ captureOutput: false,
355
+ onStdout: handleStdout,
356
+ onStderr: handleStderr,
357
+ });
358
+ }
359
+ catch (retryErr) {
360
+ markExited(session, null, null, "failed");
361
+ maybeNotifyOnExit(session, "failed");
362
+ throw retryErr;
363
+ }
364
+ }
365
+ else {
366
+ markExited(session, null, null, "failed");
367
+ maybeNotifyOnExit(session, "failed");
368
+ throw err;
369
+ }
370
+ }
371
+ session.stdin = managedRun.stdin;
372
+ session.pid = managedRun.pid;
373
+ const promise = managedRun
374
+ .wait()
375
+ .then((exit) => {
376
+ const durationMs = Date.now() - startedAt;
377
+ const isNormalExit = exit.reason === "exit";
378
+ const status = isNormalExit ? "completed" : "failed";
379
+ markExited(session, exit.exitCode, exit.exitSignal, status);
380
+ maybeNotifyOnExit(session, status);
381
+ if (!session.child && session.stdin) {
382
+ session.stdin.destroyed = true;
383
+ }
384
+ const aggregated = session.aggregated.trim();
385
+ if (status === "completed") {
386
+ const exitCode = exit.exitCode ?? 0;
387
+ const exitMsg = exitCode !== 0 ? `\n\n(Command exited with code ${exitCode})` : "";
388
+ return {
389
+ status: "completed",
390
+ exitCode,
391
+ exitSignal: exit.exitSignal,
392
+ durationMs,
393
+ aggregated: aggregated + exitMsg,
394
+ timedOut: false,
395
+ };
396
+ }
397
+ const reason = exit.reason === "overall-timeout"
398
+ ? `Command timed out after ${opts.timeoutSec} seconds`
399
+ : exit.reason === "no-output-timeout"
400
+ ? "Command timed out waiting for output"
401
+ : exit.exitSignal != null
402
+ ? `Command aborted by signal ${exit.exitSignal}`
403
+ : "Command aborted before exit code was captured";
404
+ return {
405
+ status: "failed",
406
+ exitCode: exit.exitCode,
407
+ exitSignal: exit.exitSignal,
408
+ durationMs,
409
+ aggregated,
410
+ timedOut: exit.timedOut,
411
+ reason: aggregated ? `${aggregated}\n\n${reason}` : reason,
412
+ };
413
+ })
414
+ .catch((err) => {
415
+ markExited(session, null, null, "failed");
416
+ maybeNotifyOnExit(session, "failed");
417
+ const aggregated = session.aggregated.trim();
418
+ const message = aggregated ? `${aggregated}\n\n${String(err)}` : String(err);
419
+ return {
420
+ status: "failed",
421
+ exitCode: null,
422
+ exitSignal: null,
423
+ durationMs: Date.now() - startedAt,
424
+ aggregated,
425
+ timedOut: false,
426
+ reason: message,
427
+ };
428
+ });
429
+ return {
430
+ session,
431
+ startedAt,
432
+ pid: session.pid ?? undefined,
433
+ promise,
434
+ kill: () => {
435
+ managedRun?.cancel("manual-cancel");
436
+ },
437
+ };
438
+ }
@@ -197,3 +197,9 @@ export function pad(str, width) {
197
197
  return str;
198
198
  return str + " ".repeat(width - str.length);
199
199
  }
200
+ export function clampWithDefault(value, defaultValue, min, max) {
201
+ if (value === undefined || Number.isNaN(value)) {
202
+ return defaultValue;
203
+ }
204
+ return Math.min(Math.max(value, min), max);
205
+ }
@@ -0,0 +1,61 @@
1
+ import path from "node:path";
2
+ import { CLI_FRESH_WATCHDOG_DEFAULTS, CLI_RESUME_WATCHDOG_DEFAULTS, CLI_WATCHDOG_MIN_TIMEOUT_MS, } from "../cli-watchdog-defaults.js";
3
+ function pickWatchdogProfile(backend, useResume) {
4
+ const defaults = useResume ? CLI_RESUME_WATCHDOG_DEFAULTS : CLI_FRESH_WATCHDOG_DEFAULTS;
5
+ const configured = useResume
6
+ ? backend.reliability?.watchdog?.resume
7
+ : backend.reliability?.watchdog?.fresh;
8
+ const ratio = (() => {
9
+ const value = configured?.noOutputTimeoutRatio;
10
+ if (typeof value !== "number" || !Number.isFinite(value)) {
11
+ return defaults.noOutputTimeoutRatio;
12
+ }
13
+ return Math.max(0.05, Math.min(0.95, value));
14
+ })();
15
+ const minMs = (() => {
16
+ const value = configured?.minMs;
17
+ if (typeof value !== "number" || !Number.isFinite(value)) {
18
+ return defaults.minMs;
19
+ }
20
+ return Math.max(CLI_WATCHDOG_MIN_TIMEOUT_MS, Math.floor(value));
21
+ })();
22
+ const maxMs = (() => {
23
+ const value = configured?.maxMs;
24
+ if (typeof value !== "number" || !Number.isFinite(value)) {
25
+ return defaults.maxMs;
26
+ }
27
+ return Math.max(CLI_WATCHDOG_MIN_TIMEOUT_MS, Math.floor(value));
28
+ })();
29
+ return {
30
+ noOutputTimeoutMs: typeof configured?.noOutputTimeoutMs === "number" &&
31
+ Number.isFinite(configured.noOutputTimeoutMs)
32
+ ? Math.max(CLI_WATCHDOG_MIN_TIMEOUT_MS, Math.floor(configured.noOutputTimeoutMs))
33
+ : undefined,
34
+ noOutputTimeoutRatio: ratio,
35
+ minMs: Math.min(minMs, maxMs),
36
+ maxMs: Math.max(minMs, maxMs),
37
+ };
38
+ }
39
+ export function resolveCliNoOutputTimeoutMs(params) {
40
+ const profile = pickWatchdogProfile(params.backend, params.useResume);
41
+ // Keep watchdog below global timeout in normal cases.
42
+ const cap = Math.max(CLI_WATCHDOG_MIN_TIMEOUT_MS, params.timeoutMs - 1_000);
43
+ if (profile.noOutputTimeoutMs !== undefined) {
44
+ return Math.min(profile.noOutputTimeoutMs, cap);
45
+ }
46
+ const computed = Math.floor(params.timeoutMs * profile.noOutputTimeoutRatio);
47
+ const bounded = Math.min(profile.maxMs, Math.max(profile.minMs, computed));
48
+ return Math.min(bounded, cap);
49
+ }
50
+ export function buildCliSupervisorScopeKey(params) {
51
+ const commandToken = path
52
+ .basename(params.backend.command ?? "")
53
+ .trim()
54
+ .toLowerCase();
55
+ const backendToken = params.backendId.trim().toLowerCase();
56
+ const sessionToken = params.cliSessionId?.trim();
57
+ if (!sessionToken) {
58
+ return undefined;
59
+ }
60
+ return `cli:${backendToken}:${commandToken}:${sessionToken}`;
61
+ }
@@ -0,0 +1,11 @@
1
+ export const CLI_WATCHDOG_MIN_TIMEOUT_MS = 1_000;
2
+ export const CLI_FRESH_WATCHDOG_DEFAULTS = {
3
+ noOutputTimeoutRatio: 0.8,
4
+ minMs: 180_000,
5
+ maxMs: 600_000,
6
+ };
7
+ export const CLI_RESUME_WATCHDOG_DEFAULTS = {
8
+ noOutputTimeoutRatio: 0.3,
9
+ minMs: 60_000,
10
+ maxMs: 180_000,
11
+ };
@@ -0,0 +1,63 @@
1
+ // Exponential backoff schedule for command polling
2
+ const BACKOFF_SCHEDULE_MS = [5000, 10000, 30000, 60000];
3
+ /**
4
+ * Calculate suggested retry delay based on consecutive no-output poll count.
5
+ * Implements exponential backoff schedule: 5s → 10s → 30s → 60s (capped).
6
+ */
7
+ export function calculateBackoffMs(consecutiveNoOutputPolls) {
8
+ const index = Math.min(consecutiveNoOutputPolls, BACKOFF_SCHEDULE_MS.length - 1);
9
+ return BACKOFF_SCHEDULE_MS[index] ?? 60000;
10
+ }
11
+ /**
12
+ * Record a command poll and return suggested retry delay.
13
+ * @param state Session state to track polling in
14
+ * @param commandId Unique identifier for the command being polled
15
+ * @param hasNewOutput Whether this poll returned new output
16
+ * @returns Suggested delay in milliseconds before next poll
17
+ */
18
+ export function recordCommandPoll(state, commandId, hasNewOutput) {
19
+ if (!state.commandPollCounts) {
20
+ state.commandPollCounts = new Map();
21
+ }
22
+ const existing = state.commandPollCounts.get(commandId);
23
+ const now = Date.now();
24
+ if (hasNewOutput) {
25
+ state.commandPollCounts.set(commandId, { count: 0, lastPollAt: now });
26
+ return BACKOFF_SCHEDULE_MS[0] ?? 5000;
27
+ }
28
+ const newCount = (existing?.count ?? -1) + 1;
29
+ state.commandPollCounts.set(commandId, { count: newCount, lastPollAt: now });
30
+ return calculateBackoffMs(newCount);
31
+ }
32
+ /**
33
+ * Get current suggested backoff for a command without modifying state.
34
+ * Useful for checking current backoff level.
35
+ */
36
+ export function getCommandPollSuggestion(state, commandId) {
37
+ const pollData = state.commandPollCounts?.get(commandId);
38
+ if (!pollData) {
39
+ return undefined;
40
+ }
41
+ return calculateBackoffMs(pollData.count);
42
+ }
43
+ /**
44
+ * Reset poll count for a command (e.g., when command completes).
45
+ */
46
+ export function resetCommandPollCount(state, commandId) {
47
+ state.commandPollCounts?.delete(commandId);
48
+ }
49
+ /**
50
+ * Prune stale command poll records (older than 1 hour).
51
+ * Call periodically to prevent memory bloat.
52
+ */
53
+ export function pruneStaleCommandPolls(state, maxAgeMs = 3600000) {
54
+ if (!state.commandPollCounts) {
55
+ return;
56
+ }
57
+ const now = Date.now();
58
+ for (const [commandId, data] of state.commandPollCounts.entries()) {
59
+ if (now - data.lastPollAt > maxAgeMs) {
60
+ state.commandPollCounts.delete(commandId);
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,16 @@
1
+ import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone, } from "./date-time.js";
2
+ export function resolveCronStyleNow(cfg, nowMs) {
3
+ const userTimezone = resolveUserTimezone(cfg.agents?.defaults?.userTimezone);
4
+ const userTimeFormat = resolveUserTimeFormat(cfg.agents?.defaults?.timeFormat);
5
+ const formattedTime = formatUserTime(new Date(nowMs), userTimezone, userTimeFormat) ?? new Date(nowMs).toISOString();
6
+ const timeLine = `Current time: ${formattedTime} (${userTimezone})`;
7
+ return { userTimezone, formattedTime, timeLine };
8
+ }
9
+ export function appendCronStyleCurrentTimeLine(text, cfg, nowMs) {
10
+ const base = text.trimEnd();
11
+ if (!base || base.includes("Current time:")) {
12
+ return base;
13
+ }
14
+ const { timeLine } = resolveCronStyleNow(cfg, nowMs);
15
+ return `${base}\n${timeLine}`;
16
+ }