@vellumai/assistant 0.8.3 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (665) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docker-entrypoint.sh +0 -1
  3. package/docs/browser-use-architecture-phase2.md +1 -1
  4. package/knip.json +2 -1
  5. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  6. package/openapi.yaml +1492 -100
  7. package/package.json +1 -1
  8. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  9. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +302 -33
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  14. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  15. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  16. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
  17. package/src/__tests__/btw-routes.test.ts +2 -3
  18. package/src/__tests__/call-controller.test.ts +0 -1
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  20. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  21. package/src/__tests__/channel-guardian.test.ts +3 -3
  22. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  23. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  24. package/src/__tests__/checker.test.ts +6 -15
  25. package/src/__tests__/compaction-events.test.ts +2 -1
  26. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  27. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  29. package/src/__tests__/computer-use-tools.test.ts +2 -4
  30. package/src/__tests__/config-watcher.test.ts +1 -1
  31. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  32. package/src/__tests__/context-token-estimator.test.ts +91 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
  37. package/src/__tests__/conversation-agent-loop.test.ts +188 -129
  38. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  39. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  40. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  41. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  42. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  43. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  44. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  45. package/src/__tests__/conversation-error.test.ts +31 -0
  46. package/src/__tests__/conversation-fork-crud.test.ts +324 -0
  47. package/src/__tests__/conversation-lifecycle.test.ts +53 -12
  48. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  49. package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
  50. package/src/__tests__/conversation-pairing.test.ts +2 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
  53. package/src/__tests__/conversation-queue.test.ts +1 -1
  54. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  57. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  58. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  59. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  60. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  61. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  62. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  63. package/src/__tests__/conversation-store.test.ts +1 -1
  64. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  65. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
  67. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  69. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  70. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  71. package/src/__tests__/credential-security-invariants.test.ts +7 -0
  72. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  73. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  74. package/src/__tests__/dm-backfill.test.ts +64 -0
  75. package/src/__tests__/dm-persistence.test.ts +33 -0
  76. package/src/__tests__/document-find-replace.test.ts +501 -0
  77. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  78. package/src/__tests__/email-html-renderer.test.ts +12 -0
  79. package/src/__tests__/first-greeting.test.ts +23 -2
  80. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  81. package/src/__tests__/gemini-provider.test.ts +78 -0
  82. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  83. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  84. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  85. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  87. package/src/__tests__/heartbeat-service.test.ts +4 -0
  88. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  89. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  90. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  91. package/src/__tests__/host-file-proxy.test.ts +8 -1
  92. package/src/__tests__/host-shell-tool.test.ts +1 -1
  93. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  94. package/src/__tests__/identity-routes.test.ts +57 -0
  95. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  96. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  97. package/src/__tests__/injector-chain.test.ts +2 -0
  98. package/src/__tests__/injector-document-comments.test.ts +378 -0
  99. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  100. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  102. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  103. package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
  104. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  105. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  106. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  107. package/src/__tests__/llm-resolver.test.ts +161 -9
  108. package/src/__tests__/llm-usage-store.test.ts +66 -0
  109. package/src/__tests__/log-export-routes.test.ts +99 -2
  110. package/src/__tests__/logger.test.ts +89 -0
  111. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  112. package/src/__tests__/media-generate-image.test.ts +31 -0
  113. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  114. package/src/__tests__/message-queue-steer.test.ts +114 -0
  115. package/src/__tests__/model-intents.test.ts +2 -4
  116. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  117. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  118. package/src/__tests__/openai-provider.test.ts +151 -0
  119. package/src/__tests__/openai-responses-provider.test.ts +118 -16
  120. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  121. package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
  122. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  123. package/src/__tests__/platform.test.ts +2 -5
  124. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  125. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  126. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  127. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  128. package/src/__tests__/plugin-types.test.ts +3 -2
  129. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  130. package/src/__tests__/pricing.test.ts +12 -0
  131. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  132. package/src/__tests__/process-message-display-content.test.ts +21 -16
  133. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  134. package/src/__tests__/registry.test.ts +2 -8
  135. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  136. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  137. package/src/__tests__/server-history-render.test.ts +83 -4
  138. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  139. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  140. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  141. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  142. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  143. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  144. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  145. package/src/__tests__/suggestion-routes.test.ts +1 -0
  146. package/src/__tests__/sync-message-contract.test.ts +59 -0
  147. package/src/__tests__/system-prompt.test.ts +161 -124
  148. package/src/__tests__/terminal-tools.test.ts +12 -2
  149. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  150. package/src/__tests__/thread-backfill.test.ts +370 -22
  151. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  152. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  153. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  155. package/src/__tests__/tool-executor.test.ts +89 -53
  156. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  157. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  158. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  159. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  160. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  161. package/src/__tests__/twilio-routes.test.ts +1 -1
  162. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  163. package/src/__tests__/usage-routes.test.ts +3 -0
  164. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  165. package/src/__tests__/web-fetch.test.ts +2 -2
  166. package/src/__tests__/workspace-git-service.test.ts +94 -10
  167. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  168. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  169. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  170. package/src/acp/prepare-agent-env.ts +78 -0
  171. package/src/acp/session-manager.ts +1 -1
  172. package/src/agent/attachments.ts +1 -0
  173. package/src/agent/loop.ts +65 -20
  174. package/src/api/README.md +5 -0
  175. package/src/api/index.ts +4 -0
  176. package/src/api/package.json +10 -0
  177. package/src/background-wake/background-wake-routes.test.ts +233 -0
  178. package/src/background-wake/next-wake.test.ts +289 -0
  179. package/src/background-wake/next-wake.ts +172 -0
  180. package/src/background-wake/runtime-registry.ts +24 -0
  181. package/src/browser/operations.ts +15 -0
  182. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  183. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  184. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  185. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  186. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  187. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  188. package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
  189. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  190. package/src/cli/commands/browser.ts +247 -0
  191. package/src/cli/commands/conversations.ts +128 -1
  192. package/src/cli/commands/domain.ts +91 -41
  193. package/src/cli/commands/inference-providers.ts +147 -1
  194. package/src/cli/commands/inference.ts +93 -40
  195. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  196. package/src/cli/commands/memory-v2.ts +483 -0
  197. package/src/cli/commands/memory-v3-render.ts +344 -0
  198. package/src/cli/commands/memory-v3.ts +316 -0
  199. package/src/cli/commands/notifications.ts +24 -2
  200. package/src/cli/program.ts +2 -0
  201. package/src/cli/utils/conversation-id.ts +17 -5
  202. package/src/config/assistant-feature-flags.ts +21 -9
  203. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  204. package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
  205. package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
  206. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  207. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  208. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  209. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  210. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  211. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  212. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  213. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  214. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  215. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  216. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  217. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  218. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  219. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  220. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  221. package/src/config/bundled-tool-registry.ts +24 -12
  222. package/src/config/call-site-defaults.ts +20 -0
  223. package/src/config/feature-flag-registry.json +115 -3
  224. package/src/config/llm-resolver.ts +16 -2
  225. package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
  226. package/src/config/schemas/call-site-catalog.ts +35 -0
  227. package/src/config/schemas/llm.ts +14 -0
  228. package/src/config/schemas/memory-v2.ts +294 -1
  229. package/src/config/schemas/memory.ts +2 -1
  230. package/src/context/compactor.ts +60 -1
  231. package/src/context/token-estimator.ts +47 -4
  232. package/src/context/window-manager.ts +25 -0
  233. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  234. package/src/conversations/message-consolidation.ts +404 -0
  235. package/src/credential-health/credential-health-service.ts +34 -19
  236. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  237. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  238. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  239. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  240. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  241. package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
  242. package/src/daemon/conversation-agent-loop.ts +307 -88
  243. package/src/daemon/conversation-error.ts +31 -1
  244. package/src/daemon/conversation-lifecycle.ts +149 -118
  245. package/src/daemon/conversation-messaging.ts +3 -0
  246. package/src/daemon/conversation-process.ts +273 -0
  247. package/src/daemon/conversation-queue-manager.ts +14 -0
  248. package/src/daemon/conversation-runtime-assembly.ts +145 -84
  249. package/src/daemon/conversation-slash.ts +37 -5
  250. package/src/daemon/conversation-surfaces.ts +45 -2
  251. package/src/daemon/conversation-tool-setup.ts +70 -3
  252. package/src/daemon/conversation-usage.ts +2 -0
  253. package/src/daemon/conversation.ts +54 -32
  254. package/src/daemon/disk-pressure-guard.ts +14 -2
  255. package/src/daemon/first-greeting.ts +10 -0
  256. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  257. package/src/daemon/handlers/config-a2a.ts +160 -0
  258. package/src/daemon/handlers/config-model.test.ts +2 -0
  259. package/src/daemon/handlers/conversations.ts +90 -3
  260. package/src/daemon/handlers/shared.ts +92 -29
  261. package/src/daemon/host-bash-proxy.ts +1 -1
  262. package/src/daemon/host-browser-proxy.ts +5 -5
  263. package/src/daemon/host-cu-proxy.ts +5 -5
  264. package/src/daemon/host-file-proxy.ts +5 -5
  265. package/src/daemon/host-proxy-base.ts +4 -4
  266. package/src/daemon/host-transfer-proxy.ts +11 -11
  267. package/src/daemon/lifecycle.ts +40 -23
  268. package/src/daemon/meet-manifest-loader.ts +1 -7
  269. package/src/daemon/message-protocol.ts +4 -0
  270. package/src/daemon/message-types/conversations.ts +14 -9
  271. package/src/daemon/message-types/document-comments.ts +50 -0
  272. package/src/daemon/message-types/home.ts +1 -13
  273. package/src/daemon/message-types/messages.ts +66 -7
  274. package/src/daemon/message-types/surfaces.ts +3 -1
  275. package/src/daemon/message-types/sync.ts +14 -0
  276. package/src/daemon/message-types/web-activity.ts +57 -0
  277. package/src/daemon/plugin-source-watcher.ts +135 -3
  278. package/src/daemon/process-message.ts +69 -12
  279. package/src/daemon/shutdown-handlers.ts +24 -5
  280. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  281. package/src/daemon/tool-setup-types.ts +13 -0
  282. package/src/daemon/trust-context.ts +6 -0
  283. package/src/documents/document-comments-store.test.ts +338 -0
  284. package/src/documents/document-comments-store.ts +237 -0
  285. package/src/documents/document-store.ts +202 -0
  286. package/src/events/relationship-state-updated.ts +25 -0
  287. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
  288. package/src/heartbeat/heartbeat-service.ts +1 -0
  289. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  290. package/src/home/feed-types.ts +6 -1
  291. package/src/home/home-content-refresh.ts +52 -0
  292. package/src/home/home-greeting-cache.ts +69 -0
  293. package/src/home/home-greeting.ts +85 -0
  294. package/src/home/suggested-prompts.ts +168 -9
  295. package/src/ipc/gateway-flag-listener.ts +123 -0
  296. package/src/ipc/skill-routes/registries.ts +8 -12
  297. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  298. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  299. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  300. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  301. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  302. package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
  303. package/src/memory/auto-analysis-enqueue.ts +5 -1
  304. package/src/memory/conversation-crud.ts +191 -100
  305. package/src/memory/conversation-starters-cadence.ts +3 -1
  306. package/src/memory/conversation-title-service.ts +19 -3
  307. package/src/memory/db-async-query.ts +214 -0
  308. package/src/memory/db-init.ts +26 -0
  309. package/src/memory/db-maintenance.ts +30 -21
  310. package/src/memory/delivery-crud.ts +41 -0
  311. package/src/memory/delivery-status.ts +141 -15
  312. package/src/memory/external-conversation-store.ts +32 -1
  313. package/src/memory/graph/bootstrap.ts +8 -1
  314. package/src/memory/graph/capability-seed.ts +7 -3
  315. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  316. package/src/memory/graph/extraction.ts +1 -5
  317. package/src/memory/graph/graph-search.ts +7 -1
  318. package/src/memory/indexer.ts +28 -18
  319. package/src/memory/job-handlers/cleanup.ts +76 -18
  320. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  321. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  322. package/src/memory/jobs-store.ts +14 -0
  323. package/src/memory/jobs-worker.ts +68 -15
  324. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  325. package/src/memory/llm-request-log-source-local.ts +7 -0
  326. package/src/memory/llm-request-log-source.ts +9 -2
  327. package/src/memory/llm-request-log-store.ts +43 -1
  328. package/src/memory/llm-usage-store.ts +24 -0
  329. package/src/memory/memory-retrospective-constants.ts +28 -0
  330. package/src/memory/memory-retrospective-enqueue.ts +11 -3
  331. package/src/memory/memory-retrospective-job.ts +413 -18
  332. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  333. package/src/memory/memory-v2-activation-log-store.ts +41 -14
  334. package/src/memory/migrations/100-core-tables.ts +1 -0
  335. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  336. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  337. package/src/memory/migrations/253-document-comments.ts +47 -0
  338. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  339. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  340. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  341. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  342. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  343. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  344. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  345. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  346. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  347. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  348. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  349. package/src/memory/migrations/index.ts +34 -0
  350. package/src/memory/migrations/registry.ts +58 -0
  351. package/src/memory/onboarding-events-store.ts +7 -0
  352. package/src/memory/schema/calls.ts +1 -0
  353. package/src/memory/schema/conversations.ts +3 -0
  354. package/src/memory/schema/infrastructure.ts +22 -0
  355. package/src/memory/tool-usage-store.ts +36 -8
  356. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  357. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  358. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  359. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  362. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  363. package/src/memory/v2/__tests__/injection.test.ts +158 -112
  364. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  365. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  366. package/src/memory/v2/__tests__/router.test.ts +660 -4
  367. package/src/memory/v2/consolidation-job.ts +14 -0
  368. package/src/memory/v2/harness/compare.ts +57 -0
  369. package/src/memory/v2/harness/metrics.ts +124 -0
  370. package/src/memory/v2/harness/oracle.ts +145 -0
  371. package/src/memory/v2/harness/replay-input.ts +224 -0
  372. package/src/memory/v2/harness/retriever.ts +74 -0
  373. package/src/memory/v2/harness/router-retriever.ts +43 -0
  374. package/src/memory/v2/harness/runner.ts +106 -0
  375. package/src/memory/v2/harness/trace.ts +58 -0
  376. package/src/memory/v2/injection-events.ts +101 -0
  377. package/src/memory/v2/injection.ts +42 -25
  378. package/src/memory/v2/page-index.ts +209 -7
  379. package/src/memory/v2/page-store.ts +18 -0
  380. package/src/memory/v2/prompts/router.ts +26 -1
  381. package/src/memory/v2/qdrant.ts +14 -2
  382. package/src/memory/v2/router.ts +369 -62
  383. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  384. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  385. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  386. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  387. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  388. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  389. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  390. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  391. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  392. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  393. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  394. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  395. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  396. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  397. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  398. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  399. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  400. package/src/memory/v3/auto-edges.ts +223 -0
  401. package/src/memory/v3/coactivation-store.ts +124 -0
  402. package/src/memory/v3/consolidation-job.ts +323 -0
  403. package/src/memory/v3/edge-learning-job.ts +160 -0
  404. package/src/memory/v3/edges.ts +249 -0
  405. package/src/memory/v3/filter.ts +281 -0
  406. package/src/memory/v3/gate.ts +334 -0
  407. package/src/memory/v3/index-composition.ts +113 -0
  408. package/src/memory/v3/llm-capture.ts +46 -0
  409. package/src/memory/v3/loop.ts +382 -0
  410. package/src/memory/v3/maintenance.ts +144 -0
  411. package/src/memory/v3/prompt-context.ts +33 -0
  412. package/src/memory/v3/prompts/consolidation.ts +458 -0
  413. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  414. package/src/memory/v3/retriever.ts +33 -0
  415. package/src/memory/v3/scouts.ts +420 -0
  416. package/src/memory/v3/shadow-middleware.ts +305 -0
  417. package/src/memory/v3/traversal.ts +206 -0
  418. package/src/memory/v3/tree-index.ts +237 -0
  419. package/src/memory/v3/tree-store.ts +394 -0
  420. package/src/memory/v3/tree-walk.ts +351 -0
  421. package/src/memory/v3/types.ts +65 -0
  422. package/src/memory/v3/validate.ts +300 -0
  423. package/src/messaging/providers/index.ts +7 -1
  424. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  425. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  426. package/src/messaging/providers/slack/adapter.ts +178 -25
  427. package/src/messaging/providers/slack/api.test.ts +54 -0
  428. package/src/messaging/providers/slack/api.ts +119 -3
  429. package/src/messaging/providers/slack/client.ts +12 -0
  430. package/src/messaging/providers/slack/deep-link.ts +20 -1
  431. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  432. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  433. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  434. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  435. package/src/messaging/providers/slack/send.test.ts +77 -0
  436. package/src/messaging/providers/slack/send.ts +8 -2
  437. package/src/messaging/providers/slack/types.ts +14 -0
  438. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  439. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  440. package/src/notifications/adapters/macos.ts +18 -1
  441. package/src/notifications/adapters/platform.ts +1 -1
  442. package/src/notifications/conversation-seed-composer.ts +14 -2
  443. package/src/notifications/decision-engine.ts +1 -4
  444. package/src/notifications/deferred-emit.ts +135 -0
  445. package/src/notifications/emit-signal.ts +38 -50
  446. package/src/notifications/home-feed-side-effect.ts +60 -30
  447. package/src/oauth/connect-orchestrator.ts +3 -0
  448. package/src/oauth/credential-token-resolver.ts +2 -0
  449. package/src/oauth/manual-token-connection.ts +19 -0
  450. package/src/oauth/oauth-store.ts +12 -0
  451. package/src/oauth/seed-providers.ts +22 -0
  452. package/src/permissions/prompter.ts +8 -5
  453. package/src/permissions/question-prompter.ts +5 -2
  454. package/src/permissions/secret-prompter.ts +6 -3
  455. package/src/plugin-api/index.ts +4 -0
  456. package/src/plugin-api/types.ts +7 -33
  457. package/src/plugins/defaults/index.ts +6 -0
  458. package/src/plugins/defaults/injectors.ts +100 -20
  459. package/src/plugins/external-plugin-loader.ts +5 -68
  460. package/src/plugins/types.ts +11 -16
  461. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  462. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  463. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  464. package/src/prompts/normalize-onboarding.ts +40 -0
  465. package/src/prompts/persona-resolver.ts +36 -21
  466. package/src/prompts/sections.ts +69 -19
  467. package/src/prompts/system-prompt.ts +118 -216
  468. package/src/prompts/template-detection.ts +37 -0
  469. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  470. package/src/prompts/templates/BOOTSTRAP.md +10 -2
  471. package/src/prompts/templates/VOICE.md +3 -0
  472. package/src/prompts/templates/system-sections.ts +281 -9
  473. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  474. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  475. package/src/providers/anthropic/client.ts +159 -66
  476. package/src/providers/call-site-routing.ts +14 -2
  477. package/src/providers/connection-model-compat.ts +38 -0
  478. package/src/providers/connection-resolution.ts +16 -2
  479. package/src/providers/fireworks/client.ts +20 -2
  480. package/src/providers/gemini/client.ts +49 -6
  481. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  482. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  483. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  484. package/src/providers/inference/adapter-factory.ts +18 -1
  485. package/src/providers/inference/auth.ts +3 -3
  486. package/src/providers/inference/codex-token-refresh.ts +128 -0
  487. package/src/providers/inference/resolve-auth.ts +49 -6
  488. package/src/providers/minimax/client.ts +106 -0
  489. package/src/providers/model-catalog.ts +91 -1
  490. package/src/providers/model-intents.ts +1 -1
  491. package/src/providers/openai/chat-completions-provider.ts +63 -23
  492. package/src/providers/openai/codex-models.ts +18 -0
  493. package/src/providers/openai/responses-provider.ts +86 -23
  494. package/src/providers/openrouter/client.ts +5 -1
  495. package/src/providers/provider-send-message.ts +7 -1
  496. package/src/providers/retry.ts +34 -3
  497. package/src/providers/thinking-config.ts +26 -1
  498. package/src/providers/types.ts +25 -0
  499. package/src/providers/usage-tracking.ts +2 -0
  500. package/src/runtime/AGENTS.md +2 -2
  501. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  502. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  503. package/src/runtime/agent-wake.ts +152 -56
  504. package/src/runtime/assistant-event-hub.ts +76 -6
  505. package/src/runtime/auth/route-policy.ts +43 -3
  506. package/src/runtime/background-job-runner.ts +26 -0
  507. package/src/runtime/btw-sidechain.ts +0 -6
  508. package/src/runtime/channel-reply-delivery.ts +182 -47
  509. package/src/runtime/channel-retry-sweep.ts +141 -16
  510. package/src/runtime/http-types.ts +7 -6
  511. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  512. package/src/runtime/pending-interactions.ts +50 -8
  513. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  514. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
  515. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  516. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
  517. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  518. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  519. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  520. package/src/runtime/routes/acp-routes.test.ts +255 -6
  521. package/src/runtime/routes/acp-routes.ts +8 -1
  522. package/src/runtime/routes/approval-routes.ts +4 -1
  523. package/src/runtime/routes/avatar-routes.ts +10 -10
  524. package/src/runtime/routes/background-wake-routes.ts +188 -0
  525. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  526. package/src/runtime/routes/btw-routes.ts +0 -6
  527. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  528. package/src/runtime/routes/content-source-routes.ts +78 -0
  529. package/src/runtime/routes/conversation-cli-routes.ts +147 -2
  530. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  531. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  532. package/src/runtime/routes/conversation-query-routes.ts +196 -31
  533. package/src/runtime/routes/conversation-routes.ts +472 -425
  534. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  535. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  536. package/src/runtime/routes/document-comments-routes.ts +287 -0
  537. package/src/runtime/routes/documents-routes.ts +33 -0
  538. package/src/runtime/routes/domain-routes.ts +60 -10
  539. package/src/runtime/routes/email-routes.ts +5 -2
  540. package/src/runtime/routes/events-routes.ts +54 -10
  541. package/src/runtime/routes/group-routes.ts +24 -8
  542. package/src/runtime/routes/home-feed-routes.ts +6 -3
  543. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  544. package/src/runtime/routes/host-browser-routes.ts +17 -2
  545. package/src/runtime/routes/host-cu-routes.ts +2 -2
  546. package/src/runtime/routes/identity-routes.ts +21 -0
  547. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  548. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  549. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  550. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  551. package/src/runtime/routes/index.ts +20 -4
  552. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  553. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  554. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  555. package/src/runtime/routes/integrations/a2a.ts +60 -1
  556. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  557. package/src/runtime/routes/log-export-routes.ts +39 -0
  558. package/src/runtime/routes/memory-item-routes.ts +8 -3
  559. package/src/runtime/routes/memory-v2-routes.ts +427 -0
  560. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  561. package/src/runtime/routes/migration-routes.ts +21 -24
  562. package/src/runtime/routes/notification-routes.ts +19 -2
  563. package/src/runtime/routes/plugins-routes.ts +337 -0
  564. package/src/runtime/routes/question-routes.ts +4 -1
  565. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  566. package/src/runtime/routes/sanity-routes.ts +159 -0
  567. package/src/runtime/routes/secret-routes.ts +25 -5
  568. package/src/runtime/routes/settings-routes.ts +12 -11
  569. package/src/runtime/routes/slack-channel-routes.ts +188 -0
  570. package/src/runtime/routes/workspace-routes.ts +25 -10
  571. package/src/runtime/services/conversation-serializer.ts +30 -4
  572. package/src/runtime/sync/resource-sync-events.ts +106 -38
  573. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  574. package/src/runtime/sync/sync-publisher.ts +2 -1
  575. package/src/runtime/verification-outbound-actions.ts +73 -1
  576. package/src/schedule/integration-status.ts +3 -1
  577. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  578. package/src/security/oauth2-device-code.ts +307 -0
  579. package/src/security/oauth2.ts +26 -9
  580. package/src/security/secure-keys.ts +5 -0
  581. package/src/skills/catalog-install.ts +6 -2
  582. package/src/telemetry/types.ts +12 -0
  583. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  584. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  585. package/src/tools/acp/spawn.test.ts +119 -0
  586. package/src/tools/acp/spawn.ts +15 -2
  587. package/src/tools/apps/definitions.ts +2 -8
  588. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  589. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  590. package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
  591. package/src/tools/browser/browser-execution.ts +106 -0
  592. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  593. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  594. package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
  595. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
  596. package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
  597. package/src/tools/browser/cdp-client/factory.ts +171 -4
  598. package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
  599. package/src/tools/browser/cdp-client/types.ts +101 -0
  600. package/src/tools/browser/pinned-tabs.ts +146 -0
  601. package/src/tools/computer-use/definitions.ts +22 -78
  602. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  603. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  604. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  605. package/src/tools/credentials/vault.ts +3 -9
  606. package/src/tools/document/document-comment-tool.test.ts +379 -0
  607. package/src/tools/document/document-comment-tool.ts +156 -0
  608. package/src/tools/document/document-tool.ts +187 -2
  609. package/src/tools/execution-target.ts +21 -23
  610. package/src/tools/executor.ts +6 -1
  611. package/src/tools/filesystem/edit.ts +3 -9
  612. package/src/tools/filesystem/list.ts +3 -9
  613. package/src/tools/filesystem/read.ts +3 -9
  614. package/src/tools/filesystem/write.ts +3 -9
  615. package/src/tools/host-filesystem/edit.ts +3 -9
  616. package/src/tools/host-filesystem/read.ts +3 -9
  617. package/src/tools/host-filesystem/transfer.ts +3 -9
  618. package/src/tools/host-filesystem/write.ts +3 -9
  619. package/src/tools/host-terminal/host-shell.ts +3 -9
  620. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  621. package/src/tools/memory/register.test.ts +1 -1
  622. package/src/tools/memory/register.ts +4 -9
  623. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  624. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  625. package/src/tools/network/domain-normalize.ts +17 -0
  626. package/src/tools/network/web-fetch.ts +216 -73
  627. package/src/tools/network/web-search.ts +216 -98
  628. package/src/tools/registry.ts +7 -23
  629. package/src/tools/schema-transforms.ts +1 -1
  630. package/src/tools/skills/execute.ts +3 -9
  631. package/src/tools/skills/load.ts +3 -9
  632. package/src/tools/skills/skill-tool-factory.ts +1 -8
  633. package/src/tools/subagent/notify-parent.ts +3 -9
  634. package/src/tools/system/request-permission.ts +3 -9
  635. package/src/tools/terminal/safe-env.ts +3 -2
  636. package/src/tools/terminal/shell.ts +3 -9
  637. package/src/tools/tool-approval-handler.ts +19 -12
  638. package/src/tools/tool-defaults.ts +94 -0
  639. package/src/tools/types.ts +31 -98
  640. package/src/tools/ui-surface/definitions.ts +9 -23
  641. package/src/types/onboarding-context.ts +4 -0
  642. package/src/usage/pricing.ts +23 -0
  643. package/src/usage/types.ts +12 -0
  644. package/src/util/__tests__/favicon.test.ts +84 -0
  645. package/src/util/favicon.ts +40 -0
  646. package/src/util/logger.ts +16 -7
  647. package/src/util/platform.ts +7 -7
  648. package/src/util/sqlite3-runtime.ts +65 -0
  649. package/src/workspace/git-service.ts +75 -4
  650. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  651. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  652. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  653. package/src/workspace/migrations/registry.ts +4 -0
  654. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  655. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  656. package/src/config/bundled-skills/document/SKILL.md +0 -54
  657. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  658. package/src/daemon/seed-files.ts +0 -18
  659. package/src/prompts/cache-boundary.ts +0 -8
  660. package/src/runtime/routes/interface-routes.ts +0 -43
  661. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  662. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  663. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  664. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  665. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -78,12 +78,11 @@ describe("computer-use skill manifest regression", () => {
78
78
  await initializeTools();
79
79
 
80
80
  for (const cuTool of allComputerUseTools) {
81
- const def = cuTool.getDefinition();
82
81
  const manifestTool = manifest.tools.find(
83
- (t: { name: string }) => t.name === def.name,
82
+ (t: { name: string }) => t.name === cuTool.name,
84
83
  );
85
84
  expect(manifestTool).toBeDefined();
86
- expect(manifestTool.description).toBe(def.description);
85
+ expect(manifestTool.description).toBe(cuTool.description);
87
86
  }
88
87
  });
