@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,4 +1,4 @@
1
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  mock.module("../util/logger.js", () => ({
4
4
  getLogger: () =>
@@ -15,22 +15,17 @@ import {
15
15
  getMessages,
16
16
  wipeConversation,
17
17
  } from "../memory/conversation-crud.js";
18
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
18
+ import { getDb, initializeDb } from "../memory/db.js";
19
19
  import { enqueueMemoryJob } from "../memory/jobs-store.js";
20
20
 
21
21
  // Initialize db once before all tests
22
22
  initializeDb();
23
23
 
24
- afterAll(() => {
25
- resetDb();
26
- });
27
-
28
24
  describe("wipeConversation", () => {
29
25
  beforeEach(() => {
30
26
  const db = getDb();
31
- db.run(`DELETE FROM memory_item_sources`);
27
+ db.run(`DELETE FROM memory_graph_nodes`);
32
28
  db.run(`DELETE FROM memory_segments`);
33
- db.run(`DELETE FROM memory_items`);
34
29
  db.run(`DELETE FROM memory_summaries`);
35
30
  db.run(`DELETE FROM memory_embeddings`);
36
31
  db.run(`DELETE FROM memory_jobs`);
@@ -52,237 +47,6 @@ describe("wipeConversation", () => {
52
47
  expect(getMessages(conv.id)).toEqual([]);
53
48
  });
54
49
 
55
- test("restores explicitly superseded memory items", async () => {
56
- const convA = createConversation("conversation A");
57
- const msgA = await addMessage(convA.id, "user", "I like blue");
58
-
59
- const convB = createConversation("conversation B");
60
- const msgB = await addMessage(convB.id, "user", "I like red");
61
-
62
- const db = getDb();
63
- const now = Date.now();
64
-
65
- // Insert itemA: active preference about color
66
- db.run(
67
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
68
- VALUES ('itemA', 'active', 'preference', 'color', 'likes blue', 0.8, 'fp-a', 'default', ${now}, ${now})`,
69
- );
70
-
71
- // Insert itemB: active preference about color, supersedes itemA
72
- db.run(
73
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, supersedes, first_seen_at, last_seen_at)
74
- VALUES ('itemB', 'active', 'preference', 'color', 'likes red', 0.9, 'fp-b', 'default', 'itemA', ${now}, ${now})`,
75
- );
76
-
77
- // Mark itemA as superseded by itemB
78
- db.run(
79
- `UPDATE memory_items SET status = 'superseded', superseded_by = 'itemB' WHERE id = 'itemA'`,
80
- );
81
-
82
- // Link itemA to convA's message, itemB to convB's message
83
- db.run(
84
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemA', '${msgA.id}', ${now})`,
85
- );
86
- db.run(
87
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemB', '${msgB.id}', ${now})`,
88
- );
89
-
90
- const result = wipeConversation(convB.id);
91
-
92
- // itemA should be restored to active with superseded_by cleared
93
- const raw = (
94
- getDb() as unknown as {
95
- $client: import("bun:sqlite").Database;
96
- }
97
- ).$client;
98
- const itemARow = raw
99
- .query(
100
- "SELECT status, superseded_by FROM memory_items WHERE id = 'itemA'",
101
- )
102
- .get() as { status: string; superseded_by: string | null } | null;
103
- expect(itemARow).not.toBeNull();
104
- expect(itemARow!.status).toBe("active");
105
- expect(itemARow!.superseded_by).toBeNull();
106
-
107
- // itemB should no longer exist (orphaned and deleted by deleteConversation)
108
- const itemBRow = (
109
- getDb() as unknown as {
110
- $client: import("bun:sqlite").Database;
111
- }
112
- ).$client
113
- .query("SELECT * FROM memory_items WHERE id = 'itemB'")
114
- .get();
115
- expect(itemBRow).toBeNull();
116
-
117
- expect(result.unsupersededItemIds).toContain("itemA");
118
- });
119
-
120
- test("does not restore superseded items when superseding item has other sources", async () => {
121
- const convA = createConversation("conversation A");
122
- const msgA = await addMessage(convA.id, "user", "I like red in A");
123
-
124
- const convB = createConversation("conversation B");
125
- const msgB = await addMessage(convB.id, "user", "I like red in B");
126
-
127
- const db = getDb();
128
- const now = Date.now();
129
-
130
- // Insert itemOld (will be superseded)
131
- db.run(
132
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
133
- VALUES ('itemOld', 'active', 'preference', 'color', 'likes blue', 0.8, 'fp-old', 'default', ${now}, ${now})`,
134
- );
135
-
136
- // Insert itemNew (supersedes itemOld)
137
- db.run(
138
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, supersedes, first_seen_at, last_seen_at)
139
- VALUES ('itemNew', 'active', 'preference', 'color', 'likes red', 0.9, 'fp-new', 'default', 'itemOld', ${now}, ${now})`,
140
- );
141
-
142
- // Mark itemOld as superseded
143
- db.run(
144
- `UPDATE memory_items SET status = 'superseded', superseded_by = 'itemNew' WHERE id = 'itemOld'`,
145
- );
146
-
147
- // Link itemNew to BOTH conversations
148
- db.run(
149
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgA.id}', ${now})`,
150
- );
151
- db.run(
152
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgB.id}', ${now})`,
153
- );
154
-
155
- wipeConversation(convA.id);
156
-
157
- const raw = (
158
- getDb() as unknown as {
159
- $client: import("bun:sqlite").Database;
160
- }
161
- ).$client;
162
-
163
- // itemOld should still be superseded because itemNew has another source (convB)
164
- const itemOldRow = raw
165
- .query("SELECT status FROM memory_items WHERE id = 'itemOld'")
166
- .get() as { status: string } | null;
167
- expect(itemOldRow).not.toBeNull();
168
- expect(itemOldRow!.status).toBe("superseded");
169
-
170
- // itemNew should still exist (has source from convB)
171
- const itemNewRow = raw
172
- .query("SELECT * FROM memory_items WHERE id = 'itemNew'")
173
- .get();
174
- expect(itemNewRow).not.toBeNull();
175
- });
176
-
177
- test("restores orphaned subject-match superseded items", async () => {
178
- const convB = createConversation("conversation B");
179
- const msgB = await addMessage(convB.id, "user", "I use vim");
180
-
181
- const db = getDb();
182
- const now = Date.now();
183
-
184
- // Insert itemOld: superseded with no superseded_by link
185
- db.run(
186
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
187
- VALUES ('itemOld', 'superseded', 'preference', 'editor', 'uses emacs', 0.7, 'fp-old', 'default', ${now}, ${now})`,
188
- );
189
-
190
- // Insert itemNew: active, same kind/subject/scope_id
191
- db.run(
192
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
193
- VALUES ('itemNew', 'active', 'preference', 'editor', 'uses vim', 0.9, 'fp-new', 'default', ${now}, ${now})`,
194
- );
195
-
196
- // Link itemNew to convB's message
197
- db.run(
198
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgB.id}', ${now})`,
199
- );
200
-
201
- wipeConversation(convB.id);
202
-
203
- const raw = (
204
- getDb() as unknown as {
205
- $client: import("bun:sqlite").Database;
206
- }
207
- ).$client;
208
-
209
- // itemOld should now be active (restored as orphaned subject-match superseded item)
210
- const itemOldRow = raw
211
- .query("SELECT status FROM memory_items WHERE id = 'itemOld'")
212
- .get() as { status: string } | null;
213
- expect(itemOldRow).not.toBeNull();
214
- expect(itemOldRow!.status).toBe("active");
215
- });
216
-
217
- test("does not restore superseded items from unrelated conversations", async () => {
218
- // convA has an item that superseded an older item — convA was previously
219
- // deleted via regular deleteConversation, leaving the old item superseded
220
- // with superseded_by = NULL. When we later wipe convB, Step F should NOT
221
- // restore that unrelated item.
222
- const convA = createConversation("conversation A");
223
- const _msgA = await addMessage(convA.id, "user", "I use dark theme");
224
-
225
- const convB = createConversation("conversation B");
226
- const msgB = await addMessage(convB.id, "user", "I use vim");
227
-
228
- const db = getDb();
229
- const now = Date.now();
230
-
231
- // unrelatedOld: superseded item from an old conversation (e.g. "uses light theme")
232
- // Its superseder was deleted in a prior deleteConversation, leaving
233
- // superseded_by = NULL.
234
- db.run(
235
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
236
- VALUES ('unrelatedOld', 'superseded', 'preference', 'theme', 'uses light theme', 0.7, 'fp-unrelated', 'default', ${now}, ${now})`,
237
- );
238
-
239
- // convA's active item that superseded unrelatedOld — we simulate the
240
- // case where convA was already deleted, leaving unrelatedOld with
241
- // superseded_by = NULL and no active replacement.
242
- // (We don't actually insert the superseder — just leave unrelatedOld
243
- // as a superseded item with no superseded_by and no active match.)
244
-
245
- // convB's items — itemOld is superseded by itemNew (subject: editor)
246
- db.run(
247
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
248
- VALUES ('editorOld', 'superseded', 'preference', 'editor', 'uses emacs', 0.7, 'fp-editor-old', 'default', ${now}, ${now})`,
249
- );
250
- db.run(
251
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
252
- VALUES ('editorNew', 'active', 'preference', 'editor', 'uses vim', 0.9, 'fp-editor-new', 'default', ${now}, ${now})`,
253
- );
254
- db.run(
255
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('editorNew', '${msgB.id}', ${now})`,
256
- );
257
-
258
- const result = wipeConversation(convB.id);
259
-
260
- const raw = (
261
- getDb() as unknown as {
262
- $client: import("bun:sqlite").Database;
263
- }
264
- ).$client;
265
-
266
- // editorOld SHOULD be restored (its kind+subject matches an orphaned item from convB)
267
- const editorOldRow = raw
268
- .query("SELECT status FROM memory_items WHERE id = 'editorOld'")
269
- .get() as { status: string } | null;
270
- expect(editorOldRow).not.toBeNull();
271
- expect(editorOldRow!.status).toBe("active");
272
-
273
- // unrelatedOld should NOT be restored — it was superseded by a different
274
- // conversation's item (theme, not editor) and has nothing to do with convB
275
- const unrelatedOldRow = raw
276
- .query("SELECT status FROM memory_items WHERE id = 'unrelatedOld'")
277
- .get() as { status: string } | null;
278
- expect(unrelatedOldRow).not.toBeNull();
279
- expect(unrelatedOldRow!.status).toBe("superseded");
280
-
281
- // Only editorOld should be in the unsuperseded list, not unrelatedOld
282
- expect(result.unsupersededItemIds).toContain("editorOld");
283
- expect(result.unsupersededItemIds).not.toContain("unrelatedOld");
284
- });
285
-
286
50
  test("deletes conversation summaries", async () => {
287
51
  const conv = createConversation("test");
288
52
  await addMessage(conv.id, "user", "hello");
@@ -330,7 +94,7 @@ describe("wipeConversation", () => {
330
94
 
331
95
  test("cancels pending memory jobs", async () => {
332
96
  const conv = createConversation("test");
333
- const msg = await addMessage(conv.id, "user", "hello", undefined, {
97
+ await addMessage(conv.id, "user", "hello", undefined, {
334
98
  skipIndexing: true,
335
99
  });
336
100
 
@@ -338,7 +102,7 @@ describe("wipeConversation", () => {
338
102
  const db = getDb();
339
103
  db.run(`DELETE FROM memory_jobs`);
340
104
 
341
- enqueueMemoryJob("extract_items", { messageId: msg.id });
105
+ enqueueMemoryJob("graph_extract", { conversationId: conv.id });
342
106
  enqueueMemoryJob("build_conversation_summary", {
343
107
  conversationId: conv.id,
344
108
  });
@@ -357,8 +121,6 @@ describe("wipeConversation", () => {
357
121
  .all() as Array<{ status: string; last_error: string | null }>;
358
122
 
359
123
  for (const job of jobs) {
360
- // Skip embed_item jobs enqueued by wipeConversation's unsupersede logic
361
- if (job.status === "pending") continue;
362
124
  expect(job.status).toBe("failed");
363
125
  expect(job.last_error).toContain("conversation_wiped");
364
126
  }
@@ -373,57 +135,17 @@ describe("wipeConversation", () => {
373
135
 
374
136
  expect(getConversation(conv.id)).toBeNull();
375
137
  expect(result.segmentIds).toEqual([]);
376
- expect(result.orphanedItemIds).toEqual([]);
377
- expect(result.unsupersededItemIds).toEqual([]);
378
138
  expect(result.deletedSummaryIds).toEqual([]);
379
139
  expect(result.cancelledJobCount).toBe(0);
380
140
  });
381
-
382
- test("does not affect other conversations", async () => {
383
- const convA = createConversation("conversation A");
384
- await addMessage(convA.id, "user", "message in A");
385
-
386
- const convB = createConversation("conversation B");
387
- const msgB = await addMessage(convB.id, "user", "message in B");
388
-
389
- const db = getDb();
390
- const now = Date.now();
391
-
392
- // Insert a memory item sourced from convB's message
393
- db.run(
394
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
395
- VALUES ('itemB', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-b', 'default', ${now}, ${now})`,
396
- );
397
- db.run(
398
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemB', '${msgB.id}', ${now})`,
399
- );
400
-
401
- wipeConversation(convA.id);
402
-
403
- // convB should still exist
404
- expect(getConversation(convB.id)).not.toBeNull();
405
- expect(getMessages(convB.id)).toHaveLength(1);
406
-
407
- // convB's memory item should still exist
408
- const raw = (
409
- getDb() as unknown as {
410
- $client: import("bun:sqlite").Database;
411
- }
412
- ).$client;
413
- const itemBRow = raw
414
- .query("SELECT * FROM memory_items WHERE id = 'itemB'")
415
- .get();
416
- expect(itemBRow).not.toBeNull();
417
- });
418
141
  });
419
142
 
420
143
  describe("deleteConversation — private scope cleanup", () => {
421
144
  beforeEach(() => {
422
145
  const db = getDb();
423
146
  db.run(`DELETE FROM conversation_starters`);
424
- db.run(`DELETE FROM memory_item_sources`);
147
+ db.run(`DELETE FROM memory_graph_nodes`);
425
148
  db.run(`DELETE FROM memory_segments`);
426
- db.run(`DELETE FROM memory_items`);
427
149
  db.run(`DELETE FROM memory_summaries`);
428
150
  db.run(`DELETE FROM memory_embeddings`);
429
151
  db.run(`DELETE FROM memory_jobs`);
@@ -433,37 +155,6 @@ describe("deleteConversation — private scope cleanup", () => {
433
155
  db.run(`DELETE FROM conversations`);
434
156
  });
435
157
 
436
- test("sourceless items cleaned up", () => {
437
- const conv = createConversation({ conversationType: "private" });
438
- const scopeId = conv.memoryScopeId;
439
- const now = Date.now();
440
-
441
- const raw = (
442
- getDb() as unknown as {
443
- $client: import("bun:sqlite").Database;
444
- }
445
- ).$client;
446
-
447
- // Insert a memory item with matching scopeId but no memory_item_sources
448
- raw
449
- .query(
450
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
451
- VALUES ('priv-item-1', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-priv-1', ?, ?, ?)`,
452
- )
453
- .run(scopeId, now, now);
454
-
455
- const result = deleteConversation(conv.id);
456
-
457
- // Item should be gone
458
- const itemRow = raw
459
- .query("SELECT * FROM memory_items WHERE id = 'priv-item-1'")
460
- .get();
461
- expect(itemRow).toBeNull();
462
-
463
- // Its ID should be in orphanedItemIds
464
- expect(result.orphanedItemIds).toContain("priv-item-1");
465
- });
466
-
467
158
  test("summaries cleaned up", () => {
468
159
  const conv = createConversation({ conversationType: "private" });
469
160
  const scopeId = conv.memoryScopeId;
@@ -496,71 +187,15 @@ describe("deleteConversation — private scope cleanup", () => {
496
187
  });
497
188
 
498
189
  test("standard conversations unaffected", async () => {
499
- const conv = createConversation("standard test");
500
- const now = Date.now();
190
+ // Create a standard conversation and a private one
191
+ const standardConv = createConversation("standard test");
192
+ const privateConv = createConversation({ conversationType: "private" });
501
193
 
502
- const raw = (
503
- getDb() as unknown as {
504
- $client: import("bun:sqlite").Database;
505
- }
506
- ).$client;
194
+ // Delete the private conversation
195
+ deleteConversation(privateConv.id);
507
196
 
508
- // Insert items with scopeId = "default"
509
- raw
510
- .query(
511
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
512
- VALUES ('default-item-1', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-default', 'default', ?, ?)`,
513
- )
514
- .run(now, now);
515
-
516
- deleteConversation(conv.id);
517
-
518
- // Default-scope items should still exist
519
- const itemRow = raw
520
- .query("SELECT * FROM memory_items WHERE id = 'default-item-1'")
521
- .get();
522
- expect(itemRow).not.toBeNull();
523
- });
524
-
525
- test("embeddings cleaned up", () => {
526
- const conv = createConversation({ conversationType: "private" });
527
- const scopeId = conv.memoryScopeId;
528
- const now = Date.now();
529
-
530
- const raw = (
531
- getDb() as unknown as {
532
- $client: import("bun:sqlite").Database;
533
- }
534
- ).$client;
535
-
536
- // Insert a memory item with matching scopeId
537
- raw
538
- .query(
539
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
540
- VALUES ('priv-item-emb', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-priv-emb', ?, ?, ?)`,
541
- )
542
- .run(scopeId, now, now);
543
-
544
- // Insert a corresponding embedding
545
- raw
546
- .query(
547
- `INSERT INTO memory_embeddings (id, target_type, target_id, provider, model, dimensions, created_at, updated_at)
548
- VALUES ('emb-priv-item', 'item', 'priv-item-emb', 'test', 'test', 384, ?, ?)`,
549
- )
550
- .run(now, now);
551
-
552
- deleteConversation(conv.id);
553
-
554
- // Both item and embedding should be deleted
555
- const itemRow = raw
556
- .query("SELECT * FROM memory_items WHERE id = 'priv-item-emb'")
557
- .get();
558
- expect(itemRow).toBeNull();
559
-
560
- const embeddingRow = raw
561
- .query("SELECT * FROM memory_embeddings WHERE id = 'emb-priv-item'")
562
- .get();
563
- expect(embeddingRow).toBeNull();
197
+ // Standard conversation should still exist
198
+ expect(getConversation(standardConv.id)).not.toBeNull();
564
199
  });
565
200
 
566
201
  test("conversationStarters cleaned up", () => {
@@ -604,40 +239,4 @@ describe("deleteConversation — private scope cleanup", () => {
604
239
  .get();
605
240
  expect(defaultStarterRow).not.toBeNull();
606
241
  });
607
-
608
- test("no duplicate IDs", async () => {
609
- const conv = createConversation({ conversationType: "private" });
610
- const scopeId = conv.memoryScopeId;
611
- const msg = await addMessage(conv.id, "user", "hello");
612
- const now = Date.now();
613
-
614
- const raw = (
615
- getDb() as unknown as {
616
- $client: import("bun:sqlite").Database;
617
- }
618
- ).$client;
619
-
620
- // Insert a memory item with the private scopeId AND a source linking to the message
621
- raw
622
- .query(
623
- `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
624
- VALUES ('priv-item-dup', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-priv-dup', ?, ?, ?)`,
625
- )
626
- .run(scopeId, now, now);
627
-
628
- raw
629
- .query(
630
- `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('priv-item-dup', ?, ?)`,
631
- )
632
- .run(msg.id, now);
633
-
634
- const result = deleteConversation(conv.id);
635
-
636
- // The item ID should appear exactly once in orphanedItemIds (caught by
637
- // source-based cleanup, not double-counted by scope sweep).
638
- const count = result.orphanedItemIds.filter(
639
- (id) => id === "priv-item-dup",
640
- ).length;
641
- expect(count).toBe(1);
642
- });
643
242
  });
@@ -91,7 +91,7 @@ mock.module("../memory/conversation-crud.js", () => ({
91
91
  updateConversationUsage: () => {},
92
92
  updateConversationTitle: () => {},
93
93
  updateConversationContextWindow: () => {},
94
- deleteMessageById: () => ({ segmentIds: [], orphanedItemIds: [] }),
94
+ deleteMessageById: () => ({ segmentIds: [], deletedSummaryIds: [] }),
95
95
  deleteLastExchange: () => 0,
96
96
  }));
97
97
 
@@ -227,16 +227,13 @@ describe("Conversation workspace cache state", () => {
227
227
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
228
228
  expect(conversation.getWorkspaceTopLevelContext()).not.toBeNull();
229
229
  expect(conversation.getWorkspaceTopLevelContext()!).toContain(
230
- "<workspace_top_level>",
230
+ "<workspace>",
231
231
  );
232
232
  expect(conversation.getWorkspaceTopLevelContext()!).toContain(
233
- "</workspace_top_level>",
233
+ "</workspace>",
234
234
  );
235
235
  expect(conversation.getWorkspaceTopLevelContext()!).toContain(
236
- `Current conversation folder: ${conversationPath}`,
237
- );
238
- expect(conversation.getWorkspaceTopLevelContext()!).toContain(
239
- `Attachment files: ${conversationAttachmentsPath}`,
236
+ `Current conversation attachments: ${conversationAttachmentsPath}`,
240
237
  );
241
238
  });
242
239
 
@@ -267,7 +264,7 @@ describe("Conversation workspace cache state", () => {
267
264
 
268
265
  expect(conversation.getWorkspaceTopLevelContext()).not.toBeNull();
269
266
  expect(conversation.getWorkspaceTopLevelContext()!).toContain(
270
- "<workspace_top_level>",
267
+ "<workspace>",
271
268
  );
272
269
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
273
270
  });
@@ -286,10 +283,7 @@ describe("Conversation workspace cache state", () => {
286
283
  tempConversation.refreshWorkspaceTopLevelContextIfNeeded();
287
284
 
288
285
  expect(tempConversation.getWorkspaceTopLevelContext()!).toContain(
289
- `Current conversation folder: conversations/${legacyDirName}/`,
290
- );
291
- expect(tempConversation.getWorkspaceTopLevelContext()!).toContain(
292
- `Attachment files: conversations/${legacyDirName}/attachments/`,
286
+ `Current conversation attachments: conversations/${legacyDirName}/attachments/`,
293
287
  );
294
288
  } finally {
295
289
  rmSync(workspaceRoot, { recursive: true, force: true });
@@ -1,7 +1,6 @@
1
1
  import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  import type { AgentEvent } from "../agent/loop.js";
4
- import { getConversationDirName } from "../memory/conversation-disk-view.js";
5
4
  import type { Message, ProviderResponse } from "../providers/types.js";
6
5
 
7
6
  // ---------------------------------------------------------------------------
@@ -124,7 +123,7 @@ mock.module("../memory/conversation-crud.js", () => ({
124
123
  updateConversationUsage: () => {},
125
124
  updateConversationTitle: () => {},
126
125
  updateConversationContextWindow: () => {},
127
- deleteMessageById: () => ({ segmentIds: [], orphanedItemIds: [] }),
126
+ deleteMessageById: () => ({ segmentIds: [], deletedSummaryIds: [] }),
128
127
  deleteLastExchange: () => 0,
129
128
  }));
130
129
 
@@ -274,13 +273,6 @@ function messageText(message: Message): string {
274
273
  .join("\n");
275
274
  }
276
275
 
277
- const conversationDirName = getConversationDirName(
278
- "conv-1",
279
- Date.parse("2026-03-19T12:00:00.000Z"),
280
- );
281
- const conversationPath = `conversations/${conversationDirName}/`;
282
- const conversationAttachmentsPath = `${conversationPath}attachments/`;
283
-
284
276
  // ---------------------------------------------------------------------------
285
277
  // Tests
286
278
  // ---------------------------------------------------------------------------
@@ -302,8 +294,8 @@ describe("Conversation workspace injection", () => {
302
294
  const runtimeUser = runCalls[0][runCalls[0].length - 1];
303
295
  expect(runtimeUser.role).toBe("user");
304
296
  const text = messageText(runtimeUser);
305
- expect(text).toContain("<workspace_top_level>");
306
- expect(text).toContain("</workspace_top_level>");
297
+ expect(text).toContain("<workspace>");
298
+ expect(text).toContain("</workspace>");
307
299
  });
308
300
 
309
301
  test("workspace context includes root path and directories", async () => {
@@ -316,8 +308,8 @@ describe("Conversation workspace injection", () => {
316
308
  const runtimeUser = runCalls[0][runCalls[0].length - 1];
317
309
  const text = messageText(runtimeUser);
318
310
  expect(text).toContain("Root: /tmp");
319
- expect(text).toContain(`Current conversation folder: ${conversationPath}`);
320
- expect(text).toContain(`Attachment files: ${conversationAttachmentsPath}`);
311
+ expect(text).toContain("Directories: src, tests, docs");
312
+ expect(text).toContain("Files: README.md, package.json");
321
313
  });
322
314
 
323
315
  test("workspace context is prepended before user text", async () => {
@@ -331,33 +323,40 @@ describe("Conversation workspace injection", () => {
331
323
  const firstBlock = runtimeUser.content[0];
332
324
  expect(firstBlock.type).toBe("text");
333
325
  const firstText = (firstBlock as { type: "text"; text: string }).text;
334
- expect(firstText).toContain("<workspace_top_level>");
326
+ expect(firstText).toContain("<workspace>");
335
327
  });
336
328
 
337
- test("workspace context is stripped from persisted history", async () => {
329
+ test("workspace context persists in history (not stripped between turns)", async () => {
338
330
  const conversation = makeConversation();
339
331
  await conversation.loadFromDb();
340
332
 
341
333
  await conversation.processMessage("Hello", [], () => {});
342
334
 
335
+ // Workspace blocks use <workspace> tag which is intentionally NOT stripped.
343
336
  const persistedMessages = conversation.getMessages();
344
- for (const msg of persistedMessages) {
345
- const text = messageText(msg);
346
- expect(text).not.toContain("<workspace_top_level>");
347
- }
337
+ const userMsg = persistedMessages.find((m) => m.role === "user");
338
+ expect(userMsg).toBeDefined();
339
+ const text = messageText(userMsg!);
340
+ expect(text).toContain("<workspace>");
348
341
  });
349
342
 
350
- test("no empty user messages after stripping workspace context", async () => {
343
+ test("second message does NOT include workspace block", async () => {
351
344
  const conversation = makeConversation();
352
345
  await conversation.loadFromDb();
353
346
 
347
+ // First message — workspace is injected
354
348
  await conversation.processMessage("Hello", [], () => {});
355
-
356
- const persistedMessages = conversation.getMessages();
357
- const emptyUserMsgs = persistedMessages.filter(
358
- (m) => m.role === "user" && m.content.length === 0,
359
- );
360
- expect(emptyUserMsgs).toHaveLength(0);
349
+ expect(runCalls).toHaveLength(1);
350
+ const firstCallUser = runCalls[0][runCalls[0].length - 1];
351
+ const firstText = messageText(firstCallUser);
352
+ expect(firstText).toContain("<workspace>");
353
+
354
+ // Second message — workspace is NOT injected (not first message, no compaction)
355
+ await conversation.processMessage("Follow up", [], () => {});
356
+ expect(runCalls).toHaveLength(2);
357
+ const secondCallUser = runCalls[1][runCalls[1].length - 1];
358
+ const secondText = messageText(secondCallUser);
359
+ expect(secondText).not.toContain("<workspace>");
361
360
  });
362
361
  });
363
362
 
@@ -120,7 +120,7 @@ mock.module("../memory/conversation-crud.js", () => ({
120
120
  updateConversationUsage: () => {},
121
121
  updateConversationTitle: () => {},
122
122
  updateConversationContextWindow: () => {},
123
- deleteMessageById: () => ({ segmentIds: [], orphanedItemIds: [] }),
123
+ deleteMessageById: () => ({ segmentIds: [], deletedSummaryIds: [] }),
124
124
  deleteLastExchange: () => 0,
125
125
  }));
126
126