@vellumai/assistant 0.5.16 → 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 (407) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +0 -3
  3. package/knip.json +2 -1
  4. package/openapi.yaml +660 -80
  5. package/package.json +1 -1
  6. package/src/__tests__/actor-token-service.test.ts +68 -0
  7. package/src/__tests__/agent-loop.test.ts +0 -32
  8. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  9. package/src/__tests__/anthropic-provider.test.ts +57 -3
  10. package/src/__tests__/app-compiler.test.ts +120 -0
  11. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  12. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  13. package/src/__tests__/call-domain.test.ts +2 -6
  14. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  15. package/src/__tests__/call-recovery.test.ts +2 -6
  16. package/src/__tests__/call-routes-http.test.ts +2 -6
  17. package/src/__tests__/call-store.test.ts +2 -6
  18. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  19. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  20. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  21. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  22. package/src/__tests__/checker.test.ts +25 -3
  23. package/src/__tests__/clawhub.test.ts +54 -24
  24. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  25. package/src/__tests__/cli-memory.test.ts +74 -69
  26. package/src/__tests__/config-schema.test.ts +1 -1
  27. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  28. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  29. package/src/__tests__/contacts-tools.test.ts +31 -0
  30. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  31. package/src/__tests__/context-token-estimator.test.ts +175 -10
  32. package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
  33. package/src/__tests__/conversation-agent-loop.test.ts +9 -0
  34. package/src/__tests__/conversation-attachments.test.ts +2 -6
  35. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  36. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  37. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  38. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  39. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  40. package/src/__tests__/conversation-error.test.ts +33 -2
  41. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  42. package/src/__tests__/conversation-history-web-search.test.ts +5 -0
  43. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  44. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  45. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  46. package/src/__tests__/conversation-store.test.ts +2 -6
  47. package/src/__tests__/conversation-usage.test.ts +2 -6
  48. package/src/__tests__/conversation-wipe.test.ts +11 -408
  49. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  50. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  51. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  52. package/src/__tests__/followup-tools.test.ts +2 -6
  53. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  54. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  55. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  56. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  57. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  58. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  59. package/src/__tests__/guardian-action-store.test.ts +2 -6
  60. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  61. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  62. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  63. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  64. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  65. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  66. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  67. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  68. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  69. package/src/__tests__/injection-block.test.ts +154 -0
  70. package/src/__tests__/install-meta.test.ts +506 -0
  71. package/src/__tests__/install-skill-routing.test.ts +292 -0
  72. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  73. package/src/__tests__/invite-routes-http.test.ts +2 -6
  74. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
  75. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  76. package/src/__tests__/llm-context-route-provider.test.ts +2 -6
  77. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
  78. package/src/__tests__/llm-usage-store.test.ts +2 -6
  79. package/src/__tests__/log-export-workspace.test.ts +2 -6
  80. package/src/__tests__/managed-store.test.ts +38 -11
  81. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  82. package/src/__tests__/memory-recall-log-store.test.ts +2 -6
  83. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  84. package/src/__tests__/non-member-access-request.test.ts +2 -6
  85. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  86. package/src/__tests__/oauth-cli.test.ts +364 -2
  87. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  88. package/src/__tests__/outlook-attachments.test.ts +301 -0
  89. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  90. package/src/__tests__/outlook-categories.test.ts +212 -0
  91. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  92. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  93. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  94. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  95. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  96. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  97. package/src/__tests__/outlook-trash.test.ts +77 -0
  98. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  99. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  100. package/src/__tests__/playbook-execution.test.ts +76 -80
  101. package/src/__tests__/playbook-tools.test.ts +5 -7
  102. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  103. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  104. package/src/__tests__/registry.test.ts +2 -2
  105. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  106. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  107. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  108. package/src/__tests__/schedule-store.test.ts +2 -6
  109. package/src/__tests__/schedule-tools.test.ts +2 -6
  110. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  111. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  112. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  113. package/src/__tests__/search-skills-unified.test.ts +421 -0
  114. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  115. package/src/__tests__/send-endpoint-busy.test.ts +2 -6
  116. package/src/__tests__/sequence-store.test.ts +2 -6
  117. package/src/__tests__/server-history-render.test.ts +2 -6
  118. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  119. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  120. package/src/__tests__/skill-load-feature-flag.test.ts +11 -11
  121. package/src/__tests__/skill-memory.test.ts +140 -98
  122. package/src/__tests__/skills-uninstall.test.ts +2 -2
  123. package/src/__tests__/skills.test.ts +1 -1
  124. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  125. package/src/__tests__/task-compiler.test.ts +2 -6
  126. package/src/__tests__/task-management-tools.test.ts +2 -6
  127. package/src/__tests__/task-memory-cleanup.test.ts +173 -229
  128. package/src/__tests__/task-runner.test.ts +2 -6
  129. package/src/__tests__/task-scheduler.test.ts +2 -6
  130. package/src/__tests__/test-preload.ts +3 -0
  131. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  132. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  133. package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
  134. package/src/__tests__/trust-store.test.ts +1 -1
  135. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  136. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -6
  137. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  138. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  139. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  140. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  141. package/src/__tests__/usage-routes.test.ts +2 -6
  142. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  143. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  144. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  145. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  146. package/src/__tests__/volume-security-guard.test.ts +2 -0
  147. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  148. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  149. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  150. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  151. package/src/__tests__/workspace-policy.test.ts +1 -1
  152. package/src/agent/attachments.ts +7 -2
  153. package/src/agent/image-optimize.ts +165 -0
  154. package/src/agent/loop.ts +1 -15
  155. package/src/bundler/app-compiler.ts +179 -2
  156. package/src/bundler/package-resolver.ts +3 -5
  157. package/src/cli/__tests__/notifications.test.ts +1 -2
  158. package/src/cli/cli-memory.ts +67 -64
  159. package/src/cli/commands/avatar.ts +3 -3
  160. package/src/cli/commands/config.ts +26 -13
  161. package/src/cli/commands/doctor.ts +2 -2
  162. package/src/cli/commands/memory.ts +41 -55
  163. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  164. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  165. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  166. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  167. package/src/cli/commands/oauth/connect.ts +11 -6
  168. package/src/cli/commands/oauth/mode.ts +7 -0
  169. package/src/cli/commands/oauth/shared.ts +39 -3
  170. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  171. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  172. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  173. package/src/cli/commands/platform/index.ts +16 -16
  174. package/src/cli/commands/skills.ts +88 -16
  175. package/src/cli/commands/trust.ts +2 -2
  176. package/src/cli/lib/daemon-credential-client.ts +2 -3
  177. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  178. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  179. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  180. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  181. package/src/config/bundled-skills/gmail/SKILL.md +2 -10
  182. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  183. package/src/config/bundled-skills/messaging/SKILL.md +10 -18
  184. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  185. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  186. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  187. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  188. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  189. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  190. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  191. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  192. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  193. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  194. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  195. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  196. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  197. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  198. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  199. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  200. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  201. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  202. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  203. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  204. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  205. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  206. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  207. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  208. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  209. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  210. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  211. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  212. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  213. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  214. package/src/config/bundled-skills/slack/SKILL.md +1 -7
  215. package/src/config/bundled-tool-registry.ts +56 -4
  216. package/src/config/env-registry.ts +15 -8
  217. package/src/config/feature-flag-registry.json +21 -124
  218. package/src/config/schemas/platform.ts +8 -0
  219. package/src/config/schemas/timeouts.ts +1 -1
  220. package/src/config/skills.ts +18 -7
  221. package/src/context/token-estimator.ts +25 -18
  222. package/src/context/window-manager.ts +6 -2
  223. package/src/credential-execution/process-manager.ts +3 -1
  224. package/src/daemon/context-overflow-reducer.ts +46 -2
  225. package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
  226. package/src/daemon/conversation-agent-loop.ts +96 -61
  227. package/src/daemon/conversation-error.ts +31 -8
  228. package/src/daemon/conversation-lifecycle.ts +33 -0
  229. package/src/daemon/conversation-media-retry.ts +85 -7
  230. package/src/daemon/conversation-notifiers.ts +4 -1
  231. package/src/daemon/conversation-runtime-assembly.ts +5 -0
  232. package/src/daemon/conversation.ts +41 -2
  233. package/src/daemon/daemon-control.ts +8 -2
  234. package/src/daemon/handlers/shared.ts +22 -12
  235. package/src/daemon/handlers/skills.ts +416 -202
  236. package/src/daemon/lifecycle.ts +40 -1
  237. package/src/daemon/main.ts +5 -1
  238. package/src/daemon/message-types/conversations.ts +4 -1
  239. package/src/daemon/message-types/messages.ts +3 -1
  240. package/src/daemon/message-types/skills.ts +97 -36
  241. package/src/daemon/providers-setup.ts +5 -0
  242. package/src/daemon/server.ts +11 -2
  243. package/src/daemon/tool-side-effects.ts +27 -5
  244. package/src/heartbeat/heartbeat-service.ts +1 -0
  245. package/src/hooks/cli.ts +2 -2
  246. package/src/hooks/runner.ts +15 -38
  247. package/src/inbound/platform-callback-registration.ts +14 -14
  248. package/src/memory/admin.ts +11 -45
  249. package/src/memory/conversation-bootstrap.ts +2 -0
  250. package/src/memory/conversation-crud.ts +242 -348
  251. package/src/memory/conversation-group-migration.ts +157 -0
  252. package/src/memory/conversation-queries.ts +4 -2
  253. package/src/memory/db-init.ts +30 -3
  254. package/src/memory/embed.ts +73 -0
  255. package/src/memory/embedding-backend.ts +8 -14
  256. package/src/memory/embedding-runtime-manager.ts +12 -114
  257. package/src/memory/fingerprint.ts +2 -2
  258. package/src/memory/graph/bootstrap.ts +512 -0
  259. package/src/memory/graph/capability-seed.ts +297 -0
  260. package/src/memory/graph/consolidation.ts +691 -0
  261. package/src/memory/graph/conversation-graph-memory.ts +630 -0
  262. package/src/memory/graph/decay.test.ts +208 -0
  263. package/src/memory/graph/decay.ts +195 -0
  264. package/src/memory/graph/extraction-job.ts +69 -0
  265. package/src/memory/graph/extraction.test.ts +936 -0
  266. package/src/memory/graph/extraction.ts +1254 -0
  267. package/src/memory/graph/graph-search.ts +266 -0
  268. package/src/memory/graph/image-ref-utils.ts +29 -0
  269. package/src/memory/graph/injection.test.ts +513 -0
  270. package/src/memory/graph/injection.ts +439 -0
  271. package/src/memory/graph/inspect.ts +534 -0
  272. package/src/memory/graph/narrative.ts +267 -0
  273. package/src/memory/graph/pattern-scan.ts +269 -0
  274. package/src/memory/graph/retriever.ts +1008 -0
  275. package/src/memory/graph/scoring.test.ts +548 -0
  276. package/src/memory/graph/scoring.ts +232 -0
  277. package/src/memory/graph/serendipity.ts +65 -0
  278. package/src/memory/graph/store.test.ts +1050 -0
  279. package/src/memory/graph/store.ts +699 -0
  280. package/src/memory/graph/tool-handlers.ts +426 -0
  281. package/src/memory/graph/tools.ts +141 -0
  282. package/src/memory/graph/triggers.test.ts +487 -0
  283. package/src/memory/graph/triggers.ts +223 -0
  284. package/src/memory/graph/types.ts +271 -0
  285. package/src/memory/group-crud.ts +191 -0
  286. package/src/memory/indexer.ts +37 -19
  287. package/src/memory/job-handlers/cleanup.ts +0 -53
  288. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  289. package/src/memory/job-handlers/embedding.ts +5 -31
  290. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  291. package/src/memory/job-handlers/summarization.ts +32 -17
  292. package/src/memory/job-utils.ts +1 -1
  293. package/src/memory/jobs-store.ts +50 -70
  294. package/src/memory/jobs-worker.ts +147 -112
  295. package/src/memory/message-content.ts +1 -0
  296. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  297. package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
  298. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  299. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  300. package/src/memory/migrations/index.ts +4 -0
  301. package/src/memory/migrations/registry.ts +8 -0
  302. package/src/memory/qdrant-client.ts +44 -17
  303. package/src/memory/schema/index.ts +1 -0
  304. package/src/memory/schema/memory-graph.ts +139 -0
  305. package/src/memory/search/semantic.ts +47 -91
  306. package/src/memory/task-memory-cleanup.ts +28 -50
  307. package/src/messaging/providers/outlook/adapter.ts +8 -1
  308. package/src/messaging/providers/outlook/client.ts +299 -0
  309. package/src/messaging/providers/outlook/types.ts +118 -0
  310. package/src/notifications/adapters/macos.ts +1 -0
  311. package/src/notifications/copy-composer.ts +9 -0
  312. package/src/notifications/signal.ts +16 -0
  313. package/src/oauth/seed-providers.ts +2 -1
  314. package/src/permissions/checker.ts +24 -3
  315. package/src/permissions/defaults.ts +4 -4
  316. package/src/permissions/workspace-policy.ts +1 -1
  317. package/src/playbooks/playbook-compiler.ts +19 -18
  318. package/src/playbooks/types.ts +4 -3
  319. package/src/prompts/system-prompt.ts +3 -29
  320. package/src/providers/anthropic/client.ts +47 -19
  321. package/src/providers/gemini/client.ts +1 -1
  322. package/src/providers/openai/client.ts +1 -1
  323. package/src/providers/registry.ts +1 -1
  324. package/src/providers/retry.ts +19 -3
  325. package/src/runtime/actor-trust-resolver.ts +5 -1
  326. package/src/runtime/auth/route-policy.ts +7 -0
  327. package/src/runtime/guardian-reply-router.ts +5 -1
  328. package/src/runtime/http-server.ts +23 -3
  329. package/src/runtime/middleware/auth.ts +20 -0
  330. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  331. package/src/runtime/routes/attachment-routes.ts +106 -16
  332. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  333. package/src/runtime/routes/btw-routes.ts +8 -0
  334. package/src/runtime/routes/conversation-management-routes.ts +2 -0
  335. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  336. package/src/runtime/routes/debug-routes.ts +1 -1
  337. package/src/runtime/routes/global-search-routes.ts +21 -19
  338. package/src/runtime/routes/group-routes.ts +207 -0
  339. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  340. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  341. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  342. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  343. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  344. package/src/runtime/routes/memory-item-routes.test.ts +2 -14
  345. package/src/runtime/routes/memory-item-routes.ts +341 -388
  346. package/src/runtime/routes/schedule-routes.ts +2 -0
  347. package/src/runtime/routes/skills-routes.ts +103 -37
  348. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  349. package/src/schedule/scheduler.ts +8 -1
  350. package/src/security/oauth2.ts +1 -1
  351. package/src/security/secure-keys.ts +4 -8
  352. package/src/shared/provider-env-vars.ts +19 -0
  353. package/src/skills/catalog-cache.ts +5 -0
  354. package/src/skills/catalog-install.ts +15 -14
  355. package/src/skills/clawhub.ts +134 -154
  356. package/src/skills/install-meta.ts +208 -0
  357. package/src/skills/managed-store.ts +27 -16
  358. package/src/skills/skill-memory.ts +152 -77
  359. package/src/skills/skillssh-registry.ts +19 -17
  360. package/src/tasks/task-runner.ts +3 -1
  361. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  362. package/src/tools/browser/runtime-check.ts +3 -1
  363. package/src/tools/memory/register.ts +63 -46
  364. package/src/tools/permission-checker.ts +7 -1
  365. package/src/tools/shared/filesystem/image-read.ts +22 -85
  366. package/src/tools/terminal/safe-env.ts +1 -0
  367. package/src/tools/tool-manifest.ts +3 -3
  368. package/src/util/browser.ts +25 -10
  369. package/src/util/bun-runtime.ts +172 -0
  370. package/src/watcher/providers/outlook-calendar.ts +343 -0
  371. package/src/watcher/providers/outlook.ts +198 -0
  372. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  373. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  374. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  375. package/src/workspace/migrations/registry.ts +6 -0
  376. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  377. package/src/__tests__/journal-context.test.ts +0 -268
  378. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  379. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  380. package/src/__tests__/memory-query-builder.test.ts +0 -59
  381. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  382. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  383. package/src/__tests__/memory-regressions.test.ts +0 -3696
  384. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  385. package/src/daemon/conversation-memory.ts +0 -207
  386. package/src/memory/conversation-starters-cadence.ts +0 -74
  387. package/src/memory/items-extractor.ts +0 -860
  388. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  389. package/src/memory/job-handlers/extraction.ts +0 -40
  390. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  391. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  392. package/src/memory/journal-memory.ts +0 -224
  393. package/src/memory/query-builder.ts +0 -47
  394. package/src/memory/query-expansion.ts +0 -83
  395. package/src/memory/retriever.test.ts +0 -1592
  396. package/src/memory/retriever.ts +0 -1331
  397. package/src/memory/search/formatting.test.ts +0 -140
  398. package/src/memory/search/formatting.ts +0 -262
  399. package/src/memory/search/mmr.ts +0 -139
  400. package/src/memory/search/ranking.ts +0 -15
  401. package/src/memory/search/staleness.ts +0 -40
  402. package/src/memory/search/tier-classifier.ts +0 -18
  403. package/src/memory/search/types.ts +0 -121
  404. package/src/prompts/journal-context.ts +0 -154
  405. package/src/tools/memory/definitions.ts +0 -69
  406. package/src/tools/memory/handlers.test.ts +0 -562
  407. package/src/tools/memory/handlers.ts +0 -434
