@vellumai/assistant 0.5.5 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +4 -5
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docs/architecture/keychain-broker.md +45 -240
  7. package/docs/architecture/security.md +0 -17
  8. package/docs/credential-execution-service.md +2 -2
  9. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  10. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
  11. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  12. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  13. package/package.json +2 -3
  14. package/src/__tests__/actor-token-service.test.ts +1 -2
  15. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  16. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  17. package/src/__tests__/btw-routes.test.ts +0 -39
  18. package/src/__tests__/call-domain.test.ts +0 -128
  19. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  20. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  21. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  22. package/src/__tests__/checker.test.ts +4 -2
  23. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  24. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  25. package/src/__tests__/config-schema.test.ts +3 -3
  26. package/src/__tests__/context-window-manager.test.ts +78 -0
  27. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  28. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  29. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  30. package/src/__tests__/conversation-title-service.test.ts +117 -1
  31. package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
  32. package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
  33. package/src/__tests__/credential-security-e2e.test.ts +0 -66
  34. package/src/__tests__/credential-security-invariants.test.ts +4 -45
  35. package/src/__tests__/credentials-cli.test.ts +78 -0
  36. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  37. package/src/__tests__/docker-signing-key-bootstrap.test.ts +98 -0
  38. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  39. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  40. package/src/__tests__/host-shell-tool.test.ts +6 -7
  41. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  42. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  43. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  44. package/src/__tests__/intent-routing.test.ts +0 -13
  45. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  46. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  47. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  48. package/src/__tests__/memory-regressions.test.ts +8 -30
  49. package/src/__tests__/migration-export-http.test.ts +2 -2
  50. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  51. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  52. package/src/__tests__/migration-validate-http.test.ts +2 -2
  53. package/src/__tests__/non-member-access-request.test.ts +0 -5
  54. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  55. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  56. package/src/__tests__/permission-types.test.ts +1 -0
  57. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  58. package/src/__tests__/qdrant-manager.test.ts +28 -2
  59. package/src/__tests__/registry.test.ts +0 -6
  60. package/src/__tests__/require-fresh-approval.test.ts +4 -0
  61. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  62. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  63. package/src/__tests__/secure-keys.test.ts +83 -263
  64. package/src/__tests__/shell-identity.test.ts +96 -6
  65. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  66. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  67. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  68. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  69. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  70. package/src/__tests__/skill-load-tool.test.ts +0 -2
  71. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  72. package/src/__tests__/skills.test.ts +0 -2
  73. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  74. package/src/__tests__/suggestion-routes.test.ts +1 -32
  75. package/src/__tests__/system-prompt.test.ts +0 -1
  76. package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -0
  77. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  78. package/src/__tests__/tool-executor.test.ts +4 -0
  79. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  80. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  81. package/src/__tests__/update-bulletin.test.ts +0 -2
  82. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  83. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
  84. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  85. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
  86. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  87. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  88. package/src/calls/audio-store.test.ts +97 -0
  89. package/src/calls/audio-store.ts +205 -0
  90. package/src/calls/call-controller.ts +85 -7
  91. package/src/calls/call-domain.ts +3 -0
  92. package/src/calls/call-store.ts +10 -3
  93. package/src/calls/fish-audio-client.ts +117 -0
  94. package/src/calls/relay-server.ts +27 -0
  95. package/src/calls/twilio-routes.ts +2 -1
  96. package/src/calls/types.ts +1 -0
  97. package/src/calls/voice-ingress-preflight.ts +0 -42
  98. package/src/calls/voice-quality.ts +26 -5
  99. package/src/calls/voice-session-bridge.ts +6 -12
  100. package/src/cli/commands/config.ts +1 -4
  101. package/src/cli/commands/conversations.ts +0 -18
  102. package/src/cli/commands/credentials.ts +34 -4
  103. package/src/cli/commands/oauth/index.ts +7 -0
  104. package/src/cli/commands/oauth/platform.ts +179 -0
  105. package/src/cli/commands/platform.ts +3 -3
  106. package/src/config/assistant-feature-flags.ts +186 -5
  107. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  108. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  109. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  110. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  111. package/src/config/bundled-tool-registry.ts +1 -11
  112. package/src/config/env-registry.ts +1 -1
  113. package/src/config/env.ts +16 -16
  114. package/src/config/feature-flag-registry.json +48 -16
  115. package/src/config/loader.ts +98 -31
  116. package/src/config/schema.ts +4 -25
  117. package/src/config/schemas/calls.ts +13 -0
  118. package/src/config/schemas/fish-audio.ts +39 -0
  119. package/src/config/schemas/memory.ts +0 -4
  120. package/src/config/schemas/platform.ts +1 -1
  121. package/src/config/schemas/security.ts +4 -4
  122. package/src/config/types.ts +0 -1
  123. package/src/contacts/contact-store.ts +39 -0
  124. package/src/contacts/types.ts +2 -0
  125. package/src/context/window-manager.ts +53 -2
  126. package/src/credential-execution/approval-bridge.ts +1 -0
  127. package/src/credential-execution/executable-discovery.ts +28 -4
  128. package/src/credential-execution/feature-gates.ts +16 -0
  129. package/src/credential-execution/process-manager.ts +38 -0
  130. package/src/daemon/assistant-attachments.ts +9 -0
  131. package/src/daemon/config-watcher.ts +6 -4
  132. package/src/daemon/conversation-agent-loop.ts +0 -60
  133. package/src/daemon/conversation-memory.ts +0 -117
  134. package/src/daemon/conversation-runtime-assembly.ts +0 -2
  135. package/src/daemon/conversation-tool-setup.ts +0 -105
  136. package/src/daemon/conversation.ts +10 -1
  137. package/src/daemon/handlers/config-vercel.ts +92 -0
  138. package/src/daemon/handlers/conversations.ts +0 -11
  139. package/src/daemon/handlers/skills.ts +2 -15
  140. package/src/daemon/install-symlink.ts +195 -0
  141. package/src/daemon/lifecycle.ts +229 -96
  142. package/src/daemon/message-types/conversations.ts +3 -4
  143. package/src/daemon/message-types/diagnostics.ts +3 -22
  144. package/src/daemon/message-types/messages.ts +0 -2
  145. package/src/daemon/message-types/upgrades.ts +8 -0
  146. package/src/daemon/server.ts +30 -92
  147. package/src/events/domain-events.ts +2 -1
  148. package/src/followups/followup-store.ts +5 -2
  149. package/src/inbound/platform-callback-registration.ts +3 -3
  150. package/src/instrument.ts +8 -5
  151. package/src/memory/conversation-crud.ts +0 -236
  152. package/src/memory/conversation-title-service.ts +76 -11
  153. package/src/memory/db-init.ts +15 -11
  154. package/src/memory/indexer.ts +15 -106
  155. package/src/memory/items-extractor.ts +15 -1
  156. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  157. package/src/memory/job-handlers/embedding.ts +0 -79
  158. package/src/memory/job-utils.ts +1 -1
  159. package/src/memory/jobs-store.ts +30 -13
  160. package/src/memory/jobs-worker.ts +31 -27
  161. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  162. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  163. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  164. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  165. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  166. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  167. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  168. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  169. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  170. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  171. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  172. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  173. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  174. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  175. package/src/memory/migrations/116-messages-fts.ts +106 -1
  176. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  177. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  178. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  179. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  180. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  181. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  182. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  183. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  184. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  185. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  186. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  187. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  188. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  189. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  190. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  191. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  192. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  193. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  194. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  195. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  196. package/src/memory/migrations/189-drop-simplified-memory.ts +42 -0
  197. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  198. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  199. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  200. package/src/memory/migrations/index.ts +5 -3
  201. package/src/memory/migrations/registry.ts +90 -0
  202. package/src/memory/migrations/validate-migration-state.ts +137 -11
  203. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  204. package/src/memory/qdrant-client.ts +4 -6
  205. package/src/memory/qdrant-manager.ts +64 -7
  206. package/src/memory/schema/calls.ts +1 -0
  207. package/src/memory/schema/contacts.ts +1 -0
  208. package/src/memory/schema/conversations.ts +0 -3
  209. package/src/memory/schema/index.ts +0 -2
  210. package/src/messaging/draft-store.ts +2 -2
  211. package/src/notifications/decision-engine.ts +4 -1
  212. package/src/oauth/connection-resolver.ts +6 -4
  213. package/src/permissions/checker.ts +0 -38
  214. package/src/permissions/defaults.ts +3 -3
  215. package/src/permissions/shell-identity.ts +76 -22
  216. package/src/permissions/trust-client.ts +2 -13
  217. package/src/permissions/trust-store.ts +8 -3
  218. package/src/permissions/types.ts +4 -2
  219. package/src/platform/client.ts +35 -7
  220. package/src/prompts/persona-resolver.ts +138 -0
  221. package/src/prompts/system-prompt.ts +36 -4
  222. package/src/prompts/templates/users/default.md +1 -0
  223. package/src/providers/registry.ts +27 -40
  224. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  225. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  226. package/src/runtime/auth/external-assistant-id.ts +13 -59
  227. package/src/runtime/auth/route-policy.ts +29 -1
  228. package/src/runtime/auth/token-service.ts +53 -15
  229. package/src/runtime/channel-readiness-service.ts +1 -16
  230. package/src/runtime/http-server.ts +29 -2
  231. package/src/runtime/middleware/error-handler.ts +1 -9
  232. package/src/runtime/routes/audio-routes.ts +40 -0
  233. package/src/runtime/routes/btw-routes.ts +0 -17
  234. package/src/runtime/routes/conversation-management-routes.ts +0 -36
  235. package/src/runtime/routes/conversation-query-routes.ts +106 -2
  236. package/src/runtime/routes/conversation-routes.ts +4 -43
  237. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  238. package/src/runtime/routes/identity-routes.ts +18 -29
  239. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  240. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  241. package/src/runtime/routes/integrations/vercel.ts +89 -0
  242. package/src/runtime/routes/log-export-routes.ts +5 -0
  243. package/src/runtime/routes/memory-item-routes.test.ts +221 -3
  244. package/src/runtime/routes/memory-item-routes.ts +144 -4
  245. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  246. package/src/runtime/routes/migration-routes.ts +17 -1
  247. package/src/runtime/routes/notification-routes.ts +58 -0
  248. package/src/runtime/routes/schedule-routes.ts +65 -0
  249. package/src/runtime/routes/settings-routes.ts +41 -1
  250. package/src/runtime/routes/tts-routes.ts +86 -0
  251. package/src/runtime/routes/upgrade-broadcast-routes.ts +175 -0
  252. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  253. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  254. package/src/runtime/routes/workspace-routes.ts +1 -1
  255. package/src/runtime/routes/workspace-utils.ts +86 -2
  256. package/src/schedule/schedule-store.ts +0 -21
  257. package/src/security/ces-credential-client.ts +59 -22
  258. package/src/security/ces-rpc-credential-backend.ts +85 -0
  259. package/src/security/credential-backend.ts +12 -88
  260. package/src/security/keychain-broker-client.ts +10 -2
  261. package/src/security/secure-keys.ts +94 -113
  262. package/src/skills/catalog-install.ts +13 -7
  263. package/src/skills/inline-command-render.ts +5 -1
  264. package/src/skills/inline-command-runner.ts +30 -2
  265. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  266. package/src/tools/calls/call-start.ts +1 -0
  267. package/src/tools/executor.ts +0 -4
  268. package/src/tools/memory/handlers.ts +1 -129
  269. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  270. package/src/tools/network/web-fetch.ts +3 -1
  271. package/src/tools/permission-checker.ts +18 -0
  272. package/src/tools/skills/execute.ts +1 -1
  273. package/src/tools/skills/load.ts +9 -2
  274. package/src/tools/types.ts +0 -8
  275. package/src/util/errors.ts +0 -12
  276. package/src/util/platform.ts +8 -55
  277. package/src/util/xml.ts +8 -0
  278. package/src/workspace/git-service.ts +5 -2
  279. package/src/workspace/heartbeat-service.ts +5 -24
  280. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  281. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  282. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  283. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  284. package/src/workspace/migrations/006-services-config.ts +49 -0
  285. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  286. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  287. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  288. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  289. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  290. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  291. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  292. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  293. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  294. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  295. package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
  296. package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
  297. package/src/workspace/migrations/registry.ts +8 -0
  298. package/src/workspace/migrations/runner.ts +106 -2
  299. package/src/workspace/migrations/types.ts +4 -0
  300. package/src/__tests__/archive-recall.test.ts +0 -560
  301. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  302. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  303. package/src/__tests__/conversation-memory-dirty-tail.test.ts +0 -150
  304. package/src/__tests__/conversation-switch-memory-reduction.test.ts +0 -474
  305. package/src/__tests__/db-memory-archive-migration.test.ts +0 -372
  306. package/src/__tests__/db-memory-brief-state-migration.test.ts +0 -213
  307. package/src/__tests__/db-memory-reducer-checkpoints.test.ts +0 -273
  308. package/src/__tests__/diagnostics-export.test.ts +0 -288
  309. package/src/__tests__/local-gateway-health.test.ts +0 -209
  310. package/src/__tests__/memory-brief-open-loops.test.ts +0 -530
  311. package/src/__tests__/memory-brief-time.test.ts +0 -285
  312. package/src/__tests__/memory-brief-wrapper.test.ts +0 -311
  313. package/src/__tests__/memory-chunk-archive.test.ts +0 -400
  314. package/src/__tests__/memory-chunk-dual-write.test.ts +0 -453
  315. package/src/__tests__/memory-episode-archive.test.ts +0 -370
  316. package/src/__tests__/memory-episode-dual-write.test.ts +0 -626
  317. package/src/__tests__/memory-observation-archive.test.ts +0 -375
  318. package/src/__tests__/memory-observation-dual-write.test.ts +0 -318
  319. package/src/__tests__/memory-reducer-job.test.ts +0 -538
  320. package/src/__tests__/memory-reducer-scheduling.test.ts +0 -473
  321. package/src/__tests__/memory-reducer-store.test.ts +0 -728
  322. package/src/__tests__/memory-reducer-types.test.ts +0 -707
  323. package/src/__tests__/memory-reducer.test.ts +0 -704
  324. package/src/__tests__/memory-simplified-config.test.ts +0 -281
  325. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  326. package/src/__tests__/simplified-memory-e2e.test.ts +0 -666
  327. package/src/__tests__/simplified-memory-runtime.test.ts +0 -616
  328. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  329. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  330. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  331. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  332. package/src/__tests__/swarm-recursion.test.ts +0 -197
  333. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  334. package/src/__tests__/swarm-tool.test.ts +0 -185
  335. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  336. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  337. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  338. package/src/commands/cc-command-registry.ts +0 -248
  339. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  340. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  341. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  342. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  343. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  344. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  345. package/src/config/schemas/memory-simplified.ts +0 -101
  346. package/src/config/schemas/swarm.ts +0 -82
  347. package/src/logfire.ts +0 -135
  348. package/src/memory/archive-recall.ts +0 -516
  349. package/src/memory/archive-store.ts +0 -400
  350. package/src/memory/brief-formatting.ts +0 -33
  351. package/src/memory/brief-open-loops.ts +0 -266
  352. package/src/memory/brief-time.ts +0 -162
  353. package/src/memory/brief.ts +0 -75
  354. package/src/memory/job-handlers/backfill-simplified-memory.ts +0 -462
  355. package/src/memory/job-handlers/reduce-conversation-memory.ts +0 -229
  356. package/src/memory/migrations/185-memory-brief-state.ts +0 -52
  357. package/src/memory/migrations/186-memory-archive.ts +0 -109
  358. package/src/memory/migrations/187-memory-reducer-checkpoints.ts +0 -19
  359. package/src/memory/reducer-scheduler.ts +0 -242
  360. package/src/memory/reducer-store.ts +0 -271
  361. package/src/memory/reducer-types.ts +0 -106
  362. package/src/memory/reducer.ts +0 -467
  363. package/src/memory/schema/memory-archive.ts +0 -121
  364. package/src/memory/schema/memory-brief.ts +0 -55
  365. package/src/runtime/local-gateway-health.ts +0 -275
  366. package/src/security/secret-ingress.ts +0 -68
  367. package/src/swarm/backend-claude-code.ts +0 -225
  368. package/src/swarm/checkpoint.ts +0 -137
  369. package/src/swarm/graph-utils.ts +0 -53
  370. package/src/swarm/index.ts +0 -55
  371. package/src/swarm/limits.ts +0 -66
  372. package/src/swarm/orchestrator.ts +0 -424
  373. package/src/swarm/plan-validator.ts +0 -117
  374. package/src/swarm/router-planner.ts +0 -162
  375. package/src/swarm/router-prompts.ts +0 -39
  376. package/src/swarm/synthesizer.ts +0 -81
  377. package/src/swarm/types.ts +0 -72
  378. package/src/swarm/worker-backend.ts +0 -131
  379. package/src/swarm/worker-prompts.ts +0 -80
  380. package/src/swarm/worker-runner.ts +0 -170
  381. package/src/tools/claude-code/claude-code.ts +0 -610
  382. package/src/tools/swarm/delegate.ts +0 -205
