@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
@@ -2,32 +2,39 @@ import fs from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import { createAgentSession, estimateTokens, SessionManager, SettingsManager, } from "@mariozechner/pi-coding-agent";
4
4
  import { resolveHeartbeatPrompt } from "../../auto-reply/heartbeat.js";
5
- import { listChannelSupportedActions, resolveChannelMessageToolHints } from "../channel-tools.js";
6
5
  import { resolveChannelCapabilities } from "../../config/channel-capabilities.js";
7
6
  import { getMachineDisplayName } from "../../infra/machine-name.js";
7
+ import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
8
+ import { enqueueCommandInLane } from "../../process/command-queue.js";
9
+ import { isCronSessionKey, isSubagentSessionKey } from "../../routing/session-key.js";
10
+ import { resolveSignalReactionLevel } from "../../signal/reaction-level.js";
8
11
  import { resolveTelegramInlineButtonsScope } from "../../telegram/inline-buttons.js";
9
12
  import { resolveTelegramReactionLevel } from "../../telegram/reaction-level.js";
10
- import { resolveSignalReactionLevel } from "../../signal/reaction-level.js";
11
- import { enqueueCommandInLane } from "../../process/command-queue.js";
13
+ import { buildTtsSystemPromptHint } from "../../tts/tts.js";
14
+ import { resolveUserPath } from "../../utils.js";
12
15
  import { normalizeMessageChannel } from "../../utils/message-channel.js";
13
- import { isSubagentSessionKey } from "../../routing/session-key.js";
14
16
  import { isReasoningTagProvider } from "../../utils/provider-utils.js";
15
- import { resolveUserPath } from "../../utils.js";
16
17
  import { resolvePoolbotAgentDir } from "../agent-paths.js";
17
18
  import { resolveSessionAgentIds } from "../agent-scope.js";
18
19
  import { makeBootstrapWarn, resolveBootstrapContextForRun } from "../bootstrap-files.js";
19
- import { resolvePoolbotDocsPath } from "../docs-path.js";
20
+ import { listChannelSupportedActions, resolveChannelMessageToolHints } from "../channel-tools.js";
21
+ import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js";
20
22
  import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../defaults.js";
23
+ import { resolvePoolbotDocsPath } from "../docs-path.js";
21
24
  import { getApiKeyForModel, resolveModelAuthMode } from "../model-auth.js";
22
25
  import { ensurePoolbotModelsJson } from "../models-config.js";
23
26
  import { ensureSessionHeader, validateAnthropicTurns, validateGeminiTurns, } from "../pi-embedded-helpers.js";
24
27
  import { ensurePiCompactionReserveTokens, resolveCompactionReserveTokensFloor, } from "../pi-settings.js";
25
28
  import { createPoolbotCodingTools } from "../pi-tools.js";
26
29
  import { resolveSandboxContext } from "../sandbox.js";
30
+ import { repairSessionFileIfNeeded } from "../session-file-repair.js";
27
31
  import { guardSessionManager } from "../session-tool-result-guard-wrapper.js";
28
- import { resolveTranscriptPolicy } from "../transcript-policy.js";
29
- import { acquireSessionWriteLock } from "../session-write-lock.js";
32
+ import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js";
33
+ import { acquireSessionWriteLock, resolveSessionLockMaxHoldFromTimeout, } from "../session-write-lock.js";
34
+ import { detectRuntimeShell } from "../shell-utils.js";
30
35
  import { applySkillEnvOverrides, applySkillEnvOverridesFromSnapshot, loadWorkspaceSkillEntries, resolveSkillsPromptForRun, } from "../skills.js";
36
+ import { resolveTranscriptPolicy } from "../transcript-policy.js";
37
+ import { compactWithSafetyTimeout, EMBEDDED_COMPACTION_TIMEOUT_MS, } from "./compaction-safety-timeout.js";
31
38
  import { buildEmbeddedExtensionPaths } from "./extensions.js";
32
39
  import { logToolSchemasForGoogle, sanitizeSessionHistory, sanitizeToolsForGoogle, } from "./google.js";
33
40
  import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "./history.js";
@@ -38,27 +45,137 @@ import { buildEmbeddedSandboxInfo } from "./sandbox-info.js";
38
45
  import { prewarmSessionFile, trackSessionManagerAccess } from "./session-manager-cache.js";
