@vellumai/assistant 0.5.16 → 0.6.1

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 (592) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +69 -16
  3. package/Dockerfile +2 -5
  4. package/bun.lock +6 -2
  5. package/docker-entrypoint.sh +32 -1
  6. package/docs/architecture/integrations.md +1 -1
  7. package/docs/architecture/memory.md +21 -24
  8. package/knip.json +2 -1
  9. package/openapi.yaml +1198 -83
  10. package/package.json +5 -1
  11. package/src/__tests__/actor-token-service.test.ts +68 -0
  12. package/src/__tests__/agent-loop.test.ts +0 -32
  13. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  14. package/src/__tests__/anthropic-provider.test.ts +217 -98
  15. package/src/__tests__/app-compiler.test.ts +120 -0
  16. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  17. package/src/__tests__/app-executors.test.ts +47 -1
  18. package/src/__tests__/app-source-watcher.test.ts +159 -0
  19. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  20. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  21. package/src/__tests__/call-domain.test.ts +2 -6
  22. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  23. package/src/__tests__/call-recovery.test.ts +2 -6
  24. package/src/__tests__/call-routes-http.test.ts +2 -6
  25. package/src/__tests__/call-store.test.ts +2 -6
  26. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  27. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  28. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  29. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  30. package/src/__tests__/checker.test.ts +63 -9
  31. package/src/__tests__/clawhub.test.ts +54 -24
  32. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  33. package/src/__tests__/config-schema.test.ts +6 -1
  34. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  35. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  36. package/src/__tests__/contacts-tools.test.ts +31 -0
  37. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  38. package/src/__tests__/context-token-estimator.test.ts +175 -10
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +13 -6
  40. package/src/__tests__/conversation-agent-loop.test.ts +13 -51
  41. package/src/__tests__/conversation-attachments.test.ts +2 -6
  42. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  43. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  44. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  45. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  46. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  47. package/src/__tests__/conversation-error.test.ts +33 -2
  48. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  49. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  50. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
  53. package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
  54. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  55. package/src/__tests__/conversation-store.test.ts +2 -6
  56. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
  57. package/src/__tests__/conversation-usage.test.ts +2 -6
  58. package/src/__tests__/conversation-wipe.test.ts +13 -414
  59. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
  60. package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
  61. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  62. package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
  63. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  64. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  65. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  66. package/src/__tests__/date-context.test.ts +76 -210
  67. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
  68. package/src/__tests__/file-list-tool.test.ts +219 -0
  69. package/src/__tests__/first-greeting.test.ts +1 -1
  70. package/src/__tests__/followup-tools.test.ts +2 -6
  71. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  72. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  73. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  74. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  75. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  76. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  77. package/src/__tests__/guardian-action-store.test.ts +2 -6
  78. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  79. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  80. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  81. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  82. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  83. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  84. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  85. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  86. package/src/__tests__/heartbeat-service.test.ts +180 -3
  87. package/src/__tests__/identity-routes.test.ts +328 -0
  88. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  89. package/src/__tests__/injection-block.test.ts +178 -0
  90. package/src/__tests__/install-meta.test.ts +506 -0
  91. package/src/__tests__/install-skill-routing.test.ts +293 -0
  92. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  93. package/src/__tests__/invite-routes-http.test.ts +2 -6
  94. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +17 -28
  95. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  96. package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
  97. package/src/__tests__/llm-context-normalization.test.ts +18 -18
  98. package/src/__tests__/llm-context-route-provider.test.ts +103 -6
  99. package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
  100. package/src/__tests__/llm-usage-store.test.ts +2 -6
  101. package/src/__tests__/log-export-workspace.test.ts +74 -111
  102. package/src/__tests__/managed-store.test.ts +38 -11
  103. package/src/__tests__/mcp-abort-signal.test.ts +5 -0
  104. package/src/__tests__/mcp-client-auth.test.ts +5 -0
  105. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  106. package/src/__tests__/memory-recall-log-store.test.ts +134 -6
  107. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  108. package/src/__tests__/migration-export-streaming.test.ts +304 -0
  109. package/src/__tests__/migration-import-commit-http.test.ts +11 -10
  110. package/src/__tests__/mock-fetch.ts +87 -0
  111. package/src/__tests__/non-member-access-request.test.ts +2 -6
  112. package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
  113. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  114. package/src/__tests__/oauth-cli.test.ts +364 -2
  115. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  116. package/src/__tests__/onboarding-template-contract.test.ts +62 -14
  117. package/src/__tests__/outlook-attachments.test.ts +301 -0
  118. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  119. package/src/__tests__/outlook-categories.test.ts +212 -0
  120. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  121. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  122. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  123. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  124. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  125. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  126. package/src/__tests__/outlook-trash.test.ts +77 -0
  127. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  128. package/src/__tests__/parser.test.ts +32 -0
  129. package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
  130. package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
  131. package/src/__tests__/permission-mode-sse.test.ts +418 -0
  132. package/src/__tests__/permission-mode-store.test.ts +277 -0
  133. package/src/__tests__/permission-mode.test.ts +101 -0
  134. package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
  135. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  136. package/src/__tests__/playbook-execution.test.ts +76 -80
  137. package/src/__tests__/playbook-tools.test.ts +5 -7
  138. package/src/__tests__/profiler-routes.test.ts +502 -0
  139. package/src/__tests__/profiler-run-store.test.ts +441 -0
  140. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  141. package/src/__tests__/proxy-approval-callback.test.ts +4 -75
  142. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  143. package/src/__tests__/registry.test.ts +3 -3
  144. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  145. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  146. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  147. package/src/__tests__/sandbox-host-parity.test.ts +5 -4
  148. package/src/__tests__/schedule-store.test.ts +2 -6
  149. package/src/__tests__/schedule-tools.test.ts +2 -6
  150. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  151. package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
  152. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  153. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  154. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
  155. package/src/__tests__/search-skills-unified.test.ts +422 -0
  156. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  157. package/src/__tests__/send-endpoint-busy.test.ts +44 -9
  158. package/src/__tests__/sequence-store.test.ts +2 -6
  159. package/src/__tests__/server-history-render.test.ts +2 -6
  160. package/src/__tests__/set-permission-mode.test.ts +274 -0
  161. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  162. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  163. package/src/__tests__/skill-load-feature-flag.test.ts +23 -11
  164. package/src/__tests__/skill-memory.test.ts +2 -741
  165. package/src/__tests__/skills-uninstall.test.ts +2 -2
  166. package/src/__tests__/skills.test.ts +1 -1
  167. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  168. package/src/__tests__/strip-memory-injections.test.ts +187 -0
  169. package/src/__tests__/subagent-detail.test.ts +84 -0
  170. package/src/__tests__/subagent-disposal.test.ts +308 -0
  171. package/src/__tests__/subagent-manager-notify.test.ts +19 -10
  172. package/src/__tests__/subagent-notify-parent.test.ts +390 -0
  173. package/src/__tests__/subagent-role-registry.test.ts +108 -0
  174. package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
  175. package/src/__tests__/subagent-tools.test.ts +464 -4
  176. package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
  177. package/src/__tests__/task-compiler.test.ts +2 -6
  178. package/src/__tests__/task-management-tools.test.ts +2 -6
  179. package/src/__tests__/task-memory-cleanup.test.ts +185 -241
  180. package/src/__tests__/task-runner.test.ts +2 -6
  181. package/src/__tests__/task-scheduler.test.ts +2 -6
  182. package/src/__tests__/terminal-tools.test.ts +17 -27
  183. package/src/__tests__/test-preload.ts +7 -0
  184. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  185. package/src/__tests__/tool-executor.test.ts +4 -26
  186. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  187. package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
  188. package/src/__tests__/top-level-renderer.test.ts +10 -13
  189. package/src/__tests__/trust-store.test.ts +1 -1
  190. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  191. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
  192. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  193. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  194. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  195. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  196. package/src/__tests__/usage-routes.test.ts +2 -6
  197. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  198. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  199. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  200. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  201. package/src/__tests__/volume-security-guard.test.ts +2 -0
  202. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  203. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  204. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  205. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  206. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
  207. package/src/__tests__/workspace-policy.test.ts +1 -1
  208. package/src/agent/attachments.ts +7 -2
  209. package/src/agent/image-optimize.ts +165 -0
  210. package/src/agent/loop.ts +7 -15
  211. package/src/approvals/guardian-request-resolvers.ts +24 -0
  212. package/src/avatar/traits-png-sync.ts +3 -3
  213. package/src/bundler/app-compiler.ts +179 -2
  214. package/src/bundler/package-resolver.ts +3 -5
  215. package/src/cli/__tests__/notifications.test.ts +1 -2
  216. package/src/cli/__tests__/run-assistant-command.ts +29 -0
  217. package/src/cli/commands/__tests__/email-download.test.ts +245 -0
  218. package/src/cli/commands/__tests__/email-list.test.ts +192 -0
  219. package/src/cli/commands/__tests__/email-register.test.ts +186 -0
  220. package/src/cli/commands/__tests__/email-send.test.ts +291 -0
  221. package/src/cli/commands/__tests__/email-status.test.ts +181 -0
  222. package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
  223. package/src/cli/commands/__tests__/routes.test.ts +562 -0
  224. package/src/cli/commands/avatar.ts +3 -3
  225. package/src/cli/commands/config.ts +26 -13
  226. package/src/cli/commands/conversations.ts +1 -8
  227. package/src/cli/commands/doctor.ts +2 -2
  228. package/src/cli/commands/email.ts +584 -835
  229. package/src/cli/commands/memory.ts +37 -84
  230. package/src/cli/commands/notifications.ts +7 -2
  231. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  232. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  233. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  234. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  235. package/src/cli/commands/oauth/connect.ts +25 -11
  236. package/src/cli/commands/oauth/mode.ts +7 -0
  237. package/src/cli/commands/oauth/shared.ts +39 -3
  238. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  239. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  240. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  241. package/src/cli/commands/platform/index.ts +16 -16
  242. package/src/cli/commands/routes.ts +396 -0
  243. package/src/cli/commands/skills.ts +218 -36
  244. package/src/cli/commands/trust.ts +2 -2
  245. package/src/cli/lib/daemon-credential-client.ts +2 -3
  246. package/src/cli/program.ts +2 -0
  247. package/src/cli.ts +1 -120
  248. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  249. package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
  250. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  251. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  252. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  253. package/src/config/bundled-skills/gmail/SKILL.md +4 -12
  254. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  255. package/src/config/bundled-skills/messaging/SKILL.md +17 -18
  256. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  257. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  258. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  259. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  260. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  261. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  262. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  263. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  264. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  265. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  266. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  267. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  268. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  269. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  270. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  271. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  272. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  273. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  274. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  275. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  276. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  277. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  278. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  279. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  280. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  281. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  282. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  283. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  284. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  285. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  286. package/src/config/bundled-skills/schedule/SKILL.md +22 -2
  287. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  288. package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
  289. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
  290. package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
  291. package/src/config/bundled-skills/slack/SKILL.md +3 -7
  292. package/src/config/bundled-skills/subagent/SKILL.md +43 -3
  293. package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
  294. package/src/config/bundled-tool-registry.ts +56 -4
  295. package/src/config/env-registry.ts +78 -8
  296. package/src/config/feature-flag-registry.json +38 -125
  297. package/src/config/schema.ts +8 -0
  298. package/src/config/schemas/filing.ts +51 -0
  299. package/src/config/schemas/heartbeat.ts +15 -12
  300. package/src/config/schemas/memory-lifecycle.ts +12 -0
  301. package/src/config/schemas/platform.ts +8 -0
  302. package/src/config/schemas/security.ts +14 -0
  303. package/src/config/schemas/timeouts.ts +1 -1
  304. package/src/config/skills.ts +18 -7
  305. package/src/context/token-estimator.ts +25 -18
  306. package/src/context/window-manager.ts +6 -2
  307. package/src/credential-execution/process-manager.ts +3 -1
  308. package/src/daemon/app-source-watcher.ts +93 -0
  309. package/src/daemon/config-watcher.ts +79 -1
  310. package/src/daemon/context-overflow-reducer.ts +46 -2
  311. package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
  312. package/src/daemon/conversation-agent-loop.ts +236 -108
  313. package/src/daemon/conversation-error.ts +31 -8
  314. package/src/daemon/conversation-history.ts +4 -19
  315. package/src/daemon/conversation-lifecycle.ts +36 -9
  316. package/src/daemon/conversation-media-retry.ts +85 -7
  317. package/src/daemon/conversation-notifiers.ts +4 -1
  318. package/src/daemon/conversation-process.ts +13 -7
  319. package/src/daemon/conversation-runtime-assembly.ts +305 -306
  320. package/src/daemon/conversation-tool-setup.ts +44 -14
  321. package/src/daemon/conversation-workspace.ts +1 -2
  322. package/src/daemon/conversation.ts +59 -2
  323. package/src/daemon/daemon-control.ts +8 -2
  324. package/src/daemon/date-context.ts +26 -53
  325. package/src/daemon/first-greeting.ts +1 -1
  326. package/src/daemon/handlers/conversations.ts +4 -7
  327. package/src/daemon/handlers/shared.test.ts +143 -0
  328. package/src/daemon/handlers/shared.ts +85 -17
  329. package/src/daemon/handlers/skills.ts +416 -209
  330. package/src/daemon/lifecycle.ts +212 -131
  331. package/src/daemon/main.ts +5 -1
  332. package/src/daemon/message-types/conversations.ts +29 -7
  333. package/src/daemon/message-types/messages.ts +12 -2
  334. package/src/daemon/message-types/schedules.ts +1 -0
  335. package/src/daemon/message-types/settings.ts +6 -0
  336. package/src/daemon/message-types/skills.ts +97 -36
  337. package/src/daemon/profiler-run-store.ts +557 -0
  338. package/src/daemon/providers-setup.ts +5 -0
  339. package/src/daemon/server.ts +100 -11
  340. package/src/daemon/shutdown-handlers.ts +5 -0
  341. package/src/daemon/tool-side-effects.ts +50 -8
  342. package/src/export/transcript-formatter.ts +148 -0
  343. package/src/filing/filing-service.ts +228 -0
  344. package/src/heartbeat/heartbeat-service.ts +97 -7
  345. package/src/hooks/cli.ts +2 -2
  346. package/src/hooks/runner.ts +15 -38
  347. package/src/inbound/platform-callback-registration.ts +14 -14
  348. package/src/mcp/client.ts +6 -0
  349. package/src/mcp/mcp-oauth-provider.ts +149 -27
  350. package/src/memory/admin.ts +42 -75
  351. package/src/memory/app-store.ts +69 -0
  352. package/src/memory/conversation-bootstrap.ts +3 -1
  353. package/src/memory/conversation-crud.ts +211 -288
  354. package/src/memory/conversation-group-migration.ts +157 -0
  355. package/src/memory/conversation-queries.ts +61 -13
  356. package/src/memory/conversation-title-service.ts +1 -0
  357. package/src/memory/db-init.ts +194 -361
  358. package/src/memory/embed.ts +73 -0
  359. package/src/memory/embedding-backend.ts +8 -14
  360. package/src/memory/embedding-runtime-manager.ts +12 -114
  361. package/src/memory/fingerprint.ts +2 -2
  362. package/src/memory/graph/bootstrap.ts +521 -0
  363. package/src/memory/graph/capability-seed.ts +449 -0
  364. package/src/memory/graph/consolidation.ts +725 -0
  365. package/src/memory/graph/conversation-graph-memory.ts +659 -0
  366. package/src/memory/graph/decay.test.ts +208 -0
  367. package/src/memory/graph/decay.ts +195 -0
  368. package/src/memory/graph/extraction-job.ts +74 -0
  369. package/src/memory/graph/extraction.test.ts +936 -0
  370. package/src/memory/graph/extraction.ts +1297 -0
  371. package/src/memory/graph/graph-memory-state-store.ts +37 -0
  372. package/src/memory/graph/graph-search.ts +280 -0
  373. package/src/memory/graph/image-ref-utils.ts +29 -0
  374. package/src/memory/graph/injection.test.ts +513 -0
  375. package/src/memory/graph/injection.ts +469 -0
  376. package/src/memory/graph/inspect.ts +543 -0
  377. package/src/memory/graph/narrative.ts +267 -0
  378. package/src/memory/graph/pattern-scan.ts +269 -0
  379. package/src/memory/graph/retriever.ts +1111 -0
  380. package/src/memory/graph/scoring.test.ts +548 -0
  381. package/src/memory/graph/scoring.ts +232 -0
  382. package/src/memory/graph/serendipity.ts +65 -0
  383. package/src/memory/graph/store.test.ts +1098 -0
  384. package/src/memory/graph/store.ts +838 -0
  385. package/src/memory/graph/tool-handlers.ts +301 -0
  386. package/src/memory/graph/tools.ts +97 -0
  387. package/src/memory/graph/triggers.test.ts +487 -0
  388. package/src/memory/graph/triggers.ts +223 -0
  389. package/src/memory/graph/types.ts +295 -0
  390. package/src/memory/group-crud.ts +191 -0
  391. package/src/memory/indexer.ts +37 -19
  392. package/src/memory/job-handlers/cleanup.ts +32 -42
  393. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  394. package/src/memory/job-handlers/embedding.ts +5 -31
  395. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  396. package/src/memory/job-handlers/summarization.ts +32 -17
  397. package/src/memory/job-utils.ts +1 -1
  398. package/src/memory/jobs-store.ts +21 -31
  399. package/src/memory/jobs-worker.ts +180 -129
  400. package/src/memory/llm-request-log-store.ts +96 -12
  401. package/src/memory/memory-recall-log-store.ts +49 -5
  402. package/src/memory/message-content.ts +1 -0
  403. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  404. package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -0
  405. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  406. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  407. package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
  408. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
  409. package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
  410. package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
  411. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
  412. package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
  413. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
  414. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
  415. package/src/memory/migrations/index.ts +12 -0
  416. package/src/memory/migrations/registry.ts +16 -0
  417. package/src/memory/qdrant-client.ts +44 -17
  418. package/src/memory/schema/conversations.ts +14 -0
  419. package/src/memory/schema/index.ts +1 -0
  420. package/src/memory/schema/infrastructure.ts +8 -1
  421. package/src/memory/schema/memory-core.ts +0 -51
  422. package/src/memory/schema/memory-graph.ts +154 -0
  423. package/src/memory/search/semantic.ts +47 -91
  424. package/src/memory/task-memory-cleanup.ts +58 -61
  425. package/src/messaging/providers/outlook/adapter.ts +8 -1
  426. package/src/messaging/providers/outlook/client.ts +299 -0
  427. package/src/messaging/providers/outlook/types.ts +118 -0
  428. package/src/notifications/adapters/macos.ts +1 -0
  429. package/src/notifications/copy-composer.ts +95 -0
  430. package/src/notifications/decision-engine.ts +35 -0
  431. package/src/notifications/signal.ts +16 -0
  432. package/src/oauth/seed-providers.ts +2 -1
  433. package/src/permissions/checker.ts +36 -4
  434. package/src/permissions/defaults.ts +4 -4
  435. package/src/permissions/permission-mode-store.ts +180 -0
  436. package/src/permissions/permission-mode.ts +31 -0
  437. package/src/permissions/workspace-policy.ts +10 -1
  438. package/src/playbooks/playbook-compiler.ts +19 -18
  439. package/src/playbooks/types.ts +4 -3
  440. package/src/prompts/system-prompt.ts +62 -36
  441. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
  442. package/src/prompts/templates/BOOTSTRAP.md +70 -165
  443. package/src/prompts/templates/HEARTBEAT.md +3 -1
  444. package/src/prompts/templates/SOUL.md +25 -4
  445. package/src/prompts/templates/UPDATES.md +8 -0
  446. package/src/providers/anthropic/client.ts +136 -220
  447. package/src/providers/gemini/client.ts +1 -1
  448. package/src/providers/openai/client.ts +1 -1
  449. package/src/providers/registry.ts +1 -1
  450. package/src/providers/retry.ts +19 -3
  451. package/src/runtime/actor-trust-resolver.ts +5 -1
  452. package/src/runtime/auth/route-policy.ts +30 -0
  453. package/src/runtime/guardian-reply-router.ts +5 -1
  454. package/src/runtime/http-server.ts +55 -5
  455. package/src/runtime/http-types.ts +12 -1
  456. package/src/runtime/middleware/auth.ts +20 -0
  457. package/src/runtime/migrations/vbundle-builder.ts +389 -3
  458. package/src/runtime/migrations/vbundle-importer.ts +8 -6
  459. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
  460. package/src/runtime/routes/app-management-routes.ts +1 -11
  461. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
  462. package/src/runtime/routes/archive-utils.ts +29 -0
  463. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  464. package/src/runtime/routes/attachment-routes.ts +106 -16
  465. package/src/runtime/routes/avatar-routes.ts +2 -9
  466. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  467. package/src/runtime/routes/btw-routes.ts +22 -1
  468. package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
  469. package/src/runtime/routes/conversation-management-routes.ts +3 -14
  470. package/src/runtime/routes/conversation-query-routes.ts +49 -3
  471. package/src/runtime/routes/conversation-routes.ts +264 -44
  472. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  473. package/src/runtime/routes/debug-routes.ts +1 -1
  474. package/src/runtime/routes/global-search-routes.ts +21 -19
  475. package/src/runtime/routes/group-routes.ts +207 -0
  476. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  477. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  478. package/src/runtime/routes/heartbeat-routes.ts +4 -10
  479. package/src/runtime/routes/identity-routes.ts +53 -18
  480. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  481. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  482. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  483. package/src/runtime/routes/llm-context-normalization.ts +14 -10
  484. package/src/runtime/routes/log-export-routes.ts +23 -275
  485. package/src/runtime/routes/memory-item-routes.test.ts +170 -247
  486. package/src/runtime/routes/memory-item-routes.ts +341 -388
  487. package/src/runtime/routes/migration-routes.ts +18 -7
  488. package/src/runtime/routes/profiler-routes.ts +350 -0
  489. package/src/runtime/routes/schedule-routes.ts +28 -11
  490. package/src/runtime/routes/settings-routes.ts +95 -8
  491. package/src/runtime/routes/skills-routes.ts +103 -37
  492. package/src/runtime/routes/subagents-routes.ts +28 -7
  493. package/src/runtime/routes/user-route-dispatcher.ts +223 -0
  494. package/src/runtime/routes/user-routes.ts +41 -0
  495. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  496. package/src/runtime/routes/workspace-routes.ts +0 -1
  497. package/src/schedule/schedule-store.ts +30 -0
  498. package/src/schedule/scheduler.ts +52 -18
  499. package/src/security/oauth2.ts +1 -1
  500. package/src/security/secure-keys.ts +4 -8
  501. package/src/shared/provider-env-vars.ts +19 -0
  502. package/src/skills/catalog-cache.ts +5 -0
  503. package/src/skills/catalog-install.ts +25 -16
  504. package/src/skills/clawhub.ts +134 -154
  505. package/src/skills/install-meta.ts +208 -0
  506. package/src/skills/managed-store.ts +29 -18
  507. package/src/skills/skill-memory.ts +12 -229
  508. package/src/skills/skillssh-registry.ts +19 -17
  509. package/src/subagent/index.ts +13 -3
  510. package/src/subagent/manager.ts +308 -29
  511. package/src/subagent/types.ts +68 -0
  512. package/src/tasks/task-runner.ts +7 -5
  513. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  514. package/src/tools/apps/executors.ts +29 -4
  515. package/src/tools/browser/runtime-check.ts +3 -1
  516. package/src/tools/filesystem/list.ts +93 -0
  517. package/src/tools/memory/register.ts +63 -46
  518. package/src/tools/permission-checker.ts +85 -1
  519. package/src/tools/registry.ts +4 -0
  520. package/src/tools/schedule/create.ts +3 -0
  521. package/src/tools/schedule/list.ts +1 -0
  522. package/src/tools/schedule/update.ts +6 -0
  523. package/src/tools/shared/filesystem/errors.ts +5 -0
  524. package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
  525. package/src/tools/shared/filesystem/image-read.ts +22 -85
  526. package/src/tools/shared/filesystem/types.ts +17 -0
  527. package/src/tools/shared/shell-output.ts +31 -2
  528. package/src/tools/subagent/abort.ts +12 -2
  529. package/src/tools/subagent/message.ts +9 -2
  530. package/src/tools/subagent/notify-parent.ts +79 -0
  531. package/src/tools/subagent/read.ts +29 -8
  532. package/src/tools/subagent/resolve.ts +21 -0
  533. package/src/tools/subagent/spawn.ts +2 -0
  534. package/src/tools/subagent/status.ts +11 -1
  535. package/src/tools/system/avatar-generator.ts +3 -3
  536. package/src/tools/system/register.ts +23 -0
  537. package/src/tools/system/set-permission-mode.ts +103 -0
  538. package/src/tools/terminal/parser.ts +30 -5
  539. package/src/tools/terminal/safe-env.ts +17 -1
  540. package/src/tools/tool-manifest.ts +9 -3
  541. package/src/tools/types.ts +2 -0
  542. package/src/util/browser.ts +25 -10
  543. package/src/util/bun-runtime.ts +172 -0
  544. package/src/util/logger.ts +1 -1
  545. package/src/util/platform.ts +50 -17
  546. package/src/watcher/providers/outlook-calendar.ts +343 -0
  547. package/src/watcher/providers/outlook.ts +198 -0
  548. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
  549. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
  550. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  551. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  552. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  553. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
  554. package/src/workspace/migrations/029-seed-pkb.ts +84 -0
  555. package/src/workspace/migrations/registry.ts +10 -0
  556. package/src/workspace/top-level-renderer.ts +5 -9
  557. package/src/__tests__/cli-memory.test.ts +0 -372
  558. package/src/__tests__/clipboard.test.ts +0 -88
  559. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  560. package/src/__tests__/journal-context.test.ts +0 -268
  561. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  562. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  563. package/src/__tests__/memory-query-builder.test.ts +0 -59
  564. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  565. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  566. package/src/__tests__/memory-regressions.test.ts +0 -3696
  567. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  568. package/src/cli/cli-memory.ts +0 -176
  569. package/src/daemon/conversation-memory.ts +0 -207
  570. package/src/memory/conversation-starters-cadence.ts +0 -74
  571. package/src/memory/items-extractor.ts +0 -860
  572. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  573. package/src/memory/job-handlers/extraction.ts +0 -40
  574. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  575. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  576. package/src/memory/journal-memory.ts +0 -224
  577. package/src/memory/query-builder.ts +0 -47
  578. package/src/memory/query-expansion.ts +0 -83
  579. package/src/memory/retriever.test.ts +0 -1592
  580. package/src/memory/retriever.ts +0 -1331
  581. package/src/memory/search/formatting.test.ts +0 -140
  582. package/src/memory/search/formatting.ts +0 -262
  583. package/src/memory/search/mmr.ts +0 -139
  584. package/src/memory/search/ranking.ts +0 -15
  585. package/src/memory/search/staleness.ts +0 -40
  586. package/src/memory/search/tier-classifier.ts +0 -18
  587. package/src/memory/search/types.ts +0 -121
  588. package/src/prompts/journal-context.ts +0 -154
  589. package/src/tools/memory/definitions.ts +0 -69
  590. package/src/tools/memory/handlers.test.ts +0 -562
  591. package/src/tools/memory/handlers.ts +0 -434
  592. package/src/util/clipboard.ts +0 -34
