@vellumai/assistant 0.8.4 → 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 (438) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docs/browser-use-architecture-phase2.md +1 -1
  3. package/knip.json +2 -1
  4. package/openapi.yaml +809 -11
  5. package/package.json +1 -1
  6. package/src/__tests__/anthropic-provider.test.ts +34 -37
  7. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  8. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  9. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  10. package/src/__tests__/background-workers-disk-pressure.test.ts +3 -3
  11. package/src/__tests__/btw-routes.test.ts +2 -3
  12. package/src/__tests__/call-controller.test.ts +0 -1
  13. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  14. package/src/__tests__/channel-guardian.test.ts +3 -3
  15. package/src/__tests__/checker.test.ts +6 -15
  16. package/src/__tests__/compaction-events.test.ts +1 -0
  17. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  18. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  19. package/src/__tests__/computer-use-tools.test.ts +2 -4
  20. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  21. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  22. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  23. package/src/__tests__/conversation-agent-loop-overflow.test.ts +197 -2
  24. package/src/__tests__/conversation-agent-loop.test.ts +163 -122
  25. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  26. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  27. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  28. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  29. package/src/__tests__/conversation-error.test.ts +31 -0
  30. package/src/__tests__/conversation-fork-crud.test.ts +178 -15
  31. package/src/__tests__/conversation-lifecycle.test.ts +52 -11
  32. package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +13 -13
  33. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
  34. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  35. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  36. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  37. package/src/__tests__/conversation-store.test.ts +1 -1
  38. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  39. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  40. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  41. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  42. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  43. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  44. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  45. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  46. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  47. package/src/__tests__/email-html-renderer.test.ts +12 -0
  48. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  49. package/src/__tests__/gemini-provider.test.ts +78 -0
  50. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  51. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  52. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  53. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  54. package/src/__tests__/heartbeat-service.test.ts +4 -0
  55. package/src/__tests__/host-shell-tool.test.ts +1 -1
  56. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  57. package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
  58. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  59. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  60. package/src/__tests__/llm-resolver.test.ts +77 -9
  61. package/src/__tests__/llm-usage-store.test.ts +66 -0
  62. package/src/__tests__/logger.test.ts +89 -0
  63. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  64. package/src/__tests__/media-generate-image.test.ts +31 -0
  65. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  66. package/src/__tests__/model-intents.test.ts +2 -4
  67. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  68. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  69. package/src/__tests__/openai-provider.test.ts +46 -0
  70. package/src/__tests__/openai-responses-provider.test.ts +114 -12
  71. package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
  72. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  73. package/src/__tests__/platform.test.ts +2 -2
  74. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  75. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  76. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  77. package/src/__tests__/plugin-types.test.ts +3 -2
  78. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  79. package/src/__tests__/pricing.test.ts +12 -0
  80. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  81. package/src/__tests__/registry.test.ts +2 -8
  82. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  83. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  84. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  85. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  86. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  87. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  88. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  89. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  90. package/src/__tests__/suggestion-routes.test.ts +1 -0
  91. package/src/__tests__/sync-message-contract.test.ts +59 -0
  92. package/src/__tests__/system-prompt.test.ts +145 -131
  93. package/src/__tests__/terminal-tools.test.ts +1 -1
  94. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  95. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  96. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  97. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  98. package/src/__tests__/tool-executor.test.ts +9 -62
  99. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  100. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  101. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  102. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  103. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  104. package/src/__tests__/usage-routes.test.ts +3 -0
  105. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  106. package/src/__tests__/workspace-git-service.test.ts +6 -5
  107. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  108. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  109. package/src/acp/prepare-agent-env.ts +78 -0
  110. package/src/acp/session-manager.ts +1 -1
  111. package/src/agent/loop.ts +8 -0
  112. package/src/api/README.md +5 -0
  113. package/src/api/index.ts +4 -0
  114. package/src/api/package.json +10 -0
  115. package/src/background-wake/background-wake-routes.test.ts +233 -0
  116. package/src/background-wake/runtime-registry.ts +24 -0
  117. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  118. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  119. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  120. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  121. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  122. package/src/cli/commands/__tests__/memory-v2.test.ts +1 -0
  123. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  124. package/src/cli/commands/browser.ts +247 -0
  125. package/src/cli/commands/domain.ts +91 -41
  126. package/src/cli/commands/inference.ts +93 -40
  127. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  128. package/src/cli/commands/memory-v2.ts +176 -1
  129. package/src/cli/commands/memory-v3-render.ts +344 -0
  130. package/src/cli/commands/memory-v3.ts +316 -0
  131. package/src/cli/program.ts +2 -0
  132. package/src/config/assistant-feature-flags.ts +21 -9
  133. package/src/config/bundled-skills/document-editor/SKILL.md +11 -2
  134. package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
  135. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  136. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  137. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  138. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  139. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  140. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  141. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  142. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  143. package/src/config/bundled-tool-registry.ts +2 -0
  144. package/src/config/call-site-defaults.ts +7 -6
  145. package/src/config/feature-flag-registry.json +16 -0
  146. package/src/config/schemas/__tests__/memory-v2.test.ts +213 -1
  147. package/src/config/schemas/call-site-catalog.ts +21 -7
  148. package/src/config/schemas/llm.ts +12 -1
  149. package/src/config/schemas/memory-v2.ts +246 -0
  150. package/src/config/schemas/memory.ts +2 -1
  151. package/src/context/compactor.ts +52 -0
  152. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  153. package/src/conversations/message-consolidation.ts +404 -0
  154. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  155. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  156. package/src/daemon/conversation-agent-loop-handlers.ts +2 -13
  157. package/src/daemon/conversation-agent-loop.ts +126 -76
  158. package/src/daemon/conversation-error.ts +31 -1
  159. package/src/daemon/conversation-lifecycle.ts +27 -22
  160. package/src/daemon/conversation-runtime-assembly.ts +10 -9
  161. package/src/daemon/conversation-tool-setup.ts +63 -3
  162. package/src/daemon/conversation-usage.ts +2 -0
  163. package/src/daemon/conversation.ts +14 -29
  164. package/src/daemon/disk-pressure-guard.ts +14 -2
  165. package/src/daemon/handlers/config-model.test.ts +1 -0
  166. package/src/daemon/handlers/conversations.ts +11 -3
  167. package/src/daemon/host-browser-proxy.ts +5 -5
  168. package/src/daemon/host-cu-proxy.ts +4 -4
  169. package/src/daemon/host-file-proxy.ts +4 -4
  170. package/src/daemon/host-proxy-base.ts +4 -4
  171. package/src/daemon/host-transfer-proxy.ts +10 -10
  172. package/src/daemon/lifecycle.ts +23 -20
  173. package/src/daemon/meet-manifest-loader.ts +1 -7
  174. package/src/daemon/message-types/conversations.ts +6 -9
  175. package/src/daemon/message-types/home.ts +1 -13
  176. package/src/daemon/message-types/messages.ts +6 -14
  177. package/src/daemon/message-types/sync.ts +14 -0
  178. package/src/daemon/shutdown-handlers.ts +24 -5
  179. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  180. package/src/daemon/tool-setup-types.ts +13 -0
  181. package/src/events/relationship-state-updated.ts +25 -0
  182. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
  183. package/src/home/home-greeting.ts +0 -9
  184. package/src/home/suggested-prompts.ts +0 -9
  185. package/src/ipc/gateway-flag-listener.ts +123 -0
  186. package/src/ipc/skill-routes/registries.ts +8 -12
  187. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  188. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  189. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  190. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  191. package/src/memory/__tests__/memory-retrospective-job.test.ts +7 -0
  192. package/src/memory/auto-analysis-enqueue.ts +5 -1
  193. package/src/memory/conversation-crud.ts +71 -70
  194. package/src/memory/conversation-starters-cadence.ts +3 -1
  195. package/src/memory/conversation-title-service.ts +19 -3
  196. package/src/memory/db-async-query.ts +214 -0
  197. package/src/memory/db-init.ts +10 -0
  198. package/src/memory/db-maintenance.ts +30 -21
  199. package/src/memory/graph/bootstrap.ts +8 -1
  200. package/src/memory/graph/capability-seed.ts +7 -3
  201. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  202. package/src/memory/graph/extraction.ts +1 -5
  203. package/src/memory/graph/graph-search.ts +7 -1
  204. package/src/memory/indexer.ts +28 -18
  205. package/src/memory/job-handlers/cleanup.ts +76 -18
  206. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  207. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  208. package/src/memory/jobs-store.ts +14 -0
  209. package/src/memory/jobs-worker.ts +55 -22
  210. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  211. package/src/memory/llm-request-log-source-local.ts +7 -0
  212. package/src/memory/llm-request-log-source.ts +9 -2
  213. package/src/memory/llm-request-log-store.ts +43 -1
  214. package/src/memory/llm-usage-store.ts +24 -0
  215. package/src/memory/memory-retrospective-enqueue.ts +8 -1
  216. package/src/memory/memory-retrospective-job.ts +5 -0
  217. package/src/memory/memory-v2-activation-log-store.ts +15 -6
  218. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  219. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  220. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  221. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  222. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  223. package/src/memory/migrations/index.ts +17 -0
  224. package/src/memory/migrations/registry.ts +33 -0
  225. package/src/memory/schema/conversations.ts +1 -1
  226. package/src/memory/schema/infrastructure.ts +21 -0
  227. package/src/memory/tool-usage-store.ts +36 -8
  228. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  229. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  230. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  231. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  232. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  233. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  234. package/src/memory/v2/__tests__/injection.test.ts +127 -98
  235. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  236. package/src/memory/v2/__tests__/router.test.ts +171 -3
  237. package/src/memory/v2/harness/compare.ts +57 -0
  238. package/src/memory/v2/harness/metrics.ts +124 -0
  239. package/src/memory/v2/harness/oracle.ts +145 -0
  240. package/src/memory/v2/harness/replay-input.ts +224 -0
  241. package/src/memory/v2/harness/retriever.ts +74 -0
  242. package/src/memory/v2/harness/router-retriever.ts +43 -0
  243. package/src/memory/v2/harness/runner.ts +106 -0
  244. package/src/memory/v2/harness/trace.ts +58 -0
  245. package/src/memory/v2/injection.ts +21 -15
  246. package/src/memory/v2/prompts/router.ts +26 -1
  247. package/src/memory/v2/qdrant.ts +14 -2
  248. package/src/memory/v2/router.ts +171 -18
  249. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  250. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  251. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  252. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  253. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  254. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  255. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  256. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  257. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  258. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  259. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  260. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  261. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  262. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  263. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  264. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  265. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  266. package/src/memory/v3/auto-edges.ts +223 -0
  267. package/src/memory/v3/coactivation-store.ts +124 -0
  268. package/src/memory/v3/consolidation-job.ts +323 -0
  269. package/src/memory/v3/edge-learning-job.ts +160 -0
  270. package/src/memory/v3/edges.ts +249 -0
  271. package/src/memory/v3/filter.ts +281 -0
  272. package/src/memory/v3/gate.ts +334 -0
  273. package/src/memory/v3/index-composition.ts +113 -0
  274. package/src/memory/v3/llm-capture.ts +46 -0
  275. package/src/memory/v3/loop.ts +382 -0
  276. package/src/memory/v3/maintenance.ts +144 -0
  277. package/src/memory/v3/prompt-context.ts +33 -0
  278. package/src/memory/v3/prompts/consolidation.ts +458 -0
  279. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  280. package/src/memory/v3/retriever.ts +33 -0
  281. package/src/memory/v3/scouts.ts +420 -0
  282. package/src/memory/v3/shadow-middleware.ts +305 -0
  283. package/src/memory/v3/traversal.ts +206 -0
  284. package/src/memory/v3/tree-index.ts +237 -0
  285. package/src/memory/v3/tree-store.ts +394 -0
  286. package/src/memory/v3/tree-walk.ts +351 -0
  287. package/src/memory/v3/types.ts +65 -0
  288. package/src/memory/v3/validate.ts +300 -0
  289. package/src/notifications/adapters/macos.ts +18 -1
  290. package/src/notifications/adapters/platform.ts +1 -1
  291. package/src/notifications/decision-engine.ts +1 -4
  292. package/src/notifications/emit-signal.ts +29 -49
  293. package/src/permissions/prompter.ts +3 -3
  294. package/src/permissions/question-prompter.ts +5 -2
  295. package/src/permissions/secret-prompter.ts +2 -2
  296. package/src/plugin-api/index.ts +4 -0
  297. package/src/plugin-api/types.ts +7 -33
  298. package/src/plugins/defaults/index.ts +6 -0
  299. package/src/plugins/defaults/injectors.ts +18 -11
  300. package/src/plugins/external-plugin-loader.ts +5 -68
  301. package/src/plugins/types.ts +11 -16
  302. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  303. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  304. package/src/prompts/persona-resolver.ts +36 -21
  305. package/src/prompts/sections.ts +39 -7
  306. package/src/prompts/system-prompt.ts +50 -185
  307. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  308. package/src/prompts/templates/system-sections.ts +230 -8
  309. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  310. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  311. package/src/providers/anthropic/client.ts +32 -66
  312. package/src/providers/call-site-routing.ts +14 -2
  313. package/src/providers/connection-model-compat.ts +38 -0
  314. package/src/providers/connection-resolution.ts +16 -2
  315. package/src/providers/gemini/client.ts +49 -6
  316. package/src/providers/inference/adapter-factory.ts +3 -0
  317. package/src/providers/minimax/client.ts +106 -0
  318. package/src/providers/model-catalog.ts +43 -0
  319. package/src/providers/model-intents.ts +1 -1
  320. package/src/providers/openai/chat-completions-provider.ts +6 -3
  321. package/src/providers/openai/codex-models.ts +18 -0
  322. package/src/providers/openai/responses-provider.ts +78 -21
  323. package/src/providers/provider-send-message.ts +7 -1
  324. package/src/providers/retry.ts +34 -3
  325. package/src/providers/thinking-config.ts +26 -1
  326. package/src/providers/usage-tracking.ts +2 -0
  327. package/src/runtime/AGENTS.md +2 -2
  328. package/src/runtime/agent-wake.ts +1 -0
  329. package/src/runtime/assistant-event-hub.ts +76 -6
  330. package/src/runtime/auth/route-policy.ts +36 -0
  331. package/src/runtime/btw-sidechain.ts +0 -6
  332. package/src/runtime/http-types.ts +0 -2
  333. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  334. package/src/runtime/pending-interactions.ts +0 -1
  335. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +106 -0
  336. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +25 -6
  337. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  338. package/src/runtime/routes/acp-routes.test.ts +255 -6
  339. package/src/runtime/routes/acp-routes.ts +8 -1
  340. package/src/runtime/routes/avatar-routes.ts +10 -10
  341. package/src/runtime/routes/background-wake-routes.ts +188 -0
  342. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  343. package/src/runtime/routes/btw-routes.ts +0 -6
  344. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  345. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  346. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  347. package/src/runtime/routes/conversation-query-routes.ts +142 -36
  348. package/src/runtime/routes/conversation-routes.ts +252 -410
  349. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  350. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  351. package/src/runtime/routes/domain-routes.ts +60 -10
  352. package/src/runtime/routes/email-routes.ts +5 -2
  353. package/src/runtime/routes/events-routes.ts +54 -10
  354. package/src/runtime/routes/group-routes.ts +24 -8
  355. package/src/runtime/routes/host-browser-routes.ts +10 -2
  356. package/src/runtime/routes/host-cu-routes.ts +2 -2
  357. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  358. package/src/runtime/routes/index.ts +8 -0
  359. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  360. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  361. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  362. package/src/runtime/routes/memory-item-routes.ts +8 -3
  363. package/src/runtime/routes/memory-v2-routes.ts +215 -5
  364. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  365. package/src/runtime/routes/migration-routes.ts +21 -24
  366. package/src/runtime/routes/plugins-routes.ts +337 -0
  367. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  368. package/src/runtime/routes/secret-routes.ts +25 -5
  369. package/src/runtime/routes/settings-routes.ts +12 -11
  370. package/src/runtime/routes/slack-channel-routes.ts +5 -4
  371. package/src/runtime/routes/workspace-routes.ts +25 -10
  372. package/src/runtime/sync/resource-sync-events.ts +106 -38
  373. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  374. package/src/runtime/sync/sync-publisher.ts +2 -1
  375. package/src/runtime/verification-outbound-actions.ts +73 -1
  376. package/src/telemetry/types.ts +12 -0
  377. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  378. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  379. package/src/tools/acp/spawn.test.ts +119 -0
  380. package/src/tools/acp/spawn.ts +15 -2
  381. package/src/tools/apps/definitions.ts +2 -8
  382. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  383. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  384. package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
  385. package/src/tools/browser/browser-execution.ts +16 -3
  386. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  387. package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
  388. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
  389. package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
  390. package/src/tools/browser/cdp-client/factory.ts +100 -17
  391. package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
  392. package/src/tools/browser/cdp-client/types.ts +65 -0
  393. package/src/tools/browser/pinned-tabs.ts +96 -40
  394. package/src/tools/computer-use/definitions.ts +22 -78
  395. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  396. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  397. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  398. package/src/tools/credentials/vault.ts +3 -9
  399. package/src/tools/document/document-tool.ts +59 -0
  400. package/src/tools/execution-target.ts +21 -23
  401. package/src/tools/executor.ts +6 -1
  402. package/src/tools/filesystem/edit.ts +3 -9
  403. package/src/tools/filesystem/list.ts +3 -9
  404. package/src/tools/filesystem/read.ts +3 -9
  405. package/src/tools/filesystem/write.ts +3 -9
  406. package/src/tools/host-filesystem/edit.ts +3 -9
  407. package/src/tools/host-filesystem/read.ts +3 -9
  408. package/src/tools/host-filesystem/transfer.ts +3 -9
  409. package/src/tools/host-filesystem/write.ts +3 -9
  410. package/src/tools/host-terminal/host-shell.ts +3 -9
  411. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  412. package/src/tools/memory/register.test.ts +1 -1
  413. package/src/tools/memory/register.ts +4 -9
  414. package/src/tools/network/web-fetch.ts +3 -9
  415. package/src/tools/network/web-search.ts +25 -32
  416. package/src/tools/registry.ts +7 -23
  417. package/src/tools/schema-transforms.ts +1 -1
  418. package/src/tools/skills/execute.ts +3 -9
  419. package/src/tools/skills/load.ts +3 -9
  420. package/src/tools/skills/skill-tool-factory.ts +1 -8
  421. package/src/tools/subagent/notify-parent.ts +3 -9
  422. package/src/tools/system/request-permission.ts +3 -9
  423. package/src/tools/terminal/shell.ts +3 -9
  424. package/src/tools/tool-defaults.ts +94 -0
  425. package/src/tools/types.ts +27 -98
  426. package/src/tools/ui-surface/definitions.ts +6 -22
  427. package/src/usage/pricing.ts +23 -0
  428. package/src/usage/types.ts +12 -0
  429. package/src/util/logger.ts +16 -7
  430. package/src/util/platform.ts +7 -2
  431. package/src/util/sqlite3-runtime.ts +65 -0
  432. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  433. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  434. package/src/workspace/migrations/registry.ts +2 -0
  435. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  436. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  437. package/src/daemon/query-complexity-router.ts +0 -75
  438. package/src/prompts/cache-boundary.ts +0 -8
