@vellumai/assistant 0.3.5 → 0.3.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 (487) hide show
  1. package/README.md +51 -0
  2. package/eslint.config.mjs +31 -0
  3. package/package.json +1 -1
  4. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  5. package/scripts/ipc/generate-swift.ts +18 -2
  6. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
  7. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  8. package/src/__tests__/browser-manager.test.ts +1 -0
  9. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  10. package/src/__tests__/call-orchestrator.test.ts +752 -271
  11. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  12. package/src/__tests__/call-recovery.test.ts +3 -0
  13. package/src/__tests__/call-routes-http.test.ts +5 -0
  14. package/src/__tests__/call-store.test.ts +3 -0
  15. package/src/__tests__/channel-approval-routes.test.ts +1260 -85
  16. package/src/__tests__/channel-approval.test.ts +37 -0
  17. package/src/__tests__/channel-approvals.test.ts +4 -65
  18. package/src/__tests__/channel-guardian.test.ts +556 -0
  19. package/src/__tests__/channel-readiness-service.test.ts +74 -7
  20. package/src/__tests__/checker.test.ts +14 -7
  21. package/src/__tests__/clarification-resolver.test.ts +44 -24
  22. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  23. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  24. package/src/__tests__/config-schema.test.ts +12 -7
  25. package/src/__tests__/context-window-manager.test.ts +30 -2
  26. package/src/__tests__/contradiction-checker.test.ts +20 -5
  27. package/src/__tests__/credential-security-invariants.test.ts +6 -2
  28. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  29. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  30. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  31. package/src/__tests__/guardian-action-store.test.ts +123 -0
  32. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  33. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  34. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  35. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  36. package/src/__tests__/handlers-twilio-config.test.ts +126 -0
  37. package/src/__tests__/intent-routing.test.ts +2 -0
  38. package/src/__tests__/ipc-snapshot.test.ts +228 -1
  39. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  40. package/src/__tests__/model-intents.test.ts +96 -0
  41. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  42. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  43. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  44. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  45. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  46. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  47. package/src/__tests__/qdrant-manager.test.ts +27 -20
  48. package/src/__tests__/relay-server.test.ts +779 -40
  49. package/src/__tests__/run-orchestrator-assistant-events.test.ts +2 -0
  50. package/src/__tests__/run-orchestrator.test.ts +20 -4
  51. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  52. package/src/__tests__/runtime-runs.test.ts +16 -0
  53. package/src/__tests__/schedule-store.test.ts +18 -4
  54. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  55. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  56. package/src/__tests__/session-agent-loop.test.ts +857 -0
  57. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  58. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  59. package/src/__tests__/session-profile-injection.test.ts +6 -0
  60. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  61. package/src/__tests__/session-queue.test.ts +6 -0
  62. package/src/__tests__/session-runtime-assembly.test.ts +237 -13
  63. package/src/__tests__/session-slash-known.test.ts +6 -0
  64. package/src/__tests__/session-slash-queue.test.ts +6 -0
  65. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  66. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  67. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  68. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  69. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  70. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  71. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  72. package/src/__tests__/skills.test.ts +2 -0
  73. package/src/__tests__/sms-messaging-provider.test.ts +2 -1
  74. package/src/__tests__/starter-task-flow.test.ts +2 -0
  75. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  76. package/src/__tests__/system-prompt.test.ts +2 -0
  77. package/src/__tests__/task-management-tools.test.ts +2 -2
  78. package/src/__tests__/task-runner.test.ts +14 -4
  79. package/src/__tests__/terminal-tools.test.ts +25 -19
  80. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  81. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  82. package/src/__tests__/tool-executor.test.ts +23 -24
  83. package/src/__tests__/trust-store.test.ts +3 -3
  84. package/src/__tests__/twilio-rest.test.ts +29 -0
  85. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  86. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  87. package/src/__tests__/twilio-routes.test.ts +141 -21
  88. package/src/__tests__/user-reference.test.ts +2 -0
  89. package/src/__tests__/voice-quality.test.ts +222 -0
  90. package/src/__tests__/web-search.test.ts +45 -29
  91. package/src/agent/loop.ts +1 -1
  92. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  93. package/src/amazon/client.ts +1418 -0
  94. package/src/amazon/request-extractor.ts +135 -0
  95. package/src/amazon/session.ts +109 -0
  96. package/src/autonomy/autonomy-store.ts +5 -5
  97. package/src/browser-extension-relay/client.ts +124 -0
  98. package/src/browser-extension-relay/protocol.ts +63 -0
  99. package/src/browser-extension-relay/server.ts +177 -0
  100. package/src/bundler/app-bundler.ts +3 -3
  101. package/src/bundler/bundle-signer.ts +1 -1
  102. package/src/bundler/signature-verifier.ts +1 -1
  103. package/src/calls/call-conversation-messages.ts +33 -0
  104. package/src/calls/call-domain.ts +106 -5
  105. package/src/calls/call-orchestrator.ts +252 -54
  106. package/src/calls/call-pointer-messages.ts +53 -0
  107. package/src/calls/call-recovery.ts +3 -8
  108. package/src/calls/call-store.ts +69 -87
  109. package/src/calls/elevenlabs-config.ts +3 -2
  110. package/src/calls/guardian-action-sweep.ts +105 -0
  111. package/src/calls/guardian-dispatch.ts +203 -0
  112. package/src/calls/guardian-question-copy.ts +133 -0
  113. package/src/calls/relay-server.ts +466 -8
  114. package/src/calls/speaker-identification.ts +1 -1
  115. package/src/calls/twilio-config.ts +7 -5
  116. package/src/calls/twilio-provider.ts +6 -4
  117. package/src/calls/twilio-rest.ts +40 -15
  118. package/src/calls/twilio-routes.ts +60 -45
  119. package/src/calls/types.ts +3 -1
  120. package/src/channels/types.ts +25 -0
  121. package/src/cli/amazon.ts +815 -0
  122. package/src/cli/config-commands.ts +2 -2
  123. package/src/cli/core-commands.ts +4 -3
  124. package/src/cli/influencer.ts +244 -0
  125. package/src/cli/map.ts +89 -6
  126. package/src/cli.ts +1 -1
  127. package/src/config/agent-schema.ts +171 -0
  128. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  129. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  130. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  131. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  132. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  133. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  134. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  135. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  136. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  137. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  138. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  139. package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
  140. package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
  141. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  142. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  143. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  144. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  145. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  146. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  147. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  148. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +7 -9
  149. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  150. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
  151. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
  152. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  153. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
  154. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
  155. package/src/config/bundled-skills/messaging/SKILL.md +12 -2
  156. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  157. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  158. package/src/config/bundled-skills/phone-calls/SKILL.md +86 -21
  159. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  160. package/src/config/bundled-tool-registry.ts +310 -0
  161. package/src/config/calls-schema.ts +181 -0
  162. package/src/config/core-schema.ts +309 -0
  163. package/src/config/defaults.ts +27 -3
  164. package/src/config/env-registry.ts +169 -0
  165. package/src/config/env.ts +175 -0
  166. package/src/config/loader.ts +6 -6
  167. package/src/config/memory-schema.ts +528 -0
  168. package/src/config/sandbox-schema.ts +55 -0
  169. package/src/config/schema.ts +157 -1138
  170. package/src/config/skill-state.ts +1 -1
  171. package/src/config/skills-schema.ts +32 -0
  172. package/src/config/skills.ts +35 -24
  173. package/src/config/system-prompt.ts +107 -56
  174. package/src/config/templates/SOUL.md +1 -1
  175. package/src/config/types.ts +1 -0
  176. package/src/config/user-reference.ts +4 -9
  177. package/src/config/vellum-skills/catalog.json +0 -7
  178. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  179. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
  180. package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
  181. package/src/context/window-manager.ts +27 -7
  182. package/src/daemon/approval-generators.ts +186 -0
  183. package/src/daemon/approved-devices-store.ts +140 -0
  184. package/src/daemon/assistant-attachments.ts +1 -1
  185. package/src/daemon/classifier.ts +35 -32
  186. package/src/daemon/config-watcher.ts +1 -1
  187. package/src/daemon/daemon-control.ts +254 -0
  188. package/src/daemon/handlers/apps.ts +2 -3
  189. package/src/daemon/handlers/config-channels.ts +158 -0
  190. package/src/daemon/handlers/config-inbox.ts +540 -0
  191. package/src/daemon/handlers/config-ingress.ts +231 -0
  192. package/src/daemon/handlers/config-integrations.ts +258 -0
  193. package/src/daemon/handlers/config-model.ts +143 -0
  194. package/src/daemon/handlers/config-parental.ts +163 -0
  195. package/src/daemon/handlers/config-scheduling.ts +172 -0
  196. package/src/daemon/handlers/config-slack.ts +92 -0
  197. package/src/daemon/handlers/config-telegram.ts +301 -0
  198. package/src/daemon/handlers/config-tools.ts +177 -0
  199. package/src/daemon/handlers/config-trust.ts +104 -0
  200. package/src/daemon/handlers/config-twilio.ts +1080 -0
  201. package/src/daemon/handlers/config.ts +53 -2463
  202. package/src/daemon/handlers/diagnostics.ts +1 -1
  203. package/src/daemon/handlers/dictation.ts +4 -6
  204. package/src/daemon/handlers/documents.ts +18 -32
  205. package/src/daemon/handlers/index.ts +9 -0
  206. package/src/daemon/handlers/misc.ts +3 -5
  207. package/src/daemon/handlers/pairing.ts +98 -0
  208. package/src/daemon/handlers/sessions.ts +74 -5
  209. package/src/daemon/handlers/shared.ts +3 -1
  210. package/src/daemon/handlers/skills.ts +1 -1
  211. package/src/daemon/handlers/twitter-auth.ts +2 -0
  212. package/src/daemon/handlers/work-items.ts +2 -2
  213. package/src/daemon/handlers/workspace-files.ts +4 -3
  214. package/src/daemon/install-cli-launchers.ts +113 -0
  215. package/src/daemon/ipc-contract/apps.ts +356 -0
  216. package/src/daemon/ipc-contract/browser.ts +74 -0
  217. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  218. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  219. package/src/daemon/ipc-contract/documents.ts +74 -0
  220. package/src/daemon/ipc-contract/inbox.ts +209 -0
  221. package/src/daemon/ipc-contract/integrations.ts +284 -0
  222. package/src/daemon/ipc-contract/memory.ts +48 -0
  223. package/src/daemon/ipc-contract/messages.ts +211 -0
  224. package/src/daemon/ipc-contract/pairing.ts +45 -0
  225. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  226. package/src/daemon/ipc-contract/schedules.ts +97 -0
  227. package/src/daemon/ipc-contract/sessions.ts +321 -0
  228. package/src/daemon/ipc-contract/shared.ts +42 -0
  229. package/src/daemon/ipc-contract/skills.ts +120 -0
  230. package/src/daemon/ipc-contract/subagents.ts +58 -0
  231. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  232. package/src/daemon/ipc-contract/trust.ts +60 -0
  233. package/src/daemon/ipc-contract/work-items.ts +225 -0
  234. package/src/daemon/ipc-contract/workspace.ts +113 -0
  235. package/src/daemon/ipc-contract-inventory.json +62 -0
  236. package/src/daemon/ipc-contract-inventory.ts +55 -29
  237. package/src/daemon/ipc-contract.ts +227 -2527
  238. package/src/daemon/ipc-protocol.ts +1 -1
  239. package/src/daemon/ipc-validate.ts +7 -0
  240. package/src/daemon/lifecycle.ts +97 -379
  241. package/src/daemon/pairing-store.ts +177 -0
  242. package/src/daemon/providers-setup.ts +43 -0
  243. package/src/daemon/ride-shotgun-handler.ts +67 -2
  244. package/src/daemon/server.ts +60 -44
  245. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  246. package/src/daemon/session-agent-loop.ts +113 -275
  247. package/src/daemon/session-dynamic-profile.ts +1 -1
  248. package/src/daemon/session-history.ts +1 -1
  249. package/src/daemon/session-media-retry.ts +1 -1
  250. package/src/daemon/session-messaging.ts +37 -2
  251. package/src/daemon/session-notifiers.ts +5 -25
  252. package/src/daemon/session-process.ts +99 -59
  253. package/src/daemon/session-queue-manager.ts +98 -4
  254. package/src/daemon/session-runtime-assembly.ts +149 -15
  255. package/src/daemon/session-surfaces.ts +26 -4
  256. package/src/daemon/session-tool-setup.ts +28 -30
  257. package/src/daemon/session-workspace.ts +1 -1
  258. package/src/daemon/session.ts +24 -1
  259. package/src/daemon/shutdown-handlers.ts +122 -0
  260. package/src/daemon/trace-emitter.ts +1 -1
  261. package/src/daemon/watch-handler.ts +36 -33
  262. package/src/doordash/cart-queries.ts +787 -0
  263. package/src/doordash/client.ts +144 -127
  264. package/src/doordash/order-queries.ts +85 -0
  265. package/src/doordash/queries.ts +10 -1308
  266. package/src/doordash/search-queries.ts +203 -0
  267. package/src/doordash/session.ts +3 -2
  268. package/src/doordash/store-queries.ts +246 -0
  269. package/src/doordash/types.ts +367 -0
  270. package/src/email/providers/agentmail.ts +2 -1
  271. package/src/email/providers/index.ts +3 -2
  272. package/src/email/service.ts +3 -2
  273. package/src/errors.ts +43 -0
  274. package/src/home-base/prebuilt/seed.ts +1 -1
  275. package/src/hooks/cli.ts +6 -5
  276. package/src/hooks/config.ts +6 -8
  277. package/src/hooks/discovery.ts +6 -5
  278. package/src/hooks/manager.ts +4 -3
  279. package/src/hooks/runner.ts +2 -2
  280. package/src/hooks/templates.ts +5 -5
  281. package/src/inbound/public-ingress-urls.ts +3 -1
  282. package/src/index.ts +4 -2
  283. package/src/influencer/client.ts +1104 -0
  284. package/src/instrument.ts +4 -3
  285. package/src/logfire.ts +4 -3
  286. package/src/memory/admin.ts +25 -35
  287. package/src/memory/attachments-store.ts +4 -7
  288. package/src/memory/channel-delivery-store.ts +30 -1
  289. package/src/memory/channel-guardian-store.ts +200 -1
  290. package/src/memory/clarification-resolver.ts +37 -33
  291. package/src/memory/conflict-store.ts +67 -61
  292. package/src/memory/contradiction-checker.ts +141 -117
  293. package/src/memory/conversation-store.ts +335 -51
  294. package/src/memory/db-connection.ts +27 -4
  295. package/src/memory/db-init.ts +121 -4
  296. package/src/memory/db.ts +14 -1
  297. package/src/memory/embedding-backend.ts +27 -5
  298. package/src/memory/embedding-ollama.ts +2 -1
  299. package/src/memory/entity-extractor.ts +38 -35
  300. package/src/memory/guardian-action-store.ts +430 -0
  301. package/src/memory/inbox-escalation-projection.ts +59 -0
  302. package/src/memory/inbox-thread-store.ts +218 -0
  303. package/src/memory/ingress-invite-store.ts +338 -0
  304. package/src/memory/ingress-member-store.ts +350 -0
  305. package/src/memory/items-extractor.ts +91 -97
  306. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  307. package/src/memory/job-handlers/media-processing.ts +11 -42
  308. package/src/memory/job-handlers/summarization.ts +32 -26
  309. package/src/memory/job-utils.ts +3 -10
  310. package/src/memory/jobs-store.ts +6 -9
  311. package/src/memory/jobs-worker.ts +51 -36
  312. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  313. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  314. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  315. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  316. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  317. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  318. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  319. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  320. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  321. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  322. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  323. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  324. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  325. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  326. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  327. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  328. package/src/memory/migrations/017-memory-items-indexes.ts +12 -0
  329. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  330. package/src/memory/migrations/index.ts +24 -0
  331. package/src/memory/migrations/registry.ts +79 -0
  332. package/src/memory/migrations/validate-migration-state.ts +69 -0
  333. package/src/memory/qdrant-manager.ts +49 -8
  334. package/src/memory/query-builder.ts +1 -1
  335. package/src/memory/raw-query.ts +119 -0
  336. package/src/memory/recall-cache.ts +4 -1
  337. package/src/memory/retriever.ts +163 -47
  338. package/src/memory/schema-migration.ts +25 -984
  339. package/src/memory/schema.ts +130 -7
  340. package/src/memory/search/entity.ts +10 -19
  341. package/src/memory/search/lexical.ts +81 -52
  342. package/src/memory/search/ranking.ts +21 -22
  343. package/src/memory/search/semantic.ts +157 -19
  344. package/src/memory/shared-app-links-store.ts +4 -5
  345. package/src/memory/validation.ts +19 -0
  346. package/src/messaging/draft-store.ts +5 -6
  347. package/src/messaging/providers/sms/adapter.ts +3 -6
  348. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  349. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  350. package/src/messaging/providers/whatsapp/client.ts +67 -0
  351. package/src/messaging/style-analyzer.ts +5 -4
  352. package/src/messaging/thread-summarizer.ts +61 -69
  353. package/src/messaging/triage-engine.ts +62 -71
  354. package/src/migrations/config-merge.ts +53 -0
  355. package/src/migrations/data-layout.ts +68 -0
  356. package/src/migrations/data-merge.ts +33 -0
  357. package/src/migrations/hooks-merge.ts +90 -0
  358. package/src/migrations/index.ts +6 -0
  359. package/src/migrations/log.ts +23 -0
  360. package/src/migrations/skills-merge.ts +33 -0
  361. package/src/migrations/workspace-layout.ts +79 -0
  362. package/src/permissions/checker.ts +126 -11
  363. package/src/permissions/prompter.ts +14 -0
  364. package/src/permissions/shell-identity.ts +31 -1
  365. package/src/permissions/trust-store.ts +21 -1
  366. package/src/providers/anthropic/client.ts +4 -4
  367. package/src/providers/failover.ts +2 -2
  368. package/src/providers/model-intents.ts +70 -0
  369. package/src/providers/ollama/client.ts +2 -1
  370. package/src/providers/provider-send-message.ts +176 -0
  371. package/src/providers/registry.ts +71 -30
  372. package/src/providers/retry.ts +35 -1
  373. package/src/providers/types.ts +12 -1
  374. package/src/runtime/approval-conversation-turn.ts +97 -0
  375. package/src/runtime/approval-message-composer.ts +115 -5
  376. package/src/runtime/assistant-event-hub.ts +3 -1
  377. package/src/runtime/channel-approval-parser.ts +36 -2
  378. package/src/runtime/channel-approvals.ts +0 -21
  379. package/src/runtime/channel-guardian-service.ts +48 -7
  380. package/src/runtime/channel-readiness-service.ts +160 -34
  381. package/src/runtime/channel-readiness-types.ts +10 -4
  382. package/src/runtime/channel-retry-sweep.ts +184 -0
  383. package/src/runtime/guardian-context-resolver.ts +108 -0
  384. package/src/runtime/http-server.ts +289 -745
  385. package/src/runtime/http-types.ts +56 -3
  386. package/src/runtime/middleware/auth.ts +116 -0
  387. package/src/runtime/middleware/error-handler.ts +33 -0
  388. package/src/runtime/middleware/twilio-validation.ts +127 -0
  389. package/src/runtime/routes/app-routes.ts +1 -1
  390. package/src/runtime/routes/call-routes.ts +49 -6
  391. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  392. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  393. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  394. package/src/runtime/routes/channel-route-shared.ts +144 -0
  395. package/src/runtime/routes/channel-routes.ts +32 -1634
  396. package/src/runtime/routes/conversation-routes.ts +50 -7
  397. package/src/runtime/routes/events-routes.ts +2 -2
  398. package/src/runtime/routes/identity-routes.ts +126 -0
  399. package/src/runtime/routes/pairing-routes.ts +144 -0
  400. package/src/runtime/routes/run-routes.ts +15 -1
  401. package/src/runtime/run-orchestrator.ts +52 -34
  402. package/src/schedule/schedule-store.ts +36 -32
  403. package/src/schedule/scheduler.ts +3 -3
  404. package/src/security/encrypted-store.ts +5 -7
  405. package/src/security/oauth2.ts +45 -15
  406. package/src/security/parental-control-store.ts +183 -0
  407. package/src/security/secret-allowlist.ts +4 -3
  408. package/src/security/secret-scanner.ts +5 -5
  409. package/src/security/secure-keys.ts +1 -1
  410. package/src/security/token-manager.ts +3 -2
  411. package/src/services/vercel-deploy.ts +6 -2
  412. package/src/skills/tool-manifest.ts +3 -3
  413. package/src/skills/vellum-catalog-remote.ts +75 -16
  414. package/src/slack/slack-webhook.ts +2 -1
  415. package/src/swarm/orchestrator.ts +92 -1
  416. package/src/swarm/router-planner.ts +6 -9
  417. package/src/swarm/worker-prompts.ts +9 -12
  418. package/src/tasks/task-compiler.ts +19 -28
  419. package/src/tasks/task-runner.ts +1 -1
  420. package/src/tools/assets/search.ts +15 -14
  421. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  422. package/src/tools/browser/auto-navigate.ts +1 -0
  423. package/src/tools/browser/browser-execution.ts +13 -1
  424. package/src/tools/browser/browser-manager.ts +119 -4
  425. package/src/tools/browser/network-recorder.ts +5 -0
  426. package/src/tools/credentials/broker.ts +11 -2
  427. package/src/tools/credentials/metadata-store.ts +18 -14
  428. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  429. package/src/tools/credentials/vault.ts +49 -23
  430. package/src/tools/executor.ts +80 -18
  431. package/src/tools/host-terminal/cli-discover.ts +1 -1
  432. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  433. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  434. package/src/tools/network/script-proxy/server.ts +1 -1
  435. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  436. package/src/tools/network/web-fetch.ts +18 -2
  437. package/src/tools/network/web-search.ts +7 -3
  438. package/src/tools/reminder/reminder-store.ts +14 -15
  439. package/src/tools/schedule/create.ts +1 -0
  440. package/src/tools/schedule/list.ts +2 -1
  441. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  442. package/src/tools/skills/skill-script-runner.ts +24 -9
  443. package/src/tools/skills/skill-tool-factory.ts +1 -0
  444. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  445. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  446. package/src/tools/terminal/parser.ts +50 -0
  447. package/src/tools/watcher/delete.ts +6 -0
  448. package/src/tools/weather/service.ts +1 -1
  449. package/src/twitter/client.ts +190 -24
  450. package/src/twitter/session.ts +4 -3
  451. package/src/util/clipboard.ts +1 -1
  452. package/src/util/errors.ts +65 -8
  453. package/src/util/fs.ts +40 -0
  454. package/src/util/json.ts +10 -0
  455. package/src/util/log-redact.ts +189 -0
  456. package/src/util/logger.ts +25 -18
  457. package/src/util/object.ts +3 -0
  458. package/src/util/platform.ts +72 -365
  459. package/src/util/pricing.ts +1 -1
  460. package/src/util/promise-guard.ts +1 -1
  461. package/src/util/retry.ts +19 -0
  462. package/src/util/row-mapper.ts +79 -0
  463. package/src/util/silently.ts +21 -0
  464. package/src/watcher/engine.ts +5 -1
  465. package/src/watcher/provider-types.ts +20 -0
  466. package/src/watcher/providers/github.ts +156 -0
  467. package/src/watcher/providers/gmail.ts +1 -0
  468. package/src/watcher/providers/google-calendar.ts +1 -0
  469. package/src/watcher/providers/linear.ts +460 -0
  470. package/src/watcher/providers/slack.ts +1 -0
  471. package/src/work-items/work-item-runner.ts +1 -1
  472. package/src/workspace/git-service.ts +1 -1
  473. package/src/workspace/provider-commit-message-generator.ts +51 -22
  474. package/src/__tests__/call-bridge.test.ts +0 -517
  475. package/src/__tests__/session-process-bridge.test.ts +0 -244
  476. package/src/calls/call-bridge.ts +0 -168
  477. package/src/config/bundled-skills/media-processing/services/capability-registry.ts +0 -137
  478. package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
  479. package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
  480. package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
  481. package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
  482. package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
  483. package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
  484. package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
  485. package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
  486. package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
  487. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -0,0 +1,43 @@
