@vellumai/assistant 0.3.5 → 0.3.7

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 (487) hide show
  1. package/README.md +51 -0
  2. package/eslint.config.mjs +31 -0
  3. package/package.json +1 -1
  4. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  5. package/scripts/ipc/generate-swift.ts +18 -2
  6. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
  7. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  8. package/src/__tests__/browser-manager.test.ts +1 -0
  9. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  10. package/src/__tests__/call-orchestrator.test.ts +752 -271
  11. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  12. package/src/__tests__/call-recovery.test.ts +3 -0
  13. package/src/__tests__/call-routes-http.test.ts +5 -0
  14. package/src/__tests__/call-store.test.ts +3 -0
  15. package/src/__tests__/channel-approval-routes.test.ts +1260 -85
  16. package/src/__tests__/channel-approval.test.ts +37 -0
  17. package/src/__tests__/channel-approvals.test.ts +4 -65
  18. package/src/__tests__/channel-guardian.test.ts +556 -0
  19. package/src/__tests__/channel-readiness-service.test.ts +74 -7
  20. package/src/__tests__/checker.test.ts +14 -7
  21. package/src/__tests__/clarification-resolver.test.ts +44 -24
  22. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  23. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  24. package/src/__tests__/config-schema.test.ts +12 -7
  25. package/src/__tests__/context-window-manager.test.ts +30 -2
  26. package/src/__tests__/contradiction-checker.test.ts +20 -5
  27. package/src/__tests__/credential-security-invariants.test.ts +6 -2
  28. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  29. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  30. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  31. package/src/__tests__/guardian-action-store.test.ts +123 -0
  32. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  33. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  34. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  35. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  36. package/src/__tests__/handlers-twilio-config.test.ts +126 -0
  37. package/src/__tests__/intent-routing.test.ts +2 -0
  38. package/src/__tests__/ipc-snapshot.test.ts +228 -1
  39. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  40. package/src/__tests__/model-intents.test.ts +96 -0
  41. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  42. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  43. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  44. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  45. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  46. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  47. package/src/__tests__/qdrant-manager.test.ts +27 -20
  48. package/src/__tests__/relay-server.test.ts +779 -40
  49. package/src/__tests__/run-orchestrator-assistant-events.test.ts +2 -0
  50. package/src/__tests__/run-orchestrator.test.ts +20 -4
  51. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  52. package/src/__tests__/runtime-runs.test.ts +16 -0
  53. package/src/__tests__/schedule-store.test.ts +18 -4
  54. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  55. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  56. package/src/__tests__/session-agent-loop.test.ts +857 -0
  57. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  58. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  59. package/src/__tests__/session-profile-injection.test.ts +6 -0
  60. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  61. package/src/__tests__/session-queue.test.ts +6 -0
  62. package/src/__tests__/session-runtime-assembly.test.ts +237 -13
  63. package/src/__tests__/session-slash-known.test.ts +6 -0
  64. package/src/__tests__/session-slash-queue.test.ts +6 -0
  65. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  66. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  67. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  68. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  69. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  70. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  71. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  72. package/src/__tests__/skills.test.ts +2 -0
  73. package/src/__tests__/sms-messaging-provider.test.ts +2 -1
  74. package/src/__tests__/starter-task-flow.test.ts +2 -0
  75. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  76. package/src/__tests__/system-prompt.test.ts +2 -0
  77. package/src/__tests__/task-management-tools.test.ts +2 -2
  78. package/src/__tests__/task-runner.test.ts +14 -4
  79. package/src/__tests__/terminal-tools.test.ts +25 -19
  80. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  81. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  82. package/src/__tests__/tool-executor.test.ts +23 -24
  83. package/src/__tests__/trust-store.test.ts +3 -3
  84. package/src/__tests__/twilio-rest.test.ts +29 -0
  85. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  86. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  87. package/src/__tests__/twilio-routes.test.ts +141 -21
  88. package/src/__tests__/user-reference.test.ts +2 -0
  89. package/src/__tests__/voice-quality.test.ts +222 -0
  90. package/src/__tests__/web-search.test.ts +45 -29
  91. package/src/agent/loop.ts +1 -1
  92. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  93. package/src/amazon/client.ts +1418 -0
  94. package/src/amazon/request-extractor.ts +135 -0
  95. package/src/amazon/session.ts +109 -0
  96. package/src/autonomy/autonomy-store.ts +5 -5
  97. package/src/browser-extension-relay/client.ts +124 -0
  98. package/src/browser-extension-relay/protocol.ts +63 -0
  99. package/src/browser-extension-relay/server.ts +177 -0
  100. package/src/bundler/app-bundler.ts +3 -3
  101. package/src/bundler/bundle-signer.ts +1 -1
  102. package/src/bundler/signature-verifier.ts +1 -1
  103. package/src/calls/call-conversation-messages.ts +33 -0
  104. package/src/calls/call-domain.ts +106 -5
  105. package/src/calls/call-orchestrator.ts +252 -54
  106. package/src/calls/call-pointer-messages.ts +53 -0
  107. package/src/calls/call-recovery.ts +3 -8
  108. package/src/calls/call-store.ts +69 -87
  109. package/src/calls/elevenlabs-config.ts +3 -2
  110. package/src/calls/guardian-action-sweep.ts +105 -0
  111. package/src/calls/guardian-dispatch.ts +203 -0
  112. package/src/calls/guardian-question-copy.ts +133 -0
  113. package/src/calls/relay-server.ts +466 -8
  114. package/src/calls/speaker-identification.ts +1 -1
  115. package/src/calls/twilio-config.ts +7 -5
  116. package/src/calls/twilio-provider.ts +6 -4
  117. package/src/calls/twilio-rest.ts +40 -15
  118. package/src/calls/twilio-routes.ts +60 -45
  119. package/src/calls/types.ts +3 -1
  120. package/src/channels/types.ts +25 -0
  121. package/src/cli/amazon.ts +815 -0
  122. package/src/cli/config-commands.ts +2 -2
  123. package/src/cli/core-commands.ts +4 -3
  124. package/src/cli/influencer.ts +244 -0
  125. package/src/cli/map.ts +89 -6
  126. package/src/cli.ts +1 -1
  127. package/src/config/agent-schema.ts +171 -0
  128. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  129. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  130. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  131. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  132. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  133. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  134. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  135. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  136. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  137. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  138. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  139. package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
  140. package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
  141. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  142. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  143. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  144. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  145. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  146. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  147. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  148. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +7 -9
  149. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  150. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
  151. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
  152. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  153. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
  154. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
  155. package/src/config/bundled-skills/messaging/SKILL.md +12 -2
  156. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  157. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  158. package/src/config/bundled-skills/phone-calls/SKILL.md +86 -21
  159. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  160. package/src/config/bundled-tool-registry.ts +310 -0
  161. package/src/config/calls-schema.ts +181 -0
  162. package/src/config/core-schema.ts +309 -0
  163. package/src/config/defaults.ts +27 -3
  164. package/src/config/env-registry.ts +169 -0
  165. package/src/config/env.ts +175 -0
  166. package/src/config/loader.ts +6 -6
  167. package/src/config/memory-schema.ts +528 -0
  168. package/src/config/sandbox-schema.ts +55 -0
  169. package/src/config/schema.ts +157 -1138
  170. package/src/config/skill-state.ts +1 -1
  171. package/src/config/skills-schema.ts +32 -0
  172. package/src/config/skills.ts +35 -24
  173. package/src/config/system-prompt.ts +107 -56
  174. package/src/config/templates/SOUL.md +1 -1
  175. package/src/config/types.ts +1 -0
  176. package/src/config/user-reference.ts +4 -9
  177. package/src/config/vellum-skills/catalog.json +0 -7
  178. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  179. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
  180. package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
  181. package/src/context/window-manager.ts +27 -7
  182. package/src/daemon/approval-generators.ts +186 -0
  183. package/src/daemon/approved-devices-store.ts +140 -0
  184. package/src/daemon/assistant-attachments.ts +1 -1
  185. package/src/daemon/classifier.ts +35 -32
  186. package/src/daemon/config-watcher.ts +1 -1
  187. package/src/daemon/daemon-control.ts +254 -0
  188. package/src/daemon/handlers/apps.ts +2 -3
  189. package/src/daemon/handlers/config-channels.ts +158 -0
  190. package/src/daemon/handlers/config-inbox.ts +540 -0
  191. package/src/daemon/handlers/config-ingress.ts +231 -0
  192. package/src/daemon/handlers/config-integrations.ts +258 -0
  193. package/src/daemon/handlers/config-model.ts +143 -0
  194. package/src/daemon/handlers/config-parental.ts +163 -0
  195. package/src/daemon/handlers/config-scheduling.ts +172 -0
  196. package/src/daemon/handlers/config-slack.ts +92 -0
  197. package/src/daemon/handlers/config-telegram.ts +301 -0
  198. package/src/daemon/handlers/config-tools.ts +177 -0
  199. package/src/daemon/handlers/config-trust.ts +104 -0
  200. package/src/daemon/handlers/config-twilio.ts +1080 -0
  201. package/src/daemon/handlers/config.ts +53 -2463
  202. package/src/daemon/handlers/diagnostics.ts +1 -1
  203. package/src/daemon/handlers/dictation.ts +4 -6
  204. package/src/daemon/handlers/documents.ts +18 -32
  205. package/src/daemon/handlers/index.ts +9 -0
  206. package/src/daemon/handlers/misc.ts +3 -5
  207. package/src/daemon/handlers/pairing.ts +98 -0
  208. package/src/daemon/handlers/sessions.ts +74 -5
  209. package/src/daemon/handlers/shared.ts +3 -1
  210. package/src/daemon/handlers/skills.ts +1 -1
  211. package/src/daemon/handlers/twitter-auth.ts +2 -0
  212. package/src/daemon/handlers/work-items.ts +2 -2
  213. package/src/daemon/handlers/workspace-files.ts +4 -3
  214. package/src/daemon/install-cli-launchers.ts +113 -0
  215. package/src/daemon/ipc-contract/apps.ts +356 -0
  216. package/src/daemon/ipc-contract/browser.ts +74 -0
  217. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  218. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  219. package/src/daemon/ipc-contract/documents.ts +74 -0
  220. package/src/daemon/ipc-contract/inbox.ts +209 -0
  221. package/src/daemon/ipc-contract/integrations.ts +284 -0
  222. package/src/daemon/ipc-contract/memory.ts +48 -0
  223. package/src/daemon/ipc-contract/messages.ts +211 -0
  224. package/src/daemon/ipc-contract/pairing.ts +45 -0
  225. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  226. package/src/daemon/ipc-contract/schedules.ts +97 -0
  227. package/src/daemon/ipc-contract/sessions.ts +321 -0
  228. package/src/daemon/ipc-contract/shared.ts +42 -0
  229. package/src/daemon/ipc-contract/skills.ts +120 -0
  230. package/src/daemon/ipc-contract/subagents.ts +58 -0
  231. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  232. package/src/daemon/ipc-contract/trust.ts +60 -0
  233. package/src/daemon/ipc-contract/work-items.ts +225 -0
  234. package/src/daemon/ipc-contract/workspace.ts +113 -0
  235. package/src/daemon/ipc-contract-inventory.json +62 -0
  236. package/src/daemon/ipc-contract-inventory.ts +55 -29
  237. package/src/daemon/ipc-contract.ts +227 -2527
  238. package/src/daemon/ipc-protocol.ts +1 -1
  239. package/src/daemon/ipc-validate.ts +7 -0
  240. package/src/daemon/lifecycle.ts +97 -379
  241. package/src/daemon/pairing-store.ts +177 -0
  242. package/src/daemon/providers-setup.ts +43 -0
  243. package/src/daemon/ride-shotgun-handler.ts +67 -2
  244. package/src/daemon/server.ts +60 -44
  245. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  246. package/src/daemon/session-agent-loop.ts +113 -275
  247. package/src/daemon/session-dynamic-profile.ts +1 -1
  248. package/src/daemon/session-history.ts +1 -1
  249. package/src/daemon/session-media-retry.ts +1 -1
  250. package/src/daemon/session-messaging.ts +37 -2
  251. package/src/daemon/session-notifiers.ts +5 -25
  252. package/src/daemon/session-process.ts +99 -59
  253. package/src/daemon/session-queue-manager.ts +98 -4
  254. package/src/daemon/session-runtime-assembly.ts +149 -15
  255. package/src/daemon/session-surfaces.ts +26 -4
  256. package/src/daemon/session-tool-setup.ts +28 -30
  257. package/src/daemon/session-workspace.ts +1 -1
  258. package/src/daemon/session.ts +24 -1
  259. package/src/daemon/shutdown-handlers.ts +122 -0
  260. package/src/daemon/trace-emitter.ts +1 -1
  261. package/src/daemon/watch-handler.ts +36 -33
  262. package/src/doordash/cart-queries.ts +787 -0
  263. package/src/doordash/client.ts +144 -127
  264. package/src/doordash/order-queries.ts +85 -0
  265. package/src/doordash/queries.ts +10 -1308
  266. package/src/doordash/search-queries.ts +203 -0
  267. package/src/doordash/session.ts +3 -2
  268. package/src/doordash/store-queries.ts +246 -0
  269. package/src/doordash/types.ts +367 -0
  270. package/src/email/providers/agentmail.ts +2 -1
  271. package/src/email/providers/index.ts +3 -2
  272. package/src/email/service.ts +3 -2
  273. package/src/errors.ts +43 -0
  274. package/src/home-base/prebuilt/seed.ts +1 -1
  275. package/src/hooks/cli.ts +6 -5
  276. package/src/hooks/config.ts +6 -8
  277. package/src/hooks/discovery.ts +6 -5
  278. package/src/hooks/manager.ts +4 -3
  279. package/src/hooks/runner.ts +2 -2
  280. package/src/hooks/templates.ts +5 -5
  281. package/src/inbound/public-ingress-urls.ts +3 -1
  282. package/src/index.ts +4 -2
  283. package/src/influencer/client.ts +1104 -0
  284. package/src/instrument.ts +4 -3
  285. package/src/logfire.ts +4 -3
  286. package/src/memory/admin.ts +25 -35
  287. package/src/memory/attachments-store.ts +4 -7
  288. package/src/memory/channel-delivery-store.ts +30 -1
  289. package/src/memory/channel-guardian-store.ts +200 -1
  290. package/src/memory/clarification-resolver.ts +37 -33
  291. package/src/memory/conflict-store.ts +67 -61
  292. package/src/memory/contradiction-checker.ts +141 -117
  293. package/src/memory/conversation-store.ts +335 -51
  294. package/src/memory/db-connection.ts +27 -4
  295. package/src/memory/db-init.ts +121 -4
  296. package/src/memory/db.ts +14 -1
  297. package/src/memory/embedding-backend.ts +27 -5
  298. package/src/memory/embedding-ollama.ts +2 -1
  299. package/src/memory/entity-extractor.ts +38 -35
  300. package/src/memory/guardian-action-store.ts +430 -0
  301. package/src/memory/inbox-escalation-projection.ts +59 -0
  302. package/src/memory/inbox-thread-store.ts +218 -0
  303. package/src/memory/ingress-invite-store.ts +338 -0
  304. package/src/memory/ingress-member-store.ts +350 -0
  305. package/src/memory/items-extractor.ts +91 -97
  306. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  307. package/src/memory/job-handlers/media-processing.ts +11 -42
  308. package/src/memory/job-handlers/summarization.ts +32 -26
  309. package/src/memory/job-utils.ts +3 -10
  310. package/src/memory/jobs-store.ts +6 -9
  311. package/src/memory/jobs-worker.ts +51 -36
  312. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  313. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  314. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  315. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  316. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  317. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  318. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  319. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  320. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  321. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  322. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  323. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  324. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  325. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  326. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  327. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  328. package/src/memory/migrations/017-memory-items-indexes.ts +12 -0
  329. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  330. package/src/memory/migrations/index.ts +24 -0
  331. package/src/memory/migrations/registry.ts +79 -0
  332. package/src/memory/migrations/validate-migration-state.ts +69 -0
  333. package/src/memory/qdrant-manager.ts +49 -8
  334. package/src/memory/query-builder.ts +1 -1
  335. package/src/memory/raw-query.ts +119 -0
  336. package/src/memory/recall-cache.ts +4 -1
  337. package/src/memory/retriever.ts +163 -47
  338. package/src/memory/schema-migration.ts +25 -984
  339. package/src/memory/schema.ts +130 -7
  340. package/src/memory/search/entity.ts +10 -19
  341. package/src/memory/search/lexical.ts +81 -52
  342. package/src/memory/search/ranking.ts +21 -22
  343. package/src/memory/search/semantic.ts +157 -19
  344. package/src/memory/shared-app-links-store.ts +4 -5
  345. package/src/memory/validation.ts +19 -0
  346. package/src/messaging/draft-store.ts +5 -6
  347. package/src/messaging/providers/sms/adapter.ts +3 -6
  348. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  349. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  350. package/src/messaging/providers/whatsapp/client.ts +67 -0
  351. package/src/messaging/style-analyzer.ts +5 -4
  352. package/src/messaging/thread-summarizer.ts +61 -69
  353. package/src/messaging/triage-engine.ts +62 -71
  354. package/src/migrations/config-merge.ts +53 -0
  355. package/src/migrations/data-layout.ts +68 -0
  356. package/src/migrations/data-merge.ts +33 -0
  357. package/src/migrations/hooks-merge.ts +90 -0
  358. package/src/migrations/index.ts +6 -0
  359. package/src/migrations/log.ts +23 -0
  360. package/src/migrations/skills-merge.ts +33 -0
  361. package/src/migrations/workspace-layout.ts +79 -0
  362. package/src/permissions/checker.ts +126 -11
  363. package/src/permissions/prompter.ts +14 -0
  364. package/src/permissions/shell-identity.ts +31 -1
  365. package/src/permissions/trust-store.ts +21 -1
  366. package/src/providers/anthropic/client.ts +4 -4
  367. package/src/providers/failover.ts +2 -2
  368. package/src/providers/model-intents.ts +70 -0
  369. package/src/providers/ollama/client.ts +2 -1
  370. package/src/providers/provider-send-message.ts +176 -0
  371. package/src/providers/registry.ts +71 -30
  372. package/src/providers/retry.ts +35 -1
  373. package/src/providers/types.ts +12 -1
  374. package/src/runtime/approval-conversation-turn.ts +97 -0
  375. package/src/runtime/approval-message-composer.ts +115 -5
  376. package/src/runtime/assistant-event-hub.ts +3 -1
  377. package/src/runtime/channel-approval-parser.ts +36 -2
  378. package/src/runtime/channel-approvals.ts +0 -21
  379. package/src/runtime/channel-guardian-service.ts +48 -7
  380. package/src/runtime/channel-readiness-service.ts +160 -34
  381. package/src/runtime/channel-readiness-types.ts +10 -4
  382. package/src/runtime/channel-retry-sweep.ts +184 -0
  383. package/src/runtime/guardian-context-resolver.ts +108 -0
  384. package/src/runtime/http-server.ts +289 -745
  385. package/src/runtime/http-types.ts +56 -3
  386. package/src/runtime/middleware/auth.ts +116 -0
  387. package/src/runtime/middleware/error-handler.ts +33 -0
  388. package/src/runtime/middleware/twilio-validation.ts +127 -0
  389. package/src/runtime/routes/app-routes.ts +1 -1
  390. package/src/runtime/routes/call-routes.ts +49 -6
  391. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  392. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  393. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  394. package/src/runtime/routes/channel-route-shared.ts +144 -0
  395. package/src/runtime/routes/channel-routes.ts +32 -1634
  396. package/src/runtime/routes/conversation-routes.ts +50 -7
  397. package/src/runtime/routes/events-routes.ts +2 -2
  398. package/src/runtime/routes/identity-routes.ts +126 -0
  399. package/src/runtime/routes/pairing-routes.ts +144 -0
  400. package/src/runtime/routes/run-routes.ts +15 -1
  401. package/src/runtime/run-orchestrator.ts +52 -34
  402. package/src/schedule/schedule-store.ts +36 -32
  403. package/src/schedule/scheduler.ts +3 -3
  404. package/src/security/encrypted-store.ts +5 -7
  405. package/src/security/oauth2.ts +45 -15
  406. package/src/security/parental-control-store.ts +183 -0
  407. package/src/security/secret-allowlist.ts +4 -3
  408. package/src/security/secret-scanner.ts +5 -5
  409. package/src/security/secure-keys.ts +1 -1
  410. package/src/security/token-manager.ts +3 -2
  411. package/src/services/vercel-deploy.ts +6 -2
  412. package/src/skills/tool-manifest.ts +3 -3
  413. package/src/skills/vellum-catalog-remote.ts +75 -16
  414. package/src/slack/slack-webhook.ts +2 -1
  415. package/src/swarm/orchestrator.ts +92 -1
  416. package/src/swarm/router-planner.ts +6 -9
  417. package/src/swarm/worker-prompts.ts +9 -12
  418. package/src/tasks/task-compiler.ts +19 -28
  419. package/src/tasks/task-runner.ts +1 -1
  420. package/src/tools/assets/search.ts +15 -14
  421. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  422. package/src/tools/browser/auto-navigate.ts +1 -0
  423. package/src/tools/browser/browser-execution.ts +13 -1
  424. package/src/tools/browser/browser-manager.ts +119 -4
  425. package/src/tools/browser/network-recorder.ts +5 -0
  426. package/src/tools/credentials/broker.ts +11 -2
  427. package/src/tools/credentials/metadata-store.ts +18 -14
  428. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  429. package/src/tools/credentials/vault.ts +49 -23
  430. package/src/tools/executor.ts +80 -18
  431. package/src/tools/host-terminal/cli-discover.ts +1 -1
  432. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  433. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  434. package/src/tools/network/script-proxy/server.ts +1 -1
  435. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  436. package/src/tools/network/web-fetch.ts +18 -2
  437. package/src/tools/network/web-search.ts +7 -3
  438. package/src/tools/reminder/reminder-store.ts +14 -15
  439. package/src/tools/schedule/create.ts +1 -0
  440. package/src/tools/schedule/list.ts +2 -1
  441. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  442. package/src/tools/skills/skill-script-runner.ts +24 -9
  443. package/src/tools/skills/skill-tool-factory.ts +1 -0
  444. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  445. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  446. package/src/tools/terminal/parser.ts +50 -0
  447. package/src/tools/watcher/delete.ts +6 -0
  448. package/src/tools/weather/service.ts +1 -1
  449. package/src/twitter/client.ts +190 -24
  450. package/src/twitter/session.ts +4 -3
  451. package/src/util/clipboard.ts +1 -1
  452. package/src/util/errors.ts +65 -8
  453. package/src/util/fs.ts +40 -0
  454. package/src/util/json.ts +10 -0
  455. package/src/util/log-redact.ts +189 -0
  456. package/src/util/logger.ts +25 -18
  457. package/src/util/object.ts +3 -0
  458. package/src/util/platform.ts +72 -365
  459. package/src/util/pricing.ts +1 -1
  460. package/src/util/promise-guard.ts +1 -1
  461. package/src/util/retry.ts +19 -0
  462. package/src/util/row-mapper.ts +79 -0
  463. package/src/util/silently.ts +21 -0
  464. package/src/watcher/engine.ts +5 -1
  465. package/src/watcher/provider-types.ts +20 -0
  466. package/src/watcher/providers/github.ts +156 -0
  467. package/src/watcher/providers/gmail.ts +1 -0
  468. package/src/watcher/providers/google-calendar.ts +1 -0
  469. package/src/watcher/providers/linear.ts +460 -0
  470. package/src/watcher/providers/slack.ts +1 -0
  471. package/src/work-items/work-item-runner.ts +1 -1
  472. package/src/workspace/git-service.ts +1 -1
  473. package/src/workspace/provider-commit-message-generator.ts +51 -22
  474. package/src/__tests__/call-bridge.test.ts +0 -517
  475. package/src/__tests__/session-process-bridge.test.ts +0 -244
  476. package/src/calls/call-bridge.ts +0 -168
  477. package/src/config/bundled-skills/media-processing/services/capability-registry.ts +0 -137
  478. package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
  479. package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
  480. package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
  481. package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
  482. package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
  483. package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
  484. package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
  485. package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
  486. package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
  487. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -1,4 +1,4 @@
