@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
@@ -9,6 +9,27 @@ import {
9
9
  } from "../context/token-estimator.js";
10
10
  import type { Message } from "../providers/types.js";
11
11
 
12
+ /** Build a minimal valid PNG header with the given dimensions, returned as base64. */
13
+ function makePngBase64(width: number, height: number): string {
14
+ const header = Buffer.alloc(24);
15
+ header[0] = 0x89;
16
+ header[1] = 0x50;
17
+ header[2] = 0x4e;
18
+ header[3] = 0x47;
19
+ header[4] = 0x0d;
20
+ header[5] = 0x0a;
21
+ header[6] = 0x1a;
22
+ header[7] = 0x0a;
23
+ header.writeUInt32BE(13, 8);
24
+ header[12] = 0x49;
25
+ header[13] = 0x48;
26
+ header[14] = 0x44;
27
+ header[15] = 0x52;
28
+ header.writeUInt32BE(width, 16);
29
+ header.writeUInt32BE(height, 20);
30
+ return header.toString("base64");
31
+ }
32
+
12
33
  describe("token estimator", () => {
13
34
  test("estimates text tokens from character length", () => {
14
35
  expect(estimateTextTokens("")).toBe(0);
@@ -48,7 +69,7 @@ describe("token estimator", () => {
48
69
  data: "a".repeat(100),
49
70
  },
50
71
  }),
51
- ).toBeGreaterThan(500);
72
+ ).toBeGreaterThan(0);
52
73
  });
53
74
 
