@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
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Tests for resolveSigningKey() covering env var injection (Docker)
3
+ * and file-based load/create (local mode).
4
+ */
5
+
6
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { afterAll, afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
10
+
11
+ const testDir = realpathSync(mkdtempSync(join(tmpdir(), "signing-key-test-")));
12
+
13
+ mock.module("../util/platform.js", () => ({
14
+ getRootDir: () => testDir,
15
+ getDataDir: () => testDir,
16
+ getDbPath: () => join(testDir, "test.db"),
17
+ normalizeAssistantId: (id: string) => (id === "self" ? "self" : id),
18
+ isMacOS: () => process.platform === "darwin",
19
+ isLinux: () => process.platform === "linux",
20
+ isWindows: () => process.platform === "win32",
21
+ getPidPath: () => join(testDir, "test.pid"),
22
+ getLogPath: () => join(testDir, "test.log"),
23
+ ensureDataDir: () => {},
24
+ }));
25
+
26
+ mock.module("../util/logger.js", () => ({
27
+ getLogger: () =>
28
+ new Proxy({} as Record<string, unknown>, {
29
+ get: () => () => {},
30
+ }),
31
+ }));
32
+
33
+ const { resolveSigningKey } = await import("../runtime/auth/token-service.js");
34
+
35
+ const VALID_HEX_KEY = "ab".repeat(32); // 64 hex chars = 32 bytes
36
+
37
+ const savedEnv: Record<string, string | undefined> = {};
38
+
39
+ beforeEach(() => {
40
+ savedEnv.ACTOR_TOKEN_SIGNING_KEY = process.env.ACTOR_TOKEN_SIGNING_KEY;
41
+ mkdirSync(join(testDir, "protected"), { recursive: true });
42
+ });
43
+
44
+ afterEach(() => {
45
+ if (savedEnv.ACTOR_TOKEN_SIGNING_KEY === undefined) {
46
+ delete process.env.ACTOR_TOKEN_SIGNING_KEY;
47
+ } else {
48
+ process.env.ACTOR_TOKEN_SIGNING_KEY = savedEnv.ACTOR_TOKEN_SIGNING_KEY;
49
+ }
50
+ });
51
+
52
+ afterAll(() => {
53
+ try {
54
+ rmSync(testDir, { recursive: true, force: true });
55
+ } catch {}
56
+ });
57
+
58
+ describe("resolveSigningKey", () => {
59
+ test("reads key from ACTOR_TOKEN_SIGNING_KEY env var", () => {
60
+ process.env.ACTOR_TOKEN_SIGNING_KEY = VALID_HEX_KEY;
61
+
62
+ const key = resolveSigningKey();
63
+
64
+ expect(key).toBeInstanceOf(Buffer);
65
+ expect(key.length).toBe(32);
66
+ expect(key.toString("hex")).toBe(VALID_HEX_KEY);
67
+ });
68
+
69
+ test("rejects invalid ACTOR_TOKEN_SIGNING_KEY", () => {
70
+ process.env.ACTOR_TOKEN_SIGNING_KEY = "tooshort";
71
+
72
+ expect(() => resolveSigningKey()).toThrow("Invalid ACTOR_TOKEN_SIGNING_KEY");
73
+ });
74
+
75
+ test("falls back to file-based load/create when env var is not set", () => {
76
+ delete process.env.ACTOR_TOKEN_SIGNING_KEY;
77
+
78
+ const key = resolveSigningKey();
79
+
80
+ expect(key).toBeInstanceOf(Buffer);
81
+ expect(key.length).toBe(32);
82
+ });
83
+
84
+ test("env var takes priority over file on disk", () => {
85
+ process.env.ACTOR_TOKEN_SIGNING_KEY = VALID_HEX_KEY;
86
+
87
+ // First call creates a file-based key
88
+ delete process.env.ACTOR_TOKEN_SIGNING_KEY;
89
+ const fileKey = resolveSigningKey();
90
+
91
+ // Second call with env var should use the env var, not the file
92
+ process.env.ACTOR_TOKEN_SIGNING_KEY = "cd".repeat(32);
93
+ const envKey = resolveSigningKey();
94
+
95
+ expect(envKey.toString("hex")).toBe("cd".repeat(32));
96
+ expect(envKey.toString("hex")).not.toBe(fileKey.toString("hex"));
97
+ });
98
+ });
@@ -53,9 +53,6 @@ mock.module("../util/logger.js", () => ({
53
53
 
54
54
  mock.module("../config/loader.js", () => ({
55
55
  getConfig: () => ({
56
- assistantFeatureFlagValues: {
57
- "feature_flags.browser.enabled": true,
58
- },
59
56
  services: {
60
57
  inference: {
61
58
  mode: "your-own",
@@ -77,17 +74,22 @@ mock.module("../config/loader.js", () => ({
77
74
  invalidateConfigCache: () => {},
78
75
  getNestedValue: () => undefined,
79
76
  setNestedValue: () => {},
80
- syncConfigToLockfile: () => {},
81
77
  }));
82
78
 
83
79
  const { buildSystemPrompt } = await import("../prompts/system-prompt.js");
80
+ const { _setOverridesForTesting } =
81
+ await import("../config/assistant-feature-flags.js");
84
82
 
85
83
  describe("Dynamic Skill Authoring Workflow moved to tool descriptions", () => {
86
84
  beforeEach(() => {
87
85
  mkdirSync(TEST_DIR, { recursive: true });
86
+ _setOverridesForTesting({
87
+ "feature_flags.browser.enabled": true,
88
+ });
88
89
  });
89
90
 
90
91
  afterEach(() => {
92
+ _setOverridesForTesting({});
91
93
  if (existsSync(TEST_DIR)) {
92
94
  rmSync(TEST_DIR, { recursive: true, force: true });
93
95
  }
@@ -31,11 +31,6 @@ mock.module("../util/logger.js", () => ({
31
31
  }),
32
32
  }));
33
33
 
34
- // Mock security check to always pass
35
- mock.module("../security/secret-ingress.js", () => ({
36
- checkIngressForSecrets: () => ({ blocked: false }),
37
- }));
38
-
39
34
  import { upsertContact } from "../contacts/contact-store.js";
40
35
  import { createGuardianBinding } from "../contacts/contacts-write.js";
41
36
  import type { TrustContext } from "../daemon/conversation-runtime-assembly.js";
@@ -846,12 +846,12 @@ describe("host_bash — proxy delegation", () => {
846
846
  });
847
847
 
848
848
  test("propagates VELLUM_UNTRUSTED_SHELL env to proxy under CES lockdown", async () => {
849
- // Enable CES shell lockdown
850
- const origFlags = (mockConfig as Record<string, unknown>)
851
- .assistantFeatureFlagValues;
852
- (mockConfig as Record<string, unknown>).assistantFeatureFlagValues = {
849
+ // Enable CES shell lockdown via the override cache
850
+ const { _setOverridesForTesting } =
851
+ await import("../config/assistant-feature-flags.js");
852
+ _setOverridesForTesting({
853
853
  "feature_flags.ces-shell-lockdown.enabled": true,
854
- };
854
+ });
855
855
 
856
856
  try {
857
857
  const proxyResult: ToolExecutionResult = {
@@ -875,8 +875,7 @@ describe("host_bash — proxy delegation", () => {
875
875
  expect(calls.length).toBe(1);
876
876
  expect(calls[0].input.env).toEqual({ VELLUM_UNTRUSTED_SHELL: "1" });
877
877
  } finally {
878
- (mockConfig as Record<string, unknown>).assistantFeatureFlagValues =
879
- origFlags;
878
+ _setOverridesForTesting({});
880
879
  }
881
880
  });
882
881
 
@@ -2,10 +2,9 @@
2
2
  * Tests for HTTP POST /v1/messages behavior after the legacy handleUserMessage
3
3
  * legacy entry point was retired.
4
4
  *
5
- * Secret ingress blocking has been ported to the HTTP path. Recording intent
6
- * interception has been deliberately retired the HTTP path has dedicated
7
- * /v1/recording/* endpoints and the model handles recording-related messages
8
- * through the agent loop.
5
+ * Recording intent interception has been deliberately retired the HTTP path
6
+ * has dedicated /v1/recording/* endpoints and the model handles
7
+ * recording-related messages through the agent loop.
9
8
  *
10
9
  * Approval reply interception has parity and is covered by
11
10
  * conversation-routes-guardian-reply.test.ts and send-endpoint-busy.test.ts.
@@ -121,12 +120,10 @@ mock.module("../runtime/trust-context-resolver.js", () => ({
121
120
  }),
122
121
  }));
123
122
 
124
- // Mock config to enable secret detection + ingress blocking
125
123
  mock.module("../config/loader.js", () => ({
126
124
  getConfig: () => ({
127
125
  secretDetection: {
128
126
  enabled: true,
129
- blockIngress: true,
130
127
  customPatterns: [],
131
128
  entropyThreshold: 3.5,
132
129
  },
@@ -238,103 +235,6 @@ async function sendMessage(
238
235
  );
239
236
  }
240
237
 
241
- // ============================================================================
242
- // SECRET INGRESS BLOCKING — now ported to HTTP path
243
- // ============================================================================
244
- describe("HTTP POST /v1/messages blocks secret ingress", () => {
245
- beforeEach(() => {
246
- routeGuardianReplyMock.mockClear();
247
- listPendingByDestinationMock.mockClear();
248
- listCanonicalMock.mockClear();
249
- addMessageMock.mockClear();
250
- });
251
-
252
- test("handleSendMessage rejects messages containing Telegram bot token patterns", async () => {
253
- const secretContent =
254
- "Set up Telegram with my bot token 123456789:ABCDefGHIJklmnopQRSTuvwxyz012345678";
255
- const persistUserMessage = mock(async () => "persisted-msg-id");
256
- const runAgentLoop = mock(async () => undefined);
257
- const conversation = makeConversation({ persistUserMessage, runAgentLoop });
258
-
259
- const res = await sendMessage(secretContent, conversation);
260
-
261
- expect(res.status).toBe(422);
262
- const body = (await res.json()) as {
263
- accepted: boolean;
264
- error: string;
265
- message: string;
266
- detectedTypes: string[];
267
- };
268
- expect(body.accepted).toBe(false);
269
- expect(body.error).toBe("secret_blocked");
270
- expect(body.detectedTypes.length).toBeGreaterThan(0);
271
-
272
- // The message should NOT reach the agent loop
273
- expect(persistUserMessage).toHaveBeenCalledTimes(0);
274
- expect(runAgentLoop).toHaveBeenCalledTimes(0);
275
- });
276
-
277
- test("handleSendMessage rejects messages containing AWS credentials", async () => {
278
- const secretContent =
279
- "Here is my AWS key AKIAQRSTUVWXYZ123456 and secret wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
280
- const persistUserMessage = mock(async () => "persisted-msg-id");
281
- const runAgentLoop = mock(async () => undefined);
282
- const conversation = makeConversation({ persistUserMessage, runAgentLoop });
283
-
284
- const res = await sendMessage(secretContent, conversation);
285
-
286
- expect(res.status).toBe(422);
287
- const body = (await res.json()) as {
288
- accepted: boolean;
289
- error: string;
290
- };
291
- expect(body.accepted).toBe(false);
292
- expect(body.error).toBe("secret_blocked");
293
-
294
- // The message should NOT reach the agent loop
295
- expect(persistUserMessage).toHaveBeenCalledTimes(0);
296
- expect(runAgentLoop).toHaveBeenCalledTimes(0);
297
- });
298
-
299
- test("handleSendMessage rejects messages containing Stripe live API keys", async () => {
300
- const secretContent = "My Stripe key is sk_live_4eC39HqLyjWDarjtT1zdp7dc";
301
- const persistUserMessage = mock(async () => "persisted-msg-id");
302
- const runAgentLoop = mock(async () => undefined);
303
- const conversation = makeConversation({ persistUserMessage, runAgentLoop });
304
-
305
- const res = await sendMessage(secretContent, conversation);
306
-
307
- expect(res.status).toBe(422);
308
- const body = (await res.json()) as {
309
- accepted: boolean;
310
- error: string;
311
- };
312
- expect(body.accepted).toBe(false);
313
- expect(body.error).toBe("secret_blocked");
314
-
315
- // The message should NOT reach the agent loop
316
- expect(persistUserMessage).toHaveBeenCalledTimes(0);
317
- expect(runAgentLoop).toHaveBeenCalledTimes(0);
318
- });
319
-
320
- test("handleSendMessage allows normal messages without secrets", async () => {
321
- const normalContent = "What is the weather today?";
322
- const persistUserMessage = mock(async () => "persisted-msg-id");
323
- const runAgentLoop = mock(async () => undefined);
324
- const conversation = makeConversation({ persistUserMessage, runAgentLoop });
325
-
326
- const res = await sendMessage(normalContent, conversation);
327
-
328
- expect(res.status).toBe(202);
329
- const body = (await res.json()) as { accepted: boolean };
330
- expect(body.accepted).toBe(true);
331
-
332
- // Normal messages proceed to the agent loop
333
- expect(persistUserMessage).toHaveBeenCalledTimes(1);
334
- expect(runAgentLoop).toHaveBeenCalledTimes(1);
335
- });
336
- });
337
-
338
238
  // ============================================================================
339
239
  // RECORDING INTENT — deliberately NOT intercepted on HTTP path
340
240
  // ============================================================================
@@ -35,10 +35,6 @@ mock.module("../util/logger.js", () => ({
35
35
  }),
36
36
  }));
37
37
 
38
- mock.module("../security/secret-ingress.js", () => ({
39
- checkIngressForSecrets: () => ({ blocked: false }),
40
- }));
41
-
42
38
  mock.module("../config/env.js", () => ({
43
39
  isHttpAuthDisabled: () => true,
44
40
  getGatewayInternalBaseUrl: () => "http://127.0.0.1:7830",
@@ -18,6 +18,8 @@ import { tmpdir } from "node:os";
18
18
  import { join } from "node:path";
19
19
  import { beforeEach, describe, expect, mock, test } from "bun:test";
20
20
 
21
+ import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
22
+
21
23
  // ── Mock setup (must be before any imports from the project) ──────────────
22
24
 
23
25
  const testDir = mkdtempSync(join(tmpdir(), "inline-skill-perm-test-"));
@@ -46,7 +48,6 @@ interface TestConfig {
46
48
  permissions: { mode: "strict" | "workspace" };
47
49
  skills: { load: { extraDirs: string[] } };
48
50
  sandbox: { enabled: boolean };
49
- assistantFeatureFlagValues?: Record<string, boolean>;
50
51
  [key: string]: unknown;
51
52
  }
52
53
 
@@ -54,9 +55,6 @@ const testConfig: TestConfig = {
54
55
  permissions: { mode: "workspace" },
55
56
  skills: { load: { extraDirs: [] } },
56
57
  sandbox: { enabled: true },
57
- assistantFeatureFlagValues: {
58
- "feature_flags.inline-skill-commands.enabled": true,
59
- },
60
58
  };
61
59
 
62
60
  mock.module("../config/loader.js", () => ({
@@ -118,9 +116,9 @@ describe("inline-command skill_load permissions", () => {
118
116
  clearCache();
119
117
  testConfig.permissions = { mode: "workspace" };
120
118
  testConfig.skills = { load: { extraDirs: [] } };
121
- testConfig.assistantFeatureFlagValues = {
119
+ _setOverridesForTesting({
122
120
  "feature_flags.inline-skill-commands.enabled": true,
123
- };
121
+ });
124
122
  try {
125
123
  rmSync(join(testDir, "protected", "trust.json"));
126
124
  } catch {
@@ -352,9 +350,9 @@ describe("inline-command skill_load permissions", () => {
352
350
  writeDynamicSkill("dynamic-flag-off", "Dynamic Flag Off Skill");
353
351
 
354
352
  // Disable the feature flag
355
- testConfig.assistantFeatureFlagValues = {
353
+ _setOverridesForTesting({
356
354
  "feature_flags.inline-skill-commands.enabled": false,
357
- };
355
+ });
358
356
 
359
357
  const result = await check(
360
358
  "skill_load",
@@ -53,8 +53,6 @@ mock.module("../util/logger.js", () => ({
53
53
  mock.module("../config/loader.js", () => ({
54
54
  getConfig: () => ({
55
55
  ui: {},
56
-
57
- assistantFeatureFlagValues: {},
58
56
  services: {
59
57
  inference: {
60
58
  mode: "your-own",
@@ -76,7 +74,6 @@ mock.module("../config/loader.js", () => ({
76
74
  invalidateConfigCache: () => {},
77
75
  getNestedValue: () => undefined,
78
76
  setNestedValue: () => {},
79
- syncConfigToLockfile: () => {},
80
77
  }));
81
78
 
82
79
  // ── Import after mocks ───────────────────────────────────────────────
@@ -250,16 +247,6 @@ describe("Activation hints in skills catalog", () => {
250
247
  expect(line).toContain("voice-setup");
251
248
  });
252
249
 
253
- test("orchestration skill includes hints and avoid-when in catalog line", () => {
254
- const prompt = buildSystemPrompt();
255
- const line = prompt
256
- .split("\n")
257
- .find((l) => l.includes("**orchestration**"));
258
- expect(line).toBeDefined();
259
- expect(line).toContain("parallel");
260
- expect(line).toContain("Single-focus");
261
- });
262
-
263
250
  test("browser skill includes hints in catalog line", () => {
264
251
  const prompt = buildSystemPrompt();
265
252
  const line = prompt.split("\n").find((l) => l.includes("**browser**"));
@@ -0,0 +1,178 @@
1
+ import { mkdtempSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import {
5
+ afterAll,
6
+ beforeAll,
7
+ beforeEach,
8
+ describe,
9
+ expect,
10
+ mock,
11
+ test,
12
+ } from "bun:test";
13
+
14
+ const testDir = mkdtempSync(join(tmpdir(), "jobs-store-qdrant-breaker-"));
15
+
16
+ mock.module("../util/platform.js", () => ({
17
+ getDataDir: () => testDir,
18
+ isMacOS: () => process.platform === "darwin",
19
+ isLinux: () => process.platform === "linux",
20
+ isWindows: () => process.platform === "win32",
21
+ getPidPath: () => join(testDir, "test.pid"),
22
+ getDbPath: () => join(testDir, "test.db"),
23
+ getLogPath: () => join(testDir, "test.log"),
24
+ ensureDataDir: () => {},
25
+ }));
26
+
27
+ mock.module("../util/logger.js", () => ({
28
+ getLogger: () =>
29
+ new Proxy({} as Record<string, unknown>, {
30
+ get: () => () => {},
31
+ }),
32
+ }));
33
+
34
+ mock.module("../config/loader.js", () => ({
35
+ loadConfig: () => ({}),
36
+ getConfig: () => ({}),
37
+ invalidateConfigCache: () => {},
38
+ }));
39
+
40
+ import { getDb, initializeDb, resetDb } from "../memory/db.js";
41
+ import {
42
+ claimMemoryJobs,
43
+ enqueueMemoryJob,
44
+ type MemoryJobType,
45
+ } from "../memory/jobs-store.js";
46
+ import {
47
+ _resetQdrantBreaker,
48
+ withQdrantBreaker,
49
+ } from "../memory/qdrant-circuit-breaker.js";
50
+
51
+ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
52
+ beforeAll(() => {
53
+ initializeDb();
54
+ });
55
+
56
+ beforeEach(() => {
57
+ const db = getDb();
58
+ db.run("DELETE FROM memory_jobs");
59
+ _resetQdrantBreaker();
60
+ });
61
+
62
+ afterAll(() => {
63
+ resetDb();
64
+ rmSync(testDir, { recursive: true, force: true });
65
+ });
66
+
67
+ test("claims embed jobs when circuit breaker is closed (healthy)", () => {
68
+ enqueueMemoryJob("embed_segment", { segmentId: "seg-1" });
69
+ enqueueMemoryJob("embed_item", { itemId: "item-1" });
70
+ enqueueMemoryJob("extract_items", { conversationId: "conv-1" });
71
+
72
+ const claimed = claimMemoryJobs(10);
73
+ const types = claimed.map((j) => j.type);
74
+
75
+ expect(types).toContain("embed_segment");
76
+ expect(types).toContain("embed_item");
77
+ expect(types).toContain("extract_items");
78
+ expect(claimed).toHaveLength(3);
79
+ });
80
+
81
+ test("skips embed jobs when circuit breaker is open", async () => {
82
+ // Trip the circuit breaker by recording 5 consecutive failures
83
+ for (let i = 0; i < 5; i++) {
84
+ try {
85
+ await withQdrantBreaker(async () => {
86
+ throw new Error("simulated qdrant failure");
87
+ });
88
+ } catch {
89
+ // expected
90
+ }
91
+ }
92
+
93
+ enqueueMemoryJob("embed_segment", { segmentId: "seg-1" });
94
+ enqueueMemoryJob("embed_item", { itemId: "item-1" });
95
+ enqueueMemoryJob("embed_summary", { summaryId: "sum-1" });
96
+ enqueueMemoryJob("extract_items", { conversationId: "conv-1" });
97
+ enqueueMemoryJob("build_conversation_summary", {
98
+ conversationId: "conv-1",
99
+ });
100
+
101
+ const claimed = claimMemoryJobs(10);
102
+ const types = claimed.map((j) => j.type);
103
+
104
+ // Only non-embed jobs should be claimed
105
+ expect(types).toContain("extract_items");
106
+ expect(types).toContain("build_conversation_summary");
107
+ expect(types).not.toContain("embed_segment");
108
+ expect(types).not.toContain("embed_item");
109
+ expect(types).not.toContain("embed_summary");
110
+ expect(claimed).toHaveLength(2);
111
+ });
112
+
113
+ test("resumes claiming embed jobs after circuit breaker closes", async () => {
114
+ // Trip the circuit breaker
115
+ for (let i = 0; i < 5; i++) {
116
+ try {
117
+ await withQdrantBreaker(async () => {
118
+ throw new Error("simulated qdrant failure");
119
+ });
120
+ } catch {
121
+ // expected
122
+ }
123
+ }
124
+
125
+ // Verify embed jobs are skipped while open
126
+ enqueueMemoryJob("embed_segment", { segmentId: "seg-1" });
127
+ enqueueMemoryJob("extract_items", { conversationId: "conv-1" });
128
+
129
+ const claimedWhileOpen = claimMemoryJobs(10);
130
+ expect(claimedWhileOpen.map((j) => j.type)).not.toContain("embed_segment");
131
+
132
+ // Reset breaker (simulates successful probe closing the circuit)
133
+ _resetQdrantBreaker();
134
+
135
+ // Re-enqueue an embed job (the previous one is now "running")
136
+ enqueueMemoryJob("embed_item", { itemId: "item-2" });
137
+
138
+ const claimedAfterClose = claimMemoryJobs(10);
139
+ const types = claimedAfterClose.map((j) => j.type);
140
+
141
+ expect(types).toContain("embed_item");
142
+ });
143
+
144
+ test("all embed job types are skipped when breaker is open", async () => {
145
+ const embedTypes: MemoryJobType[] = [
146
+ "embed_segment",
147
+ "embed_item",
148
+ "embed_summary",
149
+ "embed_media",
150
+ "embed_attachment",
151
+ ];
152
+
153
+ // Trip the circuit breaker
154
+ for (let i = 0; i < 5; i++) {
155
+ try {
156
+ await withQdrantBreaker(async () => {
157
+ throw new Error("simulated qdrant failure");
158
+ });
159
+ } catch {
160
+ // expected
161
+ }
162
+ }
163
+
164
+ // Enqueue one of each embed type
165
+ for (const type of embedTypes) {
166
+ enqueueMemoryJob(type, { id: `test-${type}` });
167
+ }
168
+ // Also enqueue a non-embed job
169
+ enqueueMemoryJob("extract_entities", { conversationId: "conv-1" });
170
+
171
+ const claimed = claimMemoryJobs(20);
172
+ const types = claimed.map((j) => j.type);
173
+
174
+ // Only the non-embed job should be claimed
175
+ expect(claimed).toHaveLength(1);
176
+ expect(types).toEqual(["extract_entities"]);
177
+ });
178
+ });