@poolzin/pool-bot 2026.2.20 → 2026.2.22

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 (388) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/agents/api-key-rotation.js +47 -0
  3. package/dist/agents/apply-patch-update.js +19 -9
  4. package/dist/agents/apply-patch.js +72 -47
  5. package/dist/agents/bash-tools.exec.js +141 -559
  6. package/dist/agents/cli-backends.js +49 -6
  7. package/dist/agents/cli-runner/helpers.js +69 -152
  8. package/dist/agents/cli-runner.js +70 -19
  9. package/dist/agents/identity.js +20 -1
  10. package/dist/agents/image-sanitization.js +9 -0
  11. package/dist/agents/live-auth-keys.js +123 -26
  12. package/dist/agents/live-model-filter.js +13 -4
  13. package/dist/agents/model-auth.js +12 -0
  14. package/dist/agents/model-catalog.js +40 -9
  15. package/dist/agents/model-fallback.js +24 -0
  16. package/dist/agents/model-forward-compat.js +60 -23
  17. package/dist/agents/model-selection.js +134 -41
  18. package/dist/agents/pi-auth-json.js +2 -2
  19. package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
  20. package/dist/agents/pi-embedded-helpers/errors.js +140 -15
  21. package/dist/agents/pi-embedded-helpers/images.js +22 -12
  22. package/dist/agents/pi-embedded-helpers.js +2 -2
  23. package/dist/agents/pi-embedded-runner/abort.js +10 -3
  24. package/dist/agents/pi-embedded-runner/compact.js +230 -32
  25. package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
  26. package/dist/agents/pi-embedded-runner/google.js +109 -19
  27. package/dist/agents/pi-embedded-runner/history.js +35 -17
  28. package/dist/agents/pi-embedded-runner/run/attempt.js +386 -80
  29. package/dist/agents/pi-embedded-runner/run/images.js +81 -55
  30. package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
  31. package/dist/agents/pi-embedded-runner/run.js +193 -25
  32. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
  33. package/dist/agents/pi-embedded-runner/runs.js +17 -8
  34. package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
  35. package/dist/agents/pi-embedded-runner.js +1 -1
  36. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
  37. package/dist/agents/pi-embedded-subscribe.js +37 -0
  38. package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
  39. package/dist/agents/pi-model-discovery.js +9 -2
  40. package/dist/agents/pi-tool-definition-adapter.js +60 -8
  41. package/dist/agents/pi-tools.before-tool-call.js +1 -1
  42. package/dist/agents/pi-tools.js +113 -94
  43. package/dist/agents/pi-tools.read.js +337 -38
  44. package/dist/agents/poolbot-tools.js +14 -5
  45. package/dist/agents/provider/config-loader.js +76 -0
  46. package/dist/agents/provider/index.js +15 -0
  47. package/dist/agents/provider/integration.js +136 -0
  48. package/dist/agents/provider/models-dev.js +129 -0
  49. package/dist/agents/provider/rate-limits.js +458 -0
  50. package/dist/agents/provider/request-monitor.js +449 -0
  51. package/dist/agents/provider/session-binding.js +376 -0
  52. package/dist/agents/provider/token-pool.js +541 -0
  53. package/dist/agents/sandbox/docker.js +10 -5
  54. package/dist/agents/sandbox/registry.js +96 -46
  55. package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
  56. package/dist/agents/sandbox-paths.js +43 -10
  57. package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
  58. package/dist/agents/session-tool-result-guard.js +39 -39
  59. package/dist/agents/session-transcript-repair.js +36 -33
  60. package/dist/agents/session-write-lock.js +62 -44
  61. package/dist/agents/skills/frontmatter.js +49 -88
  62. package/dist/agents/skills/workspace.js +335 -28
  63. package/dist/agents/subagent-announce.js +508 -174
  64. package/dist/agents/subagent-registry.js +45 -4
  65. package/dist/agents/subagent-spawn.js +16 -33
  66. package/dist/agents/system-prompt-report.js +27 -10
  67. package/dist/agents/system-prompt.js +26 -32
  68. package/dist/agents/tool-call-id.js +69 -17
  69. package/dist/agents/tool-display-common.js +1 -1
  70. package/dist/agents/tool-images.js +64 -31
  71. package/dist/agents/tools/canvas-tool.js +17 -11
  72. package/dist/agents/tools/common.js +37 -19
  73. package/dist/agents/tools/cron-tool.js +40 -38
  74. package/dist/agents/tools/gateway.js +70 -2
  75. package/dist/agents/tools/message-tool.js +181 -40
  76. package/dist/agents/tools/nodes-tool.js +128 -36
  77. package/dist/agents/tools/nodes-utils.js +12 -38
  78. package/dist/agents/tools/session-status-tool.js +24 -71
  79. package/dist/agents/tools/sessions-helpers.js +38 -210
  80. package/dist/agents/tools/sessions-spawn-tool.js +28 -198
  81. package/dist/agents/tools/telegram-actions.js +58 -7
  82. package/dist/agents/tools/web-fetch-utils.js +112 -7
  83. package/dist/agents/tools/web-fetch.js +279 -175
  84. package/dist/agents/tools/web-shared.js +71 -8
  85. package/dist/agents/usage.js +25 -16
  86. package/dist/auto-reply/commands-registry.data.js +85 -11
  87. package/dist/auto-reply/dispatch.js +40 -21
  88. package/dist/auto-reply/reply/abort.js +102 -33
  89. package/dist/auto-reply/reply/commands-core.js +82 -33
  90. package/dist/auto-reply/reply/commands-export-session.js +1 -1
  91. package/dist/auto-reply/reply/commands-info.js +41 -12
  92. package/dist/auto-reply/reply/commands-subagents.js +352 -100
  93. package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
  94. package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
  95. package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
  96. package/dist/auto-reply/reply/inbound-meta.js +12 -1
  97. package/dist/auto-reply/reply/mentions.js +18 -11
  98. package/dist/auto-reply/reply/normalize-reply.js +17 -8
  99. package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
  100. package/dist/auto-reply/reply/session.js +102 -21
  101. package/dist/auto-reply/reply/streaming-directives.js +16 -5
  102. package/dist/auto-reply/status.js +73 -50
  103. package/dist/browser/extension-relay.js +3 -3
  104. package/dist/browser/http-auth.js +1 -1
  105. package/dist/browser/paths.js +2 -2
  106. package/dist/build-info.json +3 -3
  107. package/dist/channels/allowlist-match.js +20 -0
  108. package/dist/channels/allowlists/resolve-utils.js +65 -2
  109. package/dist/channels/chat-type.js +8 -4
  110. package/dist/channels/dock.js +127 -35
  111. package/dist/channels/draft-stream-loop.js +6 -2
  112. package/dist/channels/plugins/actions/telegram.js +42 -18
  113. package/dist/channels/plugins/allowlist-match.js +1 -1
  114. package/dist/channels/plugins/group-mentions.js +51 -41
  115. package/dist/channels/plugins/message-action-names.js +2 -0
  116. package/dist/channels/plugins/message-actions.js +24 -5
  117. package/dist/channels/plugins/normalize/discord.js +26 -4
  118. package/dist/channels/plugins/normalize/signal.js +35 -22
  119. package/dist/channels/plugins/onboarding/helpers.js +8 -26
  120. package/dist/channels/plugins/outbound/imessage.js +15 -14
  121. package/dist/channels/registry.js +20 -7
  122. package/dist/cli/acp-cli.js +7 -5
  123. package/dist/cli/browser-cli-extension.js +25 -12
  124. package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
  125. package/dist/cli/browser-cli-state.js +101 -145
  126. package/dist/cli/command-options.js +28 -0
  127. package/dist/cli/completion-cli.js +6 -6
  128. package/dist/cli/cron-cli/register.cron-add.js +25 -1
  129. package/dist/cli/cron-cli/register.cron-edit.js +44 -0
  130. package/dist/cli/cron-cli/shared.js +7 -1
  131. package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
  132. package/dist/cli/daemon-cli/lifecycle.js +23 -247
  133. package/dist/cli/daemon-cli/register-service-commands.js +25 -4
  134. package/dist/cli/daemon-cli.js +1 -0
  135. package/dist/cli/devices-cli.js +33 -20
  136. package/dist/cli/gateway-cli/register.js +37 -105
  137. package/dist/cli/gateway-cli/run.js +49 -11
  138. package/dist/cli/nodes-camera.js +59 -4
  139. package/dist/cli/nodes-cli/register.camera.js +27 -24
  140. package/dist/cli/nodes-cli/rpc.js +21 -38
  141. package/dist/cli/qr-cli.js +2 -2
  142. package/dist/cli/skills-cli.format.js +2 -2
  143. package/dist/cli/update-cli/progress.js +2 -2
  144. package/dist/cli/update-cli/restart-helper.js +28 -7
  145. package/dist/cli/update-cli/shared.js +7 -7
  146. package/dist/cli/update-cli/status.js +1 -1
  147. package/dist/cli/update-cli/update-command.js +14 -8
  148. package/dist/cli/update-cli/wizard.js +2 -2
  149. package/dist/cli/update-cli.js +21 -1027
  150. package/dist/commands/auth-choice.apply.anthropic.js +10 -2
  151. package/dist/commands/channels/add-mutators.js +3 -35
  152. package/dist/commands/channels/add.js +39 -51
  153. package/dist/commands/config-validation.js +1 -1
  154. package/dist/commands/configure.gateway-auth.js +52 -15
  155. package/dist/commands/configure.gateway.js +84 -40
  156. package/dist/commands/doctor-completion.js +3 -3
  157. package/dist/commands/doctor-config-flow.js +536 -16
  158. package/dist/commands/doctor-gateway-services.js +103 -79
  159. package/dist/commands/doctor-memory-search.js +9 -9
  160. package/dist/commands/doctor-platform-notes.js +57 -30
  161. package/dist/commands/doctor-prompter.js +26 -15
  162. package/dist/commands/doctor-session-locks.js +1 -1
  163. package/dist/commands/doctor.js +21 -9
  164. package/dist/commands/model-picker.js +120 -95
  165. package/dist/commands/models/set.js +2 -21
  166. package/dist/commands/models/shared.js +65 -37
  167. package/dist/commands/onboard-helpers.js +81 -39
  168. package/dist/commands/openai-codex-oauth.js +1 -1
  169. package/dist/commands/sessions.js +52 -53
  170. package/dist/commands/status.summary.js +52 -34
  171. package/dist/commands/test-wizard-helpers.js +2 -2
  172. package/dist/config/defaults.js +79 -42
  173. package/dist/config/group-policy.js +50 -18
  174. package/dist/config/includes.js +37 -10
  175. package/dist/config/schema.help.js +5 -4
  176. package/dist/config/schema.hints.js +2 -2
  177. package/dist/config/schema.labels.js +1 -0
  178. package/dist/config/sessions/group.js +12 -11
  179. package/dist/config/sessions/paths.js +137 -11
  180. package/dist/config/sessions/store.js +185 -65
  181. package/dist/config/sessions/types.js +15 -1
  182. package/dist/config/sessions.js +1 -0
  183. package/dist/config/telegram-custom-commands.js +3 -2
  184. package/dist/config/types.js +2 -0
  185. package/dist/config/zod-schema.agent-defaults.js +6 -27
  186. package/dist/config/zod-schema.agent-runtime.js +171 -79
  187. package/dist/config/zod-schema.providers-core.js +138 -65
  188. package/dist/config/zod-schema.session.js +49 -22
  189. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  190. package/dist/cron/isolated-agent/run.js +224 -57
  191. package/dist/cron/normalize.js +48 -45
  192. package/dist/cron/run-log.js +14 -0
  193. package/dist/cron/service/jobs.js +190 -28
  194. package/dist/cron/service/normalize.js +29 -11
  195. package/dist/cron/service/store.js +30 -44
  196. package/dist/cron/service/timer.js +182 -96
  197. package/dist/cron/service.js +3 -0
  198. package/dist/cron/stagger.js +37 -0
  199. package/dist/daemon/inspect.js +132 -92
  200. package/dist/daemon/runtime-paths.js +25 -4
  201. package/dist/daemon/service-audit.js +47 -16
  202. package/dist/discord/accounts.js +23 -20
  203. package/dist/discord/monitor/agent-components.js +1115 -219
  204. package/dist/discord/monitor/allow-list.js +114 -34
  205. package/dist/discord/monitor/listeners.js +204 -97
  206. package/dist/discord/monitor/message-handler.js +21 -10
  207. package/dist/discord/monitor/message-handler.preflight.js +195 -101
  208. package/dist/discord/monitor/message-handler.process.js +384 -123
  209. package/dist/discord/monitor/message-utils.js +86 -23
  210. package/dist/discord/monitor/native-command.js +77 -57
  211. package/dist/discord/monitor/provider.js +122 -117
  212. package/dist/discord/monitor/reply-context.js +20 -16
  213. package/dist/discord/monitor/reply-delivery.js +40 -8
  214. package/dist/discord/monitor/rest-fetch.js +22 -0
  215. package/dist/discord/monitor/threading.js +117 -24
  216. package/dist/discord/send.js +2 -1
  217. package/dist/discord/send.outbound.js +124 -11
  218. package/dist/discord/send.shared.js +112 -72
  219. package/dist/discord/voice-message.js +3 -3
  220. package/dist/gateway/auth.js +119 -44
  221. package/dist/gateway/call.js +76 -34
  222. package/dist/gateway/channel-health-monitor.js +57 -50
  223. package/dist/gateway/client.js +63 -29
  224. package/dist/gateway/control-ui-contract.js +1 -1
  225. package/dist/gateway/gateway-config-prompts.shared.js +2 -2
  226. package/dist/gateway/net.js +109 -1
  227. package/dist/gateway/protocol/index.js +5 -8
  228. package/dist/gateway/protocol/schema/agent.js +19 -1
  229. package/dist/gateway/protocol/schema/channels.js +21 -0
  230. package/dist/gateway/protocol/schema/cron.js +43 -30
  231. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
  232. package/dist/gateway/protocol/schema/sessions.js +5 -1
  233. package/dist/gateway/protocol/schema.js +0 -1
  234. package/dist/gateway/server/presence-events.js +12 -0
  235. package/dist/gateway/server/ws-connection/message-handler.js +203 -212
  236. package/dist/gateway/server/ws-connection.js +58 -21
  237. package/dist/gateway/server-broadcast.js +18 -13
  238. package/dist/gateway/server-cron.js +177 -10
  239. package/dist/gateway/server-methods/agent-job.js +131 -38
  240. package/dist/gateway/server-methods/send.js +60 -14
  241. package/dist/gateway/server-methods/sessions.js +160 -96
  242. package/dist/gateway/server-methods/system.js +5 -7
  243. package/dist/gateway/server-methods-list.js +8 -0
  244. package/dist/gateway/server-methods.js +24 -8
  245. package/dist/gateway/server-node-events.js +278 -68
  246. package/dist/gateway/session-utils.fs.js +316 -75
  247. package/dist/gateway/session-utils.js +224 -70
  248. package/dist/gateway/sessions-patch.js +63 -20
  249. package/dist/gateway/test-temp-config.js +1 -1
  250. package/dist/gateway/tools-invoke-http.js +118 -70
  251. package/dist/gateway/ws-log.js +135 -107
  252. package/dist/hooks/frontmatter.js +36 -82
  253. package/dist/hooks/install.js +149 -139
  254. package/dist/hooks/internal-hooks.js +29 -4
  255. package/dist/hooks/plugin-hooks.js +2 -1
  256. package/dist/imessage/monitor/deliver.js +10 -4
  257. package/dist/imessage/monitor/monitor-provider.js +138 -375
  258. package/dist/imessage/monitor/runtime.js +4 -8
  259. package/dist/imessage/send.js +65 -19
  260. package/dist/infra/exec-approvals-allowlist.js +7 -0
  261. package/dist/infra/exec-approvals.js +35 -920
  262. package/dist/infra/exec-safe-bin-trust.js +64 -0
  263. package/dist/infra/heartbeat-runner.js +207 -134
  264. package/dist/infra/heartbeat-wake.js +183 -22
  265. package/dist/infra/install-source-utils.js +47 -0
  266. package/dist/infra/net/ssrf.js +170 -36
  267. package/dist/infra/outbound/deliver.js +224 -58
  268. package/dist/infra/outbound/message-action-spec.js +12 -5
  269. package/dist/infra/outbound/outbound-session.js +27 -25
  270. package/dist/infra/poolbot-root.js +32 -22
  271. package/dist/infra/ports.js +14 -11
  272. package/dist/infra/skills-remote.js +48 -37
  273. package/dist/infra/system-events.js +25 -11
  274. package/dist/infra/system-presence.js +26 -33
  275. package/dist/infra/tmp-poolbot-dir.js +81 -2
  276. package/dist/infra/wsl.js +37 -1
  277. package/dist/line/bot-message-context.js +163 -191
  278. package/dist/logging/subsystem.js +59 -22
  279. package/dist/markdown/ir.js +124 -50
  280. package/dist/media/store.js +1 -1
  281. package/dist/media-understanding/runner.entries.js +42 -25
  282. package/dist/media-understanding/runner.js +53 -488
  283. package/dist/memory/embeddings-gemini.js +53 -38
  284. package/dist/memory/manager-embedding-ops.js +48 -69
  285. package/dist/pairing/pairing-store.js +178 -119
  286. package/dist/plugin-sdk/index.js +34 -6
  287. package/dist/plugins/hooks.js +135 -14
  288. package/dist/plugins/install.js +190 -152
  289. package/dist/polls.js +11 -0
  290. package/dist/routing/resolve-route.js +190 -56
  291. package/dist/routing/session-key.js +38 -22
  292. package/dist/runtime.js +35 -9
  293. package/dist/security/audit-channel.js +1 -1
  294. package/dist/sessions/session-key-utils.js +29 -11
  295. package/dist/shared/frontmatter.js +5 -5
  296. package/dist/shared/node-list-types.js +1 -0
  297. package/dist/shared/string-normalization.js +15 -0
  298. package/dist/signal/monitor/event-handler.js +68 -36
  299. package/dist/signal/send.js +29 -37
  300. package/dist/slack/monitor/allow-list.js +10 -11
  301. package/dist/slack/monitor/commands.js +14 -3
  302. package/dist/slack/monitor/events/interactions.js +4 -4
  303. package/dist/slack/monitor/media.js +224 -16
  304. package/dist/slack/monitor/message-handler/dispatch.js +247 -13
  305. package/dist/slack/monitor/message-handler/prepare.js +128 -45
  306. package/dist/slack/monitor/slash.js +357 -144
  307. package/dist/slack/streaming.js +77 -0
  308. package/dist/telegram/accounts.js +40 -13
  309. package/dist/telegram/allowed-updates.js +3 -0
  310. package/dist/telegram/bot/delivery.js +129 -66
  311. package/dist/telegram/bot/helpers.js +136 -122
  312. package/dist/telegram/bot-handlers.js +600 -339
  313. package/dist/telegram/bot-message-context.js +115 -73
  314. package/dist/telegram/bot-message-dispatch.js +235 -104
  315. package/dist/telegram/bot-native-command-menu.js +3 -1
  316. package/dist/telegram/bot-native-commands.js +213 -193
  317. package/dist/telegram/bot.js +24 -132
  318. package/dist/telegram/draft-stream.js +84 -75
  319. package/dist/telegram/format.js +150 -6
  320. package/dist/telegram/send.js +415 -255
  321. package/dist/telegram/targets.js +21 -2
  322. package/dist/telegram/update-offset-store.js +19 -3
  323. package/dist/terminal/restore.js +5 -2
  324. package/dist/test-utils/fetch-mock.js +5 -0
  325. package/dist/version.js +18 -5
  326. package/dist/web/auto-reply/monitor/broadcast.js +7 -3
  327. package/dist/web/auto-reply/monitor/on-message.js +6 -3
  328. package/dist/web/inbound/media.js +34 -8
  329. package/dist/web/inbound/monitor.js +34 -17
  330. package/dist/web/inbound/send-api.js +18 -17
  331. package/dist/web/outbound.js +12 -5
  332. package/dist/wizard/clack-prompter.js +40 -7
  333. package/extensions/bluebubbles/package.json +1 -1
  334. package/extensions/copilot-proxy/package.json +1 -1
  335. package/extensions/diagnostics-otel/package.json +1 -1
  336. package/extensions/discord/package.json +1 -1
  337. package/extensions/feishu/package.json +1 -1
  338. package/extensions/google-antigravity-auth/package.json +1 -1
  339. package/extensions/google-gemini-cli-auth/package.json +1 -1
  340. package/extensions/googlechat/package.json +1 -1
  341. package/extensions/imessage/package.json +1 -1
  342. package/extensions/irc/package.json +1 -1
  343. package/extensions/line/package.json +1 -1
  344. package/extensions/llm-task/package.json +1 -1
  345. package/extensions/lobster/package.json +1 -1
  346. package/extensions/matrix/CHANGELOG.md +5 -0
  347. package/extensions/matrix/package.json +1 -1
  348. package/extensions/mattermost/package.json +1 -1
  349. package/extensions/memory-core/package.json +1 -1
  350. package/extensions/memory-lancedb/package.json +1 -1
  351. package/extensions/minimax-portal-auth/package.json +1 -1
  352. package/extensions/msteams/CHANGELOG.md +5 -0
  353. package/extensions/msteams/package.json +1 -1
  354. package/extensions/nextcloud-talk/package.json +1 -1
  355. package/extensions/nostr/CHANGELOG.md +5 -0
  356. package/extensions/nostr/package.json +1 -1
  357. package/extensions/open-prose/package.json +1 -1
  358. package/extensions/openai-codex-auth/package.json +1 -1
  359. package/extensions/signal/package.json +1 -1
  360. package/extensions/slack/package.json +1 -1
  361. package/extensions/telegram/package.json +1 -1
  362. package/extensions/tlon/package.json +1 -1
  363. package/extensions/twitch/CHANGELOG.md +5 -0
  364. package/extensions/twitch/package.json +1 -1
  365. package/extensions/voice-call/CHANGELOG.md +5 -0
  366. package/extensions/voice-call/package.json +1 -1
  367. package/extensions/whatsapp/package.json +1 -1
  368. package/extensions/zalo/CHANGELOG.md +5 -0
  369. package/extensions/zalo/package.json +1 -1
  370. package/extensions/zalouser/CHANGELOG.md +5 -0
  371. package/extensions/zalouser/package.json +1 -1
  372. package/package.json +1 -1
  373. package/skills/apple-reminders/SKILL.md +100 -49
  374. package/skills/coding-agent/SKILL.md +34 -28
  375. package/skills/github/SKILL.md +131 -16
  376. package/skills/imsg/SKILL.md +112 -15
  377. package/skills/openhue/SKILL.md +101 -19
  378. package/skills/plcode-controller/SKILL.md +156 -0
  379. package/skills/plcode-controller/assets/operator-prompts.md +65 -0
  380. package/skills/plcode-controller/references/command-cheatsheet.md +53 -0
  381. package/skills/plcode-controller/references/failure-handling.md +60 -0
  382. package/skills/plcode-controller/references/model-selection.md +57 -0
  383. package/skills/plcode-controller/references/plan-vs-build.md +52 -0
  384. package/skills/plcode-controller/references/question-handling.md +40 -0
  385. package/skills/plcode-controller/references/session-management.md +63 -0
  386. package/skills/plcode-controller/references/workflow.md +35 -0
  387. package/skills/tmux/SKILL.md +111 -79
  388. package/skills/weather/SKILL.md +88 -25