@@ -247,6 +247,10 @@ export class AssistantEventHub {
247
247
  * Publish an event to all matching subscribers.
248
248
  *
249
249
  * Matching rules:
250
+ * - if `excludeClientId` is set, the subscriber with that clientId is
251
+ * skipped regardless of every other rule (self-echo suppression — the
252
+ * client that originated the mutation does not receive its own
253
+ * invalidation back through the hub).
250
254
  * - if `targetClientId` is set, deliver only to the subscriber with that
251
255
  * clientId, bypassing the conversation-id filter entirely (the web-origin
252
256
  * event's conversationId differs from the macOS client's subscribed
@@ -255,6 +259,11 @@ export class AssistantEventHub {
255
259
  * `event.conversationId` must equal it
256
260
  * - if `targetCapability` is set, only subscribers whose capabilities include
257
261
  * it receive the event; untargeted events go to all
262
+ * - if `targetInterfaceId` is set, only client subscribers whose
263
+ * `interfaceId` matches receive the event; process subscribers and
264
+ * non-matching clients are skipped. Used to narrow legacy
265
+ * broadcasts (e.g. `conversation_list_invalidated`) to a specific
266
+ * client surface during a migration window.
258
267
  *
259
268
  * Fanout is isolated: a throwing or rejecting subscriber does not abort
260
269
  * delivery to remaining subscribers.
@@ -264,6 +273,15 @@ export class AssistantEventHub {
264
273
  options?: {
265
274
  targetCapability?: HostProxyCapability;
266
275
  targetClientId?: string;
276
+ targetInterfaceId?: InterfaceId;
277
+ /**
278
+ * Skip the subscriber with this `clientId`. Used for self-echo
279
+ * suppression on `sync_changed`: the route handler echoes the
280
+ * originating tab's `X-Vellum-Client-Id` back on the event, and the
281
+ * hub uses it here to avoid re-delivering the invalidation to the
282
+ * tab that already mutated its own optimistic state.
283
+ */
284
+ excludeClientId?: string;
267
285
  },
268
286
  ): Promise<void> {
269
287
  if (event.conversationId) {
@@ -276,12 +294,33 @@ export class AssistantEventHub {
276
294
 
277
295
  const targetCapability = options?.targetCapability;
278
296
  const targetClientId = options?.targetClientId;
297
+ const targetInterfaceId = options?.targetInterfaceId;
298
+ const excludeClientId = options?.excludeClientId;
279
299
  const snapshot = Array.from(this.subscribers);
280
300
  const errors: unknown[] = [];
281
301
 
282
302
  for (const entry of snapshot) {
283
303
  if (!entry.active) continue;
284
304
 
305
+ // Self-echo suppression: the originating client never receives the
306
+ // event back. Checked before every other rule so it composes with
307
+ // both targeted and untargeted broadcasts.
308
+ if (
309
+ excludeClientId != null &&
310
+ entry.type === "client" &&
311
+ entry.clientId === excludeClientId
312
+ ) {
313
+ continue;
314
+ }
315
+
316
+ // Interface targeting: skip any subscriber that is not a client of
317
+ // the requested interface. Composes with `targetClientId` and
318
+ // `targetCapability` below.
319
+ if (targetInterfaceId != null) {
320
+ if (entry.type !== "client" || entry.interfaceId !== targetInterfaceId)
321
+ continue;
322
+ }
323
+
285
324
  if (targetClientId != null) {
286
325
  // Targeted: bypass conversation filter, deliver only to the named client.
287
326
  if (entry.type !== "client" || entry.clientId !== targetClientId)
@@ -519,10 +558,11 @@ let _hubChain = Promise.resolve();
519
558
  export function broadcastMessage(
520
559
  msg: ServerMessage,
521
560
  conversationId?: string,
522
- options?: { targetClientId?: string },
561
+ options?: { targetClientId?: string; targetInterfaceId?: InterfaceId },
523
562
  ): void {
524
563
  const resolvedConversationId = conversationId ?? extractConversationId(msg);
525
564
  const targetClientId = options?.targetClientId;
565
+ const targetInterfaceId = options?.targetInterfaceId;
526
566
 
527
567
  // Confirmation-request side effects: canonical guardian request creation.
528
568
  // The home-feed `activity.failed` notification side-effect lives in the
@@ -539,16 +579,45 @@ export function broadcastMessage(
539
579
  : resolvedConversationId;
540
580
  const event = buildAssistantEvent(msg, scopedConversationId);
541
581
  const targetCapability = capabilityForMessageType(msg.type);
582
+ // Self-echo suppression: a `sync_changed` carrying an `originClientId`
583
+ // means a specific client just mutated the resource. The hub must not
584
+ // re-deliver the invalidation to that client — it already updated its
585
+ // optimistic state locally and a redundant invalidation would clobber it
586
+ // with a flash of stale-then-fresh data. Assistant-internal emits (agent
587
+ // loop, FS watcher, cron) leave `originClientId` unset and the event
588
+ // fans out to every subscriber as before.
589
+ const excludeClientId =
590
+ msg.type === "sync_changed" &&
591
+ typeof msg.originClientId === "string" &&
592
+ msg.originClientId.length > 0
593
+ ? msg.originClientId
594
+ : undefined;
542
595
  const publishOptions =
543
- targetCapability != null || targetClientId != null
544
- ? { targetCapability, targetClientId }
596
+ targetCapability != null ||
597
+ targetClientId != null ||
598
+ targetInterfaceId != null ||
599
+ excludeClientId != null
600
+ ? {
601
+ targetCapability,
602
+ targetClientId,
603
+ targetInterfaceId,
604
+ excludeClientId,
605
+ }
545
606
  : undefined;
546
607
  _hubChain = _hubChain
547
608
  .then(() => assistantEventHub.publish(event, publishOptions))
548
609
  .then(() => {
549
- // When a conversation title changes, also broadcast an unscoped
550
- // `conversation_list_invalidated` so every connected client's sidebar
551
- // refreshes not just the client viewing this conversation.
610
+ // When a conversation title changes, also publish a
611
+ // `conversation_list_invalidated` so the macOS sidebar refreshes
612
+ // its row ordering for the renamed conversation. Web consumes the
613
+ // paired `sync_changed` with `conversation:<id>:metadata` tag
614
+ // emitted by `publishConversationTitleChanged` and patches the
615
+ // single row in place, so the broadcast is scoped to macOS only.
616
+ //
617
+ // TODO(electron-cutover): remove this emission once macOS migrates
618
+ // to the Electron client and consumes `sync_changed` directly. At
619
+ // that point `conversation_list_invalidated` has no remaining
620
+ // consumers and the message type can be retired.
552
621
  if (msg.type === "conversation_title_updated") {
553
622
  return assistantEventHub
554
623
  .publish(
@@ -556,6 +625,7 @@ export function broadcastMessage(
556
625
  type: "conversation_list_invalidated",
557
626
  reason: "renamed",
558
627
  }),
628
+ { targetInterfaceId: "macos" },
559
629
  )
560
630
  .catch((err: unknown) => {
561
631
  log.warn(
@@ -417,6 +417,7 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
417
417
 
418
418
  // LLM call site catalog
419
419
  { endpoint: "config/llm/call-sites:GET", scopes: ["settings.read"] },
420
+ { endpoint: "config/llm/profiles:GET", scopes: ["settings.read"] },
420
421
 
421
422
  // Conversation management
422
423
  { endpoint: "conversations:DELETE", scopes: ["chat.write"] },
@@ -440,6 +441,7 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
440
441
  // Message content
441
442
  { endpoint: "messages/content", scopes: ["chat.read"] },
442
443
  { endpoint: "messages/llm-context", scopes: ["chat.read"] },
444
+ { endpoint: "conversations/llm-context", scopes: ["chat.read"] },
443
445
  { endpoint: "llm-request-logs/payload", scopes: ["chat.read"] },
444
446
  { endpoint: "messages/tts", scopes: ["chat.read"] },
445
447
  { endpoint: "tts/synthesize", scopes: ["chat.read"] },
@@ -458,6 +460,11 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
458
460
  { endpoint: "skills:DELETE", scopes: ["settings.write"] },
459
461
  { endpoint: "skills:PATCH", scopes: ["settings.write"] },
460
462
 
463
+ // Plugins (read-only for now — install / uninstall stay CLI-side)
464
+ { endpoint: "plugins:GET", scopes: ["settings.read"] },
465
+ { endpoint: "plugins/search:GET", scopes: ["settings.read"] },
466
+ { endpoint: "plugins:DELETE", scopes: ["settings.write"] },
467
+
461
468
  // Memory items
462
469
  { endpoint: "memory-items:GET", scopes: ["settings.read"] },
463
470
  { endpoint: "memory-items:POST", scopes: ["settings.write"] },
@@ -471,6 +478,18 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
471
478
  { endpoint: "memory/v2/concept-frequency:POST", scopes: ["settings.read"] },
472
479
  { endpoint: "memory/v2/ema-scores:POST", scopes: ["settings.read"] },
473
480
  { endpoint: "memory/v2/simulate-router:POST", scopes: ["settings.read"] },
481
+ {
482
+ endpoint: "memory/v2/compare-retrievers:POST",
483
+ scopes: ["settings.read"],
484
+ },
485
+ {
486
+ endpoint: "memory/v2/router-prompt-template:GET",
487
+ scopes: ["settings.read"],
488
+ },
489
+ { endpoint: "memory/v2/now-text:GET", scopes: ["settings.read"] },
490
+ { endpoint: "memory/v3/validate:POST", scopes: ["settings.read"] },
491
+ { endpoint: "memory/v3/tree:POST", scopes: ["settings.read"] },
492
+ { endpoint: "memory/v3/simulate:POST", scopes: ["settings.read"] },
474
493
 
475
494
  // Trust rule listing
476
495
  { endpoint: "trust-rules/manage:GET", scopes: ["settings.read"] },
@@ -665,6 +684,12 @@ registerPolicy("channels/inbound", {
665
684
  allowedPrincipalTypes: ["svc_gateway"],
666
685
  });
667
686
 
687
+ // Background wake control-plane calls from the platform.
688
+ registerPolicy("background-wake", {
689
+ requiredScopes: ["internal.write"],
690
+ allowedPrincipalTypes: ["svc_gateway"],
691
+ });
692
+
668
693
  // Internal forwarding endpoints: gateway-only
669
694
  const INTERNAL_ENDPOINTS = [
670
695
  "internal/twilio/voice-webhook",
@@ -887,6 +912,12 @@ registerPolicy("browser/execute", {
887
912
  allowedPrincipalTypes: ["local"],
888
913
  });
889
914
 
915
+ // Browser tabs operations (list/select/new/close): local-only (CLI / IPC callers)
916
+ registerPolicy("browser/tabs", {
917
+ requiredScopes: ["settings.write"],
918
+ allowedPrincipalTypes: ["local"],
919
+ });
920
+
890
921
  // Background tools: local-only (CLI / IPC callers)
891
922
  registerPolicy("background-tools", {
892
923
  requiredScopes: ["settings.read"],
@@ -922,6 +953,11 @@ registerPolicy("domain/status", {
922
953
  allowedPrincipalTypes: ["local"],
923
954
  });
924
955
 
956
+ registerPolicy("domain/verification-status", {
957
+ requiredScopes: ["settings.read"],
958
+ allowedPrincipalTypes: ["local"],
959
+ });
960
+
925
961
  // Email management (IPC-local)
926
962
  registerPolicy("email/register", {
927
963
  requiredScopes: ["settings.write"],
@@ -40,9 +40,6 @@ export interface RunBtwSidechainParams {
40
40
  signal?: AbortSignal;
41
41
  timeoutMs?: number;
42
42
  onEvent?: (event: ProviderEvent) => void;
43
- userPersona?: string | null;
44
- channelPersona?: string | null;
45
- userSlug?: string | null;
46
43
  }
47
44
 
48
45
  export interface RunBtwSidechainResult {
@@ -79,9 +76,6 @@ export async function runBtwSidechain(
79
76
  : buildSystemPrompt({
80
77
  excludeBootstrap: true,
81
78
  excludeCustomPrefix: true,
82
- userPersona: params.userPersona,
83
- channelPersona: params.channelPersona,
84
- userSlug: params.userSlug,
85
79
  }));
86
80
 
87
81
  const { signal: timeoutSignal, cleanup } = createTimeout(
@@ -148,8 +148,6 @@ export interface RuntimeAttachmentMetadata {
148
148
 
149
149
  export interface RuntimeMessagePayload {
150
150
  id: string;
151
- /** Concrete persisted assistant row id for row-scoped actions. */
152
- daemonMessageId?: string;
153
151
  role: string;
154
152
  content: string;
155
153
  timestamp: string;
@@ -681,8 +681,13 @@ export interface BuildExportVBundleOptions {
681
681
  * flushed to the main .db file. Callers should pass a function that runs
682
682
  * PRAGMA wal_checkpoint(TRUNCATE) on the live database connection.
683
683
  * Called before the workspace walk so the DB file is up to date.
684
+ *
685
+ * May return a Promise — `streamExportVBundle` awaits the result so an
686
+ * async-dispatched checkpoint (e.g. via `runAsyncSqlite`) does not race
687
+ * with the file walk. The synchronous `buildExportVBundle` path does
688
+ * not await; pass a sync callback there.
684
689
  */
685
- checkpoint?: () => void;
690
+ checkpoint?: () => void | Promise<void>;
686
691
  /** Optional credential entries to include in the archive under credentials/ prefix. */
687
692
  credentials?: Array<{ account: string; value: string }>;
688
693
  }
@@ -1120,9 +1125,11 @@ export async function streamExportVBundle(
1120
1125
  credentials,
1121
1126
  } = options;
1122
1127
 
1123
- // Flush WAL to the main database file before reading
1128
+ // Flush WAL to the main database file before reading. Awaiting allows
1129
+ // the callback to dispatch the checkpoint via `runAsyncSqlite` so the
1130
+ // daemon event loop stays responsive while SQLite truncates the WAL.
1124
1131
  if (checkpoint) {
1125
- checkpoint();
1132
+ await checkpoint();
1126
1133
  }
1127
1134
 
1128
1135
  const allFileMetadata: FileMetadata[] = [];
@@ -143,7 +143,6 @@ function emitResolved(
143
143
  type: "interaction_resolved",
144
144
  requestId,
145
145
  conversationId: interaction.conversationId,
146
- conversationKey: interaction.conversationId,
147
146
  kind: interaction.kind,
148
147
  state,
149
148
  });
@@ -66,6 +66,7 @@ import {
66
66
  recordMemoryV2ActivationLog,
67
67
  } from "../../../memory/memory-v2-activation-log-store.js";
68
68
  import {
69
+ conversationKeys,
69
70
  conversations,
70
71
  llmRequestLogs,
71
72
  memoryV2ActivationLogs,
@@ -89,6 +90,10 @@ const llmContextRoute = ROUTES.find(
89
90
  (r) => r.method === "GET" && r.endpoint === "messages/:id/llm-context",
90
91
  )!;
91
92
 
93
+ const conversationLlmContextRoute = ROUTES.find(
94
+ (r) => r.method === "GET" && r.endpoint === "conversations/llm-context",
95
+ )!;
96
+
92
97
  const replaceProfileRoute = ROUTES.find(
93
98
  (r) => r.operationId === "config_llm_profiles_replace",
94
99
  )!;
@@ -97,11 +102,16 @@ function dispatchLlmContext(messageId: string) {
97
102
  return llmContextRoute.handler({ pathParams: { id: messageId } });
98
103
  }
99
104
 
105
+ function dispatchConversationLlmContext(queryParams: Record<string, string>) {
106
+ return conversationLlmContextRoute.handler({ queryParams });
107
+ }
108
+
100
109
  function clearTables(): void {
101
110
  const db = getDb();
102
111
  db.delete(llmRequestLogs).run();
103
112
  db.delete(memoryV2ActivationLogs).run();
104
113
  db.delete(messages).run();
114
+ db.delete(conversationKeys).run();
105
115
  db.delete(conversations).run();
106
116
  }
107
117
 
@@ -165,6 +175,102 @@ function seedRequestLog(
165
175
  .run();
166
176
  }
167
177
 
178
+ function seedConversationKey(conversationKey: string, conversationId: string): void {
179
+ getDb()
180
+ .insert(conversationKeys)
181
+ .values({
182
+ id: `key-${conversationKey}`,
183
+ conversationKey,
184
+ conversationId,
185
+ createdAt: Date.now(),
186
+ })
187
+ .run();
188
+ }
189
+
190
+ describe("GET /v1/conversations/llm-context", () => {
191
+ beforeEach(() => {
192
+ clearTables();
193
+ });
194
+
195
+ test("returns all LLM calls for a resolved conversation key", async () => {
196
+ seedConversationAndMessage({
197
+ conversationId: "conv-1",
198
+ messageId: "msg-1",
199
+ source: "user",
200
+ conversationType: "standard",
201
+ totalEstimatedCost: 0.42,
202
+ });
203
+ getDb()
204
+ .insert(messages)
205
+ .values({
206
+ id: "msg-2",
207
+ conversationId: "conv-1",
208
+ role: "assistant",
209
+ content: "",
210
+ createdAt: Date.now() + 1,
211
+ metadata: null,
212
+ })
213
+ .run();
214
+ seedConversationAndMessage({
215
+ conversationId: "conv-other",
216
+ messageId: "msg-other",
217
+ source: "user",
218
+ conversationType: "standard",
219
+ });
220
+ seedConversationKey("conv-key", "conv-1");
221
+ seedRequestLog("msg-2", "log-b");
222
+ seedRequestLog("msg-1", "log-a");
223
+ getDb()
224
+ .insert(llmRequestLogs)
225
+ .values({
226
+ id: "log-other",
227
+ conversationId: "conv-other",
228
+ messageId: "msg-other",
229
+ provider: "openai",
230
+ requestPayload: JSON.stringify({ model: "gpt-4.1", messages: [] }),
231
+ responsePayload: JSON.stringify({
232
+ choices: [{ message: { content: "other" } }],
233
+ }),
234
+ createdAt: 1_700_000_000_001,
235
+ })
236
+ .run();
237
+
238
+ const body = (await dispatchConversationLlmContext({
239
+ conversationKey: "conv-key",
240
+ })) as {
241
+ conversationId: string;
242
+ conversationKey: string;
243
+ conversationKind: string;
244
+ conversationTotalEstimatedCostUsd: number | null;
245
+ logs: Array<{ id: string }>;
246
+ memoryRecall: null;
247
+ memoryV2Activation: null;
248
+ };
249
+
250
+ expect(body.conversationId).toBe("conv-1");
251
+ expect(body.conversationKey).toBe("conv-key");
252
+ expect(body.conversationKind).toBe("user");
253
+ expect(body.conversationTotalEstimatedCostUsd).toBe(0.42);
254
+ expect(body.logs.map((log) => log.id)).toEqual(["log-a", "log-b"]);
255
+ expect(body.memoryRecall).toBeNull();
256
+ expect(body.memoryV2Activation).toBeNull();
257
+ });
258
+
259
+ test("returns an empty inspector response for an unresolved conversation key", async () => {
260
+ const body = (await dispatchConversationLlmContext({
261
+ conversationKey: "missing-key",
262
+ })) as {
263
+ conversationId: string | null;
264
+ conversationKey: string;
265
+ logs: unknown[];
266
+ };
267
+
268
+ expect(body.conversationId).toBeNull();
269
+ expect(body.conversationKey).toBe("missing-key");
270
+ expect(body.logs).toEqual([]);
271
+ });
272
+ });
273
+
168
274
  describe("GET /v1/messages/:id/llm-context — memoryV2Activation", () => {
169
275
  beforeEach(() => {
170
276
  clearTables();
@@ -210,7 +210,11 @@ describe("handleSimulateRouter", () => {
210
210
  providerStub = makeProvider([3, 1]);
211
211
 
212
212
  const result = await handleSimulateRouter({
213
- body: { query: "what's relevant?" },
213
+ body: {
214
+ recentTurnPairs: [
215
+ { assistantMessage: "", userMessage: "what's relevant?" },
216
+ ],
217
+ },
214
218
  });
215
219
 
216
220
  expect(result.failureReason).toBeNull();
@@ -228,7 +232,7 @@ describe("handleSimulateRouter", () => {
228
232
 
229
233
  const result = await handleSimulateRouter({
230
234
  body: {
231
- query: "test",
235
+ recentTurnPairs: [{ assistantMessage: "", userMessage: "test" }],
232
236
  configOverrides: {
233
237
  tier1_size: 50,
234
238
  batch_size: 25,
@@ -249,22 +253,37 @@ describe("handleSimulateRouter", () => {
249
253
  providerStub = makeProvider([1, 2]);
250
254
 
251
255
  await handleSimulateRouter({
252
- body: { query: "should not record" },
256
+ body: {
257
+ recentTurnPairs: [
258
+ { assistantMessage: "", userMessage: "should not record" },
259
+ ],
260
+ },
253
261
  });
254
262
 
255
263
  expect(recordCalls).toEqual([]);
256
264
  });
257
265
 
258
- test("rejects an empty query at the schema layer", async () => {
266
+ test("rejects an empty last-pair userMessage at the schema layer", async () => {
259
267
  await expect(
260
- handleSimulateRouter({ body: { query: "" } }),
268
+ handleSimulateRouter({
269
+ body: { recentTurnPairs: [{ assistantMessage: "", userMessage: "" }] },
270
+ }),
271
+ ).rejects.toThrow();
272
+ });
273
+
274
+ test("rejects an empty recentTurnPairs array at the schema layer", async () => {
275
+ await expect(
276
+ handleSimulateRouter({ body: { recentTurnPairs: [] } }),
261
277
  ).rejects.toThrow();
262
278
  });
263
279
 
264
280
  test("rejects negative tier size at the schema layer", async () => {
265
281
  await expect(
266
282
  handleSimulateRouter({
267
- body: { query: "test", configOverrides: { tier1_size: -5 } },
283
+ body: {
284
+ recentTurnPairs: [{ assistantMessage: "", userMessage: "test" }],
285
+ configOverrides: { tier1_size: -5 },
286
+ },
268
287
  }),
269
288
  ).rejects.toThrow();
270
289
  });