@vellumai/assistant 0.5.1 → 0.5.3

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 (405) hide show
  1. package/ARCHITECTURE.md +163 -54
  2. package/docs/architecture/integrations.md +62 -67
  3. package/docs/credential-execution-service.md +3 -3
  4. package/docs/skills.md +100 -0
  5. package/package.json +1 -1
  6. package/src/__tests__/agent-loop.test.ts +111 -0
  7. package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
  8. package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
  9. package/src/__tests__/app-dir-path-guard.test.ts +78 -0
  10. package/src/__tests__/app-executors.test.ts +1 -291
  11. package/src/__tests__/app-git-history.test.ts +4 -4
  12. package/src/__tests__/app-routes-csp.test.ts +1 -0
  13. package/src/__tests__/app-store-dir-names.test.ts +426 -0
  14. package/src/__tests__/attachments-store.test.ts +169 -21
  15. package/src/__tests__/attachments.test.ts +115 -1
  16. package/src/__tests__/btw-routes.test.ts +1 -0
  17. package/src/__tests__/canonical-guardian-store.test.ts +38 -0
  18. package/src/__tests__/channel-reply-delivery.test.ts +55 -0
  19. package/src/__tests__/checker.test.ts +54 -0
  20. package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
  21. package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
  22. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  23. package/src/__tests__/config-schema-cmd.test.ts +68 -21
  24. package/src/__tests__/config-schema.test.ts +1 -1
  25. package/src/__tests__/conversation-agent-loop-overflow.test.ts +156 -5
  26. package/src/__tests__/conversation-agent-loop.test.ts +297 -2
  27. package/src/__tests__/conversation-attachments.test.ts +17 -19
  28. package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
  29. package/src/__tests__/conversation-disk-view.test.ts +810 -0
  30. package/src/__tests__/conversation-error.test.ts +1 -1
  31. package/src/__tests__/conversation-fork-crud.test.ts +551 -0
  32. package/src/__tests__/conversation-fork-route.test.ts +386 -0
  33. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  34. package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
  35. package/src/__tests__/conversation-media-retry.test.ts +8 -2
  36. package/src/__tests__/conversation-memory-dirty-tail.test.ts +150 -0
  37. package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
  38. package/src/__tests__/conversation-queue.test.ts +36 -1
  39. package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
  40. package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
  42. package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
  43. package/src/__tests__/conversation-skill-tools.test.ts +4 -9
  44. package/src/__tests__/conversation-slash-commands.test.ts +149 -0
  45. package/src/__tests__/conversation-store.test.ts +24 -21
  46. package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
  47. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
  48. package/src/__tests__/conversation-title-service.test.ts +137 -0
  49. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
  50. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
  51. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
  52. package/src/__tests__/conversation-wipe.test.ts +226 -0
  53. package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
  54. package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
  55. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  56. package/src/__tests__/credential-vault-unit.test.ts +5 -10
  57. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  58. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
  59. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
  60. package/src/__tests__/db-memory-archive-migration.test.ts +372 -0
  61. package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
  62. package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
  63. package/src/__tests__/diagnostics-export.test.ts +70 -1
  64. package/src/__tests__/first-greeting.test.ts +80 -0
  65. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  66. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
  67. package/src/__tests__/history-repair.test.ts +32 -10
  68. package/src/__tests__/http-conversation-lineage.test.ts +251 -0
  69. package/src/__tests__/image-source-path-reinject.test.ts +136 -0
  70. package/src/__tests__/inline-command-runner.test.ts +311 -0
  71. package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
  72. package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
  73. package/src/__tests__/list-messages-attachments.test.ts +96 -0
  74. package/src/__tests__/llm-context-normalization.test.ts +1116 -0
  75. package/src/__tests__/llm-context-route-provider.test.ts +217 -0
  76. package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
  77. package/src/__tests__/media-generate-image.test.ts +47 -94
  78. package/src/__tests__/memory-brief-open-loops.test.ts +530 -0
  79. package/src/__tests__/memory-brief-time.test.ts +285 -0
  80. package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
  81. package/src/__tests__/memory-chunk-archive.test.ts +400 -0
  82. package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
  83. package/src/__tests__/memory-episode-archive.test.ts +370 -0
  84. package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
  85. package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
  86. package/src/__tests__/memory-observation-archive.test.ts +375 -0
  87. package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
  88. package/src/__tests__/memory-recall-quality.test.ts +7 -7
  89. package/src/__tests__/memory-reducer-store.test.ts +728 -0
  90. package/src/__tests__/memory-reducer-types.test.ts +699 -0
  91. package/src/__tests__/memory-reducer.test.ts +698 -0
  92. package/src/__tests__/memory-regressions.test.ts +6 -4
  93. package/src/__tests__/memory-simplified-config.test.ts +281 -0
  94. package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
  95. package/src/__tests__/migration-export-http.test.ts +3 -1
  96. package/src/__tests__/migration-import-commit-http.test.ts +18 -4
  97. package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
  98. package/src/__tests__/mime-builder.test.ts +3 -2
  99. package/src/__tests__/non-member-access-request.test.ts +12 -1
  100. package/src/__tests__/notification-decision-identity.test.ts +52 -0
  101. package/src/__tests__/oauth-apps-routes.test.ts +103 -0
  102. package/src/__tests__/oauth-store.test.ts +115 -0
  103. package/src/__tests__/parse-identity-fields.test.ts +129 -0
  104. package/src/__tests__/provider-error-scenarios.test.ts +1 -3
  105. package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
  106. package/src/__tests__/recording-handler.test.ts +17 -0
  107. package/src/__tests__/registry.test.ts +3 -8
  108. package/src/__tests__/relay-server.test.ts +1 -1
  109. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
  110. package/src/__tests__/schema-transforms.test.ts +165 -5
  111. package/src/__tests__/server-history-render.test.ts +2 -2
  112. package/src/__tests__/skill-load-inline-command.test.ts +598 -0
  113. package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
  114. package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
  115. package/src/__tests__/skills-transitive-hash.test.ts +333 -0
  116. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  117. package/src/__tests__/slack-inbound-verification.test.ts +2 -2
  118. package/src/__tests__/starter-task-flow.test.ts +1 -0
  119. package/src/__tests__/suggestion-routes.test.ts +443 -0
  120. package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
  121. package/src/__tests__/swarm-recursion.test.ts +1 -0
  122. package/src/__tests__/swarm-tool.test.ts +1 -0
  123. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
  124. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
  125. package/src/__tests__/top-level-renderer.test.ts +22 -0
  126. package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
  127. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +320 -0
  128. package/src/__tests__/web-fetch.test.ts +6 -2
  129. package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
  130. package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
  131. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
  132. package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
  133. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
  134. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
  135. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
  136. package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
  137. package/src/agent/attachments.ts +27 -1
  138. package/src/agent/loop.ts +29 -1
  139. package/src/avatar/traits-png-sync.ts +80 -25
  140. package/src/bundler/app-bundler.ts +4 -4
  141. package/src/calls/call-domain.ts +1 -0
  142. package/src/calls/voice-session-bridge.ts +1 -0
  143. package/src/cli/commands/auth.ts +92 -0
  144. package/src/cli/commands/avatar.ts +7 -6
  145. package/src/cli/commands/config.ts +2 -0
  146. package/src/cli/commands/oauth/providers.ts +29 -0
  147. package/src/cli/program.ts +12 -0
  148. package/src/cli.ts +15 -48
  149. package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
  150. package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
  151. package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
  152. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
  153. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
  154. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
  155. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
  156. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
  157. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
  158. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
  159. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
  160. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
  161. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
  162. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
  163. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
  164. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
  165. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
  166. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  167. package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
  168. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
  169. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
  170. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
  171. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  172. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
  173. package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
  174. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  175. package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
  176. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
  177. package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
  178. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
  179. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
  180. package/src/config/bundled-tool-registry.ts +2 -14
  181. package/src/config/feature-flag-registry.json +24 -0
  182. package/src/config/loader.ts +65 -0
  183. package/src/config/raw-config-utils.ts +58 -0
  184. package/src/config/schema-utils.ts +28 -7
  185. package/src/config/schema.ts +20 -0
  186. package/src/config/schemas/elevenlabs.ts +18 -0
  187. package/src/config/schemas/memory-lifecycle.ts +4 -2
  188. package/src/config/schemas/memory-simplified.ts +101 -0
  189. package/src/config/schemas/memory-storage.ts +1 -1
  190. package/src/config/schemas/memory.ts +4 -0
  191. package/src/config/schemas/services.ts +8 -6
  192. package/src/config/skills.ts +50 -4
  193. package/src/contacts/contact-store.ts +13 -6
  194. package/src/contacts/contacts-write.ts +0 -1
  195. package/src/context/window-manager.ts +13 -2
  196. package/src/daemon/conversation-agent-loop-handlers.ts +54 -8
  197. package/src/daemon/conversation-agent-loop.ts +127 -20
  198. package/src/daemon/conversation-attachments.ts +18 -36
  199. package/src/daemon/conversation-error.ts +2 -1
  200. package/src/daemon/conversation-history.ts +18 -4
  201. package/src/daemon/conversation-lifecycle.ts +50 -16
  202. package/src/daemon/conversation-messaging.ts +70 -26
  203. package/src/daemon/conversation-process.ts +58 -34
  204. package/src/daemon/conversation-runtime-assembly.ts +22 -38
  205. package/src/daemon/conversation-slash.ts +121 -256
  206. package/src/daemon/conversation-surfaces.ts +170 -24
  207. package/src/daemon/conversation-tool-setup.ts +0 -6
  208. package/src/daemon/conversation-workspace.ts +21 -1
  209. package/src/daemon/conversation.ts +69 -30
  210. package/src/daemon/first-greeting.ts +35 -0
  211. package/src/daemon/handlers/config-embeddings.ts +156 -0
  212. package/src/daemon/handlers/config-model.ts +62 -26
  213. package/src/daemon/handlers/conversations.ts +0 -23
  214. package/src/daemon/handlers/identity.ts +12 -1
  215. package/src/daemon/handlers/recording.ts +26 -21
  216. package/src/daemon/host-cu-proxy.ts +2 -2
  217. package/src/daemon/lifecycle.ts +115 -65
  218. package/src/daemon/message-protocol.ts +3 -0
  219. package/src/daemon/message-types/conversations.ts +18 -0
  220. package/src/daemon/message-types/messages.ts +1 -0
  221. package/src/daemon/message-types/shared.ts +2 -0
  222. package/src/daemon/message-types/surfaces.ts +2 -0
  223. package/src/daemon/message-types/upgrades.ts +23 -0
  224. package/src/daemon/server.ts +83 -12
  225. package/src/daemon/shutdown-handlers.ts +8 -5
  226. package/src/daemon/startup-error.ts +9 -0
  227. package/src/daemon/tool-side-effects.ts +11 -28
  228. package/src/events/tool-permission-telemetry-listener.ts +1 -3
  229. package/src/followups/followup-store.ts +47 -1
  230. package/src/instrument.ts +0 -4
  231. package/src/media/app-icon-generator.ts +2 -2
  232. package/src/memory/app-git-service.ts +28 -16
  233. package/src/memory/app-store.ts +230 -41
  234. package/src/memory/archive-store.ts +400 -0
  235. package/src/memory/attachments-store.ts +558 -130
  236. package/src/memory/brief-formatting.ts +33 -0
  237. package/src/memory/brief-open-loops.ts +266 -0
  238. package/src/memory/brief-time.ts +161 -0
  239. package/src/memory/brief.ts +75 -0
  240. package/src/memory/conversation-attention-store.ts +70 -0
  241. package/src/memory/conversation-crud.ts +591 -8
  242. package/src/memory/conversation-directories.ts +125 -0
  243. package/src/memory/conversation-disk-view.ts +390 -0
  244. package/src/memory/conversation-key-store.ts +17 -5
  245. package/src/memory/conversation-queries.ts +5 -1
  246. package/src/memory/conversation-title-service.ts +21 -49
  247. package/src/memory/db-init.ts +40 -0
  248. package/src/memory/embedding-backend.ts +42 -53
  249. package/src/memory/embedding-gemini.test.ts +4 -4
  250. package/src/memory/embedding-local.ts +1 -3
  251. package/src/memory/embedding-ollama.ts +1 -3
  252. package/src/memory/embedding-openai.ts +1 -3
  253. package/src/memory/indexer.ts +114 -21
  254. package/src/memory/items-extractor.ts +42 -13
  255. package/src/memory/job-handlers/conversation-starters.ts +6 -1
  256. package/src/memory/job-handlers/embedding.test.ts +2 -4
  257. package/src/memory/job-handlers/embedding.ts +83 -0
  258. package/src/memory/job-utils.ts +1 -1
  259. package/src/memory/jobs-store.ts +6 -0
  260. package/src/memory/jobs-worker.ts +12 -0
  261. package/src/memory/llm-request-log-store.ts +100 -1
  262. package/src/memory/migrations/102-alter-table-columns.ts +5 -0
  263. package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
  264. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
  265. package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
  266. package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
  267. package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
  268. package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
  269. package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
  270. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
  271. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
  272. package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
  273. package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
  274. package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
  275. package/src/memory/migrations/185-memory-brief-state.ts +52 -0
  276. package/src/memory/migrations/186-memory-archive.ts +109 -0
  277. package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
  278. package/src/memory/migrations/index.ts +10 -0
  279. package/src/memory/migrations/registry.ts +13 -0
  280. package/src/memory/qdrant-client.ts +23 -4
  281. package/src/memory/reducer-store.ts +271 -0
  282. package/src/memory/reducer-types.ts +99 -0
  283. package/src/memory/reducer.ts +453 -0
  284. package/src/memory/retriever.test.ts +601 -2
  285. package/src/memory/retriever.ts +85 -9
  286. package/src/memory/schema/conversations.ts +9 -0
  287. package/src/memory/schema/index.ts +2 -0
  288. package/src/memory/schema/infrastructure.ts +13 -7
  289. package/src/memory/schema/memory-archive.ts +121 -0
  290. package/src/memory/schema/memory-brief.ts +55 -0
  291. package/src/memory/schema/oauth.ts +6 -0
  292. package/src/memory/search/semantic.ts +17 -4
  293. package/src/messaging/providers/gmail/mime-builder.ts +3 -1
  294. package/src/notifications/copy-composer.ts +26 -0
  295. package/src/notifications/decision-engine.ts +14 -1
  296. package/src/notifications/emit-signal.ts +1 -1
  297. package/src/notifications/signal.ts +36 -0
  298. package/src/oauth/byo-connection.test.ts +1 -45
  299. package/src/oauth/byo-connection.ts +2 -8
  300. package/src/oauth/connect-orchestrator.ts +15 -11
  301. package/src/oauth/connection-resolver.test.ts +191 -0
  302. package/src/oauth/connection-resolver.ts +66 -38
  303. package/src/oauth/connection.ts +0 -1
  304. package/src/oauth/oauth-store.ts +99 -47
  305. package/src/oauth/platform-connection.test.ts +0 -1
  306. package/src/oauth/platform-connection.ts +11 -3
  307. package/src/oauth/seed-providers.ts +78 -3
  308. package/src/oauth/token-persistence.ts +16 -10
  309. package/src/permissions/checker.ts +160 -14
  310. package/src/permissions/defaults.ts +14 -0
  311. package/src/prompts/templates/BOOTSTRAP.md +2 -0
  312. package/src/providers/anthropic/client.ts +8 -1
  313. package/src/providers/failover.ts +4 -1
  314. package/src/providers/gemini/client.ts +50 -0
  315. package/src/providers/model-catalog.ts +92 -0
  316. package/src/providers/model-intents.ts +29 -20
  317. package/src/providers/openai/client.ts +49 -0
  318. package/src/providers/types.ts +2 -0
  319. package/src/runtime/access-request-helper.ts +16 -7
  320. package/src/runtime/auth/credential-service.ts +3 -1
  321. package/src/runtime/auth/route-policy.ts +14 -1
  322. package/src/runtime/btw-sidechain.ts +101 -0
  323. package/src/runtime/channel-reply-delivery.ts +17 -1
  324. package/src/runtime/http-router.ts +3 -1
  325. package/src/runtime/http-server.ts +196 -141
  326. package/src/runtime/http-types.ts +1 -0
  327. package/src/runtime/migrations/vbundle-builder.ts +5 -1
  328. package/src/runtime/routes/access-request-decision.ts +41 -0
  329. package/src/runtime/routes/app-management-routes.ts +6 -3
  330. package/src/runtime/routes/app-routes.ts +7 -3
  331. package/src/runtime/routes/approval-routes.ts +1 -0
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
  333. package/src/runtime/routes/attachment-routes.ts +45 -15
  334. package/src/runtime/routes/btw-routes.ts +21 -61
  335. package/src/runtime/routes/conversation-management-routes.ts +74 -0
  336. package/src/runtime/routes/conversation-query-routes.ts +187 -10
  337. package/src/runtime/routes/conversation-routes.ts +269 -28
  338. package/src/runtime/routes/conversation-starter-routes.ts +9 -11
  339. package/src/runtime/routes/diagnostics-routes.ts +1 -0
  340. package/src/runtime/routes/identity-routes.ts +2 -35
  341. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
  342. package/src/runtime/routes/llm-context-normalization.ts +1212 -0
  343. package/src/runtime/routes/log-export-routes.ts +3 -0
  344. package/src/runtime/routes/memory-item-routes.test.ts +34 -0
  345. package/src/runtime/routes/memory-item-routes.ts +94 -5
  346. package/src/runtime/routes/migration-routes.ts +4 -1
  347. package/src/runtime/routes/oauth-apps.ts +291 -0
  348. package/src/runtime/routes/secret-routes.ts +30 -1
  349. package/src/runtime/routes/settings-routes.ts +14 -0
  350. package/src/runtime/routes/surface-action-routes.ts +68 -1
  351. package/src/runtime/routes/trace-event-routes.ts +4 -1
  352. package/src/schedule/schedule-store.ts +30 -21
  353. package/src/security/secure-keys.ts +21 -0
  354. package/src/signals/bash.ts +1 -1
  355. package/src/skills/inline-command-expansions.ts +204 -0
  356. package/src/skills/inline-command-render.ts +127 -0
  357. package/src/skills/inline-command-runner.ts +242 -0
  358. package/src/skills/transitive-version-hash.ts +88 -0
  359. package/src/swarm/backend-claude-code.ts +3 -6
  360. package/src/tasks/task-store.ts +43 -1
  361. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  362. package/src/telemetry/usage-telemetry-reporter.ts +3 -1
  363. package/src/tools/AGENTS.md +6 -10
  364. package/src/tools/apps/executors.ts +17 -232
  365. package/src/tools/claude-code/claude-code.ts +2 -3
  366. package/src/tools/credentials/vault.ts +7 -12
  367. package/src/tools/host-filesystem/read.ts +13 -10
  368. package/src/tools/network/__tests__/web-search.test.ts +4 -2
  369. package/src/tools/permission-checker.ts +8 -1
  370. package/src/tools/schedule/list.ts +2 -7
  371. package/src/tools/schema-transforms.ts +5 -0
  372. package/src/tools/shared/filesystem/format-diff.ts +2 -7
  373. package/src/tools/skills/execute.ts +1 -1
  374. package/src/tools/skills/load.ts +140 -6
  375. package/src/tools/tool-manifest.ts +0 -6
  376. package/src/tools/ui-surface/definitions.ts +2 -2
  377. package/src/util/device-id.ts +28 -5
  378. package/src/util/platform.ts +24 -0
  379. package/src/util/pricing.ts +1 -0
  380. package/src/util/retry.ts +1 -3
  381. package/src/workspace/migrations/003-seed-device-id.ts +3 -4
  382. package/src/workspace/migrations/006-services-config.ts +5 -0
  383. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
  384. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
  385. package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
  386. package/src/workspace/migrations/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +24 -13
  387. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
  388. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
  389. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
  390. package/src/workspace/migrations/registry.ts +11 -1
  391. package/src/workspace/top-level-renderer.ts +12 -0
  392. package/src/__tests__/asset-materialize-tool.test.ts +0 -523
  393. package/src/__tests__/asset-search-tool.test.ts +0 -536
  394. package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
  395. package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
  396. package/src/__tests__/media-visibility-policy.test.ts +0 -190
  397. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
  398. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
  399. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
  400. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
  401. package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
  402. package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
  403. package/src/daemon/media-visibility-policy.ts +0 -59
  404. package/src/tools/assets/materialize.ts +0 -248
  405. package/src/tools/assets/search.ts +0 -400
