@vellumai/assistant 0.5.15 → 0.6.0

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 (503) hide show
  1. package/ARCHITECTURE.md +3 -3
  2. package/Dockerfile +0 -3
  3. package/docs/architecture/integrations.md +15 -14
  4. package/knip.json +4 -1
  5. package/openapi.yaml +670 -122
  6. package/package.json +1 -1
  7. package/src/__tests__/actor-token-service.test.ts +68 -0
  8. package/src/__tests__/agent-loop.test.ts +0 -32
  9. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  10. package/src/__tests__/anthropic-provider.test.ts +57 -3
  11. package/src/__tests__/app-compiler.test.ts +120 -0
  12. package/src/__tests__/assistant-feature-flags-integration.test.ts +5 -377
  13. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  14. package/src/__tests__/call-domain.test.ts +2 -6
  15. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  16. package/src/__tests__/call-recovery.test.ts +2 -6
  17. package/src/__tests__/call-routes-http.test.ts +2 -6
  18. package/src/__tests__/call-store.test.ts +2 -6
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  20. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  21. package/src/__tests__/ces-rpc-credential-backend.test.ts +4 -1
  22. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  23. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  24. package/src/__tests__/checker.test.ts +84 -3
  25. package/src/__tests__/clawhub.test.ts +54 -24
  26. package/src/__tests__/cli-command-risk-guard.test.ts +108 -6
  27. package/src/__tests__/cli-memory.test.ts +377 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +12 -2
  29. package/src/__tests__/config-schema.test.ts +1 -3
  30. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  31. package/src/__tests__/config-watcher-feature-flags.test.ts +211 -0
  32. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  33. package/src/__tests__/contacts-tools.test.ts +31 -0
  34. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  35. package/src/__tests__/context-token-estimator.test.ts +175 -10
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
  37. package/src/__tests__/conversation-agent-loop.test.ts +9 -0
  38. package/src/__tests__/conversation-attachments.test.ts +2 -6
  39. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  40. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  41. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  42. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  43. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  44. package/src/__tests__/conversation-error.test.ts +33 -2
  45. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  46. package/src/__tests__/conversation-history-web-search.test.ts +5 -0
  47. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  48. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  49. package/src/__tests__/conversation-runtime-assembly.test.ts +7 -4
  50. package/src/__tests__/conversation-slash-commands.test.ts +2 -6
  51. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  52. package/src/__tests__/conversation-store.test.ts +2 -6
  53. package/src/__tests__/conversation-usage.test.ts +3 -6
  54. package/src/__tests__/conversation-wipe.test.ts +11 -408
  55. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  56. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  57. package/src/__tests__/credential-security-e2e.test.ts +6 -1
  58. package/src/__tests__/docker-signing-key-bootstrap.test.ts +7 -73
  59. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -7
  60. package/src/__tests__/followup-tools.test.ts +2 -6
  61. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  62. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  63. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  64. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  65. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  66. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  67. package/src/__tests__/guardian-action-store.test.ts +2 -6
  68. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  69. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  70. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  71. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  72. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  73. package/src/__tests__/guardian-routing-invariants.test.ts +343 -6
  74. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  75. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  76. package/src/__tests__/heartbeat-service.test.ts +1 -3
  77. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  78. package/src/__tests__/injection-block.test.ts +154 -0
  79. package/src/__tests__/install-meta.test.ts +506 -0
  80. package/src/__tests__/install-skill-routing.test.ts +292 -0
  81. package/src/__tests__/intent-routing.test.ts +6 -18
  82. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  83. package/src/__tests__/invite-routes-http.test.ts +2 -6
  84. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
  85. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  86. package/src/__tests__/llm-context-route-provider.test.ts +2 -6
  87. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
  88. package/src/__tests__/llm-usage-store.test.ts +2 -6
  89. package/src/__tests__/log-export-workspace.test.ts +4 -34
  90. package/src/__tests__/managed-skill-lifecycle.test.ts +7 -37
  91. package/src/__tests__/managed-store.test.ts +40 -21
  92. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  93. package/src/__tests__/memory-recall-log-store.test.ts +2 -6
  94. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  95. package/src/__tests__/messaging-send-tool.test.ts +6 -6
  96. package/src/__tests__/migration-cross-version-compatibility.test.ts +1 -29
  97. package/src/__tests__/migration-export-http.test.ts +3 -34
  98. package/src/__tests__/migration-import-commit-http.test.ts +1 -29
  99. package/src/__tests__/migration-import-preflight-http.test.ts +3 -34
  100. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +2 -1
  101. package/src/__tests__/non-member-access-request.test.ts +2 -6
  102. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  103. package/src/__tests__/oauth-apps-routes.test.ts +120 -10
  104. package/src/__tests__/oauth-cli.test.ts +364 -2
  105. package/src/__tests__/oauth-connect-orchestrator.test.ts +709 -0
  106. package/src/__tests__/oauth-provider-serializer.test.ts +2 -1
  107. package/src/__tests__/oauth-provider-visibility.test.ts +149 -0
  108. package/src/__tests__/oauth-providers-routes.test.ts +5 -2
  109. package/src/__tests__/oauth-store.test.ts +0 -5
  110. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  111. package/src/__tests__/outlook-attachments.test.ts +301 -0
  112. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  113. package/src/__tests__/outlook-categories.test.ts +212 -0
  114. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  115. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  116. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  117. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  118. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  119. package/src/__tests__/outlook-messaging-provider.test.ts +1071 -0
  120. package/src/__tests__/outlook-trash.test.ts +77 -0
  121. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  122. package/src/__tests__/path-policy.test.ts +2 -17
  123. package/src/__tests__/permission-types.test.ts +0 -1
  124. package/src/__tests__/platform-callback-registration.test.ts +7 -11
  125. package/src/__tests__/playbook-execution.test.ts +76 -80
  126. package/src/__tests__/playbook-tools.test.ts +5 -7
  127. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  128. package/src/__tests__/provider-error-scenarios.test.ts +21 -2
  129. package/src/__tests__/qdrant-manager.test.ts +68 -21
  130. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  131. package/src/__tests__/registry.test.ts +2 -2
  132. package/src/__tests__/require-fresh-approval.test.ts +64 -3
  133. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  134. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  135. package/src/__tests__/sandbox-diagnostics.test.ts +20 -29
  136. package/src/__tests__/scaffold-managed-skill-tool.test.ts +2 -10
  137. package/src/__tests__/schedule-store.test.ts +2 -6
  138. package/src/__tests__/schedule-tools.test.ts +2 -6
  139. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  140. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  141. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  142. package/src/__tests__/search-skills-unified.test.ts +421 -0
  143. package/src/__tests__/secret-allowlist.test.ts +20 -35
  144. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  145. package/src/__tests__/send-endpoint-busy.test.ts +2 -6
  146. package/src/__tests__/sequence-store.test.ts +2 -6
  147. package/src/__tests__/server-history-render.test.ts +2 -6
  148. package/src/__tests__/shell-credential-ref.test.ts +0 -5
  149. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  150. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  151. package/src/__tests__/skill-load-feature-flag.test.ts +13 -54
  152. package/src/__tests__/skill-load-inline-command.test.ts +3 -65
  153. package/src/__tests__/skill-load-inline-includes.test.ts +3 -65
  154. package/src/__tests__/skill-load-tool.test.ts +3 -67
  155. package/src/__tests__/skill-memory.test.ts +480 -195
  156. package/src/__tests__/skills-uninstall.test.ts +2 -2
  157. package/src/__tests__/skills.test.ts +23 -50
  158. package/src/__tests__/slack-channel-config.test.ts +2 -21
  159. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  160. package/src/__tests__/starter-bundle.test.ts +2 -8
  161. package/src/__tests__/stt-hints.test.ts +7 -2
  162. package/src/__tests__/system-prompt.test.ts +25 -45
  163. package/src/__tests__/task-compiler.test.ts +2 -27
  164. package/src/__tests__/task-management-tools.test.ts +2 -27
  165. package/src/__tests__/task-memory-cleanup.test.ts +173 -250
  166. package/src/__tests__/task-runner.test.ts +2 -27
  167. package/src/__tests__/task-scheduler.test.ts +2 -27
  168. package/src/__tests__/terminal-tools.test.ts +1 -17
  169. package/src/__tests__/test-preload.ts +3 -0
  170. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +0 -79
  171. package/src/__tests__/tool-approval-handler.test.ts +4 -27
  172. package/src/__tests__/tool-execution-abort-cleanup.test.ts +2 -11
  173. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -25
  174. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  175. package/src/__tests__/tool-executor.test.ts +0 -1
  176. package/src/__tests__/tool-grant-request-escalation.test.ts +4 -27
  177. package/src/__tests__/tool-preview-lifecycle.test.ts +0 -20
  178. package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
  179. package/src/__tests__/trust-store.test.ts +10 -42
  180. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -30
  181. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +3 -27
  182. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -28
  183. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -28
  184. package/src/__tests__/trusted-contact-verification.test.ts +2 -28
  185. package/src/__tests__/turn-boundary-resolution.test.ts +2 -34
  186. package/src/__tests__/twilio-provider.test.ts +0 -16
  187. package/src/__tests__/twilio-routes-twiml.test.ts +7 -12
  188. package/src/__tests__/twilio-routes.test.ts +0 -24
  189. package/src/__tests__/update-bulletin.test.ts +17 -89
  190. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -26
  191. package/src/__tests__/usage-routes.test.ts +2 -27
  192. package/src/__tests__/user-reference.test.ts +1 -5
  193. package/src/__tests__/vbundle-pax-and-symlink.test.ts +4 -34
  194. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +2 -53
  195. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  196. package/src/__tests__/voice-invite-redemption.test.ts +2 -27
  197. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -30
  198. package/src/__tests__/voice-session-bridge.test.ts +2 -27
  199. package/src/__tests__/volume-security-guard.test.ts +2 -0
  200. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  201. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -29
  202. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +2 -2
  203. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +4 -29
  204. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  205. package/src/__tests__/workspace-migration-down-functions.test.ts +0 -6
  206. package/src/__tests__/workspace-policy.test.ts +1 -1
  207. package/src/acp/client-handler.ts +1 -2
  208. package/src/agent/attachments.ts +7 -2
  209. package/src/agent/image-optimize.ts +165 -0
  210. package/src/agent/loop.ts +1 -15
  211. package/src/bundler/app-compiler.ts +179 -2
  212. package/src/bundler/package-resolver.ts +3 -5
  213. package/src/cli/__tests__/notifications.test.ts +1 -24
  214. package/src/cli/cli-memory.ts +179 -0
  215. package/src/cli/commands/avatar.ts +3 -3
  216. package/src/cli/commands/config.ts +26 -13
  217. package/src/cli/commands/doctor.ts +2 -2
  218. package/src/cli/commands/memory.ts +41 -55
  219. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  220. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  221. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  222. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  223. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  224. package/src/cli/commands/oauth/connect.ts +26 -6
  225. package/src/cli/commands/oauth/mode.ts +7 -0
  226. package/src/cli/commands/oauth/providers.ts +49 -42
  227. package/src/cli/commands/oauth/shared.ts +39 -3
  228. package/src/cli/commands/platform/__tests__/connect.test.ts +3 -49
  229. package/src/cli/commands/platform/__tests__/disconnect.test.ts +3 -49
  230. package/src/cli/commands/platform/__tests__/status.test.ts +5 -55
  231. package/src/cli/commands/platform/index.ts +16 -16
  232. package/src/cli/commands/skills.ts +88 -16
  233. package/src/cli/commands/trust.ts +2 -2
  234. package/src/cli/lib/daemon-credential-client.ts +2 -3
  235. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  236. package/src/config/bundled-skills/computer-use/TOOLS.json +7 -7
  237. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  238. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  239. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  240. package/src/config/bundled-skills/gmail/SKILL.md +2 -10
  241. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  242. package/src/config/bundled-skills/messaging/SKILL.md +26 -19
  243. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  244. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  245. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  246. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  247. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  248. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  249. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  250. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  251. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  252. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  253. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  254. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  255. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  256. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  257. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  258. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  259. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  260. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  261. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  262. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  263. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  264. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  265. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  266. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  267. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  268. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  269. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  270. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  271. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  272. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  273. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  274. package/src/config/bundled-skills/slack/SKILL.md +1 -7
  275. package/src/config/bundled-tool-registry.ts +56 -4
  276. package/src/config/env-registry.ts +15 -8
  277. package/src/config/feature-flag-registry.json +29 -116
  278. package/src/config/loader.ts +4 -0
  279. package/src/config/schemas/platform.ts +8 -0
  280. package/src/config/schemas/security.ts +0 -6
  281. package/src/config/schemas/services.ts +8 -0
  282. package/src/config/schemas/timeouts.ts +1 -1
  283. package/src/config/skills.ts +18 -7
  284. package/src/context/token-estimator.ts +25 -18
  285. package/src/context/window-manager.ts +32 -9
  286. package/src/credential-execution/approval-bridge.ts +0 -1
  287. package/src/credential-execution/process-manager.ts +3 -1
  288. package/src/daemon/config-watcher.ts +51 -0
  289. package/src/daemon/context-overflow-reducer.ts +46 -2
  290. package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
  291. package/src/daemon/conversation-agent-loop.ts +99 -63
  292. package/src/daemon/conversation-error.ts +31 -8
  293. package/src/daemon/conversation-lifecycle.ts +33 -0
  294. package/src/daemon/conversation-media-retry.ts +85 -7
  295. package/src/daemon/conversation-notifiers.ts +4 -1
  296. package/src/daemon/conversation-process.ts +1 -0
  297. package/src/daemon/conversation-runtime-assembly.ts +5 -0
  298. package/src/daemon/conversation-usage.ts +1 -0
  299. package/src/daemon/conversation.ts +41 -2
  300. package/src/daemon/daemon-control.ts +8 -2
  301. package/src/daemon/handlers/shared.ts +22 -12
  302. package/src/daemon/handlers/skills.ts +423 -201
  303. package/src/daemon/lifecycle.ts +52 -4
  304. package/src/daemon/main.ts +5 -1
  305. package/src/daemon/message-types/conversations.ts +5 -1
  306. package/src/daemon/message-types/messages.ts +3 -1
  307. package/src/daemon/message-types/skills.ts +97 -36
  308. package/src/daemon/providers-setup.ts +7 -0
  309. package/src/daemon/server.ts +35 -22
  310. package/src/daemon/tool-side-effects.ts +27 -5
  311. package/src/events/domain-events.ts +1 -2
  312. package/src/heartbeat/heartbeat-service.ts +1 -0
  313. package/src/hooks/cli.ts +2 -2
  314. package/src/hooks/runner.ts +15 -38
  315. package/src/inbound/platform-callback-registration.ts +14 -14
  316. package/src/memory/admin.ts +11 -45
  317. package/src/memory/conversation-bootstrap.ts +2 -0
  318. package/src/memory/conversation-crud.ts +242 -348
  319. package/src/memory/conversation-group-migration.ts +157 -0
  320. package/src/memory/conversation-queries.ts +4 -2
  321. package/src/memory/db-init.ts +39 -3
  322. package/src/memory/embed.ts +73 -0
  323. package/src/memory/embedding-backend.ts +8 -14
  324. package/src/memory/embedding-runtime-manager.ts +12 -114
  325. package/src/memory/fingerprint.ts +2 -2
  326. package/src/memory/graph/bootstrap.ts +512 -0
  327. package/src/memory/graph/capability-seed.ts +297 -0
  328. package/src/memory/graph/consolidation.ts +691 -0
  329. package/src/memory/graph/conversation-graph-memory.ts +630 -0
  330. package/src/memory/graph/decay.test.ts +208 -0
  331. package/src/memory/graph/decay.ts +195 -0
  332. package/src/memory/graph/extraction-job.ts +69 -0
  333. package/src/memory/graph/extraction.test.ts +936 -0
  334. package/src/memory/graph/extraction.ts +1254 -0
  335. package/src/memory/graph/graph-search.ts +266 -0
  336. package/src/memory/graph/image-ref-utils.ts +29 -0
  337. package/src/memory/graph/injection.test.ts +513 -0
  338. package/src/memory/graph/injection.ts +439 -0
  339. package/src/memory/graph/inspect.ts +534 -0
  340. package/src/memory/graph/narrative.ts +267 -0
  341. package/src/memory/graph/pattern-scan.ts +269 -0
  342. package/src/memory/graph/retriever.ts +1008 -0
  343. package/src/memory/graph/scoring.test.ts +548 -0
  344. package/src/memory/graph/scoring.ts +232 -0
  345. package/src/memory/graph/serendipity.ts +65 -0
  346. package/src/memory/graph/store.test.ts +1050 -0
  347. package/src/memory/graph/store.ts +699 -0
  348. package/src/memory/graph/tool-handlers.ts +426 -0
  349. package/src/memory/graph/tools.ts +141 -0
  350. package/src/memory/graph/triggers.test.ts +487 -0
  351. package/src/memory/graph/triggers.ts +223 -0
  352. package/src/memory/graph/types.ts +271 -0
  353. package/src/memory/group-crud.ts +191 -0
  354. package/src/memory/indexer.ts +37 -19
  355. package/src/memory/job-handlers/cleanup.ts +0 -53
  356. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  357. package/src/memory/job-handlers/embedding.test.ts +3 -27
  358. package/src/memory/job-handlers/embedding.ts +5 -31
  359. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  360. package/src/memory/job-handlers/summarization.ts +32 -17
  361. package/src/memory/job-utils.ts +1 -1
  362. package/src/memory/jobs-store.ts +50 -70
  363. package/src/memory/jobs-worker.ts +147 -112
  364. package/src/memory/llm-usage-store.ts +35 -2
  365. package/src/memory/message-content.ts +1 -0
  366. package/src/memory/migrations/201-oauth-providers-feature-flag.ts +11 -0
  367. package/src/memory/migrations/202-drop-callback-transport-column.ts +13 -0
  368. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  369. package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
  370. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  371. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  372. package/src/memory/migrations/index.ts +6 -0
  373. package/src/memory/migrations/registry.ts +8 -0
  374. package/src/memory/qdrant-client.ts +44 -17
  375. package/src/memory/qdrant-manager.ts +26 -5
  376. package/src/memory/schema/index.ts +1 -0
  377. package/src/memory/schema/memory-graph.ts +139 -0
  378. package/src/memory/schema/oauth.ts +1 -1
  379. package/src/memory/search/semantic.ts +47 -91
  380. package/src/memory/slack-thread-store.ts +17 -0
  381. package/src/memory/task-memory-cleanup.ts +28 -50
  382. package/src/messaging/providers/outlook/adapter.ts +200 -0
  383. package/src/messaging/providers/outlook/client.ts +610 -0
  384. package/src/messaging/providers/outlook/types.ts +201 -0
  385. package/src/notifications/adapters/macos.ts +1 -0
  386. package/src/notifications/adapters/slack.ts +1 -1
  387. package/src/notifications/copy-composer.ts +9 -0
  388. package/src/notifications/signal.ts +16 -0
  389. package/src/oauth/__tests__/identity-verifier.test.ts +1 -1
  390. package/src/oauth/connect-orchestrator.ts +10 -3
  391. package/src/oauth/oauth-store.ts +10 -11
  392. package/src/oauth/provider-serializer.ts +3 -0
  393. package/src/oauth/provider-visibility.ts +16 -0
  394. package/src/oauth/seed-providers.ts +50 -17
  395. package/src/permissions/checker.ts +62 -9
  396. package/src/permissions/defaults.ts +4 -4
  397. package/src/permissions/types.ts +2 -4
  398. package/src/permissions/workspace-policy.ts +1 -1
  399. package/src/playbooks/playbook-compiler.ts +19 -18
  400. package/src/playbooks/types.ts +4 -3
  401. package/src/prompts/system-prompt.ts +6 -93
  402. package/src/prompts/templates/UPDATES.md +6 -0
  403. package/src/providers/anthropic/client.ts +47 -19
  404. package/src/providers/gemini/client.ts +1 -1
  405. package/src/providers/openai/client.ts +1 -1
  406. package/src/providers/registry.ts +1 -1
  407. package/src/providers/retry.ts +19 -3
  408. package/src/runtime/actor-trust-resolver.ts +5 -1
  409. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -27
  410. package/src/runtime/auth/__tests__/token-service.test.ts +1 -25
  411. package/src/runtime/auth/route-policy.ts +7 -4
  412. package/src/runtime/guardian-reply-router.ts +10 -2
  413. package/src/runtime/http-server.ts +23 -3
  414. package/src/runtime/middleware/auth.ts +20 -0
  415. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  416. package/src/runtime/routes/attachment-routes.ts +106 -16
  417. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  418. package/src/runtime/routes/btw-routes.ts +8 -0
  419. package/src/runtime/routes/conversation-management-routes.ts +2 -0
  420. package/src/runtime/routes/conversation-query-routes.ts +2 -58
  421. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  422. package/src/runtime/routes/debug-routes.ts +1 -1
  423. package/src/runtime/routes/global-search-routes.ts +21 -19
  424. package/src/runtime/routes/group-routes.ts +207 -0
  425. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  426. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  427. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  428. package/src/runtime/routes/inbound-stages/background-dispatch.ts +43 -2
  429. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  430. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  431. package/src/runtime/routes/memory-item-routes.test.ts +2 -31
  432. package/src/runtime/routes/memory-item-routes.ts +385 -341
  433. package/src/runtime/routes/oauth-apps.ts +18 -1
  434. package/src/runtime/routes/oauth-providers.ts +13 -1
  435. package/src/runtime/routes/schedule-routes.ts +2 -0
  436. package/src/runtime/routes/settings-routes.ts +1 -0
  437. package/src/runtime/routes/skills-routes.ts +103 -37
  438. package/src/runtime/routes/usage-routes.ts +19 -2
  439. package/src/runtime/routes/work-items-routes.test.ts +2 -27
  440. package/src/runtime/routes/workspace-routes.test.ts +3 -27
  441. package/src/schedule/scheduler.ts +8 -1
  442. package/src/security/oauth2.ts +1 -1
  443. package/src/security/secret-allowlist.ts +4 -4
  444. package/src/security/secure-keys.ts +4 -8
  445. package/src/shared/provider-env-vars.ts +19 -0
  446. package/src/skills/catalog-cache.ts +5 -0
  447. package/src/skills/catalog-install.ts +15 -14
  448. package/src/skills/clawhub.ts +134 -154
  449. package/src/skills/install-meta.ts +208 -0
  450. package/src/skills/managed-store.ts +27 -16
  451. package/src/skills/skill-memory.ts +210 -96
  452. package/src/skills/skillssh-registry.ts +19 -17
  453. package/src/tasks/task-runner.ts +3 -1
  454. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  455. package/src/tools/browser/runtime-check.ts +3 -1
  456. package/src/tools/memory/register.ts +63 -46
  457. package/src/tools/permission-checker.ts +7 -19
  458. package/src/tools/shared/filesystem/image-read.ts +22 -85
  459. package/src/tools/skills/skill-script-runner.ts +1 -1
  460. package/src/tools/terminal/safe-env.ts +1 -0
  461. package/src/tools/tool-manifest.ts +3 -3
  462. package/src/util/browser.ts +25 -10
  463. package/src/util/bun-runtime.ts +172 -0
  464. package/src/util/device-id.ts +3 -65
  465. package/src/watcher/providers/outlook-calendar.ts +343 -0
  466. package/src/watcher/providers/outlook.ts +198 -0
  467. package/src/workspace/git-service.ts +27 -6
  468. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  469. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  470. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  471. package/src/workspace/migrations/registry.ts +6 -0
  472. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  473. package/src/__tests__/journal-context.test.ts +0 -268
  474. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  475. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  476. package/src/__tests__/memory-query-builder.test.ts +0 -59
  477. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  478. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  479. package/src/__tests__/memory-regressions.test.ts +0 -3696
  480. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  481. package/src/daemon/conversation-memory.ts +0 -207
  482. package/src/memory/conversation-starters-cadence.ts +0 -74
  483. package/src/memory/items-extractor.ts +0 -860
  484. package/src/memory/job-handlers/batch-extraction.ts +0 -741
  485. package/src/memory/job-handlers/extraction.ts +0 -40
  486. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -383
  487. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  488. package/src/memory/journal-memory.ts +0 -224
  489. package/src/memory/query-builder.ts +0 -47
  490. package/src/memory/query-expansion.ts +0 -83
  491. package/src/memory/retriever.test.ts +0 -1590
  492. package/src/memory/retriever.ts +0 -1323
  493. package/src/memory/search/formatting.test.ts +0 -140
  494. package/src/memory/search/formatting.ts +0 -262
  495. package/src/memory/search/mmr.ts +0 -136
  496. package/src/memory/search/ranking.ts +0 -15
  497. package/src/memory/search/staleness.ts +0 -40
  498. package/src/memory/search/tier-classifier.ts +0 -18
  499. package/src/memory/search/types.ts +0 -121
  500. package/src/prompts/journal-context.ts +0 -156
  501. package/src/tools/memory/definitions.ts +0 -69
  502. package/src/tools/memory/handlers.test.ts +0 -590
  503. package/src/tools/memory/handlers.ts +0 -434
