@vellumai/assistant 0.8.3 → 0.8.5

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 (665) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docker-entrypoint.sh +0 -1
  3. package/docs/browser-use-architecture-phase2.md +1 -1
  4. package/knip.json +2 -1
  5. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  6. package/openapi.yaml +1492 -100
  7. package/package.json +1 -1
  8. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  9. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +302 -33
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  14. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  15. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  16. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
  17. package/src/__tests__/btw-routes.test.ts +2 -3
  18. package/src/__tests__/call-controller.test.ts +0 -1
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  20. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  21. package/src/__tests__/channel-guardian.test.ts +3 -3
  22. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  23. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  24. package/src/__tests__/checker.test.ts +6 -15
  25. package/src/__tests__/compaction-events.test.ts +2 -1
  26. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  27. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  29. package/src/__tests__/computer-use-tools.test.ts +2 -4
  30. package/src/__tests__/config-watcher.test.ts +1 -1
  31. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  32. package/src/__tests__/context-token-estimator.test.ts +91 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
  37. package/src/__tests__/conversation-agent-loop.test.ts +188 -129
  38. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  39. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  40. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  41. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  42. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  43. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  44. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  45. package/src/__tests__/conversation-error.test.ts +31 -0
  46. package/src/__tests__/conversation-fork-crud.test.ts +324 -0
  47. package/src/__tests__/conversation-lifecycle.test.ts +53 -12
  48. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  49. package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
  50. package/src/__tests__/conversation-pairing.test.ts +2 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
  53. package/src/__tests__/conversation-queue.test.ts +1 -1
  54. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  57. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  58. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  59. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  60. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  61. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  62. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  63. package/src/__tests__/conversation-store.test.ts +1 -1
  64. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  65. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
  67. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  69. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  70. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  71. package/src/__tests__/credential-security-invariants.test.ts +7 -0
  72. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  73. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  74. package/src/__tests__/dm-backfill.test.ts +64 -0
  75. package/src/__tests__/dm-persistence.test.ts +33 -0
  76. package/src/__tests__/document-find-replace.test.ts +501 -0
  77. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  78. package/src/__tests__/email-html-renderer.test.ts +12 -0
  79. package/src/__tests__/first-greeting.test.ts +23 -2
  80. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  81. package/src/__tests__/gemini-provider.test.ts +78 -0
  82. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  83. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  84. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  85. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  87. package/src/__tests__/heartbeat-service.test.ts +4 -0
  88. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  89. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  90. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  91. package/src/__tests__/host-file-proxy.test.ts +8 -1
  92. package/src/__tests__/host-shell-tool.test.ts +1 -1
  93. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  94. package/src/__tests__/identity-routes.test.ts +57 -0
  95. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  96. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  97. package/src/__tests__/injector-chain.test.ts +2 -0
  98. package/src/__tests__/injector-document-comments.test.ts +378 -0
  99. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  100. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  102. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  103. package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
  104. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  105. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  106. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  107. package/src/__tests__/llm-resolver.test.ts +161 -9
  108. package/src/__tests__/llm-usage-store.test.ts +66 -0
  109. package/src/__tests__/log-export-routes.test.ts +99 -2
  110. package/src/__tests__/logger.test.ts +89 -0
  111. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  112. package/src/__tests__/media-generate-image.test.ts +31 -0
  113. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  114. package/src/__tests__/message-queue-steer.test.ts +114 -0
  115. package/src/__tests__/model-intents.test.ts +2 -4
  116. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  117. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  118. package/src/__tests__/openai-provider.test.ts +151 -0
  119. package/src/__tests__/openai-responses-provider.test.ts +118 -16
  120. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  121. package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
  122. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  123. package/src/__tests__/platform.test.ts +2 -5
  124. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  125. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  126. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  127. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  128. package/src/__tests__/plugin-types.test.ts +3 -2
  129. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  130. package/src/__tests__/pricing.test.ts +12 -0
  131. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  132. package/src/__tests__/process-message-display-content.test.ts +21 -16
  133. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  134. package/src/__tests__/registry.test.ts +2 -8
  135. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  136. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  137. package/src/__tests__/server-history-render.test.ts +83 -4
  138. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  139. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  140. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  141. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  142. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  143. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  144. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  145. package/src/__tests__/suggestion-routes.test.ts +1 -0
  146. package/src/__tests__/sync-message-contract.test.ts +59 -0
  147. package/src/__tests__/system-prompt.test.ts +161 -124
  148. package/src/__tests__/terminal-tools.test.ts +12 -2
  149. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  150. package/src/__tests__/thread-backfill.test.ts +370 -22
  151. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  152. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  153. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  155. package/src/__tests__/tool-executor.test.ts +89 -53
  156. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  157. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  158. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  159. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  160. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  161. package/src/__tests__/twilio-routes.test.ts +1 -1
  162. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  163. package/src/__tests__/usage-routes.test.ts +3 -0
  164. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  165. package/src/__tests__/web-fetch.test.ts +2 -2
  166. package/src/__tests__/workspace-git-service.test.ts +94 -10
  167. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  168. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  169. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  170. package/src/acp/prepare-agent-env.ts +78 -0
  171. package/src/acp/session-manager.ts +1 -1
  172. package/src/agent/attachments.ts +1 -0
  173. package/src/agent/loop.ts +65 -20
  174. package/src/api/README.md +5 -0
  175. package/src/api/index.ts +4 -0
  176. package/src/api/package.json +10 -0
  177. package/src/background-wake/background-wake-routes.test.ts +233 -0
  178. package/src/background-wake/next-wake.test.ts +289 -0
  179. package/src/background-wake/next-wake.ts +172 -0
  180. package/src/background-wake/runtime-registry.ts +24 -0
  181. package/src/browser/operations.ts +15 -0
  182. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  183. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  184. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  185. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  186. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  187. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  188. package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
  189. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  190. package/src/cli/commands/browser.ts +247 -0
  191. package/src/cli/commands/conversations.ts +128 -1
  192. package/src/cli/commands/domain.ts +91 -41
  193. package/src/cli/commands/inference-providers.ts +147 -1
  194. package/src/cli/commands/inference.ts +93 -40
  195. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  196. package/src/cli/commands/memory-v2.ts +483 -0
  197. package/src/cli/commands/memory-v3-render.ts +344 -0
  198. package/src/cli/commands/memory-v3.ts +316 -0
  199. package/src/cli/commands/notifications.ts +24 -2
  200. package/src/cli/program.ts +2 -0
  201. package/src/cli/utils/conversation-id.ts +17 -5
  202. package/src/config/assistant-feature-flags.ts +21 -9
  203. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  204. package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
  205. package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
  206. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  207. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  208. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  209. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  210. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  211. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  212. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  213. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  214. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  215. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  216. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  217. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  218. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  219. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  220. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  221. package/src/config/bundled-tool-registry.ts +24 -12
  222. package/src/config/call-site-defaults.ts +20 -0
  223. package/src/config/feature-flag-registry.json +115 -3
  224. package/src/config/llm-resolver.ts +16 -2
  225. package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
  226. package/src/config/schemas/call-site-catalog.ts +35 -0
  227. package/src/config/schemas/llm.ts +14 -0
  228. package/src/config/schemas/memory-v2.ts +294 -1
  229. package/src/config/schemas/memory.ts +2 -1
  230. package/src/context/compactor.ts +60 -1
  231. package/src/context/token-estimator.ts +47 -4
  232. package/src/context/window-manager.ts +25 -0
  233. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  234. package/src/conversations/message-consolidation.ts +404 -0
  235. package/src/credential-health/credential-health-service.ts +34 -19
  236. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  237. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  238. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  239. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  240. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  241. package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
  242. package/src/daemon/conversation-agent-loop.ts +307 -88
  243. package/src/daemon/conversation-error.ts +31 -1
  244. package/src/daemon/conversation-lifecycle.ts +149 -118
  245. package/src/daemon/conversation-messaging.ts +3 -0
  246. package/src/daemon/conversation-process.ts +273 -0
  247. package/src/daemon/conversation-queue-manager.ts +14 -0
  248. package/src/daemon/conversation-runtime-assembly.ts +145 -84
  249. package/src/daemon/conversation-slash.ts +37 -5
  250. package/src/daemon/conversation-surfaces.ts +45 -2
  251. package/src/daemon/conversation-tool-setup.ts +70 -3
  252. package/src/daemon/conversation-usage.ts +2 -0
  253. package/src/daemon/conversation.ts +54 -32
  254. package/src/daemon/disk-pressure-guard.ts +14 -2
  255. package/src/daemon/first-greeting.ts +10 -0
  256. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  257. package/src/daemon/handlers/config-a2a.ts +160 -0
  258. package/src/daemon/handlers/config-model.test.ts +2 -0
  259. package/src/daemon/handlers/conversations.ts +90 -3
  260. package/src/daemon/handlers/shared.ts +92 -29
  261. package/src/daemon/host-bash-proxy.ts +1 -1
  262. package/src/daemon/host-browser-proxy.ts +5 -5
  263. package/src/daemon/host-cu-proxy.ts +5 -5
  264. package/src/daemon/host-file-proxy.ts +5 -5
  265. package/src/daemon/host-proxy-base.ts +4 -4
  266. package/src/daemon/host-transfer-proxy.ts +11 -11
  267. package/src/daemon/lifecycle.ts +40 -23
  268. package/src/daemon/meet-manifest-loader.ts +1 -7
  269. package/src/daemon/message-protocol.ts +4 -0
  270. package/src/daemon/message-types/conversations.ts +14 -9
  271. package/src/daemon/message-types/document-comments.ts +50 -0
  272. package/src/daemon/message-types/home.ts +1 -13
  273. package/src/daemon/message-types/messages.ts +66 -7
  274. package/src/daemon/message-types/surfaces.ts +3 -1
  275. package/src/daemon/message-types/sync.ts +14 -0
  276. package/src/daemon/message-types/web-activity.ts +57 -0
  277. package/src/daemon/plugin-source-watcher.ts +135 -3
  278. package/src/daemon/process-message.ts +69 -12
  279. package/src/daemon/shutdown-handlers.ts +24 -5
  280. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  281. package/src/daemon/tool-setup-types.ts +13 -0
  282. package/src/daemon/trust-context.ts +6 -0
  283. package/src/documents/document-comments-store.test.ts +338 -0
  284. package/src/documents/document-comments-store.ts +237 -0
  285. package/src/documents/document-store.ts +202 -0
  286. package/src/events/relationship-state-updated.ts +25 -0
  287. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
  288. package/src/heartbeat/heartbeat-service.ts +1 -0
  289. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  290. package/src/home/feed-types.ts +6 -1
  291. package/src/home/home-content-refresh.ts +52 -0
  292. package/src/home/home-greeting-cache.ts +69 -0
  293. package/src/home/home-greeting.ts +85 -0
  294. package/src/home/suggested-prompts.ts +168 -9
  295. package/src/ipc/gateway-flag-listener.ts +123 -0
  296. package/src/ipc/skill-routes/registries.ts +8 -12
  297. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  298. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  299. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  300. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  301. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  302. package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
  303. package/src/memory/auto-analysis-enqueue.ts +5 -1
  304. package/src/memory/conversation-crud.ts +191 -100
  305. package/src/memory/conversation-starters-cadence.ts +3 -1
  306. package/src/memory/conversation-title-service.ts +19 -3
  307. package/src/memory/db-async-query.ts +214 -0
  308. package/src/memory/db-init.ts +26 -0
  309. package/src/memory/db-maintenance.ts +30 -21
  310. package/src/memory/delivery-crud.ts +41 -0
  311. package/src/memory/delivery-status.ts +141 -15
  312. package/src/memory/external-conversation-store.ts +32 -1
  313. package/src/memory/graph/bootstrap.ts +8 -1
  314. package/src/memory/graph/capability-seed.ts +7 -3
  315. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  316. package/src/memory/graph/extraction.ts +1 -5
  317. package/src/memory/graph/graph-search.ts +7 -1
  318. package/src/memory/indexer.ts +28 -18
  319. package/src/memory/job-handlers/cleanup.ts +76 -18
  320. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  321. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  322. package/src/memory/jobs-store.ts +14 -0
  323. package/src/memory/jobs-worker.ts +68 -15
  324. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  325. package/src/memory/llm-request-log-source-local.ts +7 -0
  326. package/src/memory/llm-request-log-source.ts +9 -2
  327. package/src/memory/llm-request-log-store.ts +43 -1
  328. package/src/memory/llm-usage-store.ts +24 -0
  329. package/src/memory/memory-retrospective-constants.ts +28 -0
  330. package/src/memory/memory-retrospective-enqueue.ts +11 -3
  331. package/src/memory/memory-retrospective-job.ts +413 -18
  332. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  333. package/src/memory/memory-v2-activation-log-store.ts +41 -14
  334. package/src/memory/migrations/100-core-tables.ts +1 -0
  335. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  336. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  337. package/src/memory/migrations/253-document-comments.ts +47 -0
  338. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  339. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  340. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  341. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  342. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  343. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  344. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  345. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  346. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  347. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  348. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  349. package/src/memory/migrations/index.ts +34 -0
  350. package/src/memory/migrations/registry.ts +58 -0
  351. package/src/memory/onboarding-events-store.ts +7 -0
  352. package/src/memory/schema/calls.ts +1 -0
  353. package/src/memory/schema/conversations.ts +3 -0
  354. package/src/memory/schema/infrastructure.ts +22 -0
  355. package/src/memory/tool-usage-store.ts +36 -8
  356. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  357. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  358. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  359. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  362. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  363. package/src/memory/v2/__tests__/injection.test.ts +158 -112
  364. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  365. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  366. package/src/memory/v2/__tests__/router.test.ts +660 -4
  367. package/src/memory/v2/consolidation-job.ts +14 -0
  368. package/src/memory/v2/harness/compare.ts +57 -0
  369. package/src/memory/v2/harness/metrics.ts +124 -0
  370. package/src/memory/v2/harness/oracle.ts +145 -0
  371. package/src/memory/v2/harness/replay-input.ts +224 -0
  372. package/src/memory/v2/harness/retriever.ts +74 -0
  373. package/src/memory/v2/harness/router-retriever.ts +43 -0
  374. package/src/memory/v2/harness/runner.ts +106 -0
  375. package/src/memory/v2/harness/trace.ts +58 -0
  376. package/src/memory/v2/injection-events.ts +101 -0
  377. package/src/memory/v2/injection.ts +42 -25
  378. package/src/memory/v2/page-index.ts +209 -7
  379. package/src/memory/v2/page-store.ts +18 -0
  380. package/src/memory/v2/prompts/router.ts +26 -1
  381. package/src/memory/v2/qdrant.ts +14 -2
  382. package/src/memory/v2/router.ts +369 -62
  383. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  384. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  385. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  386. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  387. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  388. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  389. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  390. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  391. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  392. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  393. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  394. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  395. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  396. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  397. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  398. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  399. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  400. package/src/memory/v3/auto-edges.ts +223 -0
  401. package/src/memory/v3/coactivation-store.ts +124 -0
  402. package/src/memory/v3/consolidation-job.ts +323 -0
  403. package/src/memory/v3/edge-learning-job.ts +160 -0
  404. package/src/memory/v3/edges.ts +249 -0
  405. package/src/memory/v3/filter.ts +281 -0
  406. package/src/memory/v3/gate.ts +334 -0
  407. package/src/memory/v3/index-composition.ts +113 -0
  408. package/src/memory/v3/llm-capture.ts +46 -0
  409. package/src/memory/v3/loop.ts +382 -0
  410. package/src/memory/v3/maintenance.ts +144 -0
  411. package/src/memory/v3/prompt-context.ts +33 -0
  412. package/src/memory/v3/prompts/consolidation.ts +458 -0
  413. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  414. package/src/memory/v3/retriever.ts +33 -0
  415. package/src/memory/v3/scouts.ts +420 -0
  416. package/src/memory/v3/shadow-middleware.ts +305 -0
  417. package/src/memory/v3/traversal.ts +206 -0
  418. package/src/memory/v3/tree-index.ts +237 -0
  419. package/src/memory/v3/tree-store.ts +394 -0
  420. package/src/memory/v3/tree-walk.ts +351 -0
  421. package/src/memory/v3/types.ts +65 -0
  422. package/src/memory/v3/validate.ts +300 -0
  423. package/src/messaging/providers/index.ts +7 -1
  424. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  425. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  426. package/src/messaging/providers/slack/adapter.ts +178 -25
  427. package/src/messaging/providers/slack/api.test.ts +54 -0
  428. package/src/messaging/providers/slack/api.ts +119 -3
  429. package/src/messaging/providers/slack/client.ts +12 -0
  430. package/src/messaging/providers/slack/deep-link.ts +20 -1
  431. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  432. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  433. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  434. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  435. package/src/messaging/providers/slack/send.test.ts +77 -0
  436. package/src/messaging/providers/slack/send.ts +8 -2
  437. package/src/messaging/providers/slack/types.ts +14 -0
  438. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  439. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  440. package/src/notifications/adapters/macos.ts +18 -1
  441. package/src/notifications/adapters/platform.ts +1 -1
  442. package/src/notifications/conversation-seed-composer.ts +14 -2
  443. package/src/notifications/decision-engine.ts +1 -4
  444. package/src/notifications/deferred-emit.ts +135 -0
  445. package/src/notifications/emit-signal.ts +38 -50
  446. package/src/notifications/home-feed-side-effect.ts +60 -30
  447. package/src/oauth/connect-orchestrator.ts +3 -0
  448. package/src/oauth/credential-token-resolver.ts +2 -0
  449. package/src/oauth/manual-token-connection.ts +19 -0
  450. package/src/oauth/oauth-store.ts +12 -0
  451. package/src/oauth/seed-providers.ts +22 -0
  452. package/src/permissions/prompter.ts +8 -5
  453. package/src/permissions/question-prompter.ts +5 -2
  454. package/src/permissions/secret-prompter.ts +6 -3
  455. package/src/plugin-api/index.ts +4 -0
  456. package/src/plugin-api/types.ts +7 -33
  457. package/src/plugins/defaults/index.ts +6 -0
  458. package/src/plugins/defaults/injectors.ts +100 -20
  459. package/src/plugins/external-plugin-loader.ts +5 -68
  460. package/src/plugins/types.ts +11 -16
  461. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  462. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  463. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  464. package/src/prompts/normalize-onboarding.ts +40 -0
  465. package/src/prompts/persona-resolver.ts +36 -21
  466. package/src/prompts/sections.ts +69 -19
  467. package/src/prompts/system-prompt.ts +118 -216
  468. package/src/prompts/template-detection.ts +37 -0
  469. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  470. package/src/prompts/templates/BOOTSTRAP.md +10 -2
  471. package/src/prompts/templates/VOICE.md +3 -0
  472. package/src/prompts/templates/system-sections.ts +281 -9
  473. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  474. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  475. package/src/providers/anthropic/client.ts +159 -66
  476. package/src/providers/call-site-routing.ts +14 -2
  477. package/src/providers/connection-model-compat.ts +38 -0
  478. package/src/providers/connection-resolution.ts +16 -2
  479. package/src/providers/fireworks/client.ts +20 -2
  480. package/src/providers/gemini/client.ts +49 -6
  481. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  482. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  483. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  484. package/src/providers/inference/adapter-factory.ts +18 -1
  485. package/src/providers/inference/auth.ts +3 -3
  486. package/src/providers/inference/codex-token-refresh.ts +128 -0
  487. package/src/providers/inference/resolve-auth.ts +49 -6
  488. package/src/providers/minimax/client.ts +106 -0
  489. package/src/providers/model-catalog.ts +91 -1
  490. package/src/providers/model-intents.ts +1 -1
  491. package/src/providers/openai/chat-completions-provider.ts +63 -23
  492. package/src/providers/openai/codex-models.ts +18 -0
  493. package/src/providers/openai/responses-provider.ts +86 -23
  494. package/src/providers/openrouter/client.ts +5 -1
  495. package/src/providers/provider-send-message.ts +7 -1
  496. package/src/providers/retry.ts +34 -3
  497. package/src/providers/thinking-config.ts +26 -1
  498. package/src/providers/types.ts +25 -0
  499. package/src/providers/usage-tracking.ts +2 -0
  500. package/src/runtime/AGENTS.md +2 -2
  501. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  502. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  503. package/src/runtime/agent-wake.ts +152 -56
  504. package/src/runtime/assistant-event-hub.ts +76 -6
  505. package/src/runtime/auth/route-policy.ts +43 -3
  506. package/src/runtime/background-job-runner.ts +26 -0
  507. package/src/runtime/btw-sidechain.ts +0 -6
  508. package/src/runtime/channel-reply-delivery.ts +182 -47
  509. package/src/runtime/channel-retry-sweep.ts +141 -16
  510. package/src/runtime/http-types.ts +7 -6
  511. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  512. package/src/runtime/pending-interactions.ts +50 -8
  513. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  514. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
  515. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  516. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
  517. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  518. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  519. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  520. package/src/runtime/routes/acp-routes.test.ts +255 -6
  521. package/src/runtime/routes/acp-routes.ts +8 -1
  522. package/src/runtime/routes/approval-routes.ts +4 -1
  523. package/src/runtime/routes/avatar-routes.ts +10 -10
  524. package/src/runtime/routes/background-wake-routes.ts +188 -0
  525. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  526. package/src/runtime/routes/btw-routes.ts +0 -6
  527. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  528. package/src/runtime/routes/content-source-routes.ts +78 -0
  529. package/src/runtime/routes/conversation-cli-routes.ts +147 -2
  530. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  531. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  532. package/src/runtime/routes/conversation-query-routes.ts +196 -31
  533. package/src/runtime/routes/conversation-routes.ts +472 -425
  534. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  535. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  536. package/src/runtime/routes/document-comments-routes.ts +287 -0
  537. package/src/runtime/routes/documents-routes.ts +33 -0
  538. package/src/runtime/routes/domain-routes.ts +60 -10
  539. package/src/runtime/routes/email-routes.ts +5 -2
  540. package/src/runtime/routes/events-routes.ts +54 -10
  541. package/src/runtime/routes/group-routes.ts +24 -8
  542. package/src/runtime/routes/home-feed-routes.ts +6 -3
  543. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  544. package/src/runtime/routes/host-browser-routes.ts +17 -2
  545. package/src/runtime/routes/host-cu-routes.ts +2 -2
  546. package/src/runtime/routes/identity-routes.ts +21 -0
  547. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  548. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  549. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  550. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  551. package/src/runtime/routes/index.ts +20 -4
  552. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  553. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  554. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  555. package/src/runtime/routes/integrations/a2a.ts +60 -1
  556. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  557. package/src/runtime/routes/log-export-routes.ts +39 -0
  558. package/src/runtime/routes/memory-item-routes.ts +8 -3
  559. package/src/runtime/routes/memory-v2-routes.ts +427 -0
  560. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  561. package/src/runtime/routes/migration-routes.ts +21 -24
  562. package/src/runtime/routes/notification-routes.ts +19 -2
  563. package/src/runtime/routes/plugins-routes.ts +337 -0
  564. package/src/runtime/routes/question-routes.ts +4 -1
  565. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  566. package/src/runtime/routes/sanity-routes.ts +159 -0
  567. package/src/runtime/routes/secret-routes.ts +25 -5
  568. package/src/runtime/routes/settings-routes.ts +12 -11
  569. package/src/runtime/routes/slack-channel-routes.ts +188 -0
  570. package/src/runtime/routes/workspace-routes.ts +25 -10
  571. package/src/runtime/services/conversation-serializer.ts +30 -4
  572. package/src/runtime/sync/resource-sync-events.ts +106 -38
  573. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  574. package/src/runtime/sync/sync-publisher.ts +2 -1
  575. package/src/runtime/verification-outbound-actions.ts +73 -1
  576. package/src/schedule/integration-status.ts +3 -1
  577. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  578. package/src/security/oauth2-device-code.ts +307 -0
  579. package/src/security/oauth2.ts +26 -9
  580. package/src/security/secure-keys.ts +5 -0
  581. package/src/skills/catalog-install.ts +6 -2
  582. package/src/telemetry/types.ts +12 -0
  583. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  584. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  585. package/src/tools/acp/spawn.test.ts +119 -0
  586. package/src/tools/acp/spawn.ts +15 -2
  587. package/src/tools/apps/definitions.ts +2 -8
  588. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  589. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  590. package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
  591. package/src/tools/browser/browser-execution.ts +106 -0
  592. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  593. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  594. package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
  595. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
  596. package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
  597. package/src/tools/browser/cdp-client/factory.ts +171 -4
  598. package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
  599. package/src/tools/browser/cdp-client/types.ts +101 -0
  600. package/src/tools/browser/pinned-tabs.ts +146 -0
  601. package/src/tools/computer-use/definitions.ts +22 -78
  602. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  603. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  604. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  605. package/src/tools/credentials/vault.ts +3 -9
  606. package/src/tools/document/document-comment-tool.test.ts +379 -0
  607. package/src/tools/document/document-comment-tool.ts +156 -0
  608. package/src/tools/document/document-tool.ts +187 -2
  609. package/src/tools/execution-target.ts +21 -23
  610. package/src/tools/executor.ts +6 -1
  611. package/src/tools/filesystem/edit.ts +3 -9
  612. package/src/tools/filesystem/list.ts +3 -9
  613. package/src/tools/filesystem/read.ts +3 -9
  614. package/src/tools/filesystem/write.ts +3 -9
  615. package/src/tools/host-filesystem/edit.ts +3 -9
  616. package/src/tools/host-filesystem/read.ts +3 -9
  617. package/src/tools/host-filesystem/transfer.ts +3 -9
  618. package/src/tools/host-filesystem/write.ts +3 -9
  619. package/src/tools/host-terminal/host-shell.ts +3 -9
  620. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  621. package/src/tools/memory/register.test.ts +1 -1
  622. package/src/tools/memory/register.ts +4 -9
  623. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  624. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  625. package/src/tools/network/domain-normalize.ts +17 -0
  626. package/src/tools/network/web-fetch.ts +216 -73
  627. package/src/tools/network/web-search.ts +216 -98
  628. package/src/tools/registry.ts +7 -23
  629. package/src/tools/schema-transforms.ts +1 -1
  630. package/src/tools/skills/execute.ts +3 -9
  631. package/src/tools/skills/load.ts +3 -9
  632. package/src/tools/skills/skill-tool-factory.ts +1 -8
  633. package/src/tools/subagent/notify-parent.ts +3 -9
  634. package/src/tools/system/request-permission.ts +3 -9
  635. package/src/tools/terminal/safe-env.ts +3 -2
  636. package/src/tools/terminal/shell.ts +3 -9
  637. package/src/tools/tool-approval-handler.ts +19 -12
  638. package/src/tools/tool-defaults.ts +94 -0
  639. package/src/tools/types.ts +31 -98
  640. package/src/tools/ui-surface/definitions.ts +9 -23
  641. package/src/types/onboarding-context.ts +4 -0
  642. package/src/usage/pricing.ts +23 -0
  643. package/src/usage/types.ts +12 -0
  644. package/src/util/__tests__/favicon.test.ts +84 -0
  645. package/src/util/favicon.ts +40 -0
  646. package/src/util/logger.ts +16 -7
  647. package/src/util/platform.ts +7 -7
  648. package/src/util/sqlite3-runtime.ts +65 -0
  649. package/src/workspace/git-service.ts +75 -4
  650. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  651. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  652. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  653. package/src/workspace/migrations/registry.ts +4 -0
  654. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  655. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  656. package/src/config/bundled-skills/document/SKILL.md +0 -54
  657. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  658. package/src/daemon/seed-files.ts +0 -18
  659. package/src/prompts/cache-boundary.ts +0 -8
  660. package/src/runtime/routes/interface-routes.ts +0 -43
  661. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  662. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  663. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  664. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  665. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -27,6 +27,7 @@ import {
27
27
  getAttachmentMetadataForMessage,
28
28
  } from "../memory/attachments-store.js";
