@vellumai/assistant 0.5.16 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (592) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +69 -16
  3. package/Dockerfile +2 -5
  4. package/bun.lock +6 -2
  5. package/docker-entrypoint.sh +32 -1
  6. package/docs/architecture/integrations.md +1 -1
  7. package/docs/architecture/memory.md +21 -24
  8. package/knip.json +2 -1
  9. package/openapi.yaml +1198 -83
  10. package/package.json +5 -1
  11. package/src/__tests__/actor-token-service.test.ts +68 -0
  12. package/src/__tests__/agent-loop.test.ts +0 -32
  13. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  14. package/src/__tests__/anthropic-provider.test.ts +217 -98
  15. package/src/__tests__/app-compiler.test.ts +120 -0
  16. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  17. package/src/__tests__/app-executors.test.ts +47 -1
  18. package/src/__tests__/app-source-watcher.test.ts +159 -0
  19. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  20. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  21. package/src/__tests__/call-domain.test.ts +2 -6
  22. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  23. package/src/__tests__/call-recovery.test.ts +2 -6
  24. package/src/__tests__/call-routes-http.test.ts +2 -6
  25. package/src/__tests__/call-store.test.ts +2 -6
  26. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  27. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  28. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  29. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  30. package/src/__tests__/checker.test.ts +63 -9
  31. package/src/__tests__/clawhub.test.ts +54 -24
  32. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  33. package/src/__tests__/config-schema.test.ts +6 -1
  34. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  35. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  36. package/src/__tests__/contacts-tools.test.ts +31 -0
  37. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  38. package/src/__tests__/context-token-estimator.test.ts +175 -10
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +13 -6
  40. package/src/__tests__/conversation-agent-loop.test.ts +13 -51
  41. package/src/__tests__/conversation-attachments.test.ts +2 -6
  42. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  43. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  44. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  45. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  46. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  47. package/src/__tests__/conversation-error.test.ts +33 -2
  48. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  49. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  50. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
  53. package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
  54. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  55. package/src/__tests__/conversation-store.test.ts +2 -6
  56. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
  57. package/src/__tests__/conversation-usage.test.ts +2 -6
  58. package/src/__tests__/conversation-wipe.test.ts +13 -414
  59. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
  60. package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
  61. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  62. package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
  63. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  64. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  65. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  66. package/src/__tests__/date-context.test.ts +76 -210
  67. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
  68. package/src/__tests__/file-list-tool.test.ts +219 -0
  69. package/src/__tests__/first-greeting.test.ts +1 -1
  70. package/src/__tests__/followup-tools.test.ts +2 -6
  71. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  72. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  73. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  74. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  75. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  76. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  77. package/src/__tests__/guardian-action-store.test.ts +2 -6
  78. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  79. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  80. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  81. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  82. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  83. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  84. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  85. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  86. package/src/__tests__/heartbeat-service.test.ts +180 -3
  87. package/src/__tests__/identity-routes.test.ts +328 -0
  88. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  89. package/src/__tests__/injection-block.test.ts +178 -0
  90. package/src/__tests__/install-meta.test.ts +506 -0
  91. package/src/__tests__/install-skill-routing.test.ts +293 -0
  92. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  93. package/src/__tests__/invite-routes-http.test.ts +2 -6
  94. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +17 -28
  95. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  96. package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
  97. package/src/__tests__/llm-context-normalization.test.ts +18 -18
  98. package/src/__tests__/llm-context-route-provider.test.ts +103 -6
  99. package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
  100. package/src/__tests__/llm-usage-store.test.ts +2 -6
  101. package/src/__tests__/log-export-workspace.test.ts +74 -111
  102. package/src/__tests__/managed-store.test.ts +38 -11
  103. package/src/__tests__/mcp-abort-signal.test.ts +5 -0
  104. package/src/__tests__/mcp-client-auth.test.ts +5 -0
  105. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  106. package/src/__tests__/memory-recall-log-store.test.ts +134 -6
  107. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  108. package/src/__tests__/migration-export-streaming.test.ts +304 -0
  109. package/src/__tests__/migration-import-commit-http.test.ts +11 -10
  110. package/src/__tests__/mock-fetch.ts +87 -0
  111. package/src/__tests__/non-member-access-request.test.ts +2 -6
  112. package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
  113. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  114. package/src/__tests__/oauth-cli.test.ts +364 -2
  115. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  116. package/src/__tests__/onboarding-template-contract.test.ts +62 -14
  117. package/src/__tests__/outlook-attachments.test.ts +301 -0
  118. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  119. package/src/__tests__/outlook-categories.test.ts +212 -0
  120. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  121. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  122. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  123. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  124. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  125. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  126. package/src/__tests__/outlook-trash.test.ts +77 -0
  127. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  128. package/src/__tests__/parser.test.ts +32 -0
  129. package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
  130. package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
  131. package/src/__tests__/permission-mode-sse.test.ts +418 -0
  132. package/src/__tests__/permission-mode-store.test.ts +277 -0
  133. package/src/__tests__/permission-mode.test.ts +101 -0
  134. package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
  135. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  136. package/src/__tests__/playbook-execution.test.ts +76 -80
  137. package/src/__tests__/playbook-tools.test.ts +5 -7
  138. package/src/__tests__/profiler-routes.test.ts +502 -0
  139. package/src/__tests__/profiler-run-store.test.ts +441 -0
  140. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  141. package/src/__tests__/proxy-approval-callback.test.ts +4 -75
  142. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  143. package/src/__tests__/registry.test.ts +3 -3
  144. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  145. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  146. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  147. package/src/__tests__/sandbox-host-parity.test.ts +5 -4
  148. package/src/__tests__/schedule-store.test.ts +2 -6
  149. package/src/__tests__/schedule-tools.test.ts +2 -6
  150. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  151. package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
  152. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  153. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  154. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
  155. package/src/__tests__/search-skills-unified.test.ts +422 -0
  156. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  157. package/src/__tests__/send-endpoint-busy.test.ts +44 -9
  158. package/src/__tests__/sequence-store.test.ts +2 -6
  159. package/src/__tests__/server-history-render.test.ts +2 -6
  160. package/src/__tests__/set-permission-mode.test.ts +274 -0
  161. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  162. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  163. package/src/__tests__/skill-load-feature-flag.test.ts +23 -11
  164. package/src/__tests__/skill-memory.test.ts +2 -741
  165. package/src/__tests__/skills-uninstall.test.ts +2 -2
  166. package/src/__tests__/skills.test.ts +1 -1
  167. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  168. package/src/__tests__/strip-memory-injections.test.ts +187 -0
  169. package/src/__tests__/subagent-detail.test.ts +84 -0
  170. package/src/__tests__/subagent-disposal.test.ts +308 -0
  171. package/src/__tests__/subagent-manager-notify.test.ts +19 -10
  172. package/src/__tests__/subagent-notify-parent.test.ts +390 -0
  173. package/src/__tests__/subagent-role-registry.test.ts +108 -0
  174. package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
  175. package/src/__tests__/subagent-tools.test.ts +464 -4
  176. package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
  177. package/src/__tests__/task-compiler.test.ts +2 -6
  178. package/src/__tests__/task-management-tools.test.ts +2 -6
  179. package/src/__tests__/task-memory-cleanup.test.ts +185 -241
  180. package/src/__tests__/task-runner.test.ts +2 -6
  181. package/src/__tests__/task-scheduler.test.ts +2 -6
  182. package/src/__tests__/terminal-tools.test.ts +17 -27
  183. package/src/__tests__/test-preload.ts +7 -0
  184. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  185. package/src/__tests__/tool-executor.test.ts +4 -26
  186. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  187. package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
  188. package/src/__tests__/top-level-renderer.test.ts +10 -13
  189. package/src/__tests__/trust-store.test.ts +1 -1
  190. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  191. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
  192. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  193. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  194. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  195. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  196. package/src/__tests__/usage-routes.test.ts +2 -6
  197. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  198. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  199. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  200. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  201. package/src/__tests__/volume-security-guard.test.ts +2 -0
  202. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  203. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  204. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  205. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  206. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
  207. package/src/__tests__/workspace-policy.test.ts +1 -1
  208. package/src/agent/attachments.ts +7 -2
  209. package/src/agent/image-optimize.ts +165 -0
  210. package/src/agent/loop.ts +7 -15
  211. package/src/approvals/guardian-request-resolvers.ts +24 -0
  212. package/src/avatar/traits-png-sync.ts +3 -3
  213. package/src/bundler/app-compiler.ts +179 -2
  214. package/src/bundler/package-resolver.ts +3 -5
  215. package/src/cli/__tests__/notifications.test.ts +1 -2
  216. package/src/cli/__tests__/run-assistant-command.ts +29 -0
  217. package/src/cli/commands/__tests__/email-download.test.ts +245 -0
  218. package/src/cli/commands/__tests__/email-list.test.ts +192 -0
  219. package/src/cli/commands/__tests__/email-register.test.ts +186 -0
  220. package/src/cli/commands/__tests__/email-send.test.ts +291 -0
  221. package/src/cli/commands/__tests__/email-status.test.ts +181 -0
  222. package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
  223. package/src/cli/commands/__tests__/routes.test.ts +562 -0
  224. package/src/cli/commands/avatar.ts +3 -3
  225. package/src/cli/commands/config.ts +26 -13
  226. package/src/cli/commands/conversations.ts +1 -8
  227. package/src/cli/commands/doctor.ts +2 -2
  228. package/src/cli/commands/email.ts +584 -835
  229. package/src/cli/commands/memory.ts +37 -84
  230. package/src/cli/commands/notifications.ts +7 -2
  231. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  232. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  233. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  234. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  235. package/src/cli/commands/oauth/connect.ts +25 -11
  236. package/src/cli/commands/oauth/mode.ts +7 -0
  237. package/src/cli/commands/oauth/shared.ts +39 -3
  238. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  239. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  240. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  241. package/src/cli/commands/platform/index.ts +16 -16
  242. package/src/cli/commands/routes.ts +396 -0
  243. package/src/cli/commands/skills.ts +218 -36
  244. package/src/cli/commands/trust.ts +2 -2
  245. package/src/cli/lib/daemon-credential-client.ts +2 -3
  246. package/src/cli/program.ts +2 -0
  247. package/src/cli.ts +1 -120
  248. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  249. package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
  250. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  251. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  252. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  253. package/src/config/bundled-skills/gmail/SKILL.md +4 -12
  254. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  255. package/src/config/bundled-skills/messaging/SKILL.md +17 -18
  256. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  257. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  258. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  259. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  260. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  261. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  262. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  263. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  264. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  265. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  266. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  267. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  268. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  269. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  270. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  271. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  272. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  273. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  274. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  275. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  276. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  277. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  278. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  279. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  280. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  281. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  282. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  283. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  284. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  285. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  286. package/src/config/bundled-skills/schedule/SKILL.md +22 -2
  287. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  288. package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
  289. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
  290. package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
  291. package/src/config/bundled-skills/slack/SKILL.md +3 -7
  292. package/src/config/bundled-skills/subagent/SKILL.md +43 -3
  293. package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
  294. package/src/config/bundled-tool-registry.ts +56 -4
  295. package/src/config/env-registry.ts +78 -8
  296. package/src/config/feature-flag-registry.json +38 -125
  297. package/src/config/schema.ts +8 -0
  298. package/src/config/schemas/filing.ts +51 -0
  299. package/src/config/schemas/heartbeat.ts +15 -12
  300. package/src/config/schemas/memory-lifecycle.ts +12 -0
  301. package/src/config/schemas/platform.ts +8 -0
  302. package/src/config/schemas/security.ts +14 -0
  303. package/src/config/schemas/timeouts.ts +1 -1
  304. package/src/config/skills.ts +18 -7
  305. package/src/context/token-estimator.ts +25 -18
  306. package/src/context/window-manager.ts +6 -2
  307. package/src/credential-execution/process-manager.ts +3 -1
  308. package/src/daemon/app-source-watcher.ts +93 -0
  309. package/src/daemon/config-watcher.ts +79 -1
  310. package/src/daemon/context-overflow-reducer.ts +46 -2
  311. package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
  312. package/src/daemon/conversation-agent-loop.ts +236 -108
  313. package/src/daemon/conversation-error.ts +31 -8
  314. package/src/daemon/conversation-history.ts +4 -19
  315. package/src/daemon/conversation-lifecycle.ts +36 -9
  316. package/src/daemon/conversation-media-retry.ts +85 -7
  317. package/src/daemon/conversation-notifiers.ts +4 -1
  318. package/src/daemon/conversation-process.ts +13 -7
  319. package/src/daemon/conversation-runtime-assembly.ts +305 -306
  320. package/src/daemon/conversation-tool-setup.ts +44 -14
  321. package/src/daemon/conversation-workspace.ts +1 -2
  322. package/src/daemon/conversation.ts +59 -2
  323. package/src/daemon/daemon-control.ts +8 -2
  324. package/src/daemon/date-context.ts +26 -53
  325. package/src/daemon/first-greeting.ts +1 -1
  326. package/src/daemon/handlers/conversations.ts +4 -7
  327. package/src/daemon/handlers/shared.test.ts +143 -0
  328. package/src/daemon/handlers/shared.ts +85 -17
  329. package/src/daemon/handlers/skills.ts +416 -209
  330. package/src/daemon/lifecycle.ts +212 -131
  331. package/src/daemon/main.ts +5 -1
  332. package/src/daemon/message-types/conversations.ts +29 -7
  333. package/src/daemon/message-types/messages.ts +12 -2
  334. package/src/daemon/message-types/schedules.ts +1 -0
  335. package/src/daemon/message-types/settings.ts +6 -0
  336. package/src/daemon/message-types/skills.ts +97 -36
  337. package/src/daemon/profiler-run-store.ts +557 -0
  338. package/src/daemon/providers-setup.ts +5 -0
  339. package/src/daemon/server.ts +100 -11
  340. package/src/daemon/shutdown-handlers.ts +5 -0
  341. package/src/daemon/tool-side-effects.ts +50 -8
  342. package/src/export/transcript-formatter.ts +148 -0
  343. package/src/filing/filing-service.ts +228 -0
  344. package/src/heartbeat/heartbeat-service.ts +97 -7
  345. package/src/hooks/cli.ts +2 -2
  346. package/src/hooks/runner.ts +15 -38
  347. package/src/inbound/platform-callback-registration.ts +14 -14
  348. package/src/mcp/client.ts +6 -0
  349. package/src/mcp/mcp-oauth-provider.ts +149 -27
  350. package/src/memory/admin.ts +42 -75
  351. package/src/memory/app-store.ts +69 -0
  352. package/src/memory/conversation-bootstrap.ts +3 -1
  353. package/src/memory/conversation-crud.ts +211 -288
  354. package/src/memory/conversation-group-migration.ts +157 -0
  355. package/src/memory/conversation-queries.ts +61 -13
  356. package/src/memory/conversation-title-service.ts +1 -0
  357. package/src/memory/db-init.ts +194 -361
  358. package/src/memory/embed.ts +73 -0
  359. package/src/memory/embedding-backend.ts +8 -14
  360. package/src/memory/embedding-runtime-manager.ts +12 -114
  361. package/src/memory/fingerprint.ts +2 -2
  362. package/src/memory/graph/bootstrap.ts +521 -0
  363. package/src/memory/graph/capability-seed.ts +449 -0
  364. package/src/memory/graph/consolidation.ts +725 -0
  365. package/src/memory/graph/conversation-graph-memory.ts +659 -0
  366. package/src/memory/graph/decay.test.ts +208 -0
  367. package/src/memory/graph/decay.ts +195 -0
  368. package/src/memory/graph/extraction-job.ts +74 -0
  369. package/src/memory/graph/extraction.test.ts +936 -0
  370. package/src/memory/graph/extraction.ts +1297 -0
  371. package/src/memory/graph/graph-memory-state-store.ts +37 -0
  372. package/src/memory/graph/graph-search.ts +280 -0
  373. package/src/memory/graph/image-ref-utils.ts +29 -0
  374. package/src/memory/graph/injection.test.ts +513 -0
  375. package/src/memory/graph/injection.ts +469 -0
  376. package/src/memory/graph/inspect.ts +543 -0
  377. package/src/memory/graph/narrative.ts +267 -0
  378. package/src/memory/graph/pattern-scan.ts +269 -0
  379. package/src/memory/graph/retriever.ts +1111 -0
  380. package/src/memory/graph/scoring.test.ts +548 -0
  381. package/src/memory/graph/scoring.ts +232 -0
  382. package/src/memory/graph/serendipity.ts +65 -0
  383. package/src/memory/graph/store.test.ts +1098 -0
  384. package/src/memory/graph/store.ts +838 -0
  385. package/src/memory/graph/tool-handlers.ts +301 -0
  386. package/src/memory/graph/tools.ts +97 -0
  387. package/src/memory/graph/triggers.test.ts +487 -0
  388. package/src/memory/graph/triggers.ts +223 -0
  389. package/src/memory/graph/types.ts +295 -0
  390. package/src/memory/group-crud.ts +191 -0
  391. package/src/memory/indexer.ts +37 -19
  392. package/src/memory/job-handlers/cleanup.ts +32 -42
  393. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  394. package/src/memory/job-handlers/embedding.ts +5 -31
  395. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  396. package/src/memory/job-handlers/summarization.ts +32 -17
  397. package/src/memory/job-utils.ts +1 -1
  398. package/src/memory/jobs-store.ts +21 -31
  399. package/src/memory/jobs-worker.ts +180 -129
  400. package/src/memory/llm-request-log-store.ts +96 -12
  401. package/src/memory/memory-recall-log-store.ts +49 -5
  402. package/src/memory/message-content.ts +1 -0
  403. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  404. package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -0
  405. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  406. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  407. package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
  408. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
  409. package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
  410. package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
  411. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
  412. package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
  413. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
  414. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
  415. package/src/memory/migrations/index.ts +12 -0
  416. package/src/memory/migrations/registry.ts +16 -0
  417. package/src/memory/qdrant-client.ts +44 -17
  418. package/src/memory/schema/conversations.ts +14 -0
  419. package/src/memory/schema/index.ts +1 -0
  420. package/src/memory/schema/infrastructure.ts +8 -1
  421. package/src/memory/schema/memory-core.ts +0 -51
  422. package/src/memory/schema/memory-graph.ts +154 -0
  423. package/src/memory/search/semantic.ts +47 -91
  424. package/src/memory/task-memory-cleanup.ts +58 -61
  425. package/src/messaging/providers/outlook/adapter.ts +8 -1
  426. package/src/messaging/providers/outlook/client.ts +299 -0
  427. package/src/messaging/providers/outlook/types.ts +118 -0
  428. package/src/notifications/adapters/macos.ts +1 -0
  429. package/src/notifications/copy-composer.ts +95 -0
  430. package/src/notifications/decision-engine.ts +35 -0
  431. package/src/notifications/signal.ts +16 -0
  432. package/src/oauth/seed-providers.ts +2 -1
  433. package/src/permissions/checker.ts +36 -4
  434. package/src/permissions/defaults.ts +4 -4
  435. package/src/permissions/permission-mode-store.ts +180 -0
  436. package/src/permissions/permission-mode.ts +31 -0
  437. package/src/permissions/workspace-policy.ts +10 -1
  438. package/src/playbooks/playbook-compiler.ts +19 -18
  439. package/src/playbooks/types.ts +4 -3
  440. package/src/prompts/system-prompt.ts +62 -36
  441. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
  442. package/src/prompts/templates/BOOTSTRAP.md +70 -165
  443. package/src/prompts/templates/HEARTBEAT.md +3 -1
  444. package/src/prompts/templates/SOUL.md +25 -4
  445. package/src/prompts/templates/UPDATES.md +8 -0
  446. package/src/providers/anthropic/client.ts +136 -220
  447. package/src/providers/gemini/client.ts +1 -1
  448. package/src/providers/openai/client.ts +1 -1
  449. package/src/providers/registry.ts +1 -1
  450. package/src/providers/retry.ts +19 -3
  451. package/src/runtime/actor-trust-resolver.ts +5 -1
  452. package/src/runtime/auth/route-policy.ts +30 -0
  453. package/src/runtime/guardian-reply-router.ts +5 -1
  454. package/src/runtime/http-server.ts +55 -5
  455. package/src/runtime/http-types.ts +12 -1
  456. package/src/runtime/middleware/auth.ts +20 -0
  457. package/src/runtime/migrations/vbundle-builder.ts +389 -3
  458. package/src/runtime/migrations/vbundle-importer.ts +8 -6
  459. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
  460. package/src/runtime/routes/app-management-routes.ts +1 -11
  461. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
  462. package/src/runtime/routes/archive-utils.ts +29 -0
  463. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  464. package/src/runtime/routes/attachment-routes.ts +106 -16
  465. package/src/runtime/routes/avatar-routes.ts +2 -9
  466. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  467. package/src/runtime/routes/btw-routes.ts +22 -1
  468. package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
  469. package/src/runtime/routes/conversation-management-routes.ts +3 -14
  470. package/src/runtime/routes/conversation-query-routes.ts +49 -3
  471. package/src/runtime/routes/conversation-routes.ts +264 -44
  472. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  473. package/src/runtime/routes/debug-routes.ts +1 -1
  474. package/src/runtime/routes/global-search-routes.ts +21 -19
  475. package/src/runtime/routes/group-routes.ts +207 -0
  476. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  477. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  478. package/src/runtime/routes/heartbeat-routes.ts +4 -10
  479. package/src/runtime/routes/identity-routes.ts +53 -18
  480. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  481. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  482. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  483. package/src/runtime/routes/llm-context-normalization.ts +14 -10
  484. package/src/runtime/routes/log-export-routes.ts +23 -275
  485. package/src/runtime/routes/memory-item-routes.test.ts +170 -247
  486. package/src/runtime/routes/memory-item-routes.ts +341 -388
  487. package/src/runtime/routes/migration-routes.ts +18 -7
  488. package/src/runtime/routes/profiler-routes.ts +350 -0
  489. package/src/runtime/routes/schedule-routes.ts +28 -11
  490. package/src/runtime/routes/settings-routes.ts +95 -8
  491. package/src/runtime/routes/skills-routes.ts +103 -37
  492. package/src/runtime/routes/subagents-routes.ts +28 -7
  493. package/src/runtime/routes/user-route-dispatcher.ts +223 -0
  494. package/src/runtime/routes/user-routes.ts +41 -0
  495. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  496. package/src/runtime/routes/workspace-routes.ts +0 -1
  497. package/src/schedule/schedule-store.ts +30 -0
  498. package/src/schedule/scheduler.ts +52 -18
  499. package/src/security/oauth2.ts +1 -1
  500. package/src/security/secure-keys.ts +4 -8
  501. package/src/shared/provider-env-vars.ts +19 -0
  502. package/src/skills/catalog-cache.ts +5 -0
  503. package/src/skills/catalog-install.ts +25 -16
  504. package/src/skills/clawhub.ts +134 -154
  505. package/src/skills/install-meta.ts +208 -0
  506. package/src/skills/managed-store.ts +29 -18
  507. package/src/skills/skill-memory.ts +12 -229
  508. package/src/skills/skillssh-registry.ts +19 -17
  509. package/src/subagent/index.ts +13 -3
  510. package/src/subagent/manager.ts +308 -29
  511. package/src/subagent/types.ts +68 -0
  512. package/src/tasks/task-runner.ts +7 -5
  513. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  514. package/src/tools/apps/executors.ts +29 -4
  515. package/src/tools/browser/runtime-check.ts +3 -1
  516. package/src/tools/filesystem/list.ts +93 -0
  517. package/src/tools/memory/register.ts +63 -46
  518. package/src/tools/permission-checker.ts +85 -1
  519. package/src/tools/registry.ts +4 -0
  520. package/src/tools/schedule/create.ts +3 -0
  521. package/src/tools/schedule/list.ts +1 -0
  522. package/src/tools/schedule/update.ts +6 -0
  523. package/src/tools/shared/filesystem/errors.ts +5 -0
  524. package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
  525. package/src/tools/shared/filesystem/image-read.ts +22 -85
  526. package/src/tools/shared/filesystem/types.ts +17 -0
  527. package/src/tools/shared/shell-output.ts +31 -2
  528. package/src/tools/subagent/abort.ts +12 -2
  529. package/src/tools/subagent/message.ts +9 -2
  530. package/src/tools/subagent/notify-parent.ts +79 -0
  531. package/src/tools/subagent/read.ts +29 -8
  532. package/src/tools/subagent/resolve.ts +21 -0
  533. package/src/tools/subagent/spawn.ts +2 -0
  534. package/src/tools/subagent/status.ts +11 -1
  535. package/src/tools/system/avatar-generator.ts +3 -3
  536. package/src/tools/system/register.ts +23 -0
  537. package/src/tools/system/set-permission-mode.ts +103 -0
  538. package/src/tools/terminal/parser.ts +30 -5
  539. package/src/tools/terminal/safe-env.ts +17 -1
  540. package/src/tools/tool-manifest.ts +9 -3
  541. package/src/tools/types.ts +2 -0
  542. package/src/util/browser.ts +25 -10
  543. package/src/util/bun-runtime.ts +172 -0
  544. package/src/util/logger.ts +1 -1
  545. package/src/util/platform.ts +50 -17
  546. package/src/watcher/providers/outlook-calendar.ts +343 -0
  547. package/src/watcher/providers/outlook.ts +198 -0
  548. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
  549. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
  550. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  551. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  552. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  553. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
  554. package/src/workspace/migrations/029-seed-pkb.ts +84 -0
  555. package/src/workspace/migrations/registry.ts +10 -0
  556. package/src/workspace/top-level-renderer.ts +5 -9
  557. package/src/__tests__/cli-memory.test.ts +0 -372
  558. package/src/__tests__/clipboard.test.ts +0 -88
  559. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  560. package/src/__tests__/journal-context.test.ts +0 -268
  561. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  562. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  563. package/src/__tests__/memory-query-builder.test.ts +0 -59
  564. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  565. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  566. package/src/__tests__/memory-regressions.test.ts +0 -3696
  567. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  568. package/src/cli/cli-memory.ts +0 -176
  569. package/src/daemon/conversation-memory.ts +0 -207
  570. package/src/memory/conversation-starters-cadence.ts +0 -74
  571. package/src/memory/items-extractor.ts +0 -860
  572. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  573. package/src/memory/job-handlers/extraction.ts +0 -40
  574. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  575. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  576. package/src/memory/journal-memory.ts +0 -224
  577. package/src/memory/query-builder.ts +0 -47
  578. package/src/memory/query-expansion.ts +0 -83
  579. package/src/memory/retriever.test.ts +0 -1592
  580. package/src/memory/retriever.ts +0 -1331
  581. package/src/memory/search/formatting.test.ts +0 -140
  582. package/src/memory/search/formatting.ts +0 -262
  583. package/src/memory/search/mmr.ts +0 -139
  584. package/src/memory/search/ranking.ts +0 -15
  585. package/src/memory/search/staleness.ts +0 -40
  586. package/src/memory/search/tier-classifier.ts +0 -18
  587. package/src/memory/search/types.ts +0 -121
  588. package/src/prompts/journal-context.ts +0 -154
  589. package/src/tools/memory/definitions.ts +0 -69
  590. package/src/tools/memory/handlers.test.ts +0 -562
  591. package/src/tools/memory/handlers.ts +0 -434
  592. package/src/util/clipboard.ts +0 -34
