@poolzin/pool-bot 2026.2.11 → 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 (535) hide show
  1. package/CHANGELOG.md +34 -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/usage.js +22 -0
  5. package/dist/agents/auth-profiles.js +1 -1
  6. package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +23 -0
  7. package/dist/agents/bash-tools.exec-runtime.js +438 -0
  8. package/dist/agents/bash-tools.shared.js +6 -0
  9. package/dist/agents/cli-runner/reliability.js +61 -0
  10. package/dist/agents/cli-watchdog-defaults.js +11 -0
  11. package/dist/agents/command-poll-backoff.js +63 -0
  12. package/dist/agents/current-time.js +16 -0
  13. package/dist/agents/glob-pattern.js +42 -0
  14. package/dist/agents/memory-search.js +33 -0
  15. package/dist/agents/model-alias-lines.js +18 -0
  16. package/dist/agents/model-auth-label.js +61 -0
  17. package/dist/agents/model-fallback.js +59 -8
  18. package/dist/agents/models-config.e2e-harness.js +115 -0
  19. package/dist/agents/ollama-stream.js +11 -3
  20. package/dist/agents/openclaw-tools.js +135 -0
  21. package/dist/agents/pi-auth-json.js +118 -0
  22. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +147 -0
  23. package/dist/agents/pi-embedded-subscribe.e2e-harness.js +90 -0
  24. package/dist/agents/pi-embedded-subscribe.handlers.compaction.js +63 -0
  25. package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +30 -0
  26. package/dist/agents/pi-extensions/session-manager-runtime-registry.js +23 -0
  27. package/dist/agents/pi-tools.before-tool-call.js +145 -4
  28. package/dist/agents/pi-tools.js +29 -9
  29. package/dist/agents/pi-tools.policy.js +85 -92
  30. package/dist/agents/pi-tools.schema.js +54 -27
  31. package/dist/agents/queued-file-writer.js +22 -0
  32. package/dist/agents/sandbox/docker.js +133 -40
  33. package/dist/agents/sandbox/fs-bridge.js +146 -0
  34. package/dist/agents/sandbox/fs-paths.js +205 -0
  35. package/dist/agents/sandbox/hash.js +4 -0
  36. package/dist/agents/sandbox/validate-sandbox-security.js +157 -0
  37. package/dist/agents/sandbox-paths.js +3 -0
  38. package/dist/agents/sandbox-tool-policy.js +26 -0
  39. package/dist/agents/sanitize-for-prompt.js +18 -0
  40. package/dist/agents/session-dirs.js +20 -0
  41. package/dist/agents/session-write-lock.js +203 -39
  42. package/dist/agents/skills/filter.js +24 -0
  43. package/dist/agents/skills/tools-dir.js +9 -0
  44. package/dist/agents/skills-install-download.js +290 -0
  45. package/dist/agents/skills-install-output.js +30 -0
  46. package/dist/agents/skills-install.download-test-utils.js +36 -0
  47. package/dist/agents/skills.e2e-test-helpers.js +13 -0
  48. package/dist/agents/subagent-announce-queue.js +59 -15
  49. package/dist/agents/subagent-depth.js +137 -0
  50. package/dist/agents/subagent-registry.js +448 -96
  51. package/dist/agents/subagent-spawn.js +262 -0
  52. package/dist/agents/system-prompt.js +52 -10
  53. package/dist/agents/test-helpers/fast-tool-stubs.js +18 -0
  54. package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +74 -0
  55. package/dist/agents/tool-display-common.js +782 -0
  56. package/dist/agents/tool-loop-detection.js +466 -0
  57. package/dist/agents/tool-policy.js +6 -0
  58. package/dist/agents/tools/image-tool.js +1 -1
  59. package/dist/agents/tools/sessions-access.js +178 -0
  60. package/dist/agents/tools/sessions-resolution.js +206 -0
  61. package/dist/agents/tools/subagents-tool.js +616 -0
  62. package/dist/agents/workspace-dir.js +18 -0
  63. package/dist/agents/workspace-dirs.js +14 -0
  64. package/dist/agents/workspace.js +70 -0
  65. package/dist/auto-reply/heartbeat-reply-payload.js +18 -0
  66. package/dist/auto-reply/reply/commands-export-session.js +163 -0
  67. package/dist/auto-reply/reply/commands-mesh.js +245 -0
  68. package/dist/auto-reply/reply/commands-setunset.js +28 -0
  69. package/dist/auto-reply/reply/commands-slash-parse.js +31 -0
  70. package/dist/auto-reply/reply/commands-system-prompt.js +117 -0
  71. package/dist/auto-reply/reply/directive-handling.levels.js +17 -0
  72. package/dist/auto-reply/reply/directive-handling.params.js +1 -0
  73. package/dist/auto-reply/reply/directive-parsing.js +36 -0
  74. package/dist/auto-reply/reply/dispatcher-registry.js +43 -0
  75. package/dist/auto-reply/reply/elevated-unavailable.js +20 -0
  76. package/dist/auto-reply/reply/post-compaction-audit.js +96 -0
  77. package/dist/auto-reply/reply/post-compaction-context.js +98 -0
  78. package/dist/auto-reply/reply/reply-delivery.js +92 -0
  79. package/dist/auto-reply/reply/session-reset-prompt.js +1 -0
  80. package/dist/auto-reply/reply/session-run-accounting.js +33 -0
  81. package/dist/auto-reply/reply.directive.directive-behavior.e2e-harness.js +115 -0
  82. package/dist/auto-reply/reply.directive.directive-behavior.e2e-mocks.js +12 -0
  83. package/dist/browser/bridge-auth-registry.js +26 -0
  84. package/dist/browser/client-actions-url.js +10 -0
  85. package/dist/browser/control-auth.js +73 -0
  86. package/dist/browser/csrf.js +64 -0
  87. package/dist/browser/http-auth.js +52 -0
  88. package/dist/browser/paths.js +37 -0
  89. package/dist/browser/proxy-files.js +32 -0
  90. package/dist/browser/pw-ai-state.js +7 -0
  91. package/dist/browser/resolved-config-refresh.js +42 -0
  92. package/dist/browser/routes/path-output.js +1 -0
  93. package/dist/browser/server-context.chrome-test-harness.js +20 -0
  94. package/dist/browser/server-middleware.js +31 -0
  95. package/dist/browser/test-port.js +16 -0
  96. package/dist/build-info.json +3 -3
  97. package/dist/canvas-host/file-resolver.js +43 -0
  98. package/dist/channels/account-summary.js +19 -0
  99. package/dist/channels/draft-stream-loop.js +77 -0
  100. package/dist/channels/plugins/account-helpers.js +26 -0
  101. package/dist/channels/telegram/allow-from.js +10 -0
  102. package/dist/cli/browser-cli-resize.js +22 -0
  103. package/dist/cli/browser-cli-shared.js +8 -0
  104. package/dist/cli/clawbot-cli.js +5 -0
  105. package/dist/cli/completion-cli.js +566 -0
  106. package/dist/cli/config-cli.js +63 -5
  107. package/dist/cli/daemon-cli/lifecycle-core.js +256 -0
  108. package/dist/cli/daemon-cli/register-service-commands.js +60 -0
  109. package/dist/cli/daemon-cli-compat.js +80 -0
  110. package/dist/cli/nodes-cli/pairing-render.js +26 -0
  111. package/dist/cli/program/action-reparse.js +17 -0
  112. package/dist/cli/program/command-registry.js +17 -0
  113. package/dist/cli/program/program-context.js +8 -0
  114. package/dist/cli/program/register.subclis.js +7 -0
  115. package/dist/cli/program/routes.js +233 -0
  116. package/dist/cli/qr-cli.js +132 -0
  117. package/dist/cli/requirements-test-fixtures.js +17 -0
  118. package/dist/cli/respawn-policy.js +4 -0
  119. package/dist/cli/shared/parse-port.js +18 -0
  120. package/dist/cli/skills-cli.format.js +241 -0
  121. package/dist/cli/update-cli/progress.js +121 -0
  122. package/dist/cli/update-cli/restart-helper.js +108 -0
  123. package/dist/cli/update-cli/shared.js +196 -0
  124. package/dist/cli/update-cli/status.js +97 -0
  125. package/dist/cli/update-cli/suppress-deprecations.js +17 -0
  126. package/dist/cli/update-cli/update-command.js +506 -0
  127. package/dist/cli/update-cli/wizard.js +130 -0
  128. package/dist/cli/update-cli.js +3 -9
  129. package/dist/cli/windows-argv.js +69 -0
  130. package/dist/commands/auth-choice-legacy.js +20 -0
  131. package/dist/commands/auth-choice.apply-helpers.js +8 -0
  132. package/dist/commands/channel-test-helpers.js +19 -0
  133. package/dist/commands/cleanup-plan.js +10 -0
  134. package/dist/commands/cleanup-utils.js +7 -0
  135. package/dist/commands/config-validation.js +15 -0
  136. package/dist/commands/doctor-completion.js +112 -0
  137. package/dist/commands/doctor-memory-search.js +119 -0
  138. package/dist/commands/doctor-session-locks.js +73 -0
  139. package/dist/commands/doctor.e2e-harness.js +364 -0
  140. package/dist/commands/gateway-presence.js +19 -0
  141. package/dist/commands/model-default.js +35 -0
  142. package/dist/commands/models/fallbacks-shared.js +102 -0
  143. package/dist/commands/models/shared.js +24 -0
  144. package/dist/commands/onboard-auth.config-gateways.js +64 -0
  145. package/dist/commands/onboard-auth.config-litellm.js +45 -0
  146. package/dist/commands/onboard-auth.config-shared.js +116 -0
  147. package/dist/commands/onboard-config.js +16 -0
  148. package/dist/commands/onboard-non-interactive.test-helpers.js +31 -0
  149. package/dist/commands/onboard-provider-auth-flags.js +136 -0
  150. package/dist/commands/openai-codex-oauth.js +40 -0
  151. package/dist/commands/test-runtime-config-helpers.js +21 -0
  152. package/dist/commands/test-wizard-helpers.js +68 -0
  153. package/dist/commands/vllm-setup.js +66 -0
  154. package/dist/compat/legacy-names.js +2 -0
  155. package/dist/config/backup-rotation.js +19 -0
  156. package/dist/config/env-preserve.js +122 -0
  157. package/dist/config/includes-scan.js +78 -0
  158. package/dist/config/plugins-allowlist.js +13 -0
  159. package/dist/config/schema.help.js +256 -0
  160. package/dist/config/schema.hints.js +189 -0
  161. package/dist/config/schema.irc.js +20 -0
  162. package/dist/config/schema.labels.js +317 -0
  163. package/dist/config/sessions/delivery-info.js +40 -0
  164. package/dist/config/types.irc.js +1 -0
  165. package/dist/config/zod-schema.agent-defaults.js +14 -0
  166. package/dist/config/zod-schema.agent-model.js +10 -0
  167. package/dist/config/zod-schema.agent-runtime.js +14 -0
  168. package/dist/config/zod-schema.allowdeny.js +35 -0
  169. package/dist/config/zod-schema.sensitive.js +4 -0
  170. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  171. package/dist/cron/isolated-agent/skills-snapshot.js +26 -0
  172. package/dist/cron/isolated-agent/subagent-followup.js +127 -0
  173. package/dist/cron/isolated-agent.mocks.js +12 -0
  174. package/dist/cron/isolated-agent.test-setup.js +22 -0
  175. package/dist/cron/legacy-delivery.js +43 -0
  176. package/dist/cron/webhook-url.js +22 -0
  177. package/dist/daemon/arg-split.js +40 -0
  178. package/dist/daemon/exec-file.js +23 -0
  179. package/dist/daemon/output.js +6 -0
  180. package/dist/daemon/runtime-format.js +31 -0
  181. package/dist/daemon/schtasks-exec.js +4 -0
  182. package/dist/daemon/service-audit.js +22 -0
  183. package/dist/discord/client.js +41 -0
  184. package/dist/discord/components-registry.js +57 -0
  185. package/dist/discord/components.js +816 -0
  186. package/dist/discord/guilds.js +12 -0
  187. package/dist/discord/monitor/gateway-plugin.js +48 -0
  188. package/dist/discord/monitor/presence.js +30 -0
  189. package/dist/discord/send.components.js +115 -0
  190. package/dist/discord/send.shared.js +4 -0
  191. package/dist/discord/ui.js +26 -0
  192. package/dist/discord/voice-message.js +254 -0
  193. package/dist/gateway/agent-event-assistant-text.js +5 -0
  194. package/dist/gateway/agent-prompt.js +33 -0
  195. package/dist/gateway/auth-rate-limit.js +136 -0
  196. package/dist/gateway/channel-health-monitor.js +114 -0
  197. package/dist/gateway/control-ui-contract.js +1 -0
  198. package/dist/gateway/control-ui-csp.js +15 -0
  199. package/dist/gateway/gateway-config-prompts.shared.js +25 -0
  200. package/dist/gateway/http-auth-helpers.js +18 -0
  201. package/dist/gateway/http-common.js +18 -0
  202. package/dist/gateway/http-endpoint-helpers.js +27 -0
  203. package/dist/gateway/node-invoke-sanitize.js +11 -0
  204. package/dist/gateway/node-invoke-system-run-approval.js +205 -0
  205. package/dist/gateway/probe-auth.js +21 -0
  206. package/dist/gateway/protocol/index.js +7 -2
  207. package/dist/gateway/protocol/schema/mesh.js +54 -0
  208. package/dist/gateway/protocol/schema/protocol-schemas.js +7 -0
  209. package/dist/gateway/protocol/schema.js +1 -0
  210. package/dist/gateway/server/ws-connection/auth-messages.js +54 -0
  211. package/dist/gateway/server-channels.js +11 -0
  212. package/dist/gateway/server-methods/attachment-normalize.js +16 -0
  213. package/dist/gateway/server-methods/base-hash.js +8 -0
  214. package/dist/gateway/server-methods/mesh.js +700 -0
  215. package/dist/gateway/server-methods/nodes.handlers.invoke-result.js +55 -0
  216. package/dist/gateway/server-methods/restart-request.js +13 -0
  217. package/dist/gateway/server-methods/validation.js +8 -0
  218. package/dist/gateway/server.agent.gateway-server-agent.mocks.js +35 -0
  219. package/dist/gateway/server.e2e-registry-helpers.js +1 -0
  220. package/dist/gateway/server.e2e-ws-harness.js +20 -0
  221. package/dist/gateway/test-helpers.js +2 -0
  222. package/dist/gateway/test-helpers.server.js +3 -1
  223. package/dist/gateway/test-http-response.js +12 -0
  224. package/dist/gateway/test-openai-responses-model.js +20 -0
  225. package/dist/gateway/test-temp-config.js +30 -0
  226. package/dist/gateway/test-with-server.js +32 -0
  227. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +46 -0
  228. package/dist/imessage/monitor/abort-handler.js +23 -0
  229. package/dist/imessage/monitor/inbound-processing.js +346 -0
  230. package/dist/imessage/monitor/parse-notification.js +64 -0
  231. package/dist/imessage/target-parsing-helpers.js +92 -0
  232. package/dist/infra/archive.js +244 -20
  233. package/dist/infra/detect-package-manager.js +26 -0
  234. package/dist/infra/exec-approvals-allowlist.js +257 -0
  235. package/dist/infra/exec-approvals-analysis.js +770 -0
  236. package/dist/infra/exec-approvals.js +13 -0
  237. package/dist/infra/file-lock.js +1 -0
  238. package/dist/infra/gemini-auth.js +39 -0
  239. package/dist/infra/heartbeat-active-hours.js +85 -0
  240. package/dist/infra/heartbeat-events-filter.js +50 -0
  241. package/dist/infra/heartbeat-runner.test-utils.js +39 -0
  242. package/dist/infra/http-body.js +265 -0
  243. package/dist/infra/install-package-dir.js +50 -0
  244. package/dist/infra/install-safe-path.js +49 -0
  245. package/dist/infra/json-files.js +49 -0
  246. package/dist/infra/jsonl-socket.js +52 -0
  247. package/dist/infra/map-size.js +14 -0
  248. package/dist/infra/net/hostname.js +7 -0
  249. package/dist/infra/npm-registry-spec.js +39 -0
  250. package/dist/infra/openclaw-root.js +109 -0
  251. package/dist/infra/outbound/delivery-queue.js +214 -0
  252. package/dist/infra/outbound/identity.js +23 -0
  253. package/dist/infra/outbound/message-action-params.js +307 -0
  254. package/dist/infra/outbound/tool-payload.js +21 -0
  255. package/dist/infra/package-json.js +23 -0
  256. package/dist/infra/pairing-files.js +19 -0
  257. package/dist/infra/pairing-token.js +9 -0
  258. package/dist/infra/path-prepend.js +51 -0
  259. package/dist/infra/path-safety.js +16 -0
  260. package/dist/infra/process-respawn.js +49 -0
  261. package/dist/infra/runtime-status.js +16 -0
  262. package/dist/infra/session-cost-usage.types.js +1 -0
  263. package/dist/infra/session-maintenance-warning.js +89 -0
  264. package/dist/infra/system-run-command.js +78 -0
  265. package/dist/infra/tmp-openclaw-dir.js +81 -0
  266. package/dist/infra/tmp-poolbot-dir.js +2 -0
  267. package/dist/infra/update-channels.js +19 -0
  268. package/dist/line/actions.js +45 -0
  269. package/dist/line/channel-access-token.js +9 -0
  270. package/dist/line/flex-templates/basic-cards.js +332 -0
  271. package/dist/line/flex-templates/common.js +18 -0
  272. package/dist/line/flex-templates/media-control-cards.js +453 -0
  273. package/dist/line/flex-templates/message.js +10 -0
  274. package/dist/line/flex-templates/schedule-cards.js +399 -0
  275. package/dist/line/flex-templates/types.js +1 -0
  276. package/dist/line/webhook-node.js +100 -0
  277. package/dist/line/webhook-utils.js +11 -0
  278. package/dist/logging/diagnostic-session-state.js +73 -0
  279. package/dist/logging/diagnostic.js +22 -0
  280. package/dist/logging/timestamps.js +14 -0
  281. package/dist/markdown/whatsapp.js +62 -0
  282. package/dist/media/base64.js +34 -0
  283. package/dist/media/local-roots.js +32 -0
  284. package/dist/media/outbound-attachment.js +10 -0
  285. package/dist/media/read-response-with-limit.js +41 -0
  286. package/dist/media/sniff-mime-from-base64.js +19 -0
  287. package/dist/media-understanding/audio-preflight.js +67 -0
  288. package/dist/media-understanding/fs.js +13 -0
  289. package/dist/media-understanding/output-extract.js +26 -0
  290. package/dist/media-understanding/providers/audio.test-helpers.js +34 -0
  291. package/dist/media-understanding/providers/google/inline-data.js +64 -0
  292. package/dist/media-understanding/providers/shared.js +7 -0
  293. package/dist/media-understanding/runner.entries.js +459 -0
  294. package/dist/memory/batch-error-utils.js +11 -0
  295. package/dist/memory/batch-http.js +27 -0
  296. package/dist/memory/batch-output.js +29 -0
  297. package/dist/memory/batch-runner.js +22 -0
  298. package/dist/memory/batch-upload.js +23 -0
  299. package/dist/memory/batch-utils.js +26 -0
  300. package/dist/memory/embeddings-debug.js +11 -0
  301. package/dist/memory/embeddings-remote-client.js +22 -0
  302. package/dist/memory/embeddings-remote-fetch.js +14 -0
  303. package/dist/memory/embeddings.js +36 -9
  304. package/dist/memory/hybrid.js +24 -5
  305. package/dist/memory/manager-embedding-ops.js +616 -0
  306. package/dist/memory/manager-sync-ops.js +953 -0
  307. package/dist/memory/manager.js +76 -28
  308. package/dist/memory/mmr.js +164 -0
  309. package/dist/memory/qmd-manager.js +1061 -0
  310. package/dist/memory/qmd-query-parser.js +107 -0
  311. package/dist/memory/qmd-scope.js +93 -0
  312. package/dist/memory/query-expansion.js +331 -0
  313. package/dist/memory/search-manager.js +0 -1
  314. package/dist/memory/sync-index.js +21 -0
  315. package/dist/memory/sync-progress.js +22 -0
  316. package/dist/memory/sync-stale.js +30 -0
  317. package/dist/memory/temporal-decay.js +119 -0
  318. package/dist/memory/test-embeddings-mock.js +16 -0
  319. package/dist/memory/test-manager-helpers.js +14 -0
  320. package/dist/memory/test-runtime-mocks.js +11 -0
  321. package/dist/node-host/invoke-browser.js +177 -0
  322. package/dist/node-host/invoke.js +685 -0
  323. package/dist/pairing/setup-code.js +285 -0
  324. package/dist/plugin-sdk/account-id.js +1 -0
  325. package/dist/plugin-sdk/agent-media-payload.js +13 -0
  326. package/dist/plugin-sdk/allow-from.js +47 -0
  327. package/dist/plugin-sdk/command-auth.js +23 -0
  328. package/dist/plugin-sdk/config-paths.js +9 -0
  329. package/dist/plugin-sdk/file-lock.js +116 -0
  330. package/dist/plugin-sdk/json-store.js +31 -0
  331. package/dist/plugin-sdk/onboarding.js +28 -0
  332. package/dist/plugin-sdk/provider-auth-result.js +29 -0
  333. package/dist/plugin-sdk/slack-message-actions.js +133 -0
  334. package/dist/plugin-sdk/status-helpers.js +35 -0
  335. package/dist/plugin-sdk/text-chunking.js +31 -0
  336. package/dist/plugin-sdk/tool-send.js +12 -0
  337. package/dist/plugin-sdk/webhook-path.js +27 -0
  338. package/dist/plugin-sdk/webhook-targets.js +34 -0
  339. package/dist/plugins/hooks.test-helpers.js +21 -0
  340. package/dist/plugins/uninstall.js +171 -0
  341. package/dist/process/kill-tree.js +98 -0
  342. package/dist/process/supervisor/adapters/child.js +143 -0
  343. package/dist/process/supervisor/adapters/env.js +13 -0
  344. package/dist/process/supervisor/adapters/pty.js +148 -0
  345. package/dist/process/supervisor/index.js +10 -0
  346. package/dist/process/supervisor/registry.js +117 -0
  347. package/dist/process/supervisor/supervisor.js +244 -0
  348. package/dist/process/supervisor/types.js +1 -0
  349. package/dist/providers/google-shared.test-helpers.js +75 -0
  350. package/dist/security/audit-channel.js +419 -0
  351. package/dist/security/audit-tool-policy.js +1 -0
  352. package/dist/security/scan-paths.js +12 -0
  353. package/dist/sessions/input-provenance.js +55 -0
  354. package/dist/sessions/session-key-utils.js +7 -0
  355. package/dist/shared/chat-content.js +31 -0
  356. package/dist/shared/chat-envelope.js +45 -0
  357. package/dist/shared/config-eval.js +117 -0
  358. package/dist/shared/device-auth.js +16 -0
  359. package/dist/shared/entry-metadata.js +9 -0
  360. package/dist/shared/entry-status.js +25 -0
  361. package/dist/shared/frontmatter.js +98 -0
  362. package/dist/shared/model-param-b.js +19 -0
  363. package/dist/shared/net/ipv4.js +17 -0
  364. package/dist/shared/node-match.js +53 -0
  365. package/dist/shared/pid-alive.js +12 -0
  366. package/dist/shared/process-scoped-map.js +10 -0
  367. package/dist/shared/requirements.js +128 -0
  368. package/dist/shared/subagents-format.js +84 -0
  369. package/dist/shared/usage-aggregates.js +28 -0
  370. package/dist/signal/monitor/mentions.js +45 -0
  371. package/dist/signal/rpc-context.js +19 -0
  372. package/dist/slack/blocks-fallback.js +76 -0
  373. package/dist/slack/blocks-input.js +40 -0
  374. package/dist/slack/draft-stream.js +106 -0
  375. package/dist/slack/message-actions.js +51 -0
  376. package/dist/slack/modal-metadata.js +32 -0
  377. package/dist/slack/monitor/events/interactions.js +462 -0
  378. package/dist/slack/monitor/room-context.js +17 -0
  379. package/dist/slack/stream-mode.js +41 -0
  380. package/dist/telegram/bot-native-command-menu.js +64 -0
  381. package/dist/telegram/bot.media.e2e-harness.js +81 -0
  382. package/dist/telegram/button-types.js +1 -0
  383. package/dist/telegram/group-access.js +65 -0
  384. package/dist/telegram/outbound-params.js +21 -0
  385. package/dist/telegram/poll-vote-cache.js +21 -0
  386. package/dist/terminal/health-style.js +36 -0
  387. package/dist/test-utils/chunk-test-helpers.js +21 -0
  388. package/dist/test-utils/env.js +72 -0
  389. package/dist/test-utils/exec-assertions.js +12 -0
  390. package/dist/test-utils/imessage-test-plugin.js +54 -0
  391. package/dist/test-utils/mock-http-response.js +17 -0
  392. package/dist/test-utils/vitest-mock-fn.js +1 -0
  393. package/dist/tts/tts-core.js +550 -0
  394. package/dist/utils/chunk-items.js +10 -0
  395. package/dist/utils/reaction-level.js +52 -0
  396. package/dist/utils/safe-json.js +22 -0
  397. package/dist/utils/with-timeout.js +14 -0
  398. package/dist/web/media.js +17 -5
  399. package/dist/whatsapp/resolve-outbound-target.js +42 -0
  400. package/dist/wizard/onboarding.completion.js +74 -0
  401. package/extensions/bluebubbles/package.json +1 -1
  402. package/extensions/bluebubbles/src/account-resolve.ts +29 -0
  403. package/extensions/bluebubbles/src/monitor-normalize.ts +796 -0
  404. package/extensions/bluebubbles/src/monitor-processing.ts +1007 -0
  405. package/extensions/bluebubbles/src/monitor-reply-cache.ts +185 -0
  406. package/extensions/bluebubbles/src/monitor-shared.ts +51 -0
  407. package/extensions/bluebubbles/src/multipart.ts +32 -0
  408. package/extensions/bluebubbles/src/send-helpers.ts +53 -0
  409. package/extensions/bluebubbles/src/test-harness.ts +50 -0
  410. package/extensions/bluebubbles/src/test-mocks.ts +11 -0
  411. package/extensions/copilot-proxy/package.json +1 -1
  412. package/extensions/device-pair/index.ts +554 -0
  413. package/extensions/diagnostics-otel/package.json +1 -1
  414. package/extensions/discord/package.json +1 -1
  415. package/extensions/discord/src/channel.js +366 -0
  416. package/extensions/discord/src/runtime.js +10 -0
  417. package/extensions/feishu/index.ts +63 -0
  418. package/extensions/feishu/src/accounts.ts +114 -0
  419. package/extensions/feishu/src/bitable.ts +739 -0
  420. package/extensions/feishu/src/bot.ts +965 -0
  421. package/extensions/feishu/src/channel.ts +351 -0
  422. package/extensions/feishu/src/client.ts +118 -0
  423. package/extensions/feishu/src/config-schema.ts +206 -0
  424. package/extensions/feishu/src/dedup.ts +33 -0
  425. package/extensions/feishu/src/directory.ts +177 -0
  426. package/extensions/feishu/src/doc-schema.ts +47 -0
  427. package/extensions/feishu/src/docx.ts +536 -0
  428. package/extensions/feishu/src/drive-schema.ts +46 -0
  429. package/extensions/feishu/src/drive.ts +227 -0
  430. package/extensions/feishu/src/dynamic-agent.ts +131 -0
  431. package/extensions/feishu/src/media.ts +449 -0
  432. package/extensions/feishu/src/mention.ts +126 -0
  433. package/extensions/feishu/src/monitor.ts +330 -0
  434. package/extensions/feishu/src/onboarding.ts +359 -0
  435. package/extensions/feishu/src/outbound.ts +55 -0
  436. package/extensions/feishu/src/perm-schema.ts +52 -0
  437. package/extensions/feishu/src/perm.ts +173 -0
  438. package/extensions/feishu/src/policy.ts +84 -0
  439. package/extensions/feishu/src/probe.ts +44 -0
  440. package/extensions/feishu/src/reactions.ts +160 -0
  441. package/extensions/feishu/src/reply-dispatcher.ts +239 -0
  442. package/extensions/feishu/src/runtime.ts +14 -0
  443. package/extensions/feishu/src/send-result.ts +29 -0
  444. package/extensions/feishu/src/send.ts +335 -0
  445. package/extensions/feishu/src/streaming-card.ts +223 -0
  446. package/extensions/feishu/src/targets.ts +78 -0
  447. package/extensions/feishu/src/tools-config.ts +21 -0
  448. package/extensions/feishu/src/types.ts +81 -0
  449. package/extensions/feishu/src/typing.ts +80 -0
  450. package/extensions/feishu/src/wiki-schema.ts +55 -0
  451. package/extensions/feishu/src/wiki.ts +232 -0
  452. package/extensions/google-antigravity-auth/package.json +1 -1
  453. package/extensions/google-gemini-cli-auth/package.json +1 -1
  454. package/extensions/googlechat/package.json +1 -1
  455. package/extensions/imessage/package.json +1 -1
  456. package/extensions/imessage/src/channel.js +253 -0
  457. package/extensions/imessage/src/runtime.js +10 -0
  458. package/extensions/irc/index.ts +17 -0
  459. package/extensions/irc/src/accounts.ts +268 -0
  460. package/extensions/irc/src/channel.ts +367 -0
  461. package/extensions/irc/src/client.ts +439 -0
  462. package/extensions/irc/src/config-schema.ts +97 -0
  463. package/extensions/irc/src/connect-options.ts +30 -0
  464. package/extensions/irc/src/control-chars.ts +22 -0
  465. package/extensions/irc/src/inbound.ts +334 -0
  466. package/extensions/irc/src/monitor.ts +147 -0
  467. package/extensions/irc/src/normalize.ts +117 -0
  468. package/extensions/irc/src/onboarding.ts +479 -0
  469. package/extensions/irc/src/policy.ts +157 -0
  470. package/extensions/irc/src/probe.ts +53 -0
  471. package/extensions/irc/src/protocol.ts +169 -0
  472. package/extensions/irc/src/runtime.ts +14 -0
  473. package/extensions/irc/src/send.ts +88 -0
  474. package/extensions/irc/src/types.ts +93 -0
  475. package/extensions/line/package.json +1 -1
  476. package/extensions/llm-task/package.json +1 -1
  477. package/extensions/lobster/package.json +1 -1
  478. package/extensions/matrix/CHANGELOG.md +5 -0
  479. package/extensions/matrix/package.json +1 -1
  480. package/extensions/matrix/src/matrix/client-bootstrap.ts +39 -0
  481. package/extensions/mattermost/package.json +1 -1
  482. package/extensions/mattermost/src/mattermost/monitor-onchar.ts +25 -0
  483. package/extensions/mattermost/src/mattermost/monitor-websocket.ts +221 -0
  484. package/extensions/mattermost/src/mattermost/reactions.ts +130 -0
  485. package/extensions/mattermost/src/mattermost/reconnect.ts +103 -0
  486. package/extensions/memory-core/package.json +1 -1
  487. package/extensions/memory-lancedb/package.json +1 -1
  488. package/extensions/minimax-portal-auth/index.ts +161 -0
  489. package/extensions/minimax-portal-auth/oauth.ts +247 -0
  490. package/extensions/msteams/CHANGELOG.md +5 -0
  491. package/extensions/msteams/package.json +1 -1
  492. package/extensions/msteams/src/file-lock.ts +1 -0
  493. package/extensions/msteams/src/graph.ts +92 -0
  494. package/extensions/msteams/src/mentions.ts +114 -0
  495. package/extensions/msteams/src/test-runtime.ts +16 -0
  496. package/extensions/nextcloud-talk/package.json +1 -1
  497. package/extensions/nostr/CHANGELOG.md +5 -0
  498. package/extensions/nostr/package.json +1 -1
  499. package/extensions/open-prose/package.json +1 -1
  500. package/extensions/openai-codex-auth/index.ts +177 -0
  501. package/extensions/phone-control/index.ts +421 -0
  502. package/extensions/shared/resolve-target-test-helpers.ts +66 -0
  503. package/extensions/signal/package.json +1 -1
  504. package/extensions/signal/src/channel.js +273 -0
  505. package/extensions/signal/src/runtime.js +10 -0
  506. package/extensions/slack/package.json +1 -1
  507. package/extensions/slack/src/channel.js +489 -0
  508. package/extensions/slack/src/runtime.js +10 -0
  509. package/extensions/talk-voice/index.ts +150 -0
  510. package/extensions/telegram/package.json +1 -1
  511. package/extensions/telegram/src/channel.js +424 -0
  512. package/extensions/telegram/src/runtime.js +10 -0
  513. package/extensions/thread-ownership/index.ts +133 -0
  514. package/extensions/tlon/package.json +1 -1
  515. package/extensions/tlon/src/account-fields.ts +25 -0
  516. package/extensions/tlon/src/urbit/base-url.ts +57 -0
  517. package/extensions/tlon/src/urbit/channel-client.ts +157 -0
  518. package/extensions/tlon/src/urbit/channel-ops.ts +164 -0
  519. package/extensions/tlon/src/urbit/context.ts +47 -0
  520. package/extensions/tlon/src/urbit/errors.ts +51 -0
  521. package/extensions/tlon/src/urbit/fetch.ts +39 -0
  522. package/extensions/twitch/CHANGELOG.md +5 -0
  523. package/extensions/twitch/package.json +1 -1
  524. package/extensions/twitch/src/test-fixtures.ts +30 -0
  525. package/extensions/voice-call/CHANGELOG.md +5 -0
  526. package/extensions/voice-call/package.json +1 -1
  527. package/extensions/voice-call/src/allowlist.ts +19 -0
  528. package/extensions/whatsapp/package.json +1 -1
  529. package/extensions/whatsapp/src/channel.js +429 -0
  530. package/extensions/whatsapp/src/runtime.js +10 -0
  531. package/extensions/zalo/CHANGELOG.md +5 -0
  532. package/extensions/zalo/package.json +1 -1
  533. package/extensions/zalouser/CHANGELOG.md +5 -0
  534. package/extensions/zalouser/package.json +1 -1
  535. package/package.json +1 -1