@@ -1,91 +1,7 @@
1
- import { rmSync } from "node:fs";
2
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
3
-
4
- import { eq } from "drizzle-orm";
5
-
6
- mock.module("../util/logger.js", () => ({
7
- getLogger: () =>
8
- new Proxy({} as Record<string, unknown>, {
9
- get: () => () => {},
10
- }),
11
- }));
12
-
13
- mock.module("../memory/qdrant-client.js", () => ({
14
- getQdrantClient: () => ({
15
- searchWithFilter: async () => [],
16
- hybridSearch: async () => [],
17
- upsertPoints: async () => {},
18
- deletePoints: async () => {},
19
- }),
20
- initQdrantClient: () => {},
21
- }));
22
-
23
- // Controllable mock for loadSkillCatalog used by seedCatalogSkillMemories
24
- let mockLoadSkillCatalog: () => import("../config/skills.js").SkillSummary[] =
25
- () => [];
26
-
27
- mock.module("../config/skills.js", () => ({
28
- loadSkillCatalog: (..._args: unknown[]) => mockLoadSkillCatalog(),
29
- }));
30
-
31
- // Controllable mock for isAssistantFeatureFlagEnabled used by resolveSkillStates
32
- let mockIsFeatureFlagEnabled: (key: string) => boolean = () => true;
33
-
34
- mock.module("../config/assistant-feature-flags.js", () => ({
35
- isAssistantFeatureFlagEnabled: (key: string, _config: unknown) =>
36
- mockIsFeatureFlagEnabled(key),
37
- getAssistantFeatureFlagDefaults: () => ({}),
38
- }));
39
-
40
- import { DEFAULT_CONFIG } from "../config/defaults.js";
41
-
42
- const TEST_CONFIG = {
43
- ...DEFAULT_CONFIG,
44
- memory: {
45
- ...DEFAULT_CONFIG.memory,
46
- enabled: true,
47
- extraction: {
48
- ...DEFAULT_CONFIG.memory.extraction,
49
- useLLM: false,
50
- },
51
- },
52
- };
53
-
54
- mock.module("../config/loader.js", () => ({
55
- loadConfig: () => TEST_CONFIG,
56
- getConfig: () => TEST_CONFIG,
57
- loadRawConfig: () => ({}),
58
- saveRawConfig: () => {},
59
- invalidateConfigCache: () => {},
60
- }));
1
+ import { describe, expect, test } from "bun:test";
61
2
 