1
- import { sqliteTable, text, integer, real, blob } from 'drizzle-orm/sqlite-core';
1
+ import { sqliteTable, text, integer, real, blob, index } from 'drizzle-orm/sqlite-core';
2
2
 
3
3
  export const conversations = sqliteTable('conversations', {
4
4
  id: text('id').primaryKey(),
@@ -12,7 +12,9 @@ export const conversations = sqliteTable('conversations', {
12
12
  contextCompactedMessageCount: integer('context_compacted_message_count').notNull().default(0),
13
13
  contextCompactedAt: integer('context_compacted_at'),
14
14
  threadType: text('thread_type').notNull().default('standard'),
15
+ source: text('source').notNull().default('user'),
15
16
  memoryScopeId: text('memory_scope_id').notNull().default('default'),
17
+ originChannel: text('origin_channel'),
16
18
  });
17
19
 
18
20
  export const messages = sqliteTable('messages', {
@@ -24,7 +26,9 @@ export const messages = sqliteTable('messages', {
24
26
  content: text('content').notNull(),
25
27
  createdAt: integer('created_at').notNull(),
26
28
  metadata: text('metadata'),
27
- });
29
+ }, (table) => [
30
+ index('idx_messages_conversation_id').on(table.conversationId),
31
+ ]);
28
32
 
29
33
  export const toolInvocations = sqliteTable('tool_invocations', {
30
34
  id: text('id').primaryKey(),
@@ -38,7 +42,9 @@ export const toolInvocations = sqliteTable('tool_invocations', {
38
42
  riskLevel: text('risk_level').notNull(),
39
43
  durationMs: integer('duration_ms').notNull(),
40
44
  createdAt: integer('created_at').notNull(),
41
- });
45
+ }, (table) => [
46
+ index('idx_tool_invocations_conversation_id').on(table.conversationId),
47
+ ]);
42
48
 
43
49
  export const memorySegments = sqliteTable('memory_segments', {
44
50
  id: text('id').primaryKey(),
@@ -56,7 +62,10 @@ export const memorySegments = sqliteTable('memory_segments', {
56
62
  contentHash: text('content_hash'),
57
63
  createdAt: integer('created_at').notNull(),
58
64
  updatedAt: integer('updated_at').notNull(),
59
- });
65
+ }, (table) => [
66
+ index('idx_memory_segments_scope_id').on(table.scopeId),
67
+ index('idx_memory_segments_conversation_id').on(table.conversationId),
68
+ ]);
60
69
 
61
70
  export const memoryItems = sqliteTable('memory_items', {
62
71
  id: text('id').primaryKey(),
@@ -75,7 +84,10 @@ export const memoryItems = sqliteTable('memory_items', {
75
84
  lastUsedAt: integer('last_used_at'),
76
85
  validFrom: integer('valid_from'),
77
86
  invalidAt: integer('invalid_at'),
78
- });
87
+ }, (table) => [
88
+ index('idx_memory_items_scope_id').on(table.scopeId),
89
+ index('idx_memory_items_fingerprint').on(table.fingerprint),
90
+ ]);
79
91
 
80
92
  export const memoryItemSources = sqliteTable('memory_item_sources', {
81
93
  memoryItemId: text('memory_item_id')
@@ -105,7 +117,9 @@ export const memoryItemConflicts = sqliteTable('memory_item_conflicts', {
105
117
  resolvedAt: integer('resolved_at'),
106
118
  createdAt: integer('created_at').notNull(),
107
119
  updatedAt: integer('updated_at').notNull(),
108
- });
120
+ }, (table) => [
121
+ index('idx_memory_item_conflicts_scope_id').on(table.scopeId),
122
+ ]);
109
123
 
110
124
  export const memorySummaries = sqliteTable('memory_summaries', {
111
125
  id: text('id').primaryKey(),
@@ -119,7 +133,9 @@ export const memorySummaries = sqliteTable('memory_summaries', {
119
133
  endAt: integer('end_at').notNull(),
120
134
  createdAt: integer('created_at').notNull(),
121
135
  updatedAt: integer('updated_at').notNull(),
122
- });
136
+ }, (table) => [
137
+ index('idx_memory_summaries_scope_id').on(table.scopeId),
138
+ ]);
123
139
 
124
140
  export const memoryEmbeddings = sqliteTable('memory_embeddings', {
125
141
  id: text('id').primaryKey(),
@@ -551,6 +567,8 @@ export const callSessions = sqliteTable('call_sessions', {
551
567
  status: text('status').notNull().default('initiated'),
552
568
  callerIdentityMode: text('caller_identity_mode'),
553
569
  callerIdentitySource: text('caller_identity_source'),
570
+ assistantId: text('assistant_id'),
571
+ initiatedFromConversationId: text('initiated_from_conversation_id'),
554
572
  startedAt: integer('started_at'),
555
573
  endedAt: integer('ended_at'),
556
574
  lastError: text('last_error'),
@@ -778,3 +796,108 @@ export const mediaEventFeedback = sqliteTable('media_event_feedback', {
778
796
  notes: text('notes'),
779
797
  createdAt: integer('created_at').notNull(),
780
798
  });
799
+
800
+ // ── Guardian Action Requests (cross-channel voice guardian) ──────────
801
+
802
+ export const guardianActionRequests = sqliteTable('guardian_action_requests', {
803
+ id: text('id').primaryKey(),
804
+ assistantId: text('assistant_id').notNull().default('self'),
805
+ kind: text('kind').notNull(), // 'ask_guardian'
806
+ sourceChannel: text('source_channel').notNull(), // 'voice'
807
+ sourceConversationId: text('source_conversation_id').notNull(),
808
+ callSessionId: text('call_session_id')
809
+ .notNull()
810
+ .references(() => callSessions.id, { onDelete: 'cascade' }),
811
+ pendingQuestionId: text('pending_question_id')
812
+ .notNull()
813
+ .references(() => callPendingQuestions.id, { onDelete: 'cascade' }),
814
+ questionText: text('question_text').notNull(),
815
+ requestCode: text('request_code').notNull(), // short human-readable code for routing replies
816
+ status: text('status').notNull().default('pending'), // pending | answered | expired | cancelled
817
+ answerText: text('answer_text'),
818
+ answeredByChannel: text('answered_by_channel'),
819
+ answeredByExternalUserId: text('answered_by_external_user_id'),
820
+ answeredAt: integer('answered_at'),
821
+ expiresAt: integer('expires_at').notNull(),
822
+ createdAt: integer('created_at').notNull(),
823
+ updatedAt: integer('updated_at').notNull(),
824
+ });
825
+
826
+ // ── Guardian Action Deliveries (per-channel delivery tracking) ───────
827
+
828
+ export const guardianActionDeliveries = sqliteTable('guardian_action_deliveries', {
829
+ id: text('id').primaryKey(),
830
+ requestId: text('request_id')
831
+ .notNull()
832
+ .references(() => guardianActionRequests.id, { onDelete: 'cascade' }),
833
+ destinationChannel: text('destination_channel').notNull(), // 'telegram' | 'sms' | 'macos'
834
+ destinationConversationId: text('destination_conversation_id'),
835
+ destinationChatId: text('destination_chat_id'),
836
+ destinationExternalUserId: text('destination_external_user_id'),
837
+ status: text('status').notNull().default('pending'), // pending | sent | failed | answered | expired | cancelled
838
+ sentAt: integer('sent_at'),
839
+ respondedAt: integer('responded_at'),
840
+ lastError: text('last_error'),
841
+ createdAt: integer('created_at').notNull(),
842
+ updatedAt: integer('updated_at').notNull(),
843
+ });
844
+
845
+ // ── Assistant Inbox ──────────────────────────────────────────────────
846
+
847
+ export const assistantIngressInvites = sqliteTable('assistant_ingress_invites', {
848
+ id: text('id').primaryKey(),
849
+ assistantId: text('assistant_id').notNull().default('self'),
850
+ sourceChannel: text('source_channel').notNull(),
851
+ tokenHash: text('token_hash').notNull(),
852
+ createdBySessionId: text('created_by_session_id'),
853
+ note: text('note'),
854
+ maxUses: integer('max_uses').notNull().default(1),
855
+ useCount: integer('use_count').notNull().default(0),
856
+ expiresAt: integer('expires_at').notNull(),
857
+ status: text('status').notNull().default('active'),
858
+ redeemedByExternalUserId: text('redeemed_by_external_user_id'),
859
+ redeemedByExternalChatId: text('redeemed_by_external_chat_id'),
860
+ redeemedAt: integer('redeemed_at'),
861
+ createdAt: integer('created_at').notNull(),
862
+ updatedAt: integer('updated_at').notNull(),
863
+ });
864
+
865
+ export const assistantIngressMembers = sqliteTable('assistant_ingress_members', {
866
+ id: text('id').primaryKey(),
867
+ assistantId: text('assistant_id').notNull().default('self'),
868
+ sourceChannel: text('source_channel').notNull(),
869
+ externalUserId: text('external_user_id'),
870
+ externalChatId: text('external_chat_id'),
871
+ displayName: text('display_name'),
872
+ username: text('username'),
873
+ status: text('status').notNull().default('pending'),
874
+ policy: text('policy').notNull().default('allow'),
875
+ inviteId: text('invite_id')
876
+ .references(() => assistantIngressInvites.id),
877
+ createdBySessionId: text('created_by_session_id'),
878
+ revokedReason: text('revoked_reason'),
879
+ blockedReason: text('blocked_reason'),
880
+ lastSeenAt: integer('last_seen_at'),
881
+ createdAt: integer('created_at').notNull(),
882
+ updatedAt: integer('updated_at').notNull(),
883
+ });
884
+
885
+ export const assistantInboxThreadState = sqliteTable('assistant_inbox_thread_state', {
886
+ conversationId: text('conversation_id')
887
+ .primaryKey()
888
+ .references(() => conversations.id, { onDelete: 'cascade' }),
889
+ assistantId: text('assistant_id').notNull().default('self'),
890
+ sourceChannel: text('source_channel').notNull(),
891
+ externalChatId: text('external_chat_id').notNull(),
892
+ externalUserId: text('external_user_id'),
893
+ displayName: text('display_name'),
894
+ username: text('username'),
895
+ lastInboundAt: integer('last_inbound_at'),
896
+ lastOutboundAt: integer('last_outbound_at'),
897
+ lastMessageAt: integer('last_message_at'),
898
+ unreadCount: integer('unread_count').notNull().default(0),
899
+ pendingEscalationCount: integer('pending_escalation_count').notNull().default(0),
900
+ hasPendingEscalation: integer('has_pending_escalation').notNull().default(0),
901
+ createdAt: integer('created_at').notNull(),
902
+ updatedAt: integer('updated_at').notNull(),
903
+ });
@@ -1,7 +1,7 @@
1
1
  import { and, desc, eq, inArray, isNull, or } from 'drizzle-orm';
2
2
  import type { MemoryEntityConfig } from '../../config/types.js';
3
3
  import { getLogger } from '../../util/logger.js';
4
- import { getDb } from '../db.js';
4
+ import { getDb, rawAll } from '../db.js';
5
5
  import {
6
6
  memoryEntityRelations,
7
7
  memoryItemEntities,
@@ -130,8 +130,6 @@ export function findMatchedEntities(query: string, maxMatches: number): MatchedE
130
130
  const trimmed = query.trim();
131
131
  if (trimmed.length === 0) return [];
132
132
 
133
- const db = getDb();
134
- const raw = (db as unknown as { $client: { query: (q: string) => { all: (...params: unknown[]) => unknown[] } } }).$client;
135
133
  const safeLimit = Math.max(1, Math.floor(maxMatches));
136
134
 
137
135
  // Tokenize query into words for entity matching (min length 3 to reduce false positives)
@@ -173,14 +171,12 @@ export function findMatchedEntities(query: string, maxMatches: number): MatchedE
173
171
  queryParams = [fullQuery, fullQuery];
174
172
  }
175
173
 
176
- let matchedEntities: MatchedEntityRow[] = [];
177
174
  try {
178
- matchedEntities = raw.query(entityQuery).all(...queryParams) as MatchedEntityRow[];
175
+ return rawAll<MatchedEntityRow>(entityQuery, ...queryParams);
179
176
  } catch (err) {
180
177
  log.warn({ err }, 'Entity search query failed');
181
178
  return [];
182
179
  }
183
- return matchedEntities;
184
180
  }
185
181
 
186
182
  /**
@@ -225,9 +221,10 @@ export function findNeighborEntities(
225
221
  const frontierPlaceholders = frontier.map(() => '?').join(',');
226
222
  const limit = Math.max(1, edgeBudget);
227
223
 
228
- const raw = (db as unknown as { $client: { query: (q: string) => { all: (...params: unknown[]) => unknown[] } } }).$client;
229
224
  const relationParams = relationTypes && relationTypes.length > 0 ? relationTypes : [];
230
225
 
226
+ type EdgeRow = { sourceEntityId: string; targetEntityId: string };
227
+
231
228
  if (directed) {
232
229
  // GROUP BY deduplicates entity pairs that have multiple relation rows
233
230
  const q1 = `
@@ -240,8 +237,7 @@ export function findNeighborEntities(
240
237
  ORDER BY MAX(r.last_seen_at) DESC
241
238
  LIMIT ?
242
239
  `;
243
- const params1 = [...frontier, ...relationParams, ...entityTypes, limit];
244
- rows = raw.query(q1).all(...params1) as Array<{ sourceEntityId: string; targetEntityId: string }>;
240
+ rows = rawAll<EdgeRow>(q1, ...frontier, ...relationParams, ...entityTypes, limit);
245
241
  } else {
246
242
  // Combine both directions in a single query with global recency
247
243
  // ordering so the edge budget isn't direction-biased.
@@ -263,17 +259,12 @@ export function findNeighborEntities(
263
259
  ORDER BY MAX(last_seen_at) DESC
264
260
  LIMIT ?
265
261
  `;
266
- const params = [
267
- ...frontier,
268
- ...relationParams,
269
- ...entityTypes,
270
- ...frontier,
271
- ...relationParams,
272
- ...entityTypes,
262
+ rows = rawAll<EdgeRow>(
263
+ q,
264
+ ...frontier, ...relationParams, ...entityTypes,
265
+ ...frontier, ...relationParams, ...entityTypes,
273
266
  limit,
274
- ];
275
-
276
- rows = raw.query(q).all(...params) as Array<{ sourceEntityId: string; targetEntityId: string }>;
267
+ );
277
268
  }
278
269
  } else {
279
270
  const frontierCondition = directed
@@ -1,62 +1,91 @@
1
1
  import { and, desc, eq, inArray, notInArray } from 'drizzle-orm';
2
2
  import { getLogger } from '../../util/logger.js';
3
- import { getDb } from '../db.js';
3
+ import { getDb, rawAll } from '../db.js';
4
4
  import { memorySegments } from '../schema.js';
5
5
  import type { Candidate, CandidateType } from './types.js';
6
6
  import { computeRecencyScore } from './ranking.js';
7
7
 
8
8
  const log = getLogger('memory-retriever');
9
9
 
10
+ // Threshold beyond which a raw SQL query is considered slow and logged as a warning.
11
+ const SLOW_QUERY_MS = 2000;
12
+
13
+ /**
14
+ * Execute a synchronous raw SQL query with timing. Logs a warning if the
15
+ * query exceeds SLOW_QUERY_MS. Since bun:sqlite queries are synchronous,
16
+ * we can't abort them mid-execution, but we can detect and report slowness.
17
+ */
18
+ function timedRawQuery<T>(label: string, fn: () => T[]): T[] {
19
+ const start = performance.now();
20
+ const result = fn();
21
+ const elapsed = performance.now() - start;
22
+ if (elapsed >= SLOW_QUERY_MS) {
23
+ log.warn({ label, elapsedMs: Math.round(elapsed) }, 'Raw SQL query exceeded slow threshold');
24
+ }
25
+ return result;
26
+ }
27
+
28
+ interface FtsRow {
29
+ segment_id: string;
30
+ message_id: string;
31
+ text: string;
32
+ created_at: number;
33
+ rank: number;
34
+ }
35
+
10
36
  export function lexicalSearch(query: string, limit: number, excludedMessageIds: string[] = [], scopeIds?: string[]): Candidate[] {
11
37
  const trimmed = query.trim();
12
38
  if (trimmed.length === 0 || limit <= 0) return [];
13
39
  const matchQuery = buildFtsMatchQuery(trimmed);
14
40
  if (!matchQuery) return [];
15
41
  const excluded = new Set(excludedMessageIds);
16
- const db = getDb();
17
- const raw = (db as unknown as { $client: { query: (q: string) => { all: (...params: unknown[]) => unknown[] } } }).$client;
18
- let rows: Array<{
19
- segment_id: string;
20
- message_id: string;
21
- text: string;
22
- created_at: number;
23
- rank: number;
24
- }> = [];
25
- const queryLimit = excluded.size > 0
26
- ? Math.max(limit + 24, limit * 2)
27
- : limit;
28
42
  const scopeClause = scopeIds
29
43
  ? ` AND s.scope_id IN (${scopeIds.map(() => '?').join(',')})`
30
44
  : '';
31
- const params: unknown[] = [matchQuery, ...(scopeIds ?? []), queryLimit];
32
- try {
33
- rows = raw.query(`
34
- SELECT
35
- f.segment_id AS segment_id,
36
- s.message_id AS message_id,
37
- s.text AS text,
38
- s.created_at AS created_at,
39
- bm25(memory_segment_fts) AS rank
40
- FROM memory_segment_fts f
41
- JOIN memory_segments s ON s.id = f.segment_id
42
- WHERE memory_segment_fts MATCH ?${scopeClause}
43
- ORDER BY rank
44
- LIMIT ?
45
- `).all(...params) as Array<{
46
- segment_id: string;
47
- message_id: string;
48
- text: string;
49
- created_at: number;
50
- rank: number;
51
- }>;
52
- } catch (err) {
53
- log.warn({ err, query: truncate(trimmed, 80) }, 'Memory lexical search query parse failed');
54
- return [];
45
+
46
+ // Adaptive overfetch: when exclusions are present, double the SQL LIMIT until we
47
+ // collect `limit` visible rows or the DB has no more matching results to offer.
48
+ // This handles dense exclusion sets without wasteful fixed-ratio over-fetching.
49
+ const MAX_FETCH_MULTIPLIER = 8;
50
+ let queryLimit = excluded.size > 0 ? Math.max(limit * 2, limit + 24) : limit;
51
+ let visibleRows: FtsRow[] = [];
52
+
53
+ while (true) {
54
+ let rows: FtsRow[] = [];
55
+ try {
56
+ rows = timedRawQuery('lexicalSearch', () =>
57
+ rawAll<FtsRow>(`
58
+ SELECT
59
+ f.segment_id AS segment_id,
60
+ s.message_id AS message_id,
61
+ s.text AS text,
62
+ s.created_at AS created_at,
63
+ bm25(memory_segment_fts) AS rank
64
+ FROM memory_segment_fts f
65
+ JOIN memory_segments s ON s.id = f.segment_id
66
+ WHERE memory_segment_fts MATCH ?${scopeClause}
67
+ ORDER BY rank
68
+ LIMIT ?
69
+ `, matchQuery, ...(scopeIds ?? []), queryLimit),
70
+ );
71
+ } catch (err) {
72
+ log.warn({ err, query: truncate(trimmed, 80) }, 'Memory lexical search query parse failed');
73
+ return [];
74
+ }
75
+
76
+ visibleRows = excluded.size > 0
77
+ ? rows.filter((row) => !excluded.has(row.message_id))
78
+ : rows;
79
+
80
+ // Stop when we have enough rows, the DB returned fewer than requested
81
+ // (no more results exist), or we've reached the safety cap.
82
+ if (visibleRows.length >= limit || rows.length < queryLimit || queryLimit >= limit * MAX_FETCH_MULTIPLIER) {
83
+ break;
84
+ }
85
+ queryLimit = Math.min(queryLimit * 2, limit * MAX_FETCH_MULTIPLIER);
55
86
  }
56
87
 
57
- const visibleRows = excluded.size > 0
58
- ? rows.filter((row) => !excluded.has(row.message_id)).slice(0, limit)
59
- : rows;
88
+ visibleRows = visibleRows.slice(0, limit);
60
89
 
61
90
  const finiteRanks = visibleRows
62
91
  .map((row) => row.rank)
@@ -124,17 +153,19 @@ export function recencySearch(conversationId: string, limit: number, excludedMes
124
153
  * Supplements FTS-based lexical search with LIKE-based matching on items.
125
154
  */
126
155
  export function directItemSearch(query: string, limit: number, scopeIds?: string[]): Candidate[] {
127
- const db = getDb();
128
156
  const tokens = [...new Set(query
129
157
  .toLowerCase()
130
158
  .split(/[^a-z0-9_.-]+/g)
131
159
  .filter((t) => t.length >= 2))];
132
160
  if (tokens.length === 0) return [];
133
161
 
134
- const raw = (db as unknown as { $client: { query: (q: string) => { all: (...params: unknown[]) => unknown[] } } }).$client;
135
162
  const likeClauses = tokens.map(
136
- (t) => `(LOWER(subject) LIKE '%${escapeSqlLike(t)}%' OR LOWER(statement) LIKE '%${escapeSqlLike(t)}%')`,
163
+ () => `(LOWER(subject) LIKE ? OR LOWER(statement) LIKE ?)`,
137
164
  );
165
+ const likeParams = tokens.flatMap((t) => {
166
+ const pattern = `%${escapeLikeWildcards(t)}%`;
167
+ return [pattern, pattern];
168
+ });
138
169
  const scopeClause = scopeIds
139
170
  ? ` AND scope_id IN (${scopeIds.map(() => '?').join(',')})`
140
171
  : '';
@@ -145,9 +176,8 @@ export function directItemSearch(query: string, limit: number, scopeIds?: string
145
176
  ORDER BY last_seen_at DESC
146
177
  LIMIT ?
147
178
  `;
148
- const params: unknown[] = [...(scopeIds ?? []), limit];
149
179
 
150
- let rows: Array<{
180
+ interface ItemRow {
151
181
  id: string;
152
182
  kind: string;
153
183
  subject: string;
@@ -156,9 +186,13 @@ export function directItemSearch(query: string, limit: number, scopeIds?: string
156
186
  importance: number | null;
157
187
  first_seen_at: number;
158
188
  last_seen_at: number;
159
- }> = [];
189
+ }
190
+
191
+ let rows: ItemRow[] = [];
160
192
  try {
161
- rows = raw.query(sqlQuery).all(...params) as typeof rows;
193
+ rows = timedRawQuery('directItemSearch', () =>
194
+ rawAll<ItemRow>(sqlQuery, ...likeParams, ...(scopeIds ?? []), limit),
195
+ );
162
196
  } catch {
163
197
  return [];
164
198
  }
@@ -212,11 +246,6 @@ export function buildFtsMatchQuery(text: string): string | null {
212
246
  .join(' OR ');
213
247
  }
214
248
 
215
- export function escapeSqlLike(s: string): string {
216
- return s.replace(/'/g, "''").replace(/%/g, '').replace(/_/g, '');
217
- }
218
-
219
- /** Escape only LIKE wildcards (% and _) for use with parameterized queries where the driver handles quoting. */
220
249
  export function escapeLikeWildcards(s: string): string {
221
250
  return s.replace(/%/g, '').replace(/_/g, '');
222
251
  }
@@ -1,9 +1,8 @@
1
1
  import { inArray, sql } from 'drizzle-orm';
2
- import Anthropic from '@anthropic-ai/sdk';
3
2
  import type { AssistantConfig, MemoryRerankingConfig } from '../../config/types.js';
4
- import { getConfig } from '../../config/loader.js';
5
3
  import { estimateTextTokens } from '../../context/token-estimator.js';
6
4
  import { getLogger } from '../../util/logger.js';
5
+ import { getConfiguredProvider, extractText, userMessage } from '../../providers/provider-send-message.js';
7
6
  import { getDb } from '../db.js';
8
7
  import { memoryItems } from '../schema.js';
9
8
  import type { Candidate, CandidateSource, ItemMetadata } from './types.js';
@@ -296,10 +295,9 @@ export async function rerankWithLLM(
296
295
  candidates: Candidate[],
297
296
  rerankingConfig: MemoryRerankingConfig,
298
297
  ): Promise<Candidate[]> {
299
- const config = getConfig();
300
- const apiKey = config.apiKeys.anthropic ?? process.env.ANTHROPIC_API_KEY;
301
- if (!apiKey) {
302
- log.debug('No Anthropic API key available for LLM re-ranking, skipping');
298
+ const provider = getConfiguredProvider();
299
+ if (!provider) {
300
+ log.debug('Configured provider unavailable for LLM re-ranking, skipping');
303
301
  return candidates;
304
302
  }
305
303
 
@@ -309,26 +307,27 @@ export async function rerankWithLLM(
309
307
  text: truncate(c.text, 200),
310
308
  }));
311
309
 
312
- const client = new Anthropic({ apiKey });
313
- const response = await client.messages.create({
314
- model: rerankingConfig.model,
315
- max_tokens: 1024,
316
- system: 'You are a relevance scoring assistant. Given a query and a list of memory candidates, rate each candidate\'s relevance to the query on a scale of 0-10. Return ONLY a JSON array of objects with "index" (the candidate index) and "score" (0-10 integer). No explanation.',
317
- messages: [{
318
- role: 'user',
319
- content: `Query: ${truncate(query, 200)}\n\nCandidates:\n${candidateList.map((c) => `[${c.index}] ${c.text}`).join('\n')}`,
320
- }],
321
- });
310
+ const response = await provider.sendMessage(
311
+ [userMessage(`Query: ${truncate(query, 200)}\n\nCandidates:\n${candidateList.map((c) => `[${c.index}] ${c.text}`).join('\n')}`)],
312
+ undefined,
313
+ 'You are a relevance scoring assistant. Given a query and a list of memory candidates, rate each candidate\'s relevance to the query on a scale of 0-10. Return ONLY a JSON array of objects with "index" (the candidate index) and "score" (0-10 integer). No explanation.',
314
+ {
315
+ config: {
316
+ model: rerankingConfig.model,
317
+ max_tokens: 1024,
318
+ },
319
+ },
320
+ );
322
321
 
323
322
  // Extract text from the response
324
- const textBlock = response.content.find((block) => block.type === 'text');
325
- if (!textBlock || textBlock.type !== 'text') {
323
+ const responseText = extractText(response);
324
+ if (!responseText) {
326
325
  log.warn('LLM re-ranking returned no text block, skipping');
327
326
  return candidates;
328
327
  }
329
328
 
330
329
  // Parse the JSON array from the response
331
- const jsonMatch = textBlock.text.match(/\[[\s\S]*\]/);
330
+ const jsonMatch = responseText.match(/\[[\s\S]*\]/);
332
331
  if (!jsonMatch) {
333
332
  log.warn('LLM re-ranking response did not contain JSON array, skipping');
334
333
  return candidates;
@@ -359,10 +358,10 @@ export async function rerankWithLLM(
359
358
 
360
359
  reranked.sort((a, b) => {
361
360
  // Scored items come before unscored items
362
- if (a.llmScore !== null && b.llmScore === null) return -1;
363
- if (a.llmScore === null && b.llmScore !== null) return 1;
361
+ if (a.llmScore != null && b.llmScore == null) return -1;
362
+ if (a.llmScore == null && b.llmScore != null) return 1;
364
363
  // Both scored: sort by score descending
365
- if (a.llmScore !== null && b.llmScore !== null) {
364
+ if (a.llmScore != null && b.llmScore != null) {
366
365
  const scoreDelta = b.llmScore - a.llmScore;
367
366
  if (scoreDelta !== 0) return scoreDelta;
368
367
  }