@@ -12,8 +12,8 @@ const TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
12
12
 
13
13
  let currentConfig: Record<string, unknown> = {};
14
14
 
15
- const DECLARED_SKILL_ID = "contacts";
16
- const DECLARED_FLAG_KEY = "contacts";
15
+ const DECLARED_SKILL_ID = "sounds";
16
+ const DECLARED_FLAG_KEY = "sounds";
17
17
 
18
18
  const noopLogger = new Proxy({} as Record<string, unknown>, {
19
19
  get: (_target, prop) => (prop === "child" ? () => noopLogger : () => {}),
@@ -86,8 +86,8 @@ describe("skill_load feature flag enforcement", () => {
86
86
  test("returns deterministic error for flag OFF skill", async () => {
87
87
  writeSkill(
88
88
  DECLARED_SKILL_ID,
89
- "Contacts",
90
- "Toggle contacts behavior",
89
+ "Sounds",
90
+ "Toggle sounds behavior",
91
91
  "Use the feature.",
92
92
  );
93
93
  writeFileSync(
@@ -107,8 +107,8 @@ describe("skill_load feature flag enforcement", () => {
107
107
  test("loads skill normally when flag is ON", async () => {
108
108
  writeSkill(
109
109
  DECLARED_SKILL_ID,
110
- "Contacts",
111
- "Toggle contacts behavior",
110
+ "Sounds",
111
+ "Toggle sounds behavior",
112
112
  "Use the feature.",
113
113
  );
114
114
  writeFileSync(
@@ -121,14 +121,14 @@ describe("skill_load feature flag enforcement", () => {
121
121
  const result = await executeSkillLoad({ skill: DECLARED_SKILL_ID });
122
122
 
123
123
  expect(result.isError).toBe(false);
124
- expect(result.content).toContain("Skill: Contacts");
124
+ expect(result.content).toContain("Skill: Sounds");
125
125
  });
126
126
 
127
127
  test("loads skill when flag key is absent (registry defaults to enabled)", async () => {
128
128
  writeSkill(
129
129
  DECLARED_SKILL_ID,
130
- "Contacts",
131
- "Toggle contacts behavior",
130
+ "Sounds",
131
+ "Toggle sounds behavior",
132
132
  "Use the feature.",
133
133
  );
134
134
  writeFileSync(
@@ -140,8 +140,8 @@ describe("skill_load feature flag enforcement", () => {
140
140
 
141
141
  const result = await executeSkillLoad({ skill: DECLARED_SKILL_ID });
142
142
 
143
- // contacts is declared in the registry with defaultEnabled: true
143
+ // sounds is declared in the registry with defaultEnabled: true
144
144
  expect(result.isError).toBe(false);
145
- expect(result.content).toContain("Skill: Contacts");
145
+ expect(result.content).toContain("Skill: Sounds");
146
146
  });
147
147
  });
@@ -1,7 +1,7 @@
1
1
  import { rmSync } from "node:fs";
2
2
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
3
3
 
4
- import { eq } from "drizzle-orm";
4
+ import { eq, like } from "drizzle-orm";
5
5
 
6
6
  mock.module("../util/logger.js", () => ({
7
7
  getLogger: () =>
@@ -28,6 +28,16 @@ mock.module("../config/skills.js", () => ({
28
28
  loadSkillCatalog: (..._args: unknown[]) => mockLoadSkillCatalog(),
29
29
  }));
30
30
 
31
+ // Controllable mock for getCachedCatalogSync used by seedCatalogSkillMemories
32
+ let mockGetCachedCatalogSync: () => import("../skills/catalog-install.js").CatalogSkill[] =
33
+ () => [];
34
+
35
+ mock.module("../skills/catalog-cache.js", () => ({
36
+ getCachedCatalogSync: (..._args: unknown[]) => mockGetCachedCatalogSync(),
37
+ getCatalog: async () => mockGetCachedCatalogSync(),
38
+ invalidateCatalogCache: () => {},
39
+ }));
40
+
31
41
  // Controllable mock for isAssistantFeatureFlagEnabled used by resolveSkillStates
32
42
  let mockIsFeatureFlagEnabled: (key: string) => boolean = () => true;
33
43
 
@@ -61,7 +71,7 @@ mock.module("../config/loader.js", () => ({
61
71
 
62
72
  import type { SkillSummary } from "../config/skills.js";
63
73
  import { getDb, initializeDb, resetDb } from "../memory/db.js";
64
- import { memoryItems, memoryJobs } from "../memory/schema.js";
74
+ import { memoryGraphNodes, memoryJobs } from "../memory/schema.js";
65
75
  import {
66
76
  buildCapabilityStatement,
67
77
  deleteSkillCapabilityMemory,
@@ -81,9 +91,8 @@ afterAll(() => {
81
91
 
82
92
  function resetTables() {
83
93
  const db = getDb();
84
- db.run("DELETE FROM memory_item_sources");
85
94
  db.run("DELETE FROM memory_embeddings");
86
- db.run("DELETE FROM memory_items");
95
+ db.run("DELETE FROM memory_graph_nodes");
87
96
  db.run("DELETE FROM memory_jobs");
88
97
  }
89
98
 
@@ -231,59 +240,59 @@ describe("fromSkillSummary", () => {
231
240
  describe("upsertSkillCapabilityMemory", () => {
232
241
  beforeEach(resetTables);
233
242
 
234
- test("inserts with correct kind, subject, confidence, importance", () => {
243
+ test("inserts with correct type, content, confidence, significance", () => {
235
244
  const input = fromSkillSummary(makeSkillSummary());
236
245
  upsertSkillCapabilityMemory("test-skill", input);
237
246
 
238
247
  const db = getDb();
239
- const items = db.select().from(memoryItems).all();
248
+ const items = db.select().from(memoryGraphNodes).all();
240
249
  expect(items).toHaveLength(1);
241
- expect(items[0].kind).toBe("capability");
242
- expect(items[0].subject).toBe("skill:test-skill");
250
+ expect(items[0].type).toBe("procedural");
251
+ expect(items[0].content).toMatch(/^skill:test-skill\n/);
243
252
  expect(items[0].confidence).toBe(1.0);
244
- expect(items[0].importance).toBe(0.7);
245
- expect(items[0].status).toBe("active");
253
+ expect(items[0].significance).toBe(0.7);
254
+ expect(items[0].fidelity).toBe("vivid");
246
255
  expect(items[0].scopeId).toBe("default");
247
256
 
248
- // Should also enqueue an embed_item job
257
+ // Should also enqueue an embed_graph_node job
249
258
  const jobs = db.select().from(memoryJobs).all();
250
259
  expect(jobs).toHaveLength(1);
251
- expect(jobs[0].type).toBe("embed_item");
260
+ expect(jobs[0].type).toBe("embed_graph_node");
252
261
  });
253
262
 
254
- test("is idempotent (same entry only touches lastSeenAt)", () => {
263
+ test("is idempotent (same entry only touches lastAccessed)", () => {
255
264
  const input = fromSkillSummary(makeSkillSummary());
256
265
  upsertSkillCapabilityMemory("test-skill", input);
257
266
 
258
267
  const db = getDb();
259
- const before = db.select().from(memoryItems).all();
268
+ const before = db.select().from(memoryGraphNodes).all();
260
269
  expect(before).toHaveLength(1);
261
- const originalLastSeen = before[0].lastSeenAt;
270
+ const originalLastAccessed = before[0].lastAccessed;
262
271
 
263
272
  // Upsert again
264
273
  upsertSkillCapabilityMemory("test-skill", input);
265
274
 
266
- const after = db.select().from(memoryItems).all();
275
+ const after = db.select().from(memoryGraphNodes).all();
267
276
  expect(after).toHaveLength(1);
268
- // Fingerprint should be the same, so only lastSeenAt changes
269
- expect(after[0].fingerprint).toBe(before[0].fingerprint);
270
- expect(after[0].lastSeenAt).toBeGreaterThanOrEqual(originalLastSeen);
277
+ // Same content, so only lastAccessed changes
278
+ expect(after[0].content).toBe(before[0].content);
279
+ expect(after[0].lastAccessed).toBeGreaterThanOrEqual(originalLastAccessed);
271
280
 
272
281
  // Should NOT enqueue a second embed job (only 1 from initial insert)
273
282
  const jobs = db.select().from(memoryJobs).all();
274
283
  expect(jobs).toHaveLength(1);
275
284
  });
276
285
 
277
- test("updates statement when description changes", () => {
286
+ test("updates content when description changes", () => {
278
287
  const input = fromSkillSummary(
279
288
  makeSkillSummary({ description: "Original description" }),
280
289
  );
281
290
  upsertSkillCapabilityMemory("test-skill", input);
282
291
 
283
292
  const db = getDb();
284
- const before = db.select().from(memoryItems).all();
293
+ const before = db.select().from(memoryGraphNodes).all();
285
294
  expect(before).toHaveLength(1);
286
- expect(before[0].statement).toContain("Original description");
295
+ expect(before[0].content).toContain("Original description");
287
296
 
288
297
  // Change description
289
298
  const updatedInput = fromSkillSummary(
@@ -291,10 +300,10 @@ describe("upsertSkillCapabilityMemory", () => {
291
300
  );
292
301
  upsertSkillCapabilityMemory("test-skill", updatedInput);
293
302
 
294
- const after = db.select().from(memoryItems).all();
303
+ const after = db.select().from(memoryGraphNodes).all();
295
304
  expect(after).toHaveLength(1);
296
- expect(after[0].statement).toContain("Updated description");
297
- expect(after[0].fingerprint).not.toBe(before[0].fingerprint);
305
+ expect(after[0].content).toContain("Updated description");
306
+ expect(after[0].content).not.toBe(before[0].content);
298
307
 
299
308
  // Should enqueue a second embed job
300
309
  const jobs = db.select().from(memoryJobs).all();
@@ -307,13 +316,13 @@ describe("upsertSkillCapabilityMemory", () => {
307
316
 
308
317
  const db = getDb();
309
318
  // Soft-delete the item
310
- db.update(memoryItems)
311
- .set({ status: "deleted" })
312
- .where(eq(memoryItems.subject, "skill:test-skill"))
319
+ db.update(memoryGraphNodes)
320
+ .set({ fidelity: "gone" })
321
+ .where(like(memoryGraphNodes.content, "skill:test-skill\n%"))
313
322
  .run();
314
323
 
315
- const deleted = db.select().from(memoryItems).all();
316
- expect(deleted[0].status).toBe("deleted");
324
+ const deleted = db.select().from(memoryGraphNodes).all();
325
+ expect(deleted[0].fidelity).toBe("gone");
317
326
 
318
327
  // Clear jobs from initial insert
319
328
  db.run("DELETE FROM memory_jobs");
@@ -321,14 +330,14 @@ describe("upsertSkillCapabilityMemory", () => {
321
330
  // Upsert again — should reactivate
322
331
  upsertSkillCapabilityMemory("test-skill", input);
323
332
 
324
- const reactivated = db.select().from(memoryItems).all();
333
+ const reactivated = db.select().from(memoryGraphNodes).all();
325
334
  expect(reactivated).toHaveLength(1);
326
- expect(reactivated[0].status).toBe("active");
335
+ expect(reactivated[0].fidelity).toBe("vivid");
327
336
 
328
337
  // Should enqueue embed job for reactivated item
329
338
  const jobs = db.select().from(memoryJobs).all();
330
339
  expect(jobs).toHaveLength(1);
331
- expect(jobs[0].type).toBe("embed_item");
340
+ expect(jobs[0].type).toBe("embed_graph_node");
332
341
  });
333
342
 
334
343
  test("does not throw on DB error", () => {
@@ -338,9 +347,9 @@ describe("upsertSkillCapabilityMemory", () => {
338
347
  // dropping the table it reads from. Use a fresh DB without initialization.
339
348
  // Instead, verify the try/catch by closing and reopening:
340
349
  // resetDb closes the connection; getDb lazily reconnects.
341
- // We drop the memory_items table to force an error on the next query.
350
+ // We drop the memory_graph_nodes table to force an error on the next query.
342
351
  const db = getDb();
343
- db.run("DROP TABLE IF EXISTS memory_items");
352
+ db.run("DROP TABLE IF EXISTS memory_graph_nodes");
344
353
 
345
354
  expect(() => {
346
355
  upsertSkillCapabilityMemory(
@@ -372,15 +381,15 @@ describe("deleteSkillCapabilityMemory", () => {
372
381
  upsertSkillCapabilityMemory("test-skill", input);
373
382
 
374
383
  const db = getDb();
375
- const before = db.select().from(memoryItems).all();
384
+ const before = db.select().from(memoryGraphNodes).all();
376
385
  expect(before).toHaveLength(1);
377
- expect(before[0].status).toBe("active");
386
+ expect(before[0].fidelity).toBe("vivid");
378
387
 
379
388
  deleteSkillCapabilityMemory("test-skill");
380
389
 
381
- const after = db.select().from(memoryItems).all();
390
+ const after = db.select().from(memoryGraphNodes).all();
382
391
  expect(after).toHaveLength(1);
383
- expect(after[0].status).toBe("deleted");
392
+ expect(after[0].fidelity).toBe("gone");
384
393
  });
385
394
 
386
395
  test("is no-op for missing item", () => {
@@ -390,7 +399,7 @@ describe("deleteSkillCapabilityMemory", () => {
390
399
  }).not.toThrow();
391
400
 
392
401
  const db = getDb();
393
- const items = db.select().from(memoryItems).all();
402
+ const items = db.select().from(memoryGraphNodes).all();
394
403
  expect(items).toHaveLength(0);
395
404
  });
396
405
 
@@ -398,7 +407,7 @@ describe("deleteSkillCapabilityMemory", () => {
398
407
  // Close and reopen DB, then drop the table to force a query error
399
408
  resetDb();
400
409
  const db = getDb();
401
- db.run("DROP TABLE IF EXISTS memory_items");
410
+ db.run("DROP TABLE IF EXISTS memory_graph_nodes");
402
411
 
403
412
  expect(() => {
404
413
  deleteSkillCapabilityMemory("test-skill");
@@ -423,6 +432,10 @@ describe("seedCatalogSkillMemories", () => {
423
432
  // Reset mocks to defaults
424
433
  mockLoadSkillCatalog = () => [];
425
434
  mockIsFeatureFlagEnabled = () => true;
435
+ // Default: non-empty cache so pruning is allowed
436
+ mockGetCachedCatalogSync = () => [
437
+ { id: "_sentinel", name: "_sentinel", description: "" },
438
+ ];
426
439
  });
427
440
 
428
441
  test("upserts capability memories for all enabled skills", () => {
@@ -450,21 +463,21 @@ describe("seedCatalogSkillMemories", () => {
450
463
  const db = getDb();
451
464
  const items = db
452
465
  .select()
453
- .from(memoryItems)
454
- .where(eq(memoryItems.kind, "capability"))
466
+ .from(memoryGraphNodes)
467
+ .where(eq(memoryGraphNodes.type, "procedural"))
455
468
  .all();
456
469
  expect(items).toHaveLength(3);
457
470
 
458
- const subjects = items.map((i) => i.subject).sort();
459
- expect(subjects).toEqual([
471
+ const contentPrefixes = items.map((i) => i.content.split("\n")[0]).sort();
472
+ expect(contentPrefixes).toEqual([
460
473
  "skill:skill-a",
461
474
  "skill:skill-b",
462
475
  "skill:skill-c",
463
476
  ]);
464
477
 
465
- // All should be active
478
+ // All should be vivid
466
479
  for (const item of items) {
467
- expect(item.status).toBe("active");
480
+ expect(item.fidelity).toBe("vivid");
468
481
  }
469
482
  });
470
483
 
@@ -491,16 +504,16 @@ describe("seedCatalogSkillMemories", () => {
491
504
  const db = getDb();
492
505
  const items = db
493
506
  .select()
494
- .from(memoryItems)
495
- .where(eq(memoryItems.kind, "capability"))
507
+ .from(memoryGraphNodes)
508
+ .where(eq(memoryGraphNodes.type, "procedural"))
496
509
  .all();
497
510
  expect(items).toHaveLength(2);
498
511
 
499
- const subjects = items.map((i) => i.subject).sort();
500
- expect(subjects).toEqual(["skill:bundled-skill", "skill:managed-skill"]);
512
+ const contentPrefixes = items.map((i) => i.content.split("\n")[0]).sort();
513
+ expect(contentPrefixes).toEqual(["skill:bundled-skill", "skill:managed-skill"]);
501
514
 
502
515
  for (const item of items) {
503
- expect(item.status).toBe("active");
516
+ expect(item.fidelity).toBe("vivid");
504
517
  }
505
518
  });
506
519
 
@@ -550,14 +563,14 @@ describe("seedCatalogSkillMemories", () => {
550
563
  const db = getDb();
551
564
  const items = db
552
565
  .select()
553
- .from(memoryItems)
554
- .where(eq(memoryItems.kind, "capability"))
566
+ .from(memoryGraphNodes)
567
+ .where(eq(memoryGraphNodes.type, "procedural"))
555
568
  .all();
556
569
 
557
570
  // Only allowed-bundled and managed-skill should be seeded
558
571
  expect(items).toHaveLength(2);
559
- const subjects = items.map((i) => i.subject).sort();
560
- expect(subjects).toEqual(["skill:allowed-bundled", "skill:managed-skill"]);
572
+ const contentPrefixes = items.map((i) => i.content.split("\n")[0]).sort();
573
+ expect(contentPrefixes).toEqual(["skill:allowed-bundled", "skill:managed-skill"]);
561
574
 
562
575
  // Restore default config mock
563
576
  mock.module("../config/loader.js", () => ({
@@ -594,11 +607,11 @@ describe("seedCatalogSkillMemories", () => {
594
607
  const db = getDb();
595
608
  const beforeItems = db
596
609
  .select()
597
- .from(memoryItems)
598
- .where(eq(memoryItems.kind, "capability"))
610
+ .from(memoryGraphNodes)
611
+ .where(eq(memoryGraphNodes.type, "procedural"))
599
612
  .all();
600
613
  expect(beforeItems).toHaveLength(3);
601
- expect(beforeItems.every((i) => i.status === "active")).toBe(true);
614
+ expect(beforeItems.every((i) => i.fidelity === "vivid")).toBe(true);
602
615
 
603
616
  // Now seed with only skill-a — skill-b and skill-c should be pruned
604
617
  mockLoadSkillCatalog = () => [
@@ -612,20 +625,20 @@ describe("seedCatalogSkillMemories", () => {
612
625
 
613
626
  const afterItems = db
614
627
  .select()
615
- .from(memoryItems)
616
- .where(eq(memoryItems.kind, "capability"))
628
+ .from(memoryGraphNodes)
629
+ .where(eq(memoryGraphNodes.type, "procedural"))
617
630
  .all();
618
631
  expect(afterItems).toHaveLength(3); // still 3 rows, but 2 are soft-deleted
619
632
 
620
- const active = afterItems.filter((i) => i.status === "active");
621
- const deleted = afterItems.filter((i) => i.status === "deleted");
633
+ const active = afterItems.filter((i) => i.fidelity === "vivid");
634
+ const deleted = afterItems.filter((i) => i.fidelity === "gone");
622
635
 
623
636
  expect(active).toHaveLength(1);
624
- expect(active[0].subject).toBe("skill:skill-a");
637
+ expect(active[0].content).toMatch(/^skill:skill-a\n/);
625
638
 
626
639
  expect(deleted).toHaveLength(2);
627
- const deletedSubjects = deleted.map((i) => i.subject).sort();
628
- expect(deletedSubjects).toEqual(["skill:skill-b", "skill:skill-c"]);
640
+ const deletedPrefixes = deleted.map((i) => i.content.split("\n")[0]).sort();
641
+ expect(deletedPrefixes).toEqual(["skill:skill-b", "skill:skill-c"]);
629
642
  });
630
643
 
631
644
  test("handles empty catalog without errors", () => {
@@ -636,38 +649,67 @@ describe("seedCatalogSkillMemories", () => {
636
649
  );
637
650
 
638
651
  const db = getDb();
639
- const beforeItems = db.select().from(memoryItems).all();
652
+ const beforeItems = db.select().from(memoryGraphNodes).all();
640
653
  expect(beforeItems).toHaveLength(1);
641
- expect(beforeItems[0].status).toBe("active");
654
+ expect(beforeItems[0].fidelity).toBe("vivid");
642
655
 
643
656
  // Seed with empty catalog
644
657
  mockLoadSkillCatalog = () => [];
645
658
  seedCatalogSkillMemories();
646
659
 
647
660
  // The existing skill should be pruned (soft-deleted)
648
- const afterItems = db.select().from(memoryItems).all();
661
+ const afterItems = db.select().from(memoryGraphNodes).all();
662
+ expect(afterItems).toHaveLength(1);
663
+ expect(afterItems[0].fidelity).toBe("gone");
664
+ });
665
+
666
+ test("does not prune when catalog cache is empty", () => {
667
+ // Pre-populate a skill
668
+ upsertSkillCapabilityMemory(
669
+ "existing-skill",
670
+ fromSkillSummary(makeSkillSummary({ id: "existing-skill" })),
671
+ );
672
+
673
+ const db = getDb();
674
+ const beforeItems = db.select().from(memoryGraphNodes).all();
675
+ expect(beforeItems).toHaveLength(1);
676
+ expect(beforeItems[0].fidelity).toBe("vivid");
677
+
678
+ // Seed with empty catalog AND empty cache — pruning guard should skip
679
+ mockLoadSkillCatalog = () => [];
680
+ mockGetCachedCatalogSync = () => [];
681
+ seedCatalogSkillMemories();
682
+
683
+ // The existing skill should NOT be pruned because the cache is empty
684
+ const afterItems = db.select().from(memoryGraphNodes).all();
649
685
  expect(afterItems).toHaveLength(1);
650
- expect(afterItems[0].status).toBe("deleted");
686
+ expect(afterItems[0].fidelity).toBe("vivid");
651
687
  });
652
688
 
653
689
  test("does not prune non-skill capability memories", () => {
654
690
  // Pre-insert a non-skill capability memory directly into the DB
655
691
  const db = getDb();
656
692
  const now = Date.now();
657
- db.insert(memoryItems)
693
+ db.insert(memoryGraphNodes)
658
694
  .values({
659
695
  id: "cli-doctor-item",
660
- kind: "capability",
661
- subject: "cli:doctor",
662
- statement: "The doctor command diagnoses issues.",
663
- status: "active",
696
+ type: "procedural",
697
+ content: "cli:doctor\nThe doctor command diagnoses issues.",
698
+ fidelity: "vivid",
664
699
  confidence: 1.0,
665
- importance: 0.7,
666
- fingerprint: "cli-doctor-fp",
667
- sourceType: "extraction",
700
+ significance: 0.7,
701
+ sourceType: "inferred",
668
702
  scopeId: "default",
669
- firstSeenAt: now,
670
- lastSeenAt: now,
703
+ created: now,
704
+ lastAccessed: now,
705
+ lastConsolidated: now,
706
+ emotionalCharge: '{"valence":0,"intensity":0.1,"decayCurve":"linear","decayRate":0.05,"originalIntensity":0.1}',
707
+ stability: 14,
708
+ reinforcementCount: 0,
709
+ lastReinforced: now,
710
+ sourceConversations: "[]",
711
+ narrativeRole: null,
712
+ partOfStory: null,
671
713
  })
672
714
  .run();
673
715
 
@@ -677,11 +719,11 @@ describe("seedCatalogSkillMemories", () => {
677
719
 
678
720
  const item = db
679
721
  .select()
680
- .from(memoryItems)
681
- .where(eq(memoryItems.subject, "cli:doctor"))
722
+ .from(memoryGraphNodes)
723
+ .where(like(memoryGraphNodes.content, "cli:doctor\n%"))
682
724
  .get();
683
725
  expect(item).toBeDefined();
684
- expect(item!.status).toBe("active");
726
+ expect(item!.fidelity).toBe("vivid");
685
727
  });
686
728
 
687
729
  test("does not throw when loadSkillCatalog throws", () => {
@@ -717,14 +759,14 @@ describe("seedCatalogSkillMemories", () => {
717
759
  const db = getDb();
718
760
  const items = db
719
761
  .select()
720
- .from(memoryItems)
721
- .where(eq(memoryItems.kind, "capability"))
762
+ .from(memoryGraphNodes)
763
+ .where(eq(memoryGraphNodes.type, "procedural"))
722
764
  .all();
723
765
 
724
766
  // Only the unflagged skill should have a capability row
725
767
  expect(items).toHaveLength(1);
726
- expect(items[0].subject).toBe("skill:unflagged-skill");
727
- expect(items[0].status).toBe("active");
768
+ expect(items[0].content).toMatch(/^skill:unflagged-skill\n/);
769
+ expect(items[0].fidelity).toBe("vivid");
728
770
  });
729
771
 
730
772
  test("prunes pre-existing capability for a skill whose flag becomes disabled", () => {
@@ -749,11 +791,11 @@ describe("seedCatalogSkillMemories", () => {
749
791
  const db = getDb();
750
792
  const beforeItems = db
751
793
  .select()
752
- .from(memoryItems)
753
- .where(eq(memoryItems.kind, "capability"))
794
+ .from(memoryGraphNodes)
795
+ .where(eq(memoryGraphNodes.type, "procedural"))
754
796
  .all();
755
797
  expect(beforeItems).toHaveLength(2);
756
- expect(beforeItems.every((i) => i.status === "active")).toBe(true);
798
+ expect(beforeItems.every((i) => i.fidelity === "vivid")).toBe(true);
757
799
 
758
800
  // Now disable the flag — the flagged skill should be pruned
759
801
  mockIsFeatureFlagEnabled = (key: string) => key !== "my_gated_feature";
@@ -761,19 +803,19 @@ describe("seedCatalogSkillMemories", () => {
761
803
 
762
804
  const afterItems = db
763
805
  .select()
764
- .from(memoryItems)
765
- .where(eq(memoryItems.kind, "capability"))
806
+ .from(memoryGraphNodes)
807
+ .where(eq(memoryGraphNodes.type, "procedural"))
766
808
  .all();
767
809
  expect(afterItems).toHaveLength(2); // still 2 rows, but one soft-deleted
768
810
 
769
- const active = afterItems.filter((i) => i.status === "active");
770
- const deleted = afterItems.filter((i) => i.status === "deleted");
811
+ const active = afterItems.filter((i) => i.fidelity === "vivid");
812
+ const deleted = afterItems.filter((i) => i.fidelity === "gone");
771
813
 
772
814
  expect(active).toHaveLength(1);
773
- expect(active[0].subject).toBe("skill:unflagged-skill");
815
+ expect(active[0].content).toMatch(/^skill:unflagged-skill\n/);
774
816
 
775
817
  expect(deleted).toHaveLength(1);
776
- expect(deleted[0].subject).toBe("skill:flagged-skill");
818
+ expect(deleted[0].content).toMatch(/^skill:flagged-skill\n/);
777
819
  });
778
820
 
779
821
  test("does not throw on DB error during pruning", () => {
@@ -785,10 +827,10 @@ describe("seedCatalogSkillMemories", () => {
785
827
  }),
786
828
  ];
787
829
 
788
- // Drop memory_items to force a DB error during the prune phase
830
+ // Drop memory_graph_nodes to force a DB error during the prune phase
789
831
  resetDb();
790
832
  const db = getDb();
791
- db.run("DROP TABLE IF EXISTS memory_items");
833
+ db.run("DROP TABLE IF EXISTS memory_graph_nodes");
792
834
 
793
835
  expect(() => seedCatalogSkillMemories()).not.toThrow();
794
836
 
@@ -60,7 +60,7 @@ describe("assistant skills uninstall", () => {
60
60
 
61
61
  // GIVEN a skill is installed locally
62
62
  installFakeSkill("weather");
63
- writeSkillsIndex("- weather\n- google-oauth-app-setup\n");
63
+ writeSkillsIndex("- weather\n- vellum-self-knowledge\n");
64
64
 
65
65
  // WHEN we uninstall the skill
66
66
  uninstallSkillLocally("weather");
@@ -73,7 +73,7 @@ describe("assistant skills uninstall", () => {
73
73
  expect(index).not.toContain("weather");
74
74
 
75
75
  // AND other skills should remain in the index
76
- expect(index).toContain("google-oauth-app-setup");
76
+ expect(index).toContain("vellum-self-knowledge");
77
77
  });
78
78
 
79
79
  test("errors when skill is not installed", () => {
@@ -642,7 +642,7 @@ describe("ingress-dependent setup skills declare public-ingress intentionally",
642
642
  "utf-8",
643
643
  );
644
644
  expect(content).toContain("avoid-when:");
645
- expect(content.toLowerCase()).toContain("managed/containerized");
645
+ expect(content.toLowerCase()).toContain("platform-managed");
646
646
  });
647
647
  });
648
648
 
@@ -8,7 +8,7 @@
8
8
  * 4. Notify the guardian of the access attempt
9
9
  * 5. When the user replies with the code in the DM, verify and activate
10
10
  */
11
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
11
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
12
12
 
13
13
  // ---------------------------------------------------------------------------
14
14
  // Test isolation: in-memory SQLite via temp directory
@@ -56,16 +56,12 @@ mock.module("../runtime/gateway-client.js", () => ({
56
56
  }));
57
57
 
58
58
  import { createGuardianBinding } from "../contacts/contacts-write.js";
59
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
59
+ import { getDb, initializeDb } from "../memory/db.js";
60
60
  import { findActiveSession } from "../runtime/channel-verification-service.js";
61
61
  import { handleChannelInbound } from "../runtime/routes/channel-routes.js";
62
62
 
63
63
  initializeDb();
64
64
 
65
- afterAll(() => {
66
- resetDb();
67
- });
68
-
69
65
  // ---------------------------------------------------------------------------
70
66
  // Helpers
71
67
  // ---------------------------------------------------------------------------
@@ -1,4 +1,4 @@
1
- import { afterAll, beforeEach, describe, expect, test } from "bun:test";
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
2
  import { mock } from "bun:test";
3
3
 
4
4
  mock.module("../util/logger.js", () => ({
@@ -21,7 +21,7 @@ mock.module("./indexer.js", () => ({
21
21
 
22
22
  import type { Database } from "bun:sqlite";
23
23
 
24
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
24
+ import { getDb, initializeDb } from "../memory/db.js";
25
25
  import {
26
26
  compileTaskFromConversation,
27
27
  saveCompiledTask,
@@ -31,10 +31,6 @@ import { getTask } from "../tasks/task-store.js";
31
31
 
32
32
  initializeDb();
33
33
 
34
- afterAll(() => {
35
- resetDb();
36
- });
37
-
38
34
  // ── Helpers ──────────────────────────────────────────────────────────
39
35
 
40
36
  function getRawDb(): Database {