@poolzin/pool-bot 2026.2.21 → 2026.2.23

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 (378) 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-catalog.js +40 -9
  14. package/dist/agents/model-forward-compat.js +60 -23
  15. package/dist/agents/model-selection.js +134 -41
  16. package/dist/agents/pi-auth-json.js +2 -2
  17. package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
  18. package/dist/agents/pi-embedded-helpers/errors.js +140 -15
  19. package/dist/agents/pi-embedded-helpers/images.js +22 -12
  20. package/dist/agents/pi-embedded-helpers.js +2 -2
  21. package/dist/agents/pi-embedded-runner/abort.js +10 -3
  22. package/dist/agents/pi-embedded-runner/compact.js +230 -32
  23. package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
  24. package/dist/agents/pi-embedded-runner/google.js +109 -19
  25. package/dist/agents/pi-embedded-runner/history.js +35 -17
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
  27. package/dist/agents/pi-embedded-runner/run/images.js +81 -55
  28. package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
  29. package/dist/agents/pi-embedded-runner/run.js +193 -25
  30. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
  31. package/dist/agents/pi-embedded-runner/runs.js +17 -8
  32. package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
  33. package/dist/agents/pi-embedded-runner.js +1 -1
  34. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
  35. package/dist/agents/pi-embedded-subscribe.js +37 -0
  36. package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
  37. package/dist/agents/pi-model-discovery.js +9 -2
  38. package/dist/agents/pi-tool-definition-adapter.js +60 -8
  39. package/dist/agents/pi-tools.before-tool-call.js +1 -1
  40. package/dist/agents/pi-tools.js +113 -94
  41. package/dist/agents/pi-tools.read.js +337 -38
  42. package/dist/agents/poolbot-tools.js +14 -5
  43. package/dist/agents/sandbox/docker.js +10 -5
  44. package/dist/agents/sandbox/registry.js +96 -46
  45. package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
  46. package/dist/agents/sandbox-paths.js +43 -10
  47. package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
  48. package/dist/agents/session-tool-result-guard.js +39 -39
  49. package/dist/agents/session-transcript-repair.js +36 -33
  50. package/dist/agents/session-write-lock.js +62 -44
  51. package/dist/agents/skills/frontmatter.js +49 -88
  52. package/dist/agents/skills/workspace.js +335 -28
  53. package/dist/agents/subagent-announce.js +508 -174
  54. package/dist/agents/subagent-registry.js +45 -4
  55. package/dist/agents/subagent-spawn.js +16 -33
  56. package/dist/agents/system-prompt-report.js +27 -10
  57. package/dist/agents/system-prompt.js +26 -32
  58. package/dist/agents/tool-call-id.js +69 -17
  59. package/dist/agents/tool-display-common.js +1 -1
  60. package/dist/agents/tool-images.js +64 -31
  61. package/dist/agents/tools/canvas-tool.js +17 -11
  62. package/dist/agents/tools/common.js +37 -19
  63. package/dist/agents/tools/cron-tool.js +40 -38
  64. package/dist/agents/tools/gateway.js +70 -2
  65. package/dist/agents/tools/message-tool.js +181 -40
  66. package/dist/agents/tools/nodes-tool.js +128 -36
  67. package/dist/agents/tools/nodes-utils.js +12 -38
  68. package/dist/agents/tools/session-status-tool.js +24 -71
  69. package/dist/agents/tools/sessions-helpers.js +38 -210
  70. package/dist/agents/tools/sessions-spawn-tool.js +28 -198
  71. package/dist/agents/tools/telegram-actions.js +58 -7
  72. package/dist/agents/tools/web-fetch-utils.js +112 -7
  73. package/dist/agents/tools/web-fetch.js +279 -175
  74. package/dist/agents/tools/web-shared.js +71 -8
  75. package/dist/agents/usage.js +25 -16
  76. package/dist/auto-reply/commands-registry.data.js +85 -11
  77. package/dist/auto-reply/dispatch.js +40 -21
  78. package/dist/auto-reply/reply/abort.js +102 -33
  79. package/dist/auto-reply/reply/commands-core.js +82 -33
  80. package/dist/auto-reply/reply/commands-export-session.js +1 -1
  81. package/dist/auto-reply/reply/commands-info.js +41 -12
  82. package/dist/auto-reply/reply/commands-subagents.js +352 -100
  83. package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
  84. package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
  85. package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
  86. package/dist/auto-reply/reply/inbound-meta.js +12 -1
  87. package/dist/auto-reply/reply/mentions.js +18 -11
  88. package/dist/auto-reply/reply/normalize-reply.js +17 -8
  89. package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
  90. package/dist/auto-reply/reply/session.js +102 -21
  91. package/dist/auto-reply/reply/streaming-directives.js +16 -5
  92. package/dist/auto-reply/status.js +73 -50
  93. package/dist/browser/extension-relay.js +3 -3
  94. package/dist/browser/http-auth.js +1 -1
  95. package/dist/browser/paths.js +2 -2
  96. package/dist/build-info.json +3 -3
  97. package/dist/channels/allowlist-match.js +20 -0
  98. package/dist/channels/allowlists/resolve-utils.js +65 -2
  99. package/dist/channels/chat-type.js +8 -4
  100. package/dist/channels/dock.js +127 -35
  101. package/dist/channels/draft-stream-loop.js +6 -2
  102. package/dist/channels/plugins/actions/telegram.js +42 -18
  103. package/dist/channels/plugins/allowlist-match.js +1 -1
  104. package/dist/channels/plugins/group-mentions.js +51 -41
  105. package/dist/channels/plugins/message-action-names.js +2 -0
  106. package/dist/channels/plugins/message-actions.js +24 -5
  107. package/dist/channels/plugins/normalize/discord.js +26 -4
  108. package/dist/channels/plugins/normalize/signal.js +35 -22
  109. package/dist/channels/plugins/onboarding/helpers.js +8 -26
  110. package/dist/channels/plugins/outbound/imessage.js +15 -14
  111. package/dist/channels/registry.js +20 -7
  112. package/dist/cli/acp-cli.js +7 -5
  113. package/dist/cli/browser-cli-extension.js +25 -12
  114. package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
  115. package/dist/cli/browser-cli-state.js +101 -145
  116. package/dist/cli/command-options.js +28 -0
  117. package/dist/cli/completion-cli.js +6 -6
  118. package/dist/cli/cron-cli/register.cron-add.js +25 -1
  119. package/dist/cli/cron-cli/register.cron-edit.js +44 -0
  120. package/dist/cli/cron-cli/shared.js +7 -1
  121. package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
  122. package/dist/cli/daemon-cli/lifecycle.js +23 -247
  123. package/dist/cli/daemon-cli/register-service-commands.js +25 -4
  124. package/dist/cli/daemon-cli.js +1 -0
  125. package/dist/cli/devices-cli.js +33 -20
  126. package/dist/cli/gateway-cli/register.js +37 -105
  127. package/dist/cli/gateway-cli/run.js +49 -11
  128. package/dist/cli/nodes-camera.js +59 -4
  129. package/dist/cli/nodes-cli/register.camera.js +27 -24
  130. package/dist/cli/nodes-cli/rpc.js +21 -38
  131. package/dist/cli/qr-cli.js +2 -2
  132. package/dist/cli/skills-cli.format.js +2 -2
  133. package/dist/cli/update-cli/progress.js +2 -2
  134. package/dist/cli/update-cli/restart-helper.js +28 -7
  135. package/dist/cli/update-cli/shared.js +7 -7
  136. package/dist/cli/update-cli/status.js +1 -1
  137. package/dist/cli/update-cli/update-command.js +14 -8
  138. package/dist/cli/update-cli/wizard.js +2 -2
  139. package/dist/cli/update-cli.js +21 -1027
  140. package/dist/commands/auth-choice.apply.anthropic.js +10 -2
  141. package/dist/commands/channels/add-mutators.js +3 -35
  142. package/dist/commands/channels/add.js +39 -51
  143. package/dist/commands/config-validation.js +1 -1
  144. package/dist/commands/configure.gateway-auth.js +52 -15
  145. package/dist/commands/configure.gateway.js +84 -40
  146. package/dist/commands/doctor-completion.js +3 -3
  147. package/dist/commands/doctor-config-flow.js +536 -16
  148. package/dist/commands/doctor-gateway-services.js +103 -79
  149. package/dist/commands/doctor-memory-search.js +9 -9
  150. package/dist/commands/doctor-platform-notes.js +57 -30
  151. package/dist/commands/doctor-prompter.js +26 -15
  152. package/dist/commands/doctor-session-locks.js +1 -1
  153. package/dist/commands/doctor.js +21 -9
  154. package/dist/commands/model-picker.js +120 -95
  155. package/dist/commands/models/set.js +2 -21
  156. package/dist/commands/models/shared.js +65 -37
  157. package/dist/commands/onboard-helpers.js +81 -39
  158. package/dist/commands/openai-codex-oauth.js +1 -1
  159. package/dist/commands/sessions.js +52 -53
  160. package/dist/commands/status.summary.js +52 -34
  161. package/dist/commands/test-wizard-helpers.js +2 -2
  162. package/dist/config/defaults.js +79 -42
  163. package/dist/config/group-policy.js +50 -18
  164. package/dist/config/includes.js +37 -10
  165. package/dist/config/schema.help.js +5 -4
  166. package/dist/config/schema.hints.js +2 -2
  167. package/dist/config/schema.labels.js +1 -0
  168. package/dist/config/sessions/group.js +12 -11
  169. package/dist/config/sessions/paths.js +137 -11
  170. package/dist/config/sessions/store.js +185 -65
  171. package/dist/config/sessions/types.js +15 -1
  172. package/dist/config/sessions.js +1 -0
  173. package/dist/config/telegram-custom-commands.js +3 -2
  174. package/dist/config/types.js +2 -0
  175. package/dist/config/zod-schema.agent-defaults.js +6 -27
  176. package/dist/config/zod-schema.agent-runtime.js +171 -79
  177. package/dist/config/zod-schema.providers-core.js +138 -65
  178. package/dist/config/zod-schema.session.js +49 -22
  179. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  180. package/dist/cron/isolated-agent/run.js +224 -57
  181. package/dist/cron/normalize.js +48 -45
  182. package/dist/cron/run-log.js +14 -0
  183. package/dist/cron/service/jobs.js +190 -28
  184. package/dist/cron/service/normalize.js +29 -11
  185. package/dist/cron/service/store.js +30 -44
  186. package/dist/cron/service/timer.js +182 -96
  187. package/dist/cron/service.js +3 -0
  188. package/dist/cron/stagger.js +37 -0
  189. package/dist/daemon/inspect.js +132 -92
  190. package/dist/daemon/runtime-paths.js +25 -4
  191. package/dist/daemon/service-audit.js +47 -16
  192. package/dist/discord/accounts.js +23 -20
  193. package/dist/discord/monitor/agent-components.js +1115 -219
  194. package/dist/discord/monitor/allow-list.js +114 -34
  195. package/dist/discord/monitor/listeners.js +204 -97
  196. package/dist/discord/monitor/message-handler.js +21 -10
  197. package/dist/discord/monitor/message-handler.preflight.js +195 -101
  198. package/dist/discord/monitor/message-handler.process.js +384 -123
  199. package/dist/discord/monitor/message-utils.js +86 -23
  200. package/dist/discord/monitor/native-command.js +77 -57
  201. package/dist/discord/monitor/provider.js +122 -117
  202. package/dist/discord/monitor/reply-context.js +20 -16
  203. package/dist/discord/monitor/reply-delivery.js +40 -8
  204. package/dist/discord/monitor/rest-fetch.js +22 -0
  205. package/dist/discord/monitor/threading.js +117 -24
  206. package/dist/discord/send.js +2 -1
  207. package/dist/discord/send.outbound.js +124 -11
  208. package/dist/discord/send.shared.js +112 -72
  209. package/dist/discord/voice-message.js +3 -3
  210. package/dist/gateway/auth.js +119 -44
  211. package/dist/gateway/call.js +76 -34
  212. package/dist/gateway/channel-health-monitor.js +57 -50
  213. package/dist/gateway/client.js +63 -29
  214. package/dist/gateway/control-ui-contract.js +1 -1
  215. package/dist/gateway/gateway-config-prompts.shared.js +2 -2
  216. package/dist/gateway/net.js +109 -1
  217. package/dist/gateway/protocol/index.js +5 -8
  218. package/dist/gateway/protocol/schema/agent.js +19 -1
  219. package/dist/gateway/protocol/schema/channels.js +21 -0
  220. package/dist/gateway/protocol/schema/cron.js +43 -30
  221. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
  222. package/dist/gateway/protocol/schema/sessions.js +5 -1
  223. package/dist/gateway/protocol/schema.js +0 -1
  224. package/dist/gateway/server/presence-events.js +12 -0
  225. package/dist/gateway/server/ws-connection/message-handler.js +203 -212
  226. package/dist/gateway/server/ws-connection.js +58 -21
  227. package/dist/gateway/server-broadcast.js +18 -13
  228. package/dist/gateway/server-cron.js +177 -10
  229. package/dist/gateway/server-methods/agent-job.js +131 -38
  230. package/dist/gateway/server-methods/send.js +60 -14
  231. package/dist/gateway/server-methods/sessions.js +160 -96
  232. package/dist/gateway/server-methods/system.js +5 -7
  233. package/dist/gateway/server-methods-list.js +8 -0
  234. package/dist/gateway/server-methods.js +24 -8
  235. package/dist/gateway/server-node-events.js +278 -68
  236. package/dist/gateway/session-utils.fs.js +316 -75
  237. package/dist/gateway/session-utils.js +224 -70
  238. package/dist/gateway/sessions-patch.js +63 -20
  239. package/dist/gateway/test-temp-config.js +1 -1
  240. package/dist/gateway/tools-invoke-http.js +118 -70
  241. package/dist/gateway/ws-log.js +135 -107
  242. package/dist/hooks/frontmatter.js +36 -82
  243. package/dist/hooks/install.js +149 -139
  244. package/dist/hooks/internal-hooks.js +29 -4
  245. package/dist/hooks/plugin-hooks.js +2 -1
  246. package/dist/imessage/monitor/deliver.js +10 -4
  247. package/dist/imessage/monitor/monitor-provider.js +138 -375
  248. package/dist/imessage/monitor/runtime.js +4 -8
  249. package/dist/imessage/send.js +65 -19
  250. package/dist/infra/exec-approvals-allowlist.js +7 -0
  251. package/dist/infra/exec-approvals.js +35 -920
  252. package/dist/infra/exec-safe-bin-trust.js +64 -0
  253. package/dist/infra/heartbeat-runner.js +207 -134
  254. package/dist/infra/heartbeat-wake.js +183 -22
  255. package/dist/infra/install-source-utils.js +47 -0
  256. package/dist/infra/net/ssrf.js +170 -36
  257. package/dist/infra/outbound/deliver.js +224 -58
  258. package/dist/infra/outbound/message-action-spec.js +12 -5
  259. package/dist/infra/outbound/outbound-session.js +27 -25
  260. package/dist/infra/poolbot-root.js +32 -22
  261. package/dist/infra/ports.js +14 -11
  262. package/dist/infra/skills-remote.js +48 -37
  263. package/dist/infra/system-events.js +25 -11
  264. package/dist/infra/system-presence.js +26 -33
  265. package/dist/infra/tmp-poolbot-dir.js +81 -2
  266. package/dist/infra/wsl.js +37 -1
  267. package/dist/line/bot-message-context.js +163 -191
  268. package/dist/logging/subsystem.js +59 -22
  269. package/dist/markdown/ir.js +124 -50
  270. package/dist/media/store.js +1 -1
  271. package/dist/media-understanding/runner.entries.js +42 -25
  272. package/dist/media-understanding/runner.js +53 -488
  273. package/dist/memory/embeddings-gemini.js +53 -38
  274. package/dist/memory/manager-embedding-ops.js +48 -69
  275. package/dist/pairing/pairing-store.js +178 -119
  276. package/dist/plugin-sdk/index.js +34 -6
  277. package/dist/plugins/hooks.js +135 -14
  278. package/dist/plugins/install.js +190 -152
  279. package/dist/polls.js +11 -0
  280. package/dist/routing/resolve-route.js +190 -56
  281. package/dist/routing/session-key.js +38 -22
  282. package/dist/runtime.js +35 -9
  283. package/dist/security/audit-channel.js +1 -1
  284. package/dist/sessions/session-key-utils.js +29 -11
  285. package/dist/shared/frontmatter.js +5 -5
  286. package/dist/shared/node-list-types.js +1 -0
  287. package/dist/shared/string-normalization.js +15 -0
  288. package/dist/signal/monitor/event-handler.js +68 -36
  289. package/dist/signal/send.js +29 -37
  290. package/dist/slack/monitor/allow-list.js +10 -11
  291. package/dist/slack/monitor/commands.js +14 -3
  292. package/dist/slack/monitor/events/interactions.js +4 -4
  293. package/dist/slack/monitor/media.js +224 -16
  294. package/dist/slack/monitor/message-handler/dispatch.js +247 -13
  295. package/dist/slack/monitor/message-handler/prepare.js +128 -45
  296. package/dist/slack/monitor/slash.js +357 -144
  297. package/dist/slack/streaming.js +77 -0
  298. package/dist/telegram/accounts.js +40 -13
  299. package/dist/telegram/allowed-updates.js +3 -0
  300. package/dist/telegram/bot/delivery.js +129 -66
  301. package/dist/telegram/bot/helpers.js +136 -122
  302. package/dist/telegram/bot-handlers.js +600 -339
  303. package/dist/telegram/bot-message-context.js +115 -73
  304. package/dist/telegram/bot-message-dispatch.js +235 -104
  305. package/dist/telegram/bot-native-command-menu.js +3 -1
  306. package/dist/telegram/bot-native-commands.js +213 -193
  307. package/dist/telegram/bot.js +24 -132
  308. package/dist/telegram/draft-stream.js +84 -75
  309. package/dist/telegram/format.js +150 -6
  310. package/dist/telegram/send.js +415 -255
  311. package/dist/telegram/targets.js +21 -2
  312. package/dist/telegram/update-offset-store.js +19 -3
  313. package/dist/terminal/restore.js +5 -2
  314. package/dist/test-utils/fetch-mock.js +5 -0
  315. package/dist/version.js +18 -5
  316. package/dist/web/auto-reply/monitor/broadcast.js +7 -3
  317. package/dist/web/auto-reply/monitor/on-message.js +6 -3
  318. package/dist/web/inbound/media.js +34 -8
  319. package/dist/web/inbound/monitor.js +34 -17
  320. package/dist/web/inbound/send-api.js +18 -17
  321. package/dist/web/outbound.js +12 -5
  322. package/dist/wizard/clack-prompter.js +40 -7
  323. package/extensions/bluebubbles/package.json +1 -1
  324. package/extensions/copilot-proxy/package.json +1 -1
  325. package/extensions/device-pair/index.ts +2 -2
  326. package/extensions/diagnostics-otel/package.json +1 -1
  327. package/extensions/discord/package.json +1 -1
  328. package/extensions/feishu/package.json +1 -1
  329. package/extensions/google-antigravity-auth/package.json +1 -1
  330. package/extensions/google-gemini-cli-auth/package.json +1 -1
  331. package/extensions/googlechat/package.json +1 -1
  332. package/extensions/imessage/package.json +1 -1
  333. package/extensions/irc/package.json +1 -1
  334. package/extensions/irc/src/accounts.ts +1 -1
  335. package/extensions/irc/src/onboarding.ts +4 -4
  336. package/extensions/line/package.json +1 -1
  337. package/extensions/llm-task/package.json +1 -1
  338. package/extensions/lobster/package.json +1 -1
  339. package/extensions/matrix/CHANGELOG.md +10 -0
  340. package/extensions/matrix/package.json +1 -1
  341. package/extensions/mattermost/package.json +1 -1
  342. package/extensions/memory-core/package.json +1 -1
  343. package/extensions/memory-lancedb/package.json +1 -1
  344. package/extensions/minimax-portal-auth/package.json +1 -1
  345. package/extensions/msteams/CHANGELOG.md +10 -0
  346. package/extensions/msteams/package.json +1 -1
  347. package/extensions/nextcloud-talk/package.json +1 -1
  348. package/extensions/nostr/CHANGELOG.md +10 -0
  349. package/extensions/nostr/package.json +1 -1
  350. package/extensions/open-prose/package.json +1 -1
  351. package/extensions/openai-codex-auth/package.json +1 -1
  352. package/extensions/signal/package.json +1 -1
  353. package/extensions/slack/package.json +1 -1
  354. package/extensions/telegram/package.json +1 -1
  355. package/extensions/tlon/package.json +1 -1
  356. package/extensions/twitch/CHANGELOG.md +10 -0
  357. package/extensions/twitch/package.json +1 -1
  358. package/extensions/voice-call/CHANGELOG.md +10 -0
  359. package/extensions/voice-call/package.json +1 -1
  360. package/extensions/whatsapp/package.json +1 -1
  361. package/extensions/zalo/CHANGELOG.md +10 -0
  362. package/extensions/zalo/package.json +1 -1
  363. package/extensions/zalouser/CHANGELOG.md +10 -0
  364. package/extensions/zalouser/package.json +1 -1
  365. package/package.json +1 -1
  366. package/skills/apple-reminders/SKILL.md +100 -49
  367. package/skills/coding-agent/SKILL.md +34 -28
  368. package/skills/github/SKILL.md +131 -16
  369. package/skills/imsg/SKILL.md +112 -15
  370. package/skills/openhue/SKILL.md +101 -19
  371. package/skills/tmux/SKILL.md +111 -79
  372. package/skills/weather/SKILL.md +88 -25
  373. package/dist/agents/openclaw-tools.js +0 -151
  374. package/dist/agents/tool-security.js +0 -96
  375. package/dist/gateway/url-validation.js +0 -94
  376. package/dist/infra/openclaw-root.js +0 -109
  377. package/dist/infra/tmp-openclaw-dir.js +0 -81
  378. package/dist/media/path-sanitization.js +0 -78
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs/promises";
2
+ import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
2
3
  import { enqueueCommandInLane } from "../../process/command-queue.js";