@@ -5,9 +5,7 @@ import {
5
5
  type SlashContext,
6
6
  } from "../daemon/conversation-slash.js";
7
7
 
8
- function makeSlashContext(
9
- overrides: Partial<SlashContext> = {},
10
- ): SlashContext {
8
+ function makeSlashContext(overrides: Partial<SlashContext> = {}): SlashContext {
11
9
  return {
12
10
  messageCount: 4,
13
11
  inputTokens: 1024,
@@ -43,9 +41,7 @@ describe("resolveSlash /commands interface-aware help", () => {
43
41
  "/fork — Fork the current conversation into a new branch",
44
42
  "/pair — Generate pairing info for connecting a mobile device",
45
43
  ]);
46
- expect(lines).not.toContain(
47
- "/model — Switch the active model",
48
- );
44
+ expect(lines).not.toContain("/model — Switch the active model");
49
45
  });
50
46
 
51
47
  test("renders iOS command help with /fork but without /pair", async () => {
@@ -1,4 +1,4 @@
1
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  import { v4 as uuid } from "uuid";
4
4
 
@@ -9,7 +9,7 @@ mock.module("../util/logger.js", () => ({
9
9
  }),
10
10
  }));
11
11
 
12
- import { getSqlite, initializeDb, resetDb } from "../memory/db.js";
12
+ import { getSqlite, initializeDb } from "../memory/db.js";
13
13
  import {
14
14
  conversationStarterRouteDefinitions,
15
15
  orderStrongestFirst,
@@ -17,10 +17,6 @@ import {
17
17
 
18
18
  initializeDb();
19
19
 
20
- afterAll(() => {
21
- resetDb();
22
- });
23
-
24
20
  const routes = conversationStarterRouteDefinitions();
25
21
 
26
22
  function dispatch(path: string): Response | Promise<Response> {
@@ -41,7 +37,7 @@ function dispatch(path: string): Response | Promise<Response> {
41
37
 
42
38
  function clearTables() {
43
39
  getSqlite().run("DELETE FROM conversation_starters");
44
- getSqlite().run("DELETE FROM memory_items");
40
+ getSqlite().run("DELETE FROM memory_graph_nodes");
45
41
  getSqlite().run("DELETE FROM memory_jobs");
46
42
  getSqlite().run("DELETE FROM memory_checkpoints");
47
43
  }
@@ -73,10 +69,23 @@ function insertStarter(overrides: {
73
69
  function insertMemoryItem(scopeId = "default") {
74
70
  const now = Date.now();
75
71
  getSqlite().run(
76
- `INSERT INTO memory_items (
77
- id, kind, subject, statement, status, confidence, fingerprint, scope_id, first_seen_at, last_seen_at
78
- ) VALUES (?, 'fact', 'test', 'test statement', 'active', 0.9, ?, ?, ?, ?)`,
79
- [uuid(), `fingerprint-${uuid()}`, scopeId, now, now],
72
+ `INSERT INTO memory_graph_nodes (
73
+ id, content, type, created, last_accessed, last_consolidated,
74
+ emotional_charge, fidelity, confidence, significance,
75
+ stability, reinforcement_count, last_reinforced,
76
+ source_conversations, source_type, scope_id
77
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, 'vivid', 0.8, 0.5, 14, 0, ?, '[]', 'inferred', ?)`,
78
+ [
79
+ uuid(),
80
+ "test\ntest statement",
81
+ "semantic",
82
+ now,
83
+ now,
84
+ now,
85
+ '{"valence":0,"intensity":0.1,"decayCurve":"linear","decayRate":0.05,"originalIntensity":0.1}',
86
+ now,
87
+ scopeId,
88
+ ],
80
89
  );
81
90
  }
82
91
 
@@ -1,4 +1,4 @@
1
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  mock.module("../util/logger.js", () => ({
4
4
  getLogger: () =>
@@ -24,15 +24,11 @@ import {
24
24
  getMessages,
25
25
  } from "../memory/conversation-crud.js";
26
26
  import { isLastUserMessageToolResult } from "../memory/conversation-queries.js";
27
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
27
+ import { getDb, initializeDb } from "../memory/db.js";
28
28
 
29
29
  // Initialize db once before all tests
30
30
  initializeDb();
31
31
 
32
- afterAll(() => {
33
- resetDb();
34
- });
35
-
36
32
  describe("deleteLastExchange", () => {
37
33
  beforeEach(() => {
38
34
  // Reset database between tests by dropping and recreating tables
@@ -1,4 +1,4 @@
1
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  const updateConversationUsageCalls: Array<{
4
4
  conversationId: string;
@@ -37,17 +37,13 @@ mock.module("../memory/conversation-crud.js", () => ({
37
37
  }));
38
38
 
39
39
  import { recordUsage } from "../daemon/conversation-usage.js";
40
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
40
+ import { getDb, initializeDb } from "../memory/db.js";
41
41
  import { listUsageEvents } from "../memory/llm-usage-store.js";
42
42
  import type { PricingUsage } from "../usage/types.js";
43
43
  import { resolvePricingForUsageWithOverrides } from "../util/pricing.js";
44
44
 
45
45
  initializeDb();
46
46
 
47
- afterAll(() => {
48
- resetDb();
49
- });
50
-
51
47
  describe("recordUsage", () => {
52
48
  beforeEach(() => {
53
49
  const db = getDb();
@@ -202,6 +198,7 @@ describe("recordUsage", () => {
202
198
  expect(onEventMessages).toEqual([
203
199
  {
204
200
  type: "usage_update",
201
+ conversationId: "conv-usage-1",
205
202
  inputTokens: 3_420_218,
206
203
  outputTokens: 11_768,
207
204
  totalInputTokens: 3_420_218,
@@ -1,4 +1,4 @@
1
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  mock.module("../util/logger.js", () => ({
4
4
  getLogger: () =>
@@ -15,22 +15,17 @@ import {
15
15
  getMessages,
16
16
  wipeConversation,
17
17
  } from "../memory/conversation-crud.js";
18
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
18
+ import { getDb, initializeDb } from "../memory/db.js";
19
19
  import { enqueueMemoryJob } from "../memory/jobs-store.js";
20
20
 
21
21
  // Initialize db once before all tests
22
22
  initializeDb();
23
23
 
24
- afterAll(() => {
25
- resetDb();
26
- });
27
-
28
24
  describe("wipeConversation", () => {
29
25
  beforeEach(() => {
30
26
  const db = getDb();
31
- db.run(`DELETE FROM memory_item_sources`);
27
+ db.run(`DELETE FROM memory_graph_nodes`);
32
28
  db.run(`DELETE FROM memory_segments`);
33
- db.run(`DELETE FROM memory_items`);
34
29
  db.run(`DELETE FROM memory_summaries`);
35
30
  db.run(`DELETE FROM memory_embeddings`);
36
31
  db.run(`DELETE FROM memory_jobs`);
@@ -52,237 +47,6 @@ describe("wipeConversation", () => {
52
47
  expect(getMessages(conv.id)).toEqual([]);
53
48
  });
54
49
 
55
- test("restores explicitly superseded memory items", async () => {
56
- const convA = createConversation("conversation A");
57
- const msgA = await addMessage(convA.id, "user", "I like blue");
58
-
59
- const convB = createConversation("conversation B");
60
- const msgB = await addMessage(convB.id, "user", "I like red");
61
-
62
- const db = getDb();
63
- const now = Date.now();
64
-
65
- // Insert itemA: active preference about color
66
- db.run(
67
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
68
- VALUES ('itemA', 'active', 'preference', 'color', 'likes blue', 0.8, 'fp-a', 'default', ${now}, ${now})`,
69
- );
70
-
71
- // Insert itemB: active preference about color, supersedes itemA
72
- db.run(
73
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, supersedes, first_seen_at, last_seen_at)
74
- VALUES ('itemB', 'active', 'preference', 'color', 'likes red', 0.9, 'fp-b', 'default', 'itemA', ${now}, ${now})`,
75
- );
76
-
77
- // Mark itemA as superseded by itemB
78
- db.run(
79
- `UPDATE memory_items SET status = 'superseded', superseded_by = 'itemB' WHERE id = 'itemA'`,
80
- );
81
-
82
- // Link itemA to convA's message, itemB to convB's message
83
- db.run(
84
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemA', '${msgA.id}', ${now})`,
85
- );
86
- db.run(
87
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemB', '${msgB.id}', ${now})`,
88
- );
89
-
90
- const result = wipeConversation(convB.id);
91
-
92
- // itemA should be restored to active with superseded_by cleared
93
- const raw = (
94
- getDb() as unknown as {
95
- $client: import("bun:sqlite").Database;
96
- }
97
- ).$client;
98
- const itemARow = raw
99
- .query(
100
- "SELECT status, superseded_by FROM memory_items WHERE id = 'itemA'",
101
- )
102
- .get() as { status: string; superseded_by: string | null } | null;
103
- expect(itemARow).not.toBeNull();
104
- expect(itemARow!.status).toBe("active");
105
- expect(itemARow!.superseded_by).toBeNull();
106
-
107
- // itemB should no longer exist (orphaned and deleted by deleteConversation)
108
- const itemBRow = (
109
- getDb() as unknown as {
110
- $client: import("bun:sqlite").Database;
111
- }
112
- ).$client
113
- .query("SELECT * FROM memory_items WHERE id = 'itemB'")
114
- .get();
115
- expect(itemBRow).toBeNull();
116
-
117
- expect(result.unsupersededItemIds).toContain("itemA");
118
- });
119
-
120
- test("does not restore superseded items when superseding item has other sources", async () => {
121
- const convA = createConversation("conversation A");
122
- const msgA = await addMessage(convA.id, "user", "I like red in A");
123
-
124
- const convB = createConversation("conversation B");
125
- const msgB = await addMessage(convB.id, "user", "I like red in B");
126
-
127
- const db = getDb();
128
- const now = Date.now();
129
-
130
- // Insert itemOld (will be superseded)
131
- db.run(
132
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
133
- VALUES ('itemOld', 'active', 'preference', 'color', 'likes blue', 0.8, 'fp-old', 'default', ${now}, ${now})`,
134
- );
135
-
136
- // Insert itemNew (supersedes itemOld)
137
- db.run(
138
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, supersedes, first_seen_at, last_seen_at)
139
- VALUES ('itemNew', 'active', 'preference', 'color', 'likes red', 0.9, 'fp-new', 'default', 'itemOld', ${now}, ${now})`,
140
- );
141
-
142
- // Mark itemOld as superseded
143
- db.run(
144
- `UPDATE memory_items SET status = 'superseded', superseded_by = 'itemNew' WHERE id = 'itemOld'`,
145
- );
146
-
147
- // Link itemNew to BOTH conversations
148
- db.run(
149
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgA.id}', ${now})`,
150
- );
151
- db.run(
152
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgB.id}', ${now})`,
153
- );
154
-
155
- wipeConversation(convA.id);
156
-
157
- const raw = (
158
- getDb() as unknown as {
159
- $client: import("bun:sqlite").Database;
160
- }
161
- ).$client;
162
-
163
- // itemOld should still be superseded because itemNew has another source (convB)
164
- const itemOldRow = raw
165
- .query("SELECT status FROM memory_items WHERE id = 'itemOld'")
166
- .get() as { status: string } | null;
167
- expect(itemOldRow).not.toBeNull();
168
- expect(itemOldRow!.status).toBe("superseded");
169
-
170
- // itemNew should still exist (has source from convB)
171
- const itemNewRow = raw
172
- .query("SELECT * FROM memory_items WHERE id = 'itemNew'")
173
- .get();
174
- expect(itemNewRow).not.toBeNull();
175
- });
176
-
177
- test("restores orphaned subject-match superseded items", async () => {
178
- const convB = createConversation("conversation B");
179
- const msgB = await addMessage(convB.id, "user", "I use vim");
180
-
181
- const db = getDb();
182
- const now = Date.now();
183
-
184
- // Insert itemOld: superseded with no superseded_by link
185
- db.run(
186
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
187
- VALUES ('itemOld', 'superseded', 'preference', 'editor', 'uses emacs', 0.7, 'fp-old', 'default', ${now}, ${now})`,
188
- );
189
-
190
- // Insert itemNew: active, same kind/subject/scope_id
191
- db.run(
192
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
193
- VALUES ('itemNew', 'active', 'preference', 'editor', 'uses vim', 0.9, 'fp-new', 'default', ${now}, ${now})`,
194
- );
195
-
196
- // Link itemNew to convB's message
197
- db.run(
198
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgB.id}', ${now})`,
199
- );
200
-
201
- wipeConversation(convB.id);
202
-
203
- const raw = (
204
- getDb() as unknown as {
205
- $client: import("bun:sqlite").Database;
206
- }
207
- ).$client;
208
-
209
- // itemOld should now be active (restored as orphaned subject-match superseded item)
210
- const itemOldRow = raw
211
- .query("SELECT status FROM memory_items WHERE id = 'itemOld'")
212
- .get() as { status: string } | null;
213
- expect(itemOldRow).not.toBeNull();
214
- expect(itemOldRow!.status).toBe("active");
215
- });
216
-
217
- test("does not restore superseded items from unrelated conversations", async () => {
218
- // convA has an item that superseded an older item — convA was previously
219
- // deleted via regular deleteConversation, leaving the old item superseded
220
- // with superseded_by = NULL. When we later wipe convB, Step F should NOT
221
- // restore that unrelated item.
222
- const convA = createConversation("conversation A");
223
- const _msgA = await addMessage(convA.id, "user", "I use dark theme");
224
-
225
- const convB = createConversation("conversation B");
226
- const msgB = await addMessage(convB.id, "user", "I use vim");
227
-
228
- const db = getDb();
229
- const now = Date.now();
230
-
231
- // unrelatedOld: superseded item from an old conversation (e.g. "uses light theme")
232
- // Its superseder was deleted in a prior deleteConversation, leaving
233
- // superseded_by = NULL.
234
- db.run(
235
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
236
- VALUES ('unrelatedOld', 'superseded', 'preference', 'theme', 'uses light theme', 0.7, 'fp-unrelated', 'default', ${now}, ${now})`,
237
- );
238
-
239
- // convA's active item that superseded unrelatedOld — we simulate the
240
- // case where convA was already deleted, leaving unrelatedOld with
241
- // superseded_by = NULL and no active replacement.
242
- // (We don't actually insert the superseder — just leave unrelatedOld
243
- // as a superseded item with no superseded_by and no active match.)
244
-
245
- // convB's items — itemOld is superseded by itemNew (subject: editor)
246
- db.run(
247
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
248
- VALUES ('editorOld', 'superseded', 'preference', 'editor', 'uses emacs', 0.7, 'fp-editor-old', 'default', ${now}, ${now})`,
249
- );
250
- db.run(
251
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
252
- VALUES ('editorNew', 'active', 'preference', 'editor', 'uses vim', 0.9, 'fp-editor-new', 'default', ${now}, ${now})`,
253
- );
254
- db.run(
255
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('editorNew', '${msgB.id}', ${now})`,
256
- );
257
-
258
- const result = wipeConversation(convB.id);
259
-
260
- const raw = (
261
- getDb() as unknown as {
262
- $client: import("bun:sqlite").Database;
263
- }
264
- ).$client;
265
-
266
- // editorOld SHOULD be restored (its kind+subject matches an orphaned item from convB)
267
- const editorOldRow = raw
268
- .query("SELECT status FROM memory_items WHERE id = 'editorOld'")
269
- .get() as { status: string } | null;
270
- expect(editorOldRow).not.toBeNull();
271
- expect(editorOldRow!.status).toBe("active");
272
-
273
- // unrelatedOld should NOT be restored — it was superseded by a different
274
- // conversation's item (theme, not editor) and has nothing to do with convB
275
- const unrelatedOldRow = raw
276
- .query("SELECT status FROM memory_items WHERE id = 'unrelatedOld'")
277
- .get() as { status: string } | null;
278
- expect(unrelatedOldRow).not.toBeNull();
279
- expect(unrelatedOldRow!.status).toBe("superseded");
280
-
281
- // Only editorOld should be in the unsuperseded list, not unrelatedOld
282
- expect(result.unsupersededItemIds).toContain("editorOld");
283
- expect(result.unsupersededItemIds).not.toContain("unrelatedOld");
284
- });
285
-
286
50
  test("deletes conversation summaries", async () => {
287
51
  const conv = createConversation("test");
288
52
  await addMessage(conv.id, "user", "hello");
@@ -378,52 +142,14 @@ describe("wipeConversation", () => {
378
142
  expect(result.deletedSummaryIds).toEqual([]);
379
143
  expect(result.cancelledJobCount).toBe(0);
380
144
  });
381
-
382
- test("does not affect other conversations", async () => {
383
- const convA = createConversation("conversation A");
384
- await addMessage(convA.id, "user", "message in A");
385
-
386
- const convB = createConversation("conversation B");
387
- const msgB = await addMessage(convB.id, "user", "message in B");
388
-
389
- const db = getDb();
390
- const now = Date.now();
391
-
392
- // Insert a memory item sourced from convB's message
393
- db.run(
394
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
395
- VALUES ('itemB', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-b', 'default', ${now}, ${now})`,
396
- );
397
- db.run(
398
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemB', '${msgB.id}', ${now})`,
399
- );
400
-
401
- wipeConversation(convA.id);
402
-
403
- // convB should still exist
404
- expect(getConversation(convB.id)).not.toBeNull();
405
- expect(getMessages(convB.id)).toHaveLength(1);
406
-
407
- // convB's memory item should still exist
408
- const raw = (
409
- getDb() as unknown as {
410
- $client: import("bun:sqlite").Database;
411
- }
412
- ).$client;
413
- const itemBRow = raw
414
- .query("SELECT * FROM memory_items WHERE id = 'itemB'")
415
- .get();
416
- expect(itemBRow).not.toBeNull();
417
- });
418
145
  });
419
146
 
420
147
  describe("deleteConversation — private scope cleanup", () => {
421
148
  beforeEach(() => {
422
149
  const db = getDb();
423
150
  db.run(`DELETE FROM conversation_starters`);
424
- db.run(`DELETE FROM memory_item_sources`);
151
+ db.run(`DELETE FROM memory_graph_nodes`);
425
152
  db.run(`DELETE FROM memory_segments`);
426
- db.run(`DELETE FROM memory_items`);
427
153
  db.run(`DELETE FROM memory_summaries`);
428
154
  db.run(`DELETE FROM memory_embeddings`);
429
155
  db.run(`DELETE FROM memory_jobs`);
@@ -433,37 +159,6 @@ describe("deleteConversation — private scope cleanup", () => {
433
159
  db.run(`DELETE FROM conversations`);
434
160
  });
435
161
 
436
- test("sourceless items cleaned up", () => {
437
- const conv = createConversation({ conversationType: "private" });
438
- const scopeId = conv.memoryScopeId;
439
- const now = Date.now();
440
-
441
- const raw = (
442
- getDb() as unknown as {
443
- $client: import("bun:sqlite").Database;
444
- }
445
- ).$client;
446
-
447
- // Insert a memory item with matching scopeId but no memory_item_sources
448
- raw
449
- .query(
450
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
451
- VALUES ('priv-item-1', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-priv-1', ?, ?, ?)`,
452
- )
453
- .run(scopeId, now, now);
454
-
455
- const result = deleteConversation(conv.id);
456
-
457
- // Item should be gone
458
- const itemRow = raw
459
- .query("SELECT * FROM memory_items WHERE id = 'priv-item-1'")
460
- .get();
461
- expect(itemRow).toBeNull();
462
-
463
- // Its ID should be in orphanedItemIds
464
- expect(result.orphanedItemIds).toContain("priv-item-1");
465
- });
466
-
467
162
  test("summaries cleaned up", () => {
468
163
  const conv = createConversation({ conversationType: "private" });
469
164
  const scopeId = conv.memoryScopeId;
@@ -496,71 +191,15 @@ describe("deleteConversation — private scope cleanup", () => {
496
191
  });
497
192
 
498
193
  test("standard conversations unaffected", async () => {
499
- const conv = createConversation("standard test");
500
- const now = Date.now();
194
+ // Create a standard conversation and a private one
195
+ const standardConv = createConversation("standard test");
196
+ const privateConv = createConversation({ conversationType: "private" });
501
197
 
502
- const raw = (
503
- getDb() as unknown as {
504
- $client: import("bun:sqlite").Database;
505
- }
506
- ).$client;
198
+ // Delete the private conversation
199
+ deleteConversation(privateConv.id);
507
200
 
508
- // Insert items with scopeId = "default"
509
- raw
510
- .query(
511
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
512
- VALUES ('default-item-1', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-default', 'default', ?, ?)`,
513
- )
514
- .run(now, now);
515
-
516
- deleteConversation(conv.id);
517
-
518
- // Default-scope items should still exist
519
- const itemRow = raw
520
- .query("SELECT * FROM memory_items WHERE id = 'default-item-1'")
521
- .get();
522
- expect(itemRow).not.toBeNull();
523
- });
524
-
525
- test("embeddings cleaned up", () => {
526
- const conv = createConversation({ conversationType: "private" });
527
- const scopeId = conv.memoryScopeId;
528
- const now = Date.now();
529
-
530
- const raw = (
531
- getDb() as unknown as {
532
- $client: import("bun:sqlite").Database;
533
- }
534
- ).$client;
535
-
536
- // Insert a memory item with matching scopeId
537
- raw
538
- .query(
539
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
540
- VALUES ('priv-item-emb', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-priv-emb', ?, ?, ?)`,
541
- )
542
- .run(scopeId, now, now);
543
-
544
- // Insert a corresponding embedding
545
- raw
546
- .query(
547
- `INSERT INTO memory_embeddings (id, target_type, target_id, provider, model, dimensions, created_at, updated_at)
548
- VALUES ('emb-priv-item', 'item', 'priv-item-emb', 'test', 'test', 384, ?, ?)`,
549
- )
550
- .run(now, now);
551
-
552
- deleteConversation(conv.id);
553
-
554
- // Both item and embedding should be deleted
555
- const itemRow = raw
556
- .query("SELECT * FROM memory_items WHERE id = 'priv-item-emb'")
557
- .get();
558
- expect(itemRow).toBeNull();
559
-
560
- const embeddingRow = raw
561
- .query("SELECT * FROM memory_embeddings WHERE id = 'emb-priv-item'")
562
- .get();
563
- expect(embeddingRow).toBeNull();
201
+ // Standard conversation should still exist
202
+ expect(getConversation(standardConv.id)).not.toBeNull();
564
203
  });
565
204
 
566
205
  test("conversationStarters cleaned up", () => {
@@ -604,40 +243,4 @@ describe("deleteConversation — private scope cleanup", () => {
604
243
  .get();
605
244
  expect(defaultStarterRow).not.toBeNull();
606
245
  });
607
-
608
- test("no duplicate IDs", async () => {
609
- const conv = createConversation({ conversationType: "private" });
610
- const scopeId = conv.memoryScopeId;
611
- const msg = await addMessage(conv.id, "user", "hello");
612
- const now = Date.now();
613
-
614
- const raw = (
615
- getDb() as unknown as {
616
- $client: import("bun:sqlite").Database;
617
- }
618
- ).$client;
619
-
620
- // Insert a memory item with the private scopeId AND a source linking to the message
621
- raw
622
- .query(
623
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
624
- VALUES ('priv-item-dup', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-priv-dup', ?, ?, ?)`,
625
- )
626
- .run(scopeId, now, now);
627
-
628
- raw
629
- .query(
630
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('priv-item-dup', ?, ?)`,
631
- )
632
- .run(msg.id, now);
633
-
634
- const result = deleteConversation(conv.id);
635
-
636
- // The item ID should appear exactly once in orphanedItemIds (caught by
637
- // source-based cleanup, not double-counted by scope sweep).
638
- const count = result.orphanedItemIds.filter(
639
- (id) => id === "priv-item-dup",
640
- ).length;
641
- expect(count).toBe(1);
642
- });
643
246
  });
@@ -180,7 +180,7 @@ describe("CES flags do not affect unrelated flags", () => {
180
180
  expect(isAssistantFeatureFlagEnabled("browser", config)).toBe(true);
181
181
  });
182
182
 
183
- test("enabling all CES flags does not change contacts flag (defaultEnabled: true)", () => {
183
+ test("enabling all CES flags does not change sounds flag (defaultEnabled: true)", () => {
184
184
  const overrides: Record<string, boolean> = {};
185
185
  for (const key of ALL_CES_FLAG_KEYS) {
186
186
  overrides[key] = true;
@@ -188,7 +188,7 @@ describe("CES flags do not affect unrelated flags", () => {
188
188
  _setOverridesForTesting(overrides);
189
189
  const config = makeConfig();
190
190
 
191
- // contacts defaults to true in the registry and should stay true
192
- expect(isAssistantFeatureFlagEnabled("contacts", config)).toBe(true);
191
+ // sounds defaults to true in the registry and should stay true
192
+ expect(isAssistantFeatureFlagEnabled("sounds", config)).toBe(true);
193
193
  });
194
194
  });
@@ -33,8 +33,8 @@ describe("trust class categorization for CES lockdown", () => {
33
33
  expect(isUntrustedTrustClass("unknown")).toBe(true);
34
34
  });
35
35
 
36
- test("undefined is not untrusted", () => {
37
- expect(isUntrustedTrustClass(undefined)).toBe(false);
36
+ test("undefined is untrusted", () => {
37
+ expect(isUntrustedTrustClass(undefined)).toBe(true);
38
38
  });
39
39
  });
40
40
 
@@ -66,7 +66,12 @@ mock.module("../security/secure-keys.js", () => {
66
66
  setSecureKeyAsync: async (key: string, value: string) =>
67
67
  syncSet(key, value),
68
68
  deleteSecureKeyAsync: async (key: string) => syncDelete(key),
69
- listSecureKeysAsync: async () => ({ accounts: [...storedKeys.keys()], unreachable: false }),
69
+ listSecureKeysAsync: async () => ({
70
+ accounts: [...storedKeys.keys()],
71
+ unreachable: false,
72
+ }),
73
+ getProviderKeyAsync: async () => undefined,
74
+ getMaskedProviderKey: async () => null,
70
75
  };
71
76
  });
72
77