@vellumai/assistant 0.5.15 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (503) hide show
  1. package/ARCHITECTURE.md +3 -3
  2. package/Dockerfile +0 -3
  3. package/docs/architecture/integrations.md +15 -14
  4. package/knip.json +4 -1
  5. package/openapi.yaml +670 -122
  6. package/package.json +1 -1
  7. package/src/__tests__/actor-token-service.test.ts +68 -0
  8. package/src/__tests__/agent-loop.test.ts +0 -32
  9. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  10. package/src/__tests__/anthropic-provider.test.ts +57 -3
  11. package/src/__tests__/app-compiler.test.ts +120 -0
  12. package/src/__tests__/assistant-feature-flags-integration.test.ts +5 -377
  13. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  14. package/src/__tests__/call-domain.test.ts +2 -6
  15. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  16. package/src/__tests__/call-recovery.test.ts +2 -6
  17. package/src/__tests__/call-routes-http.test.ts +2 -6
  18. package/src/__tests__/call-store.test.ts +2 -6
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  20. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  21. package/src/__tests__/ces-rpc-credential-backend.test.ts +4 -1
  22. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  23. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  24. package/src/__tests__/checker.test.ts +84 -3
  25. package/src/__tests__/clawhub.test.ts +54 -24
  26. package/src/__tests__/cli-command-risk-guard.test.ts +108 -6
  27. package/src/__tests__/cli-memory.test.ts +377 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +12 -2
  29. package/src/__tests__/config-schema.test.ts +1 -3
  30. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  31. package/src/__tests__/config-watcher-feature-flags.test.ts +211 -0
  32. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  33. package/src/__tests__/contacts-tools.test.ts +31 -0
  34. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  35. package/src/__tests__/context-token-estimator.test.ts +175 -10
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
  37. package/src/__tests__/conversation-agent-loop.test.ts +9 -0
  38. package/src/__tests__/conversation-attachments.test.ts +2 -6
  39. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  40. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  41. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  42. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  43. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  44. package/src/__tests__/conversation-error.test.ts +33 -2
  45. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  46. package/src/__tests__/conversation-history-web-search.test.ts +5 -0
  47. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  48. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  49. package/src/__tests__/conversation-runtime-assembly.test.ts +7 -4
  50. package/src/__tests__/conversation-slash-commands.test.ts +2 -6
  51. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  52. package/src/__tests__/conversation-store.test.ts +2 -6
  53. package/src/__tests__/conversation-usage.test.ts +3 -6
  54. package/src/__tests__/conversation-wipe.test.ts +11 -408
  55. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  56. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  57. package/src/__tests__/credential-security-e2e.test.ts +6 -1
  58. package/src/__tests__/docker-signing-key-bootstrap.test.ts +7 -73
  59. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -7
  60. package/src/__tests__/followup-tools.test.ts +2 -6
  61. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  62. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  63. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  64. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  65. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  66. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  67. package/src/__tests__/guardian-action-store.test.ts +2 -6
  68. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  69. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  70. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  71. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  72. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  73. package/src/__tests__/guardian-routing-invariants.test.ts +343 -6
  74. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  75. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  76. package/src/__tests__/heartbeat-service.test.ts +1 -3
  77. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  78. package/src/__tests__/injection-block.test.ts +154 -0
  79. package/src/__tests__/install-meta.test.ts +506 -0
  80. package/src/__tests__/install-skill-routing.test.ts +292 -0
  81. package/src/__tests__/intent-routing.test.ts +6 -18
  82. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  83. package/src/__tests__/invite-routes-http.test.ts +2 -6
  84. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
  85. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  86. package/src/__tests__/llm-context-route-provider.test.ts +2 -6
  87. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
  88. package/src/__tests__/llm-usage-store.test.ts +2 -6
  89. package/src/__tests__/log-export-workspace.test.ts +4 -34
  90. package/src/__tests__/managed-skill-lifecycle.test.ts +7 -37
  91. package/src/__tests__/managed-store.test.ts +40 -21
  92. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  93. package/src/__tests__/memory-recall-log-store.test.ts +2 -6
  94. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  95. package/src/__tests__/messaging-send-tool.test.ts +6 -6
  96. package/src/__tests__/migration-cross-version-compatibility.test.ts +1 -29
  97. package/src/__tests__/migration-export-http.test.ts +3 -34
  98. package/src/__tests__/migration-import-commit-http.test.ts +1 -29
  99. package/src/__tests__/migration-import-preflight-http.test.ts +3 -34
  100. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +2 -1
  101. package/src/__tests__/non-member-access-request.test.ts +2 -6
  102. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  103. package/src/__tests__/oauth-apps-routes.test.ts +120 -10
  104. package/src/__tests__/oauth-cli.test.ts +364 -2
  105. package/src/__tests__/oauth-connect-orchestrator.test.ts +709 -0
  106. package/src/__tests__/oauth-provider-serializer.test.ts +2 -1
  107. package/src/__tests__/oauth-provider-visibility.test.ts +149 -0
  108. package/src/__tests__/oauth-providers-routes.test.ts +5 -2
  109. package/src/__tests__/oauth-store.test.ts +0 -5
  110. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  111. package/src/__tests__/outlook-attachments.test.ts +301 -0
  112. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  113. package/src/__tests__/outlook-categories.test.ts +212 -0
  114. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  115. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  116. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  117. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  118. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  119. package/src/__tests__/outlook-messaging-provider.test.ts +1071 -0
  120. package/src/__tests__/outlook-trash.test.ts +77 -0
  121. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  122. package/src/__tests__/path-policy.test.ts +2 -17
  123. package/src/__tests__/permission-types.test.ts +0 -1
  124. package/src/__tests__/platform-callback-registration.test.ts +7 -11
  125. package/src/__tests__/playbook-execution.test.ts +76 -80
  126. package/src/__tests__/playbook-tools.test.ts +5 -7
  127. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  128. package/src/__tests__/provider-error-scenarios.test.ts +21 -2
  129. package/src/__tests__/qdrant-manager.test.ts +68 -21
  130. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  131. package/src/__tests__/registry.test.ts +2 -2
  132. package/src/__tests__/require-fresh-approval.test.ts +64 -3
  133. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  134. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  135. package/src/__tests__/sandbox-diagnostics.test.ts +20 -29
  136. package/src/__tests__/scaffold-managed-skill-tool.test.ts +2 -10
  137. package/src/__tests__/schedule-store.test.ts +2 -6
  138. package/src/__tests__/schedule-tools.test.ts +2 -6
  139. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  140. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  141. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  142. package/src/__tests__/search-skills-unified.test.ts +421 -0
  143. package/src/__tests__/secret-allowlist.test.ts +20 -35
  144. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  145. package/src/__tests__/send-endpoint-busy.test.ts +2 -6
  146. package/src/__tests__/sequence-store.test.ts +2 -6
  147. package/src/__tests__/server-history-render.test.ts +2 -6
  148. package/src/__tests__/shell-credential-ref.test.ts +0 -5
  149. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  150. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  151. package/src/__tests__/skill-load-feature-flag.test.ts +13 -54
  152. package/src/__tests__/skill-load-inline-command.test.ts +3 -65
  153. package/src/__tests__/skill-load-inline-includes.test.ts +3 -65
  154. package/src/__tests__/skill-load-tool.test.ts +3 -67
  155. package/src/__tests__/skill-memory.test.ts +480 -195
  156. package/src/__tests__/skills-uninstall.test.ts +2 -2
  157. package/src/__tests__/skills.test.ts +23 -50
  158. package/src/__tests__/slack-channel-config.test.ts +2 -21
  159. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  160. package/src/__tests__/starter-bundle.test.ts +2 -8
  161. package/src/__tests__/stt-hints.test.ts +7 -2
  162. package/src/__tests__/system-prompt.test.ts +25 -45
  163. package/src/__tests__/task-compiler.test.ts +2 -27
  164. package/src/__tests__/task-management-tools.test.ts +2 -27
  165. package/src/__tests__/task-memory-cleanup.test.ts +173 -250
  166. package/src/__tests__/task-runner.test.ts +2 -27
  167. package/src/__tests__/task-scheduler.test.ts +2 -27
  168. package/src/__tests__/terminal-tools.test.ts +1 -17
  169. package/src/__tests__/test-preload.ts +3 -0
  170. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +0 -79
  171. package/src/__tests__/tool-approval-handler.test.ts +4 -27
  172. package/src/__tests__/tool-execution-abort-cleanup.test.ts +2 -11
  173. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -25
  174. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  175. package/src/__tests__/tool-executor.test.ts +0 -1
  176. package/src/__tests__/tool-grant-request-escalation.test.ts +4 -27
  177. package/src/__tests__/tool-preview-lifecycle.test.ts +0 -20
  178. package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
  179. package/src/__tests__/trust-store.test.ts +10 -42
  180. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -30
  181. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +3 -27
  182. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -28
  183. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -28
  184. package/src/__tests__/trusted-contact-verification.test.ts +2 -28
  185. package/src/__tests__/turn-boundary-resolution.test.ts +2 -34
  186. package/src/__tests__/twilio-provider.test.ts +0 -16
  187. package/src/__tests__/twilio-routes-twiml.test.ts +7 -12
  188. package/src/__tests__/twilio-routes.test.ts +0 -24
  189. package/src/__tests__/update-bulletin.test.ts +17 -89
  190. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -26
  191. package/src/__tests__/usage-routes.test.ts +2 -27
  192. package/src/__tests__/user-reference.test.ts +1 -5
  193. package/src/__tests__/vbundle-pax-and-symlink.test.ts +4 -34
  194. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +2 -53
  195. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  196. package/src/__tests__/voice-invite-redemption.test.ts +2 -27
  197. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -30
  198. package/src/__tests__/voice-session-bridge.test.ts +2 -27
  199. package/src/__tests__/volume-security-guard.test.ts +2 -0
  200. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  201. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -29
  202. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +2 -2
  203. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +4 -29
  204. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  205. package/src/__tests__/workspace-migration-down-functions.test.ts +0 -6
  206. package/src/__tests__/workspace-policy.test.ts +1 -1
  207. package/src/acp/client-handler.ts +1 -2
  208. package/src/agent/attachments.ts +7 -2
  209. package/src/agent/image-optimize.ts +165 -0
  210. package/src/agent/loop.ts +1 -15
  211. package/src/bundler/app-compiler.ts +179 -2
  212. package/src/bundler/package-resolver.ts +3 -5
  213. package/src/cli/__tests__/notifications.test.ts +1 -24
  214. package/src/cli/cli-memory.ts +179 -0
  215. package/src/cli/commands/avatar.ts +3 -3
  216. package/src/cli/commands/config.ts +26 -13
  217. package/src/cli/commands/doctor.ts +2 -2
  218. package/src/cli/commands/memory.ts +41 -55
  219. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  220. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  221. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  222. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  223. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  224. package/src/cli/commands/oauth/connect.ts +26 -6
  225. package/src/cli/commands/oauth/mode.ts +7 -0
  226. package/src/cli/commands/oauth/providers.ts +49 -42
  227. package/src/cli/commands/oauth/shared.ts +39 -3
  228. package/src/cli/commands/platform/__tests__/connect.test.ts +3 -49
  229. package/src/cli/commands/platform/__tests__/disconnect.test.ts +3 -49
  230. package/src/cli/commands/platform/__tests__/status.test.ts +5 -55
  231. package/src/cli/commands/platform/index.ts +16 -16
  232. package/src/cli/commands/skills.ts +88 -16
  233. package/src/cli/commands/trust.ts +2 -2
  234. package/src/cli/lib/daemon-credential-client.ts +2 -3
  235. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  236. package/src/config/bundled-skills/computer-use/TOOLS.json +7 -7
  237. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  238. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  239. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  240. package/src/config/bundled-skills/gmail/SKILL.md +2 -10
  241. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  242. package/src/config/bundled-skills/messaging/SKILL.md +26 -19
  243. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  244. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  245. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  246. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  247. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  248. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  249. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  250. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  251. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  252. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  253. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  254. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  255. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  256. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  257. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  258. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  259. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  260. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  261. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  262. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  263. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  264. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  265. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  266. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  267. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  268. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  269. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  270. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  271. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  272. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  273. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  274. package/src/config/bundled-skills/slack/SKILL.md +1 -7
  275. package/src/config/bundled-tool-registry.ts +56 -4
  276. package/src/config/env-registry.ts +15 -8
  277. package/src/config/feature-flag-registry.json +29 -116
  278. package/src/config/loader.ts +4 -0
  279. package/src/config/schemas/platform.ts +8 -0
  280. package/src/config/schemas/security.ts +0 -6
  281. package/src/config/schemas/services.ts +8 -0
  282. package/src/config/schemas/timeouts.ts +1 -1
  283. package/src/config/skills.ts +18 -7
  284. package/src/context/token-estimator.ts +25 -18
  285. package/src/context/window-manager.ts +32 -9
  286. package/src/credential-execution/approval-bridge.ts +0 -1
  287. package/src/credential-execution/process-manager.ts +3 -1
  288. package/src/daemon/config-watcher.ts +51 -0
  289. package/src/daemon/context-overflow-reducer.ts +46 -2
  290. package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
  291. package/src/daemon/conversation-agent-loop.ts +99 -63
  292. package/src/daemon/conversation-error.ts +31 -8
  293. package/src/daemon/conversation-lifecycle.ts +33 -0
  294. package/src/daemon/conversation-media-retry.ts +85 -7
  295. package/src/daemon/conversation-notifiers.ts +4 -1
  296. package/src/daemon/conversation-process.ts +1 -0
  297. package/src/daemon/conversation-runtime-assembly.ts +5 -0
  298. package/src/daemon/conversation-usage.ts +1 -0
  299. package/src/daemon/conversation.ts +41 -2
  300. package/src/daemon/daemon-control.ts +8 -2
  301. package/src/daemon/handlers/shared.ts +22 -12
  302. package/src/daemon/handlers/skills.ts +423 -201
  303. package/src/daemon/lifecycle.ts +52 -4
  304. package/src/daemon/main.ts +5 -1
  305. package/src/daemon/message-types/conversations.ts +5 -1
  306. package/src/daemon/message-types/messages.ts +3 -1
  307. package/src/daemon/message-types/skills.ts +97 -36
  308. package/src/daemon/providers-setup.ts +7 -0
  309. package/src/daemon/server.ts +35 -22
  310. package/src/daemon/tool-side-effects.ts +27 -5
  311. package/src/events/domain-events.ts +1 -2
  312. package/src/heartbeat/heartbeat-service.ts +1 -0
  313. package/src/hooks/cli.ts +2 -2
  314. package/src/hooks/runner.ts +15 -38
  315. package/src/inbound/platform-callback-registration.ts +14 -14
  316. package/src/memory/admin.ts +11 -45
  317. package/src/memory/conversation-bootstrap.ts +2 -0
  318. package/src/memory/conversation-crud.ts +242 -348
  319. package/src/memory/conversation-group-migration.ts +157 -0
  320. package/src/memory/conversation-queries.ts +4 -2
  321. package/src/memory/db-init.ts +39 -3
  322. package/src/memory/embed.ts +73 -0
  323. package/src/memory/embedding-backend.ts +8 -14
  324. package/src/memory/embedding-runtime-manager.ts +12 -114
  325. package/src/memory/fingerprint.ts +2 -2
  326. package/src/memory/graph/bootstrap.ts +512 -0
  327. package/src/memory/graph/capability-seed.ts +297 -0
  328. package/src/memory/graph/consolidation.ts +691 -0
  329. package/src/memory/graph/conversation-graph-memory.ts +630 -0
  330. package/src/memory/graph/decay.test.ts +208 -0
  331. package/src/memory/graph/decay.ts +195 -0
  332. package/src/memory/graph/extraction-job.ts +69 -0
  333. package/src/memory/graph/extraction.test.ts +936 -0
  334. package/src/memory/graph/extraction.ts +1254 -0
  335. package/src/memory/graph/graph-search.ts +266 -0
  336. package/src/memory/graph/image-ref-utils.ts +29 -0
  337. package/src/memory/graph/injection.test.ts +513 -0
  338. package/src/memory/graph/injection.ts +439 -0
  339. package/src/memory/graph/inspect.ts +534 -0
  340. package/src/memory/graph/narrative.ts +267 -0
  341. package/src/memory/graph/pattern-scan.ts +269 -0
  342. package/src/memory/graph/retriever.ts +1008 -0
  343. package/src/memory/graph/scoring.test.ts +548 -0
  344. package/src/memory/graph/scoring.ts +232 -0
  345. package/src/memory/graph/serendipity.ts +65 -0
  346. package/src/memory/graph/store.test.ts +1050 -0
  347. package/src/memory/graph/store.ts +699 -0
  348. package/src/memory/graph/tool-handlers.ts +426 -0
  349. package/src/memory/graph/tools.ts +141 -0
  350. package/src/memory/graph/triggers.test.ts +487 -0
  351. package/src/memory/graph/triggers.ts +223 -0
  352. package/src/memory/graph/types.ts +271 -0
  353. package/src/memory/group-crud.ts +191 -0
  354. package/src/memory/indexer.ts +37 -19
  355. package/src/memory/job-handlers/cleanup.ts +0 -53
  356. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  357. package/src/memory/job-handlers/embedding.test.ts +3 -27
  358. package/src/memory/job-handlers/embedding.ts +5 -31
  359. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  360. package/src/memory/job-handlers/summarization.ts +32 -17
  361. package/src/memory/job-utils.ts +1 -1
  362. package/src/memory/jobs-store.ts +50 -70
  363. package/src/memory/jobs-worker.ts +147 -112
  364. package/src/memory/llm-usage-store.ts +35 -2
  365. package/src/memory/message-content.ts +1 -0
  366. package/src/memory/migrations/201-oauth-providers-feature-flag.ts +11 -0
  367. package/src/memory/migrations/202-drop-callback-transport-column.ts +13 -0
  368. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  369. package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
  370. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  371. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  372. package/src/memory/migrations/index.ts +6 -0
  373. package/src/memory/migrations/registry.ts +8 -0
  374. package/src/memory/qdrant-client.ts +44 -17
  375. package/src/memory/qdrant-manager.ts +26 -5
  376. package/src/memory/schema/index.ts +1 -0
  377. package/src/memory/schema/memory-graph.ts +139 -0
  378. package/src/memory/schema/oauth.ts +1 -1
  379. package/src/memory/search/semantic.ts +47 -91
  380. package/src/memory/slack-thread-store.ts +17 -0
  381. package/src/memory/task-memory-cleanup.ts +28 -50
  382. package/src/messaging/providers/outlook/adapter.ts +200 -0
  383. package/src/messaging/providers/outlook/client.ts +610 -0
  384. package/src/messaging/providers/outlook/types.ts +201 -0
  385. package/src/notifications/adapters/macos.ts +1 -0
  386. package/src/notifications/adapters/slack.ts +1 -1
  387. package/src/notifications/copy-composer.ts +9 -0
  388. package/src/notifications/signal.ts +16 -0
  389. package/src/oauth/__tests__/identity-verifier.test.ts +1 -1
  390. package/src/oauth/connect-orchestrator.ts +10 -3
  391. package/src/oauth/oauth-store.ts +10 -11
  392. package/src/oauth/provider-serializer.ts +3 -0
  393. package/src/oauth/provider-visibility.ts +16 -0
  394. package/src/oauth/seed-providers.ts +50 -17
  395. package/src/permissions/checker.ts +62 -9
  396. package/src/permissions/defaults.ts +4 -4
  397. package/src/permissions/types.ts +2 -4
  398. package/src/permissions/workspace-policy.ts +1 -1
  399. package/src/playbooks/playbook-compiler.ts +19 -18
  400. package/src/playbooks/types.ts +4 -3
  401. package/src/prompts/system-prompt.ts +6 -93
  402. package/src/prompts/templates/UPDATES.md +6 -0
  403. package/src/providers/anthropic/client.ts +47 -19
  404. package/src/providers/gemini/client.ts +1 -1
  405. package/src/providers/openai/client.ts +1 -1
  406. package/src/providers/registry.ts +1 -1
  407. package/src/providers/retry.ts +19 -3
  408. package/src/runtime/actor-trust-resolver.ts +5 -1
  409. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -27
  410. package/src/runtime/auth/__tests__/token-service.test.ts +1 -25
  411. package/src/runtime/auth/route-policy.ts +7 -4
  412. package/src/runtime/guardian-reply-router.ts +10 -2
  413. package/src/runtime/http-server.ts +23 -3
  414. package/src/runtime/middleware/auth.ts +20 -0
  415. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  416. package/src/runtime/routes/attachment-routes.ts +106 -16
  417. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  418. package/src/runtime/routes/btw-routes.ts +8 -0
  419. package/src/runtime/routes/conversation-management-routes.ts +2 -0
  420. package/src/runtime/routes/conversation-query-routes.ts +2 -58
  421. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  422. package/src/runtime/routes/debug-routes.ts +1 -1
  423. package/src/runtime/routes/global-search-routes.ts +21 -19
  424. package/src/runtime/routes/group-routes.ts +207 -0
  425. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  426. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  427. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  428. package/src/runtime/routes/inbound-stages/background-dispatch.ts +43 -2
  429. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  430. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  431. package/src/runtime/routes/memory-item-routes.test.ts +2 -31
  432. package/src/runtime/routes/memory-item-routes.ts +385 -341
  433. package/src/runtime/routes/oauth-apps.ts +18 -1
  434. package/src/runtime/routes/oauth-providers.ts +13 -1
  435. package/src/runtime/routes/schedule-routes.ts +2 -0
  436. package/src/runtime/routes/settings-routes.ts +1 -0
  437. package/src/runtime/routes/skills-routes.ts +103 -37
  438. package/src/runtime/routes/usage-routes.ts +19 -2
  439. package/src/runtime/routes/work-items-routes.test.ts +2 -27
  440. package/src/runtime/routes/workspace-routes.test.ts +3 -27
  441. package/src/schedule/scheduler.ts +8 -1
  442. package/src/security/oauth2.ts +1 -1
  443. package/src/security/secret-allowlist.ts +4 -4
  444. package/src/security/secure-keys.ts +4 -8
  445. package/src/shared/provider-env-vars.ts +19 -0
  446. package/src/skills/catalog-cache.ts +5 -0
  447. package/src/skills/catalog-install.ts +15 -14
  448. package/src/skills/clawhub.ts +134 -154
  449. package/src/skills/install-meta.ts +208 -0
  450. package/src/skills/managed-store.ts +27 -16
  451. package/src/skills/skill-memory.ts +210 -96
  452. package/src/skills/skillssh-registry.ts +19 -17
  453. package/src/tasks/task-runner.ts +3 -1
  454. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  455. package/src/tools/browser/runtime-check.ts +3 -1
  456. package/src/tools/memory/register.ts +63 -46
  457. package/src/tools/permission-checker.ts +7 -19
  458. package/src/tools/shared/filesystem/image-read.ts +22 -85
  459. package/src/tools/skills/skill-script-runner.ts +1 -1
  460. package/src/tools/terminal/safe-env.ts +1 -0
  461. package/src/tools/tool-manifest.ts +3 -3
  462. package/src/util/browser.ts +25 -10
  463. package/src/util/bun-runtime.ts +172 -0
  464. package/src/util/device-id.ts +3 -65
  465. package/src/watcher/providers/outlook-calendar.ts +343 -0
  466. package/src/watcher/providers/outlook.ts +198 -0
  467. package/src/workspace/git-service.ts +27 -6
  468. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  469. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  470. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  471. package/src/workspace/migrations/registry.ts +6 -0
  472. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  473. package/src/__tests__/journal-context.test.ts +0 -268
  474. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  475. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  476. package/src/__tests__/memory-query-builder.test.ts +0 -59
  477. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  478. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  479. package/src/__tests__/memory-regressions.test.ts +0 -3696
  480. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  481. package/src/daemon/conversation-memory.ts +0 -207
  482. package/src/memory/conversation-starters-cadence.ts +0 -74
  483. package/src/memory/items-extractor.ts +0 -860
  484. package/src/memory/job-handlers/batch-extraction.ts +0 -741
  485. package/src/memory/job-handlers/extraction.ts +0 -40
  486. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -383
  487. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  488. package/src/memory/journal-memory.ts +0 -224
  489. package/src/memory/query-builder.ts +0 -47
  490. package/src/memory/query-expansion.ts +0 -83
  491. package/src/memory/retriever.test.ts +0 -1590
  492. package/src/memory/retriever.ts +0 -1323
  493. package/src/memory/search/formatting.test.ts +0 -140
  494. package/src/memory/search/formatting.ts +0 -262
  495. package/src/memory/search/mmr.ts +0 -136
  496. package/src/memory/search/ranking.ts +0 -15
  497. package/src/memory/search/staleness.ts +0 -40
  498. package/src/memory/search/tier-classifier.ts +0 -18
  499. package/src/memory/search/types.ts +0 -121
  500. package/src/prompts/journal-context.ts +0 -156
  501. package/src/tools/memory/definitions.ts +0 -69
  502. package/src/tools/memory/handlers.test.ts +0 -590
  503. package/src/tools/memory/handlers.ts +0 -434
