@vellumai/assistant 0.8.3 → 0.8.5

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 (665) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docker-entrypoint.sh +0 -1
  3. package/docs/browser-use-architecture-phase2.md +1 -1
  4. package/knip.json +2 -1
  5. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  6. package/openapi.yaml +1492 -100
  7. package/package.json +1 -1
  8. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  9. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +302 -33
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  14. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  15. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  16. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
  17. package/src/__tests__/btw-routes.test.ts +2 -3
  18. package/src/__tests__/call-controller.test.ts +0 -1
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  20. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  21. package/src/__tests__/channel-guardian.test.ts +3 -3
  22. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  23. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  24. package/src/__tests__/checker.test.ts +6 -15
  25. package/src/__tests__/compaction-events.test.ts +2 -1
  26. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  27. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  29. package/src/__tests__/computer-use-tools.test.ts +2 -4
  30. package/src/__tests__/config-watcher.test.ts +1 -1
  31. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  32. package/src/__tests__/context-token-estimator.test.ts +91 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
  37. package/src/__tests__/conversation-agent-loop.test.ts +188 -129
  38. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  39. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  40. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  41. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  42. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  43. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  44. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  45. package/src/__tests__/conversation-error.test.ts +31 -0
  46. package/src/__tests__/conversation-fork-crud.test.ts +324 -0
  47. package/src/__tests__/conversation-lifecycle.test.ts +53 -12
  48. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  49. package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
  50. package/src/__tests__/conversation-pairing.test.ts +2 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
  53. package/src/__tests__/conversation-queue.test.ts +1 -1
  54. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  57. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  58. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  59. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  60. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  61. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  62. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  63. package/src/__tests__/conversation-store.test.ts +1 -1
  64. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  65. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
  67. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  69. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  70. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  71. package/src/__tests__/credential-security-invariants.test.ts +7 -0
  72. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  73. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  74. package/src/__tests__/dm-backfill.test.ts +64 -0
  75. package/src/__tests__/dm-persistence.test.ts +33 -0
  76. package/src/__tests__/document-find-replace.test.ts +501 -0
  77. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  78. package/src/__tests__/email-html-renderer.test.ts +12 -0
  79. package/src/__tests__/first-greeting.test.ts +23 -2
  80. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  81. package/src/__tests__/gemini-provider.test.ts +78 -0
  82. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  83. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  84. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  85. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  87. package/src/__tests__/heartbeat-service.test.ts +4 -0
  88. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  89. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  90. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  91. package/src/__tests__/host-file-proxy.test.ts +8 -1
  92. package/src/__tests__/host-shell-tool.test.ts +1 -1
  93. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  94. package/src/__tests__/identity-routes.test.ts +57 -0
  95. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  96. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  97. package/src/__tests__/injector-chain.test.ts +2 -0
  98. package/src/__tests__/injector-document-comments.test.ts +378 -0
  99. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  100. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  102. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  103. package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
  104. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  105. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  106. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  107. package/src/__tests__/llm-resolver.test.ts +161 -9
  108. package/src/__tests__/llm-usage-store.test.ts +66 -0
  109. package/src/__tests__/log-export-routes.test.ts +99 -2
  110. package/src/__tests__/logger.test.ts +89 -0
  111. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  112. package/src/__tests__/media-generate-image.test.ts +31 -0
  113. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  114. package/src/__tests__/message-queue-steer.test.ts +114 -0
  115. package/src/__tests__/model-intents.test.ts +2 -4
  116. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  117. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  118. package/src/__tests__/openai-provider.test.ts +151 -0
  119. package/src/__tests__/openai-responses-provider.test.ts +118 -16
  120. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  121. package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
  122. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  123. package/src/__tests__/platform.test.ts +2 -5
  124. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  125. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  126. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  127. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  128. package/src/__tests__/plugin-types.test.ts +3 -2
  129. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  130. package/src/__tests__/pricing.test.ts +12 -0
  131. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  132. package/src/__tests__/process-message-display-content.test.ts +21 -16
  133. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  134. package/src/__tests__/registry.test.ts +2 -8
  135. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  136. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  137. package/src/__tests__/server-history-render.test.ts +83 -4
  138. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  139. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  140. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  141. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  142. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  143. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  144. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  145. package/src/__tests__/suggestion-routes.test.ts +1 -0
  146. package/src/__tests__/sync-message-contract.test.ts +59 -0
  147. package/src/__tests__/system-prompt.test.ts +161 -124
  148. package/src/__tests__/terminal-tools.test.ts +12 -2
  149. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  150. package/src/__tests__/thread-backfill.test.ts +370 -22
  151. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  152. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  153. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  155. package/src/__tests__/tool-executor.test.ts +89 -53
  156. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  157. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  158. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  159. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  160. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  161. package/src/__tests__/twilio-routes.test.ts +1 -1
  162. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  163. package/src/__tests__/usage-routes.test.ts +3 -0
  164. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  165. package/src/__tests__/web-fetch.test.ts +2 -2
  166. package/src/__tests__/workspace-git-service.test.ts +94 -10
  167. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  168. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  169. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  170. package/src/acp/prepare-agent-env.ts +78 -0
  171. package/src/acp/session-manager.ts +1 -1
  172. package/src/agent/attachments.ts +1 -0
  173. package/src/agent/loop.ts +65 -20
  174. package/src/api/README.md +5 -0
  175. package/src/api/index.ts +4 -0
  176. package/src/api/package.json +10 -0
  177. package/src/background-wake/background-wake-routes.test.ts +233 -0
  178. package/src/background-wake/next-wake.test.ts +289 -0
  179. package/src/background-wake/next-wake.ts +172 -0
  180. package/src/background-wake/runtime-registry.ts +24 -0
  181. package/src/browser/operations.ts +15 -0
  182. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  183. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  184. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  185. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  186. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  187. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  188. package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
  189. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  190. package/src/cli/commands/browser.ts +247 -0
  191. package/src/cli/commands/conversations.ts +128 -1
  192. package/src/cli/commands/domain.ts +91 -41
  193. package/src/cli/commands/inference-providers.ts +147 -1
  194. package/src/cli/commands/inference.ts +93 -40
  195. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  196. package/src/cli/commands/memory-v2.ts +483 -0
  197. package/src/cli/commands/memory-v3-render.ts +344 -0
  198. package/src/cli/commands/memory-v3.ts +316 -0
  199. package/src/cli/commands/notifications.ts +24 -2
  200. package/src/cli/program.ts +2 -0
  201. package/src/cli/utils/conversation-id.ts +17 -5
  202. package/src/config/assistant-feature-flags.ts +21 -9
  203. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  204. package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
  205. package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
  206. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  207. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  208. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  209. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  210. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  211. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  212. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  213. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  214. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  215. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  216. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  217. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  218. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  219. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  220. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  221. package/src/config/bundled-tool-registry.ts +24 -12
  222. package/src/config/call-site-defaults.ts +20 -0
  223. package/src/config/feature-flag-registry.json +115 -3
  224. package/src/config/llm-resolver.ts +16 -2
  225. package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
  226. package/src/config/schemas/call-site-catalog.ts +35 -0
  227. package/src/config/schemas/llm.ts +14 -0
  228. package/src/config/schemas/memory-v2.ts +294 -1
  229. package/src/config/schemas/memory.ts +2 -1
  230. package/src/context/compactor.ts +60 -1
  231. package/src/context/token-estimator.ts +47 -4
  232. package/src/context/window-manager.ts +25 -0
  233. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  234. package/src/conversations/message-consolidation.ts +404 -0
  235. package/src/credential-health/credential-health-service.ts +34 -19
  236. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  237. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  238. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  239. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  240. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  241. package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
  242. package/src/daemon/conversation-agent-loop.ts +307 -88
  243. package/src/daemon/conversation-error.ts +31 -1
  244. package/src/daemon/conversation-lifecycle.ts +149 -118
  245. package/src/daemon/conversation-messaging.ts +3 -0
  246. package/src/daemon/conversation-process.ts +273 -0
  247. package/src/daemon/conversation-queue-manager.ts +14 -0
  248. package/src/daemon/conversation-runtime-assembly.ts +145 -84
  249. package/src/daemon/conversation-slash.ts +37 -5
  250. package/src/daemon/conversation-surfaces.ts +45 -2
  251. package/src/daemon/conversation-tool-setup.ts +70 -3
  252. package/src/daemon/conversation-usage.ts +2 -0
  253. package/src/daemon/conversation.ts +54 -32
  254. package/src/daemon/disk-pressure-guard.ts +14 -2
  255. package/src/daemon/first-greeting.ts +10 -0
  256. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  257. package/src/daemon/handlers/config-a2a.ts +160 -0
  258. package/src/daemon/handlers/config-model.test.ts +2 -0
  259. package/src/daemon/handlers/conversations.ts +90 -3
  260. package/src/daemon/handlers/shared.ts +92 -29
  261. package/src/daemon/host-bash-proxy.ts +1 -1
  262. package/src/daemon/host-browser-proxy.ts +5 -5
  263. package/src/daemon/host-cu-proxy.ts +5 -5
  264. package/src/daemon/host-file-proxy.ts +5 -5
  265. package/src/daemon/host-proxy-base.ts +4 -4
  266. package/src/daemon/host-transfer-proxy.ts +11 -11
  267. package/src/daemon/lifecycle.ts +40 -23
  268. package/src/daemon/meet-manifest-loader.ts +1 -7
  269. package/src/daemon/message-protocol.ts +4 -0
  270. package/src/daemon/message-types/conversations.ts +14 -9
  271. package/src/daemon/message-types/document-comments.ts +50 -0
  272. package/src/daemon/message-types/home.ts +1 -13
  273. package/src/daemon/message-types/messages.ts +66 -7
  274. package/src/daemon/message-types/surfaces.ts +3 -1
  275. package/src/daemon/message-types/sync.ts +14 -0
  276. package/src/daemon/message-types/web-activity.ts +57 -0
  277. package/src/daemon/plugin-source-watcher.ts +135 -3
  278. package/src/daemon/process-message.ts +69 -12
  279. package/src/daemon/shutdown-handlers.ts +24 -5
  280. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  281. package/src/daemon/tool-setup-types.ts +13 -0
  282. package/src/daemon/trust-context.ts +6 -0
  283. package/src/documents/document-comments-store.test.ts +338 -0
  284. package/src/documents/document-comments-store.ts +237 -0
  285. package/src/documents/document-store.ts +202 -0
  286. package/src/events/relationship-state-updated.ts +25 -0
  287. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
  288. package/src/heartbeat/heartbeat-service.ts +1 -0
  289. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  290. package/src/home/feed-types.ts +6 -1
  291. package/src/home/home-content-refresh.ts +52 -0
  292. package/src/home/home-greeting-cache.ts +69 -0
  293. package/src/home/home-greeting.ts +85 -0
  294. package/src/home/suggested-prompts.ts +168 -9
  295. package/src/ipc/gateway-flag-listener.ts +123 -0
  296. package/src/ipc/skill-routes/registries.ts +8 -12
  297. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  298. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  299. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  300. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  301. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  302. package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
  303. package/src/memory/auto-analysis-enqueue.ts +5 -1
  304. package/src/memory/conversation-crud.ts +191 -100
  305. package/src/memory/conversation-starters-cadence.ts +3 -1
  306. package/src/memory/conversation-title-service.ts +19 -3
  307. package/src/memory/db-async-query.ts +214 -0
  308. package/src/memory/db-init.ts +26 -0
  309. package/src/memory/db-maintenance.ts +30 -21
  310. package/src/memory/delivery-crud.ts +41 -0
  311. package/src/memory/delivery-status.ts +141 -15
  312. package/src/memory/external-conversation-store.ts +32 -1
  313. package/src/memory/graph/bootstrap.ts +8 -1
  314. package/src/memory/graph/capability-seed.ts +7 -3
  315. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  316. package/src/memory/graph/extraction.ts +1 -5
  317. package/src/memory/graph/graph-search.ts +7 -1
  318. package/src/memory/indexer.ts +28 -18
  319. package/src/memory/job-handlers/cleanup.ts +76 -18
  320. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  321. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  322. package/src/memory/jobs-store.ts +14 -0
  323. package/src/memory/jobs-worker.ts +68 -15
  324. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  325. package/src/memory/llm-request-log-source-local.ts +7 -0
  326. package/src/memory/llm-request-log-source.ts +9 -2
  327. package/src/memory/llm-request-log-store.ts +43 -1
  328. package/src/memory/llm-usage-store.ts +24 -0
  329. package/src/memory/memory-retrospective-constants.ts +28 -0
  330. package/src/memory/memory-retrospective-enqueue.ts +11 -3
  331. package/src/memory/memory-retrospective-job.ts +413 -18
  332. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  333. package/src/memory/memory-v2-activation-log-store.ts +41 -14
  334. package/src/memory/migrations/100-core-tables.ts +1 -0
  335. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  336. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  337. package/src/memory/migrations/253-document-comments.ts +47 -0
  338. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  339. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  340. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  341. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  342. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  343. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  344. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  345. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  346. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  347. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  348. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  349. package/src/memory/migrations/index.ts +34 -0
  350. package/src/memory/migrations/registry.ts +58 -0
  351. package/src/memory/onboarding-events-store.ts +7 -0
  352. package/src/memory/schema/calls.ts +1 -0
  353. package/src/memory/schema/conversations.ts +3 -0
  354. package/src/memory/schema/infrastructure.ts +22 -0
  355. package/src/memory/tool-usage-store.ts +36 -8
  356. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  357. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  358. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  359. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  362. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  363. package/src/memory/v2/__tests__/injection.test.ts +158 -112
  364. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  365. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  366. package/src/memory/v2/__tests__/router.test.ts +660 -4
  367. package/src/memory/v2/consolidation-job.ts +14 -0
  368. package/src/memory/v2/harness/compare.ts +57 -0
  369. package/src/memory/v2/harness/metrics.ts +124 -0
  370. package/src/memory/v2/harness/oracle.ts +145 -0
  371. package/src/memory/v2/harness/replay-input.ts +224 -0
  372. package/src/memory/v2/harness/retriever.ts +74 -0
  373. package/src/memory/v2/harness/router-retriever.ts +43 -0
  374. package/src/memory/v2/harness/runner.ts +106 -0
  375. package/src/memory/v2/harness/trace.ts +58 -0
  376. package/src/memory/v2/injection-events.ts +101 -0
  377. package/src/memory/v2/injection.ts +42 -25
  378. package/src/memory/v2/page-index.ts +209 -7
  379. package/src/memory/v2/page-store.ts +18 -0
  380. package/src/memory/v2/prompts/router.ts +26 -1
  381. package/src/memory/v2/qdrant.ts +14 -2
  382. package/src/memory/v2/router.ts +369 -62
  383. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  384. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  385. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  386. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  387. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  388. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  389. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  390. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  391. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  392. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  393. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  394. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  395. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  396. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  397. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  398. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  399. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  400. package/src/memory/v3/auto-edges.ts +223 -0
  401. package/src/memory/v3/coactivation-store.ts +124 -0
  402. package/src/memory/v3/consolidation-job.ts +323 -0
  403. package/src/memory/v3/edge-learning-job.ts +160 -0
  404. package/src/memory/v3/edges.ts +249 -0
  405. package/src/memory/v3/filter.ts +281 -0
  406. package/src/memory/v3/gate.ts +334 -0
  407. package/src/memory/v3/index-composition.ts +113 -0
  408. package/src/memory/v3/llm-capture.ts +46 -0
  409. package/src/memory/v3/loop.ts +382 -0
  410. package/src/memory/v3/maintenance.ts +144 -0
  411. package/src/memory/v3/prompt-context.ts +33 -0
  412. package/src/memory/v3/prompts/consolidation.ts +458 -0
  413. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  414. package/src/memory/v3/retriever.ts +33 -0
  415. package/src/memory/v3/scouts.ts +420 -0
  416. package/src/memory/v3/shadow-middleware.ts +305 -0
  417. package/src/memory/v3/traversal.ts +206 -0
  418. package/src/memory/v3/tree-index.ts +237 -0
  419. package/src/memory/v3/tree-store.ts +394 -0
  420. package/src/memory/v3/tree-walk.ts +351 -0
  421. package/src/memory/v3/types.ts +65 -0
  422. package/src/memory/v3/validate.ts +300 -0
  423. package/src/messaging/providers/index.ts +7 -1
  424. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  425. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  426. package/src/messaging/providers/slack/adapter.ts +178 -25
  427. package/src/messaging/providers/slack/api.test.ts +54 -0
  428. package/src/messaging/providers/slack/api.ts +119 -3
  429. package/src/messaging/providers/slack/client.ts +12 -0
  430. package/src/messaging/providers/slack/deep-link.ts +20 -1
  431. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  432. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  433. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  434. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  435. package/src/messaging/providers/slack/send.test.ts +77 -0
  436. package/src/messaging/providers/slack/send.ts +8 -2
  437. package/src/messaging/providers/slack/types.ts +14 -0
  438. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  439. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  440. package/src/notifications/adapters/macos.ts +18 -1
  441. package/src/notifications/adapters/platform.ts +1 -1
  442. package/src/notifications/conversation-seed-composer.ts +14 -2
  443. package/src/notifications/decision-engine.ts +1 -4
  444. package/src/notifications/deferred-emit.ts +135 -0
  445. package/src/notifications/emit-signal.ts +38 -50
  446. package/src/notifications/home-feed-side-effect.ts +60 -30
  447. package/src/oauth/connect-orchestrator.ts +3 -0
  448. package/src/oauth/credential-token-resolver.ts +2 -0
  449. package/src/oauth/manual-token-connection.ts +19 -0
  450. package/src/oauth/oauth-store.ts +12 -0
  451. package/src/oauth/seed-providers.ts +22 -0
  452. package/src/permissions/prompter.ts +8 -5
  453. package/src/permissions/question-prompter.ts +5 -2
  454. package/src/permissions/secret-prompter.ts +6 -3
  455. package/src/plugin-api/index.ts +4 -0
  456. package/src/plugin-api/types.ts +7 -33
  457. package/src/plugins/defaults/index.ts +6 -0
  458. package/src/plugins/defaults/injectors.ts +100 -20
  459. package/src/plugins/external-plugin-loader.ts +5 -68
  460. package/src/plugins/types.ts +11 -16
  461. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  462. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  463. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  464. package/src/prompts/normalize-onboarding.ts +40 -0
  465. package/src/prompts/persona-resolver.ts +36 -21
  466. package/src/prompts/sections.ts +69 -19
  467. package/src/prompts/system-prompt.ts +118 -216
  468. package/src/prompts/template-detection.ts +37 -0
  469. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  470. package/src/prompts/templates/BOOTSTRAP.md +10 -2
  471. package/src/prompts/templates/VOICE.md +3 -0
  472. package/src/prompts/templates/system-sections.ts +281 -9
  473. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  474. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  475. package/src/providers/anthropic/client.ts +159 -66
  476. package/src/providers/call-site-routing.ts +14 -2
  477. package/src/providers/connection-model-compat.ts +38 -0
  478. package/src/providers/connection-resolution.ts +16 -2
  479. package/src/providers/fireworks/client.ts +20 -2
  480. package/src/providers/gemini/client.ts +49 -6
  481. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  482. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  483. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  484. package/src/providers/inference/adapter-factory.ts +18 -1
  485. package/src/providers/inference/auth.ts +3 -3
  486. package/src/providers/inference/codex-token-refresh.ts +128 -0
  487. package/src/providers/inference/resolve-auth.ts +49 -6
  488. package/src/providers/minimax/client.ts +106 -0
  489. package/src/providers/model-catalog.ts +91 -1
  490. package/src/providers/model-intents.ts +1 -1
  491. package/src/providers/openai/chat-completions-provider.ts +63 -23
  492. package/src/providers/openai/codex-models.ts +18 -0
  493. package/src/providers/openai/responses-provider.ts +86 -23
  494. package/src/providers/openrouter/client.ts +5 -1
  495. package/src/providers/provider-send-message.ts +7 -1
  496. package/src/providers/retry.ts +34 -3
  497. package/src/providers/thinking-config.ts +26 -1
  498. package/src/providers/types.ts +25 -0
  499. package/src/providers/usage-tracking.ts +2 -0
  500. package/src/runtime/AGENTS.md +2 -2
  501. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  502. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  503. package/src/runtime/agent-wake.ts +152 -56
  504. package/src/runtime/assistant-event-hub.ts +76 -6
  505. package/src/runtime/auth/route-policy.ts +43 -3
  506. package/src/runtime/background-job-runner.ts +26 -0
  507. package/src/runtime/btw-sidechain.ts +0 -6
  508. package/src/runtime/channel-reply-delivery.ts +182 -47
  509. package/src/runtime/channel-retry-sweep.ts +141 -16
  510. package/src/runtime/http-types.ts +7 -6
  511. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  512. package/src/runtime/pending-interactions.ts +50 -8
  513. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  514. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
  515. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  516. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
  517. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  518. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  519. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  520. package/src/runtime/routes/acp-routes.test.ts +255 -6
  521. package/src/runtime/routes/acp-routes.ts +8 -1
  522. package/src/runtime/routes/approval-routes.ts +4 -1
  523. package/src/runtime/routes/avatar-routes.ts +10 -10
  524. package/src/runtime/routes/background-wake-routes.ts +188 -0
  525. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  526. package/src/runtime/routes/btw-routes.ts +0 -6
  527. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  528. package/src/runtime/routes/content-source-routes.ts +78 -0
  529. package/src/runtime/routes/conversation-cli-routes.ts +147 -2
  530. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  531. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  532. package/src/runtime/routes/conversation-query-routes.ts +196 -31
  533. package/src/runtime/routes/conversation-routes.ts +472 -425
  534. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  535. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  536. package/src/runtime/routes/document-comments-routes.ts +287 -0
  537. package/src/runtime/routes/documents-routes.ts +33 -0
  538. package/src/runtime/routes/domain-routes.ts +60 -10
  539. package/src/runtime/routes/email-routes.ts +5 -2
  540. package/src/runtime/routes/events-routes.ts +54 -10
  541. package/src/runtime/routes/group-routes.ts +24 -8
  542. package/src/runtime/routes/home-feed-routes.ts +6 -3
  543. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  544. package/src/runtime/routes/host-browser-routes.ts +17 -2
  545. package/src/runtime/routes/host-cu-routes.ts +2 -2
  546. package/src/runtime/routes/identity-routes.ts +21 -0
  547. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  548. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  549. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  550. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  551. package/src/runtime/routes/index.ts +20 -4
  552. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  553. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  554. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  555. package/src/runtime/routes/integrations/a2a.ts +60 -1
  556. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  557. package/src/runtime/routes/log-export-routes.ts +39 -0
  558. package/src/runtime/routes/memory-item-routes.ts +8 -3
  559. package/src/runtime/routes/memory-v2-routes.ts +427 -0
  560. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  561. package/src/runtime/routes/migration-routes.ts +21 -24
  562. package/src/runtime/routes/notification-routes.ts +19 -2
  563. package/src/runtime/routes/plugins-routes.ts +337 -0
  564. package/src/runtime/routes/question-routes.ts +4 -1
  565. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  566. package/src/runtime/routes/sanity-routes.ts +159 -0
  567. package/src/runtime/routes/secret-routes.ts +25 -5
  568. package/src/runtime/routes/settings-routes.ts +12 -11
  569. package/src/runtime/routes/slack-channel-routes.ts +188 -0
  570. package/src/runtime/routes/workspace-routes.ts +25 -10
  571. package/src/runtime/services/conversation-serializer.ts +30 -4
  572. package/src/runtime/sync/resource-sync-events.ts +106 -38
  573. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  574. package/src/runtime/sync/sync-publisher.ts +2 -1
  575. package/src/runtime/verification-outbound-actions.ts +73 -1
  576. package/src/schedule/integration-status.ts +3 -1
  577. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  578. package/src/security/oauth2-device-code.ts +307 -0
  579. package/src/security/oauth2.ts +26 -9
  580. package/src/security/secure-keys.ts +5 -0
  581. package/src/skills/catalog-install.ts +6 -2
  582. package/src/telemetry/types.ts +12 -0
  583. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  584. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  585. package/src/tools/acp/spawn.test.ts +119 -0
  586. package/src/tools/acp/spawn.ts +15 -2
  587. package/src/tools/apps/definitions.ts +2 -8
  588. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  589. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  590. package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
  591. package/src/tools/browser/browser-execution.ts +106 -0
  592. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  593. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  594. package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
  595. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
  596. package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
  597. package/src/tools/browser/cdp-client/factory.ts +171 -4
  598. package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
  599. package/src/tools/browser/cdp-client/types.ts +101 -0
  600. package/src/tools/browser/pinned-tabs.ts +146 -0
  601. package/src/tools/computer-use/definitions.ts +22 -78
  602. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  603. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  604. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  605. package/src/tools/credentials/vault.ts +3 -9
  606. package/src/tools/document/document-comment-tool.test.ts +379 -0
  607. package/src/tools/document/document-comment-tool.ts +156 -0
  608. package/src/tools/document/document-tool.ts +187 -2
  609. package/src/tools/execution-target.ts +21 -23
  610. package/src/tools/executor.ts +6 -1
  611. package/src/tools/filesystem/edit.ts +3 -9
  612. package/src/tools/filesystem/list.ts +3 -9
  613. package/src/tools/filesystem/read.ts +3 -9
  614. package/src/tools/filesystem/write.ts +3 -9
  615. package/src/tools/host-filesystem/edit.ts +3 -9
  616. package/src/tools/host-filesystem/read.ts +3 -9
  617. package/src/tools/host-filesystem/transfer.ts +3 -9
  618. package/src/tools/host-filesystem/write.ts +3 -9
  619. package/src/tools/host-terminal/host-shell.ts +3 -9
  620. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  621. package/src/tools/memory/register.test.ts +1 -1
  622. package/src/tools/memory/register.ts +4 -9
  623. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  624. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  625. package/src/tools/network/domain-normalize.ts +17 -0
  626. package/src/tools/network/web-fetch.ts +216 -73
  627. package/src/tools/network/web-search.ts +216 -98
  628. package/src/tools/registry.ts +7 -23
  629. package/src/tools/schema-transforms.ts +1 -1
  630. package/src/tools/skills/execute.ts +3 -9
  631. package/src/tools/skills/load.ts +3 -9
  632. package/src/tools/skills/skill-tool-factory.ts +1 -8
  633. package/src/tools/subagent/notify-parent.ts +3 -9
  634. package/src/tools/system/request-permission.ts +3 -9
  635. package/src/tools/terminal/safe-env.ts +3 -2
  636. package/src/tools/terminal/shell.ts +3 -9
  637. package/src/tools/tool-approval-handler.ts +19 -12
  638. package/src/tools/tool-defaults.ts +94 -0
  639. package/src/tools/types.ts +31 -98
  640. package/src/tools/ui-surface/definitions.ts +9 -23
  641. package/src/types/onboarding-context.ts +4 -0
  642. package/src/usage/pricing.ts +23 -0
  643. package/src/usage/types.ts +12 -0
  644. package/src/util/__tests__/favicon.test.ts +84 -0
  645. package/src/util/favicon.ts +40 -0
  646. package/src/util/logger.ts +16 -7
  647. package/src/util/platform.ts +7 -7
  648. package/src/util/sqlite3-runtime.ts +65 -0
  649. package/src/workspace/git-service.ts +75 -4
  650. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  651. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  652. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  653. package/src/workspace/migrations/registry.ts +4 -0
  654. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  655. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  656. package/src/config/bundled-skills/document/SKILL.md +0 -54
  657. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  658. package/src/daemon/seed-files.ts +0 -18
  659. package/src/prompts/cache-boundary.ts +0 -8
  660. package/src/runtime/routes/interface-routes.ts +0 -43
  661. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  662. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  663. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  664. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  665. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Run a SQL statement asynchronously, without blocking the daemon's
