@vellumai/assistant 0.5.6 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +3 -2
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docker-entrypoint.sh +9 -0
  7. package/docs/architecture/keychain-broker.md +45 -240
  8. package/docs/architecture/memory.md +13 -11
  9. package/docs/architecture/security.md +0 -17
  10. package/docs/credential-execution-service.md +2 -2
  11. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-contracts/src/error.ts +1 -1
  13. package/node_modules/@vellumai/ces-contracts/src/grants.ts +1 -1
  14. package/node_modules/@vellumai/ces-contracts/src/handles.ts +1 -1
  15. package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -1
  16. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +120 -1
  17. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  18. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  19. package/package.json +2 -3
  20. package/src/__tests__/actor-token-service.test.ts +0 -114
  21. package/src/__tests__/approval-cascade.test.ts +0 -1
  22. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  23. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  24. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  25. package/src/__tests__/btw-routes.test.ts +0 -39
  26. package/src/__tests__/call-controller.test.ts +0 -1
  27. package/src/__tests__/call-domain.test.ts +0 -128
  28. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  29. package/src/__tests__/ces-startup-timeout.test.ts +40 -0
  30. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  31. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  32. package/src/__tests__/checker.test.ts +4 -2
  33. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  34. package/src/__tests__/config-schema-cmd.test.ts +0 -2
  35. package/src/__tests__/config-schema.test.ts +3 -1
  36. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -1
  37. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
  38. package/src/__tests__/conversation-agent-loop.test.ts +2 -4
  39. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  40. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -1
  41. package/src/__tests__/conversation-error.test.ts +15 -1
  42. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  43. package/src/__tests__/conversation-messaging-secret-redirect.test.ts +1 -1
  44. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -1
  45. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -1
  46. package/src/__tests__/conversation-queue.test.ts +0 -1
  47. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  48. package/src/__tests__/conversation-slash-queue.test.ts +0 -1
  49. package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
  50. package/src/__tests__/conversation-title-service.test.ts +87 -0
  51. package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
  52. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
  53. package/src/__tests__/credential-execution-client.test.ts +5 -2
  54. package/src/__tests__/credential-execution-feature-gates.test.ts +59 -30
  55. package/src/__tests__/credential-execution-managed-contract.test.ts +35 -20
  56. package/src/__tests__/credential-security-e2e.test.ts +1 -67
  57. package/src/__tests__/credential-security-invariants.test.ts +6 -50
  58. package/src/__tests__/credentials-cli.test.ts +82 -3
  59. package/src/__tests__/daemon-credential-client.test.ts +123 -0
  60. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  61. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -0
  62. package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
  63. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  64. package/src/__tests__/gateway-client-managed-outbound.test.ts +79 -1
  65. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  66. package/src/__tests__/host-shell-tool.test.ts +6 -7
  67. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  68. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  69. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  70. package/src/__tests__/intent-routing.test.ts +0 -13
  71. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  72. package/src/__tests__/journal-context.test.ts +335 -0
  73. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  74. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -3
  75. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  76. package/src/__tests__/memory-lifecycle-e2e.test.ts +70 -25
  77. package/src/__tests__/memory-recall-quality.test.ts +48 -17
  78. package/src/__tests__/memory-regressions.test.ts +408 -363
  79. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -3
  80. package/src/__tests__/migration-export-http.test.ts +2 -2
  81. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  82. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  83. package/src/__tests__/migration-validate-http.test.ts +2 -2
  84. package/src/__tests__/non-member-access-request.test.ts +2 -7
  85. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  86. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  87. package/src/__tests__/notification-decision-strategy.test.ts +71 -0
  88. package/src/__tests__/oauth-cli.test.ts +5 -1
  89. package/src/__tests__/permission-types.test.ts +1 -0
  90. package/src/__tests__/provider-commit-message-generator.test.ts +0 -37
  91. package/src/__tests__/provider-error-scenarios.test.ts +0 -267
  92. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  93. package/src/__tests__/provider-streaming.benchmark.test.ts +2 -81
  94. package/src/__tests__/qdrant-manager.test.ts +28 -2
  95. package/src/__tests__/registry.test.ts +0 -6
  96. package/src/__tests__/relay-server.test.ts +1 -2
  97. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  98. package/src/__tests__/script-proxy-injection-runtime.test.ts +1 -1
  99. package/src/__tests__/secret-onetime-send.test.ts +1 -1
  100. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  101. package/src/__tests__/secure-keys.test.ts +95 -272
  102. package/src/__tests__/shell-identity.test.ts +96 -6
  103. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  104. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  105. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  106. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  107. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  108. package/src/__tests__/skill-load-tool.test.ts +0 -2
  109. package/src/__tests__/skill-memory.test.ts +17 -3
  110. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  111. package/src/__tests__/skills.test.ts +0 -2
  112. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  113. package/src/__tests__/stale-approval-dedup.test.ts +171 -0
  114. package/src/__tests__/stt-hints.test.ts +437 -0
  115. package/src/__tests__/suggestion-routes.test.ts +1 -32
  116. package/src/__tests__/system-prompt.test.ts +0 -1
  117. package/src/__tests__/task-memory-cleanup.test.ts +14 -0
  118. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  119. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  120. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  121. package/src/__tests__/twilio-routes-twiml.test.ts +139 -1
  122. package/src/__tests__/update-bulletin.test.ts +0 -2
  123. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  124. package/src/__tests__/voice-quality.test.ts +58 -0
  125. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -7
  126. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  127. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +220 -0
  128. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  129. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  130. package/src/acp/agent-process.ts +9 -1
  131. package/src/agent/loop.ts +1 -1
  132. package/src/approvals/guardian-request-resolvers.ts +164 -38
  133. package/src/calls/__tests__/tts-text-sanitizer.test.ts +254 -0
  134. package/src/calls/audio-store.test.ts +97 -0
  135. package/src/calls/audio-store.ts +205 -0
  136. package/src/calls/call-controller.ts +90 -8
  137. package/src/calls/call-domain.ts +3 -0
  138. package/src/calls/call-store.ts +10 -3
  139. package/src/calls/fish-audio-client.ts +129 -0
  140. package/src/calls/relay-server.ts +27 -0
  141. package/src/calls/stt-hints.ts +189 -0
  142. package/src/calls/tts-text-sanitizer.ts +61 -0
  143. package/src/calls/twilio-routes.ts +34 -5
  144. package/src/calls/types.ts +1 -0
  145. package/src/calls/voice-ingress-preflight.ts +0 -42
  146. package/src/calls/voice-quality.ts +38 -5
  147. package/src/calls/voice-session-bridge.ts +7 -12
  148. package/src/cli/commands/avatar.ts +2 -2
  149. package/src/cli/commands/config.ts +1 -4
  150. package/src/cli/commands/credentials.ts +128 -82
  151. package/src/cli/commands/doctor.ts +2 -2
  152. package/src/cli/commands/keys.ts +7 -7
  153. package/src/cli/commands/memory.ts +1 -1
  154. package/src/cli/commands/oauth/connections.ts +11 -29
  155. package/src/cli/commands/oauth/index.ts +7 -0
  156. package/src/cli/commands/oauth/platform.ts +525 -0
  157. package/src/cli/commands/platform.ts +3 -3
  158. package/src/cli/lib/daemon-credential-client.ts +284 -0
  159. package/src/cli.ts +1 -1
  160. package/src/config/assistant-feature-flags.ts +186 -5
  161. package/src/config/bundled-skills/AGENTS.md +34 -0
  162. package/src/config/bundled-skills/acp/SKILL.md +10 -0
  163. package/src/config/bundled-skills/app-builder/SKILL.md +0 -4
  164. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  165. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  166. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  167. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -0
  168. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -0
  169. package/src/config/bundled-skills/settings/SKILL.md +15 -2
  170. package/src/config/bundled-skills/settings/TOOLS.json +47 -2
  171. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +59 -0
  172. package/src/config/bundled-skills/settings/tools/avatar-update.ts +80 -0
  173. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  174. package/src/config/bundled-skills/slack/SKILL.md +1 -1
  175. package/src/config/bundled-tool-registry.ts +5 -11
  176. package/src/config/defaults.ts +0 -2
  177. package/src/config/env-registry.ts +5 -5
  178. package/src/config/env.ts +21 -14
  179. package/src/config/feature-flag-registry.json +49 -9
  180. package/src/config/loader.ts +106 -42
  181. package/src/config/schema.ts +9 -29
  182. package/src/config/schemas/calls.ts +30 -0
  183. package/src/config/schemas/fish-audio.ts +39 -0
  184. package/src/config/schemas/inference.ts +2 -2
  185. package/src/config/schemas/journal.ts +16 -0
  186. package/src/config/schemas/memory-processing.ts +2 -2
  187. package/src/config/schemas/security.ts +0 -4
  188. package/src/config/types.ts +1 -1
  189. package/src/contacts/contact-store.ts +39 -0
  190. package/src/contacts/types.ts +2 -0
  191. package/src/credential-execution/approval-bridge.ts +1 -0
  192. package/src/credential-execution/executable-discovery.ts +28 -4
  193. package/src/credential-execution/feature-gates.ts +16 -0
  194. package/src/credential-execution/process-manager.ts +38 -0
  195. package/src/credential-execution/startup-timeout.ts +36 -0
  196. package/src/daemon/approval-generators.ts +3 -9
  197. package/src/daemon/assistant-attachments.ts +9 -0
  198. package/src/daemon/config-watcher.ts +5 -0
  199. package/src/daemon/conversation-error.ts +13 -1
  200. package/src/daemon/conversation-memory.ts +1 -2
  201. package/src/daemon/conversation-process.ts +18 -1
  202. package/src/daemon/conversation-surfaces.ts +30 -1
  203. package/src/daemon/conversation-tool-setup.ts +0 -105
  204. package/src/daemon/conversation.ts +21 -1
  205. package/src/daemon/guardian-action-generators.ts +3 -9
  206. package/src/daemon/handlers/config-vercel.ts +92 -0
  207. package/src/daemon/handlers/skills.ts +2 -15
  208. package/src/daemon/install-symlink.ts +195 -0
  209. package/src/daemon/lifecycle.ts +234 -51
  210. package/src/daemon/message-types/conversations.ts +4 -4
  211. package/src/daemon/message-types/diagnostics.ts +3 -22
  212. package/src/daemon/message-types/messages.ts +0 -2
  213. package/src/daemon/message-types/upgrades.ts +8 -0
  214. package/src/daemon/server.ts +32 -95
  215. package/src/events/domain-events.ts +2 -1
  216. package/src/inbound/platform-callback-registration.ts +3 -3
  217. package/src/instrument.ts +8 -5
  218. package/src/memory/app-store.ts +31 -0
  219. package/src/memory/conversation-title-service.ts +50 -1
  220. package/src/memory/db-init.ts +16 -0
  221. package/src/memory/indexer.ts +19 -10
  222. package/src/memory/items-extractor.ts +328 -321
  223. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  224. package/src/memory/job-handlers/summarization.ts +26 -16
  225. package/src/memory/jobs-store.ts +63 -6
  226. package/src/memory/jobs-worker.ts +31 -7
  227. package/src/memory/journal-memory.ts +214 -0
  228. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  229. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  230. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  231. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  232. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  233. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  234. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  235. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  236. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  237. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  238. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  239. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  240. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  241. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  242. package/src/memory/migrations/116-messages-fts.ts +106 -1
  243. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  244. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  245. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  246. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  247. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  248. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  249. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  250. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  251. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  252. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  253. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  254. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  255. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  256. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  257. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  258. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  259. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  260. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  261. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  262. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  263. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  264. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  265. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  266. package/src/memory/migrations/193-add-source-type-columns.ts +81 -0
  267. package/src/memory/migrations/index.ts +5 -0
  268. package/src/memory/migrations/registry.ts +98 -0
  269. package/src/memory/migrations/validate-migration-state.ts +137 -11
  270. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  271. package/src/memory/qdrant-manager.ts +64 -7
  272. package/src/memory/retriever.test.ts +37 -25
  273. package/src/memory/retriever.ts +24 -49
  274. package/src/memory/schema/calls.ts +1 -0
  275. package/src/memory/schema/contacts.ts +1 -0
  276. package/src/memory/schema/memory-core.ts +2 -0
  277. package/src/memory/search/formatting.ts +7 -44
  278. package/src/memory/search/staleness.ts +4 -0
  279. package/src/memory/search/tier-classifier.ts +10 -2
  280. package/src/memory/search/types.ts +2 -5
  281. package/src/memory/task-memory-cleanup.ts +4 -3
  282. package/src/notifications/adapters/slack.ts +168 -6
  283. package/src/notifications/broadcaster.ts +1 -0
  284. package/src/notifications/copy-composer.ts +59 -2
  285. package/src/notifications/decision-engine.ts +4 -1
  286. package/src/notifications/signal.ts +2 -0
  287. package/src/notifications/types.ts +2 -0
  288. package/src/oauth/connection-resolver.ts +6 -4
  289. package/src/permissions/checker.ts +0 -38
  290. package/src/permissions/shell-identity.ts +76 -22
  291. package/src/permissions/types.ts +4 -2
  292. package/src/platform/client.ts +35 -7
  293. package/src/prompts/journal-context.ts +133 -0
  294. package/src/prompts/persona-resolver.ts +194 -0
  295. package/src/prompts/system-prompt.ts +44 -4
  296. package/src/prompts/templates/SOUL.md +10 -0
  297. package/src/prompts/templates/users/default.md +1 -0
  298. package/src/providers/provider-send-message.ts +3 -32
  299. package/src/providers/registry.ts +29 -179
  300. package/src/providers/types.ts +1 -1
  301. package/src/runtime/access-request-helper.ts +4 -0
  302. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  303. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  304. package/src/runtime/auth/__tests__/guard-tests.test.ts +9 -50
  305. package/src/runtime/auth/external-assistant-id.ts +13 -59
  306. package/src/runtime/auth/route-policy.ts +17 -1
  307. package/src/runtime/auth/token-service.ts +43 -138
  308. package/src/runtime/channel-readiness-service.ts +1 -16
  309. package/src/runtime/gateway-client.ts +47 -4
  310. package/src/runtime/guardian-decision-types.ts +45 -4
  311. package/src/runtime/http-server.ts +31 -3
  312. package/src/runtime/middleware/error-handler.ts +1 -9
  313. package/src/runtime/routes/access-request-decision.ts +2 -2
  314. package/src/runtime/routes/app-management-routes.ts +2 -1
  315. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +219 -30
  316. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +37 -14
  317. package/src/runtime/routes/audio-routes.ts +40 -0
  318. package/src/runtime/routes/btw-routes.ts +0 -17
  319. package/src/runtime/routes/channel-readiness-routes.ts +9 -4
  320. package/src/runtime/routes/conversation-query-routes.ts +63 -1
  321. package/src/runtime/routes/conversation-routes.ts +4 -44
  322. package/src/runtime/routes/debug-routes.ts +12 -9
  323. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  324. package/src/runtime/routes/guardian-approval-interception.ts +168 -11
  325. package/src/runtime/routes/guardian-approval-prompt.ts +6 -1
  326. package/src/runtime/routes/guardian-approval-reply-helpers.ts +103 -21
  327. package/src/runtime/routes/identity-routes.ts +19 -30
  328. package/src/runtime/routes/inbound-message-handler.ts +31 -1
  329. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +64 -5
  330. package/src/runtime/routes/inbound-stages/background-dispatch.ts +52 -40
  331. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  332. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  333. package/src/runtime/routes/integrations/twilio.ts +52 -10
  334. package/src/runtime/routes/integrations/vercel.ts +89 -0
  335. package/src/runtime/routes/log-export-routes.ts +5 -0
  336. package/src/runtime/routes/memory-item-routes.test.ts +3 -3
  337. package/src/runtime/routes/memory-item-routes.ts +46 -14
  338. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  339. package/src/runtime/routes/migration-routes.ts +17 -1
  340. package/src/runtime/routes/notification-routes.ts +58 -0
  341. package/src/runtime/routes/schedule-routes.ts +65 -0
  342. package/src/runtime/routes/secret-routes.ts +141 -10
  343. package/src/runtime/routes/settings-routes.ts +41 -1
  344. package/src/runtime/routes/tts-routes.ts +96 -0
  345. package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
  346. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  347. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  348. package/src/runtime/routes/workspace-routes.ts +1 -1
  349. package/src/runtime/routes/workspace-utils.ts +86 -2
  350. package/src/security/ces-credential-client.ts +75 -29
  351. package/src/security/ces-rpc-credential-backend.ts +86 -0
  352. package/src/security/credential-backend.ts +22 -92
  353. package/src/security/keychain-broker-client.ts +10 -2
  354. package/src/security/secure-keys.ts +113 -115
  355. package/src/skills/catalog-install.ts +6 -32
  356. package/src/skills/skill-memory.ts +1 -0
  357. package/src/subagent/manager.ts +2 -5
  358. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  359. package/src/tools/acp/spawn.ts +78 -1
  360. package/src/tools/calls/call-start.ts +1 -0
  361. package/src/tools/credentials/vault.ts +5 -3
  362. package/src/tools/executor.ts +0 -4
  363. package/src/tools/memory/definitions.ts +3 -2
  364. package/src/tools/memory/handlers.ts +10 -7
  365. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  366. package/src/tools/network/web-fetch.ts +3 -1
  367. package/src/tools/skills/execute.ts +1 -1
  368. package/src/tools/terminal/safe-env.ts +1 -0
  369. package/src/tools/types.ts +0 -8
  370. package/src/util/browser.ts +15 -0
  371. package/src/util/errors.ts +0 -12
  372. package/src/util/platform.ts +4 -51
  373. package/src/workspace/git-service.ts +5 -2
  374. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  375. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  376. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  377. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  378. package/src/workspace/migrations/006-services-config.ts +49 -0
  379. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  380. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  381. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  382. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  383. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  384. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  385. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  386. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  387. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  388. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  389. package/src/workspace/migrations/017-seed-persona-dirs.ts +96 -0
  390. package/src/workspace/migrations/018-rekey-compound-credential-keys.ts +184 -0
  391. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +103 -0
  392. package/src/workspace/migrations/migrate-to-workspace-volume.ts +27 -5
  393. package/src/workspace/migrations/registry.ts +12 -0
  394. package/src/workspace/migrations/runner.ts +106 -2
  395. package/src/workspace/migrations/types.ts +4 -0
  396. package/src/workspace/provider-commit-message-generator.ts +12 -21
  397. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  398. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  399. package/src/__tests__/diagnostics-export.test.ts +0 -288
  400. package/src/__tests__/local-gateway-health.test.ts +0 -209
  401. package/src/__tests__/provider-fail-open-selection.test.ts +0 -271
  402. package/src/__tests__/provider-failover-actual-provider.test.ts +0 -66
  403. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  404. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  405. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  406. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  407. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  408. package/src/__tests__/swarm-recursion.test.ts +0 -197
  409. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  410. package/src/__tests__/swarm-tool.test.ts +0 -185
  411. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  412. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  413. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  414. package/src/commands/cc-command-registry.ts +0 -248
  415. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  416. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  417. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  418. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  419. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  420. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  421. package/src/config/schemas/swarm.ts +0 -82
  422. package/src/logfire.ts +0 -135
  423. package/src/memory/search/lexical.ts +0 -48
  424. package/src/providers/failover.ts +0 -186
  425. package/src/runtime/local-gateway-health.ts +0 -275
  426. package/src/security/secret-ingress.ts +0 -68
  427. package/src/swarm/backend-claude-code.ts +0 -225
  428. package/src/swarm/checkpoint.ts +0 -137
  429. package/src/swarm/graph-utils.ts +0 -53
  430. package/src/swarm/index.ts +0 -55
  431. package/src/swarm/limits.ts +0 -66
  432. package/src/swarm/orchestrator.ts +0 -424
  433. package/src/swarm/plan-validator.ts +0 -117
  434. package/src/swarm/router-planner.ts +0 -162
  435. package/src/swarm/router-prompts.ts +0 -39
  436. package/src/swarm/synthesizer.ts +0 -81
  437. package/src/swarm/types.ts +0 -72
  438. package/src/swarm/worker-backend.ts +0 -131
  439. package/src/swarm/worker-prompts.ts +0 -80
  440. package/src/swarm/worker-runner.ts +0 -170
  441. package/src/tools/claude-code/claude-code.ts +0 -610
  442. package/src/tools/swarm/delegate.ts +0 -205
