@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e

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 (456) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +8 -0
  3. package/docs/activation-funnel-telemetry.md +13 -7
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  12. package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  13. package/openapi.yaml +530 -107
  14. package/package.json +1 -1
  15. package/scripts/generate-openapi.ts +8 -0
  16. package/src/__tests__/activation-early-marking.test.ts +6 -5
  17. package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
  18. package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
  19. package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
  20. package/src/__tests__/app-bundler.test.ts +4 -12
  21. package/src/__tests__/app-executors.test.ts +2 -49
  22. package/src/__tests__/app-routes-csp.test.ts +114 -146
  23. package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
  24. package/src/__tests__/build-persisted-content.test.ts +96 -0
  25. package/src/__tests__/bundle-scanner.test.ts +27 -1
  26. package/src/__tests__/call-controller.test.ts +291 -0
  27. package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
  28. package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
  29. package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
  30. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  31. package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
  32. package/src/__tests__/config-loader-backfill.test.ts +9 -4
  33. package/src/__tests__/config-schema-cmd.test.ts +10 -11
  34. package/src/__tests__/config-schema.test.ts +190 -257
  35. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
  37. package/src/__tests__/conversation-agent-loop.test.ts +22 -9
  38. package/src/__tests__/conversation-error.test.ts +31 -0
  39. package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
  40. package/src/__tests__/conversation-process-callsite.test.ts +12 -19
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
  42. package/src/__tests__/conversation-summarize-route.test.ts +36 -44
  43. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  44. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
  45. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
  46. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
  47. package/src/__tests__/conversation-usage.test.ts +4 -12
  48. package/src/__tests__/credential-routes.test.ts +250 -0
  49. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  50. package/src/__tests__/db-migration-rollback.test.ts +22 -0
  51. package/src/__tests__/empty-response-hook.test.ts +186 -1
  52. package/src/__tests__/external-plugin-loader.test.ts +11 -5
  53. package/src/__tests__/heartbeat-service.test.ts +0 -28
  54. package/src/__tests__/host-shell-tool.test.ts +2 -0
  55. package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
  56. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
  57. package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
  58. package/src/__tests__/invite-routes-http.test.ts +12 -16
  59. package/src/__tests__/list-all-apps.test.ts +0 -4
  60. package/src/__tests__/llm-context-resolution.test.ts +32 -56
  61. package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
  62. package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
  63. package/src/__tests__/llm-resolver.test.ts +342 -602
  64. package/src/__tests__/llm-schema.test.ts +79 -37
  65. package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
  66. package/src/__tests__/media-stream-output.test.ts +259 -3
  67. package/src/__tests__/media-stream-server-integration.test.ts +22 -1
  68. package/src/__tests__/media-stream-stt-session.test.ts +47 -0
  69. package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
  70. package/src/__tests__/memory-recall-log-store.test.ts +47 -13
  71. package/src/__tests__/mock-gateway-ipc.ts +46 -1
  72. package/src/__tests__/mtime-cache.test.ts +61 -0
  73. package/src/__tests__/navigate-settings-tab.test.ts +2 -0
  74. package/src/__tests__/normalize-onboarding.test.ts +33 -0
  75. package/src/__tests__/onboarding-persona-write.test.ts +26 -0
  76. package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
  77. package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
  78. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
  79. package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
  80. package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
  81. package/src/__tests__/provider-connections-backfill.test.ts +138 -0
  82. package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
  83. package/src/__tests__/provider-registry-ollama.test.ts +8 -18
  84. package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
  85. package/src/__tests__/provider-usage-tracking.test.ts +9 -19
  86. package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
  87. package/src/__tests__/published-app-updater.test.ts +22 -16
  88. package/src/__tests__/registry.test.ts +5 -16
  89. package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
  90. package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
  91. package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
  92. package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
  93. package/src/__tests__/reveal-success-registry.test.ts +123 -0
  94. package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
  95. package/src/__tests__/secret-fixtures.ts +9 -0
  96. package/src/__tests__/server-history-render.test.ts +28 -0
  97. package/src/__tests__/skills.test.ts +9 -4
  98. package/src/__tests__/slack-share-routes.test.ts +0 -1
  99. package/src/__tests__/stt-stream-session.test.ts +6 -5
  100. package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
  101. package/src/__tests__/subagent-disposal.test.ts +2 -0
  102. package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
  103. package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
  104. package/src/__tests__/subagent-manager-notify.test.ts +2 -0
  105. package/src/__tests__/subagent-role-registry.test.ts +37 -0
  106. package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
  107. package/src/__tests__/subagent-terminal-message.test.ts +50 -0
  108. package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
  109. package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
  110. package/src/__tests__/telemetry-routes.test.ts +99 -19
  111. package/src/__tests__/tool-audit.test.ts +34 -4
  112. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  113. package/src/__tests__/tool-profiler.test.ts +72 -1
  114. package/src/__tests__/tool-result-spool.test.ts +49 -4
  115. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
  116. package/src/__tests__/ui-channel-variants.test.ts +108 -0
  117. package/src/__tests__/ui-shape-teaching.test.ts +255 -0
  118. package/src/__tests__/usage-attribution.test.ts +18 -41
  119. package/src/__tests__/user-plugin-loader.test.ts +4 -4
  120. package/src/__tests__/voice-config-update.test.ts +46 -0
  121. package/src/__tests__/voice-session-bridge.test.ts +216 -84
  122. package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
  123. package/src/agent/loop.ts +4 -3
  124. package/src/api/events/open-conversation.test.ts +64 -0
  125. package/src/api/events/open-conversation.ts +33 -0
  126. package/src/api/index.ts +6 -0
  127. package/src/api/responses/conversation-message.ts +5 -0
  128. package/src/apps/app-store.ts +25 -35
  129. package/src/bundler/app-bundler.ts +34 -48
  130. package/src/bundler/app-compiler.ts +39 -4
  131. package/src/bundler/bundle-scanner.ts +13 -0
  132. package/src/bundler/manifest.ts +1 -1
  133. package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
  134. package/src/calls/call-constants.ts +5 -0
  135. package/src/calls/call-controller.ts +100 -32
  136. package/src/calls/call-transport.ts +9 -0
  137. package/src/calls/media-stream-output.ts +107 -4
  138. package/src/calls/media-stream-server.ts +29 -9
  139. package/src/calls/media-stream-stt-session.ts +7 -1
  140. package/src/calls/media-turn-detector.ts +11 -1
  141. package/src/calls/voice-session-bridge.ts +84 -63
  142. package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
  143. package/src/cli/commands/__tests__/notifications.test.ts +24 -3
  144. package/src/cli/commands/config.help.ts +6 -6
  145. package/src/cli/commands/credentials.help.ts +13 -0
  146. package/src/cli/commands/credentials.ts +6 -1
  147. package/src/cli/commands/email.help.ts +7 -0
  148. package/src/cli/commands/email.ts +35 -1
  149. package/src/cli/commands/inference-providers.ts +168 -107
  150. package/src/cli/commands/inference.help.ts +118 -46
  151. package/src/cli/commands/memory/index.help.ts +23 -0
  152. package/src/cli/commands/memory/nodes.ts +146 -0
  153. package/src/cli/commands/notifications.help.ts +10 -10
  154. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
  155. package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
  156. package/src/cli/commands/oauth/connect.test.ts +126 -0
  157. package/src/cli/commands/oauth/connect.ts +29 -6
  158. package/src/cli/commands/oauth/index.help.ts +7 -1
  159. package/src/cli/commands/oauth/status.test.ts +69 -3
  160. package/src/cli/commands/oauth/status.ts +50 -12
  161. package/src/cli/commands/plugins.help.ts +13 -2
  162. package/src/cli/commands/plugins.ts +61 -7
  163. package/src/cli/commands/telemetry.help.ts +13 -0
  164. package/src/cli/commands/telemetry.ts +45 -5
  165. package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
  166. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
  167. package/src/cli/lib/bundled-marketplace.json +93 -36
  168. package/src/cli/lib/inspect-plugin.ts +5 -1
  169. package/src/cli/lib/install-from-github.ts +68 -4
  170. package/src/cli/lib/plugin-catalog-local.ts +6 -2
  171. package/src/cli/lib/plugin-fingerprint.ts +3 -3
  172. package/src/cli/lib/upgrade-plugin.ts +236 -35
  173. package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
  174. package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
  175. package/src/config/__tests__/profile-materialization.test.ts +1 -88
  176. package/src/config/bundled-skills/AGENTS.md +3 -30
  177. package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
  178. package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
  180. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  181. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
  182. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  183. package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
  184. package/src/config/bundled-skills/settings/TOOLS.json +3 -1
  185. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
  186. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
  187. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  188. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  189. package/src/config/call-site-defaults.ts +3 -3
  190. package/src/config/feature-flag-registry.json +24 -39
  191. package/src/config/llm-resolver.ts +88 -475
  192. package/src/config/profile-materialization.ts +11 -11
  193. package/src/config/schema.ts +93 -127
  194. package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
  195. package/src/config/schemas/__tests__/stt.test.ts +31 -3
  196. package/src/config/schemas/live-voice.ts +10 -4
  197. package/src/config/schemas/llm.ts +52 -15
  198. package/src/config/schemas/memory-lifecycle.ts +1 -1
  199. package/src/config/schemas/memory-retrospective.ts +8 -0
  200. package/src/config/schemas/memory-v2.ts +2 -2
  201. package/src/config/schemas/memory-v3.ts +1 -1
  202. package/src/config/schemas/services.ts +6 -3
  203. package/src/config/schemas/stt.ts +38 -21
  204. package/src/config/schemas/tts.ts +13 -18
  205. package/src/config/skills.ts +31 -21
  206. package/src/context/compactor.ts +44 -7
  207. package/src/context/post-turn-tool-result-truncation.ts +4 -2
  208. package/src/context/tool-result-spool.ts +26 -5
  209. package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
  210. package/src/conversations/message-consolidation.ts +22 -2
  211. package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
  212. package/src/daemon/app-source-watcher.ts +17 -23
  213. package/src/daemon/chat-credential-redaction.ts +1365 -0
  214. package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
  215. package/src/daemon/conversation-agent-loop.ts +71 -31
  216. package/src/daemon/conversation-error.ts +36 -14
  217. package/src/daemon/conversation-process.ts +22 -0
  218. package/src/daemon/conversation-store.ts +35 -0
  219. package/src/daemon/conversation-surfaces.ts +25 -25
  220. package/src/daemon/conversation-tool-setup.ts +33 -7
  221. package/src/daemon/conversation.ts +55 -5
  222. package/src/daemon/handlers/shared.ts +33 -2
  223. package/src/daemon/lifecycle.ts +4 -4
  224. package/src/daemon/message-types/conversations.ts +6 -17
  225. package/src/daemon/providers-setup.ts +8 -0
  226. package/src/daemon/tool-setup-types.ts +7 -1
  227. package/src/daemon/wake-conversation-ops.ts +10 -1
  228. package/src/hooks/types.ts +9 -0
  229. package/src/ipc/__tests__/email-ipc.test.ts +90 -0
  230. package/src/ipc/gateway-client.test.ts +59 -0
  231. package/src/ipc/gateway-client.ts +70 -27
  232. package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
  233. package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
  234. package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
  235. package/src/live-voice/__tests__/protocol.test.ts +122 -0
  236. package/src/live-voice/live-voice-session.ts +443 -40
  237. package/src/live-voice/protocol.ts +143 -1
  238. package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
  239. package/src/monitoring/plugin-source-watch.ts +3 -62
  240. package/src/notifications/README.md +1 -1
  241. package/src/permissions/checker.ts +8 -4
  242. package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
  243. package/src/persistence/conversation-crud.ts +104 -2
  244. package/src/persistence/db-init.ts +14 -3
  245. package/src/persistence/job-handlers/cleanup.ts +15 -7
  246. package/src/persistence/llm-request-log-store.ts +88 -52
  247. package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
  248. package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
  249. package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
  250. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
  251. package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
  252. package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
  253. package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
  254. package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
  255. package/src/persistence/migrations/helpers/relocation.ts +44 -1
  256. package/src/persistence/migrations/run-migrations.ts +25 -1
  257. package/src/persistence/schema/infrastructure.ts +9 -0
  258. package/src/persistence/schema/memory-core.ts +3 -0
  259. package/src/persistence/schema/memory-injection.ts +2 -0
  260. package/src/persistence/steps.ts +36 -0
  261. package/src/platform/client.test.ts +1 -44
  262. package/src/platform/client.ts +10 -20
  263. package/src/platform/consent-cache.test.ts +89 -23
  264. package/src/platform/consent-cache.ts +71 -33
  265. package/src/plugin-api/constants.ts +12 -0
  266. package/src/plugin-api/conversation-turn.ts +37 -14
  267. package/src/plugin-api/index.ts +11 -1
  268. package/src/plugin-api/resolve-credential.ts +75 -0
  269. package/src/plugin-api/vision-support.test.ts +8 -19
  270. package/src/plugin-api/vision-support.ts +26 -17
  271. package/src/plugins/collect-source-versions.ts +77 -0
  272. package/src/plugins/defaults/compaction/compact.ts +6 -0
  273. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
  275. package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
  276. package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
  277. package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
  278. package/src/plugins/defaults/index.ts +8 -1
  279. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
  280. package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
  281. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
  282. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
  283. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
  284. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
  285. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
  286. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
  287. package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
  288. package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
  289. package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
  290. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
  291. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
  292. package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
  293. package/src/plugins/defaults/memory/graph/store.ts +114 -0
  294. package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
  295. package/src/plugins/defaults/memory/graph/tools.ts +47 -13
  296. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
  297. package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
  298. package/src/plugins/defaults/memory/memory-db.ts +3 -2
  299. package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
  300. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
  301. package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
  302. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
  303. package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
  304. package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
  305. package/src/plugins/defaults/memory/path-containment.ts +21 -0
  306. package/src/plugins/defaults/memory/prompt-override.ts +51 -13
  307. package/src/plugins/defaults/memory/tools.test.ts +34 -0
  308. package/src/plugins/defaults/memory/tools.ts +12 -2
  309. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
  310. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
  311. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
  312. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
  313. package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
  314. package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
  315. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
  316. package/src/plugins/defaults/memory/v2/migration.ts +46 -16
  317. package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
  318. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
  319. package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
  320. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
  321. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
  322. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
  323. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
  324. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
  325. package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
  326. package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
  327. package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
  328. package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
  329. package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
  330. package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
  331. package/src/plugins/defaults/memory/v3/prune.ts +17 -11
  332. package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
  333. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
  334. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
  335. package/src/plugins/external-plugin-loader.ts +15 -15
  336. package/src/plugins/mtime-cache.ts +47 -0
  337. package/src/plugins/pipeline.ts +9 -1
  338. package/src/plugins/plugin-execution-context.ts +44 -0
  339. package/src/plugins/plugin-tree-walk.ts +31 -24
  340. package/src/plugins/source-fingerprint.ts +3 -4
  341. package/src/prompts/normalize-onboarding.ts +12 -0
  342. package/src/prompts/persona-resolver.ts +8 -0
  343. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
  344. package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
  345. package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
  346. package/src/providers/__tests__/retry-callsite.test.ts +235 -215
  347. package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
  348. package/src/providers/atlascloud/client.ts +10 -49
  349. package/src/providers/baseten/client.ts +43 -0
  350. package/src/providers/call-site-routing.ts +26 -13
  351. package/src/providers/connection-resolution.ts +9 -11
  352. package/src/providers/fetch-provider-catalog.ts +4 -2
  353. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
  354. package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
  355. package/src/providers/inference/adapter-factory.ts +27 -6
  356. package/src/providers/inference/auth.ts +29 -1
  357. package/src/providers/inference/backfill.ts +61 -10
  358. package/src/providers/inference/connection-availability.ts +3 -2
  359. package/src/providers/inference/resolve-auth.ts +9 -1
  360. package/src/providers/model-catalog.ts +37 -0
  361. package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
  362. package/src/providers/openai/api-key-validation.ts +70 -0
  363. package/src/providers/retry.ts +2 -0
  364. package/src/providers/types.ts +11 -4
  365. package/src/providers/vellum-model-routing.test.ts +26 -0
  366. package/src/providers/vellum-model-routing.ts +30 -0
  367. package/src/providers/voice-error-copy.ts +47 -0
  368. package/src/runtime/agent-wake.ts +40 -24
  369. package/src/runtime/for-chat-mint-registry.ts +118 -0
  370. package/src/runtime/reveal-nonce.ts +49 -0
  371. package/src/runtime/reveal-success-registry.ts +306 -0
  372. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
  373. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
  374. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
  375. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
  376. package/src/runtime/routes/app-management-routes.ts +152 -45
  377. package/src/runtime/routes/app-routes.ts +16 -77
  378. package/src/runtime/routes/canned-message-complete.ts +16 -16
  379. package/src/runtime/routes/conversation-management-routes.ts +17 -14
  380. package/src/runtime/routes/conversation-query-routes.ts +54 -6
  381. package/src/runtime/routes/conversation-routes.ts +48 -2
  382. package/src/runtime/routes/credential-routes.ts +116 -3
  383. package/src/runtime/routes/email-routes.ts +17 -1
  384. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
  385. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
  386. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
  387. package/src/runtime/routes/inference-send-routes.ts +1 -1
  388. package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
  389. package/src/runtime/routes/plugins-routes.ts +37 -6
  390. package/src/runtime/routes/publish-routes.ts +15 -18
  391. package/src/runtime/routes/schedule-worker-routes.ts +9 -0
  392. package/src/runtime/routes/secret-routes.ts +10 -0
  393. package/src/runtime/routes/telemetry-routes.ts +149 -45
  394. package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
  395. package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
  396. package/src/schedule/schedule-store.ts +12 -1
  397. package/src/schedule/schedule-timezone.ts +63 -0
  398. package/src/schedule/scheduler.ts +100 -6
  399. package/src/schedule/worker-control.ts +19 -0
  400. package/src/security/auth-fallback-events-store.ts +7 -6
  401. package/src/security/secret-scanner.ts +26 -1
  402. package/src/services/published-app-updater.ts +6 -11
  403. package/src/stt/stt-stream-session.ts +36 -16
  404. package/src/subagent/manager.ts +34 -8
  405. package/src/telemetry/AGENTS.md +30 -1
  406. package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
  407. package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
  408. package/src/telemetry/config-setting-snapshot.ts +44 -10
  409. package/src/telemetry/telemetry-event-sources.test.ts +124 -30
  410. package/src/telemetry/telemetry-event-sources.ts +137 -83
  411. package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
  412. package/src/telemetry/telemetry-events-outbox.ts +60 -9
  413. package/src/telemetry/telemetry-wire-source.json +1 -1
  414. package/src/telemetry/telemetry-wire-validation.ts +39 -2
  415. package/src/telemetry/telemetry-wire.generated.ts +8 -0
  416. package/src/telemetry/tool-audit.ts +15 -9
  417. package/src/telemetry/tool-executed-events-store.test.ts +1 -1
  418. package/src/telemetry/turn-events-store.ts +20 -0
  419. package/src/telemetry/types.ts +45 -12
  420. package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
  421. package/src/telemetry/usage-telemetry-reporter.ts +117 -18
  422. package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
  423. package/src/telemetry/watchdog-direct-emit.ts +11 -6
  424. package/src/tools/apps/executors.ts +11 -36
  425. package/src/tools/executor.ts +14 -1
  426. package/src/tools/host-terminal/host-shell.ts +6 -2
  427. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
  428. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
  429. package/src/tools/network/web-fetch.ts +6 -2
  430. package/src/tools/skills/sandbox-runner.ts +5 -2
  431. package/src/tools/subagent/spawn.ts +7 -11
  432. package/src/tools/terminal/shell.ts +5 -2
  433. package/src/tools/tool-manifest.ts +0 -2
  434. package/src/tools/tool-profiler.ts +37 -6
  435. package/src/tools/ui-surface/channel-variants.ts +101 -0
  436. package/src/tools/ui-surface/definitions.ts +23 -67
  437. package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
  438. package/src/tts/__tests__/provider-adapters.test.ts +18 -0
  439. package/src/tts/provider-catalog.ts +2 -4
  440. package/src/tts/providers/deepgram-provider.ts +10 -1
  441. package/src/types/onboarding-context.ts +8 -0
  442. package/src/usage/attribution.ts +18 -112
  443. package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
  444. package/src/util/provider-error-patterns.ts +7 -1
  445. package/src/util/worker-process.ts +105 -0
  446. package/src/watcher/__tests__/telemetry.test.ts +17 -4
  447. package/src/watcher/telemetry.ts +5 -5
  448. package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
  449. package/src/workspace/migrations/registry.ts +2 -0
  450. package/src/workspace/provider-commit-message-generator.ts +6 -4
  451. package/src/__tests__/app-open-proxy.test.ts +0 -67
  452. package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
  453. package/src/onboarding/onboarding-research-events-store.ts +0 -104
  454. package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
  455. package/src/tools/apps/definitions.ts +0 -73
  456. package/src/tools/apps/open-proxy.ts +0 -43