@@ -11,7 +11,10 @@ import type { MemoryJob } from "../jobs-store.js";
11
11
  import { extractMediaBlocks } from "../message-content.js";
12
12
  import {
13
13
  mediaAssets,
14
+ memoryChunks,
15
+ memoryEpisodes,
14
16
  memoryItems,
17
+ memoryObservations,
15
18
  memorySegments,
16
19
  memorySummaries,
17
20
  messages,
@@ -34,6 +37,7 @@ export async function embedSegmentJob(
34
37
  conversation_id: segment.conversationId,
35
38
  message_id: segment.messageId,
36
39
  created_at: segment.createdAt,
40
+ memory_scope_id: segment.scopeId,
37
41
  });
38
42
  }
39
43
 
@@ -58,6 +62,7 @@ export async function embedItemJob(
58
62
  confidence: item.confidence,
59
63
  created_at: item.firstSeenAt,
60
64
  last_seen_at: item.lastSeenAt,
65
+ memory_scope_id: item.scopeId,
61
66
  });
62
67
  }
63
68
 
@@ -83,10 +88,31 @@ export async function embedSummaryJob(
83
88
  kind: summary.scope,
84
89
  created_at: summary.startAt,
85
90
  last_seen_at: summary.endAt,
91
+ memory_scope_id: summary.scopeId,
86
92
  },
