@vellumai/assistant 0.5.6 → 0.5.8

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 (442) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +3 -2
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docker-entrypoint.sh +9 -0
  7. package/docs/architecture/keychain-broker.md +45 -240
  8. package/docs/architecture/memory.md +13 -11
  9. package/docs/architecture/security.md +0 -17
  10. package/docs/credential-execution-service.md +2 -2
  11. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-contracts/src/error.ts +1 -1
  13. package/node_modules/@vellumai/ces-contracts/src/grants.ts +1 -1
  14. package/node_modules/@vellumai/ces-contracts/src/handles.ts +1 -1
  15. package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -1
  16. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +120 -1
  17. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  18. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  19. package/package.json +2 -3
  20. package/src/__tests__/actor-token-service.test.ts +0 -114
  21. package/src/__tests__/approval-cascade.test.ts +0 -1
  22. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  23. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  24. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  25. package/src/__tests__/btw-routes.test.ts +0 -39
  26. package/src/__tests__/call-controller.test.ts +0 -1
  27. package/src/__tests__/call-domain.test.ts +0 -128
  28. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  29. package/src/__tests__/ces-startup-timeout.test.ts +40 -0
  30. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  31. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  32. package/src/__tests__/checker.test.ts +4 -2
  33. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  34. package/src/__tests__/config-schema-cmd.test.ts +0 -2
  35. package/src/__tests__/config-schema.test.ts +3 -1
  36. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -1
  37. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
  38. package/src/__tests__/conversation-agent-loop.test.ts +2 -4
  39. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  40. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -1
  41. package/src/__tests__/conversation-error.test.ts +15 -1
  42. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  43. package/src/__tests__/conversation-messaging-secret-redirect.test.ts +1 -1
  44. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -1
  45. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -1
  46. package/src/__tests__/conversation-queue.test.ts +0 -1
  47. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  48. package/src/__tests__/conversation-slash-queue.test.ts +0 -1
  49. package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
  50. package/src/__tests__/conversation-title-service.test.ts +87 -0
  51. package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
  52. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
  53. package/src/__tests__/credential-execution-client.test.ts +5 -2
  54. package/src/__tests__/credential-execution-feature-gates.test.ts +59 -30
  55. package/src/__tests__/credential-execution-managed-contract.test.ts +35 -20
  56. package/src/__tests__/credential-security-e2e.test.ts +1 -67
  57. package/src/__tests__/credential-security-invariants.test.ts +6 -50
  58. package/src/__tests__/credentials-cli.test.ts +82 -3
  59. package/src/__tests__/daemon-credential-client.test.ts +123 -0
  60. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  61. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -0
  62. package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
  63. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  64. package/src/__tests__/gateway-client-managed-outbound.test.ts +79 -1
  65. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  66. package/src/__tests__/host-shell-tool.test.ts +6 -7
  67. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  68. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  69. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  70. package/src/__tests__/intent-routing.test.ts +0 -13
  71. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  72. package/src/__tests__/journal-context.test.ts +335 -0
  73. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  74. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -3
  75. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  76. package/src/__tests__/memory-lifecycle-e2e.test.ts +70 -25
  77. package/src/__tests__/memory-recall-quality.test.ts +48 -17
  78. package/src/__tests__/memory-regressions.test.ts +408 -363
  79. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -3
  80. package/src/__tests__/migration-export-http.test.ts +2 -2
  81. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  82. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  83. package/src/__tests__/migration-validate-http.test.ts +2 -2
  84. package/src/__tests__/non-member-access-request.test.ts +2 -7
  85. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  86. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  87. package/src/__tests__/notification-decision-strategy.test.ts +71 -0
  88. package/src/__tests__/oauth-cli.test.ts +5 -1
  89. package/src/__tests__/permission-types.test.ts +1 -0
  90. package/src/__tests__/provider-commit-message-generator.test.ts +0 -37
  91. package/src/__tests__/provider-error-scenarios.test.ts +0 -267
  92. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  93. package/src/__tests__/provider-streaming.benchmark.test.ts +2 -81
  94. package/src/__tests__/qdrant-manager.test.ts +28 -2
  95. package/src/__tests__/registry.test.ts +0 -6
  96. package/src/__tests__/relay-server.test.ts +1 -2
  97. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  98. package/src/__tests__/script-proxy-injection-runtime.test.ts +1 -1
  99. package/src/__tests__/secret-onetime-send.test.ts +1 -1
  100. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  101. package/src/__tests__/secure-keys.test.ts +95 -272
  102. package/src/__tests__/shell-identity.test.ts +96 -6
  103. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  104. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  105. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  106. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  107. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  108. package/src/__tests__/skill-load-tool.test.ts +0 -2
  109. package/src/__tests__/skill-memory.test.ts +17 -3
  110. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  111. package/src/__tests__/skills.test.ts +0 -2
  112. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  113. package/src/__tests__/stale-approval-dedup.test.ts +171 -0
  114. package/src/__tests__/stt-hints.test.ts +437 -0
  115. package/src/__tests__/suggestion-routes.test.ts +1 -32
  116. package/src/__tests__/system-prompt.test.ts +0 -1
  117. package/src/__tests__/task-memory-cleanup.test.ts +14 -0
  118. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  119. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  120. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  121. package/src/__tests__/twilio-routes-twiml.test.ts +139 -1
  122. package/src/__tests__/update-bulletin.test.ts +0 -2
  123. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  124. package/src/__tests__/voice-quality.test.ts +58 -0
  125. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -7
  126. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  127. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +220 -0
  128. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  129. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  130. package/src/acp/agent-process.ts +9 -1
  131. package/src/agent/loop.ts +1 -1
  132. package/src/approvals/guardian-request-resolvers.ts +164 -38
  133. package/src/calls/__tests__/tts-text-sanitizer.test.ts +254 -0
  134. package/src/calls/audio-store.test.ts +97 -0
  135. package/src/calls/audio-store.ts +205 -0
  136. package/src/calls/call-controller.ts +90 -8
  137. package/src/calls/call-domain.ts +3 -0
  138. package/src/calls/call-store.ts +10 -3
  139. package/src/calls/fish-audio-client.ts +129 -0
  140. package/src/calls/relay-server.ts +27 -0
  141. package/src/calls/stt-hints.ts +189 -0
  142. package/src/calls/tts-text-sanitizer.ts +61 -0
  143. package/src/calls/twilio-routes.ts +34 -5
  144. package/src/calls/types.ts +1 -0
  145. package/src/calls/voice-ingress-preflight.ts +0 -42
  146. package/src/calls/voice-quality.ts +38 -5
  147. package/src/calls/voice-session-bridge.ts +7 -12
  148. package/src/cli/commands/avatar.ts +2 -2
  149. package/src/cli/commands/config.ts +1 -4
  150. package/src/cli/commands/credentials.ts +128 -82
  151. package/src/cli/commands/doctor.ts +2 -2
  152. package/src/cli/commands/keys.ts +7 -7
  153. package/src/cli/commands/memory.ts +1 -1
  154. package/src/cli/commands/oauth/connections.ts +11 -29
  155. package/src/cli/commands/oauth/index.ts +7 -0
  156. package/src/cli/commands/oauth/platform.ts +525 -0
  157. package/src/cli/commands/platform.ts +3 -3
  158. package/src/cli/lib/daemon-credential-client.ts +284 -0
  159. package/src/cli.ts +1 -1
  160. package/src/config/assistant-feature-flags.ts +186 -5
  161. package/src/config/bundled-skills/AGENTS.md +34 -0
  162. package/src/config/bundled-skills/acp/SKILL.md +10 -0
  163. package/src/config/bundled-skills/app-builder/SKILL.md +0 -4
  164. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  165. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  166. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  167. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -0
  168. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -0
  169. package/src/config/bundled-skills/settings/SKILL.md +15 -2
  170. package/src/config/bundled-skills/settings/TOOLS.json +47 -2
  171. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +59 -0
  172. package/src/config/bundled-skills/settings/tools/avatar-update.ts +80 -0
  173. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  174. package/src/config/bundled-skills/slack/SKILL.md +1 -1
  175. package/src/config/bundled-tool-registry.ts +5 -11
  176. package/src/config/defaults.ts +0 -2
  177. package/src/config/env-registry.ts +5 -5
  178. package/src/config/env.ts +21 -14
  179. package/src/config/feature-flag-registry.json +49 -9
  180. package/src/config/loader.ts +106 -42
  181. package/src/config/schema.ts +9 -29
  182. package/src/config/schemas/calls.ts +30 -0
  183. package/src/config/schemas/fish-audio.ts +39 -0
  184. package/src/config/schemas/inference.ts +2 -2
  185. package/src/config/schemas/journal.ts +16 -0
  186. package/src/config/schemas/memory-processing.ts +2 -2
  187. package/src/config/schemas/security.ts +0 -4
  188. package/src/config/types.ts +1 -1
  189. package/src/contacts/contact-store.ts +39 -0
  190. package/src/contacts/types.ts +2 -0
  191. package/src/credential-execution/approval-bridge.ts +1 -0
  192. package/src/credential-execution/executable-discovery.ts +28 -4
  193. package/src/credential-execution/feature-gates.ts +16 -0
  194. package/src/credential-execution/process-manager.ts +38 -0
  195. package/src/credential-execution/startup-timeout.ts +36 -0
  196. package/src/daemon/approval-generators.ts +3 -9
  197. package/src/daemon/assistant-attachments.ts +9 -0
  198. package/src/daemon/config-watcher.ts +5 -0
  199. package/src/daemon/conversation-error.ts +13 -1
  200. package/src/daemon/conversation-memory.ts +1 -2
  201. package/src/daemon/conversation-process.ts +18 -1
  202. package/src/daemon/conversation-surfaces.ts +30 -1
  203. package/src/daemon/conversation-tool-setup.ts +0 -105
  204. package/src/daemon/conversation.ts +21 -1
  205. package/src/daemon/guardian-action-generators.ts +3 -9
  206. package/src/daemon/handlers/config-vercel.ts +92 -0
  207. package/src/daemon/handlers/skills.ts +2 -15
  208. package/src/daemon/install-symlink.ts +195 -0
  209. package/src/daemon/lifecycle.ts +234 -51
  210. package/src/daemon/message-types/conversations.ts +4 -4
  211. package/src/daemon/message-types/diagnostics.ts +3 -22
  212. package/src/daemon/message-types/messages.ts +0 -2
  213. package/src/daemon/message-types/upgrades.ts +8 -0
  214. package/src/daemon/server.ts +32 -95
  215. package/src/events/domain-events.ts +2 -1
  216. package/src/inbound/platform-callback-registration.ts +3 -3
  217. package/src/instrument.ts +8 -5
  218. package/src/memory/app-store.ts +31 -0
  219. package/src/memory/conversation-title-service.ts +50 -1
  220. package/src/memory/db-init.ts +16 -0
  221. package/src/memory/indexer.ts +19 -10
  222. package/src/memory/items-extractor.ts +328 -321
  223. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  224. package/src/memory/job-handlers/summarization.ts +26 -16
  225. package/src/memory/jobs-store.ts +63 -6
  226. package/src/memory/jobs-worker.ts +31 -7
  227. package/src/memory/journal-memory.ts +214 -0
  228. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  229. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  230. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  231. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  232. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  233. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  234. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  235. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  236. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  237. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  238. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  239. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  240. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  241. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  242. package/src/memory/migrations/116-messages-fts.ts +106 -1
  243. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  244. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  245. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  246. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  247. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  248. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  249. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  250. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  251. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  252. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  253. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  254. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  255. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  256. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  257. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  258. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  259. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  260. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  261. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  262. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  263. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  264. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  265. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  266. package/src/memory/migrations/193-add-source-type-columns.ts +81 -0
  267. package/src/memory/migrations/index.ts +5 -0
  268. package/src/memory/migrations/registry.ts +98 -0
  269. package/src/memory/migrations/validate-migration-state.ts +137 -11
  270. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  271. package/src/memory/qdrant-manager.ts +64 -7
  272. package/src/memory/retriever.test.ts +37 -25
  273. package/src/memory/retriever.ts +24 -49
  274. package/src/memory/schema/calls.ts +1 -0
  275. package/src/memory/schema/contacts.ts +1 -0
  276. package/src/memory/schema/memory-core.ts +2 -0
  277. package/src/memory/search/formatting.ts +7 -44
  278. package/src/memory/search/staleness.ts +4 -0
  279. package/src/memory/search/tier-classifier.ts +10 -2
  280. package/src/memory/search/types.ts +2 -5
  281. package/src/memory/task-memory-cleanup.ts +4 -3
  282. package/src/notifications/adapters/slack.ts +168 -6
  283. package/src/notifications/broadcaster.ts +1 -0
  284. package/src/notifications/copy-composer.ts +59 -2
  285. package/src/notifications/decision-engine.ts +4 -1
  286. package/src/notifications/signal.ts +2 -0
  287. package/src/notifications/types.ts +2 -0
  288. package/src/oauth/connection-resolver.ts +6 -4
  289. package/src/permissions/checker.ts +0 -38
  290. package/src/permissions/shell-identity.ts +76 -22
  291. package/src/permissions/types.ts +4 -2
  292. package/src/platform/client.ts +35 -7
  293. package/src/prompts/journal-context.ts +133 -0
  294. package/src/prompts/persona-resolver.ts +194 -0
  295. package/src/prompts/system-prompt.ts +44 -4
  296. package/src/prompts/templates/SOUL.md +10 -0
  297. package/src/prompts/templates/users/default.md +1 -0
  298. package/src/providers/provider-send-message.ts +3 -32
  299. package/src/providers/registry.ts +29 -179
  300. package/src/providers/types.ts +1 -1
  301. package/src/runtime/access-request-helper.ts +4 -0
  302. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  303. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  304. package/src/runtime/auth/__tests__/guard-tests.test.ts +9 -50
  305. package/src/runtime/auth/external-assistant-id.ts +13 -59
  306. package/src/runtime/auth/route-policy.ts +17 -1
  307. package/src/runtime/auth/token-service.ts +43 -138
  308. package/src/runtime/channel-readiness-service.ts +1 -16
  309. package/src/runtime/gateway-client.ts +47 -4
  310. package/src/runtime/guardian-decision-types.ts +45 -4
  311. package/src/runtime/http-server.ts +31 -3
  312. package/src/runtime/middleware/error-handler.ts +1 -9
  313. package/src/runtime/routes/access-request-decision.ts +2 -2
  314. package/src/runtime/routes/app-management-routes.ts +2 -1
  315. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +219 -30
  316. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +37 -14
  317. package/src/runtime/routes/audio-routes.ts +40 -0
  318. package/src/runtime/routes/btw-routes.ts +0 -17
  319. package/src/runtime/routes/channel-readiness-routes.ts +9 -4
  320. package/src/runtime/routes/conversation-query-routes.ts +63 -1
  321. package/src/runtime/routes/conversation-routes.ts +4 -44
  322. package/src/runtime/routes/debug-routes.ts +12 -9
  323. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  324. package/src/runtime/routes/guardian-approval-interception.ts +168 -11
  325. package/src/runtime/routes/guardian-approval-prompt.ts +6 -1
  326. package/src/runtime/routes/guardian-approval-reply-helpers.ts +103 -21
  327. package/src/runtime/routes/identity-routes.ts +19 -30
  328. package/src/runtime/routes/inbound-message-handler.ts +31 -1
  329. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +64 -5
  330. package/src/runtime/routes/inbound-stages/background-dispatch.ts +52 -40
  331. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  332. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  333. package/src/runtime/routes/integrations/twilio.ts +52 -10
  334. package/src/runtime/routes/integrations/vercel.ts +89 -0
  335. package/src/runtime/routes/log-export-routes.ts +5 -0
  336. package/src/runtime/routes/memory-item-routes.test.ts +3 -3
  337. package/src/runtime/routes/memory-item-routes.ts +46 -14
  338. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  339. package/src/runtime/routes/migration-routes.ts +17 -1
  340. package/src/runtime/routes/notification-routes.ts +58 -0
  341. package/src/runtime/routes/schedule-routes.ts +65 -0
  342. package/src/runtime/routes/secret-routes.ts +141 -10
  343. package/src/runtime/routes/settings-routes.ts +41 -1
  344. package/src/runtime/routes/tts-routes.ts +96 -0
  345. package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
  346. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  347. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  348. package/src/runtime/routes/workspace-routes.ts +1 -1
  349. package/src/runtime/routes/workspace-utils.ts +86 -2
  350. package/src/security/ces-credential-client.ts +75 -29
  351. package/src/security/ces-rpc-credential-backend.ts +86 -0
  352. package/src/security/credential-backend.ts +22 -92
  353. package/src/security/keychain-broker-client.ts +10 -2
  354. package/src/security/secure-keys.ts +113 -115
  355. package/src/skills/catalog-install.ts +6 -32
  356. package/src/skills/skill-memory.ts +1 -0
  357. package/src/subagent/manager.ts +2 -5
  358. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  359. package/src/tools/acp/spawn.ts +78 -1
  360. package/src/tools/calls/call-start.ts +1 -0
  361. package/src/tools/credentials/vault.ts +5 -3
  362. package/src/tools/executor.ts +0 -4
  363. package/src/tools/memory/definitions.ts +3 -2
  364. package/src/tools/memory/handlers.ts +10 -7
  365. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  366. package/src/tools/network/web-fetch.ts +3 -1
  367. package/src/tools/skills/execute.ts +1 -1
  368. package/src/tools/terminal/safe-env.ts +1 -0
  369. package/src/tools/types.ts +0 -8
  370. package/src/util/browser.ts +15 -0
  371. package/src/util/errors.ts +0 -12
  372. package/src/util/platform.ts +4 -51
  373. package/src/workspace/git-service.ts +5 -2
  374. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  375. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  376. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  377. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  378. package/src/workspace/migrations/006-services-config.ts +49 -0
  379. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  380. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  381. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  382. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  383. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  384. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  385. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  386. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  387. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  388. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  389. package/src/workspace/migrations/017-seed-persona-dirs.ts +96 -0
  390. package/src/workspace/migrations/018-rekey-compound-credential-keys.ts +184 -0
  391. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +103 -0
  392. package/src/workspace/migrations/migrate-to-workspace-volume.ts +27 -5
  393. package/src/workspace/migrations/registry.ts +12 -0
  394. package/src/workspace/migrations/runner.ts +106 -2
  395. package/src/workspace/migrations/types.ts +4 -0
  396. package/src/workspace/provider-commit-message-generator.ts +12 -21
  397. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  398. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  399. package/src/__tests__/diagnostics-export.test.ts +0 -288
  400. package/src/__tests__/local-gateway-health.test.ts +0 -209
  401. package/src/__tests__/provider-fail-open-selection.test.ts +0 -271
  402. package/src/__tests__/provider-failover-actual-provider.test.ts +0 -66
  403. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  404. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  405. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  406. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  407. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  408. package/src/__tests__/swarm-recursion.test.ts +0 -197
  409. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  410. package/src/__tests__/swarm-tool.test.ts +0 -185
  411. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  412. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  413. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  414. package/src/commands/cc-command-registry.ts +0 -248
  415. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  416. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  417. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  418. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  419. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  420. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  421. package/src/config/schemas/swarm.ts +0 -82
  422. package/src/logfire.ts +0 -135
  423. package/src/memory/search/lexical.ts +0 -48
  424. package/src/providers/failover.ts +0 -186
  425. package/src/runtime/local-gateway-health.ts +0 -275
  426. package/src/security/secret-ingress.ts +0 -68
  427. package/src/swarm/backend-claude-code.ts +0 -225
  428. package/src/swarm/checkpoint.ts +0 -137
  429. package/src/swarm/graph-utils.ts +0 -53
  430. package/src/swarm/index.ts +0 -55
  431. package/src/swarm/limits.ts +0 -66
  432. package/src/swarm/orchestrator.ts +0 -424
  433. package/src/swarm/plan-validator.ts +0 -117
  434. package/src/swarm/router-planner.ts +0 -162
  435. package/src/swarm/router-prompts.ts +0 -39
  436. package/src/swarm/synthesizer.ts +0 -81
  437. package/src/swarm/types.ts +0 -72
  438. package/src/swarm/worker-backend.ts +0 -131
  439. package/src/swarm/worker-prompts.ts +0 -80
  440. package/src/swarm/worker-runner.ts +0 -170
  441. package/src/tools/claude-code/claude-code.ts +0 -610
  442. package/src/tools/swarm/delegate.ts +0 -205
