@vellumai/assistant 0.4.49 → 0.4.50

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 (239) hide show
  1. package/ARCHITECTURE.md +24 -33
  2. package/README.md +3 -3
  3. package/docs/architecture/memory.md +180 -119
  4. package/package.json +2 -2
  5. package/src/__tests__/agent-loop.test.ts +3 -1
  6. package/src/__tests__/anthropic-provider.test.ts +114 -23
  7. package/src/__tests__/approval-cascade.test.ts +1 -15
  8. package/src/__tests__/approval-routes-http.test.ts +2 -0
  9. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  10. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  11. package/src/__tests__/checker.test.ts +13 -0
  12. package/src/__tests__/config-schema.test.ts +1 -68
  13. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  14. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  15. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  16. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  17. package/src/__tests__/credential-vault-unit.test.ts +4 -0
  18. package/src/__tests__/credential-vault.test.ts +13 -1
  19. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  20. package/src/__tests__/date-context.test.ts +93 -77
  21. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  22. package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
  23. package/src/__tests__/history-repair.test.ts +245 -0
  24. package/src/__tests__/host-cu-proxy.test.ts +165 -3
  25. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  26. package/src/__tests__/invite-redemption-service.test.ts +65 -1
  27. package/src/__tests__/keychain-broker-client.test.ts +4 -4
  28. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  29. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  30. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  31. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  32. package/src/__tests__/memory-regressions.test.ts +477 -2841
  33. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  34. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  35. package/src/__tests__/mime-builder.test.ts +28 -0
  36. package/src/__tests__/native-web-search.test.ts +1 -0
  37. package/src/__tests__/oauth-cli.test.ts +572 -5
  38. package/src/__tests__/oauth-store.test.ts +120 -6
  39. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  40. package/src/__tests__/registry.test.ts +0 -1
  41. package/src/__tests__/relay-server.test.ts +46 -1
  42. package/src/__tests__/schedule-tools.test.ts +32 -0
  43. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  44. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  45. package/src/__tests__/secure-keys.test.ts +7 -2
  46. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  47. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  48. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  49. package/src/__tests__/session-agent-loop.test.ts +19 -15
  50. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  51. package/src/__tests__/session-error.test.ts +124 -2
  52. package/src/__tests__/session-history-web-search.test.ts +918 -0
  53. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  54. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  55. package/src/__tests__/session-queue.test.ts +37 -27
  56. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  57. package/src/__tests__/session-slash-known.test.ts +1 -15
  58. package/src/__tests__/session-slash-queue.test.ts +1 -15
  59. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  60. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  61. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  62. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  63. package/src/__tests__/skills-install-extract.test.ts +93 -0
  64. package/src/__tests__/skillssh-registry.test.ts +451 -0
  65. package/src/__tests__/trust-store.test.ts +15 -0
  66. package/src/__tests__/voice-invite-redemption.test.ts +32 -1
  67. package/src/agent/ax-tree-compaction.test.ts +51 -0
  68. package/src/agent/loop.ts +39 -12
  69. package/src/approvals/AGENTS.md +1 -1
  70. package/src/approvals/guardian-request-resolvers.ts +14 -2
  71. package/src/bundler/compiler-tools.ts +66 -2
  72. package/src/calls/call-domain.ts +132 -0
  73. package/src/calls/call-store.ts +6 -0
  74. package/src/calls/relay-server.ts +43 -5
  75. package/src/calls/relay-setup-router.ts +17 -1
  76. package/src/calls/twilio-config.ts +1 -1
  77. package/src/calls/types.ts +3 -1
  78. package/src/cli/commands/doctor.ts +4 -3
  79. package/src/cli/commands/mcp.ts +46 -59
  80. package/src/cli/commands/memory.ts +16 -165
  81. package/src/cli/commands/oauth/apps.ts +31 -2
  82. package/src/cli/commands/oauth/connections.ts +431 -97
  83. package/src/cli/commands/oauth/providers.ts +15 -1
  84. package/src/cli/commands/sessions.ts +5 -2
  85. package/src/cli/commands/skills.ts +173 -1
  86. package/src/cli/http-client.ts +0 -20
  87. package/src/cli/main-screen.tsx +2 -2
  88. package/src/cli/program.ts +5 -6
  89. package/src/cli.ts +4 -10
  90. package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
  91. package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
  92. package/src/config/bundled-tool-registry.ts +2 -5
  93. package/src/config/schema.ts +1 -12
  94. package/src/config/schemas/memory-lifecycle.ts +0 -9
  95. package/src/config/schemas/memory-processing.ts +0 -180
  96. package/src/config/schemas/memory-retrieval.ts +32 -104
  97. package/src/config/schemas/memory.ts +0 -10
  98. package/src/config/types.ts +0 -4
  99. package/src/context/window-manager.ts +4 -1
  100. package/src/daemon/config-watcher.ts +61 -3
  101. package/src/daemon/daemon-control.ts +1 -1
  102. package/src/daemon/date-context.ts +114 -31
  103. package/src/daemon/handlers/sessions.ts +18 -13
  104. package/src/daemon/handlers/skills.ts +20 -1
  105. package/src/daemon/history-repair.ts +72 -8
  106. package/src/daemon/host-cu-proxy.ts +55 -26
  107. package/src/daemon/lifecycle.ts +31 -3
  108. package/src/daemon/mcp-reload-service.ts +2 -2
  109. package/src/daemon/message-types/computer-use.ts +1 -12
  110. package/src/daemon/message-types/memory.ts +4 -16
  111. package/src/daemon/message-types/messages.ts +1 -0
  112. package/src/daemon/message-types/sessions.ts +4 -0
  113. package/src/daemon/server.ts +12 -1
  114. package/src/daemon/session-agent-loop-handlers.ts +38 -0
  115. package/src/daemon/session-agent-loop.ts +334 -48
  116. package/src/daemon/session-error.ts +89 -6
  117. package/src/daemon/session-history.ts +17 -7
  118. package/src/daemon/session-media-retry.ts +6 -2
  119. package/src/daemon/session-memory.ts +69 -149
  120. package/src/daemon/session-process.ts +10 -1
  121. package/src/daemon/session-runtime-assembly.ts +49 -19
  122. package/src/daemon/session-surfaces.ts +4 -1
  123. package/src/daemon/session-tool-setup.ts +7 -1
  124. package/src/daemon/session.ts +12 -2
  125. package/src/instrument.ts +61 -1
  126. package/src/memory/admin.ts +2 -191
  127. package/src/memory/canonical-guardian-store.ts +38 -2
  128. package/src/memory/conversation-crud.ts +0 -33
  129. package/src/memory/conversation-queries.ts +22 -3
  130. package/src/memory/db-init.ts +28 -0
  131. package/src/memory/embedding-backend.ts +84 -8
  132. package/src/memory/embedding-types.ts +9 -1
  133. package/src/memory/indexer.ts +7 -46
  134. package/src/memory/items-extractor.ts +274 -76
  135. package/src/memory/job-handlers/backfill.ts +2 -127
  136. package/src/memory/job-handlers/cleanup.ts +2 -16
  137. package/src/memory/job-handlers/extraction.ts +2 -138
  138. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  139. package/src/memory/job-handlers/summarization.ts +3 -148
  140. package/src/memory/job-utils.ts +21 -59
  141. package/src/memory/jobs-store.ts +1 -159
  142. package/src/memory/jobs-worker.ts +9 -52
  143. package/src/memory/migrations/104-core-indexes.ts +3 -3
  144. package/src/memory/migrations/149-oauth-tables.ts +2 -0
  145. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  146. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  147. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  148. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  149. package/src/memory/migrations/154-drop-fts.ts +20 -0
  150. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  151. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  152. package/src/memory/migrations/index.ts +7 -0
  153. package/src/memory/qdrant-client.ts +148 -51
  154. package/src/memory/raw-query.ts +1 -1
  155. package/src/memory/retriever.test.ts +294 -273
  156. package/src/memory/retriever.ts +421 -645
  157. package/src/memory/schema/calls.ts +2 -0
  158. package/src/memory/schema/memory-core.ts +3 -48
  159. package/src/memory/schema/oauth.ts +2 -0
  160. package/src/memory/search/formatting.ts +263 -176
  161. package/src/memory/search/lexical.ts +1 -254
  162. package/src/memory/search/ranking.ts +0 -455
  163. package/src/memory/search/semantic.ts +100 -14
  164. package/src/memory/search/staleness.ts +47 -0
  165. package/src/memory/search/tier-classifier.ts +21 -0
  166. package/src/memory/search/types.ts +15 -77
  167. package/src/memory/task-memory-cleanup.ts +4 -6
  168. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  169. package/src/oauth/byo-connection.test.ts +8 -1
  170. package/src/oauth/oauth-store.ts +113 -27
  171. package/src/oauth/seed-providers.ts +6 -0
  172. package/src/oauth/token-persistence.ts +11 -3
  173. package/src/permissions/defaults.ts +1 -0
  174. package/src/permissions/trust-store.ts +23 -1
  175. package/src/playbooks/playbook-compiler.ts +1 -1
  176. package/src/prompts/system-prompt.ts +18 -2
  177. package/src/providers/anthropic/client.ts +56 -126
  178. package/src/providers/types.ts +7 -1
  179. package/src/runtime/AGENTS.md +9 -0
  180. package/src/runtime/auth/route-policy.ts +6 -3
  181. package/src/runtime/guardian-reply-router.ts +24 -22
  182. package/src/runtime/http-server.ts +2 -2
  183. package/src/runtime/invite-redemption-service.ts +19 -1
  184. package/src/runtime/invite-service.ts +25 -0
  185. package/src/runtime/pending-interactions.ts +2 -2
  186. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  187. package/src/runtime/routes/conversation-routes.ts +9 -1
  188. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  189. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  190. package/src/runtime/routes/memory-item-routes.ts +503 -0
  191. package/src/runtime/routes/session-management-routes.ts +3 -3
  192. package/src/runtime/routes/settings-routes.ts +2 -2
  193. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  194. package/src/runtime/routes/workspace-routes.ts +2 -1
  195. package/src/security/keychain-broker-client.ts +17 -4
  196. package/src/security/secure-keys.ts +25 -3
  197. package/src/security/token-manager.ts +36 -36
  198. package/src/skills/catalog-install.ts +74 -18
  199. package/src/skills/skillssh-registry.ts +503 -0
  200. package/src/tools/assets/search.ts +5 -1
  201. package/src/tools/computer-use/definitions.ts +0 -10
  202. package/src/tools/computer-use/registry.ts +1 -1
  203. package/src/tools/credentials/vault.ts +1 -3
  204. package/src/tools/memory/definitions.ts +4 -13
  205. package/src/tools/memory/handlers.test.ts +83 -103
  206. package/src/tools/memory/handlers.ts +50 -85
  207. package/src/tools/schedule/create.ts +8 -1
  208. package/src/tools/schedule/update.ts +8 -1
  209. package/src/tools/skills/load.ts +25 -2
  210. package/src/__tests__/clarification-resolver.test.ts +0 -193
  211. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  212. package/src/__tests__/conflict-policy.test.ts +0 -269
  213. package/src/__tests__/conflict-store.test.ts +0 -372
  214. package/src/__tests__/contradiction-checker.test.ts +0 -361
  215. package/src/__tests__/entity-extractor.test.ts +0 -211
  216. package/src/__tests__/entity-search.test.ts +0 -1117
  217. package/src/__tests__/profile-compiler.test.ts +0 -392
  218. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  219. package/src/__tests__/session-profile-injection.test.ts +0 -557
  220. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  221. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  222. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  223. package/src/daemon/session-conflict-gate.ts +0 -167
  224. package/src/daemon/session-dynamic-profile.ts +0 -77
  225. package/src/memory/clarification-resolver.ts +0 -417
  226. package/src/memory/conflict-intent.ts +0 -205
  227. package/src/memory/conflict-policy.ts +0 -127
  228. package/src/memory/conflict-store.ts +0 -410
  229. package/src/memory/contradiction-checker.ts +0 -508
  230. package/src/memory/entity-extractor.ts +0 -535
  231. package/src/memory/format-recall.ts +0 -47
  232. package/src/memory/fts-reconciler.ts +0 -165
  233. package/src/memory/job-handlers/conflict.ts +0 -200
  234. package/src/memory/profile-compiler.ts +0 -195
  235. package/src/memory/recall-cache.ts +0 -117
  236. package/src/memory/search/entity.ts +0 -535
  237. package/src/memory/search/query-expansion.test.ts +0 -70
  238. package/src/memory/search/query-expansion.ts +0 -118
  239. package/src/runtime/routes/mcp-routes.ts +0 -20
