@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
@@ -27,6 +27,7 @@ mock.module("../memory/conversation-crud.js", () => ({
27
27
  getConversationOriginChannel: () => null,
28
28
  getMessages: () => null,
29
29
  createConversation: () => ({ id: "mock-conv" }),
30
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
30
31
  }));
31
32
 
32
33
  /**
@@ -37,11 +38,11 @@ const capturedMessages: string[] = [];
37
38
 
38
39
  mock.module("../daemon/conversation-store.js", () => ({
39
40
  findConversation: (_id: string) => ({
40
- enqueueMessage: (content: string) => {
41
- capturedMessages.push(content);
41
+ enqueueMessage: (options: { content: string }) => {
42
+ capturedMessages.push(options.content);
42
43
  return { queued: true };
43
44
  },
44
- persistUserMessage: async () => "mock-msg",
45
+ persistUserMessage: async () => ({ id: "mock-msg", deduplicated: false }),
45
46
  runAgentLoop: async () => {},
46
47
  }),
47
48
  addConversation: () => {},
@@ -106,7 +107,7 @@ function injectSubagent(
106
107
  sendToClient: () => {},
107
108
  usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
108
109
  enqueueMessage: () => ({ queued: false }),
109
- persistUserMessage: () => "msg-1",
110
+ persistUserMessage: async () => ({ id: "msg-1", deduplicated: false }),
110
111
  runAgentLoop: async () => {},
111
112
  };
112
113
  internals.subagents.set(subagentId, {
@@ -26,6 +26,7 @@ mock.module("../memory/conversation-crud.js", () => ({
26
26
  getConversationOriginChannel: () => null,
27
27
  getMessages: () => null,
28
28
  createConversation: () => ({ id: "mock-conv" }),
29
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
29
30
  }));
30
31
 
31
32
  import {
@@ -318,6 +319,63 @@ describe("subagent_spawn fork parameter", () => {
318
319
  );
319
320
  });
320
321
 
322
+ test("threads context.toolUseId into config as parentToolUseId", async () => {
323
+ const manager = getSubagentManager();
324
+ const originalSpawn = manager.spawn.bind(manager);
325
+
326
+ let capturedConfig: Record<string, unknown> | undefined;
327
+ manager.spawn = async (config: Record<string, unknown>) => {
328
+ capturedConfig = config;
329
+ return "tool-use-id-subagent";
330
+ };
331
+
332
+ try {
333
+ const result = await executeSubagentSpawn(
334
+ {
335
+ label: "Stamped task",
336
+ objective: "Do something",
337
+ },
338
+ makeContext("tool-use-conv", {
339
+ sendToClient: () => {},
340
+ toolUseId: "toolu_123",
341
+ }),
342
+ );
343
+
344
+ expect(result.isError).toBe(false);
345
+ expect(capturedConfig).toBeDefined();
346
+ expect(capturedConfig!.parentToolUseId).toBe("toolu_123");
347
+ } finally {
348
+ manager.spawn = originalSpawn;
349
+ }
350
+ });
351
+
352
+ test("omits parentToolUseId when no toolUseId is in context", async () => {
353
+ const manager = getSubagentManager();
354
+ const originalSpawn = manager.spawn.bind(manager);
355
+
356
+ let capturedConfig: Record<string, unknown> | undefined;
357
+ manager.spawn = async (config: Record<string, unknown>) => {
358
+ capturedConfig = config;
359
+ return "no-tool-use-id-subagent";
360
+ };
361
+
362
+ try {
363
+ const result = await executeSubagentSpawn(
364
+ {
365
+ label: "Unstamped task",
366
+ objective: "Do something",
367
+ },
368
+ makeContext("no-tool-use-conv", { sendToClient: () => {} }),
369
+ );
370
+
371
+ expect(result.isError).toBe(false);
372
+ expect(capturedConfig).toBeDefined();
373
+ expect(capturedConfig!.parentToolUseId).toBeUndefined();
374
+ } finally {
375
+ manager.spawn = originalSpawn;
376
+ }
377
+ });
378
+
321
379
  test("fork: true shallow copies parent messages", async () => {
322
380
  const manager = getSubagentManager();
323
381
  const originalSpawn = manager.spawn.bind(manager);
@@ -30,6 +30,7 @@ mock.module("../memory/conversation-crud.js", () => ({
30
30
  getConversationOriginChannel: () => null,
31
31
  getMessages: (conversationId: string) => mockGetMessages(conversationId),
32
32
  createConversation: () => ({ id: "mock-conv" }),
33
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
33
34
  }));
34
35
 
35
36
  import { getSubagentManager } from "../subagent/index.js";
@@ -97,7 +98,7 @@ function injectSubagent(
97
98
  sendToClient: () => {},
98
99
  usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
99
100
  enqueueMessage: () => ({ queued: false }),
100
- persistUserMessage: () => "msg-1",
101
+ persistUserMessage: async () => ({ id: "msg-1", deduplicated: false }),
101
102
  runAgentLoop: async () => {},
102
103
  };
103
104
  internals.subagents.set(subagentId, {
@@ -52,6 +52,7 @@ const mockGetMessages = mock((_conversationId: string) => [
52
52
  mock.module("../memory/conversation-crud.js", () => ({
53
53
  getMessages: mockGetMessages,
54
54
  getConversation: (_id: string) => null,
55
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
55
56
  }));
56
57
 
57
58
  const mockGetConfiguredProvider = mock(async () => ({
@@ -171,6 +171,44 @@ describe("buildSystemPrompt", () => {
171
171
  expect(result).toContain("# Soul\n\nBe thoughtful.");
172
172
  });
173
173
 
174
+ test("gates off the unmodified bundled IDENTITY.md template when no BOOTSTRAP.md is present", () => {
175
+ // Regression: the seeded IDENTITY.md ships with `_`-comment lines, so
176
+ // the raw workspace body never equals the comment-stripped bundled
177
+ // template. `isTemplateContent` must comment-strip BOTH sides — otherwise
178
+ // detection fails and the `08-identity` transform leaks the blank
179
+ // template scaffolding into every fresh post-onboarding prompt.
180
+ const bundledIdentity = readFileSync(
181
+ join(import.meta.dirname, "..", "prompts", "templates", "IDENTITY.md"),
182
+ "utf-8",
183
+ );
184
+ writeFileSync(join(TEST_DIR, "IDENTITY.md"), bundledIdentity);
185
+ writeFileSync(join(TEST_DIR, "SOUL.md"), "# Soul\n\nBe thoughtful.");
186
+ const result = buildSystemPrompt();
187
+ // SOUL still renders; the template scaffolding must not.
188
+ expect(result).toContain("# Soul\n\nBe thoughtful.");
189
+ expect(result).not.toContain("(not yet chosen)");
190
+ });
191
+
192
+ test("includes the unmodified bundled IDENTITY.md template during bootstrap", () => {
193
+ // The mirror case: when BOOTSTRAP.md is active the template is included
194
+ // verbatim so the model can see the field structure and produce a valid
195
+ // file_write on the first turn.
196
+ const bundledIdentity = readFileSync(
197
+ join(import.meta.dirname, "..", "prompts", "templates", "IDENTITY.md"),
198
+ "utf-8",
199
+ );
200
+ writeFileSync(join(TEST_DIR, "IDENTITY.md"), bundledIdentity);
201
+ writeFileSync(
202
+ join(TEST_DIR, "BOOTSTRAP.md"),
203
+ "# First run\n\nGet started.",
204
+ );
205
+ const result = buildSystemPrompt();
206
+ // The bundled template's placeholder scaffolding (the `(not yet chosen)`
207
+ // field markers) renders verbatim during bootstrap — the mirror of the
208
+ // gating test above, which asserts it is absent without BOOTSTRAP.md.
209
+ expect(result).toContain("(not yet chosen)");
210
+ });
211
+
174
212
  test("trims whitespace from file content", () => {
175
213
  writeFileSync(join(TEST_DIR, "SOUL.md"), "\n Be kind \n\n");
176
214
  const result = buildSystemPrompt();
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Test preload verifier — runs immediately after test-preload.ts.
3
+ *
4
+ * Bun already exits with code 1 when a preload throws, has an unresolvable
5
+ * import, or doesn't exist on disk (verified empirically). This file is
6
+ * the explicit check for the remaining failure mode: the main preload
7
+ * ran successfully but didn't actually isolate workspace state (e.g. a
8
+ * future refactor of test-preload.ts introduces a logic bug that no-ops
9
+ * the env override).
10
+ *
11
+ * Without this verifier, that failure mode would silently let tests
12
+ * resolve `VELLUM_WORKSPACE_DIR` to the real `/workspace` and potentially
13
+ * destroy live data.
14
+ *
15
+ * Order matters: bunfig.toml lists this AFTER test-preload.ts so the
16
+ * main preload's env writes are observable here.
17
+ *
18
+ * No source-module imports — only node stdlib (matches the same rule
19
+ * test-preload.ts enforces).
20
+ *
21
+ * Positive assertion: `VELLUM_WORKSPACE_DIR` MUST resolve under
22
+ * `os.tmpdir()`. This mirrors exactly what test-preload.ts does
23
+ * (`mkdtempSync(join(tmpdir(), "vellum-test-"))`), so any other value
24
+ * indicates the preload either didn't run or didn't take effect.
25
+ */
26
+
27
+ import { realpathSync } from "node:fs";
28
+ import { tmpdir } from "node:os";
29
+ import { sep } from "node:path";
30
+
31
+ function fail(reason: string): never {
32
+ throw new Error(
33
+ [
34
+ `Test preload verifier failed: ${reason}.`,
35
+ "",
36
+ "test-preload.ts ran but did not isolate workspace state. Common",
37
+ "causes:",
38
+ " - a logic bug in test-preload.ts (env override removed/reordered)",
39
+ " - a preload phase threw silently and the verifier still picked up",
40
+ " partial state",
41
+ ].join("\n"),
42
+ );
43
+ }
44
+
45
+ function canonicalize(p: string): string {
46
+ try {
47
+ return realpathSync(p);
48
+ } catch {
49
+ return p;
50
+ }
51
+ }
52
+
53
+ const workspaceDir = process.env.VELLUM_WORKSPACE_DIR?.trim();
54
+ if (!workspaceDir) {
55
+ fail("VELLUM_WORKSPACE_DIR is not set after main preload");
56
+ }
57
+
58
+ const resolvedWorkspace = canonicalize(workspaceDir);
59
+ const tmpRoot = canonicalize(tmpdir());
60
+
61
+ if (
62
+ resolvedWorkspace !== tmpRoot &&
63
+ !resolvedWorkspace.startsWith(tmpRoot + sep)
64
+ ) {
65
+ fail(
66
+ `VELLUM_WORKSPACE_DIR resolves to ${resolvedWorkspace}, which is not under ${tmpRoot}`,
67
+ );
68
+ }
@@ -1,56 +1,42 @@
1
1
  /**
2
2
  * Shared test preload — runs before every test file.
3
3
  *
4
- * Creates a per-file temporary directory and sets VELLUM_WORKSPACE_DIR so that
5
- * all workspace-derived helpers (getDataDir, getDbPath, getConversationsDir, …)
6
- * resolve under the temp dir instead of the real ~/.vellum/workspace.
7
- *
8
- * Individual test files can retrieve the workspace dir via getWorkspaceDir()
9
- * from platform.ts, or directly from process.env.VELLUM_WORKSPACE_DIR.
4
+ * Creates a per-process temporary directory and sets VELLUM_WORKSPACE_DIR so
5
+ * that all workspace-derived helpers (getDataDir, getDbPath,
6
+ * getConversationsDir, getProtectedDir, …) resolve under the temp dir
7
+ * instead of the real $VELLUM_WORKSPACE_DIR.
10
8
  *
11
9
  * Cleanup: the temp dir is removed after all tests in the file complete.
10
+ *
11
+ * No source-module imports
12
+ * ------------------------
13
+ * The only static imports in this file are node stdlib (`node:fs`,
14
+ * `node:os`, `node:path`), `bun:test`, and helpers in this same directory.
15
+ * Importing from the assistant directly runs the risk of triggering import
16
+ * time side effects and import from node modules that may not exist in
17
+ * some environments.
12
18
  */