29
29
  import { getMessages } from "../memory/conversation-crud.js";
30
+ import { recordRequestLog } from "../memory/llm-request-log-store.js";
30
31
  import type {
31
32
  ContentBlock,
32
33
  ImageContent,
@@ -50,6 +51,49 @@ const log = getLogger("compactor");
50
51
  */
51
52
  const COMPACTION_CALL_SITE: LLMCallSite = "mainAgent";
52
53
 
54
+ /**
55
+ * Tag stamped on `llm_request_logs.call_site` for compaction-driven rows.
56
+ *
57
+ * Distinct from `COMPACTION_CALL_SITE` (above) on purpose: that constant
58
+ * names the **provider config resolution** site (set to `mainAgent` so we
59
+ * inherit the agent's profile and keep the prefix cache warm). This
60
+ * constant names the **observability** site — what the row IS — so
61
+ * inspectors can filter "show me only compaction calls". They're
62
+ * semantically different even though both come from the same enum.
63
+ */
64
+ const COMPACTION_LOG_CALL_SITE: LLMCallSite = "compactionAgent";
65
+
66
+ /**
67
+ * Best-effort: persist a successful compaction LLM call into
68
+ * `llm_request_logs` with `call_site = "compactionAgent"`. The compactor
69
+ * opts out of automatic usage tracking (`usageTracking: "manual"`), so
70
+ * its calls otherwise never reach `recordRequestLog` via the agent-loop
71
+ * dispatcher. Failures are swallowed (warn-logged) so a DB hiccup never
72
+ * escalates compaction into a failure.
73
+ */
74
+ function recordCompactionRequestLog(
75
+ conversationId: string,
76
+ response: ProviderResponse,
77
+ provider: Provider,
78
+ ): void {
79
+ if (!response.rawRequest || !response.rawResponse) return;
80
+ try {
81
+ recordRequestLog(
82
+ conversationId,
83
+ JSON.stringify(response.rawRequest),
84
+ JSON.stringify(response.rawResponse),
85
+ undefined,
86
+ response.actualProvider ?? provider.name,
87
+ COMPACTION_LOG_CALL_SITE,
88
+ );
89
+ } catch (err) {
90
+ log.warn(
91
+ { err, conversationId },
92
+ "Failed to persist compaction LLM request log (non-fatal)",
93
+ );
94
+ }
95
+ }
96
+
53
97
  const RESULT_TAG_OPEN = "<compaction_result>";
54
98
  const RESULT_TAG_CLOSE = "</compaction_result>";
55
99
 
@@ -155,6 +199,11 @@ export interface CompactionRunResult {
155
199
  thresholdTokens: number;
156
200
  compactedMessages: number;
157
201
  compactedPersistedMessages: number;
202
+ /**
203
+ * Number of recent ("tail") messages preserved verbatim alongside the
204
+ * summary. Omitted on no-op / skipped results — defaults to 0 at render.
205
+ */
206
+ preservedTailMessages?: number;
158
207
  summaryCalls: number;
159
208
  summaryInputTokens: number;
160
209
  summaryOutputTokens: number;
@@ -327,7 +376,7 @@ export function renderImageManifest(entries: ManifestEntry[]): string {
327
376
  * runtime emitted — typically
328
377
  * `2026-04-02 (Thursday) 01:52:33 -05:00 (America/Chicago)`).
329
378
  */
330
- function extractTurnContextTimestamp(message: Message): string | null {
379
+ export function extractTurnContextTimestamp(message: Message): string | null {
331
380
  if (message.role !== "user") return null;
332
381
  for (const block of message.content) {
333
382
  if (block.type !== "text") continue;
@@ -669,6 +718,10 @@ export async function runAssistantDrivenCompaction(
669
718
  };
670
719
  }
671
720
 
721
+ // Persist the compaction LLM call into `llm_request_logs` with
722
+ // `call_site = "compactionAgent"`. Non-fatal on DB error — see helper.
723
+ recordCompactionRequestLog(args.conversationId, response, args.provider);
724
+
672
725
  const rawText = extractTextFromResponse(response.content);
673
726
  const parsed = parseCompactionResult(rawText);
674
727
  if (!parsed) {
@@ -841,6 +894,7 @@ export async function runAssistantDrivenCompaction(
841
894
  thresholdTokens,
842
895
  compactedMessages: compactableMessages.length,
843
896
  compactedPersistedMessages,
897
+ preservedTailMessages: args.messages.length - tailIndex,
844
898
  summaryCalls: 1,
845
899
  summaryInputTokens: response.usage.inputTokens,
846
900
  summaryOutputTokens: response.usage.outputTokens,
@@ -1037,6 +1091,10 @@ export async function runEmergencyCompaction(
1037
1091
  };
1038
1092
  }
1039
1093
 
1094
+ // Persist the emergency compaction LLM call into `llm_request_logs` with
1095
+ // `call_site = "compactionAgent"`. Non-fatal on DB error — see helper.
1096
+ recordCompactionRequestLog(args.conversationId, response, args.provider);
1097
+
1040
1098
  const rawText = extractTextFromResponse(response.content);
1041
1099
  const parsed = parseCompactionResult(rawText);
1042
1100
  if (!parsed) {
@@ -1090,6 +1148,7 @@ export async function runEmergencyCompaction(
1090
1148
  thresholdTokens,
1091
1149
  compactedMessages: compactedCount,
1092
1150
  compactedPersistedMessages: Math.max(0, compactedCount - nonPersistedAway),
1151
+ preservedTailMessages: keptTail.length,
1093
1152
  summaryCalls: 1,
1094
1153
  summaryInputTokens: response.usage.inputTokens,
1095
1154
  summaryOutputTokens: response.usage.outputTokens,
@@ -52,6 +52,24 @@ const IMAGE_MAX_PIXELS = 1_200_000;
52
52
  const IMAGE_TOKENS_PER_PIXEL = 1 / 750;
53
53
  const IMAGE_MAX_TOKENS = 1_600;
54
54
 
55
+ // Gemini prices images differently: any side ≤384px counts as a single 258-token
56
+ // tile; anything larger is resized so the longest side is ≤3072px and then
57
+ // split into 768x768 tiles at 258 tokens each. A 4000x4000 image clamps to
58
+ // 3072x3072 → ceil(3072/768)^2 = 16 tiles = 4,128 tokens. Without the clamp
59
+ // we'd over-count it as 36 tiles (~9,288 tokens) and trigger spurious
60
+ // compaction. The clamped 16-tile, 4,128-token figure is also the per-image
61
+ // ceiling we fall back to when dimensions are unparseable (e.g. HEIC/HEIF
62
+ // from iOS attachments) — the generic 1,600 cap can under-count Gemini
63
+ // images by ~2.5x.
64
+ // See: https://ai.google.dev/gemini-api/docs/tokens#multimodal-tokens
65
+ const GEMINI_IMAGE_SMALL_THRESHOLD = 384;
66
+ const GEMINI_IMAGE_TILE_SIZE = 768;
67
+ const GEMINI_IMAGE_TOKENS_PER_TILE = 258;
68
+ const GEMINI_IMAGE_MAX_DIMENSION = 3072;
69
+ const GEMINI_IMAGE_MAX_TOKENS =
70
+ Math.ceil(GEMINI_IMAGE_MAX_DIMENSION / GEMINI_IMAGE_TILE_SIZE) ** 2 *
71
+ GEMINI_IMAGE_TOKENS_PER_TILE;
72
+
55
73
  // Anthropic renders each PDF page as an image (~1,568 tokens at standard
56
74
  // resolution) plus any extracted text. Typical PDF pages are 50-150 KB.
57
75
  // Using ~100 KB/page and ~1,600 tokens/page gives ~0.016 tokens/byte.
@@ -129,16 +147,41 @@ function estimateImageTokensByDimensions(
129
147
  return Math.ceil(scaledWidth * scaledHeight * IMAGE_TOKENS_PER_PIXEL);
130
148
  }
131
149
 
150
+ function estimateGeminiImageTokens(width: number, height: number): number {
151
+ if (
152
+ width <= GEMINI_IMAGE_SMALL_THRESHOLD &&
153
+ height <= GEMINI_IMAGE_SMALL_THRESHOLD
154
+ ) {
155
+ return GEMINI_IMAGE_TOKENS_PER_TILE;
156
+ }
157
+ // Gemini resizes images so the longest side is ≤3072px before tiling.
158
+ const clampedWidth = Math.min(width, GEMINI_IMAGE_MAX_DIMENSION);
159
+ const clampedHeight = Math.min(height, GEMINI_IMAGE_MAX_DIMENSION);
160
+ const tilesWide = Math.ceil(clampedWidth / GEMINI_IMAGE_TILE_SIZE);
161
+ const tilesHigh = Math.ceil(clampedHeight / GEMINI_IMAGE_TILE_SIZE);
162
+ return tilesWide * tilesHigh * GEMINI_IMAGE_TOKENS_PER_TILE;
163
+ }
164
+
132
165
  function estimateImageTokens(
133
166
  block: Extract<ContentBlock, { type: "image" }>,
167
+ options?: TokenEstimatorOptions,
134
168
  ): number {
135
169
  const dims = parseImageDimensions(block.source.data, block.source.media_type);
136
170
  if (dims) {
171
+ if (options?.providerName === "gemini") {
172
+ return estimateGeminiImageTokens(dims.width, dims.height);
173
+ }
137
174
  return estimateImageTokensByDimensions(dims.width, dims.height);
138
175
  }
139
- // Dimensions unparseable (corrupt header, exotic format): use the per-image
140
- // cap rather than the raw base64 length, which over-counts by 30-100x for
141
- // non-Anthropic providers and trips spurious compaction.
176
+ // Dimensions unparseable (corrupt header, or formats parseImageDimensions
177
+ // doesn't recognize like HEIC/HEIF coming from iOS attachments). Fall back
178
+ // to the per-provider per-image ceiling rather than the raw base64 length,
179
+ // which over-counts by 30-100x. Gemini's tile pricing tops out well above
180
+ // the universal 1,600-token cap, so use its max-tile budget instead to
181
+ // avoid under-counting large iPhone screenshots.
182
+ if (options?.providerName === "gemini") {
183
+ return GEMINI_IMAGE_MAX_TOKENS;
184
+ }
142
185
  return IMAGE_MAX_TOKENS;
143
186
  }
144
187
 
@@ -186,7 +229,7 @@ export function estimateContentBlockTokens(
186
229
  return (
187
230
  IMAGE_BLOCK_OVERHEAD_TOKENS +
188
231
  estimateTextTokens(block.source.media_type) +
189
- estimateImageTokens(block)
232
+ estimateImageTokens(block, options)
190
233
  );
191
234
  case "file":
192
235
  return (
@@ -51,6 +51,11 @@ export interface ContextWindowResult {
51
51
  thresholdTokens: number;
52
52
  compactedMessages: number;
53
53
  compactedPersistedMessages: number;
54
+ /**
55
+ * Number of recent ("tail") messages preserved verbatim alongside the
56
+ * summary. Omitted on no-op / skipped results — defaults to 0 at render.
57
+ */
58
+ preservedTailMessages?: number;
54
59
  summaryCalls: number;
55
60
  summaryInputTokens: number;
56
61
  summaryOutputTokens: number;
@@ -216,6 +221,26 @@ export class ContextWindowManager {
216
221
  return getConfig().compaction;
217
222
  }
218
223
 
224
+ get maxInputTokens(): number {
225
+ return this.config.maxInputTokens;
226
+ }
227
+
228
+ /**
229
+ * Estimate the prompt-token cost of `messages` using the same path as the
230
+ * auto-compaction pre-check. Clears the system-prompt cache so the next
231
+ * turn re-resolves it (the system prompt is lazy and may have changed).
232
+ */
233
+ estimateInputTokens(messages: Message[]): number {
234
+ try {
235
+ return estimatePromptTokens(messages, this.systemPrompt, {
236
+ providerName: this.estimationProviderName,
237
+ toolTokenBudget: this.toolTokenBudget,
238
+ });
239
+ } finally {
240
+ this.clearSystemPromptCache();
241
+ }
242
+ }
243
+
219
244
  /**
220
245
  * Cheap pre-check — estimate the current token count and compare against
221
246
  * `compaction.autoThreshold`. Callers pass the estimate back through
@@ -0,0 +1,350 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import type { MessageRow } from "../../memory/conversation-crud.js";
4
+ import {
5
+ findDisplayTurnEndIndex,
6
+ isToolResultOnlyUserMessage,
7
+ mergeConsecutiveAssistantMessages,
8
+ mergeToolResultsIntoAssistantMessages,
9
+ } from "../message-consolidation.js";
10
+
11
+ function makeMsg(
12
+ role: string,
13
+ content: string,
14
+ overrides: Partial<MessageRow> = {},
15
+ ): MessageRow {
16
+ return {
17
+ id: `msg-${Math.random().toString(36).slice(2, 10)}`,
18
+ conversationId: "conv-1",
19
+ role,
20
+ content,
21
+ createdAt: Date.now(),
22
+ displayOrder: 0,
23
+ seen: 1,
24
+ metadata: null,
25
+ ...overrides,
26
+ } as MessageRow;
27
+ }
28
+
29
+ describe("isToolResultOnlyUserMessage", () => {
30
+ test("returns false for assistant rows", () => {
31
+ expect(isToolResultOnlyUserMessage(makeMsg("assistant", "hello"))).toBe(
32
+ false,
33
+ );
34
+ });
35
+
36
+ test("returns false for plain user text (non-JSON content)", () => {
37
+ expect(isToolResultOnlyUserMessage(makeMsg("user", "hello world"))).toBe(
38
+ false,
39
+ );
40
+ });
41
+
42
+ test("returns false when JSON content is not an array", () => {
43
+ expect(
44
+ isToolResultOnlyUserMessage(
45
+ makeMsg("user", JSON.stringify({ type: "tool_result" })),
46
+ ),
47
+ ).toBe(false);
48
+ });
49
+
50
+ test("returns true for a single tool_result block", () => {
51
+ expect(
52
+ isToolResultOnlyUserMessage(
53
+ makeMsg(
54
+ "user",
55
+ JSON.stringify([
56
+ { type: "tool_result", tool_use_id: "abc", content: "ok" },
57
+ ]),
58
+ ),
59
+ ),
60
+ ).toBe(true);
61
+ });
62
+
63
+ test("returns true for a web_search_tool_result block", () => {
64
+ expect(
65
+ isToolResultOnlyUserMessage(
66
+ makeMsg(
67
+ "user",
68
+ JSON.stringify([
69
+ { type: "web_search_tool_result", tool_use_id: "abc" },
70
+ ]),
71
+ ),
72
+ ),
73
+ ).toBe(true);
74
+ });
75
+
76
+ test("returns true when tool_result is wrapped with system_notice blocks", () => {
77
+ expect(
78
+ isToolResultOnlyUserMessage(
79
+ makeMsg(
80
+ "user",
81
+ JSON.stringify([
82
+ { type: "tool_result", tool_use_id: "abc" },
83
+ {
84
+ type: "text",
85
+ text: "<system_notice>info</system_notice>",
86
+ },
87
+ ]),
88
+ ),
89
+ ),
90
+ ).toBe(true);
91
+ });
92
+
93
+ test("returns false when mixed with real user text", () => {
94
+ expect(
95
+ isToolResultOnlyUserMessage(
96
+ makeMsg(
97
+ "user",
98
+ JSON.stringify([
99
+ { type: "tool_result", tool_use_id: "abc" },
100
+ { type: "text", text: "thanks!" },
101
+ ]),
102
+ ),
103
+ ),
104
+ ).toBe(false);
105
+ });
106
+
107
+ test("returns false when there are no tool_result blocks (system_notice alone)", () => {
108
+ expect(
109
+ isToolResultOnlyUserMessage(
110
+ makeMsg(
111
+ "user",
112
+ JSON.stringify([
113
+ {
114
+ type: "text",
115
+ text: "<system_notice>info</system_notice>",
116
+ },
117
+ ]),
118
+ ),
119
+ ),
120
+ ).toBe(false);
121
+ });
122
+
123
+ test("returns false for malformed JSON content", () => {
124
+ expect(
125
+ isToolResultOnlyUserMessage(makeMsg("user", "{not json")),
126
+ ).toBe(false);
127
+ });
128
+
129
+ test("returns false when array contains a non-object block", () => {
130
+ expect(
131
+ isToolResultOnlyUserMessage(
132
+ makeMsg("user", JSON.stringify(["bare string"])),
133
+ ),
134
+ ).toBe(false);
135
+ });
136
+ });
137
+
138
+ describe("findDisplayTurnEndIndex", () => {
139
+ test("returns startIdx unchanged for negative index", () => {
140
+ const messages = [makeMsg("user", "hi")];
141
+ expect(findDisplayTurnEndIndex(messages, -1)).toBe(-1);
142
+ });
143
+
144
+ test("returns startIdx unchanged for out-of-range index", () => {
145
+ const messages = [makeMsg("user", "hi")];
146
+ expect(findDisplayTurnEndIndex(messages, 5)).toBe(5);
147
+ });
148
+
149
+ test("returns startIdx unchanged for non-assistant rows", () => {
150
+ const messages = [
151
+ makeMsg("user", "hi"),
152
+ makeMsg("assistant", "back"),
153
+ ];
154
+ expect(findDisplayTurnEndIndex(messages, 0)).toBe(0);
155
+ });
156
+
157
+ test("returns startIdx for a lone assistant row at end of array", () => {
158
+ const messages = [
159
+ makeMsg("user", "hi"),
160
+ makeMsg("assistant", "back"),
161
+ ];
162
+ expect(findDisplayTurnEndIndex(messages, 1)).toBe(1);
163
+ });
164
+
165
+ test("advances past consecutive assistant rows", () => {
166
+ const messages = [
167
+ makeMsg("user", "hi"),
168
+ makeMsg("assistant", "step 1"),
169
+ makeMsg("assistant", "step 2"),
170
+ makeMsg("assistant", "step 3"),
171
+ makeMsg("user", "next"),
172
+ ];
173
+ expect(findDisplayTurnEndIndex(messages, 1)).toBe(3);
174
+ });
175
+
176
+ test("advances across tool-result-only user rows between assistants", () => {
177
+ const messages = [
178
+ makeMsg("user", "lookup data"),
179
+ makeMsg("assistant", "calling tool"),
180
+ makeMsg(
181
+ "user",
182
+ JSON.stringify([
183
+ { type: "tool_result", tool_use_id: "t1", content: "ok" },
184
+ ]),
185
+ ),
186
+ makeMsg("assistant", "here are the results"),
187
+ makeMsg("user", "thanks"),
188
+ ];
189
+ expect(findDisplayTurnEndIndex(messages, 1)).toBe(3);
190
+ });
191
+
192
+ test("stops at a real-text user row even after tool-result-only rows", () => {
193
+ const messages = [
194
+ makeMsg("user", "first"),
195
+ makeMsg("assistant", "tool call"),
196
+ makeMsg(
197
+ "user",
198
+ JSON.stringify([
199
+ { type: "tool_result", tool_use_id: "t1" },
200
+ ]),
201
+ ),
202
+ makeMsg("assistant", "intermediate"),
203
+ makeMsg("user", "real user follow-up"),
204
+ makeMsg("assistant", "should not be included"),
205
+ ];
206
+ expect(findDisplayTurnEndIndex(messages, 1)).toBe(3);
207
+ });
208
+
209
+ test("handles assistant followed by tool-result-only user at end of array", () => {
210
+ // No tail assistant follows — the suppressed user row still belongs to
211
+ // the cluster (its tool_result content gets folded into the preceding
212
+ // assistant by the read-path collapse).
213
+ const messages = [
214
+ makeMsg("user", "hi"),
215
+ makeMsg("assistant", "tool call"),
216
+ makeMsg(
217
+ "user",
218
+ JSON.stringify([
219
+ { type: "tool_result", tool_use_id: "t1" },
220
+ ]),
221
+ ),
222
+ ];
223
+ expect(findDisplayTurnEndIndex(messages, 1)).toBe(2);
224
+ });
225
+
226
+ test("handles a long mixed cluster", () => {
227
+ const messages = [
228
+ makeMsg("user", "go"),
229
+ makeMsg("assistant", "A"),
230
+ makeMsg("assistant", "B"),
231
+ makeMsg(
232
+ "user",
233
+ JSON.stringify([{ type: "tool_result", tool_use_id: "t1" }]),
234
+ ),
235
+ makeMsg("assistant", "C"),
236
+ makeMsg(
237
+ "user",
238
+ JSON.stringify([
239
+ { type: "tool_result", tool_use_id: "t2" },
240
+ { type: "text", text: "<system_notice>x</system_notice>" },
241
+ ]),
242
+ ),
243
+ makeMsg("assistant", "D"),
244
+ makeMsg("user", "done"),
245
+ ];
246
+ expect(findDisplayTurnEndIndex(messages, 1)).toBe(6);
247
+ });
248
+ });
249
+
250
+ describe("mergeToolResultsIntoAssistantMessages", () => {
251
+ test("suppresses tool-result-only user rows and lifts blocks onto preceding assistant", () => {
252
+ const messages = [
253
+ makeMsg(
254
+ "assistant",
255
+ JSON.stringify([{ type: "tool_use", id: "t1", name: "x", input: {} }]),
256
+ ),
257
+ makeMsg(
258
+ "user",
259
+ JSON.stringify([
260
+ { type: "tool_result", tool_use_id: "t1", content: "ok" },
261
+ ]),
262
+ ),
263
+ ];
264
+ const merged = mergeToolResultsIntoAssistantMessages(messages);
265
+ expect(merged).toHaveLength(1);
266
+ expect(merged[0].role).toBe("assistant");
267
+ const blocks = JSON.parse(merged[0].content) as Array<{ type: string }>;
268
+ expect(blocks.map((b) => b.type)).toEqual(["tool_use", "tool_result"]);
269
+ });
270
+
271
+ test("keeps mixed user messages with their non-tool-result content", () => {
272
+ const messages = [
273
+ makeMsg(
274
+ "assistant",
275
+ JSON.stringify([{ type: "tool_use", id: "t1", name: "x", input: {} }]),
276
+ ),
277
+ makeMsg(
278
+ "user",
279
+ JSON.stringify([
280
+ { type: "tool_result", tool_use_id: "t1", content: "ok" },
281
+ { type: "text", text: "thanks!" },
282
+ ]),
283
+ ),
284
+ ];
285
+ const merged = mergeToolResultsIntoAssistantMessages(messages);
286
+ expect(merged).toHaveLength(2);
287
+ expect(merged[1].role).toBe("user");
288
+ const userBlocks = JSON.parse(merged[1].content) as Array<{ type: string }>;
289
+ expect(userBlocks.map((b) => b.type)).toEqual(["text"]);
290
+ });
291
+
292
+ test("passes plain user text through unchanged", () => {
293
+ const messages = [
294
+ makeMsg("user", "hi"),
295
+ makeMsg("assistant", "hello"),
296
+ ];
297
+ const merged = mergeToolResultsIntoAssistantMessages(messages);
298
+ expect(merged).toHaveLength(2);
299
+ expect(merged[0].content).toBe("hi");
300
+ });
301
+ });
302
+
303
+ describe("mergeConsecutiveAssistantMessages", () => {
304
+ test("collapses adjacent assistant rows onto the first row", () => {
305
+ const a = makeMsg(
306
+ "assistant",
307
+ JSON.stringify([{ type: "text", text: "part 1" }]),
308
+ { id: "anchor" },
309
+ );
310
+ const b = makeMsg(
311
+ "assistant",
312
+ JSON.stringify([{ type: "text", text: "part 2" }]),
313
+ { id: "tail" },
314
+ );
315
+ const { messages, mergedIdMap } = mergeConsecutiveAssistantMessages([
316
+ makeMsg("user", "hi"),
317
+ a,
318
+ b,
319
+ ]);
320
+ expect(messages).toHaveLength(2);
321
+ expect(messages[1].id).toBe("anchor");
322
+ const blocks = JSON.parse(messages[1].content) as Array<{ text: string }>;
323
+ expect(blocks.map((blk) => blk.text)).toEqual(["part 1", "part 2"]);
324
+ expect(mergedIdMap.get("anchor")).toEqual(["tail"]);
325
+ });
326
+
327
+ test("leaves a single assistant row unchanged", () => {
328
+ const messages = [
329
+ makeMsg("user", "hi"),
330
+ makeMsg(
331
+ "assistant",
332
+ JSON.stringify([{ type: "text", text: "hello" }]),
333
+ ),
334
+ ];
335
+ const { messages: result, mergedIdMap } =
336
+ mergeConsecutiveAssistantMessages(messages);
337
+ expect(result).toHaveLength(2);
338
+ expect(mergedIdMap.size).toBe(0);
339
+ });
340
+
341
+ test("does not merge assistant rows separated by a real user row", () => {
342
+ const messages = [
343
+ makeMsg("assistant", JSON.stringify([{ type: "text", text: "A" }])),
344
+ makeMsg("user", "interrupt"),
345
+ makeMsg("assistant", JSON.stringify([{ type: "text", text: "B" }])),
346
+ ];
347
+ const { messages: result } = mergeConsecutiveAssistantMessages(messages);
348
+ expect(result).toHaveLength(3);
349
+ });
350
+ });