@@ -1,4 +1,7 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { Database } from "bun:sqlite";
2
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
3
+
4
+ import { drizzle } from "drizzle-orm/bun-sqlite";
2
5
 
3
6
  let listPagesImpl: (workspaceDir: string) => Promise<string[]> = async () => [];
4
7
 
@@ -6,9 +9,12 @@ mock.module("../v2/page-store.js", () => ({
6
9
  listPages: (workspaceDir: string) => listPagesImpl(workspaceDir),
7
10
  }));
8
11
 
9
- import { getDb } from "../../../../persistence/db-connection.js";
10
- import { initializeDb } from "../../../../persistence/db-init.js";
11
- import { memoryV2ActivationLogs } from "../../../../persistence/schema/index.js";
12
+ import {
13
+ clearStoredDb,
14
+ setStoredDb,
15
+ } from "../../../../persistence/db-singleton.js";
16
+ import { ensureActivationLogsSchema } from "../../../../persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.js";
17
+ import * as schema from "../../../../persistence/schema/index.js";
12
18
  import {
13
19
  type MemoryV2ConceptRowRecord,
14
20
  recordMemoryV2ActivationLog,
@@ -16,7 +22,10 @@ import {
16
22
  import { getConceptFrequencySummary } from "../memory-v2-concept-frequency.js";
17
23
  import { sampleConfig } from "./fixtures/memory-v2-activation-fixtures.js";
18
24
 
19
- await initializeDb();
25
+ // The store and aggregator resolve the dedicated memory connection through
26
+ // the `memory` singleton slot — install a fresh in-memory DB there with the
27
+ // relocated table's schema.
28
+ let memorySqlite: Database;
20
29
 
21
30
  const WORKSPACE = "/tmp/memory-v2-concept-frequency-test";
22
31
 
@@ -41,16 +50,20 @@ function makeConcept(
41
50
  };
42
51
  }
43
52
 
44
- function resetTables(): void {
45
- getDb().delete(memoryV2ActivationLogs).run();
46
- }
47
-
48
53
  describe("memory-v2-concept-frequency", () => {
49
54
  beforeEach(() => {
50
- resetTables();
55
+ memorySqlite = new Database(":memory:");
56
+ ensureActivationLogsSchema(memorySqlite);
57
+ setStoredDb("memory", drizzle(memorySqlite, { schema }), () =>
58
+ memorySqlite.close(),
59
+ );
51
60
  listPagesImpl = async () => [];
52
61
  });
53
62
 
63
+ afterEach(() => {
64
+ clearStoredDb("memory");
65
+ });
66
+
54
67
  test("aggregates per-status counts across multiple turns", async () => {
55
68
  const conv = "conv-1";
56
69
  recordMemoryV2ActivationLog({
@@ -185,7 +198,7 @@ describe("memory-v2-concept-frequency", () => {
185
198
  config: sampleConfig,
186
199
  });
187
200
  // Backdate the just-written row — recordMemoryV2ActivationLog uses Date.now().
188
- getDb().update(memoryV2ActivationLogs).set({ createdAt: 1_000 }).run();
201
+ memorySqlite.exec(`UPDATE memory_v2_activation_logs SET created_at = 1000`);
189
202
 
190
203
  recordMemoryV2ActivationLog({
191
204
  conversationId: "conv-1",
@@ -3,13 +3,13 @@
3
3
  * (`assistant/src/memory/prompt-override.ts`) and the memory-v3
4
4
  * `resolveSelectorPrompt` built on it. Mirrors the v2 router/consolidation
5
5
  * prompt-path suites: a configured file replaces the bundled prompt, and any
6
- * missing / empty / oversized / non-regular / unreadable file degrades to the
7
- * bundled prompt with a diagnostic warning.
6
+ * out-of-workspace / missing / empty / oversized / non-regular / unreadable
7
+ * file degrades to the bundled prompt with a diagnostic warning.
8
8
  */
9
9
  import { execFileSync } from "node:child_process";
10
10
  import { mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
11
11
  import { homedir, tmpdir } from "node:os";
12
- import { join } from "node:path";
12
+ import { basename, join } from "node:path";
13
13
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
14
14
 
15
15
  import {
@@ -50,6 +50,7 @@ const load = (
50
50
  });
51
51
 
52
52
  describe("resolveOverridePath", () => {
53
+ // Pure resolution — workspace containment is enforced by loadPromptOverride.
53
54
  test("expands a leading ~/ to the home directory", () => {
54
55
  expect(resolveOverridePath("~/sub/x.md", tmpDir)).toBe(
55
56
  join(homedir(), "sub/x.md"),
@@ -91,17 +92,80 @@ describe("loadPromptOverride — usable override", () => {
91
92
  expect(warnCalls).toHaveLength(0);
92
93
  });
93
94
 
94
- test("expands a leading ~/ to the home directory", () => {
95
+ test("honors a ~/ path that resolves inside the workspace", () => {
96
+ let wsUnderHome: string;
97
+ try {
98
+ wsUnderHome = mkdtempSync(join(homedir(), ".vellum-prompt-override-ws-"));
99
+ } catch {
100
+ // Home directory not writable on this platform — skip without failing.
101
+ return;
102
+ }
103
+ try {
104
+ writeFileSync(join(wsUnderHome, "x.md"), "home workspace body\n");
105
+ const out = loadPromptOverride({
106
+ overridePath: `~/${basename(wsUnderHome)}/x.md`,
107
+ workspaceDir: wsUnderHome,
108
+ log: recordingLogger,
109
+ label: "test prompt",
110
+ });
111
+ expect(out).toBe("home workspace body\n");
112
+ expect(warnCalls).toHaveLength(0);
113
+ } finally {
114
+ rmSync(wsUnderHome, { recursive: true, force: true });
115
+ }
116
+ });
117
+ });
118
+
119
+ describe("loadPromptOverride — workspace containment", () => {
120
+ test("an absolute path outside the workspace is rejected", () => {
121
+ const outside = mkdtempSync(join(tmpdir(), "prompt-override-outside-"));
122
+ try {
123
+ const p = join(outside, "secret.md");
124
+ writeFileSync(p, "SENSITIVE CONTENTS\n");
125
+ expect(load(p)).toBeNull();
126
+ expect(warnCalls).toHaveLength(1);
127
+ expect(warnCalls[0].data.reason).toBe("outside_workspace");
128
+ expect(warnCalls[0].data.fallback).toBe("bundled");
129
+ } finally {
130
+ rmSync(outside, { recursive: true, force: true });
131
+ }
132
+ });
133
+
134
+ test("a relative path escaping the workspace via .. is rejected", () => {
135
+ const name = `prompt-override-escape-${process.pid}.md`;
136
+ const target = join(tmpDir, "..", name);
137
+ writeFileSync(target, "escaped\n");
138
+ try {
139
+ expect(load(join("..", name))).toBeNull();
140
+ expect(warnCalls[0].data.reason).toBe("outside_workspace");
141
+ } finally {
142
+ rmSync(target, { force: true });
143
+ }
144
+ });
145
+
146
+ test("a ~/ path outside the workspace is rejected", () => {
95
147
  const filename = `.vellum-prompt-override-test-${process.pid}.md`;
96
148
  const p = join(homedir(), filename);
97
149
  writeFileSync(p, "home body\n");
98
150
  try {
99
- expect(load(`~/${filename}`)).toBe("home body\n");
100
- expect(warnCalls).toHaveLength(0);
151
+ expect(load(`~/${filename}`)).toBeNull();
152
+ expect(warnCalls[0].data.reason).toBe("outside_workspace");
101
153
  } finally {
102
154
  rmSync(p, { force: true });
103
155
  }
104
156
  });
157
+
158
+ test("a symlinked directory pointing outside the workspace is rejected", () => {
159
+ const outside = mkdtempSync(join(tmpdir(), "prompt-override-outside-"));
160
+ try {
161
+ writeFileSync(join(outside, "secret.md"), "SENSITIVE CONTENTS\n");
162
+ symlinkSync(outside, join(tmpDir, "linked-dir"));
163
+ expect(load(join("linked-dir", "secret.md"))).toBeNull();
164
+ expect(warnCalls[0].data.reason).toBe("outside_workspace");
165
+ } finally {
166
+ rmSync(outside, { recursive: true, force: true });
167
+ }
168
+ });
105
169
  });
106
170
 
107
171
  describe("loadPromptOverride — fallback to null with a diagnostic warning", () => {
@@ -28,6 +28,14 @@ const { MEMORY_JOBS_RELOCATION } =
28
28
  await import("../../../../persistence/migrations/298-move-memory-jobs-to-memory-db.js");
29
29
  const { INJECTION_EVENTS_RELOCATION } =
30
30
  await import("../../../../persistence/migrations/326-move-injection-events-to-memory-db.js");
31
+ const { ACTIVATION_LOGS_RELOCATION } =
32
+ await import("../../../../persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.js");
33
+ const { RECALL_LOGS_RELOCATION } =
34
+ await import("../../../../persistence/migrations/337-move-memory-recall-logs-to-memory-db.js");
35
+ const { MEMORY_V3_SELECTIONS_RELOCATION } =
36
+ await import("../../../../persistence/migrations/338-move-memory-v3-selections-to-memory-db.js");
37
+ const { ACTIVATION_SESSIONS_RELOCATION } =
38
+ await import("../../../../persistence/migrations/339-move-activation-sessions-to-memory-db.js");
31
39
 
32
40
  await initializeDb();
33
41
 
@@ -177,3 +185,273 @@ describe("memory_v2_injection_events drain", () => {
177
185
  ]);
178
186
  });
179
187
  });
188
+
189
+ describe("memory_v3_selections drain", () => {
190
+ test("copies every row (full copy) and drops staging", async () => {
191
+ const sqlite = getSqlite();
192
+ const memory = getMemorySqlite()!;
193
+
194
+ // Clean slate: empty live table, fresh populated staging table with the
195
+ // full post-283 column set.
196
+ memory.exec(`DELETE FROM memory_v3_selections`);
197
+ sqlite.exec(`DROP TABLE IF EXISTS main."memory_v3_selections__relocating"`);
198
+ sqlite.exec(/*sql*/ `
199
+ CREATE TABLE main."memory_v3_selections__relocating" (
200
+ conversation_id TEXT NOT NULL,
201
+ turn INTEGER NOT NULL,
202
+ slug TEXT NOT NULL,
203
+ source TEXT NOT NULL,
204
+ pinned INTEGER NOT NULL DEFAULT 0,
205
+ created_at INTEGER NOT NULL,
206
+ message_id TEXT,
207
+ section_ordinal INTEGER,
208
+ section_title TEXT,
209
+ PRIMARY KEY (conversation_id, turn, slug)
210
+ )
211
+ `);
212
+
213
+ const insert = sqlite.prepare(
214
+ `INSERT INTO main."memory_v3_selections__relocating"
215
+ (conversation_id, turn, slug, source, pinned, created_at,
216
+ message_id, section_ordinal, section_title)
217
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
218
+ );
219
+ insert.run("conv-1", 1, "page-a", "needle", 0, 1_000, "msg-1", 2, "Head");
220
+ insert.run("conv-1", 2, "page-b", "core", 1, 2_000, null, null, null);
221
+
222
+ await drainStagedTable(sqlite, MEMORY_V3_SELECTIONS_RELOCATION);
223
+
224
+ expect(existsInMain("memory_v3_selections__relocating")).toBe(false);
225
+
226
+ // Both rows landed intact, secondary attributes included.
227
+ const kept = memory
228
+ .query(
229
+ `SELECT conversation_id, turn, slug, source, pinned, created_at,
230
+ message_id, section_ordinal, section_title
231
+ FROM memory_v3_selections WHERE conversation_id = 'conv-1'
232
+ ORDER BY turn`,
233
+ )
234
+ .all();
235
+ expect(kept).toEqual([
236
+ {
237
+ conversation_id: "conv-1",
238
+ turn: 1,
239
+ slug: "page-a",
240
+ source: "needle",
241
+ pinned: 0,
242
+ created_at: 1_000,
243
+ message_id: "msg-1",
244
+ section_ordinal: 2,
245
+ section_title: "Head",
246
+ },
247
+ {
248
+ conversation_id: "conv-1",
249
+ turn: 2,
250
+ slug: "page-b",
251
+ source: "core",
252
+ pinned: 1,
253
+ created_at: 2_000,
254
+ message_id: null,
255
+ section_ordinal: null,
256
+ section_title: null,
257
+ },
258
+ ]);
259
+ });
260
+
261
+ test("a pre-283 legacy source NULL-fills the missing columns", async () => {
262
+ const sqlite = getSqlite();
263
+ const memory = getMemorySqlite()!;
264
+
265
+ // Staging table shaped like migration 268's original schema — no
266
+ // message_id / section_ordinal / section_title columns.
267
+ memory.exec(`DELETE FROM memory_v3_selections`);
268
+ sqlite.exec(`DROP TABLE IF EXISTS main."memory_v3_selections__relocating"`);
269
+ sqlite.exec(/*sql*/ `
270
+ CREATE TABLE main."memory_v3_selections__relocating" (
271
+ conversation_id TEXT NOT NULL,
272
+ turn INTEGER NOT NULL,
273
+ slug TEXT NOT NULL,
274
+ source TEXT NOT NULL,
275
+ pinned INTEGER NOT NULL DEFAULT 0,
276
+ created_at INTEGER NOT NULL,
277
+ PRIMARY KEY (conversation_id, turn, slug)
278
+ )
279
+ `);
280
+ sqlite
281
+ .prepare(
282
+ `INSERT INTO main."memory_v3_selections__relocating"
283
+ (conversation_id, turn, slug, source, pinned, created_at)
284
+ VALUES (?, ?, ?, ?, ?, ?)`,
285
+ )
286
+ .run("conv-legacy", 7, "page-old", "needle", 0, 5_000);
287
+
288
+ await drainStagedTable(sqlite, MEMORY_V3_SELECTIONS_RELOCATION);
289
+
290
+ expect(existsInMain("memory_v3_selections__relocating")).toBe(false);
291
+
292
+ const row = memory
293
+ .query(
294
+ `SELECT slug, created_at, message_id, section_ordinal, section_title
295
+ FROM memory_v3_selections WHERE conversation_id = 'conv-legacy'`,
296
+ )
297
+ .get();
298
+ expect(row).toEqual({
299
+ slug: "page-old",
300
+ created_at: 5_000,
301
+ message_id: null,
302
+ section_ordinal: null,
303
+ section_title: null,
304
+ });
305
+ });
306
+ });
307
+
308
+ describe("activation_sessions drain", () => {
309
+ test("copies every row and drops staging", async () => {
310
+ const sqlite = getSqlite();
311
+ const memory = getMemorySqlite()!;
312
+
313
+ memory.exec(`DELETE FROM activation_sessions`);
314
+ sqlite.exec(`DROP TABLE IF EXISTS main."activation_sessions__relocating"`);
315
+ sqlite.exec(/*sql*/ `
316
+ CREATE TABLE main."activation_sessions__relocating" (
317
+ conversation_id TEXT PRIMARY KEY,
318
+ created_at INTEGER NOT NULL
319
+ )
320
+ `);
321
+ const insert = sqlite.prepare(
322
+ `INSERT INTO main."activation_sessions__relocating"
323
+ (conversation_id, created_at) VALUES (?, ?)`,
324
+ );
325
+ insert.run("conv-a", 1_000);
326
+ insert.run("conv-b", 2_000);
327
+
328
+ await drainStagedTable(sqlite, ACTIVATION_SESSIONS_RELOCATION);
329
+
330
+ expect(existsInMain("activation_sessions__relocating")).toBe(false);
331
+
332
+ const kept = memory
333
+ .query(
334
+ `SELECT conversation_id, created_at FROM activation_sessions
335
+ ORDER BY conversation_id`,
336
+ )
337
+ .all();
338
+ expect(kept).toEqual([
339
+ { conversation_id: "conv-a", created_at: 1_000 },
340
+ { conversation_id: "conv-b", created_at: 2_000 },
341
+ ]);
342
+ });
343
+ });
344
+
345
+ describe("memory_v2_activation_logs drain", () => {
346
+ test("copies every row, drops staging", async () => {
347
+ const sqlite = getSqlite();
348
+ const memory = getMemorySqlite()!;
349
+
350
+ // Clean slate: empty live table, fresh populated staging table.
351
+ memory.exec(`DELETE FROM memory_v2_activation_logs`);
352
+ sqlite.exec(
353
+ `DROP TABLE IF EXISTS main."memory_v2_activation_logs__relocating"`,
354
+ );
355
+ sqlite.exec(/*sql*/ `
356
+ CREATE TABLE main."memory_v2_activation_logs__relocating" (
357
+ id TEXT PRIMARY KEY,
358
+ conversation_id TEXT NOT NULL,
359
+ message_id TEXT,
360
+ turn INTEGER NOT NULL,
361
+ mode TEXT NOT NULL,
362
+ concepts_json TEXT NOT NULL,
363
+ skills_json TEXT NOT NULL,
364
+ config_json TEXT NOT NULL,
365
+ created_at INTEGER NOT NULL
366
+ )
367
+ `);
368
+
369
+ const insert = sqlite.prepare(
370
+ `INSERT INTO main."memory_v2_activation_logs__relocating"
371
+ (id, conversation_id, message_id, turn, mode,
372
+ concepts_json, skills_json, config_json, created_at)
373
+ VALUES (?, 'conv-1', ?, ?, 'router', '[]', '[]', '{}', ?)`,
374
+ );
375
+ insert.run("act-1", "msg-1", 1, 1_000);
376
+ insert.run("act-2", null, 2, 2_000);
377
+
378
+ await drainStagedTable(sqlite, ACTIVATION_LOGS_RELOCATION);
379
+
380
+ // Staging dropped; the full-copy spec preserved every row and column.
381
+ expect(existsInMain("memory_v2_activation_logs__relocating")).toBe(false);
382
+ const kept = memory
383
+ .query<
384
+ { id: string; message_id: string | null; turn: number },
385
+ []
386
+ >(`SELECT id, message_id, turn FROM memory_v2_activation_logs ORDER BY id`)
387
+ .all();
388
+ expect(kept).toEqual([
389
+ { id: "act-1", message_id: "msg-1", turn: 1 },
390
+ { id: "act-2", message_id: null, turn: 2 },
391
+ ]);
392
+ });
393
+ });
394
+
395
+ describe("memory_recall_logs drain", () => {
396
+ test("copies every row, NULL-fills query_context on a pre-211 source, drops staging", async () => {
397
+ const sqlite = getSqlite();
398
+ const memory = getMemorySqlite()!;
399
+
400
+ // Clean slate: empty live table, fresh populated staging table shaped
401
+ // like a legacy source that predates the query_context column.
402
+ memory.exec(`DELETE FROM memory_recall_logs`);
403
+ sqlite.exec(`DROP TABLE IF EXISTS main."memory_recall_logs__relocating"`);
404
+ sqlite.exec(/*sql*/ `
405
+ CREATE TABLE main."memory_recall_logs__relocating" (
406
+ id TEXT PRIMARY KEY,
407
+ conversation_id TEXT NOT NULL,
408
+ message_id TEXT,
409
+ enabled INTEGER NOT NULL,
410
+ degraded INTEGER NOT NULL,
411
+ provider TEXT,
412
+ model TEXT,
413
+ degradation_json TEXT,
414
+ semantic_hits INTEGER NOT NULL,
415
+ merged_count INTEGER NOT NULL,
416
+ selected_count INTEGER NOT NULL,
417
+ tier1_count INTEGER NOT NULL,
418
+ tier2_count INTEGER NOT NULL,
419
+ hybrid_search_latency_ms INTEGER NOT NULL,
420
+ sparse_vector_used INTEGER NOT NULL,
421
+ injected_tokens INTEGER NOT NULL,
422
+ latency_ms INTEGER NOT NULL,
423
+ top_candidates_json TEXT NOT NULL,
424
+ injected_text TEXT,
425
+ reason TEXT,
426
+ created_at INTEGER NOT NULL
427
+ )
428
+ `);
429
+
430
+ const insert = sqlite.prepare(
431
+ `INSERT INTO main."memory_recall_logs__relocating"
432
+ (id, conversation_id, message_id, enabled, degraded, semantic_hits,
433
+ merged_count, selected_count, tier1_count, tier2_count,
434
+ hybrid_search_latency_ms, sparse_vector_used, injected_tokens,
435
+ latency_ms, top_candidates_json, created_at)
436
+ VALUES (?, 'conv-1', ?, 1, 0, 3, 2, 1, 1, 0, 100, 0, 300, 150, '[]', ?)`,
437
+ );
438
+ insert.run("rec-1", "msg-1", 1_000);
439
+ insert.run("rec-2", null, 2_000);
440
+
441
+ await drainStagedTable(sqlite, RECALL_LOGS_RELOCATION);
442
+
443
+ // Staging dropped; both rows copied with the absent legacy column
444
+ // NULL-filled.
445
+ expect(existsInMain("memory_recall_logs__relocating")).toBe(false);
446
+ const kept = memory
447
+ .query<
448
+ { id: string; message_id: string | null; query_context: string | null },
449
+ []
450
+ >(`SELECT id, message_id, query_context FROM memory_recall_logs ORDER BY id`)
451
+ .all();
452
+ expect(kept).toEqual([
453
+ { id: "rec-1", message_id: "msg-1", query_context: null },
454
+ { id: "rec-2", message_id: null, query_context: null },
455
+ ]);
456
+ });
457
+ });
@@ -1,41 +1,48 @@
1
- import { eq } from "drizzle-orm";
2
-
3
- import { getDb } from "../../../persistence/db-connection.js";
4
- import { activationSessions } from "../../../persistence/schema/index.js";
5
1
  import { getLogger } from "./logging.js";
2
+ import { memorySqliteOrNull } from "./memory-db.js";
6
3
 
7
4
  const log = getLogger("activation-session-store");
8
5
 
9
6
  /**
10
7
  * Mark a conversation as an activation-rail session. Idempotent (the row's
11
- * primary key is the conversation id) and best-effort: a write failure is
12
- * logged and swallowed so it never blocks the turn that triggered it.
8
+ * primary key is the conversation id) and best-effort: an unavailable memory
9
+ * database or a write failure is logged and swallowed so it never blocks the
10
+ * turn that triggered it.
13
11
  */
14
12
  export function markActivationSession(conversationId: string): void {
15
13
  try {
16
- getDb()
17
- .insert(activationSessions)
18
- .values({ conversationId, createdAt: Date.now() })
19
- .onConflictDoNothing()
20
- .run();
14
+ const raw = memorySqliteOrNull("markActivationSession");
15
+ if (!raw) {
16
+ return;
17
+ }
18
+ raw
19
+ .query(
20
+ /*sql*/ `INSERT OR IGNORE INTO activation_sessions (conversation_id, created_at) VALUES (?, ?)`,
21
+ )
22
+ .run(conversationId, Date.now());
21
23
  } catch (err) {
22
24
  log.warn({ err, conversationId }, "Failed to mark activation session");
23
25
  }
24
26
  }
25
27
 
26
28
  /**
27
- * Whether the given conversation was started on the activation rail. Best-effort:
28
- * a read failure is logged and treated as "not an activation session".
29
+ * Whether the given conversation was started on the activation rail.
30
+ * Best-effort: an unavailable memory database or a read failure is logged and
31
+ * treated as "not an activation session".
29
32
  */
30
33
  export function isActivationSession(conversationId: string): boolean {
31
34
  try {
32
- const row = getDb()
33
- .select({ conversationId: activationSessions.conversationId })
34
- .from(activationSessions)
35
- .where(eq(activationSessions.conversationId, conversationId))
36
- .limit(1)
37
- .get();
38
- return row !== undefined;
35
+ const raw = memorySqliteOrNull("isActivationSession");
36
+ if (!raw) {
37
+ return false;
38
+ }
39
+ return (
40
+ raw
41
+ .query(
42
+ /*sql*/ `SELECT 1 FROM activation_sessions WHERE conversation_id = ?`,
43
+ )
44
+ .get(conversationId) != null
45
+ );
39
46
  } catch (err) {
40
47
  log.warn({ err, conversationId }, "Failed to read activation session");
41
48
  return false;
@@ -24,10 +24,11 @@
24
24
  // still surface lexical hits, and vice versa.
25
25
 
26
26
  import { readdir, readFile, realpath, stat } from "node:fs/promises";
27
- import { extname, isAbsolute, join, relative } from "node:path";
27
+ import { extname, join } from "node:path";
28
28
 
29
29
  import { embedWithRetry } from "../../../../../persistence/embeddings/embed.js";
30
30
  import { getLogger } from "../../logging.js";
31
+ import { isPathInsideRoot } from "../../path-containment.js";
31
32
  import { spreadActivation } from "../../v2/activation.js";
32
33
  import { getEdgeIndex } from "../../v2/edge-index.js";
33
34
  import {
@@ -580,14 +581,6 @@ function normalizeExcerpt(excerpt: string): string {
580
581
  return excerpt.trim().replace(/\s+/g, " ").toLowerCase();
581
582
  }
582
583
 
583
- function isPathInsideRoot(pathToCheck: string, rootRealPath: string): boolean {
584
- const pathRelativeToRoot = relative(rootRealPath, pathToCheck);
585
- return (
586
- pathRelativeToRoot === "" ||
587
- (!pathRelativeToRoot.startsWith("..") && !isAbsolute(pathRelativeToRoot))
588
- );
589
- }
590
-
591
584
  function truncateExcerpt(text: string, maxChars: number): string {
592
585
  if (text.length <= maxChars) return text;
593
586
  return `${text.slice(0, maxChars - 3).trimEnd()}...`;
@@ -1,6 +1,7 @@
1
1
  import { readdir, readFile, realpath, stat } from "node:fs/promises";
2
2
  import { extname, isAbsolute, join, relative, sep } from "node:path";
3
3
 
4
+ import { isPathInsideRoot } from "../../path-containment.js";
4
5
  import type {
5
6
  RecallEvidence,
6
7
  RecallSearchContext,
@@ -1208,14 +1209,6 @@ export function normalizeWorkspacePathLiteral(
1208
1209
  return withoutLineSuffix.split(sep).join("/");
1209
1210
  }
1210
1211
 
1211
- function isPathInsideRoot(pathToCheck: string, rootRealPath: string): boolean {
1212
- const pathRelativeToRoot = relative(rootRealPath, pathToCheck);
1213
- return (
1214
- pathRelativeToRoot === "" ||
1215
- (!pathRelativeToRoot.startsWith("..") && !isAbsolute(pathRelativeToRoot))
1216
- );
1217
- }
1218
-
1219
1212
  function toWorkspaceRelativePath(
1220
1213
  rootRealPath: string,
1221
1214
  filePath: string,
@@ -126,7 +126,7 @@ function makeCapabilityNode(content: string, capId: string): NewNode {
126
126
  describe("loadContextMemory — query/sparse vector surfacing", () => {
127
127
  beforeAll(async () => {
128
128
  await initializeDb();
129
- });
129
+ }, 30_000);
130
130
 
131
131
  beforeEach(async () => {
132
132
  embedShouldThrow = false;
@@ -136,7 +136,7 @@ describe("loadContextMemory — query/sparse vector surfacing", () => {
136
136
  searchRouter = null;
137
137
  resetDbForTesting();
138
138
  await initializeDb();
139
- });
139
+ }, 30_000);
140
140
 
141
141
  test("returns the dense queryVector when embedding succeeds", async () => {
142
142
  embedVector = [0.42, 0.5, 0.7];
@@ -179,7 +179,7 @@ describe("loadContextMemory — query/sparse vector surfacing", () => {
179
179
  describe("retrieveForTurn — query/sparse vector surfacing", () => {
180
180
  beforeAll(async () => {
181
181
  await initializeDb();
182
- });
182
+ }, 30_000);
183
183
 
184
184
  beforeEach(async () => {
185
185
  embedShouldThrow = false;
@@ -189,7 +189,7 @@ describe("retrieveForTurn — query/sparse vector surfacing", () => {
189
189
  searchRouter = null;
190
190
  resetDbForTesting();
191
191
  await initializeDb();
192
- });
192
+ }, 30_000);
193
193
 
194
194
  test("returns the dense queryVector when embedding succeeds", async () => {
195
195
  embedVector = [0.9, 0.8, 0.7];
@@ -312,7 +312,7 @@ describe("retrieveForTurn — topic-pivot recovery", () => {
312
312
 
313
313
  beforeAll(async () => {
314
314
  await initializeDb();
315
- });
315
+ }, 30_000);
316
316
 
317
317
  beforeEach(() => {
318
318
  embedShouldThrow = false;
@@ -462,7 +462,7 @@ describe("loadContextMemory — dual-query capability ranking", () => {
462
462
 
463
463
  beforeAll(async () => {
464
464
  await initializeDb();
465
- });
465
+ }, 30_000);
466
466
 
467
467
  beforeEach(() => {
468
468
  embedShouldThrow = false;