@@ -8,15 +8,42 @@ import type {
8
8
  QdrantSparseVector,
9
9
  } from "../qdrant-client.js";
10
10
  import { getQdrantClient } from "../qdrant-client.js";
11
- import {
12
- conversations,
13
- memoryItems,
14
- memoryItemSources,
15
- memorySegments,
16
- memorySummaries,
17
- } from "../schema.js";
18
- import { computeRecencyScore } from "./ranking.js";
19
- import type { Candidate } from "./types.js";
11
+ import { conversations, memorySegments, memorySummaries } from "../schema.js";
12
+ // ── Types (inlined from deleted types.ts) ──────────────────────────
13
+
14
+ type CandidateType = "segment" | "item" | "summary" | "media";
15
+
16
+ export interface Candidate {
17
+ key: string;
18
+ type: CandidateType;
19
+ id: string;
20
+ source: "semantic";
21
+ text: string;
22
+ kind: string;
23
+ modality?: "text" | "image" | "audio" | "video";
24
+ conversationId?: string;
25
+ messageId?: string;
26
+ confidence: number;
27
+ importance: number;
28
+ createdAt: number;
29
+ semantic: number;
30
+ recency: number;
31
+ finalScore: number;
32
+ }
33
+
34
+ // ── Recency scoring (inlined from deleted ranking.ts) ──────────────
35
+
36
+ /**
37
+ * Logarithmic recency decay (ACT-R inspired).
38
+ *
39
+ * 1 day -> 0.50, 7 days -> 0.25, 30 days -> 0.17
40
+ * 90 days -> 0.15, 1 year -> 0.12, 2 years -> 0.10
41
+ */
42
+ function computeRecencyScore(createdAt: number): number {
43
+ const ageMs = Math.max(0, Date.now() - createdAt);
44
+ const ageDays = ageMs / (24 * 60 * 60 * 1000);
45
+ return 1 / (1 + Math.log2(1 + ageDays));
46
+ }
20
47
 