54
75
  test("estimates message and prompt totals", () => {
@@ -264,9 +285,11 @@ describe("token estimator", () => {
264
285
  { providerName: "anthropic" },
265
286
  );
266
287
 
267
- // 1920x1080 scaled to fit 1568x1568: scale = 1568/1920 = 0.8167
288
+ // 1920x1080 scaled to fit 1568px bounding box: dimScale = 1568/1920 = 0.8167
268
289
  // scaledWidth = round(1920 * 0.8167) = 1568, scaledHeight = round(1080 * 0.8167) = 882
269
- // tokens = ceil(1568 * 882 / 750) = ceil(1843.968) = ~1844
290
+ // pixels = 1568 * 882 = 1,382,976 > 1,200,000 → mpScale = sqrt(1200000/1382976) = 0.9315
291
+ // scaledWidth = round(1568 * 0.9315) = 1461, scaledHeight = round(882 * 0.9315) = 822
292
+ // tokens = ceil(1461 * 822 / 750) = ceil(1601.26) = ~1,602
270
293
  // With IMAGE_BLOCK_OVERHEAD_TOKENS and media_type overhead, still well under 5000
271
294
  expect(anthropicTokens).toBeLessThan(5_000);
272
295
 
@@ -299,13 +322,10 @@ describe("token estimator", () => {
299
322
  { providerName: "anthropic" },
300
323
  );
301
324
 
302
- // Should fall back to ANTHROPIC_IMAGE_MAX_TOKENS (~3,277)
303
- // The total will include IMAGE_BLOCK_OVERHEAD_TOKENS + media_type overhead,
304
- // but the max is applied at the outer Math.max(IMAGE_BLOCK_TOKENS, ...) level
305
- // ANTHROPIC_IMAGE_MAX_TOKENS = ceil(1568*1568/750) = 3277
306
- // Total = max(1024, 16 + ceil(9/4) + 3277) = max(1024, 3296) = 3296
307
- expect(tokens).toBeGreaterThanOrEqual(3_277);
308
- expect(tokens).toBeLessThan(4_000);
325
+ // Should fall back to ANTHROPIC_IMAGE_MAX_TOKENS (1,600)
326
+ // Total = 16 (block overhead) + ceil(9/4) (media_type) + 1600 = 1619
327
+ expect(tokens).toBeGreaterThanOrEqual(1_600);
328
+ expect(tokens).toBeLessThan(2_000);
309
329
  });
310
330
 
311
331
  test("Anthropic image tokens are the same for same-dimension images regardless of payload size", () => {
@@ -356,4 +376,149 @@ describe("token estimator", () => {
356
376
  // For Anthropic, same dimensions should produce the same estimate
357
377
  expect(largeTokens).toBe(smallTokens);
358
378
  });
379
+
380
+ test("applies megapixel cap for square images on Anthropic", () => {
381
+ // Build a minimal valid PNG header encoding 2000x2000 dimensions.
382
+ const pngHeader = Buffer.alloc(24);
383
+ // PNG signature
384
+ pngHeader[0] = 0x89;
385
+ pngHeader[1] = 0x50;
386
+ pngHeader[2] = 0x4e;
387
+ pngHeader[3] = 0x47;
388
+ pngHeader[4] = 0x0d;
389
+ pngHeader[5] = 0x0a;
390
+ pngHeader[6] = 0x1a;
391
+ pngHeader[7] = 0x0a;
392
+ // IHDR chunk length (13 bytes)
393
+ pngHeader.writeUInt32BE(13, 8);
394
+ // "IHDR"
395
+ pngHeader[12] = 0x49;
396
+ pngHeader[13] = 0x48;
397
+ pngHeader[14] = 0x44;
398
+ pngHeader[15] = 0x52;
399
+ // Width: 2000
400
+ pngHeader.writeUInt32BE(2000, 16);
401
+ // Height: 2000
402
+ pngHeader.writeUInt32BE(2000, 20);
403
+
404
+ const base64Data = pngHeader.toString("base64");
405
+
406
+ const tokens = estimateContentBlockTokens(
407
+ {
408
+ type: "image",
409
+ source: { type: "base64", media_type: "image/png", data: base64Data },
410
+ },
411
+ { providerName: "anthropic" },
412
+ );
413
+
414
+ // 2000x2000 → dimScale = 1568/2000 = 0.784 → 1568x1568 = 2,458,624 pixels
415
+ // 2,458,624 > 1,200,000 → mpScale = sqrt(1200000/2458624) ≈ 0.6987
416
+ // scaledWidth = round(1568 * 0.6987) = 1096, scaledHeight = round(1568 * 0.6987) = 1096
417
+ // tokens = ceil(1096 * 1096 / 750) = ceil(1601.6) ≈ 1602
418
+ // Without megapixel cap would have been ceil(1568 * 1568 / 750) ≈ 3277
419
+ expect(tokens).toBeLessThanOrEqual(1_700);
420
+ });
421
+
422
+ test("small Anthropic images are not inflated to 1024 tokens", () => {
423
+ // 200x200 image: ceil(200*200/750) = ceil(53.33) = 54 tokens
424
+ const tokens = estimateContentBlockTokens(
425
+ {
426
+ type: "image",
427
+ source: {
428
+ type: "base64",
429
+ media_type: "image/png",
430
+ data: makePngBase64(200, 200),
431
+ },
432
+ },
433
+ { providerName: "anthropic" },
434
+ );
435
+
436
+ // 54 (dimension-based) + 16 (block overhead) + 3 (media type) = 73
437
+ expect(tokens).toBeLessThan(100);
438
+ expect(tokens).toBeGreaterThan(50);
439
+ });
440
+
441
+ test("thumbnail Anthropic images estimate accurately", () => {
442
+ // 150x150 image: ceil(150*150/750) = ceil(30) = 30 tokens
443
+ const tokens = estimateContentBlockTokens(
444
+ {
445
+ type: "image",
446
+ source: {
447
+ type: "base64",
448
+ media_type: "image/png",
449
+ data: makePngBase64(150, 150),
450
+ },
451
+ },
452
+ { providerName: "anthropic" },
453
+ );
454
+
455
+ // 30 + 16 + 3 = 49
456
+ expect(tokens).toBeLessThan(70);
457
+ expect(tokens).toBeGreaterThan(30);
458
+ });
459
+
460
+ test("many small Anthropic images do not trigger phantom token inflation", () => {
461
+ const messages: Message[] = [
462
+ {
463
+ role: "user",
464
+ content: Array.from({ length: 100 }, () => ({
465
+ type: "image" as const,
466
+ source: {
467
+ type: "base64" as const,
468
+ media_type: "image/png",
469
+ data: makePngBase64(200, 200),
470
+ },
471
+ })),
472
+ },
473
+ ];
474
+
475
+ const total = estimateMessagesTokens(messages, { providerName: "anthropic" });
476
+
477
+ // Each image: ~73 tokens. 100 images + message overhead ≈ 7,304
478
+ // Old behavior: 100 * ~1,043 = ~104,300 (14x overestimate)
479
+ expect(total).toBeLessThan(10_000);
480
+ });
481
+
482
+ test("matches Anthropic's published table for common aspect ratios", () => {
483
+ // These are the max sizes that should NOT be further scaled (at or below the megapixel cap).
484
+ // 1:1 → 1092x1092 (~1,590 tokens)
485
+ const squareTokens = estimateContentBlockTokens(
486
+ {
487
+ type: "image",
488
+ source: {
489
+ type: "base64",
490
+ media_type: "image/png",
491
+ data: makePngBase64(1092, 1092),
492
+ },
493
+ },
494
+ { providerName: "anthropic" },
495
+ );
496
+
497
+ // 1:2 → 784x1568 (~1,639 tokens)
498
+ const tallTokens = estimateContentBlockTokens(
499
+ {
500
+ type: "image",
501
+ source: {
502
+ type: "base64",
503
+ media_type: "image/png",
504
+ data: makePngBase64(784, 1568),
505
+ },
506
+ },
507
+ { providerName: "anthropic" },
508
+ );
509
+
510
+ // 1092x1092 = 1,192,464 pixels < 1,200,000 → no megapixel scaling needed.
511
+ // tokens = ceil(1092 * 1092 / 750) = ceil(1589.95) ≈ 1590
512
+ // With overhead: 16 + 3 + 1590 = 1609
513
+ expect(squareTokens).toBeGreaterThan(1_400);
514
+ expect(squareTokens).toBeLessThan(1_800);
515
+
516
+ // 784*1568 = 1,229,312 > 1,200,000 → slight scaling applies
517
+ // mpScale = sqrt(1200000/1229312) ≈ 0.9881
518
+ // scaledWidth = round(784 * 0.9881) = 775, scaledHeight = round(1568 * 0.9881) = 1549
519
+ // tokens = ceil(775 * 1549 / 750) = ceil(1600.6) ≈ 1601
520
+ // With overhead: 16 + 3 + 1601 = 1620
521
+ expect(tallTokens).toBeGreaterThan(1_400);
522
+ expect(tallTokens).toBeLessThan(1_800);
523
+ });
359
524
  });
@@ -457,6 +457,15 @@ function makeCtx(
457
457
  assistantMessageChannel: "vellum" as const,
458
458
  }),
459
459
 
460
+ graphMemory: {
461
+ onCompacted: () => {},
462
+ prepareMemory: async () => ({ runMessages: [], injectedTokens: 0, latencyMs: 0, mode: "none" as const }),
463
+ reinjectCachedMemory: (messages: Message[]) => ({
464
+ runMessages: messages,
465
+ injectedTokens: 0,
466
+ }),
467
+ } as unknown as AgentLoopConversationContext["graphMemory"],
468
+
460
469
  ...overrides,
461
470
  } as AgentLoopConversationContext;