@@ -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_"
@@ -1,6 +1,41 @@
1
1
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
2
  import { withCrashRecovery } from "./validate-migration-state.js";
3
3
 
4
+ /**
5
+ * Reverse v23: add the "guardian_" prefix back to verification-related
6
+ * call_mode and event_type values.
7
+ */
8
+ export function downRenameGuardianVerificationValues(
9
+ database: DrizzleDb,
10
+ ): void {
11
+ const raw = getSqliteFrom(database);
12
+
13
+ // Rename call_mode values back
14
+ raw.exec(
15
+ /*sql*/ `UPDATE call_sessions SET call_mode = 'guardian_verification' WHERE call_mode = 'verification'`,
16
+ );
17
+
18
+ // Rename event_type values back
19
+ raw.exec(
20
+ /*sql*/ `UPDATE call_events SET event_type = 'guardian_voice_verification_started' WHERE event_type = 'voice_verification_started'`,
21
+ );
22
+ raw.exec(
23
+ /*sql*/ `UPDATE call_events SET event_type = 'guardian_voice_verification_succeeded' WHERE event_type = 'voice_verification_succeeded'`,
24
+ );
25
+ raw.exec(
26
+ /*sql*/ `UPDATE call_events SET event_type = 'guardian_voice_verification_failed' WHERE event_type = 'voice_verification_failed'`,
27
+ );
28
+ raw.exec(
29
+ /*sql*/ `UPDATE call_events SET event_type = 'outbound_guardian_voice_verification_started' WHERE event_type = 'outbound_voice_verification_started'`,
30
+ );
31
+ raw.exec(
32
+ /*sql*/ `UPDATE call_events SET event_type = 'outbound_guardian_voice_verification_succeeded' WHERE event_type = 'outbound_voice_verification_succeeded'`,
33
+ );
34
+ raw.exec(
35
+ /*sql*/ `UPDATE call_events SET event_type = 'outbound_guardian_voice_verification_failed' WHERE event_type = 'outbound_voice_verification_failed'`,
36
+ );
37
+ }
38
+
4
39
  /**
5
40
  * One-shot migration: rename persisted `guardian_verification` and
6
41
  * `guardian_voice_verification_*` / `outbound_guardian_voice_verification_*`
@@ -1,6 +1,142 @@
1
1
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
2
  import { withCrashRecovery } from "./validate-migration-state.js";
3
3
 
4
+ /**
5
+ * Reverse v24: rename "phone" channel values back to "voice" across all
6
+ * tables with channel text columns.
7
+ */
8
+ export function downRenameVoiceToPhone(database: DrizzleDb): void {
9
+ const raw = getSqliteFrom(database);
10
+
11
+ // contact_channels.type
12
+ raw.exec(
13
+ /*sql*/ `UPDATE contact_channels SET type = 'voice' WHERE type = 'phone'`,
14
+ );
15
+
16
+ // conversations.origin_channel
17
+ raw.exec(
18
+ /*sql*/ `UPDATE conversations SET origin_channel = 'voice' WHERE origin_channel = 'phone'`,
19
+ );
20
+
21
+ // conversations.origin_interface
22
+ raw.exec(
23
+ /*sql*/ `UPDATE conversations SET origin_interface = 'voice' WHERE origin_interface = 'phone'`,
24
+ );
25
+
26
+ // messages.metadata — reverse the JSON string replacement
27
+ raw.exec(
28
+ /*sql*/ `UPDATE messages SET metadata = REPLACE(metadata, '"phone"', '"voice"') WHERE metadata LIKE '%"phone"%'`,
29
+ );
30
+
31
+ // assistant_ingress_invites.source_channel
32
+ raw.exec(
33
+ /*sql*/ `UPDATE assistant_ingress_invites SET source_channel = 'voice' WHERE source_channel = 'phone'`,
34
+ );
35
+
36
+ // assistant_inbox_thread_state.source_channel
37
+ raw.exec(
38
+ /*sql*/ `UPDATE assistant_inbox_thread_state SET source_channel = 'voice' WHERE source_channel = 'phone'`,
39
+ );
40
+
41
+ // guardian_action_requests.source_channel
42
+ raw.exec(
43
+ /*sql*/ `UPDATE guardian_action_requests SET source_channel = 'voice' WHERE source_channel = 'phone'`,
44
+ );
45
+
46
+ // guardian_action_requests.answered_by_channel
47
+ raw.exec(
48
+ /*sql*/ `UPDATE guardian_action_requests SET answered_by_channel = 'voice' WHERE answered_by_channel = 'phone'`,
49
+ );
50
+
51
+ // channel_verification_sessions.channel (the forward migration ran after v21 rename)
52
+ raw.exec(
53
+ /*sql*/ `UPDATE channel_verification_sessions SET channel = 'voice' WHERE channel = 'phone'`,
54
+ );
55
+
56
+ // channel_guardian_approval_requests.channel
57
+ raw.exec(
58
+ /*sql*/ `UPDATE channel_guardian_approval_requests SET channel = 'voice' WHERE channel = 'phone'`,
59
+ );
60
+
61
+ // channel_guardian_rate_limits.channel
62
+ // Dedup: remove phone rows that would collide with existing voice rows
63
+ raw.exec(/*sql*/ `DELETE FROM channel_guardian_rate_limits WHERE channel = 'phone' AND EXISTS (
64
+ SELECT 1 FROM channel_guardian_rate_limits AS t2
65
+ WHERE t2.channel = 'voice'
66
+ AND t2.actor_external_user_id = channel_guardian_rate_limits.actor_external_user_id
67
+ AND t2.actor_chat_id = channel_guardian_rate_limits.actor_chat_id
68
+ )`);
69
+ raw.exec(
70
+ /*sql*/ `UPDATE channel_guardian_rate_limits SET channel = 'voice' WHERE channel = 'phone'`,
71
+ );
72
+
73
+ // notification_events.source_channel
74
+ raw.exec(
75
+ /*sql*/ `UPDATE notification_events SET source_channel = 'voice' WHERE source_channel = 'phone'`,
76
+ );
77
+
78
+ // notification_deliveries.channel
79
+ raw.exec(
80
+ /*sql*/ `UPDATE notification_deliveries SET channel = 'voice' WHERE channel = 'phone'`,
81
+ );
82
+
83
+ // external_conversation_bindings.source_channel
84
+ raw.exec(
85
+ /*sql*/ `UPDATE external_conversation_bindings SET source_channel = 'voice' WHERE source_channel = 'phone'`,
86
+ );
87
+
88
+ // channel_inbound_events.source_channel
89
+ raw.exec(
90
+ /*sql*/ `UPDATE channel_inbound_events SET source_channel = 'voice' WHERE source_channel = 'phone'`,
91
+ );
92
+
93
+ // conversation_attention_events.source_channel
94
+ raw.exec(
95
+ /*sql*/ `UPDATE conversation_attention_events SET source_channel = 'voice' WHERE source_channel = 'phone'`,
96
+ );
97
+
98
+ // conversation_assistant_attention_state.last_seen_source_channel
99
+ raw.exec(
100
+ /*sql*/ `UPDATE conversation_assistant_attention_state SET last_seen_source_channel = 'voice' WHERE last_seen_source_channel = 'phone'`,
101
+ );
102
+
103
+ // canonical_guardian_requests.source_channel
104
+ raw.exec(
105
+ /*sql*/ `UPDATE canonical_guardian_requests SET source_channel = 'voice' WHERE source_channel = 'phone'`,
106
+ );
107
+
108
+ // canonical_guardian_deliveries.destination_channel
109
+ raw.exec(
110
+ /*sql*/ `UPDATE canonical_guardian_deliveries SET destination_channel = 'voice' WHERE destination_channel = 'phone'`,
111
+ );
112
+
113
+ // guardian_action_deliveries.destination_channel
114
+ raw.exec(
115
+ /*sql*/ `UPDATE guardian_action_deliveries SET destination_channel = 'voice' WHERE destination_channel = 'phone'`,
116
+ );
117
+
118
+ // scoped_approval_grants: request_channel, decision_channel, execution_channel
119
+ raw.exec(
120
+ /*sql*/ `UPDATE scoped_approval_grants SET request_channel = 'voice' WHERE request_channel = 'phone'`,
121
+ );
122
+ raw.exec(
123
+ /*sql*/ `UPDATE scoped_approval_grants SET decision_channel = 'voice' WHERE decision_channel = 'phone'`,
124
+ );
125
+ raw.exec(
126
+ /*sql*/ `UPDATE scoped_approval_grants SET execution_channel = 'voice' WHERE execution_channel = 'phone'`,
127
+ );
128
+
129
+ // sequences.channel
130
+ raw.exec(
131
+ /*sql*/ `UPDATE sequences SET channel = 'voice' WHERE channel = 'phone'`,
132
+ );
133
+
134
+ // followups.channel
135
+ raw.exec(
136
+ /*sql*/ `UPDATE followups SET channel = 'voice' WHERE channel = 'phone'`,
137
+ );
138
+ }
139
+
4
140
  /**
5
141
  * One-shot migration: rename stored "voice" channel values to "phone" across
6
142
  * all tables that persist channel identifiers as text.
@@ -17,3 +17,35 @@ export function migrateDropAccountsTable(database: DrizzleDb): void {
17
17
  raw.exec(/*sql*/ `DROP TABLE IF EXISTS accounts`);