21
48
  const _log = getLogger("semantic-search");
22
49
 
@@ -27,7 +54,7 @@ export async function semanticSearch(
27
54
  limit: number,
28
55
  excludedMessageIds: string[] = [],
29
56
  scopeIds?: string[],
30
- sparseVector?: QdrantSparseVector,
57
+ sparseVector?: QdrantSparseVector
31
58
  ): Promise<Candidate[]> {
32
59
  if (limit <= 0) return [];
33
60
 
@@ -52,31 +79,28 @@ export async function semanticSearch(
52
79
  filter,
53
80
  limit: fetchLimit,
54
81
  prefetchLimit: fetchLimit,
55
- }),
82
+ })
56
83
  );
57
84
  } else {
58
85
  results = await withQdrantBreaker(() =>
59
86
  qdrant.searchWithFilter(
60
87
  queryVector,
61
88
  fetchLimit,
62
- ["item", "summary", "segment", "media"],
89
+ ["summary", "segment", "media"],
63
90
  excludedMessageIds,
64
- scopeIds,
65
- ),
91
+ scopeIds
92
+ )
66
93
  );
67
94
  }
68
95
 
69
96
  const db = getDb();
70
97
 
71
98
  // Batch-fetch all backing records upfront to avoid N+1 queries per result
72
- const itemTargetIds: string[] = [];
73
99
  const summaryTargetIds: string[] = [];