62
3
  import type { SkillSummary } from "../config/skills.js";
63
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
64
- import { memoryItems, memoryJobs } from "../memory/schema.js";
65
- import {
66
- buildCapabilityStatement,
67
- deleteSkillCapabilityMemory,
68
- fromSkillSummary,
69
- seedCatalogSkillMemories,
70
- type SkillCapabilityInput,
71
- upsertSkillCapabilityMemory,
72
- } from "../skills/skill-memory.js";
73
- import { ensureDataDir, getDbPath } from "../util/platform.js";
74
-
75
- ensureDataDir();
76
- initializeDb();
77
-
78
- afterAll(() => {
79
- resetDb();
80
- });
81
-
82
- function resetTables() {
83
- const db = getDb();
84
- db.run("DELETE FROM memory_item_sources");
85
- db.run("DELETE FROM memory_embeddings");
86
- db.run("DELETE FROM memory_items");
87
- db.run("DELETE FROM memory_jobs");
88
- }
4
+ import { fromSkillSummary } from "../skills/skill-memory.js";
89
5
 
90
6
  function makeSkillSummary(
91
7
  overrides: Partial<SkillSummary> = {},
@@ -102,84 +18,6 @@ function makeSkillSummary(
102
18
  };
103
19
  }
104
20
 