87
93
  );
88
94
  }
89
95
 
96
+ export async function embedChunkJob(
97
+ job: MemoryJob,
98
+ config: AssistantConfig,
99
+ ): Promise<void> {
100
+ const chunkId = asString(job.payload.chunkId);
101
+ if (!chunkId) return;
102
+ const db = getDb();
103
+ const chunk = db
104
+ .select()
105
+ .from(memoryChunks)
106
+ .where(eq(memoryChunks.id, chunkId))
107
+ .get();
108
+ if (!chunk) return;
109
+ await embedAndUpsert(config, "chunk", chunk.id, chunk.content, {
110
+ observation_id: chunk.observationId,
111
+ created_at: chunk.createdAt,
112
+ memory_scope_id: chunk.scopeId,
113
+ });
114
+ }
115
+
90
116
  export async function embedMediaJob(
91
117
  job: MemoryJob,
92
118
  config: AssistantConfig,
@@ -116,6 +142,41 @@ export async function embedMediaJob(
116
142
  created_at: asset.createdAt,
117
143
  kind: asset.mediaType,
118
144
  subject: asset.title,
145
+ memory_scope_id: "default",
146
+ });
147
+ }
148
+
149
+ export async function embedObservationJob(
150
+ job: MemoryJob,
151
+ config: AssistantConfig,
152
+ ): Promise<void> {
153
+ const observationId = asString(job.payload.observationId);
154
+ const chunkId = asString(job.payload.chunkId);
155
+ if (!observationId || !chunkId) return;
156
+
157
+ const db = getDb();
158
+ const observation = db
159
+ .select()
160
+ .from(memoryObservations)
161
+ .where(eq(memoryObservations.id, observationId))
162
+ .get();
163
+ if (!observation) return;
164
+
165
+ const chunk = db
166
+ .select()
167
+ .from(memoryChunks)
168
+ .where(eq(memoryChunks.id, chunkId))
169
+ .get();
170
+ if (!chunk) return;
171
+
172
+ await embedAndUpsert(config, "observation", chunk.id, chunk.content, {
173
+ observation_id: observationId,
174
+ conversation_id: observation.conversationId,
175
+ role: observation.role,
176
+ modality: observation.modality,
177
+ source: observation.source,
178
+ created_at: observation.createdAt,
179
+ memory_scope_id: observation.scopeId,
119
180
  });
120
181
  }