@@ -2,20 +2,11 @@ import { getConfig } from "../config/loader.js";
2
2
  import type { AssistantConfig } from "../config/types.js";
3
3
  import { getLogger } from "../util/logger.js";
4
4
  import { rawRun } from "./db.js";
5
- import { reconcileFtsIndexes } from "./fts-reconciler.js";
5
+ import { backfillJob } from "./job-handlers/backfill.js";
6
6
  import {
7
- backfillEntityRelationsJob,
8
- backfillJob,
9
- } from "./job-handlers/backfill.js";
10
- import {
11
- checkContradictionsJob,
12
7
  cleanupStaleSupersededItemsJob,
13
8
  pruneOldConversationsJob,
14
9
  } from "./job-handlers/cleanup.js";
15
- import {
16
- cleanupResolvedConflictsJob,
17
- resolvePendingConflictsForMessageJob,
18
- } from "./job-handlers/conflict.js";
19
10
  // ── Per-job-type handlers ──────────────────────────────────────────
20
11
  import {
21
12
  embedAttachmentJob,
@@ -24,19 +15,13 @@ import {
24
15
  embedSegmentJob,
25
16
  embedSummaryJob,
26
17
  } from "./job-handlers/embedding.js";
27
- import {
28
- extractEntitiesJob,
29
- extractItemsJob,
30
- } from "./job-handlers/extraction.js";
18
+ import { extractItemsJob } from "./job-handlers/extraction.js";
31
19
  import {
32
20
  deleteQdrantVectorsJob,
33
21
  rebuildIndexJob,
34
22
  } from "./job-handlers/index-maintenance.js";
35
23
  import { mediaProcessingJob } from "./job-handlers/media-processing.js";
36
- import {
37
- buildConversationSummaryJob,
38
- buildGlobalSummaryJob,
39
- } from "./job-handlers/summarization.js";
24
+ import { buildConversationSummaryJob } from "./job-handlers/summarization.js";
40
25
  import {
41
26
  BackendUnavailableError,
42
27
  classifyError,
@@ -47,20 +32,14 @@ import {
47
32
  claimMemoryJobs,
48
33
  completeMemoryJob,
49
34
  deferMemoryJob,
50
- enqueueCleanupResolvedConflictsJob,
51
35
  enqueueCleanupStaleSupersededItemsJob,
52
36
  enqueuePruneOldConversationsJob,
53
- enqueueReconcileFtsJob,
54
37
  failMemoryJob,
55
38
  failStalledJobs,
56
39
  type MemoryJob,
57
40
  resetRunningJobsToPending,
58
41
  } from "./jobs-store.js";
59
42
  import { QdrantCircuitOpenError } from "./qdrant-circuit-breaker.js";
60
- import { bumpMemoryVersion } from "./recall-cache.js";
61
-
62
- // Re-export public utilities consumed by tests and other modules
63
- export { currentWeekWindow } from "./job-utils.js";
64
43
 
65
44
  const log = getLogger("memory-jobs-worker");
66
45
 
@@ -157,7 +136,6 @@ export async function runMemoryJobsOnce(
157
136
  try {
158
137
  await processJob(job, config);
159
138
  completeMemoryJob(job.id);
160
- bumpMemoryVersion();
161
139
  groupProcessed += 1;
162
140
  } catch (err) {
163
141
  try {
@@ -292,13 +270,7 @@ async function processJob(
292
270
  await extractItemsJob(job);
293
271
  return;
294
272
  case "extract_entities":
295
- await extractEntitiesJob(job, config);
296
- return;
297
- case "resolve_pending_conflicts_for_message":
298
- await resolvePendingConflictsForMessageJob(job, config);
299
- return;
300
- case "cleanup_resolved_conflicts":
301
- cleanupResolvedConflictsJob(job, config);
273
+ // Entity extraction has been removed — silently drop legacy jobs
302
274
  return;
303
275
  case "cleanup_stale_superseded_items":
304
276
  cleanupStaleSupersededItemsJob(job, config);
@@ -306,30 +278,22 @@ async function processJob(
306
278
  case "prune_old_conversations":
307
279
  pruneOldConversationsJob(job, config);
308
280
  return;
309
- case "check_contradictions":
310
- await checkContradictionsJob(job);
311
- return;
312
281
  case "build_conversation_summary":
313
282
  await buildConversationSummaryJob(job, config);
314
283
  return;
315
- case "refresh_weekly_summary":
316
- await buildGlobalSummaryJob("weekly_global", config);
317
- return;
318
- case "refresh_monthly_summary":
319
- await buildGlobalSummaryJob("monthly_global", config);
320
- return;
321
284
  case "backfill":
322
285
  backfillJob(job, config);
323
286
  return;
324
287
  case "backfill_entity_relations":
325
- backfillEntityRelationsJob(job, config);
288
+ // Entity relation backfill has been removed — silently drop legacy jobs
289
+ return;
290
+ case "refresh_weekly_summary":
291
+ case "refresh_monthly_summary":
292
+ // Global summary rollups have been removed — silently drop legacy jobs
326
293
  return;
327
294
  case "rebuild_index":
328
295
  rebuildIndexJob();
329
296
  return;
330
- case "reconcile_fts":
331
- reconcileFtsIndexes();
332
- return;
333
297
  case "delete_qdrant_vectors":
334
298
  await deleteQdrantVectorsJob(job);
335
299
  return;
@@ -371,9 +335,6 @@ export function maybeEnqueueScheduledCleanupJobs(
371
335
  if (nowMs - lastScheduledCleanupEnqueueMs < cleanup.enqueueIntervalMs)
372
336
  return false;
373
337
 
374
- const resolvedConflictsJobId = enqueueCleanupResolvedConflictsJob(
375
- cleanup.resolvedConflictRetentionMs,
376
- );
377
338
  const staleSupersededItemsJobId = enqueueCleanupStaleSupersededItemsJob(
378
339
  cleanup.supersededItemRetentionMs,
379
340
  );
@@ -381,16 +342,12 @@ export function maybeEnqueueScheduledCleanupJobs(
381
342
  cleanup.conversationRetentionDays > 0
382
343
  ? enqueuePruneOldConversationsJob(cleanup.conversationRetentionDays)
383
344
  : null;
384
- const reconcileFtsJobId = enqueueReconcileFtsJob();
385
345
  lastScheduledCleanupEnqueueMs = nowMs;
386
346
  log.debug(
387
347
  {
388
- resolvedConflictsJobId,
389
348
  staleSupersededItemsJobId,
390
349
  pruneConversationsJobId,
391
- reconcileFtsJobId,
392
350
  enqueueIntervalMs: cleanup.enqueueIntervalMs,
393
- resolvedConflictRetentionMs: cleanup.resolvedConflictRetentionMs,
394
351
  supersededItemRetentionMs: cleanup.supersededItemRetentionMs,
395
352
  conversationRetentionDays: cleanup.conversationRetentionDays,
396
353
  },
@@ -69,9 +69,9 @@ export function createCoreIndexes(database: DrizzleDb): void {
69
69
  database.run(
70
70
  /*sql*/ `CREATE INDEX IF NOT EXISTS idx_memory_items_last_seen_at ON memory_items(last_seen_at)`,
71
71
  );
72
- // Partial covering index for directItemSearch: the LIKE '%term%' pattern can't
73
- // seek a B-tree, but this index lets SQLite scan only active non-invalidated rows
74
- // and evaluate LIKE + return columns without touching the main table.
72
+ // Partial covering index for active memory item queries: this index lets SQLite
73
+ // scan only active non-invalidated rows and return columns without touching
74
+ // the main table.
75
75
  migrateDropActiveSearchIndex(database);
76
76
  database.run(/*sql*/ `
77
77
  CREATE INDEX IF NOT EXISTS idx_memory_items_active_search
@@ -18,6 +18,7 @@ export function createOAuthTables(database: DrizzleDb): void {
18
18
  extra_params TEXT,
19
19
  callback_transport TEXT,
20
20
  loopback_port INTEGER,
21
+ ping_url TEXT,
21
22
  created_at INTEGER NOT NULL,
22
23
  updated_at INTEGER NOT NULL
23
24
  )
@@ -28,6 +29,7 @@ export function createOAuthTables(database: DrizzleDb): void {
28
29
  id TEXT PRIMARY KEY,
29
30
  provider_key TEXT NOT NULL REFERENCES oauth_providers(provider_key),
30
31
  client_id TEXT NOT NULL,
32
+ client_secret_credential_path TEXT NOT NULL DEFAULT '',
31
33
  created_at INTEGER NOT NULL,
32
34
  updated_at INTEGER NOT NULL
33
35
  )
@@ -0,0 +1,98 @@
1
+ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
+ import { withCrashRecovery } from "./validate-migration-state.js";
3
+
4
+ /**
5
+ * Add client_secret_credential_path column to oauth_apps.
6
+ *
7
+ * Makes explicit what was previously implicit: the credential path pattern
8
+ * `oauth_app/${id}/client_secret`. Steps:
9
+ *
10
+ * 1. ALTER TABLE to add the column (nullable initially).
11
+ * 2. Backfill existing rows with `oauth_app/${id}/client_secret`.
12
+ * 3. Rebuild the table to enforce NOT NULL (SQLite doesn't support ALTER COLUMN).
13
+ *
14
+ * Idempotent — skips if the column already exists with NOT NULL.
15
+ */
16
+ export function migrateOAuthAppsClientSecretPath(database: DrizzleDb): void {
17
+ withCrashRecovery(
18
+ database,
19
+ "migration_oauth_apps_client_secret_path_v1",
20
+ () => {
21
+ const raw = getSqliteFrom(database);
22
+
23
+ // Guard: table must exist
24
+ const tableExists = raw
25
+ .query(
26
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'oauth_apps'`,
27
+ )
28
+ .get();
29
+ if (!tableExists) return;
30
+
31
+ // Guard: check if column already exists with NOT NULL
32
+ const colInfo = raw
33
+ .query(
34
+ `SELECT "notnull" FROM pragma_table_info('oauth_apps') WHERE name = 'client_secret_credential_path'`,
35
+ )
36
+ .get() as { notnull: number } | null;
37
+ if (colInfo && colInfo.notnull === 1) return;
38
+
39
+ // Step 1: Add the column (nullable) — wrapped in try/catch for idempotency
40
+ if (!colInfo) {
41
+ try {
42
+ raw.exec(
43
+ /*sql*/ `ALTER TABLE oauth_apps ADD COLUMN client_secret_credential_path TEXT`,
44
+ );
45
+ } catch {
46
+ // Column may already exist from a previous partial run
47
+ }
48
+ }
49
+
50
+ // Step 2: Backfill existing rows
51
+ raw.exec(
52
+ /*sql*/ `UPDATE oauth_apps SET client_secret_credential_path = 'oauth_app/' || id || '/client_secret' WHERE client_secret_credential_path IS NULL`,
53
+ );
54
+
55
+ // Step 3: Rebuild the table to enforce NOT NULL
56
+ raw.exec("PRAGMA foreign_keys = OFF");
57
+ try {
58
+ raw.exec("BEGIN");
59
+
60
+ raw.exec(/*sql*/ `
61
+ CREATE TABLE oauth_apps_new (
62
+ id TEXT PRIMARY KEY,
63
+ provider_key TEXT NOT NULL REFERENCES oauth_providers(provider_key),
64
+ client_id TEXT NOT NULL,
65
+ client_secret_credential_path TEXT NOT NULL,
66
+ created_at INTEGER NOT NULL,
67
+ updated_at INTEGER NOT NULL
68
+ )
69
+ `);
70
+
71
+ raw.exec(/*sql*/ `
72
+ INSERT INTO oauth_apps_new
73
+ SELECT id, provider_key, client_id, client_secret_credential_path, created_at, updated_at
74
+ FROM oauth_apps
75
+ `);
76
+
77
+ raw.exec(/*sql*/ `DROP TABLE oauth_apps`);
78
+ raw.exec(/*sql*/ `ALTER TABLE oauth_apps_new RENAME TO oauth_apps`);
79
+
80
+ // Recreate the unique index
81
+ raw.exec(
82
+ /*sql*/ `CREATE UNIQUE INDEX IF NOT EXISTS idx_oauth_apps_provider_client ON oauth_apps(provider_key, client_id)`,
83
+ );
84
+
85
+ raw.exec("COMMIT");
86
+ } catch (e) {
87
+ try {
88
+ raw.exec("ROLLBACK");
89
+ } catch {
90
+ /* no active transaction */
91
+ }
92
+ throw e;
93
+ } finally {
94
+ raw.exec("PRAGMA foreign_keys = ON");
95
+ }
96
+ },
97
+ );
98
+ }
@@ -0,0 +1,11 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ export function migrateOAuthProvidersPingUrl(database: DrizzleDb): void {
5
+ const raw = getSqliteFrom(database);
6
+ try {
7
+ raw.exec(/*sql*/ `ALTER TABLE oauth_providers ADD COLUMN ping_url TEXT`);
8
+ } catch {
9
+ // Column already exists — nothing to do.
10
+ }
11
+ }
@@ -0,0 +1,44 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ /**
5
+ * Add supersession tracking columns and override confidence to memory_items.
6
+ *
7
+ * - `supersedes` — references the ID of the item this one replaces
8
+ * - `superseded_by` — references the ID of the item that replaced this one
9
+ * - `override_confidence` — enum: "explicit", "tentative", "inferred" (default "inferred")
10
+ * - Index on (status, superseded_by) for filtering active non-superseded items
11
+ *
12
+ * All columns are added via ALTER TABLE with try/catch for idempotency.
13
+ */
14
+ export function migrateMemoryItemSupersession(database: DrizzleDb): void {
15
+ const raw = getSqliteFrom(database);
16
+
17
+ try {
18
+ raw.exec(
19
+ /*sql*/ `ALTER TABLE memory_items ADD COLUMN supersedes TEXT DEFAULT NULL`,
20
+ );
21
+ } catch {
22
+ // Column already exists
23
+ }
24
+
25
+ try {
26
+ raw.exec(
27
+ /*sql*/ `ALTER TABLE memory_items ADD COLUMN superseded_by TEXT DEFAULT NULL`,
28
+ );
29
+ } catch {
30
+ // Column already exists
31
+ }
32
+
33
+ try {
34
+ raw.exec(
35
+ /*sql*/ `ALTER TABLE memory_items ADD COLUMN override_confidence TEXT DEFAULT 'inferred'`,
36
+ );
37
+ } catch {
38
+ // Column already exists
39
+ }
40
+
41
+ raw.exec(
42
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_memory_items_status_superseded_by ON memory_items(status, superseded_by)`,
43
+ );
44
+ }
@@ -0,0 +1,15 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ /**
5
+ * Drop entity-related tables that are no longer used.
6
+ *
7
+ * Entity search has been replaced by hybrid search on entity-rich text
8
+ * in item statements, so these tables are now dead weight.
9
+ */
10
+ export function migrateDropEntityTables(database: DrizzleDb): void {
11
+ const raw = getSqliteFrom(database);
12
+ raw.exec(/*sql*/ `DROP TABLE IF EXISTS memory_item_entities`);
13
+ raw.exec(/*sql*/ `DROP TABLE IF EXISTS memory_entity_relations`);
14
+ raw.exec(/*sql*/ `DROP TABLE IF EXISTS memory_entities`);
15
+ }
@@ -0,0 +1,20 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ /**
5
+ * Drop the memory_segment_fts virtual table and its associated triggers.
6
+ *
7
+ * The FTS-based lexical search pipeline has been replaced by Qdrant hybrid
8
+ * search. Keeping the FTS table around wastes disk space and adds write
9
+ * overhead on every segment insert/update/delete.
10
+ */
11
+ export function migrateDropMemorySegmentFts(database: DrizzleDb): void {
12
+ const raw = getSqliteFrom(database);
13
+
14
+ // Drop triggers first — they reference the FTS table.
15
+ raw.exec(/*sql*/ `DROP TRIGGER IF EXISTS memory_segments_ai`);
16
+ raw.exec(/*sql*/ `DROP TRIGGER IF EXISTS memory_segments_ad`);
17
+ raw.exec(/*sql*/ `DROP TRIGGER IF EXISTS memory_segments_au`);
18
+
19
+ raw.exec(/*sql*/ `DROP TABLE IF EXISTS memory_segment_fts`);
20
+ }
@@ -0,0 +1,7 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ export function migrateDropConflicts(database: DrizzleDb): void {
5
+ const raw = getSqliteFrom(database);
6
+ raw.exec(/*sql*/ `DROP TABLE IF EXISTS memory_item_conflicts`);
7
+ }
@@ -0,0 +1,24 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+
3
+ /**
4
+ * Add invite metadata columns to call_sessions so outbound invite calls
5
+ * can persist friend/guardian names for deterministic routing in the
6
+ * relay setup router (mirroring how verification_session_id works for
7
+ * guardian verification calls).
8
+ */
9
+ export function migrateCallSessionInviteMetadata(database: DrizzleDb): void {
10
+ try {
11
+ database.run(
12
+ /*sql*/ `ALTER TABLE call_sessions ADD COLUMN invite_friend_name TEXT`,
13
+ );
14
+ } catch {
15
+ /* already exists */
16
+ }
17
+ try {
18
+ database.run(
19
+ /*sql*/ `ALTER TABLE call_sessions ADD COLUMN invite_guardian_name TEXT`,
20
+ );
21
+ } catch {
22
+ /* already exists */
23
+ }
24
+ }
@@ -91,6 +91,13 @@ export { migrateScheduleOneShotRouting } from "./146-schedule-oneshot-routing.js
91
91
  export { migrateRemindersToSchedules } from "./147-migrate-reminders-to-schedules.js";
92
92
  export { migrateDropRemindersTable } from "./148-drop-reminders-table.js";
93
93
  export { createOAuthTables } from "./149-oauth-tables.js";
94
+ export { migrateOAuthAppsClientSecretPath } from "./150-oauth-apps-client-secret-path.js";
95
+ export { migrateOAuthProvidersPingUrl } from "./151-oauth-providers-ping-url.js";
96
+ export { migrateMemoryItemSupersession } from "./152-memory-item-supersession.js";
97
+ export { migrateDropEntityTables } from "./153-drop-entity-tables.js";
98
+ export { migrateDropMemorySegmentFts } from "./154-drop-fts.js";
99
+ export { migrateDropConflicts } from "./155-drop-conflicts.js";
100
+ export { migrateCallSessionInviteMetadata } from "./156-call-session-invite-metadata.js";
94
101
  export {
95
102
  MIGRATION_REGISTRY,
96
103
  type MigrationRegistryEntry,