@vellumai/assistant 0.4.49 → 0.4.51

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 (353) hide show
  1. package/ARCHITECTURE.md +24 -33
  2. package/README.md +3 -3
  3. package/docs/architecture/integrations.md +2 -2
  4. package/docs/architecture/keychain-broker.md +6 -6
  5. package/docs/architecture/memory.md +180 -119
  6. package/knip.json +32 -0
  7. package/package.json +3 -2
  8. package/src/__tests__/agent-loop.test.ts +3 -1
  9. package/src/__tests__/anthropic-provider.test.ts +114 -23
  10. package/src/__tests__/approval-cascade.test.ts +1 -15
  11. package/src/__tests__/approval-routes-http.test.ts +2 -0
  12. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  13. package/src/__tests__/btw-routes.test.ts +61 -5
  14. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  15. package/src/__tests__/checker.test.ts +13 -0
  16. package/src/__tests__/config-schema.test.ts +1 -68
  17. package/src/__tests__/config-watcher.test.ts +8 -0
  18. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  19. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  20. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  21. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  22. package/src/__tests__/credential-security-invariants.test.ts +8 -7
  23. package/src/__tests__/credential-vault-unit.test.ts +23 -18
  24. package/src/__tests__/credential-vault.test.ts +30 -18
  25. package/src/__tests__/credentials-cli.test.ts +257 -82
  26. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  27. package/src/__tests__/date-context.test.ts +93 -77
  28. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  29. package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
  30. package/src/__tests__/history-repair.test.ts +245 -0
  31. package/src/__tests__/host-cu-proxy.test.ts +165 -3
  32. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  33. package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
  34. package/src/__tests__/integration-status.test.ts +31 -30
  35. package/src/__tests__/invite-redemption-service.test.ts +166 -13
  36. package/src/__tests__/invite-routes-http.test.ts +166 -5
  37. package/src/__tests__/keychain-broker-client.test.ts +4 -4
  38. package/src/__tests__/list-messages-attachments.test.ts +193 -0
  39. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  40. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  41. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  42. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  43. package/src/__tests__/memory-regressions.test.ts +477 -2841
  44. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  45. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  46. package/src/__tests__/mime-builder.test.ts +28 -0
  47. package/src/__tests__/native-web-search.test.ts +1 -0
  48. package/src/__tests__/oauth-cli.test.ts +824 -31
  49. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  50. package/src/__tests__/oauth-store.test.ts +363 -17
  51. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  52. package/src/__tests__/registry.test.ts +0 -1
  53. package/src/__tests__/relay-server.test.ts +55 -1
  54. package/src/__tests__/schedule-tools.test.ts +32 -0
  55. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  56. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  57. package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
  58. package/src/__tests__/secure-keys.test.ts +78 -18
  59. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  60. package/src/__tests__/server-history-render.test.ts +2 -2
  61. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  62. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  63. package/src/__tests__/session-agent-loop.test.ts +19 -15
  64. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  65. package/src/__tests__/session-error.test.ts +124 -2
  66. package/src/__tests__/session-history-web-search.test.ts +918 -0
  67. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  68. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  69. package/src/__tests__/session-queue.test.ts +37 -27
  70. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  71. package/src/__tests__/session-slash-known.test.ts +1 -15
  72. package/src/__tests__/session-slash-queue.test.ts +1 -15
  73. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  74. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  75. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  76. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  77. package/src/__tests__/skills-install-extract.test.ts +93 -0
  78. package/src/__tests__/skills.test.ts +2 -2
  79. package/src/__tests__/skillssh-registry.test.ts +451 -0
  80. package/src/__tests__/slack-channel-config.test.ts +10 -8
  81. package/src/__tests__/trust-store.test.ts +15 -0
  82. package/src/__tests__/twilio-config.test.ts +11 -10
  83. package/src/__tests__/twilio-provider.test.ts +9 -4
  84. package/src/__tests__/voice-invite-redemption.test.ts +85 -5
  85. package/src/agent/ax-tree-compaction.test.ts +51 -0
  86. package/src/agent/loop.ts +39 -12
  87. package/src/approvals/AGENTS.md +1 -1
  88. package/src/approvals/guardian-request-resolvers.ts +14 -2
  89. package/src/bundler/compiler-tools.ts +66 -2
  90. package/src/calls/call-domain.ts +134 -3
  91. package/src/calls/call-store.ts +6 -0
  92. package/src/calls/relay-server.ts +44 -6
  93. package/src/calls/relay-setup-router.ts +17 -1
  94. package/src/calls/twilio-config.ts +5 -4
  95. package/src/calls/twilio-provider.ts +14 -9
  96. package/src/calls/twilio-rest.ts +10 -7
  97. package/src/calls/types.ts +3 -1
  98. package/src/cli/commands/config.ts +14 -9
  99. package/src/cli/commands/contacts.ts +3 -0
  100. package/src/cli/commands/credentials.ts +170 -174
  101. package/src/cli/commands/doctor.ts +11 -8
  102. package/src/cli/commands/keys.ts +9 -9
  103. package/src/cli/commands/mcp.ts +46 -59
  104. package/src/cli/commands/memory.ts +16 -165
  105. package/src/cli/commands/oauth/apps.ts +68 -10
  106. package/src/cli/commands/oauth/connections.ts +475 -105
  107. package/src/cli/commands/oauth/index.ts +3 -3
  108. package/src/cli/commands/oauth/providers.ts +18 -4
  109. package/src/cli/commands/sessions.ts +5 -2
  110. package/src/cli/commands/skills.ts +173 -1
  111. package/src/cli/http-client.ts +0 -20
  112. package/src/cli/main-screen.tsx +2 -2
  113. package/src/cli/program.ts +5 -6
  114. package/src/cli.ts +20 -22
  115. package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
  116. package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
  117. package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
  118. package/src/config/bundled-skills/contacts/SKILL.md +35 -11
  119. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  120. package/src/config/bundled-skills/gmail/SKILL.md +1 -1
  121. package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
  122. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
  123. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
  124. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
  125. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
  126. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
  127. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
  128. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
  129. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
  130. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
  131. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
  132. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
  133. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
  134. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
  135. package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
  136. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
  137. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
  138. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
  139. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
  140. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
  141. package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
  142. package/src/config/bundled-skills/messaging/SKILL.md +1 -1
  143. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  144. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  145. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
  146. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
  147. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
  148. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
  149. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
  150. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
  151. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
  152. package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
  153. package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
  154. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
  155. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
  156. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
  157. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
  158. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
  159. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
  160. package/src/config/bundled-tool-registry.ts +2 -5
  161. package/src/config/loader.ts +6 -42
  162. package/src/config/schema.ts +1 -12
  163. package/src/config/schemas/memory-lifecycle.ts +0 -9
  164. package/src/config/schemas/memory-processing.ts +0 -180
  165. package/src/config/schemas/memory-retrieval.ts +32 -104
  166. package/src/config/schemas/memory.ts +0 -10
  167. package/src/config/types.ts +0 -4
  168. package/src/contacts/contact-store.ts +39 -2
  169. package/src/contacts/contacts-write.ts +9 -0
  170. package/src/context/window-manager.ts +4 -1
  171. package/src/daemon/config-watcher.ts +55 -2
  172. package/src/daemon/daemon-control.ts +1 -1
  173. package/src/daemon/date-context.ts +114 -31
  174. package/src/daemon/handlers/config-ingress.ts +2 -2
  175. package/src/daemon/handlers/config-slack-channel.ts +59 -39
  176. package/src/daemon/handlers/config-telegram.ts +23 -14
  177. package/src/daemon/handlers/session-history.ts +1 -358
  178. package/src/daemon/handlers/sessions.ts +18 -13
  179. package/src/daemon/handlers/shared.ts +3 -17
  180. package/src/daemon/handlers/skills.ts +20 -1
  181. package/src/daemon/history-repair.ts +72 -8
  182. package/src/daemon/host-cu-proxy.ts +55 -26
  183. package/src/daemon/lifecycle.ts +39 -4
  184. package/src/daemon/mcp-reload-service.ts +2 -2
  185. package/src/daemon/message-types/computer-use.ts +1 -12
  186. package/src/daemon/message-types/memory.ts +4 -16
  187. package/src/daemon/message-types/messages.ts +1 -0
  188. package/src/daemon/message-types/sessions.ts +4 -42
  189. package/src/daemon/server.ts +6 -1
  190. package/src/daemon/session-agent-loop-handlers.ts +38 -0
  191. package/src/daemon/session-agent-loop.ts +334 -48
  192. package/src/daemon/session-error.ts +89 -6
  193. package/src/daemon/session-history.ts +17 -7
  194. package/src/daemon/session-media-retry.ts +6 -2
  195. package/src/daemon/session-memory.ts +69 -149
  196. package/src/daemon/session-process.ts +10 -1
  197. package/src/daemon/session-runtime-assembly.ts +49 -19
  198. package/src/daemon/session-slash.ts +3 -5
  199. package/src/daemon/session-surfaces.ts +4 -1
  200. package/src/daemon/session-tool-setup.ts +7 -1
  201. package/src/daemon/session.ts +12 -2
  202. package/src/email/providers/index.ts +2 -2
  203. package/src/instrument.ts +61 -1
  204. package/src/media/avatar-router.ts +1 -1
  205. package/src/memory/admin.ts +2 -191
  206. package/src/memory/canonical-guardian-store.ts +38 -2
  207. package/src/memory/conversation-crud.ts +0 -33
  208. package/src/memory/conversation-queries.ts +25 -83
  209. package/src/memory/db-init.ts +32 -0
  210. package/src/memory/embedding-backend.ts +84 -8
  211. package/src/memory/embedding-types.ts +9 -1
  212. package/src/memory/indexer.ts +7 -46
  213. package/src/memory/invite-store.ts +19 -0
  214. package/src/memory/items-extractor.ts +274 -76
  215. package/src/memory/job-handlers/backfill.ts +2 -127
  216. package/src/memory/job-handlers/cleanup.ts +2 -16
  217. package/src/memory/job-handlers/extraction.ts +2 -138
  218. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  219. package/src/memory/job-handlers/summarization.ts +3 -148
  220. package/src/memory/job-utils.ts +21 -59
  221. package/src/memory/jobs-store.ts +1 -159
  222. package/src/memory/jobs-worker.ts +9 -52
  223. package/src/memory/migrations/104-core-indexes.ts +3 -3
  224. package/src/memory/migrations/149-oauth-tables.ts +2 -0
  225. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  226. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  227. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  228. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  229. package/src/memory/migrations/154-drop-fts.ts +20 -0
  230. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  231. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  232. package/src/memory/migrations/157-invite-contact-id.ts +104 -0
  233. package/src/memory/migrations/index.ts +8 -0
  234. package/src/memory/migrations/registry.ts +6 -0
  235. package/src/memory/qdrant-client.ts +148 -51
  236. package/src/memory/raw-query.ts +1 -1
  237. package/src/memory/retriever.test.ts +294 -273
  238. package/src/memory/retriever.ts +421 -645
  239. package/src/memory/schema/calls.ts +2 -0
  240. package/src/memory/schema/contacts.ts +1 -0
  241. package/src/memory/schema/memory-core.ts +3 -48
  242. package/src/memory/schema/oauth.ts +2 -0
  243. package/src/memory/search/formatting.ts +263 -176
  244. package/src/memory/search/lexical.ts +1 -254
  245. package/src/memory/search/ranking.ts +0 -455
  246. package/src/memory/search/semantic.ts +100 -14
  247. package/src/memory/search/staleness.ts +47 -0
  248. package/src/memory/search/tier-classifier.ts +21 -0
  249. package/src/memory/search/types.ts +15 -77
  250. package/src/memory/task-memory-cleanup.ts +4 -6
  251. package/src/messaging/provider.ts +1 -1
  252. package/src/messaging/providers/gmail/adapter.ts +1 -1
  253. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  254. package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
  255. package/src/messaging/providers/whatsapp/adapter.ts +13 -9
  256. package/src/messaging/registry.ts +9 -5
  257. package/src/oauth/byo-connection.test.ts +40 -25
  258. package/src/oauth/connect-orchestrator.ts +4 -10
  259. package/src/oauth/connection-resolver.ts +20 -6
  260. package/src/oauth/manual-token-connection.ts +5 -5
  261. package/src/oauth/oauth-store.ts +183 -31
  262. package/src/oauth/platform-connection.test.ts +1 -1
  263. package/src/oauth/provider-behaviors.ts +503 -4
  264. package/src/oauth/seed-providers.ts +214 -8
  265. package/src/oauth/token-persistence.ts +31 -16
  266. package/src/permissions/defaults.ts +1 -0
  267. package/src/permissions/trust-store.ts +23 -1
  268. package/src/playbooks/playbook-compiler.ts +1 -1
  269. package/src/prompts/system-prompt.ts +18 -2
  270. package/src/providers/anthropic/client.ts +56 -126
  271. package/src/providers/types.ts +7 -1
  272. package/src/runtime/AGENTS.md +9 -0
  273. package/src/runtime/auth/route-policy.ts +6 -3
  274. package/src/runtime/channel-readiness-service.ts +48 -40
  275. package/src/runtime/guardian-reply-router.ts +24 -22
  276. package/src/runtime/http-server.ts +2 -2
  277. package/src/runtime/http-types.ts +2 -0
  278. package/src/runtime/invite-redemption-service.ts +72 -12
  279. package/src/runtime/invite-service.ts +43 -0
  280. package/src/runtime/middleware/twilio-validation.ts +1 -1
  281. package/src/runtime/pending-interactions.ts +2 -2
  282. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  283. package/src/runtime/routes/btw-routes.ts +10 -5
  284. package/src/runtime/routes/conversation-routes.ts +56 -11
  285. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  286. package/src/runtime/routes/integrations/slack/channel.ts +2 -2
  287. package/src/runtime/routes/integrations/telegram.ts +2 -2
  288. package/src/runtime/routes/integrations/twilio.ts +17 -17
  289. package/src/runtime/routes/invite-routes.ts +29 -4
  290. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  291. package/src/runtime/routes/memory-item-routes.ts +503 -0
  292. package/src/runtime/routes/secret-routes.ts +17 -0
  293. package/src/runtime/routes/session-management-routes.ts +3 -3
  294. package/src/runtime/routes/settings-routes.ts +3 -3
  295. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  296. package/src/runtime/routes/workspace-routes.ts +9 -4
  297. package/src/runtime/routes/workspace-utils.ts +8 -2
  298. package/src/schedule/integration-status.ts +26 -19
  299. package/src/security/keychain-broker-client.ts +17 -4
  300. package/src/security/oauth2.ts +6 -7
  301. package/src/security/secure-keys.ts +44 -19
  302. package/src/security/token-manager.ts +46 -39
  303. package/src/services/vercel-deploy.ts +0 -24
  304. package/src/signals/confirm.ts +78 -0
  305. package/src/signals/mcp-reload.ts +18 -0
  306. package/src/skills/catalog-install.ts +74 -18
  307. package/src/skills/skillssh-registry.ts +503 -0
  308. package/src/tools/assets/search.ts +5 -1
  309. package/src/tools/computer-use/definitions.ts +0 -10
  310. package/src/tools/computer-use/registry.ts +1 -1
  311. package/src/tools/credentials/vault.ts +22 -7
  312. package/src/tools/memory/definitions.ts +4 -13
  313. package/src/tools/memory/handlers.test.ts +83 -103
  314. package/src/tools/memory/handlers.ts +50 -85
  315. package/src/tools/network/script-proxy/session-manager.ts +8 -8
  316. package/src/tools/schedule/create.ts +10 -3
  317. package/src/tools/schedule/update.ts +8 -1
  318. package/src/tools/skills/load.ts +25 -2
  319. package/src/watcher/provider-types.ts +1 -1
  320. package/src/watcher/providers/github.ts +1 -1
  321. package/src/watcher/providers/gmail.ts +3 -3
  322. package/src/watcher/providers/google-calendar.ts +3 -3
  323. package/src/watcher/providers/linear.ts +1 -1
  324. package/src/__tests__/clarification-resolver.test.ts +0 -193
  325. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  326. package/src/__tests__/conflict-policy.test.ts +0 -269
  327. package/src/__tests__/conflict-store.test.ts +0 -372
  328. package/src/__tests__/contradiction-checker.test.ts +0 -361
  329. package/src/__tests__/entity-extractor.test.ts +0 -211
  330. package/src/__tests__/entity-search.test.ts +0 -1117
  331. package/src/__tests__/profile-compiler.test.ts +0 -392
  332. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  333. package/src/__tests__/session-profile-injection.test.ts +0 -557
  334. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  335. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  336. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  337. package/src/daemon/session-conflict-gate.ts +0 -167
  338. package/src/daemon/session-dynamic-profile.ts +0 -77
  339. package/src/memory/clarification-resolver.ts +0 -417
  340. package/src/memory/conflict-intent.ts +0 -205
  341. package/src/memory/conflict-policy.ts +0 -127
  342. package/src/memory/conflict-store.ts +0 -410
  343. package/src/memory/contradiction-checker.ts +0 -508
  344. package/src/memory/entity-extractor.ts +0 -535
  345. package/src/memory/format-recall.ts +0 -47
  346. package/src/memory/fts-reconciler.ts +0 -165
  347. package/src/memory/job-handlers/conflict.ts +0 -200
  348. package/src/memory/profile-compiler.ts +0 -195
  349. package/src/memory/recall-cache.ts +0 -117
  350. package/src/memory/search/entity.ts +0 -535
  351. package/src/memory/search/query-expansion.test.ts +0 -70
  352. package/src/memory/search/query-expansion.ts +0 -118
  353. package/src/runtime/routes/mcp-routes.ts +0 -20
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Tests for handleListMessages attachment handling.
3
+ *
4
+ * Verifies that:
5
+ * - User message image attachments include base64 data for client thumbnail generation
6
+ * - User message non-image attachments stay metadata-only (no base64 blob)
7
+ * - Assistant message attachments remain metadata-only
8
+ */
9
+
10
+ import { mkdtempSync, rmSync } from "node:fs";
11
+ import { tmpdir } from "node:os";
12
+ import { join } from "node:path";
13
+ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
14
+
15
+ const testDir = mkdtempSync(join(tmpdir(), "list-messages-test-"));
16
+
17
+ mock.module("../util/platform.js", () => ({
18
+ getDataDir: () => testDir,
19
+ isMacOS: () => process.platform === "darwin",
20
+ isLinux: () => process.platform === "linux",
21
+ isWindows: () => process.platform === "win32",
22
+ getPidPath: () => join(testDir, "test.pid"),
23
+ getDbPath: () => join(testDir, "test.db"),
24
+ getLogPath: () => join(testDir, "test.log"),
25
+ ensureDataDir: () => {},
26
+ getRootDir: () => testDir,
27
+ }));
28
+
29
+ mock.module("../util/logger.js", () => ({
30
+ getLogger: () =>
31
+ new Proxy({} as Record<string, unknown>, {
32
+ get: () => () => {},
33
+ }),
34
+ }));
35
+
36
+ mock.module("../config/loader.js", () => ({
37
+ getConfig: () => ({
38
+ ui: {},
39
+ model: "test",
40
+ provider: "test",
41
+ apiKeys: {},
42
+ memory: { enabled: false },
43
+ rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
44
+ }),
45
+ }));
46
+
47
+ import {
48
+ linkAttachmentToMessage,
49
+ uploadAttachment,
50
+ } from "../memory/attachments-store.js";
51
+ import { addMessage, createConversation } from "../memory/conversation-crud.js";
52
+ import { getDb, initializeDb, resetDb } from "../memory/db.js";
53
+ import { handleListMessages } from "../runtime/routes/conversation-routes.js";
54
+
55
+ initializeDb();
56
+
57
+ afterAll(() => {
58
+ resetDb();
59
+ try {
60
+ rmSync(testDir, { recursive: true });
61
+ } catch {
62
+ /* best effort */
63
+ }
64
+ });
65
+
66
+ function resetTables() {
67
+ const db = getDb();
68
+ db.run("DELETE FROM message_attachments");
69
+ db.run("DELETE FROM attachments");
70
+ db.run("DELETE FROM messages");
71
+ db.run("DELETE FROM conversations");
72
+ }
73
+
74
+ function createTestUrl(conversationId: string): URL {
75
+ return new URL(
76
+ `http://localhost/v1/messages?conversationId=${conversationId}`,
77
+ );
78
+ }
79
+
80
+ interface AttachmentPayload {
81
+ data?: string;
82
+ mimeType: string;
83
+ thumbnailData?: string;
84
+ }
85
+
86
+ interface MessagePayload {
87
+ attachments?: AttachmentPayload[];
88
+ }
89
+
90
+ const IMAGE_BASE64 =
91
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk";
92
+ const DOC_BASE64 = "JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwo";
93
+
94
+ describe("handleListMessages attachments", () => {
95
+ beforeEach(resetTables);
96
+
97
+ test("user message image attachments include base64 data", async () => {
98
+ const conv = createConversation();
99
+ const msg = await addMessage(
100
+ conv.id,
101
+ "user",
102
+ JSON.stringify([{ type: "text", text: "check this image" }]),
103
+ );
104
+ const stored = uploadAttachment("photo.png", "image/png", IMAGE_BASE64);
105
+ linkAttachmentToMessage(msg.id, stored.id, 0);
106
+
107
+ const response = handleListMessages(createTestUrl(conv.id), null);
108
+ const body = (await response.json()) as { messages: MessagePayload[] };
109
+
110
+ expect(body.messages).toHaveLength(1);
111
+ const attachments = body.messages[0].attachments;
112
+ expect(attachments).toBeDefined();
113
+ expect(attachments).toHaveLength(1);
114
+ expect(attachments![0].mimeType).toBe("image/png");
115
+ expect(attachments![0].data).toBe(IMAGE_BASE64);
116
+ });
117
+
118
+ test("user message non-image attachments stay metadata-only", async () => {
119
+ const conv = createConversation();
120
+ const msg = await addMessage(
121
+ conv.id,
122
+ "user",
123
+ JSON.stringify([{ type: "text", text: "check this doc" }]),
124
+ );
125
+ const stored = uploadAttachment(
126
+ "report.pdf",
127
+ "application/pdf",
128
+ DOC_BASE64,
129
+ );
130
+ linkAttachmentToMessage(msg.id, stored.id, 0);
131
+
132
+ const response = handleListMessages(createTestUrl(conv.id), null);
133
+ const body = (await response.json()) as { messages: MessagePayload[] };
134
+
135
+ expect(body.messages).toHaveLength(1);
136
+ const attachments = body.messages[0].attachments;
137
+ expect(attachments).toBeDefined();
138
+ expect(attachments).toHaveLength(1);
139
+ expect(attachments![0].mimeType).toBe("application/pdf");
140
+ // Non-image attachments should NOT include base64 data
141
+ expect(attachments![0].data).toBeUndefined();
142
+ });
143
+
144
+ test("assistant message attachments remain metadata-only", async () => {
145
+ const conv = createConversation();
146
+ const msg = await addMessage(
147
+ conv.id,
148
+ "assistant",
149
+ JSON.stringify([{ type: "text", text: "here is an image" }]),
150
+ );
151
+ const stored = uploadAttachment("result.png", "image/png", IMAGE_BASE64);
152
+ linkAttachmentToMessage(msg.id, stored.id, 0);
153
+
154
+ const response = handleListMessages(createTestUrl(conv.id), null);
155
+ const body = (await response.json()) as { messages: MessagePayload[] };
156
+
157
+ expect(body.messages).toHaveLength(1);
158
+ const attachments = body.messages[0].attachments;
159
+ expect(attachments).toBeDefined();
160
+ expect(attachments).toHaveLength(1);
161
+ expect(attachments![0].mimeType).toBe("image/png");
162
+ // Assistant attachments should NOT include base64 data (metadata-only)
163
+ expect(attachments![0].data).toBeUndefined();
164
+ });
165
+
166
+ test("user message with mixed attachments only inlines images", async () => {
167
+ const conv = createConversation();
168
+ const msg = await addMessage(
169
+ conv.id,
170
+ "user",
171
+ JSON.stringify([{ type: "text", text: "here are files" }]),
172
+ );
173
+ const imgStored = uploadAttachment("photo.jpg", "image/jpeg", IMAGE_BASE64);
174
+ const docStored = uploadAttachment(
175
+ "doc.pdf",
176
+ "application/pdf",
177
+ DOC_BASE64,
178
+ );
179
+ linkAttachmentToMessage(msg.id, imgStored.id, 0);
180
+ linkAttachmentToMessage(msg.id, docStored.id, 1);
181
+
182
+ const response = handleListMessages(createTestUrl(conv.id), null);
183
+ const body = (await response.json()) as { messages: MessagePayload[] };
184
+
185
+ const attachments = body.messages[0].attachments!;
186
+ expect(attachments).toHaveLength(2);
187
+
188
+ const imgAtt = attachments.find((a) => a.mimeType === "image/jpeg");
189
+ const docAtt = attachments.find((a) => a.mimeType === "application/pdf");
190
+ expect(imgAtt!.data).toBe(IMAGE_BASE64);
191
+ expect(docAtt!.data).toBeUndefined();
192
+ });
193
+ });
@@ -5,8 +5,8 @@
5
5
  * - compaction.summaryCalls: 2-6
