@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
@@ -18,11 +18,16 @@
18
18
  // fusion using `dense_weight` / `sparse_weight` from `config.memory.v2`,
19
19
  // which RRF fusion would discard.
20
20
 
21
+ import { existsSync } from "node:fs";
22
+ import { mkdir, unlink, writeFile } from "node:fs/promises";
23
+ import { dirname, join } from "node:path";
24
+
21
25
  import { QdrantClient as QdrantRestClient } from "@qdrant/js-client-rest";
22
26
  import { v5 as uuidv5 } from "uuid";
23
27
 
24
28
  import { getConfig } from "../../config/loader.js";
25
29
  import { getLogger } from "../../util/logger.js";
30
+ import { getDataDir } from "../../util/platform.js";
26
31
  import type { SparseEmbedding } from "../embedding-types.js";
27
32
  import { resolveQdrantUrl } from "../qdrant-client.js";
28
33
 
@@ -48,21 +53,86 @@ export interface ConceptPagePayload {
48
53
  export interface ConceptPageQueryResult {
49
54
  slug: string;
50
55
  /**
51
- * Dense cosine similarity, when the slug appeared in the dense top-`limit`.
52
- * `undefined` if the slug only appeared in the sparse channel.
56
+ * Dense cosine similarity against the page body, when the slug appeared in
57
+ * the body dense top-`limit`. `undefined` if the slug only appeared in the
58
+ * sparse channel — or in a summary-side channel.
53
59
  */
54
60
  denseScore?: number;
55
61
  /**
56
- * Sparse score, when the slug appeared in the sparse top-`limit`.
57
- * `undefined` if the slug only appeared in the dense channel. Lives on a
58
- * different scale than `denseScore` — callers must normalize before fusing.
62
+ * Sparse score against the page body, when the slug appeared in the body
63
+ * sparse top-`limit`. `undefined` if the slug only appeared in the dense
64
+ * channel. Lives on a different scale than `denseScore` — callers must
65
+ * normalize before fusing.
59
66
  */
60
67
  sparseScore?: number;
68
+ /**
69
+ * Dense cosine similarity against the page's frontmatter `summary`, when
70
+ * the page has a summary embedded and the slug appeared in the summary
71
+ * dense top-`limit`. `undefined` for pages without a summary embedding —
72
+ * those fall back to body-only scoring.
73
+ */
74
+ summaryDenseScore?: number;
75
+ /**
76
+ * Sparse score against the page's frontmatter `summary`, paired with
77
+ * `summaryDenseScore`. `undefined` for pages without a summary embedding.
78
+ */
79
+ summarySparseScore?: number;
61
80
  }
62
81
 
63
82
  let _client: QdrantRestClient | null = null;
64
83
  let _collectionReady = false;
65
- let _collectionReadyPromise: Promise<void> | null = null;
84
+ let _collectionReadyPromise: Promise<{ migrated: boolean }> | null = null;
85
+
86
+ /**
87
+ * Named vectors the v2 concept-page collection must expose. Existing
88
+ * collections that lack any of these get destructively recreated by
89
+ * `ensureConceptPageCollectionOnce` — see the `migrated` return flag.
90
+ */
91
+ const REQUIRED_DENSE_VECTORS = ["dense", "summary_dense"] as const;
92
+ const REQUIRED_SPARSE_VECTORS = ["sparse", "summary_sparse"] as const;
93
+
94
+ /**
95
+ * Marker file written before the destructive collection-recreate path runs,
96
+ * cleared by the lifecycle hook once the reembed job has been enqueued.
97
+ *
98
+ * The sentinel exists to close a narrow data-loss window in
99
+ * `ensureConceptPageCollectionOnce`: a transient Qdrant failure between
100
+ * `deleteCollection` and `createCollection` would otherwise lose the
101
+ * "needs reembed" signal — `migrated` is reinitialized on the next call,
102
+ * any subsequent caller (e.g. an upsert) recreates the collection empty,
103
+ * and the lifecycle hook never enqueues the backfill. By persisting the
104
+ * intent on disk *before* delete, the signal survives crashes and
105
+ * intra-process retries: every later `ensureConceptPageCollection` call
106
+ * returns `migrated: true` until the lifecycle hook enqueues the reembed
107
+ * and clears the sentinel.
108
+ */
109
+ const REEMBED_SENTINEL_FILENAME = ".memory-v2-reembed-required";
110
+
111
+ function reembedSentinelPath(): string {
112
+ return join(getDataDir(), REEMBED_SENTINEL_FILENAME);
113
+ }
114
+
115
+ function reembedSentinelExists(): boolean {
116
+ return existsSync(reembedSentinelPath());
117
+ }
118
+
119
+ async function writeReembedSentinel(): Promise<void> {
120
+ const path = reembedSentinelPath();
121
+ await mkdir(dirname(path), { recursive: true });
122
+ await writeFile(path, "");
123
+ }
124
+
125
+ /**
126
+ * Remove the reembed sentinel after the lifecycle hook has enqueued the
127
+ * `memory_v2_reembed` job. Idempotent — missing-file is not an error.
128
+ */
129
+ export async function clearReembedSentinel(): Promise<void> {
130
+ try {
131
+ await unlink(reembedSentinelPath());
132
+ } catch (err) {
133
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") throw err;
134
+ }
135
+ }
66
136
 
67
137
  /** Lazily create a Qdrant REST client bound to the resolved URL. */
68
138
  function getClient(): QdrantRestClient {
@@ -76,16 +146,19 @@ function getClient(): QdrantRestClient {
76
146
  }
77
147
 
78
148
  /**
79
- * Create the v2 concept-page collection if it does not already exist.
80
- * Idempotent: a no-op when the collection is already present.
81
- *
82
- * Vector layout mirrors `VellumQdrantClient.ensureCollection` named dense
83
- * (cosine, configurable size + on-disk) and sparse vectors. The vector size
84
- * and on-disk flag inherit from `config.memory.qdrant` so v2 stays aligned
85
- * with the user's existing embedding backend without separate knobs.
149
+ * Create the v2 concept-page collection if it does not already exist, or
150
+ * destructively recreate it when the existing schema is missing any of the
151
+ * required named vectors (see `REQUIRED_DENSE_VECTORS` /
152
+ * `REQUIRED_SPARSE_VECTORS`). The latter case is signalled to callers via
153
+ * `{ migrated: true }` so they can enqueue a backfill — pre-#29823
154
+ * collections lack `summary_dense` / `summary_sparse` and every query
155
+ * referencing those named vectors fails with HTTP 400 until the collection
156
+ * is rebuilt. Mirrors `VellumQdrantClient.ensureCollection` for v1.
86
157
  */
87
- export async function ensureConceptPageCollection(): Promise<void> {
88
- if (_collectionReady) return;
158
+ export async function ensureConceptPageCollection(): Promise<{
159
+ migrated: boolean;
160
+ }> {
161
+ if (_collectionReady) return { migrated: false };
89
162
  if (_collectionReadyPromise) return _collectionReadyPromise;
90
163
 
91
164
  _collectionReadyPromise = ensureConceptPageCollectionOnce().finally(() => {
@@ -94,17 +167,54 @@ export async function ensureConceptPageCollection(): Promise<void> {
94
167
  return _collectionReadyPromise;
95
168
  }
96
169
 
97
- async function ensureConceptPageCollectionOnce(): Promise<void> {
170
+ async function ensureConceptPageCollectionOnce(): Promise<{
171
+ migrated: boolean;
172
+ }> {
98
173
  const client = getClient();
99
174
  const config = getConfig();
100
175
  const vectorSize = config.memory.qdrant.vectorSize;
101
176
  const onDisk = config.memory.qdrant.onDisk;
102
177
 
178
+ // A leftover sentinel means a prior call deleted the collection but never
179
+ // got to enqueue the reembed (e.g. createCollection threw, or the process
180
+ // died mid-rebuild). Carry that signal forward until the lifecycle hook
181
+ // clears it.
182
+ let migrated = reembedSentinelExists();
183
+
103
184
  try {
104
185
  const exists = await client.collectionExists(MEMORY_V2_COLLECTION);
105
186
  if (exists.exists) {
106
- _collectionReady = true;
107
- return;
187
+ // Assume compatible on probe failure rather than risk a destructive
188
+ // recreate — mirrors v1's posture in `VellumQdrantClient.ensureCollection`.
189
+ let info: Awaited<ReturnType<typeof client.getCollection>>;
190
+ try {
191
+ info = await client.getCollection(MEMORY_V2_COLLECTION);
192
+ } catch (err) {
193
+ log.warn(
194
+ { err, collection: MEMORY_V2_COLLECTION },
195
+ "Failed to probe v2 collection schema; assuming compatible",
196
+ );
197
+ _collectionReady = true;
198
+ return { migrated: false };
199
+ }
200
+
201
+ const missing = missingNamedVectors(info);
202
+ if (missing.length === 0) {
203
+ _collectionReady = true;
204
+ return { migrated: false };
205
+ }
206
+
207
+ log.warn(
208
+ { collection: MEMORY_V2_COLLECTION, missingNamedVectors: missing },
209
+ "Memory v2 concept-page collection schema drift detected — deleting and recreating; embeddings will be regenerated by background reembed",
210
+ );
211
+ // Persist the reembed intent BEFORE the destructive delete so a crash
212
+ // (or transient createCollection failure) between delete and recreate
213
+ // still triggers reembed on the next ensure call.
214
+ await writeReembedSentinel();
215
+ await client.deleteCollection(MEMORY_V2_COLLECTION);
216
+ migrated = true;
217
+ // Fall through to creation below.
108
218
  }
109
219
  } catch (err) {
110
220
  // Treat "not found"-shaped errors as "needs creation" and fall through.
@@ -124,15 +234,28 @@ async function ensureConceptPageCollectionOnce(): Promise<void> {
124
234
  distance: "Cosine",
125
235
  on_disk: onDisk,
126
236
  },
237
+ // Optional second dense vector covering the page's frontmatter
238
+ // `summary`. Pages without a summary store nothing under this name —
239
+ // Qdrant supports per-point named-vector subsets — so the named-vector
240
+ // index stays cheap until summaries are populated.
241
+ summary_dense: {
242
+ size: vectorSize,
243
+ distance: "Cosine",
244
+ on_disk: onDisk,
245
+ },
127
246
  },
128
247
  sparse_vectors: {
129
248
  sparse: {}, // Qdrant auto-infers sparse vector params
249
+ summary_sparse: {}, // BM25 sparse vector for the summary
130
250
  },
131
251
  hnsw_config: {
132
252
  on_disk: onDisk,
133
253
  m: 16,
134
254
  ef_construct: 100,
135
255
  },
256
+ optimizers_config: {
257
+ default_segment_number: 2,
258
+ },
136
259
  on_disk_payload: onDisk,
137
260
  });
138
261
  } catch (err) {
@@ -143,7 +266,7 @@ async function ensureConceptPageCollectionOnce(): Promise<void> {
143
266
  (err as { status: number }).status === 409
144
267
  ) {
145
268
  _collectionReady = true;
146
- return;
269
+ return { migrated };
147
270
  }
148
271
  throw err;
149
272
  }
@@ -156,33 +279,97 @@ async function ensureConceptPageCollectionOnce(): Promise<void> {
156
279
  });
157
280
 
158
281
  _collectionReady = true;
282
+ return { migrated };
283
+ }
284
+
285
+ /**
286
+ * Return the names of required named vectors absent from the collection's
287
+ * current schema. An empty array means the collection is fully migrated.
288
+ *
289
+ * If the response shape is unparseable (e.g. Qdrant returns an unexpected
290
+ * structure) we treat it as "everything is missing" so the caller's drift
291
+ * branch fires — combined with the `getCollection` try/catch in the caller,
292
+ * a thrown probe falls back to "assume compatible" while a parsed-but-empty
293
+ * response triggers the safer recreate.
294
+ */
295
+ function missingNamedVectors(
296
+ info: Awaited<ReturnType<QdrantRestClient["getCollection"]>>,
297
+ ): string[] {
298
+ const params = info.config?.params;
299
+ const dense = params?.vectors;
300
+ const sparse = (params as { sparse_vectors?: unknown } | undefined)
301
+ ?.sparse_vectors;
302
+ const denseNames =
303
+ dense && typeof dense === "object" && !("size" in dense)
304
+ ? new Set(Object.keys(dense))
305
+ : new Set<string>();
306
+ const sparseNames =
307
+ sparse && typeof sparse === "object"
308
+ ? new Set(Object.keys(sparse as Record<string, unknown>))
309
+ : new Set<string>();
310
+
311
+ const missing: string[] = [];
312
+ for (const name of REQUIRED_DENSE_VECTORS) {
313
+ if (!denseNames.has(name)) missing.push(name);
314
+ }
315
+ for (const name of REQUIRED_SPARSE_VECTORS) {
316
+ if (!sparseNames.has(name)) missing.push(name);
317
+ }
318
+ return missing;
159
319
  }
160
320
 
161
321
  /**
162
322
  * Upsert a concept page's dense + sparse embedding. The point ID is derived
163
323
  * deterministically from the slug so subsequent calls for the same slug
164
324
  * replace the prior point in place rather than accumulating duplicates.
325
+ *
326
+ * `summary` is optional — supplied when the page's frontmatter carries a
327
+ * `summary`, omitted otherwise. Pages without a summary store only the body
328
+ * vectors and fall back to body-only scoring at query time. The grouped
329
+ * shape enforces at the type level that summary dense and sparse are
330
+ * always written together.
165
331
  */
166
332
  export async function upsertConceptPageEmbedding(params: {
167
333
  slug: string;
168
334
  dense: number[];
169
335
  sparse: SparseEmbedding;
336
+ summary?: { dense: number[]; sparse: SparseEmbedding };
170
337
  updatedAt: number;
338
+ /**
339
+ * Optional payload discriminator. Used to distinguish skill-seeded points
340
+ * (`kind: "skill"`) from user-authored concept pages so namespace pruning
341
+ * via {@link pruneSlugsWithPrefixExcept} can scope deletes to a single kind.
342
+ * Omitted for plain concept pages.
343
+ */
344
+ kind?: string;
171
345
  }): Promise<void> {
172
346
  await ensureConceptPageCollection();
173
347
 
174
- const { slug, dense, sparse, updatedAt } = params;
348
+ const { slug, dense, sparse, summary, updatedAt, kind } = params;
175
349
  const client = getClient();
176
350
  const pointId = pointIdForSlug(slug);
177
351
 
352
+ // Qdrant lets us upsert any subset of named vectors per point. The summary
353
+ // entries appear only when the caller passed a `summary` block — pairing
354
+ // them at the type level keeps query-time fusion symmetric with the body
355
+ // channels.
356
+ const vector: Record<string, number[] | SparseEmbedding> = { dense, sparse };
357
+ if (summary) {
358
+ vector.summary_dense = summary.dense;
359
+ vector.summary_sparse = summary.sparse;
360
+ }
361
+
362
+ const payload: Record<string, unknown> = { slug, updated_at: updatedAt };
363
+ if (kind !== undefined) payload.kind = kind;
364
+
178
365
  const upsertOnce = () =>
179
366
  client.upsert(MEMORY_V2_COLLECTION, {
180
367
  wait: true,
181
368
  points: [
182
369
  {
183
370
  id: pointId,
184
- vector: { dense, sparse },
185
- payload: { slug, updated_at: updatedAt },
371
+ vector,
372
+ payload,
186
373
  },
187
374
  ],
188
375
  });
@@ -231,17 +418,25 @@ export async function deleteConceptPageEmbedding(slug: string): Promise<void> {
231
418
  * since skills now share the concept-page collection rather than living in a
232
419
  * dedicated one.
233
420
  *
421
+ * `kind` scopes pruning to a payload discriminator: only points whose
422
+ * `payload.kind` matches are eligible for deletion. This is critical because
423
+ * `validateSlug` permits user-authored concept pages slugged like
424
+ * `skills/foo`; without a kind filter they would collide with the skill
425
+ * namespace and be repeatedly pruned every seed run.
426
+ *
234
427
  * Idempotent: when the live `<prefix>*` slugs already match `activeSuffixes`,
235
428
  * the function performs a single scroll and no deletes.
236
429
  */
237
430
  export async function pruneSlugsWithPrefixExcept(
238
431
  prefix: string,
239
432
  activeSuffixes: readonly string[],
433
+ options: { kind?: string } = {},
240
434
  ): Promise<void> {
241
435
  await ensureConceptPageCollection();
242
436
 
243
437
  const client = getClient();
244
438
  const activeSet = new Set(activeSuffixes);
439
+ const requiredKind = options.kind;
245
440
 
246
441
  const doPrune = async (): Promise<void> => {
247
442
  const stalePointIds: Array<string | number> = [];
@@ -256,9 +451,15 @@ export async function pruneSlugsWithPrefixExcept(
256
451
  ...(offset !== undefined ? { offset } : {}),
257
452
  });
258
453
  for (const point of result.points) {
259
- const slug = (point.payload as { slug?: unknown } | null)?.slug;
454
+ const payload = point.payload as {
455
+ slug?: unknown;
456
+ kind?: unknown;
457
+ } | null;
458
+ const slug = payload?.slug;
260
459
  if (typeof slug !== "string") continue;
261
460
  if (!slug.startsWith(prefix)) continue;
461
+ if (requiredKind !== undefined && payload?.kind !== requiredKind)
462
+ continue;
262
463
  const suffix = slug.slice(prefix.length);
263
464
  if (!activeSet.has(suffix)) {
264
465
  stalePointIds.push(point.id);
@@ -290,6 +491,46 @@ export async function pruneSlugsWithPrefixExcept(
290
491
  }
291
492
  }
292
493
 
494
+ /**
495
+ * Approximate count of points in the v2 concept-page collection. Used by the
496
+ * daemon-startup rebuild hook to detect "collection exists but empty" — the
497
+ * crash-mid-rebuild recovery case where a prior boot dropped + recreated the
498
+ * collection but died before reembed completed. Returns `0` if the collection
499
+ * does not exist or the count call fails (treated as "needs reembed" by the
500
+ * caller).
501
+ */
502
+ export async function countConceptPagePoints(): Promise<number> {
503
+ await ensureConceptPageCollection();
504
+ try {
505
+ const result = await getClient().count(MEMORY_V2_COLLECTION, {
506
+ exact: false,
507
+ });
508
+ return result.count;
509
+ } catch (err) {
510
+ log.warn(
511
+ { err, collection: MEMORY_V2_COLLECTION },
512
+ "Failed to count v2 concept-page collection — treating as empty",
513
+ );
514
+ return 0;
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Probe whether the v2 concept-page collection currently exists in Qdrant
520
+ * **without** triggering creation. Read-only diagnostics use this to avoid
521
+ * the side effect of bootstrapping storage just by inspecting it.
522
+ */
523
+ export async function conceptPageCollectionExists(): Promise<boolean> {
524
+ const client = getClient();
525
+ try {
526
+ const result = await client.collectionExists(MEMORY_V2_COLLECTION);
527
+ return result.exists;
528
+ } catch (err) {
529
+ if (isCollectionMissing(err)) return false;
530
+ throw err;
531
+ }
532
+ }
533
+
293
534
  /**
294
535
  * Best-effort delete of the legacy `memory_v2_skills` Qdrant collection. Skill
295
536
  * embeddings now live alongside concept pages in `memory_v2_concept_pages`
@@ -319,9 +560,15 @@ export async function dropLegacySkillsCollection(): Promise<void> {
319
560
  * a normalized weighted-sum — because RRF would discard the score magnitudes
320
561
  * the activation formula needs.
321
562
  *
563
+ * Four channels are queried concurrently: body dense, body sparse, summary
564
+ * dense, summary sparse. The summary channels only return hits for pages whose
565
+ * frontmatter carries a `summary` (and therefore stored `summary_dense` /
566
+ * `summary_sparse` named vectors at upsert time). Pages without a summary
567
+ * surface body-only scores; callers fall back to body-only fusion for those.
568
+ *
322
569
  * Each channel returns up to `limit` hits. A slug is included in the result
323
- * if it appears in either channel; the missing channel's score is left
324
- * `undefined` so callers can detect single-channel matches.
570
+ * if it appears in any channel; missing channel scores stay `undefined` so
571
+ * callers can distinguish "no match in this channel" from "match with score 0".
325
572
  *
326
573
  * `restrictToSlugs`, when provided, filters the search server-side to only
327
574
  * those slugs (Qdrant `slug IN [...]` filter). Used by `simBatch` when the
@@ -355,42 +602,51 @@ export async function hybridQueryConceptPages(
355
602
  // Qdrant 1.13.x sparse-index crash that we've reproduced in the wild.
356
603
  const skipSparse = options?.skipSparse ?? false;
357
604
 
358
- const denseQuery = () =>
605
+ const queryDense = (using: string) =>
359
606
  client.query(MEMORY_V2_COLLECTION, {
360
607
  query: dense,
361
- using: "dense",
608
+ using,
362
609
  limit,
363
610
  with_payload: true,
364
611
  filter,
365
612
  });
366
- const sparseQuery = () =>
613
+ const querySparse = (using: string) =>
367
614
  client.query(MEMORY_V2_COLLECTION, {
368
615
  query: sparse,
369
- using: "sparse",
616
+ using,
370
617
  limit,
371
618
  with_payload: true,
372
619
  filter,
373
620
  });
374
621
 
375
- // Run both queries concurrently — they hit independent named vectors.
376
- // When sparse is gated off we still resolve a Promise so the destructuring
377
- // below stays uniform; the empty `points: []` matches the shape of a
378
- // no-hit Qdrant response.
622
+ // Run all four channels concurrently — they hit independent named vectors.
623
+ // When sparse is gated off the sparse channels still resolve a Promise so
624
+ // the destructuring below stays uniform; the empty `points: []` matches
625
+ // the shape of a no-hit Qdrant response.
379
626
  const emptyResult = {
380
627
  points: [] as Array<{ payload?: unknown; score?: number }>,
381
628
  };
382
629
  const runQueries = async () =>
383
- Promise.all([denseQuery(), skipSparse ? emptyResult : sparseQuery()]);
630
+ Promise.all([
631
+ queryDense("dense"),
632
+ skipSparse ? emptyResult : querySparse("sparse"),
633
+ queryDense("summary_dense"),
634
+ skipSparse ? emptyResult : querySparse("summary_sparse"),
635
+ ]);
384
636
 
385
637
  let denseResults;
386
638
  let sparseResults;
639
+ let summaryDenseResults;
640
+ let summarySparseResults;
387
641
  try {
388
- [denseResults, sparseResults] = await runQueries();
642
+ [denseResults, sparseResults, summaryDenseResults, summarySparseResults] =
643
+ await runQueries();
389
644
  } catch (err) {
390
645
  if (isCollectionMissing(err)) {
391
646
  _collectionReady = false;
392
647
  await ensureConceptPageCollection();
393
- [denseResults, sparseResults] = await runQueries();
648
+ [denseResults, sparseResults, summaryDenseResults, summarySparseResults] =
649
+ await runQueries();
394
650
  } else {
395
651
  throw err;
396
652
  }
@@ -399,108 +655,26 @@ export async function hybridQueryConceptPages(
399
655
  // Merge by slug. Missing-side scores stay undefined so the fuser can tell
400
656
  // "no match in this channel" apart from "match with score 0".
401
657
  const merged = new Map<string, ConceptPageQueryResult>();
402
- for (const point of denseResults.points ?? []) {
403
- const slug = (point.payload as { slug?: unknown } | null)?.slug;
404
- if (typeof slug !== "string") continue;
405
- merged.set(slug, { slug, denseScore: point.score ?? 0 });
406
- }
407
- for (const point of sparseResults.points ?? []) {
408
- const slug = (point.payload as { slug?: unknown } | null)?.slug;
409
- if (typeof slug !== "string") continue;
410
- const existing = merged.get(slug);
411
- if (existing) {
412
- existing.sparseScore = point.score ?? 0;
413
- } else {
414
- merged.set(slug, { slug, sparseScore: point.score ?? 0 });
658
+ const recordHit = (
659
+ points: Array<{ payload?: unknown; score?: number }> | undefined,
660
+ set: (entry: ConceptPageQueryResult, score: number) => void,
661
+ ): void => {
662
+ for (const point of points ?? []) {
663
+ const slug = (point.payload as { slug?: unknown } | null)?.slug;
664
+ if (typeof slug !== "string") continue;
665
+ const existing = merged.get(slug) ?? { slug };
666
+ set(existing, point.score ?? 0);
667
+ merged.set(slug, existing);
415
668
  }
416
- }
669
+ };
670
+ recordHit(denseResults.points, (e, s) => (e.denseScore = s));
671
+ recordHit(sparseResults.points, (e, s) => (e.sparseScore = s));
672
+ recordHit(summaryDenseResults.points, (e, s) => (e.summaryDenseScore = s));
673
+ recordHit(summarySparseResults.points, (e, s) => (e.summarySparseScore = s));
417
674
 
418
675
  return Array.from(merged.values());
419
676
  }
420
677
 
421
- /**
422
- * Page through the v2 concept-page collection and return up to `maxSamples`
423
- * stored dense vectors. Used by the anisotropy-fit pipeline to compute a
424
- * corpus mean + top-k principal components without re-embedding every page.
425
- *
426
- * Sparse vectors are skipped — anisotropy is a dense-embedding phenomenon, and
427
- * pulling the sparse side would just inflate the response. Payload is also
428
- * skipped because the fit doesn't need slug identity.
429
- *
430
- * Returns an empty array when the collection is empty or missing. Caller
431
- * decides what to do (typically: surface a "no vectors to fit" error).
432
- */
433
- export async function sampleConceptPageDenseVectors(
434
- maxSamples: number,
435
- ): Promise<number[][]> {
436
- if (maxSamples <= 0) return [];
437
- await ensureConceptPageCollection();
438
-
439
- const client = getClient();
440
- const out: number[][] = [];
441
- let offset: string | number | undefined = undefined;
442
- // Same pagination guard pattern as the rest of the file — bounds the loop
443
- // even if Qdrant somehow keeps handing back a non-null offset.
444
- const maxIterations = 10_000;
445
- const batchSize = Math.min(256, maxSamples);
446
-
447
- for (let i = 0; i < maxIterations; i++) {
448
- if (out.length >= maxSamples) break;
449
- const remaining = maxSamples - out.length;
450
- let result;
451
- try {
452
- result = await client.scroll(MEMORY_V2_COLLECTION, {
453
- limit: Math.min(batchSize, remaining),
454
- with_payload: false,
455
- // Fetch only the dense named vector — sparse is irrelevant for
456
- // anisotropy correction.
457
- with_vector: ["dense"],
458
- ...(offset !== undefined ? { offset } : {}),
459
- });
460
- } catch (err) {
461
- if (isCollectionMissing(err)) {
462
- _collectionReady = false;
463
- return out;
464
- }
465
- throw err;
466
- }
467
-
468
- for (const point of result.points) {
469
- const v = extractDenseVector(point.vector);
470
- if (v) out.push(v);
471
- if (out.length >= maxSamples) break;
472
- }
473
-
474
- const next = result.next_page_offset;
475
- if (next == null) break;
476
- offset = typeof next === "string" ? next : (next as number);
477
- }
478
-
479
- return out;
480
- }
481
-
482
- /**
483
- * Pull the `dense` named-vector payload out of a Qdrant point. Defensively
484
- * handles both the named-vector shape (`{ dense: [...] }`) and the legacy
485
- * unnamed-vector shape (`number[]`) so older collection layouts don't trip
486
- * the sampler. Returns `null` for shapes we don't recognise.
487
- */
488
- function extractDenseVector(vector: unknown): number[] | null {
489
- if (Array.isArray(vector)) {
490
- if (vector.every((n) => typeof n === "number")) {
491
- return vector as number[];
492
- }
493
- return null;
494
- }
495
- if (vector && typeof vector === "object") {
496
- const dense = (vector as { dense?: unknown }).dense;
497
- if (Array.isArray(dense) && dense.every((n) => typeof n === "number")) {
498
- return dense as number[];
499
- }
500
- }
501
- return null;
502
- }
503
-
504
678
  /**
505
679
  * Detect "collection not found" errors so callers can reset readiness and
506
680
  * retry after an external deletion (e.g. workspace reset).
@@ -10,8 +10,8 @@ import { readPage } from "./page-store.js";
10
10
 
11
11
  const log = getLogger("memory-v2-reranker");
12
12
 
13
- // ~512-token model context for bge-reranker-base; cap input to bound payload.
14
- const PASSAGE_CHAR_CAP = 240;
13
+ // Cap passage input to bound batched payload size and tokenization cost.
14
+ const PASSAGE_CHAR_CAP = 1500;
15
15
 
16
16
  interface CacheEntry {
17
17
  scores: Map<string, number>;
@@ -22,9 +22,16 @@ const CACHE_TTL_MS = 2 * 60 * 1000;
22
22
  const CACHE_MAX_ENTRIES = 64;
23
23
  const cache = new Map<string, CacheEntry>();
24
24
 
25
- function cacheKey(query: string, slugs: readonly string[]): string {
25
+ function cacheKey(
26
+ query: string,
27
+ slugs: readonly string[],
28
+ model: string,
29
+ dtype: string,
30
+ ): string {
26
31
  const sorted = [...slugs].sort().join("\0");
27
- return createHash("sha256").update(`${query}\0${sorted}`).digest("hex");
32
+ return createHash("sha256")
33
+ .update(`${model}\0${dtype}\0${query}\0${sorted}`)
34
+ .digest("hex");
28
35
  }
29
36
 
30
37
  function evictExpired(now: number): void {
@@ -73,6 +80,7 @@ export async function rerankCandidates(
73
80
  if (queries.length === 0) return [];
74
81
  if (candidates.length === 0) return queries.map(() => new Map());
75
82
 
83
+ const { model, dtype } = config.memory.v2.rerank;
76
84
  const now = Date.now();
77
85
  evictExpired(now);
78
86
 
@@ -84,7 +92,7 @@ export async function rerankCandidates(
84
92
  results[i] = new Map();
85
93
  continue;
86
94
  }
87
- const key = cacheKey(q, candidates);
95
+ const key = cacheKey(q, candidates, model, dtype);
88
96
  const cached = cache.get(key);
89
97
  if (cached) {
90
98
  // Refresh insertion order so frequently-hit entries survive eviction.
@@ -137,7 +145,6 @@ export async function rerankCandidates(
137
145
  }
138
146
  }
139
147
 
140
- const { model, dtype } = config.memory.v2.rerank;
141
148
  let scores: number[];
142
149
  try {
143
150
  const backend = getOrCreateRerankBackend(model, dtype);
@@ -162,7 +169,7 @@ export async function rerankCandidates(
162
169
  result.set(slugsForPassages[i], Math.max(0, Math.min(1, s)));
163
170
  }
164
171
  results[qi] = result;
165
- cache.set(cacheKey(queries[qi], candidates), {
172
+ cache.set(cacheKey(queries[qi], candidates, model, dtype), {
166
173
  scores: new Map(result),
167
174
  ts: now,
168
175
  });