@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
@@ -159,8 +159,8 @@ mock.module("../daemon/disk-pressure-policy.js", () => ({
159
159
  const updateMessageMetadataMock = mock(
160
160
  (_id: string, _updates: Record<string, unknown>) => {},
161
161
  );
162
- const clearStrippedInjectionMetadataForConversationMock = mock(
163
- (_conversationId: string) => {},
162
+ const setConversationHistoryStrippedAtMock = mock(
163
+ (_conversationId: string, _historyStrippedAt: number | null) => {},
164
164
  );
165
165
  const updateConversationSlackContextWatermarkMock = mock(
166
166
  (_conversationId: string, _watermarkTs: string, _compactedAt?: number) => {},
@@ -180,8 +180,7 @@ mock.module("../memory/conversation-crud.js", () => ({
180
180
  setConversationOriginChannelIfUnset: () => {},
181
181
  updateConversationUsage: () => {},
182
182
  updateMessageMetadata: updateMessageMetadataMock,
183
- clearStrippedInjectionMetadataForConversation:
184
- clearStrippedInjectionMetadataForConversationMock,
183
+ setConversationHistoryStrippedAt: setConversationHistoryStrippedAtMock,
185
184
  getMessages: () => [],
186
185
  getConversation: () => mockConversationRow,
187
186
  provenanceFromTrustContext: () => ({
@@ -684,10 +683,8 @@ beforeEach(() => {
684
683
  mockHasProactiveArtifactCompleted = true;
685
684
  mockTryClaimProactiveArtifactTrigger = false;
686
685
  runProactiveArtifactJobMock.mockClear();
687
- clearStrippedInjectionMetadataForConversationMock.mockClear();
688
- clearStrippedInjectionMetadataForConversationMock.mockImplementation(
689
- () => {},
690
- );
686
+ setConversationHistoryStrippedAtMock.mockClear();
687
+ setConversationHistoryStrippedAtMock.mockImplementation(() => {});
691
688
  applyRuntimeInjectionsMock.mockClear();
692
689
  buildUnifiedTurnContextBlockMock.mockClear();
693
690
  resolveTurnTimezoneContextMock.mockClear();
@@ -1218,20 +1215,8 @@ describe("session-agent-loop", () => {
1218
1215
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
1219
1216
 
1220
1217
  expect(recordRequestLogMock).toHaveBeenCalledTimes(1);
1221
- const call = recordRequestLogMock.mock.calls[0] as unknown as [
1222
- string,
1223
- string,
1224
- string,
1225
- undefined,
1226
- string,
1227
- ];
1228
- expect(call).toEqual([
1229
- "test-conv",
1230
- JSON.stringify(rawRequest),
1231
- JSON.stringify(rawResponse),
1232
- undefined,
1233
- "fireworks",
1234
- ]);
1218
+ const call = recordRequestLogMock.mock.calls[0] as unknown as unknown[];
1219
+ expect(call[4]).toBe("fireworks");
1235
1220
  });
1236
1221
 
1237
1222
  test("record request log falls back to the runtime provider when no actual provider is supplied", async () => {
@@ -1377,20 +1362,9 @@ describe("session-agent-loop", () => {
1377
1362
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
1378
1363
 
1379
1364
  expect(recordRequestLogMock).toHaveBeenCalledTimes(1);
1380
- const call = recordRequestLogMock.mock.calls[0] as unknown as [
1381
- string,
1382
- string,
1383
- string,
1384
- undefined,
1385
- string,
1386
- ];
1387
- expect(call).toEqual([
1388
- "test-conv",
1389
- JSON.stringify(rawRequest),
1390
- JSON.stringify(rawResponse),
1391
- undefined,
1392
- "openai",
1393
- ]);
1365
+ const call = recordRequestLogMock.mock.calls[0] as unknown as unknown[];
1366
+ expect(call[1]).toBe(JSON.stringify(rawRequest));
1367
+ expect(call[2]).toBe(JSON.stringify(rawResponse));
1394
1368
  });
1395
1369
  });
1396
1370
 
@@ -2042,6 +2016,154 @@ describe("session-agent-loop", () => {
2042
2016
  expect(complete).toBeDefined();
2043
2017
  });
2044
2018
 
2019
+ test("emits budget_yield_unrecovered when auto_compress rerun yields at mid-loop budget", async () => {
2020
+ // Regression test for the silent-stall failure mode:
2021
+ // when every recovery layer has been applied (tier reducer
2022
+ // exhausted + auto_compress_latest_turn emergency compaction)
2023
+ // and the final `agentLoop.run` STILL yields at the mid-loop
2024
+ // budget checkpoint, the orchestrator used to fall through to
2025
+ // post-turn cleanup with NO `agent_loop_exit_reason` emitted —
2026
+ // the turn just stopped mid-action and `llm_request_logs` showed
2027
+ // a NULL exit reason on the final row. We now emit
2028
+ // `budget_yield_unrecovered` so the inspector and dashboards can
2029
+ // attribute the silent stall.
2030
+ const events: ServerMessage[] = [];
2031
+ let callCount = 0;
2032
+
2033
+ // Reducer exhausts all 4 tiers on first call so the convergence
2034
+ // loop runs exactly one iteration before falling through to
2035
+ // the auto_compress_latest_turn branch.
2036
+ mockReducerStepFn = (msgs: Message[]) => ({
2037
+ messages: msgs,
2038
+ tier: "injection_downgrade",
2039
+ state: {
2040
+ appliedTiers: [
2041
+ "forced_compaction",
2042
+ "tool_result_truncation",
2043
+ "media_stubbing",
2044
+ "injection_downgrade",
2045
+ ],
2046
+ injectionMode: "minimal",
2047
+ exhausted: true,
2048
+ },
2049
+ estimatedTokens: 120_000,
2050
+ });
2051
+
2052
+ mockOverflowAction = "auto_compress_latest_turn";
2053
+
2054
+ // Sits between the preflight budget (preflightBudget =
2055
+ // 100k * 0.95 = 95k — anything above triggers preflight reducer
2056
+ // *before* we get to the convergence/auto_compress path under
2057
+ // test) and the mid-loop threshold (preflightBudget * 0.85 =
2058
+ // ≈80.75k — anything above flips yieldedForBudget on a checkpoint
2059
+ // call). 90k satisfies both so the path reaches call 3.
2060
+ mockEstimateTokens = 90_000;
2061
+
2062
+ const agentLoopRun: AgentLoopRun = async (
2063
+ messages,
2064
+ onEvent,
2065
+ _signal,
2066
+ _reqId,
2067
+ onCheckpoint,
2068
+ ) => {
2069
+ callCount++;
2070
+ if (callCount <= 2) {
2071
+ // Calls 1 (initial) and 2 (convergence rerun): error so
2072
+ // `state.contextTooLargeDetected` stays true through
2073
+ // convergence exit and we enter the auto_compress branch.
2074
+ onEvent({
2075
+ type: "error",
2076
+ error: new Error("context_length_exceeded"),
2077
+ });
2078
+ onEvent({
2079
+ type: "usage",
2080
+ inputTokens: 100,
2081
+ outputTokens: 0,
2082
+ model: "test-model",
2083
+ providerDurationMs: 50,
2084
+ });
2085
+ return messages;
2086
+ }
2087
+ // Call 3: the auto_compress_latest_turn rerun. Invoke
2088
+ // onCheckpoint so the orchestrator's mid-loop budget check
2089
+ // flips `yieldedForBudget` to true, then return without
2090
+ // finishing — mirroring what AgentLoop.run does when its
2091
+ // checkpoint returns "yield".
2092
+ if (onCheckpoint) {
2093
+ await onCheckpoint({
2094
+ turnIndex: 0,
2095
+ toolCount: 1,
2096
+ hasToolUse: true,
2097
+ history: messages,
2098
+ });
2099
+ }
2100
+ return messages;
2101
+ };
2102
+
2103
+ const ctx = makeCtx({
2104
+ agentLoopRun,
2105
+ hasNoClient: true,
2106
+ contextWindowManager: {
2107
+ shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
2108
+ maybeCompact: async () => ({
2109
+ compacted: true,
2110
+ messages: [
2111
+ { role: "user", content: [{ type: "text", text: "Hello" }] },
2112
+ ] as Message[],
2113
+ compactedPersistedMessages: 3,
2114
+ summaryText: "Compressed summary",
2115
+ previousEstimatedInputTokens: 120000,
2116
+ estimatedInputTokens: 30000,
2117
+ maxInputTokens: 100000,
2118
+ thresholdTokens: 80000,
2119
+ compactedMessages: 5,
2120
+ summaryCalls: 1,
2121
+ summaryInputTokens: 300,
2122
+ summaryOutputTokens: 100,
2123
+ summaryModel: "mock-model",
2124
+ }),
2125
+ } as unknown as AgentLoopConversationContext["contextWindowManager"],
2126
+ });
2127
+
2128
+ await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
2129
+
2130
+ // Observability emit: exit reason was stamped onto the latest
2131
+ // llm_request_logs row.
2132
+ expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
2133
+ "test-conv",
2134
+ "budget_yield_unrecovered",
2135
+ );
2136
+
2137
+ // We did NOT also emit context_too_large — the auto_compress
2138
+ // branch resets `contextTooLargeDetected` before its rerun and
2139
+ // the rerun's yield-for-budget keeps it false, so the error
2140
+ // branch above stays skipped.
2141
+ expect(setAgentLoopExitReasonOnLatestLogMock).not.toHaveBeenCalledWith(
2142
+ "test-conv",
2143
+ "context_too_large",
2144
+ );
2145
+
2146
+ // User-facing emit: the classified BUDGET_YIELD_UNRECOVERED
2147
+ // error is sent to the client so the UI can render a notice
2148
+ // instead of leaving the turn looking like a silent ghost. The
2149
+ // assistant-side notice persistence is exercised in the overflow
2150
+ // suite (`conversation-agent-loop-overflow.test.ts`); this test
2151
+ // owns the observability + emit contract.
2152
+ const conversationError = events.find(
2153
+ (e) => e.type === "conversation_error",
2154
+ );
2155
+ expect(conversationError).toBeDefined();
2156
+ if (conversationError && "code" in conversationError) {
2157
+ expect(conversationError.code).toBe("BUDGET_YIELD_UNRECOVERED");
2158
+ expect(conversationError.retryable).toBe(true);
2159
+ expect(conversationError.errorCategory).toBe(
2160
+ "budget_yield_unrecovered",
2161
+ );
2162
+ } else {
2163
+ throw new Error("conversation_error missing `code` field");
2164
+ }
2165
+ });
2166
+
2045
2167
  test("recovery loop is bounded by maxAttempts", async () => {
2046
2168
  const events: ServerMessage[] = [];
2047
2169
  let reducerCalls = 0;
@@ -3769,8 +3891,8 @@ describe("session-agent-loop", () => {
3769
3891
  });
3770
3892
  });
3771
3893
 
3772
- describe("compaction-strip metadata consistency", () => {
3773
- test("clears pkbSystemReminderBlock metadata when convergence strip runs", async () => {
3894
+ describe("compaction-strip marker persistence", () => {
3895
+ test("records historyStrippedAt when convergence strip runs", async () => {
3774
3896
  // Reducer: succeed on first call, returning reduced messages.
3775
3897
  mockReducerStepFn = (msgs: Message[]) => ({
3776
3898
  messages: msgs,
@@ -3841,91 +3963,10 @@ describe("session-agent-loop", () => {
3841
3963
 
3842
3964
  await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
3843
3965
 
3844
- // The bulk-clear helper must have been called with the conversation id
3845
- // at least once (one of the three strip sites fired).
3846
- const clearCalls =
3847
- clearStrippedInjectionMetadataForConversationMock.mock.calls.filter(
3848
- (call) => call[0] === "test-conv",
3849
- );
3850
- expect(clearCalls.length).toBeGreaterThanOrEqual(1);
3851
- });
3852
-
3853
- test("strip-site clear is non-fatal when the helper throws", async () => {
3854
- clearStrippedInjectionMetadataForConversationMock.mockImplementation(
3855
- () => {
3856
- throw new Error("db write failed");
3857
- },
3966
+ const stripCalls = setConversationHistoryStrippedAtMock.mock.calls.filter(
3967
+ (call) => call[0] === "test-conv",
3858
3968
  );
3859
-
3860
- mockReducerStepFn = (msgs: Message[]) => ({
3861
- messages: msgs,
3862
- tier: "forced_compaction",
3863
- state: {
3864
- appliedTiers: ["forced_compaction"],
3865
- injectionMode: "full",
3866
- exhausted: false,
3867
- },
3868
- estimatedTokens: 5000,
3869
- });
3870
-
3871
- let callCount = 0;
3872
- const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
3873
- callCount++;
3874
- if (callCount === 1) {
3875
- onEvent({
3876
- type: "error",
3877
- error: new Error("context_length_exceeded"),
3878
- });
3879
- onEvent({
3880
- type: "usage",
3881
- inputTokens: 100,
3882
- outputTokens: 0,
3883
- model: "test-model",
3884
- providerDurationMs: 50,
3885
- });
3886
- return [
3887
- ...messages,
3888
- {
3889
- role: "assistant" as const,
3890
- content: [{ type: "text", text: "partial" }] as ContentBlock[],
3891
- },
3892
- ];
3893
- }
3894
- onEvent({
3895
- type: "message_complete",
3896
- message: {
3897
- role: "assistant",
3898
- content: [{ type: "text", text: "recovered" }],
3899
- },
3900
- });
3901
- onEvent({
3902
- type: "usage",
3903
- inputTokens: 50,
3904
- outputTokens: 25,
3905
- model: "test-model",
3906
- providerDurationMs: 100,
3907
- });
3908
- return [
3909
- ...messages,
3910
- {
3911
- role: "assistant" as const,
3912
- content: [{ type: "text", text: "recovered" }] as ContentBlock[],
3913
- },
3914
- ];
3915
- };
3916
-
3917
- const ctx = makeCtx({
3918
- agentLoopRun,
3919
- contextWindowManager: {
3920
- shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
3921
- maybeCompact: async () => ({ compacted: false }),
3922
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
3923
- });
3924
-
3925
- // Must not throw — the strip-site clear is wrapped in try/catch.
3926
- await expect(
3927
- runAgentLoopImpl(ctx, "hello", "msg-1", () => {}),
3928
- ).resolves.toBeUndefined();
3969
+ expect(stripCalls.length).toBeGreaterThanOrEqual(1);
3929
3970
  });
3930
3971
  });
3931
3972
  });
@@ -110,15 +110,12 @@ mock.module("../tools/skills/skill-tool-factory.js", () => ({
110
110
  description: entry.description,
111
111
  category: entry.category,
112
112
  defaultRiskLevel: RiskLevel.Medium,
113
+ executionTarget: "sandbox" as const,
113
114
  origin: "skill" as const,
114
115
  ownerSkillId: skillId,
115
116
  ownerSkillVersionHash: versionHash,
116
117
  ownerSkillBundled: bundled ?? undefined,
117
- getDefinition: () => ({
118
- name: entry.name,
119
- description: entry.description,
120
- input_schema: entry.input_schema as object,
121
- }),
118
+ input_schema: entry.input_schema as object,
122
119
  execute: async () => ({ content: "", isError: false }),
123
120
  })),
124
121
  }));
@@ -27,8 +27,8 @@ mock.module("../config/env.js", () => ({
27
27
 
28
28
  mock.module("../daemon/handlers/conversations.js", () => ({
29
29
  cancelGeneration: () => true,
30
- clearAllConversations: () => {
31
- clearAll();
30
+ clearAllConversations: async () => {
31
+ await clearAll();
32
32
  return 0;
33
33
  },
34
34
  switchConversation: async () => null,
@@ -129,26 +129,28 @@ describe("DELETE /v1/conversations — route handler", () => {
129
129
  (r) => r.operationId === "clearAllConversations",
130
130
  )!;
131
131
 
132
- test("missing X-Confirm-Destructive header throws BadRequestError", () => {
133
- expect(() =>
134
- clearRoute.handler({
135
- pathParams: {},
136
- body: {},
137
- headers: {},
138
-
139
- }),
140
- ).toThrow(BadRequestError);
132
+ test("missing X-Confirm-Destructive header throws BadRequestError", async () => {
133
+ await expect(
134
+ Promise.resolve().then(() =>
135
+ clearRoute.handler({
136
+ pathParams: {},
137
+ body: {},
138
+ headers: {},
139
+ }),
140
+ ),
141
+ ).rejects.toThrow(BadRequestError);
141
142
  });
142
143
 
143
- test("wrong X-Confirm-Destructive header value throws BadRequestError", () => {
144
- expect(() =>
145
- clearRoute.handler({
146
- pathParams: {},
147
- body: {},
148
- headers: { "x-confirm-destructive": "wrong-value" },
149
-
150
- }),
151
- ).toThrow(BadRequestError);
144
+ test("wrong X-Confirm-Destructive header value throws BadRequestError", async () => {
145
+ await expect(
146
+ Promise.resolve().then(() =>
147
+ clearRoute.handler({
148
+ pathParams: {},
149
+ body: {},
150
+ headers: { "x-confirm-destructive": "wrong-value" },
151
+ }),
152
+ ),
153
+ ).rejects.toThrow(BadRequestError);
152
154
  });
153
155
 
154
156
  test("correct header clears data", async () => {
@@ -156,23 +158,21 @@ describe("DELETE /v1/conversations — route handler", () => {
156
158
  await addMessage(conv.id, "user", "hello from safety test");
157
159
  expect(getConversation(conv.id)).not.toBeNull();
158
160
 
159
- const result = clearRoute.handler({
161
+ const result = await clearRoute.handler({
160
162
  pathParams: {},
161
163
  body: {},
162
164
  headers: { "x-confirm-destructive": "clear-all-conversations" },
163
-
164
165
  });
165
166
  expect(result).toBeUndefined();
166
167
 
167
168
  expect(getConversation(conv.id)).toBeNull();
168
169
  });
169
170
 
170
- test("lifecycle_events contains conversations_clear_all after successful clear", () => {
171
- clearRoute.handler({
171
+ test("lifecycle_events contains conversations_clear_all after successful clear", async () => {
172
+ await clearRoute.handler({
172
173
  pathParams: {},
173
174
  body: {},
174
175
  headers: { "x-confirm-destructive": "clear-all-conversations" },
175
-
176
176
  });
177
177
 
178
178
  const raw = (
@@ -23,7 +23,7 @@ mock.module("../daemon/conversation-store.js", () => ({
23
23
 
24
24
  mock.module("../daemon/handlers/conversations.js", () => ({
25
25
  cancelGeneration: () => true,
26
- clearAllConversations: () => 0,
26
+ clearAllConversations: async () => 0,
27
27
  switchConversation: async () => null,
28
28
  undoLastMessage: async () => null,
29
29
  regenerateResponse: async () => null,
@@ -231,7 +231,7 @@ describe("clearAll → disk view", () => {
231
231
  resetConversationsDir();
232
232
  });
233
233
 
234
- test("empties the conversations directory", () => {
234
+ test("empties the conversations directory", async () => {
235
235
  // Create two conversations
236
236
  createConversation("Conv A");
237
237
  createConversation("Conv B");
@@ -241,7 +241,7 @@ describe("clearAll → disk view", () => {
241
241
  expect(entries.length).toBe(2);
242
242
 
243
243
  // Clear all
244
- clearAll();
244
+ await clearAll();
245
245
 
246
246
  // Conversations directory should exist but be empty
247
247
  expect(existsSync(conversationsDir)).toBe(true);
@@ -9,6 +9,7 @@ mock.module("../providers/registry.js", () => ({
9
9
 
10
10
  import type { ErrorContext } from "../daemon/conversation-error.js";
11
11
  import {
12
+ budgetYieldUnrecoveredClassification,
12
13
  buildConversationErrorMessage,
13
14
  classifyConversationError,
14
15
  isUserCancellation,
@@ -947,3 +948,33 @@ describe("buildConversationErrorMessage", () => {
947
948
  expect(msg.code).toBe("PROVIDER_WEB_SEARCH");
948
949
  });
949
950
  });
951
+
952
+ describe("budgetYieldUnrecoveredClassification", () => {
953
+ it("returns the BUDGET_YIELD_UNRECOVERED code and retryable=true", () => {
954
+ const classified = budgetYieldUnrecoveredClassification();
955
+ expect(classified.code).toBe("BUDGET_YIELD_UNRECOVERED");
956
+ expect(classified.retryable).toBe(true);
957
+ expect(classified.errorCategory).toBe("budget_yield_unrecovered");
958
+ });
959
+
960
+ it("returns a user-facing message that explains the situation", () => {
961
+ const classified = budgetYieldUnrecoveredClassification();
962
+ // The message must communicate (a) compaction was attempted, (b) send
963
+ // another message to continue. Avoid asserting exact wording so copy
964
+ // tweaks don't trip the test, but lock down the two semantic anchors
965
+ // a downstream client could test against.
966
+ expect(classified.userMessage).toContain("compact");
967
+ expect(classified.userMessage).toContain("Send another message");
968
+ });
969
+
970
+ it("survives the buildConversationErrorMessage envelope unchanged", () => {
971
+ const classified = budgetYieldUnrecoveredClassification();
972
+ const envelope = buildConversationErrorMessage("conv-1", classified);
973
+ expect(envelope.type).toBe("conversation_error");
974
+ expect(envelope.conversationId).toBe("conv-1");
975
+ expect(envelope.code).toBe("BUDGET_YIELD_UNRECOVERED");
976
+ expect(envelope.retryable).toBe(true);
977
+ expect(envelope.errorCategory).toBe("budget_yield_unrecovered");
978
+ expect(envelope.userMessage).toBe(classified.userMessage);
979
+ });
980
+ });