@vellumai/assistant 0.7.3 → 0.8.1

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 (778) hide show
  1. package/AGENTS.md +11 -0
  2. package/ARCHITECTURE.md +29 -28
  3. package/Dockerfile +6 -4
  4. package/README.md +2 -2
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  6. package/bun.lock +3 -0
  7. package/docker-entrypoint.sh +16 -0
  8. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  9. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  10. package/eslint.config.mjs +12 -0
  11. package/knip.json +3 -1
  12. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  13. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  14. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  15. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  16. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  17. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  19. package/openapi.yaml +4126 -959
  20. package/package.json +5 -1
  21. package/scripts/generate-openapi.ts +52 -4
  22. package/scripts/sync-llm-catalog.ts +165 -0
  23. package/scripts/sync-web-search-catalog.ts +107 -0
  24. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  25. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  26. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  27. package/src/__tests__/anthropic-provider.test.ts +92 -2
  28. package/src/__tests__/app-control-flow.test.ts +7 -0
  29. package/src/__tests__/approval-cascade.test.ts +8 -16
  30. package/src/__tests__/approval-routes-http.test.ts +6 -0
  31. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  32. package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
  33. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  34. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  35. package/src/__tests__/btw-routes.test.ts +1 -0
  36. package/src/__tests__/call-constants.test.ts +10 -1
  37. package/src/__tests__/call-controller.test.ts +127 -0
  38. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  39. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  40. package/src/__tests__/channel-policy.test.ts +12 -0
  41. package/src/__tests__/checker.test.ts +89 -0
  42. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +88 -30
  43. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  44. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  45. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  46. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  47. package/src/__tests__/config-loader-platform-defaults.test.ts +345 -8
  48. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  49. package/src/__tests__/config-schema.test.ts +14 -3
  50. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  51. package/src/__tests__/config-set-route.test.ts +198 -0
  52. package/src/__tests__/config-watcher.test.ts +6 -0
  53. package/src/__tests__/contacts-tools.test.ts +51 -199
  54. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  55. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  56. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  57. package/src/__tests__/context-search-fanout.test.ts +20 -157
  58. package/src/__tests__/context-search-memory-source.test.ts +3 -26
  59. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  60. package/src/__tests__/context-search-types.test.ts +7 -2
  61. package/src/__tests__/context-window-manager.test.ts +389 -1
  62. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
  63. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  64. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  65. package/src/__tests__/conversation-agent-loop.test.ts +3 -3
  66. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  67. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  68. package/src/__tests__/conversation-error.test.ts +38 -0
  69. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  70. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  71. package/src/__tests__/conversation-init.benchmark.test.ts +2 -1
  72. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  73. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  74. package/src/__tests__/conversation-process-callsite.test.ts +22 -7
  75. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
  76. package/src/__tests__/conversation-runtime-assembly.test.ts +19 -10
  77. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  78. package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
  79. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  81. package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
  82. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
  83. package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
  84. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
  85. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  86. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  87. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  88. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  89. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  90. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  91. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  92. package/src/__tests__/filing-service.test.ts +25 -22
  93. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  94. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  95. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  96. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -34
  97. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  98. package/src/__tests__/heartbeat-service.test.ts +50 -233
  99. package/src/__tests__/history-repair.test.ts +89 -0
  100. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  101. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  102. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  103. package/src/__tests__/host-browser-routes.test.ts +325 -33
  104. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  105. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  106. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  107. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  108. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  109. package/src/__tests__/injector-chain.test.ts +24 -16
  110. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  111. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  112. package/src/__tests__/install-skill-routing.test.ts +2 -2
  113. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +169 -67
  114. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  115. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  116. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  117. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  118. package/src/__tests__/llm-resolver.test.ts +46 -0
  119. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  120. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  121. package/src/__tests__/mcp-cli.test.ts +182 -220
  122. package/src/__tests__/mcp-health-check.test.ts +56 -27
  123. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  124. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  125. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  126. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  127. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  128. package/src/__tests__/oauth-cli.test.ts +38 -1888
  129. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  130. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  131. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  132. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  133. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  134. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  135. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  136. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  137. package/src/__tests__/plugin-types.test.ts +13 -11
  138. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  139. package/src/__tests__/profile-entry-status.test.ts +43 -0
  140. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  141. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  142. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  143. package/src/__tests__/relay-server.test.ts +164 -2
  144. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  145. package/src/__tests__/schedule-retry.test.ts +56 -4
  146. package/src/__tests__/schedule-routes.test.ts +104 -0
  147. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  148. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  149. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  150. package/src/__tests__/scheduler-wake.test.ts +0 -63
  151. package/src/__tests__/secret-allowlist.test.ts +1 -0
  152. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  153. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  154. package/src/__tests__/secret-response-routing.test.ts +7 -5
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  156. package/src/__tests__/server-history-render.test.ts +82 -0
  157. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  158. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  159. package/src/__tests__/skill-include-graph.test.ts +31 -0
  160. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  161. package/src/__tests__/skill-load-tool.test.ts +42 -16
  162. package/src/__tests__/skills.test.ts +39 -0
  163. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  164. package/src/__tests__/suggestion-routes.test.ts +3 -3
  165. package/src/__tests__/sync-message-contract.test.ts +63 -0
  166. package/src/__tests__/task-scheduler.test.ts +88 -23
  167. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  168. package/src/__tests__/tool-executor.test.ts +155 -0
  169. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  170. package/src/__tests__/usage-cli.test.ts +11 -73
  171. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  172. package/src/__tests__/vercel-config.test.ts +168 -0
  173. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  174. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  175. package/src/__tests__/web-search.test.ts +303 -2
  176. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  177. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  178. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +153 -0
  179. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  180. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  181. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  182. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  183. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  184. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  185. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  186. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  187. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
  188. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  189. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  190. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  191. package/src/acp/resolve-agent.test.ts +25 -0
  192. package/src/acp/resolve-agent.ts +13 -2
  193. package/src/acp/session-manager.ts +14 -0
  194. package/src/agent/loop.ts +11 -0
  195. package/src/approvals/guardian-decision-primitive.ts +0 -13
  196. package/src/approvals/guardian-request-resolvers.ts +19 -102
  197. package/src/calls/call-constants.ts +5 -8
  198. package/src/calls/call-controller.ts +130 -67
  199. package/src/calls/relay-server.ts +42 -1
  200. package/src/calls/relay-setup-router.ts +36 -0
  201. package/src/calls/types.ts +1 -0
  202. package/src/calls/voice-session-bridge.ts +24 -5
  203. package/src/channels/config.ts +14 -1
  204. package/src/channels/types.ts +1 -0
  205. package/src/cli/AGENTS.md +164 -4
  206. package/src/cli/__tests__/notifications.test.ts +54 -0
  207. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  208. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  209. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  210. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  211. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  212. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  213. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  214. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  215. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  216. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  217. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  218. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  219. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  220. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  221. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  222. package/src/cli/commands/__tests__/status.test.ts +249 -0
  223. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  224. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  225. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  226. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  227. package/src/cli/commands/attachment.ts +8 -3
  228. package/src/cli/commands/audit.ts +95 -64
  229. package/src/cli/commands/auth.ts +61 -58
  230. package/src/cli/commands/avatar.ts +276 -390
  231. package/src/cli/commands/backup.ts +409 -505
  232. package/src/cli/commands/bash.ts +9 -5
  233. package/src/cli/commands/browser.ts +28 -9
  234. package/src/cli/commands/cache.ts +9 -4
  235. package/src/cli/commands/changelog.ts +414 -0
  236. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  237. package/src/cli/commands/clients.ts +8 -3
  238. package/src/cli/commands/completions.ts +9 -9
  239. package/src/cli/commands/config.ts +102 -72
  240. package/src/cli/commands/contacts.ts +575 -696
  241. package/src/cli/commands/conversations-defer.ts +17 -69
  242. package/src/cli/commands/conversations-import.ts +90 -253
  243. package/src/cli/commands/conversations.ts +346 -436
  244. package/src/cli/commands/credential-execution.ts +9 -6
  245. package/src/cli/commands/credentials.ts +456 -736
  246. package/src/cli/commands/domain.ts +128 -206
  247. package/src/cli/commands/email.ts +606 -794
  248. package/src/cli/commands/gateway.ts +8 -1
  249. package/src/cli/commands/image-generation.ts +157 -205
  250. package/src/cli/commands/inference-providers.ts +352 -0
  251. package/src/cli/commands/inference-session.ts +415 -0
  252. package/src/cli/commands/inference.ts +87 -65
  253. package/src/cli/commands/keys.ts +8 -3
  254. package/src/cli/commands/mcp.ts +103 -287
  255. package/src/cli/commands/memory-v2.ts +163 -517
  256. package/src/cli/commands/notifications.ts +33 -7
  257. package/src/cli/commands/oauth/apps.ts +292 -261
  258. package/src/cli/commands/oauth/connect.ts +182 -345
  259. package/src/cli/commands/oauth/disconnect.ts +16 -215
  260. package/src/cli/commands/oauth/index.ts +49 -45
  261. package/src/cli/commands/oauth/mode.ts +43 -199
  262. package/src/cli/commands/oauth/ping.ts +17 -125
  263. package/src/cli/commands/oauth/providers.ts +732 -921
  264. package/src/cli/commands/oauth/request.ts +60 -350
  265. package/src/cli/commands/oauth/shared.ts +11 -121
  266. package/src/cli/commands/oauth/status.ts +31 -121
  267. package/src/cli/commands/oauth/token.ts +13 -55
  268. package/src/cli/commands/pending.ts +19 -10
  269. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  270. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  271. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  272. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  273. package/src/cli/commands/platform/connect.ts +16 -80
  274. package/src/cli/commands/platform/disconnect.ts +14 -112
  275. package/src/cli/commands/platform/index.ts +177 -246
  276. package/src/cli/commands/routes.ts +153 -336
  277. package/src/cli/commands/sequence.ts +316 -360
  278. package/src/cli/commands/skills.ts +449 -671
  279. package/src/cli/commands/status.ts +58 -37
  280. package/src/cli/commands/stt.ts +94 -262
  281. package/src/cli/commands/task.ts +14 -40
  282. package/src/cli/commands/trust.ts +8 -3
  283. package/src/cli/commands/tts.ts +162 -167
  284. package/src/cli/commands/ui.ts +35 -42
  285. package/src/cli/commands/usage.ts +188 -126
  286. package/src/cli/commands/watchers.ts +8 -3
  287. package/src/cli/commands/webhooks.ts +99 -193
  288. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  289. package/src/cli/lib/daemon-credential-client.ts +4 -5
  290. package/src/cli/lib/nested-value.ts +44 -0
  291. package/src/cli/lib/open-browser.ts +36 -0
  292. package/src/cli/lib/register-command.ts +19 -0
  293. package/src/cli/lib/time-ago.ts +34 -0
  294. package/src/cli/program.ts +2 -4
  295. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  296. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  297. package/src/cli/utils/conversation-id.ts +30 -0
  298. package/src/cli/utils/parse-duration.ts +41 -0
  299. package/src/config/acp-defaults.test.ts +5 -1
  300. package/src/config/acp-defaults.ts +11 -4
  301. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  303. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  304. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  305. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  306. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  307. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  308. package/src/config/bundled-tool-registry.ts +0 -2
  309. package/src/config/feature-flag-registry.json +17 -17
  310. package/src/config/llm-resolver.ts +16 -1
  311. package/src/config/loader.ts +148 -33
  312. package/src/config/raw-config-utils.ts +2 -30
  313. package/src/config/schema.ts +4 -0
  314. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  315. package/src/config/schemas/call-site-catalog.ts +29 -7
  316. package/src/config/schemas/llm-request-logs.ts +57 -0
  317. package/src/config/schemas/llm.ts +52 -2
  318. package/src/config/schemas/memory-retrospective.ts +48 -0
  319. package/src/config/schemas/memory-v2.ts +33 -2
  320. package/src/config/schemas/memory.ts +4 -0
  321. package/src/config/schemas/services.ts +15 -12
  322. package/src/config/seed-inference-profiles.ts +195 -134
  323. package/src/contacts/contact-store.ts +0 -61
  324. package/src/context/window-manager.ts +191 -5
  325. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +111 -0
  326. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  327. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  328. package/src/daemon/approval-generators.ts +23 -29
  329. package/src/daemon/config-watcher.ts +2 -0
  330. package/src/daemon/conversation-agent-loop-handlers.ts +56 -0
  331. package/src/daemon/conversation-agent-loop.ts +140 -107
  332. package/src/daemon/conversation-error.ts +21 -0
  333. package/src/daemon/conversation-lifecycle.ts +68 -13
  334. package/src/daemon/conversation-process.ts +36 -19
  335. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  336. package/src/daemon/conversation-slash.ts +175 -23
  337. package/src/daemon/conversation-store.ts +17 -10
  338. package/src/daemon/conversation-surfaces.ts +92 -26
  339. package/src/daemon/conversation-tool-setup.ts +33 -19
  340. package/src/daemon/conversation.ts +49 -10
  341. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  342. package/src/daemon/guardian-action-generators.ts +7 -22
  343. package/src/daemon/handlers/config-model.ts +8 -126
  344. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  345. package/src/daemon/handlers/config-vercel.ts +3 -1
  346. package/src/daemon/handlers/shared.ts +26 -0
  347. package/src/daemon/handlers/skills.ts +84 -5
  348. package/src/daemon/history-repair.ts +33 -6
  349. package/src/daemon/host-app-control-proxy.ts +44 -19
  350. package/src/daemon/host-bash-proxy.ts +85 -158
  351. package/src/daemon/host-browser-proxy.ts +97 -36
  352. package/src/daemon/host-cu-proxy.ts +1 -1
  353. package/src/daemon/host-file-proxy.ts +1 -1
  354. package/src/daemon/host-proxy-base.ts +13 -1
  355. package/src/daemon/host-proxy-preactivation.ts +25 -1
  356. package/src/daemon/host-transfer-proxy.ts +2 -2
  357. package/src/daemon/identity-helpers.ts +19 -0
  358. package/src/daemon/lifecycle.ts +128 -114
  359. package/src/daemon/meet-host-supervisor.ts +15 -15
  360. package/src/daemon/memory-v2-startup.ts +62 -14
  361. package/src/daemon/message-protocol.ts +6 -0
  362. package/src/daemon/message-types/bookmarks.ts +18 -0
  363. package/src/daemon/message-types/conversations.ts +12 -9
  364. package/src/daemon/message-types/messages.ts +28 -2
  365. package/src/daemon/message-types/sync.ts +60 -0
  366. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  367. package/src/daemon/pkb-reminder-builder.ts +21 -7
  368. package/src/daemon/process-message.ts +56 -23
  369. package/src/daemon/server.ts +23 -18
  370. package/src/daemon/shutdown-handlers.ts +0 -2
  371. package/src/daemon/tool-setup-types.ts +9 -0
  372. package/src/daemon/tool-side-effects.ts +6 -4
  373. package/src/daemon/wake-target-adapter.ts +11 -0
  374. package/src/documents/document-store.ts +35 -1
  375. package/src/export/transcript-formatter.ts +61 -2
  376. package/src/filing/filing-service.ts +42 -56
  377. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  378. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  379. package/src/heartbeat/heartbeat-service.ts +149 -128
  380. package/src/home/__tests__/feed-types.test.ts +63 -131
  381. package/src/home/__tests__/feed-writer.test.ts +77 -278
  382. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  383. package/src/home/feed-types.ts +19 -73
  384. package/src/home/feed-writer.ts +25 -156
  385. package/src/home/post-connect-feed.ts +1 -3
  386. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  387. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  388. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  389. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  390. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  391. package/src/ipc/assistant-server.ts +148 -42
  392. package/src/ipc/cli-client.ts +370 -50
  393. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  394. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  395. package/src/ipc/skill-routes/events.ts +30 -3
  396. package/src/ipc/skill-server.ts +99 -42
  397. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  398. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  399. package/src/live-voice/live-voice-session-manager.ts +11 -4
  400. package/src/live-voice/live-voice-session.ts +14 -6
  401. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  402. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  403. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  404. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  405. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
  406. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  407. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  408. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  409. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  410. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  411. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  412. package/src/memory/bookmark-crud.ts +179 -0
  413. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  414. package/src/memory/context-search/agent-protocol.ts +5 -1
  415. package/src/memory/context-search/agent-runner.ts +60 -85
  416. package/src/memory/context-search/limits.ts +1 -4
  417. package/src/memory/context-search/search.ts +23 -113
  418. package/src/memory/context-search/sources/conversations.ts +18 -6
  419. package/src/memory/context-search/sources/memory-v2.ts +40 -31
  420. package/src/memory/context-search/sources/memory.ts +9 -2
  421. package/src/memory/context-search/sources/workspace.ts +13 -10
  422. package/src/memory/context-search/types.ts +1 -1
  423. package/src/memory/conversation-bootstrap.ts +11 -0
  424. package/src/memory/conversation-crud.ts +312 -10
  425. package/src/memory/conversation-queries.ts +9 -5
  426. package/src/memory/conversation-title-service.ts +1 -0
  427. package/src/memory/conversation-types.ts +16 -0
  428. package/src/memory/db-init.ts +14 -0
  429. package/src/memory/embedding-backend.ts +2 -1
  430. package/src/memory/embedding-runtime-manager.ts +1 -2
  431. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
  432. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  433. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  434. package/src/memory/graph/conversation-graph-memory.ts +108 -14
  435. package/src/memory/graph/extraction.ts +4 -0
  436. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  437. package/src/memory/graph/graph-search.test.ts +6 -5
  438. package/src/memory/graph/graph-search.ts +3 -4
  439. package/src/memory/graph/retriever.test.ts +12 -7
  440. package/src/memory/graph/retriever.ts +4 -5
  441. package/src/memory/graph/tool-handlers.ts +20 -11
  442. package/src/memory/graph/tools.ts +48 -9
  443. package/src/memory/indexer.ts +18 -2
  444. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +120 -6
  445. package/src/memory/jobs/embed-concept-page.ts +261 -89
  446. package/src/memory/jobs-store.ts +51 -1
  447. package/src/memory/jobs-worker.ts +60 -7
  448. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  449. package/src/memory/llm-request-log-source-local.ts +26 -0
  450. package/src/memory/llm-request-log-source.ts +97 -0
  451. package/src/memory/llm-request-log-store.ts +1 -1
  452. package/src/memory/memory-retrospective-constants.ts +13 -0
  453. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  454. package/src/memory/memory-retrospective-job.ts +351 -0
  455. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  456. package/src/memory/memory-retrospective-state.ts +162 -0
  457. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  458. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  459. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  460. package/src/memory/message-content.ts +38 -1
  461. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  462. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  463. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  464. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  465. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  466. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  467. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  468. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  469. package/src/memory/migrations/243-provider-connections.ts +68 -0
  470. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  471. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  472. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  473. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  474. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  475. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  476. package/src/memory/migrations/index.ts +7 -0
  477. package/src/memory/pkb/pkb-search.test.ts +6 -5
  478. package/src/memory/pkb/pkb-search.ts +4 -5
  479. package/src/memory/published-pages-store.ts +16 -0
  480. package/src/memory/qdrant-client.ts +3 -0
  481. package/src/memory/schema/bookmarks.ts +38 -0
  482. package/src/memory/schema/conversations.ts +2 -0
  483. package/src/memory/schema/index.ts +2 -0
  484. package/src/memory/schema/inference.ts +29 -0
  485. package/src/memory/schema/memory-core.ts +9 -0
  486. package/src/memory/search/semantic.ts +5 -9
  487. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  488. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  489. package/src/memory/v2/__tests__/activation.test.ts +46 -9
  490. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  491. package/src/memory/v2/__tests__/consolidation-job.test.ts +140 -163
  492. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  493. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  494. package/src/memory/v2/__tests__/injection.test.ts +768 -33
  495. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  496. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  497. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  498. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  499. package/src/memory/v2/__tests__/qdrant.test.ts +382 -9
  500. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  501. package/src/memory/v2/__tests__/router.test.ts +516 -0
  502. package/src/memory/v2/__tests__/sim.test.ts +163 -8
  503. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  504. package/src/memory/v2/__tests__/static-context.test.ts +8 -35
  505. package/src/memory/v2/__tests__/sweep-job.test.ts +114 -33
  506. package/src/memory/v2/activation-store.ts +34 -5
  507. package/src/memory/v2/activation.ts +40 -27
  508. package/src/memory/v2/backfill-jobs.ts +17 -84
  509. package/src/memory/v2/consolidation-job.ts +92 -86
  510. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  511. package/src/memory/v2/injection.ts +466 -115
  512. package/src/memory/v2/migration.ts +117 -20
  513. package/src/memory/v2/page-index.ts +191 -0
  514. package/src/memory/v2/page-store.ts +42 -0
  515. package/src/memory/v2/prompts/consolidation.ts +14 -7
  516. package/src/memory/v2/prompts/router.ts +192 -0
  517. package/src/memory/v2/qdrant.ts +307 -133
  518. package/src/memory/v2/reranker.ts +14 -7
  519. package/src/memory/v2/router.ts +322 -0
  520. package/src/memory/v2/sim.ts +88 -34
  521. package/src/memory/v2/skill-store.ts +118 -29
  522. package/src/memory/v2/static-context.ts +20 -17
  523. package/src/memory/v2/sweep-job.ts +127 -102
  524. package/src/memory/v2/types.ts +16 -5
  525. package/src/memory/validation.ts +13 -0
  526. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  527. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  528. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  529. package/src/notifications/adapters/platform.ts +171 -0
  530. package/src/notifications/conversation-pairing.ts +2 -2
  531. package/src/notifications/copy-composer.ts +61 -12
  532. package/src/notifications/decision-engine.ts +46 -0
  533. package/src/notifications/destination-resolver.ts +21 -0
  534. package/src/notifications/emit-signal.ts +28 -1
  535. package/src/notifications/home-feed-side-effect.ts +111 -0
  536. package/src/notifications/signal.ts +5 -0
  537. package/src/permissions/checker.ts +12 -0
  538. package/src/permissions/gateway-threshold-reader.ts +116 -8
  539. package/src/permissions/ipc-risk-types.ts +2 -0
  540. package/src/permissions/prompter.ts +86 -96
  541. package/src/permissions/secret-prompter.ts +31 -31
  542. package/src/plugin-api/index.ts +13 -0
  543. package/src/plugin-api/package.json +12 -0
  544. package/src/plugin-api/types.ts +62 -0
  545. package/src/plugins/defaults/injectors.ts +20 -5
  546. package/src/plugins/external-plugin-loader.ts +294 -0
  547. package/src/plugins/types.ts +46 -30
  548. package/src/plugins/user-loader.ts +64 -41
  549. package/src/proactive-artifact/job.test.ts +63 -8
  550. package/src/proactive-artifact/job.ts +20 -2
  551. package/src/proactive-artifact/message-copy.ts +18 -1
  552. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  553. package/src/proactive-artifact/trigger-state.ts +4 -0
  554. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  555. package/src/prompts/system-prompt.ts +22 -1
  556. package/src/prompts/templates/SOUL.md +13 -28
  557. package/src/prompts/update-bulletin-job.ts +61 -73
  558. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  559. package/src/providers/__tests__/inference.test.ts +288 -0
  560. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  561. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  562. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  563. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  564. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  565. package/src/providers/anthropic/client.ts +95 -26
  566. package/src/providers/call-site-routing.ts +94 -16
  567. package/src/providers/connection-resolution.ts +163 -0
  568. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  569. package/src/providers/inference/adapter-factory.ts +173 -0
  570. package/src/providers/inference/auth.ts +112 -0
  571. package/src/providers/inference/backfill.ts +196 -0
  572. package/src/providers/inference/connections.ts +356 -0
  573. package/src/providers/inference/resolve-auth.ts +65 -0
  574. package/src/providers/model-catalog.ts +104 -6
  575. package/src/providers/openai/responses-provider.ts +4 -2
  576. package/src/providers/provider-env-vars.ts +17 -7
  577. package/src/providers/provider-secret-catalog.ts +49 -30
  578. package/src/providers/provider-send-message.ts +41 -20
  579. package/src/providers/registry.ts +143 -159
  580. package/src/providers/retry.ts +18 -10
  581. package/src/providers/search-provider-catalog.ts +121 -0
  582. package/src/runtime/AGENTS.md +18 -5
  583. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  584. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  585. package/src/runtime/actor-trust-resolver.ts +32 -10
  586. package/src/runtime/agent-wake.ts +35 -6
  587. package/src/runtime/assistant-event-hub.ts +3 -85
  588. package/src/runtime/auth/route-policy.ts +304 -8
  589. package/src/runtime/auth/same-actor.ts +2 -0
  590. package/src/runtime/background-job-runner.ts +339 -0
  591. package/src/runtime/btw-sidechain.ts +1 -0
  592. package/src/runtime/channel-approvals.ts +3 -2
  593. package/src/runtime/guardian-reply-router.ts +0 -10
  594. package/src/runtime/http-router.ts +36 -1
  595. package/src/runtime/http-server.ts +31 -5
  596. package/src/runtime/http-types.ts +2 -0
  597. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  598. package/src/runtime/middleware/request-logger.ts +62 -1
  599. package/src/runtime/pending-interactions.ts +19 -15
  600. package/src/runtime/pre-first-message-gate.ts +83 -0
  601. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  602. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  603. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  604. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  605. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  606. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  607. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  608. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  609. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  610. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  611. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  612. package/src/runtime/routes/acp-routes.ts +10 -8
  613. package/src/runtime/routes/app-management-routes.ts +228 -3
  614. package/src/runtime/routes/approval-routes.ts +7 -21
  615. package/src/runtime/routes/audit-routes.ts +43 -0
  616. package/src/runtime/routes/auth-routes.ts +72 -0
  617. package/src/runtime/routes/avatar-routes.ts +273 -20
  618. package/src/runtime/routes/backup-routes.ts +406 -2
  619. package/src/runtime/routes/bookmark-routes.ts +154 -0
  620. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  621. package/src/runtime/routes/consolidation-routes.ts +8 -9
  622. package/src/runtime/routes/contact-routes.ts +0 -160
  623. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  624. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  625. package/src/runtime/routes/conversation-query-routes.ts +373 -82
  626. package/src/runtime/routes/conversation-routes.ts +31 -10
  627. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  628. package/src/runtime/routes/credential-routes.ts +540 -0
  629. package/src/runtime/routes/debug-bash-routes.ts +2 -0
  630. package/src/runtime/routes/debug-routes.ts +2 -2
  631. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  632. package/src/runtime/routes/domain-routes.ts +167 -0
  633. package/src/runtime/routes/email-routes.ts +603 -0
  634. package/src/runtime/routes/errors.ts +2 -2
  635. package/src/runtime/routes/events-routes.ts +192 -0
  636. package/src/runtime/routes/filing-routes.ts +2 -3
  637. package/src/runtime/routes/home-feed-routes.ts +6 -78
  638. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  639. package/src/runtime/routes/host-browser-routes.ts +103 -22
  640. package/src/runtime/routes/http-adapter.ts +2 -0
  641. package/src/runtime/routes/identity-routes.ts +5 -0
  642. package/src/runtime/routes/image-generation-routes.ts +99 -0
  643. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  644. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  645. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  646. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -7
  647. package/src/runtime/routes/index.ts +36 -0
  648. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  649. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  650. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  651. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  652. package/src/runtime/routes/inference-send-routes.ts +115 -0
  653. package/src/runtime/routes/integrations/twilio.ts +1 -0
  654. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  655. package/src/runtime/routes/memory-item-routes.test.ts +3 -9
  656. package/src/runtime/routes/memory-item-routes.ts +5 -6
  657. package/src/runtime/routes/memory-v2-routes.ts +105 -404
  658. package/src/runtime/routes/notification-routes.ts +2 -0
  659. package/src/runtime/routes/oauth-apps.ts +112 -7
  660. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  661. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  662. package/src/runtime/routes/oauth-providers.ts +298 -8
  663. package/src/runtime/routes/platform-routes.ts +336 -0
  664. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  665. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  666. package/src/runtime/routes/playground/state.ts +2 -1
  667. package/src/runtime/routes/publish-routes.ts +221 -0
  668. package/src/runtime/routes/schedule-routes.ts +82 -0
  669. package/src/runtime/routes/sequence-routes.ts +291 -0
  670. package/src/runtime/routes/settings-routes.ts +2 -10
  671. package/src/runtime/routes/skills-routes.ts +31 -1
  672. package/src/runtime/routes/stt-routes.ts +240 -3
  673. package/src/runtime/routes/surface-action-routes.ts +43 -7
  674. package/src/runtime/routes/tts-routes.ts +67 -0
  675. package/src/runtime/routes/types.ts +32 -0
  676. package/src/runtime/routes/user-routes-cli.ts +243 -0
  677. package/src/runtime/routes/webhook-routes.ts +165 -0
  678. package/src/runtime/sync/resource-sync-events.ts +25 -0
  679. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  680. package/src/runtime/sync/sync-publisher.ts +21 -0
  681. package/src/schedule/scheduler.ts +200 -123
  682. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  683. package/src/security/secret-patterns.ts +3 -0
  684. package/src/sequence/engine.ts +38 -40
  685. package/src/skills/include-graph.ts +35 -13
  686. package/src/subagent/manager.ts +20 -15
  687. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  688. package/src/tools/browser/browser-execution.ts +15 -4
  689. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  690. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  691. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  692. package/src/tools/browser/cdp-client/factory.ts +66 -5
  693. package/src/tools/browser/runtime-check.ts +77 -0
  694. package/src/tools/document/document-tool.ts +20 -0
  695. package/src/tools/executor.ts +18 -2
  696. package/src/tools/memory/register.test.ts +10 -8
  697. package/src/tools/memory/register.ts +9 -1
  698. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  699. package/src/tools/network/web-search.ts +280 -37
  700. package/src/tools/permission-checker.ts +28 -5
  701. package/src/tools/skills/load.ts +24 -20
  702. package/src/tools/subagent/spawn.ts +3 -3
  703. package/src/tools/terminal/shell.ts +44 -0
  704. package/src/tools/tool-name-aliases.ts +19 -0
  705. package/src/tools/types.ts +19 -1
  706. package/src/usage/attribution.ts +3 -2
  707. package/src/util/pricing.ts +86 -160
  708. package/src/watcher/__tests__/engine.test.ts +301 -0
  709. package/src/watcher/constants.ts +7 -0
  710. package/src/watcher/engine.ts +90 -90
  711. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  712. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  713. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  714. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
  715. package/src/workspace/migrations/069-seed-onboarding-threads.ts +34 -0
  716. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  717. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  718. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  719. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  720. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  721. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  722. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  723. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  724. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  725. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  726. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  727. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  728. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  729. package/src/workspace/migrations/registry.ts +28 -0
  730. package/src/workspace/migrations/runner.ts +13 -2
  731. package/src/workspace/migrations/types.ts +13 -3
  732. package/src/workspace/provider-commit-message-generator.ts +3 -2
  733. package/src/__tests__/context-search-pkb-source.test.ts +0 -492
  734. package/src/__tests__/credentials-cli.test.ts +0 -1225
  735. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  736. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  737. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  738. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  739. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  740. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  741. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  742. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  743. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  744. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  745. package/src/cli/commands/autonomy.ts +0 -365
  746. package/src/cli/commands/memory.ts +0 -424
  747. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -1201
  748. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  749. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  750. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  751. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  752. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  753. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  754. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  755. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  756. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  757. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  758. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  759. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  760. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  761. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  762. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  763. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  764. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  765. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  766. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  767. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  768. package/src/home/assistant-feed-authoring.ts +0 -135
  769. package/src/home/emit-feed-event.ts +0 -169
  770. package/src/home/feed-scheduler.ts +0 -281
  771. package/src/home/platform-gmail-digest.ts +0 -163
  772. package/src/home/rewrite-command-preview.ts +0 -66
  773. package/src/home/rewrite-feed-title.ts +0 -58
  774. package/src/home/rollup-producer.ts +0 -426
  775. package/src/memory/admin.ts +0 -326
  776. package/src/memory/context-search/sources/pkb.ts +0 -477
  777. package/src/memory/graph/compaction.ts +0 -299
  778. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -1,11 +1,30 @@
