@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.8.5",
3
+ "version": "0.8.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -29,7 +29,7 @@ mock.module("../config/env.js", () => ({
29
29
  checkUnrecognizedEnvVars: () => {},
30
30
  }));
31
31
 
32
- import { getDb, resetDb } from "../memory/db-connection.js";
32
+ import { getDb } from "../memory/db-connection.js";
33
33
  import { initializeDb } from "../memory/db-init.js";
34
34
  import { resetExternalAssistantIdCache } from "../runtime/auth/external-assistant-id.js";
35
35
  import { initAuthSigningKey } from "../runtime/auth/token-service.js";
@@ -37,6 +37,7 @@ import {
37
37
  resolveLocalAuthContext,
38
38
  resolveLocalTrustContext,
39
39
  } from "../runtime/local-actor-identity.js";
40
+ import { resetDbForTesting } from "./db-test-helpers.js";
40
41
 
41
42
  // ---------------------------------------------------------------------------
42
43
  // Test signing key
@@ -50,7 +51,7 @@ initializeDb();
50
51
  beforeEach(() => {
51
52
  initAuthSigningKey(TEST_KEY);
52
53
  resetExternalAssistantIdCache();
53
- resetDb();
54
+ resetDbForTesting();
54
55
  initializeDb();
55
56
  });
56
57
 
@@ -21,7 +21,7 @@ import type {
21
21
  CheckpointDecision,
22
22
  CheckpointInfo,
23
23
  } from "../agent/loop.js";
24
- import { AgentLoop } from "../agent/loop.js";
24
+ import { AgentLoop, isMaxTokensStopReason } from "../agent/loop.js";
25
25
  import type {
26
26
  Message,
27
27
  Provider,
@@ -63,6 +63,18 @@ function textResponse(text: string): ProviderResponse {
63
63
  };
64
64
  }
65
65
 