6
6
  * - compaction.estimatedInputTokens: < previousEstimatedInputTokens
7
7
  * - recall.injectedTokens: <= computed dynamic budget
8
- * - recall.lexicalHits: > 0
9
8
  * - recall.recencyHits: > 0
9
+ * - recall.enabled: true
10
10
  */
11
11
  import { mkdtempSync, rmSync } from "node:fs";
12
12
  import { tmpdir } from "node:os";
@@ -50,6 +50,37 @@ mock.module("../util/logger.js", () => ({
50
50
  }),
51
51
  }));
52
52
 
53
+ // Stub the local embedding backend so the real ONNX model never loads.
54
+ mock.module("../memory/embedding-local.js", () => ({
55
+ LocalEmbeddingBackend: class {
56
+ readonly provider = "local" as const;
57
+ readonly model: string;
58
+ constructor(model: string) {
59
+ this.model = model;
60
+ }
61
+ async embed(texts: string[]): Promise<number[][]> {
62
+ return texts.map(() => new Array(384).fill(0));
63
+ }
64
+ },
65
+ }));
66
+
67
+ // Dynamic Qdrant mock so the benchmark can inject high-scoring results
68
+ let mockQdrantResults: Array<{
69
+ id: string;
70
+ score: number;
71
+ payload: Record<string, unknown>;
72
+ }> = [];
73
+
74
+ mock.module("../memory/qdrant-client.js", () => ({
75
+ getQdrantClient: () => ({
76
+ searchWithFilter: async () => mockQdrantResults,
77
+ hybridSearch: async () => mockQdrantResults,
78
+ upsertPoints: async () => {},
79
+ deletePoints: async () => {},
80
+ }),
81
+ initQdrantClient: () => {},
82
+ }));
83
+
53
84
  function makeLongMessages(turns: number): Message[] {
54
85
  const rows: Message[] = [];
55
86
  const userTail =
@@ -161,18 +192,15 @@ describe("Memory context benchmark", () => {
161
192
  beforeEach(() => {
162
193
  const db = getDb();
163
194
  db.run("DELETE FROM memory_item_sources");
164
- db.run("DELETE FROM memory_item_entities");
165
- db.run("DELETE FROM memory_entity_relations");
166
- db.run("DELETE FROM memory_entities");
167
195
  db.run("DELETE FROM memory_embeddings");
168
- db.run("DELETE FROM memory_summaries");
169
196
  db.run("DELETE FROM memory_items");
170
- db.run("DELETE FROM memory_segment_fts");
197
+
171
198
  db.run("DELETE FROM memory_segments");
172
199
  db.run("DELETE FROM messages");
173
200
  db.run("DELETE FROM conversations");
174
201
  db.run("DELETE FROM memory_jobs");
175
202
  db.run("DELETE FROM memory_checkpoints");
203
+ mockQdrantResults = [];
176
204
  });
177
205
 
178
206
  afterAll(() => {
@@ -225,13 +253,7 @@ describe("Memory context benchmark", () => {
225
253
  },
226
254
  retrieval: {
227
255
  ...DEFAULT_CONFIG.memory.retrieval,
228
- lexicalTopK: 50,
229
- semanticTopK: 20,
230
256
  maxInjectTokens: 750,
231
- reranking: {
232
- ...DEFAULT_CONFIG.memory.retrieval.reranking,
233
- enabled: false,
234
- },
235
257
  dynamicBudget: {
236
258
  enabled: true,
237
259
  minInjectTokens: 160,
@@ -257,6 +279,23 @@ describe("Memory context benchmark", () => {
257
279
  recallConfig.memory.retrieval.dynamicBudget.maxInjectTokens,
258
280
  });
259
281
 
282
+ // Seed Qdrant mock with a representative decision segment so
283
+ // the benchmark validates content quality, not just pipeline completion.
284
+ mockQdrantResults = [
285
+ {
286
+ id: "emb-bench-decision",
287
+ score: 0.9,
288
+ payload: {
289
+ target_type: "segment",
290
+ target_id: "seg-bench-0",
291
+ text: "Decision 0: use Bun test fixtures for memory regressions and recall ranking checks.",
292
+ kind: "segment",
293
+ created_at: now,
294
+ last_seen_at: now,
295
+ },
296
+ },
297
+ ];
298
+
260
299
  const recall = await buildMemoryRecall(
261
300
  "What decisions did we make about Bun tests and retrieval diagnostics?",
262
301
  conversationId,
@@ -264,13 +303,13 @@ describe("Memory context benchmark", () => {
264
303
  { maxInjectTokensOverride: recallBudget },
265
304
  );
266
305
 
267
- // In CI, Qdrant/embedding providers are unavailable, so semantic search
268
- // fails and the retriever marks the result as degraded. The benchmark
269
- // cares about compaction and lexical recall quality, not embedding
270
- // availability, so we do not assert on `recall.degraded`.
271
- expect(recall.lexicalHits).toBeGreaterThan(0);
306
+ // Recency search finds conversation-scoped segments.
272
307
  expect(recall.recencyHits).toBeGreaterThan(0);
308
+ expect(recall.enabled).toBe(true);
309
+ // With Qdrant mock returning a high-scoring result, content should be injected.
273
310
  expect(recall.selectedCount).toBeGreaterThan(0);
311
+ expect(recall.injectedText).toContain("Bun test fixtures");
312
+ expect(recall.injectedTokens).toBeGreaterThan(0);
274
313
  expect(recall.injectedTokens).toBeLessThanOrEqual(recallBudget);
275
314
  expect(recallBudget).toBeGreaterThanOrEqual(
276
315
  recallConfig.memory.retrieval.dynamicBudget.minInjectTokens,
@@ -278,6 +317,5 @@ describe("Memory context benchmark", () => {
278
317
  expect(recallBudget).toBeLessThanOrEqual(
279
318
  recallConfig.memory.retrieval.dynamicBudget.maxInjectTokens,
280
319
  );
281
- expect(recall.injectedText).toContain("Bun test fixtures");
282
320
  });
283
321
  });