@vellumai/assistant 0.5.5 → 0.5.7

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 (382) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +4 -5
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docs/architecture/keychain-broker.md +45 -240
  7. package/docs/architecture/security.md +0 -17
  8. package/docs/credential-execution-service.md +2 -2
  9. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  10. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
  11. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  12. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  13. package/package.json +2 -3
  14. package/src/__tests__/actor-token-service.test.ts +1 -2
  15. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  16. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  17. package/src/__tests__/btw-routes.test.ts +0 -39
  18. package/src/__tests__/call-domain.test.ts +0 -128
  19. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  20. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  21. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  22. package/src/__tests__/checker.test.ts +4 -2
  23. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  24. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  25. package/src/__tests__/config-schema.test.ts +3 -3
  26. package/src/__tests__/context-window-manager.test.ts +78 -0
  27. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  28. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  29. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  30. package/src/__tests__/conversation-title-service.test.ts +117 -1
  31. package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
  32. package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
  33. package/src/__tests__/credential-security-e2e.test.ts +0 -66
  34. package/src/__tests__/credential-security-invariants.test.ts +4 -45
  35. package/src/__tests__/credentials-cli.test.ts +78 -0
  36. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  37. package/src/__tests__/docker-signing-key-bootstrap.test.ts +98 -0
  38. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  39. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  40. package/src/__tests__/host-shell-tool.test.ts +6 -7
  41. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  42. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  43. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  44. package/src/__tests__/intent-routing.test.ts +0 -13
  45. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  46. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  47. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  48. package/src/__tests__/memory-regressions.test.ts +8 -30
  49. package/src/__tests__/migration-export-http.test.ts +2 -2
  50. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  51. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  52. package/src/__tests__/migration-validate-http.test.ts +2 -2
  53. package/src/__tests__/non-member-access-request.test.ts +0 -5
  54. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  55. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  56. package/src/__tests__/permission-types.test.ts +1 -0
  57. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  58. package/src/__tests__/qdrant-manager.test.ts +28 -2
  59. package/src/__tests__/registry.test.ts +0 -6
  60. package/src/__tests__/require-fresh-approval.test.ts +4 -0
  61. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  62. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  63. package/src/__tests__/secure-keys.test.ts +83 -263
  64. package/src/__tests__/shell-identity.test.ts +96 -6
  65. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  66. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  67. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  68. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  69. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  70. package/src/__tests__/skill-load-tool.test.ts +0 -2
  71. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  72. package/src/__tests__/skills.test.ts +0 -2
  73. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  74. package/src/__tests__/suggestion-routes.test.ts +1 -32
  75. package/src/__tests__/system-prompt.test.ts +0 -1
  76. package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -0
  77. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  78. package/src/__tests__/tool-executor.test.ts +4 -0
  79. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  80. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  81. package/src/__tests__/update-bulletin.test.ts +0 -2
  82. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  83. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
  84. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  85. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
  86. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  87. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  88. package/src/calls/audio-store.test.ts +97 -0
  89. package/src/calls/audio-store.ts +205 -0
  90. package/src/calls/call-controller.ts +85 -7
  91. package/src/calls/call-domain.ts +3 -0
  92. package/src/calls/call-store.ts +10 -3
  93. package/src/calls/fish-audio-client.ts +117 -0
  94. package/src/calls/relay-server.ts +27 -0
  95. package/src/calls/twilio-routes.ts +2 -1
  96. package/src/calls/types.ts +1 -0
  97. package/src/calls/voice-ingress-preflight.ts +0 -42
  98. package/src/calls/voice-quality.ts +26 -5
  99. package/src/calls/voice-session-bridge.ts +6 -12
  100. package/src/cli/commands/config.ts +1 -4
  101. package/src/cli/commands/conversations.ts +0 -18
  102. package/src/cli/commands/credentials.ts +34 -4
  103. package/src/cli/commands/oauth/index.ts +7 -0
  104. package/src/cli/commands/oauth/platform.ts +179 -0
  105. package/src/cli/commands/platform.ts +3 -3
  106. package/src/config/assistant-feature-flags.ts +186 -5
  107. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  108. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  109. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  110. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  111. package/src/config/bundled-tool-registry.ts +1 -11
  112. package/src/config/env-registry.ts +1 -1
  113. package/src/config/env.ts +16 -16
  114. package/src/config/feature-flag-registry.json +48 -16
  115. package/src/config/loader.ts +98 -31
  116. package/src/config/schema.ts +4 -25
  117. package/src/config/schemas/calls.ts +13 -0
  118. package/src/config/schemas/fish-audio.ts +39 -0
  119. package/src/config/schemas/memory.ts +0 -4
  120. package/src/config/schemas/platform.ts +1 -1
  121. package/src/config/schemas/security.ts +4 -4
  122. package/src/config/types.ts +0 -1
  123. package/src/contacts/contact-store.ts +39 -0
  124. package/src/contacts/types.ts +2 -0
  125. package/src/context/window-manager.ts +53 -2
  126. package/src/credential-execution/approval-bridge.ts +1 -0
  127. package/src/credential-execution/executable-discovery.ts +28 -4
  128. package/src/credential-execution/feature-gates.ts +16 -0
  129. package/src/credential-execution/process-manager.ts +38 -0
  130. package/src/daemon/assistant-attachments.ts +9 -0
  131. package/src/daemon/config-watcher.ts +6 -4
  132. package/src/daemon/conversation-agent-loop.ts +0 -60
  133. package/src/daemon/conversation-memory.ts +0 -117
  134. package/src/daemon/conversation-runtime-assembly.ts +0 -2
  135. package/src/daemon/conversation-tool-setup.ts +0 -105
  136. package/src/daemon/conversation.ts +10 -1
  137. package/src/daemon/handlers/config-vercel.ts +92 -0
  138. package/src/daemon/handlers/conversations.ts +0 -11
  139. package/src/daemon/handlers/skills.ts +2 -15
  140. package/src/daemon/install-symlink.ts +195 -0
  141. package/src/daemon/lifecycle.ts +229 -96
  142. package/src/daemon/message-types/conversations.ts +3 -4
  143. package/src/daemon/message-types/diagnostics.ts +3 -22
  144. package/src/daemon/message-types/messages.ts +0 -2
  145. package/src/daemon/message-types/upgrades.ts +8 -0
  146. package/src/daemon/server.ts +30 -92
  147. package/src/events/domain-events.ts +2 -1
  148. package/src/followups/followup-store.ts +5 -2
  149. package/src/inbound/platform-callback-registration.ts +3 -3
  150. package/src/instrument.ts +8 -5
  151. package/src/memory/conversation-crud.ts +0 -236
  152. package/src/memory/conversation-title-service.ts +76 -11
  153. package/src/memory/db-init.ts +15 -11
  154. package/src/memory/indexer.ts +15 -106
  155. package/src/memory/items-extractor.ts +15 -1
  156. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  157. package/src/memory/job-handlers/embedding.ts +0 -79
  158. package/src/memory/job-utils.ts +1 -1
  159. package/src/memory/jobs-store.ts +30 -13
  160. package/src/memory/jobs-worker.ts +31 -27
  161. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  162. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  163. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  164. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  165. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  166. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  167. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  168. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  169. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  170. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  171. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  172. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  173. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  174. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  175. package/src/memory/migrations/116-messages-fts.ts +106 -1
  176. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  177. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  178. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  179. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  180. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  181. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  182. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  183. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  184. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  185. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  186. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  187. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  188. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  189. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  190. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  191. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  192. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  193. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  194. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  195. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  196. package/src/memory/migrations/189-drop-simplified-memory.ts +42 -0
  197. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  198. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  199. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  200. package/src/memory/migrations/index.ts +5 -3
  201. package/src/memory/migrations/registry.ts +90 -0
  202. package/src/memory/migrations/validate-migration-state.ts +137 -11
  203. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  204. package/src/memory/qdrant-client.ts +4 -6
  205. package/src/memory/qdrant-manager.ts +64 -7
  206. package/src/memory/schema/calls.ts +1 -0
  207. package/src/memory/schema/contacts.ts +1 -0
  208. package/src/memory/schema/conversations.ts +0 -3
  209. package/src/memory/schema/index.ts +0 -2
  210. package/src/messaging/draft-store.ts +2 -2
  211. package/src/notifications/decision-engine.ts +4 -1
  212. package/src/oauth/connection-resolver.ts +6 -4
  213. package/src/permissions/checker.ts +0 -38
  214. package/src/permissions/defaults.ts +3 -3
  215. package/src/permissions/shell-identity.ts +76 -22
  216. package/src/permissions/trust-client.ts +2 -13
  217. package/src/permissions/trust-store.ts +8 -3
  218. package/src/permissions/types.ts +4 -2
  219. package/src/platform/client.ts +35 -7
  220. package/src/prompts/persona-resolver.ts +138 -0
  221. package/src/prompts/system-prompt.ts +36 -4
  222. package/src/prompts/templates/users/default.md +1 -0
  223. package/src/providers/registry.ts +27 -40
  224. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  225. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  226. package/src/runtime/auth/external-assistant-id.ts +13 -59
  227. package/src/runtime/auth/route-policy.ts +29 -1
  228. package/src/runtime/auth/token-service.ts +53 -15
  229. package/src/runtime/channel-readiness-service.ts +1 -16
  230. package/src/runtime/http-server.ts +29 -2
  231. package/src/runtime/middleware/error-handler.ts +1 -9
  232. package/src/runtime/routes/audio-routes.ts +40 -0
  233. package/src/runtime/routes/btw-routes.ts +0 -17
  234. package/src/runtime/routes/conversation-management-routes.ts +0 -36
  235. package/src/runtime/routes/conversation-query-routes.ts +106 -2
  236. package/src/runtime/routes/conversation-routes.ts +4 -43
  237. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  238. package/src/runtime/routes/identity-routes.ts +18 -29
  239. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  240. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  241. package/src/runtime/routes/integrations/vercel.ts +89 -0
  242. package/src/runtime/routes/log-export-routes.ts +5 -0
  243. package/src/runtime/routes/memory-item-routes.test.ts +221 -3
  244. package/src/runtime/routes/memory-item-routes.ts +144 -4
  245. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  246. package/src/runtime/routes/migration-routes.ts +17 -1
  247. package/src/runtime/routes/notification-routes.ts +58 -0
  248. package/src/runtime/routes/schedule-routes.ts +65 -0
  249. package/src/runtime/routes/settings-routes.ts +41 -1
  250. package/src/runtime/routes/tts-routes.ts +86 -0
  251. package/src/runtime/routes/upgrade-broadcast-routes.ts +175 -0
  252. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  253. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  254. package/src/runtime/routes/workspace-routes.ts +1 -1
  255. package/src/runtime/routes/workspace-utils.ts +86 -2
  256. package/src/schedule/schedule-store.ts +0 -21
  257. package/src/security/ces-credential-client.ts +59 -22
  258. package/src/security/ces-rpc-credential-backend.ts +85 -0
  259. package/src/security/credential-backend.ts +12 -88
  260. package/src/security/keychain-broker-client.ts +10 -2
  261. package/src/security/secure-keys.ts +94 -113
  262. package/src/skills/catalog-install.ts +13 -7
  263. package/src/skills/inline-command-render.ts +5 -1
  264. package/src/skills/inline-command-runner.ts +30 -2
  265. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  266. package/src/tools/calls/call-start.ts +1 -0
  267. package/src/tools/executor.ts +0 -4
  268. package/src/tools/memory/handlers.ts +1 -129
  269. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  270. package/src/tools/network/web-fetch.ts +3 -1
  271. package/src/tools/permission-checker.ts +18 -0
  272. package/src/tools/skills/execute.ts +1 -1
  273. package/src/tools/skills/load.ts +9 -2
  274. package/src/tools/types.ts +0 -8
  275. package/src/util/errors.ts +0 -12
  276. package/src/util/platform.ts +8 -55
  277. package/src/util/xml.ts +8 -0
  278. package/src/workspace/git-service.ts +5 -2
  279. package/src/workspace/heartbeat-service.ts +5 -24
  280. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  281. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  282. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  283. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  284. package/src/workspace/migrations/006-services-config.ts +49 -0
  285. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  286. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  287. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  288. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  289. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  290. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  291. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  292. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  293. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  294. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  295. package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
  296. package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
  297. package/src/workspace/migrations/registry.ts +8 -0
  298. package/src/workspace/migrations/runner.ts +106 -2
  299. package/src/workspace/migrations/types.ts +4 -0
  300. package/src/__tests__/archive-recall.test.ts +0 -560
  301. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  302. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  303. package/src/__tests__/conversation-memory-dirty-tail.test.ts +0 -150
  304. package/src/__tests__/conversation-switch-memory-reduction.test.ts +0 -474
  305. package/src/__tests__/db-memory-archive-migration.test.ts +0 -372
  306. package/src/__tests__/db-memory-brief-state-migration.test.ts +0 -213
  307. package/src/__tests__/db-memory-reducer-checkpoints.test.ts +0 -273
  308. package/src/__tests__/diagnostics-export.test.ts +0 -288
  309. package/src/__tests__/local-gateway-health.test.ts +0 -209
  310. package/src/__tests__/memory-brief-open-loops.test.ts +0 -530
  311. package/src/__tests__/memory-brief-time.test.ts +0 -285
  312. package/src/__tests__/memory-brief-wrapper.test.ts +0 -311
  313. package/src/__tests__/memory-chunk-archive.test.ts +0 -400
  314. package/src/__tests__/memory-chunk-dual-write.test.ts +0 -453
  315. package/src/__tests__/memory-episode-archive.test.ts +0 -370
  316. package/src/__tests__/memory-episode-dual-write.test.ts +0 -626
  317. package/src/__tests__/memory-observation-archive.test.ts +0 -375
  318. package/src/__tests__/memory-observation-dual-write.test.ts +0 -318
  319. package/src/__tests__/memory-reducer-job.test.ts +0 -538
  320. package/src/__tests__/memory-reducer-scheduling.test.ts +0 -473
  321. package/src/__tests__/memory-reducer-store.test.ts +0 -728
  322. package/src/__tests__/memory-reducer-types.test.ts +0 -707
  323. package/src/__tests__/memory-reducer.test.ts +0 -704
  324. package/src/__tests__/memory-simplified-config.test.ts +0 -281
  325. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  326. package/src/__tests__/simplified-memory-e2e.test.ts +0 -666
  327. package/src/__tests__/simplified-memory-runtime.test.ts +0 -616
  328. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  329. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  330. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  331. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  332. package/src/__tests__/swarm-recursion.test.ts +0 -197
  333. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  334. package/src/__tests__/swarm-tool.test.ts +0 -185
  335. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  336. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  337. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  338. package/src/commands/cc-command-registry.ts +0 -248
  339. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  340. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  341. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  342. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  343. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  344. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  345. package/src/config/schemas/memory-simplified.ts +0 -101
  346. package/src/config/schemas/swarm.ts +0 -82
  347. package/src/logfire.ts +0 -135
  348. package/src/memory/archive-recall.ts +0 -516
  349. package/src/memory/archive-store.ts +0 -400
  350. package/src/memory/brief-formatting.ts +0 -33
  351. package/src/memory/brief-open-loops.ts +0 -266
  352. package/src/memory/brief-time.ts +0 -162
  353. package/src/memory/brief.ts +0 -75
  354. package/src/memory/job-handlers/backfill-simplified-memory.ts +0 -462
  355. package/src/memory/job-handlers/reduce-conversation-memory.ts +0 -229
  356. package/src/memory/migrations/185-memory-brief-state.ts +0 -52
  357. package/src/memory/migrations/186-memory-archive.ts +0 -109
  358. package/src/memory/migrations/187-memory-reducer-checkpoints.ts +0 -19
  359. package/src/memory/reducer-scheduler.ts +0 -242
  360. package/src/memory/reducer-store.ts +0 -271
  361. package/src/memory/reducer-types.ts +0 -106
  362. package/src/memory/reducer.ts +0 -467
  363. package/src/memory/schema/memory-archive.ts +0 -121
  364. package/src/memory/schema/memory-brief.ts +0 -55
  365. package/src/runtime/local-gateway-health.ts +0 -275
  366. package/src/security/secret-ingress.ts +0 -68
  367. package/src/swarm/backend-claude-code.ts +0 -225
  368. package/src/swarm/checkpoint.ts +0 -137
  369. package/src/swarm/graph-utils.ts +0 -53
  370. package/src/swarm/index.ts +0 -55
  371. package/src/swarm/limits.ts +0 -66
  372. package/src/swarm/orchestrator.ts +0 -424
  373. package/src/swarm/plan-validator.ts +0 -117
  374. package/src/swarm/router-planner.ts +0 -162
  375. package/src/swarm/router-prompts.ts +0 -39
  376. package/src/swarm/synthesizer.ts +0 -81
  377. package/src/swarm/types.ts +0 -72
  378. package/src/swarm/worker-backend.ts +0 -131
  379. package/src/swarm/worker-prompts.ts +0 -80
  380. package/src/swarm/worker-runner.ts +0 -170
  381. package/src/tools/claude-code/claude-code.ts +0 -610
  382. package/src/tools/swarm/delegate.ts +0 -205