105
- // ─── buildCapabilityStatement ────────────────────────────────────────────────
106
-
107
- describe("buildCapabilityStatement", () => {
108
- test("includes display name, id, and description", () => {
109
- const input: SkillCapabilityInput = {
110
- id: "test-skill",
111
- displayName: "My Skill",
112
- description: "A skill for testing",
113
- };
114
- const result = buildCapabilityStatement(input);
115
- expect(result).toContain('"My Skill"');
116
- expect(result).toContain("(test-skill)");
117
- expect(result).toContain("A skill for testing");
118
- });
119
-
120
- test("includes activation hints when present", () => {
121
- const input: SkillCapabilityInput = {
122
- id: "test-skill",
123
- displayName: "My Skill",
124
- description: "A skill for testing",
125
- activationHints: ["user asks to search", "needs web data"],
126
- };
127
- const result = buildCapabilityStatement(input);
128
- expect(result).toContain("Use when:");
129
- expect(result).toContain("user asks to search");
130
- expect(result).toContain("needs web data");
131
- });
132
-
133
- test("includes avoidWhen routing cues when present", () => {
134
- const input: SkillCapabilityInput = {
135
- id: "test-skill",
136
- displayName: "My Skill",
137
- description: "A skill for testing",
138
- avoidWhen: ["user wants local files only", "offline mode"],
139
- };
140
- const result = buildCapabilityStatement(input);
141
- expect(result).toContain("Avoid when:");
142
- expect(result).toContain("user wants local files only");
143
- expect(result).toContain("offline mode");
144
- });
145
-
146
- test("includes both activationHints and avoidWhen when present", () => {
147
- const input: SkillCapabilityInput = {
148
- id: "test-skill",
149
- displayName: "My Skill",
150
- description: "A skill for testing",
151
- activationHints: ["user asks to search"],
152
- avoidWhen: ["offline mode"],
153
- };
154
- const result = buildCapabilityStatement(input);
155
- expect(result).toContain("Use when: user asks to search.");
156
- expect(result).toContain("Avoid when: offline mode.");
157
- });
158
-
159
- test("works with just name as displayName", () => {
160
- const input: SkillCapabilityInput = {
161
- id: "test-skill",
162
- displayName: "Test Skill",
163
- description: "A skill for testing",
164
- };
165
- const result = buildCapabilityStatement(input);
166
- expect(result).toContain('"Test Skill"');
167
- expect(result).toContain("(test-skill)");
168
- expect(result).toContain("A skill for testing");
169
- });
170
-
171
- test("truncates long statements to 500 chars", () => {
172
- const longDesc = "x".repeat(600);
173
- const input: SkillCapabilityInput = {
174
- id: "test-skill",
175
- displayName: "Test Skill",
176
- description: longDesc,
177
- };
178
- const result = buildCapabilityStatement(input);
179
- expect(result.length).toBe(500);
180
- });
181
- });
182
-
183
21
  // ─── fromSkillSummary ────────────────────────────────────────────────────────
