@poolzin/pool-bot 2026.2.21 → 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 (369) hide show
  1. package/CHANGELOG.md +17 -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/diagnostics-otel/package.json +1 -1
  326. package/extensions/discord/package.json +1 -1
  327. package/extensions/feishu/package.json +1 -1
  328. package/extensions/google-antigravity-auth/package.json +1 -1
  329. package/extensions/google-gemini-cli-auth/package.json +1 -1
  330. package/extensions/googlechat/package.json +1 -1
  331. package/extensions/imessage/package.json +1 -1
  332. package/extensions/irc/package.json +1 -1
  333. package/extensions/line/package.json +1 -1
  334. package/extensions/llm-task/package.json +1 -1
  335. package/extensions/lobster/package.json +1 -1
  336. package/extensions/matrix/CHANGELOG.md +5 -0
  337. package/extensions/matrix/package.json +1 -1
  338. package/extensions/mattermost/package.json +1 -1
  339. package/extensions/memory-core/package.json +1 -1
  340. package/extensions/memory-lancedb/package.json +1 -1
  341. package/extensions/minimax-portal-auth/package.json +1 -1
  342. package/extensions/msteams/CHANGELOG.md +5 -0
  343. package/extensions/msteams/package.json +1 -1
  344. package/extensions/nextcloud-talk/package.json +1 -1
  345. package/extensions/nostr/CHANGELOG.md +5 -0
  346. package/extensions/nostr/package.json +1 -1
  347. package/extensions/open-prose/package.json +1 -1
  348. package/extensions/openai-codex-auth/package.json +1 -1
  349. package/extensions/signal/package.json +1 -1
  350. package/extensions/slack/package.json +1 -1
  351. package/extensions/telegram/package.json +1 -1
  352. package/extensions/tlon/package.json +1 -1
  353. package/extensions/twitch/CHANGELOG.md +5 -0
  354. package/extensions/twitch/package.json +1 -1
  355. package/extensions/voice-call/CHANGELOG.md +5 -0
  356. package/extensions/voice-call/package.json +1 -1
  357. package/extensions/whatsapp/package.json +1 -1
  358. package/extensions/zalo/CHANGELOG.md +5 -0
  359. package/extensions/zalo/package.json +1 -1
  360. package/extensions/zalouser/CHANGELOG.md +5 -0
  361. package/extensions/zalouser/package.json +1 -1
  362. package/package.json +1 -1
  363. package/skills/apple-reminders/SKILL.md +100 -49
  364. package/skills/coding-agent/SKILL.md +34 -28
  365. package/skills/github/SKILL.md +131 -16
  366. package/skills/imsg/SKILL.md +112 -15
  367. package/skills/openhue/SKILL.md +101 -19
  368. package/skills/tmux/SKILL.md +111 -79
  369. package/skills/weather/SKILL.md +88 -25
@@ -2,18 +2,18 @@ import { constants as fsConstants } from "node:fs";
2
2
  import fs from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
- import { requireApiKey, resolveApiKeyForProvider } from "../agents/model-auth.js";
5
+ import { resolveApiKeyForProvider } from "../agents/model-auth.js";
6
6
  import { findModelInCatalog, loadModelCatalog, modelSupportsVision, } from "../agents/model-catalog.js";
7
- import { applyTemplate } from "../auto-reply/templating.js";
8
7
  import { logVerbose, shouldLogVerbose } from "../globals.js";
9
8
  import { runExec } from "../process/exec.js";
10
9
  import { MediaAttachmentCache, normalizeAttachments, selectAttachments } from "./attachments.js";
