@vellumai/assistant 0.8.5 → 0.8.6

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 (544) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +1 -1
  3. package/bunfig.toml +6 -1
  4. package/docs/credential-execution-service.md +6 -6
  5. package/docs/plugins.md +4 -3
  6. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
  7. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
  8. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  9. package/openapi.yaml +1900 -166
  10. package/package.json +1 -1
  11. package/src/__tests__/actor-token-service.test.ts +3 -2
  12. package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
  13. package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
  14. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  15. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  16. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  17. package/src/__tests__/annotate-risk-options.test.ts +1 -0
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/approval-routes-http.test.ts +9 -13
  20. package/src/__tests__/assert-not-live-db.ts +79 -0
  21. package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
  22. package/src/__tests__/audit-log-rotation.test.ts +2 -2
  23. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  24. package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
  25. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  26. package/src/__tests__/btw-routes.test.ts +3 -2
  27. package/src/__tests__/call-controller.test.ts +3 -2
  28. package/src/__tests__/channel-approval-routes.test.ts +3 -2
  29. package/src/__tests__/channel-guardian.test.ts +3 -2
  30. package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
  31. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  32. package/src/__tests__/channel-retry-sweep.test.ts +320 -3
  33. package/src/__tests__/checker.test.ts +12 -12
  34. package/src/__tests__/compaction-events.test.ts +1 -0
  35. package/src/__tests__/compaction-trail-store.test.ts +264 -0
  36. package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
  37. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  38. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
  39. package/src/__tests__/computer-use-tools.test.ts +12 -14
  40. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  41. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  42. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  43. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  44. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  45. package/src/__tests__/config-schema.test.ts +10 -10
  46. package/src/__tests__/connection-model-compat.test.ts +83 -0
  47. package/src/__tests__/contacts-tools.test.ts +3 -2
  48. package/src/__tests__/context-token-estimator.test.ts +22 -0
  49. package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
  50. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  52. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  53. package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
  54. package/src/__tests__/conversation-agent-loop.test.ts +488 -2
  55. package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
  56. package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
  57. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
  58. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  59. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  61. package/src/__tests__/conversation-error.test.ts +30 -0
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -8
  63. package/src/__tests__/conversation-fork-route.test.ts +3 -2
  64. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  65. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  66. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  67. package/src/__tests__/conversation-lifecycle.test.ts +1 -0
  68. package/src/__tests__/conversation-list-source.test.ts +3 -2
  69. package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
  70. package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
  71. package/src/__tests__/conversation-pairing.test.ts +53 -0
  72. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
  73. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  74. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
  75. package/src/__tests__/conversation-queue.test.ts +333 -291
  76. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
  77. package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
  78. package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
  79. package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
  80. package/src/__tests__/conversation-skill-tools.test.ts +38 -142
  81. package/src/__tests__/conversation-slash-queue.test.ts +84 -32
  82. package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
  83. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
  85. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
  90. package/src/__tests__/conversation-sync-tags.test.ts +128 -12
  91. package/src/__tests__/conversation-title-service.test.ts +1 -0
  92. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
  93. package/src/__tests__/conversation-usage.test.ts +1 -0
  94. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  95. package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
  96. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
  97. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  98. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  99. package/src/__tests__/credential-execution-client.test.ts +72 -1
  100. package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
  101. package/src/__tests__/credential-health-service.test.ts +252 -3
  102. package/src/__tests__/credential-security-invariants.test.ts +5 -5
  103. package/src/__tests__/credential-vault-unit.test.ts +19 -19
  104. package/src/__tests__/credential-vault.test.ts +5 -5
  105. package/src/__tests__/cross-provider-web-search.test.ts +56 -2
  106. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  107. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  108. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  109. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  110. package/src/__tests__/db-test-helpers.ts +58 -0
  111. package/src/__tests__/disk-pressure-guard.test.ts +58 -41
  112. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  113. package/src/__tests__/disk-pressure-routes.test.ts +0 -33
  114. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  115. package/src/__tests__/dm-persistence.test.ts +26 -40
  116. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  117. package/src/__tests__/document-find-replace.test.ts +3 -2
  118. package/src/__tests__/document-tool-security.test.ts +81 -2
  119. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  120. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  121. package/src/__tests__/encrypted-store.test.ts +11 -9
  122. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  123. package/src/__tests__/filing-service.test.ts +1 -0
  124. package/src/__tests__/first-greeting.test.ts +62 -12
  125. package/src/__tests__/gateway-flag-listener.test.ts +0 -1
  126. package/src/__tests__/gemini-provider.test.ts +26 -0
  127. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  128. package/src/__tests__/guardian-outbound-http.test.ts +3 -2
  129. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  130. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
  131. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  132. package/src/__tests__/heartbeat-service.test.ts +1 -0
  133. package/src/__tests__/helpers/mock-logger.ts +26 -0
  134. package/src/__tests__/host-bash-routes.test.ts +1 -0
  135. package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
  136. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  137. package/src/__tests__/host-shell-tool.test.ts +5 -4
  138. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  139. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  140. package/src/__tests__/http-user-message-parity.test.ts +29 -7
  141. package/src/__tests__/identity-intro-cache.test.ts +133 -22
  142. package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
  143. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  144. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  145. package/src/__tests__/injector-disk-pressure.test.ts +3 -17
  146. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  147. package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
  148. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  149. package/src/__tests__/llm-resolver.test.ts +331 -0
  150. package/src/__tests__/llm-schema.test.ts +1 -1
  151. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  152. package/src/__tests__/mcp-abort-signal.test.ts +14 -0
  153. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  154. package/src/__tests__/messaging-send-tool.test.ts +1 -0
  155. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  156. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  157. package/src/__tests__/model-intents.test.ts +3 -3
  158. package/src/__tests__/native-web-search.test.ts +30 -2
  159. package/src/__tests__/notification-deep-link.test.ts +62 -0
  160. package/src/__tests__/oauth-commands-routes.test.ts +37 -0
  161. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  162. package/src/__tests__/oauth-store.test.ts +3 -2
  163. package/src/__tests__/onboarding-template-contract.test.ts +3 -2
  164. package/src/__tests__/openai-provider.test.ts +8 -9
  165. package/src/__tests__/openai-responses-provider.test.ts +70 -10
  166. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  167. package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
  168. package/src/__tests__/persistence-pipeline.test.ts +139 -1
  169. package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
  170. package/src/__tests__/plugin-bootstrap.test.ts +9 -11
  171. package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
  172. package/src/__tests__/process-message-background-slack.test.ts +21 -16
  173. package/src/__tests__/process-message-display-content.test.ts +19 -22
  174. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  175. package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
  176. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  177. package/src/__tests__/recording-handler.test.ts +1 -0
  178. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  179. package/src/__tests__/registry.test.ts +82 -76
  180. package/src/__tests__/relay-server.test.ts +10 -10
  181. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  182. package/src/__tests__/schedule-store.test.ts +16 -1
  183. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  184. package/src/__tests__/secret-ingress-http.test.ts +5 -1
  185. package/src/__tests__/secure-keys.test.ts +3 -3
  186. package/src/__tests__/send-endpoint-busy.test.ts +81 -42
  187. package/src/__tests__/server-history-render.test.ts +4 -1
  188. package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
  189. package/src/__tests__/skill-feature-flags.test.ts +14 -16
  190. package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
  191. package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
  192. package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
  193. package/src/__tests__/skill-tool-factory.test.ts +96 -95
  194. package/src/__tests__/slack-channel-config.test.ts +3 -3
  195. package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
  196. package/src/__tests__/subagent-disposal.test.ts +27 -8
  197. package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
  198. package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
  199. package/src/__tests__/subagent-manager-notify.test.ts +20 -8
  200. package/src/__tests__/subagent-notify-parent.test.ts +5 -4
  201. package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
  202. package/src/__tests__/subagent-tools.test.ts +2 -1
  203. package/src/__tests__/suggestion-routes.test.ts +1 -0
  204. package/src/__tests__/system-prompt.test.ts +38 -0
  205. package/src/__tests__/test-preload-verifier.ts +68 -0
  206. package/src/__tests__/test-preload.ts +32 -39
  207. package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
  208. package/src/__tests__/tool-executor.test.ts +55 -10
  209. package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
  210. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  211. package/src/__tests__/twilio-routes.test.ts +3 -2
  212. package/src/__tests__/validate-input.test.ts +381 -0
  213. package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
  214. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
  215. package/src/__tests__/voice-session-bridge.test.ts +37 -28
  216. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  217. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  218. package/src/acp/session-manager.ts +5 -6
  219. package/src/agent/loop.ts +80 -0
  220. package/src/api/README.md +124 -2
  221. package/src/api/constants/call-sites.ts +27 -0
  222. package/src/api/events/assistant-outbound-attachment.ts +51 -0
  223. package/src/api/events/assistant-text-delta.ts +32 -0
  224. package/src/api/events/assistant-turn-start.ts +33 -0
  225. package/src/api/events/document-comment-created.ts +48 -0
  226. package/src/api/events/document-comment-deleted.ts +24 -0
  227. package/src/api/events/document-comment-reopened.ts +25 -0
  228. package/src/api/events/document-comment-resolved.ts +27 -0
  229. package/src/api/events/generation-cancelled.ts +24 -0
  230. package/src/api/events/generation-handoff.ts +41 -0
  231. package/src/api/events/message-complete.ts +42 -0
  232. package/src/api/events/open-url.ts +30 -0
  233. package/src/{events → api/events}/relationship-state-updated.ts +3 -3
  234. package/src/api/events/tool-use-start.ts +32 -0
  235. package/src/api/index.ts +128 -3
  236. package/src/api/responses/llm-context-response.ts +39 -0
  237. package/src/api/responses/llm-request-log-entry.ts +93 -0
  238. package/src/api/responses/memory-recall-log.ts +65 -0
  239. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  240. package/src/background-wake/background-wake-routes.test.ts +687 -52
  241. package/src/background-wake/platform-client.test.ts +308 -0
  242. package/src/background-wake/platform-client.ts +167 -0
  243. package/src/background-wake/publisher.ts +91 -0
  244. package/src/background-wake/runtime-registry.ts +2 -2
  245. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  246. package/src/calls/guardian-dispatch.ts +1 -0
  247. package/src/calls/voice-session-bridge.ts +4 -4
  248. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  249. package/src/cli/commands/__tests__/notifications.test.ts +184 -40
  250. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  251. package/src/cli/commands/channels/index.ts +229 -0
  252. package/src/cli/commands/memory-v3-render.ts +147 -0
  253. package/src/cli/commands/memory-v3.ts +255 -4
  254. package/src/cli/commands/notifications.ts +365 -55
  255. package/src/cli/lib/open-browser.ts +7 -2
  256. package/src/cli/program.ts +2 -0
  257. package/src/config/assistant-feature-flags.ts +23 -42
  258. package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
  259. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  260. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  261. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  262. package/src/config/call-site-defaults.ts +1 -1
  263. package/src/config/feature-flag-cache.ts +86 -0
  264. package/src/config/feature-flag-registry.json +17 -17
  265. package/src/config/llm-context-resolution.ts +10 -1
  266. package/src/config/llm-resolver.ts +121 -15
  267. package/src/config/loader.ts +4 -5
  268. package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
  269. package/src/config/schemas/heartbeat.ts +1 -1
  270. package/src/config/schemas/llm.ts +90 -1
  271. package/src/config/schemas/memory-v2.ts +26 -0
  272. package/src/config/schemas/services.ts +6 -2
  273. package/src/config/seed-inference-profiles.ts +36 -16
  274. package/src/context/token-estimator.ts +10 -5
  275. package/src/credential-execution/executable-discovery.ts +40 -0
  276. package/src/credential-execution/process-manager.ts +6 -2
  277. package/src/credential-health/credential-health-service.ts +125 -40
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
  279. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
  280. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
  281. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  282. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  283. package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
  284. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  285. package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
  286. package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
  287. package/src/daemon/conversation-agent-loop.ts +132 -28
  288. package/src/daemon/conversation-error.ts +33 -5
  289. package/src/daemon/conversation-messaging.ts +84 -43
  290. package/src/daemon/conversation-process.ts +74 -37
  291. package/src/daemon/conversation-runtime-assembly.ts +29 -9
  292. package/src/daemon/conversation-skill-tools.ts +14 -30
  293. package/src/daemon/conversation-surfaces.ts +69 -34
  294. package/src/daemon/conversation-tool-setup.ts +33 -48
  295. package/src/daemon/conversation.ts +26 -46
  296. package/src/daemon/daemon-control.ts +1 -1
  297. package/src/daemon/daemon-skill-host.ts +9 -2
  298. package/src/daemon/disk-pressure-guard.ts +27 -29
  299. package/src/daemon/first-greeting.ts +31 -13
  300. package/src/daemon/handlers/shared.ts +6 -1
  301. package/src/daemon/lifecycle.ts +12 -12
  302. package/src/daemon/mcp-reload-service.ts +1 -1
  303. package/src/daemon/meet-manifest-loader.ts +10 -17
  304. package/src/daemon/message-types/conversations.ts +20 -22
  305. package/src/daemon/message-types/document-comments.ts +8 -44
  306. package/src/daemon/message-types/home.ts +2 -2
  307. package/src/daemon/message-types/integrations.ts +2 -7
  308. package/src/daemon/message-types/messages.ts +23 -38
  309. package/src/daemon/message-types/subagents.ts +6 -0
  310. package/src/daemon/process-message.ts +9 -9
  311. package/src/daemon/providers-setup.ts +1 -1
  312. package/src/daemon/server.ts +16 -0
  313. package/src/daemon/switch-inference-profile-tool.ts +13 -3
  314. package/src/daemon/tool-setup-types.ts +0 -6
  315. package/src/daemon/wake-target-adapter.ts +10 -0
  316. package/src/documents/document-store.ts +38 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  318. package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
  319. package/src/heartbeat/heartbeat-service.ts +63 -0
  320. package/src/home/__tests__/feed-writer.test.ts +161 -0
  321. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  322. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  323. package/src/home/feed-writer.ts +146 -7
  324. package/src/home/suggested-prompts.ts +27 -145
  325. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  326. package/src/ipc/gateway-client.test.ts +4 -1
  327. package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
  328. package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
  329. package/src/ipc/skill-routes/memory.ts +4 -3
  330. package/src/ipc/skill-routes/registries.ts +28 -29
  331. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
  332. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
  333. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  334. package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
  335. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  336. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  337. package/src/memory/conversation-attention-store.ts +17 -3
  338. package/src/memory/conversation-crud.ts +352 -112
  339. package/src/memory/db-connection.ts +29 -19
  340. package/src/memory/db-init.ts +4 -0
  341. package/src/memory/db-singleton.ts +77 -0
  342. package/src/memory/delivery-channels.ts +82 -0
  343. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  344. package/src/memory/graph/retriever.test.ts +3 -3
  345. package/src/memory/job-handlers/embedding.test.ts +3 -2
  346. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  347. package/src/memory/jobs-worker.ts +12 -1
  348. package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
  349. package/src/memory/llm-request-log-source-local.ts +24 -0
  350. package/src/memory/llm-request-log-source.ts +31 -0
  351. package/src/memory/llm-request-log-store.ts +188 -3
  352. package/src/memory/memory-v2-activation-log-store.ts +95 -1
  353. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  354. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  355. package/src/memory/migrations/index.ts +2 -0
  356. package/src/memory/schema/conversations.ts +9 -1
  357. package/src/memory/schema/inference.ts +0 -1
  358. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  359. package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
  362. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
  363. package/src/memory/v2/harness/metrics.ts +5 -1
  364. package/src/memory/v2/harness/replay-input.ts +19 -3
  365. package/src/memory/v2/harness/runner.ts +6 -0
  366. package/src/memory/v2/harness/trace.ts +6 -0
  367. package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
  368. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
  369. package/src/memory/v3/__tests__/edges.test.ts +144 -1
  370. package/src/memory/v3/__tests__/filter.test.ts +48 -0
  371. package/src/memory/v3/__tests__/gate.test.ts +96 -33
  372. package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
  373. package/src/memory/v3/__tests__/loop.test.ts +250 -5
  374. package/src/memory/v3/__tests__/scouts.test.ts +49 -0
  375. package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
  376. package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
  377. package/src/memory/v3/__tests__/traversal.test.ts +39 -0
  378. package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
  379. package/src/memory/v3/__tests__/validate.test.ts +32 -0
  380. package/src/memory/v3/coretrieval-seed.ts +240 -0
  381. package/src/memory/v3/edges.ts +58 -21
  382. package/src/memory/v3/filter.ts +27 -22
  383. package/src/memory/v3/gate.ts +51 -36
  384. package/src/memory/v3/index-composition.ts +18 -5
  385. package/src/memory/v3/loop.ts +65 -17
  386. package/src/memory/v3/scouts.ts +15 -4
  387. package/src/memory/v3/shadow-diff.ts +287 -0
  388. package/src/memory/v3/shadow-middleware.ts +44 -2
  389. package/src/memory/v3/traversal.ts +6 -1
  390. package/src/memory/v3/tree-walk.ts +6 -1
  391. package/src/memory/v3/validate.ts +56 -33
  392. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  393. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  394. package/src/notifications/adapters/slack.ts +45 -11
  395. package/src/notifications/broadcaster.ts +114 -63
  396. package/src/notifications/conversation-pairing.ts +23 -3
  397. package/src/notifications/decisions-store.ts +32 -1
  398. package/src/notifications/deliveries-store.ts +45 -0
  399. package/src/notifications/edit-notification.ts +201 -0
  400. package/src/notifications/emit-signal.ts +11 -1
  401. package/src/notifications/signal.ts +10 -0
  402. package/src/notifications/types.ts +37 -0
  403. package/src/oauth/byo-connection.test.ts +67 -3
  404. package/src/oauth/byo-connection.ts +32 -5
  405. package/src/oauth/connect-orchestrator.ts +9 -0
  406. package/src/oauth/connection-resolver.test.ts +76 -0
  407. package/src/oauth/connection-resolver.ts +49 -10
  408. package/src/oauth/manual-token-connection.ts +51 -3
  409. package/src/oauth/seed-providers.ts +3 -0
  410. package/src/permissions/approval-policy.test.ts +19 -5
  411. package/src/permissions/approval-policy.ts +14 -3
  412. package/src/permissions/checker.ts +21 -8
  413. package/src/platform/client.test.ts +24 -1
  414. package/src/platform/client.ts +8 -0
  415. package/src/platform/feature-gate.ts +15 -0
  416. package/src/plugins/defaults/injectors.ts +2 -8
  417. package/src/plugins/defaults/persistence.ts +25 -6
  418. package/src/plugins/types.ts +57 -13
  419. package/src/proactive-artifact/job.test.ts +1 -0
  420. package/src/prompts/__tests__/system-prompt.test.ts +4 -4
  421. package/src/prompts/system-prompt.ts +38 -40
  422. package/src/prompts/template-detection.ts +10 -4
  423. package/src/prompts/templates/BOOTSTRAP.md +7 -11
  424. package/src/prompts/templates/IDENTITY.md +0 -2
  425. package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
  426. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  427. package/src/providers/call-site-routing.ts +33 -9
  428. package/src/providers/connection-model-compat.ts +23 -0
  429. package/src/providers/connection-resolution.ts +39 -20
  430. package/src/providers/fireworks/client.ts +1 -0
  431. package/src/providers/gemini/client.ts +24 -3
  432. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  433. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  434. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  435. package/src/providers/inference/auth.ts +0 -8
  436. package/src/providers/inference/connections.ts +3 -66
  437. package/src/providers/inference/resolve-auth.ts +2 -3
  438. package/src/providers/model-catalog.ts +35 -1
  439. package/src/providers/model-intents.ts +3 -3
  440. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  441. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
  442. package/src/providers/openai/chat-completions-provider.ts +110 -12
  443. package/src/providers/openai/codex-models.ts +2 -0
  444. package/src/providers/openai/responses-provider.ts +53 -53
  445. package/src/providers/openrouter/client.ts +13 -8
  446. package/src/providers/provider-send-message.ts +18 -9
  447. package/src/providers/registry.ts +48 -8
  448. package/src/providers/retry.ts +16 -4
  449. package/src/providers/search-provider-catalog.ts +17 -9
  450. package/src/providers/types.ts +9 -0
  451. package/src/runtime/__tests__/agent-wake.test.ts +1 -0
  452. package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
  453. package/src/runtime/access-request-helper.ts +1 -0
  454. package/src/runtime/auth/route-policy.ts +10 -0
  455. package/src/runtime/channel-readiness-service.ts +68 -0
  456. package/src/runtime/channel-reply-delivery.ts +23 -0
  457. package/src/runtime/channel-retry-sweep.ts +47 -14
  458. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  459. package/src/runtime/migrations/vbundle-builder.ts +3 -2
  460. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  461. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
  462. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  463. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  464. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  465. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
  466. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
  467. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
  468. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  469. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
  470. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  471. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  472. package/src/runtime/routes/app-management-routes.ts +111 -4
  473. package/src/runtime/routes/background-wake-routes.ts +188 -20
  474. package/src/runtime/routes/btw-routes.ts +4 -4
  475. package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
  476. package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
  477. package/src/runtime/routes/conversation-list-routes.ts +147 -0
  478. package/src/runtime/routes/conversation-management-routes.ts +39 -14
  479. package/src/runtime/routes/conversation-query-routes.ts +60 -10
  480. package/src/runtime/routes/conversation-routes.ts +186 -140
  481. package/src/runtime/routes/conversations-import-routes.ts +19 -6
  482. package/src/runtime/routes/documents-routes.ts +10 -1
  483. package/src/runtime/routes/group-routes.ts +11 -0
  484. package/src/runtime/routes/home-feed-routes.ts +129 -0
  485. package/src/runtime/routes/identity-intro-cache.ts +61 -16
  486. package/src/runtime/routes/identity-routes.ts +30 -9
  487. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
  488. package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
  489. package/src/runtime/routes/index.ts +2 -0
  490. package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
  491. package/src/runtime/routes/integrations/vercel.ts +15 -0
  492. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  493. package/src/runtime/routes/memory-v3-routes.ts +160 -2
  494. package/src/runtime/routes/migration-routes.ts +20 -13
  495. package/src/runtime/routes/notification-routes.ts +63 -1
  496. package/src/runtime/routes/oauth-commands-routes.ts +6 -1
  497. package/src/runtime/routes/surface-action-routes.ts +1 -38
  498. package/src/runtime/routes/surface-content-routes.ts +12 -5
  499. package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
  500. package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
  501. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
  502. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  503. package/src/runtime/sync/resource-sync-events.ts +1 -1
  504. package/src/runtime/tool-grant-request-helper.ts +1 -0
  505. package/src/schedule/schedule-store.ts +8 -1
  506. package/src/schedule/scheduler.ts +111 -15
  507. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  508. package/src/security/encrypted-store.ts +7 -16
  509. package/src/security/store-path-override.ts +61 -0
  510. package/src/signals/user-message.ts +5 -8
  511. package/src/skills/validate-input.ts +177 -0
  512. package/src/subagent/manager.ts +13 -13
  513. package/src/subagent/types.ts +6 -0
  514. package/src/tasks/tool-sanitizer.ts +2 -2
  515. package/src/tools/apps/definitions.ts +35 -21
  516. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  517. package/src/tools/computer-use/definitions.ts +268 -266
  518. package/src/tools/document/document-tool.ts +131 -8
  519. package/src/tools/execution-target.ts +2 -5
  520. package/src/tools/executor.ts +18 -55
  521. package/src/tools/host-filesystem/edit.test.ts +1 -0
  522. package/src/tools/host-filesystem/read.test.ts +1 -0
  523. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  524. package/src/tools/host-filesystem/write.test.ts +1 -0
  525. package/src/tools/mcp/mcp-tool-factory.ts +0 -2
  526. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  527. package/src/tools/network/__tests__/web-search.test.ts +211 -3
  528. package/src/tools/network/managed-search-proxy.ts +183 -0
  529. package/src/tools/network/web-search.ts +199 -44
  530. package/src/tools/policy-context.ts +3 -1
  531. package/src/tools/registry.ts +146 -103
  532. package/src/tools/schedule/create.ts +1 -1
  533. package/src/tools/skills/skill-tool-factory.ts +17 -36
  534. package/src/tools/subagent/spawn.ts +3 -0
  535. package/src/tools/tool-approval-handler.ts +10 -4
  536. package/src/tools/tool-name-aliases.ts +72 -14
  537. package/src/tools/types.ts +17 -15
  538. package/src/tools/ui-surface/definitions.ts +98 -86
  539. package/src/types/onboarding-context.ts +6 -0
  540. package/src/usage/attribution.ts +32 -1
  541. package/src/util/browser.ts +7 -2
  542. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  543. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  544. package/src/workspace/migrations/registry.ts +4 -0