3
4
  import { isMarkdownCapableMessageChannel } from "../../utils/message-channel.js";
4
5
  import { resolvePoolbotAgentDir } from "../agent-paths.js";
@@ -9,8 +10,8 @@ import { FailoverError, resolveFailoverStatus } from "../failover-error.js";
9
10
  import { ensureAuthProfileStore, getApiKeyForModel, resolveAuthProfileOrder, } from "../model-auth.js";
10
11
  import { normalizeProviderId } from "../model-selection.js";
11
12
  import { ensurePoolbotModelsJson } from "../models-config.js";
12
- import { BILLING_ERROR_USER_MESSAGE, classifyFailoverReason, formatAssistantErrorText, isAuthAssistantError, isBillingAssistantError, isCompactionFailureError, isContextOverflowError, isFailoverAssistantError, isFailoverErrorMessage, parseImageSizeError, parseImageDimensionError, isRateLimitAssistantError, isTimeoutErrorMessage, pickFallbackThinkingLevel, } from "../pi-embedded-helpers.js";
13
- import { normalizeUsage } from "../usage.js";
13
+ import { formatBillingErrorMessage, classifyFailoverReason, formatAssistantErrorText, isAuthAssistantError, isBillingAssistantError, isCompactionFailureError, isLikelyContextOverflowError, isFailoverAssistantError, isFailoverErrorMessage, parseImageSizeError, parseImageDimensionError, isRateLimitAssistantError, isTimeoutErrorMessage, pickFallbackThinkingLevel, } from "../pi-embedded-helpers.js";
14
+ import { derivePromptTokens, normalizeUsage } from "../usage.js";
14
15
  import { redactRunIdentifier, resolveRunWorkspaceDir } from "../workspace-run.js";