11
- import { AUTO_AUDIO_KEY_PROVIDERS, AUTO_IMAGE_KEY_PROVIDERS, AUTO_VIDEO_KEY_PROVIDERS, CLI_OUTPUT_MAX_BUFFER, DEFAULT_AUDIO_MODELS, DEFAULT_IMAGE_MODELS, DEFAULT_TIMEOUT_SECONDS, } from "./defaults.js";
12
- import { isMediaUnderstandingSkipError, MediaUnderstandingSkipError } from "./errors.js";
13
- import { describeImageWithModel } from "./providers/image.js";
10
+ import { AUTO_AUDIO_KEY_PROVIDERS, AUTO_IMAGE_KEY_PROVIDERS, AUTO_VIDEO_KEY_PROVIDERS, DEFAULT_IMAGE_MODELS, } from "./defaults.js";
11
+ import { isMediaUnderstandingSkipError } from "./errors.js";
12
+ import { fileExists } from "./fs.js";
13
+ import { extractGeminiResponse } from "./output-extract.js";
14
14
  import { buildMediaUnderstandingRegistry, getMediaUnderstandingProvider, normalizeMediaProviderId, } from "./providers/index.js";
15
- import { resolveMaxBytes, resolveMaxChars, resolveModelEntries, resolvePrompt, resolveScopeDecision, resolveTimeoutMs, } from "./resolve.js";
16
- import { estimateBase64Size, resolveVideoMaxBase64Bytes } from "./video.js";
15
+ import { resolveModelEntries, resolveScopeDecision } from "./resolve.js";
16
+ import { buildModelDecision, formatDecisionSummary, runCliEntry, runProviderEntry, } from "./runner.entries.js";
17
17
  export function buildProviderRegistry(overrides) {
18
18
  return buildMediaUnderstandingRegistry(overrides);
19
19
  }
@@ -25,6 +25,10 @@ export function createMediaAttachmentCache(attachments) {
25
25
  }
26
26
  const binaryCache = new Map();
27
27
  const geminiProbeCache = new Map();