@@ -71,3 +71,77 @@ export function migrateEmbeddingVectorBlob(database: DrizzleDb): void {
71
71
  .run(checkpointKey, Date.now());
72
72
  }
73
73
  }
74
+
75
+ /**
76
+ * Drop the vector_blob column from memory_embeddings.
77
+ *
78
+ * NOTE: Binary embedding data stored in vector_blob is lost on rollback.
79
+ * Rows that still have vector_json will continue to work; rows that only
80
+ * had vector_blob will lose their embedding vectors.
81
+ *
82
+ * SQLite does not support DROP COLUMN on all versions, so we rebuild the table.
83
+ */
84
+ export function downEmbeddingVectorBlob(database: DrizzleDb): void {
85
+ const raw = getSqliteFrom(database);
86
+
87
+ // Check if vector_blob column exists
88
+ const hasColumn = raw
89
+ .query(
90
+ `SELECT 1 FROM pragma_table_info('memory_embeddings') WHERE name = 'vector_blob'`,
91
+ )
92
+ .get();
93
+ if (!hasColumn) return;
94
+
95
+ raw.exec("PRAGMA foreign_keys = OFF");
96
+ try {
97
+ raw.exec("BEGIN");
98
+
99
+ // Get the current columns minus vector_blob
100
+ const columns = raw
101
+ .query(`SELECT name FROM pragma_table_info('memory_embeddings')`)
102
+ .all() as Array<{ name: string }>;
103
+ const keepColumns = columns
104
+ .map((c) => c.name)
105
+ .filter((n) => n !== "vector_blob");
106
+
107
+ // Get the current DDL to understand the table structure
108
+ const ddl = raw
109
+ .query(
110
+ `SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memory_embeddings'`,
111
+ )
112
+ .get() as { sql: string } | null;
113
+ if (!ddl) {
114
+ raw.exec("ROLLBACK");
115
+ return;
116
+ }
117
+
118
+ // Remove the vector_blob column definition from the DDL
119
+ const newDdl = ddl.sql
120
+ .replace(/,\s*vector_blob\s+BLOB/i, "")
121
+ .replace("memory_embeddings", "memory_embeddings_new");
122
+
123
+ raw.exec(newDdl);
124
+
125
+ const colList = keepColumns.join(", ");
126
+ raw.exec(/*sql*/ `
127
+ INSERT INTO memory_embeddings_new (${colList})
128
+ SELECT ${colList} FROM memory_embeddings
129
+ `);
130
+
131
+ raw.exec(/*sql*/ `DROP TABLE memory_embeddings`);
132
+ raw.exec(
133
+ /*sql*/ `ALTER TABLE memory_embeddings_new RENAME TO memory_embeddings`,
134
+ );
135
+
136
+ raw.exec("COMMIT");
137
+ } catch (e) {
138
+ try {
139
+ raw.exec("ROLLBACK");
140
+ } catch {
141
+ /* no active transaction */
142
+ }
143
+ throw e;
144
+ } finally {
145
+ raw.exec("PRAGMA foreign_keys = ON");
146
+ }
147
+ }
@@ -105,6 +105,88 @@ export function migrateEmbeddingsNullableVectorJson(database: DrizzleDb): void {
105
105
  }