89
88
 
@@ -91,12 +90,11 @@ describe("computer-use skill manifest regression", () => {
91
90
  await initializeTools();
92
91
 
93
92
  for (const cuTool of allComputerUseTools) {
94
- const def = cuTool.getDefinition();
95
93
  const manifestTool = manifest.tools.find(
96
- (t: { name: string }) => t.name === def.name,
94
+ (t: { name: string }) => t.name === cuTool.name,
97
95
  );
98
96
  expect(manifestTool).toBeDefined();
99
- expect(manifestTool.input_schema).toEqual(def.input_schema);
97
+ expect(manifestTool.input_schema).toEqual(cuTool.input_schema);
100
98
  }
101
99
  });
102
100
 
@@ -121,15 +119,11 @@ describe("computer-use skill manifest regression", () => {
121
119
  (entry: { name: string; description: string }) => ({
122
120
  name: entry.name,
123
121
  description: entry.description,
122
+ input_schema: { type: "object" as const, properties: {} },
124
123
  category: "computer-use",
125
124
  defaultRiskLevel: RiskLevel.Low,
126
125
  origin: "skill" as const,
127
126
  ownerSkillId: "computer-use",
128
- getDefinition: () => ({
129
- name: entry.name,
130
- description: entry.description,
131
- input_schema: { type: "object" as const, properties: {} },
132
- }),
133
127
  execute: async () => ({ content: "stub", isError: false }),
134
128
  }),
135
129
  );
@@ -23,10 +23,8 @@ interface JsonSchema {
23
23
  }
24
24
 
25
25
  /** Cast a tool definition's input_schema to a usable JSON Schema shape. */
26
- function schema(tool: {
27
- getDefinition(): { input_schema: object };
28
- }): JsonSchema {
29
- return tool.getDefinition().input_schema as JsonSchema;
26
+ function schema(tool: { input_schema: object }): JsonSchema {
27
+ return tool.input_schema as JsonSchema;
30
28
  }
31
29
 
32
30
  const ctx: ToolContext = {
@@ -117,7 +117,7 @@ mock.module("../providers/registry.js", () => ({
117
117
  getProvider: () => undefined,
118
118
  listProviders: () => [],
119
119
  getProviderRoutingSource: () => undefined,
120
- initializeProviders: () => {},
120
+ initializeProviders: async () => {},
121
121
  // Required by `providers/inference/connections.ts` and
122
122
  // `providers/connection-resolution.ts`, both loaded transitively when
123
123
  // ConfigWatcher's deps resolve. Without these, the import chain throws
@@ -50,7 +50,6 @@ mock.module("../notifications/emit-signal.js", () => ({
50
50
  ],
51
51
  };
52
52
  },
53
- registerBroadcastFn: () => {},
54
53
  }));
55
54
 
56
55
  // Mock channel guardian service — provide a guardian binding for 'self' + 'telegram'
@@ -256,7 +256,12 @@ describe("token estimator", () => {
256
256
  // tokens = ceil(1461 * 822 / 750) = ceil(1601.26) = ~1,602
257
257
  // With IMAGE_BLOCK_OVERHEAD_TOKENS and media_type overhead, still well under 5000.
258
258
  // Same result for every provider — dimension-based estimate is universal.
259
- for (const providerName of ["anthropic", "openai", "openrouter"]) {
259
+ for (const providerName of [
260
+ "anthropic",
261
+ "openai",
262
+ "openrouter",
263
+ "gemini",
264
+ ]) {
260
265
  const tokens = estimateContentBlockTokens(
261
266
  {
262
267
  type: "image",
@@ -294,6 +299,91 @@ describe("token estimator", () => {
294
299
  }
295
300
  });
296
301
 
302
+ test("Gemini falls back to its max-tile budget for unparseable / HEIC images", () => {
303
+ // HEIC/HEIF coming from iOS attachments aren't parsed by
304
+ // parseImageDimensions, so the estimator sees null dims. The generic
305
+ // 1,600-token cap would under-count by ~2.5x for a typical iPhone photo
306
+ // that ends up at Gemini's 16-tile / 4,128-token ceiling. Use the
307
+ // Gemini-specific cap instead to avoid skipping compaction.
308
+ for (const mediaType of [
309
+ "image/heic",
310
+ "image/heif",
311
+ "image/png", // corrupted PNG also exercises the fallback
312
+ ]) {
313
+ const data = Buffer.from("not-a-valid-image-header-at-all").toString(
314
+ "base64",
315
+ );
316
+ const tokens = estimateContentBlockTokens(
317
+ {
318
+ type: "image",
319
+ source: { type: "base64", media_type: mediaType, data },
320
+ },
321
+ { providerName: "gemini" },
322
+ );
323
+ // 4128 (16 tiles * 258) + 16 (block overhead) + ceil(mediaType len / 4)
324
+ expect(tokens).toBeGreaterThanOrEqual(4_128);
325
+ expect(tokens).toBeLessThan(4_200);
326
+ }
327
+ });
328
+
329
+ test("Gemini image tokens scale with image area via 768x768 tiling", () => {
330
+ // Per Google's docs, Gemini tiles images larger than 384px into 768x768
331
+ // chunks at 258 tokens each, after resizing the longest side to ≤3072px.
332
+ // 3000x3000 (under the cap) → ceil(3000/768)^2 = 4*4 = 16 tiles → 4,128
333
+ // image tokens.
334
+ const tokens = estimateContentBlockTokens(
335
+ {
336
+ type: "image",
337
+ source: {
338
+ type: "base64",
339
+ media_type: "image/png",
340
+ data: makePngBase64(3000, 3000),
341
+ },
342
+ },
343
+ { providerName: "gemini" },
344
+ );
345
+ expect(tokens).toBeGreaterThanOrEqual(4_128);
346
+ expect(tokens).toBeLessThan(4_200);
347
+ });
348
+
349
+ test("Gemini clamps image dimensions to 3072px before tiling", () => {
350
+ // Google's docs state images are resized to a 3072px max side before
351
+ // tiling. Without the clamp, a 4000x4000 image would be counted as
352
+ // ceil(4000/768)^2 = 36 tiles (~9,288 tokens) instead of the actual
353
+ // ceil(3072/768)^2 = 16 tiles (~4,128 tokens), over-counting by ~2.25x
354
+ // and triggering spurious compaction.
355
+ const tokens = estimateContentBlockTokens(
356
+ {
357
+ type: "image",
358
+ source: {
359
+ type: "base64",
360
+ media_type: "image/png",
361
+ data: makePngBase64(4000, 4000),
362
+ },
363
+ },
364
+ { providerName: "gemini" },
365
+ );
366
+ expect(tokens).toBeGreaterThanOrEqual(4_128);
367
+ expect(tokens).toBeLessThan(4_200);
368
+ });
369
+
370
+ test("Gemini images ≤384px on both sides count as a single 258-token tile", () => {
371
+ const tokens = estimateContentBlockTokens(
372
+ {
373
+ type: "image",
374
+ source: {
375
+ type: "base64",
376
+ media_type: "image/png",
377
+ data: makePngBase64(200, 200),
378
+ },
379
+ },
380
+ { providerName: "gemini" },
381
+ );
382
+ // 258 (tile) + 16 (block overhead) + 3 (media type) = 277
383
+ expect(tokens).toBeGreaterThanOrEqual(258);
384
+ expect(tokens).toBeLessThan(300);
385
+ });
386
+
297
387
  test("Anthropic image tokens are the same for same-dimension images regardless of payload size", () => {
298
388
  // Build two PNG headers with the same dimensions (800x600) but different payload sizes
299
389
  function makePng(
@@ -19,7 +19,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
19
19
 
20
20
  mock.module("../providers/registry.js", () => ({
21
21
  getProvider: () => ({ name: "mock-provider" }),
22
- initializeProviders: () => {},
22
+ initializeProviders: async () => {},
23
23
  }));
24
24
 
25
25
  mock.module("../config/loader.js", () => ({
@@ -86,7 +86,6 @@ mock.module("../daemon/disk-pressure-guard.js", () => ({
86
86
  }));
87
87
 
88
88
  mock.module("../memory/conversation-crud.js", () => ({
89
- clearStrippedInjectionMetadataForConversation: () => {},
90
89
  getConversation: () => ({
91
90
  id: "conv-123",
92
91
  conversationType: "background",
@@ -98,6 +97,7 @@ mock.module("../memory/conversation-crud.js", () => ({
98
97
  getConversationOriginInterface: () => null,
99
98
  getConversationOverrideProfileFromRow: () => null,
100
99
  provenanceFromTrustContext: () => ({}),
100
+ setConversationHistoryStrippedAt: () => {},
101
101
  updateConversationContextWindow: () => {},
102
102
  updateConversationSlackContextWatermark: () => {},
103
103
  }));
@@ -78,7 +78,11 @@ mock.module("../config/loader.js", () => ({
78
78
  },
79
79
  },
80
80
  },
81
- profiles: {},
81
+ profiles: {
82
+ "quality-optimized": {
83
+ contextWindow: { maxInputTokens: 50000 },
84
+ },
85
+ },
82
86
  callSites: {},
83
87
  pricingOverrides: [],
84
88
  },
@@ -141,9 +145,9 @@ let mockConversationRow: {
141
145
 
142
146
  mock.module("../memory/conversation-crud.js", () => ({
143
147
  setConversationOriginChannelIfUnset: () => {},
148
+ setConversationHistoryStrippedAt: () => {},
144
149
  updateConversationUsage: () => {},
145
150
  updateMessageMetadata: () => {},
146
- clearStrippedInjectionMetadataForConversation: () => {},
147
151
  getMessages: () => [],
148
152
  getConversation: () => mockConversationRow,
149
153
  getConversationOverrideProfileFromRow: (
@@ -165,6 +169,7 @@ mock.module("../memory/conversation-crud.js", () => ({
165
169
  getConversationOriginChannel: () => null,
166
170
  getMessageById: () => null,
167
171
  getLastUserTimestampBefore: () => 0,
172
+ setLastNotifiedInferenceProfile: () => {},
168
173
  }));
169
174
 
170
175
  mock.module("../memory/conversation-disk-view.js", () => ({
@@ -350,12 +355,17 @@ import {
350
355
 
351
356
  // Captures every positional argument the loop passes to `agentLoop.run`.
352
357
  // The 8th positional argument is the per-turn `overrideProfile`, which is
353
- // what these tests assert on.
358
+ // what most tests assert on. The 10th and 11th positional arguments re-resolve
359
+ // that profile and its max-token budget between provider calls.
354
360
  interface CapturedAgentLoopRun {
355
361
  callSite: LLMCallSite | undefined;
356
362
  overrideProfile: string | undefined;
363
+ resolvedOverrideProfile: string | undefined;
364
+ resolvedEffectiveMaxInputTokens: number | undefined;
357
365
  }
358
366
 
367
+ let mutateBeforeResolveOverrideProfile: (() => void) | undefined;
368
+
359
369
  function makeCtx(
360
370
  captured: CapturedAgentLoopRun[],
361
371
  overrides?: Partial<AgentLoopConversationContext>,
@@ -371,8 +381,17 @@ function makeCtx(
371
381
  callSite?: LLMCallSite,
372
382
  _turnContext?: unknown,
373
383
  overrideProfile?: string,
384
+ _effectiveMaxInputTokens?: number,
385
+ resolveOverrideProfile?: () => string | undefined,
386
+ resolveEffectiveMaxInputTokens?: () => number | undefined,
374
387
  ): Promise<Message[]> => {
375
- captured.push({ callSite, overrideProfile });
388
+ mutateBeforeResolveOverrideProfile?.();
389
+ captured.push({
390
+ callSite,
391
+ overrideProfile,
392
+ resolvedOverrideProfile: resolveOverrideProfile?.(),
393
+ resolvedEffectiveMaxInputTokens: resolveEffectiveMaxInputTokens?.(),
394
+ });
376
395
  return [
377
396
  ...messages,
378
397
  {
@@ -511,6 +530,7 @@ beforeEach(() => {
511
530
  totalEstimatedCost: 0,
512
531
  title: null,
513
532
  };
533
+ mutateBeforeResolveOverrideProfile = undefined;
514
534
  resetPluginRegistryAndRegisterDefaults();
515
535
  });
516
536
 
@@ -631,4 +651,35 @@ describe("runAgentLoopImpl — per-conversation inferenceProfile", () => {
631
651
  expect(call.overrideProfile).toBe("fast");
632
652
  }
633
653
  });
654
+
655
+ test("re-resolves inferenceProfile when a tool changes it mid-turn", async () => {
656
+ mockConversationRow = {
657
+ id: "conv-1",
658
+ conversationType: "standard",
659
+ inferenceProfile: null,
660
+ contextSummary: null,
661
+ contextCompactedMessageCount: 0,
662
+ totalInputTokens: 0,
663
+ totalOutputTokens: 0,
664
+ totalEstimatedCost: 0,
665
+ title: null,
666
+ };
667
+ mutateBeforeResolveOverrideProfile = () => {
668
+ mockConversationRow = {
669
+ ...mockConversationRow!,
670
+ inferenceProfile: "quality-optimized",
671
+ };
672
+ };
673
+
674
+ const captured: CapturedAgentLoopRun[] = [];
675
+ const ctx = makeCtx(captured);
676
+
677
+ await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
678
+
679
+ expect(captured.length).toBeGreaterThan(0);
680
+ expect(captured[0].overrideProfile).toBeUndefined();
681
+ expect(captured[0].resolvedOverrideProfile).toBe("quality-optimized");
682
+ expect(captured[0].resolvedEffectiveMaxInputTokens).toBe(50000);
683
+ expect(ctx.currentTurnOverrideProfile).toBeUndefined();
684
+ });
634
685
  });
@@ -170,6 +170,7 @@ mock.module("../daemon/context-overflow-policy.js", () => ({
170
170
 
171
171
  mock.module("../memory/conversation-crud.js", () => ({
172
172
  setConversationOriginChannelIfUnset: () => {},
173
+ setConversationHistoryStrippedAt: () => {},
173
174
  updateConversationUsage: () => {},
174
175
  getMessages: () => [],
175
176
  getConversation: () => ({
@@ -186,7 +187,7 @@ mock.module("../memory/conversation-crud.js", () => ({
186
187
  trustContext: undefined,
187
188
  }),
188
189
  getConversationOriginInterface: () => null,
189
- addMessage: () => ({ id: "mock-msg-id" }),
190
+ addMessage: (...args: unknown[]) => addMessageMock(...args),
190
191
  deleteMessageById: () => {},
191
192
  updateConversationContextWindow: () => {},
192
193
  updateConversationTitle: () => {},
@@ -194,7 +195,9 @@ mock.module("../memory/conversation-crud.js", () => ({
194
195
  getMessageById: () => null,
195
196
  updateMessageContent: () => {},
196
197
  updateMessageMetadata: () => {},
197
- clearStrippedInjectionMetadataForConversation: () => {},
198
+ setLastNotifiedInferenceProfile: () => {},
199
+ getLastUserTimestampBefore: () => 0,
200
+ getConversationOverrideProfileFromRow: () => undefined,
198
201
  }));
199
202
 
200
203
  afterAll(() => {
@@ -301,6 +304,10 @@ mock.module("../daemon/history-repair.js", () => ({
301
304
  }));
302
305
 
303
306
  const recordUsageMock = mock((..._args: unknown[]) => {});
307
+ const setAgentLoopExitReasonOnLatestLogMock = mock(() => {});
308
+ const addMessageMock = mock(
309
+ (..._args: unknown[]) => ({ id: "mock-msg-id" }) as { id: string },
310
+ );
304
311
  mock.module("../daemon/conversation-usage.js", () => ({
305
312
  recordUsage: recordUsageMock,
306
313
  }));
@@ -349,12 +356,23 @@ mock.module("../workspace/git-service.js", () => ({
349
356
  }));
350
357
 
351
358
  mock.module("../daemon/conversation-error.js", () => ({
352
- classifyConversationError: (_err: unknown, _ctx: unknown) => ({
353
- code: "CONVERSATION_PROCESSING_FAILED",
354
- userMessage: "Something went wrong processing your message.",
355
- retryable: false,
356
- errorCategory: "processing_failed",
357
- }),
359
+ classifyConversationError: (err: unknown, _ctx: unknown) => {
360
+ const message = err instanceof Error ? err.message : String(err);
361
+ if (/context.?length.?exceeded/i.test(message)) {
362
+ return {
363
+ code: "CONTEXT_TOO_LARGE",
364
+ userMessage: "Context too large.",
365
+ retryable: false,
366
+ errorCategory: "context_too_large",
367
+ };
368
+ }
369
+ return {
370
+ code: "CONVERSATION_PROCESSING_FAILED",
371
+ userMessage: "Something went wrong processing your message.",
372
+ retryable: false,
373
+ errorCategory: "processing_failed",
374
+ };
375
+ },
358
376
  isUserCancellation: (err: unknown, ctx: { aborted?: boolean }) => {
359
377
  if (!ctx.aborted) return false;
360
378
  if (err instanceof DOMException && err.name === "AbortError") return true;
@@ -394,6 +412,7 @@ mock.module("../agent/message-types.js", () => ({
394
412
  mock.module("../memory/llm-request-log-store.js", () => ({
395
413
  recordRequestLog: () => {},
396
414
  backfillMessageIdOnLogs: () => {},
415
+ setAgentLoopExitReasonOnLatestLog: setAgentLoopExitReasonOnLatestLogMock,
397
416
  }));
398
417
 
399
418
  mock.module("../memory/archive-store.js", () => ({
@@ -616,6 +635,8 @@ beforeEach(() => {
616
635
  mockOverflowAction = "fail_gracefully";
617
636
  mockApplyRuntimeInjections = (msgs) => msgs;
618
637
  recordUsageMock.mockClear();
638
+ setAgentLoopExitReasonOnLatestLogMock.mockClear();
639
+ addMessageMock.mockClear();
619
640
  // Reset the plugin registry and re-register every default so the
620
641
  // orchestrator's pipelines (`overflowReduce`, `persistence`, …) dispatch to
621
642
  // the default middleware, which in turn hits the mocked collaborators
@@ -1907,6 +1928,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1907
1928
  // After exhausting mid-loop attempts, the convergence loop should
1908
1929
  // have been triggered (contextTooLargeDetected set to true)
1909
1930
  expect(convergenceReducerCalled).toBe(true);
1931
+ expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
1932
+ "test-conv",
1933
+ "context_too_large",
1934
+ );
1910
1935
  });
1911
1936
 
1912
1937
  // ── Test 9 ────────────────────────────────────────────────────────
@@ -2068,6 +2093,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
2068
2093
 
2069
2094
  // Agent loop: 1 initial + 3 mid-loop re-entries + 2 convergence re-runs = 6 calls
2070
2095
  expect(agentLoopCallCount).toBe(6);
2096
+ expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
2097
+ "test-conv",
2098
+ "context_too_large",
2099
+ );
2071
2100
  });
2072
2101
 
2073
2102
  // ── Test 8 ────────────────────────────────────────────────────────
@@ -2209,4 +2238,195 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
2209
2238
  );
2210
2239
  expect(conversationError).toBeUndefined();
2211
2240
  });
2241
+
2242
+ // ── Test 9 ────────────────────────────────────────────────────────
2243
+ // When the `auto_compress_latest_turn` rerun (the last layer of the
2244
+ // overflow-recovery ladder) still yields at the mid-loop checkpoint,
2245
+ // the turn cannot proceed. Before PR 1 of the Compaction Visibility
2246
+ // workstream this terminated silently — no `agent_loop_exit_reason`,
2247
+ // no client notice, no durable transcript row. Now the loop must:
2248
+ // 1. emit a `conversation_error` event with code
2249
+ // `BUDGET_YIELD_UNRECOVERED`,
2250
+ // 2. persist a `role="assistant"` notice via the persistence
2251
+ // pipeline (so reloads keep the message),
2252
+ // 3. stamp `budget_yield_unrecovered` onto the latest llm_request_logs
2253
+ // row.
2254
+ test("budget_yield_unrecovered: classified error emitted, persisted, and stamped", async () => {
2255
+ const events: ServerMessage[] = [];
2256
+
2257
+ // Every estimate after the very first preflight is above the mid-loop
2258
+ // threshold (190_000 × 0.85 = 161_500). This makes every checkpoint
2259
+ // yield, including the one inside the auto_compress rerun.
2260
+ let estimateCallCount = 0;
2261
+ mockEstimateTokens = () => {
2262
+ estimateCallCount++;
2263
+ if (estimateCallCount === 1) return 100_000;
2264
+ return 170_000;
2265
+ };
2266
+
2267
+ // Convergence reducer becomes exhausted on the second tier so the
2268
+ // loop escalates from convergence to the action-resolution block.
2269
+ let reducerCallCount = 0;
2270
+ mockReducerStepFn = (msgs: Message[]) => {
2271
+ reducerCallCount++;
2272
+ const exhausted = reducerCallCount >= 2;
2273
+ return {
2274
+ messages: msgs,
2275
+ tier: exhausted ? "tool_result_truncation" : "forced_compaction",
2276
+ state: {
2277
+ appliedTiers: exhausted
2278
+ ? ["forced_compaction", "tool_result_truncation"]
2279
+ : ["forced_compaction"],
2280
+ injectionMode: "full" as const,
2281
+ exhausted,
2282
+ },
2283
+ estimatedTokens: exhausted ? 60_000 : 80_000,
2284
+ };
2285
+ };
2286
+
2287
+ // The overflow policy directs us into auto_compress_latest_turn so the
2288
+ // emergency compaction + final agentLoop.run path executes.
2289
+ mockOverflowAction = "auto_compress_latest_turn";
2290
+
2291
+ let agentLoopCallCount = 0;
2292
+ const agentLoopRun: AgentLoopRun = async (
2293
+ messages,
2294
+ onEvent,
2295
+ _signal,
2296
+ _requestId,
2297
+ onCheckpoint,
2298
+ ) => {
2299
+ agentLoopCallCount++;
2300
+
2301
+ const withProgress: Message[] = [
2302
+ ...messages,
2303
+ {
2304
+ role: "assistant" as const,
2305
+ content: [
2306
+ { type: "text", text: `tool call ${agentLoopCallCount}` },
2307
+ {
2308
+ type: "tool_use",
2309
+ id: `tu-${agentLoopCallCount}`,
2310
+ name: "bash",
2311
+ input: { command: "ls" },
2312
+ },
2313
+ ] as ContentBlock[],
2314
+ },
2315
+ {
2316
+ role: "user" as const,
2317
+ content: [
2318
+ {
2319
+ type: "tool_result",
2320
+ tool_use_id: `tu-${agentLoopCallCount}`,
2321
+ content: "output",
2322
+ is_error: false,
2323
+ },
2324
+ ] as ContentBlock[],
2325
+ },
2326
+ ];
2327
+
2328
+ onEvent({
2329
+ type: "message_complete",
2330
+ message: {
2331
+ role: "assistant",
2332
+ content: [
2333
+ { type: "text", text: `tool call ${agentLoopCallCount}` },
2334
+ {
2335
+ type: "tool_use",
2336
+ id: `tu-${agentLoopCallCount}`,
2337
+ name: "bash",
2338
+ input: { command: "ls" },
2339
+ },
2340
+ ],
2341
+ },
2342
+ });
2343
+ onEvent({
2344
+ type: "usage",
2345
+ inputTokens: 100,
2346
+ outputTokens: 50,
2347
+ model: "test-model",
2348
+ providerDurationMs: 100,
2349
+ });
2350
+
2351
+ // Every checkpoint yields — including the final auto_compress rerun.
2352
+ if (onCheckpoint) {
2353
+ const decision = await onCheckpoint({
2354
+ turnIndex: 0,
2355
+ toolCount: 1,
2356
+ hasToolUse: true,
2357
+ history: withProgress,
2358
+ });
2359
+ if (decision === "yield") {
2360
+ return withProgress;
2361
+ }
2362
+ }
2363
+
2364
+ return withProgress;
2365
+ };
2366
+
2367
+ const ctx = makeCtx({
2368
+ agentLoopRun,
2369
+ contextWindowManager: {
2370
+ shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
2371
+ // The compaction pipeline (default terminal) routes through this
2372
+ // for the emergency `auto_compress_latest_turn` path.
2373
+ maybeCompact: async () => ({
2374
+ compacted: true,
2375
+ messages: [
2376
+ {
2377
+ role: "user" as const,
2378
+ content: [{ type: "text", text: "compacted" }],
2379
+ },
2380
+ ] as Message[],
2381
+ compactedPersistedMessages: 5,
2382
+ summaryText: "Emergency summary",
2383
+ previousEstimatedInputTokens: 170_000,
2384
+ estimatedInputTokens: 90_000,
2385
+ maxInputTokens: 200_000,
2386
+ thresholdTokens: 160_000,
2387
+ compactedMessages: 10,
2388
+ summaryCalls: 1,
2389
+ summaryInputTokens: 500,
2390
+ summaryOutputTokens: 200,
2391
+ summaryModel: "mock-model",
2392
+ }),
2393
+ } as unknown as AgentLoopConversationContext["contextWindowManager"],
2394
+ });
2395
+
2396
+ await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
2397
+
2398
+ // The classified error is emitted to the client.
2399
+ const errorEvents = events.filter((e) => e.type === "conversation_error");
2400
+ expect(errorEvents).toHaveLength(1);
2401
+ const errorEvent = errorEvents[0];
2402
+ if (errorEvent && "code" in errorEvent) {
2403
+ expect(errorEvent.code).toBe("BUDGET_YIELD_UNRECOVERED");
2404
+ expect(errorEvent.retryable).toBe(true);
2405
+ expect(errorEvent.errorCategory).toBe("budget_yield_unrecovered");
2406
+ } else {
2407
+ throw new Error("conversation_error event missing `code` field");
2408
+ }
2409
+
2410
+ // The exit reason is stamped onto the latest llm_request_logs row.
2411
+ expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
2412
+ "test-conv",
2413
+ "budget_yield_unrecovered",
2414
+ );
2415
+
2416
+ // A `role="assistant"` notice is persisted via the persistence pipeline.
2417
+ // The default persistence terminal calls
2418
+ // `addMessage(conversationId, role, content, metadata, addOptions)` —
2419
+ // we look for the call whose role positional arg is "assistant" and
2420
+ // whose content positional arg mentions compaction.
2421
+ const assistantPersistCall = addMessageMock.mock.calls.find((call) => {
2422
+ const role = call[1];
2423
+ const content = call[2];
2424
+ return (
2425
+ role === "assistant" &&
2426
+ typeof content === "string" &&
2427
+ content.includes("compact")
2428
+ );
2429
+ });
2430
+ expect(assistantPersistCall).toBeDefined();
2431
+ });
2212
2432
  });