184
22
 
185
23
  describe("fromSkillSummary", () => {
@@ -225,580 +63,3 @@ describe("fromSkillSummary", () => {
225
63
  expect(input.description).toBe("Does amazing things");
226
64
  });
227
65
  });
228
-
229
- // ─── upsertSkillCapabilityMemory ─────────────────────────────────────────────
230
-
231
- describe("upsertSkillCapabilityMemory", () => {
232
- beforeEach(resetTables);
233
-
234
- test("inserts with correct kind, subject, confidence, importance", () => {
235
- const input = fromSkillSummary(makeSkillSummary());
236
- upsertSkillCapabilityMemory("test-skill", input);
237
-
238
- const db = getDb();
239
- const items = db.select().from(memoryItems).all();
240
- expect(items).toHaveLength(1);
241
- expect(items[0].kind).toBe("capability");
242
- expect(items[0].subject).toBe("skill:test-skill");
243
- expect(items[0].confidence).toBe(1.0);
244
- expect(items[0].importance).toBe(0.7);
245
- expect(items[0].status).toBe("active");
246
- expect(items[0].scopeId).toBe("default");
247
-
248
- // Should also enqueue an embed_item job
249
- const jobs = db.select().from(memoryJobs).all();
250
- expect(jobs).toHaveLength(1);
251
- expect(jobs[0].type).toBe("embed_item");
252
- });
253
-
254
- test("is idempotent (same entry only touches lastSeenAt)", () => {
255
- const input = fromSkillSummary(makeSkillSummary());
256
- upsertSkillCapabilityMemory("test-skill", input);
257
-
258
- const db = getDb();
259
- const before = db.select().from(memoryItems).all();
260
- expect(before).toHaveLength(1);
261
- const originalLastSeen = before[0].lastSeenAt;
262
-
263
- // Upsert again
264
- upsertSkillCapabilityMemory("test-skill", input);
265
-
266
- const after = db.select().from(memoryItems).all();
267
- 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);
271
-
272
- // Should NOT enqueue a second embed job (only 1 from initial insert)
273
- const jobs = db.select().from(memoryJobs).all();
274
- expect(jobs).toHaveLength(1);
275
- });
276
-
277
- test("updates statement when description changes", () => {
278
- const input = fromSkillSummary(
279
- makeSkillSummary({ description: "Original description" }),
280
- );
281
- upsertSkillCapabilityMemory("test-skill", input);
282
-
283
- const db = getDb();
284
- const before = db.select().from(memoryItems).all();
285
- expect(before).toHaveLength(1);
286
- expect(before[0].statement).toContain("Original description");
287
-
288
- // Change description
289
- const updatedInput = fromSkillSummary(
290
- makeSkillSummary({ description: "Updated description" }),
291
- );
292
- upsertSkillCapabilityMemory("test-skill", updatedInput);
293
-
294
- const after = db.select().from(memoryItems).all();
295
- expect(after).toHaveLength(1);
296
- expect(after[0].statement).toContain("Updated description");
297
- expect(after[0].fingerprint).not.toBe(before[0].fingerprint);
298
-
299
- // Should enqueue a second embed job
300
- const jobs = db.select().from(memoryJobs).all();
301
- expect(jobs).toHaveLength(2);
302
- });
303
-
304
- test("reactivates soft-deleted items", () => {
305
- const input = fromSkillSummary(makeSkillSummary());
306
- upsertSkillCapabilityMemory("test-skill", input);
307
-
308
- const db = getDb();
309
- // Soft-delete the item
310
- db.update(memoryItems)
311
- .set({ status: "deleted" })
312
- .where(eq(memoryItems.subject, "skill:test-skill"))
313
- .run();
314
-
315
- const deleted = db.select().from(memoryItems).all();
316
- expect(deleted[0].status).toBe("deleted");
317
-
318
- // Clear jobs from initial insert
319
- db.run("DELETE FROM memory_jobs");
320
-
321
- // Upsert again — should reactivate
322
- upsertSkillCapabilityMemory("test-skill", input);
323
-
324
- const reactivated = db.select().from(memoryItems).all();
325
- expect(reactivated).toHaveLength(1);
326
- expect(reactivated[0].status).toBe("active");
327
-
328
- // Should enqueue embed job for reactivated item
329
- const jobs = db.select().from(memoryJobs).all();
330
- expect(jobs).toHaveLength(1);
331
- expect(jobs[0].type).toBe("embed_item");
332
- });
333
-
334
- test("does not throw on DB error", () => {
335
- // Close the DB connection to force errors, then reinitialize
336
- resetDb();
337
- // getDb() will create a new connection, but we can force a DB error by
338
- // dropping the table it reads from. Use a fresh DB without initialization.
339
- // Instead, verify the try/catch by closing and reopening:
340
- // resetDb closes the connection; getDb lazily reconnects.
341
- // We drop the memory_items table to force an error on the next query.
342
- const db = getDb();
343
- db.run("DROP TABLE IF EXISTS memory_items");
344
-
345
- expect(() => {
346
- upsertSkillCapabilityMemory(
347
- "test-skill",
348
- fromSkillSummary(makeSkillSummary()),
349
- );
350
- }).not.toThrow();
351
-
352
- // Restore DB state for subsequent tests.
353
- // Delete the entire DB so initializeDb recreates it from scratch — just
354
- // resetting the connection leaves stale migration checkpoints that skip
355
- // checkpoint-guarded ALTER TABLE migrations (e.g. source_type column).
356
- resetDb();
357
- const dbPath = getDbPath();
358
- for (const ext of ["", "-wal", "-shm"]) {
359
- rmSync(`${dbPath}${ext}`, { force: true });
360
- }
361
- initializeDb();
362
- });
363
- });
364
-
365
- // ─── deleteSkillCapabilityMemory ─────────────────────────────────────────────
366
-
367
- describe("deleteSkillCapabilityMemory", () => {
368
- beforeEach(resetTables);
369
-
370
- test("soft-deletes matching item", () => {
371
- const input = fromSkillSummary(makeSkillSummary());
372
- upsertSkillCapabilityMemory("test-skill", input);
373
-
374
- const db = getDb();
375
- const before = db.select().from(memoryItems).all();
376
- expect(before).toHaveLength(1);
377
- expect(before[0].status).toBe("active");
378
-
379
- deleteSkillCapabilityMemory("test-skill");
380
-
381
- const after = db.select().from(memoryItems).all();
382
- expect(after).toHaveLength(1);
383
- expect(after[0].status).toBe("deleted");
384
- });
385
-
386
- test("is no-op for missing item", () => {
387
- // Should not throw when no matching item exists
388
- expect(() => {
389
- deleteSkillCapabilityMemory("nonexistent-skill");
390
- }).not.toThrow();
391
-
392
- const db = getDb();
393
- const items = db.select().from(memoryItems).all();
394
- expect(items).toHaveLength(0);
395
- });
396
-
397
- test("does not throw on DB error", () => {
398
- // Close and reopen DB, then drop the table to force a query error
399
- resetDb();
400
- const db = getDb();
401
- db.run("DROP TABLE IF EXISTS memory_items");
402
-
403
- expect(() => {
404
- deleteSkillCapabilityMemory("test-skill");
405
- }).not.toThrow();
406
-
407
- // Restore DB state for subsequent tests (see upsert "does not throw" test
408
- // for rationale on why we delete the DB file).
409
- resetDb();
410
- const dbPath = getDbPath();
411
- for (const ext of ["", "-wal", "-shm"]) {
412
- rmSync(`${dbPath}${ext}`, { force: true });
413
- }
414
- initializeDb();
415
- });
416
- });
417
-
418
- // ─── seedCatalogSkillMemories ─────────────────────────────────────────────
419
-
420
- describe("seedCatalogSkillMemories", () => {
421
- beforeEach(() => {
422
- resetTables();
423
- // Reset mocks to defaults
424
- mockLoadSkillCatalog = () => [];
425
- mockIsFeatureFlagEnabled = () => true;
426
- });
427
-
428
- test("upserts capability memories for all enabled skills", () => {
429
- const skills: SkillSummary[] = [
430
- makeSkillSummary({
431
- id: "skill-a",
432
- displayName: "Skill A",
433
- description: "Does A",
434
- }),
435
- makeSkillSummary({
436
- id: "skill-b",
437
- displayName: "Skill B",
438
- description: "Does B",
439
- }),
440
- makeSkillSummary({
441
- id: "skill-c",
442
- displayName: "Skill C",
443
- description: "Does C",
444
- }),
445
- ];
446
- mockLoadSkillCatalog = () => skills;
447
-
448
- seedCatalogSkillMemories();
449
-
450
- const db = getDb();
451
- const items = db
452
- .select()
453
- .from(memoryItems)
454
- .where(eq(memoryItems.kind, "capability"))
455
- .all();
456
- expect(items).toHaveLength(3);
457
-
458
- const subjects = items.map((i) => i.subject).sort();
459
- expect(subjects).toEqual([
460
- "skill:skill-a",
461
- "skill:skill-b",
462
- "skill:skill-c",
463
- ]);
464
-
465
- // All should be active
466
- for (const item of items) {
467
- expect(item.status).toBe("active");
468
- }
469
- });
470
-
471
- test("includes bundled skills in seeded memories", () => {
472
- const skills: SkillSummary[] = [
473
- makeSkillSummary({
474
- id: "managed-skill",
475
- displayName: "Managed",
476
- description: "A managed skill",
477
- source: "managed",
478
- }),
479
- makeSkillSummary({
480
- id: "bundled-skill",
481
- displayName: "Bundled",
482
- description: "A bundled skill",
483
- source: "bundled",
484
- bundled: true,
485
- }),
486
- ];
487
- mockLoadSkillCatalog = () => skills;
488
-
489
- seedCatalogSkillMemories();
490
-
491
- const db = getDb();
492
- const items = db
493
- .select()
494
- .from(memoryItems)
495
- .where(eq(memoryItems.kind, "capability"))
496
- .all();
497
- expect(items).toHaveLength(2);
498
-
499
- const subjects = items.map((i) => i.subject).sort();
500
- expect(subjects).toEqual(["skill:bundled-skill", "skill:managed-skill"]);
501
-
502
- for (const item of items) {
503
- expect(item.status).toBe("active");
504
- }
505
- });
506
-
507
- test("excludes bundled skills filtered by allowBundled config", () => {
508
- const skills: SkillSummary[] = [
509
- makeSkillSummary({
510
- id: "allowed-bundled",
511
- displayName: "Allowed Bundled",
512
- description: "This bundled skill is allowed",
513
- source: "bundled",
514
- bundled: true,
515
- }),
516
- makeSkillSummary({
517
- id: "blocked-bundled",
518
- displayName: "Blocked Bundled",
519
- description: "This bundled skill is not in allowBundled",
520
- source: "bundled",
521
- bundled: true,
522
- }),
523
- makeSkillSummary({
524
- id: "managed-skill",
525
- displayName: "Managed",
526
- description: "A managed skill",
527
- source: "managed",
528
- }),
529
- ];
530
- mockLoadSkillCatalog = () => skills;
531
-
532
- // Override config to set allowBundled to only allow one bundled skill
533
- const configWithAllowBundled = {
534
- ...TEST_CONFIG,
535
- skills: {
536
- ...TEST_CONFIG.skills,
537
- allowBundled: ["allowed-bundled"],
538
- },
539
- };
540
- mock.module("../config/loader.js", () => ({
541
- loadConfig: () => configWithAllowBundled,
542
- getConfig: () => configWithAllowBundled,
543
- loadRawConfig: () => ({}),
544
- saveRawConfig: () => {},
545
- invalidateConfigCache: () => {},
546
- }));
547
-
548
- seedCatalogSkillMemories();
549
-
550
- const db = getDb();
551
- const items = db
552
- .select()
553
- .from(memoryItems)
554
- .where(eq(memoryItems.kind, "capability"))
555
- .all();
556
-
557
- // Only allowed-bundled and managed-skill should be seeded
558
- expect(items).toHaveLength(2);
559
- const subjects = items.map((i) => i.subject).sort();
560
- expect(subjects).toEqual(["skill:allowed-bundled", "skill:managed-skill"]);
561
-
562
- // Restore default config mock
563
- mock.module("../config/loader.js", () => ({
564
- loadConfig: () => TEST_CONFIG,
565
- getConfig: () => TEST_CONFIG,
566
- loadRawConfig: () => ({}),
567
- saveRawConfig: () => {},
568
- invalidateConfigCache: () => {},
569
- }));
570
- });
571
-
572
- test("prunes stale capabilities for skills no longer enabled", () => {
573
- // First seed with three skills
574
- const initialSkills: SkillSummary[] = [
575
- makeSkillSummary({
576
- id: "skill-a",
577
- displayName: "Skill A",
578
- description: "Does A",
579
- }),
580
- makeSkillSummary({
581
- id: "skill-b",
582
- displayName: "Skill B",
583
- description: "Does B",
584
- }),
585
- makeSkillSummary({
586
- id: "skill-c",
587
- displayName: "Skill C",
588
- description: "Does C",
589
- }),
590
- ];
591
- mockLoadSkillCatalog = () => initialSkills;
592
- seedCatalogSkillMemories();
593
-
594
- const db = getDb();
595
- const beforeItems = db
596
- .select()
597
- .from(memoryItems)
598
- .where(eq(memoryItems.kind, "capability"))
599
- .all();
600
- expect(beforeItems).toHaveLength(3);
601
- expect(beforeItems.every((i) => i.status === "active")).toBe(true);
602
-
603
- // Now seed with only skill-a — skill-b and skill-c should be pruned
604
- mockLoadSkillCatalog = () => [
605
- makeSkillSummary({
606
- id: "skill-a",
607
- displayName: "Skill A",
608
- description: "Does A",
609
- }),
610
- ];
611
- seedCatalogSkillMemories();
612
-
613
- const afterItems = db
614
- .select()
615
- .from(memoryItems)
616
- .where(eq(memoryItems.kind, "capability"))
617
- .all();
618
- expect(afterItems).toHaveLength(3); // still 3 rows, but 2 are soft-deleted
619
-
620
- const active = afterItems.filter((i) => i.status === "active");
621
- const deleted = afterItems.filter((i) => i.status === "deleted");
622
-
623
- expect(active).toHaveLength(1);
624
- expect(active[0].subject).toBe("skill:skill-a");
625
-
626
- expect(deleted).toHaveLength(2);
627
- const deletedSubjects = deleted.map((i) => i.subject).sort();
628
- expect(deletedSubjects).toEqual(["skill:skill-b", "skill:skill-c"]);
629
- });
630
-
631
- test("handles empty catalog without errors", () => {
632
- // Pre-populate a skill so we can verify it gets pruned
633
- upsertSkillCapabilityMemory(
634
- "existing-skill",
635
- fromSkillSummary(makeSkillSummary({ id: "existing-skill" })),
636
- );
637
-
638
- const db = getDb();
639
- const beforeItems = db.select().from(memoryItems).all();
640
- expect(beforeItems).toHaveLength(1);
641
- expect(beforeItems[0].status).toBe("active");
642
-
643
- // Seed with empty catalog
644
- mockLoadSkillCatalog = () => [];
645
- seedCatalogSkillMemories();
646
-
647
- // The existing skill should be pruned (soft-deleted)
648
- const afterItems = db.select().from(memoryItems).all();
649
- expect(afterItems).toHaveLength(1);
650
- expect(afterItems[0].status).toBe("deleted");
651
- });
652
-
653
- test("does not prune non-skill capability memories", () => {
654
- // Pre-insert a non-skill capability memory directly into the DB
655
- const db = getDb();
656
- const now = Date.now();
657
- db.insert(memoryItems)
658
- .values({
659
- id: "cli-doctor-item",
660
- kind: "capability",
661
- subject: "cli:doctor",
662
- statement: "The doctor command diagnoses issues.",
663
- status: "active",
664
- confidence: 1.0,
665
- importance: 0.7,
666
- fingerprint: "cli-doctor-fp",
667
- sourceType: "extraction",
668
- scopeId: "default",
669
- firstSeenAt: now,
670
- lastSeenAt: now,
671
- })
672
- .run();
673
-
674
- // Seed with empty catalog — skill pruner runs but should skip cli:* items
675
- mockLoadSkillCatalog = () => [];
676
- seedCatalogSkillMemories();
677
-
678
- const item = db
679
- .select()
680
- .from(memoryItems)
681
- .where(eq(memoryItems.subject, "cli:doctor"))
682
- .get();
683
- expect(item).toBeDefined();
684
- expect(item!.status).toBe("active");
685
- });
686
-
687
- test("does not throw when loadSkillCatalog throws", () => {
688
- mockLoadSkillCatalog = () => {
689
- throw new Error("Catalog load failure");
690
- };
691
-
692
- // Best-effort: should not propagate the error
693
- expect(() => seedCatalogSkillMemories()).not.toThrow();
694
- });
695
-
696
- test("skips skills whose feature flag is disabled", () => {
697
- const skills: SkillSummary[] = [
698
- makeSkillSummary({
699
- id: "unflagged-skill",
700
- displayName: "Unflagged",
701
- description: "No flag",
702
- }),
703
- makeSkillSummary({
704
- id: "flagged-skill",
705
- displayName: "Flagged",
706
- description: "Has flag",
707
- featureFlag: "my_gated_feature",
708
- }),
709
- ];
710
- mockLoadSkillCatalog = () => skills;
711
-
712
- // Disable the feature flag for the flagged skill
713
- mockIsFeatureFlagEnabled = (key: string) => key !== "my_gated_feature";
714
-
715
- seedCatalogSkillMemories();
716
-
717
- const db = getDb();
718
- const items = db
719
- .select()
720
- .from(memoryItems)
721
- .where(eq(memoryItems.kind, "capability"))
722
- .all();
723
-
724
- // Only the unflagged skill should have a capability row
725
- expect(items).toHaveLength(1);
726
- expect(items[0].subject).toBe("skill:unflagged-skill");
727
- expect(items[0].status).toBe("active");
728
- });
729
-
730
- test("prunes pre-existing capability for a skill whose flag becomes disabled", () => {
731
- // First seed with both skills, all flags enabled
732
- const skills: SkillSummary[] = [
733
- makeSkillSummary({
734
- id: "unflagged-skill",
735
- displayName: "Unflagged",
736
- description: "No flag",
737
- }),
738
- makeSkillSummary({
739
- id: "flagged-skill",
740
- displayName: "Flagged",
741
- description: "Has flag",
742
- featureFlag: "my_gated_feature",
743
- }),
744
- ];
745
- mockLoadSkillCatalog = () => skills;
746
- mockIsFeatureFlagEnabled = () => true;
747
- seedCatalogSkillMemories();
748
-
749
- const db = getDb();
750
- const beforeItems = db
751
- .select()
752
- .from(memoryItems)
753
- .where(eq(memoryItems.kind, "capability"))
754
- .all();
755
- expect(beforeItems).toHaveLength(2);
756
- expect(beforeItems.every((i) => i.status === "active")).toBe(true);
757
-
758
- // Now disable the flag — the flagged skill should be pruned
759
- mockIsFeatureFlagEnabled = (key: string) => key !== "my_gated_feature";
760
- seedCatalogSkillMemories();
761
-
762
- const afterItems = db
763
- .select()
764
- .from(memoryItems)
765
- .where(eq(memoryItems.kind, "capability"))
766
- .all();
767
- expect(afterItems).toHaveLength(2); // still 2 rows, but one soft-deleted
768
-
769
- const active = afterItems.filter((i) => i.status === "active");
770
- const deleted = afterItems.filter((i) => i.status === "deleted");
771
-
772
- expect(active).toHaveLength(1);
773
- expect(active[0].subject).toBe("skill:unflagged-skill");
774
-
775
- expect(deleted).toHaveLength(1);
776
- expect(deleted[0].subject).toBe("skill:flagged-skill");
777
- });
778
-
779
- test("does not throw on DB error during pruning", () => {
780
- mockLoadSkillCatalog = () => [
781
- makeSkillSummary({
782
- id: "skill-a",
783
- displayName: "Skill A",
784
- description: "Does A",
785
- }),
786
- ];
787
-
788
- // Drop memory_items to force a DB error during the prune phase
789
- resetDb();
790
- const db = getDb();
791
- db.run("DROP TABLE IF EXISTS memory_items");
792
-
793
- expect(() => seedCatalogSkillMemories()).not.toThrow();
794
-
795
- // Restore DB state for subsequent tests (see upsert "does not throw" test
796
- // for rationale on why we delete the DB file).
797
- resetDb();
798
- const dbPath = getDbPath();
799
- for (const ext of ["", "-wal", "-shm"]) {
800
- rmSync(`${dbPath}${ext}`, { force: true });
801
- }
802
- initializeDb();
803
- });
804
- });