13
19
 
14
- import { mkdtempSync, realpathSync, rmSync } from "node:fs";
20
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
15
21
  import { tmpdir } from "node:os";
16
22
  import { join } from "node:path";
17
23
  import { afterAll } from "bun:test";
18
24
 
19
- import { installGatewayIpcMock } from "../__tests__/mock-gateway-ipc.js";
20
- import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
21
- import { resetDb } from "../memory/db-connection.js";
22
- import { _setStorePath } from "../security/encrypted-store.js";
25
+ import { installGatewayIpcMock } from "./mock-gateway-ipc.js";
23
26
 
24
- const testDir = realpathSync(
25
- mkdtempSync(join(tmpdir(), "vellum-test-workspace-")),
26
- );
27
+ // --- Phase 1: env override (zero source-module imports above this point) ---
28
+
29
+ // Layout: <tmpRoot>/workspace as VELLUM_WORKSPACE_DIR. The parent of
30
+ // VELLUM_WORKSPACE_DIR is what `vellumRoot()` resolves to, so a separate
31
+ // tmpRoot per process gives `getProtectedDir()` and friends per-process
32
+ // isolation without needing an explicit `setStorePathForTesting()`.
33
+ const tmpRoot = realpathSync(mkdtempSync(join(tmpdir(), "vellum-test-")));
34
+ const testDir = join(tmpRoot, "workspace");
35
+ mkdirSync(testDir);
27
36
  process.env.VELLUM_WORKSPACE_DIR = testDir;