18
18
  });
19
19
  }
20
+
21
+ /**
22
+ * Reverse: recreate the accounts table with its original schema.
23
+ *
24
+ * Data is permanently lost — the table was dropped. This only restores the
25
+ * empty table structure so that earlier migrations referencing it can operate.
26
+ */
27
+ export function migrateDropAccountsTableDown(database: DrizzleDb): void {
28
+ const raw = getSqliteFrom(database);
29
+
30
+ raw.exec(/*sql*/ `
31
+ CREATE TABLE IF NOT EXISTS accounts (
32
+ id TEXT PRIMARY KEY,
33
+ service TEXT NOT NULL,
34
+ username TEXT,
35
+ email TEXT,
36
+ display_name TEXT,
37
+ status TEXT NOT NULL DEFAULT 'active',
38
+ credential_ref TEXT,
39
+ metadata_json TEXT,
40
+ created_at INTEGER NOT NULL,
41
+ updated_at INTEGER NOT NULL
42
+ )
43
+ `);
44
+
45
+ raw.exec(
46
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_accounts_service ON accounts(service)`,
47
+ );
48
+ raw.exec(
49
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_accounts_status ON accounts(status)`,
50
+ );
51
+ }
@@ -1,4 +1,5 @@
1
- import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
2
3
  import { withCrashRecovery } from "./validate-migration-state.js";