3
+ * main event loop.
4
+ *
5
+ * `bun:sqlite` is synchronous, so any long-running statement on the
6
+ * shared in-process connection stalls the event loop for the full
7
+ * duration of the statement. For multi-minute operations like `VACUUM`
8
+ * on a multi-GB database, this stalls every other piece of I/O —
9
+ * including the healthz handler — and on platform that has been
10
+ * observed to fail liveness probes and crashloop the pod.
11
+ *
12
+ * Backend selection:
13
+ * 1. **`sqlite3` CLI subprocess (preferred).** Spawn a child process
14
+ * that opens its own SQLite connection, runs the statement, and
15
+ * exits. The daemon's event loop is free for the full duration.
16
+ * SQLite's own file-locking arbitrates between the subprocess and
17
+ * the still-running in-process connection.
18
+ * 2. **In-process `bun:sqlite` (fallback).** Synchronous, blocking.
19
+ * Only fires when no `sqlite3` binary is on the host. This is the
20
+ * same behavior the daemon had before this abstraction existed,
21
+ * and is acceptable on desktop where no liveness probe is going to
22
+ * kill the process for a long stall.
23
+ *
24
+ * Use this for statements known to be slow (`VACUUM`, `ANALYZE`,
25
+ * `PRAGMA optimize`, large bulk `DELETE`/`UPDATE`). Fast queries (a few
26
+ * ms) should keep using the in-process drizzle / `bun:sqlite` handle
27
+ * directly — the subprocess overhead would dominate.
28
+ */
29
+ import { getLogger } from "../util/logger.js";
30
+ import { getDbPath } from "../util/platform.js";
31
+ import { findSqlite3 } from "../util/sqlite3-runtime.js";
32
+ import { getSqlite } from "./db-connection.js";
33
+
34
+ const log = getLogger("db-async-query");
35
+
36
+ /**
37
+ * Default wall-clock cap for an async statement. A real `VACUUM` on a
38
+ * multi-GB database can legitimately take many minutes; the cap is
39
+ * here to bound a runaway subprocess (e.g. one stuck on a stale file
40
+ * lock). Override per call via `runAsyncSqlite(sql, { timeoutMs })`.
41
+ */
42
+ const DEFAULT_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour
43
+
44
+ export type AsyncSqliteBackend = "sqlite3-cli" | "in-process-blocking";
45
+
46
+ export interface AsyncSqliteResult {
47
+ ok: boolean;
48
+ backend: AsyncSqliteBackend;
49
+ error: string | null;
50
+ elapsedMs: number;
51
+ stdout?: string;
52
+ stderr?: string;
53
+ timedOut?: boolean;
54
+ }
55
+
56
+ export interface RunAsyncSqliteOptions {
57
+ /** Override the default 1 h subprocess timeout. */
58
+ timeoutMs?: number;
59
+ /**
60
+ * Force a specific backend. Test-only; production callers should let
61
+ * the runtime pick.
62
+ */
63
+ forceBackend?: AsyncSqliteBackend;
64
+ }
65
+
66
+ let warnedAboutFallback = false;
67
+
68
+ export async function runAsyncSqlite(
69
+ sql: string,
70
+ options: RunAsyncSqliteOptions = {},
71
+ ): Promise<AsyncSqliteResult> {
72
+ const forced = options.forceBackend;
73
+ const sqlite3Path =
74
+ forced === "in-process-blocking" ? undefined : findSqlite3();
75
+
76
+ if (sqlite3Path && forced !== "in-process-blocking") {
77
+ return runViaCli(sqlite3Path, sql, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
78
+ }
79
+
80
+ if (!warnedAboutFallback) {
81
+ warnedAboutFallback = true;
82
+ log.warn(
83
+ "No sqlite3 CLI found on host — falling back to in-process blocking " +
84
+ "execution for slow SQLite statements. Install sqlite3 to keep the " +
85
+ "event loop responsive during VACUUM and other long operations.",
86
+ );
87
+ }
88
+ return runInProcessBlocking(sql);
89
+ }
90
+
91
+ /** For tests: reset the once-only fallback warning. */
92
+ export function _resetFallbackWarning(): void {
93
+ warnedAboutFallback = false;
94
+ }
95
+
96
+ async function runViaCli(
97
+ sqlite3Path: string,
98
+ sql: string,
99
+ timeoutMs: number,
100
+ ): Promise<AsyncSqliteResult> {
101
+ const startMs = Date.now();
102
+ const dbPath = getDbPath();
103
+
104
+ log.info(
105
+ { sqlite3Path, dbPath, timeoutMs, sqlPreview: sql.slice(0, 80) },
106
+ "Running async SQL via sqlite3 CLI subprocess",
107
+ );
108
+
109
+ // Pass the SQL via stdin rather than -cmd so newlines and quoting are
110
+ // never an issue regardless of the statement complexity.
111
+ const proc = Bun.spawn({
112
+ cmd: [sqlite3Path, dbPath],
113
+ stdin: "pipe",
114
+ stdout: "pipe",
115
+ stderr: "pipe",
116
+ });
117
+
118
+ // Write the SQL and close stdin so sqlite3 sees EOF and exits.
119
+ proc.stdin.write(sql + "\n");
120
+ await proc.stdin.end();
121
+
122
+ // Begin draining the streams immediately so the subprocess never
123
+ // blocks on a full pipe buffer.
124
+ const stdoutPromise = new Response(proc.stdout).text();
125
+ const stderrPromise = new Response(proc.stderr).text();
126
+
127
+ let timedOut = false;
128
+ const timer = setTimeout(() => {
129
+ timedOut = true;
130
+ proc.kill("SIGKILL");
131
+ }, timeoutMs);
132
+ if (typeof timer.unref === "function") timer.unref();
133
+
134
+ const exitCode = await proc.exited;
135
+ clearTimeout(timer);
136
+
137
+ const stdout = await stdoutPromise;
138
+ const stderr = await stderrPromise;
139
+ const elapsedMs = Date.now() - startMs;
140
+
141
+ if (timedOut) {
142
+ log.error(
143
+ { timeoutMs, elapsedMs, stderr: stderr.slice(0, 2000) },
144
+ "Async SQL subprocess timed out — killed",
145
+ );
146
+ return {
147
+ ok: false,
148
+ backend: "sqlite3-cli",
149
+ error: `sqlite3 subprocess timed out after ${timeoutMs}ms`,
150
+ elapsedMs,
151
+ stdout,
152
+ stderr,
153
+ timedOut: true,
154
+ };
155
+ }
156
+ if (exitCode !== 0) {
157
+ log.error(
158
+ { exitCode, elapsedMs, stderr: stderr.slice(0, 2000) },
159
+ "Async SQL subprocess failed",
160
+ );
161
+ return {
162
+ ok: false,
163
+ backend: "sqlite3-cli",
164
+ error: `sqlite3 exited with code ${exitCode}: ${stderr.slice(0, 500)}`,
165
+ elapsedMs,
166
+ stdout,
167
+ stderr,
168
+ };
169
+ }
170
+ return {
171
+ ok: true,
172
+ backend: "sqlite3-cli",
173
+ error: null,
174
+ elapsedMs,
175
+ stdout,
176
+ stderr,
177
+ };
178
+ }
179
+
180
+ async function runInProcessBlocking(sql: string): Promise<AsyncSqliteResult> {
181
+ const startMs = Date.now();
182
+ try {
183
+ const sqlite = getSqlite();
184
+ sqlite.exec(sql);
185
+ // Synthesize `stdout` to match what the CLI backend would emit
186
+ // when the caller chained `SELECT changes();` at the end of their
187
+ // SQL. `bun:sqlite`'s `exec()` discards SELECT results, so without
188
+ // this synthesis, callers that read `stdout` to get the row count
189
+ // (the prune jobs in cleanup.ts, for one) would see `undefined`
190
+ // and treat the run as "no rows deleted" — silently dropping the
191
+ // re-enqueue gate on every fallback host. Captured atomically with
192
+ // exec (same synchronous slice — no other code can run between
193
+ // these two lines), so the count is accurate for the SQL we just
194
+ // ran. Harmless for callers that don't read stdout.
195
+ const changes = (
196
+ sqlite.query("SELECT changes() AS c").get() as { c: number }
197
+ ).c;
198
+ return {
199
+ ok: true,
200
+ backend: "in-process-blocking",
201
+ error: null,
202
+ elapsedMs: Date.now() - startMs,
203
+ stdout: `${changes}\n`,
204
+ };
205
+ } catch (err) {
206
+ const message = err instanceof Error ? err.message : String(err);
207
+ return {
208
+ ok: false,
209
+ backend: "in-process-blocking",
210
+ error: message,
211
+ elapsedMs: Date.now() - startMs,
212
+ };
213
+ }
214
+ }
@@ -58,6 +58,7 @@ import {
58
58
  migrateCanonicalGuardianRequesterChatId,
59
59
  migrateCapabilityCardColumns,
60
60
  migrateChannelInboundDeliveredSegments,
61
+ migrateChannelInboundDeliveryAttempts,
61
62
  migrateChannelInteractionColumns,
62
63
  migrateContactChannelsAccessFields,
63
64
  migrateContactChannelsTypeChatIdIndex,
@@ -65,13 +66,16 @@ import {
65
66
  migrateContactsNotesColumn,
66
67
  migrateContactsRolePrincipal,
67
68
  migrateContactsUserFileColumn,
69
+ migrateConversationCleanedAt,
68
70
  migrateConversationForkLineage,
69
71
  migrateConversationHostAccess,
70
72
  migrateConversationInferenceProfileSession,
73
+ migrateConversationLastNotifiedProfile,
71
74
  migrateConversationsArchivedAt,
72
75
  migrateConversationsLastMessageAt,
73
76
  migrateConversationsThreadTypeIndex,
74
77
  migrateCreateConversationGraphMemoryState,
78
+ migrateCreateDocumentComments,
75
79
  migrateCreateDocumentConversations,
76
80
  migrateCreateMemoryGraphNodeEdits,
77
81
  migrateCreateMemoryGraphTables,
@@ -96,6 +100,7 @@ import {
96
100
  migrateDropSetupSkillIdColumn,
97
101
  migrateDropSimplifiedMemory,
98
102
  migrateDropUsageCompositeIndexes,
103
+ migrateExternalConversationBindingChatName,
99
104
  migrateExternalConversationBindingThreadId,
100
105
  migrateFkCascadeRebuilds,
101
106
  migrateGuardianActionFollowup,
@@ -113,15 +118,20 @@ import {
113
118
  migrateInviteCodeHashColumn,
114
119
  migrateInviteContactId,
115
120
  migrateLlmRequestLogAgentLoopExitReason,
121
+ migrateLlmRequestLogCallSite,
116
122
  migrateLlmRequestLogMessageId,
117
123
  migrateLlmRequestLogProvider,
118
124
  migrateLlmRequestLogsCreatedAtIndex,
125
+ migrateLlmUsageAddRawUsage,
119
126
  migrateLlmUsageAttribution,
120
127
  migrateMemoryGraphImageRefs,
121
128
  migrateMemoryItemSupersession,
122
129
  migrateMemoryRecallLogsQueryContext,
123
130
  migrateMemoryRetrospectiveState,
124
131
  migrateMemoryV2ActivationLogs,
132
+ migrateMemoryV2InjectionEvents,
133
+ migrateMemoryV3AutoEdges,
134
+ migrateMemoryV3Coactivation,
125
135
  migrateMessageBookmarks,
126
136
  migrateMessagesConversationCreatedAtIndex,
127
137
  migrateMessagesFtsBackfill,
@@ -144,10 +154,12 @@ import {
144
154
  migrateOAuthProvidersScopeSeparator,
145
155
  migrateOAuthProvidersTokenAuthMethodDefault,
146
156
  migrateOAuthProvidersTokenExchangeBodyFormat,
157
+ migrateOnboardingEventsPriorAssistants,
147
158
  migrateProviderConnectionBaseUrlAndModels,
148
159
  migrateProviderConnectionStatusLabel,
149
160
  migrateReminderRoutingIntent,
150
161
  migrateRemindersToSchedules,
162
+ migrateRenameCleanedAt,
151
163
  migrateRenameConversationTypeColumn,
152
164
  migrateRenameCreatedBySessionIdColumns,
153
165
  migrateRenameFollowupsThreadIdColumn,
@@ -174,6 +186,7 @@ import {
174
186
  migrateSchemaIndexesAndColumns,
175
187
  migrateScrubCorruptedImageAttachments,
176
188
  migrateSlackCompactionWatermark,
189
+ migrateStripBaseUrlNonOpenaiCompatible,
177
190
  migrateStripIntegrationPrefixFromProviderKeys,
178
191
  migrateStripPlaceholderSentinelsFromMessages,
179
192
  migrateStripThinkingFromConsolidated,
@@ -436,6 +449,19 @@ export function initializeDb(): void {
436
449
  migrateProviderConnectionBaseUrlAndModels,
437
450
  migrateA2ATasks,
438
451
  migrateLlmRequestLogAgentLoopExitReason,
452
+ migrateCreateDocumentComments,
453
+ migrateExternalConversationBindingChatName,
454
+ migrateChannelInboundDeliveryAttempts,
455
+ migrateMemoryV2InjectionEvents,
456
+ migrateConversationLastNotifiedProfile,
457
+ migrateStripBaseUrlNonOpenaiCompatible,
458
+ migrateOnboardingEventsPriorAssistants,
459
+ migrateConversationCleanedAt,
460
+ migrateRenameCleanedAt,
461
+ migrateLlmUsageAddRawUsage,
462
+ migrateMemoryV3Coactivation,
463
+ migrateMemoryV3AutoEdges,
464
+ migrateLlmRequestLogCallSite,
439
465
  ];
440
466
 
441
467
  // Run each migration step, catching and logging individual failures so one
@@ -3,6 +3,7 @@ import { statSync } from "node:fs";
3
3
  import { getLogger } from "../util/logger.js";
4
4
  import { getDbPath } from "../util/platform.js";
5
5
  import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
6
+ import { runAsyncSqlite } from "./db-async-query.js";
6
7
  import { getSqlite } from "./db-connection.js";
7
8
 
8
9
  const log = getLogger("db-maintenance");
@@ -11,7 +12,7 @@ const DB_MAINTENANCE_CHECKPOINT_KEY = "db_maintenance:last_run";
11
12
  const DB_MAINTENANCE_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
12
13
 
13
14
  interface DbStats {
14
- pageSizeBytes: number;
15
+ pageSize: number;
15
16
  pageCount: number;
16
17
  freelistCount: number;
17
18
  fileSizeBytes: number | null;
@@ -19,7 +20,7 @@ interface DbStats {
19
20
 
20
21
  function getDbStats(): DbStats {
21
22
  const sqlite = getSqlite();
22
- const pageSizeBytes = (
23
+ const pageSize = (
23
24
  sqlite.query("PRAGMA page_size").get() as { page_size: number }
24
25
  ).page_size;
25
26
  const pageCount = (
@@ -34,10 +35,10 @@ function getDbStats(): DbStats {
34
35
  } catch {
35
36
  /* non-fatal */
36
37
  }
37
- return { pageSizeBytes, pageCount, freelistCount, fileSizeBytes };
38
+ return { pageSize, pageCount, freelistCount, fileSizeBytes };
38
39
  }
39
40
 
40
- function runDbMaintenance(): void {
41
+ async function runDbMaintenance(): Promise<void> {
41
42
  const before = getDbStats();
42
43
  const freelistPct =
43
44
  before.pageCount > 0
@@ -54,22 +55,24 @@ function runDbMaintenance(): void {
54
55
  "Starting database maintenance",
55
56
  );
56
57
 
57
- try {
58
- getSqlite().exec("VACUUM");
59
- } catch (err) {
60
- log.warn({ err }, "VACUUM failed (non-fatal)");
61
- try {
62
- getSqlite().exec("PRAGMA optimize");
63
- } catch (optErr) {
64
- log.warn({ err: optErr }, "PRAGMA optimize failed (non-fatal)");
65
- }
66
- return;
58
+ // VACUUM is the long-running one — minutes on a multi-GB DB. PRAGMA
59
+ // optimize is fast but routed through the same async path for
60
+ // consistency and to keep both off the main thread when the CLI
61
+ // backend is available.
62
+ const vacuumResult = await runAsyncSqlite("VACUUM");
63
+ if (!vacuumResult.ok) {
64
+ log.warn(
65
+ { error: vacuumResult.error, backend: vacuumResult.backend },
66
+ "VACUUM failed (non-fatal)",
67
+ );
67
68
  }
68
69
 
69
- try {
70
- getSqlite().exec("PRAGMA optimize");
71
- } catch (err) {
72
- log.warn({ err }, "PRAGMA optimize failed (non-fatal)");
70
+ const optimizeResult = await runAsyncSqlite("PRAGMA optimize");
71
+ if (!optimizeResult.ok) {
72
+ log.warn(
73
+ { error: optimizeResult.error, backend: optimizeResult.backend },
74
+ "PRAGMA optimize failed (non-fatal)",
75
+ );
73
76
  }
74
77
 
75
78
  const after = getDbStats();
@@ -81,17 +84,23 @@ function runDbMaintenance(): void {
81
84
 
82
85
  log.info(
83
86
  {
87
+ backend: vacuumResult.backend,
88
+ vacuumOk: vacuumResult.ok,
89
+ optimizeOk: optimizeResult.ok,
90
+ vacuumElapsedMs: vacuumResult.elapsedMs,
91
+ optimizeElapsedMs: optimizeResult.elapsedMs,
84
92
  beforePageCount: before.pageCount,
85
93
  afterPageCount: after.pageCount,
86
94
  reclaimedPages,
87
- reclaimedBytes,
95
+ beforeFileSizeBytes: before.fileSizeBytes,
88
96
  afterFileSizeBytes: after.fileSizeBytes,
97
+ reclaimedBytes,
89
98
  },
90
99
  "Database maintenance complete",
91
100
  );
92
101
  }
93
102
 
94
- export function maybeRunDbMaintenance(nowMs = Date.now()): void {
103
+ export async function maybeRunDbMaintenance(nowMs = Date.now()): Promise<void> {
95
104
  const lastRun = parseInt(
96
105
  getMemoryCheckpoint(DB_MAINTENANCE_CHECKPOINT_KEY) ?? "0",
97
106
  10,
@@ -99,7 +108,7 @@ export function maybeRunDbMaintenance(nowMs = Date.now()): void {
99
108
  if (nowMs - lastRun < DB_MAINTENANCE_INTERVAL_MS) return;
100
109
 
101
110
  try {
102
- runDbMaintenance();
111
+ await runDbMaintenance();
103
112
  } catch (err) {
104
113
  log.error({ err }, "Database maintenance failed unexpectedly");
105
114
  }
@@ -313,6 +313,47 @@ export function storePayload(
313
313
  .run();
314
314
  }
315
315
 
316
+ /**
317
+ * Persist the assistant reply row generated for an inbound event so callback
318
+ * delivery retries target that exact response instead of the latest message in
319
+ * the conversation.
320
+ */
321
+ export function storeReplyMessageId(
322
+ eventId: string,
323
+ replyMessageId: string,
324
+ ): void {
325
+ const db = getDb();
326
+ const row = db
327
+ .select({ rawPayload: channelInboundEvents.rawPayload })
328
+ .from(channelInboundEvents)
329
+ .where(eq(channelInboundEvents.id, eventId))
330
+ .get();
331
+ if (!row?.rawPayload) return;
332
+
333
+ let payload: Record<string, unknown>;
334
+ try {
335
+ const parsed = JSON.parse(row.rawPayload) as unknown;
336
+ if (
337
+ parsed === null ||
338
+ typeof parsed !== "object" ||
339
+ Array.isArray(parsed)
340
+ ) {
341
+ return;
342
+ }
343
+ payload = parsed as Record<string, unknown>;
344
+ } catch {
345
+ return;
346
+ }
347
+
348
+ db.update(channelInboundEvents)
349
+ .set({
350
+ rawPayload: JSON.stringify({ ...payload, replyMessageId }),
351
+ updatedAt: Date.now(),
352
+ })
353
+ .where(eq(channelInboundEvents.id, eventId))
354
+ .run();
355
+ }
356
+
316
357
  /**
317
358
  * Clear a previously stored payload. Used when the ingress check
318
359
  * detects secret-bearing content — the payload must not remain on disk.
@@ -6,7 +6,7 @@
6
6
  * retryable and dead-lettered events, and replaying dead letters.
7
7
  */
8
8
 
9
- import { and, eq, lte } from "drizzle-orm";
9
+ import { and, eq, lte, or } from "drizzle-orm";
10
10
 
11
11
  import { getDb } from "./db-connection.js";
12
12
  import {
@@ -44,6 +44,7 @@ export function acknowledgeDelivery(
44
44
  db.update(channelInboundEvents)
45
45
  .set({
46
46
  deliveryStatus: "delivered",
47
+ retryAfter: null,
47
48
  updatedAt: now,
48
49
  })
49
50
  .where(eq(channelInboundEvents.id, existing.id))
@@ -61,6 +62,23 @@ export function markProcessed(eventId: string): void {
61
62
  .run();
62
63
  }
63
64
 
65
+ /** Mark an event's outbound callback delivery as complete. */
66
+ export function markDeliveryDelivered(eventId: string): void {
67
+ const db = getDb();
68
+ db.update(channelInboundEvents)
69
+ .set({
70
+ deliveryStatus: "delivered",
71
+ retryAfter: null,
72
+ updatedAt: Date.now(),
73
+ })
74
+ .where(eq(channelInboundEvents.id, eventId))
75
+ .run();
76
+ }
77
+
78
+ function errorMessage(err: unknown): string {
79
+ return err instanceof Error ? err.message : String(err);
80
+ }
81
+
64
82
  /**
65
83
  * Record a processing failure. Classifies the error to decide whether
66
84
  * the event should be retried (status='failed') or dead-lettered
@@ -79,7 +97,7 @@ export function recordProcessingFailure(eventId: string, err: unknown): void {
79
97
 
80
98
  const attempts = (row?.attempts ?? 0) + 1;
81
99
  const category = classifyError(err);
82
- const errorMsg = err instanceof Error ? err.message : String(err);
100
+ const errorMsg = errorMessage(err);
83
101
 
84
102
  if (category === "fatal" || attempts >= RETRY_MAX_ATTEMPTS) {
85
103
  db.update(channelInboundEvents)
@@ -107,6 +125,51 @@ export function recordProcessingFailure(eventId: string, err: unknown): void {
107
125
  }
108
126
  }
109
127
 
128
+ /**
129
+ * Record an outbound callback delivery failure without changing the processing
130
+ * status. Delivery uses its own retry budget so a turn that needed processing
131
+ * retries still gets a full delivery retry window.
132
+ */
133
+ export function recordDeliveryFailure(eventId: string, err: unknown): void {
134
+ const db = getDb();
135
+ const now = Date.now();
136
+
137
+ const row = db
138
+ .select({ attempts: channelInboundEvents.deliveryAttempts })
139
+ .from(channelInboundEvents)
140
+ .where(eq(channelInboundEvents.id, eventId))
141
+ .get();
142
+
143
+ const attempts = (row?.attempts ?? 0) + 1;
144
+ const category = classifyError(err);
145
+ const errorMsg = errorMessage(err);
146
+
147
+ if (category === "fatal" || attempts >= RETRY_MAX_ATTEMPTS) {
148
+ db.update(channelInboundEvents)
149
+ .set({
150
+ deliveryStatus: "dead_letter",
151
+ deliveryAttempts: attempts,
152
+ lastProcessingError: errorMsg,
153
+ retryAfter: null,
154
+ updatedAt: now,
155
+ })
156
+ .where(eq(channelInboundEvents.id, eventId))
157
+ .run();
158
+ } else {
159
+ const delay = retryDelayForAttempt(attempts);
160
+ db.update(channelInboundEvents)
161
+ .set({
162
+ deliveryStatus: "failed",
163
+ deliveryAttempts: attempts,
164
+ lastProcessingError: errorMsg,
165
+ retryAfter: now + delay,
166
+ updatedAt: now,
167
+ })
168
+ .where(eq(channelInboundEvents.id, eventId))
169
+ .run();
170
+ }
171
+ }
172
+
110
173
  /**
111
174
  * Mark an event as failed with a specific error message, bypassing error
112
175
  * classification. Use this when the failure reason is known and the event
@@ -180,6 +243,38 @@ export function getRetryableEvents(limit = 20): Array<{
180
243
  .all();
181
244
  }
182
245
 
246
+ /** Fetch callback deliveries eligible for retry without rerunning processing. */
247
+ export function getRetryableDeliveryEvents(limit = 20): Array<{
248
+ id: string;
249
+ conversationId: string;
250
+ messageId: string | null;
251
+ processingAttempts: number;
252
+ rawPayload: string | null;
253
+ deliveredSegmentCount: number;
254
+ }> {
255
+ const db = getDb();
256
+ const now = Date.now();
257
+ return db
258
+ .select({
259
+ id: channelInboundEvents.id,
260
+ conversationId: channelInboundEvents.conversationId,
261
+ messageId: channelInboundEvents.messageId,
262
+ processingAttempts: channelInboundEvents.processingAttempts,
263
+ rawPayload: channelInboundEvents.rawPayload,
264
+ deliveredSegmentCount: channelInboundEvents.deliveredSegmentCount,
265
+ })
266
+ .from(channelInboundEvents)
267
+ .where(
268
+ and(
269
+ eq(channelInboundEvents.processingStatus, "processed"),
270
+ eq(channelInboundEvents.deliveryStatus, "failed"),
271
+ lte(channelInboundEvents.retryAfter, now),
272
+ ),
273
+ )
274
+ .limit(limit)
275
+ .all();
276
+ }
277
+
183
278
  /** Fetch dead-lettered events. */
184
279
  export function getDeadLetterEvents(): Array<{
185
280
  id: string;
@@ -204,7 +299,15 @@ export function getDeadLetterEvents(): Array<{
204
299
  createdAt: channelInboundEvents.createdAt,
205
300
  })
206
301
  .from(channelInboundEvents)
207
- .where(eq(channelInboundEvents.processingStatus, "dead_letter"))
302
+ .where(
303
+ or(
304
+ eq(channelInboundEvents.processingStatus, "dead_letter"),
305
+ and(
306
+ eq(channelInboundEvents.processingStatus, "processed"),
307
+ eq(channelInboundEvents.deliveryStatus, "dead_letter"),
308
+ ),
309
+ ),
310
+ )
208
311
  .all();
209
312
  }
210
313
 
@@ -218,27 +321,50 @@ export function replayDeadLetters(eventIds: string[]): number {
218
321
  let count = 0;
219
322
  for (const id of eventIds) {
220
323
  const existing = db
221
- .select({ id: channelInboundEvents.id })
324
+ .select({
325
+ id: channelInboundEvents.id,
326
+ processingStatus: channelInboundEvents.processingStatus,
327
+ deliveryStatus: channelInboundEvents.deliveryStatus,
328
+ })
222
329
  .from(channelInboundEvents)
223
330
  .where(
224
331
  and(
225
332
  eq(channelInboundEvents.id, id),
226
- eq(channelInboundEvents.processingStatus, "dead_letter"),
333
+ or(
334
+ eq(channelInboundEvents.processingStatus, "dead_letter"),
335
+ and(
336
+ eq(channelInboundEvents.processingStatus, "processed"),
337
+ eq(channelInboundEvents.deliveryStatus, "dead_letter"),
338
+ ),
339
+ ),
227
340
  ),
228
341
  )
229
342
  .get();
230
343
  if (!existing) continue;
231
344
 
232
- db.update(channelInboundEvents)
233
- .set({
234
- processingStatus: "failed",
235
- processingAttempts: 0,
236
- lastProcessingError: null,
237
- retryAfter: now,
238
- updatedAt: now,
239
- })
240
- .where(eq(channelInboundEvents.id, id))
241
- .run();
345
+ if (existing.processingStatus === "dead_letter") {
346
+ db.update(channelInboundEvents)
347
+ .set({
348
+ processingStatus: "failed",
349
+ processingAttempts: 0,
350
+ lastProcessingError: null,
351
+ retryAfter: now,
352
+ updatedAt: now,
353
+ })
354
+ .where(eq(channelInboundEvents.id, id))
355
+ .run();
356
+ } else {
357
+ db.update(channelInboundEvents)
358
+ .set({
359
+ deliveryStatus: "failed",
360
+ deliveryAttempts: 0,
361
+ lastProcessingError: null,
362
+ retryAfter: now,
363
+ updatedAt: now,
364
+ })
365
+ .where(eq(channelInboundEvents.id, id))
366
+ .run();
367
+ }
242
368
  count++;
243
369
  }
244
370
  return count;