462
471
  }
@@ -445,6 +445,15 @@ function makeCtx(
445
445
  assistantMessageChannel: "vellum" as const,
446
446
  }),
447
447
 
448
+ graphMemory: {
449
+ onCompacted: () => {},
450
+ prepareMemory: async () => ({ runMessages: [], injectedTokens: 0, latencyMs: 0, mode: "none" as const }),
451
+ reinjectCachedMemory: (messages: Message[]) => ({
452
+ runMessages: messages,
453
+ injectedTokens: 0,
454
+ }),
455
+ } as unknown as AgentLoopConversationContext["graphMemory"],
456
+
448
457
  ...overrides,
449
458
  } as AgentLoopConversationContext;
450
459
  }
@@ -1,5 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
2
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
3
3
 
4
4
  mock.module("../util/logger.js", () => ({
5
5
  getLogger: () =>
@@ -48,14 +48,10 @@ mock.module("../permissions/types.js", () => ({
48
48
  import type { AssistantAttachmentDraft } from "../daemon/assistant-attachments.js";
49
49
  import { getFilePathForAttachment } from "../memory/attachments-store.js";
50
50
  import { addMessage, createConversation } from "../memory/conversation-crud.js";
51
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
51
+ import { getDb, initializeDb } from "../memory/db.js";
52
52
 
53
53
  initializeDb();
54
54
 
55
- afterAll(() => {
56
- resetDb();
57
- });
58
-
59
55
  function resetTables() {
60
56
  const db = getDb();
61
57
  db.run("DELETE FROM message_attachments");
@@ -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: () =>
@@ -17,7 +17,7 @@ import {
17
17
  projectAssistantMessage,
18
18
  recordConversationSeenSignal,
19
19
  } from "../memory/conversation-attention-store.js";
20
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
20
+ import { getDb, initializeDb } from "../memory/db.js";
21
21
  import {
22
22
  conversationAssistantAttentionState,
23
23
  conversationAttentionEvents,
@@ -66,10 +66,6 @@ function insertAssistantMessage(
66
66
  .run();
67
67
  }
68
68
 
69
- afterAll(() => {
70
- resetDb();
71
- });
72
-
73
69
  describe("conversation-attention-store", () => {
74
70
  beforeEach(() => {
75
71
  clearTables();
@@ -9,7 +9,7 @@
9
9
  * - lifecycle_events contains `conversations_clear_all` audit entry after clear
10
10
  */
11
11
 
12
- import { afterAll, describe, expect, mock, test } from "bun:test";
12
+ import { describe, expect, mock, test } from "bun:test";
13
13
 
14
14
  mock.module("../util/logger.js", () => ({
15
15
  getLogger: () =>
@@ -31,17 +31,13 @@ import {
31
31
  createConversation,
32
32
  getConversation,
33
33
  } from "../memory/conversation-crud.js";
34
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
34
+ import { getDb, initializeDb } from "../memory/db.js";
35
35
  import { enforcePolicy, getPolicy } from "../runtime/auth/route-policy.js";
36
36
  import type { AuthContext, Scope } from "../runtime/auth/types.js";
37
37
  import { conversationManagementRouteDefinitions } from "../runtime/routes/conversation-management-routes.js";
38
38
 
39
39
  initializeDb();
40
40
 
41
- afterAll(() => {
42
- resetDb();
43
- });
44
-
45
41
  /** Build a synthetic AuthContext for testing. */
46
42
  function buildAuthContext(overrides?: {
47
43
  principalType?: AuthContext["principalType"];
@@ -3,7 +3,7 @@
3
3
  * job also deletes the schedule, preventing orphaned scheduled automations.
4
4
  */
5
5
 
6
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
6
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
7
7
 
8
8
  mock.module("../util/logger.js", () => ({
9
9
  getLogger: () =>
@@ -23,16 +23,12 @@ import {
23
23
  createConversation,
24
24
  getConversation,
25
25
  } from "../memory/conversation-crud.js";
26
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
26
+ import { getDb, initializeDb } from "../memory/db.js";
27
27
  import { conversationManagementRouteDefinitions } from "../runtime/routes/conversation-management-routes.js";
28
28
  import { createSchedule, getSchedule } from "../schedule/schedule-store.js";
29
29
 
30
30
  initializeDb();
31
31
 
32
- afterAll(() => {
33
- resetDb();
34
- });
35
-
36
32
  function getRawDb(): Database {
37
33
  return (getDb() as unknown as { $client: Database }).$client;
38
34
  }
@@ -72,9 +68,8 @@ describe("DELETE /conversations/:id — schedule cleanup", () => {
72
68
  beforeEach(() => {
73
69
  getRawDb().run("DELETE FROM cron_runs");
74
70
  getRawDb().run("DELETE FROM cron_jobs");
75
- getRawDb().run("DELETE FROM memory_item_sources");
71
+ getRawDb().run("DELETE FROM memory_graph_nodes");
76
72
  getRawDb().run("DELETE FROM memory_segments");
77
- getRawDb().run("DELETE FROM memory_items");
78
73
  getRawDb().run("DELETE FROM memory_summaries");
79
74
  getRawDb().run("DELETE FROM memory_embeddings");
80
75
  getRawDb().run("DELETE FROM memory_jobs");
@@ -297,9 +292,8 @@ describe("POST /conversations/:id/wipe — schedule cleanup", () => {
297
292
  beforeEach(() => {
298
293
  getRawDb().run("DELETE FROM cron_runs");
299
294
  getRawDb().run("DELETE FROM cron_jobs");
300
- getRawDb().run("DELETE FROM memory_item_sources");
295
+ getRawDb().run("DELETE FROM memory_graph_nodes");
301
296
  getRawDb().run("DELETE FROM memory_segments");
302
- getRawDb().run("DELETE FROM memory_items");
303
297
  getRawDb().run("DELETE FROM memory_summaries");
304
298
  getRawDb().run("DELETE FROM memory_embeddings");
305
299
  getRawDb().run("DELETE FROM memory_jobs");
@@ -13,7 +13,7 @@ import {
13
13
  rmSync,
14
14
  } from "node:fs";
15
15
  import { join } from "node:path";
16
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
16
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
17
17
 
18
18
  // ---------------------------------------------------------------------------
19
19
  // Mocks — must come before any imports that depend on them
@@ -59,14 +59,10 @@ import {
59
59
  getConversationDirPath,
60
60
  syncMessageToDisk,
61
61
  } from "../memory/conversation-disk-view.js";
62
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
62
+ import { getDb, initializeDb } from "../memory/db.js";
63
63
 
64
64
  initializeDb();
65
65
 
66
- afterAll(() => {
67
- resetDb();
68
- });
69
-
70
66
  function resetTables() {
71
67
  const db = getDb();
72
68
  db.run("DELETE FROM message_attachments");
@@ -8,7 +8,7 @@ import {
8
8
  } from "node:fs";
9
9
  import { tmpdir } from "node:os";
10
10
  import { join } from "node:path";
11
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
11
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
12
12
 
13
13
  // ---------------------------------------------------------------------------
14
14
  // Mocks — must come before any imports that depend on them
@@ -61,14 +61,10 @@ import {
61
61
  syncMessageToDisk,
62
62
  updateMetaFile,
63
63
  } from "../memory/conversation-disk-view.js";
64
- import { getDb, initializeDb, rawRun, resetDb } from "../memory/db.js";
64
+ import { getDb, initializeDb, rawRun } from "../memory/db.js";
65
65
 
66
66
  initializeDb();
67
67
 
68
- afterAll(() => {
69
- resetDb();
70
- });
71
-
72
68
  function resetTables() {
73
69
  const db = getDb();
74
70
  db.run("DELETE FROM message_attachments");
@@ -90,7 +90,6 @@ describe("classifyConversationError", () => {
90
90
  "rate limit exceeded",
91
91
  "Rate-limit hit",
92
92
  "too many requests",
93
- "overloaded",
94
93
  ];
95
94
 
96
95
  for (const msg of cases) {
@@ -98,12 +97,44 @@ describe("classifyConversationError", () => {
98
97
  const result = classifyConversationError(new Error(msg), baseCtx);
99
98
  expect(result.code).toBe("PROVIDER_RATE_LIMIT");
100
99
  expect(result.retryable).toBe(true);
101
- expect(result.userMessage).toContain("busy");
100
+ expect(result.userMessage).toContain("rate limited");
102
101
  expect(result.errorCategory).toBe("rate_limit");
103
102
  });
104
103
  }
105
104
  });
106
105
 
106
+ describe("provider overloaded errors", () => {
107
+ it('classifies "overloaded" as PROVIDER_OVERLOADED', () => {
108
+ const result = classifyConversationError(
109
+ new Error("overloaded"),
110
+ baseCtx,
111
+ );
112
+ expect(result.code).toBe("PROVIDER_OVERLOADED");
113
+ expect(result.retryable).toBe(true);
114
+ expect(result.userMessage).toContain("overloaded");
115
+ expect(result.errorCategory).toBe("provider_overloaded");
116
+ });
117
+
118
+ it("classifies Anthropic overloaded_error (no statusCode) as PROVIDER_OVERLOADED", () => {
119
+ const err = new ProviderError(
120
+ 'Anthropic API error (undefined): {"type":"error","error":{"type":"overloaded_error","message":"Overloaded"}}',
121
+ "anthropic",
122
+ );
123
+ const result = classifyConversationError(err, baseCtx);
124
+ expect(result.code).toBe("PROVIDER_OVERLOADED");
125
+ expect(result.retryable).toBe(true);
126
+ expect(result.errorCategory).toBe("provider_overloaded");
127
+ });
128
+
129
+ it("classifies ProviderError with 529 as PROVIDER_OVERLOADED", () => {
130
+ const err = new ProviderError("Overloaded", "anthropic", 529);
131
+ const result = classifyConversationError(err, baseCtx);
132
+ expect(result.code).toBe("PROVIDER_OVERLOADED");
133
+ expect(result.retryable).toBe(true);
134
+ expect(result.errorCategory).toBe("provider_overloaded");
135
+ });
136
+ });
137
+
107
138
  describe("provider API errors", () => {
108
139
  const cases = [
109
140
  "HTTP 500 Internal Server Error",
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
3
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
4
4
 
5
5
  import { eq, like } from "drizzle-orm";
6
6
 
@@ -39,7 +39,7 @@ import {
39
39
  PRIVATE_CONVERSATION_FORK_ERROR,
40
40
  } from "../memory/conversation-crud.js";
41
41
  import { getConversationDirPath } from "../memory/conversation-disk-view.js";
42
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
42
+ import { getDb, initializeDb } from "../memory/db.js";
43
43
  import { getRequestLogsByMessageId } from "../memory/llm-request-log-store.js";
44
44
  import {
45
45
  channelInboundEvents,
@@ -71,10 +71,6 @@ function parseMetadata(metadata: string | null): unknown {
71
71
  return metadata == null ? null : JSON.parse(metadata);
72
72
  }
73
73
 
74
- afterAll(() => {
75
- resetDb();
76
- });
77
-
78
74
  describe("forkConversation", () => {
79
75
  beforeEach(() => {
80
76
  resetTables();
@@ -782,6 +782,11 @@ describe("web_search_tool_result structural guard", () => {
782
782
  // Extracts tool results from persisted message content for work-item
783
783
  // display. web_search_tool_result blocks are not relevant here.
784
784
  "runtime/routes/work-items-routes.ts",
785
+
786
+ // Media token counting iterates tool_result.contentBlocks for nested
787
+ // image/file blocks. web_search_tool_result has opaque content with no
788
+ // contentBlocks property, so it cannot contain nested media.
789
+ "daemon/context-overflow-reducer.ts",
785
790
  ]);
786
791
 
787
792
  /**
@@ -106,7 +106,7 @@ function makeConversation(): Conversation {
106
106
  stopReason: "end_turn",
107
107
  }),
108
108
  };
109
- return new Conversation(
109
+ const conv = new Conversation(
110
110
  "conv-1",
111
111
  provider,
112
112
  "system prompt",
@@ -114,6 +114,10 @@ function makeConversation(): Conversation {
114
114
  () => {},
115
115
  "/tmp",
116
116
  );
117
+ // Default to guardian trust so history repair tests load all messages.
118
+ // Tests that exercise untrusted-actor filtering override this explicitly.
119
+ conv.setTrustContext({ trustClass: "guardian", sourceChannel: "vellum" });
120
+ return conv;
117
121
  }
118
122
 
119
123
  describe("loadFromDb history repair", () => {
@@ -18,6 +18,15 @@ function makeImageBlock(
18
18
  };
19
19
  }
20
20
 
21
+ function makeImageBlockWithSize(
22
+ dataLength: number,
23
+ ): Extract<ContentBlock, { type: "image" }> {
24
+ return {
25
+ type: "image",
26
+ source: { type: "base64", media_type: "image/png", data: "A".repeat(dataLength) },
27
+ };
28
+ }
29
+
21
30
  function makeUserMessage(...blocks: ContentBlock[]): Message {
22
31
  return { role: "user", content: blocks };
23
32
  }
@@ -88,4 +97,86 @@ describe("stripMediaPayloadsForRetry", () => {
88
97
  const latestBlocks = result.messages[3].content;
89
98
  expect(latestBlocks.filter((b) => b.type === "image").length).toBe(1);
90
99
  });
100
+
101
+ // ---------------------------------------------------------------------------
102
+ // Budget-aware media retention
103
+ // ---------------------------------------------------------------------------
104
+
105
+ test("budget-aware: keeps images that fit within token budget", () => {
106
+ // Non-Anthropic estimation: estimateTextTokens(base64Data) + overhead (~19 tokens).
107
+ // Data length 4000 → 1000 data tokens + 19 overhead ≈ 1019 tokens/image.
108
+ // Budget of 3500 allows 3 images (3 * 1019 = 3057 <= 3500) but not 4.
109
+ const images = Array.from({ length: 5 }, () => makeImageBlockWithSize(4000));
110
+ const messages: Message[] = [
111
+ makeUserMessage({ type: "text", text: "describe these" }, ...images),
112
+ ];
113
+
114
+ const result = stripMediaPayloadsForRetry(messages, {
115
+ mediaTokenBudget: 3500,
116
+ providerName: "mock",
117
+ });
118
+ expect(result.modified).toBe(true);
119
+
120
+ const content = result.messages[0].content;
121
+ const keptImages = content.filter((b) => b.type === "image");
122
+ const stubs = content.filter(
123
+ (b) => b.type === "text" && (b as { text: string }).text.includes("Image omitted"),
124
+ );
125
+ expect(keptImages.length).toBe(3);
126
+ expect(stubs.length).toBe(2);
127
+ });
128
+
129
+ test("budget-aware: keeps all images when budget is generous", () => {
130
+ const images = Array.from({ length: 3 }, () => makeImageBlockWithSize(100));
131
+ const messages: Message[] = [
132
+ makeUserMessage({ type: "text", text: "describe these" }, ...images),
133
+ ];
134
+
135
+ const result = stripMediaPayloadsForRetry(messages, {
136
+ mediaTokenBudget: 100_000,
137
+ providerName: "mock",
138
+ });
139
+ expect(result.modified).toBe(false);
140
+ expect(result.replacedBlocks).toBe(0);
141
+
142
+ const content = result.messages[0].content;
143
+ const keptImages = content.filter((b) => b.type === "image");
144
+ expect(keptImages.length).toBe(3);
145
+ });
146
+
147
+ test("budget-aware: stubs all when budget is zero", () => {
148
+ const images = Array.from({ length: 3 }, () => makeImageBlockWithSize(100));
149
+ const messages: Message[] = [
150
+ makeUserMessage({ type: "text", text: "describe these" }, ...images),
151
+ ];
152
+
153
+ const result = stripMediaPayloadsForRetry(messages, {
154
+ mediaTokenBudget: 0,
155
+ providerName: "mock",
156
+ });
157
+ expect(result.modified).toBe(true);
158
+ expect(result.replacedBlocks).toBe(3);
159
+
160
+ const content = result.messages[0].content;
161
+ const keptImages = content.filter((b) => b.type === "image");
162
+ expect(keptImages.length).toBe(0);
163
+ });
164
+
165
+ test("no options falls back to hardcoded limit", () => {
166
+ const images = Array.from({ length: 5 }, () => makeImageBlockWithSize(100));
167
+ const messages: Message[] = [
168
+ makeUserMessage({ type: "text", text: "describe these" }, ...images),
169
+ ];
170
+
171
+ const result = stripMediaPayloadsForRetry(messages);
172
+ expect(result.modified).toBe(true);
173
+
174
+ const content = result.messages[0].content;
175
+ const keptImages = content.filter((b) => b.type === "image");
176
+ const stubs = content.filter(
177
+ (b) => b.type === "text" && (b as { text: string }).text.includes("Image omitted"),
178
+ );
179
+ expect(keptImages.length).toBe(3);
180
+ expect(stubs.length).toBe(2);
181
+ });
91
182
  });
@@ -1458,7 +1458,10 @@ describe("injectNowScratchpad", () => {
1458
1458
  const messageWithMemory: Message = {
1459
1459
  role: "user",
1460
1460
  content: [
1461
- { type: "text", text: "<memory_context __injected>\nrecalled notes\n</memory_context>" },
1461
+ {
1462
+ type: "text",
1463
+ text: "<memory_context __injected>\nrecalled notes\n</memory_context>",
1464
+ },
1462
1465
  { type: "text", text: "What should I work on?" },
1463
1466
  ],
1464
1467
  };
@@ -1635,9 +1638,9 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
1635
1638
  (result[0].content[0] as { type: "text"; text: string }).text,
1636
1639
  ).toContain("<NOW.md");
1637
1640
  // Original text is last
1638
- expect(
1639
- (result[0].content[1] as { type: "text"; text: string }).text,
1640
- ).toBe("What should I do?");
1641
+ expect((result[0].content[1] as { type: "text"; text: string }).text).toBe(
1642
+ "What should I do?",
1643
+ );
1641
1644
  });
1642
1645
 
1643
1646
  test("does not inject when nowScratchpad is null", () => {