@@ -7,10 +7,16 @@ import type { WorkspaceMigration } from "./types.js";
7
7
 
8
8
  const log = getLogger("workspace-migrations");
9
9
 
10
+ export function getLastWorkspaceMigrationId(
11
+ migrations: WorkspaceMigration[],
12
+ ): string | null {
13
+ return migrations.length > 0 ? migrations[migrations.length - 1].id : null;
14
+ }
15
+
10
16
  export type CheckpointFile = {
11
17
  applied: Record<
12
18
  string,
13
- { appliedAt: string; status?: "started" | "completed" }
19
+ { appliedAt: string; status?: "started" | "completed" | "rolling_back" }
14
20
  >;
15
21
  };
16
22
 
@@ -73,7 +79,7 @@ export async function runWorkspaceMigrations(
73
79
  const checkpoints = loadCheckpoints(workspaceDir);
74
80
 
75
81
  for (const [id, entry] of Object.entries(checkpoints.applied)) {
76
- if (entry.status === "started") {
82
+ if (entry.status === "started" || entry.status === "rolling_back") {
77
83
  log.warn(
78
84
  `Workspace migration "${id}" was interrupted during a previous run; will re-run`,
79
85
  );
@@ -115,3 +121,101 @@ export async function runWorkspaceMigrations(
115
121
  saveCheckpoints(workspaceDir, checkpoints);
116
122
  }
117
123
  }
124
+
125
+ /**
126
+ * Roll back workspace (filesystem) migrations in reverse order, stopping before
127
+ * the target migration.
128
+ *
129
+ * Migrations after `targetMigrationId` in the registry array are reversed in
130
+ * reverse order; the target migration itself is kept applied.
131
+ *
132
+ * **Usage**: Pass the full migrations array (typically `WORKSPACE_MIGRATIONS`
133
+ * from `registry.ts`) and the ID of the migration you want to roll back *to*.
134
+ * For example, `rollbackWorkspaceMigrations(dir, migrations, "010-app-dir-rename")`
135
+ * rolls back all applied migrations that appear after `010-app-dir-rename` in
136
+ * the registry.
137
+ *
138
+ * **Checkpoint state**: Each rolled-back migration's entry is deleted from the
139
+ * `.workspace-migrations.json` checkpoint file. If the process crashes
140
+ * mid-rollback, the `"rolling_back"` marker is detected and cleared by
141
+ * `runWorkspaceMigrations` on the next startup (it re-runs interrupted
142
+ * migrations).
143
+ *
144
+ * **Warning — data loss**: Every workspace migration must define a `down()`
145
+ * method (enforced at the type level), but some rollbacks are lossy (e.g.,
146
+ * file deletions or format conversions that discard the original cannot fully
147
+ * restore prior state). Review each migration's `down()` implementation
148
+ * before calling this function.
149
+ *
150
+ * **Important**: Stop the assistant before running rollbacks. Rolling back
151
+ * workspace migrations while the assistant is running may cause file conflicts,
152
+ * stale caches, or data corruption.
153
+ *
154
+ * @param workspaceDir The workspace directory path (e.g., `~/.vellum/workspace`).
155
+ * @param migrations The full ordered array of workspace migrations (from `WORKSPACE_MIGRATIONS`).
156
+ * @param targetMigrationId The migration ID to roll back to (exclusive — all
157
+ * migrations after this one are reversed).
158
+ */
159
+ export async function rollbackWorkspaceMigrations(
160
+ workspaceDir: string,
161
+ migrations: WorkspaceMigration[],
162
+ targetMigrationId: string,
163
+ ): Promise<void> {
164
+ // Find the index of the target migration
165
+ const targetIndex = migrations.findIndex((m) => m.id === targetMigrationId);
166
+ if (targetIndex === -1) {
167
+ throw new Error(
168
+ `Target migration "${targetMigrationId}" not found in the migrations array`,
169
+ );
170
+ }
171
+
172
+ // Collect migrations that come after the target, in reverse order
173
+ const migrationsToRollback = migrations.slice(targetIndex + 1).reverse();
174
+ if (migrationsToRollback.length === 0) {
175
+ log.info("No migrations to roll back");
176
+ return;
177
+ }
178
+
179
+ const checkpoints = loadCheckpoints(workspaceDir);
180
+
181
+ for (const migration of migrationsToRollback) {
182
+ // Only roll back migrations that have been fully applied.
183
+ // Legacy checkpoints may not have a status field (just appliedAt) — treat
184
+ // missing/undefined status as completed, matching runWorkspaceMigrations behavior.
185
+ const entry = checkpoints.applied[migration.id];
186
+ if (
187
+ !entry ||
188
+ entry.status === "started" ||
189
+ entry.status === "rolling_back"
190
+ ) {
191
+ continue;
192
+ }
193
+
194
+ log.info(
195
+ `Rolling back workspace migration: ${migration.id} — ${migration.description}`,
196
+ );
197
+
198
+ // Mark as rolling_back before execution (for crash recovery)
199
+ checkpoints.applied[migration.id] = {
200
+ appliedAt: checkpoints.applied[migration.id]!.appliedAt,
201
+ status: "rolling_back",
202
+ };
203
+ saveCheckpoints(workspaceDir, checkpoints);
204
+
205
+ try {
206
+ await migration.down(workspaceDir);
207
+ } catch (error) {
208
+ log.error(
209
+ { migrationId: migration.id, error },
210
+ `Workspace migration rollback failed: ${migration.id}`,
211
+ );
212
+ throw error;
213
+ }
214
+
215
+ // Remove the migration entry from checkpoints
216
+ delete checkpoints.applied[migration.id];
217
+ saveCheckpoints(workspaceDir, checkpoints);
218
+
219
+ log.info(`Rolled back workspace migration: ${migration.id}`);
220
+ }
221
+ }
@@ -8,4 +8,8 @@ export interface WorkspaceMigration {
8
8
  * Must be idempotent — safe to re-run if it was interrupted.
9
9
  * Both synchronous and asynchronous migrations are supported. */
10
10
  run(workspaceDir: string): void | Promise<void>;
11
+ /** Reverse the migration. Receives the workspace directory path.
12
+ * Must be idempotent — safe to re-run if it was interrupted.
13
+ * Both synchronous and asynchronous rollbacks are supported. */
14
+ down(workspaceDir: string): void | Promise<void>;
11
15
  }
@@ -1,560 +0,0 @@
1
- /**
2
- * Tests for the archive recall module.
3
- *
4
- * Covers:
5
- * - Explicit artifact recall (past-reference triggers)
6
- * - Analogy/debugging-shaped recall
7
- * - Strong prefetch triggers
8
- * - Empty result omission (no `<supporting_recall>` when nothing found)
9
- * - Keyword extraction
10
- * - Rendering format
11
- */
12
- import { mkdtempSync, rmSync } from "node:fs";
13
- import { tmpdir } from "node:os";
14
- import { join } from "node:path";
15
- import {
16
- afterAll,
17
- beforeAll,
18
- beforeEach,
19
- describe,
20
- expect,
21
- mock,
22
- test,
23
- } from "bun:test";
24
-
25
- const testDir = mkdtempSync(join(tmpdir(), "archive-recall-test-"));
26
- const dbPath = join(testDir, "test.db");
27
-
28
- mock.module("../util/platform.js", () => ({
29
- getDataDir: () => testDir,
30
- isMacOS: () => process.platform === "darwin",
31
- isLinux: () => process.platform === "linux",
32
- isWindows: () => process.platform === "win32",
33
- getPidPath: () => join(testDir, "test.pid"),
34
- getDbPath: () => dbPath,
35
- getLogPath: () => join(testDir, "test.log"),
36
- ensureDataDir: () => {},
37
- }));
38
-
39
- mock.module("../util/logger.js", () => ({
40
- getLogger: () =>
41
- new Proxy({} as Record<string, unknown>, {
42
- get: () => () => {},
43
- }),
44
- }));
45
-
46
- import { v4 as uuid } from "uuid";
47
-
48
- import {
49
- buildArchiveRecall,
50
- classifyRecallTrigger,
51
- extractKeywords,
52
- prefetchArchive,
53
- type RecallBullet,
54
- renderSupportingRecall,
55
- } from "../memory/archive-recall.js";
56
- import {
57
- insertCompactionEpisode,
58
- insertObservation,
59
- } from "../memory/archive-store.js";
60
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
61
- import { conversations, messages } from "../memory/schema.js";
62
-
63
- // ── Helpers ─────────────────────────────────────────────────────────
64
-
65
- function removeTestDbFiles(): void {
66
- rmSync(dbPath, { force: true });
67
- rmSync(`${dbPath}-shm`, { force: true });
68
- rmSync(`${dbPath}-wal`, { force: true });
69
- }
70
-
71
- function createConversation(id: string, title: string | null = null): void {
72
- const db = getDb();
73
- const now = Date.now();
74
- db.insert(conversations)
75
- .values({
76
- id,
77
- title,
78
- createdAt: now,
79
- updatedAt: now,
80
- })
81
- .run();
82
- }
83
-
84
- function createMessage(
85
- id: string,
86
- conversationId: string,
87
- role: string = "user",
88
- content: string = "test message",
89
- ): void {
90
- const db = getDb();
91
- db.insert(messages)
92
- .values({
93
- id,
94
- conversationId,
95
- role,
96
- content,
97
- createdAt: Date.now(),
98
- })
99
- .run();
100
- }
101
-
102
- // ── Test suite ──────────────────────────────────────────────────────
103
-
104
- describe("Archive Recall", () => {
105
- beforeAll(() => {
106
- initializeDb();
107
- });
108
-
109
- beforeEach(() => {
110
- resetDb();
111
- removeTestDbFiles();
112
- initializeDb();
113
- });
114
-
115
- afterAll(() => {
116
- resetDb();
117
- rmSync(testDir, { recursive: true, force: true });
118
- });
119
-
120
- // ─────────────────────────────────────────────────────────────────
121
- // classifyRecallTrigger
122
- // ─────────────────────────────────────────────────────────────────
123
-
124
- describe("classifyRecallTrigger", () => {
125
- test("detects explicit past-reference phrases", () => {
126
- expect(
127
- classifyRecallTrigger("Do you remember the API we discussed?", 0),
128
- ).toBe("explicit_past_reference");
129
- expect(classifyRecallTrigger("We talked about this last time", 0)).toBe(
130
- "explicit_past_reference",
131
- );
132
- expect(
133
- classifyRecallTrigger("As I mentioned earlier, the config is wrong", 0),
134
- ).toBe("explicit_past_reference");
135
- expect(
136
- classifyRecallTrigger("I previously told you about the bug", 0),
137
- ).toBe("explicit_past_reference");
138
- });
139
-
140
- test("detects analogy/debugging-shaped phrases", () => {
141
- expect(
142
- classifyRecallTrigger("This is similar to the issue we had", 0),
143
- ).toBe("analogy_debug");
144
- expect(classifyRecallTrigger("I keep getting this error", 0)).toBe(
145
- "analogy_debug",
146
- );
147
- expect(classifyRecallTrigger("Same problem as yesterday", 0)).toBe(
148
- "analogy_debug",
149
- );
150
- });
151
-
152
- test("detects strong prefetch hits", () => {
153
- expect(
154
- classifyRecallTrigger("How should I configure the database?", 2),
155
- ).toBe("strong_prefetch");
156
- expect(
157
- classifyRecallTrigger("How should I configure the database?", 5),
158
- ).toBe("strong_prefetch");
159
- });
160
-
161
- test("returns none for ordinary turns", () => {
162
- expect(classifyRecallTrigger("What is the capital of France?", 0)).toBe(
163
- "none",
164
- );
165
- expect(
166
- classifyRecallTrigger("Write a function to sort an array", 1),
167
- ).toBe("none");
168
- });
169
-
170
- test("explicit past-reference takes priority over analogy", () => {
171
- // "remember" matches past-reference, "same issue" matches analogy
172
- expect(
173
- classifyRecallTrigger("Do you remember the same issue we had?", 0),
174
- ).toBe("explicit_past_reference");
175
- });
176
- });
177
-
178
- // ─────────────────────────────────────────────────────────────────
179
- // extractKeywords
180
- // ─────────────────────────────────────────────────────────────────
181
-
182
- describe("extractKeywords", () => {
183
- test("extracts meaningful words >= 4 chars", () => {
184
- const kw = extractKeywords("How do I fix the authentication error?");
185
- expect(kw).toContain("authentication");
186
- expect(kw).toContain("error");
187
- // "how", "do", "I", "fix", "the" are too short or stop words
188
- expect(kw).not.toContain("how");
189
- expect(kw).not.toContain("the");
190
- });
191
-
192
- test("removes stop words", () => {
193
- const kw = extractKeywords("I want to make this very much better");
194
- expect(kw).not.toContain("want");
195
- expect(kw).not.toContain("very");
196
- expect(kw).not.toContain("much");
197
- expect(kw).toContain("better");
198
- });
199
-
200
- test("deduplicates keywords", () => {
201
- const kw = extractKeywords("error error error authentication");
202
- expect(kw.filter((w) => w === "error")).toHaveLength(1);
203
- });
204
-
205
- test("returns empty for short/stop-word-only input", () => {
206
- expect(extractKeywords("hi")).toEqual([]);
207
- expect(extractKeywords("the a an")).toEqual([]);
208
- });
209
- });
210
-
211
- // ─────────────────────────────────────────────────────────────────
212
- // renderSupportingRecall
213
- // ─────────────────────────────────────────────────────────────────
214
-
215
- describe("renderSupportingRecall", () => {
216
- test("renders bullets in <supporting_recall> tag", () => {
217
- const bullets: RecallBullet[] = [
218
- {
219
- text: "User prefers REST APIs",
220
- source: "observation",
221
- sourceId: "obs-1",
222
- conversationTitle: "API Discussion",
223
- },
224
- {
225
- text: "Deployed to production last week",
226
- source: "episode",
227
- sourceId: "ep-1",
228
- },
229
- ];
230
-
231
- const result = renderSupportingRecall(bullets);
232
- expect(result).toContain("<supporting_recall>");
233
- expect(result).toContain("</supporting_recall>");
234
- expect(result).toContain(
235
- "- User prefers REST APIs (from: API Discussion)",
236
- );
237
- expect(result).toContain("- Deployed to production last week");
238
- // No provenance for second bullet (no conversationTitle)
239
- expect(result).not.toContain("(from: undefined)");
240
- expect(result).not.toContain("(from: null)");
241
- });
242
-
243
- test("returns empty string for empty bullets", () => {
244
- expect(renderSupportingRecall([])).toBe("");
245
- });
246
- });
247
-
248
- // ─────────────────────────────────────────────────────────────────
249
- // Explicit artifact recall
250
- // ─────────────────────────────────────────────────────────────────
251
-
252
- describe("explicit artifact recall", () => {
253
- test("recalls observations when user references past discussion", () => {
254
- const convId = uuid();
255
- const msgId = uuid();
256
- createConversation(convId, "Authentication Redesign");
257
- createMessage(msgId, convId);
258
-
259
- insertObservation({
260
- conversationId: convId,
261
- messageId: msgId,
262
- role: "user",
263
- content:
264
- "User wants to migrate authentication from JWT to session tokens",
265
- scopeId: "default",
266
- });
267
-
268
- const result = buildArchiveRecall(
269
- "default",
270
- "Do you remember what we discussed about authentication?",
271
- );
272
-
273
- expect(result.trigger).toBe("explicit_past_reference");
274
- expect(result.bullets.length).toBeGreaterThan(0);
275
- expect(result.text).toContain("<supporting_recall>");
276
- expect(result.text).toContain("authentication");
277
- });
278
-
279
- test("recalls episodes when user references past work", () => {
280
- const convId = uuid();
281
- createConversation(convId, "Database Migration Sprint");
282
-
283
- insertCompactionEpisode({
284
- scopeId: "default",
285
- conversationId: convId,
286
- title: "PostgreSQL Migration Planning",
287
- summary:
288
- "Discussed migrating from MySQL to PostgreSQL, decided on a phased approach starting with read replicas",
289
- tokenEstimate: 25,
290
- startAt: Date.now() - 86_400_000,
291
- endAt: Date.now() - 43_200_000,
292
- });
293
-
294
- const result = buildArchiveRecall(
295
- "default",
296
- "What did we talk about regarding the PostgreSQL migration?",
297
- );
298
-
299
- expect(result.trigger).toBe("explicit_past_reference");
300
- expect(result.bullets.length).toBeGreaterThan(0);
301
- expect(result.text).toContain("<supporting_recall>");
302
- expect(result.text).toContain("PostgreSQL");
303
- });
304
- });
305
-
306
- // ─────────────────────────────────────────────────────────────────
307
- // Analogy/debugging-shaped recall
308
- // ─────────────────────────────────────────────────────────────────
309
-
310
- describe("analogy-shaped recall", () => {
311
- test("recalls when user reports a recurring issue", () => {
312
- const convId = uuid();
313
- const msgId = uuid();
314
- createConversation(convId, "Debugging Session");
315
- createMessage(msgId, convId);
316
-
317
- insertObservation({
318
- conversationId: convId,
319
- messageId: msgId,
320
- role: "user",
321
- content:
322
- "Connection timeout error when calling the payment gateway service",
323
- scopeId: "default",
324
- });
325
-
326
- const result = buildArchiveRecall(
327
- "default",
328
- "I keep getting a timeout error with the payment service",
329
- );
330
-
331
- expect(result.trigger).toBe("analogy_debug");
332
- expect(result.bullets.length).toBeGreaterThan(0);
333
- expect(result.text).toContain("<supporting_recall>");
334
- expect(result.text).toContain("timeout");
335
- });
336
-
337
- test("recalls similar past episodes for analogy queries", () => {
338
- const convId = uuid();
339
- createConversation(convId, "Infrastructure Issues");
340
-
341
- insertCompactionEpisode({
342
- scopeId: "default",
343
- conversationId: convId,
344
- title: "Redis Connection Pool Exhaustion",
345
- summary:
346
- "Debugged Redis connection pool exhaustion caused by missing connection.release() calls in the retry handler",
347
- tokenEstimate: 30,
348
- startAt: Date.now() - 172_800_000,
349
- endAt: Date.now() - 86_400_000,
350
- });
351
-
352
- const result = buildArchiveRecall(
353
- "default",
354
- "This is similar to the Redis connection issue we had",
355
- );
356
-
357
- expect(result.trigger).toBe("analogy_debug");
358
- expect(result.bullets.length).toBeGreaterThan(0);
359
- expect(result.text).toContain("Redis");
360
- });
361
- });
362
-
363
- // ─────────────────────────────────────────────────────────────────
364
- // Empty result omission
365
- // ─────────────────────────────────────────────────────────────────
366
-
367
- describe("empty result omission", () => {
368
- test("returns empty text when no archive content exists", () => {
369
- const result = buildArchiveRecall(
370
- "default",
371
- "Do you remember what we discussed about quantum computing?",
372
- );
373
-
374
- expect(result.trigger).toBe("explicit_past_reference");
375
- expect(result.bullets).toHaveLength(0);
376
- expect(result.text).toBe("");
377
- });
378
-
379
- test("returns empty text for ordinary turns with no matches", () => {
380
- const result = buildArchiveRecall(
381
- "default",
382
- "Write a hello world program in Python",
383
- );
384
-
385
- expect(result.trigger).toBe("none");
386
- expect(result.bullets).toHaveLength(0);
387
- expect(result.text).toBe("");
388
- });
389
-
390
- test("does not emit <supporting_recall> when trigger fires but no data matches", () => {
391
- // Seed with unrelated data
392
- const convId = uuid();
393
- const msgId = uuid();
394
- createConversation(convId, "Cooking Tips");
395
- createMessage(msgId, convId);
396
-
397
- insertObservation({
398
- conversationId: convId,
399
- messageId: msgId,
400
- role: "user",
401
- content: "User enjoys Italian cooking with fresh basil",
402
- scopeId: "default",
403
- });
404
-
405
- // Ask about something completely unrelated
406
- const result = buildArchiveRecall(
407
- "default",
408
- "Do you remember what we discussed about Kubernetes deployments?",
409
- );
410
-
411
- expect(result.trigger).toBe("explicit_past_reference");
412
- expect(result.bullets).toHaveLength(0);
413
- expect(result.text).toBe("");
414
- });
415
- });
416
-
417
- // ─────────────────────────────────────────────────────────────────
418
- // Prefetch behavior
419
- // ─────────────────────────────────────────────────────────────────
420
-
421
- describe("prefetch", () => {
422
- test("returns hits from episodes and observations", () => {
423
- const convId = uuid();
424
- const msgId = uuid();
425
- createConversation(convId);
426
- createMessage(msgId, convId);
427
-
428
- insertObservation({
429
- conversationId: convId,
430
- messageId: msgId,
431
- role: "user",
432
- content: "User prefers TypeScript over JavaScript",
433
- scopeId: "default",
434
- });
435
-
436
- insertCompactionEpisode({
437
- scopeId: "default",
438
- conversationId: convId,
439
- title: "TypeScript Configuration",
440
- summary: "Set up strict TypeScript config with path aliases",
441
- tokenEstimate: 15,
442
- startAt: Date.now() - 3600_000,
443
- endAt: Date.now() - 1800_000,
444
- });
445
-
446
- const hits = prefetchArchive("default", "TypeScript configuration setup");
447
- expect(hits.length).toBeGreaterThan(0);
448
- expect(hits.some((h) => h.source === "episode")).toBe(true);
449
- expect(hits.some((h) => h.source === "observation")).toBe(true);
450
- });
451
-
452
- test("returns empty for no matches", () => {
453
- const hits = prefetchArchive("default", "xyzzy nonexistent topic");
454
- expect(hits).toHaveLength(0);
455
- });
456
- });
457
-
458
- // ─────────────────────────────────────────────────────────────────
459
- // Bullet cap and deduplication
460
- // ─────────────────────────────────────────────────────────────────
461
-
462
- describe("bullet cap and dedup", () => {
463
- test("returns at most 3 bullets", () => {
464
- const convId = uuid();
465
- createConversation(convId);
466
-
467
- // Insert 5 distinct observations
468
- for (let i = 0; i < 5; i++) {
469
- const msgId = uuid();
470
- createMessage(msgId, convId);
471
- insertObservation({
472
- conversationId: convId,
473
- messageId: msgId,
474
- role: "user",
475
- content: `Authentication fact number ${i}: uses OAuth2 flow variant ${i}`,
476
- scopeId: "default",
477
- });
478
- }
479
-
480
- const result = buildArchiveRecall(
481
- "default",
482
- "Do you remember what authentication method we use?",
483
- );
484
-
485
- expect(result.trigger).toBe("explicit_past_reference");
486
- expect(result.bullets.length).toBeLessThanOrEqual(3);
487
- });
488
-
489
- test("deduplicates identical content from different sources", () => {
490
- const convId = uuid();
491
- const msgId = uuid();
492
- createConversation(convId);
493
- createMessage(msgId, convId);
494
-
495
- // Insert the same content as both an observation and in an episode
496
- const content = "User prefers dark mode for all development tools";
497
- insertObservation({
498
- conversationId: convId,
499
- messageId: msgId,
500
- role: "user",
501
- content,
502
- scopeId: "default",
503
- });
504
-
505
- insertCompactionEpisode({
506
- scopeId: "default",
507
- conversationId: convId,
508
- title: "Development Preferences",
509
- summary: content,
510
- tokenEstimate: 10,
511
- startAt: Date.now() - 3600_000,
512
- endAt: Date.now() - 1800_000,
513
- });
514
-
515
- const result = buildArchiveRecall(
516
- "default",
517
- "Do you recall my preference for dark mode development tools?",
518
- );
519
-
520
- // Should have bullets but content should not be duplicated
521
- if (result.bullets.length > 1) {
522
- const texts = result.bullets.map((b) => b.text.toLowerCase());
523
- // Each bullet text should be distinct
524
- const uniqueTexts = new Set(texts);
525
- expect(uniqueTexts.size).toBe(texts.length);
526
- }
527
- });
528
- });
529
-
530
- // ─────────────────────────────────────────────────────────────────
531
- // Scope isolation
532
- // ─────────────────────────────────────────────────────────────────
533
-
534
- describe("scope isolation", () => {
535
- test("only returns results from the requested scope", () => {
536
- const convId = uuid();
537
- const msgId = uuid();
538
- createConversation(convId);
539
- createMessage(msgId, convId);
540
-
541
- insertObservation({
542
- conversationId: convId,
543
- messageId: msgId,
544
- role: "user",
545
- content: "Deployment uses Kubernetes with Helm charts",
546
- scopeId: "other-scope",
547
- });
548
-
549
- const result = buildArchiveRecall(
550
- "default",
551
- "Do you remember our Kubernetes deployment setup?",
552
- );
553
-
554
- // Should trigger but find no results in "default" scope
555
- expect(result.trigger).toBe("explicit_past_reference");
556
- expect(result.bullets).toHaveLength(0);
557
- expect(result.text).toBe("");
558
- });
559
- });
560
- });