1
+ import { existsSync, mkdtempSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
1
4
  import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
5
 
3
6
  import { makeMockLogger } from "../../../__tests__/helpers/mock-logger.js";
4
7
 
8
+ // Per-suite tmp data dir so the reembed sentinel never lands in the
9
+ // developer's real ~/.vellum workspace.
10
+ const TEST_DATA_DIR = mkdtempSync(join(tmpdir(), "memory-v2-qdrant-test-"));
11
+ const REEMBED_SENTINEL_PATH = join(
12
+ TEST_DATA_DIR,
13
+ ".memory-v2-reembed-required",
14
+ );
15
+
5
16
  mock.module("../../../util/logger.js", () => ({
6
17
  getLogger: () => makeMockLogger(),
7
18
  }));
8
19
 
20
+ mock.module("../../../util/platform.js", () => ({
21
+ getDataDir: () => TEST_DATA_DIR,
22
+ // Bun shares mocked modules across test files; some peer tests import
23
+ // `getWorkspaceDir` from this same module, so re-export it here to avoid
24
+ // an `undefined` if this mock is the one that wins evaluation order.
25
+ getWorkspaceDir: () => TEST_DATA_DIR,
26
+ }));
27
+
9
28
  // Stub getConfig — only the qdrant.url / vectorSize / onDisk fields matter.
10
29
  mock.module("../../../config/loader.js", () => ({
11
30
  getConfig: () => ({
@@ -27,10 +46,39 @@ mock.module("../../qdrant-client.js", () => ({
27
46
  // records every call and lets each test program the next response.
28
47
  type MockPoint = {
29
48
  id: string;
30
- vector: { dense: number[]; sparse: { indices: number[]; values: number[] } };
49
+ vector: {
50
+ dense: number[];
51
+ sparse: { indices: number[]; values: number[] };
52
+ summary_dense?: number[];
53
+ summary_sparse?: { indices: number[]; values: number[] };
54
+ };
31
55
  payload: { slug: string; updated_at: number };
32
56
  };
33
57
 
58
+ type MockCollectionInfo = {
59
+ config: {
60
+ params: {
61
+ vectors?: Record<string, { size: number }> | { size: number };
62
+ sparse_vectors?: Record<string, unknown>;
63
+ };
64
+ };
65
+ };
66
+
67
+ const FULL_SCHEMA_INFO: MockCollectionInfo = {
68
+ config: {
69
+ params: {
70
+ vectors: {
71
+ dense: { size: 384 },
72
+ summary_dense: { size: 384 },
73
+ },
74
+ sparse_vectors: {
75
+ sparse: {},
76
+ summary_sparse: {},
77
+ },
78
+ },
79
+ },
80
+ };
81
+
34
82
  const state = {
35
83
  collectionExistsBeforeCreate: false,
36
84
  collectionExistsCalls: 0,
@@ -39,6 +87,10 @@ const state = {
39
87
  createIndexCalls: [] as Array<{ field_name: string; field_schema: string }>,
40
88
  upsertCalls: [] as Array<{ wait: boolean; points: MockPoint[] }>,
41
89
  deleteCalls: [] as Array<{ wait: boolean; points: string[] }>,
90
+ // Tracks `client.deleteCollection(name)` calls (distinct from `delete()`,
91
+ // which targets points). The schema-drift recreate path drops the
92
+ // collection entirely and we want to assert it ran exactly once.
93
+ deleteCollectionCalls: [] as string[],
42
94
  queryCalls: [] as Array<{
43
95
  using: string;
44
96
  query: unknown;
@@ -55,6 +107,17 @@ const state = {
55
107
  }>,
56
108
  },
57
109
  createCollectionThrows: null as Error | null,
110
+ // Schema returned by `client.getCollection`. Tests that exercise the
111
+ // drift path point this at a partial schema; the default mirrors a fully
112
+ // migrated collection so the no-drift path is the silent default.
113
+ getCollectionInfo: FULL_SCHEMA_INFO as MockCollectionInfo,
114
+ getCollectionThrows: null as Error | null,
115
+ getCollectionCalls: 0,
116
+ // Point count returned by `client.count`. Used by `countConceptPagePoints`
117
+ // which the lifecycle hook reads for the empty-after-create recovery path.
118
+ countResult: 0,
119
+ countThrows: null as Error | null,
120
+ countCalls: 0,
58
121
  // Throw queue for upsert: first call shifts and throws if non-null;
59
122
  // subsequent calls succeed once the queue is exhausted.
60
123
  upsertThrowQueue: [] as Array<Error | null>,
@@ -66,13 +129,29 @@ class MockQdrantClient {
66
129
  state.collectionExistsCalls++;
67
130
  return { exists: state.collectionExistsBeforeCreate };
68
131
  }
132
+ async getCollection(_name: string) {
133
+ state.getCollectionCalls++;
134
+ if (state.getCollectionThrows) throw state.getCollectionThrows;
135
+ return state.getCollectionInfo;
136
+ }
69
137
  async createCollection(_name: string, params: unknown) {
70
138
  state.createCollectionCalls++;
71
139
  state.createCollectionParams = params;
72
140
  if (state.createCollectionThrows) throw state.createCollectionThrows;
73
141
  state.collectionExistsBeforeCreate = true;
142
+ state.getCollectionInfo = FULL_SCHEMA_INFO;
143
+ return {};
144
+ }
145
+ async deleteCollection(name: string) {
146
+ state.deleteCollectionCalls.push(name);
147
+ state.collectionExistsBeforeCreate = false;
74
148
  return {};
75
149
  }
150
+ async count(_name: string, _opts: { exact: boolean }) {
151
+ state.countCalls++;
152
+ if (state.countThrows) throw state.countThrows;
153
+ return { count: state.countResult };
154
+ }
76
155
  async createPayloadIndex(
77
156
  _name: string,
78
157
  params: { field_name: string; field_schema: string },
@@ -102,7 +181,14 @@ class MockQdrantClient {
102
181
  },
103
182
  ) {
104
183
  state.queryCalls.push(params);
105
- const queue = state.queryResponses[params.using as "dense" | "sparse"];
184
+ // Both `dense` and `summary_dense` consume from the dense queue (and
185
+ // similarly for sparse). The four-channel hybrid query fires them in
186
+ // order: body-dense, body-sparse, summary-dense, summary-sparse — so
187
+ // queue order matches call order.
188
+ const queue =
189
+ state.queryResponses[
190
+ params.using.endsWith("sparse") ? "sparse" : "dense"
191
+ ];
106
192
  return queue.shift() ?? { points: [] };
107
193
  }
108
194
  }
@@ -116,6 +202,8 @@ const {
116
202
  upsertConceptPageEmbedding,
117
203
  deleteConceptPageEmbedding,
118
204
  hybridQueryConceptPages,
205
+ countConceptPagePoints,
206
+ clearReembedSentinel,
119
207
  MEMORY_V2_COLLECTION,
120
208
  _resetMemoryV2QdrantForTests,
121
209
  } = await import("../qdrant.js");
@@ -128,19 +216,31 @@ function resetState(): void {
128
216
  state.createIndexCalls.length = 0;
129
217
  state.upsertCalls.length = 0;
130
218
  state.deleteCalls.length = 0;
219
+ state.deleteCollectionCalls.length = 0;
131
220
  state.queryCalls.length = 0;
132
221
  state.queryResponses.dense.length = 0;
133
222
  state.queryResponses.sparse.length = 0;
134
223
  state.createCollectionThrows = null;
224
+ state.getCollectionInfo = FULL_SCHEMA_INFO;
225
+ state.getCollectionThrows = null;
226
+ state.getCollectionCalls = 0;
227
+ state.countResult = 0;
228
+ state.countThrows = null;
229
+ state.countCalls = 0;
135
230
  state.upsertThrowQueue.length = 0;
136
231
  _resetMemoryV2QdrantForTests();
232
+ // Drop any sentinel a prior test left behind so the no-drift default path
233
+ // doesn't accidentally report `migrated: true`.
234
+ if (existsSync(REEMBED_SENTINEL_PATH)) {
235
+ rmSync(REEMBED_SENTINEL_PATH);
236
+ }
137
237
  }
138
238
 
139
239
  describe("memory v2 qdrant — collection lifecycle", () => {
140
240
  beforeEach(resetState);
141
241
  afterEach(resetState);
142
242
 
143
- test("creates the collection with named dense + sparse vectors", async () => {
243
+ test("creates the collection with named dense + sparse vectors (body and summary)", async () => {
144
244
  state.collectionExistsBeforeCreate = false;
145
245
 
146
246
  await ensureConceptPageCollection();
@@ -149,8 +249,12 @@ describe("memory v2 qdrant — collection lifecycle", () => {
149
249
  const params = state.createCollectionParams as {
150
250
  vectors: {
151
251
  dense: { size: number; distance: string; on_disk: boolean };
252
+ summary_dense: { size: number; distance: string; on_disk: boolean };
253
+ };
254
+ sparse_vectors: {
255
+ sparse: Record<string, unknown>;
256
+ summary_sparse: Record<string, unknown>;
152
257
  };
153
- sparse_vectors: { sparse: Record<string, unknown> };
154
258
  hnsw_config: { on_disk: boolean; m: number; ef_construct: number };
155
259
  on_disk_payload: boolean;
156
260
  };
@@ -159,7 +263,14 @@ describe("memory v2 qdrant — collection lifecycle", () => {
159
263
  distance: "Cosine",
160
264
  on_disk: true,
161
265
  });
266
+ // Summary side mirrors body so the activation pipeline can fuse symmetrically.
267
+ expect(params.vectors.summary_dense).toEqual({
268
+ size: 384,
269
+ distance: "Cosine",
270
+ on_disk: true,
271
+ });
162
272
  expect(params.sparse_vectors.sparse).toEqual({});
273
+ expect(params.sparse_vectors.summary_sparse).toEqual({});
163
274
  expect(params.hnsw_config).toEqual({
164
275
  on_disk: true,
165
276
  m: 16,
@@ -219,6 +330,162 @@ describe("memory v2 qdrant — collection lifecycle", () => {
219
330
  // expected to have created it (it ran the same code).
220
331
  expect(state.createIndexCalls).toEqual([]);
221
332
  });
333
+
334
+ test("detects missing summary_dense / summary_sparse on an existing collection and recreates", async () => {
335
+ // Pre-#29823 schema: only body channels, no summary_*.
336
+ state.collectionExistsBeforeCreate = true;
337
+ state.getCollectionInfo = {
338
+ config: {
339
+ params: {
340
+ vectors: { dense: { size: 384 } },
341
+ sparse_vectors: { sparse: {} },
342
+ },
343
+ },
344
+ };
345
+
346
+ const result = await ensureConceptPageCollection();
347
+
348
+ // Drift path probed once, dropped the collection once, and recreated
349
+ // with the full four-vector schema (the create-success branch resets
350
+ // `getCollectionInfo` to FULL_SCHEMA_INFO so a follow-up probe agrees).
351
+ expect(state.getCollectionCalls).toBe(1);
352
+ expect(state.deleteCollectionCalls).toEqual([MEMORY_V2_COLLECTION]);
353
+ expect(state.createCollectionCalls).toBe(1);
354
+ expect(result).toEqual({ migrated: true });
355
+
356
+ // Recreated schema carries summary_dense + summary_sparse.
357
+ const params = state.createCollectionParams as {
358
+ vectors: Record<string, unknown>;
359
+ sparse_vectors: Record<string, unknown>;
360
+ };
361
+ expect(params.vectors.summary_dense).toBeDefined();
362
+ expect(params.sparse_vectors.summary_sparse).toBeDefined();
363
+ });
364
+
365
+ test("leaves a fully migrated collection untouched", async () => {
366
+ // Default `getCollectionInfo` is FULL_SCHEMA_INFO — already migrated.
367
+ state.collectionExistsBeforeCreate = true;
368
+
369
+ const result = await ensureConceptPageCollection();
370
+
371
+ expect(state.getCollectionCalls).toBe(1);
372
+ expect(state.deleteCollectionCalls).toEqual([]);
373
+ expect(state.createCollectionCalls).toBe(0);
374
+ expect(result).toEqual({ migrated: false });
375
+ });
376
+
377
+ test("getCollection failure is treated as compatible (no destructive recreate)", async () => {
378
+ state.collectionExistsBeforeCreate = true;
379
+ state.getCollectionThrows = new Error("transient REST error");
380
+
381
+ const result = await ensureConceptPageCollection();
382
+
383
+ expect(state.getCollectionCalls).toBe(1);
384
+ expect(state.deleteCollectionCalls).toEqual([]);
385
+ expect(state.createCollectionCalls).toBe(0);
386
+ expect(result).toEqual({ migrated: false });
387
+ });
388
+
389
+ test("preserves the reembed signal across calls when createCollection fails after delete", async () => {
390
+ // Pre-#29823 schema triggers the destructive recreate path.
391
+ state.collectionExistsBeforeCreate = true;
392
+ state.getCollectionInfo = {
393
+ config: {
394
+ params: {
395
+ vectors: { dense: { size: 384 } },
396
+ sparse_vectors: { sparse: {} },
397
+ },
398
+ },
399
+ };
400
+ state.createCollectionThrows = new Error("Qdrant transient failure");
401
+
402
+ let firstError: unknown = null;
403
+ try {
404
+ await ensureConceptPageCollection();
405
+ } catch (err) {
406
+ firstError = err;
407
+ }
408
+ expect(firstError).not.toBeNull();
409
+ // The sentinel must outlive the failed call so the retry knows data was lost.
410
+ expect(existsSync(REEMBED_SENTINEL_PATH)).toBe(true);
411
+
412
+ // Simulate a follow-up call after the transient failure clears. The
413
+ // collection no longer exists (delete succeeded earlier) so the ensure
414
+ // path falls through to createCollection without re-entering the drift
415
+ // branch — but the sentinel must still surface as `migrated: true` so
416
+ // the lifecycle hook enqueues reembed.
417
+ state.createCollectionThrows = null;
418
+ _resetMemoryV2QdrantForTests();
419
+ const result = await ensureConceptPageCollection();
420
+
421
+ expect(result).toEqual({ migrated: true });
422
+
423
+ // Lifecycle hook clears the sentinel after enqueueing the reembed job.
424
+ await clearReembedSentinel();
425
+ expect(existsSync(REEMBED_SENTINEL_PATH)).toBe(false);
426
+ });
427
+
428
+ test("clearReembedSentinel is a no-op when no sentinel exists", async () => {
429
+ // Idempotent: missing-file does not throw, so the lifecycle hook can
430
+ // call it unconditionally without guarding.
431
+ expect(existsSync(REEMBED_SENTINEL_PATH)).toBe(false);
432
+ await clearReembedSentinel();
433
+ expect(existsSync(REEMBED_SENTINEL_PATH)).toBe(false);
434
+ });
435
+
436
+ test("concurrent ensure during a schema rebuild only deletes/creates once", async () => {
437
+ state.collectionExistsBeforeCreate = true;
438
+ state.getCollectionInfo = {
439
+ config: {
440
+ params: {
441
+ vectors: { dense: { size: 384 } },
442
+ sparse_vectors: { sparse: {} },
443
+ },
444
+ },
445
+ };
446
+
447
+ const results = await Promise.all([
448
+ ensureConceptPageCollection(),
449
+ ensureConceptPageCollection(),
450
+ ensureConceptPageCollection(),
451
+ ]);
452
+
453
+ expect(state.deleteCollectionCalls).toEqual([MEMORY_V2_COLLECTION]);
454
+ expect(state.createCollectionCalls).toBe(1);
455
+ // All three concurrent callers see the same migrated signal so any one
456
+ // of them is safe to enqueue the reembed (the lifecycle hook is the
457
+ // single producer in practice).
458
+ expect(results).toEqual([
459
+ { migrated: true },
460
+ { migrated: true },
461
+ { migrated: true },
462
+ ]);
463
+ });
464
+ });
465
+
466
+ describe("memory v2 qdrant — point count", () => {
467
+ beforeEach(resetState);
468
+ afterEach(resetState);
469
+
470
+ test("returns the approximate Qdrant count for the v2 collection", async () => {
471
+ state.collectionExistsBeforeCreate = true;
472
+ state.countResult = 1185;
473
+
474
+ const count = await countConceptPagePoints();
475
+
476
+ expect(count).toBe(1185);
477
+ expect(state.countCalls).toBe(1);
478
+ });
479
+
480
+ test("returns 0 when the count call fails (treated as needs-reembed)", async () => {
481
+ state.collectionExistsBeforeCreate = true;
482
+ state.countThrows = new Error("Qdrant unreachable");
483
+
484
+ const count = await countConceptPagePoints();
485
+
486
+ expect(count).toBe(0);
487
+ expect(state.countCalls).toBe(1);
488
+ });
222
489
  });
223
490
 
224
491
  describe("memory v2 qdrant — upsert", () => {
@@ -249,12 +516,67 @@ describe("memory v2 qdrant — upsert", () => {
249
516
  indices: [1, 2],
250
517
  values: [0.5, 0.5],
251
518
  });
519
+ // No summary vectors when caller didn't pass them — Qdrant accepts a
520
+ // partial named-vector subset, and pages without a frontmatter summary
521
+ // legitimately have nothing to embed on the summary side.
522
+ const vectorRecord = point.vector as unknown as Record<string, unknown>;
523
+ expect(vectorRecord.summary_dense).toBeUndefined();
524
+ expect(vectorRecord.summary_sparse).toBeUndefined();
252
525
  // Point ID is a UUID-shaped string derived from the slug.
253
526
  expect(point.id).toMatch(
254
527
  /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,
255
528
  );
256
529
  });
257
530
 
531
+ test("upserts summary vectors alongside body vectors when both are provided", async () => {
532
+ state.collectionExistsBeforeCreate = true;
533
+
534
+ await upsertConceptPageEmbedding({
535
+ slug: "summarized-page",
536
+ dense: [0.1, 0.2, 0.3],
537
+ sparse: { indices: [1, 2], values: [0.5, 0.5] },
538
+ summary: {
539
+ dense: [0.4, 0.5, 0.6],
540
+ sparse: { indices: [3, 4], values: [0.7, 0.7] },
541
+ },
542
+ updatedAt: 1714000000000,
543
+ });
544
+
545
+ expect(state.upsertCalls).toHaveLength(1);
546
+ const [point] = state.upsertCalls[0].points;
547
+ const vectorRecord = point.vector as unknown as Record<string, unknown>;
548
+ expect(vectorRecord.dense).toEqual([0.1, 0.2, 0.3]);
549
+ expect(vectorRecord.sparse).toEqual({
550
+ indices: [1, 2],
551
+ values: [0.5, 0.5],
552
+ });
553
+ expect(vectorRecord.summary_dense).toEqual([0.4, 0.5, 0.6]);
554
+ expect(vectorRecord.summary_sparse).toEqual({
555
+ indices: [3, 4],
556
+ values: [0.7, 0.7],
557
+ });
558
+ });
559
+
560
+ test("omits summary vectors when the summary block is undefined", async () => {
561
+ // The grouped-shape signature enforces summary as a paired { dense, sparse }
562
+ // block; passing `undefined` (or omitting it) leaves the summary vectors off
563
+ // the point entirely so query-time fusion stays symmetric.
564
+ state.collectionExistsBeforeCreate = true;
565
+
566
+ await upsertConceptPageEmbedding({
567
+ slug: "no-summary",
568
+ dense: [0.1],
569
+ sparse: { indices: [1], values: [1] },
570
+ // summary intentionally omitted
571
+ updatedAt: 1,
572
+ });
573
+
574
+ const [point] = state.upsertCalls[0].points;
575
+ const vectorRecord = point.vector as unknown as Record<string, unknown>;
576
+ expect(vectorRecord.summary_dense).toBeUndefined();
577
+ expect(vectorRecord.summary_sparse).toBeUndefined();
578
+ });
579
+
258
580
  test("two upserts for the same slug share the same point id (overwrites in place)", async () => {
259
581
  state.collectionExistsBeforeCreate = true;
260
582
 
@@ -357,8 +679,9 @@ describe("memory v2 qdrant — hybrid query", () => {
357
679
  beforeEach(resetState);
358
680
  afterEach(resetState);
359
681
 
360
- test("runs both dense and sparse queries and returns per-channel scores", async () => {
682
+ test("runs all four channels (body dense/sparse + summary dense/sparse) and returns per-channel scores", async () => {
361
683
  state.collectionExistsBeforeCreate = true;
684
+ // Body channel hits.
362
685
  state.queryResponses.dense.push({
363
686
  points: [
364
687
  { score: 0.91, payload: { slug: "alice-prefers-vs-code" } },
@@ -371,6 +694,14 @@ describe("memory v2 qdrant — hybrid query", () => {
371
694
  { score: 3, payload: { slug: "bob-uses-zsh" } },
372
695
  ],
373
696
  });
697
+ // Summary channel hits — queue order is body-dense, body-sparse,
698
+ // summary-dense, summary-sparse, so push summaries after bodies.
699
+ state.queryResponses.dense.push({
700
+ points: [{ score: 0.81, payload: { slug: "alice-prefers-vs-code" } }],
701
+ });
702
+ state.queryResponses.sparse.push({
703
+ points: [{ score: 9, payload: { slug: "alice-prefers-vs-code" } }],
704
+ });
374
705
 
375
706
  const results = await hybridQueryConceptPages(
376
707
  [0.1, 0.2, 0.3],
@@ -378,14 +709,19 @@ describe("memory v2 qdrant — hybrid query", () => {
378
709
  5,
379
710
  );
380
711
 
381
- // Both queries fired, with the same limit and the right `using`.
382
- expect(state.queryCalls).toHaveLength(2);
712
+ // All four queries fired with the same limit and distinct `using`.
713
+ expect(state.queryCalls).toHaveLength(4);
383
714
  const usings = state.queryCalls.map((c) => c.using).sort();
384
- expect(usings).toEqual(["dense", "sparse"]);
715
+ expect(usings).toEqual([
716
+ "dense",
717
+ "sparse",
718
+ "summary_dense",
719
+ "summary_sparse",
720
+ ]);
385
721
  expect(state.queryCalls.every((c) => c.limit === 5)).toBe(true);
386
722
  expect(state.queryCalls.every((c) => c.with_payload === true)).toBe(true);
387
723
 
388
- // Each slug exposes both channel scores.
724
+ // Alice has hits on all four channels; bob is body-only.
389
725
  expect(results).toHaveLength(2);
390
726
  const alice = results.find((r) => r.slug === "alice-prefers-vs-code");
391
727
  const bob = results.find((r) => r.slug === "bob-uses-zsh");
@@ -393,6 +729,8 @@ describe("memory v2 qdrant — hybrid query", () => {
393
729
  slug: "alice-prefers-vs-code",
394
730
  denseScore: 0.91,
395
731
  sparseScore: 12,
732
+ summaryDenseScore: 0.81,
733
+ summarySparseScore: 9,
396
734
  });
397
735
  expect(bob).toEqual({
398
736
  slug: "bob-uses-zsh",
@@ -403,6 +741,8 @@ describe("memory v2 qdrant — hybrid query", () => {
403
741
 
404
742
  test("dense-only hits leave sparseScore undefined (and vice versa)", async () => {
405
743
  state.collectionExistsBeforeCreate = true;
744
+ // Body dense + sparse hits. Summary channels stay empty (no push) →
745
+ // they fall through to `{ points: [] }` and produce no summary scores.
406
746
  state.queryResponses.dense.push({
407
747
  points: [{ score: 0.7, payload: { slug: "dense-only" } }],
408
748
  });
@@ -420,8 +760,41 @@ describe("memory v2 qdrant — hybrid query", () => {
420
760
  const sparseOnly = results.find((r) => r.slug === "sparse-only");
421
761
  expect(denseOnly).toEqual({ slug: "dense-only", denseScore: 0.7 });
422
762
  expect(denseOnly?.sparseScore).toBeUndefined();
763
+ expect(denseOnly?.summaryDenseScore).toBeUndefined();
423
764
  expect(sparseOnly).toEqual({ slug: "sparse-only", sparseScore: 2 });
424
765
  expect(sparseOnly?.denseScore).toBeUndefined();
766
+ expect(sparseOnly?.summarySparseScore).toBeUndefined();
767
+ });
768
+
769
+ test("returns summary-channel scores when only the summary side hits", async () => {
770
+ // Page has no body hits but matches via the summary embedding —
771
+ // exercises the path where `simBatch` falls back to summary-only.
772
+ state.collectionExistsBeforeCreate = true;
773
+ // Body channels empty.
774
+ state.queryResponses.dense.push({ points: [] });
775
+ state.queryResponses.sparse.push({ points: [] });
776
+ // Summary channels hit.
777
+ state.queryResponses.dense.push({
778
+ points: [{ score: 0.6, payload: { slug: "summary-only" } }],
779
+ });
780
+ state.queryResponses.sparse.push({
781
+ points: [{ score: 4, payload: { slug: "summary-only" } }],
782
+ });
783
+
784
+ const results = await hybridQueryConceptPages(
785
+ [0.1],
786
+ { indices: [1], values: [1] },
787
+ 5,
788
+ );
789
+
790
+ const summaryOnly = results.find((r) => r.slug === "summary-only");
791
+ expect(summaryOnly).toEqual({
792
+ slug: "summary-only",
793
+ summaryDenseScore: 0.6,
794
+ summarySparseScore: 4,
795
+ });
796
+ expect(summaryOnly?.denseScore).toBeUndefined();
797
+ expect(summaryOnly?.sparseScore).toBeUndefined();
425
798
  });
426
799
 
427
800
  test("does not use Qdrant-side RRF fusion (separate per-channel queries)", async () => {
@@ -215,8 +215,8 @@ describe("rerankCandidates", () => {
215
215
  expect(backendState.calls).toHaveLength(1);
216
216
  });
217
217
 
218
- test("passage construction caps at 240 chars after slug newline", async () => {
219
- const longBody = "a".repeat(500);
218
+ test("passage construction caps at 1500 chars after slug newline", async () => {
219
+ const longBody = "a".repeat(2000);
220
220
  pageState.pages.set("slug", { body: longBody });
221
221
  backendState.scores = [0.5];
222
222
 
@@ -224,9 +224,9 @@ describe("rerankCandidates", () => {
224
224
 
225
225
  expect(backendState.calls).toHaveLength(1);
226
226
  const passage = backendState.calls[0].passages[0];
227
- // "slug\n" prefix + 240 chars of body
227
+ // "slug\n" prefix + 1500 chars of body
228
228
  expect(passage.startsWith("slug\n")).toBe(true);
229
- expect(passage.length).toBeLessThanOrEqual(5 + 240);
229
+ expect(passage.length).toBeLessThanOrEqual(5 + 1500);
230
230
  });
231
231
 
232
232
  test("first paragraph is taken (body truncated at blank line)", async () => {