@@ -1,7 +1,7 @@
1
1
  import { beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
3
  import { getDocumentById } from "../documents/document-store.js";
4
- import { getSqlite, resetDb } from "../memory/db-connection.js";
4
+ import { getSqlite } from "../memory/db-connection.js";
5
5
  import {
6
6
  executeDocumentDelete,
7
7
  executeDocumentList,
@@ -9,6 +9,7 @@ import {
9
9
  executeDocumentUpdate,
10
10
  } from "../tools/document/document-tool.js";
11
11
  import type { ToolContext, ToolExecutionResult } from "../tools/types.js";
12
+ import { resetDbForTesting } from "./db-test-helpers.js";
12
13
 
13
14
  function makeContext(overrides: Partial<ToolContext> = {}): ToolContext {
14
15
  return {
@@ -25,7 +26,7 @@ function parseResult<T>(result: ToolExecutionResult): T {
25
26
  }
26
27
 
27
28
  function bootstrapDocumentTables(): void {
28
- resetDb();
29
+ resetDbForTesting();
29
30
  const raw = getSqlite();
30
31
  raw.exec(/*sql*/ `
31
32
  DROP TABLE IF EXISTS document_conversations;
@@ -256,3 +257,81 @@ describe("document tool security", () => {
256
257
  expect(getDocumentById("doc-current")).toBeNull();
257
258
  });
258
259
  });
260
+
261
+ describe("executeDocumentUpdate — input validation", () => {
262
+ beforeEach(() => {
263
+ bootstrapDocumentTables();
264
+ seedFixtureDocuments();
265
+ });
266
+
267
+ test("returns Invalid input when surface_id is missing", () => {
268
+ const result = executeDocumentUpdate({}, makeContext());
269
+ expect(result.isError).toBe(true);
270
+ const body = parseResult<{ error: string }>(result);
271
+ expect(body.error).toContain("Invalid input: surface_id is required");
272
+ expect(body.error).not.toContain("Document not found");
273
+ });
274
+
275
+ test("returns Invalid input when content is missing", () => {
276
+ const result = executeDocumentUpdate(
277
+ { surface_id: "doc-x" },
278
+ makeContext(),
279
+ );
280
+ expect(result.isError).toBe(true);
281
+ const body = parseResult<{ error: string }>(result);
282
+ expect(body.error).toBe(
283
+ "Invalid input: content is required and must be a string",
284
+ );
285
+ });
286
+
287
+ test("allows empty string content (falls through to access check)", () => {
288
+ const result = executeDocumentUpdate(
289
+ { surface_id: "doc-x", content: "" },
290
+ makeContext(),
291
+ );
292
+ expect(result.isError).toBe(true);
293
+ const body = parseResult<{ error: string }>(result);
294
+ // Did NOT fail input validation; fell through to access/not-found path.
295
+ expect(body.error).toBe("Document not found");
296
+ });
297
+
298
+ test("returns Invalid input when mode is not replace or append", () => {
299
+ const result = executeDocumentUpdate(
300
+ { surface_id: "doc-x", content: "hi", mode: "bogus" },
301
+ makeContext(),
302
+ );
303
+ expect(result.isError).toBe(true);
304
+ const body = parseResult<{ error: string }>(result);
305
+ expect(body.error).toBe(
306
+ 'Invalid input: mode must be "replace" or "append"',
307
+ );
308
+ });
309
+
310
+ test("treats mode: null the same as undefined (factory validator accepts both)", () => {
311
+ // validateInputAgainstSchema treats null as "absent" for enum checks, so
312
+ // the executor must agree — { mode: null } should fall through to the
313
+ // access check, not return a confusing 'mode must be ...' error.
314
+ const result = executeDocumentUpdate(
315
+ { surface_id: "doc-x", content: "hi", mode: null },
316
+ makeContext(),
317
+ );
318
+ expect(result.isError).toBe(true);
319
+ const body = parseResult<{ error: string }>(result);
320
+ expect(body.error).not.toContain("Invalid input: mode");
321
+ expect(body.error).toBe("Document not found");
322
+ });
323
+
324
+ test("executeDocumentRead returns Invalid input when surface_id is missing", () => {
325
+ const result = executeDocumentRead({}, makeContext());
326
+ expect(result.isError).toBe(true);
327
+ const body = parseResult<{ error: string }>(result);
328
+ expect(body.error).toContain("Invalid input: surface_id is required");
329
+ });
330
+
331
+ test("executeDocumentDelete returns Invalid input when surface_id is missing", () => {
332
+ const result = executeDocumentDelete({}, makeContext());
333
+ expect(result.isError).toBe(true);
334
+ const body = parseResult<{ error: string }>(result);
335
+ expect(body.error).toContain("Invalid input: surface_id is required");
336
+ });
337
+ });
@@ -46,18 +46,19 @@ mock.module("../config/loader.js", () => ({
46
46
  }));
47
47
 
48
48
  const { buildSystemPrompt } = await import("../prompts/system-prompt.js");
49
- const { _setOverridesForTesting } =
50
- await import("../config/assistant-feature-flags.js");
49
+ const { setOverridesForTesting } = await import(
50
+ "./feature-flag-test-helpers.js"
51
+ );
51
52
 
52
53
  describe("Dynamic Skill Authoring Workflow moved to tool descriptions", () => {
53
54
  beforeEach(() => {
54
- _setOverridesForTesting({
55
+ setOverridesForTesting({
55
56
  browser: true,
56
57
  });
57
58
  });
58
59
 
59
60
  afterEach(() => {
60
- _setOverridesForTesting({});
61
+ setOverridesForTesting({});
61
62
  });
62
63
 
63
64
  test("system prompt no longer contains Dynamic Skill Authoring section", () => {
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Test-only utilities for overriding the encrypted credential store paths.
3
+ *
4
+ * Replaces the removed `_setStorePath` and `_setStoreKeyPath` exports
5
+ * from `encrypted-store.ts`. Lives here (not in the source module)
6
+ * because production modules should not expose test backdoors.
7
+ *
8
+ * No source-module imports
9
+ * ------------------------
10
+ * This file has ZERO imports from `src/`. It accesses the store-path
11
+ * override state via the shared `globalThis.vellumAssistant.storePathOverride`
12
+ * slot that `src/security/store-path-override.ts` also reads/writes. The
13
+ * slot shape is duplicated here on purpose: keeping this file off the
14
+ * production import graph is what protects the test preload from a
15
+ * broken `node_modules` symlink (DB ghost #3). The two declarations MUST
16
+ * stay in sync — if you change one, change the other.
17
+ *
18
+ * Most tests no longer need these overrides: the test preload places
19
+ * `VELLUM_WORKSPACE_DIR` at `<tmpRoot>/workspace`, so `getProtectedDir()`
20
+ * resolves to `<tmpRoot>/protected` per process. The setters here exist
21
+ * for the small set of tests that exercise specific path scenarios
22
+ * (env-var fallbacks, migration corner cases, etc.).
23
+ */
24
+
25
+ // Mirrors `src/security/store-path-override.ts`. Duplicated by design — see
26
+ // the "No source-module imports" section above.
27
+ type PathSlot = {
28
+ storePath: string | null;
29
+ storeKeyPath: string | null;
30
+ };
31
+
32
+ type VellumAssistantNamespace = {
33
+ storePathOverride?: PathSlot;
34
+ };
35
+
36
+ function pathSlot(): PathSlot {
37
+ const g = globalThis as { vellumAssistant?: VellumAssistantNamespace };
38
+ const ns = (g.vellumAssistant ??= {});
39
+ return (ns.storePathOverride ??= { storePath: null, storeKeyPath: null });
40
+ }
41
+
42
+ /**
43
+ * Override the encrypted store file path. Pass `null` to reset to the
44
+ * default (`<protectedDir>/keys.enc`).
45
+ */
46
+ export function setStorePathForTesting(path: string | null): void {
47
+ pathSlot().storePath = path;
48
+ }
49
+
50
+ /**
51
+ * Override the store-key file path. Pass `null` to reset to the default
52
+ * (`<dirname(storePath)>/store.key`).
53
+ */
54
+ export function setStoreKeyPathForTesting(path: string | null): void {
55
+ pathSlot().storeKeyPath = path;
56
+ }
@@ -24,7 +24,7 @@ import {
24
24
  } from "bun:test";
25
25
 
26
26
  // ---------------------------------------------------------------------------
27
- // Mock only the logger (not platform -- we use _setStorePath instead)
27
+ // Mock only the logger (not platform -- we use setStorePathForTesting instead)
28
28
  // ---------------------------------------------------------------------------
29
29
 
30
30
  mock.module("../util/logger.js", () => ({
@@ -35,13 +35,15 @@ mock.module("../util/logger.js", () => ({
35
35
  }));
36
36
 
37
37
  import {
38
- _setStoreKeyPath,
39
- _setStorePath,
40
38
  deleteKey,
41
39
  getKey,
42
40
  listKeys,
43
41
  setKey,
44
42
  } from "../security/encrypted-store.js";
43
+ import {
44
+ setStoreKeyPathForTesting,
45
+ setStorePathForTesting,
46
+ } from "./encrypted-store-test-helpers.js";
45
47
 
46
48
  // ---------------------------------------------------------------------------
47
49
  // Use a temp directory so tests don't touch the real ~/.vellum
@@ -141,13 +143,13 @@ describe("encrypted-store", () => {
141
143
  for (const entry of readdirSync(TEST_DIR)) {
142
144
  rmSync(join(TEST_DIR, entry), { recursive: true, force: true });
143
145
  }
144
- _setStorePath(STORE_PATH);
145
- _setStoreKeyPath(STORE_KEY_PATH);
146
+ setStorePathForTesting(STORE_PATH);
147
+ setStoreKeyPathForTesting(STORE_KEY_PATH);
146
148
  });
147
149
 
148
150
  afterEach(() => {
149
- _setStorePath(null);
150
- _setStoreKeyPath(null);
151
+ setStorePathForTesting(null);
152
+ setStoreKeyPathForTesting(null);
151
153
  });
152
154
 
153
155
  afterAll(() => {
@@ -508,8 +510,8 @@ describe("encrypted-store", () => {
508
510
  // Point to a path in a non-existent subdirectory
509
511
  const nestedPath = join(TEST_DIR, "sub", "dir", "keys.enc");
510
512
  const nestedKeyPath = join(TEST_DIR, "sub", "dir", "store.key");
511
- _setStorePath(nestedPath);
512
- _setStoreKeyPath(nestedKeyPath);
513
+ setStorePathForTesting(nestedPath);
514
+ setStoreKeyPathForTesting(nestedKeyPath);
513
515
  const result = setKey("test", "value");
514
516
  expect(result).toBe(true);
515
517
  expect(getKey("test")).toBe("value");
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Test-only utilities for seeding the feature flag override cache.
3
+ *
4
+ * Replaces the removed `_setOverridesForTesting` export from
5
+ * `assistant-feature-flags.ts`. Lives here (not in the source module)
6
+ * because production modules should not expose test backdoors.
7
+ *
8
+ * No source-module imports
9
+ * ------------------------
10
+ * This file has ZERO imports from `src/`. It accesses the feature flag
11
+ * cache's state via the shared `globalThis.vellumAssistant.featureFlagCache`
12
+ * slot that `src/config/feature-flag-cache.ts` also reads/writes. The slot
13
+ * shape is duplicated here on purpose: keeping this file off the
14
+ * production import graph is what protects the test preload from a
15
+ * broken `node_modules` symlink (DB ghost #3). The two declarations MUST
16
+ * stay in sync — if you change one, change the other.
17
+ */
18
+
19
+ // Mirrors `src/config/feature-flag-cache.ts`. Duplicated by design — see
20
+ // the "No source-module imports" section above.
21
+ type FlagSlot = {
22
+ overrides: Record<string, boolean> | null;
23
+ fromGateway: boolean;
24
+ };
25
+
26
+ type VellumAssistantNamespace = {
27
+ featureFlagCache?: FlagSlot;
28
+ };
29
+
30
+ function flagSlot(): FlagSlot {
31
+ const g = globalThis as { vellumAssistant?: VellumAssistantNamespace };
32
+ const ns = (g.vellumAssistant ??= {});
33
+ return (ns.featureFlagCache ??= { overrides: null, fromGateway: false });
34
+ }
35
+
36
+ /**
37
+ * Synchronously seed the feature flag override cache for the current test.
38
+ *
39
+ * Sets the cache to a clone of `overrides` and marks it as
40
+ * gateway-populated, so subsequent `initFeatureFlagOverrides()` calls are
41
+ * no-ops (preventing the production retry loop from running during tests).
42
+ *
43
+ * Tests that want the gateway IPC retry path to actually run should not
44
+ * call this — they should leave the cache empty or call
45
+ * `clearFeatureFlagOverridesCache()` from `assistant-feature-flags.ts`.
46
+ */
47
+ export function setOverridesForTesting(
48
+ overrides: Record<string, boolean>,
49
+ ): void {
50
+ const s = flagSlot();
51
+ s.overrides = { ...overrides };
52
+ s.fromGateway = true;
53
+ }
@@ -83,6 +83,7 @@ mock.module("../memory/conversation-crud.js", () => ({
83
83
  createdConversations.push(opts);
84
84
  return { id: `conv-${++conversationIdCounter}`, ...opts };
85
85
  },
86
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
86
87
  }));
87
88
 
88
89
  // Mock logger
@@ -96,7 +96,7 @@ describe("first-greeting", () => {
96
96
  assistantName: "Pax",
97
97
  });
98
98
  expect(greeting).toBe(
99
- "Hey Alice, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.",
99
+ "Hey Alice, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you. If you have context or workflows from another assistant or harness, bring them over early and I'll help port them.",
100
100
  );
101
101
  });
102
102
 
@@ -108,7 +108,7 @@ describe("first-greeting", () => {
108
108
  assistantName: "Remy",
109
109
  });
110
110
  expect(greeting).toBe(
111
- "Hey Alice, I'm Remy. Good to meet you.\n\nWe can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here.",
111
+ "Hey Alice, I'm Remy. Good to meet you.\n\nWe can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here. If you have context or workflows from another assistant or harness, bring them over early and I can help port them.",
112
112
  );
113
113
  });
114
114
 
@@ -119,7 +119,7 @@ describe("first-greeting", () => {
119
119
  assistantName: "Pax",
120
120
  });
121
121
  expect(greeting).toBe(
122
- "Hey, I'm Pax. Let's see what you've got.\n\nWe can jump straight into whatever you've got, or take a few minutes to just talk first. What sounds right?",
122
+ "Hey, I'm Pax. Let's see what you've got.\n\nWe can jump straight into whatever you've got, or take a few minutes to just talk first. If you've got context or workflows from another assistant or harness, bring them over early and I'll port them with you. What sounds right?",
123
123
  );
124
124
  });
125
125
 
@@ -131,7 +131,7 @@ describe("first-greeting", () => {
131
131
  assistantName: "Pax",
132
132
  });
133
133
  expect(greeting).toBe(
134
- "Hey Alice, I'm Pax.\n\nWe can start with whatever's in front of you, or just talk for a bit first. Either way.",
134
+ "Hey Alice, I'm Pax.\n\nWe can start with whatever's in front of you, or just talk for a bit first. Either way. If there's old context or workflows from another assistant or harness, bring them over early and I'll help port them.",
135
135
  );
136
136
  });
137
137
 
@@ -142,7 +142,7 @@ describe("first-greeting", () => {
142
142
  userName: "Alice",
143
143
  });
144
144
  expect(greeting).toBe(
145
- "Hey Alice,\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.",
145
+ "Hey Alice,\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you. If you have context or workflows from another assistant or harness, bring them over early and I'll help port them.",
146
146
  );
147
147
  });
148
148
 
@@ -153,7 +153,7 @@ describe("first-greeting", () => {
153
153
  assistantName: "Pax",
154
154
  });
155
155
  expect(greeting).toBe(
156
- "Hey, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.",
156
+ "Hey, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you. If you have context or workflows from another assistant or harness, bring them over early and I'll help port them.",
157
157
  );
158
158
  });
159
159
 
@@ -173,7 +173,7 @@ describe("first-greeting", () => {
173
173
  tone: "warm",
174
174
  });
175
175
  expect(greeting).toBe(
176
- "Hey,\n\nWe can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here.",
176
+ "Hey,\n\nWe can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here. If you have context or workflows from another assistant or harness, bring them over early and I can help port them.",
177
177
  );
178
178
  });
179
179
 
@@ -185,6 +185,17 @@ describe("first-greeting", () => {
185
185
  expect(unique.size).toBe(4);
186
186
  });
187
187
 
188
+ it("google-connected greeting points to Gmail only", () => {
189
+ const greeting = getCannedFirstGreeting({
190
+ ...base,
191
+ tone: "grounded",
192
+ googleConnected: true,
193
+ });
194
+ expect(greeting).toContain("scan Gmail");
195
+ expect(greeting).not.toContain("calendar");
196
+ expect(greeting).not.toContain("drive");
197
+ });
198
+
188
199
  it("unknown tone falls back to grounded defaults", () => {
189
200
  const greeting = getCannedFirstGreeting({
190
201
  ...base,
@@ -193,7 +204,7 @@ describe("first-greeting", () => {
193
204
  assistantName: "Pax",
194
205
  });
195
206
  expect(greeting).toBe(
196
- "Hey Alice, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.",
207
+ "Hey Alice, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you. If you have context or workflows from another assistant or harness, bring them over early and I'll help port them.",
197
208
  );
198
209
  });
199
210
 
@@ -223,7 +234,7 @@ describe("first-greeting", () => {
223
234
  const [intro, invite] = greeting.split("\n\n");
224
235
  expect(intro).toBe("Hey Bob, I'm Pax.");
225
236
  expect(invite).toBe(
226
- "We can get into whatever you've got, or just talk first — that tends to go better. Up to you.",
237
+ "We can get into whatever you've got, or just talk first — that tends to go better. Up to you. If you have context or workflows from another assistant or harness, bring them over early and I'll help port them.",
227
238
  );
228
239
  });
229
240
 
@@ -232,7 +243,7 @@ describe("first-greeting", () => {
232
243
  const [intro, invite] = greeting.split("\n\n");
233
244
  expect(intro).toBe("Hey Bob, I'm Pax. Good to meet you.");
234
245
  expect(invite).toBe(
235
- "We can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here.",
246
+ "We can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here. If you have context or workflows from another assistant or harness, bring them over early and I can help port them.",
236
247
  );
237
248
  });
238
249
 
@@ -241,7 +252,7 @@ describe("first-greeting", () => {
241
252
  const [intro, invite] = greeting.split("\n\n");
242
253
  expect(intro).toBe("Hey Bob, I'm Pax. Let's see what you've got.");
243
254
  expect(invite).toBe(
244
- "We can jump straight into whatever you've got, or take a few minutes to just talk first. What sounds right?",
255
+ "We can jump straight into whatever you've got, or take a few minutes to just talk first. If you've got context or workflows from another assistant or harness, bring them over early and I'll port them with you. What sounds right?",
245
256
  );
246
257
  });
247
258
 
@@ -250,7 +261,7 @@ describe("first-greeting", () => {
250
261
  const [intro, invite] = greeting.split("\n\n");
251
262
  expect(intro).toBe("Hey Bob, I'm Pax.");
252
263
  expect(invite).toBe(
253
- "We can start with whatever's in front of you, or just talk for a bit first. Either way.",
264
+ "We can start with whatever's in front of you, or just talk for a bit first. Either way. If there's old context or workflows from another assistant or harness, bring them over early and I'll help port them.",
254
265
  );
255
266
  });
256
267
 
@@ -274,6 +285,45 @@ describe("first-greeting", () => {
274
285
  });
275
286
  });
276
287
 
288
+ describe("migration offer is present in every variant", () => {
289
+ const MIGRATION_MARKER = "bring them over early";
290
+
291
+ it("no-onboarding greeting includes the migration offer", () => {
292
+ expect(getCannedFirstGreeting(undefined)).toContain(MIGRATION_MARKER);
293
+ expect(CANNED_FIRST_GREETING).toContain(MIGRATION_MARKER);
294
+ });
295
+
296
+ it("minimal onboarding (falls back to CANNED) includes the migration offer", () => {
297
+ expect(
298
+ getCannedFirstGreeting({ tools: [], tasks: [], tone: "" }),
299
+ ).toContain(MIGRATION_MARKER);
300
+ });
301
+
302
+ it("every tone variant includes the migration offer", () => {
303
+ for (const tone of ["grounded", "warm", "energetic", "poetic"]) {
304
+ const greeting = getCannedFirstGreeting({
305
+ tools: [],
306
+ tasks: [],
307
+ tone,
308
+ userName: "Alice",
309
+ assistantName: "Pax",
310
+ });
311
+ expect(greeting).toContain(MIGRATION_MARKER);
312
+ expect(greeting).toContain("port them");
313
+ }
314
+ });
315
+
316
+ it("google-connected greeting still includes the migration offer", () => {
317
+ const greeting = getCannedFirstGreeting({
318
+ tools: [],
319
+ tasks: [],
320
+ tone: "warm",
321
+ googleConnected: true,
322
+ });
323
+ expect(greeting).toContain(MIGRATION_MARKER);
324
+ });
325
+ });
326
+
277
327
  describe("buildScanFirstMessage", () => {
278
328
  it("website variant includes 'my website' and the URL", () => {
279
329
  const msg = buildScanFirstMessage("https://acme.com", "website");
@@ -33,7 +33,6 @@ mock.module("../config/assistant-feature-flags.js", () => ({
33
33
  initFeatureFlagOverrides: async () => {},
34
34
  clearFeatureFlagOverridesCache: () => {},
35
35
  isAssistantFeatureFlagEnabled: () => true,
36
- _setOverridesForTesting: () => {},
37
36
  }));
38
37
 
39
38
  // ---------------------------------------------------------------------------
@@ -285,6 +285,32 @@ describe("GeminiProvider", () => {
285
285
  });
286
286
  });
287
287
 
288
+ test("omits thinkingConfig for models that do not support thinking", async () => {
289
+ fakeChunks = [textChunk("OK"), finishChunk("STOP", 10, 2)];
290
+
291
+ // gemini-2.5-flash-lite has supportsThinking: false in the catalog; the
292
+ // provider must not forward thinking params even when a thinking config is
293
+ // supplied (gemini is in THINKING_AWARE_PROVIDERS, so retry.ts no longer
294
+ // strips them).
295
+ const liteProvider = new GeminiProvider(
296
+ "test-api-key",
297
+ "gemini-2.5-flash-lite",
298
+ );
299
+ await liteProvider.sendMessage(
300
+ [{ role: "user", content: [{ type: "text", text: "Hi" }] }],
301
+ undefined,
302
+ undefined,
303
+ {
304
+ config: {
305
+ thinking: { type: "adaptive", level: "high", streamThinking: false },
306
+ },
307
+ },
308
+ );
309
+
310
+ const config = lastStreamParams!.config as Record<string, unknown>;
311
+ expect(config.thinkingConfig).toBeUndefined();
312
+ });
313
+
288
314
  test("omits thinkingConfig when wire shape is adaptive with no extras", async () => {
289
315
  fakeChunks = [textChunk("OK"), finishChunk("STOP", 10, 2)];
290
316
 
@@ -28,7 +28,7 @@ import {
28
28
  stopGuardianActionSweep,
29
29
  sweepExpiredGuardianActions,
30
30
  } from "../calls/guardian-action-sweep.js";
31
- import { getDb, resetDb } from "../memory/db-connection.js";
31
+ import { getDb } from "../memory/db-connection.js";
32
32
  import { initializeDb } from "../memory/db-init.js";
33
33
  import {
34
34
  createGuardianActionDelivery,
@@ -38,6 +38,7 @@ import {
38
38
  updateDeliveryStatus,
39
39
  } from "../memory/guardian-action-store.js";
40
40
  import { conversations } from "../memory/schema.js";
41
+ import { resetDbForTesting } from "./db-test-helpers.js";
41
42
 
42
43
  initializeDb();
43
44
 
@@ -73,7 +74,7 @@ describe("guardian-action-sweep", () => {
73
74
 
74
75
  afterAll(() => {
75
76
  stopGuardianActionSweep();
76
- resetDb();
77
+ resetDbForTesting();
77
78
  });
78
79
 
79
80
  test("sweepExpiredGuardianActions expires requests past their expiresAt", async () => {
@@ -88,7 +88,7 @@ globalThis.fetch = (async (
88
88
  // Now import modules under test (after mocks are in place)
89
89
  // ---------------------------------------------------------------------------
90
90
 
91
- import { getDb, resetDb } from "../memory/db-connection.js";
91
+ import { getDb } from "../memory/db-connection.js";
92
92
  import { initializeDb } from "../memory/db-init.js";
93
93
  import { updateSessionDelivery } from "../runtime/channel-verification-service.js";
94
94
  import {
@@ -102,13 +102,14 @@ import {
102
102
  resendOutbound,
103
103
  startOutbound,
104
104
  } from "../runtime/verification-outbound-actions.js";
105
+ import { resetDbForTesting } from "./db-test-helpers.js";
105
106
 
106
107
  // Initialize the database (creates all tables)
107
108
  initializeDb();
108
109
 
109
110
  afterAll(() => {
110
111
  globalThis.fetch = originalFetch;
111
- resetDb();
112
+ resetDbForTesting();
112
113
  });
113
114
 
114
115
  function resetTables(): void {
@@ -156,9 +156,54 @@ mock.module("../daemon/skill-memory-refresh.js", () => ({
156
156
  refreshSkillCapabilityMemories: mockRefreshSkillCapabilityMemories,
157
157
  }));
158
158
 
159
- mock.module("../util/platform.js", () => ({
160
- getWorkspaceSkillsDir: () => "/tmp/test-skills",
161
- }));
159
+ mock.module("../util/platform.js", () => {
160
+ const stub = () => "/tmp/test-stub";
161
+ return {
162
+ getWorkspaceSkillsDir: () => "/tmp/test-skills",
163
+ vellumRoot: stub,
164
+ isMacOS: () => false,
165
+ isLinux: () => true,
166
+ isWindows: () => false,
167
+ getPlatformName: () => "linux",
168
+ normalizeAssistantId: (id: string) => id,
169
+ getDataDir: stub,
170
+ getEmbeddingModelsDir: stub,
171
+ getSandboxRootDir: stub,
172
+ getSandboxWorkingDir: stub,
173
+ getSoundsDir: stub,
174
+ getAvatarDir: stub,
175
+ AVATAR_IMAGE_FILENAME: "avatar-image.png",
176
+ getAvatarImagePath: stub,
177
+ getXdgVellumConfigDirName: () => ".vellum",
178
+ getPidPath: stub,
179
+ getDbPath: stub,
180
+ getLogsDir: stub,
181
+ getHistoryPath: stub,
182
+ getProtectedDir: stub,
183
+ getSignalsDir: stub,
184
+ getDaemonStderrLogPath: stub,
185
+ getDaemonStartupLockPath: stub,
186
+ getExternalDir: stub,
187
+ getBinDir: stub,
188
+ getDotEnvPath: stub,
189
+ getEmbedWorkerPidPath: stub,
190
+ getWorkspaceDir: stub,
191
+ getWorkspaceDirDisplay: stub,
192
+ getWorkspaceConfigPath: stub,
193
+ getWorkspaceHooksDir: stub,
194
+ getWorkspacePluginsDir: stub,
195
+ getWorkspaceRoutesDir: stub,
196
+ getDeprecatedDir: stub,
197
+ getConversationsDir: stub,
198
+ getWorkspacePromptPath: stub,
199
+ getProfilerRootDir: stub,
200
+ getProfilerRunsDir: stub,
201
+ getProfilerRunDir: stub,
202
+ getSkillRuntimePath: stub,
203
+ getBundledBunPath: () => undefined,
204
+ ensureDataDir: () => {},
205
+ };
206
+ });
162
207
 
163
208
  mock.module("../daemon/handlers/shared.js", () => ({
164
209
  CONFIG_RELOAD_DEBOUNCE_MS: 100,
@@ -92,6 +92,7 @@ mock.module("../runtime/pending-interactions.js", () => ({
92
92
 
93
93
  mock.module("../memory/conversation-crud.js", () => ({
94
94
  addMessage: mock(async () => ({ id: "persisted-message-id" })),
95
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
95
96
  }));
96
97
 
97
98
  mock.module("../config/loader.js", () => ({
@@ -74,6 +74,7 @@ mock.module("../memory/conversation-crud.js", () => ({
74
74
  // addMessage. Disk-pressure short-circuits before addMessage ever runs,
75
75
  // but the mock module must still expose every name the real module does.
76
76
  addMessage: () => Promise.resolve({ id: "mock-msg-id" }),
77
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
77
78
  }));
78
79
 
79
80
  const mockProcessMessage = mock(() => Promise.resolve({ messageId: "msg-1" }));
@@ -159,6 +159,7 @@ mock.module("../memory/conversation-crud.js", () => ({
159
159
  createdConversations.push(opts);
160
160
  return { id: `conv-${++conversationIdCounter}`, ...opts };
161
161
  },
162
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
162
163
  }));
163
164
 
164
165
  // Mock logger — capture warn calls for unreachable-credential assertions