@@ -537,8 +537,8 @@ describe("keychain-broker-client", () => {
537
537
  writeFileSync(SOCKET_PATH, "");
538
538
  expect(client.isAvailable()).toBe(false);
539
539
 
540
- // Advance time past the first cooldown (30s)
541
- fakeNow += 30_001;
540
+ // Advance time past the first cooldown (5s)
541
+ fakeNow += 5_001;
542
542
  expect(client.isAvailable()).toBe(true);
543
543
 
544
544
  // Now start a real broker and verify the client reconnects
@@ -558,8 +558,8 @@ describe("keychain-broker-client", () => {
558
558
  const client = createBrokerClient();
559
559
  await client.ping();
560
560
 
561
- // Advance past first cooldown (30s)
562
- fakeNow += 30_001;
561
+ // Advance past first cooldown (5s)
562
+ fakeNow += 5_001;
563
563
 
564
564
  // Start broker — reconnection should succeed and reset counters
565
565
  const broker = createMockBroker();
@@ -578,15 +578,15 @@ describe("keychain-broker-client", () => {
578
578
  rmSync(SOCKET_PATH, { force: true });
579
579
 
580
580
  // This new failure should start from the beginning of the cooldown
581
- // schedule (30s), not escalated.
581
+ // schedule (5s), not escalated.
582
582
  await client.ping();
583
583
 
584
- // Verify cooldown is back to 30s (not 60s)
584
+ // Verify cooldown is back to 5s (not 15s)
585
585
  writeFileSync(SOCKET_PATH, "");
586
586
  expect(client.isAvailable()).toBe(false);
587
587
 
588
- // 30s should be enough to clear cooldown
589
- fakeNow += 30_001;
588
+ // 5s should be enough to clear cooldown
589
+ fakeNow += 5_001;
590
590
  expect(client.isAvailable()).toBe(true);
591
591
  }, 15_000);
592
592
 
@@ -594,60 +594,73 @@ describe("keychain-broker-client", () => {
594
594
  const client = createBrokerClient();
595
595
 
596
596
  // First failure round: two attempts (first + immediate retry) ->
597
- // consecutiveFailures=2, cooldown index = max(2-2,0) = 0 -> 30s.
597
+ // consecutiveFailures=2, cooldown index = max(2-2,0) = 0 -> 5s.
598
598
  await client.ping();
599
599
 
600
600
  writeFileSync(SOCKET_PATH, "");
601
601
  expect(client.isAvailable()).toBe(false);
602
602
 
603
- // 30s should clear the first cooldown
604
- fakeNow += 30_001;
603
+ // 5s should clear the first cooldown
604
+ fakeNow += 5_001;
605
605
  expect(client.isAvailable()).toBe(true);
606
606
 
607
607
  // Remove socket to trigger another failure. After cooldown elapses,
608
608
  // ensureConnected clears unavailableSince and tries connect().
609
609
  // This failure increments consecutiveFailures to 3 (no immediate retry
610
610
  // since consecutiveFailures > 1 after increment).
611
- // Cooldown index = max(3-2,0) = 1 -> 60s.
611
+ // Cooldown index = max(3-2,0) = 1 -> 15s.
612
612
  rmSync(SOCKET_PATH, { force: true });
613
613
  await client.ping();
614
614
 
615
615
  writeFileSync(SOCKET_PATH, "");
616
616
  expect(client.isAvailable()).toBe(false);
617
617
 
618
- fakeNow += 30_001;
619
- expect(client.isAvailable()).toBe(false); // 30s not enough
618
+ fakeNow += 5_001;
619
+ expect(client.isAvailable()).toBe(false); // 5s not enough
620
620
 
621
- fakeNow += 30_000; // total 60_001ms since this cooldown started
621
+ fakeNow += 10_000; // total 15_001ms since this cooldown started
622
622
  expect(client.isAvailable()).toBe(true);
623
623
 
624
- // Another failure -> consecutiveFailures=4, index = max(4-2,0) = 2 -> 120s (2min)
624
+ // Another failure -> consecutiveFailures=4, index = max(4-2,0) = 2 -> 30s
625
625
  rmSync(SOCKET_PATH, { force: true });
626
626
  await client.ping();
627
627
 
628
628
  writeFileSync(SOCKET_PATH, "");
629
629
  expect(client.isAvailable()).toBe(false);
630
630
 
631
- fakeNow += 60_001;
631
+ fakeNow += 15_001;
632
+ expect(client.isAvailable()).toBe(false);
633
+
634
+ fakeNow += 15_000; // total 30_001ms
635
+ expect(client.isAvailable()).toBe(true);
636
+
637
+ // Another failure -> consecutiveFailures=5, index = max(5-2,0) = 3 -> 60s
638
+ rmSync(SOCKET_PATH, { force: true });
639
+ await client.ping();
640
+
641
+ writeFileSync(SOCKET_PATH, "");
642
+ expect(client.isAvailable()).toBe(false);
643
+
644
+ fakeNow += 30_001;
632
645
  expect(client.isAvailable()).toBe(false);
633
646
 
634
- fakeNow += 60_000; // total 120_001ms
647
+ fakeNow += 30_000; // total 60_001ms
635
648
  expect(client.isAvailable()).toBe(true);
636
649
 
637
- // Another failure -> consecutiveFailures=5, index = max(5-2,0) = 3 -> 300s (5min)
650
+ // Another failure -> consecutiveFailures=6, index = min(max(6-2,0), 4) = 4 -> 300s (5min)
638
651
  rmSync(SOCKET_PATH, { force: true });
639
652
  await client.ping();
640
653
 
641
654
  writeFileSync(SOCKET_PATH, "");
642
655
  expect(client.isAvailable()).toBe(false);
643
656
 
644
- fakeNow += 120_001;
657
+ fakeNow += 60_001;
645
658
  expect(client.isAvailable()).toBe(false);
646
659
 
647
- fakeNow += 180_000; // total 300_001ms
660
+ fakeNow += 240_000; // total 300_001ms
648
661
  expect(client.isAvailable()).toBe(true);
649
662
 
650
- // Another failure -> consecutiveFailures=6, index = min(max(6-2,0), 3) = 3 -> 300s (capped)
663
+ // Another failure -> consecutiveFailures=7, index = min(max(7-2,0), 4) = 4 -> 300s (capped)
651
664
  rmSync(SOCKET_PATH, { force: true });
652
665
  await client.ping();
653
666
 
@@ -658,4 +671,130 @@ describe("keychain-broker-client", () => {
658
671
  expect(client.isAvailable()).toBe(true);
659
672
  });
660
673
  });
674
+
675
+ // -----------------------------------------------------------------------
676
+ // Connect timeout
677
+ // -----------------------------------------------------------------------
678
+ describe("connect timeout", () => {
679
+ let stopFn: (() => Promise<void>) | null = null;
680
+
681
+ beforeEach(() => {
682
+ writeFileSync(TOKEN_PATH, TEST_TOKEN);
683
+ });
684
+
685
+ afterEach(async () => {
686
+ if (stopFn) {
687
+ await stopFn();
688
+ stopFn = null;
689
+ }
690
+ });
691
+
692
+ test("rejects connect within 3 seconds when broker is unresponsive", async () => {
693
+ // Create a server that accepts connections but never responds
694
+ // (simulates an unresponsive broker process).
695
+ const activeConns = new Set<import("node:net").Socket>();
696
+ const server = createServer((conn) => {
697
+ activeConns.add(conn);
698
+ conn.on("close", () => activeConns.delete(conn));
699
+ // Accept connection but do nothing — no data, no close
700
+ });
701
+ await new Promise<void>((resolve) => {
702
+ server.listen(SOCKET_PATH, () => resolve());
703
+ });
704
+ stopFn = () =>
705
+ new Promise<void>((resolve) => {
706
+ for (const conn of activeConns) conn.destroy();
707
+ activeConns.clear();
708
+ server.close(() => resolve());
709
+ });
710
+
711
+ const client = createBrokerClient();
712
+ const start = Date.now();
713
+ const result = await client.ping();
714
+ const elapsed = Date.now() - start;
715
+
716
+ // Should return null (graceful fallback) and not hang indefinitely.
717
+ // The connect timeout is 3s; allow some slack but it should be well
718
+ // under 10s (the old behavior would hang for REQUEST_TIMEOUT_MS * retries).
719
+ expect(result).toBeNull();
720
+ expect(elapsed).toBeLessThan(10_000);
721
+ }, 15_000);
722
+
723
+ test("successful connect clears the connect timer", async () => {
724
+ // Normal broker that responds to pings — verifies the timer is cleared
725
+ // and doesn't fire after a successful connection.
726
+ const broker = createMockBroker();
727
+ broker.setHandler(() => ({ ok: true, result: { pong: true } }));
728
+ await broker.start();
729
+ stopFn = () => broker.stop();
730
+
731
+ const client = createBrokerClient();
732
+ const result = await client.ping();
733
+ expect(result).toEqual({ pong: true });
734
+
735
+ // Wait a bit past the connect timeout to ensure no stale timer fires
736
+ await new Promise((r) => setTimeout(r, 100));
737
+
738
+ // Client should still work fine
739
+ const result2 = await client.ping();
740
+ expect(result2).toEqual({ pong: true });
741
+ });
742
+ });
743
+
744
+ // -----------------------------------------------------------------------
745
+ // Reduced initial cooldown
746
+ // -----------------------------------------------------------------------
747
+ describe("reduced initial cooldown", () => {
748
+ const originalDateNow = Date.now;
749
+ let fakeNow: number;
750
+
751
+ beforeEach(() => {
752
+ fakeNow = originalDateNow.call(Date);
753
+ Date.now = () => fakeNow;
754
+ writeFileSync(TOKEN_PATH, TEST_TOKEN);
755
+ });
756
+
757
+ afterEach(() => {
758
+ Date.now = originalDateNow;
759
+ });
760
+
761
+ test("first cooldown is 5 seconds, not 30 seconds", async () => {
762
+ const client = createBrokerClient();
763
+
764
+ // Trigger two connection failures (first + immediate retry)
765
+ await client.ping();
766
+
767
+ writeFileSync(SOCKET_PATH, "");
768
+
769
+ // Should still be in cooldown at 4 seconds
770
+ fakeNow += 4_000;
771
+ expect(client.isAvailable()).toBe(false);
772
+
773
+ // Should be available after 5 seconds
774
+ fakeNow += 1_001;
775
+ expect(client.isAvailable()).toBe(true);
776
+ });
777
+
778
+ test("second cooldown is 15 seconds", async () => {
779
+ const client = createBrokerClient();
780
+
781
+ // First failure round -> cooldown 5s
782
+ await client.ping();
783
+
784
+ // Clear first cooldown
785
+ fakeNow += 5_001;
786
+
787
+ // Second failure -> cooldown 15s
788
+ await client.ping();
789
+
790
+ writeFileSync(SOCKET_PATH, "");
791
+ expect(client.isAvailable()).toBe(false);
792
+
793
+ fakeNow += 14_000;
794
+ expect(client.isAvailable()).toBe(false);
795
+
796
+ fakeNow += 1_001;
797
+ expect(client.isAvailable()).toBe(true);
798
+ });
799
+ });
661
800
  });
@@ -5,7 +5,6 @@
5
5
  * - compaction.summaryCalls: 2-6
6
6
  * - compaction.estimatedInputTokens: < previousEstimatedInputTokens
7
7
  * - recall.injectedTokens: <= computed dynamic budget
8
- * - recall.recencyHits: > 0
9
8
  * - recall.enabled: true
10
9
  */
11
10
  import { mkdtempSync, rmSync } from "node:fs";
@@ -303,8 +302,6 @@ describe("Memory context benchmark", () => {
303
302
  { maxInjectTokensOverride: recallBudget },
304
303
  );
305
304
 
306
- // Recency search finds conversation-scoped segments.
307
- expect(recall.recencyHits).toBeGreaterThan(0);
308
305
  expect(recall.enabled).toBe(true);
309
306
  // With Qdrant mock returning a high-scoring result, content should be injected.
310
307
  expect(recall.selectedCount).toBeGreaterThan(0);
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Tests for adaptive poll interval backoff in the memory jobs worker.
3
+ *
4
+ * Verifies that when no jobs are claimable, the poll interval doubles each
5
+ * tick (1.5s -> 3s -> 6s -> ... -> 30s cap), and resets to 1.5s when work
6
+ * is found.
7
+ */
8
+ import { describe, expect, mock, test } from "bun:test";
9
+
10
+ // ── Mocks (must precede imports of tested module) ──────────────────
11
+
12
+ mock.module("../util/platform.js", () => ({
13
+ getDataDir: () => "/tmp/test-backoff",
14
+ isMacOS: () => false,
15
+ isLinux: () => true,
16
+ isWindows: () => false,
17
+ getPidPath: () => "/tmp/test-backoff/test.pid",
18
+ getDbPath: () => "/tmp/test-backoff/test.db",
19
+ getLogPath: () => "/tmp/test-backoff/test.log",
20
+ ensureDataDir: () => {},
21
+ }));
22
+
23
+ mock.module("../util/logger.js", () => ({
24
+ getLogger: () =>
25
+ new Proxy({} as Record<string, unknown>, {
26
+ get: () => () => {},
27
+ }),
28
+ }));
29
+
30
+ // Mock config — memory disabled so runMemoryJobsOnce returns 0 immediately
31
+ mock.module("../config/loader.js", () => ({
32
+ getConfig: () => ({
33
+ memory: { enabled: false },
34
+ }),
35
+ loadConfig: () => ({
36
+ memory: { enabled: false },
37
+ }),
38
+ }));
39
+
40
+ // Mock jobs-store (accesses DB)
41
+ mock.module("../memory/jobs-store.js", () => ({
42
+ resetRunningJobsToPending: () => 0,
43
+ claimMemoryJobs: () => [],
44
+ completeMemoryJob: () => {},
45
+ deferMemoryJob: () => "deferred",
46
+ failMemoryJob: () => {},
47
+ failStalledJobs: () => 0,
48
+ enqueueCleanupStaleSupersededItemsJob: () => null,
49
+ enqueuePruneOldConversationsJob: () => null,
50
+ }));
51
+
52
+ // Mock db.js (rawRun used in sweepStaleItems)
53
+ mock.module("../memory/db.js", () => ({
54
+ rawRun: () => 0,
55
+ }));
56
+
57
+ import {
58
+ POLL_INTERVAL_MAX_MS,
59
+ POLL_INTERVAL_MIN_MS,
60
+ startMemoryJobsWorker,
61
+ } from "../memory/jobs-worker.js";
62
+
63
+ describe("memory jobs worker adaptive poll interval", () => {
64
+ test("exports expected poll interval constants", () => {
65
+ expect(POLL_INTERVAL_MIN_MS).toBe(1_500);
66
+ expect(POLL_INTERVAL_MAX_MS).toBe(30_000);
67
+ });
68
+
69
+ test("backoff sequence doubles from min to max then caps", () => {
70
+ // Verify the math: starting at 1500, doubling each step, capped at 30000
71
+ const intervals: number[] = [];
72
+ let current = POLL_INTERVAL_MIN_MS;
73
+ for (let i = 0; i < 10; i++) {
74
+ intervals.push(current);
75
+ current = Math.min(current * 2, POLL_INTERVAL_MAX_MS);
76
+ }
77
+ expect(intervals).toEqual([
78
+ 1_500, // tick 1
79
+ 3_000, // tick 2
80
+ 6_000, // tick 3
81
+ 12_000, // tick 4
82
+ 24_000, // tick 5
83
+ 30_000, // tick 6 (capped)
84
+ 30_000, // stays capped
85
+ 30_000,
86
+ 30_000,
87
+ 30_000,
88
+ ]);
89
+ });
90
+
91
+ test("worker schedules setTimeout with increasing intervals when idle", async () => {
92
+ const timeoutDelays: number[] = [];
93
+ const originalSetTimeout = globalThis.setTimeout;
94
+ const originalClearTimeout = globalThis.clearTimeout;
95
+
96
+ // Collect pending timer callbacks so we can fire them manually
97
+ const pendingCallbacks: Array<() => void> = [];
98
+
99
+ globalThis.setTimeout = ((fn: () => void, delay?: number) => {
100
+ if (delay !== undefined && delay >= POLL_INTERVAL_MIN_MS) {
101
+ timeoutDelays.push(delay);
102
+ pendingCallbacks.push(fn);
103
+ }
104
+ return 999 as unknown as ReturnType<typeof setTimeout>;
105
+ }) as typeof setTimeout;
106
+ globalThis.clearTimeout = (() => {}) as typeof clearTimeout;
107
+
108
+ try {
109
+ const worker = startMemoryJobsWorker();
110
+
111
+ // Wait for the initial tick() promise to settle
112
+ await new Promise((resolve) => originalSetTimeout(resolve, 20));
113
+
114
+ // Fire pending timer callbacks to advance through the backoff sequence.
115
+ // Each callback triggers tick() which is async, so we await a microtask
116
+ // after each to let the promise chain settle and schedule the next timer.
117
+ for (let i = 0; i < 6; i++) {
118
+ const cb = pendingCallbacks.shift();
119
+ if (cb) {
120
+ cb();
121
+ await new Promise((resolve) => originalSetTimeout(resolve, 20));
122
+ }
123
+ }
124
+
125
+ worker.stop();
126
+
127
+ // We should have captured several setTimeout calls with increasing delays
128
+ expect(timeoutDelays.length).toBeGreaterThanOrEqual(4);
129
+
130
+ // Intervals should be non-decreasing (backoff)
131
+ for (let i = 1; i < timeoutDelays.length; i++) {
132
+ expect(timeoutDelays[i]).toBeGreaterThanOrEqual(timeoutDelays[i - 1]!);
133
+ }
134
+
135
+ // All intervals within bounds
136
+ for (const delay of timeoutDelays) {
137
+ expect(delay).toBeGreaterThanOrEqual(POLL_INTERVAL_MIN_MS);
138
+ expect(delay).toBeLessThanOrEqual(POLL_INTERVAL_MAX_MS);
139
+ }
140
+
141
+ // Should eventually reach the cap
142
+ expect(timeoutDelays[timeoutDelays.length - 1]).toBe(
143
+ POLL_INTERVAL_MAX_MS,
144
+ );
145
+ } finally {
146
+ globalThis.setTimeout = originalSetTimeout;
147
+ globalThis.clearTimeout = originalClearTimeout;
148
+ }
149
+ });
150
+ });
@@ -2,7 +2,7 @@
2
2
  * Memory lifecycle E2E regression test.
3
3
  *
4
4
  * Verifies the new memory pipeline end-to-end:
5
- * - 6-kind enum items (identity, preference, project, decision, constraint, event)
5
+ * - Standard-kind enum items (identity, preference, project, decision, constraint, event, journal, capability, ...)
6
6
  * - Supersession chains (supersedes/supersededBy fields)
7
7
  * - Hybrid search retrieval
8
8
  * - Two-layer XML injection format (<memory_context> with sections)
@@ -44,10 +44,31 @@ mock.module("../util/logger.js", () => ({
44
44
  }),
45
45
  }));
46
46
 
47
+ // Stub the local embedding backend so the real ONNX model never loads
48
+ mock.module("../memory/embedding-local.js", () => ({
49
+ LocalEmbeddingBackend: class {
50
+ readonly provider = "local" as const;
51
+ readonly model: string;
52
+ constructor(model: string) {
53
+ this.model = model;
54
+ }
55
+ async embed(texts: string[]): Promise<number[][]> {
56
+ return texts.map(() => new Array(384).fill(0));
57
+ }
58
+ },
59
+ }));
60
+
61
+ // Dynamic Qdrant mock: tests can push results to be returned by searchWithFilter/hybridSearch
62
+ let mockQdrantResults: Array<{
63
+ id: string;
64
+ score: number;
65
+ payload: Record<string, unknown>;
66
+ }> = [];
67
+
47
68
  mock.module("../memory/qdrant-client.js", () => ({
48
69
  getQdrantClient: () => ({
49
- searchWithFilter: async () => [],
50
- hybridSearch: async () => [],
70
+ searchWithFilter: async () => mockQdrantResults,
71
+ hybridSearch: async () => mockQdrantResults,
51
72
  upsertPoints: async () => {},
52
73
  deletePoints: async () => {},
53
74
  }),
@@ -60,7 +81,6 @@ const TEST_CONFIG = {
60
81
  ...DEFAULT_CONFIG.memory,
61
82
  embeddings: {
62
83
  ...DEFAULT_CONFIG.memory.embeddings,
63
- provider: "openai" as const,
64
84
  required: false,
65
85
  },
66
86
  extraction: {
@@ -115,6 +135,7 @@ describe("Memory lifecycle E2E regression", () => {
115
135
  db.run("DELETE FROM conversations");
116
136
  db.run("DELETE FROM memory_jobs");
117
137
  db.run("DELETE FROM memory_checkpoints");
138
+ mockQdrantResults = [];
118
139
  resetCleanupScheduleThrottle();
119
140
  resetStaleSweepThrottle();
120
141
  });
@@ -128,7 +149,7 @@ describe("Memory lifecycle E2E regression", () => {
128
149
  }
129
150
  });
130
151
 
131
- test("extraction produces items with 6-kind enum and supersession chains form correctly", async () => {
152
+ test("extraction produces items with standard-kind enum and supersession chains form correctly", async () => {
132
153
  const db = getDb();
133
154
  const now = 1_701_100_000_000;
134
155
  const conversationId = "conv-memory-lifecycle";
@@ -165,7 +186,7 @@ describe("Memory lifecycle E2E regression", () => {
165
186
  ])
166
187
  .run();
167
188
 
168
- // Seed items using the 6-kind enum
189
+ // Seed items using the standard-kind enum
169
190
  const kinds = [
170
191
  "identity",
171
192
  "preference",
@@ -299,12 +320,8 @@ describe("Memory lifecycle E2E regression", () => {
299
320
  TEST_CONFIG,
300
321
  );
301
322
 
302
- // Recency search finds segments but their finalScore (semantic*0.7 +
303
- // recency*0.2 + confidence*0.1) is too low to pass tier classification
304
- // (threshold > 0.6) because semantic=0 with Qdrant mocked empty.
305
- // Verify recency search ran successfully.
306
- expect(recall.recencyHits).toBeGreaterThan(0);
307
- // Candidates exist but don't pass tier classification, so injectedText is empty
323
+ // Without semantic search (Qdrant mocked empty), no candidates pass
324
+ // tier classification (threshold > 0.6).
308
325
  expect(recall.enabled).toBe(true);
309
326
  });
310
327
 
@@ -343,15 +360,47 @@ describe("Memory lifecycle E2E regression", () => {
343
360
  })
344
361
  .run();
345
362
 
346
- db.run(`
347
- INSERT INTO memory_segments (
348
- id, message_id, conversation_id, role, segment_index, text, token_estimate, scope_id, created_at, updated_at
349
- ) VALUES (
350
- 'seg-injection-seed', 'msg-injection-seed', '${conversationId}', 'user', 0,
351
- 'My preferred timezone is America/Los_Angeles.', 10, 'default',
352
- ${now + 10}, ${now + 10}
353
- )
354
- `);
363
+ // Seed a memory item so the semantic search path can find it
364
+ db.insert(memoryItems)
365
+ .values({
366
+ id: "item-timezone-pref",
367
+ kind: "preference",
368
+ subject: "timezone preference",
369
+ statement: "My preferred timezone is America/Los_Angeles.",
370
+ status: "active",
371
+ confidence: 0.9,
372
+ importance: 0.8,
373
+ fingerprint: "fp-item-timezone-pref",
374
+ firstSeenAt: now + 10,
375
+ lastSeenAt: now + 10,
376
+ })
377
+ .run();
378
+
379
+ db.insert(memoryItemSources)
380
+ .values({
381
+ memoryItemId: "item-timezone-pref",
382
+ messageId: "msg-injection-seed",
383
+ evidence: "timezone preference evidence",
384
+ createdAt: now + 10,
385
+ })
386
+ .run();
387
+
388
+ // Mock Qdrant to return the timezone preference item
389
+ mockQdrantResults = [
390
+ {
391
+ id: "emb-timezone-pref",
392
+ score: 0.92,
393
+ payload: {
394
+ target_type: "item",
395
+ target_id: "item-timezone-pref",
396
+ text: "My preferred timezone is America/Los_Angeles.",
397
+ kind: "preference",
398
+ status: "active",
399
+ created_at: now + 10,
400
+ last_seen_at: now + 10,
401
+ },
402
+ },
403
+ ];
355
404
 
356
405
  const recall = await buildMemoryRecall(
357
406
  "timezone",
@@ -359,10 +408,6 @@ describe("Memory lifecycle E2E regression", () => {
359
408
  TEST_CONFIG,
360
409
  );
361
410
 
362
- // The recency-only promotion path (Step 6 in retriever) ensures the
363
- // seeded segment reaches tier 2 and is injected even without semantic
364
- // search. Verify structure of the two-layer XML format.
365
- expect(recall.recencyHits).toBeGreaterThan(0);
366
411
  expect(recall.enabled).toBe(true);
367
412
  expect(recall.injectedText.length).toBeGreaterThan(0);
368
413
  expect(recall.injectedTokens).toBeGreaterThan(0);