74
100
  const segmentTargetIds: string[] = [];
75
101
  const mediaConversationIds: string[] = [];
76
102
  for (const r of results) {
77
- if (r.payload.target_type === "item")
78
- itemTargetIds.push(r.payload.target_id);
79
- else if (r.payload.target_type === "summary")
103
+ if (r.payload.target_type === "summary")
80
104
  summaryTargetIds.push(r.payload.target_id);
81
105
  else if (r.payload.target_type === "segment")
82
106
  segmentTargetIds.push(r.payload.target_id);
@@ -84,33 +108,6 @@ export async function semanticSearch(
84
108
  mediaConversationIds.push(r.payload.conversation_id);
85
109
  }
86
110
 
87
- const itemsMap = new Map<string, typeof memoryItems.$inferSelect>();
88
- if (itemTargetIds.length > 0) {
89
- const allItems = db
90
- .select()
91
- .from(memoryItems)
92
- .where(inArray(memoryItems.id, itemTargetIds))
93
- .all();
94
- for (const item of allItems) itemsMap.set(item.id, item);
95
- }
96
-
97
- const sourcesMap = new Map<string, string[]>();
98
- if (itemTargetIds.length > 0) {
99
- const allSources = db
100
- .select({
101
- memoryItemId: memoryItemSources.memoryItemId,
102
- messageId: memoryItemSources.messageId,
103
- })
104
- .from(memoryItemSources)
105
- .where(inArray(memoryItemSources.memoryItemId, itemTargetIds))
106
- .all();
107
- for (const s of allSources) {
108
- const existing = sourcesMap.get(s.memoryItemId);
109
- if (existing) existing.push(s.messageId);
110
- else sourcesMap.set(s.memoryItemId, [s.messageId]);
111
- }
112
- }
113
-
114
111
  const summariesMap = new Map<string, typeof memorySummaries.$inferSelect>();
115
112
  if (scopeIds && summaryTargetIds.length > 0) {
116
113
  const allSummaries = db
@@ -148,8 +145,6 @@ export async function semanticSearch(
148
145
  for (const row of rows) mediaScopeMap.set(row.id, row.memoryScopeId);
149
146
  }
150
147
 
151
- const excludedSet =
152
- excludedMessageIds.length > 0 ? new Set(excludedMessageIds) : null;
153
148
 
154
149
  const candidates: Candidate[] = [];
155
150
  for (const result of results) {
@@ -159,29 +154,8 @@ export async function semanticSearch(
159
154
  const createdAt = payload.created_at ?? Date.now();
160
155
 
161
156
  if (payload.target_type === "item") {
162
- const item = itemsMap.get(payload.target_id);
163
- if (!item || item.status !== "active" || item.invalidAt != null) continue;
164
- if (scopeIds && !scopeIds.includes(item.scopeId)) continue;
165
- const sources = sourcesMap.get(payload.target_id);
166
- if (!sources || sources.length === 0) continue;
167
- if (excludedSet) {
168
- const hasNonExcluded = sources.some((msgId) => !excludedSet.has(msgId));
169
- if (!hasNonExcluded) continue;
170
- }
171
- candidates.push({
172
- key: `item:${payload.target_id}`,
173
- type: "item",
174
- id: payload.target_id,
175
- source: "semantic",
176
- text: `${item.subject}: ${item.statement}`,
177
- kind: item.kind,
178
- confidence: item.confidence,
179
- importance: item.importance ?? 0.5,
180
- createdAt: item.lastSeenAt,
181
- semantic,
182
- recency: computeRecencyScore(item.lastSeenAt),
183
- finalScore: 0,
184
- });
157
+ // Legacy item vectors — skip (table dropped, Qdrant cleanup pending)
158
+ continue;
185
159
  } else if (payload.target_type === "summary") {
186
160
  if (scopeIds) {
187
161
  const summary = summariesMap.get(payload.target_id);
@@ -284,33 +258,15 @@ export async function semanticSearch(
284
258
  */
285
259
  function buildHybridFilter(
286
260
  excludeMessageIds: string[],
287
- scopeIds?: string[],
261
+ scopeIds?: string[]
288
262
  ): Record<string, unknown> {
289
263
  const mustConditions: Array<Record<string, unknown>> = [
290
264
  {
291
265
  key: "target_type",
292
- match: { any: ["item", "summary", "segment", "media"] },
266
+ match: { any: ["summary", "segment", "media"] },
293
267
  },
294
268
  ];
295
269
 
296
- if (excludeMessageIds.length > 0) {
297
- // Only require status=active for items; segments and summaries don't have a status field
298
- mustConditions.push({
299
- should: [
300
- {
301
- must: [
302
- { key: "target_type", match: { value: "item" } },
303
- { key: "status", match: { value: "active" } },
304
- ],
305
- },
306
- {
307
- key: "target_type",
308
- match: { any: ["segment", "summary", "media"] },
309
- },
310
- ],
311
- });
312
- }
313
-
314
270
  // Scope filtering: accept points whose memory_scope_id matches one of the
315
271
  // allowed scopes, OR points that lack the field entirely (legacy data).
316
272
  // Post-query DB filtering remains as defense-in-depth for legacy points.
@@ -346,6 +302,6 @@ export function mapCosineToUnit(value: number): number {
346
302
  export function isQdrantConnectionError(err: unknown): boolean {
347
303
  if (!(err instanceof Error)) return false;
348
304
  return /ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENETUNREACH|fetch failed/i.test(
349
- err.message,
305
+ err.message
350
306
  );
351
307
  }
@@ -78,6 +78,23 @@ export function extractThreadTsFromCallbackUrl(
78
78
  }
79
79
  }
80
80
 
81
+ /**
82
+ * Extract the messageTs from a Slack reply callback URL, if present.
83
+ * The gateway encodes messageTs for non-threaded DMs so the runtime
84
+ * can target the original message for emoji-based indicators.
85
+ */
86
+ export function extractMessageTsFromCallbackUrl(
87
+ callbackUrl: string | undefined,
88
+ ): string | null {
89
+ if (!callbackUrl) return null;
90
+ try {
91
+ const url = new URL(callbackUrl);
92
+ return url.searchParams.get("messageTs");
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+
81
98
  /**
82
99
  * Extract the channel from a Slack reply callback URL, if present.
83
100
  */
@@ -24,62 +24,41 @@ export function isConversationFailed(conversationId: string): boolean {
24
24
  }
25
25
 
26
26
  /**
27
- * Invalidate assistant-extracted memory items sourced *exclusively* from
28
- * messages in the given conversation. Called when a background task or
29
- * schedule fails — the assistant's optimistic claims (e.g., "I booked an
30
- * appointment") are not trustworthy if the task didn't complete.
27
+ * Invalidate assistant-inferred memory graph nodes sourced *exclusively* from
28
+ * the given conversation. Called when a background task or schedule fails —
29
+ * the assistant's optimistic claims are not trustworthy if the task didn't
30
+ * complete.
31
31
  *
32
- * The failed state is derived from durable storage (task_runs / cron_runs),
33
- * so any pending or future extraction jobs for this conversation are blocked
34
- * from creating new assistant-extracted items — even after daemon restarts.
35
- *
36
- * Items that also have sources from other conversations are left alone
37
- * only when those conversations come from non-failed task/schedule runs
38
- * (or are ordinary user conversations). This prevents cascading failures
39
- * from mutually protecting each other — if two conversations both source
40
- * a memory item and both fail, the item is correctly invalidated.
32
+ * Nodes that also have sources from other non-failed conversations are left
33
+ * alone (corroboration). Uses the `source_conversations` JSON array to
34
+ * determine provenance.
41
35
  */
42
36
  export function invalidateAssistantInferredItemsForConversation(
43
37
  conversationId: string,
44
38
  ): number {
45
- // Cancel pending extraction jobs for this conversation's messages
46
- // so the worker never processes them. Jobs already running will be
47
- // caught by the isConversationFailed check in the extraction handler.
48
- // NOTE: Only extract_items jobs are cancelled here — not embed_item or
49
- // other job types. Multi-sourced items may still be valid (corroborated
50
- // by other conversations), and their embedding jobs must not be killed.
51
- // The broader cancelPendingJobsForConversation is used by the wipe path.
52
39
  cancelPendingExtractionJobsForConversation(conversationId);
53
40
 
54
41
  const affected = rawRun(
55
- `UPDATE memory_items
56
- SET status = 'invalidated',
57
- invalid_at = ?
58
- WHERE source_type = 'extraction'
59
- AND source_message_role = 'assistant'
60
- AND status = 'active'
61
- AND id IN (
62
- SELECT mis.memory_item_id
63
- FROM memory_item_sources mis
64
- JOIN messages m ON m.id = mis.message_id
65
- WHERE m.conversation_id = ?
42
+ `UPDATE memory_graph_nodes
43
+ SET fidelity = 'gone',
44
+ last_accessed = ?
45
+ WHERE source_type = 'inferred'
46
+ AND fidelity != 'gone'
47
+ AND EXISTS (
48
+ SELECT 1 FROM json_each(source_conversations) jc
49
+ WHERE jc.value = ?
66
50
  )
67
51
  AND NOT EXISTS (
68
- SELECT 1
69
- FROM memory_item_sources mis2
70
- JOIN messages m2 ON m2.id = mis2.message_id
71
- WHERE mis2.memory_item_id = memory_items.id
72
- AND m2.conversation_id != ?
73
- -- Only count as corroboration if the other conversation is NOT
74
- -- from a failed task run or failed schedule run.
52
+ SELECT 1 FROM json_each(source_conversations) jc2
53
+ WHERE jc2.value != ?
75
54
  AND NOT EXISTS (
76
55
  SELECT 1 FROM task_runs tr
77
- WHERE tr.conversation_id = m2.conversation_id
56
+ WHERE tr.conversation_id = jc2.value
78
57
  AND tr.status = 'failed'
79
58
  )
80
59
  AND NOT EXISTS (
81
60
  SELECT 1 FROM cron_runs cr
82
- WHERE cr.conversation_id = m2.conversation_id
61
+ WHERE cr.conversation_id = jc2.value
83
62
  AND cr.status = 'error'
84
63
  )
85
64
  )`,
@@ -91,7 +70,7 @@ export function invalidateAssistantInferredItemsForConversation(
91
70
  if (affected > 0) {
92
71
  log.info(
93
72
  { conversationId, affected },
94
- "Invalidated assistant-inferred memory items after task failure",
73
+ "Invalidated assistant-inferred memory graph nodes after task failure",
95
74
  );
96
75
  }
97
76
 
@@ -103,7 +82,7 @@ export function invalidateAssistantInferredItemsForConversation(
103
82
  * Covers every job type: `extract_items`, `embed_attachment` (keyed by messageId),
104
83
  * `embed_segment` (keyed by segmentId via memory_segments),
105
84
  * `build_conversation_summary` (keyed by conversationId),
106
- * and `embed_item` (keyed by itemId sourced from the conversation's messages).
85
+ * and `embed_graph_node` (keyed by nodeId sourced from the conversation).
107
86
  */
108
87
  export function cancelPendingJobsForConversation(
109
88
  conversationId: string,
@@ -155,18 +134,17 @@ export function cancelPendingJobsForConversation(
155
134
  conversationId,
156
135
  );
157
136
 
158
- // Jobs keyed by itemId: embed_item (items sourced from this conversation)
137
+ // Jobs keyed by nodeId: embed_graph_node (nodes sourced from this conversation)
159
138
  total += rawRun(
160
139
  `UPDATE memory_jobs
161
140
  SET status = 'failed',
162
141
  last_error = ?,
163
142
  updated_at = ?
164
143
  WHERE status IN ('pending', 'running')
165
- AND json_extract(payload, '$.itemId') IN (
166
- SELECT mis.memory_item_id
167
- FROM memory_item_sources mis
168
- JOIN messages m ON m.id = mis.message_id
169
- WHERE m.conversation_id = ?
144
+ AND json_extract(payload, '$.nodeId') IN (
145
+ SELECT mgn.id
146
+ FROM memory_graph_nodes mgn, json_each(mgn.source_conversations) jc
147
+ WHERE jc.value = ?
170
148
  )`,
171
149
  reason,
172
150
  now,
@@ -186,8 +164,8 @@ export function cancelPendingJobsForConversation(
186
164
  /**
187
165
  * Cancel only pending/running `extract_items` jobs for messages in the
188
166
  * given conversation. Used by the task-failure path where we want to
189
- * stop new extractions but must NOT cancel `embed_item` jobs — those
190
- * items may be multi-sourced and still valid.
167
+ * stop new extractions but must NOT cancel `embed_graph_node` jobs —
168
+ * those nodes may be multi-sourced and still valid.
191
169
  */
192
170
  function cancelPendingExtractionJobsForConversation(
193
171
  conversationId: string,
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Outlook messaging provider adapter.
3
+ *
4
+ * Maps Microsoft Graph API responses to the platform-agnostic messaging types
5
+ * and implements the MessagingProvider interface.
6
+ */
7
+
8
+ import type { OAuthConnection } from "../../../oauth/connection.js";
9
+ import type { MessagingProvider } from "../../provider.js";
10
+ import type {
11
+ ConnectionInfo,
12
+ Conversation,
13
+ HistoryOptions,
14
+ ListOptions,
15
+ Message,
16
+ SearchOptions,
17
+ SearchResult,
18
+ SendOptions,
19
+ SendResult,
20
+ } from "../../provider-types.js";
21
+ import * as outlook from "./client.js";
22
+ import type { OutlookMessage } from "./types.js";
23
+
24
+ function requireConnection(
25
+ connection: OAuthConnection | undefined,
26
+ ): OAuthConnection {
27
+ if (!connection) {
28
+ throw new Error(
29
+ "Outlook requires an OAuth connection — is the account connected?",
30
+ );
31
+ }
32
+ return connection;
33
+ }
34
+
35
+ function mapOutlookMessage(msg: OutlookMessage): Message {
36
+ const senderEmail = msg.from?.emailAddress?.address ?? "";
37
+ const senderName = msg.from?.emailAddress?.name || senderEmail || "Unknown";
38
+
39
+ return {
40
+ id: msg.id,
41
+ conversationId: msg.conversationId,
42
+ sender: {
43
+ id: senderEmail,
44
+ name: senderName,
45
+ email: senderEmail,
46
+ },
47
+ text: msg.body.contentType === "text" ? msg.body.content : msg.bodyPreview,
48
+ timestamp: new Date(msg.receivedDateTime).getTime(),
49
+ threadId: msg.conversationId,
50
+ platform: "outlook",
51
+ hasAttachments: msg.hasAttachments ?? false,
52
+ metadata: {
53
+ subject: msg.subject,
54
+ categories: msg.categories,
55
+ isRead: msg.isRead,
56
+ parentFolderId: msg.parentFolderId,
57
+ },
58
+ };
59
+ }
60
+
61
+ const MESSAGE_SELECT_FIELDS =
62
+ "id,conversationId,subject,bodyPreview,body,from,toRecipients,receivedDateTime,isRead,hasAttachments,parentFolderId,categories,flag";
63
+
64
+ export const outlookMessagingProvider: MessagingProvider = {
65
+ id: "outlook",
66
+ displayName: "Outlook",
67
+ credentialService: "outlook",
68
+ capabilities: new Set([
69
+ "threads",
70
+ "folders",
71
+ "categories",
72
+ "drafts_native",
73
+ "archive",
74
+ "unsubscribe",
75
+ ]),
76
+
77
+ async testConnection(connection?: OAuthConnection): Promise<ConnectionInfo> {
78
+ const conn = requireConnection(connection);
79
+ const profile = await outlook.getProfile(conn);
80
+ return {
81
+ connected: true,
82
+ user: profile.mail || profile.userPrincipalName,
83
+ platform: "outlook",
84
+ };
85
+ },
86
+
87
+ async listConversations(
88
+ connection: OAuthConnection | undefined,
89
+ _options?: ListOptions,
90
+ ): Promise<Conversation[]> {
91
+ const conn = requireConnection(connection);
92
+ const folders = await outlook.listMailFolders(conn);
93
+ return folders.map((folder) => ({
94
+ id: folder.id,
95
+ name: folder.displayName,
96
+ type: "inbox" as const,
97
+ platform: "outlook",
98
+ unreadCount: folder.unreadItemCount ?? 0,
99
+ lastActivityAt: Date.now(),
100
+ metadata: {
101
+ totalItemCount: folder.totalItemCount,
102
+ childFolderCount: folder.childFolderCount,
103
+ },
104
+ }));
105
+ },
106
+
107
+ async getHistory(
108
+ connection: OAuthConnection | undefined,
109
+ conversationId: string,
110
+ options?: HistoryOptions,
111
+ ): Promise<Message[]> {
112
+ const conn = requireConnection(connection);
113
+ const result = await outlook.listMessages(conn, {
114
+ folderId: conversationId,
115
+ top: options?.limit ?? 50,
116
+ orderby: "receivedDateTime desc",
117
+ select: MESSAGE_SELECT_FIELDS,
118
+ });
119
+ return (result.value ?? []).map(mapOutlookMessage);
120
+ },
121
+
122
+ async search(
123
+ connection: OAuthConnection | undefined,
124
+ query: string,
125
+ options?: SearchOptions,
126
+ ): Promise<SearchResult> {
127
+ const conn = requireConnection(connection);
128
+ const result = await outlook.searchMessages(conn, query, {
129
+ top: options?.count ?? 20,
130
+ });
131
+ const messages = result.value ?? [];
132
+ return {
133
+ total: result["@odata.count"] ?? messages.length,
134
+ messages: messages.map(mapOutlookMessage),
135
+ hasMore: !!result["@odata.nextLink"],
136
+ };
137
+ },
138
+
139
+ async sendMessage(
140
+ connection: OAuthConnection | undefined,
141
+ conversationId: string,
142
+ text: string,
143
+ options?: SendOptions,
144
+ ): Promise<SendResult> {
145
+ const conn = requireConnection(connection);
146
+
147
+ if (options?.inReplyTo) {
148
+ await outlook.replyToMessage(conn, options.inReplyTo, text);
149
+ return {
150
+ id: "",
151
+ timestamp: Date.now(),
152
+ conversationId,
153
+ threadId: options?.threadId,
154
+ };
155
+ }
156
+
157
+ await outlook.sendMessage(conn, {
158
+ message: {
159
+ subject: options?.subject ?? "",
160
+ body: { contentType: "text", content: text },
161
+ toRecipients: [{ emailAddress: { address: conversationId } }],
162
+ },
163
+ });
164
+
165
+ // Microsoft Graph's sendMail returns 202 with no body
166
+ return {
167
+ id: "",
168
+ timestamp: Date.now(),
169
+ conversationId,
170
+ threadId: options?.threadId,
171
+ };
172
+ },
173
+
174
+ async getThreadReplies(
175
+ connection: OAuthConnection | undefined,
176
+ _conversationId: string,
177
+ threadId: string,
178
+ options?: HistoryOptions,
179
+ ): Promise<Message[]> {
180
+ const conn = requireConnection(connection);
181
+ const result = await outlook.listMessages(conn, {
182
+ filter: `conversationId eq '${threadId.replace(/'/g, "''")}'`,
183
+ top: options?.limit ?? 50,
184
+ orderby: "receivedDateTime asc",
185
+ select: MESSAGE_SELECT_FIELDS,
186
+ });
187
+ return (result.value ?? []).map(mapOutlookMessage);
188
+ },
189
+
190
+ async markRead(
191
+ connection: OAuthConnection | undefined,
192
+ _conversationId: string,
193
+ messageId?: string,
194
+ ): Promise<void> {
195
+ const conn = requireConnection(connection);
196
+ if (messageId) {
197
+ await outlook.markMessageRead(conn, messageId);
198
+ }
199
+ },
200
+ };