@@ -20,6 +20,7 @@ import { buildFileEntry, chunkMarkdown, ensureDir, hashText, isMemoryPath, listM
20
20
  import { bm25RankToScore, buildFtsQuery, mergeHybridResults } from "./hybrid.js";
21
21
  import { searchKeyword, searchVector } from "./manager-search.js";
22
22
  import { ensureMemoryIndexSchema } from "./memory-schema.js";
23
+ import { extractKeywords } from "./query-expansion.js";
23
24
  import { requireNodeSqlite } from "./sqlite.js";
24
25
  import { loadSqliteVecExtension } from "./sqlite-vec.js";
25
26
  const META_KEY = "memory_index_meta_v1";
@@ -54,6 +55,7 @@ export class MemoryIndexManager {
54
55
  requestedProvider;
55
56
  fallbackFrom;
56
57
  fallbackReason;
58
+ providerUnavailableReason;
57
59
  openAi;
58
60
  gemini;
59
61
  voyage;
@@ -108,6 +110,7 @@ export class MemoryIndexManager {
108
110
  workspaceDir,
109
111
  settings,
110
112
  providerResult,
113
+ purpose: params.purpose,
111
114
  });
112
115
  INDEX_CACHE.set(key, manager);
113
116
  return manager;
@@ -122,6 +125,7 @@ export class MemoryIndexManager {
122
125
  this.requestedProvider = params.providerResult.requestedProvider;
123
126
  this.fallbackFrom = params.providerResult.fallbackFrom;
124
127
  this.fallbackReason = params.providerResult.fallbackReason;
128
+ this.providerUnavailableReason = params.providerResult.providerUnavailableReason;
125
129
  this.openAi = params.providerResult.openAi;
126
130
  this.gemini = params.providerResult.gemini;
127
131
  this.voyage = params.providerResult.voyage;
@@ -146,7 +150,8 @@ export class MemoryIndexManager {
146
150
  this.ensureWatcher();
147
151
  this.ensureSessionListener();
148
152
  this.ensureIntervalSync();
149
- this.dirty = this.sources.has("memory");
153
+ const statusOnly = params.purpose === "status";
154
+ this.dirty = this.sources.has("memory") && (statusOnly ? !meta : true);
150
155
  this.batch = this.resolveBatchConfig();
151
156
  }
152
157
  async warmSession(sessionKey) {
@@ -175,6 +180,30 @@ export class MemoryIndexManager {
175
180
  const maxResults = opts?.maxResults ?? this.settings.query.maxResults;
176
181
  const hybrid = this.settings.query.hybrid;
177
182
  const candidates = Math.min(200, Math.max(1, Math.floor(maxResults * hybrid.candidateMultiplier)));
183
+ // FTS-only mode: no embedding provider available
184
+ if (!this.provider) {
185
+ if (!this.fts.enabled || !this.fts.available) {
186
+ log.warn("memory search: no provider and FTS unavailable");
187
+ return [];
188
+ }
189
+ const keywords = extractKeywords(cleaned);
190
+ const searchTerms = keywords.length > 0 ? keywords : [cleaned];
191
+ const resultSets = await Promise.all(searchTerms.map((term) => this.searchKeyword(term, candidates).catch(() => [])));
192
+ const seenIds = new Map();
193
+ for (const results of resultSets) {
194
+ for (const result of results) {
195
+ const existing = seenIds.get(result.id);
196
+ if (!existing || result.score > existing.score) {
197
+ seenIds.set(result.id, result);
198
+ }
199
+ }
200
+ }
201
+ const merged = [...seenIds.values()]
202
+ .toSorted((a, b) => b.score - a.score)
203
+ .filter((entry) => entry.score >= minScore)
204
+ .slice(0, maxResults);
205
+ return merged;
206
+ }
178
207
  const keywordResults = hybrid.enabled
179
208
  ? await this.searchKeyword(cleaned, candidates).catch(() => [])
180
209
  : [];
@@ -186,15 +215,19 @@ export class MemoryIndexManager {
186
215
  if (!hybrid.enabled) {
187
216
  return vectorResults.filter((entry) => entry.score >= minScore).slice(0, maxResults);
188
217
  }
189
- const merged = this.mergeHybridResults({
218
+ const merged = await this.mergeHybridResults({
190
219
  vector: vectorResults,
191
220
  keyword: keywordResults,
192
221
  vectorWeight: hybrid.vectorWeight,
193
222
  textWeight: hybrid.textWeight,
223
+ mmr: hybrid.mmr,
224
+ temporalDecay: hybrid.temporalDecay,
194
225
  });
195
226
  return merged.filter((entry) => entry.score >= minScore).slice(0, maxResults);
196
227
  }
197
228
  async searchVector(queryVec, limit) {
229
+ if (!this.provider)
230
+ return [];
198
231
  const results = await searchVector({
199
232
  db: this.db,
200
233
  vectorTable: VECTOR_TABLE,
@@ -218,7 +251,7 @@ export class MemoryIndexManager {
218
251
  const results = await searchKeyword({
219
252
  db: this.db,
220
253
  ftsTable: FTS_TABLE,
221
- providerModel: this.provider.model,
254
+ providerModel: this.provider?.model ?? "fts-only",
222
255
  query,
223
256
  limit,
224
257
  snippetMaxChars: SNIPPET_MAX_CHARS,
@@ -228,8 +261,8 @@ export class MemoryIndexManager {
228
261
  });
229
262
  return results.map((entry) => entry);
230
263
  }
231
- mergeHybridResults(params) {
232
- const merged = mergeHybridResults({
264
+ async mergeHybridResults(params) {
265
+ const merged = await mergeHybridResults({
233
266
  vector: params.vector.map((r) => ({
234
267
  id: r.id,
235
268
  path: r.path,
@@ -250,6 +283,9 @@ export class MemoryIndexManager {
250
283
  })),
251
284
  vectorWeight: params.vectorWeight,
252
285
  textWeight: params.textWeight,
286
+ workspaceDir: this.workspaceDir,
287
+ mmr: params.mmr,
288
+ temporalDecay: params.temporalDecay,
253
289
  });
254
290
  return merged.map((entry) => entry);
255
291
  }
@@ -359,8 +395,8 @@ export class MemoryIndexManager {
359
395
  dirty: this.dirty || this.sessionsDirty,
360
396
  workspaceDir: this.workspaceDir,
361
397
  dbPath: this.settings.store.path,
362
- provider: this.provider.id,
363
- model: this.provider.model,
398
+ provider: this.provider?.id ?? "none",
399
+ model: this.provider?.model,
364
400
  requestedProvider: this.requestedProvider,
365
401
  sources: Array.from(this.sources),
366
402
  extraPaths: this.settings.extraPaths,
@@ -882,7 +918,7 @@ export class MemoryIndexManager {
882
918
  try {
883
919
  this.db
884
920
  .prepare(`DELETE FROM ${FTS_TABLE} WHERE path = ? AND source = ? AND model = ?`)
885
- .run(stale.path, "memory", this.provider.model);
921
+ .run(stale.path, "memory", this.provider?.model ?? "fts-only");
886
922
  }
887
923
  catch { }
888
924
  }
@@ -976,7 +1012,7 @@ export class MemoryIndexManager {
976
1012
  try {
977
1013
  this.db
978
1014
  .prepare(`DELETE FROM ${FTS_TABLE} WHERE path = ? AND source = ? AND model = ?`)
979
- .run(stale.path, "sessions", this.provider.model);
1015
+ .run(stale.path, "sessions", this.provider?.model ?? "fts-only");
980
1016
  }
981
1017
  catch { }
982
1018
  }
@@ -1015,8 +1051,8 @@ export class MemoryIndexManager {
1015
1051
  const meta = this.readMeta();
1016
1052
  const needsFullReindex = params?.force ||
1017
1053
  !meta ||
1018
- meta.model !== this.provider.model ||
1019
- meta.provider !== this.provider.id ||
1054
+ meta.model !== this.provider?.model ||
1055
+ meta.provider !== this.provider?.id ||
1020
1056
  meta.providerKey !== this.providerKey ||
1021
1057
  meta.chunkTokens !== this.settings.chunking.tokens ||
1022
1058
  meta.chunkOverlap !== this.settings.chunking.overlap ||
@@ -1068,9 +1104,9 @@ export class MemoryIndexManager {
1068
1104
  resolveBatchConfig() {
1069
1105
  const batch = this.settings.remote?.batch;
1070
1106
  const enabled = Boolean(batch?.enabled &&
1071
- ((this.openAi && this.provider.id === "openai") ||
1072
- (this.gemini && this.provider.id === "gemini") ||
1073
- (this.voyage && this.provider.id === "voyage")));
1107
+ ((this.openAi && this.provider?.id === "openai") ||
1108
+ (this.gemini && this.provider?.id === "gemini") ||
1109
+ (this.voyage && this.provider?.id === "voyage")));
1074
1110
  return {
1075
1111
  enabled,
1076
1112
  wait: batch?.wait ?? true,
@@ -1081,7 +1117,7 @@ export class MemoryIndexManager {
1081
1117
  }
1082
1118
  async activateFallbackProvider(reason) {
1083
1119
  const fallback = this.settings.fallback;
1084
- if (!fallback || fallback === "none" || fallback === this.provider.id)
1120
+ if (!fallback || fallback === "none" || !this.provider || fallback === this.provider.id)
1085
1121
  return false;
1086
1122
  if (this.fallbackFrom)
1087
1123
  return false;
@@ -1170,8 +1206,8 @@ export class MemoryIndexManager {
1170
1206
  this.sessionsDirty = false;
1171
1207
  }
1172
1208
  nextMeta = {
1173
- model: this.provider.model,
1174
- provider: this.provider.id,
1209
+ model: this.provider?.model ?? "fts-only",
1210
+ provider: this.provider?.id ?? "none",
1175
1211
  providerKey: this.providerKey,
1176
1212
  chunkTokens: this.settings.chunking.tokens,
1177
1213
  chunkOverlap: this.settings.chunking.overlap,
@@ -1371,7 +1407,11 @@ export class MemoryIndexManager {
1371
1407
  if (unique.length === 0)
1372
1408
  return new Map();
1373
1409
  const out = new Map();
1374
- const baseParams = [this.provider.id, this.provider.model, this.providerKey];
1410
+ const baseParams = [
1411
+ this.provider?.id ?? "none",
1412
+ this.provider?.model ?? "fts-only",
1413
+ this.providerKey,
1414
+ ];
1375
1415
  const batchSize = 400;
1376
1416
  for (let start = 0; start < unique.length; start += batchSize) {
1377
1417
  const batch = unique.slice(start, start + batchSize);
@@ -1387,7 +1427,7 @@ export class MemoryIndexManager {
1387
1427
  return out;
1388
1428
  }
1389
1429
  upsertEmbeddingCache(entries) {
1390
- if (!this.cache.enabled)
1430
+ if (!this.cache.enabled || !this.provider)
1391
1431
  return;
1392
1432
  if (entries.length === 0)
1393
1433
  return;
@@ -1461,6 +1501,9 @@ export class MemoryIndexManager {
1461
1501
  return embeddings;
1462
1502
  }
1463
1503
  computeProviderKey() {
1504
+ if (!this.provider) {
1505
+ return hashText(JSON.stringify({ provider: "none", model: "fts-only" }));
1506
+ }
1464
1507
  if (this.provider.id === "openai" && this.openAi) {
1465
1508
  const entries = Object.entries(this.openAi.headers)
1466
1509
  .filter(([key]) => key.toLowerCase() !== "authorization")
@@ -1491,13 +1534,13 @@ export class MemoryIndexManager {
1491
1534
  return hashText(JSON.stringify({ provider: this.provider.id, model: this.provider.model }));
1492
1535
  }
1493
1536
  async embedChunksWithBatch(chunks, entry, source) {
1494
- if (this.provider.id === "openai" && this.openAi) {
1537
+ if (this.provider?.id === "openai" && this.openAi) {
1495
1538
  return this.embedChunksWithOpenAiBatch(chunks, entry, source);
1496
1539
  }
1497
- if (this.provider.id === "gemini" && this.gemini) {
1540
+ if (this.provider?.id === "gemini" && this.gemini) {
1498
1541
  return this.embedChunksWithGeminiBatch(chunks, entry, source);
1499
1542
  }
1500
- if (this.provider.id === "voyage" && this.voyage) {
1543
+ if (this.provider?.id === "voyage" && this.voyage) {
1501
1544
  return this.embedChunksWithVoyageBatch(chunks, entry, source);
1502
1545
  }
1503
1546
  return this.embedChunksInBatches(chunks);
@@ -1602,7 +1645,7 @@ export class MemoryIndexManager {
1602
1645
  method: "POST",
1603
1646
  url: OPENAI_BATCH_ENDPOINT,
1604
1647
  body: {
1605
- model: this.openAi?.model ?? this.provider.model,
1648
+ model: this.openAi?.model ?? this.provider?.model ?? "fts-only",
1606
1649
  input: chunk.text,
1607
1650
  },
1608
1651
  });
@@ -1700,6 +1743,8 @@ export class MemoryIndexManager {
1700
1743
  async embedBatchWithRetry(texts) {
1701
1744
  if (texts.length === 0)
1702
1745
  return [];
1746
+ if (!this.provider)
1747
+ throw new Error("embedding provider unavailable");
1703
1748
  let attempt = 0;
1704
1749
  let delayMs = EMBEDDING_RETRY_BASE_DELAY_MS;
1705
1750
  while (true) {
@@ -1729,13 +1774,15 @@ export class MemoryIndexManager {
1729
1774
  return /(rate[_ ]limit|too many requests|429|resource has been exhausted|5\d\d|cloudflare)/i.test(message);
1730
1775
  }
1731
1776
  resolveEmbeddingTimeout(kind) {
1732
- const isLocal = this.provider.id === "local";
1777
+ const isLocal = this.provider?.id === "local";
1733
1778
  if (kind === "query") {
1734
1779
  return isLocal ? EMBEDDING_QUERY_TIMEOUT_LOCAL_MS : EMBEDDING_QUERY_TIMEOUT_REMOTE_MS;
1735
1780
  }
1736
1781
  return isLocal ? EMBEDDING_BATCH_TIMEOUT_LOCAL_MS : EMBEDDING_BATCH_TIMEOUT_REMOTE_MS;
1737
1782
  }
1738
1783
  async embedQueryWithTimeout(text) {
1784
+ if (!this.provider)
1785
+ throw new Error("embedding provider unavailable");
1739
1786
  const timeoutMs = this.resolveEmbeddingTimeout("query");
1740
1787
  log.debug("memory embeddings: query start", { provider: this.provider.id, timeoutMs });
1741
1788
  return await this.withTimeout(this.provider.embedQuery(text), timeoutMs, `memory embeddings query timed out after ${Math.round(timeoutMs / 1000)}s`);
@@ -1873,7 +1920,7 @@ export class MemoryIndexManager {
1873
1920
  try {
1874
1921
  this.db
1875
1922
  .prepare(`DELETE FROM ${FTS_TABLE} WHERE path = ? AND source = ? AND model = ?`)
1876
- .run(entry.path, options.source, this.provider.model);
1923
+ .run(entry.path, options.source, this.provider?.model ?? "fts-only");
1877
1924
  }
1878
1925
  catch { }
1879
1926
  }
@@ -1883,7 +1930,8 @@ export class MemoryIndexManager {
1883
1930
  for (let i = 0; i < chunks.length; i++) {
1884
1931
  const chunk = chunks[i];
1885
1932
  const embedding = embeddings[i] ?? [];
1886
- const id = hashText(`${options.source}:${entry.path}:${chunk.startLine}:${chunk.endLine}:${chunk.hash}:${this.provider.model}`);
1933
+ const providerModel = this.provider?.model ?? "fts-only";
1934
+ const id = hashText(`${options.source}:${entry.path}:${chunk.startLine}:${chunk.endLine}:${chunk.hash}:${providerModel}`);
1887
1935
  this.db
1888
1936
  .prepare(`INSERT INTO chunks (id, path, source, start_line, end_line, hash, model, text, embedding, updated_at)
1889
1937
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
@@ -1893,7 +1941,7 @@ export class MemoryIndexManager {
1893
1941
  text=excluded.text,
1894
1942
  embedding=excluded.embedding,
1895
1943
  updated_at=excluded.updated_at`)
1896
- .run(id, entry.path, options.source, chunk.startLine, chunk.endLine, chunk.hash, this.provider.model, chunk.text, JSON.stringify(embedding), now);
1944
+ .run(id, entry.path, options.source, chunk.startLine, chunk.endLine, chunk.hash, providerModel, chunk.text, JSON.stringify(embedding), now);
1897
1945
  if (vectorReady && embedding.length > 0) {
1898
1946
  try {
1899
1947
  this.db.prepare(`DELETE FROM ${VECTOR_TABLE} WHERE id = ?`).run(id);
@@ -1907,7 +1955,7 @@ export class MemoryIndexManager {
1907
1955
  this.db
1908
1956
  .prepare(`INSERT INTO ${FTS_TABLE} (text, id, path, source, model, start_line, end_line)\n` +
1909
1957
  ` VALUES (?, ?, ?, ?, ?, ?, ?)`)
1910
- .run(chunk.text, id, entry.path, options.source, this.provider.model, chunk.startLine, chunk.endLine);
1958
+ .run(chunk.text, id, entry.path, options.source, providerModel, chunk.startLine, chunk.endLine);
1911
1959
  }
1912
1960
  }
1913
1961
  this.db
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Maximal Marginal Relevance (MMR) re-ranking algorithm.
3
+ *
4
+ * MMR balances relevance with diversity by iteratively selecting results
5
+ * that maximize: λ * relevance - (1-λ) * max_similarity_to_selected
6
+ *
7
+ * @see Carbonell & Goldstein, "The Use of MMR, Diversity-Based Reranking" (1998)
8
+ */
9
+ export const DEFAULT_MMR_CONFIG = {
10
+ enabled: false,
11
+ lambda: 0.7,
12
+ };
13
+ /**
14
+ * Tokenize text for Jaccard similarity computation.
15
+ * Extracts alphanumeric tokens and normalizes to lowercase.
16
+ */
17
+ export function tokenize(text) {
18
+ const tokens = text.toLowerCase().match(/[a-z0-9_]+/g) ?? [];
19
+ return new Set(tokens);
20
+ }
21
+ /**
22
+ * Compute Jaccard similarity between two token sets.
23
+ * Returns a value in [0, 1] where 1 means identical sets.
24
+ */
25
+ export function jaccardSimilarity(setA, setB) {
26
+ if (setA.size === 0 && setB.size === 0) {
27
+ return 1;
28
+ }
29
+ if (setA.size === 0 || setB.size === 0) {
30
+ return 0;
31
+ }
32
+ let intersectionSize = 0;
33
+ const smaller = setA.size <= setB.size ? setA : setB;
34
+ const larger = setA.size <= setB.size ? setB : setA;
35
+ for (const token of smaller) {
36
+ if (larger.has(token)) {
37
+ intersectionSize++;
38
+ }
39
+ }
40
+ const unionSize = setA.size + setB.size - intersectionSize;
41
+ return unionSize === 0 ? 0 : intersectionSize / unionSize;
42
+ }
43
+ /**
44
+ * Compute text similarity between two content strings using Jaccard on tokens.
45
+ */
46
+ export function textSimilarity(contentA, contentB) {
47
+ return jaccardSimilarity(tokenize(contentA), tokenize(contentB));
48
+ }
49
+ /**
50
+ * Compute the maximum similarity between an item and all selected items.
51
+ */
52
+ function maxSimilarityToSelected(item, selectedItems, tokenCache) {
53
+ if (selectedItems.length === 0) {
54
+ return 0;
55
+ }
56
+ let maxSim = 0;
57
+ const itemTokens = tokenCache.get(item.id) ?? tokenize(item.content);
58
+ for (const selected of selectedItems) {
59
+ const selectedTokens = tokenCache.get(selected.id) ?? tokenize(selected.content);
60
+ const sim = jaccardSimilarity(itemTokens, selectedTokens);
61
+ if (sim > maxSim) {
62
+ maxSim = sim;
63
+ }
64
+ }
65
+ return maxSim;
66
+ }
67
+ /**
68
+ * Compute MMR score for a candidate item.
69
+ * MMR = λ * relevance - (1-λ) * max_similarity_to_selected
70
+ */
71
+ export function computeMMRScore(relevance, maxSimilarity, lambda) {
72
+ return lambda * relevance - (1 - lambda) * maxSimilarity;
73
+ }
74
+ /**
75
+ * Re-rank items using Maximal Marginal Relevance (MMR).
76
+ *
77
+ * The algorithm iteratively selects items that balance relevance with diversity:
78
+ * 1. Start with the highest-scoring item
79
+ * 2. For each remaining slot, select the item that maximizes the MMR score
80
+ * 3. MMR score = λ * relevance - (1-λ) * max_similarity_to_already_selected
81
+ *
82
+ * @param items - Items to re-rank, must have score and content
83
+ * @param config - MMR configuration (lambda, enabled)
84
+ * @returns Re-ranked items in MMR order
85
+ */
86
+ export function mmrRerank(items, config = {}) {
87
+ const { enabled = DEFAULT_MMR_CONFIG.enabled, lambda = DEFAULT_MMR_CONFIG.lambda } = config;
88
+ // Early exits
89
+ if (!enabled || items.length <= 1) {
90
+ return [...items];
91
+ }
92
+ // Clamp lambda to valid range
93
+ const clampedLambda = Math.max(0, Math.min(1, lambda));
94
+ // If lambda is 1, just return sorted by relevance (no diversity penalty)
95
+ if (clampedLambda === 1) {
96
+ return [...items].toSorted((a, b) => b.score - a.score);
97
+ }
98
+ // Pre-tokenize all items for efficiency
99
+ const tokenCache = new Map();
100
+ for (const item of items) {
101
+ tokenCache.set(item.id, tokenize(item.content));
102
+ }
103
+ // Normalize scores to [0, 1] for fair comparison with similarity
104
+ const maxScore = Math.max(...items.map((i) => i.score));
105
+ const minScore = Math.min(...items.map((i) => i.score));
106
+ const scoreRange = maxScore - minScore;
107
+ const normalizeScore = (score) => {
108
+ if (scoreRange === 0) {
109
+ return 1; // All scores equal
110
+ }
111
+ return (score - minScore) / scoreRange;
112
+ };
113
+ const selected = [];
114
+ const remaining = new Set(items);
115
+ // Select items iteratively
116
+ while (remaining.size > 0) {
117
+ let bestItem = null;
118
+ let bestMMRScore = -Infinity;
119
+ for (const candidate of remaining) {
120
+ const normalizedRelevance = normalizeScore(candidate.score);
121
+ const maxSim = maxSimilarityToSelected(candidate, selected, tokenCache);
122
+ const mmrScore = computeMMRScore(normalizedRelevance, maxSim, clampedLambda);
123
+ // Use original score as tiebreaker (higher is better)
124
+ if (mmrScore > bestMMRScore ||
125
+ (mmrScore === bestMMRScore && candidate.score > (bestItem?.score ?? -Infinity))) {
126
+ bestMMRScore = mmrScore;
127
+ bestItem = candidate;
128
+ }
129
+ }
130
+ if (bestItem) {
131
+ selected.push(bestItem);
132
+ remaining.delete(bestItem);
133
+ }
134
+ else {
135
+ // Should never happen, but safety exit
136
+ break;
137
+ }
138
+ }
139
+ return selected;
140
+ }
141
+ /**
142
+ * Apply MMR re-ranking to hybrid search results.
143
+ * Adapts the generic MMR function to work with the hybrid search result format.
144
+ */
145
+ export function applyMMRToHybridResults(results, config = {}) {
146
+ if (results.length === 0) {
147
+ return results;
148
+ }
149
+ // Create a map from ID to original item for type-safe retrieval
150
+ const itemById = new Map();
151
+ // Create MMR items with unique IDs
152
+ const mmrItems = results.map((r, index) => {
153
+ const id = `${r.path}:${r.startLine}:${index}`;
154
+ itemById.set(id, r);
155
+ return {
156
+ id,
157
+ score: r.score,
158
+ content: r.snippet,
159
+ };
160
+ });
161
+ const reranked = mmrRerank(mmrItems, config);
162
+ // Map back to original items using the ID
163
+ return reranked.map((item) => itemById.get(item.id));
164
+ }