66
+ function maxTokensResponse(
67
+ text: string,
68
+ stopReason: string = "max_tokens",
69
+ ): ProviderResponse {
70
+ return {
71
+ content: [{ type: "text", text }],
72
+ model: "mock-model",
73
+ usage: { inputTokens: 10, outputTokens: 5 },
74
+ stopReason,
75
+ };
76
+ }
77
+
66
78
  function toolUseResponse(
67
79
  id: string,
68
80
  name: string,
@@ -107,6 +119,15 @@ function countExitEvents(events: AgentEvent[]): number {
107
119
  // ---------------------------------------------------------------------------
108
120
 
109
121
  describe("AgentLoop exit-reason instrumentation", () => {
122
+ test("recognizes provider output-token stop reasons", () => {
123
+ expect(isMaxTokensStopReason("max_tokens")).toBe(true);
124
+ expect(isMaxTokensStopReason("MAX_TOKENS")).toBe(true);
125
+ expect(isMaxTokensStopReason("length")).toBe(true);
126
+ expect(isMaxTokensStopReason("max_output_tokens")).toBe(true);
127
+ expect(isMaxTokensStopReason("end_turn")).toBe(false);
128
+ expect(isMaxTokensStopReason(undefined)).toBe(false);
129
+ });
130
+
110
131
  test("emits exit event exactly once with 'no_tool_calls' on plain text response", async () => {
111
132
  const { provider } = createMockProvider([textResponse("Hi there!")]);
112
133
  const loop = new AgentLoop(provider, "system prompt");
@@ -134,6 +155,59 @@ describe("AgentLoop exit-reason instrumentation", () => {
134
155
  expect(events[events.length - 1].type).toBe("agent_loop_exit");
135
156
  });
136
157
 
158
+ test("emits continuation surface event and exits on max_tokens", async () => {
159
+ const { provider } = createMockProvider([
160
+ maxTokensResponse("Partial answer"),
161
+ ]);
162
+ const loop = new AgentLoop(provider, "system prompt");
163
+
164
+ const events: AgentEvent[] = [];
165
+ await loop.run([userMessage], (e) => {
166
+ events.push(e);
167
+ });
168
+
169
+ expect(events.map((e) => e.type)).toEqual([
170
+ "llm_call_started",
171
+ "usage",
172
+ "max_tokens_reached",
173
+ "message_complete",
174
+ "agent_loop_exit",
175
+ ]);
176
+ expect(countExitEvents(events)).toBe(1);
177
+ expect(lastExitEvent(events)?.reason).toBe("max_tokens_reached");
178
+ });
179
+
180
+ test("does not persist unexecuted tool_use blocks when max_tokens stops output", async () => {
181
+ const { provider } = createMockProvider([
182
+ {
183
+ content: [
184
+ { type: "text", text: "I need to check that." },
185
+ {
186
+ type: "tool_use",
187
+ id: "tool-1",
188
+ name: "read_file",
189
+ input: { path: "/tmp/example.txt" },
190
+ },
191
+ ],
192
+ model: "mock-model",
193
+ usage: { inputTokens: 10, outputTokens: 5 },
194
+ stopReason: "max_tokens",
195
+ },
196
+ ]);
197
+ const loop = new AgentLoop(provider, "system prompt", {}, dummyTools);
198
+
199
+ const events: AgentEvent[] = [];
200
+ const result = await loop.run([userMessage], (e) => {
201
+ events.push(e);
202
+ });
203
+
204
+ expect(events.some((e) => e.type === "tool_use")).toBe(false);
205
+ expect(lastExitEvent(events)?.reason).toBe("max_tokens_reached");
206
+ expect(result[result.length - 1]!.content).toEqual([
207
+ { type: "text", text: "I need to check that." },
208
+ ]);
209
+ });
210
+
137
211
  test("emits 'aborted_pre_call' when signal is already aborted at run start", async () => {
138
212
  const { provider } = createMockProvider([textResponse("never sent")]);
139
213
  const loop = new AgentLoop(provider, "system prompt");
@@ -142,7 +216,13 @@ describe("AgentLoop exit-reason instrumentation", () => {
142
216
  controller.abort();
143
217
 
144
218
  const events: AgentEvent[] = [];
145
- await loop.run([userMessage], (e) => { events.push(e); }, controller.signal);
219
+ await loop.run(
220
+ [userMessage],
221
+ (e) => {
222
+ events.push(e);
223
+ },
224
+ controller.signal,
225
+ );
146
226
 
147
227
  expect(countExitEvents(events)).toBe(1);
148
228
  expect(lastExitEvent(events)?.reason).toBe("aborted_pre_call");
@@ -166,7 +246,9 @@ describe("AgentLoop exit-reason instrumentation", () => {
166
246
  );
167
247
 
168
248
  const events: AgentEvent[] = [];
169
- await loop.run([userMessage], (e) => { events.push(e); });
249
+ await loop.run([userMessage], (e) => {
250
+ events.push(e);
251
+ });
170
252
 
171
253
  expect(countExitEvents(events)).toBe(1);
172
254
  expect(lastExitEvent(events)?.reason).toBe("yield_to_user");
@@ -186,13 +268,14 @@ describe("AgentLoop exit-reason instrumentation", () => {
186
268
  toolExecutor,
187
269
  );
188
270
 
189
- const onCheckpoint = (_info: CheckpointInfo): CheckpointDecision =>
190
- "yield";
271
+ const onCheckpoint = (_info: CheckpointInfo): CheckpointDecision => "yield";
191
272
 
192
273
  const events: AgentEvent[] = [];
193
274
  await loop.run(
194
275
  [userMessage],
195
- (e) => { events.push(e); },
276
+ (e) => {
277
+ events.push(e);
278
+ },
196
279
  undefined,
197
280
  undefined,
198
281
  onCheckpoint,
@@ -211,7 +294,9 @@ describe("AgentLoop exit-reason instrumentation", () => {
211
294
  const loop = new AgentLoop(provider, "system prompt");
212
295
 
213
296
  const events: AgentEvent[] = [];
214
- await loop.run([userMessage], (e) => { events.push(e); });
297
+ await loop.run([userMessage], (e) => {
298
+ events.push(e);
299
+ });
215
300
 
216
301
  expect(countExitEvents(events)).toBe(1);
217
302
  expect(lastExitEvent(events)?.reason).toBe("error");
@@ -238,7 +323,9 @@ describe("AgentLoop exit-reason instrumentation", () => {
238
323
  );
239
324
 
240
325
  const events: AgentEvent[] = [];
241
- await loop.run([userMessage], (e) => { events.push(e); });
326
+ await loop.run([userMessage], (e) => {
327
+ events.push(e);
328
+ });
242
329
 
243
330
  expect(countExitEvents(events)).toBe(1);
244
331
  });
@@ -263,7 +350,13 @@ describe("AgentLoop exit-reason instrumentation", () => {
263
350
  );
264
351
 
265
352
  const events: AgentEvent[] = [];
266
- await loop.run([userMessage], (e) => { events.push(e); }, controller.signal);
353
+ await loop.run(
354
+ [userMessage],
355
+ (e) => {
356
+ events.push(e);
357
+ },
358
+ controller.signal,
359
+ );
267
360
 
268
361
  expect(countExitEvents(events)).toBe(1);
269
362
  expect(lastExitEvent(events)?.reason).toBe("aborted_during_tools");
@@ -223,7 +223,7 @@ class FakeConversation {
223
223
  return Promise.resolve();
224
224
  }
225
225
  persistUserMessage() {
226
- return Promise.resolve("msg-id");
226
+ return Promise.resolve({ id: "msg-id", deduplicated: false });
227
227
  }
228
228
  runAgentLoop(
229
229
  _content: string,
@@ -353,6 +353,7 @@ mock.module("../memory/conversation-crud.js", () => ({
353
353
  // Always return undefined for the row read so the test fails fast unless
354
354
  // executeSubagentSpawn reads from context.overrideProfile first.
355
355
  getConversationOverrideProfile: () => undefined,
356
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
356
357
  }));
357
358
 
358
359
  import { getSubagentManager } from "../subagent/index.js";
@@ -22,6 +22,7 @@ import type { Message } from "../providers/types.js";
22
22
 
23
23
  mock.module("../memory/conversation-crud.js", () => ({
24
24
  getConversationOverrideProfile: () => undefined,
25
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
25
26
  }));
26
27
 
27
28
  mock.module("../config/loader.js", () => ({
@@ -32,6 +32,7 @@ let mockOverrideProfile: string | undefined = undefined;
32
32
 
33
33
  mock.module("../memory/conversation-crud.js", () => ({
34
34
  getConversationOverrideProfile: (_id: string) => mockOverrideProfile,
35
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
35
36
  }));
36
37
 
37
38
  // Mutable stub for `getConfig().llm` consumed by `RetryProvider`'s
@@ -14,12 +14,12 @@
14
14
  import { afterAll, describe, expect, test } from "bun:test";
15
15
 
16
16
  import {
17
- buildToolDefinitions,
18
17
  isToolActiveForContext,
19
18
  type SkillProjectionContext,
20
19
  } from "../daemon/conversation-tool-setup.js";
21
20
  import {
22
21
  __resetRegistryForTesting,
22
+ getAllToolDefinitions,
23
23
  initializeTools,
24
24
  } from "../tools/registry.js";
25
25
 
@@ -30,7 +30,7 @@ afterAll(() => {
30
30
  describe("always-loaded tool count", () => {
31
31
  test("should be exactly 11 with recall occupying the existing slot", async () => {
32
32
  await initializeTools();
33
- const allDefs = buildToolDefinitions();
33
+ const allDefs = getAllToolDefinitions();
34
34
 
35
35
  // Minimal context: no client, no capabilities
36
36
  const minimalContext: SkillProjectionContext = {
@@ -57,6 +57,7 @@ mock.module("../memory/conversation-crud.js", () => ({
57
57
  updates.push({ id, content });
58
58
  },
59
59
  provenanceFromTrustContext: () => ({}),
60
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
60
61
  }));
61
62
 
62
63
  mock.module("../memory/llm-request-log-store.js", () => ({
@@ -151,6 +151,7 @@ mock.module("../memory/conversation-crud.js", () => ({
151
151
  addMessage: () => ({ id: `msg-${Date.now()}` }),
152
152
  updateConversationUsage: () => {},
153
153
  updateConversationTitle: () => {},
154
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
154
155
  }));
155
156
 
156
157
  mock.module("../memory/conversation-queries.js", () => ({
@@ -154,13 +154,9 @@ function makeIdleSession(opts?: {
154
154
  let processing = false;
155
155
  return {
156
156
  isProcessing: () => processing,
157
- persistUserMessage: (
158
- _content: string,
159
- _attachments: unknown[],
160
- requestId?: string,
161
- ) => {
157
+ persistUserMessage: (options: { requestId?: string }) => {
162
158
  processing = true;
163
- return requestId ?? "msg-1";
159
+ return { id: options.requestId ?? "msg-1", deduplicated: false };
164
160
  },
165
161
  memoryPolicy: {
166
162
  scopeId: "default",
@@ -223,13 +219,9 @@ function makeConfirmationEmittingSession(opts?: {
223
219
  const tool = opts?.toolName ?? "shell_command";
224
220
  return {
225
221
  isProcessing: () => processing,
226
- persistUserMessage: (
227
- _content: string,
228
- _attachments: unknown[],
229
- requestId?: string,
230
- ) => {
222
+ persistUserMessage: (options: { requestId?: string }) => {
231
223
  processing = true;
232
- return requestId ?? "msg-1";
224
+ return { id: options.requestId ?? "msg-1", deduplicated: false };
233
225
  },
234
226
  memoryPolicy: {
235
227
  scopeId: "default",
@@ -266,7 +258,11 @@ function makeConfirmationEmittingSession(opts?: {
266
258
  input: { command: "ls" },
267
259
  riskLevel: "medium",
268
260
  allowlistOptions: [
269
- { label: "Allow ls", description: "Allow ls command", pattern: "ls" },
261
+ {
262
+ label: "Allow ls",
263
+ description: "Allow ls command",
264
+ pattern: "ls",
265
+ },
270
266
  ],
271
267
  scopeOptions: [{ label: "This conversation", scope: "session" }],
272
268
  persistentDecisionsAllowed: true,
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Test-only utilities for safely interacting with on-disk DB files.
3
+ *
4
+ * Two exports:
5
+ *
6
+ * - `assertNotLiveDb(dbPath)` — per-callsite guard. Throws unless the
7
+ * path resolves under `os.tmpdir()`. Use it before any custom
8
+ * destructive operation not covered by `removeTestDbFiles`.
9
+ * - `removeTestDbFiles(dbPath)` — guarded `rmSync` of the DB file and
10
+ * its sibling `-shm` / `-wal` files. The standard way to clear a
11
+ * test DB between migrations.
12
+ *
13
+ * Defence-in-depth complement to the test-preload-verifier
14
+ * (`os.tmpdir()` containment check during preload). If a future bug
15
+ * lets a test see a non-tmpdir path despite the preload guard, every
16
+ * destructive call still has its own line of defence.
17
+ *
18
+ * Positive assertion: the path MUST resolve under `os.tmpdir()`. Same
19
+ * shape as the preload verifier — no enumeration of "real" paths,
20
+ * works across every deployment.
21
+ *
22
+ * No source-module imports — only node stdlib. Helpers in `__tests__/`
23
+ * must not import from `src/` (see assistant/AGENTS.md). Callers pass
24
+ * the resolved `dbPath` in so this file stays decoupled from
25
+ * `src/util/platform.ts`.
26
+ */
27
+
28
+ import { realpathSync, rmSync } from "node:fs";
29
+ import { tmpdir } from "node:os";
30
+ import { dirname, sep } from "node:path";
31
+
32
+ function canonicalize(p: string): string {
33
+ try {
34
+ return realpathSync(p);
35
+ } catch {
36
+ // realpathSync throws if the path doesn't exist. Fall back to the
37
+ // parent directory — which must exist for any rmSync target that's
38
+ // about to be touched. Returning the parent path here lets the
39
+ // containment check succeed in the common "file already deleted"
40
+ // case (rmSync with `force: true`) without falsely failing.
41
+ try {
42
+ return realpathSync(dirname(p));
43
+ } catch {
44
+ return p;
45
+ }
46
+ }
47
+ }
48
+
49
+ export function assertNotLiveDb(dbPath: string): void {
50
+ const resolved = canonicalize(dbPath);
51
+ const tmpRoot = canonicalize(tmpdir());
52
+ if (resolved !== tmpRoot && !resolved.startsWith(tmpRoot + sep)) {
53
+ throw new Error(
54
+ [
55
+ `assertNotLiveDb refused: ${dbPath} resolves to ${resolved}, which is not under ${tmpRoot}.`,
56
+ "",
57
+ "Test code must never destructively touch a DB path outside the",
58
+ "process temp directory. If you're seeing this in a passing test,",
59
+ "the test-preload likely didn't run (the verifier should have",
60
+ "caught that already — investigate why it didn't).",
61
+ ].join("\n"),
62
+ );
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Remove a SQLite DB file and its sibling `-shm` / `-wal` files. Always
68
+ * `force: true` (no error if the file doesn't exist yet). Guarded by
69
+ * `assertNotLiveDb` so a test can never destroy a non-tmpdir path.
70
+ *
71
+ * Caller passes `dbPath` (typically `getDbPath()` from the test file)
72
+ * to keep this helper free of source-module imports.
73
+ */
74
+ export function removeTestDbFiles(dbPath: string): void {
75
+ assertNotLiveDb(dbPath);
76
+ rmSync(dbPath, { force: true });
77
+ rmSync(`${dbPath}-shm`, { force: true });
78
+ rmSync(`${dbPath}-wal`, { force: true });
79
+ }
@@ -14,10 +14,11 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
14
14
 
15
15
  const DECLARED_FLAG_ID = "email-channel";
16
16
  const DECLARED_FLAG_KEY = DECLARED_FLAG_ID;
17
- const SAFE_STORAGE_LIMITS_FLAG = "safe-storage-limits";
18
17
 
19
- const { isAssistantFeatureFlagEnabled, _setOverridesForTesting } =
18
+ const { isAssistantFeatureFlagEnabled } =
20
19
  await import("../config/assistant-feature-flags.js");
20
+ const { setOverridesForTesting } =
21
+ await import("./feature-flag-test-helpers.js");
21
22
  const { skillFlagKey } = await import("../config/skill-state.js");
22
23
 
23
24
  // ---------------------------------------------------------------------------
@@ -25,11 +26,11 @@ const { skillFlagKey } = await import("../config/skill-state.js");
25
26
  // ---------------------------------------------------------------------------
26
27
 
27
28
  beforeEach(() => {
28
- _setOverridesForTesting({});
29
+ setOverridesForTesting({});
29
30
  });
30
31
 
31
32
  afterEach(() => {
32
- _setOverridesForTesting({});
33
+ setOverridesForTesting({});
33
34
  });
34
35
 
35
36
  // ---------------------------------------------------------------------------
@@ -38,14 +39,14 @@ afterEach(() => {
38
39
 
39
40
  describe("isAssistantFeatureFlagEnabled", () => {
40
41
  test("reads from file-based overrides", () => {
41
- _setOverridesForTesting({ [DECLARED_FLAG_KEY]: true });
42
+ setOverridesForTesting({ [DECLARED_FLAG_KEY]: true });
42
43
  const config = {} as any;
43
44
 
44
45
  expect(isAssistantFeatureFlagEnabled(DECLARED_FLAG_KEY, config)).toBe(true);
45
46
  });
46
47
 
47
48
  test("explicit false override in file-based overrides", () => {
48
- _setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
49
+ setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
49
50
  const config = {} as any;
50
51
 
51
52
  expect(isAssistantFeatureFlagEnabled(DECLARED_FLAG_KEY, config)).toBe(
@@ -63,23 +64,6 @@ describe("isAssistantFeatureFlagEnabled", () => {
63
64
  );
64
65
  });
65
66
 
66
- test("safe-storage-limits defaults to disabled", () => {
67
- const config = {} as any;
68
-
69
- expect(
70
- isAssistantFeatureFlagEnabled(SAFE_STORAGE_LIMITS_FLAG, config),
71
- ).toBe(false);
72
- });
73
-
74
- test("safe-storage-limits respects explicit override", () => {
75
- _setOverridesForTesting({ [SAFE_STORAGE_LIMITS_FLAG]: true });
76
- const config = {} as any;
77
-
78
- expect(
79
- isAssistantFeatureFlagEnabled(SAFE_STORAGE_LIMITS_FLAG, config),
80
- ).toBe(true);
81
- });
82
-
83
67
  test("unknown flag defaults to false when no persisted override", () => {
84
68
  const config = {} as any;
85
69
 
@@ -87,7 +71,7 @@ describe("isAssistantFeatureFlagEnabled", () => {
87
71
  });
88
72
 
89
73
  test("undeclared flag respects persisted override", () => {
90
- _setOverridesForTesting({ "some-undeclared-flag": false });
74
+ setOverridesForTesting({ "some-undeclared-flag": false });
91
75
  const config = {} as any;
92
76
 
93
77
  expect(isAssistantFeatureFlagEnabled("some-undeclared-flag", config)).toBe(
@@ -98,7 +82,7 @@ describe("isAssistantFeatureFlagEnabled", () => {
98
82
 
99
83
  describe("isAssistantFeatureFlagEnabled with skillFlagKey", () => {
100
84
  test("resolves skill flag via canonical path", () => {
101
- _setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
85
+ setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
102
86
  const config = {} as any;
103
87
 
104
88
  expect(
@@ -12,13 +12,13 @@ mock.module("../util/logger.js", () => ({
12
12
  }),
13
13
  }));
14
14
 
15
- import { resetDb } from "../memory/db-connection.js";
16
15
  import { getSqlite } from "../memory/db-connection.js";
17
16
  import { initializeDb } from "../memory/db-init.js";
18
17
  import {
19
18
  getRecentInvocations,
20
19
  rotateToolInvocations,
21
20
  } from "../memory/tool-usage-store.js";
21
+ import { resetDbForTesting } from "./db-test-helpers.js";
22
22
 
23
23
  // ---------------------------------------------------------------------------
24
24
  // Helpers
@@ -57,7 +57,7 @@ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
57
57
 
58
58
  describe("audit log rotation", () => {
59
59
  beforeAll(() => {
60
- resetDb();
60
+ resetDbForTesting();
61
61
  initializeDb();
62
62
  // Insert a conversations row so FK-enforced ORM inserts succeed
63
63
  getSqlite().run(
@@ -130,7 +130,6 @@ mock.module("../runtime/services/analyze-conversation.js", () => ({
130
130
 
131
131
  // ── Real imports ──────────────────────────────────────────────────
132
132
 
133
- import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
134
133
  import { conversationAnalyzeJob } from "../memory/conversation-analyze-job.js";
135
134
  import { createConversation } from "../memory/conversation-crud.js";
136
135
  import { getDb } from "../memory/db-connection.js";
@@ -138,6 +137,7 @@ import { initializeDb } from "../memory/db-init.js";
138
137
  import { indexMessageNow } from "../memory/indexer.js";
139
138
  import type { MemoryJob } from "../memory/jobs-store.js";
140
139
  import { conversations, memoryJobs, messages } from "../memory/schema.js";
140
+ import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
141
141
 
142
142
  // ── Helpers ───────────────────────────────────────────────────────
143
143
 
@@ -279,14 +279,14 @@ beforeEach(() => {
279
279
  resetTables();
280
280
  analyzeCalls.length = 0;
281
281
  // Clear any stale feature-flag overrides between tests.
282
- _setOverridesForTesting({});
282
+ setOverridesForTesting({});
283
283
  });
284
284
 
285
285
  // ─────────────────────────────────────────────────────────────────
286
286
 
287
287
  describe("auto-analysis end-to-end trigger path", () => {
288
288
  test("flag on, batch threshold reached → conversation_analyze enqueued, handler runs, analysis conversation created", async () => {
289
- _setOverridesForTesting({ "auto-analyze": true });
289
+ setOverridesForTesting({ "auto-analyze": true });
290
290
 
291
291
  const source = createConversation("source-conv");
292
292
 
@@ -329,7 +329,7 @@ describe("auto-analysis end-to-end trigger path", () => {
329
329
  });
330
330
 
331
331
  test("flag off → no conversation_analyze job is ever enqueued", async () => {
332
- _setOverridesForTesting({ "auto-analyze": false });
332
+ setOverridesForTesting({ "auto-analyze": false });
333
333
 
334
334
  const source = createConversation("source-conv-flag-off");
335
335
  await indexMessages(source.id, 3);
@@ -343,7 +343,7 @@ describe("auto-analysis end-to-end trigger path", () => {
343
343
  });
344
344
 
345
345
  test("recursion guard: indexing into an auto-analysis conversation does not enqueue conversation_analyze or graph_extract", async () => {
346
- _setOverridesForTesting({ "auto-analyze": true });
346
+ setOverridesForTesting({ "auto-analyze": true });
347
347
 
348
348
  // Set up an auto-analysis conversation directly. This is the
349
349
  // scenario we need to protect against — the analysis agent writes
@@ -392,7 +392,7 @@ describe("auto-analysis batch trigger uses analysis.batchSize cadence", () => {
392
392
  const originalV2Enabled = TEST_CONFIG.memory.v2.enabled;
393
393
 
394
394
  beforeEach(() => {
395
- _setOverridesForTesting({ "auto-analyze": true });
395
+ setOverridesForTesting({ "auto-analyze": true });
396
396
  // memory.v2.enabled gates v1 graph_extract enqueue; force off so
397
397
  // these cadence tests can observe the v1 path.
398
398
  TEST_CONFIG.memory.v2.enabled = false;
@@ -1,5 +1,7 @@
1
1
  import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
+ import { createMockLoggerModule } from "./helpers/mock-logger.js";
4
+
3
5
  // Default the warm-pool gate to OPEN — these tests probe background-job
4
6
  // disk-pressure behavior, not the pre-first-message guard.
5
7
  mock.module("../runtime/pre-first-message-gate.js", () => ({
@@ -7,14 +9,7 @@ mock.module("../runtime/pre-first-message-gate.js", () => ({
7
9
  _resetPreFirstMessageGateCacheForTests: () => {},
8
10
  }));
9
11
 
10
- mock.module("../util/logger.js", () => ({
11
- getLogger: () => ({
12
- info: () => {},
13
- debug: () => {},
14
- warn: () => {},
15
- error: () => {},
16
- }),
17
- }));
12
+ mock.module("../util/logger.js", () => createMockLoggerModule());
18
13
 
19
14
  mock.module("../config/loader.js", () => ({
20
15
  getConfig: () => ({
@@ -157,6 +152,8 @@ mock.module("../memory/conversation-crud.js", () => ({
157
152
  setLastNotifiedInferenceProfile: mock(() => {}),
158
153
  setConversationHistoryStrippedAt: mock(() => {}),
159
154
  wipeConversation: mock(() => ({ memoryIds: [] })),
155
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
156
+ extractImageSourcePaths: () => undefined,
160
157
  }));
161
158
 
162
159
  mock.module("../memory/conversation-title-service.js", () => ({
@@ -12,7 +12,6 @@ mock.module("../config/loader.js", () => ({
12
12
 
13
13
  import { BROWSER_OPERATION_META } from "../browser/operations.js";
14
14
  import { BROWSER_OPERATIONS } from "../browser/types.js";
15
- import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
16
15
  import {
17
16
  projectSkillTools,
18
17
  resetSkillToolProjection,
@@ -23,6 +22,7 @@ import {
23
22
  getAllTools,
24
23
  initializeTools,
25
24
  } from "../tools/registry.js";
25
+ import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
26
26
  import {
27
27
  BROWSER_SKILL_ID,
28
28
  buildSkillLoadHistory,
@@ -30,13 +30,13 @@ import {
30
30
 
31
31
  afterAll(() => {
32
32
  __resetRegistryForTesting();
33
- _setOverridesForTesting({});
33
+ setOverridesForTesting({});
34
34
  });
35
35
 
36
36
  describe("browser CLI-only architecture end-state", () => {
37
37
  beforeAll(async () => {
38
38
  __resetRegistryForTesting();
39
- _setOverridesForTesting({
39
+ setOverridesForTesting({
40
40
  browser: true,
41
41
  });
42
42
  await initializeTools();
@@ -38,6 +38,7 @@ const mockAddMessage = mock(() => {});
38
38
 
39
39
  mock.module("../memory/conversation-crud.js", () => ({
40
40
  addMessage: mockAddMessage,
41
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
41
42
  }));
42
43
 
43
44
  const MOCK_TOOLS = [
@@ -48,8 +49,8 @@ const MOCK_TOOLS = [
48
49
  },
49
50
  ];
50
51
 
51
- mock.module("../daemon/conversation-tool-setup.js", () => ({
52
- buildToolDefinitions: () => MOCK_TOOLS,
52
+ mock.module("../tools/registry.js", () => ({
53
+ getAllToolDefinitions: () => MOCK_TOOLS,
53
54
  }));
54
55
 
55
56
  const MOCK_SYSTEM_PROMPT = "You are a helpful assistant.";