@@ -1,14 +1,19 @@
1
+ import { CLI_FRESH_WATCHDOG_DEFAULTS, CLI_RESUME_WATCHDOG_DEFAULTS, } from "./cli-watchdog-defaults.js";
1
2
  import { normalizeProviderId } from "./model-selection.js";
2
3
  const CLAUDE_MODEL_ALIASES = {
3
4
  opus: "opus",
5
+ "opus-4.6": "opus",
4
6
  "opus-4.5": "opus",
5
7
  "opus-4": "opus",
8
+ "claude-opus-4-6": "opus",
6
9
  "claude-opus-4-5": "opus",
7
10
  "claude-opus-4": "opus",
8
11
  sonnet: "sonnet",
12
+ "sonnet-4.6": "sonnet",
9
13
  "sonnet-4.5": "sonnet",
10
14
  "sonnet-4.1": "sonnet",
11
15
  "sonnet-4.0": "sonnet",
16
+ "claude-sonnet-4-6": "sonnet",
12
17
  "claude-sonnet-4-5": "sonnet",
13
18
  "claude-sonnet-4-1": "sonnet",
14
19
  "claude-sonnet-4-0": "sonnet",
@@ -38,6 +43,12 @@ const DEFAULT_CLAUDE_BACKEND = {
38
43
  systemPromptMode: "append",
39
44
  systemPromptWhen: "first",
40
45
  clearEnv: ["ANTHROPIC_API_KEY", "ANTHROPIC_API_KEY_OLD"],
46
+ reliability: {
47
+ watchdog: {
48
+ fresh: { ...CLI_FRESH_WATCHDOG_DEFAULTS },
49
+ resume: { ...CLI_RESUME_WATCHDOG_DEFAULTS },
50
+ },
51
+ },
41
52
  serialize: true,
42
53
  };
43
54
  const DEFAULT_CODEX_BACKEND = {
@@ -61,6 +72,12 @@ const DEFAULT_CODEX_BACKEND = {
61
72
  sessionMode: "existing",
62
73
  imageArg: "--image",
63
74
  imageMode: "repeat",
75
+ reliability: {
76
+ watchdog: {
77
+ fresh: { ...CLI_FRESH_WATCHDOG_DEFAULTS },
78
+ resume: { ...CLI_RESUME_WATCHDOG_DEFAULTS },
79
+ },
80
+ },
64
81
  serialize: true,
65
82
  };
66
83
  function normalizeBackendKey(key) {
@@ -68,14 +85,20 @@ function normalizeBackendKey(key) {
68
85
  }
69
86
  function pickBackendConfig(config, normalizedId) {
70
87
  for (const [key, entry] of Object.entries(config)) {
71
- if (normalizeBackendKey(key) === normalizedId)
88
+ if (normalizeBackendKey(key) === normalizedId) {
72
89
  return entry;
90
+ }
73
91
  }
74
92
  return undefined;
75
93
  }
76
94
  function mergeBackendConfig(base, override) {
77
- if (!override)
95
+ if (!override) {
78
96
  return { ...base };
97
+ }
98
+ const baseFresh = base.reliability?.watchdog?.fresh ?? {};
99
+ const baseResume = base.reliability?.watchdog?.resume ?? {};
100
+ const overrideFresh = override.reliability?.watchdog?.fresh ?? {};
101
+ const overrideResume = override.reliability?.watchdog?.resume ?? {};
79
102
  return {
80
103
  ...base,
81
104
  ...override,
@@ -86,6 +109,22 @@ function mergeBackendConfig(base, override) {
86
109
  sessionIdFields: override.sessionIdFields ?? base.sessionIdFields,
87
110
  sessionArgs: override.sessionArgs ?? base.sessionArgs,
88
111
  resumeArgs: override.resumeArgs ?? base.resumeArgs,
112
+ reliability: {
113
+ ...base.reliability,
114
+ ...override.reliability,
115
+ watchdog: {
116
+ ...base.reliability?.watchdog,
117
+ ...override.reliability?.watchdog,
118
+ fresh: {
119
+ ...baseFresh,
120
+ ...overrideFresh,
121
+ },
122
+ resume: {
123
+ ...baseResume,
124
+ ...overrideResume,
125
+ },
126
+ },
127
+ },
89
128
  };
90
129
  }
91
130
  export function resolveCliBackendIds(cfg) {
@@ -106,21 +145,25 @@ export function resolveCliBackendConfig(provider, cfg) {
106
145
  if (normalized === "claude-cli") {
107
146
  const merged = mergeBackendConfig(DEFAULT_CLAUDE_BACKEND, override);
108
147
  const command = merged.command?.trim();
109
- if (!command)
148
+ if (!command) {
110
149
  return null;
150
+ }
111
151
  return { id: normalized, config: { ...merged, command } };
112
152
  }
113
153
  if (normalized === "codex-cli") {
114
154
  const merged = mergeBackendConfig(DEFAULT_CODEX_BACKEND, override);
115
155
  const command = merged.command?.trim();
116
- if (!command)
156
+ if (!command) {
117
157
  return null;
158
+ }
118
159
  return { id: normalized, config: { ...merged, command } };
119
160
  }
120
- if (!override)
161
+ if (!override) {
121
162
  return null;
163
+ }
122
164
  const command = override.command?.trim();
123
- if (!command)
165
+ if (!command) {
124
166
  return null;
167
+ }
125
168
  return { id: normalized, config: { ...override, command } };
126
169
  }
@@ -2,120 +2,22 @@ import crypto from "node:crypto";
2
2
  import fs from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
- import { runExec } from "../../process/exec.js";
6
5
  import { buildTtsSystemPromptHint } from "../../tts/tts.js";
7
- import { escapeRegExp, isRecord } from "../../utils.js";
6
+ import { isRecord } from "../../utils.js";
7
+ import { buildModelAliasLines } from "../model-alias-lines.js";
8
8
  import { resolveDefaultModelForAgent } from "../model-selection.js";
9
9
  import { detectRuntimeShell } from "../shell-utils.js";
10
10
  import { buildSystemPromptParams } from "../system-prompt-params.js";
11
11
  import { buildAgentSystemPrompt } from "../system-prompt.js";
12
+ export { buildCliSupervisorScopeKey, resolveCliNoOutputTimeoutMs } from "./reliability.js";
12
13
  const CLI_RUN_QUEUE = new Map();
13
- export async function cleanupResumeProcesses(backend, sessionId) {
14
- if (process.platform === "win32")
15
- return;
16
- const resumeArgs = backend.resumeArgs ?? [];
17
- if (resumeArgs.length === 0)
18
- return;
19
- if (!resumeArgs.some((arg) => arg.includes("{sessionId}")))
20
- return;
21
- const commandToken = path.basename(backend.command ?? "").trim();
22
- if (!commandToken)
23
- return;
24
- const resumeTokens = resumeArgs.map((arg) => arg.replaceAll("{sessionId}", sessionId));
25
- const pattern = [commandToken, ...resumeTokens]
26
- .filter(Boolean)
27
- .map((token) => escapeRegExp(token))
28
- .join(".*");
29
- if (!pattern)
30
- return;
31
- try {
32
- await runExec("pkill", ["-f", pattern]);
33
- }
34
- catch {
35
- // ignore missing pkill or no matches
36
- }
37
- }
38
- function buildSessionMatchers(backend) {
39
- const commandToken = path.basename(backend.command ?? "").trim();
40
- if (!commandToken)
41
- return [];
42
- const matchers = [];
43
- const sessionArg = backend.sessionArg?.trim();
44
- const sessionArgs = backend.sessionArgs ?? [];
45
- const resumeArgs = backend.resumeArgs ?? [];
46
- const addMatcher = (args) => {
47
- if (args.length === 0)
48
- return;
49
- const tokens = [commandToken, ...args];
50
- const pattern = tokens
51
- .map((token, index) => {
52
- const tokenPattern = tokenToRegex(token);
53
- return index === 0 ? `(?:^|\\s)${tokenPattern}` : `\\s+${tokenPattern}`;
54
- })
55
- .join("");
56
- matchers.push(new RegExp(pattern));
57
- };
58
- if (sessionArgs.some((arg) => arg.includes("{sessionId}"))) {
59
- addMatcher(sessionArgs);
60
- }
61
- else if (sessionArg) {
62
- addMatcher([sessionArg, "{sessionId}"]);
63
- }
64
- if (resumeArgs.some((arg) => arg.includes("{sessionId}"))) {
65
- addMatcher(resumeArgs);
66
- }
67
- return matchers;
68
- }
69
- function tokenToRegex(token) {
70
- if (!token.includes("{sessionId}"))
71
- return escapeRegExp(token);
72
- const parts = token.split("{sessionId}").map((part) => escapeRegExp(part));
73
- return parts.join("\\S+");
74
- }
75
- /**
76
- * Cleanup suspended Poolbot CLI processes that have accumulated.
77
- * Only cleans up if there are more than the threshold (default: 10).
78
- */
79
- export async function cleanupSuspendedCliProcesses(backend, threshold = 10) {
80
- if (process.platform === "win32")
81
- return;
82
- const matchers = buildSessionMatchers(backend);
83
- if (matchers.length === 0)
84
- return;
85
- try {
86
- const { stdout } = await runExec("ps", ["-ax", "-o", "pid=,stat=,command="]);
87
- const suspended = [];
88
- for (const line of stdout.split("\n")) {
89
- const trimmed = line.trim();
90
- if (!trimmed)
91
- continue;
92
- const match = /^(\d+)\s+(\S+)\s+(.*)$/.exec(trimmed);
93
- if (!match)
94
- continue;
95
- const pid = Number(match[1]);
96
- const stat = match[2] ?? "";
97
- const command = match[3] ?? "";
98
- if (!Number.isFinite(pid))
99
- continue;
100
- if (!stat.includes("T"))
101
- continue;
102
- if (!matchers.some((matcher) => matcher.test(command)))
103
- continue;
104
- suspended.push(pid);
105
- }
106
- if (suspended.length > threshold) {
107
- // Verified locally: stopped (T) processes ignore SIGTERM, so use SIGKILL.
108
- await runExec("kill", ["-9", ...suspended.map((pid) => String(pid))]);
109
- }
110
- }
111
- catch {
112
- // ignore errors - best effort cleanup
113
- }
114
- }
115
14
  export function enqueueCliRun(key, task) {
116
15
  const prior = CLI_RUN_QUEUE.get(key) ?? Promise.resolve();
117
16
  const chained = prior.catch(() => undefined).then(task);
118
- const tracked = chained.finally(() => {
17
+ // Keep queue continuity even when a run rejects, without emitting unhandled rejections.
18
+ const tracked = chained
19
+ .catch(() => undefined)
20
+ .finally(() => {
119
21
  if (CLI_RUN_QUEUE.get(key) === tracked) {
120
22
  CLI_RUN_QUEUE.delete(key);
121
23
  }
@@ -123,22 +25,6 @@ export function enqueueCliRun(key, task) {
123
25
  CLI_RUN_QUEUE.set(key, tracked);
124
26
  return chained;
125
27
  }
126
- function buildModelAliasLines(cfg) {
127
- const models = cfg?.agents?.defaults?.models ?? {};
128
- const entries = [];
129
- for (const [keyRaw, entryRaw] of Object.entries(models)) {
130
- const model = String(keyRaw ?? "").trim();
131
- if (!model)
132
- continue;
133
- const alias = String(entryRaw?.alias ?? "").trim();
134
- if (!alias)
135
- continue;
136
- entries.push({ alias, model });
137
- }
138
- return entries
139
- .sort((a, b) => a.alias.localeCompare(b.alias))
140
- .map((entry) => `- ${entry.alias}: ${entry.model}`);
141
- }
142
28
  export function buildSystemPrompt(params) {
143
29
  const defaultModelRef = resolveDefaultModelForAgent({
144
30
  cfg: params.config ?? {},
@@ -182,15 +68,18 @@ export function buildSystemPrompt(params) {
182
68
  }
183
69
  export function normalizeCliModel(modelId, backend) {
184
70
  const trimmed = modelId.trim();
185
- if (!trimmed)
71
+ if (!trimmed) {
186
72
  return trimmed;
73
+ }
187
74
  const direct = backend.modelAliases?.[trimmed];
188
- if (direct)
75
+ if (direct) {
189
76
  return direct;
77
+ }
190
78
  const lower = trimmed.toLowerCase();
191
79
  const mapped = backend.modelAliases?.[lower];
192
- if (mapped)
80
+ if (mapped) {
193
81
  return mapped;
82
+ }
194
83
  return trimmed;
195
84
  }
196
85
  function toUsage(raw) {
@@ -200,27 +89,36 @@ function toUsage(raw) {
200
89
  const cacheRead = pick("cache_read_input_tokens") ?? pick("cached_input_tokens") ?? pick("cacheRead");
201
90
  const cacheWrite = pick("cache_write_input_tokens") ?? pick("cacheWrite");
202
91
  const total = pick("total_tokens") ?? pick("total");
203
- if (!input && !output && !cacheRead && !cacheWrite && !total)
92
+ if (!input && !output && !cacheRead && !cacheWrite && !total) {
204
93
  return undefined;
94
+ }
205
95
  return { input, output, cacheRead, cacheWrite, total };
206
96
  }
207
97
  function collectText(value) {
208
- if (!value)
98
+ if (!value) {
209
99
  return "";
210
- if (typeof value === "string")
100
+ }
101
+ if (typeof value === "string") {
211
102
  return value;
212
- if (Array.isArray(value))
103
+ }
104
+ if (Array.isArray(value)) {
213
105
  return value.map((entry) => collectText(entry)).join("");
214
- if (!isRecord(value))
106
+ }
107
+ if (!isRecord(value)) {
215
108
  return "";
216
- if (typeof value.text === "string")
109
+ }
110
+ if (typeof value.text === "string") {
217
111
  return value.text;
218
- if (typeof value.content === "string")
112
+ }
113
+ if (typeof value.content === "string") {
219
114
  return value.content;
220
- if (Array.isArray(value.content))
115
+ }
116
+ if (Array.isArray(value.content)) {
221
117
  return value.content.map((entry) => collectText(entry)).join("");
222
- if (isRecord(value.message))
118
+ }
119
+ if (isRecord(value.message)) {
223
120
  return collectText(value.message);
121
+ }
224
122
  return "";
225
123
  }
226
124
  function pickSessionId(parsed, backend) {
@@ -232,15 +130,17 @@ function pickSessionId(parsed, backend) {
232
130
  ];
233
131
  for (const field of fields) {
234
132
  const value = parsed[field];
235
- if (typeof value === "string" && value.trim())
133
+ if (typeof value === "string" && value.trim()) {
236
134
  return value.trim();
135
+ }
237
136
  }
238
137
  return undefined;
239
138
  }
240
139
  export function parseCliJson(raw, backend) {
241
140
  const trimmed = raw.trim();
242
- if (!trimmed)
141
+ if (!trimmed) {
243
142
  return null;
143
+ }
244
144
  let parsed;
245
145
  try {
246
146
  parsed = JSON.parse(trimmed);
@@ -248,8 +148,9 @@ export function parseCliJson(raw, backend) {
248
148
  catch {
249
149
  return null;
250
150
  }
251
- if (!isRecord(parsed))
151
+ if (!isRecord(parsed)) {
252
152
  return null;
153
+ }
253
154
  const sessionId = pickSessionId(parsed, backend);
254
155
  const usage = isRecord(parsed.usage) ? toUsage(parsed.usage) : undefined;
255
156
  const text = collectText(parsed.message) ||
@@ -263,8 +164,9 @@ export function parseCliJsonl(raw, backend) {
263
164
  .split(/\r?\n/g)
264
165
  .map((line) => line.trim())
265
166
  .filter(Boolean);
266
- if (lines.length === 0)
167
+ if (lines.length === 0) {
267
168
  return null;
169
+ }
268
170
  let sessionId;
269
171
  let usage;
270
172
  const texts = [];
@@ -276,10 +178,12 @@ export function parseCliJsonl(raw, backend) {
276
178
  catch {
277
179
  continue;
278
180
  }
279
- if (!isRecord(parsed))
181
+ if (!isRecord(parsed)) {
280
182
  continue;
281
- if (!sessionId)
183
+ }
184
+ if (!sessionId) {
282
185
  sessionId = pickSessionId(parsed, backend);
186
+ }
283
187
  if (!sessionId && typeof parsed.thread_id === "string") {
284
188
  sessionId = parsed.thread_id.trim();
285
189
  }
@@ -295,32 +199,40 @@ export function parseCliJsonl(raw, backend) {
295
199
  }
296
200
  }
297
201
  const text = texts.join("\n").trim();
298
- if (!text)
202
+ if (!text) {
299
203
  return null;
204
+ }
300
205
  return { text, sessionId, usage };
301
206
  }
302
207
  export function resolveSystemPromptUsage(params) {
303
208
  const systemPrompt = params.systemPrompt?.trim();
304
- if (!systemPrompt)
209
+ if (!systemPrompt) {
305
210
  return null;
211
+ }
306
212
  const when = params.backend.systemPromptWhen ?? "first";
307
- if (when === "never")
213
+ if (when === "never") {
308
214
  return null;
309
- if (when === "first" && !params.isNewSession)
215
+ }
216
+ if (when === "first" && !params.isNewSession) {
310
217
  return null;
311
- if (!params.backend.systemPromptArg?.trim())
218
+ }
219
+ if (!params.backend.systemPromptArg?.trim()) {
312
220
  return null;
221
+ }
313
222
  return systemPrompt;
314
223
  }
315
224
  export function resolveSessionIdToSend(params) {
316
225
  const mode = params.backend.sessionMode ?? "always";
317
226
  const existing = params.cliSessionId?.trim();
318
- if (mode === "none")
227
+ if (mode === "none") {
319
228
  return { sessionId: undefined, isNew: !existing };
320
- if (mode === "existing")
229
+ }
230
+ if (mode === "existing") {
321
231
  return { sessionId: existing, isNew: !existing };
322
- if (existing)
232
+ }
233
+ if (existing) {
323
234
  return { sessionId: existing, isNew: false };
235
+ }
324
236
  return { sessionId: crypto.randomUUID(), isNew: true };
325
237
  }
326
238
  export function resolvePromptInput(params) {
@@ -335,19 +247,24 @@ export function resolvePromptInput(params) {
335
247
  }
336
248
  function resolveImageExtension(mimeType) {
337
249
  const normalized = mimeType.toLowerCase();
338
- if (normalized.includes("png"))
250
+ if (normalized.includes("png")) {
339
251
  return "png";
340
- if (normalized.includes("jpeg") || normalized.includes("jpg"))
252
+ }
253
+ if (normalized.includes("jpeg") || normalized.includes("jpg")) {
341
254
  return "jpg";
342
- if (normalized.includes("gif"))
255
+ }
256
+ if (normalized.includes("gif")) {
343
257
  return "gif";
344
- if (normalized.includes("webp"))
258
+ }
259
+ if (normalized.includes("webp")) {
345
260
  return "webp";
261
+ }
346
262
  return "bin";
347
263
  }
348
264
  export function appendImagePathsToPrompt(prompt, paths) {
349
- if (!paths.length)
265
+ if (!paths.length) {
350
266
  return prompt;
267
+ }
351
268
  const trimmed = prompt.trimEnd();
352
269
  const separator = trimmed ? "\n\n" : "";
353
270
  return `${trimmed}${separator}${paths.join("\n")}`;
@@ -1,20 +1,32 @@
1
1
  import { resolveHeartbeatPrompt } from "../auto-reply/heartbeat.js";
2
- import { isTruthyEnvValue } from "../infra/env.js";
3
2
  import { shouldLogVerbose } from "../globals.js";
3
+ import { isTruthyEnvValue } from "../infra/env.js";
4
4
  import { createSubsystemLogger } from "../logging/subsystem.js";
5
- import { runCommandWithTimeout } from "../process/exec.js";
6
- import { resolveUserPath } from "../utils.js";
7
- import { resolvePoolbotDocsPath } from "./docs-path.js";
5
+ import { getProcessSupervisor } from "../process/supervisor/index.js";
8
6
  import { resolveSessionAgentIds } from "./agent-scope.js";
9
7
  import { makeBootstrapWarn, resolveBootstrapContextForRun } from "./bootstrap-files.js";
10
8
  import { resolveCliBackendConfig } from "./cli-backends.js";
11
- import { appendImagePathsToPrompt, buildCliArgs, buildSystemPrompt, cleanupResumeProcesses, cleanupSuspendedCliProcesses, enqueueCliRun, normalizeCliModel, parseCliJson, parseCliJsonl, resolvePromptInput, resolveSessionIdToSend, resolveSystemPromptUsage, writeCliImages, } from "./cli-runner/helpers.js";
9
+ import { appendImagePathsToPrompt, buildCliSupervisorScopeKey, buildCliArgs, buildSystemPrompt, enqueueCliRun, normalizeCliModel, parseCliJson, parseCliJsonl, resolveCliNoOutputTimeoutMs, resolvePromptInput, resolveSessionIdToSend, resolveSystemPromptUsage, writeCliImages, } from "./cli-runner/helpers.js";
10
+ import { resolvePoolbotDocsPath } from "./docs-path.js";
12
11
  import { FailoverError, resolveFailoverStatus } from "./failover-error.js";
13
12
  import { classifyFailoverReason, isFailoverErrorMessage } from "./pi-embedded-helpers.js";
13
+ import { redactRunIdentifier, resolveRunWorkspaceDir } from "./workspace-run.js";
14
14
  const log = createSubsystemLogger("agent/claude-cli");
15
15
  export async function runCliAgent(params) {
16
16
  const started = Date.now();
17
- const resolvedWorkspace = resolveUserPath(params.workspaceDir);
17
+ const workspaceResolution = resolveRunWorkspaceDir({
18
+ workspaceDir: params.workspaceDir,
19
+ sessionKey: params.sessionKey,
20
+ agentId: params.agentId,
21
+ config: params.config,
22
+ });
23
+ const resolvedWorkspace = workspaceResolution.workspaceDir;
24
+ const redactedSessionId = redactRunIdentifier(params.sessionId);
25
+ const redactedSessionKey = redactRunIdentifier(params.sessionKey);
26
+ const redactedWorkspace = redactRunIdentifier(resolvedWorkspace);
27
+ if (workspaceResolution.usedFallback) {
28
+ log.warn(`[workspace-fallback] caller=runCliAgent reason=${workspaceResolution.fallbackReason} run=${params.runId} session=${redactedSessionId} sessionKey=${redactedSessionKey} agent=${workspaceResolution.agentId} workspace=${redactedWorkspace}`);
29
+ }
18
30
  const workspaceDir = resolvedWorkspace;
19
31
  const backendResolved = resolveCliBackendConfig(params.provider, params.config);
20
32
  if (!backendResolved) {
@@ -117,7 +129,7 @@ export async function runCliAgent(params) {
117
129
  try {
118
130
  const output = await enqueueCliRun(queueKey, async () => {
119
131
  log.info(`cli exec: provider=${params.provider} model=${normalizedModel} promptChars=${params.prompt.length}`);
120
- const logOutputText = isTruthyEnvValue(process.env.POOLBOT_CLAUDE_CLI_LOG_OUTPUT || process.env.CLAWDBOT_CLAUDE_CLI_LOG_OUTPUT);
132
+ const logOutputText = isTruthyEnvValue(process.env.POOLBOT_CLAUDE_CLI_LOG_OUTPUT);
121
133
  if (logOutputText) {
122
134
  const logArgs = [];
123
135
  for (let i = 0; i < args.length; i += 1) {
@@ -160,32 +172,69 @@ export async function runCliAgent(params) {
160
172
  }
161
173
  return next;
162
174
  })();
163
- // Cleanup suspended processes that have accumulated (regardless of sessionId)
164
- await cleanupSuspendedCliProcesses(backend);
165
- if (useResume && cliSessionIdToSend) {
166
- await cleanupResumeProcesses(backend, cliSessionIdToSend);
167
- }
168
- const result = await runCommandWithTimeout([backend.command, ...args], {
175
+ const noOutputTimeoutMs = resolveCliNoOutputTimeoutMs({
176
+ backend,
169
177
  timeoutMs: params.timeoutMs,
178
+ useResume,
179
+ });
180
+ const supervisor = getProcessSupervisor();
181
+ const scopeKey = buildCliSupervisorScopeKey({
182
+ backend,
183
+ backendId: backendResolved.id,
184
+ cliSessionId: useResume ? cliSessionIdToSend : undefined,
185
+ });
186
+ const managedRun = await supervisor.spawn({
187
+ sessionId: params.sessionId,
188
+ backendId: backendResolved.id,
189
+ scopeKey,
190
+ replaceExistingScope: Boolean(useResume && scopeKey),
191
+ mode: "child",
192
+ argv: [backend.command, ...args],
193
+ timeoutMs: params.timeoutMs,
194
+ noOutputTimeoutMs,
170
195
  cwd: workspaceDir,
171
196
  env,
172
197
  input: stdinPayload,
173
198
  });
199
+ const result = await managedRun.wait();
174
200
  const stdout = result.stdout.trim();
175
201
  const stderr = result.stderr.trim();
176
202
  if (logOutputText) {
177
- if (stdout)
203
+ if (stdout) {
178
204
  log.info(`cli stdout:\n${stdout}`);
179
- if (stderr)
205
+ }
206
+ if (stderr) {
180
207
  log.info(`cli stderr:\n${stderr}`);
208
+ }
181
209
  }
182
210
  if (shouldLogVerbose()) {
183
- if (stdout)
211
+ if (stdout) {
184
212
  log.debug(`cli stdout:\n${stdout}`);
185
- if (stderr)
213
+ }
214
+ if (stderr) {
186
215
  log.debug(`cli stderr:\n${stderr}`);
216
+ }
187
217
  }
188
- if (result.code !== 0) {
218
+ if (result.exitCode !== 0 || result.reason !== "exit") {
219
+ if (result.reason === "no-output-timeout" || result.noOutputTimedOut) {
220
+ const timeoutReason = `CLI produced no output for ${Math.round(noOutputTimeoutMs / 1000)}s and was terminated.`;
221
+ log.warn(`cli watchdog timeout: provider=${params.provider} model=${modelId} session=${cliSessionIdToSend ?? params.sessionId} noOutputTimeoutMs=${noOutputTimeoutMs} pid=${managedRun.pid ?? "unknown"}`);
222
+ throw new FailoverError(timeoutReason, {
223
+ reason: "timeout",
224
+ provider: params.provider,
225
+ model: modelId,
226
+ status: resolveFailoverStatus("timeout"),
227
+ });
228
+ }
229
+ if (result.reason === "overall-timeout") {
230
+ const timeoutReason = `CLI exceeded timeout (${Math.round(params.timeoutMs / 1000)}s) and was terminated.`;
231
+ throw new FailoverError(timeoutReason, {
232
+ reason: "timeout",
233
+ provider: params.provider,
234
+ model: modelId,
235
+ status: resolveFailoverStatus("timeout"),
236
+ });
237
+ }
189
238
  const err = stderr || stdout || "CLI failed.";
190
239
  const reason = classifyFailoverReason(err) ?? "unknown";
191
240
  const status = resolveFailoverStatus(reason);
@@ -223,8 +272,9 @@ export async function runCliAgent(params) {
223
272
  };
224
273
  }
225
274
  catch (err) {
226
- if (err instanceof FailoverError)
275
+ if (err instanceof FailoverError) {
227
276
  throw err;
277
+ }
228
278
  const message = err instanceof Error ? err.message : String(err);
229
279
  if (isFailoverErrorMessage(message)) {
230
280
  const reason = classifyFailoverReason(message) ?? "unknown";
@@ -248,6 +298,7 @@ export async function runClaudeCliAgent(params) {
248
298
  return runCliAgent({
249
299
  sessionId: params.sessionId,
250
300
  sessionKey: params.sessionKey,
301
+ agentId: params.agentId,
251
302
  sessionFile: params.sessionFile,
252
303
  workspaceDir: params.workspaceDir,
253
304
  config: params.config,
@@ -3,11 +3,30 @@ const DEFAULT_ACK_REACTION = "👀";
3
3
  export function resolveAgentIdentity(cfg, agentId) {
4
4
  return resolveAgentConfig(cfg, agentId)?.identity;
5
5
  }
6
- export function resolveAckReaction(cfg, agentId) {
6
+ export function resolveAckReaction(cfg, agentId, opts) {
7
+ // L1: Channel account level
8
+ if (opts?.channel && opts?.accountId) {
9
+ const channelCfg = getChannelConfig(cfg, opts.channel);
10
+ const accounts = channelCfg?.accounts;
11
+ const accountReaction = accounts?.[opts.accountId]?.ackReaction;
12
+ if (accountReaction !== undefined) {
13
+ return accountReaction.trim();
14
+ }
15
+ }
16
+ // L2: Channel level
17
+ if (opts?.channel) {
18
+ const channelCfg = getChannelConfig(cfg, opts.channel);
19
+ const channelReaction = channelCfg?.ackReaction;
20
+ if (channelReaction !== undefined) {
21
+ return channelReaction.trim();
22
+ }
23
+ }
24
+ // L3: Global messages level
7
25
  const configured = cfg.messages?.ackReaction;
8
26
  if (configured !== undefined) {
9
27
  return configured.trim();
10
28
  }
29
+ // L4: Agent identity emoji fallback
11
30
  const emoji = resolveAgentIdentity(cfg, agentId)?.emoji?.trim();
12
31
  return emoji || DEFAULT_ACK_REACTION;
13
32
  }
@@ -0,0 +1,9 @@
1
+ export const DEFAULT_IMAGE_MAX_DIMENSION_PX = 1200;
2
+ export const DEFAULT_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
3
+ export function resolveImageSanitizationLimits(cfg) {
4
+ const configured = cfg?.agents?.defaults?.imageMaxDimensionPx;
5
+ if (typeof configured !== "number" || !Number.isFinite(configured)) {
6
+ return {};
7
+ }
8
+ return { maxDimensionPx: Math.max(1, Math.floor(configured)) };
9
+ }