@vellumai/assistant 0.7.2 → 0.8.0

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 (424) hide show
  1. package/ARCHITECTURE.md +45 -29
  2. package/Dockerfile +1 -0
  3. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  4. package/bun.lock +3 -0
  5. package/docs/architecture/memory.md +5 -2
  6. package/knip.json +1 -0
  7. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
  8. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  9. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  10. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  11. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  12. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  13. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  14. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
  15. package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
  16. package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
  17. package/openapi.yaml +470 -25
  18. package/package.json +3 -1
  19. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  20. package/src/__tests__/app-control-flow.test.ts +21 -11
  21. package/src/__tests__/approval-cascade.test.ts +8 -16
  22. package/src/__tests__/approval-routes-http.test.ts +6 -0
  23. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  24. package/src/__tests__/assistant-event.test.ts +0 -10
  25. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
  26. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
  27. package/src/__tests__/auto-analysis-end-to-end.test.ts +48 -0
  28. package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
  29. package/src/__tests__/call-constants.test.ts +10 -1
  30. package/src/__tests__/call-controller.test.ts +127 -0
  31. package/src/__tests__/call-conversation-messages.test.ts +8 -2
  32. package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
  33. package/src/__tests__/channel-readiness-service.test.ts +4 -2
  34. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +58 -28
  35. package/src/__tests__/config-loader-backfill.test.ts +379 -0
  36. package/src/__tests__/config-loader-platform-defaults.test.ts +284 -1
  37. package/src/__tests__/config-schema.test.ts +1 -0
  38. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
  39. package/src/__tests__/config-watcher.test.ts +140 -69
  40. package/src/__tests__/context-search-agent-runner.test.ts +61 -3
  41. package/src/__tests__/context-search-conversations-source.test.ts +0 -24
  42. package/src/__tests__/context-search-fanout.test.ts +0 -1
  43. package/src/__tests__/context-search-memory-source.test.ts +6 -33
  44. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
  45. package/src/__tests__/context-search-pkb-source.test.ts +12 -7
  46. package/src/__tests__/context-search-workspace-source.test.ts +0 -1
  47. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -0
  48. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
  49. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  50. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  51. package/src/__tests__/conversation-agent-loop.test.ts +457 -8
  52. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  53. package/src/__tests__/conversation-error.test.ts +150 -3
  54. package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
  55. package/src/__tests__/conversation-process-callsite.test.ts +38 -0
  56. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +74 -0
  58. package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
  59. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  60. package/src/__tests__/conversation-store.test.ts +0 -18
  61. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  62. package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
  63. package/src/__tests__/conversation-surfaces-data-persist.test.ts +476 -0
  64. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +61 -5
  65. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  66. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  67. package/src/__tests__/credentials-cli.test.ts +7 -0
  68. package/src/__tests__/cu-unified-flow.test.ts +176 -10
  69. package/src/__tests__/date-context.test.ts +164 -2
  70. package/src/__tests__/disk-pressure-guard.test.ts +262 -0
  71. package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
  72. package/src/__tests__/disk-pressure-policy.test.ts +241 -0
  73. package/src/__tests__/disk-pressure-routes.test.ts +379 -0
  74. package/src/__tests__/disk-pressure-tools.test.ts +277 -0
  75. package/src/__tests__/disk-usage.test.ts +150 -0
  76. package/src/__tests__/events-client-registration.test.ts +52 -0
  77. package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
  78. package/src/__tests__/file-write-tool.test.ts +4 -10
  79. package/src/__tests__/filing-service.test.ts +2 -20
  80. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -26
  81. package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
  82. package/src/__tests__/heartbeat-service.test.ts +260 -11
  83. package/src/__tests__/host-app-control-proxy.test.ts +195 -25
  84. package/src/__tests__/host-bash-proxy.test.ts +227 -34
  85. package/src/__tests__/host-bash-routes.test.ts +178 -13
  86. package/src/__tests__/host-cu-proxy.test.ts +210 -3
  87. package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
  88. package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
  89. package/src/__tests__/host-file-proxy.test.ts +268 -6
  90. package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
  91. package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
  92. package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
  93. package/src/__tests__/http-user-message-parity.test.ts +107 -1
  94. package/src/__tests__/injector-chain.test.ts +36 -16
  95. package/src/__tests__/injector-disk-pressure.test.ts +224 -0
  96. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +154 -67
  98. package/src/__tests__/managed-profile-guard.test.ts +18 -0
  99. package/src/__tests__/mcp-abort-signal.test.ts +130 -0
  100. package/src/__tests__/memory-admin-recall.test.ts +3 -11
  101. package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
  102. package/src/__tests__/normalize-onboarding.test.ts +180 -0
  103. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  104. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  105. package/src/__tests__/oauth-cli.test.ts +121 -0
  106. package/src/__tests__/oauth-connect-routes.test.ts +316 -0
  107. package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
  108. package/src/__tests__/onboarding-persona-write.test.ts +308 -0
  109. package/src/__tests__/openai-provider.test.ts +45 -8
  110. package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
  111. package/src/__tests__/platform-callback-registration.test.ts +21 -4
  112. package/src/__tests__/platform.test.ts +2 -1
  113. package/src/__tests__/playbook-execution.test.ts +0 -43
  114. package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
  115. package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
  116. package/src/__tests__/provider-tool-name.test.ts +23 -0
  117. package/src/__tests__/relay-server.test.ts +60 -5
  118. package/src/__tests__/runtime-events-sse.test.ts +4 -8
  119. package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
  120. package/src/__tests__/secret-ingress-http.test.ts +0 -1
  121. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  122. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  123. package/src/__tests__/secret-response-routing.test.ts +7 -5
  124. package/src/__tests__/server-history-render.test.ts +82 -0
  125. package/src/__tests__/skill-include-graph.test.ts +31 -0
  126. package/src/__tests__/skill-load-tool.test.ts +44 -16
  127. package/src/__tests__/skills.test.ts +39 -0
  128. package/src/__tests__/suggestion-routes.test.ts +46 -0
  129. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  130. package/src/__tests__/tool-executor.test.ts +155 -0
  131. package/src/__tests__/twilio-validation.test.ts +2 -2
  132. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  133. package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
  134. package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
  135. package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
  136. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +120 -0
  137. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  138. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +78 -0
  139. package/src/agent/loop.ts +11 -0
  140. package/src/approvals/guardian-request-resolvers.ts +3 -32
  141. package/src/backup/snapshot-lock.ts +2 -27
  142. package/src/bundler/compiler-tools.ts +3 -2
  143. package/src/calls/call-constants.ts +5 -8
  144. package/src/calls/call-controller.ts +130 -67
  145. package/src/calls/call-conversation-messages.ts +46 -10
  146. package/src/calls/relay-server.ts +7 -1
  147. package/src/calls/voice-session-bridge.ts +1 -1
  148. package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
  149. package/src/cli/commands/bash.ts +35 -108
  150. package/src/cli/commands/contacts.ts +64 -25
  151. package/src/cli/commands/credentials.ts +56 -0
  152. package/src/cli/commands/memory-v2.ts +11 -10
  153. package/src/cli/commands/oauth/__tests__/connect.test.ts +401 -219
  154. package/src/cli/commands/oauth/connect.ts +124 -40
  155. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
  156. package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
  157. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  158. package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
  159. package/src/cli/commands/platform/index.ts +16 -7
  160. package/src/cli/commands/status.ts +57 -0
  161. package/src/cli/program.ts +4 -2
  162. package/src/config/assistant-feature-flags.ts +13 -3
  163. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  164. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
  165. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
  166. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
  167. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
  168. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
  169. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
  170. package/src/config/env.ts +0 -8
  171. package/src/config/feature-flag-registry.json +13 -5
  172. package/src/config/loader.ts +199 -27
  173. package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
  174. package/src/config/schemas/call-site-catalog.ts +14 -0
  175. package/src/config/schemas/channels.ts +0 -5
  176. package/src/config/schemas/heartbeat.ts +1 -1
  177. package/src/config/schemas/llm.ts +2 -0
  178. package/src/config/schemas/memory-lifecycle.ts +13 -0
  179. package/src/config/schemas/memory-v2.ts +76 -12
  180. package/src/config/schemas/platform.ts +43 -3
  181. package/src/config/schemas/services.ts +28 -0
  182. package/src/config/seed-inference-profiles.ts +230 -33
  183. package/src/contacts/contact-store.ts +0 -25
  184. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +32 -0
  185. package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
  186. package/src/daemon/assistant-attachments.ts +4 -4
  187. package/src/daemon/config-watcher.ts +85 -57
  188. package/src/daemon/conversation-agent-loop-handlers.ts +38 -0
  189. package/src/daemon/conversation-agent-loop.ts +183 -43
  190. package/src/daemon/conversation-error.ts +87 -15
  191. package/src/daemon/conversation-lifecycle.ts +22 -10
  192. package/src/daemon/conversation-process.ts +8 -0
  193. package/src/daemon/conversation-runtime-assembly.ts +26 -0
  194. package/src/daemon/conversation-store.ts +2 -2
  195. package/src/daemon/conversation-surfaces.ts +211 -29
  196. package/src/daemon/conversation-tool-setup.ts +66 -19
  197. package/src/daemon/conversation.ts +18 -23
  198. package/src/daemon/date-context.ts +71 -22
  199. package/src/daemon/disk-pressure-background-gate.ts +73 -0
  200. package/src/daemon/disk-pressure-guard.ts +343 -0
  201. package/src/daemon/disk-pressure-policy.ts +163 -0
  202. package/src/daemon/handlers/shared.ts +26 -1
  203. package/src/daemon/handlers/skills.ts +3 -4
  204. package/src/daemon/host-app-control-proxy.ts +137 -41
  205. package/src/daemon/host-bash-proxy.ts +47 -22
  206. package/src/daemon/host-browser-proxy.ts +1 -1
  207. package/src/daemon/host-cu-proxy.ts +50 -4
  208. package/src/daemon/host-file-proxy.ts +44 -8
  209. package/src/daemon/host-transfer-proxy.ts +97 -6
  210. package/src/daemon/lifecycle.ts +167 -101
  211. package/src/daemon/meet-host-supervisor.ts +4 -4
  212. package/src/daemon/meet-manifest-loader.ts +0 -1
  213. package/src/daemon/memory-v2-startup.ts +66 -15
  214. package/src/daemon/message-protocol.ts +3 -0
  215. package/src/daemon/message-types/conversations.ts +4 -0
  216. package/src/daemon/message-types/disk-pressure.ts +9 -0
  217. package/src/daemon/message-types/messages.ts +22 -1
  218. package/src/daemon/profiler-run-store.ts +5 -5
  219. package/src/daemon/tool-setup-types.ts +2 -2
  220. package/src/documents/document-store.ts +119 -0
  221. package/src/filing/filing-service.ts +29 -5
  222. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
  223. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
  224. package/src/heartbeat/heartbeat-run-store.ts +13 -0
  225. package/src/heartbeat/heartbeat-service.ts +205 -31
  226. package/src/home/feed-scheduler.ts +18 -0
  227. package/src/inbound/platform-callback-registration.ts +8 -15
  228. package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
  229. package/src/ipc/assistant-server.ts +149 -38
  230. package/src/ipc/gateway-client.ts +37 -3
  231. package/src/ipc/skill-server.ts +99 -42
  232. package/src/live-voice/live-voice-archive.ts +4 -4
  233. package/src/live-voice/protocol.ts +5 -7
  234. package/src/media/image-service.ts +1 -7
  235. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
  236. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +34 -51
  237. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
  238. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
  239. package/src/memory/admin.ts +5 -9
  240. package/src/memory/context-search/agent-runner.ts +19 -2
  241. package/src/memory/context-search/sources/conversations.ts +2 -11
  242. package/src/memory/context-search/sources/memory-v2.ts +1 -16
  243. package/src/memory/context-search/sources/memory.ts +2 -3
  244. package/src/memory/context-search/sources/pkb.ts +2 -3
  245. package/src/memory/context-search/types.ts +0 -1
  246. package/src/memory/conversation-crud.ts +4 -12
  247. package/src/memory/db-init.ts +2 -0
  248. package/src/memory/embedding-runtime-manager.ts +119 -5
  249. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +136 -82
  250. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  251. package/src/memory/graph/conversation-graph-memory.ts +72 -61
  252. package/src/memory/graph/extraction.ts +1 -3
  253. package/src/memory/graph/graph-search.test.ts +11 -67
  254. package/src/memory/graph/graph-search.ts +4 -24
  255. package/src/memory/graph/retriever.test.ts +12 -1
  256. package/src/memory/graph/retriever.ts +10 -15
  257. package/src/memory/graph/tool-handlers.ts +3 -4
  258. package/src/memory/graph/tools.ts +4 -4
  259. package/src/memory/indexer.ts +53 -45
  260. package/src/memory/job-handlers/backfill.ts +2 -11
  261. package/src/memory/job-handlers/cleanup.ts +43 -0
  262. package/src/memory/job-handlers/embedding.ts +6 -8
  263. package/src/memory/job-handlers/summarization.ts +2 -7
  264. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +116 -0
  265. package/src/memory/jobs/embed-concept-page.ts +223 -87
  266. package/src/memory/jobs-store.ts +48 -0
  267. package/src/memory/jobs-worker.ts +85 -43
  268. package/src/memory/memory-v2-activation-log-store.ts +32 -14
  269. package/src/memory/memory-v2-concept-frequency.ts +169 -0
  270. package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
  271. package/src/memory/migrations/index.ts +1 -0
  272. package/src/memory/pkb/pkb-search.test.ts +7 -0
  273. package/src/memory/pkb/pkb-search.ts +4 -5
  274. package/src/memory/qdrant-client.ts +3 -13
  275. package/src/memory/rerank-local.ts +374 -0
  276. package/src/memory/search/semantic.ts +10 -72
  277. package/src/memory/trace-event-store.ts +1 -17
  278. package/src/memory/v2/__tests__/activation.test.ts +346 -255
  279. package/src/memory/v2/__tests__/consolidation-job.test.ts +61 -40
  280. package/src/memory/v2/__tests__/injection.test.ts +297 -190
  281. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
  282. package/src/memory/v2/__tests__/qdrant.test.ts +326 -9
  283. package/src/memory/v2/__tests__/reranker.test.ts +338 -0
  284. package/src/memory/v2/__tests__/sim.test.ts +113 -196
  285. package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
  286. package/src/memory/v2/__tests__/static-context.test.ts +77 -14
  287. package/src/memory/v2/__tests__/sweep-job.test.ts +19 -33
  288. package/src/memory/v2/activation.ts +149 -156
  289. package/src/memory/v2/consolidation-job.ts +69 -20
  290. package/src/memory/v2/injection.ts +75 -68
  291. package/src/memory/v2/page-store.ts +39 -0
  292. package/src/memory/v2/prompts/consolidation.ts +41 -1
  293. package/src/memory/v2/qdrant.ts +306 -46
  294. package/src/memory/v2/reranker.ts +177 -0
  295. package/src/memory/v2/sim.ts +77 -110
  296. package/src/memory/v2/skill-content.ts +4 -3
  297. package/src/memory/v2/skill-store.ts +82 -59
  298. package/src/memory/v2/static-context.ts +26 -8
  299. package/src/memory/v2/sweep-job.ts +5 -6
  300. package/src/memory/v2/types.ts +17 -10
  301. package/src/notifications/copy-composer.ts +47 -0
  302. package/src/notifications/decision-engine.ts +46 -0
  303. package/src/notifications/signal.ts +4 -0
  304. package/src/oauth/AGENTS.md +3 -1
  305. package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
  306. package/src/oauth/connect-orchestrator.ts +2 -0
  307. package/src/oauth/connection-resolver.test.ts +66 -1
  308. package/src/oauth/connection-resolver.ts +55 -1
  309. package/src/oauth/oauth-connect-state.ts +77 -0
  310. package/src/oauth/seed-providers.ts +58 -1
  311. package/src/permissions/gateway-threshold-reader.ts +116 -8
  312. package/src/permissions/prompter.ts +86 -96
  313. package/src/permissions/secret-prompter.ts +31 -31
  314. package/src/plugins/defaults/injectors.ts +36 -4
  315. package/src/plugins/defaults/memory-retrieval.ts +5 -6
  316. package/src/plugins/types.ts +7 -0
  317. package/src/proactive-artifact/aux-message-injector.ts +74 -0
  318. package/src/proactive-artifact/decision.test.ts +226 -0
  319. package/src/proactive-artifact/decision.ts +165 -0
  320. package/src/proactive-artifact/index.ts +7 -0
  321. package/src/proactive-artifact/job.test.ts +914 -0
  322. package/src/proactive-artifact/job.ts +366 -0
  323. package/src/proactive-artifact/message-copy.ts +58 -0
  324. package/src/proactive-artifact/trigger-state.test.ts +277 -0
  325. package/src/proactive-artifact/trigger-state.ts +119 -0
  326. package/src/prompts/normalize-onboarding.ts +80 -0
  327. package/src/prompts/persona-resolver.ts +101 -9
  328. package/src/prompts/system-prompt.ts +21 -7
  329. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  330. package/src/prompts/templates/SOUL.md +13 -28
  331. package/src/providers/__tests__/retry-callsite.test.ts +222 -1
  332. package/src/providers/model-intents.ts +7 -0
  333. package/src/providers/openrouter/client.ts +8 -0
  334. package/src/providers/retry.ts +50 -0
  335. package/src/providers/types.ts +1 -0
  336. package/src/runtime/__tests__/agent-wake.test.ts +456 -3
  337. package/src/runtime/agent-wake.ts +238 -100
  338. package/src/runtime/assistant-event-hub.ts +36 -6
  339. package/src/runtime/assistant-event.ts +0 -1
  340. package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
  341. package/src/runtime/auth/route-policy.ts +15 -1
  342. package/src/runtime/auth/same-actor.ts +216 -0
  343. package/src/runtime/channel-approvals.ts +3 -2
  344. package/src/runtime/channel-retry-sweep.ts +65 -1
  345. package/src/runtime/local-actor-identity.ts +52 -11
  346. package/src/runtime/pending-interactions.ts +27 -15
  347. package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
  348. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
  349. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  350. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  351. package/src/runtime/routes/approval-routes.ts +7 -3
  352. package/src/runtime/routes/client-routes.ts +20 -2
  353. package/src/runtime/routes/consolidation-routes.ts +8 -9
  354. package/src/runtime/routes/contact-routes.ts +0 -25
  355. package/src/runtime/routes/conversation-query-routes.ts +44 -1
  356. package/src/runtime/routes/conversation-routes.ts +35 -26
  357. package/src/runtime/routes/debug-bash-routes.ts +165 -0
  358. package/src/runtime/routes/disk-pressure-routes.ts +121 -0
  359. package/src/runtime/routes/document-pdf-renderer.ts +6 -2
  360. package/src/runtime/routes/documents-routes.ts +2 -75
  361. package/src/runtime/routes/events-routes.ts +41 -9
  362. package/src/runtime/routes/filing-routes.ts +2 -3
  363. package/src/runtime/routes/host-bash-routes.ts +23 -3
  364. package/src/runtime/routes/host-cu-routes.ts +33 -6
  365. package/src/runtime/routes/host-file-routes.ts +32 -6
  366. package/src/runtime/routes/host-transfer-routes.ts +79 -16
  367. package/src/runtime/routes/identity-routes.ts +7 -138
  368. package/src/runtime/routes/inbound-message-handler.ts +77 -12
  369. package/src/runtime/routes/index.ts +6 -0
  370. package/src/runtime/routes/memory-item-routes.test.ts +37 -17
  371. package/src/runtime/routes/memory-item-routes.ts +5 -6
  372. package/src/runtime/routes/memory-v2-routes.ts +136 -17
  373. package/src/runtime/routes/oauth-connect-routes.ts +153 -0
  374. package/src/runtime/verification-outbound-actions.ts +4 -4
  375. package/src/schedule/run-script.ts +37 -5
  376. package/src/schedule/scheduler.ts +20 -1
  377. package/src/security/encrypted-store.ts +2 -0
  378. package/src/security/secure-keys.ts +55 -0
  379. package/src/skills/include-graph.ts +35 -13
  380. package/src/skills/remote-skill-policy.ts +4 -10
  381. package/src/subagent/index.ts +1 -7
  382. package/src/subagent/manager.ts +1 -15
  383. package/src/tasks/task-runner.ts +0 -1
  384. package/src/tasks/task-store.ts +0 -3
  385. package/src/tools/background-tool-registry.ts +17 -3
  386. package/src/tools/document/document-tool.ts +20 -0
  387. package/src/tools/executor.ts +18 -2
  388. package/src/tools/host-filesystem/edit.test.ts +151 -0
  389. package/src/tools/host-filesystem/edit.ts +43 -1
  390. package/src/tools/host-filesystem/read.test.ts +129 -0
  391. package/src/tools/host-filesystem/read.ts +43 -1
  392. package/src/tools/host-filesystem/transfer.test.ts +127 -2
  393. package/src/tools/host-filesystem/transfer.ts +56 -11
  394. package/src/tools/host-filesystem/write.test.ts +134 -0
  395. package/src/tools/host-filesystem/write.ts +43 -1
  396. package/src/tools/host-terminal/host-shell.ts +13 -6
  397. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  398. package/src/tools/memory/register.test.ts +14 -9
  399. package/src/tools/memory/register.ts +1 -2
  400. package/src/tools/permission-checker.ts +15 -0
  401. package/src/tools/provider-tool-name.ts +28 -0
  402. package/src/tools/registry.ts +30 -9
  403. package/src/tools/skills/load.ts +24 -20
  404. package/src/tools/terminal/shell.ts +9 -1
  405. package/src/tools/tool-approval-handler.ts +31 -6
  406. package/src/tools/tool-name-aliases.ts +19 -0
  407. package/src/tools/types.ts +43 -3
  408. package/src/tts/provider-catalog.ts +3 -5
  409. package/src/util/disk-usage.ts +138 -0
  410. package/src/util/platform.ts +21 -11
  411. package/src/util/process-liveness.ts +26 -0
  412. package/src/workspace/heartbeat-service.ts +19 -0
  413. package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
  414. package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
  415. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +14 -0
  416. package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
  417. package/src/workspace/migrations/069-seed-onboarding-threads.ts +28 -0
  418. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  419. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  420. package/src/workspace/migrations/registry.ts +14 -0
  421. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
  422. package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
  423. package/src/memory/v2/skill-qdrant.ts +0 -404
  424. package/src/signals/bash.ts +0 -198