1
+ import { getSqliteFrom, type DrizzleDb } from '../db-connection.js';
2
+
3
+ /**
4
+ * Migrate existing tool_invocations table to add FK constraint with ON DELETE CASCADE.
5
+ * SQLite doesn't support ALTER TABLE ADD CONSTRAINT, so we rebuild the table.
6
+ * This is idempotent: it checks whether the FK already exists before migrating.
7
+ */
8
+ export function migrateToolInvocationsFk(database: DrizzleDb): void {
9
+ const raw = getSqliteFrom(database);
10
+ const row = raw.query(`SELECT sql FROM sqlite_master WHERE type='table' AND name='tool_invocations'`).get() as { sql: string } | null;
11
+ if (!row) return; // table doesn't exist yet (will be created above)
12
+
13
+ // If the DDL already contains REFERENCES, the FK is in place
14
+ if (row.sql.includes('REFERENCES')) return;
15
+
16
+ raw.exec('PRAGMA foreign_keys = OFF');
17
+ try {
18
+ raw.exec(/*sql*/ `
19
+ BEGIN;
20
+ CREATE TABLE tool_invocations_new (
21
+ id TEXT PRIMARY KEY,
22
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
23
+ tool_name TEXT NOT NULL,
24
+ input TEXT NOT NULL,
25
+ result TEXT NOT NULL,
26
+ decision TEXT NOT NULL,
27
+ risk_level TEXT NOT NULL,
28
+ duration_ms INTEGER NOT NULL,
29
+ created_at INTEGER NOT NULL
30
+ );
31
+ INSERT INTO tool_invocations_new SELECT t.* FROM tool_invocations t
32
+ WHERE EXISTS (SELECT 1 FROM conversations c WHERE c.id = t.conversation_id);
33
+ DROP TABLE tool_invocations;
34
+ ALTER TABLE tool_invocations_new RENAME TO tool_invocations;
35
+ COMMIT;
36
+ `);
37
+ } catch (e) {
38
+ try { raw.exec('ROLLBACK'); } catch { /* no active transaction */ }
39
+ throw e;
40
+ } finally {
41
+ raw.exec('PRAGMA foreign_keys = ON');
42
+ }
43
+ }
@@ -0,0 +1,24 @@
1
+ import { getSqliteFrom, type DrizzleDb } from '../db-connection.js';
2
+
3
+ /**
4
+ * Backfill FTS rows for existing memory_segments records when upgrading from a
5
+ * version that may not have had trigger-managed FTS.
6
+ */
7
+ export function migrateMemoryFtsBackfill(database: DrizzleDb): void {
8
+ const raw = getSqliteFrom(database);
9
+ const ftsCountRow = raw.query(`SELECT COUNT(*) AS c FROM memory_segment_fts`).get() as { c: number } | null;
10
+ const ftsCount = ftsCountRow?.c ?? 0;
11
+ if (ftsCount > 0) return;
12
+
13
+ try {
14
+ raw.exec('BEGIN');
15
+ raw.exec(/*sql*/ `
16
+ INSERT INTO memory_segment_fts(segment_id, text)
17
+ SELECT id, text FROM memory_segments
18
+ `);
19
+ raw.exec('COMMIT');
20
+ } catch (e) {
21
+ try { raw.exec('ROLLBACK'); } catch { /* no active transaction */ }
22
+ throw e;
23
+ }
24
+ }
@@ -0,0 +1,87 @@
1
+ import { getSqliteFrom, type DrizzleDb } from '../db-connection.js';
2
+
3
+ /**
4
+ * One-shot migration: merge duplicate relation edges so uniqueness can be
5
+ * enforced on (source_entity_id, target_entity_id, relation).
6
+ */
7
+ export function migrateMemoryEntityRelationDedup(database: DrizzleDb): void {
8
+ const raw = getSqliteFrom(database);
9
+ const checkpointKey = 'migration_memory_entity_relations_dedup_v1';
10
+ const checkpoint = raw.query(
11
+ `SELECT 1 FROM memory_checkpoints WHERE key = ?`,
12
+ ).get(checkpointKey);
13
+ if (checkpoint) return;
14
+
15
+ // Drop the staging temp table if it was left behind by a previous failed
16
+ // attempt in the same connection. TEMP tables survive ROLLBACK (they live
17
+ // in a separate SQLite schema) so a mid-migration exception can leave the
18
+ // table present even after the transaction rolls back. Clearing it here
19
+ // makes re-entry safe without needing IF NOT EXISTS semantics on the full
20
+ // CREATE ... AS SELECT.
21
+ raw.exec(/*sql*/ `DROP TABLE IF EXISTS temp.memory_entity_relation_merge`);
22
+
23
+ try {
24
+ raw.exec('BEGIN');
25
+
26
+ raw.exec(/*sql*/ `
27
+ CREATE TEMP TABLE memory_entity_relation_merge AS
28
+ WITH ranked AS (
29
+ SELECT
30
+ source_entity_id,
31
+ target_entity_id,
32
+ relation,
33
+ first_seen_at,
34
+ last_seen_at,
35
+ evidence,
36
+ ROW_NUMBER() OVER (
37
+ PARTITION BY source_entity_id, target_entity_id, relation
38
+ ORDER BY last_seen_at DESC, first_seen_at DESC, id DESC
39
+ ) AS rank_latest
40
+ FROM memory_entity_relations
41
+ )
42
+ SELECT
43
+ source_entity_id,
44
+ target_entity_id,
45
+ relation,
46
+ MIN(first_seen_at) AS merged_first_seen_at,
47
+ MAX(last_seen_at) AS merged_last_seen_at,
48
+ MAX(CASE WHEN rank_latest = 1 THEN evidence ELSE NULL END) AS merged_evidence
49
+ FROM ranked
50
+ GROUP BY source_entity_id, target_entity_id, relation
51
+ `);
52
+
53
+ raw.exec(/*sql*/ `DELETE FROM memory_entity_relations`);
54
+
55
+ raw.exec(/*sql*/ `
56
+ INSERT INTO memory_entity_relations (
57
+ id,
58
+ source_entity_id,
59
+ target_entity_id,
60
+ relation,
61
+ evidence,
62
+ first_seen_at,
63
+ last_seen_at
64
+ )
65
+ SELECT
66
+ lower(hex(randomblob(16))),
67
+ source_entity_id,
68
+ target_entity_id,
69
+ relation,
70
+ merged_evidence,
71
+ merged_first_seen_at,
72
+ merged_last_seen_at
73
+ FROM memory_entity_relation_merge
74
+ `);
75
+
76
+ raw.exec(/*sql*/ `DROP TABLE temp.memory_entity_relation_merge`);
77
+
78
+ raw.query(
79
+ `INSERT OR IGNORE INTO memory_checkpoints (key, value, updated_at) VALUES (?, '1', ?)`,
80
+ ).run(checkpointKey, Date.now());
81
+
82
+ raw.exec('COMMIT');
83
+ } catch (e) {
84
+ try { raw.exec('ROLLBACK'); } catch { /* no active transaction */ }
85
+ throw e;
86
+ }
87
+ }
@@ -0,0 +1,80 @@
1
+ import { getSqliteFrom, type DrizzleDb } from '../db-connection.js';
2
+
3
+ /**
4
+ * Migrate from a column-level UNIQUE on fingerprint to a compound unique
5
+ * index on (fingerprint, scope_id) so that the same item can exist in
6
+ * different scopes independently.
7
+ */
8
+ export function migrateMemoryItemsFingerprintScopeUnique(database: DrizzleDb): void {
9
+ const raw = getSqliteFrom(database);
10
+ const checkpointKey = 'migration_memory_items_fingerprint_scope_unique_v1';
11
+ const checkpoint = raw.query(
12
+ `SELECT 1 FROM memory_checkpoints WHERE key = ?`,
13
+ ).get(checkpointKey);
14
+ if (checkpoint) return;
15
+
16
+ // Check if the old column-level UNIQUE constraint still exists by inspecting
17
+ // the CREATE TABLE DDL for the word UNIQUE (the PK also creates an autoindex,
18
+ // so we cannot rely on sqlite_autoindex_* presence alone).
19
+ const tableDdl = raw.query(
20
+ `SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memory_items'`,
21
+ ).get() as { sql: string } | null;
22
+ if (!tableDdl || !tableDdl.sql.match(/fingerprint\s+TEXT\s+NOT\s+NULL\s+UNIQUE/i)) {
23
+ // No column-level UNIQUE on fingerprint — either fresh DB or already migrated.
24
+ raw.query(
25
+ `INSERT OR IGNORE INTO memory_checkpoints (key, value, updated_at) VALUES (?, '1', ?)`,
26
+ ).run(checkpointKey, Date.now());
27
+ return;
28
+ }
29
+
30
+ // Rebuild the table without the column-level UNIQUE constraint.
31
+ raw.exec('PRAGMA foreign_keys = OFF');
32
+ try {
33
+ raw.exec('BEGIN');
34
+
35
+ // Create new table without UNIQUE on fingerprint — all other columns
36
+ // match the latest schema (including migration-added columns).
37
+ raw.exec(/*sql*/ `
38
+ CREATE TABLE memory_items_new (
39
+ id TEXT PRIMARY KEY,
40
+ kind TEXT NOT NULL,
41
+ subject TEXT NOT NULL,
42
+ statement TEXT NOT NULL,
43
+ status TEXT NOT NULL,
44
+ confidence REAL NOT NULL,
45
+ fingerprint TEXT NOT NULL,
46
+ first_seen_at INTEGER NOT NULL,
47
+ last_seen_at INTEGER NOT NULL,
48
+ last_used_at INTEGER,
49
+ importance REAL,
50
+ access_count INTEGER NOT NULL DEFAULT 0,
51
+ valid_from INTEGER,
52
+ invalid_at INTEGER,
53
+ verification_state TEXT NOT NULL DEFAULT 'assistant_inferred',
54
+ scope_id TEXT NOT NULL DEFAULT 'default'
55
+ )
56
+ `);
57
+
58
+ raw.exec(/*sql*/ `
59
+ INSERT INTO memory_items_new
60
+ SELECT id, kind, subject, statement, status, confidence, fingerprint,
61
+ first_seen_at, last_seen_at, last_used_at, importance, access_count,
62
+ valid_from, invalid_at, verification_state, scope_id
63
+ FROM memory_items
64
+ `);
65
+
66
+ raw.exec(/*sql*/ `DROP TABLE memory_items`);
67
+ raw.exec(/*sql*/ `ALTER TABLE memory_items_new RENAME TO memory_items`);
68
+
69
+ raw.query(
70
+ `INSERT OR IGNORE INTO memory_checkpoints (key, value, updated_at) VALUES (?, '1', ?)`,
71
+ ).run(checkpointKey, Date.now());
72
+
73
+ raw.exec('COMMIT');
74
+ } catch (e) {
75
+ try { raw.exec('ROLLBACK'); } catch { /* no active transaction */ }
76
+ throw e;
77
+ } finally {
78
+ raw.exec('PRAGMA foreign_keys = ON');
79
+ }
80
+ }
@@ -0,0 +1,62 @@
1
+ import { computeMemoryFingerprint } from '../fingerprint.js';
2
+ import { getSqliteFrom, type DrizzleDb } from '../db-connection.js';
3
+
4
+ /**
5
+ * One-shot migration: recompute fingerprints for existing memory items to
6
+ * include the scope_id prefix introduced in the scope-salted fingerprint PR.
7
+ *
8
+ * Old format: sha256(`${kind}|${subject.toLowerCase()}|${statement.toLowerCase()}`)
9
+ * New format: sha256(`${scopeId}|${kind}|${subject.toLowerCase()}|${statement.toLowerCase()}`)
10
+ *
11
+ * Without this migration, pre-upgrade items would never match on re-extraction,
12
+ * causing duplicates and broken deduplication.
13
+ */
14
+ export function migrateMemoryItemsScopeSaltedFingerprints(database: DrizzleDb): void {
15
+ const raw = getSqliteFrom(database);
16
+ const checkpointKey = 'migration_memory_items_scope_salted_fingerprints_v1';
17
+ const checkpoint = raw.query(
18
+ `SELECT 1 FROM memory_checkpoints WHERE key = ?`,
19
+ ).get(checkpointKey);
20
+ if (checkpoint) return;
21
+
22
+ interface ItemRow {
23
+ id: string;
24
+ kind: string;
25
+ subject: string;
26
+ statement: string;
27
+ scope_id: string;
28
+ }
29
+
30
+ const items = raw.query(
31
+ `SELECT id, kind, subject, statement, scope_id FROM memory_items`,
32
+ ).all() as ItemRow[];
33
+
34
+ if (items.length === 0) {
35
+ raw.query(
36
+ `INSERT OR IGNORE INTO memory_checkpoints (key, value, updated_at) VALUES (?, '1', ?)`,
37
+ ).run(checkpointKey, Date.now());
38
+ return;
39
+ }
40
+
41
+ try {
42
+ raw.exec('BEGIN');
43
+
44
+ const updateStmt = raw.prepare(
45
+ `UPDATE memory_items SET fingerprint = ? WHERE id = ?`,
46
+ );
47
+
48
+ for (const item of items) {
49
+ const fingerprint = computeMemoryFingerprint(item.scope_id, item.kind, item.subject, item.statement);
50
+ updateStmt.run(fingerprint, item.id);
51
+ }
52
+
53
+ raw.query(
54
+ `INSERT OR IGNORE INTO memory_checkpoints (key, value, updated_at) VALUES (?, '1', ?)`,
55
+ ).run(checkpointKey, Date.now());
56
+
57
+ raw.exec('COMMIT');
58
+ } catch (e) {
59
+ try { raw.exec('ROLLBACK'); } catch { /* no active transaction */ }
60
+ throw e;
61
+ }
62
+ }
@@ -0,0 +1,254 @@
1
+ import { getSqliteFrom, type DrizzleDb } from '../db-connection.js';
2
+
3
+ /**
4
+ * One-shot migration: normalize all assistant_id values in assistant-scoped tables
5
+ * to "self" so they are visible after the daemon switched to the implicit single-tenant
6
+ * identity.
7
+ *
8
+ * Before this change, rows were keyed by the real assistantId string passed via the
9
+ * HTTP route. After the route change, all lookups use the constant "self". Without this
10
+ * migration an upgraded daemon would see empty history / attachment lists for existing
11
+ * data that was stored under the old assistantId.
12
+ *
13
+ * Affected tables:
14
+ * - conversation_keys UNIQUE (assistant_id, conversation_key)
15
+ * - attachments UNIQUE (assistant_id, content_hash) WHERE content_hash IS NOT NULL
16
+ * - channel_inbound_events UNIQUE (assistant_id, source_channel, external_chat_id, external_message_id)
17
+ * - message_runs no unique constraint on assistant_id
18
+ *
19
+ * Data-safety guarantees:
20
+ * - conversation_keys: when a key exists under both 'self' and a real assistantId, the
21
+ * 'self' row is updated to point to the real-assistantId conversation (which holds the
22
+ * historical message thread). The 'self' conversation may be orphaned but is not deleted.
23
+ * - attachments: message_attachments links are remapped to the surviving attachment before
24
+ * any duplicate row is deleted, so no message loses its attachment metadata.
25
+ * - channel_inbound_events: only delivery-tracking metadata, not user content; dedup
26
+ * keeps one row per unique (channel, chat, message) tuple.
27
+ * - All conversations and messages remain untouched — only assistant_id index columns
28
+ * and key-lookup rows are modified.
29
+ */
30
+ export function migrateAssistantIdToSelf(database: DrizzleDb): void {
31
+ const raw = getSqliteFrom(database);
32
+ const checkpointKey = 'migration_normalize_assistant_id_to_self_v1';
33
+ const checkpoint = raw.query(
34
+ `SELECT 1 FROM memory_checkpoints WHERE key = ?`,
35
+ ).get(checkpointKey);
36
+ if (checkpoint) return;
37
+
38
+ // On fresh installs the tables are created without assistant_id (PR 7+). Skip the
39
+ // migration if NONE of the four affected tables have the column — pre-seed the
40
+ // checkpoint so subsequent startups are also skipped. Checking all four (not just
41
+ // conversation_keys) avoids a false negative on very old installs where
42
+ // conversation_keys may not exist yet but other tables still carry assistant_id data.
43
+ const affectedTables = ['conversation_keys', 'attachments', 'channel_inbound_events', 'message_runs'];
44
+ const anyHasAssistantId = affectedTables.some((tbl) => {
45
+ const ddl = raw.query(
46
+ `SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?`,
47
+ ).get(tbl) as { sql: string } | null;
48
+ return ddl?.sql.includes('assistant_id') ?? false;
49
+ });
50
+ if (!anyHasAssistantId) {
51
+ raw.query(
52
+ `INSERT OR IGNORE INTO memory_checkpoints (key, value, updated_at) VALUES (?, '1', ?)`,
53
+ ).run(checkpointKey, Date.now());
54
+ return;
55
+ }
56
+
57
+ // Helper: returns true if the given table's current DDL contains 'assistant_id'.
58
+ const tableHasAssistantId = (tbl: string): boolean => {
59
+ const ddl = raw.query(
60
+ `SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?`,
61
+ ).get(tbl) as { sql: string } | null;
62
+ return ddl?.sql.includes('assistant_id') ?? false;
63
+ };
64
+
65
+ try {
66
+ raw.exec('BEGIN');
67
+
68
+ // Each section is guarded so that SQL referencing assistant_id is only executed
69
+ // when the column still exists in that table. This handles mixed-schema states
70
+ // (e.g., very old installs where some tables may already lack the column).
71
+
72
+ // conversation_keys: UNIQUE (assistant_id, conversation_key)
73
+ if (tableHasAssistantId('conversation_keys')) {
74
+ // Step 1: Among non-self rows, keep only one per conversation_key so the
75
+ // bulk UPDATE cannot hit a (non-self-A, key) + (non-self-B, key) collision.
76
+ raw.exec(/*sql*/ `
77
+ DELETE FROM conversation_keys
78
+ WHERE assistant_id != 'self'
79
+ AND rowid NOT IN (
80
+ SELECT MIN(rowid) FROM conversation_keys
81
+ WHERE assistant_id != 'self'
82
+ GROUP BY conversation_key
83
+ )
84
+ `);
85
+ // Step 2: For 'self' rows that have a non-self counterpart with the same
86
+ // conversation_key, update the 'self' row to use the non-self row's
87
+ // conversation_id. This preserves the historical conversation (which
88
+ // has the message history from before the route change) rather than
89
+ // discarding it in favour of a potentially-empty 'self' conversation.
90
+ raw.exec(/*sql*/ `
91
+ UPDATE conversation_keys
92
+ SET conversation_id = (
93
+ SELECT ck_ns.conversation_id
94
+ FROM conversation_keys ck_ns
95
+ WHERE ck_ns.assistant_id != 'self'
96
+ AND ck_ns.conversation_key = conversation_keys.conversation_key
97
+ ORDER BY ck_ns.rowid
98
+ LIMIT 1
99
+ )
100
+ WHERE assistant_id = 'self'
101
+ AND EXISTS (
102
+ SELECT 1 FROM conversation_keys ck_ns
103
+ WHERE ck_ns.assistant_id != 'self'
104
+ AND ck_ns.conversation_key = conversation_keys.conversation_key
105
+ )
106
+ `);
107
+ // Step 3: Delete the now-redundant non-self rows (their conversation_ids
108
+ // have been preserved in the 'self' rows above).
109
+ raw.exec(/*sql*/ `
110
+ DELETE FROM conversation_keys
111
+ WHERE assistant_id != 'self'
112
+ AND EXISTS (
113
+ SELECT 1 FROM conversation_keys ck2
114
+ WHERE ck2.assistant_id = 'self'
115
+ AND ck2.conversation_key = conversation_keys.conversation_key
116
+ )
117
+ `);
118
+ // Step 4: Remaining non-self rows have no 'self' counterpart — safe to bulk-update.
119
+ raw.exec(/*sql*/ `
120
+ UPDATE conversation_keys SET assistant_id = 'self' WHERE assistant_id != 'self'
121
+ `);
122
+ }
123
+
124
+ // attachments: UNIQUE (assistant_id, content_hash) WHERE content_hash IS NOT NULL
125
+ //
126
+ // message_attachments rows reference attachment IDs with ON DELETE CASCADE, so we
127
+ // must remap links to the surviving row BEFORE deleting duplicates to avoid
128
+ // silently dropping attachment metadata from messages.
129
+ if (tableHasAssistantId('attachments')) {
130
+ // Step 1: Remap message_attachments from non-self duplicates to their survivor
131
+ // (MIN rowid per content_hash group), then delete the duplicates.
132
+ raw.exec(/*sql*/ `
133
+ UPDATE message_attachments
134
+ SET attachment_id = (
135
+ SELECT a_survivor.id
136
+ FROM attachments a_survivor
137
+ WHERE a_survivor.assistant_id != 'self'
138
+ AND a_survivor.content_hash = (
139
+ SELECT a_dup.content_hash FROM attachments a_dup
140
+ WHERE a_dup.id = message_attachments.attachment_id
141
+ )
142
+ ORDER BY a_survivor.rowid
143
+ LIMIT 1
144
+ )
145
+ WHERE attachment_id IN (
146
+ SELECT id FROM attachments
147
+ WHERE assistant_id != 'self'
148
+ AND content_hash IS NOT NULL
149
+ AND rowid NOT IN (
150
+ SELECT MIN(rowid) FROM attachments
151
+ WHERE assistant_id != 'self' AND content_hash IS NOT NULL
152
+ GROUP BY content_hash
153
+ )
154
+ )
155
+ `);
156
+ raw.exec(/*sql*/ `
157
+ DELETE FROM attachments
158
+ WHERE assistant_id != 'self'
159
+ AND content_hash IS NOT NULL
160
+ AND rowid NOT IN (
161
+ SELECT MIN(rowid) FROM attachments
162
+ WHERE assistant_id != 'self'
163
+ AND content_hash IS NOT NULL
164
+ GROUP BY content_hash
165
+ )
166
+ `);
167
+ // Step 2: Remap message_attachments from non-self rows conflicting with a 'self'
168
+ // row to the 'self' row, then delete the now-unlinked non-self rows.
169
+ raw.exec(/*sql*/ `
170
+ UPDATE message_attachments
171
+ SET attachment_id = (
172
+ SELECT a_self.id
173
+ FROM attachments a_self
174
+ WHERE a_self.assistant_id = 'self'
175
+ AND a_self.content_hash = (
176
+ SELECT a_ns.content_hash FROM attachments a_ns
177
+ WHERE a_ns.id = message_attachments.attachment_id
178
+ )
179
+ LIMIT 1
180
+ )
181
+ WHERE attachment_id IN (
182
+ SELECT id FROM attachments
183
+ WHERE assistant_id != 'self'
184
+ AND content_hash IS NOT NULL
185
+ AND EXISTS (
186
+ SELECT 1 FROM attachments a2
187
+ WHERE a2.assistant_id = 'self'
188
+ AND a2.content_hash = attachments.content_hash
189
+ )
190
+ )
191
+ `);
192
+ raw.exec(/*sql*/ `
193
+ DELETE FROM attachments
194
+ WHERE assistant_id != 'self'
195
+ AND content_hash IS NOT NULL
196
+ AND EXISTS (
197
+ SELECT 1 FROM attachments a2
198
+ WHERE a2.assistant_id = 'self'
199
+ AND a2.content_hash = attachments.content_hash
200
+ )
201
+ `);
202
+ // Step 3: Bulk-update remaining non-self rows.
203
+ raw.exec(/*sql*/ `
204
+ UPDATE attachments SET assistant_id = 'self' WHERE assistant_id != 'self'
205
+ `);
206
+ }
207
+
208
+ // channel_inbound_events: UNIQUE (assistant_id, source_channel, external_chat_id, external_message_id)
209
+ if (tableHasAssistantId('channel_inbound_events')) {
210
+ // Step 1: Dedup non-self rows sharing the same (source_channel, external_chat_id, external_message_id).
211
+ raw.exec(/*sql*/ `
212
+ DELETE FROM channel_inbound_events
213
+ WHERE assistant_id != 'self'
214
+ AND rowid NOT IN (
215
+ SELECT MIN(rowid) FROM channel_inbound_events
216
+ WHERE assistant_id != 'self'
217
+ GROUP BY source_channel, external_chat_id, external_message_id
218
+ )
219
+ `);
220
+ // Step 2: Delete non-self rows conflicting with existing 'self' rows.
221
+ raw.exec(/*sql*/ `
222
+ DELETE FROM channel_inbound_events
223
+ WHERE assistant_id != 'self'
224
+ AND EXISTS (
225
+ SELECT 1 FROM channel_inbound_events e2
226
+ WHERE e2.assistant_id = 'self'
227
+ AND e2.source_channel = channel_inbound_events.source_channel
228
+ AND e2.external_chat_id = channel_inbound_events.external_chat_id
229
+ AND e2.external_message_id = channel_inbound_events.external_message_id
230
+ )
231
+ `);
232
+ // Step 3: Bulk-update remaining non-self rows.
233
+ raw.exec(/*sql*/ `
234
+ UPDATE channel_inbound_events SET assistant_id = 'self' WHERE assistant_id != 'self'
235
+ `);
236
+ }
237
+
238
+ // message_runs: no unique constraint on assistant_id — simple bulk update
239
+ if (tableHasAssistantId('message_runs')) {
240
+ raw.exec(/*sql*/ `
241
+ UPDATE message_runs SET assistant_id = 'self' WHERE assistant_id != 'self'
242
+ `);
243
+ }
244
+
245
+ raw.query(
246
+ `INSERT OR IGNORE INTO memory_checkpoints (key, value, updated_at) VALUES (?, '1', ?)`,
247
+ ).run(checkpointKey, Date.now());
248
+
249
+ raw.exec('COMMIT');
250
+ } catch (e) {
251
+ try { raw.exec('ROLLBACK'); } catch { /* no active transaction */ }
252
+ throw e;
253
+ }
254
+ }