15
16
  import { compactEmbeddedPiSessionDirect } from "./compact.js";
16
17
  import { resolveGlobalLane, resolveSessionLane } from "./lanes.js";
@@ -24,8 +25,9 @@ import { describeUnknownError } from "./utils.js";
24
25
  const ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL = "ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL";
25
26
  const ANTHROPIC_MAGIC_STRING_REPLACEMENT = "ANTHROPIC MAGIC STRING TRIGGER REFUSAL (redacted)";
26
27
  function scrubAnthropicRefusalMagic(prompt) {
27
- if (!prompt.includes(ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL))
28
+ if (!prompt.includes(ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL)) {
28
29
  return prompt;
30
+ }
29
31
  return prompt.replaceAll(ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL, ANTHROPIC_MAGIC_STRING_REPLACEMENT);
30
32
  }
31
33
  const createUsageAccumulator = () => ({
@@ -34,7 +36,13 @@ const createUsageAccumulator = () => ({
34
36
  cacheRead: 0,
35
37
  cacheWrite: 0,
36
38
  total: 0,
39
+ lastCacheRead: 0,
40
+ lastCacheWrite: 0,
41
+ lastInput: 0,
37
42
  });
43
+ function createCompactionDiagId() {
44
+ return `ovf-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
45
+ }
38
46
  const hasUsageValues = (usage) => !!usage &&
39
47
  [usage.input, usage.output, usage.cacheRead, usage.cacheWrite, usage.total].some((value) => typeof value === "number" && Number.isFinite(value) && value > 0);
40
48
  const mergeUsageIntoAccumulator = (target, usage) => {
@@ -48,6 +56,12 @@ const mergeUsageIntoAccumulator = (target, usage) => {
48
56
  target.total +=
49
57
  usage.total ??
50
58
  (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
59
+ // Track the most recent API call's cache fields for accurate context-size reporting.
60
+ // Accumulated cache totals inflate context size when there are multiple tool-call round-trips,
61
+ // since each call reports cacheRead ≈ current_context_size.
62
+ target.lastCacheRead = usage.cacheRead ?? 0;
63
+ target.lastCacheWrite = usage.cacheWrite ?? 0;
64
+ target.lastInput = usage.input ?? 0;
51
65
  };
52
66
  const toNormalizedUsage = (usage) => {
53
67
  const hasUsage = usage.input > 0 ||
@@ -58,13 +72,21 @@ const toNormalizedUsage = (usage) => {
58
72
  if (!hasUsage) {
59
73
  return undefined;
60
74
  }
61
- const derivedTotal = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
75
+ // Use the LAST API call's cache fields for context-size calculation.
76
+ // The accumulated cacheRead/cacheWrite inflate context size because each tool-call
77
+ // round-trip reports cacheRead ≈ current_context_size, and summing N calls gives
78
+ // N × context_size which gets clamped to contextWindow (e.g. 200k).
79
+ // See: https://github.com/poolbot/poolbot/issues/13698
80
+ //
81
+ // We use lastInput/lastCacheRead/lastCacheWrite (from the most recent API call) for
82
+ // cache-related fields, but keep accumulated output (total generated text this turn).
83
+ const lastPromptTokens = usage.lastInput + usage.lastCacheRead + usage.lastCacheWrite;
62
84
  return {
63
- input: usage.input || undefined,
85
+ input: usage.lastInput || undefined,
64
86
  output: usage.output || undefined,
65
- cacheRead: usage.cacheRead || undefined,
66
- cacheWrite: usage.cacheWrite || undefined,
67
- total: usage.total || derivedTotal || undefined,
87
+ cacheRead: usage.lastCacheRead || undefined,
88
+ cacheWrite: usage.lastCacheWrite || undefined,
89
+ total: lastPromptTokens + usage.output || undefined,
68
90
  };
69
91
  };
70
92
  export async function runEmbeddedPiAgent(params) {
@@ -85,6 +107,7 @@ export async function runEmbeddedPiAgent(params) {
85
107
  const workspaceResolution = resolveRunWorkspaceDir({
86
108
  workspaceDir: params.workspaceDir,
87
109
  sessionKey: params.sessionKey,
110
+ agentId: params.agentId,
88
111
  config: params.config,
89
112
  });
90
113
  const resolvedWorkspace = workspaceResolution.workspaceDir;
@@ -95,11 +118,53 @@ export async function runEmbeddedPiAgent(params) {
95
118
  log.warn(`[workspace-fallback] caller=runEmbeddedPiAgent reason=${workspaceResolution.fallbackReason} run=${params.runId} session=${redactedSessionId} sessionKey=${redactedSessionKey} agent=${workspaceResolution.agentId} workspace=${redactedWorkspace}`);
96
119
  }
97
120
  const prevCwd = process.cwd();
98
- const provider = (params.provider ?? DEFAULT_PROVIDER).trim() || DEFAULT_PROVIDER;
99
- const modelId = (params.model ?? DEFAULT_MODEL).trim() || DEFAULT_MODEL;
121
+ let provider = (params.provider ?? DEFAULT_PROVIDER).trim() || DEFAULT_PROVIDER;
122
+ let modelId = (params.model ?? DEFAULT_MODEL).trim() || DEFAULT_MODEL;
100
123
  const agentDir = params.agentDir ?? resolvePoolbotAgentDir();
101
124
  const fallbackConfigured = (params.config?.agents?.defaults?.model?.fallbacks?.length ?? 0) > 0;
102
125
  await ensurePoolbotModelsJson(params.config, agentDir);
126
+ // Run before_model_resolve hooks early so plugins can override the
127
+ // provider/model before resolveModel().
128
+ //
129
+ // Legacy compatibility: before_agent_start is also checked for override
130
+ // fields if present. New hook takes precedence when both are set.
131
+ let modelResolveOverride;
132
+ const hookRunner = getGlobalHookRunner();
133
+ const hookCtx = {
134
+ agentId: workspaceResolution.agentId,
135
+ sessionKey: params.sessionKey,
136
+ sessionId: params.sessionId,
137
+ workspaceDir: resolvedWorkspace,
138
+ messageProvider: params.messageProvider ?? undefined,
139
+ };
140
+ if (hookRunner?.hasHooks("before_model_resolve")) {
141
+ try {
142
+ modelResolveOverride = await hookRunner.runBeforeModelResolve({ prompt: params.prompt }, hookCtx);
143
+ }
144
+ catch (hookErr) {
145
+ log.warn(`before_model_resolve hook failed: ${String(hookErr)}`);
146
+ }
147
+ }
148
+ if (hookRunner?.hasHooks("before_agent_start")) {
149
+ try {
150
+ const legacyResult = await hookRunner.runBeforeAgentStart({ prompt: params.prompt }, hookCtx);
151
+ modelResolveOverride = {
152
+ providerOverride: modelResolveOverride?.providerOverride ?? legacyResult?.providerOverride,
153
+ modelOverride: modelResolveOverride?.modelOverride ?? legacyResult?.modelOverride,
154
+ };
155
+ }
156
+ catch (hookErr) {
157
+ log.warn(`before_agent_start hook (legacy model resolve path) failed: ${String(hookErr)}`);
158
+ }
159
+ }
160
+ if (modelResolveOverride?.providerOverride) {
161
+ provider = modelResolveOverride.providerOverride;
162
+ log.info(`[hooks] provider overridden to ${provider}`);
163
+ }
164
+ if (modelResolveOverride?.modelOverride) {
165
+ modelId = modelResolveOverride.modelOverride;
166
+ log.info(`[hooks] model overridden to ${modelId}`);
167
+ }
103
168
  const { model, error, authStorage, modelRegistry } = resolveModel(provider, modelId, agentDir, params.config);
104
169
  if (!model) {
105
170
  throw new Error(error ?? `Unknown model: ${provider}/${modelId}`);
@@ -154,8 +219,9 @@ export async function runEmbeddedPiAgent(params) {
154
219
  let apiKeyInfo = null;
155
220
  let lastProfileId;
156
221
  const resolveAuthProfileFailoverReason = (params) => {
157
- if (params.allInCooldown)
222
+ if (params.allInCooldown) {
158
223
  return "rate_limit";
224
+ }
159
225
  const classified = classifyFailoverReason(params.message);
160
226
  return classified ?? "auth";
161
227
  };
@@ -177,8 +243,9 @@ export async function runEmbeddedPiAgent(params) {
177
243
  cause: params.error,
178
244
  });
179
245
  }
180
- if (params.error instanceof Error)
246
+ if (params.error instanceof Error) {
181
247
  throw params.error;
248
+ }
182
249
  throw new Error(message);
183
250
  };
184
251
  const resolveApiKeyForCandidate = async (candidate) => {
@@ -213,8 +280,9 @@ export async function runEmbeddedPiAgent(params) {
213
280
  lastProfileId = apiKeyInfo.profileId;
214
281
  };
215
282
  const advanceAuthProfile = async () => {
216
- if (lockedProfileId)
283
+ if (lockedProfileId) {
217
284
  return false;
285
+ }
218
286
  let nextIndex = profileIndex + 1;
219
287
  while (nextIndex < profileCandidates.length) {
220
288
  const candidate = profileCandidates[nextIndex];
@@ -230,8 +298,9 @@ export async function runEmbeddedPiAgent(params) {
230
298
  return true;
231
299
  }
232
300
  catch (err) {
233
- if (candidate && candidate === lockedProfileId)
301
+ if (candidate && candidate === lockedProfileId) {
234
302
  throw err;
303
+ }
235
304
  nextIndex += 1;
236
305
  }
237
306
  }
@@ -254,8 +323,9 @@ export async function runEmbeddedPiAgent(params) {
254
323
  }
255
324
  }
256
325
  catch (err) {
257
- if (err instanceof FailoverError)
326
+ if (err instanceof FailoverError) {
258
327
  throw err;
328
+ }
259
329
  if (profileCandidates[profileIndex] === lockedProfileId) {
260
330
  throwAuthProfileFailover({ allInCooldown: false, error: err });
261
331
  }
@@ -268,6 +338,7 @@ export async function runEmbeddedPiAgent(params) {
268
338
  let overflowCompactionAttempts = 0;
269
339
  let toolResultTruncationAttempted = false;
270
340
  const usageAccumulator = createUsageAccumulator();
341
+ let lastRunPromptUsage;
271
342
  let autoCompactionCount = 0;
272
343
  try {
273
344
  while (true) {
@@ -304,6 +375,7 @@ export async function runEmbeddedPiAgent(params) {
304
375
  model,
305
376
  authStorage,
306
377
  modelRegistry,
378
+ agentId: workspaceResolution.agentId,
307
379
  thinkLevel,
308
380
  verboseLevel: params.verboseLevel,
309
381
  reasoningLevel: params.reasoningLevel,
@@ -322,20 +394,30 @@ export async function runEmbeddedPiAgent(params) {
322
394
  blockReplyBreak: params.blockReplyBreak,
323
395
  blockReplyChunking: params.blockReplyChunking,
324
396
  onReasoningStream: params.onReasoningStream,
397
+ onReasoningEnd: params.onReasoningEnd,
325
398
  onToolResult: params.onToolResult,
326
399
  onAgentEvent: params.onAgentEvent,
327
400
  extraSystemPrompt: params.extraSystemPrompt,
401
+ inputProvenance: params.inputProvenance,
328
402
  streamParams: params.streamParams,
329
403
  ownerNumbers: params.ownerNumbers,
330
404
  enforceFinalTag: params.enforceFinalTag,
331
405
  });
332
- const { aborted, promptError, timedOut, sessionIdUsed, lastAssistant } = attempt;
333
- mergeUsageIntoAccumulator(usageAccumulator, attempt.attemptUsage ?? normalizeUsage(lastAssistant?.usage));
334
- autoCompactionCount += Math.max(0, attempt.compactionCount ?? 0);
406
+ const { aborted, promptError, timedOut, timedOutDuringCompaction, sessionIdUsed, lastAssistant, } = attempt;
407
+ const lastAssistantUsage = normalizeUsage(lastAssistant?.usage);
408
+ const attemptUsage = attempt.attemptUsage ?? lastAssistantUsage;
409
+ mergeUsageIntoAccumulator(usageAccumulator, attemptUsage);
410
+ // Keep prompt size from the latest model call so session totalTokens
411
+ // reflects current context usage, not accumulated tool-loop usage.
412
+ lastRunPromptUsage = lastAssistantUsage ?? attemptUsage;
413
+ const lastTurnTotal = lastAssistantUsage?.total ?? attemptUsage?.total;
414
+ const attemptCompactionCount = Math.max(0, attempt.compactionCount ?? 0);
415
+ autoCompactionCount += attemptCompactionCount;
335
416
  const formattedAssistantErrorText = lastAssistant
336
417
  ? formatAssistantErrorText(lastAssistant, {
337
418
  cfg: params.config,
338
419
  sessionKey: params.sessionKey ?? params.sessionId,
420
+ provider,
339
421
  })
340
422
  : undefined;
341
423
  const assistantErrorText = lastAssistant?.stopReason === "error"
@@ -345,30 +427,49 @@ export async function runEmbeddedPiAgent(params) {
345
427
  ? (() => {
346
428
  if (promptError) {
347
429
  const errorText = describeUnknownError(promptError);
348
- if (isContextOverflowError(errorText)) {
430
+ if (isLikelyContextOverflowError(errorText)) {
349
431
  return { text: errorText, source: "promptError" };
350
432
  }
351
433
  // Prompt submission failed with a non-overflow error. Do not
352
434
  // inspect prior assistant errors from history for this attempt.
353
435
  return null;
354
436
  }
355
- if (assistantErrorText && isContextOverflowError(assistantErrorText)) {
437
+ if (assistantErrorText && isLikelyContextOverflowError(assistantErrorText)) {
356
438
  return { text: assistantErrorText, source: "assistantError" };
357
439
  }
358
440
  return null;
359
441
  })()
360
442
  : null;
361
443
  if (contextOverflowError) {
444
+ const overflowDiagId = createCompactionDiagId();
362
445
  const errorText = contextOverflowError.text;
363
446
  const msgCount = attempt.messagesSnapshot?.length ?? 0;
364
447
  log.warn(`[context-overflow-diag] sessionKey=${params.sessionKey ?? params.sessionId} ` +
365
448
  `provider=${provider}/${modelId} source=${contextOverflowError.source} ` +
366
449
  `messages=${msgCount} sessionFile=${params.sessionFile} ` +
367
- `compactionAttempts=${overflowCompactionAttempts} error=${errorText.slice(0, 200)}`);
450
+ `diagId=${overflowDiagId} compactionAttempts=${overflowCompactionAttempts} ` +
451
+ `error=${errorText.slice(0, 200)}`);
368
452
  const isCompactionFailure = isCompactionFailureError(errorText);
369
- // Attempt auto-compaction on context overflow (not compaction_failure)
453
+ const hadAttemptLevelCompaction = attemptCompactionCount > 0;
454
+ // If this attempt already compacted (SDK auto-compaction), avoid immediately
455
+ // running another explicit compaction for the same overflow trigger.
456
+ if (!isCompactionFailure &&
457
+ hadAttemptLevelCompaction &&
458
+ overflowCompactionAttempts < MAX_OVERFLOW_COMPACTION_ATTEMPTS) {
459
+ overflowCompactionAttempts++;
460
+ log.warn(`context overflow persisted after in-attempt compaction (attempt ${overflowCompactionAttempts}/${MAX_OVERFLOW_COMPACTION_ATTEMPTS}); retrying prompt without additional compaction for ${provider}/${modelId}`);
461
+ continue;
462
+ }
463
+ // Attempt explicit overflow compaction only when this attempt did not
464
+ // already auto-compact.
370
465
  if (!isCompactionFailure &&
466
+ !hadAttemptLevelCompaction &&
371
467
  overflowCompactionAttempts < MAX_OVERFLOW_COMPACTION_ATTEMPTS) {
468
+ if (log.isEnabled("debug")) {
469
+ log.debug(`[compaction-diag] decision diagId=${overflowDiagId} branch=compact ` +
470
+ `isCompactionFailure=${isCompactionFailure} hasOversizedToolResults=unknown ` +
471
+ `attempt=${overflowCompactionAttempts + 1} maxAttempts=${MAX_OVERFLOW_COMPACTION_ATTEMPTS}`);
472
+ }
372
473
  overflowCompactionAttempts++;
373
474
  log.warn(`context overflow detected (attempt ${overflowCompactionAttempts}/${MAX_OVERFLOW_COMPACTION_ATTEMPTS}); attempting auto-compaction for ${provider}/${modelId}`);
374
475
  const compactResult = await compactEmbeddedPiSessionDirect({
@@ -386,11 +487,16 @@ export async function runEmbeddedPiAgent(params) {
386
487
  senderIsOwner: params.senderIsOwner,
387
488
  provider,
388
489
  model: modelId,
490
+ runId: params.runId,
389
491
  thinkLevel,
390
492
  reasoningLevel: params.reasoningLevel,
391
493
  bashElevated: params.bashElevated,
392
494
  extraSystemPrompt: params.extraSystemPrompt,
393
495
  ownerNumbers: params.ownerNumbers,
496
+ trigger: "overflow",
497
+ diagId: overflowDiagId,
498
+ attempt: overflowCompactionAttempts,
499
+ maxAttempts: MAX_OVERFLOW_COMPACTION_ATTEMPTS,
394
500
  });
395
501
  if (compactResult.compacted) {
396
502
  autoCompactionCount += 1;
@@ -411,6 +517,11 @@ export async function runEmbeddedPiAgent(params) {
411
517
  })
412
518
  : false;
413
519
  if (hasOversized) {
520
+ if (log.isEnabled("debug")) {
521
+ log.debug(`[compaction-diag] decision diagId=${overflowDiagId} branch=truncate_tool_results ` +
522
+ `isCompactionFailure=${isCompactionFailure} hasOversizedToolResults=${hasOversized} ` +
523
+ `attempt=${overflowCompactionAttempts} maxAttempts=${MAX_OVERFLOW_COMPACTION_ATTEMPTS}`);
524
+ }
414
525
  toolResultTruncationAttempted = true;
415
526
  log.warn(`[context-overflow-recovery] Attempting tool result truncation for ${provider}/${modelId} ` +
416
527
  `(contextWindow=${contextWindowTokens} tokens)`);
@@ -428,6 +539,19 @@ export async function runEmbeddedPiAgent(params) {
428
539
  }
429
540
  log.warn(`[context-overflow-recovery] Tool result truncation did not help: ${truncResult.reason ?? "unknown"}`);
430
541
  }
542
+ else if (log.isEnabled("debug")) {
543
+ log.debug(`[compaction-diag] decision diagId=${overflowDiagId} branch=give_up ` +
544
+ `isCompactionFailure=${isCompactionFailure} hasOversizedToolResults=${hasOversized} ` +
545
+ `attempt=${overflowCompactionAttempts} maxAttempts=${MAX_OVERFLOW_COMPACTION_ATTEMPTS}`);
546
+ }
547
+ }
548
+ if ((isCompactionFailure ||
549
+ overflowCompactionAttempts >= MAX_OVERFLOW_COMPACTION_ATTEMPTS ||
550
+ toolResultTruncationAttempted) &&
551
+ log.isEnabled("debug")) {
552
+ log.debug(`[compaction-diag] decision diagId=${overflowDiagId} branch=give_up ` +
553
+ `isCompactionFailure=${isCompactionFailure} hasOversizedToolResults=unknown ` +
554
+ `attempt=${overflowCompactionAttempts} maxAttempts=${MAX_OVERFLOW_COMPACTION_ATTEMPTS}`);
431
555
  }
432
556
  const kind = isCompactionFailure ? "compaction_failure" : "context_overflow";
433
557
  return {
@@ -570,7 +694,8 @@ export async function runEmbeddedPiAgent(params) {
570
694
  log.warn(`Profile ${lastProfileId} rejected image payload${details ? ` (${details})` : ""}.`);
571
695
  }
572
696
  // Treat timeout as potential rate limit (Antigravity hangs on rate limit)
573
- const shouldRotate = (!aborted && failoverFailure) || timedOut;
697
+ // But exclude post-prompt compaction timeouts (model succeeded; no profile issue)
698
+ const shouldRotate = (!aborted && failoverFailure) || (timedOut && !timedOutDuringCompaction);
574
699
  if (shouldRotate) {
575
700
  if (lastProfileId) {
576
701
  const reason = timedOut || assistantFailoverReason === "timeout"
@@ -591,14 +716,16 @@ export async function runEmbeddedPiAgent(params) {
591
716
  }
592
717
  }
593
718
  const rotated = await advanceAuthProfile();
594
- if (rotated)
719
+ if (rotated) {
595
720
  continue;
721
+ }
596
722
  if (fallbackConfigured) {
597
723
  // Prefer formatted error message (user-friendly) over raw errorMessage
598
724
  const message = (lastAssistant
599
725
  ? formatAssistantErrorText(lastAssistant, {
600
726
  cfg: params.config,
601
727
  sessionKey: params.sessionKey ?? params.sessionId,
728
+ provider,
602
729
  })
603
730
  : undefined) ||
604
731
  lastAssistant?.errorMessage?.trim() ||
@@ -607,7 +734,7 @@ export async function runEmbeddedPiAgent(params) {
607
734
  : rateLimitFailure
608
735
  ? "LLM request rate limited."
609
736
  : billingFailure
610
- ? BILLING_ERROR_USER_MESSAGE
737
+ ? formatBillingErrorMessage(provider)
611
738
  : authFailure
612
739
  ? "LLM request unauthorized."
613
740
  : "LLM request failed.");
@@ -623,11 +750,23 @@ export async function runEmbeddedPiAgent(params) {
623
750
  }
624
751
  }
625
752
  const usage = toNormalizedUsage(usageAccumulator);
753
+ if (usage && lastTurnTotal && lastTurnTotal > 0) {
754
+ usage.total = lastTurnTotal;
755
+ }
756
+ // Extract the last individual API call's usage for context-window
757
+ // utilization display. The accumulated `usage` sums input tokens
758
+ // across all calls (tool-use loops, compaction retries), which
759
+ // overstates the actual context size. `lastCallUsage` reflects only
760
+ // the final call, giving an accurate snapshot of current context.
761
+ const lastCallUsage = normalizeUsage(lastAssistant?.usage);
762
+ const promptTokens = derivePromptTokens(lastRunPromptUsage);
626
763
  const agentMeta = {
627
764
  sessionId: sessionIdUsed,
628
765
  provider: lastAssistant?.provider ?? provider,
629
766
  model: lastAssistant?.model ?? model.id,
630
767
  usage,
768
+ lastCallUsage: lastCallUsage ?? undefined,
769
+ promptTokens,
631
770
  compactionCount: autoCompactionCount > 0 ? autoCompactionCount : undefined,
632
771
  };
633
772
  const payloads = buildEmbeddedRunPayloads({
@@ -637,11 +776,38 @@ export async function runEmbeddedPiAgent(params) {
637
776
  lastToolError: attempt.lastToolError,
638
777
  config: params.config,
639
778
  sessionKey: params.sessionKey ?? params.sessionId,
779
+ provider,
640
780
  verboseLevel: params.verboseLevel,
641
781
  reasoningLevel: params.reasoningLevel,
642
782
  toolResultFormat: resolvedToolResultFormat,
783
+ suppressToolErrorWarnings: params.suppressToolErrorWarnings,
643
784
  inlineToolResultsAllowed: false,
644
785
  });
786
+ // Timeout aborts can leave the run without any assistant payloads.
787
+ // Emit an explicit timeout error instead of silently completing, so
788
+ // callers do not lose the turn as an orphaned user message.
789
+ if (timedOut && !timedOutDuringCompaction && payloads.length === 0) {
790
+ return {
791
+ payloads: [
792
+ {
793
+ text: "Request timed out before a response was generated. " +
794
+ "Please try again, or increase `agents.defaults.timeoutSeconds` in your config.",
795
+ isError: true,
796
+ },
797
+ ],
798
+ meta: {
799
+ durationMs: Date.now() - started,
800
+ agentMeta,
801
+ aborted,
802
+ systemPromptReport: attempt.systemPromptReport,
803
+ },
804
+ didSendViaMessagingTool: attempt.didSendViaMessagingTool,
805
+ messagingToolSentTexts: attempt.messagingToolSentTexts,
806
+ messagingToolSentMediaUrls: attempt.messagingToolSentMediaUrls,
807
+ messagingToolSentTargets: attempt.messagingToolSentTargets,
808
+ successfulCronAdds: attempt.successfulCronAdds,
809
+ };
810
+ }
645
811
  log.debug(`embedded run done: runId=${params.runId} sessionId=${params.sessionId} durationMs=${Date.now() - started} aborted=${aborted}`);
646
812
  if (lastProfileId) {
647
813
  await markAuthProfileGood({
@@ -677,7 +843,9 @@ export async function runEmbeddedPiAgent(params) {
677
843
  },
678
844
  didSendViaMessagingTool: attempt.didSendViaMessagingTool,
679
845
  messagingToolSentTexts: attempt.messagingToolSentTexts,
846
+ messagingToolSentMediaUrls: attempt.messagingToolSentMediaUrls,
680
847
  messagingToolSentTargets: attempt.messagingToolSentTargets,
848
+ successfulCronAdds: attempt.successfulCronAdds,
681
849
  };
682
850
  }
683
851
  }
@@ -78,7 +78,7 @@ vi.mock("../model-auth.js", () => ({
78
78
  resolveAuthProfileOrder: vi.fn(() => []),
79
79
  }));
80
80
  vi.mock("../models-config.js", () => ({
81
- ensureOpenClawModelsJson: vi.fn(async () => { }),
81
+ ensurePoolbotModelsJson: vi.fn(async () => { }),
82
82
  }));
83
83
  vi.mock("../context-window-guard.js", () => ({
84
84
  CONTEXT_WINDOW_HARD_MIN_TOKENS: 1000,
@@ -101,7 +101,7 @@ vi.mock("../../utils/message-channel.js", () => ({
101
101
  isMarkdownCapableMessageChannel: vi.fn(() => true),
102
102
  }));
103
103
  vi.mock("../agent-paths.js", () => ({
104
- resolveOpenClawAgentDir: vi.fn(() => "/tmp/agent-dir"),
104
+ resolvePoolbotAgentDir: vi.fn(() => "/tmp/agent-dir"),
105
105
  }));
106
106
  vi.mock("../defaults.js", () => ({
107
107
  DEFAULT_CONTEXT_TOKENS: 200000,
@@ -38,13 +38,18 @@ export function isEmbeddedPiRunActive(sessionId) {
38
38
  }
39
39
  export function isEmbeddedPiRunStreaming(sessionId) {
40
40
  const handle = ACTIVE_EMBEDDED_RUNS.get(sessionId);
41
- if (!handle)
41
+ if (!handle) {
42
42
  return false;
43
+ }
43
44
  return handle.isStreaming();
44
45
  }
46
+ export function getActiveEmbeddedRunCount() {
47
+ return ACTIVE_EMBEDDED_RUNS.size;
48
+ }
45
49
  export function waitForEmbeddedPiRunEnd(sessionId, timeoutMs = 15_000) {
46
- if (!sessionId || !ACTIVE_EMBEDDED_RUNS.has(sessionId))
50
+ if (!sessionId || !ACTIVE_EMBEDDED_RUNS.has(sessionId)) {
47
51
  return Promise.resolve(true);
52
+ }
48
53
  diag.debug(`waiting for run end: sessionId=${sessionId} timeoutMs=${timeoutMs}`);
49
54
  return new Promise((resolve) => {
50
55
  const waiters = EMBEDDED_RUN_WAITERS.get(sessionId) ?? new Set();
@@ -52,8 +57,9 @@ export function waitForEmbeddedPiRunEnd(sessionId, timeoutMs = 15_000) {
52
57
  resolve,
53
58
  timer: setTimeout(() => {
54
59
  waiters.delete(waiter);
55
- if (waiters.size === 0)
60
+ if (waiters.size === 0) {
56
61
  EMBEDDED_RUN_WAITERS.delete(sessionId);
62
+ }
57
63
  diag.warn(`wait timeout: sessionId=${sessionId} timeoutMs=${timeoutMs}`);
58
64
  resolve(false);
59
65
  }, Math.max(100, timeoutMs)),
@@ -62,8 +68,9 @@ export function waitForEmbeddedPiRunEnd(sessionId, timeoutMs = 15_000) {
62
68
  EMBEDDED_RUN_WAITERS.set(sessionId, waiters);
63
69
  if (!ACTIVE_EMBEDDED_RUNS.has(sessionId)) {
64
70
  waiters.delete(waiter);
65
- if (waiters.size === 0)
71
+ if (waiters.size === 0) {
66
72
  EMBEDDED_RUN_WAITERS.delete(sessionId);
73
+ }
67
74
  clearTimeout(waiter.timer);
68
75
  resolve(true);
69
76
  }
@@ -71,8 +78,9 @@ export function waitForEmbeddedPiRunEnd(sessionId, timeoutMs = 15_000) {
71
78
  }
72
79
  function notifyEmbeddedRunEnded(sessionId) {
73
80
  const waiters = EMBEDDED_RUN_WAITERS.get(sessionId);
74
- if (!waiters || waiters.size === 0)
81
+ if (!waiters || waiters.size === 0) {
75
82
  return;
83
+ }
76
84
  EMBEDDED_RUN_WAITERS.delete(sessionId);
77
85
  diag.debug(`notifying waiters: sessionId=${sessionId} waiterCount=${waiters.size}`);
78
86
  for (const waiter of waiters) {
@@ -80,11 +88,12 @@ function notifyEmbeddedRunEnded(sessionId) {
80
88
  waiter.resolve(true);
81
89
  }
82
90
  }
83
- export function setActiveEmbeddedRun(sessionId, handle) {
91
+ export function setActiveEmbeddedRun(sessionId, handle, sessionKey) {
84
92
  const wasActive = ACTIVE_EMBEDDED_RUNS.has(sessionId);
85
93
  ACTIVE_EMBEDDED_RUNS.set(sessionId, handle);
86
94
  logSessionStateChange({
87
95
  sessionId,
96
+ sessionKey,
88
97
  state: "processing",
89
98
  reason: wasActive ? "run_replaced" : "run_started",
90
99
  });
@@ -92,10 +101,10 @@ export function setActiveEmbeddedRun(sessionId, handle) {
92
101
  diag.debug(`run registered: sessionId=${sessionId} totalActive=${ACTIVE_EMBEDDED_RUNS.size}`);
93
102
  }
94
103
  }
95
- export function clearActiveEmbeddedRun(sessionId, handle) {
104
+ export function clearActiveEmbeddedRun(sessionId, handle, sessionKey) {
96
105
  if (ACTIVE_EMBEDDED_RUNS.get(sessionId) === handle) {
97
106
  ACTIVE_EMBEDDED_RUNS.delete(sessionId);
98
- logSessionStateChange({ sessionId, state: "idle", reason: "run_completed" });
107
+ logSessionStateChange({ sessionId, sessionKey, state: "idle", reason: "run_completed" });
99
108
  if (!sessionId.startsWith("probe-")) {
100
109
  diag.debug(`run cleared: sessionId=${sessionId} totalActive=${ACTIVE_EMBEDDED_RUNS.size}`);
101
110
  }