28
37
  process.env.VELLUM_PLATFORM_URL = "https://test-platform.vellum.ai";
29
38
  process.exitCode = 0;
30
39
 
31
- // Isolate the encrypted credential store per test file. Without this,
32
- // parallel test processes all read/write the same ~/.vellum/protected/keys.enc,
33
- // causing races when one file deletes a key while another sets it.
34
- _setStorePath(join(testDir, "keys.enc"));
35
-
36
- // Mock gateway IPC so no test accidentally connects to a real gateway socket.
37
- // Tests that need to control IPC responses use mockGatewayIpc() / resetMockGatewayIpc().
38
- installGatewayIpcMock();
39
-
40
- // Pre-populate the feature-flag override cache so `initFeatureFlagOverrides()`
41
- // short-circuits its retry loop — there is no real gateway in tests, and the
42
- // retry backoff would otherwise exceed the per-test timeout for any test that
43
- // builds the CLI program. Tests exercising the retry behavior call
44
- // `clearFeatureFlagOverridesCache()` first.
45
- _setOverridesForTesting({});
46
-
47
- // Force-close any DB connection inherited from the parent process (e.g. when
48
- // the test runner is spawned by the running assistant via a pre-push hook).
49
- // Without this, the db singleton in db-connection.ts may still point at the
50
- // real ~/.vellum/workspace database, and test cleanup (DELETE FROM …) would
51
- // wipe production data — contacts, channels, credentials, etc.
52
- resetDb();
53
-
54
40
  // Prevent tests from routing credential writes through the real CES