@@ -137,6 +137,37 @@ describe("contact_upsert tool", () => {
137
137
  expect(result.content).toContain("slack: @bob");
138
138
  });
139
139
 
140
+ test("ignores external identity bindings supplied through tool input", async () => {
141
+ const result = await executeContactUpsert(
142
+ {
143
+ display_name: "Eve",
144
+ channels: [
145
+ {
146
+ type: "slack",
147
+ address: "@eve",
148
+ external_user_id: "UATTACKER",
149
+ external_chat_id: "DATTACKER",
150
+ },
151
+ ],
152
+ },
153
+ ctx,
154
+ );
155
+
156
+ expect(result.isError).toBe(false);
157
+
158
+ const row = getRawDb()
159
+ .query(
160
+ "SELECT external_user_id, external_chat_id FROM contact_channels WHERE type = 'slack' AND address = '@eve'",
161
+ )
162
+ .get() as {
163
+ external_user_id: string | null;
164
+ external_chat_id: string | null;
165
+ };
166
+
167
+ expect(row.external_user_id).toBeNull();
168
+ expect(row.external_chat_id).toBeNull();
169
+ });
170
+
140
171
  test("updates an existing contact by ID", async () => {
141
172
  const createResult = await executeContactUpsert(
142
173
  { display_name: "Charlie" },
@@ -467,6 +467,92 @@ describe("context-overflow-reducer", () => {
467
467
  });
468
468
  });
469
469
 
470
+ describe("budget-aware media stubbing", () => {
471
+ test("media stubbing tier retains images within budget", async () => {
472
+ // Create messages with multiple image-only user messages (5 images in the
473
+ // latest user message). With budget-aware retention, the reducer should
474
+ // keep more than the old hardcoded limit of 3 when targetTokens is high.
475
+ const makeImageBlock = () => ({
476
+ type: "image" as const,
477
+ source: {
478
+ type: "base64" as const,
479
+ media_type: "image/png" as const,
480
+ // Small base64 payload so each image doesn't cost many tokens
481
+ data: "A".repeat(1_000),
482
+ },
483
+ });
484
+
485
+ const messages: Message[] = [
486
+ msg("user", "Here are some old images"),
487
+ {
488
+ role: "user",
489
+ content: [makeImageBlock(), makeImageBlock()],
490
+ },
491
+ msg("assistant", "I see the old images."),
492
+ msg("user", "And some more old images"),
493
+ {
494
+ role: "user",
495
+ content: [makeImageBlock()],
496
+ },
497
+ msg("assistant", "Got those too."),
498
+ // Latest user message with 5 images — should retain more than 3
499
+ {
500
+ role: "user",
501
+ content: [
502
+ makeImageBlock(),
503
+ makeImageBlock(),
504
+ makeImageBlock(),
505
+ makeImageBlock(),
506
+ makeImageBlock(),
507
+ ],
508
+ },
509
+ ];
510
+
511
+ // Set targetTokens very high so all images in the latest message fit
512
+ const config = makeConfig({
513
+ targetTokens: 500_000,
514
+ });
515
+ const compactFn = makeNoOpCompactFn();
516
+
517
+ // Run through forced_compaction and tool_result_truncation first
518
+ const step1 = await reduceContextOverflow(
519
+ messages,
520
+ config,
521
+ undefined,
522
+ compactFn,
523
+ );
524
+ expect(step1.tier).toBe("forced_compaction");
525
+
526
+ const step2 = await reduceContextOverflow(
527
+ step1.messages,
528
+ config,
529
+ step1.state,
530
+ compactFn,
531
+ );
532
+ expect(step2.tier).toBe("tool_result_truncation");
533
+
534
+ // Now apply media stubbing
535
+ const step3 = await reduceContextOverflow(
536
+ step2.messages,
537
+ config,
538
+ step2.state,
539
+ compactFn,
540
+ );
541
+ expect(step3.tier).toBe("media_stubbing");
542
+
543
+ // Count remaining image blocks in the latest user message
544
+ const latestUserMsg = step3.messages[step3.messages.length - 1];
545
+ expect(latestUserMsg.role).toBe("user");
546
+ const remainingImages = latestUserMsg.content.filter(
547
+ (b) => b.type === "image",
548
+ );
549
+
550
+ // With budget-aware retention and a high target, all 5 images should be
551
+ // retained — more than the old hardcoded limit of 3.
552
+ expect(remainingImages.length).toBeGreaterThan(3);
553
+ });
554
+ });
555
+
470
556
  describe("createInitialReducerState", () => {
471
557
  test("returns a clean state with no applied tiers", () => {
472
558
  const state = createInitialReducerState();
@@ -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
  });
@@ -206,11 +206,13 @@ mock.module("../daemon/conversation-memory.js", () => ({
206
206
  let mockApplyRuntimeInjections: (msgs: Message[]) => Message[] = (msgs) => msgs;
207
207
  mock.module("../daemon/conversation-runtime-assembly.js", () => ({
208
208
  applyRuntimeInjections: (msgs: Message[]) => mockApplyRuntimeInjections(msgs),
209
- stripInjectedContext: (msgs: Message[]) => msgs,
209
+ stripInjectionsForCompaction: (msgs: Message[]) => msgs,
210
+ findLastInjectedNowContent: () => null,
211
+ readNowScratchpad: () => null,
210
212
  }));
211
213
 
212
214
  mock.module("../daemon/date-context.js", () => ({
213
- buildTemporalContext: () => null,
215
+ formatTurnTimestamp: () => "2026-01-01 (Thu) 00:00:00 +00:00 (UTC)",
214
216
  }));
215
217
 
216
218
  mock.module("../daemon/history-repair.js", () => ({
@@ -226,10 +228,6 @@ mock.module("../daemon/history-repair.js", () => ({
226
228
  deepRepairHistory: (msgs: Message[]) => ({ messages: msgs, stats: {} }),
227
229
  }));
228
230
 
229
- mock.module("../daemon/conversation-history.js", () => ({
230
- consolidateAssistantMessages: () => {},
231
- }));
232
-
233
231
  const recordUsageMock = mock(() => {});
234
232
  mock.module("../daemon/conversation-usage.js", () => ({
235
233
  recordUsage: recordUsageMock,
@@ -457,6 +455,15 @@ function makeCtx(
457
455
  assistantMessageChannel: "vellum" as const,
458
456
  }),
459
457
 
458
+ graphMemory: {
459
+ onCompacted: () => {},
460
+ prepareMemory: async () => ({ runMessages: [], injectedTokens: 0, latencyMs: 0, mode: "none" as const }),
461
+ reinjectCachedMemory: (messages: Message[]) => ({
462
+ runMessages: messages,
463
+ injectedTokens: 0,
464
+ }),
465
+ } as unknown as AgentLoopConversationContext["graphMemory"],
466
+
460
467
  ...overrides,
461
468
  } as AgentLoopConversationContext;
462
469
  }
@@ -195,11 +195,13 @@ mock.module("../daemon/conversation-memory.js", () => ({
195
195
 
196
196
  mock.module("../daemon/conversation-runtime-assembly.js", () => ({
197
197
  applyRuntimeInjections: (msgs: Message[]) => msgs,
198
- stripInjectedContext: (msgs: Message[]) => msgs,
198
+ stripInjectionsForCompaction: (msgs: Message[]) => msgs,
199
+ findLastInjectedNowContent: () => null,
200
+ readNowScratchpad: () => null,
199
201
  }));
200
202
 
201
203
  mock.module("../daemon/date-context.js", () => ({
202
- buildTemporalContext: () => null,
204
+ formatTurnTimestamp: () => "2026-01-01 (Thu) 00:00:00 +00:00 (UTC)",
203
205
  }));
204
206
 
205
207
  mock.module("../daemon/history-repair.js", () => ({
@@ -215,11 +217,6 @@ mock.module("../daemon/history-repair.js", () => ({
215
217
  deepRepairHistory: (msgs: Message[]) => ({ messages: msgs, stats: {} }),
216
218
  }));
217
219
 
218
- const consolidateAssistantMessagesMock = mock(() => false);
219
- mock.module("../daemon/conversation-history.js", () => ({
220
- consolidateAssistantMessages: consolidateAssistantMessagesMock,
221
- }));
222
-
223
220
  const recordUsageMock = mock(() => {});
224
221
  const recordRequestLogMock = mock(() => {});
225
222
  mock.module("../daemon/conversation-usage.js", () => ({
@@ -445,6 +442,15 @@ function makeCtx(
445
442
  assistantMessageChannel: "vellum" as const,
446
443
  }),
447
444
 
445
+ graphMemory: {
446
+ onCompacted: () => {},
447
+ prepareMemory: async () => ({ runMessages: [], injectedTokens: 0, latencyMs: 0, mode: "none" as const }),
448
+ reinjectCachedMemory: (messages: Message[]) => ({
449
+ runMessages: messages,
450
+ injectedTokens: 0,
451
+ }),
452
+ } as unknown as AgentLoopConversationContext["graphMemory"],
453
+
448
454
  ...overrides,
449
455
  } as AgentLoopConversationContext;
450
456
  }
@@ -462,8 +468,6 @@ beforeEach(() => {
462
468
  recordRequestLogMock.mockClear();
463
469
  syncMessageToDiskMock.mockClear();
464
470
  rebuildConversationDiskViewFromDbStateMock.mockClear();
465
- consolidateAssistantMessagesMock.mockReset();
466
- consolidateAssistantMessagesMock.mockImplementation(() => false);
467
471
  });
468
472
 
469
473
  describe("session-agent-loop", () => {
@@ -1935,48 +1939,6 @@ describe("session-agent-loop", () => {
1935
1939
  expect(drainReason).toBe("loop_complete");
1936
1940
  });
1937
1941
 
1938
- test("rebuilds disk view after consolidation mutates persisted history", async () => {
1939
- consolidateAssistantMessagesMock.mockReturnValue(true);
1940
-
1941
- const ctx = makeCtx({
1942
- agentLoopRun: async (
1943
- messages: Message[],
1944
- onEvent: (event: AgentEvent) => void,
1945
- ) => {
1946
- onEvent({
1947
- type: "message_complete",
1948
- message: {
1949
- role: "assistant",
1950
- content: [{ type: "text", text: "done" }],
1951
- },
1952
- });
1953
- onEvent({
1954
- type: "usage",
1955
- inputTokens: 10,
1956
- outputTokens: 5,
1957
- model: "test",
1958
- providerDurationMs: 50,
1959
- });
1960
- return [
1961
- ...messages,
1962
- {
1963
- role: "assistant" as const,
1964
- content: [{ type: "text", text: "done" }] as ContentBlock[],
1965
- },
1966
- ];
1967
- },
1968
- });
1969
-
1970
- await runAgentLoopImpl(ctx, "hi", "msg-consolidate", () => {});
1971
-
1972
- expect(consolidateAssistantMessagesMock).toHaveBeenCalledWith(
1973
- "test-conv",
1974
- "msg-consolidate",
1975
- );
1976
- expect(rebuildConversationDiskViewFromDbStateMock).toHaveBeenCalledWith(
1977
- "test-conv",
1978
- );
1979
- });
1980
1942
  });
1981
1943
 
1982
1944
  describe("stale pending surface cleanup", () => {
@@ -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");