@poolzin/pool-bot 2026.2.17 → 2026.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/agents/agent-scope.js +4 -0
  3. package/dist/agents/announce-idempotency.js +14 -0
  4. package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +23 -0
  5. package/dist/agents/bash-tools.exec-runtime.js +438 -0
  6. package/dist/agents/bash-tools.shared.js +6 -0
  7. package/dist/agents/cli-runner/reliability.js +61 -0
  8. package/dist/agents/cli-watchdog-defaults.js +11 -0
  9. package/dist/agents/command-poll-backoff.js +63 -0
  10. package/dist/agents/current-time.js +16 -0
  11. package/dist/agents/model-alias-lines.js +18 -0
  12. package/dist/agents/model-auth-label.js +61 -0
  13. package/dist/agents/models-config.e2e-harness.js +115 -0
  14. package/dist/agents/ollama-stream.js +11 -3
  15. package/dist/agents/openclaw-tools.js +135 -0
  16. package/dist/agents/pi-auth-json.js +118 -0
  17. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +147 -0
  18. package/dist/agents/pi-embedded-subscribe.e2e-harness.js +90 -0
  19. package/dist/agents/pi-embedded-subscribe.handlers.compaction.js +63 -0
  20. package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +30 -0
  21. package/dist/agents/pi-extensions/session-manager-runtime-registry.js +23 -0
  22. package/dist/agents/pi-tools.js +2 -0
  23. package/dist/agents/queued-file-writer.js +22 -0
  24. package/dist/agents/sandbox/docker.js +133 -40
  25. package/dist/agents/sandbox/fs-bridge.js +146 -0
  26. package/dist/agents/sandbox/fs-paths.js +205 -0
  27. package/dist/agents/sandbox/hash.js +4 -0
  28. package/dist/agents/sandbox-paths.js +3 -0
  29. package/dist/agents/session-dirs.js +20 -0
  30. package/dist/agents/skills/filter.js +24 -0
  31. package/dist/agents/skills/tools-dir.js +9 -0
  32. package/dist/agents/skills-install-download.js +290 -0
  33. package/dist/agents/skills-install-output.js +30 -0
  34. package/dist/agents/skills-install.download-test-utils.js +36 -0
  35. package/dist/agents/skills.e2e-test-helpers.js +13 -0
  36. package/dist/agents/subagent-announce-queue.js +59 -15
  37. package/dist/agents/subagent-depth.js +137 -0
  38. package/dist/agents/subagent-registry.js +448 -96
  39. package/dist/agents/subagent-spawn.js +262 -0
  40. package/dist/agents/test-helpers/fast-tool-stubs.js +18 -0
  41. package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +74 -0
  42. package/dist/agents/tool-display-common.js +782 -0
  43. package/dist/agents/tools/image-tool.js +1 -1
  44. package/dist/agents/tools/sessions-access.js +178 -0
  45. package/dist/agents/tools/sessions-resolution.js +206 -0
  46. package/dist/agents/tools/subagents-tool.js +616 -0
  47. package/dist/agents/workspace-dir.js +18 -0
  48. package/dist/agents/workspace-dirs.js +14 -0
  49. package/dist/agents/workspace.js +70 -0
  50. package/dist/auto-reply/heartbeat-reply-payload.js +18 -0
  51. package/dist/auto-reply/reply/commands-export-session.js +163 -0
  52. package/dist/auto-reply/reply/commands-mesh.js +245 -0
  53. package/dist/auto-reply/reply/commands-setunset.js +28 -0
  54. package/dist/auto-reply/reply/commands-slash-parse.js +31 -0
  55. package/dist/auto-reply/reply/commands-system-prompt.js +117 -0
  56. package/dist/auto-reply/reply/directive-handling.levels.js +17 -0
  57. package/dist/auto-reply/reply/directive-handling.params.js +1 -0
  58. package/dist/auto-reply/reply/directive-parsing.js +36 -0
  59. package/dist/auto-reply/reply/dispatcher-registry.js +43 -0
  60. package/dist/auto-reply/reply/elevated-unavailable.js +20 -0
  61. package/dist/auto-reply/reply/reply-delivery.js +92 -0
  62. package/dist/auto-reply/reply/session-reset-prompt.js +1 -0
  63. package/dist/auto-reply/reply/session-run-accounting.js +33 -0
  64. package/dist/auto-reply/reply.directive.directive-behavior.e2e-harness.js +115 -0
  65. package/dist/auto-reply/reply.directive.directive-behavior.e2e-mocks.js +12 -0
  66. package/dist/browser/bridge-auth-registry.js +26 -0
  67. package/dist/browser/client-actions-url.js +10 -0
  68. package/dist/browser/control-auth.js +73 -0
  69. package/dist/browser/csrf.js +64 -0
  70. package/dist/browser/http-auth.js +52 -0
  71. package/dist/browser/paths.js +37 -0
  72. package/dist/browser/proxy-files.js +32 -0
  73. package/dist/browser/pw-ai-state.js +7 -0
  74. package/dist/browser/resolved-config-refresh.js +42 -0
  75. package/dist/browser/routes/path-output.js +1 -0
  76. package/dist/browser/server-context.chrome-test-harness.js +20 -0
  77. package/dist/browser/server-middleware.js +31 -0
  78. package/dist/browser/test-port.js +16 -0
  79. package/dist/build-info.json +3 -3
  80. package/dist/canvas-host/file-resolver.js +43 -0
  81. package/dist/channels/account-summary.js +19 -0
  82. package/dist/channels/draft-stream-loop.js +77 -0
  83. package/dist/channels/plugins/account-helpers.js +26 -0
  84. package/dist/channels/telegram/allow-from.js +10 -0
  85. package/dist/cli/browser-cli-resize.js +22 -0
  86. package/dist/cli/browser-cli-shared.js +8 -0
  87. package/dist/cli/clawbot-cli.js +5 -0
  88. package/dist/cli/completion-cli.js +566 -0
  89. package/dist/cli/config-cli.js +63 -5
  90. package/dist/cli/daemon-cli/lifecycle-core.js +256 -0
  91. package/dist/cli/daemon-cli/register-service-commands.js +60 -0
  92. package/dist/cli/daemon-cli-compat.js +80 -0
  93. package/dist/cli/nodes-cli/pairing-render.js +26 -0
  94. package/dist/cli/program/action-reparse.js +17 -0
  95. package/dist/cli/program/command-registry.js +17 -0
  96. package/dist/cli/program/program-context.js +8 -0
  97. package/dist/cli/program/register.subclis.js +7 -0
  98. package/dist/cli/program/routes.js +233 -0
  99. package/dist/cli/qr-cli.js +132 -0
  100. package/dist/cli/requirements-test-fixtures.js +17 -0
  101. package/dist/cli/respawn-policy.js +4 -0
  102. package/dist/cli/shared/parse-port.js +18 -0
  103. package/dist/cli/skills-cli.format.js +241 -0
  104. package/dist/cli/update-cli/progress.js +121 -0
  105. package/dist/cli/update-cli/restart-helper.js +108 -0
  106. package/dist/cli/update-cli/shared.js +196 -0
  107. package/dist/cli/update-cli/status.js +97 -0
  108. package/dist/cli/update-cli/suppress-deprecations.js +17 -0
  109. package/dist/cli/update-cli/update-command.js +506 -0
  110. package/dist/cli/update-cli/wizard.js +130 -0
  111. package/dist/cli/update-cli.js +3 -9
  112. package/dist/cli/windows-argv.js +69 -0
  113. package/dist/commands/auth-choice-legacy.js +20 -0
  114. package/dist/commands/auth-choice.apply-helpers.js +8 -0
  115. package/dist/commands/channel-test-helpers.js +19 -0
  116. package/dist/commands/cleanup-plan.js +10 -0
  117. package/dist/commands/cleanup-utils.js +7 -0
  118. package/dist/commands/config-validation.js +15 -0
  119. package/dist/commands/doctor-completion.js +112 -0
  120. package/dist/commands/doctor-memory-search.js +119 -0
  121. package/dist/commands/doctor-session-locks.js +73 -0
  122. package/dist/commands/doctor.e2e-harness.js +364 -0
  123. package/dist/commands/gateway-presence.js +19 -0
  124. package/dist/commands/model-default.js +35 -0
  125. package/dist/commands/models/fallbacks-shared.js +102 -0
  126. package/dist/commands/models/shared.js +24 -0
  127. package/dist/commands/onboard-auth.config-gateways.js +64 -0
  128. package/dist/commands/onboard-auth.config-litellm.js +45 -0
  129. package/dist/commands/onboard-auth.config-shared.js +116 -0
  130. package/dist/commands/onboard-config.js +16 -0
  131. package/dist/commands/onboard-non-interactive.test-helpers.js +31 -0
  132. package/dist/commands/onboard-provider-auth-flags.js +136 -0
  133. package/dist/commands/openai-codex-oauth.js +40 -0
  134. package/dist/commands/test-runtime-config-helpers.js +21 -0
  135. package/dist/commands/test-wizard-helpers.js +68 -0
  136. package/dist/commands/vllm-setup.js +66 -0
  137. package/dist/compat/legacy-names.js +2 -0
  138. package/dist/config/backup-rotation.js +19 -0
  139. package/dist/config/env-preserve.js +122 -0
  140. package/dist/config/includes-scan.js +78 -0
  141. package/dist/config/plugins-allowlist.js +13 -0
  142. package/dist/config/schema.help.js +256 -0
  143. package/dist/config/schema.hints.js +189 -0
  144. package/dist/config/schema.irc.js +20 -0
  145. package/dist/config/schema.labels.js +317 -0
  146. package/dist/config/sessions/delivery-info.js +40 -0
  147. package/dist/config/types.irc.js +1 -0
  148. package/dist/config/zod-schema.agent-model.js +10 -0
  149. package/dist/config/zod-schema.allowdeny.js +35 -0
  150. package/dist/config/zod-schema.sensitive.js +4 -0
  151. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  152. package/dist/cron/isolated-agent/skills-snapshot.js +26 -0
  153. package/dist/cron/isolated-agent/subagent-followup.js +127 -0
  154. package/dist/cron/isolated-agent.mocks.js +12 -0
  155. package/dist/cron/isolated-agent.test-setup.js +22 -0
  156. package/dist/cron/legacy-delivery.js +43 -0
  157. package/dist/cron/webhook-url.js +22 -0
  158. package/dist/daemon/arg-split.js +40 -0
  159. package/dist/daemon/exec-file.js +23 -0
  160. package/dist/daemon/output.js +6 -0
  161. package/dist/daemon/runtime-format.js +31 -0
  162. package/dist/daemon/schtasks-exec.js +4 -0
  163. package/dist/daemon/service-audit.js +22 -0
  164. package/dist/discord/client.js +41 -0
  165. package/dist/discord/components-registry.js +57 -0
  166. package/dist/discord/components.js +816 -0
  167. package/dist/discord/guilds.js +12 -0
  168. package/dist/discord/monitor/gateway-plugin.js +48 -0
  169. package/dist/discord/monitor/presence.js +30 -0
  170. package/dist/discord/send.components.js +115 -0
  171. package/dist/discord/send.shared.js +4 -0
  172. package/dist/discord/ui.js +26 -0
  173. package/dist/discord/voice-message.js +254 -0
  174. package/dist/gateway/agent-event-assistant-text.js +5 -0
  175. package/dist/gateway/agent-prompt.js +33 -0
  176. package/dist/gateway/auth-rate-limit.js +136 -0
  177. package/dist/gateway/channel-health-monitor.js +114 -0
  178. package/dist/gateway/control-ui-contract.js +1 -0
  179. package/dist/gateway/control-ui-csp.js +15 -0
  180. package/dist/gateway/gateway-config-prompts.shared.js +25 -0
  181. package/dist/gateway/http-auth-helpers.js +18 -0
  182. package/dist/gateway/http-common.js +18 -0
  183. package/dist/gateway/http-endpoint-helpers.js +27 -0
  184. package/dist/gateway/node-invoke-sanitize.js +11 -0
  185. package/dist/gateway/node-invoke-system-run-approval.js +205 -0
  186. package/dist/gateway/probe-auth.js +21 -0
  187. package/dist/gateway/protocol/index.js +7 -2
  188. package/dist/gateway/protocol/schema/mesh.js +54 -0
  189. package/dist/gateway/protocol/schema/protocol-schemas.js +7 -0
  190. package/dist/gateway/protocol/schema.js +1 -0
  191. package/dist/gateway/server/ws-connection/auth-messages.js +54 -0
  192. package/dist/gateway/server-channels.js +11 -0
  193. package/dist/gateway/server-methods/attachment-normalize.js +16 -0
  194. package/dist/gateway/server-methods/base-hash.js +8 -0
  195. package/dist/gateway/server-methods/mesh.js +700 -0
  196. package/dist/gateway/server-methods/nodes.handlers.invoke-result.js +55 -0
  197. package/dist/gateway/server-methods/restart-request.js +13 -0
  198. package/dist/gateway/server-methods/validation.js +8 -0
  199. package/dist/gateway/server.agent.gateway-server-agent.mocks.js +35 -0
  200. package/dist/gateway/server.e2e-registry-helpers.js +1 -0
  201. package/dist/gateway/server.e2e-ws-harness.js +20 -0
  202. package/dist/gateway/test-helpers.js +2 -0
  203. package/dist/gateway/test-helpers.server.js +3 -1
  204. package/dist/gateway/test-http-response.js +12 -0
  205. package/dist/gateway/test-openai-responses-model.js +20 -0
  206. package/dist/gateway/test-temp-config.js +30 -0
  207. package/dist/gateway/test-with-server.js +32 -0
  208. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +46 -0
  209. package/dist/imessage/monitor/abort-handler.js +23 -0
  210. package/dist/imessage/monitor/inbound-processing.js +346 -0
  211. package/dist/imessage/monitor/parse-notification.js +64 -0
  212. package/dist/imessage/target-parsing-helpers.js +92 -0
  213. package/dist/infra/archive.js +244 -20
  214. package/dist/infra/detect-package-manager.js +26 -0
  215. package/dist/infra/exec-approvals-allowlist.js +257 -0
  216. package/dist/infra/exec-approvals-analysis.js +770 -0
  217. package/dist/infra/exec-approvals.js +13 -0
  218. package/dist/infra/file-lock.js +1 -0
  219. package/dist/infra/gemini-auth.js +39 -0
  220. package/dist/infra/heartbeat-active-hours.js +85 -0
  221. package/dist/infra/heartbeat-events-filter.js +50 -0
  222. package/dist/infra/heartbeat-runner.test-utils.js +39 -0
  223. package/dist/infra/http-body.js +265 -0
  224. package/dist/infra/install-package-dir.js +50 -0
  225. package/dist/infra/install-safe-path.js +49 -0
  226. package/dist/infra/json-files.js +49 -0
  227. package/dist/infra/jsonl-socket.js +52 -0
  228. package/dist/infra/map-size.js +14 -0
  229. package/dist/infra/net/hostname.js +7 -0
  230. package/dist/infra/npm-registry-spec.js +39 -0
  231. package/dist/infra/openclaw-root.js +109 -0
  232. package/dist/infra/outbound/delivery-queue.js +214 -0
  233. package/dist/infra/outbound/identity.js +23 -0
  234. package/dist/infra/outbound/message-action-params.js +307 -0
  235. package/dist/infra/outbound/tool-payload.js +21 -0
  236. package/dist/infra/package-json.js +23 -0
  237. package/dist/infra/pairing-files.js +19 -0
  238. package/dist/infra/pairing-token.js +9 -0
  239. package/dist/infra/path-prepend.js +51 -0
  240. package/dist/infra/process-respawn.js +49 -0
  241. package/dist/infra/runtime-status.js +16 -0
  242. package/dist/infra/session-cost-usage.types.js +1 -0
  243. package/dist/infra/session-maintenance-warning.js +89 -0
  244. package/dist/infra/system-run-command.js +78 -0
  245. package/dist/infra/tmp-openclaw-dir.js +81 -0
  246. package/dist/infra/tmp-poolbot-dir.js +2 -0
  247. package/dist/infra/update-channels.js +19 -0
  248. package/dist/line/actions.js +45 -0
  249. package/dist/line/channel-access-token.js +9 -0
  250. package/dist/line/flex-templates/basic-cards.js +332 -0
  251. package/dist/line/flex-templates/common.js +18 -0
  252. package/dist/line/flex-templates/media-control-cards.js +453 -0
  253. package/dist/line/flex-templates/message.js +10 -0
  254. package/dist/line/flex-templates/schedule-cards.js +399 -0
  255. package/dist/line/flex-templates/types.js +1 -0
  256. package/dist/line/webhook-node.js +100 -0
  257. package/dist/line/webhook-utils.js +11 -0
  258. package/dist/logging/timestamps.js +14 -0
  259. package/dist/markdown/whatsapp.js +62 -0
  260. package/dist/media/base64.js +34 -0
  261. package/dist/media/local-roots.js +32 -0
  262. package/dist/media/outbound-attachment.js +10 -0
  263. package/dist/media/read-response-with-limit.js +41 -0
  264. package/dist/media/sniff-mime-from-base64.js +19 -0
  265. package/dist/media-understanding/audio-preflight.js +67 -0
  266. package/dist/media-understanding/fs.js +13 -0
  267. package/dist/media-understanding/output-extract.js +26 -0
  268. package/dist/media-understanding/providers/audio.test-helpers.js +34 -0
  269. package/dist/media-understanding/providers/google/inline-data.js +64 -0
  270. package/dist/media-understanding/providers/shared.js +7 -0
  271. package/dist/media-understanding/runner.entries.js +459 -0
  272. package/dist/memory/batch-error-utils.js +11 -0
  273. package/dist/memory/batch-http.js +27 -0
  274. package/dist/memory/batch-output.js +29 -0
  275. package/dist/memory/batch-runner.js +22 -0
  276. package/dist/memory/batch-upload.js +23 -0
  277. package/dist/memory/batch-utils.js +26 -0
  278. package/dist/memory/embeddings-debug.js +11 -0
  279. package/dist/memory/embeddings-remote-client.js +22 -0
  280. package/dist/memory/embeddings-remote-fetch.js +14 -0
  281. package/dist/memory/manager-embedding-ops.js +616 -0
  282. package/dist/memory/manager-sync-ops.js +953 -0
  283. package/dist/memory/qmd-manager.js +1061 -0
  284. package/dist/memory/qmd-query-parser.js +107 -0
  285. package/dist/memory/qmd-scope.js +93 -0
  286. package/dist/memory/search-manager.js +0 -1
  287. package/dist/memory/sync-index.js +21 -0
  288. package/dist/memory/sync-progress.js +22 -0
  289. package/dist/memory/sync-stale.js +30 -0
  290. package/dist/memory/test-embeddings-mock.js +16 -0
  291. package/dist/memory/test-manager-helpers.js +14 -0
  292. package/dist/memory/test-runtime-mocks.js +11 -0
  293. package/dist/node-host/invoke-browser.js +177 -0
  294. package/dist/node-host/invoke.js +685 -0
  295. package/dist/pairing/setup-code.js +285 -0
  296. package/dist/plugin-sdk/account-id.js +1 -0
  297. package/dist/plugin-sdk/agent-media-payload.js +13 -0
  298. package/dist/plugin-sdk/allow-from.js +47 -0
  299. package/dist/plugin-sdk/command-auth.js +23 -0
  300. package/dist/plugin-sdk/config-paths.js +9 -0
  301. package/dist/plugin-sdk/file-lock.js +116 -0
  302. package/dist/plugin-sdk/json-store.js +31 -0
  303. package/dist/plugin-sdk/onboarding.js +28 -0
  304. package/dist/plugin-sdk/provider-auth-result.js +29 -0
  305. package/dist/plugin-sdk/slack-message-actions.js +133 -0
  306. package/dist/plugin-sdk/status-helpers.js +35 -0
  307. package/dist/plugin-sdk/text-chunking.js +31 -0
  308. package/dist/plugin-sdk/tool-send.js +12 -0
  309. package/dist/plugin-sdk/webhook-path.js +27 -0
  310. package/dist/plugin-sdk/webhook-targets.js +34 -0
  311. package/dist/plugins/hooks.test-helpers.js +21 -0
  312. package/dist/plugins/uninstall.js +171 -0
  313. package/dist/process/supervisor/adapters/child.js +143 -0
  314. package/dist/process/supervisor/adapters/env.js +13 -0
  315. package/dist/process/supervisor/adapters/pty.js +148 -0
  316. package/dist/process/supervisor/index.js +10 -0
  317. package/dist/process/supervisor/registry.js +117 -0
  318. package/dist/process/supervisor/supervisor.js +244 -0
  319. package/dist/process/supervisor/types.js +1 -0
  320. package/dist/providers/google-shared.test-helpers.js +75 -0
  321. package/dist/security/audit-channel.js +419 -0
  322. package/dist/security/audit-tool-policy.js +1 -0
  323. package/dist/security/scan-paths.js +12 -0
  324. package/dist/sessions/input-provenance.js +55 -0
  325. package/dist/sessions/session-key-utils.js +7 -0
  326. package/dist/shared/chat-content.js +31 -0
  327. package/dist/shared/chat-envelope.js +45 -0
  328. package/dist/shared/config-eval.js +117 -0
  329. package/dist/shared/device-auth.js +16 -0
  330. package/dist/shared/entry-metadata.js +9 -0
  331. package/dist/shared/entry-status.js +25 -0
  332. package/dist/shared/frontmatter.js +98 -0
  333. package/dist/shared/model-param-b.js +19 -0
  334. package/dist/shared/net/ipv4.js +17 -0
  335. package/dist/shared/node-match.js +53 -0
  336. package/dist/shared/requirements.js +128 -0
  337. package/dist/shared/subagents-format.js +84 -0
  338. package/dist/shared/usage-aggregates.js +28 -0
  339. package/dist/signal/monitor/mentions.js +45 -0
  340. package/dist/signal/rpc-context.js +19 -0
  341. package/dist/slack/blocks-fallback.js +76 -0
  342. package/dist/slack/blocks-input.js +40 -0
  343. package/dist/slack/draft-stream.js +106 -0
  344. package/dist/slack/message-actions.js +51 -0
  345. package/dist/slack/modal-metadata.js +32 -0
  346. package/dist/slack/monitor/events/interactions.js +462 -0
  347. package/dist/slack/monitor/room-context.js +17 -0
  348. package/dist/slack/stream-mode.js +41 -0
  349. package/dist/telegram/bot-native-command-menu.js +64 -0
  350. package/dist/telegram/bot.media.e2e-harness.js +81 -0
  351. package/dist/telegram/button-types.js +1 -0
  352. package/dist/telegram/group-access.js +65 -0
  353. package/dist/telegram/outbound-params.js +21 -0
  354. package/dist/telegram/poll-vote-cache.js +21 -0
  355. package/dist/terminal/health-style.js +36 -0
  356. package/dist/test-utils/chunk-test-helpers.js +21 -0
  357. package/dist/test-utils/env.js +72 -0
  358. package/dist/test-utils/exec-assertions.js +12 -0
  359. package/dist/test-utils/imessage-test-plugin.js +54 -0
  360. package/dist/test-utils/mock-http-response.js +17 -0
  361. package/dist/test-utils/vitest-mock-fn.js +1 -0
  362. package/dist/tts/tts-core.js +550 -0
  363. package/dist/utils/chunk-items.js +10 -0
  364. package/dist/utils/reaction-level.js +52 -0
  365. package/dist/utils/safe-json.js +22 -0
  366. package/dist/utils/with-timeout.js +14 -0
  367. package/dist/web/media.js +17 -5
  368. package/dist/whatsapp/resolve-outbound-target.js +42 -0
  369. package/dist/wizard/onboarding.completion.js +74 -0
  370. package/extensions/bluebubbles/src/account-resolve.ts +29 -0
  371. package/extensions/bluebubbles/src/monitor-normalize.ts +796 -0
  372. package/extensions/bluebubbles/src/monitor-processing.ts +1007 -0
  373. package/extensions/bluebubbles/src/monitor-reply-cache.ts +185 -0
  374. package/extensions/bluebubbles/src/monitor-shared.ts +51 -0
  375. package/extensions/bluebubbles/src/multipart.ts +32 -0
  376. package/extensions/bluebubbles/src/send-helpers.ts +53 -0
  377. package/extensions/bluebubbles/src/test-harness.ts +50 -0
  378. package/extensions/bluebubbles/src/test-mocks.ts +11 -0
  379. package/extensions/device-pair/index.ts +554 -0
  380. package/extensions/discord/src/channel.js +366 -0
  381. package/extensions/discord/src/runtime.js +10 -0
  382. package/extensions/feishu/index.ts +63 -0
  383. package/extensions/feishu/src/accounts.ts +114 -0
  384. package/extensions/feishu/src/bitable.ts +739 -0
  385. package/extensions/feishu/src/bot.ts +965 -0
  386. package/extensions/feishu/src/channel.ts +351 -0
  387. package/extensions/feishu/src/client.ts +118 -0
  388. package/extensions/feishu/src/config-schema.ts +206 -0
  389. package/extensions/feishu/src/dedup.ts +33 -0
  390. package/extensions/feishu/src/directory.ts +177 -0
  391. package/extensions/feishu/src/doc-schema.ts +47 -0
  392. package/extensions/feishu/src/docx.ts +536 -0
  393. package/extensions/feishu/src/drive-schema.ts +46 -0
  394. package/extensions/feishu/src/drive.ts +227 -0
  395. package/extensions/feishu/src/dynamic-agent.ts +131 -0
  396. package/extensions/feishu/src/media.ts +449 -0
  397. package/extensions/feishu/src/mention.ts +126 -0
  398. package/extensions/feishu/src/monitor.ts +330 -0
  399. package/extensions/feishu/src/onboarding.ts +359 -0
  400. package/extensions/feishu/src/outbound.ts +55 -0
  401. package/extensions/feishu/src/perm-schema.ts +52 -0
  402. package/extensions/feishu/src/perm.ts +173 -0
  403. package/extensions/feishu/src/policy.ts +84 -0
  404. package/extensions/feishu/src/probe.ts +44 -0
  405. package/extensions/feishu/src/reactions.ts +160 -0
  406. package/extensions/feishu/src/reply-dispatcher.ts +239 -0
  407. package/extensions/feishu/src/runtime.ts +14 -0
  408. package/extensions/feishu/src/send-result.ts +29 -0
  409. package/extensions/feishu/src/send.ts +335 -0
  410. package/extensions/feishu/src/streaming-card.ts +223 -0
  411. package/extensions/feishu/src/targets.ts +78 -0
  412. package/extensions/feishu/src/tools-config.ts +21 -0
  413. package/extensions/feishu/src/types.ts +81 -0
  414. package/extensions/feishu/src/typing.ts +80 -0
  415. package/extensions/feishu/src/wiki-schema.ts +55 -0
  416. package/extensions/feishu/src/wiki.ts +232 -0
  417. package/extensions/imessage/src/channel.js +253 -0
  418. package/extensions/imessage/src/runtime.js +10 -0
  419. package/extensions/irc/index.ts +17 -0
  420. package/extensions/irc/src/accounts.ts +268 -0
  421. package/extensions/irc/src/channel.ts +367 -0
  422. package/extensions/irc/src/client.ts +439 -0
  423. package/extensions/irc/src/config-schema.ts +97 -0
  424. package/extensions/irc/src/connect-options.ts +30 -0
  425. package/extensions/irc/src/control-chars.ts +22 -0
  426. package/extensions/irc/src/inbound.ts +334 -0
  427. package/extensions/irc/src/monitor.ts +147 -0
  428. package/extensions/irc/src/normalize.ts +117 -0
  429. package/extensions/irc/src/onboarding.ts +479 -0
  430. package/extensions/irc/src/policy.ts +157 -0
  431. package/extensions/irc/src/probe.ts +53 -0
  432. package/extensions/irc/src/protocol.ts +169 -0
  433. package/extensions/irc/src/runtime.ts +14 -0
  434. package/extensions/irc/src/send.ts +88 -0
  435. package/extensions/irc/src/types.ts +93 -0
  436. package/extensions/matrix/src/matrix/client-bootstrap.ts +39 -0
  437. package/extensions/mattermost/src/mattermost/monitor-onchar.ts +25 -0
  438. package/extensions/mattermost/src/mattermost/monitor-websocket.ts +221 -0
  439. package/extensions/mattermost/src/mattermost/reactions.ts +130 -0
  440. package/extensions/mattermost/src/mattermost/reconnect.ts +103 -0
  441. package/extensions/minimax-portal-auth/index.ts +161 -0
  442. package/extensions/minimax-portal-auth/oauth.ts +247 -0
  443. package/extensions/msteams/src/file-lock.ts +1 -0
  444. package/extensions/msteams/src/graph.ts +92 -0
  445. package/extensions/msteams/src/mentions.ts +114 -0
  446. package/extensions/msteams/src/test-runtime.ts +16 -0
  447. package/extensions/openai-codex-auth/index.ts +177 -0
  448. package/extensions/phone-control/index.ts +421 -0
  449. package/extensions/shared/resolve-target-test-helpers.ts +66 -0
  450. package/extensions/signal/src/channel.js +273 -0
  451. package/extensions/signal/src/runtime.js +10 -0
  452. package/extensions/slack/src/channel.js +489 -0
  453. package/extensions/slack/src/runtime.js +10 -0
  454. package/extensions/talk-voice/index.ts +150 -0
  455. package/extensions/telegram/src/channel.js +424 -0
  456. package/extensions/telegram/src/runtime.js +10 -0
  457. package/extensions/thread-ownership/index.ts +133 -0
  458. package/extensions/tlon/src/account-fields.ts +25 -0
  459. package/extensions/tlon/src/urbit/base-url.ts +57 -0
  460. package/extensions/tlon/src/urbit/channel-client.ts +157 -0
  461. package/extensions/tlon/src/urbit/channel-ops.ts +164 -0
  462. package/extensions/tlon/src/urbit/context.ts +47 -0
  463. package/extensions/tlon/src/urbit/errors.ts +51 -0
  464. package/extensions/tlon/src/urbit/fetch.ts +39 -0
  465. package/extensions/twitch/src/test-fixtures.ts +30 -0
  466. package/extensions/voice-call/src/allowlist.ts +19 -0
  467. package/extensions/whatsapp/src/channel.js +429 -0
  468. package/extensions/whatsapp/src/runtime.js +10 -0
  469. package/package.json +1 -1