28
+ export function clearMediaUnderstandingBinaryCacheForTests() {
29
+ binaryCache.clear();
30
+ geminiProbeCache.clear();
31
+ }
28
32
  function expandHomeDir(value) {
29
33
  if (!value.startsWith("~")) {
30
34
  return value;
@@ -113,85 +117,6 @@ async function findBinary(name) {
113
117
  async function hasBinary(name) {
114
118
  return Boolean(await findBinary(name));
115
119
  }
116
- async function fileExists(filePath) {
117
- if (!filePath) {
118
- return false;
119
- }
120
- try {
121
- await fs.stat(filePath);
122
- return true;
123
- }
124
- catch {
125
- return false;
126
- }
127
- }
128
- function extractLastJsonObject(raw) {
129
- const trimmed = raw.trim();
130
- const start = trimmed.lastIndexOf("{");
131
- if (start === -1) {
132
- return null;
133
- }
134
- const slice = trimmed.slice(start);
135
- try {
136
- return JSON.parse(slice);
137
- }
138
- catch {
139
- return null;
140
- }
141
- }
142
- function extractGeminiResponse(raw) {
143
- const payload = extractLastJsonObject(raw);
144
- if (!payload || typeof payload !== "object") {
145
- return null;
146
- }
147
- const response = payload.response;
148
- if (typeof response !== "string") {
149
- return null;
150
- }
151
- const trimmed = response.trim();
152
- return trimmed || null;
153
- }
154
- function extractSherpaOnnxText(raw) {
155
- const tryParse = (value) => {
156
- const trimmed = value.trim();
157
- if (!trimmed) {
158
- return null;
159
- }
160
- const head = trimmed[0];
161
- if (head !== "{" && head !== '"') {
162
- return null;
163
- }
164
- try {
165
- const parsed = JSON.parse(trimmed);
166
- if (typeof parsed === "string") {
167
- return tryParse(parsed);
168
- }
169
- if (parsed && typeof parsed === "object") {
170
- const text = parsed.text;
171
- if (typeof text === "string" && text.trim()) {
172
- return text.trim();
173
- }
174
- }
175
- }
176
- catch { }
177
- return null;
178
- };
179
- const direct = tryParse(raw);
180
- if (direct) {
181
- return direct;
182
- }
183
- const lines = raw
184
- .split("\n")
185
- .map((line) => line.trim())
186
- .filter(Boolean);
187
- for (let i = lines.length - 1; i >= 0; i -= 1) {
188
- const parsed = tryParse(lines[i] ?? "");
189
- if (parsed) {
190
- return parsed;
191
- }
192
- }
193
- return null;
194
- }
195
120
  async function probeGeminiCli() {
196
121
  const cached = geminiProbeCache.get("gemini");
197
122
  if (cached) {
@@ -388,6 +313,41 @@ async function resolveKeyEntry(params) {
388
313
  }
389
314
  return null;
390
315
  }
316
+ function resolveImageModelFromAgentDefaults(cfg) {
317
+ const imageModel = cfg.agents?.defaults?.imageModel;
318
+ if (!imageModel) {
319
+ return [];
320
+ }
321
+ const refs = [];
322
+ if (typeof imageModel === "string") {
323
+ if (imageModel.trim()) {
324
+ refs.push(imageModel.trim());
325
+ }
326
+ }
327
+ else {
328
+ if (imageModel.primary?.trim()) {
329
+ refs.push(imageModel.primary.trim());
330
+ }
331
+ for (const fb of imageModel.fallbacks ?? []) {
332
+ if (fb?.trim()) {
333
+ refs.push(fb.trim());
334
+ }
335
+ }
336
+ }
337
+ const entries = [];
338
+ for (const ref of refs) {
339
+ const slashIdx = ref.indexOf("/");
340
+ if (slashIdx <= 0 || slashIdx >= ref.length - 1) {
341
+ continue;
342
+ }
343
+ entries.push({
344
+ type: "provider",
345
+ provider: ref.slice(0, slashIdx),
346
+ model: ref.slice(slashIdx + 1),
347
+ });
348
+ }
349
+ return entries;
350
+ }
391
351
  async function resolveAutoEntries(params) {
392
352
  const activeEntry = await resolveActiveModelEntry(params);
393
353
  if (activeEntry) {
@@ -399,6 +359,12 @@ async function resolveAutoEntries(params) {
399
359
  return [localAudio];
400
360
  }
401
361
  }
362
+ if (params.capability === "image") {
363
+ const imageModelEntries = resolveImageModelFromAgentDefaults(params.cfg);
364
+ if (imageModelEntries.length > 0) {
365
+ return imageModelEntries;
366
+ }
367
+ }
402
368
  const gemini = await resolveGeminiCliEntry(params.capability);
403
369
  if (gemini) {
404
370
  return [gemini];
@@ -477,413 +443,12 @@ async function resolveActiveModelEntry(params) {
477
443
  catch {
478
444
  return null;
479
445
  }
480
- // Use the default vision/video model for the provider rather than the
481
- // user's chat model — the chat model (e.g. glm-4.7) may not support
482
- // vision, while the provider has a dedicated model (e.g. glm-4.6v).
483
- const model = params.capability === "image"
484
- ? (DEFAULT_IMAGE_MODELS[providerId] ?? params.activeModel?.model)
485
- : params.capability === "audio"
486
- ? (DEFAULT_AUDIO_MODELS[providerId] ?? params.activeModel?.model)
487
- : params.activeModel?.model;
488
446
  return {
489
447
  type: "provider",
490
448
  provider: providerId,
491
- model,
449
+ model: params.activeModel?.model,
492
450
  };
493
451
  }
494
- function trimOutput(text, maxChars) {
495
- const trimmed = text.trim();
496
- if (!maxChars || trimmed.length <= maxChars) {
497
- return trimmed;
498
- }
499
- return trimmed.slice(0, maxChars).trim();
500
- }
501
- function commandBase(command) {
502
- return path.parse(command).name;
503
- }
504
- function findArgValue(args, keys) {
505
- for (let i = 0; i < args.length; i += 1) {
506
- if (keys.includes(args[i] ?? "")) {
507
- const value = args[i + 1];
508
- if (value) {
509
- return value;
510
- }
511
- }
512
- }
513
- return undefined;
514
- }
515
- function hasArg(args, keys) {
516
- return args.some((arg) => keys.includes(arg));
517
- }
518
- function resolveWhisperOutputPath(args, mediaPath) {
519
- const outputDir = findArgValue(args, ["--output_dir", "-o"]);
520
- const outputFormat = findArgValue(args, ["--output_format"]);
521
- if (!outputDir || !outputFormat) {
522
- return null;
523
- }
524
- const formats = outputFormat.split(",").map((value) => value.trim());
525
- if (!formats.includes("txt")) {
526
- return null;
527
- }
528
- const base = path.parse(mediaPath).name;
529
- return path.join(outputDir, `${base}.txt`);
530
- }
531
- function resolveWhisperCppOutputPath(args) {
532
- if (!hasArg(args, ["-otxt", "--output-txt"])) {
533
- return null;
534
- }
535
- const outputBase = findArgValue(args, ["-of", "--output-file"]);
536
- if (!outputBase) {
537
- return null;
538
- }
539
- return `${outputBase}.txt`;
540
- }
541
- async function resolveCliOutput(params) {
542
- const commandId = commandBase(params.command);
543
- const fileOutput = commandId === "whisper-cli"
544
- ? resolveWhisperCppOutputPath(params.args)
545
- : commandId === "whisper"
546
- ? resolveWhisperOutputPath(params.args, params.mediaPath)
547
- : null;
548
- if (fileOutput && (await fileExists(fileOutput))) {
549
- try {
550
- const content = await fs.readFile(fileOutput, "utf8");
551
- if (content.trim()) {
552
- return content.trim();
553
- }
554
- }
555
- catch { }
556
- }
557
- if (commandId === "gemini") {
558
- const response = extractGeminiResponse(params.stdout);
559
- if (response) {
560
- return response;
561
- }
562
- }
563
- if (commandId === "sherpa-onnx-offline") {
564
- const response = extractSherpaOnnxText(params.stdout);
565
- if (response) {
566
- return response;
567
- }
568
- }
569
- return params.stdout.trim();
570
- }
571
- function normalizeProviderQuery(options) {
572
- if (!options) {
573
- return undefined;
574
- }
575
- const query = {};
576
- for (const [key, value] of Object.entries(options)) {
577
- if (value === undefined) {
578
- continue;
579
- }
580
- query[key] = value;
581
- }
582
- return Object.keys(query).length > 0 ? query : undefined;
583
- }
584
- function buildDeepgramCompatQuery(options) {
585
- if (!options) {
586
- return undefined;
587
- }
588
- const query = {};
589
- if (typeof options.detectLanguage === "boolean") {
590
- query.detect_language = options.detectLanguage;
591
- }
592
- if (typeof options.punctuate === "boolean") {
593
- query.punctuate = options.punctuate;
594
- }
595
- if (typeof options.smartFormat === "boolean") {
596
- query.smart_format = options.smartFormat;
597
- }
598
- return Object.keys(query).length > 0 ? query : undefined;
599
- }
600
- function normalizeDeepgramQueryKeys(query) {
601
- const normalized = { ...query };
602
- if ("detectLanguage" in normalized) {
603
- normalized.detect_language = normalized.detectLanguage;
604
- delete normalized.detectLanguage;
605
- }
606
- if ("smartFormat" in normalized) {
607
- normalized.smart_format = normalized.smartFormat;
608
- delete normalized.smartFormat;
609
- }
610
- return normalized;
611
- }
612
- function resolveProviderQuery(params) {
613
- const { providerId, config, entry } = params;
614
- const mergedOptions = normalizeProviderQuery({
615
- ...config?.providerOptions?.[providerId],
616
- ...entry.providerOptions?.[providerId],
617
- });
618
- if (providerId !== "deepgram") {
619
- return mergedOptions;
620
- }
621
- let query = normalizeDeepgramQueryKeys(mergedOptions ?? {});
622
- const compat = buildDeepgramCompatQuery({ ...config?.deepgram, ...entry.deepgram });
623
- for (const [key, value] of Object.entries(compat ?? {})) {
624
- if (query[key] === undefined) {
625
- query[key] = value;
626
- }
627
- }
628
- return Object.keys(query).length > 0 ? query : undefined;
629
- }
630
- function buildModelDecision(params) {
631
- if (params.entryType === "cli") {
632
- const command = params.entry.command?.trim();
633
- return {
634
- type: "cli",
635
- provider: command ?? "cli",
636
- model: params.entry.model ?? command,
637
- outcome: params.outcome,
638
- reason: params.reason,
639
- };
640
- }
641
- const providerIdRaw = params.entry.provider?.trim();
642
- const providerId = providerIdRaw ? normalizeMediaProviderId(providerIdRaw) : undefined;
643
- return {
644
- type: "provider",
645
- provider: providerId ?? providerIdRaw,
646
- model: params.entry.model,
647
- outcome: params.outcome,
648
- reason: params.reason,
649
- };
650
- }
651
- function formatDecisionSummary(decision) {
652
- const total = decision.attachments.length;
653
- const success = decision.attachments.filter((entry) => entry.chosen?.outcome === "success").length;
654
- const chosen = decision.attachments.find((entry) => entry.chosen)?.chosen;
655
- const provider = chosen?.provider?.trim();
656
- const model = chosen?.model?.trim();
657
- const modelLabel = provider ? (model ? `${provider}/${model}` : provider) : undefined;
658
- const reason = decision.attachments
659
- .flatMap((entry) => entry.attempts.map((attempt) => attempt.reason).filter(Boolean))
660
- .find(Boolean);
661
- const shortReason = reason ? reason.split(":")[0]?.trim() : undefined;
662
- const countLabel = total > 0 ? ` (${success}/${total})` : "";
663
- const viaLabel = modelLabel ? ` via ${modelLabel}` : "";
664
- const reasonLabel = shortReason ? ` reason=${shortReason}` : "";
665
- return `${decision.capability}: ${decision.outcome}${countLabel}${viaLabel}${reasonLabel}`;
666
- }
667
- async function runProviderEntry(params) {
668
- const { entry, capability, cfg } = params;
669
- const providerIdRaw = entry.provider?.trim();
670
- if (!providerIdRaw) {
671
- throw new Error(`Provider entry missing provider for ${capability}`);
672
- }
673
- const providerId = normalizeMediaProviderId(providerIdRaw);
674
- const maxBytes = resolveMaxBytes({ capability, entry, cfg, config: params.config });
675
- const maxChars = resolveMaxChars({ capability, entry, cfg, config: params.config });
676
- const timeoutMs = resolveTimeoutMs(entry.timeoutSeconds ??
677
- params.config?.timeoutSeconds ??
678
- cfg.tools?.media?.[capability]?.timeoutSeconds, DEFAULT_TIMEOUT_SECONDS[capability]);
679
- const prompt = resolvePrompt(capability, entry.prompt ?? params.config?.prompt ?? cfg.tools?.media?.[capability]?.prompt, maxChars);
680
- if (capability === "image") {
681
- if (!params.agentDir) {
682
- throw new Error("Image understanding requires agentDir");
683
- }
684
- const modelId = entry.model?.trim();
685
- if (!modelId) {
686
- throw new Error("Image understanding requires model id");
687
- }
688
- const media = await params.cache.getBuffer({
689
- attachmentIndex: params.attachmentIndex,
690
- maxBytes,
691
- timeoutMs,
692
- });
693
- const provider = getMediaUnderstandingProvider(providerId, params.providerRegistry);
694
- const result = provider?.describeImage
695
- ? await provider.describeImage({
696
- buffer: media.buffer,
697
- fileName: media.fileName,
698
- mime: media.mime,
699
- model: modelId,
700
- provider: providerId,
701
- prompt,
702
- timeoutMs,
703
- profile: entry.profile,
704
- preferredProfile: entry.preferredProfile,
705
- agentDir: params.agentDir,
706
- cfg: params.cfg,
707
- })
708
- : await describeImageWithModel({
709
- buffer: media.buffer,
710
- fileName: media.fileName,
711
- mime: media.mime,
712
- model: modelId,
713
- provider: providerId,
714
- prompt,
715
- timeoutMs,
716
- profile: entry.profile,
717
- preferredProfile: entry.preferredProfile,
718
- agentDir: params.agentDir,
719
- cfg: params.cfg,
720
- });
721
- return {
722
- kind: "image.description",
723
- attachmentIndex: params.attachmentIndex,
724
- text: trimOutput(result.text, maxChars),
725
- provider: providerId,
726
- model: result.model ?? modelId,
727
- };
728
- }
729
- const provider = getMediaUnderstandingProvider(providerId, params.providerRegistry);
730
- if (!provider) {
731
- throw new Error(`Media provider not available: ${providerId}`);
732
- }
733
- if (capability === "audio") {
734
- if (!provider.transcribeAudio) {
735
- throw new Error(`Audio transcription provider "${providerId}" not available.`);
736
- }
737
- const media = await params.cache.getBuffer({
738
- attachmentIndex: params.attachmentIndex,
739
- maxBytes,
740
- timeoutMs,
741
- });
742
- const auth = await resolveApiKeyForProvider({
743
- provider: providerId,
744
- cfg,
745
- profileId: entry.profile,
746
- preferredProfile: entry.preferredProfile,
747
- agentDir: params.agentDir,
748
- });
749
- const apiKey = requireApiKey(auth, providerId);
750
- const providerConfig = cfg.models?.providers?.[providerId];
751
- const baseUrl = entry.baseUrl ?? params.config?.baseUrl ?? providerConfig?.baseUrl;
752
- const mergedHeaders = {
753
- ...providerConfig?.headers,
754
- ...params.config?.headers,
755
- ...entry.headers,
756
- };
757
- const headers = Object.keys(mergedHeaders).length > 0 ? mergedHeaders : undefined;
758
- const providerQuery = resolveProviderQuery({
759
- providerId,
760
- config: params.config,
761
- entry,
762
- });
763
- const model = entry.model?.trim() || DEFAULT_AUDIO_MODELS[providerId] || entry.model;
764
- const result = await provider.transcribeAudio({
765
- buffer: media.buffer,
766
- fileName: media.fileName,
767
- mime: media.mime,
768
- apiKey,
769
- baseUrl,
770
- headers,
771
- model,
772
- language: entry.language ?? params.config?.language ?? cfg.tools?.media?.audio?.language,
773
- prompt,
774
- query: providerQuery,
775
- timeoutMs,
776
- });
777
- return {
778
- kind: "audio.transcription",
779
- attachmentIndex: params.attachmentIndex,
780
- text: trimOutput(result.text, maxChars),
781
- provider: providerId,
782
- model: result.model ?? model,
783
- };
784
- }
785
- if (!provider.describeVideo) {
786
- throw new Error(`Video understanding provider "${providerId}" not available.`);
787
- }
788
- const media = await params.cache.getBuffer({
789
- attachmentIndex: params.attachmentIndex,
790
- maxBytes,
791
- timeoutMs,
792
- });
793
- const estimatedBase64Bytes = estimateBase64Size(media.size);
794
- const maxBase64Bytes = resolveVideoMaxBase64Bytes(maxBytes);
795
- if (estimatedBase64Bytes > maxBase64Bytes) {
796
- throw new MediaUnderstandingSkipError("maxBytes", `Video attachment ${params.attachmentIndex + 1} base64 payload ${estimatedBase64Bytes} exceeds ${maxBase64Bytes}`);
797
- }
798
- const auth = await resolveApiKeyForProvider({
799
- provider: providerId,
800
- cfg,
801
- profileId: entry.profile,
802
- preferredProfile: entry.preferredProfile,
803
- agentDir: params.agentDir,
804
- });
805
- const apiKey = requireApiKey(auth, providerId);
806
- const providerConfig = cfg.models?.providers?.[providerId];
807
- const result = await provider.describeVideo({
808
- buffer: media.buffer,
809
- fileName: media.fileName,
810
- mime: media.mime,
811
- apiKey,
812
- baseUrl: providerConfig?.baseUrl,
813
- headers: providerConfig?.headers,
814
- model: entry.model,
815
- prompt,
816
- timeoutMs,
817
- });
818
- return {
819
- kind: "video.description",
820
- attachmentIndex: params.attachmentIndex,
821
- text: trimOutput(result.text, maxChars),
822
- provider: providerId,
823
- model: result.model ?? entry.model,
824
- };
825
- }
826
- async function runCliEntry(params) {
827
- const { entry, capability, cfg, ctx } = params;
828
- const command = entry.command?.trim();
829
- const args = entry.args ?? [];
830
- if (!command) {
831
- throw new Error(`CLI entry missing command for ${capability}`);
832
- }
833
- const maxBytes = resolveMaxBytes({ capability, entry, cfg, config: params.config });
834
- const maxChars = resolveMaxChars({ capability, entry, cfg, config: params.config });
835
- const timeoutMs = resolveTimeoutMs(entry.timeoutSeconds ??
836
- params.config?.timeoutSeconds ??
837
- cfg.tools?.media?.[capability]?.timeoutSeconds, DEFAULT_TIMEOUT_SECONDS[capability]);
838
- const prompt = resolvePrompt(capability, entry.prompt ?? params.config?.prompt ?? cfg.tools?.media?.[capability]?.prompt, maxChars);
839
- const pathResult = await params.cache.getPath({
840
- attachmentIndex: params.attachmentIndex,
841
- maxBytes,
842
- timeoutMs,
843
- });
844
- const outputDir = await fs.mkdtemp(path.join(os.tmpdir(), "poolbot-media-cli-"));
845
- const mediaPath = pathResult.path;
846
- const outputBase = path.join(outputDir, path.parse(mediaPath).name);
847
- const templCtx = {
848
- ...ctx,
849
- MediaPath: mediaPath,
850
- MediaDir: path.dirname(mediaPath),
851
- OutputDir: outputDir,
852
- OutputBase: outputBase,
853
- Prompt: prompt,
854
- MaxChars: maxChars,
855
- };
856
- const argv = [command, ...args].map((part, index) => index === 0 ? part : applyTemplate(part, templCtx));
857
- try {
858
- if (shouldLogVerbose()) {
859
- logVerbose(`Media understanding via CLI: ${argv.join(" ")}`);
860
- }
861
- const { stdout } = await runExec(argv[0], argv.slice(1), {
862
- timeoutMs,
863
- maxBuffer: CLI_OUTPUT_MAX_BUFFER,
864
- });
865
- const resolved = await resolveCliOutput({
866
- command,
867
- args: argv.slice(1),
868
- stdout,
869
- mediaPath,
870
- });
871
- const text = trimOutput(resolved, maxChars);
872
- if (!text) {
873
- return null;
874
- }
875
- return {
876
- kind: capability === "audio" ? "audio.transcription" : `${capability}.description`,
877
- attachmentIndex: params.attachmentIndex,
878
- text,
879
- provider: "cli",
880
- model: command,
881
- };
882
- }
883
- finally {
884
- await fs.rm(outputDir, { recursive: true, force: true }).catch(() => { });
885
- }
886
- }
887
452
  async function runAttachmentEntries(params) {
888
453
  const { entries, capability } = params;
889
454
  const attempts = [];