55
41
  // (Credential Execution Service). Without this, setSecureKeyAsync() in
56
42
  // containerized environments writes to the live credential store.
@@ -59,8 +45,15 @@ const savedCesCredentialUrl = process.env.CES_CREDENTIAL_URL;
59
45
  delete process.env.IS_CONTAINERIZED;
60
46
  delete process.env.CES_CREDENTIAL_URL;
61
47
 
48
+ // --- Phase 2: install the IPC mock (no source-module imports) ---
49
+
50
+ // Mock gateway IPC so no test accidentally connects to a real gateway socket.
51
+ // The mock returns a sentinel for `get_feature_flags` to short-circuit the
52
+ // retry loop in `initFeatureFlagOverrides()`. Tests that need specific IPC
53
+ // responses use `mockGatewayIpc()` / `resetMockGatewayIpc()`.
54
+ installGatewayIpcMock();
55
+
62
56
  afterAll(() => {
63
- resetDb();
64
57
  process.exitCode = 0;
65
58
  delete process.env.VELLUM_WORKSPACE_DIR;
66
59
  delete process.env.VELLUM_PLATFORM_URL;
@@ -71,7 +64,7 @@ afterAll(() => {
71
64
  process.env.CES_CREDENTIAL_URL = savedCesCredentialUrl;
72
65
  }
73
66
  try {
74
- rmSync(testDir, { recursive: true, force: true });
67
+ rmSync(tmpRoot, { recursive: true, force: true });
75
68
  } catch {
76
69
  /* best-effort cleanup */
77
70
  }
@@ -72,6 +72,7 @@ mock.module("../permissions/checker.js", () => ({
72
72
 
73
73
  mock.module("../memory/conversation-crud.js", () => ({
74
74
  createConversation: (title: string) => ({ id: "conversation-1", title }),
75
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
75
76
  }));
76
77
 
77
78
  // Mock every export so downstream test files that dynamically import modules
@@ -85,15 +86,16 @@ mock.module("../memory/tool-usage-store.js", () => ({
85
86
  mock.module("../tools/registry.js", () => ({
86
87
  getTool: (name: string) => {
87
88
  if (name === "unknown_tool") return undefined;
88
- // Skill tools carry origin and executionTarget from their manifest
89
+ // Skill tools carry executionTarget from their manifest. Ownership lives
90
+ // on the registry (mocked below via getToolOwner reading the override's
91
+ // owner field), so it doesn't appear on the Tool object itself.
89
92
  if (name === "skill_host_tool") {
90
93
  return {
91
94
  name,
92
95
  description: "skill host tool",
93
96
  category: "skill",
94
97
  defaultRiskLevel: "low",
95
- origin: "skill" as const,
96
- ownerSkillId: "test-skill",
98
+ owner: { kind: "skill", id: "test-skill" },
97
99
  executionTarget: "host" as const,
98
100
  input_schema: {},
99
101
  execute: async () => {
@@ -108,8 +110,7 @@ mock.module("../tools/registry.js", () => ({
108
110
  description: "skill sandbox tool",
109
111
  category: "skill",
110
112
  defaultRiskLevel: "low",
111
- origin: "skill" as const,
112
- ownerSkillId: "test-skill",
113
+ owner: { kind: "skill", id: "test-skill" },
113
114
  executionTarget: "sandbox" as const,
114
115
  input_schema: {},
115
116
  execute: async () => {
@@ -126,8 +127,7 @@ mock.module("../tools/registry.js", () => ({
126
127
  description: "skill tool with host_ prefix but sandbox target",
127
128
  category: "skill",
128
129
  defaultRiskLevel: "low",
129
- origin: "skill" as const,
130
- ownerSkillId: "test-skill",
130
+ owner: { kind: "skill", id: "test-skill" },
131
131
  executionTarget: "sandbox" as const,
132
132
  input_schema: {},
133
133
  execute: async () => {
@@ -158,6 +158,19 @@ mock.module("../tools/registry.js", () => ({
158
158
  },
159
159
  };
160
160
  },
161
+ // Ownership lives on the registry post-refactor. Mirror that by surfacing
162
+ // the optional `owner`-shaped field set inline on the override-produced
163
+ // tool (see the skill_* branches above).
164
+ getToolOwner: (name: string) => {
165
+ if (
166
+ name === "skill_host_tool" ||
167
+ name === "skill_sandbox_tool" ||
168
+ name === "host_skill_sandboxed"
169
+ ) {
170
+ return { kind: "skill" as const, id: "test-skill" };
171
+ }
172
+ return undefined;
173
+ },
161
174
  }));
162
175
 
163
176
  mock.module("../tools/shared/filesystem/path-policy.js", () => ({
@@ -148,6 +148,16 @@ mock.module("../tools/registry.js", () => ({
148
148
  };
149
149
  },
150
150
  getAllTools: () => (getAllToolsOverride ? getAllToolsOverride() : []),
151
+ // Ownership lives on the registry post-refactor; production reads it via
152
+ // getToolOwner(name) rather than a field on the Tool object. Mirror that by
153
+ // surfacing the optional `owner`-shaped field from the override-produced
154
+ // tool so existing tests can encode owner inline.
155
+ getToolOwner: (name: string) => {
156
+ const t = getToolOverride?.(name) as
157
+ | { owner?: { kind: "skill" | "mcp" | "plugin"; id: string } }
158
+ | undefined;
159
+ return t?.owner;
160
+ },
151
161
  }));
152
162
 
153
163
  mock.module("../tools/shared/filesystem/path-policy.js", () => ({
@@ -224,6 +234,48 @@ describe("ToolExecutor allowedToolNames gating", () => {
224
234
  expect(result.content).toBe("ok");
225
235
  });
226
236
 
237
+ test("canonicalizes legacy computer_use_press_key alias before active-tool gating", async () => {
238
+ const executor = new ToolExecutor(makePrompter());
239
+ const allowed = new Set(["computer_use_key"]);
240
+ const result = await executor.execute(
241
+ "computer_use_press_key",
242
+ {
243
+ key: "Space",
244
+ modifiers: ["Command"],
245
+ reasoning: "Open Spotlight",
246
+ },
247
+ makeContext({ allowedToolNames: allowed }),
248
+ );
249
+
250
+ expect(result.isError).toBe(false);
251
+ expect(result.content).toBe("ok");
252
+ expect(lastCheckArgs?.toolName).toBe("computer_use_key");
253
+ expect(lastCheckArgs?.input).toEqual({
254
+ key: "cmd+space",
255
+ reasoning: "Open Spotlight",
256
+ });
257
+ });
258
+
259
+ test("lowercases legacy computer_use_press_key key-only input", async () => {
260
+ const executor = new ToolExecutor(makePrompter());
261
+ const allowed = new Set(["computer_use_key"]);
262
+ const result = await executor.execute(
263
+ "computer_use_press_key",
264
+ {
265
+ key: "Space",
266
+ reasoning: "Pause media",
267
+ },
268
+ makeContext({ allowedToolNames: allowed }),
269
+ );
270
+
271
+ expect(result.isError).toBe(false);
272
+ expect(lastCheckArgs?.toolName).toBe("computer_use_key");
273
+ expect(lastCheckArgs?.input).toEqual({
274
+ key: "space",
275
+ reasoning: "Pause media",
276
+ });
277
+ });
278
+
227
279
  test("preserves exact active create_app tool before applying compatibility aliases", async () => {
228
280
  const executor = new ToolExecutor(makePrompter());
229
281
  const allowed = new Set(["create_app", "app_create"]);
@@ -334,8 +386,7 @@ describe("ToolExecutor allowedToolNames gating", () => {
334
386
  category: "skill",
335
387
  defaultRiskLevel: RiskLevel.Low,
336
388
  executionTarget: "sandbox" as const,
337
- origin: "skill" as const,
338
- ownerSkillId: "my-skill",
389
+ owner: { kind: "skill", id: "my-skill" },
339
390
  input_schema: { type: "object" as const, properties: {} },
340
391
  execute: async () => fakeToolResult,
341
392
  };
@@ -371,9 +422,7 @@ describe("ToolExecutor policy context plumbing", () => {
371
422
  description: "skill tool",
372
423
  category: "skill",
373
424
  defaultRiskLevel: RiskLevel.Low,
374
- origin: "skill" as const,
375
- ownerSkillId: "my-skill-123",
376
- ownerSkillVersionHash: "abc123hash",
425
+ owner: { kind: "skill", id: "my-skill-123" },
377
426
  executionTarget: "sandbox" as const,
378
427
  input_schema: { type: "object" as const, properties: {} },
379
428
  execute: async () => fakeToolResult,
@@ -424,7 +473,6 @@ describe("ToolExecutor policy context plumbing", () => {
424
473
  category: "core",
425
474
  defaultRiskLevel: RiskLevel.Low,
426
475
  executionTarget: "sandbox" as const,
427
- origin: "core" as const,
428
476
  input_schema: { type: "object" as const, properties: {} },
429
477
  execute: async () => fakeToolResult,
430
478
  };
@@ -453,9 +501,7 @@ describe("ToolExecutor policy context plumbing", () => {
453
501
  description: "host skill tool",
454
502
  category: "skill",
455
503
  defaultRiskLevel: RiskLevel.Low,
456
- origin: "skill" as const,
457
- ownerSkillId: "host-skill",
458
- ownerSkillVersionHash: "host-hash",
504
+ owner: { kind: "skill", id: "host-skill" },
459
505
  executionTarget: "host" as const,
460
506
  input_schema: { type: "object" as const, properties: {} },
461
507
  execute: async () => fakeToolResult,
@@ -477,7 +523,6 @@ describe("ToolExecutor policy context plumbing", () => {
477
523
  executionTarget: "host",
478
524
  });
479
525
  });
480
-
481
526
  });
482
527
 
483
528
  // ---------------------------------------------------------------------------
@@ -45,6 +45,7 @@ mock.module("../memory/conversation-crud.js", () => ({
45
45
  getMessageById: () => null,
46
46
  updateMessageContent: () => {},
47
47
  provenanceFromTrustContext: () => ({}),
48
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
48
49
  }));
49
50
 
50
51
  mock.module("../memory/llm-request-log-store.js", () => ({
@@ -44,6 +44,7 @@ mock.module("../memory/conversation-crud.js", () => ({
44
44
  getMessageById: () => null,
45
45
  updateMessageContent: () => {},
46
46
  provenanceFromTrustContext: () => ({}),
47
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
47
48
  }));
48
49
 
49
50
  mock.module("../memory/llm-request-log-store.js", () => ({
@@ -337,7 +337,7 @@ import {
337
337
  handleVoiceWebhook,
338
338
  } from "../calls/twilio-routes.js";
339
339
  import { DEFAULT_ELEVENLABS_VOICE_ID } from "../config/schemas/elevenlabs.js";
340
- import { getDb, resetDb } from "../memory/db-connection.js";
340
+ import { getDb } from "../memory/db-connection.js";
341
341
  import { initializeDb } from "../memory/db-init.js";
342
342
  import { conversations } from "../memory/schema.js";
343
343
  import {
@@ -347,6 +347,7 @@ import {
347
347
  handleSetTwilioCredentials,
348
348
  } from "../runtime/routes/integrations/twilio.js";
349
349
  import { credentialKey } from "../security/credential-key.js";
350
+ import { resetDbForTesting } from "./db-test-helpers.js";
350
351
 
351
352
  initializeDb();
352
353
 
@@ -487,7 +488,7 @@ describe("twilio webhook routes", () => {
487
488
 
488
489
  afterAll(() => {
489
490
  globalThis.fetch = originalFetch;
490
- resetDb();
491
+ resetDbForTesting();
491
492
  });
492
493
 
493
494
  // ── Callback idempotency / replay tests ───────────────────────────