@@ -0,0 +1,459 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { requireApiKey, resolveApiKeyForProvider } from "../agents/model-auth.js";
5
+ import { applyTemplate } from "../auto-reply/templating.js";
6
+ import { logVerbose, shouldLogVerbose } from "../globals.js";
7
+ import { runExec } from "../process/exec.js";
8
+ import { CLI_OUTPUT_MAX_BUFFER, DEFAULT_AUDIO_MODELS, DEFAULT_TIMEOUT_SECONDS, } from "./defaults.js";
9
+ import { MediaUnderstandingSkipError } from "./errors.js";
10
+ import { fileExists } from "./fs.js";
11
+ import { extractGeminiResponse } from "./output-extract.js";
12
+ import { describeImageWithModel } from "./providers/image.js";
13
+ import { getMediaUnderstandingProvider, normalizeMediaProviderId } from "./providers/index.js";
14
+ import { resolveMaxBytes, resolveMaxChars, resolvePrompt, resolveTimeoutMs } from "./resolve.js";
15
+ import { estimateBase64Size, resolveVideoMaxBase64Bytes } from "./video.js";
16
+ function trimOutput(text, maxChars) {
17
+ const trimmed = text.trim();
18
+ if (!maxChars || trimmed.length <= maxChars) {
19
+ return trimmed;
20
+ }
21
+ return trimmed.slice(0, maxChars).trim();
22
+ }
23
+ function extractSherpaOnnxText(raw) {
24
+ const tryParse = (value) => {
25
+ const trimmed = value.trim();
26
+ if (!trimmed) {
27
+ return null;
28
+ }
29
+ const head = trimmed[0];
30
+ if (head !== "{" && head !== '"') {
31
+ return null;
32
+ }
33
+ try {
34
+ const parsed = JSON.parse(trimmed);
35
+ if (typeof parsed === "string") {
36
+ return tryParse(parsed);
37
+ }
38
+ if (parsed && typeof parsed === "object") {
39
+ const text = parsed.text;
40
+ if (typeof text === "string" && text.trim()) {
41
+ return text.trim();
42
+ }
43
+ }
44
+ }
45
+ catch { }
46
+ return null;
47
+ };
48
+ const direct = tryParse(raw);
49
+ if (direct) {
50
+ return direct;
51
+ }
52
+ const lines = raw
53
+ .split("\n")
54
+ .map((line) => line.trim())
55
+ .filter(Boolean);
56
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
57
+ const parsed = tryParse(lines[i] ?? "");
58
+ if (parsed) {
59
+ return parsed;
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+ function commandBase(command) {
65
+ return path.parse(command).name;
66
+ }
67
+ function findArgValue(args, keys) {
68
+ for (let i = 0; i < args.length; i += 1) {
69
+ if (keys.includes(args[i] ?? "")) {
70
+ const value = args[i + 1];
71
+ if (value) {
72
+ return value;
73
+ }
74
+ }
75
+ }
76
+ return undefined;
77
+ }
78
+ function hasArg(args, keys) {
79
+ return args.some((arg) => keys.includes(arg));
80
+ }
81
+ function resolveWhisperOutputPath(args, mediaPath) {
82
+ const outputDir = findArgValue(args, ["--output_dir", "-o"]);
83
+ const outputFormat = findArgValue(args, ["--output_format"]);
84
+ if (!outputDir || !outputFormat) {
85
+ return null;
86
+ }
87
+ const formats = outputFormat.split(",").map((value) => value.trim());
88
+ if (!formats.includes("txt")) {
89
+ return null;
90
+ }
91
+ const base = path.parse(mediaPath).name;
92
+ return path.join(outputDir, `${base}.txt`);
93
+ }
94
+ function resolveWhisperCppOutputPath(args) {
95
+ if (!hasArg(args, ["-otxt", "--output-txt"])) {
96
+ return null;
97
+ }
98
+ const outputBase = findArgValue(args, ["-of", "--output-file"]);
99
+ if (!outputBase) {
100
+ return null;
101
+ }
102
+ return `${outputBase}.txt`;
103
+ }
104
+ async function resolveCliOutput(params) {
105
+ const commandId = commandBase(params.command);
106
+ const fileOutput = commandId === "whisper-cli"
107
+ ? resolveWhisperCppOutputPath(params.args)
108
+ : commandId === "whisper"
109
+ ? resolveWhisperOutputPath(params.args, params.mediaPath)
110
+ : null;
111
+ if (fileOutput && (await fileExists(fileOutput))) {
112
+ try {
113
+ const content = await fs.readFile(fileOutput, "utf8");
114
+ if (content.trim()) {
115
+ return content.trim();
116
+ }
117
+ }
118
+ catch { }
119
+ }
120
+ if (commandId === "gemini") {
121
+ const response = extractGeminiResponse(params.stdout);
122
+ if (response) {
123
+ return response;
124
+ }
125
+ }
126
+ if (commandId === "sherpa-onnx-offline") {
127
+ const response = extractSherpaOnnxText(params.stdout);
128
+ if (response) {
129
+ return response;
130
+ }
131
+ }
132
+ return params.stdout.trim();
133
+ }
134
+ function normalizeProviderQuery(options) {
135
+ if (!options) {
136
+ return undefined;
137
+ }
138
+ const query = {};
139
+ for (const [key, value] of Object.entries(options)) {
140
+ if (value === undefined) {
141
+ continue;
142
+ }
143
+ query[key] = value;
144
+ }
145
+ return Object.keys(query).length > 0 ? query : undefined;
146
+ }
147
+ function buildDeepgramCompatQuery(options) {
148
+ if (!options) {
149
+ return undefined;
150
+ }
151
+ const query = {};
152
+ if (typeof options.detectLanguage === "boolean") {
153
+ query.detect_language = options.detectLanguage;
154
+ }
155
+ if (typeof options.punctuate === "boolean") {
156
+ query.punctuate = options.punctuate;
157
+ }
158
+ if (typeof options.smartFormat === "boolean") {
159
+ query.smart_format = options.smartFormat;
160
+ }
161
+ return Object.keys(query).length > 0 ? query : undefined;
162
+ }
163
+ function normalizeDeepgramQueryKeys(query) {
164
+ const normalized = { ...query };
165
+ if ("detectLanguage" in normalized) {
166
+ normalized.detect_language = normalized.detectLanguage;
167
+ delete normalized.detectLanguage;
168
+ }
169
+ if ("smartFormat" in normalized) {
170
+ normalized.smart_format = normalized.smartFormat;
171
+ delete normalized.smartFormat;
172
+ }
173
+ return normalized;
174
+ }
175
+ function resolveProviderQuery(params) {
176
+ const { providerId, config, entry } = params;
177
+ const mergedOptions = normalizeProviderQuery({
178
+ ...config?.providerOptions?.[providerId],
179
+ ...entry.providerOptions?.[providerId],
180
+ });
181
+ if (providerId !== "deepgram") {
182
+ return mergedOptions;
183
+ }
184
+ const query = normalizeDeepgramQueryKeys(mergedOptions ?? {});
185
+ const compat = buildDeepgramCompatQuery({ ...config?.deepgram, ...entry.deepgram });
186
+ for (const [key, value] of Object.entries(compat ?? {})) {
187
+ if (query[key] === undefined) {
188
+ query[key] = value;
189
+ }
190
+ }
191
+ return Object.keys(query).length > 0 ? query : undefined;
192
+ }
193
+ export function buildModelDecision(params) {
194
+ if (params.entryType === "cli") {
195
+ const command = params.entry.command?.trim();
196
+ return {
197
+ type: "cli",
198
+ provider: command ?? "cli",
199
+ model: params.entry.model ?? command,
200
+ outcome: params.outcome,
201
+ reason: params.reason,
202
+ };
203
+ }
204
+ const providerIdRaw = params.entry.provider?.trim();
205
+ const providerId = providerIdRaw ? normalizeMediaProviderId(providerIdRaw) : undefined;
206
+ return {
207
+ type: "provider",
208
+ provider: providerId ?? providerIdRaw,
209
+ model: params.entry.model,
210
+ outcome: params.outcome,
211
+ reason: params.reason,
212
+ };
213
+ }
214
+ function resolveEntryRunOptions(params) {
215
+ const { capability, entry, cfg } = params;
216
+ const maxBytes = resolveMaxBytes({ capability, entry, cfg, config: params.config });
217
+ const maxChars = resolveMaxChars({ capability, entry, cfg, config: params.config });
218
+ const timeoutMs = resolveTimeoutMs(entry.timeoutSeconds ??
219
+ params.config?.timeoutSeconds ??
220
+ cfg.tools?.media?.[capability]?.timeoutSeconds, DEFAULT_TIMEOUT_SECONDS[capability]);
221
+ const prompt = resolvePrompt(capability, entry.prompt ?? params.config?.prompt ?? cfg.tools?.media?.[capability]?.prompt, maxChars);
222
+ return { maxBytes, maxChars, timeoutMs, prompt };
223
+ }
224
+ export function formatDecisionSummary(decision) {
225
+ const total = decision.attachments.length;
226
+ const success = decision.attachments.filter((entry) => entry.chosen?.outcome === "success").length;
227
+ const chosen = decision.attachments.find((entry) => entry.chosen)?.chosen;
228
+ const provider = chosen?.provider?.trim();
229
+ const model = chosen?.model?.trim();
230
+ const modelLabel = provider ? (model ? `${provider}/${model}` : provider) : undefined;
231
+ const reason = decision.attachments
232
+ .flatMap((entry) => entry.attempts.map((attempt) => attempt.reason).filter(Boolean))
233
+ .find(Boolean);
234
+ const shortReason = reason ? reason.split(":")[0]?.trim() : undefined;
235
+ const countLabel = total > 0 ? ` (${success}/${total})` : "";
236
+ const viaLabel = modelLabel ? ` via ${modelLabel}` : "";
237
+ const reasonLabel = shortReason ? ` reason=${shortReason}` : "";
238
+ return `${decision.capability}: ${decision.outcome}${countLabel}${viaLabel}${reasonLabel}`;
239
+ }
240
+ export async function runProviderEntry(params) {
241
+ const { entry, capability, cfg } = params;
242
+ const providerIdRaw = entry.provider?.trim();
243
+ if (!providerIdRaw) {
244
+ throw new Error(`Provider entry missing provider for ${capability}`);
245
+ }
246
+ const providerId = normalizeMediaProviderId(providerIdRaw);
247
+ const { maxBytes, maxChars, timeoutMs, prompt } = resolveEntryRunOptions({
248
+ capability,
249
+ entry,
250
+ cfg,
251
+ config: params.config,
252
+ });
253
+ if (capability === "image") {
254
+ if (!params.agentDir) {
255
+ throw new Error("Image understanding requires agentDir");
256
+ }
257
+ const modelId = entry.model?.trim();
258
+ if (!modelId) {
259
+ throw new Error("Image understanding requires model id");
260
+ }
261
+ const media = await params.cache.getBuffer({
262
+ attachmentIndex: params.attachmentIndex,
263
+ maxBytes,
264
+ timeoutMs,
265
+ });
266
+ const provider = getMediaUnderstandingProvider(providerId, params.providerRegistry);
267
+ const result = provider?.describeImage
268
+ ? await provider.describeImage({
269
+ buffer: media.buffer,
270
+ fileName: media.fileName,
271
+ mime: media.mime,
272
+ model: modelId,
273
+ provider: providerId,
274
+ prompt,
275
+ timeoutMs,
276
+ profile: entry.profile,
277
+ preferredProfile: entry.preferredProfile,
278
+ agentDir: params.agentDir,
279
+ cfg: params.cfg,
280
+ })
281
+ : await describeImageWithModel({
282
+ buffer: media.buffer,
283
+ fileName: media.fileName,
284
+ mime: media.mime,
285
+ model: modelId,
286
+ provider: providerId,
287
+ prompt,
288
+ timeoutMs,
289
+ profile: entry.profile,
290
+ preferredProfile: entry.preferredProfile,
291
+ agentDir: params.agentDir,
292
+ cfg: params.cfg,
293
+ });
294
+ return {
295
+ kind: "image.description",
296
+ attachmentIndex: params.attachmentIndex,
297
+ text: trimOutput(result.text, maxChars),
298
+ provider: providerId,
299
+ model: result.model ?? modelId,
300
+ };
301
+ }
302
+ const provider = getMediaUnderstandingProvider(providerId, params.providerRegistry);
303
+ if (!provider) {
304
+ throw new Error(`Media provider not available: ${providerId}`);
305
+ }
306
+ if (capability === "audio") {
307
+ if (!provider.transcribeAudio) {
308
+ throw new Error(`Audio transcription provider "${providerId}" not available.`);
309
+ }
310
+ const media = await params.cache.getBuffer({
311
+ attachmentIndex: params.attachmentIndex,
312
+ maxBytes,
313
+ timeoutMs,
314
+ });
315
+ const auth = await resolveApiKeyForProvider({
316
+ provider: providerId,
317
+ cfg,
318
+ profileId: entry.profile,
319
+ preferredProfile: entry.preferredProfile,
320
+ agentDir: params.agentDir,
321
+ });
322
+ const apiKey = requireApiKey(auth, providerId);
323
+ const providerConfig = cfg.models?.providers?.[providerId];
324
+ const baseUrl = entry.baseUrl ?? params.config?.baseUrl ?? providerConfig?.baseUrl;
325
+ const mergedHeaders = {
326
+ ...providerConfig?.headers,
327
+ ...params.config?.headers,
328
+ ...entry.headers,
329
+ };
330
+ const headers = Object.keys(mergedHeaders).length > 0 ? mergedHeaders : undefined;
331
+ const providerQuery = resolveProviderQuery({
332
+ providerId,
333
+ config: params.config,
334
+ entry,
335
+ });
336
+ const model = entry.model?.trim() || DEFAULT_AUDIO_MODELS[providerId] || entry.model;
337
+ const result = await provider.transcribeAudio({
338
+ buffer: media.buffer,
339
+ fileName: media.fileName,
340
+ mime: media.mime,
341
+ apiKey,
342
+ baseUrl,
343
+ headers,
344
+ model,
345
+ language: entry.language ?? params.config?.language ?? cfg.tools?.media?.audio?.language,
346
+ prompt,
347
+ query: providerQuery,
348
+ timeoutMs,
349
+ });
350
+ return {
351
+ kind: "audio.transcription",
352
+ attachmentIndex: params.attachmentIndex,
353
+ text: trimOutput(result.text, maxChars),
354
+ provider: providerId,
355
+ model: result.model ?? model,
356
+ };
357
+ }
358
+ if (!provider.describeVideo) {
359
+ throw new Error(`Video understanding provider "${providerId}" not available.`);
360
+ }
361
+ const media = await params.cache.getBuffer({
362
+ attachmentIndex: params.attachmentIndex,
363
+ maxBytes,
364
+ timeoutMs,
365
+ });
366
+ const estimatedBase64Bytes = estimateBase64Size(media.size);
367
+ const maxBase64Bytes = resolveVideoMaxBase64Bytes(maxBytes);
368
+ if (estimatedBase64Bytes > maxBase64Bytes) {
369
+ throw new MediaUnderstandingSkipError("maxBytes", `Video attachment ${params.attachmentIndex + 1} base64 payload ${estimatedBase64Bytes} exceeds ${maxBase64Bytes}`);
370
+ }
371
+ const auth = await resolveApiKeyForProvider({
372
+ provider: providerId,
373
+ cfg,
374
+ profileId: entry.profile,
375
+ preferredProfile: entry.preferredProfile,
376
+ agentDir: params.agentDir,
377
+ });
378
+ const apiKey = requireApiKey(auth, providerId);
379
+ const providerConfig = cfg.models?.providers?.[providerId];
380
+ const result = await provider.describeVideo({
381
+ buffer: media.buffer,
382
+ fileName: media.fileName,
383
+ mime: media.mime,
384
+ apiKey,
385
+ baseUrl: providerConfig?.baseUrl,
386
+ headers: providerConfig?.headers,
387
+ model: entry.model,
388
+ prompt,
389
+ timeoutMs,
390
+ });
391
+ return {
392
+ kind: "video.description",
393
+ attachmentIndex: params.attachmentIndex,
394
+ text: trimOutput(result.text, maxChars),
395
+ provider: providerId,
396
+ model: result.model ?? entry.model,
397
+ };
398
+ }
399
+ export async function runCliEntry(params) {
400
+ const { entry, capability, cfg, ctx } = params;
401
+ const command = entry.command?.trim();
402
+ const args = entry.args ?? [];
403
+ if (!command) {
404
+ throw new Error(`CLI entry missing command for ${capability}`);
405
+ }
406
+ const { maxBytes, maxChars, timeoutMs, prompt } = resolveEntryRunOptions({
407
+ capability,
408
+ entry,
409
+ cfg,
410
+ config: params.config,
411
+ });
412
+ const pathResult = await params.cache.getPath({
413
+ attachmentIndex: params.attachmentIndex,
414
+ maxBytes,
415
+ timeoutMs,
416
+ });
417
+ const outputDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-media-cli-"));
418
+ const mediaPath = pathResult.path;
419
+ const outputBase = path.join(outputDir, path.parse(mediaPath).name);
420
+ const templCtx = {
421
+ ...ctx,
422
+ MediaPath: mediaPath,
423
+ MediaDir: path.dirname(mediaPath),
424
+ OutputDir: outputDir,
425
+ OutputBase: outputBase,
426
+ Prompt: prompt,
427
+ MaxChars: maxChars,
428
+ };
429
+ const argv = [command, ...args].map((part, index) => index === 0 ? part : applyTemplate(part, templCtx));
430
+ try {
431
+ if (shouldLogVerbose()) {
432
+ logVerbose(`Media understanding via CLI: ${argv.join(" ")}`);
433
+ }
434
+ const { stdout } = await runExec(argv[0], argv.slice(1), {
435
+ timeoutMs,
436
+ maxBuffer: CLI_OUTPUT_MAX_BUFFER,
437
+ });
438
+ const resolved = await resolveCliOutput({
439
+ command,
440
+ args: argv.slice(1),
441
+ stdout,
442
+ mediaPath,
443
+ });
444
+ const text = trimOutput(resolved, maxChars);
445
+ if (!text) {
446
+ return null;
447
+ }
448
+ return {
449
+ kind: capability === "audio" ? "audio.transcription" : `${capability}.description`,
450
+ attachmentIndex: params.attachmentIndex,
451
+ text,
452
+ provider: "cli",
453
+ model: command,
454
+ };
455
+ }
456
+ finally {
457
+ await fs.rm(outputDir, { recursive: true, force: true }).catch(() => { });
458
+ }
459
+ }
@@ -0,0 +1,11 @@
1
+ export function extractBatchErrorMessage(lines) {
2
+ const first = lines.find((line) => line.error?.message || line.response?.body?.error);
3
+ return (first?.error?.message ??
4
+ (typeof first?.response?.body?.error?.message === "string"
5
+ ? first?.response?.body?.error?.message
6
+ : undefined));
7
+ }
8
+ export function formatUnavailableBatchError(err) {
9
+ const message = err instanceof Error ? err.message : String(err);
10
+ return message ? `error file unavailable: ${message}` : undefined;
11
+ }
@@ -0,0 +1,27 @@
1
+ import { retryAsync } from "../infra/retry.js";
2
+ export async function postJsonWithRetry(params) {
3
+ const res = await retryAsync(async () => {
4
+ const res = await fetch(params.url, {
5
+ method: "POST",
6
+ headers: params.headers,
7
+ body: JSON.stringify(params.body),
8
+ });
9
+ if (!res.ok) {
10
+ const text = await res.text();
11
+ const err = new Error(`${params.errorPrefix}: ${res.status} ${text}`);
12
+ err.status = res.status;
13
+ throw err;
14
+ }
15
+ return res;
16
+ }, {
17
+ attempts: 3,
18
+ minDelayMs: 300,
19
+ maxDelayMs: 2000,
20
+ jitter: 0.2,
21
+ shouldRetry: (err) => {
22
+ const status = err.status;
23
+ return status === 429 || (typeof status === "number" && status >= 500);
24
+ },
25
+ });
26
+ return (await res.json());
27
+ }
@@ -0,0 +1,29 @@
1
+ export function applyEmbeddingBatchOutputLine(params) {
2
+ const customId = params.line.custom_id;
3
+ if (!customId) {
4
+ return;
5
+ }
6
+ params.remaining.delete(customId);
7
+ const errorMessage = params.line.error?.message;
8
+ if (errorMessage) {
9
+ params.errors.push(`${customId}: ${errorMessage}`);
10
+ return;
11
+ }
12
+ const response = params.line.response;
13
+ const statusCode = response?.status_code ?? 0;
14
+ if (statusCode >= 400) {
15
+ const messageFromObject = response?.body && typeof response.body === "object"
16
+ ? response.body.error?.message
17
+ : undefined;
18
+ const messageFromString = typeof response?.body === "string" ? response.body : undefined;
19
+ params.errors.push(`${customId}: ${messageFromObject ?? messageFromString ?? "unknown error"}`);
20
+ return;
21
+ }
22
+ const data = response?.body && typeof response.body === "object" ? (response.body.data ?? []) : [];
23
+ const embedding = data[0]?.embedding ?? [];
24
+ if (embedding.length === 0) {
25
+ params.errors.push(`${customId}: empty embedding`);
26
+ return;
27
+ }
28
+ params.byCustomId.set(customId, embedding);
29
+ }
@@ -0,0 +1,22 @@
1
+ import { splitBatchRequests } from "./batch-utils.js";
2
+ import { runWithConcurrency } from "./internal.js";
3
+ export async function runEmbeddingBatchGroups(params) {
4
+ if (params.requests.length === 0) {
5
+ return new Map();
6
+ }
7
+ const groups = splitBatchRequests(params.requests, params.maxRequests);
8
+ const byCustomId = new Map();
9
+ const tasks = groups.map((group, groupIndex) => async () => {
10
+ await params.runGroup({ group, groupIndex, groups: groups.length, byCustomId });
11
+ });
12
+ params.debug?.(params.debugLabel, {
13
+ requests: params.requests.length,
14
+ groups: groups.length,
15
+ wait: params.wait,
16
+ concurrency: params.concurrency,
17
+ pollIntervalMs: params.pollIntervalMs,
18
+ timeoutMs: params.timeoutMs,
19
+ });
20
+ await runWithConcurrency(tasks, params.concurrency);
21
+ return byCustomId;
22
+ }
@@ -0,0 +1,23 @@
1
+ import { buildBatchHeaders, normalizeBatchBaseUrl, } from "./batch-utils.js";
2
+ import { hashText } from "./internal.js";
3
+ export async function uploadBatchJsonlFile(params) {
4
+ const baseUrl = normalizeBatchBaseUrl(params.client);
5
+ const jsonl = params.requests.map((request) => JSON.stringify(request)).join("\n");
6
+ const form = new FormData();
7
+ form.append("purpose", "batch");
8
+ form.append("file", new Blob([jsonl], { type: "application/jsonl" }), `memory-embeddings.${hashText(String(Date.now()))}.jsonl`);
9
+ const fileRes = await fetch(`${baseUrl}/files`, {
10
+ method: "POST",
11
+ headers: buildBatchHeaders(params.client, { json: false }),
12
+ body: form,
13
+ });
14
+ if (!fileRes.ok) {
15
+ const text = await fileRes.text();
16
+ throw new Error(`${params.errorPrefix}: ${fileRes.status} ${text}`);
17
+ }
18
+ const filePayload = (await fileRes.json());
19
+ if (!filePayload.id) {
20
+ throw new Error(`${params.errorPrefix}: missing file id`);
21
+ }
22
+ return filePayload.id;
23
+ }
@@ -0,0 +1,26 @@
1
+ export function normalizeBatchBaseUrl(client) {
2
+ return client.baseUrl?.replace(/\/$/, "") ?? "";
3
+ }
4
+ export function buildBatchHeaders(client, params) {
5
+ const headers = client.headers ? { ...client.headers } : {};
6
+ if (params.json) {
7
+ if (!headers["Content-Type"] && !headers["content-type"]) {
8
+ headers["Content-Type"] = "application/json";
9
+ }
10
+ }
11
+ else {
12
+ delete headers["Content-Type"];
13
+ delete headers["content-type"];
14
+ }
15
+ return headers;
16
+ }
17
+ export function splitBatchRequests(requests, maxRequests) {
18
+ if (requests.length <= maxRequests) {
19
+ return [requests];
20
+ }
21
+ const groups = [];
22
+ for (let i = 0; i < requests.length; i += maxRequests) {
23
+ groups.push(requests.slice(i, i + maxRequests));
24
+ }
25
+ return groups;
26
+ }
@@ -0,0 +1,11 @@
1
+ import { isTruthyEnvValue } from "../infra/env.js";
2
+ import { createSubsystemLogger } from "../logging/subsystem.js";
3
+ const debugEmbeddings = isTruthyEnvValue(process.env.POOLBOT_DEBUG_MEMORY_EMBEDDINGS);
4
+ const log = createSubsystemLogger("memory/embeddings");
5
+ export function debugEmbeddingsLog(message, meta) {
6
+ if (!debugEmbeddings) {
7
+ return;
8
+ }
9
+ const suffix = meta ? ` ${JSON.stringify(meta)}` : "";
10
+ log.raw(`${message}${suffix}`);
11
+ }
@@ -0,0 +1,22 @@
1
+ import { requireApiKey, resolveApiKeyForProvider } from "../agents/model-auth.js";
2
+ export async function resolveRemoteEmbeddingBearerClient(params) {
3
+ const remote = params.options.remote;
4
+ const remoteApiKey = remote?.apiKey?.trim();
5
+ const remoteBaseUrl = remote?.baseUrl?.trim();
6
+ const providerConfig = params.options.config.models?.providers?.[params.provider];
7
+ const apiKey = remoteApiKey
8
+ ? remoteApiKey
9
+ : requireApiKey(await resolveApiKeyForProvider({
10
+ provider: params.provider,
11
+ cfg: params.options.config,
12
+ agentDir: params.options.agentDir,
13
+ }), params.provider);
14
+ const baseUrl = remoteBaseUrl || providerConfig?.baseUrl?.trim() || params.defaultBaseUrl;
15
+ const headerOverrides = Object.assign({}, providerConfig?.headers, remote?.headers);
16
+ const headers = {
17
+ "Content-Type": "application/json",
18
+ Authorization: `Bearer ${apiKey}`,
19
+ ...headerOverrides,
20
+ };
21
+ return { baseUrl, headers };
22
+ }
@@ -0,0 +1,14 @@
1
+ export async function fetchRemoteEmbeddingVectors(params) {
2
+ const res = await fetch(params.url, {
3
+ method: "POST",
4
+ headers: params.headers,
5
+ body: JSON.stringify(params.body),
6
+ });
7
+ if (!res.ok) {
8
+ const text = await res.text();
9
+ throw new Error(`${params.errorPrefix}: ${res.status} ${text}`);
10
+ }
11
+ const payload = (await res.json());
12
+ const data = payload.data ?? [];
13
+ return data.map((entry) => entry.embedding ?? []);
14
+ }