106
106
  }
107
107
 
108
+ /**
109
+ * Reverse v13: rebuild memory_embeddings with NOT NULL on vector_json.
110
+ *
111
+ * WARNING: Any rows with NULL vector_json will be lost — they cannot satisfy
112
+ * the NOT NULL constraint. This is acceptable because the forward migration
113
+ * only relaxed the constraint; rows written after the forward migration may
114
+ * have NULL vector_json (relying on vector_blob instead).
115
+ */
116
+ export function downEmbeddingsNullableVectorJson(database: DrizzleDb): void {
117
+ const raw = getSqliteFrom(database);
118
+
119
+ const tableExists = raw
120
+ .query(
121
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'memory_embeddings'`,
122
+ )
123
+ .get();
124
+ if (!tableExists) return;
125
+
126
+ // Check if vector_json already has NOT NULL — already rolled back
127
+ const ddl = raw
128
+ .query(
129
+ `SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memory_embeddings'`,
130
+ )
131
+ .get() as { sql: string } | null;
132
+ if (ddl && isColumnNotNull(ddl.sql, "vector_json")) return;
133
+
134
+ raw.exec("PRAGMA foreign_keys = OFF");
135
+ try {
136
+ raw.exec("BEGIN");
137
+
138
+ raw.exec(/*sql*/ `
139
+ CREATE TABLE memory_embeddings_new (
140
+ id TEXT PRIMARY KEY,
141
+ target_type TEXT NOT NULL,
142
+ target_id TEXT NOT NULL,
143
+ provider TEXT NOT NULL,
144
+ model TEXT NOT NULL,
145
+ dimensions INTEGER NOT NULL,
146
+ vector_json TEXT NOT NULL,
147
+ vector_blob BLOB,
148
+ content_hash TEXT,
149
+ created_at INTEGER NOT NULL,
150
+ updated_at INTEGER NOT NULL,
151
+ UNIQUE (target_type, target_id, provider, model)
152
+ )
153
+ `);
154
+ // Only copy rows where vector_json is NOT NULL — rows with NULL cannot
155
+ // satisfy the restored constraint and are lost.
156
+ raw.exec(/*sql*/ `
157
+ INSERT OR IGNORE INTO memory_embeddings_new (
158
+ id, target_type, target_id, provider, model, dimensions,
159
+ vector_json, vector_blob, content_hash, created_at, updated_at
160
+ )
161
+ SELECT
162
+ id, target_type, target_id, provider, model, dimensions,
163
+ vector_json, vector_blob, content_hash, created_at, updated_at
164
+ FROM memory_embeddings
165
+ WHERE vector_json IS NOT NULL
166
+ ORDER BY updated_at DESC
167
+ `);
168
+ raw.exec(/*sql*/ `DROP TABLE memory_embeddings`);
169
+ raw.exec(
170
+ /*sql*/ `ALTER TABLE memory_embeddings_new RENAME TO memory_embeddings`,
171
+ );
172
+
173
+ raw.exec(
174
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_memory_embeddings_content_hash ON memory_embeddings(content_hash, provider, model)`,
175
+ );
176
+
177
+ raw.exec("COMMIT");
178
+ } catch (e) {
179
+ try {
180
+ raw.exec("ROLLBACK");
181
+ } catch {
182
+ /* no active transaction */
183
+ }
184
+ throw e;
185
+ } finally {
186
+ raw.exec("PRAGMA foreign_keys = ON");
187
+ }
188
+ }
189
+
108
190
  /** Check whether a column is declared NOT NULL in a CREATE TABLE DDL string. */
109
191
  function isColumnNotNull(ddl: string, column: string): boolean {
110
192
  const pattern = new RegExp(`${column}\\s+\\w+.*?NOT\\s+NULL`, "i");
@@ -336,3 +336,14 @@ export function migrateNormalizePhoneIdentities(database: DrizzleDb): void {
336
336
  throw e;
337
337
  }
338
338
  }
339
+
340
+ /**
341
+ * Reverse v14: no-op — original non-E.164 phone formats are not recoverable.
342
+ *
343
+ * The forward migration normalised phone numbers to E.164. The original
344
+ * formatting (parentheses, dashes, spaces, country-code variants) was
345
+ * discarded during normalisation and cannot be reconstructed.
346
+ */
347
+ export function downNormalizePhoneIdentities(_database: DrizzleDb): void {
348
+ // Lossy — original phone formats are not recoverable.
349
+ }
@@ -1,4 +1,7 @@
1
- import type { DrizzleDb } from "../db-connection.js";
1
+ import { getLogger } from "../../util/logger.js";
2
+ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
3
+
4
+ const logger = getLogger("messages-fts");
2
5
 
3
6
  /**
4
7
  * FTS5 virtual table for full-text search over messages.content.
@@ -21,7 +24,81 @@ import type { DrizzleDb } from "../db-connection.js";
21
24
  * ALL writes to the messages table to fail until the FTS table is rebuilt.
22
25
  * If this happens, `messages_fts` should be dropped and recreated, then
23
26
  * backfilled via `migrateMessagesFtsBackfill`.
27
+ *
28
+ * ## Auto-recovery from corruption
29
+ *
30
+ * After creating (or finding an existing) messages_fts table, we probe it
31
+ * with a lightweight MATCH query that exercises the FTS inverted index
32
+ * in O(1). If the probe throws SQLITE_CORRUPT_VTAB or SQLITE_CORRUPT,
33
+ * we force-remove all shadow tables and the vtable entry (falling back
34
+ * to `PRAGMA writable_schema` if DROP TABLE itself fails on the corrupt
35
+ * vtable) and recreate it from scratch. The subsequent
36
+ * `migrateMessagesFtsBackfill` call in db-init.ts will repopulate the
37
+ * index from the messages table — no message data is lost.
38
+ */
39
+ function isSqliteCorruptionError(err: unknown): boolean {
40
+ const code =
41
+ err != null && typeof err === "object" && "code" in err
42
+ ? (err as { code: string }).code
43
+ : undefined;
44
+ return code === "SQLITE_CORRUPT_VTAB" || code === "SQLITE_CORRUPT";
45
+ }
46
+
47
+ /**
48
+ * Force-remove all FTS5 shadow tables, triggers, and the vtable entry.
49
+ *
50
+ * We drop each artifact individually so that a corrupt shadow table
51
+ * doesn't block cleanup of the others. If `DROP TABLE messages_fts`
52
+ * itself fails (FTS5's xDestroy hits a corrupt shadow table), we fall
53
+ * back to `PRAGMA writable_schema` to delete the vtable entry directly
54
+ * from `sqlite_schema`. Without this fallback, `CREATE VIRTUAL TABLE
55
+ * IF NOT EXISTS` would be a no-op and the crash loop would persist.
24
56
  */
57
+ function dropFtsShadowTables(raw: ReturnType<typeof getSqliteFrom>): void {
58
+ const drops = [
59
+ `DROP TRIGGER IF EXISTS messages_fts_ai`,
60
+ `DROP TRIGGER IF EXISTS messages_fts_ad`,
61
+ `DROP TRIGGER IF EXISTS messages_fts_au`,
62
+ `DROP TABLE IF EXISTS messages_fts_config`,
63
+ `DROP TABLE IF EXISTS messages_fts_docsize`,
64
+ `DROP TABLE IF EXISTS messages_fts_content`,
65
+ `DROP TABLE IF EXISTS messages_fts_idx`,
66
+ `DROP TABLE IF EXISTS messages_fts_data`,
67
+ ];
68
+ for (const sql of drops) {
69
+ try {
70
+ raw.exec(sql);
71
+ } catch {
72
+ // Shadow table may itself be corrupt — ignore and continue
73
+ }
74
+ }
75
+
76
+ // Try the normal DROP TABLE path first (lets FTS5 clean up properly).
77
+ try {
78
+ raw.exec(`DROP TABLE IF EXISTS messages_fts`);
79
+ } catch {
80
+ // FTS5's xDestroy failed — force-remove the vtable entry from
81
+ // sqlite_schema so CREATE VIRTUAL TABLE isn't a no-op.
82
+ logger.warn(
83
+ "[messages-fts] DROP TABLE messages_fts failed — removing vtable entry via writable_schema",
84
+ );
85
+ raw.exec(`PRAGMA writable_schema = ON`);
86
+ try {
87
+ raw.exec(
88
+ `DELETE FROM sqlite_schema WHERE type = 'table' AND name = 'messages_fts'`,
89
+ );
90
+ } catch (schemaErr) {
91
+ logger.error(
92
+ { err: schemaErr },
93
+ "[messages-fts] Failed to remove vtable entry from sqlite_schema",
94
+ );
95
+ throw schemaErr;
96
+ } finally {
97
+ raw.exec(`PRAGMA writable_schema = OFF`);
98
+ }
99
+ }
100
+ }
101
+
25
102
  export function createMessagesFts(database: DrizzleDb): void {
26
103
  database.run(/*sql*/ `
27
104
  CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
@@ -30,6 +107,34 @@ export function createMessagesFts(database: DrizzleDb): void {
30
107
  )
31
108
  `);
32
109
 
110
+ // Probe the FTS inverted index for corruption. A MATCH query exercises
111
+ // the index structures (not just the content store), so it catches
112
+ // corruption in shadow tables like _idx and _data. On empty tables
113
+ // this returns null gracefully. O(1) with LIMIT 1.
114
+ const raw = getSqliteFrom(database);
115
+ try {
116
+ raw
117
+ .query(`SELECT * FROM messages_fts WHERE messages_fts MATCH 'a' LIMIT 1`)
118
+ .get();
119
+ } catch (err: unknown) {
120
+ if (!isSqliteCorruptionError(err)) {
121
+ throw err;
122
+ }
123
+ logger.warn(
124
+ { err },
125
+ "[messages-fts] Detected corrupt messages_fts virtual table — dropping and recreating",
126
+ );
127
+ // DROP TABLE on a corrupt vtable can itself throw, so drop the
128
+ // FTS5 shadow tables directly to guarantee cleanup.
129
+ dropFtsShadowTables(raw);
130
+ database.run(/*sql*/ `
131
+ CREATE VIRTUAL TABLE messages_fts USING fts5(
132
+ message_id UNINDEXED,
133
+ content
134
+ )
135
+ `);
136
+ }
137
+
33
138
  database.run(/*sql*/ `
34
139
  CREATE TRIGGER IF NOT EXISTS messages_fts_ai
35
140
  AFTER INSERT ON messages
@@ -1,6 +1,58 @@
1
1
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
2
  import { withCrashRecovery } from "./validate-migration-state.js";
3
3
 
4
+ /**
5
+ * Reverse v15: set guardian_principal_id back to NULL on all rows in
6
+ * channel_guardian_bindings and canonical_guardian_requests.
7
+ *
8
+ * Also un-expires requests that the forward migration expired (sets them
9
+ * back to 'pending'). This is a best-effort reversal — the original status
10
+ * of expired requests cannot be perfectly reconstructed if they were already
11
+ * expired before the forward migration ran, but the forward migration only
12
+ * expired requests that had NULL guardian_principal_id and status = 'pending'.
13
+ */
14
+ export function downBackfillGuardianPrincipalId(database: DrizzleDb): void {
15
+ const raw = getSqliteFrom(database);
16
+
17
+ // Null out guardian_principal_id on channel_guardian_bindings
18
+ const bindingsExists = raw
19
+ .query(
20
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'channel_guardian_bindings'`,
21
+ )
22
+ .get();
23
+ if (bindingsExists) {
24
+ const colExists = raw
25
+ .query(
26
+ `SELECT 1 FROM pragma_table_info('channel_guardian_bindings') WHERE name = 'guardian_principal_id'`,
27
+ )
28
+ .get();
29
+ if (colExists) {
30
+ raw.exec(
31
+ /*sql*/ `UPDATE channel_guardian_bindings SET guardian_principal_id = NULL`,
32
+ );
33
+ }
34
+ }
35
+
36
+ // Null out guardian_principal_id on canonical_guardian_requests
37
+ const requestsExists = raw
38
+ .query(
39
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'canonical_guardian_requests'`,
40
+ )
41
+ .get();
42
+ if (requestsExists) {
43
+ const colExists = raw
44
+ .query(
45
+ `SELECT 1 FROM pragma_table_info('canonical_guardian_requests') WHERE name = 'guardian_principal_id'`,
46
+ )
47
+ .get();
48
+ if (colExists) {
49
+ raw.exec(
50
+ /*sql*/ `UPDATE canonical_guardian_requests SET guardian_principal_id = NULL`,
51
+ );
52
+ }
53
+ }
54
+ }
55
+
4
56
  /**
5
57
  * Backfill guardianPrincipalId for existing channel_guardian_bindings and
6
58
  * canonical_guardian_requests rows.
@@ -1,6 +1,83 @@
1
1
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
2
  import { withCrashRecovery } from "./validate-migration-state.js";
3
3
 
4
+ /**
5
+ * Reverse v16: rebuild channel_guardian_bindings to make guardian_principal_id
6
+ * nullable again (removing the NOT NULL constraint added by the forward migration).
7
+ */
8
+ export function downGuardianPrincipalIdNotNull(database: DrizzleDb): void {
9
+ const raw = getSqliteFrom(database);
10
+
11
+ const tableExists = raw
12
+ .query(
13
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'channel_guardian_bindings'`,
14
+ )
15
+ .get();
16
+ if (!tableExists) return;
17
+
18
+ // Check if guardian_principal_id has NOT NULL — if not, already rolled back
19
+ const colInfo = raw
20
+ .query(
21
+ `SELECT "notnull" FROM pragma_table_info('channel_guardian_bindings') WHERE name = 'guardian_principal_id'`,
22
+ )
23
+ .get() as { notnull: number } | null;
24
+ if (!colInfo || colInfo.notnull === 0) return;
25
+
26
+ raw.exec("PRAGMA foreign_keys = OFF");
27
+ try {
28
+ raw.exec("BEGIN");
29
+
30
+ raw.exec(/*sql*/ `
31
+ CREATE TABLE channel_guardian_bindings_new (
32
+ id TEXT PRIMARY KEY,
33
+ assistant_id TEXT NOT NULL,
34
+ channel TEXT NOT NULL,
35
+ guardian_external_user_id TEXT NOT NULL,
36
+ guardian_delivery_chat_id TEXT NOT NULL,
37
+ guardian_principal_id TEXT,
38
+ status TEXT NOT NULL DEFAULT 'active',
39
+ verified_at INTEGER NOT NULL,
40
+ verified_via TEXT NOT NULL DEFAULT 'challenge',
41
+ metadata_json TEXT,
42
+ created_at INTEGER NOT NULL,
43
+ updated_at INTEGER NOT NULL
44
+ )
45
+ `);
46
+
47
+ raw.exec(/*sql*/ `
48
+ INSERT INTO channel_guardian_bindings_new
49
+ SELECT id, assistant_id, channel, guardian_external_user_id,
50
+ guardian_delivery_chat_id, guardian_principal_id,
51
+ status, verified_at, verified_via, metadata_json,
52
+ created_at, updated_at
53
+ FROM channel_guardian_bindings
54
+ `);
55
+
56
+ raw.exec(/*sql*/ `DROP TABLE channel_guardian_bindings`);
57
+ raw.exec(
58
+ /*sql*/ `ALTER TABLE channel_guardian_bindings_new RENAME TO channel_guardian_bindings`,
59
+ );
60
+
61
+ // Recreate the unique index for active bindings
62
+ raw.exec(/*sql*/ `
63
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_guardian_bindings_active
64
+ ON channel_guardian_bindings(assistant_id, channel)
65
+ WHERE status = 'active'
66
+ `);
67
+
68
+ raw.exec("COMMIT");
69
+ } catch (e) {
70
+ try {
71
+ raw.exec("ROLLBACK");
72
+ } catch {
73
+ /* no active transaction */
74
+ }
75
+ throw e;
76
+ } finally {
77
+ raw.exec("PRAGMA foreign_keys = ON");
78
+ }
79
+ }
80
+
4
81
  /**
5
82
  * Enforce NOT NULL on channel_guardian_bindings.guardian_principal_id.
6
83
  *
@@ -2,6 +2,19 @@ import { getLogger } from "../../util/logger.js";
2
2
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
3
3
  import { withCrashRecovery } from "./validate-migration-state.js";
4
4
 
5
+ /**
6
+ * Reverse v17: no-op — the original separate columns (relationship, importance,
7
+ * response_expectation, preferred_tone) cannot be reliably restored from the
8
+ * consolidated notes TEXT column.
9
+ *
10
+ * The forward migration concatenated multiple typed fields into a single
11
+ * free-text notes field and then dropped the original columns. Parsing the
12
+ * notes back into structured fields would be lossy and error-prone.
13
+ */
14
+ export function downContactsNotesColumn(_database: DrizzleDb): void {
15
+ // Lossy — original structured columns cannot be restored from notes text.
16
+ }
17
+
5
18
  const log = getLogger("migration-134");
6
19
 
7
20
  export function migrateContactsNotesColumn(database: DrizzleDb): void {
@@ -1,6 +1,26 @@
1
1
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
2
  import { withCrashRecovery } from "./validate-migration-state.js";
3
3
 
4
+ /**
5
+ * Reverse v18: set contacts.last_interaction back to NULL.
6
+ *
7
+ * The forward migration backfilled last_interaction from channel data.
8
+ * Rolling back simply clears the column — the data can be re-derived by
9
+ * re-running the forward migration.
10
+ */
11
+ export function downBackfillContactInteractionStats(database: DrizzleDb): void {
12
+ const raw = getSqliteFrom(database);
13
+
14
+ const colExists = raw
15
+ .query(
16
+ `SELECT 1 FROM pragma_table_info('contacts') WHERE name = 'last_interaction'`,
17
+ )
18
+ .get();
19
+ if (!colExists) return;
20
+
21
+ raw.exec(/*sql*/ `UPDATE contacts SET last_interaction = NULL`);
22
+ }
23
+
4
24
  /**
5
25
  * Backfill contacts.last_interaction from the max lastSeenAt across each
6
26
  * contact's channels. interactionCount cannot be reliably derived from
@@ -2,6 +2,58 @@ import { getLogger } from "../../util/logger.js";
2
2
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
3
3
  import { withCrashRecovery } from "./validate-migration-state.js";
4
4
 
5
+ /**
6
+ * Reverse v19: add assistant_id columns back to all 16 tables via
7
+ * ALTER TABLE ADD COLUMN, defaulting to 'self'.
8
+ *
9
+ * This restores the column that the forward migration dropped. All rows
10
+ * get assistant_id = 'self' since that was the only value before dropping.
11
+ */
12
+ export function downDropAssistantIdColumns(database: DrizzleDb): void {
13
+ const raw = getSqliteFrom(database);
14
+
15
+ const tables = [
16
+ "contacts",
17
+ "assistant_ingress_invites",
18
+ "assistant_inbox_thread_state",
19
+ "call_sessions",
20
+ "channel_guardian_verification_challenges",
21
+ "channel_guardian_approval_requests",
22
+ "channel_guardian_rate_limits",
23
+ "guardian_action_requests",
24
+ "scoped_approval_grants",
25
+ "notification_events",
26
+ "notification_preferences",
27
+ "notification_deliveries",
28
+ "conversation_attention_events",
29
+ "conversation_assistant_attention_state",
30
+ "actor_token_records",
31
+ "actor_refresh_token_records",
32
+ ];
33
+
34
+ for (const table of tables) {
35
+ // Skip if table doesn't exist
36
+ const tableExists = raw
37
+ .query(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?`)
38
+ .get(table);
39
+ if (!tableExists) continue;
40
+
41
+ // Skip if column already exists (idempotent)
42
+ const colExists = raw
43
+ .query(`SELECT 1 FROM pragma_table_info(?) WHERE name = 'assistant_id'`)
44
+ .get(table);
45
+ if (colExists) continue;
46
+
47
+ try {
48
+ raw.exec(
49
+ /*sql*/ `ALTER TABLE ${table} ADD COLUMN assistant_id TEXT NOT NULL DEFAULT 'self'`,
50
+ );
51
+ } catch {
52
+ /* column may already exist from partial run */
53
+ }
54
+ }
55
+ }
56
+
5
57
  const log = getLogger("migration-136");
6
58
 
7
59
  /**
@@ -8,6 +8,19 @@ import { resolvePricingForUsageWithOverrides } from "../../util/pricing.js";
8
8
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
9
9
  import { withCrashRecovery } from "./validate-migration-state.js";
10
10
 
11
+ /**
12
+ * Reverse v20: no-op — cannot reliably identify which llm_usage_events rows
13
+ * were updated by the backfill vs already had correct cache accounting.
14
+ *
15
+ * The forward migration updated input_tokens, output_tokens, cache token
16
+ * columns, estimated_cost_usd, and pricing_status based on request logs.
17
+ * There is no marker distinguishing backfilled rows from naturally-written
18
+ * ones, so a reversal cannot be performed without risking data corruption.
19
+ */
20
+ export function downBackfillUsageCacheAccounting(_database: DrizzleDb): void {
21
+ // Lossy — cannot identify which rows were backfilled.
22
+ }
23
+
11
24
  const log = getLogger("memory-db");
12
25
 
13
26
  const CHECKPOINT_KEY = "migration_backfill_usage_cache_accounting_v1";
@@ -1,6 +1,60 @@
1
1
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
2
  import { withCrashRecovery } from "./validate-migration-state.js";
3
3
 
4
+ /**
5
+ * Reverse v21: rename channel_verification_sessions back to
6
+ * channel_guardian_verification_challenges and recreate old indexes.
7
+ */
8
+ export function downRenameVerificationTable(database: DrizzleDb): void {
9
+ const raw = getSqliteFrom(database);
10
+
11
+ // Check the new table exists before attempting anything
12
+ const newTableExists = raw
13
+ .query(
14
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'channel_verification_sessions'`,
15
+ )
16
+ .get();
17
+ if (!newTableExists) return;
18
+
19
+ // If the old table already exists, skip (already rolled back)
20
+ const oldTableExists = raw
21
+ .query(
22
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'channel_guardian_verification_challenges'`,
23
+ )
24
+ .get();
25
+ if (oldTableExists) return;
26
+
27
+ // Rename back to old name
28
+ raw.exec(
29
+ /*sql*/ `ALTER TABLE channel_verification_sessions RENAME TO channel_guardian_verification_challenges`,
30
+ );
31
+
32
+ // Drop new-style indexes and recreate old-style ones
33
+ raw.exec(/*sql*/ `DROP INDEX IF EXISTS idx_verification_sessions_lookup`);
34
+ raw.exec(/*sql*/ `DROP INDEX IF EXISTS idx_verification_sessions_active`);
35
+ raw.exec(/*sql*/ `DROP INDEX IF EXISTS idx_verification_sessions_identity`);
36
+ raw.exec(
37
+ /*sql*/ `DROP INDEX IF EXISTS idx_verification_sessions_destination`,
38
+ );
39
+ raw.exec(/*sql*/ `DROP INDEX IF EXISTS idx_verification_sessions_bootstrap`);
40
+
41
+ raw.exec(
42
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_channel_guardian_challenges_lookup ON channel_guardian_verification_challenges(channel, challenge_hash, status)`,
43
+ );
44
+ raw.exec(
45
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_guardian_sessions_active ON channel_guardian_verification_challenges(channel, status)`,
46
+ );
47
+ raw.exec(
48
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_guardian_sessions_identity ON channel_guardian_verification_challenges(channel, expected_external_user_id, expected_chat_id, status)`,
49
+ );
50
+ raw.exec(
51
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_guardian_sessions_destination ON channel_guardian_verification_challenges(channel, destination_address)`,
52
+ );
53
+ raw.exec(
54
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_guardian_sessions_bootstrap ON channel_guardian_verification_challenges(channel, bootstrap_token_hash, status)`,
55
+ );
56
+ }
57
+
4
58
  /**
5
59
  * One-shot migration: rename channel_guardian_verification_challenges →
6
60
  * channel_verification_sessions, including all indexes that reference the
@@ -1,6 +1,31 @@
1
1
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
2
  import { withCrashRecovery } from "./validate-migration-state.js";
3
3
 
4
+ /**
5
+ * Reverse v22: rename verification_session_id back to
6
+ * guardian_verification_session_id in call_sessions.
7
+ */
8
+ export function downRenameVerificationSessionIdColumn(
9
+ database: DrizzleDb,
10
+ ): void {
11
+ const raw = getSqliteFrom(database);
12
+
13
+ const columns = raw.query(`PRAGMA table_info(call_sessions)`).all() as Array<{
14
+ name: string;
15
+ }>;
16
+ const hasNewColumn = columns.some(
17
+ (c) => c.name === "verification_session_id",
18
+ );
19
+ const hasOldColumn = columns.some(
20
+ (c) => c.name === "guardian_verification_session_id",
21
+ );
22
+ if (!hasNewColumn || hasOldColumn) return;
23
+
24
+ raw.exec(
25
+ /*sql*/ `ALTER TABLE call_sessions RENAME COLUMN verification_session_id TO guardian_verification_session_id`,
26
+ );
27
+ }
28
+
4
29
  /**
5
30
  * One-shot migration: rename the guardian_verification_session_id column
6
31
  * in call_sessions to verification_session_id, dropping the "guardian_"