39
46
  import { applySystemPromptOverrideToSession, buildEmbeddedSystemPrompt, createSystemPromptOverride, } from "./system-prompt.js";
40
47
  import { splitSdkTools } from "./tool-split.js";
41
- import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js";
42
- import { describeUnknownError, mapThinkingLevel, resolveExecToolDefaults } from "./utils.js";
43
- import { buildTtsSystemPromptHint } from "../../tts/tts.js";
48
+ import { describeUnknownError, mapThinkingLevel } from "./utils.js";
49
+ import { flushPendingToolResultsAfterIdle } from "./wait-for-idle-before-flush.js";
50
+ function createCompactionDiagId() {
51
+ return `cmp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
52
+ }
53
+ function getMessageTextChars(msg) {
54
+ const content = msg.content;
55
+ if (typeof content === "string") {
56
+ return content.length;
57
+ }
58
+ if (!Array.isArray(content)) {
59
+ return 0;
60
+ }
61
+ let total = 0;
62
+ for (const block of content) {
63
+ if (!block || typeof block !== "object") {
64
+ continue;
65
+ }
66
+ const text = block.text;
67
+ if (typeof text === "string") {
68
+ total += text.length;
69
+ }
70
+ }
71
+ return total;
72
+ }
73
+ function resolveMessageToolLabel(msg) {
74
+ const candidate = msg.toolName ??
75
+ msg.name ??
76
+ msg.tool;
77
+ return typeof candidate === "string" && candidate.trim().length > 0 ? candidate : undefined;
78
+ }
79
+ function summarizeCompactionMessages(messages) {
80
+ let historyTextChars = 0;
81
+ let toolResultChars = 0;
82
+ const contributors = [];
83
+ let estTokens = 0;
84
+ let tokenEstimationFailed = false;
85
+ for (const msg of messages) {
86
+ const role = typeof msg.role === "string" ? msg.role : "unknown";
87
+ const chars = getMessageTextChars(msg);
88
+ historyTextChars += chars;
89
+ if (role === "toolResult") {
90
+ toolResultChars += chars;
91
+ }
92
+ contributors.push({ role, chars, tool: resolveMessageToolLabel(msg) });
93
+ if (!tokenEstimationFailed) {
94
+ try {
95
+ estTokens += estimateTokens(msg);
96
+ }
97
+ catch {
98
+ tokenEstimationFailed = true;
99
+ }
100
+ }
101
+ }
102
+ return {
103
+ messages: messages.length,
104
+ historyTextChars,
105
+ toolResultChars,
106
+ estTokens: tokenEstimationFailed ? undefined : estTokens,
107
+ contributors: contributors.toSorted((a, b) => b.chars - a.chars).slice(0, 3),
108
+ };
109
+ }
110
+ function classifyCompactionReason(reason) {
111
+ const text = (reason ?? "").trim().toLowerCase();
112
+ if (!text) {
113
+ return "unknown";
114
+ }
115
+ if (text.includes("nothing to compact")) {
116
+ return "no_compactable_entries";
117
+ }
118
+ if (text.includes("below threshold")) {
119
+ return "below_threshold";
120
+ }
121
+ if (text.includes("already compacted")) {
122
+ return "already_compacted_recently";
123
+ }
124
+ if (text.includes("guard")) {
125
+ return "guard_blocked";
126
+ }
127
+ if (text.includes("summary")) {
128
+ return "summary_failed";
129
+ }
130
+ if (text.includes("timed out") || text.includes("timeout")) {
131
+ return "timeout";
132
+ }
133
+ if (text.includes("400") ||
134
+ text.includes("401") ||
135
+ text.includes("403") ||
136
+ text.includes("429")) {
137
+ return "provider_error_4xx";
138
+ }
139
+ if (text.includes("500") ||
140
+ text.includes("502") ||
141
+ text.includes("503") ||
142
+ text.includes("504")) {
143
+ return "provider_error_5xx";
144
+ }
145
+ return "unknown";
146
+ }
44
147
  /**
45
148
  * Core compaction logic without lane queueing.
46
149
  * Use this when already inside a session/global lane to avoid deadlocks.
47
150
  */
48
151
  export async function compactEmbeddedPiSessionDirect(params) {
152
+ const startedAt = Date.now();
153
+ const diagId = params.diagId?.trim() || createCompactionDiagId();
154
+ const trigger = params.trigger ?? "manual";
155
+ const attempt = params.attempt ?? 1;
156
+ const maxAttempts = params.maxAttempts ?? 1;
157
+ const runId = params.runId ?? params.sessionId;
49
158
  const resolvedWorkspace = resolveUserPath(params.workspaceDir);
50
159
  const prevCwd = process.cwd();
51
160
  const provider = (params.provider ?? DEFAULT_PROVIDER).trim() || DEFAULT_PROVIDER;
52
161
  const modelId = (params.model ?? DEFAULT_MODEL).trim() || DEFAULT_MODEL;
53
- const agentDir = params.agentDir ?? resolvePoolbotAgentDir();
54
- await ensurePoolbotModelsJson(params.config, agentDir);
55
- const { model, error, authStorage, modelRegistry } = resolveModel(provider, modelId, agentDir, params.config);
56
- if (!model) {
162
+ const fail = (reason) => {
163
+ log.warn(`[compaction-diag] end runId=${runId} sessionKey=${params.sessionKey ?? params.sessionId} ` +
164
+ `diagId=${diagId} trigger=${trigger} provider=${provider}/${modelId} ` +
165
+ `attempt=${attempt} maxAttempts=${maxAttempts} outcome=failed reason=${classifyCompactionReason(reason)} ` +
166
+ `durationMs=${Date.now() - startedAt}`);
57
167
  return {
58
168
  ok: false,
59
169
  compacted: false,
60
- reason: error ?? `Unknown model: ${provider}/${modelId}`,
170
+ reason,
61
171
  };
172
+ };
173
+ const agentDir = params.agentDir ?? resolvePoolbotAgentDir();
174
+ await ensurePoolbotModelsJson(params.config, agentDir);
175
+ const { model, error, authStorage, modelRegistry } = resolveModel(provider, modelId, agentDir, params.config);
176
+ if (!model) {
177
+ const reason = error ?? `Unknown model: ${provider}/${modelId}`;
178
+ return fail(reason);
62
179
  }
63
180
  try {
64
181
  const apiKeyInfo = await getApiKeyForModel({
@@ -84,11 +201,8 @@ export async function compactEmbeddedPiSessionDirect(params) {
84
201
  }
85
202
  }
86
203
  catch (err) {
87
- return {
88
- ok: false,
89
- compacted: false,
90
- reason: describeUnknownError(err),
91
- };
204
+ const reason = describeUnknownError(err);
205
+ return fail(reason);
92
206
  }
93
207
  await fs.mkdir(resolvedWorkspace, { recursive: true });
94
208
  const sandboxSessionKey = params.sessionKey?.trim() || params.sessionId;
@@ -141,7 +255,6 @@ export async function compactEmbeddedPiSessionDirect(params) {
141
255
  const runAbortController = new AbortController();
142
256
  const toolsRaw = createPoolbotCodingTools({
143
257
  exec: {
144
- ...resolveExecToolDefaults(params.config),
145
258
  elevated: params.bashElevated,
146
259
  },
147
260
  sandbox,
@@ -159,6 +272,7 @@ export async function compactEmbeddedPiSessionDirect(params) {
159
272
  abortSignal: runAbortController.signal,
160
273
  modelProvider: model.provider,
161
274
  modelId,
275
+ modelContextWindowTokens: model.contextWindow,
162
276
  modelAuthMode: resolveModelAuthMode(model.provider, params.config),
163
277
  });
164
278
  const tools = sanitizeToolsForGoogle({ tools: toolsRaw, provider });
@@ -178,8 +292,9 @@ export async function compactEmbeddedPiSessionDirect(params) {
178
292
  accountId: params.agentAccountId ?? undefined,
179
293
  });
180
294
  if (inlineButtonsScope !== "off") {
181
- if (!runtimeCapabilities)
295
+ if (!runtimeCapabilities) {
182
296
  runtimeCapabilities = [];
297
+ }
183
298
  if (!runtimeCapabilities.some((cap) => String(cap).trim().toLowerCase() === "inlinebuttons")) {
184
299
  runtimeCapabilities.push("inlineButtons");
185
300
  }
@@ -226,6 +341,7 @@ export async function compactEmbeddedPiSessionDirect(params) {
226
341
  arch: os.arch(),
227
342
  node: process.version,
228
343
  model: `${provider}/${modelId}`,
344
+ shell: detectRuntimeShell(),
229
345
  channel: runtimeChannel,
230
346
  capabilities: runtimeCapabilities,
231
347
  channelActions,
@@ -240,7 +356,9 @@ export async function compactEmbeddedPiSessionDirect(params) {
240
356
  config: params.config,
241
357
  });
242
358
  const isDefaultAgent = sessionAgentId === defaultAgentId;
243
- const promptMode = isSubagentSessionKey(params.sessionKey) ? "minimal" : "full";
359
+ const promptMode = isSubagentSessionKey(params.sessionKey) || isCronSessionKey(params.sessionKey)
360
+ ? "minimal"
361
+ : "full";
244
362
  const docsPath = await resolvePoolbotDocsPath({
245
363
  workspaceDir: effectiveWorkspace,
246
364
  argv1: process.argv[1],
@@ -272,12 +390,20 @@ export async function compactEmbeddedPiSessionDirect(params) {
272
390
  userTime,
273
391
  userTimeFormat,
274
392
  contextFiles,
393
+ memoryCitationsMode: params.config?.memory?.citations,
275
394
  });
276
395
  const systemPromptOverride = createSystemPromptOverride(appendPrompt);
277
396
  const sessionLock = await acquireSessionWriteLock({
278
397
  sessionFile: params.sessionFile,
398
+ maxHoldMs: resolveSessionLockMaxHoldFromTimeout({
399
+ timeoutMs: EMBEDDED_COMPACTION_TIMEOUT_MS,
400
+ }),
279
401
  });
280
402
  try {
403
+ await repairSessionFileIfNeeded({
404
+ sessionFile: params.sessionFile,
405
+ warn: (message) => log.warn(message),
406
+ });
281
407
  await prewarmSessionFile(params.sessionFile);
282
408
  const transcriptPolicy = resolveTranscriptPolicy({
283
409
  modelApi: model.api,
@@ -326,6 +452,7 @@ export async function compactEmbeddedPiSessionDirect(params) {
326
452
  modelApi: model.api,
327
453
  modelId,
328
454
  provider,
455
+ config: params.config,
329
456
  sessionManager,
330
457
  sessionId: params.sessionId,
331
458
  policy: transcriptPolicy,
@@ -336,11 +463,54 @@ export async function compactEmbeddedPiSessionDirect(params) {
336
463
  const validated = transcriptPolicy.validateAnthropicTurns
337
464
  ? validateAnthropicTurns(validatedGemini)
338
465
  : validatedGemini;
339
- const limited = limitHistoryTurns(validated, getDmHistoryLimitFromSessionKey(params.sessionKey, params.config));
466
+ // Capture full message history BEFORE limiting — plugins need the complete conversation
467
+ const preCompactionMessages = [...session.messages];
468
+ const truncated = limitHistoryTurns(validated, getDmHistoryLimitFromSessionKey(params.sessionKey, params.config));
469
+ // Re-run tool_use/tool_result pairing repair after truncation, since
470
+ // limitHistoryTurns can orphan tool_result blocks by removing the
471
+ // assistant message that contained the matching tool_use.
472
+ const limited = transcriptPolicy.repairToolUseResultPairing
473
+ ? sanitizeToolUseResultPairing(truncated)
474
+ : truncated;
340
475
  if (limited.length > 0) {
341
476
  session.agent.replaceMessages(limited);
342
477
  }
343
- const result = await session.compact(params.customInstructions);
478
+ // Run before_compaction hooks (fire-and-forget).
479
+ // The session JSONL already contains all messages on disk, so plugins
480
+ // can read sessionFile asynchronously and process in parallel with
481
+ // the compaction LLM call — no need to block or wait for after_compaction.
482
+ const hookRunner = getGlobalHookRunner();
483
+ const hookCtx = {
484
+ agentId: params.sessionKey?.split(":")[0] ?? "main",
485
+ sessionKey: params.sessionKey,
486
+ sessionId: params.sessionId,
487
+ workspaceDir: params.workspaceDir,
488
+ messageProvider: params.messageChannel ?? params.messageProvider,
489
+ };
490
+ if (hookRunner?.hasHooks("before_compaction")) {
491
+ hookRunner
492
+ .runBeforeCompaction({
493
+ messageCount: preCompactionMessages.length,
494
+ compactingCount: limited.length,
495
+ messages: preCompactionMessages,
496
+ sessionFile: params.sessionFile,
497
+ }, hookCtx)
498
+ .catch((hookErr) => {
499
+ log.warn(`before_compaction hook failed: ${String(hookErr)}`);
500
+ });
501
+ }
502
+ const diagEnabled = log.isEnabled("debug");
503
+ const preMetrics = diagEnabled ? summarizeCompactionMessages(session.messages) : undefined;
504
+ if (diagEnabled && preMetrics) {
505
+ log.debug(`[compaction-diag] start runId=${runId} sessionKey=${params.sessionKey ?? params.sessionId} ` +
506
+ `diagId=${diagId} trigger=${trigger} provider=${provider}/${modelId} ` +
507
+ `attempt=${attempt} maxAttempts=${maxAttempts} ` +
508
+ `pre.messages=${preMetrics.messages} pre.historyTextChars=${preMetrics.historyTextChars} ` +
509
+ `pre.toolResultChars=${preMetrics.toolResultChars} pre.estTokens=${preMetrics.estTokens ?? "unknown"}`);
510
+ log.debug(`[compaction-diag] contributors diagId=${diagId} top=${JSON.stringify(preMetrics.contributors)}`);
511
+ }
512
+ const compactStartedAt = Date.now();
513
+ const result = await compactWithSafetyTimeout(() => session.compact(params.customInstructions));
344
514
  // Estimate tokens after compaction by summing token estimates for remaining messages
345
515
  let tokensAfter;
346
516
  try {
@@ -357,6 +527,34 @@ export async function compactEmbeddedPiSessionDirect(params) {
357
527
  // If estimation fails, leave tokensAfter undefined
358
528
  tokensAfter = undefined;
359
529
  }
530
+ // Run after_compaction hooks (fire-and-forget).
531
+ // Also includes sessionFile for plugins that only need to act after
532
+ // compaction completes (e.g. analytics, cleanup).
533
+ if (hookRunner?.hasHooks("after_compaction")) {
534
+ hookRunner
535
+ .runAfterCompaction({
536
+ messageCount: session.messages.length,
537
+ tokenCount: tokensAfter,
538
+ compactedCount: limited.length - session.messages.length,
539
+ sessionFile: params.sessionFile,
540
+ }, hookCtx)
541
+ .catch((hookErr) => {
542
+ log.warn(`after_compaction hook failed: ${hookErr}`);
543
+ });
544
+ }
545
+ const postMetrics = diagEnabled ? summarizeCompactionMessages(session.messages) : undefined;
546
+ if (diagEnabled && preMetrics && postMetrics) {
547
+ log.debug(`[compaction-diag] end runId=${runId} sessionKey=${params.sessionKey ?? params.sessionId} ` +
548
+ `diagId=${diagId} trigger=${trigger} provider=${provider}/${modelId} ` +
549
+ `attempt=${attempt} maxAttempts=${maxAttempts} outcome=compacted reason=none ` +
550
+ `durationMs=${Date.now() - compactStartedAt} retrying=false ` +
551
+ `post.messages=${postMetrics.messages} post.historyTextChars=${postMetrics.historyTextChars} ` +
552
+ `post.toolResultChars=${postMetrics.toolResultChars} post.estTokens=${postMetrics.estTokens ?? "unknown"} ` +
553
+ `delta.messages=${postMetrics.messages - preMetrics.messages} ` +
554
+ `delta.historyTextChars=${postMetrics.historyTextChars - preMetrics.historyTextChars} ` +
555
+ `delta.toolResultChars=${postMetrics.toolResultChars - preMetrics.toolResultChars} ` +
556
+ `delta.estTokens=${typeof preMetrics.estTokens === "number" && typeof postMetrics.estTokens === "number" ? postMetrics.estTokens - preMetrics.estTokens : "unknown"}`);
557
+ }
360
558
  return {
361
559
  ok: true,
362
560
  compacted: true,
@@ -370,7 +568,10 @@ export async function compactEmbeddedPiSessionDirect(params) {
370
568
  };
371
569
  }
372
570
  finally {
373
- sessionManager.flushPendingToolResults?.();
571
+ await flushPendingToolResultsAfterIdle({
572
+ agent: session?.agent,
573
+ sessionManager,
574
+ });
374
575
  session.dispose();
375
576
  }
376
577
  }
@@ -379,11 +580,8 @@ export async function compactEmbeddedPiSessionDirect(params) {
379
580
  }
380
581
  }
381
582
  catch (err) {
382
- return {
383
- ok: false,
384
- compacted: false,
385
- reason: describeUnknownError(err),
386
- };
583
+ const reason = describeUnknownError(err);
584
+ return fail(reason);
387
585
  }
388
586
  finally {
389
587
  restoreSkillEnv?.();
@@ -1,5 +1,15 @@
1
1
  import { streamSimple } from "@mariozechner/pi-ai";
2
2
  import { log } from "./logger.js";
3
+ const OPENROUTER_APP_HEADERS = {
4
+ "HTTP-Referer": "https://poolbot.dev",
5
+ "X-Title": "PoolBot",
6
+ };
7
+ const ANTHROPIC_CONTEXT_1M_BETA = "context-1m-2025-08-07";
8
+ const ANTHROPIC_1M_MODEL_PREFIXES = ["claude-opus-4", "claude-sonnet-4"];
9
+ // NOTE: We only force `store=true` for *direct* OpenAI Responses.
10
+ // Codex responses (chatgpt.com/backend-api/codex/responses) require `store=false`.
11
+ const OPENAI_RESPONSES_APIS = new Set(["openai-responses"]);
12
+ const OPENAI_RESPONSES_PROVIDERS = new Set(["openai"]);
3
13
  /**
4
14
  * Resolve provider-specific extra params from model config.
5
15
  * Used to pass through stream params like temperature/maxTokens.
@@ -11,17 +21,35 @@ export function resolveExtraParams(params) {
11
21
  const modelConfig = params.cfg?.agents?.defaults?.models?.[modelKey];
12
22
  return modelConfig?.params ? { ...modelConfig.params } : undefined;
13
23
  }
14
- function resolveCacheControlTtl(extraParams, provider, modelId) {
15
- const raw = extraParams?.cacheControlTtl;
16
- if (raw !== "5m" && raw !== "1h")
24
+ /**
25
+ * Resolve cacheRetention from extraParams, supporting both new `cacheRetention`
26
+ * and legacy `cacheControlTtl` values for backwards compatibility.
27
+ *
28
+ * Mapping: "5m" → "short", "1h" → "long"
29
+ *
30
+ * Only applies to Anthropic provider (OpenRouter uses openai-completions API
31
+ * with hardcoded cache_control, not the cacheRetention stream option).
32
+ */
33
+ function resolveCacheRetention(extraParams, provider) {
34
+ if (provider !== "anthropic") {
17
35
  return undefined;
18
- if (provider === "anthropic")
19
- return raw;
20
- if (provider === "openrouter" && modelId.startsWith("anthropic/"))
21
- return raw;
36
+ }
37
+ // Prefer new cacheRetention if present
38
+ const newVal = extraParams?.cacheRetention;
39
+ if (newVal === "none" || newVal === "short" || newVal === "long") {
40
+ return newVal;
41
+ }
42
+ // Fall back to legacy cacheControlTtl with mapping
43
+ const legacy = extraParams?.cacheControlTtl;
44
+ if (legacy === "5m") {
45
+ return "short";
46
+ }
47
+ if (legacy === "1h") {
48
+ return "long";
49
+ }
22
50
  return undefined;
23
51
  }
24
- function createStreamFnWithExtraParams(baseStreamFn, extraParams, provider, modelId) {
52
+ function createStreamFnWithExtraParams(baseStreamFn, extraParams, provider) {
25
53
  if (!extraParams || Object.keys(extraParams).length === 0) {
26
54
  return undefined;
27
55
  }
@@ -32,9 +60,9 @@ function createStreamFnWithExtraParams(baseStreamFn, extraParams, provider, mode
32
60
  if (typeof extraParams.maxTokens === "number") {
33
61
  streamParams.maxTokens = extraParams.maxTokens;
34
62
  }
35
- const cacheControlTtl = resolveCacheControlTtl(extraParams, provider, modelId);
36
- if (cacheControlTtl) {
37
- streamParams.cacheControlTtl = cacheControlTtl;
63
+ const cacheRetention = resolveCacheRetention(extraParams, provider);
64
+ if (cacheRetention) {
65
+ streamParams.cacheRetention = cacheRetention;
38
66
  }
39
67
  if (Object.keys(streamParams).length === 0) {
40
68
  return undefined;
@@ -47,8 +75,149 @@ function createStreamFnWithExtraParams(baseStreamFn, extraParams, provider, mode
47
75
  });
48
76
  return wrappedStreamFn;
49
77
  }
78
+ function isDirectOpenAIBaseUrl(baseUrl) {
79
+ if (typeof baseUrl !== "string" || !baseUrl.trim()) {
80
+ return true;
81
+ }
82
+ try {
83
+ const host = new URL(baseUrl).hostname.toLowerCase();
84
+ return host === "api.openai.com" || host === "chatgpt.com";
85
+ }
86
+ catch {
87
+ const normalized = baseUrl.toLowerCase();
88
+ return normalized.includes("api.openai.com") || normalized.includes("chatgpt.com");
89
+ }
90
+ }
91
+ function shouldForceResponsesStore(model) {
92
+ if (typeof model.api !== "string" || typeof model.provider !== "string") {
93
+ return false;
94
+ }
95
+ if (!OPENAI_RESPONSES_APIS.has(model.api)) {
96
+ return false;
97
+ }
98
+ if (!OPENAI_RESPONSES_PROVIDERS.has(model.provider)) {
99
+ return false;
100
+ }
101
+ return isDirectOpenAIBaseUrl(model.baseUrl);
102
+ }
103
+ function createOpenAIResponsesStoreWrapper(baseStreamFn) {
104
+ const underlying = baseStreamFn ?? streamSimple;
105
+ return (model, context, options) => {
106
+ if (!shouldForceResponsesStore(model)) {
107
+ return underlying(model, context, options);
108
+ }
109
+ const originalOnPayload = options?.onPayload;
110
+ return underlying(model, context, {
111
+ ...options,
112
+ onPayload: (payload) => {
113
+ if (payload && typeof payload === "object") {
114
+ payload.store = true;
115
+ }
116
+ originalOnPayload?.(payload);
117
+ },
118
+ });
119
+ };
120
+ }
121
+ function isAnthropic1MModel(modelId) {
122
+ const normalized = modelId.trim().toLowerCase();
123
+ return ANTHROPIC_1M_MODEL_PREFIXES.some((prefix) => normalized.startsWith(prefix));
124
+ }
125
+ function parseHeaderList(value) {
126
+ if (typeof value !== "string") {
127
+ return [];
128
+ }
129
+ return value
130
+ .split(",")
131
+ .map((item) => item.trim())
132
+ .filter(Boolean);
133
+ }
134
+ function resolveAnthropicBetas(extraParams, provider, modelId) {
135
+ if (provider !== "anthropic") {
136
+ return undefined;
137
+ }
138
+ const betas = new Set();
139
+ const configured = extraParams?.anthropicBeta;
140
+ if (typeof configured === "string" && configured.trim()) {
141
+ betas.add(configured.trim());
142
+ }
143
+ else if (Array.isArray(configured)) {
144
+ for (const beta of configured) {
145
+ if (typeof beta === "string" && beta.trim()) {
146
+ betas.add(beta.trim());
147
+ }
148
+ }
149
+ }
150
+ if (extraParams?.context1m === true) {
151
+ if (isAnthropic1MModel(modelId)) {
152
+ betas.add(ANTHROPIC_CONTEXT_1M_BETA);
153
+ }
154
+ else {
155
+ log.warn(`ignoring context1m for non-opus/sonnet model: ${provider}/${modelId}`);
156
+ }
157
+ }
158
+ return betas.size > 0 ? [...betas] : undefined;
159
+ }
160
+ function mergeAnthropicBetaHeader(headers, betas) {
161
+ const merged = { ...headers };
162
+ const existingKey = Object.keys(merged).find((key) => key.toLowerCase() === "anthropic-beta");
163
+ const existing = existingKey ? parseHeaderList(merged[existingKey]) : [];
164
+ const values = Array.from(new Set([...existing, ...betas]));
165
+ const key = existingKey ?? "anthropic-beta";
166
+ merged[key] = values.join(",");
167
+ return merged;
168
+ }
169
+ function createAnthropicBetaHeadersWrapper(baseStreamFn, betas) {
170
+ const underlying = baseStreamFn ?? streamSimple;
171
+ return (model, context, options) => underlying(model, context, {
172
+ ...options,
173
+ headers: mergeAnthropicBetaHeader(options?.headers, betas),
174
+ });
175
+ }
176
+ /**
177
+ * Create a streamFn wrapper that adds OpenRouter app attribution headers.
178
+ * These headers allow Pool Bot to appear on OpenRouter's leaderboard.
179
+ */
180
+ function createOpenRouterHeadersWrapper(baseStreamFn) {
181
+ const underlying = baseStreamFn ?? streamSimple;
182
+ return (model, context, options) => underlying(model, context, {
183
+ ...options,
184
+ headers: {
185
+ ...OPENROUTER_APP_HEADERS,
186
+ ...options?.headers,
187
+ },
188
+ });
189
+ }
190
+ /**
191
+ * Create a streamFn wrapper that injects tool_stream=true for Z.AI providers.
192
+ *
193
+ * Z.AI's API supports the `tool_stream` parameter to enable real-time streaming
194
+ * of tool call arguments and reasoning content. When enabled, the API returns
195
+ * progressive tool_call deltas, allowing users to see tool execution in real-time.
196
+ *
197
+ * @see https://docs.z.ai/api-reference#streaming
198
+ */
199
+ function createZaiToolStreamWrapper(baseStreamFn, enabled) {
200
+ const underlying = baseStreamFn ?? streamSimple;
201
+ return (model, context, options) => {
202
+ if (!enabled) {
203
+ return underlying(model, context, options);
204
+ }
205
+ const originalOnPayload = options?.onPayload;
206
+ return underlying(model, context, {
207
+ ...options,
208
+ onPayload: (payload) => {
209
+ if (payload && typeof payload === "object") {
210
+ // Inject tool_stream: true for Z.AI API
211
+ payload.tool_stream = true;
212
+ }
213
+ originalOnPayload?.(payload);
214
+ },
215
+ });
216
+ };
217
+ }
50
218
  /**
51
219
  * Apply extra params (like temperature) to an agent's streamFn.
220
+ * Also adds OpenRouter app attribution headers when using the OpenRouter provider.
52
221
  *
53
222
  * @internal Exported for testing
54
223
  */
@@ -62,9 +231,31 @@ export function applyExtraParamsToAgent(agent, cfg, provider, modelId, extraPara
62
231
  ? Object.fromEntries(Object.entries(extraParamsOverride).filter(([, value]) => value !== undefined))
63
232
  : undefined;
64
233
  const merged = Object.assign({}, extraParams, override);
65
- const wrappedStreamFn = createStreamFnWithExtraParams(agent.streamFn, merged, provider, modelId);
234
+ const wrappedStreamFn = createStreamFnWithExtraParams(agent.streamFn, merged, provider);
66
235
  if (wrappedStreamFn) {
67
236
  log.debug(`applying extraParams to agent streamFn for ${provider}/${modelId}`);
68
237
  agent.streamFn = wrappedStreamFn;
69
238
  }
239
+ const anthropicBetas = resolveAnthropicBetas(merged, provider, modelId);
240
+ if (anthropicBetas?.length) {
241
+ log.debug(`applying Anthropic beta header for ${provider}/${modelId}: ${anthropicBetas.join(",")}`);
242
+ agent.streamFn = createAnthropicBetaHeadersWrapper(agent.streamFn, anthropicBetas);
243
+ }
244
+ if (provider === "openrouter") {
245
+ log.debug(`applying OpenRouter app attribution headers for ${provider}/${modelId}`);
246
+ agent.streamFn = createOpenRouterHeadersWrapper(agent.streamFn);
247
+ }
248
+ // Enable Z.AI tool_stream for real-time tool call streaming.
249
+ // Enabled by default for Z.AI provider, can be disabled via params.tool_stream: false
250
+ if (provider === "zai" || provider === "z-ai") {
251
+ const toolStreamEnabled = merged?.tool_stream !== false;
252
+ if (toolStreamEnabled) {
253
+ log.debug(`enabling Z.AI tool_stream for ${provider}/${modelId}`);
254
+ agent.streamFn = createZaiToolStreamWrapper(agent.streamFn, true);
255
+ }
256
+ }
257
+ // Work around upstream pi-ai hardcoding `store: false` for Responses API.
258
+ // Force `store=true` for direct OpenAI/OpenAI Codex providers so multi-turn
259
+ // server-side conversation state is preserved.
260
+ agent.streamFn = createOpenAIResponsesStoreWrapper(agent.streamFn);
70
261
  }