3
4
 
4
5
  /**
@@ -123,3 +124,15 @@ export function migrateRemindersToSchedules(database: DrizzleDb): void {
123
124
  }
124
125
  });
125
126
  }
127
+
128
+ /**
129
+ * Reverse: no-op.
130
+ *
131
+ * Cannot reliably identify which cron_jobs rows were migrated from reminders
132
+ * versus created natively as schedules. Rows share the same table and there
133
+ * is no origin marker. Deleting the wrong rows would destroy user-created
134
+ * schedules.
135
+ */
136
+ export function migrateRemindersToSchedulesDown(_database: DrizzleDb): void {
137
+ // No-op — see comment above.
138
+ }
@@ -1,4 +1,5 @@
1
- import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
2
3
  import { withCrashRecovery } from "./validate-migration-state.js";
3
4
 
4
5
  /**
@@ -12,3 +13,36 @@ export function migrateDropRemindersTable(database: DrizzleDb): void {
12
13
  raw.run("DROP TABLE IF EXISTS reminders");
13
14
  });
14
15
  }
16
+
17
+ /**
18
+ * Reverse: recreate the reminders table with its original schema.
19
+ *
20
+ * Data is permanently lost — the table was dropped. This only restores the
21
+ * empty table structure so that earlier migrations referencing it can operate.
22
+ * Includes the routing_intent and routing_hints_json columns added by
23
+ * migration 118.
24
+ */
25
+ export function migrateDropRemindersTableDown(database: DrizzleDb): void {
26
+ const raw = getSqliteFrom(database);
27
+
28
+ raw.exec(/*sql*/ `
29
+ CREATE TABLE IF NOT EXISTS reminders (
30
+ id TEXT PRIMARY KEY,
31
+ label TEXT NOT NULL,
32
+ message TEXT NOT NULL,
33
+ fire_at INTEGER NOT NULL,
34
+ mode TEXT NOT NULL,
35
+ status TEXT NOT NULL,
36
+ fired_at INTEGER,
37
+ conversation_id TEXT,
38
+ routing_intent TEXT NOT NULL DEFAULT 'all_channels',
39
+ routing_hints_json TEXT NOT NULL DEFAULT '{}',
40
+ created_at INTEGER NOT NULL,
41
+ updated_at INTEGER NOT NULL
42
+ )
43
+ `);
44
+
45
+ raw.exec(
46
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_reminders_status_fire_at ON reminders(status, fire_at)`,
47
+ );
48
+ }
@@ -1,4 +1,5 @@
1
- import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
2
3
  import { withCrashRecovery } from "./validate-migration-state.js";
3
4
 
4
5
  /**
@@ -96,3 +97,70 @@ export function migrateOAuthAppsClientSecretPath(database: DrizzleDb): void {
96
97
  },
97
98
  );
98
99
  }
100
+
101
+ /**
102
+ * Reverse: drop the client_secret_credential_path column from oauth_apps.
103
+ *
104
+ * Rebuilds the table without the column (SQLite doesn't support DROP COLUMN
105
+ * on older versions). Idempotent — skips if the column doesn't exist.
106
+ */
107
+ export function migrateOAuthAppsClientSecretPathDown(
108
+ database: DrizzleDb,
109
+ ): void {
110
+ const raw = getSqliteFrom(database);
111
+
112
+ // Guard: table must exist
113
+ const tableExists = raw
114
+ .query(
115
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'oauth_apps'`,
116
+ )
117
+ .get();
118
+ if (!tableExists) return;
119
+
120
+ // Guard: if the column doesn't exist, nothing to do
121
+ const colInfo = raw
122
+ .query(
123
+ `SELECT 1 FROM pragma_table_info('oauth_apps') WHERE name = 'client_secret_credential_path'`,
124
+ )
125
+ .get();
126
+ if (!colInfo) return;
127
+
128
+ raw.exec("PRAGMA foreign_keys = OFF");
129
+ try {
130
+ raw.exec("BEGIN");
131
+
132
+ raw.exec(/*sql*/ `
133
+ CREATE TABLE oauth_apps_rollback (
134
+ id TEXT PRIMARY KEY,
135
+ provider_key TEXT NOT NULL REFERENCES oauth_providers(provider_key),
136
+ client_id TEXT NOT NULL,
137
+ created_at INTEGER NOT NULL,
138
+ updated_at INTEGER NOT NULL
139
+ )
140
+ `);
141
+
142
+ raw.exec(/*sql*/ `
143
+ INSERT INTO oauth_apps_rollback
144
+ SELECT id, provider_key, client_id, created_at, updated_at
145
+ FROM oauth_apps
146
+ `);
147
+
148
+ raw.exec(/*sql*/ `DROP TABLE oauth_apps`);
149
+ raw.exec(/*sql*/ `ALTER TABLE oauth_apps_rollback RENAME TO oauth_apps`);
150
+
151
+ raw.exec(
152
+ /*sql*/ `CREATE UNIQUE INDEX IF NOT EXISTS idx_oauth_apps_provider_client ON oauth_apps(provider_key, client_id)`,
153
+ );
154
+
155
+ raw.exec("COMMIT");
156
+ } catch (e) {
157
+ try {
158
+ raw.exec("ROLLBACK");
159
+ } catch {
160
+ /* no active transaction */
161
+ }
162
+ throw e;
163
+ } finally {
164
+ raw.exec("PRAGMA foreign_keys = ON");
165
+ }
166
+ }