121
182
 
@@ -155,3 +216,25 @@ export async function embedAttachmentJob(
155
216
  memory_scope_id: memoryScopeId,
156
217
  });
157
218
  }
219
+
220
+ export async function embedEpisodeJob(
221
+ job: MemoryJob,
222
+ config: AssistantConfig,
223
+ ): Promise<void> {
224
+ const episodeId = asString(job.payload.episodeId);
225
+ if (!episodeId) return;
226
+ const db = getDb();
227
+ const episode = db
228
+ .select()
229
+ .from(memoryEpisodes)
230
+ .where(eq(memoryEpisodes.id, episodeId))
231
+ .get();
232
+ if (!episode) return;
233
+ const text = `[episode] ${episode.title}: ${episode.summary}`;
234
+ await embedAndUpsert(config, "episode", episode.id, text, {
235
+ conversation_id: episode.conversationId,
236
+ created_at: episode.startAt,
237
+ last_seen_at: episode.endAt,
238
+ memory_scope_id: episode.scopeId,
239
+ });
240
+ }
@@ -142,7 +142,7 @@ export function truncate(text: string, max: number): string {
142
142
 
143
143
  export async function embedAndUpsert(
144
144
  config: AssistantConfig,
145
- targetType: "segment" | "item" | "summary" | "media",
145
+ targetType: "segment" | "item" | "summary" | "observation" | "chunk" | "episode" | "media",
146
146
  targetId: string,
147
147
  input: EmbeddingInput,
148
148
  extraPayload?: Record<string, unknown>,
@@ -12,6 +12,9 @@ export type MemoryJobType =
12
12
  | "embed_segment"
13
13
  | "embed_item"
14
14
  | "embed_summary"
15
+ | "embed_chunk"
16
+ | "embed_episode"
17
+ | "embed_observation"
15
18
  | "extract_items"
16
19
  | "extract_entities"
17
20
  | "cleanup_stale_superseded_items"
@@ -34,6 +37,9 @@ const EMBED_JOB_TYPES: MemoryJobType[] = [
34
37
  "embed_segment",
35
38
  "embed_item",
36
39
  "embed_summary",
40
+ "embed_chunk",
41
+ "embed_episode",
42
+ "embed_observation",
37
43
  "embed_media",
38
44
  "embed_attachment",
39
45
  ];
@@ -11,8 +11,11 @@ import { generateConversationStartersJob } from "./job-handlers/conversation-sta
11
11
  // ── Per-job-type handlers ──────────────────────────────────────────
12
12
  import {
13
13
  embedAttachmentJob,
14
+ embedChunkJob,
15
+ embedEpisodeJob,
14
16
  embedItemJob,
15
17
  embedMediaJob,
18
+ embedObservationJob,
16
19
  embedSegmentJob,
17
20
  embedSummaryJob,
18
21
  } from "./job-handlers/embedding.js";
@@ -267,6 +270,15 @@ async function processJob(
267
270
  case "embed_summary":
268
271
  await embedSummaryJob(job, config);
269
272
  return;
273
+ case "embed_chunk":
274
+ await embedChunkJob(job, config);
275
+ return;
276
+ case "embed_episode":
277
+ await embedEpisodeJob(job, config);
278
+ return;
279
+ case "embed_observation":
280
+ await embedObservationJob(job, config);
281
+ return;
270
282
  case "extract_items":
271
283
  await extractItemsJob(job);
272
284
  return;
@@ -1,6 +1,11 @@
1
- import { and, eq, gte, lte } from "drizzle-orm";
1
+ import { and, eq, gte, inArray, isNull, lte } from "drizzle-orm";
2
2
  import { v4 as uuid } from "uuid";
3
3
 
4
+ import {
5
+ getAssistantMessageIdsInTurn,
6
+ getMessageById,
7
+ messageMetadataSchema,
8
+ } from "./conversation-crud.js";
4
9
  import { getDb } from "./db.js";
5
10
  import { llmRequestLogs } from "./schema.js";
6
11
 
@@ -8,12 +13,16 @@ export function recordRequestLog(
8
13
  conversationId: string,
9
14
  requestPayload: string,
10
15
  responsePayload: string,
16
+ messageId?: string,
17
+ provider?: string,
11
18
  ): void {
12
19
  const db = getDb();
13
20
  db.insert(llmRequestLogs)
14
21
  .values({
15
22
  id: uuid(),
16
23
  conversationId,
24
+ messageId: messageId ?? null,
25
+ provider: provider ?? null,
17
26
  requestPayload,
18
27
  responsePayload,
19
28
  createdAt: Date.now(),
@@ -46,3 +55,93 @@ export function queryRequestLogs(
46
55
  .orderBy(llmRequestLogs.createdAt)
47
56
  .all();
48
57
  }
58
+
59
+ export function backfillMessageIdOnLogs(
60
+ conversationId: string,
61
+ messageId: string,
62
+ ): void {
63
+ const db = getDb();
64
+ db.update(llmRequestLogs)
65
+ .set({ messageId })
66
+ .where(
67
+ and(
68
+ eq(llmRequestLogs.conversationId, conversationId),
69
+ isNull(llmRequestLogs.messageId),
70
+ ),
71
+ )
72
+ .run();
73
+ }
74
+
75
+ /**
76
+ * Internal helper: query `llm_request_logs` for rows matching any of the
77
+ * given message IDs, ordered by `createdAt ASC`. Uses the existing
78
+ * `idx_llm_request_logs_message_id` index via `inArray`.
79
+ */
80
+ function selectLogsByMessageIds(messageIds: string[]): Array<{
81
+ id: string;
82
+ conversationId: string;
83
+ messageId: string | null;
84
+ provider: string | null;
85
+ requestPayload: string;
86
+ responsePayload: string;
87
+ createdAt: number;
88
+ }> {
89
+ if (messageIds.length === 0) return [];
90
+ const db = getDb();
91
+ return db
92
+ .select({
93
+ id: llmRequestLogs.id,
94
+ conversationId: llmRequestLogs.conversationId,
95
+ messageId: llmRequestLogs.messageId,
96
+ provider: llmRequestLogs.provider,
97
+ requestPayload: llmRequestLogs.requestPayload,
98
+ responsePayload: llmRequestLogs.responsePayload,
99
+ createdAt: llmRequestLogs.createdAt,
100
+ })
101
+ .from(llmRequestLogs)
102
+ .where(inArray(llmRequestLogs.messageId, messageIds))
103
+ .orderBy(llmRequestLogs.createdAt)
104
+ .all();
105
+ }
106
+
107
+ export function getRequestLogsByMessageId(messageId: string): Array<{
108
+ id: string;
109
+ conversationId: string;
110
+ messageId: string | null;
111
+ provider: string | null;
112
+ requestPayload: string;
113
+ responsePayload: string;
114
+ createdAt: number;
115
+ }> {
116
+ // Resolve all assistant message IDs in the same turn so the inspector
117
+ // shows every LLM call from the entire agent turn, not just the queried message.
118
+ const turnMessageIds = getAssistantMessageIdsInTurn(messageId);
119
+ const turnLogs = selectLogsByMessageIds(turnMessageIds);
120
+ if (turnLogs.length > 0) {
121
+ return turnLogs;
122
+ }
123
+
124
+ // Fork-source fallback: if no logs found for the turn, check whether
125
+ // the queried message was forked from a source and resolve that source's turn.
126
+ const message = getMessageById(messageId);
127
+ if (!message?.metadata) {
128
+ return [];
129
+ }
130
+
131
+ try {
132
+ const parsed = messageMetadataSchema.safeParse(
133
+ JSON.parse(message.metadata),
134
+ );
135
+ const sourceMessageId =
136
+ parsed.success && typeof parsed.data.forkSourceMessageId === "string"
137
+ ? parsed.data.forkSourceMessageId
138
+ : null;
139
+ if (!sourceMessageId || sourceMessageId === messageId) {
140
+ return [];
141
+ }
142
+ const sourceTurnIds = getAssistantMessageIdsInTurn(sourceMessageId);
143
+ return selectLogsByMessageIds(sourceTurnIds);
144
+ } catch {
145
+ return [];
146
+ }
147
+ }
@@ -261,6 +261,11 @@ export function addCoreColumns(database: DrizzleDb): void {
261
261
  } catch {
262
262
  /* already exists */
263
263
  }
264
+ try {
265
+ database.run(/*sql*/ `ALTER TABLE attachments ADD COLUMN source_path TEXT`);
266
+ } catch {
267
+ /* already exists */
268
+ }
264
269
 
265
270
  // cron_jobs
266
271
  try {
@@ -15,9 +15,9 @@ export function migrateScheduleOneShotRouting(database: DrizzleDb): void {
15
15
  const raw = getSqliteFrom(database);
16
16
 
17
17
  // Check if migration is already done by inspecting whether the status column exists
18
- const tableInfo = raw
19
- .query("PRAGMA table_info(cron_jobs)")
20
- .all() as Array<{ name: string }>;
18
+ const tableInfo = raw.query("PRAGMA table_info(cron_jobs)").all() as Array<{
19
+ name: string;
20
+ }>;
21
21
  const hasStatusColumn = tableInfo.some((col) => col.name === "status");
22
22
  if (hasStatusColumn) {
23
23
  // Ensure all indexes exist even if the column migration already ran
@@ -30,40 +30,37 @@ import { withCrashRecovery } from "./validate-migration-state.js";
30
30
  * preserved for the subsequent cleanup PR.
31
31
  */
32
32
  export function migrateRemindersToSchedules(database: DrizzleDb): void {
33
- withCrashRecovery(
34
- database,
35
- "migration_reminders_to_schedules_v1",
36
- () => {
37
- const raw = getSqliteFrom(database);
33
+ withCrashRecovery(database, "migration_reminders_to_schedules_v1", () => {
34
+ const raw = getSqliteFrom(database);
38
35
 
39
- // Guard: if the reminders table doesn't exist, nothing to migrate.
40
- const hasReminders = raw
41
- .query(
42
- "SELECT name FROM sqlite_master WHERE type='table' AND name='reminders'",
43
- )
44
- .get();
45
- if (!hasReminders) return;
36
+ // Guard: if the reminders table doesn't exist, nothing to migrate.
37
+ const hasReminders = raw
38
+ .query(
39
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='reminders'",
40
+ )
41
+ .get();
42
+ if (!hasReminders) return;
46
43
 
47
- // Read all reminders into memory. We use the reminder's original ID as
48
- // the cron_jobs primary key so that INSERT OR IGNORE deduplicates
49
- // correctly if the migration re-runs after a crash.
50
- const reminders = raw.query("SELECT * FROM reminders").all() as Array<{
51
- id: string;
52
- label: string;
53
- message: string;
54
- fire_at: number;
55
- mode: string;
56
- status: string;
57
- fired_at: number | null;
58
- routing_intent: string;
59
- routing_hints_json: string;
60
- created_at: number;
61
- updated_at: number;
62
- }>;
44
+ // Read all reminders into memory. We use the reminder's original ID as
45
+ // the cron_jobs primary key so that INSERT OR IGNORE deduplicates
46
+ // correctly if the migration re-runs after a crash.
47
+ const reminders = raw.query("SELECT * FROM reminders").all() as Array<{
48
+ id: string;
49
+ label: string;
50
+ message: string;
51
+ fire_at: number;
52
+ mode: string;
53
+ status: string;
54
+ fired_at: number | null;
55
+ routing_intent: string;
56
+ routing_hints_json: string;
57
+ created_at: number;
58
+ updated_at: number;
59
+ }>;
63
60
 
64
- if (reminders.length === 0) return;
61
+ if (reminders.length === 0) return;
65
62
 
66
- const insert = raw.query(/*sql*/ `
63
+ const insert = raw.query(/*sql*/ `
67
64
  INSERT OR IGNORE INTO cron_jobs (
68
65
  id, name, enabled, cron_expression, schedule_syntax, timezone,
69
66
  message, next_run_at, last_run_at, last_status, retry_count,
@@ -77,53 +74,52 @@ export function migrateRemindersToSchedules(database: DrizzleDb): void {
77
74
  )
78
75
  `);
79
76
 
80
- // Mark migrated pending reminders as fired so the legacy
81
- // claimDueReminders path doesn't fire them a second time.
82
- const markFired = raw.query(/*sql*/ `
77
+ // Mark migrated pending reminders as fired so the legacy
78
+ // claimDueReminders path doesn't fire them a second time.
79
+ const markFired = raw.query(/*sql*/ `
83
80
  UPDATE reminders SET status = 'fired', fired_at = ?
84
81
  WHERE id = ? AND status = 'pending'
85
82
  `);
86
83
 
87
- try {
88
- raw.exec("BEGIN");
84
+ try {
85
+ raw.exec("BEGIN");
89
86
 
90
- for (const r of reminders) {
91
- const statusMap: Record<string, string> = {
92
- pending: "active",
93
- firing: "firing",
94
- fired: "fired",
95
- cancelled: "cancelled",
96
- };
87
+ for (const r of reminders) {
88
+ const statusMap: Record<string, string> = {
89
+ pending: "active",
90
+ firing: "firing",
91
+ fired: "fired",
92
+ cancelled: "cancelled",
93
+ };
97
94
 
98
- insert.run(
99
- r.id,
100
- r.label,
101
- r.status === "pending" ? 1 : 0,
102
- // message, next_run_at, last_run_at, last_status
103
- r.message,
104
- r.fire_at,
105
- r.fired_at,
106
- r.status === "fired" ? "ok" : null,
107
- // mode, routing_intent, routing_hints_json, status
108
- r.mode,
109
- r.routing_intent,
110
- r.routing_hints_json,
111
- statusMap[r.status] ?? "active",
112
- // created_at, updated_at
113
- r.created_at,
114
- r.updated_at,
115
- );
95
+ insert.run(
96
+ r.id,
97
+ r.label,
98
+ r.status === "pending" ? 1 : 0,
99
+ // message, next_run_at, last_run_at, last_status
100
+ r.message,
101
+ r.fire_at,
102
+ r.fired_at,
103
+ r.status === "fired" ? "ok" : null,
104
+ // mode, routing_intent, routing_hints_json, status
105
+ r.mode,
106
+ r.routing_intent,
107
+ r.routing_hints_json,
108
+ statusMap[r.status] ?? "active",
109
+ // created_at, updated_at
110
+ r.created_at,
111
+ r.updated_at,
112
+ );
116
113
 
117
- if (r.status === "pending") {
118
- markFired.run(Date.now(), r.id);
119
- }
114
+ if (r.status === "pending") {
115
+ markFired.run(Date.now(), r.id);
120
116
  }
121
-
122
- raw.exec("COMMIT");
123
- } catch (err) {
124
- raw.exec("ROLLBACK");
125
- throw err;
126
117
  }
127
- },
128
- );
118
+
119
+ raw.exec("COMMIT");
120
+ } catch (err) {
121
+ raw.exec("ROLLBACK");
122
+ throw err;
123
+ }
124
+ });
129
125
  }
@@ -6,13 +6,9 @@ import { withCrashRecovery } from "./validate-migration-state.js";
6
6
  * migrated into cron_jobs as one-shot schedules (migration 147).
7
7
  */
8
8
  export function migrateDropRemindersTable(database: DrizzleDb): void {
9
- withCrashRecovery(
10
- database,
11
- "migration_drop_reminders_table_v1",
12
- () => {
13
- const raw = getSqliteFrom(database);
14
- raw.run("DROP INDEX IF EXISTS idx_reminders_status_fire_at");
15
- raw.run("DROP TABLE IF EXISTS reminders");
16
- },
17
- );
9
+ withCrashRecovery(database, "migration_drop_reminders_table_v1", () => {
10
+ const raw = getSqliteFrom(database);
11
+ raw.run("DROP INDEX IF EXISTS idx_reminders_status_fire_at");
12
+ raw.run("DROP TABLE IF EXISTS reminders");
13
+ });
18
14
  }
@@ -4,9 +4,7 @@ import { getSqliteFrom } from "../db-connection.js";
4
4
  export function migrateDropLoopbackPortColumn(database: DrizzleDb): void {
5
5
  const raw = getSqliteFrom(database);
6
6
  try {
7
- raw.exec(
8
- /*sql*/ `ALTER TABLE oauth_providers DROP COLUMN loopback_port`,
9
- );
7
+ raw.exec(/*sql*/ `ALTER TABLE oauth_providers DROP COLUMN loopback_port`);
10
8
  } catch {
11
9
  // Column already dropped or doesn't exist — nothing to do.
12
10
  }
@@ -40,13 +40,6 @@ export function migrateRenameThreadStartersTable(database: DrizzleDb): void {
40
40
  raw.exec(
41
41
  /*sql*/ `CREATE INDEX IF NOT EXISTS idx_conversation_starters_card_type ON conversation_starters(card_type, scope_id)`,
42
42
  );
43
-
44
- // Migrate checkpoint keys from old thread_starters: prefix to
45
- // conversation_starters: so existing checkpoint data is found by
46
- // the renamed code paths and unnecessary re-generation is avoided.
47
- raw.exec(
48
- /*sql*/ `UPDATE memory_checkpoints SET key = replace(key, 'thread_starters:', 'conversation_starters:') WHERE key LIKE 'thread_starters:%'`,
49
- );
50
43
  },
51
44
  );
52
45
  }
@@ -0,0 +1,15 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ export function migrateOAuthProvidersManagedServiceConfigKey(
5
+ database: DrizzleDb,
6
+ ): void {
7
+ const raw = getSqliteFrom(database);
8
+ try {
9
+ raw.exec(
10
+ /*sql*/ `ALTER TABLE oauth_providers ADD COLUMN managed_service_config_key TEXT`,
11
+ );
12
+ } catch {
13
+ // Column already exists — nothing to do.
14
+ }
15
+ }
@@ -0,0 +1,16 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ export function migrateLlmRequestLogMessageId(database: DrizzleDb): void {
5
+ const raw = getSqliteFrom(database);
6
+ try {
7
+ raw.exec(/*sql*/ `ALTER TABLE llm_request_logs ADD COLUMN message_id TEXT`);
8
+ } catch {
9
+ // Column already exists — nothing to do.
10
+ }
11
+
12
+ raw.exec(/*sql*/ `
13
+ CREATE INDEX IF NOT EXISTS idx_llm_request_logs_message_id
14
+ ON llm_request_logs(message_id)
15
+ `);
16
+ }
@@ -0,0 +1,66 @@
1
+ import { writeAttachmentToDisk } from "../attachments-store.js";
2
+ import type { DrizzleDb } from "../db-connection.js";
3
+ import { getSqliteFrom } from "../db-connection.js";
4
+ import { withCrashRecovery } from "./validate-migration-state.js";
5
+
6
+ /**
7
+ * Backfill existing inline (base64-in-DB) attachments to disk.
8
+ *
9
+ * Finds attachment rows that have non-empty data_base64 but no file_path,
10
+ * writes each one to disk, and updates the row to store the file path
11
+ * while clearing the inline data.
12
+ *
13
+ * Processes in batches of 50 to avoid holding large amounts of base64 data
14
+ * in memory at once.
15
+ */
16
+ export function migrateBackfillInlineAttachmentsToDisk(
17
+ database: DrizzleDb,
18
+ ): void {
19
+ withCrashRecovery(
20
+ database,
21
+ "migration_backfill_inline_attachments_v1",
22
+ () => {
23
+ const raw = getSqliteFrom(database);
24
+ const BATCH_SIZE = 50;
25
+ let totalMigrated = 0;
26
+
27
+ for (;;) {
28
+ const rows = raw
29
+ .query(
30
+ `SELECT id, original_filename, data_base64 FROM attachments
31
+ WHERE (file_path IS NULL OR file_path = '')
32
+ AND data_base64 != ''
33
+ AND length(data_base64) > 0
34
+ LIMIT ?`,
35
+ )
36
+ .all(BATCH_SIZE) as Array<{
37
+ id: string;
38
+ original_filename: string;
39
+ data_base64: string;
40
+ }>;
41
+
42
+ if (rows.length === 0) break;
43
+
44
+ for (const row of rows) {
45
+ const filePath = writeAttachmentToDisk(
46
+ row.data_base64,
47
+ row.original_filename,
48
+ );
49
+ raw
50
+ .query(
51
+ `UPDATE attachments SET file_path = ?, data_base64 = '' WHERE id = ?`,
52
+ )
53
+ .run(filePath, row.id);
54
+ }
55
+
56
+ totalMigrated += rows.length;
57
+ }
58
+
59
+ if (totalMigrated > 0) {
60
+ // Log is intentionally not imported to keep migration self-contained;
61
+ // the checkpoint value records completion.
62
+ console.log(`Migrated ${totalMigrated} inline attachments to disk`);
63
+ }
64
+ },
65
+ );
66
+ }