@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
@@ -6,12 +6,15 @@ import { Database } from "bun:sqlite";
6
6
  import { drizzle } from "drizzle-orm/bun-sqlite";
7
7
 
8
8
  import { ensureDataDir, getDbPath } from "../util/platform.js";
9
+ import {
10
+ clearStoredDb,
11
+ getStoredDb,
12
+ setStoredDb,
13
+ } from "./db-singleton.js";
9
14
  import * as schema from "./schema.js";
10
15
 
11
16
  export type DrizzleDb = ReturnType<typeof drizzle<typeof schema>>;
12
17
 
13
- let db: DrizzleDb | null = null;
14
-
15
18
  function canonicalizePathThroughExistingParent(path: string): string {
16
19
  const resolvedPath = resolve(path);
17
20
  const pendingSegments: string[] = [];
@@ -71,18 +74,20 @@ function assertTestDbIsIsolated(): void {
71
74
  }
72
75
 
73
76
  export function getDb(): DrizzleDb {
74
- if (!db) {
75
- assertTestDbIsIsolated();
76
- ensureDataDir();
77
- const sqlite = new Database(getDbPath());
78
- sqlite.exec("PRAGMA journal_mode=WAL");
79
- sqlite.exec("PRAGMA synchronous=FULL");
80
- sqlite.exec("PRAGMA busy_timeout=5000");
81
- sqlite.exec("PRAGMA foreign_keys = ON");
82
- sqlite.exec("PRAGMA cache_size=-256000");
83
- sqlite.exec("PRAGMA temp_store=MEMORY");
84
- db = drizzle(sqlite, { schema });
85
- }
77
+ const existing = getStoredDb<DrizzleDb>();
78
+ if (existing) return existing;
79
+
80
+ assertTestDbIsIsolated();
81
+ ensureDataDir();
82
+ const sqlite = new Database(getDbPath());
83
+ sqlite.exec("PRAGMA journal_mode=WAL");
84
+ sqlite.exec("PRAGMA synchronous=FULL");
85
+ sqlite.exec("PRAGMA busy_timeout=5000");
86
+ sqlite.exec("PRAGMA foreign_keys = ON");
87
+ sqlite.exec("PRAGMA cache_size=-256000");
88
+ sqlite.exec("PRAGMA temp_store=MEMORY");
89
+ const db = drizzle(sqlite, { schema });
90
+ setStoredDb(db, () => sqlite.close());
86
91
  return db;
87
92
  }
88
93
 
@@ -107,10 +112,15 @@ export function getSqliteFrom(drizzleDb: DrizzleDb): Database {
107
112
  return (drizzleDb as unknown as { $client: Database }).$client;
108
113
  }
109
114
 
110
- /** Reset the db singleton. Used by tests to ensure isolation between test files. */
115
+ /**
116
+ * Reset the db singleton. Used by production callers that need to close
117
+ * the live connection so the file can be replaced (post-migration,
118
+ * post-restore, post-vbundle-import) and on graceful shutdown.
119
+ *
120
+ * Tests should use `resetDbForTesting()` from
121
+ * `__tests__/db-test-helpers.ts` instead so they don't depend on this
122
+ * module's heavy import chain (`drizzle-orm/bun-sqlite`).
123
+ */
111
124
  export function resetDb(): void {
112
- if (db) {
113
- getSqliteFrom(db).close();
114
- db = null;
115
- }
125
+ clearStoredDb();
116
126
  }
@@ -96,6 +96,7 @@ import {
96
96
  migrateDropMemoryItemsTables,
97
97
  migrateDropMemorySegmentFts,
98
98
  migrateDropOrphanedMediaTables,
99
+ migrateDropProviderConnectionStatus,
99
100
  migrateDropRemindersTable,
100
101
  migrateDropSetupSkillIdColumn,
101
102
  migrateDropSimplifiedMemory,
@@ -133,6 +134,7 @@ import {
133
134
  migrateMemoryV3AutoEdges,
134
135
  migrateMemoryV3Coactivation,
135
136
  migrateMessageBookmarks,
137
+ migrateMessagesClientMessageId,
136
138
  migrateMessagesConversationCreatedAtIndex,
137
139
  migrateMessagesFtsBackfill,
138
140
  migrateNormalizePhoneIdentities,
@@ -462,6 +464,8 @@ export function initializeDb(): void {
462
464
  migrateMemoryV3Coactivation,
463
465
  migrateMemoryV3AutoEdges,
464
466
  migrateLlmRequestLogCallSite,
467
+ migrateDropProviderConnectionStatus,
468
+ migrateMessagesClientMessageId,
465
469
  ];
466
470
 
467
471
  // Run each migration step, catching and logging individual failures so one
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Holds the assistant DB connection singleton and its close callback.
3
+ *
4
+ * Lives in its own module (rather than alongside the resolver in
5
+ * `db-connection.ts`) so test code can reset the singleton without
6
+ * importing `db-connection.ts` — which transitively pulls
7
+ * `drizzle-orm/bun-sqlite`. Stdlib-only by design: this file must remain
8
+ * safe to import from the test preload's load-time chain, where a broken
9
+ * `node_modules` symlink has historically tripped the env override
10
+ * (see DB ghost #3, /workspace/journal/2026-05-25-db-ghost-3-recovery.md).
11
+ *
12
+ * State is held on `globalThis.vellumAssistant.dbSingleton` so test
13
+ * helpers in `__tests__/` can read/write it WITHOUT importing this
14
+ * module — they declare the same slot shape locally and access the
15
+ * globalThis namespace directly. See
16
+ * `__tests__/db-test-helpers.ts` for the test-side mirror; the slot
17
+ * shape MUST stay in sync between the two.
18
+ *
19
+ * The stored value is typed as `unknown` so this file never has to import
20
+ * Drizzle types. Callers in `db-connection.ts` narrow via the type
21
+ * parameter on `getStoredDb<DrizzleDb>()`.
22
+ *
23
+ * Consumers:
24
+ * - `db-connection.ts` (opens/owns the connection)
25
+ * - production callers that need to close the active connection
26
+ * (migration routes, vbundle import, backup/restore, daemon shutdown)
27
+ * - `__tests__/db-test-helpers.ts` (per-test reset, via globalThis)
28
+ */
29
+
30
+ type DbSlot = {
31
+ db: unknown;
32
+ closer: (() => void) | null;
33
+ };
34
+
35
+ type VellumAssistantNamespace = {
36
+ dbSingleton?: DbSlot;
37
+ };
38
+
39
+ function slot(): DbSlot {
40
+ const g = globalThis as { vellumAssistant?: VellumAssistantNamespace };
41
+ const ns = (g.vellumAssistant ??= {});
42
+ return (ns.dbSingleton ??= { db: null, closer: null });
43
+ }
44
+
45
+ /** Read the current singleton, narrowed to `T`. `null` means not yet opened. */
46
+ export function getStoredDb<T>(): T | null {
47
+ return slot().db as T | null;
48
+ }
49
+
50
+ /**
51
+ * Store a freshly-opened connection and the closer to run on
52
+ * `clearStoredDb()`. The closer must be self-contained — it is invoked
53
+ * inside a try/catch, so partial failures are swallowed (best-effort
54
+ * close on shutdown / restore paths).
55
+ */
56
+ export function setStoredDb<T>(newDb: T, close: () => void): void {
57
+ const s = slot();
58
+ s.db = newDb;
59
+ s.closer = close;
60
+ }
61
+
62
+ /**
63
+ * Close the active connection (if any) via the stored closer, then drop
64
+ * both. Idempotent: safe to call when no connection is stored.
65
+ */
66
+ export function clearStoredDb(): void {
67
+ const s = slot();
68
+ if (s.closer) {
69
+ try {
70
+ s.closer();
71
+ } catch {
72
+ /* best-effort close */
73
+ }
74
+ }
75
+ s.db = null;
76
+ s.closer = null;
77
+ }
@@ -10,6 +10,36 @@ import { eq } from "drizzle-orm";
10
10
  import { getDb } from "./db-connection.js";
11
11
  import { channelInboundEvents } from "./schema.js";
12
12
 
13
+ const SLACK_DM_LIVE_DELIVERED_TEXT_RESPONSE_INDEXES =
14
+ "slackDmLiveDeliveredTextResponseIndexes";
15
+
16
+ function parseRawPayload(
17
+ rawPayload: string | null,
18
+ ): Record<string, unknown> | undefined {
19
+ if (!rawPayload) return undefined;
20
+ try {
21
+ const parsed = JSON.parse(rawPayload) as unknown;
22
+ if (
23
+ parsed === null ||
24
+ typeof parsed !== "object" ||
25
+ Array.isArray(parsed)
26
+ ) {
27
+ return undefined;
28
+ }
29
+ return parsed as Record<string, unknown>;
30
+ } catch {
31
+ return undefined;
32
+ }
33
+ }
34
+
35
+ function normalizePositiveIntegerIndexes(value: unknown): number[] {
36
+ if (!Array.isArray(value)) return [];
37
+ const indexes = value.filter(
38
+ (item): item is number => Number.isSafeInteger(item) && item > 0,
39
+ );
40
+ return [...new Set(indexes)].sort((a, b) => a - b);
41
+ }
42
+
13
43
  // ── Pending verification reply helpers ───────────────────────────────
14
44
  //
15
45
  // When a guardian verification succeeds but the confirmation reply fails
@@ -100,6 +130,58 @@ export function updateDeliveredSegmentCount(
100
130
  .run();
101
131
  }
102
132
 
133
+ export function getSlackDmLiveDeliveredTextResponseIndexes(
134
+ eventId: string,
135
+ ): number[] {
136
+ const db = getDb();
137
+ const row = db
138
+ .select({ rawPayload: channelInboundEvents.rawPayload })
139
+ .from(channelInboundEvents)
140
+ .where(eq(channelInboundEvents.id, eventId))
141
+ .get();
142
+ const payload = parseRawPayload(row?.rawPayload ?? null);
143
+ if (!payload) return [];
144
+ return normalizePositiveIntegerIndexes(
145
+ payload[SLACK_DM_LIVE_DELIVERED_TEXT_RESPONSE_INDEXES],
146
+ );
147
+ }
148
+
149
+ export function addSlackDmLiveDeliveredTextResponseIndex(
150
+ eventId: string,
151
+ responseIndex: number,
152
+ ): void {
153
+ if (!Number.isSafeInteger(responseIndex) || responseIndex <= 0) return;
154
+
155
+ const db = getDb();
156
+ db.transaction((tx) => {
157
+ const row = tx
158
+ .select({ rawPayload: channelInboundEvents.rawPayload })
159
+ .from(channelInboundEvents)
160
+ .where(eq(channelInboundEvents.id, eventId))
161
+ .get();
162
+ if (!row?.rawPayload) return;
163
+
164
+ const payload = parseRawPayload(row.rawPayload);
165
+ if (!payload) return;
166
+ const indexes = normalizePositiveIntegerIndexes(
167
+ payload[SLACK_DM_LIVE_DELIVERED_TEXT_RESPONSE_INDEXES],
168
+ );
169
+ if (!indexes.includes(responseIndex)) indexes.push(responseIndex);
170
+ indexes.sort((a, b) => a - b);
171
+
172
+ tx.update(channelInboundEvents)
173
+ .set({
174
+ rawPayload: JSON.stringify({
175
+ ...payload,
176
+ [SLACK_DM_LIVE_DELIVERED_TEXT_RESPONSE_INDEXES]: indexes,
177
+ }),
178
+ updatedAt: Date.now(),
179
+ })
180
+ .where(eq(channelInboundEvents.id, eventId))
181
+ .run();
182
+ });
183
+ }
184
+
103
185
  // ── Deliver-once guard for terminal reply idempotency ────────────────
104
186
  //
105
187
  // When both the main poll (processChannelMessageWithApprovals) and the
@@ -28,6 +28,7 @@ import {
28
28
 
29
29
  import { drizzle } from "drizzle-orm/bun-sqlite";
30
30
 
31
+ import { createMockLoggerModule } from "../../../__tests__/helpers/mock-logger.js";
31
32
  import type { AssistantConfig } from "../../../config/types.js";
32
33
  import type { Message } from "../../../providers/types.js";
33
34
 
@@ -35,10 +36,7 @@ import type { Message } from "../../../providers/types.js";
35
36
  // Module mocks (must precede the dynamic imports below)
36
37
  // ---------------------------------------------------------------------------
37
38
 
38
- mock.module("../../../util/logger.js", () => ({
39
- getLogger: () =>
40
- new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
41
- }));
39
+ mock.module("../../../util/logger.js", () => createMockLoggerModule());
42
40
 
43
41
  // Stub the v1 retriever so we don't reach Qdrant. Both modes return zero
44
42
  // nodes — the v1 injection branch becomes a no-op, isolating the assertion
@@ -71,9 +71,9 @@ mock.module("../../providers/provider-send-message.js", () => ({
71
71
  extractToolUse: () => null,
72
72
  }));
73
73
 
74
+ import { resetDbForTesting } from "../../__tests__/db-test-helpers.js";
74
75
  import { DEFAULT_CONFIG } from "../../config/defaults.js";
75
76
  import type { AssistantConfig } from "../../config/types.js";
76
- import { resetDb } from "../db-connection.js";
77
77
  import { initializeDb } from "../db-init.js";
78
78
  import { resetTestTables } from "../raw-query.js";
79
79
  import { InContextTracker } from "./injection.js";
@@ -134,7 +134,7 @@ describe("loadContextMemory — query/sparse vector surfacing", () => {
134
134
  embedCallCount = 0;
135
135
  embedRouter = null;
136
136
  searchRouter = null;
137
- resetDb();
137
+ resetDbForTesting();
138
138
  initializeDb();
139
139
  });
140
140
 
@@ -190,7 +190,7 @@ describe("retrieveForTurn — query/sparse vector surfacing", () => {
190
190
  embedCallCount = 0;
191
191
  embedRouter = null;
192
192
  searchRouter = null;
193
- resetDb();
193
+ resetDbForTesting();
194
194
  initializeDb();
195
195
  });
196
196
 
@@ -40,9 +40,10 @@ mock.module("../job-utils.js", () => ({
40
40
  },
41
41
  }));
42
42
 
43
+ import { resetDbForTesting } from "../../__tests__/db-test-helpers.js";
43
44
  import { DEFAULT_CONFIG } from "../../config/defaults.js";
44
45
  import type { AssistantConfig } from "../../config/types.js";
45
- import { getDb, resetDb } from "../db-connection.js";
46
+ import { getDb } from "../db-connection.js";
46
47
  import { initializeDb } from "../db-init.js";
47
48
  import type { MemoryJob } from "../jobs-store.js";
48
49
  import { mediaAssets } from "../schema.js";
@@ -66,7 +67,7 @@ describe("embedMediaJob", () => {
66
67
 
67
68
  beforeEach(() => {
68
69
  embedAndUpsertCalls.length = 0;
69
- resetDb();
70
+ resetDbForTesting();
70
71
  initializeDb();
71
72
  });
72
73
 
@@ -142,7 +142,10 @@ afterAll(() => {
142
142
  // Imports are deferred to after the env var is set so any internal use of
143
143
  // `getWorkspaceDir()` resolves to the tmpdir.
144
144
  const { DEFAULT_CONFIG } = await import("../../../config/defaults.js");
145
- const { getDb, resetDb } = await import("../../db-connection.js");
145
+ const { getDb } = await import("../../db-connection.js");
146
+ const { resetDbForTesting } = await import(
147
+ "../../../__tests__/db-test-helpers.js"
148
+ );
146
149
  const { initializeDb } = await import("../../db-init.js");
147
150
  const { memoryEmbeddings, memoryJobs } = await import("../../schema.js");
148
151
  const { claimMemoryJobs } = await import("../../jobs-store.js");
@@ -183,7 +186,7 @@ function makeJob(payload: Record<string, unknown>): MemoryJob {
183
186
  }
184
187
 
185
188
  beforeEach(() => {
186
- resetDb();
189
+ resetDbForTesting();
187
190
  initializeDb();
188
191
  embedWithBackendCalls.length = 0;
189
192
  upsertCalls.length = 0;
@@ -65,6 +65,7 @@ import {
65
65
  enqueuePruneOldTraceEventsJob,
66
66
  failMemoryJob,
67
67
  failStalledJobs,
68
+ hasActiveJobOfType,
68
69
  type MemoryJob,
69
70
  type MemoryJobType,
70
71
  resetRunningJobsToPending,
@@ -791,8 +792,18 @@ export function maybeEnqueueGraphMaintenanceJobs(
791
792
  // Size-based trigger: when the shared buffer crosses the configured line
792
793
  // count, drain it now rather than waiting out the interval. Retargets to the
793
794
  // same consolidator the interval branch above selected.
795
+ //
796
+ // The size branch is checkpoint-blind by design (it must fire before the
797
+ // interval elapses), so it dedupes against an already-active consolidate job
798
+ // instead — otherwise it would re-enqueue on every worker tick while the
799
+ // buffer stays over threshold, flooding the queue with redundant LLM work.
794
800
  const maxLines = config.memory.v2.consolidation_max_buffer_lines;
795
- if (v2Active && !enqueuedConsolidate && maxLines !== null) {
801
+ if (
802
+ v2Active &&
803
+ !enqueuedConsolidate &&
804
+ maxLines !== null &&
805
+ !hasActiveJobOfType(consolidateEntry.jobType)
806
+ ) {
796
807
  const bufferPath = join(getWorkspaceDir(), "memory", "buffer.md");
797
808
  if (countBufferLines(bufferPath) >= maxLines) {
798
809
  enqueueMemoryJob(consolidateEntry.jobType, {});
@@ -209,6 +209,86 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
209
209
  return rows.map((r) => this.toLogRow(r));
210
210
  }
211
211
 
212
+ async getCompactionLogsBetween(
213
+ conversationId: string,
214
+ afterCreatedAt: number | null,
215
+ beforeCreatedAt: number,
216
+ ): Promise<LogRow[]> {
217
+ const aid = await this.assistantId();
218
+ // `call_site` is bound as a literal via type-bound parameter (not
219
+ // string interpolation) for parity with the rest of this class —
220
+ // even though "compactionAgent" is a hard-coded identifier today,
221
+ // future call sites should plug into the same parameter slot
222
+ // without re-templating the query string.
223
+ //
224
+ // The `afterCreatedAt` lower bound is appended dynamically because
225
+ // type-bound parameter slots that are referenced in the SQL but
226
+ // unbound at exec time return a server error — so we only template
227
+ // the predicate in when the caller actually has a floor to enforce.
228
+ const params: Record<string, string> = {
229
+ assistant_id: aid,
230
+ conversation_id: conversationId,
231
+ call_site: "compactionAgent",
232
+ before_created_at: String(beforeCreatedAt),
233
+ };
234
+ let afterPredicate = "";
235
+ if (afterCreatedAt !== null) {
236
+ params.after_created_at = String(afterCreatedAt);
237
+ afterPredicate =
238
+ " AND created_at > fromUnixTimestamp64Milli({after_created_at:Int64})";
239
+ }
240
+ const sql = `SELECT
241
+ id,
242
+ conversation_id,
243
+ message_id,
244
+ provider,
245
+ request_payload,
246
+ response_payload,
247
+ toUnixTimestamp64Milli(created_at) AS created_at,
248
+ agent_loop_exit_reason,
249
+ call_site
250
+ FROM ${this.tableRef()}
251
+ WHERE assistant_id = {assistant_id:String}
252
+ AND conversation_id = {conversation_id:String}
253
+ AND call_site = {call_site:String}
254
+ AND created_at < fromUnixTimestamp64Milli({before_created_at:Int64})${afterPredicate}
255
+ ORDER BY created_at ASC, id ASC
256
+ LIMIT 1 BY id
257
+ FORMAT JSONEachRow`;
258
+ const rows = await this.exec(sql, params);
259
+ return rows.map((r) => this.toLogRow(r));
260
+ }
261
+
262
+ async getPreviousNonCompactionCallCreatedAt(
263
+ conversationId: string,
264
+ beforeCreatedAt: number,
265
+ ): Promise<number | null> {
266
+ const aid = await this.assistantId();
267
+ // "Non-compactionAgent" includes empty-string `call_site`, which is
268
+ // what the CH mirror writes for pre-migration-264 rows whose SQLite
269
+ // value was NULL (CH columns are `DEFAULT ''`, not Nullable — see
270
+ // `toLogRow`). The local store treats NULL `callSite` the same way,
271
+ // so the predicate stays in sync.
272
+ const sql = `SELECT
273
+ toUnixTimestamp64Milli(created_at) AS created_at
274
+ FROM ${this.tableRef()}
275
+ WHERE assistant_id = {assistant_id:String}
276
+ AND conversation_id = {conversation_id:String}
277
+ AND created_at < fromUnixTimestamp64Milli({before_created_at:Int64})
278
+ AND call_site != {call_site:String}
279
+ ORDER BY created_at DESC, id DESC
280
+ LIMIT 1
281
+ FORMAT JSONEachRow`;
282
+ const rows = await this.exec(sql, {
283
+ assistant_id: aid,
284
+ conversation_id: conversationId,
285
+ call_site: "compactionAgent",
286
+ before_created_at: String(beforeCreatedAt),
287
+ });
288
+ const value = rows[0]?.created_at;
289
+ return value === undefined ? null : Number(value);
290
+ }
291
+
212
292
  private async selectByMessageIds(ids: string[]): Promise<LogRow[]> {
213
293
  if (ids.length === 0) return [];
214
294
  const aid = await this.assistantId();
@@ -10,6 +10,8 @@
10
10
  */
11
11
  import type { LlmRequestLogSource } from "./llm-request-log-source.js";
12
12
  import {
13
+ getCompactionLogsBetween,
14
+ getPreviousNonCompactionCallCreatedAt,
13
15
  getRequestLogById,
14
16
  getRequestLogsByConversationId,
15
17
  getRequestLogsByMessageId,
@@ -30,4 +32,26 @@ export class LocalLlmRequestLogSource implements LlmRequestLogSource {
30
32
  ): Promise<LogRow[]> {
31
33
  return getRequestLogsByConversationId(conversationId);
32
34
  }
35
+
36
+ async getCompactionLogsBetween(
37
+ conversationId: string,
38
+ afterCreatedAt: number | null,
39
+ beforeCreatedAt: number,
40
+ ): Promise<LogRow[]> {
41
+ return getCompactionLogsBetween(
42
+ conversationId,
43
+ afterCreatedAt,
44
+ beforeCreatedAt,
45
+ );
46
+ }
47
+
48
+ async getPreviousNonCompactionCallCreatedAt(
49
+ conversationId: string,
50
+ beforeCreatedAt: number,
51
+ ): Promise<number | null> {
52
+ return getPreviousNonCompactionCallCreatedAt(
53
+ conversationId,
54
+ beforeCreatedAt,
55
+ );
56
+ }
33
57
  }
@@ -38,6 +38,37 @@ export interface LlmRequestLogSource {
38
38
  * and orphaned logs are all included because they share conversation_id.
39
39
  */
40
40
  getRequestLogsByConversationId(conversationId: string): Promise<LogRow[]>;
41
+
42
+ /**
43
+ * Fetch every `callSite = "compactionAgent"` log row in the conversation
44
+ * whose `createdAt` falls in the **open window**
45
+ * `(afterCreatedAt, beforeCreatedAt)`, ordered chronologically.
46
+ *
47
+ * Drives the Inspector's Compaction tab. The route handler resolves
48
+ * both bounds: the selected LLM call's `createdAt` (ceiling) and the
49
+ * previous non-`compactionAgent` call's `createdAt` (floor) — passing
50
+ * `null` for the floor when the selected call is the first real call
51
+ * in the conversation. See `getCompactionLogsBetween` and
52
+ * `getPreviousNonCompactionCallCreatedAt` in `llm-request-log-store.ts`
53
+ * for the full bound semantics.
54
+ */
55
+ getCompactionLogsBetween(
56
+ conversationId: string,
57
+ afterCreatedAt: number | null,
58
+ beforeCreatedAt: number,
59
+ ): Promise<LogRow[]>;
60
+
61
+ /**
62
+ * Find the `createdAt` of the most recent non-`compactionAgent` LLM
63
+ * call in the conversation strictly before `beforeCreatedAt`, or
64
+ * `null` when no such call exists. Pairs with
65
+ * `getCompactionLogsBetween` to bound the compaction trail to the
66
+ * window between the prior real call and the selected call.
67
+ */
68
+ getPreviousNonCompactionCallCreatedAt(
69
+ conversationId: string,
70
+ beforeCreatedAt: number,
71
+ ): Promise<number | null>;
41
72
  }
42
73
 
43
74
  /**