@@ -1,23 +1,20 @@
1
1
  /**
2
- * Tests for the v2 consolidation entry in `maybeEnqueueGraphMaintenanceJobs`.
2
+ * Tests for v1/v2 mutual exclusion in `maybeEnqueueGraphMaintenanceJobs`.
3
3
  *
4
- * Coverage matrix (from PR 24 acceptance criteria):
5
- * - Flag off → no `memory_v2_consolidate` row enqueued, even after the
6
- * interval has elapsed.
7
- * - Flag on + config off → still no row enqueued (both gates required).
8
- * - Flag on + config on, no prior checkpoint → row enqueued, checkpoint
9
- * stamped to `nowMs`.
10
- * - Flag on + config on, recent checkpoint → no row enqueued (interval
11
- * not yet elapsed).
12
- * - Flag on + config on, stale checkpoint → row enqueued, checkpoint
13
- * refreshed.
14
- * - The v1 maintenance entries (decay, consolidate, pattern_scan,
15
- * narrative) still fire under the v2 path — adding the v2 entry must
16
- * not regress v1 scheduling.
4
+ * The schedule is mutually exclusive: when `memory.v2.enabled` is true,
5
+ * only `memory_v2_consolidate` is scheduled; otherwise the four v1
6
+ * entries (decay, consolidate, pattern_scan, narrative) fire and the v2
7
+ * entry does not.
17
8
  *
18
- * The sweep job is intentionally NOT scheduled here: PR 24 wires it into
19
- * the `graph_extract` debounce in `indexer.ts`. Those triggers are covered
20
- * by the separate trigger-path tests; this file owns only the cron entries.
9
+ * Coverage:
10
+ * - Config off only v1 entries fire (no `memory_v2_consolidate`).
11
+ * - Config on, no prior checkpoint only the v2 entry fires.
12
+ * - Config on, recent checkpoint → no v2 row (interval not yet elapsed).
13
+ * - Config on, stale checkpoint → v2 row enqueued, checkpoint refreshed.
14
+ *
15
+ * The sweep job is intentionally NOT scheduled here: it is wired into the
16
+ * `graph_extract` debounce in `indexer.ts`. Those triggers are covered by
17
+ * the separate trigger-path tests; this file owns only the cron entries.
21
18
  *
22
19
  * Tests use a temp workspace pinned via `VELLUM_WORKSPACE_DIR` so the DB
23
20
  * lives under `tmpdir()` and `~/.vellum/` is never touched.
@@ -27,7 +24,6 @@ import { tmpdir } from "node:os";
27
24
  import { join } from "node:path";
28
25
  import {
29
26
  afterAll,
30
- afterEach,
31
27
  beforeAll,
32
28
  beforeEach,
33
29
  describe,
@@ -69,8 +65,6 @@ const { getDb } = await import("../db-connection.js");
69
65
  const { initializeDb } = await import("../db-init.js");
70
66
  const { resetTestTables } = await import("../raw-query.js");
71
67
  const { memoryJobs } = await import("../schema.js");
72
- const { _setOverridesForTesting } =
73
- await import("../../config/assistant-feature-flags.js");
74
68
  const { applyNestedDefaults } = await import("../../config/loader.js");
75
69
  const { setMemoryCheckpoint, deleteMemoryCheckpoint } =
76
70
  await import("../checkpoints.js");
@@ -111,26 +105,10 @@ beforeEach(() => {
111
105
  resetTestTables("memory_jobs", "memory_checkpoints");
112
106
  });
113
107
 
114
- afterEach(() => {
115
- _setOverridesForTesting({});
116
- });
117
-
118
108
  // ---------------------------------------------------------------------------
119
109
 
120
110
  describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
121
- test("does not enqueue consolidate when memory-v2-enabled flag is off", () => {
122
- _setOverridesForTesting({ "memory-v2-enabled": false });
123
- const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
124
-
125
- // Force the interval to look elapsed. If the gate failed open, this
126
- // would be enough to enqueue a job.
127
- maybeEnqueueGraphMaintenanceJobs(config, Date.now());
128
-
129
- expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
130
- });
131
-
132
111
  test("does not enqueue consolidate when config.memory.v2.enabled is off", () => {
133
- _setOverridesForTesting({ "memory-v2-enabled": true });
134
112
  const config = buildConfig({ v2Enabled: false, intervalHours: 1 });
135
113
 
136
114
  maybeEnqueueGraphMaintenanceJobs(config, Date.now());
@@ -138,17 +116,20 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
138
116
  expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
139
117
  });
140
118
 
141
- test("enqueues consolidate when both gates are on and no checkpoint exists", () => {
142
- _setOverridesForTesting({ "memory-v2-enabled": true });
119
+ test("enqueues consolidate when v2 is on and no checkpoint exists", () => {
143
120
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
144
121
 
145
122
  maybeEnqueueGraphMaintenanceJobs(config, Date.now());
146
123
 
147
124
  expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
125
+ // v1 entries are suppressed when v2 is active.
126
+ expect(countPendingJobs("graph_decay")).toBe(0);
127
+ expect(countPendingJobs("graph_consolidate")).toBe(0);
128
+ expect(countPendingJobs("graph_pattern_scan")).toBe(0);
129
+ expect(countPendingJobs("graph_narrative_refine")).toBe(0);
148
130
  });
149
131
 
150
132
  test("does not enqueue consolidate before the interval has elapsed", () => {
151
- _setOverridesForTesting({ "memory-v2-enabled": true });
152
133
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
153
134
 
154
135
  const now = Date.now();
@@ -161,7 +142,6 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
161
142
  });
162
143
 
163
144
  test("enqueues consolidate again once the interval elapses", () => {
164
- _setOverridesForTesting({ "memory-v2-enabled": true });
165
145
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
166
146
 
167
147
  const now = Date.now();
@@ -177,7 +157,6 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
177
157
  });
178
158
 
179
159
  test("respects a custom consolidation_interval_hours value", () => {
180
- _setOverridesForTesting({ "memory-v2-enabled": true });
181
160
  const config = buildConfig({ v2Enabled: true, intervalHours: 6 });
182
161
 
183
162
  const now = Date.now();
@@ -200,11 +179,10 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
200
179
  expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
201
180
  });
202
181
 
203
- test("v1 graph maintenance entries still fire alongside the v2 entry", () => {
204
- _setOverridesForTesting({ "memory-v2-enabled": true });
182
+ test("v1 maintenance entries are suppressed when v2 is active", () => {
205
183
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
206
184
 
207
- // No checkpoints set — every entry should be due.
185
+ // No checkpoints set — every entry would be due if it were scheduled.
208
186
  deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
209
187
  deleteMemoryCheckpoint("graph_maintenance:consolidate:last_run");
210
188
  deleteMemoryCheckpoint("graph_maintenance:pattern_scan:last_run");
@@ -213,23 +191,28 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
213
191
 
214
192
  maybeEnqueueGraphMaintenanceJobs(config, Date.now());
215
193
 
216
- expect(countPendingJobs("graph_decay")).toBe(1);
217
- expect(countPendingJobs("graph_consolidate")).toBe(1);
218
- expect(countPendingJobs("graph_pattern_scan")).toBe(1);
219
- expect(countPendingJobs("graph_narrative_refine")).toBe(1);
194
+ expect(countPendingJobs("graph_decay")).toBe(0);
195
+ expect(countPendingJobs("graph_consolidate")).toBe(0);
196
+ expect(countPendingJobs("graph_pattern_scan")).toBe(0);
197
+ expect(countPendingJobs("graph_narrative_refine")).toBe(0);
220
198
  expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
221
199
  });
222
200
 
223
- test("flag-off path does not enqueue v2 but still fires the v1 entries", () => {
224
- _setOverridesForTesting({ "memory-v2-enabled": false });
225
- const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
201
+ test("v2-off path fires v1 entries and does not enqueue v2", () => {
202
+ const config = buildConfig({ v2Enabled: false, intervalHours: 1 });
226
203
 
227
204
  deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
205
+ deleteMemoryCheckpoint("graph_maintenance:consolidate:last_run");
206
+ deleteMemoryCheckpoint("graph_maintenance:pattern_scan:last_run");
207
+ deleteMemoryCheckpoint("graph_maintenance:narrative:last_run");
228
208
  deleteMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY);
229
209
 
230
210
  maybeEnqueueGraphMaintenanceJobs(config, Date.now());
231
211
 
232
212
  expect(countPendingJobs("graph_decay")).toBe(1);
213
+ expect(countPendingJobs("graph_consolidate")).toBe(1);
214
+ expect(countPendingJobs("graph_pattern_scan")).toBe(1);
215
+ expect(countPendingJobs("graph_narrative_refine")).toBe(1);
233
216
  expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
234
217
  });
235
218
  });
@@ -29,7 +29,6 @@ import { memoryV2ActivationLogs } from "../schema.js";
29
29
  import {
30
30
  sampleConcepts,
31
31
  sampleConfig,
32
- sampleSkills,
33
32
  } from "./fixtures/memory-v2-activation-fixtures.js";
34
33
 
35
34
  initializeDb();
@@ -53,7 +52,6 @@ describe("memory-v2-activation-log-store", () => {
53
52
  turn: 3,
54
53
  mode: "per-turn",
55
54
  concepts: sampleConcepts,
56
- skills: sampleSkills,
57
55
  config: sampleConfig,
58
56
  });
59
57
 
@@ -65,7 +63,6 @@ describe("memory-v2-activation-log-store", () => {
65
63
  expect(result!.turn).toBe(3);
66
64
  expect(result!.mode).toBe("per-turn");
67
65
  expect(result!.concepts).toEqual(sampleConcepts);
68
- expect(result!.skills).toEqual(sampleSkills);
69
66
  expect(result!.config).toEqual(sampleConfig);
70
67
  });
71
68
 
@@ -82,7 +79,6 @@ describe("memory-v2-activation-log-store", () => {
82
79
  turn: 1,
83
80
  mode: "context-load",
84
81
  concepts: sampleConcepts,
85
- skills: sampleSkills,
86
82
  config: sampleConfig,
87
83
  });
88
84
  recordMemoryV2ActivationLog({
@@ -90,7 +86,6 @@ describe("memory-v2-activation-log-store", () => {
90
86
  turn: 2,
91
87
  mode: "per-turn",
92
88
  concepts: sampleConcepts,
93
- skills: sampleSkills,
94
89
  config: sampleConfig,
95
90
  });
96
91
 
@@ -110,7 +105,6 @@ describe("memory-v2-activation-log-store", () => {
110
105
  turn: 3,
111
106
  mode: "per-turn",
112
107
  concepts: sampleConcepts,
113
- skills: sampleSkills,
114
108
  config: sampleConfig,
115
109
  });
116
110
 
@@ -0,0 +1,272 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ mock.module("../../util/logger.js", () => ({
4
+ getLogger: () =>
5
+ new Proxy({} as Record<string, unknown>, {
6
+ get: () => () => {},
7
+ }),
8
+ }));
9
+
10
+ mock.module("../../config/loader.js", () => ({
11
+ getConfig: () => ({
12
+ ui: {},
13
+ model: "test",
14
+ provider: "test",
15
+ memory: { enabled: false },
16
+ rateLimit: { maxRequestsPerMinute: 0 },
17
+ secretDetection: { enabled: false },
18
+ }),
19
+ }));
20
+
21
+ let listPagesImpl: (workspaceDir: string) => Promise<string[]> = async () => [];
22
+
23
+ mock.module("../v2/page-store.js", () => ({
24
+ listPages: (workspaceDir: string) => listPagesImpl(workspaceDir),
25
+ }));
26
+
27
+ import { getDb } from "../db-connection.js";
28
+ import { initializeDb } from "../db-init.js";
29
+ import {
30
+ type MemoryV2ConceptRowRecord,
31
+ recordMemoryV2ActivationLog,
32
+ } from "../memory-v2-activation-log-store.js";
33
+ import { getConceptFrequencySummary } from "../memory-v2-concept-frequency.js";
34
+ import { memoryV2ActivationLogs } from "../schema.js";
35
+ import { sampleConfig } from "./fixtures/memory-v2-activation-fixtures.js";
36
+
37
+ initializeDb();
38
+
39
+ const WORKSPACE = "/tmp/memory-v2-concept-frequency-test";
40
+
41
+ function makeConcept(
42
+ slug: string,
43
+ status: MemoryV2ConceptRowRecord["status"],
44
+ ): MemoryV2ConceptRowRecord {
45
+ return {
46
+ slug,
47
+ finalActivation: 0.5,
48
+ ownActivation: 0.4,
49
+ priorActivation: 0.1,
50
+ simUser: 0.3,
51
+ simAssistant: 0.2,
52
+ simNow: 0.1,
53
+ simUserRerankBoost: 0,
54
+ simAssistantRerankBoost: 0,
55
+ inRerankPool: false,
56
+ spreadContribution: 0.1,
57
+ source: "ann_top50",
58
+ status,
59
+ };
60
+ }
61
+
62
+ function resetTables(): void {
63
+ getDb().delete(memoryV2ActivationLogs).run();
64
+ }
65
+
66
+ describe("memory-v2-concept-frequency", () => {
67
+ beforeEach(() => {
68
+ resetTables();
69
+ listPagesImpl = async () => [];
70
+ });
71
+
72
+ test("aggregates per-status counts across multiple turns", async () => {
73
+ const conv = "conv-1";
74
+ recordMemoryV2ActivationLog({
75
+ conversationId: conv,
76
+ turn: 1,
77
+ mode: "context-load",
78
+ concepts: [
79
+ makeConcept("alice", "injected"),
80
+ makeConcept("bob", "not_injected"),
81
+ ],
82
+ config: sampleConfig,
83
+ });
84
+ recordMemoryV2ActivationLog({
85
+ conversationId: conv,
86
+ turn: 2,
87
+ mode: "per-turn",
88
+ concepts: [
89
+ makeConcept("alice", "in_context"),
90
+ makeConcept("bob", "injected"),
91
+ ],
92
+ config: sampleConfig,
93
+ });
94
+ recordMemoryV2ActivationLog({
95
+ conversationId: conv,
96
+ turn: 3,
97
+ mode: "per-turn",
98
+ concepts: [
99
+ makeConcept("alice", "injected"),
100
+ makeConcept("charlie", "page_missing"),
101
+ ],
102
+ config: sampleConfig,
103
+ });
104
+
105
+ listPagesImpl = async () => ["alice", "bob", "delta"];
106
+
107
+ const result = await getConceptFrequencySummary(WORKSPACE, {});
108
+
109
+ expect(result.totals.logCount).toBe(3);
110
+ expect(result.totals.conceptOccurrences).toBe(6);
111
+ expect(result.filters).toEqual({ conversationId: null, sinceMs: null });
112
+
113
+ const bySlug = new Map(result.concepts.map((c) => [c.slug, c]));
114
+
115
+ expect(bySlug.get("alice")!.counts).toEqual({
116
+ injected: 2,
117
+ in_context: 1,
118
+ not_injected: 0,
119
+ page_missing: 0,
120
+ });
121
+ expect(bySlug.get("alice")!.totalEvaluations).toBe(3);
122
+ expect(bySlug.get("alice")!.onDisk).toBe(true);
123
+ expect(bySlug.get("alice")!.lastInjectedAt).not.toBeNull();
124
+
125
+ expect(bySlug.get("bob")!.counts).toEqual({
126
+ injected: 1,
127
+ in_context: 0,
128
+ not_injected: 1,
129
+ page_missing: 0,
130
+ });
131
+ expect(bySlug.get("bob")!.totalEvaluations).toBe(2);
132
+ expect(bySlug.get("bob")!.onDisk).toBe(true);
133
+
134
+ expect(bySlug.get("charlie")!.counts).toEqual({
135
+ injected: 0,
136
+ in_context: 0,
137
+ not_injected: 0,
138
+ page_missing: 1,
139
+ });
140
+ expect(bySlug.get("charlie")!.onDisk).toBe(false);
141
+ expect(bySlug.get("charlie")!.lastInjectedAt).toBeNull();
142
+
143
+ // Sorted by totalEvaluations desc — alice (3) before bob (2) before charlie (1).
144
+ expect(result.concepts.map((c) => c.slug)).toEqual([
145
+ "alice",
146
+ "bob",
147
+ "charlie",
148
+ ]);
149
+
150
+ // delta is on disk but never appeared in any log row.
151
+ expect(result.neverEvaluatedSlugs).toEqual(["delta"]);
152
+ });
153
+
154
+ test("conversationId filter narrows aggregation", async () => {
155
+ recordMemoryV2ActivationLog({
156
+ conversationId: "conv-a",
157
+ turn: 1,
158
+ mode: "per-turn",
159
+ concepts: [makeConcept("alice", "injected")],
160
+ config: sampleConfig,
161
+ });
162
+ recordMemoryV2ActivationLog({
163
+ conversationId: "conv-b",
164
+ turn: 1,
165
+ mode: "per-turn",
166
+ concepts: [
167
+ makeConcept("alice", "injected"),
168
+ makeConcept("alice", "injected"),
169
+ ],
170
+ config: sampleConfig,
171
+ });
172
+
173
+ listPagesImpl = async () => ["alice"];
174
+
175
+ const all = await getConceptFrequencySummary(WORKSPACE, {});
176
+ expect(all.totals.logCount).toBe(2);
177
+ expect(all.concepts[0]!.counts.injected).toBe(3);
178
+ expect(all.filters.conversationId).toBeNull();
179
+
180
+ const onlyA = await getConceptFrequencySummary(WORKSPACE, {
181
+ conversationId: "conv-a",
182
+ });
183
+ expect(onlyA.totals.logCount).toBe(1);
184
+ expect(onlyA.concepts[0]!.counts.injected).toBe(1);
185
+ expect(onlyA.filters.conversationId).toBe("conv-a");
186
+
187
+ const onlyB = await getConceptFrequencySummary(WORKSPACE, {
188
+ conversationId: "conv-b",
189
+ });
190
+ expect(onlyB.totals.logCount).toBe(1);
191
+ expect(onlyB.concepts[0]!.counts.injected).toBe(2);
192
+ });
193
+
194
+ test("sinceMs filter excludes older log rows", async () => {
195
+ recordMemoryV2ActivationLog({
196
+ conversationId: "conv-1",
197
+ turn: 1,
198
+ mode: "per-turn",
199
+ concepts: [makeConcept("alice", "injected")],
200
+ config: sampleConfig,
201
+ });
202
+ // Backdate the just-written row — recordMemoryV2ActivationLog uses Date.now().
203
+ getDb().update(memoryV2ActivationLogs).set({ createdAt: 1_000 }).run();
204
+
205
+ recordMemoryV2ActivationLog({
206
+ conversationId: "conv-1",
207
+ turn: 2,
208
+ mode: "per-turn",
209
+ concepts: [makeConcept("alice", "injected")],
210
+ config: sampleConfig,
211
+ });
212
+
213
+ listPagesImpl = async () => ["alice"];
214
+
215
+ const all = await getConceptFrequencySummary(WORKSPACE, {});
216
+ expect(all.totals.logCount).toBe(2);
217
+ expect(all.concepts[0]!.counts.injected).toBe(2);
218
+
219
+ const recent = await getConceptFrequencySummary(WORKSPACE, {
220
+ sinceMs: 10_000,
221
+ });
222
+ expect(recent.totals.logCount).toBe(1);
223
+ expect(recent.concepts[0]!.counts.injected).toBe(1);
224
+ expect(recent.filters.sinceMs).toBe(10_000);
225
+ });
226
+
227
+ test("never-evaluated list excludes slugs that appeared in any status", async () => {
228
+ recordMemoryV2ActivationLog({
229
+ conversationId: "conv-1",
230
+ turn: 1,
231
+ mode: "per-turn",
232
+ concepts: [
233
+ makeConcept("alice", "injected"),
234
+ makeConcept("bob", "not_injected"),
235
+ makeConcept("charlie", "page_missing"),
236
+ ],
237
+ config: sampleConfig,
238
+ });
239
+
240
+ listPagesImpl = async () => ["alice", "bob", "delta", "echo"];
241
+
242
+ const result = await getConceptFrequencySummary(WORKSPACE, {});
243
+ // bob was scored but rejected — still excluded from neverEvaluated.
244
+ expect(result.neverEvaluatedSlugs).toEqual(["delta", "echo"]);
245
+ });
246
+
247
+ test("returns empty result when no logs exist", async () => {
248
+ listPagesImpl = async () => ["alice", "bob"];
249
+
250
+ const result = await getConceptFrequencySummary(WORKSPACE, {});
251
+ expect(result.totals).toEqual({ logCount: 0, conceptOccurrences: 0 });
252
+ expect(result.concepts).toEqual([]);
253
+ expect(result.neverEvaluatedSlugs).toEqual(["alice", "bob"]);
254
+ });
255
+
256
+ test("flags slugs that appear in logs but no longer have a page on disk", async () => {
257
+ recordMemoryV2ActivationLog({
258
+ conversationId: "conv-1",
259
+ turn: 1,
260
+ mode: "per-turn",
261
+ concepts: [makeConcept("ghost", "injected")],
262
+ config: sampleConfig,
263
+ });
264
+
265
+ listPagesImpl = async () => ["alice"];
266
+
267
+ const result = await getConceptFrequencySummary(WORKSPACE, {});
268
+ const ghost = result.concepts.find((c) => c.slug === "ghost")!;
269
+ expect(ghost.onDisk).toBe(false);
270
+ expect(ghost.counts.injected).toBe(1);
271
+ });
272
+ });
@@ -6,7 +6,6 @@ import { getWorkspaceDir } from "../util/platform.js";
6
6
  import { deleteMemoryCheckpoint } from "./checkpoints.js";
7
7
  import { runDeterministicRecallSearch } from "./context-search/search.js";
8
8
  import type { RecallEvidence } from "./context-search/types.js";
9
- import { getConversationMemoryScopeId } from "./conversation-crud.js";
10
9
  import { getDb } from "./db-connection.js";
11
10
  import { getMemoryBackendStatus } from "./embedding-backend.js";
12
11
  import {
@@ -32,7 +31,7 @@ import {
32
31
 
33
32
  const log = getLogger("memory-admin");
34
33
 
35
- export interface MemorySystemStatus {
34
+ interface MemorySystemStatus {
36
35
  enabled: boolean;
37
36
  degraded: boolean;
38
37
  reason: string | null;
@@ -47,7 +46,7 @@ export interface MemorySystemStatus {
47
46
  jobs: Record<string, number>;
48
47
  }
49
48
 
50
- export interface AdminMemoryQueryResult {
49
+ interface AdminMemoryQueryResult {
51
50
  results: Array<{
52
51
  id: string;
53
52
  content: string;
@@ -106,7 +105,6 @@ export async function queryMemory(
106
105
  { query, sources: ["memory", "conversations", "pkb"] },
107
106
  {
108
107
  workingDir: getWorkspaceDir(),
109
- memoryScopeId: getConversationMemoryScopeId(conversationId) ?? "default",
110
108
  conversationId,
111
109
  config,
112
110
  },
@@ -148,7 +146,7 @@ function readNumericMetadata(
148
146
 
149
147
  // ── Short segment cleanup ─────────────────────────────────────────────
150
148
 
151
- export interface CleanupShortSegmentsResult {
149
+ interface CleanupShortSegmentsResult {
152
150
  removed: number;
153
151
  failed: number;
154
152
  dryRunCount?: number;
@@ -216,7 +214,7 @@ export async function compactLongMemoryNodes(
216
214
 
217
215
  // ── Re-extraction ──────────────────────────────────────────────────────
218
216
 
219
- export interface ReextractTarget {
217
+ interface ReextractTarget {
220
218
  conversationId: string;
221
219
  title: string | null;
222
220
  messageCount: number;
@@ -312,11 +310,9 @@ export function requestReextract(targets: ReextractTarget[]): {
312
310
  )
313
311
  .run();
314
312
 
315
- // Resolve scope and enqueue re-extraction
316
- const scopeId = getConversationMemoryScopeId(conversationId);
317
313
  const jobId = enqueueMemoryJob("graph_extract", {
318
314
  conversationId,
319
- scopeId,
315
+ scopeId: "default",
320
316
  });
321
317
  jobIds.push(jobId);
322
318
 
@@ -306,7 +306,17 @@ export async function runAgenticRecall(
306
306
  [...RECALL_AGENT_TOOL_DEFINITIONS],
307
307
  undefined,
308
308
  {
309
- config: { callSite: "recall", temperature: 0 },
309
+ // `thinking: disabled` is required because we set `temperature: 0`
310
+ // explicitly. Anthropic 400s on `temperature` ≠ 1 whenever thinking
311
+ // is enabled or in adaptive mode; without this, profiles that
312
+ // resolve thinking-enabled (Opus 4.x at `effort: high|xhigh`, etc.)
313
+ // would fail. Recall is tool-call-heavy reasoning where determinism
314
+ // matters more than extended chain-of-thought.
315
+ config: {
316
+ callSite: "recall",
317
+ temperature: 0,
318
+ thinking: { type: "disabled" },
319
+ },
310
320
  signal: context.signal,
311
321
  },
312
322
  );
@@ -597,7 +607,14 @@ async function tryFinalFinishRecall(options: {
597
607
  [FINISH_RECALL_TOOL_DEFINITION],
598
608
  undefined,
599
609
  {
600
- config: { callSite: "recall", temperature: 0 },
610
+ // `thinking: disabled` required for the same reason as the agent
611
+ // round above — Anthropic 400s on `temperature` ≠ 1 whenever
612
+ // thinking is enabled or in adaptive mode.
613
+ config: {
614
+ callSite: "recall",
615
+ temperature: 0,
616
+ thinking: { type: "disabled" },
617
+ },
601
618
  signal: options.context.signal,
602
619
  },
603
620
  );
@@ -88,10 +88,7 @@ export async function searchConversationSource(
88
88
 
89
89
  for (const ftsMatch of ftsMatches) {
90
90
  try {
91
- rows = mergeConversationRows(
92
- rows,
93
- searchWithFts(ftsMatch, context.memoryScopeId, queryLimit),
94
- );
91
+ rows = mergeConversationRows(rows, searchWithFts(ftsMatch, queryLimit));
95
92
  } catch {
96
93
  // Try the next, broader query shape.
97
94
  }
@@ -100,7 +97,7 @@ export async function searchConversationSource(
100
97
  }
101
98
 
102
99
  if (rows.length === 0) {
103
- rows = searchWithLike(trimmedQuery, context.memoryScopeId, queryLimit);
100
+ rows = searchWithLike(trimmedQuery, queryLimit);
104
101
  }
105
102
 
106
103
  const sortedRows = rows
@@ -130,7 +127,6 @@ export async function searchConversationSource(
130
127
 
131
128
  function searchWithFts(
132
129
  ftsMatch: string,
133
- memoryScopeId: string,
134
130
  limit: number,
135
131
  ): ConversationEvidenceRow[] {
136
132
  return rawAll<ConversationEvidenceRow>(
@@ -146,14 +142,12 @@ function searchWithFts(
146
142
  JOIN messages m ON m.id = messages_fts.message_id
147
143
  JOIN conversations c ON c.id = m.conversation_id
148
144
  WHERE messages_fts MATCH ?
149
- AND c.memory_scope_id = ?
150
145
  AND (c.conversation_type IS NULL OR c.conversation_type != 'private')
151
146
  AND (c.source IS NULL OR c.source NOT IN (?, ?))
152
147
  ORDER BY bm25(messages_fts), m.created_at DESC
153
148
  LIMIT ?
154
149
  `,
155
150
  ftsMatch,
156
- memoryScopeId,
157
151
  SUBAGENT_SOURCE,
158
152
  AUTO_ANALYSIS_SOURCE,
159
153
  limit,
@@ -162,7 +156,6 @@ function searchWithFts(
162
156
 
163
157
  function searchWithLike(
164
158
  query: string,
165
- memoryScopeId: string,
166
159
  limit: number,
167
160
  ): ConversationEvidenceRow[] {
168
161
  return rawAll<ConversationEvidenceRow>(
@@ -177,14 +170,12 @@ function searchWithLike(
177
170
  FROM messages m
178
171
  JOIN conversations c ON c.id = m.conversation_id
179
172
  WHERE m.content LIKE ? ESCAPE '\\'
180
- AND c.memory_scope_id = ?
181
173
  AND (c.conversation_type IS NULL OR c.conversation_type != 'private')
182
174
  AND (c.source IS NULL OR c.source NOT IN (?, ?))
183
175
  ORDER BY m.created_at DESC
184
176
  LIMIT ?
185
177
  `,
186
178
  buildLikePattern(query),
187
- memoryScopeId,
188
179
  SUBAGENT_SOURCE,
189
180
  AUTO_ANALYSIS_SOURCE,
190
181
  limit,
@@ -2,7 +2,7 @@
2
2
  // Memory v2 — `recall` adapter for the `memory` source
3
3
  // ---------------------------------------------------------------------------
4
4
  //
5
- // When the v2 flag is on, the `memory` recall source reads from the v2
5
+ // When v2 is enabled, the `memory` recall source reads from the v2
6
6
  // concept-page subsystem (under `<workspace>/memory/concepts/`) instead of
7
7
  // the legacy graph. Two retrieval paths run in parallel and merge:
8
8
  //
@@ -26,8 +26,6 @@
26
26
  import { readdir, readFile, realpath, stat } from "node:fs/promises";
27
27
  import { extname, isAbsolute, join, relative } from "node:path";
28
28
 
29
- import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
30
- import type { AssistantConfig } from "../../../config/schema.js";
31
29
  import { getLogger } from "../../../util/logger.js";
32
30
  import { embedWithRetry } from "../../embed.js";
33
31
  import { generateSparseEmbedding } from "../../embedding-backend.js";
@@ -46,19 +44,6 @@ import type {
46
44
  RecallSearchResult,
47
45
  } from "../types.js";
48
46
 
49
- /**
50
- * True when both v2 gates are on. Single source of truth shared by the recall
51
- * `memory` source (which delegates to v2), the `pkb` source (which
52
- * short-circuits because v2 absorbs PKB as a read source), and the per-turn
53
- * PKB injectors (which go silent so v2 owns the read path end-to-end).
54
- */
55
- export function isMemoryV2ReadActive(config: AssistantConfig): boolean {
56
- return (
57
- isAssistantFeatureFlagEnabled("memory-v2-enabled", config) &&
58
- config.memory.v2.enabled
59
- );
60
- }
61
-
62
47
  const log = getLogger("context-search-memory-v2-source");
63
48
 
64
49
  /**