@vellumai/assistant 0.8.4 → 0.8.6

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 (802) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +3 -3
  3. package/bunfig.toml +6 -1
  4. package/docs/browser-use-architecture-phase2.md +1 -1
  5. package/docs/credential-execution-service.md +6 -6
  6. package/docs/plugins.md +4 -3
  7. package/knip.json +2 -1
  8. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
  9. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
  10. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  11. package/openapi.yaml +2748 -216
  12. package/package.json +1 -1
  13. package/src/__tests__/actor-token-service.test.ts +3 -2
  14. package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
  15. package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
  16. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  17. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  18. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  19. package/src/__tests__/annotate-risk-options.test.ts +1 -0
  20. package/src/__tests__/anthropic-provider.test.ts +34 -37
  21. package/src/__tests__/approval-cascade.test.ts +1 -0
  22. package/src/__tests__/approval-routes-http.test.ts +9 -13
  23. package/src/__tests__/assert-not-live-db.ts +79 -0
  24. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  25. package/src/__tests__/assistant-feature-flags-integration.test.ts +12 -28
  26. package/src/__tests__/audit-log-rotation.test.ts +72 -18
  27. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  28. package/src/__tests__/background-workers-disk-pressure.test.ts +8 -11
  29. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  30. package/src/__tests__/btw-routes.test.ts +5 -5
  31. package/src/__tests__/call-controller.test.ts +3 -3
  32. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  33. package/src/__tests__/channel-approval-routes.test.ts +3 -2
  34. package/src/__tests__/channel-guardian.test.ts +6 -5
  35. package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
  36. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  37. package/src/__tests__/channel-retry-sweep.test.ts +320 -3
  38. package/src/__tests__/checker.test.ts +18 -27
  39. package/src/__tests__/compaction-events.test.ts +2 -0
  40. package/src/__tests__/compaction-trail-store.test.ts +264 -0
  41. package/src/__tests__/compactor-call-site-logging.test.ts +215 -0
  42. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  43. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +12 -16
  44. package/src/__tests__/computer-use-tools.test.ts +14 -18
  45. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  46. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  47. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  48. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  49. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  50. package/src/__tests__/config-schema.test.ts +10 -10
  51. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  52. package/src/__tests__/connection-model-compat.test.ts +83 -0
  53. package/src/__tests__/contacts-tools.test.ts +3 -2
  54. package/src/__tests__/context-token-estimator.test.ts +22 -0
  55. package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
  56. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -1
  57. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  58. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -1
  59. package/src/__tests__/conversation-agent-loop-overflow.test.ts +231 -2
  60. package/src/__tests__/conversation-agent-loop.test.ts +581 -54
  61. package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
  62. package/src/__tests__/conversation-app-control-instantiation.test.ts +31 -24
  63. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
  64. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  65. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  66. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  67. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  68. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  69. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  70. package/src/__tests__/conversation-error.test.ts +61 -0
  71. package/src/__tests__/conversation-fork-crud.test.ts +239 -15
  72. package/src/__tests__/conversation-fork-route.test.ts +3 -2
  73. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  74. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  75. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  76. package/src/__tests__/conversation-lifecycle.test.ts +53 -11
  77. package/src/__tests__/conversation-list-source.test.ts +3 -2
  78. package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
  79. package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +14 -13
  80. package/src/__tests__/conversation-pairing.test.ts +53 -0
  81. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
  82. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  83. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  84. package/src/__tests__/conversation-queue.test.ts +333 -291
  85. package/src/__tests__/conversation-routes-disk-view.test.ts +112 -18
  86. package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
  87. package/src/__tests__/conversation-routes-slash-commands.test.ts +68 -2
  88. package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
  89. package/src/__tests__/conversation-skill-tools.test.ts +40 -147
  90. package/src/__tests__/conversation-slash-queue.test.ts +84 -32
  91. package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
  92. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  93. package/src/__tests__/conversation-store.test.ts +1 -1
  94. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
  95. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  96. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  97. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  98. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
  99. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
  100. package/src/__tests__/conversation-sync-tags.test.ts +218 -35
  101. package/src/__tests__/conversation-title-service.test.ts +1 -0
  102. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
  103. package/src/__tests__/conversation-usage.test.ts +1 -0
  104. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
  105. package/src/__tests__/conversation-workspace-injection.test.ts +6 -1
  106. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -1
  107. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  108. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  109. package/src/__tests__/credential-execution-client.test.ts +72 -1
  110. package/src/__tests__/credential-execution-feature-gates.test.ts +19 -19
  111. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  112. package/src/__tests__/credential-health-service.test.ts +252 -3
  113. package/src/__tests__/credential-security-invariants.test.ts +6 -5
  114. package/src/__tests__/credential-vault-unit.test.ts +21 -21
  115. package/src/__tests__/credential-vault.test.ts +5 -5
  116. package/src/__tests__/cross-provider-web-search.test.ts +56 -2
  117. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  118. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  119. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  120. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  121. package/src/__tests__/db-test-helpers.ts +58 -0
  122. package/src/__tests__/disk-pressure-guard.test.ts +58 -41
  123. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  124. package/src/__tests__/disk-pressure-routes.test.ts +0 -33
  125. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  126. package/src/__tests__/dm-persistence.test.ts +26 -40
  127. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  128. package/src/__tests__/document-find-replace.test.ts +3 -2
  129. package/src/__tests__/document-tool-security.test.ts +81 -2
  130. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  131. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  132. package/src/__tests__/email-html-renderer.test.ts +12 -0
  133. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  134. package/src/__tests__/encrypted-store.test.ts +11 -9
  135. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  136. package/src/__tests__/filing-service.test.ts +1 -0
  137. package/src/__tests__/first-greeting.test.ts +62 -12
  138. package/src/__tests__/gateway-flag-listener.test.ts +236 -0
  139. package/src/__tests__/gemini-provider.test.ts +104 -0
  140. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  141. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  142. package/src/__tests__/guardian-outbound-http.test.ts +10 -7
  143. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  144. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -1
  145. package/src/__tests__/heartbeat-disk-pressure.test.ts +5 -0
  146. package/src/__tests__/heartbeat-service.test.ts +5 -0
  147. package/src/__tests__/helpers/mock-logger.ts +26 -0
  148. package/src/__tests__/host-bash-routes.test.ts +1 -0
  149. package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
  150. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  151. package/src/__tests__/host-shell-tool.test.ts +6 -5
  152. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  153. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  154. package/src/__tests__/http-user-message-parity.test.ts +29 -7
  155. package/src/__tests__/identity-intro-cache.test.ts +133 -22
  156. package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
  157. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  158. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  159. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  160. package/src/__tests__/injector-disk-pressure.test.ts +3 -17
  161. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  162. package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
  163. package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
  164. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  165. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  166. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  167. package/src/__tests__/llm-resolver.test.ts +408 -9
  168. package/src/__tests__/llm-schema.test.ts +1 -1
  169. package/src/__tests__/llm-usage-store.test.ts +66 -0
  170. package/src/__tests__/logger.test.ts +89 -0
  171. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  172. package/src/__tests__/mcp-abort-signal.test.ts +16 -2
  173. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  174. package/src/__tests__/media-generate-image.test.ts +31 -0
  175. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  176. package/src/__tests__/messaging-send-tool.test.ts +1 -0
  177. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  178. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  179. package/src/__tests__/model-intents.test.ts +4 -6
  180. package/src/__tests__/native-web-search.test.ts +30 -2
  181. package/src/__tests__/notification-deep-link.test.ts +62 -0
  182. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  183. package/src/__tests__/oauth-commands-routes.test.ts +37 -0
  184. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  185. package/src/__tests__/oauth-store.test.ts +3 -2
  186. package/src/__tests__/onboarding-template-contract.test.ts +4 -3
  187. package/src/__tests__/openai-provider.test.ts +54 -9
  188. package/src/__tests__/openai-responses-provider.test.ts +176 -14
  189. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  190. package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
  191. package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
  192. package/src/__tests__/persistence-pipeline.test.ts +139 -1
  193. package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
  194. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  195. package/src/__tests__/platform.test.ts +2 -2
  196. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  197. package/src/__tests__/plugin-bootstrap.test.ts +11 -13
  198. package/src/__tests__/plugin-tool-contribution.test.ts +50 -40
  199. package/src/__tests__/plugin-types.test.ts +3 -2
  200. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  201. package/src/__tests__/pricing.test.ts +12 -0
  202. package/src/__tests__/process-message-background-slack.test.ts +21 -16
  203. package/src/__tests__/process-message-display-content.test.ts +19 -22
  204. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  205. package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
  206. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  207. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  208. package/src/__tests__/recording-handler.test.ts +1 -0
  209. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  210. package/src/__tests__/registry.test.ts +84 -84
  211. package/src/__tests__/relay-server.test.ts +10 -10
  212. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  213. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  214. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  215. package/src/__tests__/schedule-store.test.ts +16 -1
  216. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  217. package/src/__tests__/secret-ingress-http.test.ts +5 -1
  218. package/src/__tests__/secure-keys.test.ts +3 -3
  219. package/src/__tests__/send-endpoint-busy.test.ts +81 -42
  220. package/src/__tests__/server-history-render.test.ts +4 -1
  221. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  222. package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
  223. package/src/__tests__/skill-feature-flags.test.ts +16 -18
  224. package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
  225. package/src/__tests__/skill-projection-feature-flag.test.ts +48 -37
  226. package/src/__tests__/skill-projection.benchmark.test.ts +7 -13
  227. package/src/__tests__/skill-tool-factory.test.ts +97 -96
  228. package/src/__tests__/slack-channel-config.test.ts +3 -3
  229. package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
  230. package/src/__tests__/subagent-disposal.test.ts +27 -8
  231. package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
  232. package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
  233. package/src/__tests__/subagent-manager-notify.test.ts +20 -8
  234. package/src/__tests__/subagent-notify-parent.test.ts +6 -5
  235. package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
  236. package/src/__tests__/subagent-tools.test.ts +2 -1
  237. package/src/__tests__/suggestion-routes.test.ts +2 -0
  238. package/src/__tests__/sync-message-contract.test.ts +59 -0
  239. package/src/__tests__/system-prompt.test.ts +183 -131
  240. package/src/__tests__/terminal-tools.test.ts +1 -1
  241. package/src/__tests__/test-preload-verifier.ts +68 -0
  242. package/src/__tests__/test-preload.ts +32 -39
  243. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  244. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  245. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  246. package/src/__tests__/tool-executor-lifecycle-events.test.ts +35 -12
  247. package/src/__tests__/tool-executor.test.ts +64 -72
  248. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  249. package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
  250. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  251. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  252. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  253. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  254. package/src/__tests__/twilio-routes.test.ts +3 -2
  255. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  256. package/src/__tests__/usage-routes.test.ts +3 -0
  257. package/src/__tests__/validate-input.test.ts +381 -0
  258. package/src/__tests__/verification-control-plane-policy.test.ts +3 -2
  259. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
  260. package/src/__tests__/voice-session-bridge.test.ts +37 -28
  261. package/src/__tests__/workspace-git-service.test.ts +6 -5
  262. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  263. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  264. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  265. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  266. package/src/acp/prepare-agent-env.ts +78 -0
  267. package/src/acp/session-manager.ts +6 -7
  268. package/src/agent/loop.ts +88 -0
  269. package/src/api/README.md +127 -0
  270. package/src/api/constants/call-sites.ts +27 -0
  271. package/src/api/events/assistant-outbound-attachment.ts +51 -0
  272. package/src/api/events/assistant-text-delta.ts +32 -0
  273. package/src/api/events/assistant-turn-start.ts +33 -0
  274. package/src/api/events/document-comment-created.ts +48 -0
  275. package/src/api/events/document-comment-deleted.ts +24 -0
  276. package/src/api/events/document-comment-reopened.ts +25 -0
  277. package/src/api/events/document-comment-resolved.ts +27 -0
  278. package/src/api/events/generation-cancelled.ts +24 -0
  279. package/src/api/events/generation-handoff.ts +41 -0
  280. package/src/api/events/message-complete.ts +42 -0
  281. package/src/api/events/open-url.ts +30 -0
  282. package/src/api/events/relationship-state-updated.ts +25 -0
  283. package/src/api/events/tool-use-start.ts +32 -0
  284. package/src/api/index.ts +129 -0
  285. package/src/api/package.json +10 -0
  286. package/src/api/responses/llm-context-response.ts +39 -0
  287. package/src/api/responses/llm-request-log-entry.ts +93 -0
  288. package/src/api/responses/memory-recall-log.ts +65 -0
  289. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  290. package/src/background-wake/background-wake-routes.test.ts +868 -0
  291. package/src/background-wake/platform-client.test.ts +308 -0
  292. package/src/background-wake/platform-client.ts +167 -0
  293. package/src/background-wake/publisher.ts +91 -0
  294. package/src/background-wake/runtime-registry.ts +24 -0
  295. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  296. package/src/calls/guardian-dispatch.ts +1 -0
  297. package/src/calls/voice-session-bridge.ts +4 -4
  298. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  299. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  300. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  301. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  302. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  303. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  304. package/src/cli/commands/__tests__/memory-v2.test.ts +1 -0
  305. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  306. package/src/cli/commands/__tests__/notifications.test.ts +184 -40
  307. package/src/cli/commands/browser.ts +247 -0
  308. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  309. package/src/cli/commands/channels/index.ts +229 -0
  310. package/src/cli/commands/domain.ts +91 -41
  311. package/src/cli/commands/inference.ts +93 -40
  312. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  313. package/src/cli/commands/memory-v2.ts +176 -1
  314. package/src/cli/commands/memory-v3-render.ts +491 -0
  315. package/src/cli/commands/memory-v3.ts +567 -0
  316. package/src/cli/commands/notifications.ts +365 -55
  317. package/src/cli/lib/open-browser.ts +7 -2
  318. package/src/cli/program.ts +4 -0
  319. package/src/config/assistant-feature-flags.ts +39 -46
  320. package/src/config/bundled-skills/document-editor/SKILL.md +16 -3
  321. package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
  322. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  323. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  324. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  325. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  326. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  327. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  328. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  329. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  330. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  331. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  332. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  333. package/src/config/bundled-tool-registry.ts +2 -0
  334. package/src/config/call-site-defaults.ts +8 -7
  335. package/src/config/feature-flag-cache.ts +86 -0
  336. package/src/config/feature-flag-registry.json +33 -17
  337. package/src/config/llm-context-resolution.ts +10 -1
  338. package/src/config/llm-resolver.ts +121 -15
  339. package/src/config/loader.ts +4 -5
  340. package/src/config/schemas/__tests__/memory-v2.test.ts +228 -1
  341. package/src/config/schemas/call-site-catalog.ts +21 -7
  342. package/src/config/schemas/heartbeat.ts +1 -1
  343. package/src/config/schemas/llm.ts +102 -2
  344. package/src/config/schemas/memory-v2.ts +272 -0
  345. package/src/config/schemas/memory.ts +2 -1
  346. package/src/config/schemas/services.ts +6 -2
  347. package/src/config/seed-inference-profiles.ts +36 -16
  348. package/src/context/compactor.ts +52 -0
  349. package/src/context/token-estimator.ts +10 -5
  350. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  351. package/src/conversations/message-consolidation.ts +404 -0
  352. package/src/credential-execution/executable-discovery.ts +40 -0
  353. package/src/credential-execution/process-manager.ts +6 -2
  354. package/src/credential-health/credential-health-service.ts +125 -40
  355. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
  356. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
  357. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +2 -3
  358. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  359. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  360. package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
  361. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  362. package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
  363. package/src/daemon/conversation-agent-loop-handlers.ts +390 -80
  364. package/src/daemon/conversation-agent-loop.ts +244 -90
  365. package/src/daemon/conversation-error.ts +64 -6
  366. package/src/daemon/conversation-lifecycle.ts +27 -22
  367. package/src/daemon/conversation-messaging.ts +84 -43
  368. package/src/daemon/conversation-process.ts +74 -37
  369. package/src/daemon/conversation-runtime-assembly.ts +38 -17
  370. package/src/daemon/conversation-skill-tools.ts +14 -30
  371. package/src/daemon/conversation-surfaces.ts +69 -34
  372. package/src/daemon/conversation-tool-setup.ts +77 -32
  373. package/src/daemon/conversation-usage.ts +2 -0
  374. package/src/daemon/conversation.ts +40 -75
  375. package/src/daemon/daemon-control.ts +1 -1
  376. package/src/daemon/daemon-skill-host.ts +9 -2
  377. package/src/daemon/disk-pressure-guard.ts +39 -29
  378. package/src/daemon/first-greeting.ts +31 -13
  379. package/src/daemon/handlers/config-model.test.ts +1 -0
  380. package/src/daemon/handlers/conversations.ts +11 -3
  381. package/src/daemon/handlers/shared.ts +6 -1
  382. package/src/daemon/host-browser-proxy.ts +5 -5
  383. package/src/daemon/host-cu-proxy.ts +4 -4
  384. package/src/daemon/host-file-proxy.ts +4 -4
  385. package/src/daemon/host-proxy-base.ts +4 -4
  386. package/src/daemon/host-transfer-proxy.ts +10 -10
  387. package/src/daemon/lifecycle.ts +29 -26
  388. package/src/daemon/mcp-reload-service.ts +1 -1
  389. package/src/daemon/meet-manifest-loader.ts +11 -24
  390. package/src/daemon/message-types/conversations.ts +22 -27
  391. package/src/daemon/message-types/document-comments.ts +8 -44
  392. package/src/daemon/message-types/home.ts +2 -14
  393. package/src/daemon/message-types/integrations.ts +2 -7
  394. package/src/daemon/message-types/messages.ts +25 -48
  395. package/src/daemon/message-types/subagents.ts +6 -0
  396. package/src/daemon/message-types/sync.ts +14 -0
  397. package/src/daemon/process-message.ts +9 -9
  398. package/src/daemon/providers-setup.ts +1 -1
  399. package/src/daemon/server.ts +16 -0
  400. package/src/daemon/shutdown-handlers.ts +24 -5
  401. package/src/daemon/switch-inference-profile-tool.ts +62 -0
  402. package/src/daemon/tool-setup-types.ts +7 -0
  403. package/src/daemon/wake-target-adapter.ts +10 -0
  404. package/src/documents/document-store.ts +38 -0
  405. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  406. package/src/heartbeat/__tests__/heartbeat-service.test.ts +30 -1
  407. package/src/heartbeat/heartbeat-service.ts +63 -0
  408. package/src/home/__tests__/feed-writer.test.ts +161 -0
  409. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  410. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  411. package/src/home/feed-writer.ts +146 -7
  412. package/src/home/home-greeting.ts +0 -9
  413. package/src/home/suggested-prompts.ts +27 -154
  414. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  415. package/src/ipc/gateway-client.test.ts +4 -1
  416. package/src/ipc/gateway-flag-listener.ts +123 -0
  417. package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
  418. package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
  419. package/src/ipc/skill-routes/memory.ts +4 -3
  420. package/src/ipc/skill-routes/registries.ts +35 -40
  421. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  422. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  423. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +242 -0
  424. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  425. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
  426. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  427. package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
  428. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  429. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  430. package/src/memory/auto-analysis-enqueue.ts +5 -1
  431. package/src/memory/conversation-attention-store.ts +17 -3
  432. package/src/memory/conversation-crud.ts +423 -182
  433. package/src/memory/conversation-starters-cadence.ts +3 -1
  434. package/src/memory/conversation-title-service.ts +19 -3
  435. package/src/memory/db-async-query.ts +214 -0
  436. package/src/memory/db-connection.ts +29 -19
  437. package/src/memory/db-init.ts +14 -0
  438. package/src/memory/db-maintenance.ts +30 -21
  439. package/src/memory/db-singleton.ts +77 -0
  440. package/src/memory/delivery-channels.ts +82 -0
  441. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  442. package/src/memory/graph/bootstrap.ts +8 -1
  443. package/src/memory/graph/capability-seed.ts +7 -3
  444. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  445. package/src/memory/graph/extraction.ts +1 -5
  446. package/src/memory/graph/graph-search.ts +7 -1
  447. package/src/memory/graph/retriever.test.ts +3 -3
  448. package/src/memory/indexer.ts +28 -18
  449. package/src/memory/job-handlers/cleanup.ts +76 -18
  450. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  451. package/src/memory/job-handlers/embedding.test.ts +3 -2
  452. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  453. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  454. package/src/memory/jobs-store.ts +14 -0
  455. package/src/memory/jobs-worker.ts +66 -22
  456. package/src/memory/llm-request-log-source-clickhouse.ts +122 -2
  457. package/src/memory/llm-request-log-source-local.ts +31 -0
  458. package/src/memory/llm-request-log-source.ts +40 -2
  459. package/src/memory/llm-request-log-store.ts +228 -1
  460. package/src/memory/llm-usage-store.ts +24 -0
  461. package/src/memory/memory-retrospective-enqueue.ts +8 -1
  462. package/src/memory/memory-retrospective-job.ts +5 -0
  463. package/src/memory/memory-v2-activation-log-store.ts +110 -7
  464. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  465. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  466. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  467. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  468. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  469. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  470. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  471. package/src/memory/migrations/index.ts +19 -0
  472. package/src/memory/migrations/registry.ts +33 -0
  473. package/src/memory/schema/conversations.ts +10 -2
  474. package/src/memory/schema/inference.ts +0 -1
  475. package/src/memory/schema/infrastructure.ts +21 -0
  476. package/src/memory/tool-usage-store.ts +36 -8
  477. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  478. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  479. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  480. package/src/memory/v2/__tests__/harness-metrics.test.ts +83 -0
  481. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  482. package/src/memory/v2/__tests__/harness-replay-input.test.ts +230 -0
  483. package/src/memory/v2/__tests__/harness-runner.test.ts +135 -0
  484. package/src/memory/v2/__tests__/injection.test.ts +127 -98
  485. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  486. package/src/memory/v2/__tests__/router.test.ts +171 -3
  487. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
  488. package/src/memory/v2/harness/compare.ts +57 -0
  489. package/src/memory/v2/harness/metrics.ts +128 -0
  490. package/src/memory/v2/harness/oracle.ts +145 -0
  491. package/src/memory/v2/harness/replay-input.ts +240 -0
  492. package/src/memory/v2/harness/retriever.ts +74 -0
  493. package/src/memory/v2/harness/router-retriever.ts +43 -0
  494. package/src/memory/v2/harness/runner.ts +112 -0
  495. package/src/memory/v2/harness/trace.ts +64 -0
  496. package/src/memory/v2/injection.ts +21 -15
  497. package/src/memory/v2/prompts/router.ts +26 -1
  498. package/src/memory/v2/qdrant.ts +14 -2
  499. package/src/memory/v2/router.ts +171 -18
  500. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  501. package/src/memory/v3/__tests__/consolidation-job.test.ts +466 -0
  502. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
  503. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  504. package/src/memory/v3/__tests__/edges.test.ts +706 -0
  505. package/src/memory/v3/__tests__/filter.test.ts +560 -0
  506. package/src/memory/v3/__tests__/gate.test.ts +637 -0
  507. package/src/memory/v3/__tests__/index-composition.test.ts +291 -0
  508. package/src/memory/v3/__tests__/loop.test.ts +775 -0
  509. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  510. package/src/memory/v3/__tests__/scouts.test.ts +489 -0
  511. package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
  512. package/src/memory/v3/__tests__/shadow-middleware.test.ts +398 -0
  513. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  514. package/src/memory/v3/__tests__/traversal.test.ts +508 -0
  515. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  516. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  517. package/src/memory/v3/__tests__/tree-walk.test.ts +784 -0
  518. package/src/memory/v3/__tests__/validate.test.ts +277 -0
  519. package/src/memory/v3/auto-edges.ts +223 -0
  520. package/src/memory/v3/coactivation-store.ts +124 -0
  521. package/src/memory/v3/consolidation-job.ts +323 -0
  522. package/src/memory/v3/coretrieval-seed.ts +240 -0
  523. package/src/memory/v3/edge-learning-job.ts +160 -0
  524. package/src/memory/v3/edges.ts +286 -0
  525. package/src/memory/v3/filter.ts +286 -0
  526. package/src/memory/v3/gate.ts +349 -0
  527. package/src/memory/v3/index-composition.ts +126 -0
  528. package/src/memory/v3/llm-capture.ts +46 -0
  529. package/src/memory/v3/loop.ts +430 -0
  530. package/src/memory/v3/maintenance.ts +144 -0
  531. package/src/memory/v3/prompt-context.ts +33 -0
  532. package/src/memory/v3/prompts/consolidation.ts +458 -0
  533. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  534. package/src/memory/v3/retriever.ts +33 -0
  535. package/src/memory/v3/scouts.ts +431 -0
  536. package/src/memory/v3/shadow-diff.ts +287 -0
  537. package/src/memory/v3/shadow-middleware.ts +347 -0
  538. package/src/memory/v3/traversal.ts +211 -0
  539. package/src/memory/v3/tree-index.ts +237 -0
  540. package/src/memory/v3/tree-store.ts +394 -0
  541. package/src/memory/v3/tree-walk.ts +356 -0
  542. package/src/memory/v3/types.ts +65 -0
  543. package/src/memory/v3/validate.ts +323 -0
  544. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  545. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  546. package/src/notifications/adapters/macos.ts +18 -1
  547. package/src/notifications/adapters/platform.ts +1 -1
  548. package/src/notifications/adapters/slack.ts +45 -11
  549. package/src/notifications/broadcaster.ts +114 -63
  550. package/src/notifications/conversation-pairing.ts +23 -3
  551. package/src/notifications/decision-engine.ts +1 -4
  552. package/src/notifications/decisions-store.ts +32 -1
  553. package/src/notifications/deliveries-store.ts +45 -0
  554. package/src/notifications/edit-notification.ts +201 -0
  555. package/src/notifications/emit-signal.ts +40 -50
  556. package/src/notifications/signal.ts +10 -0
  557. package/src/notifications/types.ts +37 -0
  558. package/src/oauth/byo-connection.test.ts +67 -3
  559. package/src/oauth/byo-connection.ts +32 -5
  560. package/src/oauth/connect-orchestrator.ts +9 -0
  561. package/src/oauth/connection-resolver.test.ts +76 -0
  562. package/src/oauth/connection-resolver.ts +49 -10
  563. package/src/oauth/manual-token-connection.ts +51 -3
  564. package/src/oauth/seed-providers.ts +3 -0
  565. package/src/permissions/approval-policy.test.ts +19 -5
  566. package/src/permissions/approval-policy.ts +14 -3
  567. package/src/permissions/checker.ts +21 -8
  568. package/src/permissions/prompter.ts +3 -3
  569. package/src/permissions/question-prompter.ts +5 -2
  570. package/src/permissions/secret-prompter.ts +2 -2
  571. package/src/platform/client.test.ts +24 -1
  572. package/src/platform/client.ts +8 -0
  573. package/src/platform/feature-gate.ts +15 -0
  574. package/src/plugin-api/index.ts +4 -0
  575. package/src/plugin-api/types.ts +7 -33
  576. package/src/plugins/defaults/index.ts +6 -0
  577. package/src/plugins/defaults/injectors.ts +20 -19
  578. package/src/plugins/defaults/persistence.ts +25 -6
  579. package/src/plugins/external-plugin-loader.ts +5 -68
  580. package/src/plugins/types.ts +68 -29
  581. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  582. package/src/proactive-artifact/job.test.ts +1 -0
  583. package/src/prompts/__tests__/system-prompt.test.ts +4 -4
  584. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  585. package/src/prompts/persona-resolver.ts +36 -21
  586. package/src/prompts/sections.ts +39 -7
  587. package/src/prompts/system-prompt.ts +84 -221
  588. package/src/prompts/template-detection.ts +10 -4
  589. package/src/prompts/templates/BOOTSTRAP.md +9 -13
  590. package/src/prompts/templates/IDENTITY.md +0 -2
  591. package/src/prompts/templates/system-sections.ts +230 -8
  592. package/src/providers/__tests__/connection-model-compat.test.ts +233 -0
  593. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  594. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  595. package/src/providers/anthropic/client.ts +32 -66
  596. package/src/providers/call-site-routing.ts +42 -6
  597. package/src/providers/connection-model-compat.ts +61 -0
  598. package/src/providers/connection-resolution.ts +47 -14
  599. package/src/providers/fireworks/client.ts +1 -0
  600. package/src/providers/gemini/client.ts +70 -6
  601. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  602. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  603. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  604. package/src/providers/inference/adapter-factory.ts +3 -0
  605. package/src/providers/inference/auth.ts +0 -8
  606. package/src/providers/inference/connections.ts +3 -66
  607. package/src/providers/inference/resolve-auth.ts +2 -3
  608. package/src/providers/minimax/client.ts +106 -0
  609. package/src/providers/model-catalog.ts +78 -1
  610. package/src/providers/model-intents.ts +4 -4
  611. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  612. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
  613. package/src/providers/openai/chat-completions-provider.ts +116 -15
  614. package/src/providers/openai/codex-models.ts +20 -0
  615. package/src/providers/openai/responses-provider.ts +87 -30
  616. package/src/providers/openrouter/client.ts +13 -8
  617. package/src/providers/provider-send-message.ts +20 -5
  618. package/src/providers/registry.ts +48 -8
  619. package/src/providers/retry.ts +50 -7
  620. package/src/providers/search-provider-catalog.ts +17 -9
  621. package/src/providers/thinking-config.ts +26 -1
  622. package/src/providers/types.ts +9 -0
  623. package/src/providers/usage-tracking.ts +2 -0
  624. package/src/runtime/AGENTS.md +2 -2
  625. package/src/runtime/__tests__/agent-wake.test.ts +1 -0
  626. package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
  627. package/src/runtime/access-request-helper.ts +1 -0
  628. package/src/runtime/agent-wake.ts +1 -0
  629. package/src/runtime/assistant-event-hub.ts +76 -6
  630. package/src/runtime/auth/route-policy.ts +46 -0
  631. package/src/runtime/btw-sidechain.ts +0 -6
  632. package/src/runtime/channel-readiness-service.ts +68 -0
  633. package/src/runtime/channel-reply-delivery.ts +23 -0
  634. package/src/runtime/channel-retry-sweep.ts +47 -14
  635. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  636. package/src/runtime/http-types.ts +0 -2
  637. package/src/runtime/migrations/vbundle-builder.ts +12 -4
  638. package/src/runtime/pending-interactions.ts +0 -1
  639. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  640. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
  641. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +204 -0
  642. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  643. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  644. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
  645. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +76 -9
  646. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
  647. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  648. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  649. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
  650. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  651. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  652. package/src/runtime/routes/acp-routes.test.ts +255 -6
  653. package/src/runtime/routes/acp-routes.ts +8 -1
  654. package/src/runtime/routes/app-management-routes.ts +111 -4
  655. package/src/runtime/routes/avatar-routes.ts +10 -10
  656. package/src/runtime/routes/background-wake-routes.ts +356 -0
  657. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  658. package/src/runtime/routes/btw-routes.ts +4 -10
  659. package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
  660. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  661. package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
  662. package/src/runtime/routes/conversation-list-routes.ts +159 -4
  663. package/src/runtime/routes/conversation-management-routes.ts +108 -26
  664. package/src/runtime/routes/conversation-query-routes.ts +200 -44
  665. package/src/runtime/routes/conversation-routes.ts +409 -521
  666. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  667. package/src/runtime/routes/conversations-import-routes.ts +19 -6
  668. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  669. package/src/runtime/routes/documents-routes.ts +10 -1
  670. package/src/runtime/routes/domain-routes.ts +60 -10
  671. package/src/runtime/routes/email-routes.ts +5 -2
  672. package/src/runtime/routes/events-routes.ts +54 -10
  673. package/src/runtime/routes/group-routes.ts +35 -8
  674. package/src/runtime/routes/home-feed-routes.ts +129 -0
  675. package/src/runtime/routes/host-browser-routes.ts +10 -2
  676. package/src/runtime/routes/host-cu-routes.ts +2 -2
  677. package/src/runtime/routes/identity-intro-cache.ts +61 -16
  678. package/src/runtime/routes/identity-routes.ts +30 -9
  679. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  680. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
  681. package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
  682. package/src/runtime/routes/index.ts +10 -0
  683. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  684. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  685. package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
  686. package/src/runtime/routes/integrations/vercel.ts +15 -0
  687. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  688. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  689. package/src/runtime/routes/memory-item-routes.ts +8 -3
  690. package/src/runtime/routes/memory-v2-routes.ts +215 -5
  691. package/src/runtime/routes/memory-v3-routes.ts +474 -0
  692. package/src/runtime/routes/migration-routes.ts +32 -28
  693. package/src/runtime/routes/notification-routes.ts +63 -1
  694. package/src/runtime/routes/oauth-commands-routes.ts +6 -1
  695. package/src/runtime/routes/plugins-routes.ts +337 -0
  696. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  697. package/src/runtime/routes/secret-routes.ts +25 -5
  698. package/src/runtime/routes/settings-routes.ts +12 -11
  699. package/src/runtime/routes/slack-channel-routes.ts +5 -4
  700. package/src/runtime/routes/surface-action-routes.ts +1 -38
  701. package/src/runtime/routes/surface-content-routes.ts +12 -5
  702. package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
  703. package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
  704. package/src/runtime/routes/workspace-routes.ts +25 -10
  705. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
  706. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  707. package/src/runtime/sync/resource-sync-events.ts +106 -38
  708. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  709. package/src/runtime/sync/sync-publisher.ts +2 -1
  710. package/src/runtime/tool-grant-request-helper.ts +1 -0
  711. package/src/runtime/verification-outbound-actions.ts +73 -1
  712. package/src/schedule/schedule-store.ts +8 -1
  713. package/src/schedule/scheduler.ts +111 -15
  714. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  715. package/src/security/encrypted-store.ts +7 -16
  716. package/src/security/store-path-override.ts +61 -0
  717. package/src/signals/user-message.ts +5 -8
  718. package/src/skills/validate-input.ts +177 -0
  719. package/src/subagent/manager.ts +13 -13
  720. package/src/subagent/types.ts +6 -0
  721. package/src/tasks/tool-sanitizer.ts +2 -2
  722. package/src/telemetry/types.ts +12 -0
  723. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  724. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  725. package/src/tools/acp/spawn.test.ts +119 -0
  726. package/src/tools/acp/spawn.ts +15 -2
  727. package/src/tools/apps/definitions.ts +36 -28
  728. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  729. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  730. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  731. package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
  732. package/src/tools/browser/browser-execution.ts +16 -3
  733. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  734. package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
  735. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
  736. package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
  737. package/src/tools/browser/cdp-client/factory.ts +100 -17
  738. package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
  739. package/src/tools/browser/cdp-client/types.ts +65 -0
  740. package/src/tools/browser/pinned-tabs.ts +96 -40
  741. package/src/tools/computer-use/definitions.ts +282 -336
  742. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  743. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  744. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  745. package/src/tools/credentials/vault.ts +3 -9
  746. package/src/tools/document/document-tool.ts +189 -7
  747. package/src/tools/execution-target.ts +18 -23
  748. package/src/tools/executor.ts +24 -56
  749. package/src/tools/filesystem/edit.ts +3 -9
  750. package/src/tools/filesystem/list.ts +3 -9
  751. package/src/tools/filesystem/read.ts +3 -9
  752. package/src/tools/filesystem/write.ts +3 -9
  753. package/src/tools/host-filesystem/edit.test.ts +1 -0
  754. package/src/tools/host-filesystem/edit.ts +3 -9
  755. package/src/tools/host-filesystem/read.test.ts +1 -0
  756. package/src/tools/host-filesystem/read.ts +3 -9
  757. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  758. package/src/tools/host-filesystem/transfer.ts +3 -9
  759. package/src/tools/host-filesystem/write.test.ts +1 -0
  760. package/src/tools/host-filesystem/write.ts +3 -9
  761. package/src/tools/host-terminal/host-shell.ts +3 -9
  762. package/src/tools/mcp/mcp-tool-factory.ts +1 -10
  763. package/src/tools/memory/register.test.ts +1 -1
  764. package/src/tools/memory/register.ts +4 -9
  765. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  766. package/src/tools/network/__tests__/web-search.test.ts +211 -3
  767. package/src/tools/network/managed-search-proxy.ts +183 -0
  768. package/src/tools/network/web-fetch.ts +3 -9
  769. package/src/tools/network/web-search.ts +224 -76
  770. package/src/tools/policy-context.ts +3 -1
  771. package/src/tools/registry.ts +150 -123
  772. package/src/tools/schedule/create.ts +1 -1
  773. package/src/tools/schema-transforms.ts +1 -1
  774. package/src/tools/skills/execute.ts +3 -9
  775. package/src/tools/skills/load.ts +3 -9
  776. package/src/tools/skills/skill-tool-factory.ts +18 -44
  777. package/src/tools/subagent/notify-parent.ts +3 -9
  778. package/src/tools/subagent/spawn.ts +3 -0
  779. package/src/tools/system/request-permission.ts +3 -9
  780. package/src/tools/terminal/shell.ts +3 -9
  781. package/src/tools/tool-approval-handler.ts +10 -4
  782. package/src/tools/tool-defaults.ts +94 -0
  783. package/src/tools/tool-name-aliases.ts +72 -14
  784. package/src/tools/types.ts +32 -101
  785. package/src/tools/ui-surface/definitions.ts +104 -108
  786. package/src/types/onboarding-context.ts +6 -0
  787. package/src/usage/attribution.ts +32 -1
  788. package/src/usage/pricing.ts +23 -0
  789. package/src/usage/types.ts +12 -0
  790. package/src/util/browser.ts +7 -2
  791. package/src/util/logger.ts +16 -7
  792. package/src/util/platform.ts +7 -2
  793. package/src/util/sqlite3-runtime.ts +65 -0
  794. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  795. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  796. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  797. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  798. package/src/workspace/migrations/registry.ts +6 -0
  799. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  800. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  801. package/src/daemon/query-complexity-router.ts +0 -75
  802. package/src/prompts/cache-boundary.ts +0 -8
@@ -193,6 +193,8 @@ mock.module("../memory/conversation-crud.js", () => ({
193
193
  updateConversationTitle: () => {},
194
194
  getMessageById: () => null,
195
195
  getLastUserTimestampBefore: () => 0,
196
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
197
+ updateMessageContent: mock(() => {}),
196
198
  }));
197
199
 
198
200
  mock.module("../memory/conversation-queries.js", () => ({
@@ -315,7 +317,7 @@ interface PendingRun {
315
317
  resolve: (history: Message[]) => void;
316
318
  reject: (err: Error) => void;
317
319
  messages: Message[];
318
- onEvent: (event: AgentEvent) => void;
320
+ onEvent: (event: AgentEvent) => void | Promise<void>;
319
321
  onCheckpoint?: (
320
322
  checkpoint: CheckpointInfo,
321
323
  ) => CheckpointDecision | Promise<CheckpointDecision>;
@@ -337,7 +339,7 @@ mock.module("../agent/loop.js", () => ({
337
339
  }
338
340
  async run(
339
341
  messages: Message[],
340
- onEvent: (event: AgentEvent) => void,
342
+ onEvent: (event: AgentEvent) => void | Promise<void>,
341
343
  _signal?: AbortSignal,
342
344
  _requestId?: string,
343
345
  onCheckpoint?: (
@@ -471,7 +473,7 @@ async function waitForCondition(
471
473
  * that `runAgentLoop` expects (usage + message_complete) so the conversation
472
474
  * cleanly transitions out of its processing state.
473
475
  */
474
- function resolveRun(index: number) {
476
+ async function resolveRun(index: number) {
475
477
  const run = pendingRuns[index];
476
478
  if (!run) throw new Error(`No pending run at index ${index}`);
477
479
  // Emit the events runAgentLoop expects
@@ -479,14 +481,17 @@ function resolveRun(index: number) {
479
481
  role: "assistant",
480
482
  content: [{ type: "text", text: `reply-${index}` }],
481
483
  };
482
- run.onEvent({
484
+ // Prime the assistant row anchor — production code emits this from
485
+ // `AgentLoop.run` just before `provider.sendMessage`.
486
+ await run.onEvent({ type: "llm_call_started" });
487
+ await run.onEvent({
483
488
  type: "usage",
484
489
  inputTokens: 10,
485
490
  outputTokens: 5,
486
491
  model: "mock",
487
492
  providerDurationMs: 100,
488
493
  });
489
- run.onEvent({ type: "message_complete", message: assistantMsg });
494
+ await run.onEvent({ type: "message_complete", message: assistantMsg });
490
495
  // Return updated history with the assistant message appended
491
496
  run.resolve([...run.messages, assistantMsg]);
492
497
  }
@@ -533,18 +538,17 @@ describe("Conversation message queue", () => {
533
538
  expect(conversation.isProcessing()).toBe(true);
534
539
 
535
540
  // Enqueue second message — should NOT throw
536
- const result = conversation.enqueueMessage(
537
- "msg-2",
538
- [],
539
- (e) => events2.push(e),
540
- "req-2",
541
- );
541
+ const result = conversation.enqueueMessage({
542
+ content: "msg-2",
543
+ onEvent: (e) => events2.push(e),
544
+ requestId: "req-2",
545
+ });
542
546
  expect(result.queued).toBe(true);
543
547
  expect(result.requestId).toBe("req-2");
544
548
  expect(conversation.getQueueDepth()).toBe(1);
545
549
 
546
550
  // Complete the first message
547
- resolveRun(0);
551
+ await resolveRun(0);
548
552
  await p1;
549
553
 
550
554
  // After the first run resolves, the queue drains and triggers a second run.
@@ -557,7 +561,7 @@ describe("Conversation message queue", () => {
557
561
  expect(pendingRuns.length).toBe(2);
558
562
 
559
563
  // Complete the second run
560
- resolveRun(1);
564
+ await resolveRun(1);
561
565
  await new Promise((r) => setTimeout(r, 10));
562
566
  });
563
567
 
@@ -579,12 +583,20 @@ describe("Conversation message queue", () => {
579
583
  await waitForPendingRun(1);
580
584
 
581
585
  // Enqueue two more sibling passthrough messages
582
- conversation.enqueueMessage("msg-2", [], (e) => events2.push(e), "req-2");
583
- conversation.enqueueMessage("msg-3", [], (e) => events3.push(e), "req-3");
586
+ conversation.enqueueMessage({
587
+ content: "msg-2",
588
+ onEvent: (e) => events2.push(e),
589
+ requestId: "req-2",
590
+ });
591
+ conversation.enqueueMessage({
592
+ content: "msg-3",
593
+ onEvent: (e) => events3.push(e),
594
+ requestId: "req-3",
595
+ });
584
596
  expect(conversation.getQueueDepth()).toBe(2);
585
597
 
586
598
  // Complete run 0 → drain pulls msg-2 and msg-3 into ONE batched run.
587
- resolveRun(0);
599
+ await resolveRun(0);
588
600
  await p1;
589
601
  await waitForPendingRun(2);
590
602
 
@@ -623,7 +635,7 @@ describe("Conversation message queue", () => {
623
635
  expect(combinedUserText).toContain("msg-3");
624
636
 
625
637
  // Resolve the batched run; message_complete must fan out to both clients.
626
- resolveRun(1);
638
+ await resolveRun(1);
627
639
  await new Promise((r) => setTimeout(r, 10));
628
640
 
629
641
  expect(events2.some((e) => e.type === "message_complete")).toBe(true);
@@ -641,16 +653,15 @@ describe("Conversation message queue", () => {
641
653
  await waitForPendingRun(1);
642
654
 
643
655
  // Enqueue second — simulating what handleUserMessage does
644
- const result = conversation.enqueueMessage(
645
- "msg-2",
646
- [],
647
- (e) => events2.push(e),
648
- "req-2",
649
- );
656
+ const result = conversation.enqueueMessage({
657
+ content: "msg-2",
658
+ onEvent: (e) => events2.push(e),
659
+ requestId: "req-2",
660
+ });
650
661
  expect(result.queued).toBe(true);
651
662
 
652
663
  // Complete first
653
- resolveRun(0);
664
+ await resolveRun(0);
654
665
  await p1;
655
666
  await waitForPendingRun(2);
656
667
 
@@ -664,7 +675,7 @@ describe("Conversation message queue", () => {
664
675
  });
665
676
 
666
677
  // Complete second run so the conversation finishes cleanly
667
- resolveRun(1);
678
+ await resolveRun(1);
668
679
  await new Promise((r) => setTimeout(r, 10));
669
680
  });
670
681
 
@@ -680,8 +691,16 @@ describe("Conversation message queue", () => {
680
691
  await waitForPendingRun(1);
681
692
 
682
693
  // Enqueue two more
683
- conversation.enqueueMessage("msg-2", [], (e) => events2.push(e), "req-2");
684
- conversation.enqueueMessage("msg-3", [], (e) => events3.push(e), "req-3");
694
+ conversation.enqueueMessage({
695
+ content: "msg-2",
696
+ onEvent: (e) => events2.push(e),
697
+ requestId: "req-2",
698
+ });
699
+ conversation.enqueueMessage({
700
+ content: "msg-3",
701
+ onEvent: (e) => events3.push(e),
702
+ requestId: "req-3",
703
+ });
685
704
  expect(conversation.getQueueDepth()).toBe(2);
686
705
 
687
706
  // Abort
@@ -759,18 +778,18 @@ describe("Conversation message queue", () => {
759
778
 
760
779
  expect(conversation.getQueueDepth()).toBe(0);
761
780
 
762
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
781
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
763
782
  expect(conversation.getQueueDepth()).toBe(1);
764
783
 
765
- conversation.enqueueMessage("msg-3", [], () => {}, "req-3");
784
+ conversation.enqueueMessage({ content: "msg-3", requestId: "req-3" });
766
785
  expect(conversation.getQueueDepth()).toBe(2);
767
786
 
768
- conversation.enqueueMessage("msg-4", [], () => {}, "req-4");
787
+ conversation.enqueueMessage({ content: "msg-4", requestId: "req-4" });
769
788
  expect(conversation.getQueueDepth()).toBe(3);
770
789
 
771
790
  // Complete first → drain pulls all three same-interface passthroughs
772
791
  // into a single batched run (depth → 0, runs → 2 total).
773
- resolveRun(0);
792
+ await resolveRun(0);
774
793
  await p1;
775
794
  await waitForPendingRun(2);
776
795
 
@@ -778,7 +797,7 @@ describe("Conversation message queue", () => {
778
797
  expect(pendingRuns.length).toBe(2);
779
798
 
780
799
  // Complete the batched run; conversation finishes cleanly.
781
- resolveRun(1);
800
+ await resolveRun(1);
782
801
  await new Promise((r) => setTimeout(r, 10));
783
802
  });
784
803
 
@@ -800,14 +819,22 @@ describe("Conversation message queue", () => {
800
819
  await waitForPendingRun(1);
801
820
 
802
821
  // Enqueue a message with empty content (will fail persistUserMessage)
803
- conversation.enqueueMessage("", [], (e) => events2.push(e), "req-2");
822
+ conversation.enqueueMessage({
823
+ content: "",
824
+ onEvent: (e) => events2.push(e),
825
+ requestId: "req-2",
826
+ });
804
827
  // Enqueue a valid message after the bad one
805
- conversation.enqueueMessage("msg-3", [], (e) => events3.push(e), "req-3");
828
+ conversation.enqueueMessage({
829
+ content: "msg-3",
830
+ onEvent: (e) => events3.push(e),
831
+ requestId: "req-3",
832
+ });
806
833
  expect(conversation.getQueueDepth()).toBe(2);
807
834
 
808
835
  // Complete first message — triggers drain. The empty message should fail
809
836
  // to persist, but the drain should continue to msg-3.
810
- resolveRun(0);
837
+ await resolveRun(0);
811
838
  await p1;
812
839
 
813
840
  // msg-3 should have been dequeued and started a new AgentLoop.run
@@ -824,7 +851,7 @@ describe("Conversation message queue", () => {
824
851
  expect(events3.some((e) => e.type === "message_dequeued")).toBe(true);
825
852
 
826
853
  // Complete the third message's run
827
- resolveRun(1);
854
+ await resolveRun(1);
828
855
  await new Promise((r) => setTimeout(r, 10));
829
856
 
830
857
  // msg-3 should have completed successfully
@@ -860,46 +887,34 @@ describe("Batched drain", () => {
860
887
  userMessageInterface: iface,
861
888
  assistantMessageInterface: iface,
862
889
  });
863
- conversation.enqueueMessage(
864
- "msg-2",
865
- [],
866
- (e) => events2.push(e),
867
- "req-2",
868
- undefined,
869
- undefined,
870
- meta("macos"),
871
- );
872
- conversation.enqueueMessage(
873
- "msg-3",
874
- [],
875
- (e) => events3.push(e),
876
- "req-3",
877
- undefined,
878
- undefined,
879
- meta("macos"),
880
- );
881
- conversation.enqueueMessage(
882
- "msg-4",
883
- [],
884
- (e) => events4.push(e),
885
- "req-4",
886
- undefined,
887
- undefined,
888
- meta("cli"),
889
- );
890
- conversation.enqueueMessage(
891
- "msg-5",
892
- [],
893
- (e) => events5.push(e),
894
- "req-5",
895
- undefined,
896
- undefined,
897
- meta("macos"),
898
- );
890
+ conversation.enqueueMessage({
891
+ content: "msg-2",
892
+ onEvent: (e) => events2.push(e),
893
+ requestId: "req-2",
894
+ metadata: meta("macos"),
895
+ });
896
+ conversation.enqueueMessage({
897
+ content: "msg-3",
898
+ onEvent: (e) => events3.push(e),
899
+ requestId: "req-3",
900
+ metadata: meta("macos"),
901
+ });
902
+ conversation.enqueueMessage({
903
+ content: "msg-4",
904
+ onEvent: (e) => events4.push(e),
905
+ requestId: "req-4",
906
+ metadata: meta("cli"),
907
+ });
908
+ conversation.enqueueMessage({
909
+ content: "msg-5",
910
+ onEvent: (e) => events5.push(e),
911
+ requestId: "req-5",
912
+ metadata: meta("macos"),
913
+ });
899
914
  expect(conversation.getQueueDepth()).toBe(4);
900
915
 
901
916
  // Resolve msg-1 → batched run pulls macos msg-2 + msg-3.
902
- resolveRun(0);
917
+ await resolveRun(0);
903
918
  await p1;
904
919
  await waitForPendingRun(2);
905
920
 
@@ -927,7 +942,7 @@ describe("Batched drain", () => {
927
942
  );
928
943
 
929
944
  // Resolve the batched run → drain pulls the cli single-message run.
930
- resolveRun(1);
945
+ await resolveRun(1);
931
946
  await waitForPendingRun(3);
932
947
 
933
948
  // cli run contains msg-4 as a single-message run.
@@ -942,7 +957,7 @@ describe("Batched drain", () => {
942
957
  );
943
958
 
944
959
  // Resolve the cli run → drain pulls the final macos single-message run.
945
- resolveRun(2);
960
+ await resolveRun(2);
946
961
  await waitForPendingRun(4);
947
962
  const finalHistory = pendingRuns[3].messages;
948
963
  const finalUserText = finalHistory
@@ -957,7 +972,7 @@ describe("Batched drain", () => {
957
972
  // Four total runs: msg-1, batched [msg-2, msg-3], msg-4, msg-5.
958
973
  expect(pendingRuns.length).toBe(4);
959
974
 
960
- resolveRun(3);
975
+ await resolveRun(3);
961
976
  await new Promise((r) => setTimeout(r, 10));
962
977
  });
963
978
 
@@ -977,29 +992,26 @@ describe("Batched drain", () => {
977
992
  // passthrough slash, so the batch builder stops at "hello" (length 1),
978
993
  // then /compact takes the single-message /compact short-circuit path
979
994
  // (no new runAgentLoop invocation), then "world" drains as its own run.
980
- conversation.enqueueMessage(
981
- "hello",
982
- [],
983
- (e) => eventsHello.push(e),
984
- "req-hello",
985
- );
986
- conversation.enqueueMessage(
987
- "/compact",
988
- [],
989
- (e) => eventsSlash.push(e),
990
- "req-slash",
991
- );
992
- conversation.enqueueMessage(
993
- "world",
994
- [],
995
- (e) => eventsWorld.push(e),
996
- "req-world",
997
- );
995
+ conversation.enqueueMessage({
996
+ content: "hello",
997
+ onEvent: (e) => eventsHello.push(e),
998
+ requestId: "req-hello",
999
+ });
1000
+ conversation.enqueueMessage({
1001
+ content: "/compact",
1002
+ onEvent: (e) => eventsSlash.push(e),
1003
+ requestId: "req-slash",
1004
+ });
1005
+ conversation.enqueueMessage({
1006
+ content: "world",
1007
+ onEvent: (e) => eventsWorld.push(e),
1008
+ requestId: "req-world",
1009
+ });
998
1010
  expect(conversation.getQueueDepth()).toBe(3);
999
1011
 
1000
1012
  // Resolve msg-1 → drain pulls "hello" as its own run (batch stops at
1001
1013
  // /compact boundary).
1002
- resolveRun(0);
1014
+ await resolveRun(0);
1003
1015
  await p1;
1004
1016
  await waitForPendingRun(2);
1005
1017
 
@@ -1010,7 +1022,7 @@ describe("Batched drain", () => {
1010
1022
 
1011
1023
  // Resolve "hello" → drain pops /compact via the builder-rejected path,
1012
1024
  // runs its short-circuit (no new runAgentLoop), then drains "world".
1013
- resolveRun(1);
1025
+ await resolveRun(1);
1014
1026
  await waitForPendingRun(3);
1015
1027
 
1016
1028
  // /compact should have emitted its own message_complete via the short-
@@ -1019,7 +1031,7 @@ describe("Batched drain", () => {
1019
1031
  expect(eventsWorld.some((e) => e.type === "message_dequeued")).toBe(true);
1020
1032
  expect(pendingRuns.length).toBe(3);
1021
1033
 
1022
- resolveRun(2);
1034
+ await resolveRun(2);
1023
1035
  await new Promise((r) => setTimeout(r, 10));
1024
1036
  });
1025
1037
 
@@ -1051,29 +1063,26 @@ describe("Batched drain", () => {
1051
1063
  // unknown-slash short-circuit path (no new runAgentLoop invocation — it
1052
1064
  // emits assistant_text_delta + message_complete inline), then "plain-b"
1053
1065
  // drains as its own run.
1054
- conversation.enqueueMessage(
1055
- "plain-a",
1056
- [],
1057
- (e) => eventsPlainA.push(e),
1058
- "req-plain-a",
1059
- );
1060
- conversation.enqueueMessage(
1061
- "/status",
1062
- [],
1063
- (e) => eventsSlash.push(e),
1064
- "req-slash",
1065
- );
1066
- conversation.enqueueMessage(
1067
- "plain-b",
1068
- [],
1069
- (e) => eventsPlainB.push(e),
1070
- "req-plain-b",
1071
- );
1066
+ conversation.enqueueMessage({
1067
+ content: "plain-a",
1068
+ onEvent: (e) => eventsPlainA.push(e),
1069
+ requestId: "req-plain-a",
1070
+ });
1071
+ conversation.enqueueMessage({
1072
+ content: "/status",
1073
+ onEvent: (e) => eventsSlash.push(e),
1074
+ requestId: "req-slash",
1075
+ });
1076
+ conversation.enqueueMessage({
1077
+ content: "plain-b",
1078
+ onEvent: (e) => eventsPlainB.push(e),
1079
+ requestId: "req-plain-b",
1080
+ });
1072
1081
  expect(conversation.getQueueDepth()).toBe(3);
1073
1082
 
1074
1083
  // Resolve msg-1 → drain pulls "plain-a" as its own run (batch stops at
1075
1084
  // the /status boundary).
1076
- resolveRun(0);
1085
+ await resolveRun(0);
1077
1086
  await p1;
1078
1087
  await waitForPendingRun(2);
1079
1088
 
@@ -1086,7 +1095,7 @@ describe("Batched drain", () => {
1086
1095
  // runs its unknown-slash short-circuit (no new runAgentLoop, emits
1087
1096
  // assistant_text_delta + message_complete inline), then drains "plain-b"
1088
1097
  // as its own run.
1089
- resolveRun(1);
1098
+ await resolveRun(1);
1090
1099
  await waitForPendingRun(3);
1091
1100
 
1092
1101
  // /status should have emitted its own assistant_text_delta + message_complete
@@ -1100,7 +1109,7 @@ describe("Batched drain", () => {
1100
1109
  // without a runAgentLoop invocation.
1101
1110
  expect(pendingRuns.length).toBe(3);
1102
1111
 
1103
- resolveRun(2);
1112
+ await resolveRun(2);
1104
1113
  await new Promise((r) => setTimeout(r, 10));
1105
1114
  });
1106
1115
 
@@ -1132,11 +1141,19 @@ describe("Batched drain", () => {
1132
1141
  filePath: "/tmp/b.png",
1133
1142
  },
1134
1143
  ];
1135
- conversation.enqueueMessage("with-A", attachA, () => {}, "req-A");
1136
- conversation.enqueueMessage("with-B", attachB, () => {}, "req-B");
1144
+ conversation.enqueueMessage({
1145
+ content: "with-A",
1146
+ attachments: attachA,
1147
+ requestId: "req-A",
1148
+ });
1149
+ conversation.enqueueMessage({
1150
+ content: "with-B",
1151
+ attachments: attachB,
1152
+ requestId: "req-B",
1153
+ });
1137
1154
  expect(conversation.getQueueDepth()).toBe(2);
1138
1155
 
1139
- resolveRun(0);
1156
+ await resolveRun(0);
1140
1157
  await p1;
1141
1158
  await waitForPendingRun(2);
1142
1159
 
@@ -1170,7 +1187,7 @@ describe("Batched drain", () => {
1170
1187
  expect(allText).toContain("a.png");
1171
1188
  expect(allText).toContain("b.png");
1172
1189
 
1173
- resolveRun(1);
1190
+ await resolveRun(1);
1174
1191
  await new Promise((r) => setTimeout(r, 10));
1175
1192
  });
1176
1193
 
@@ -1189,30 +1206,25 @@ describe("Batched drain", () => {
1189
1206
  await waitForPendingRun(1);
1190
1207
 
1191
1208
  // Fill to just-under budget: two ~500-char messages (1512+1512 = 3024 bytes).
1192
- const accepted1 = conversation.enqueueMessage(
1193
- "x".repeat(500),
1194
- [],
1195
- () => {},
1196
- "req-big-1",
1197
- );
1198
- const accepted2 = conversation.enqueueMessage(
1199
- "y".repeat(500),
1200
- [],
1201
- () => {},
1202
- "req-big-2",
1203
- );
1209
+ const accepted1 = conversation.enqueueMessage({
1210
+ content: "x".repeat(500),
1211
+ requestId: "req-big-1",
1212
+ });
1213
+ const accepted2 = conversation.enqueueMessage({
1214
+ content: "y".repeat(500),
1215
+ requestId: "req-big-2",
1216
+ });
1204
1217
  expect(accepted1.queued).toBe(true);
1205
1218
  expect(accepted2.queued).toBe(true);
1206
1219
  // A third would push the queue over budget → rejected. Capture its
1207
1220
  // onEvent callback so we can verify the queue_full error event reaches
1208
1221
  // the rejected caller (not just the synchronous return value).
1209
1222
  const rejectedEvents: ServerMessage[] = [];
1210
- const rejected = conversation.enqueueMessage(
1211
- "z".repeat(500),
1212
- [],
1213
- (e) => rejectedEvents.push(e),
1214
- "req-over",
1215
- );
1223
+ const rejected = conversation.enqueueMessage({
1224
+ content: "z".repeat(500),
1225
+ onEvent: (e) => rejectedEvents.push(e),
1226
+ requestId: "req-over",
1227
+ });
1216
1228
  expect(rejected.queued).toBe(false);
1217
1229
  expect(rejected.rejected).toBe(true);
1218
1230
  expect(conversation.getQueueDepth()).toBe(2);
@@ -1231,13 +1243,13 @@ describe("Batched drain", () => {
1231
1243
  }
1232
1244
 
1233
1245
  // Complete in-flight → drain pulls both queued passthroughs as ONE batched run.
1234
- resolveRun(0);
1246
+ await resolveRun(0);
1235
1247
  await p1;
1236
1248
  await waitForPendingRun(2);
1237
1249
  expect(conversation.getQueueDepth()).toBe(0);
1238
1250
 
1239
1251
  // Resolve the batched run.
1240
- resolveRun(1);
1252
+ await resolveRun(1);
1241
1253
  await new Promise((r) => setTimeout(r, 10));
1242
1254
 
1243
1255
  // After the full drain, the byte budget must be fully reclaimed — a fresh
@@ -1246,18 +1258,22 @@ describe("Batched drain", () => {
1246
1258
  const p2 = conversation.processMessage("msg-2", [], () => {}, "req-2");
1247
1259
  await waitForPendingRun(3);
1248
1260
  expect(
1249
- conversation.enqueueMessage("a".repeat(500), [], () => {}, "req-a")
1250
- .queued,
1261
+ conversation.enqueueMessage({
1262
+ content: "a".repeat(500),
1263
+ requestId: "req-a",
1264
+ }).queued,
1251
1265
  ).toBe(true);
1252
1266
  expect(
1253
- conversation.enqueueMessage("b".repeat(500), [], () => {}, "req-b")
1254
- .queued,
1267
+ conversation.enqueueMessage({
1268
+ content: "b".repeat(500),
1269
+ requestId: "req-b",
1270
+ }).queued,
1255
1271
  ).toBe(true);
1256
1272
 
1257
- resolveRun(2);
1273
+ await resolveRun(2);
1258
1274
  await p2;
1259
1275
  await waitForPendingRun(4);
1260
- resolveRun(3);
1276
+ await resolveRun(3);
1261
1277
  await new Promise((r) => setTimeout(r, 10));
1262
1278
  });
1263
1279
  });
@@ -1289,25 +1305,23 @@ describe("Batched drain correctness fixes", () => {
1289
1305
  // same interface. The batch builder must reject the surface-action head
1290
1306
  // so each drains as its own run.
1291
1307
  conversation.surfaceActionRequestIds.add("req-surface");
1292
- conversation.enqueueMessage(
1293
- "surface action response",
1294
- [],
1295
- (e) => eventsSurface.push(e),
1296
- "req-surface",
1297
- "surface-1", // activeSurfaceId
1298
- );
1299
- conversation.enqueueMessage(
1300
- "regular follow-up",
1301
- [],
1302
- (e) => eventsRegular.push(e),
1303
- "req-regular",
1304
- );
1308
+ conversation.enqueueMessage({
1309
+ content: "surface action response",
1310
+ onEvent: (e) => eventsSurface.push(e),
1311
+ requestId: "req-surface",
1312
+ activeSurfaceId: "surface-1",
1313
+ });
1314
+ conversation.enqueueMessage({
1315
+ content: "regular follow-up",
1316
+ onEvent: (e) => eventsRegular.push(e),
1317
+ requestId: "req-regular",
1318
+ });
1305
1319
  expect(conversation.getQueueDepth()).toBe(2);
1306
1320
 
1307
1321
  // Complete run 0 → drain must NOT batch the surface-action with the
1308
1322
  // regular passthrough. Expect the surface-action to drain as a single
1309
1323
  // run first.
1310
- resolveRun(0);
1324
+ await resolveRun(0);
1311
1325
  await p1;
1312
1326
  await waitForPendingRun(2);
1313
1327
 
@@ -1322,7 +1336,7 @@ describe("Batched drain correctness fixes", () => {
1322
1336
 
1323
1337
  // Complete the surface-action run; drain pulls the regular passthrough
1324
1338
  // as its own separate run.
1325
- resolveRun(1);
1339
+ await resolveRun(1);
1326
1340
  await waitForPendingRun(3);
1327
1341
  expect(pendingRuns.length).toBe(3);
1328
1342
  expect(
@@ -1331,7 +1345,7 @@ describe("Batched drain correctness fixes", () => {
1331
1345
 
1332
1346
  // Total runs = 3: msg-1, surface-action, regular — NOT 2 (would mean
1333
1347
  // they were batched).
1334
- resolveRun(2);
1348
+ await resolveRun(2);
1335
1349
  await new Promise((r) => setTimeout(r, 10));
1336
1350
  });
1337
1351
 
@@ -1360,7 +1374,11 @@ describe("Batched drain correctness fixes", () => {
1360
1374
  // fresh one). Calling abort() now aborts that fresh controller, and
1361
1375
  // the drainBatch loop's abort check after msg-3's persist will break,
1362
1376
  // so msg-4 never persists.
1363
- conversation.enqueueMessage("msg-2", [], (e) => events2.push(e), "req-2");
1377
+ conversation.enqueueMessage({
1378
+ content: "msg-2",
1379
+ onEvent: (e) => events2.push(e),
1380
+ requestId: "req-2",
1381
+ });
1364
1382
 
1365
1383
  // Install a one-shot abort trigger on msg-3's dequeue event. We do
1366
1384
  // this before enqueueing so the wrapped callback is what drainBatch
@@ -1373,8 +1391,16 @@ describe("Batched drain correctness fixes", () => {
1373
1391
  conversation.abort();
1374
1392
  }
1375
1393
  };
1376
- conversation.enqueueMessage("msg-3", [], onMsg3Event, "req-3");
1377
- conversation.enqueueMessage("msg-4", [], (e) => events4.push(e), "req-4");
1394
+ conversation.enqueueMessage({
1395
+ content: "msg-3",
1396
+ onEvent: onMsg3Event,
1397
+ requestId: "req-3",
1398
+ });
1399
+ conversation.enqueueMessage({
1400
+ content: "msg-4",
1401
+ onEvent: (e) => events4.push(e),
1402
+ requestId: "req-4",
1403
+ });
1378
1404
  expect(conversation.getQueueDepth()).toBe(3);
1379
1405
 
1380
1406
  const persistedUserRowCountBefore = capturedAddMessages.filter(
@@ -1382,7 +1408,7 @@ describe("Batched drain correctness fixes", () => {
1382
1408
  ).length;
1383
1409
 
1384
1410
  // Complete run 0 → drain pulls the sibling batch.
1385
- resolveRun(0);
1411
+ await resolveRun(0);
1386
1412
  await p1;
1387
1413
 
1388
1414
  // Give the drain loop a chance to iterate. Abort happens on msg-3's
@@ -1426,28 +1452,25 @@ describe("Batched drain correctness fixes", () => {
1426
1452
  // msg-tail's requestId (the LAST successful persist), not msg-mid's.
1427
1453
  addMessageShouldThrowForContent.add("msg-mid-unique-marker");
1428
1454
 
1429
- conversation.enqueueMessage(
1430
- "msg-head",
1431
- [],
1432
- (e) => events2.push(e),
1433
- "req-head",
1434
- );
1435
- conversation.enqueueMessage(
1436
- "msg-mid-unique-marker",
1437
- [],
1438
- (e) => events3.push(e),
1439
- "req-mid",
1440
- );
1441
- conversation.enqueueMessage(
1442
- "msg-tail",
1443
- [],
1444
- (e) => events4.push(e),
1445
- "req-tail",
1446
- );
1455
+ conversation.enqueueMessage({
1456
+ content: "msg-head",
1457
+ onEvent: (e) => events2.push(e),
1458
+ requestId: "req-head",
1459
+ });
1460
+ conversation.enqueueMessage({
1461
+ content: "msg-mid-unique-marker",
1462
+ onEvent: (e) => events3.push(e),
1463
+ requestId: "req-mid",
1464
+ });
1465
+ conversation.enqueueMessage({
1466
+ content: "msg-tail",
1467
+ onEvent: (e) => events4.push(e),
1468
+ requestId: "req-tail",
1469
+ });
1447
1470
  expect(conversation.getQueueDepth()).toBe(3);
1448
1471
 
1449
1472
  // Complete run 0 → batched drain.
1450
- resolveRun(0);
1473
+ await resolveRun(0);
1451
1474
  await p1;
1452
1475
  await waitForPendingRun(2);
1453
1476
 
@@ -1464,7 +1487,7 @@ describe("Batched drain correctness fixes", () => {
1464
1487
  ).toBe("req-tail");
1465
1488
 
1466
1489
  // Cleanup: resolve the batched run.
1467
- resolveRun(1);
1490
+ await resolveRun(1);
1468
1491
  await new Promise((r) => setTimeout(r, 20));
1469
1492
  });
1470
1493
 
@@ -1492,31 +1515,28 @@ describe("Batched drain correctness fixes", () => {
1492
1515
  // desync the client.
1493
1516
  addMessageShouldThrowForContent.add("fanout-mid-marker");
1494
1517
 
1495
- conversation.enqueueMessage(
1496
- "fanout-head",
1497
- [],
1498
- (e) => events2.push(e),
1499
- "req-fanout-head",
1500
- );
1501
- conversation.enqueueMessage(
1502
- "fanout-mid-marker",
1503
- [],
1504
- (e) => events3.push(e),
1505
- "req-fanout-mid",
1506
- );
1507
- conversation.enqueueMessage(
1508
- "fanout-tail",
1509
- [],
1510
- (e) => events4.push(e),
1511
- "req-fanout-tail",
1512
- );
1518
+ conversation.enqueueMessage({
1519
+ content: "fanout-head",
1520
+ onEvent: (e) => events2.push(e),
1521
+ requestId: "req-fanout-head",
1522
+ });
1523
+ conversation.enqueueMessage({
1524
+ content: "fanout-mid-marker",
1525
+ onEvent: (e) => events3.push(e),
1526
+ requestId: "req-fanout-mid",
1527
+ });
1528
+ conversation.enqueueMessage({
1529
+ content: "fanout-tail",
1530
+ onEvent: (e) => events4.push(e),
1531
+ requestId: "req-fanout-tail",
1532
+ });
1513
1533
 
1514
- resolveRun(0);
1534
+ await resolveRun(0);
1515
1535
  await p1;
1516
1536
  await waitForPendingRun(2);
1517
1537
 
1518
1538
  // Drive the batched run to emit message_complete via fanOutOnEvent.
1519
- resolveRun(1);
1539
+ await resolveRun(1);
1520
1540
  await new Promise((r) => setTimeout(r, 20));
1521
1541
 
1522
1542
  expect(events3.find((e) => e.type === "error")).toBeDefined();
@@ -1544,12 +1564,12 @@ describe("Batched drain correctness fixes", () => {
1544
1564
  const baseline = activityStates.length;
1545
1565
 
1546
1566
  // Enqueue three sibling passthroughs.
1547
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
1548
- conversation.enqueueMessage("msg-3", [], () => {}, "req-3");
1549
- conversation.enqueueMessage("msg-4", [], () => {}, "req-4");
1567
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
1568
+ conversation.enqueueMessage({ content: "msg-3", requestId: "req-3" });
1569
+ conversation.enqueueMessage({ content: "msg-4", requestId: "req-4" });
1550
1570
 
1551
1571
  // Complete run 0 → drain pulls the batched siblings as ONE run.
1552
- resolveRun(0);
1572
+ await resolveRun(0);
1553
1573
  await p1;
1554
1574
  await waitForPendingRun(2);
1555
1575
 
@@ -1569,7 +1589,7 @@ describe("Batched drain correctness fixes", () => {
1569
1589
  requestId: "req-2", // head's requestId, per the fix
1570
1590
  });
1571
1591
 
1572
- resolveRun(1);
1592
+ await resolveRun(1);
1573
1593
  await new Promise((r) => setTimeout(r, 10));
1574
1594
  });
1575
1595
 
@@ -1614,13 +1634,13 @@ describe("Conversation queue policy helpers", () => {
1614
1634
  await waitForPendingRun(1);
1615
1635
 
1616
1636
  // Enqueue a message while processing
1617
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
1637
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
1618
1638
  expect(conversation.hasQueuedMessages()).toBe(true);
1619
1639
 
1620
1640
  // Cleanup: resolve the pending run
1621
- resolveRun(0);
1641
+ await resolveRun(0);
1622
1642
  await waitForPendingRun(2);
1623
- resolveRun(1);
1643
+ await resolveRun(1);
1624
1644
  await new Promise((r) => setTimeout(r, 10));
1625
1645
  });
1626
1646
 
@@ -1645,7 +1665,7 @@ describe("Conversation queue policy helpers", () => {
1645
1665
  expect(conversation.canHandoffAtCheckpoint()).toBe(false);
1646
1666
 
1647
1667
  // Cleanup
1648
- resolveRun(0);
1668
+ await resolveRun(0);
1649
1669
  await new Promise((r) => setTimeout(r, 10));
1650
1670
  });
1651
1671
 
@@ -1658,16 +1678,16 @@ describe("Conversation queue policy helpers", () => {
1658
1678
  await waitForPendingRun(1);
1659
1679
 
1660
1680
  // Enqueue a message
1661
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
1681
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
1662
1682
 
1663
1683
  expect(conversation.isProcessing()).toBe(true);
1664
1684
  expect(conversation.hasQueuedMessages()).toBe(true);
1665
1685
  expect(conversation.canHandoffAtCheckpoint()).toBe(true);
1666
1686
 
1667
1687
  // Cleanup
1668
- resolveRun(0);
1688
+ await resolveRun(0);
1669
1689
  await waitForPendingRun(2);
1670
- resolveRun(1);
1690
+ await resolveRun(1);
1671
1691
  await new Promise((r) => setTimeout(r, 10));
1672
1692
  });
1673
1693
 
@@ -1714,7 +1734,7 @@ describe("Conversation checkpoint handoff", () => {
1714
1734
  await waitForPendingRun(1);
1715
1735
 
1716
1736
  // Enqueue a second message while the first is processing
1717
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
1737
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
1718
1738
  expect(conversation.hasQueuedMessages()).toBe(true);
1719
1739
 
1720
1740
  // The pending run should have received an onCheckpoint callback.
@@ -1732,7 +1752,7 @@ describe("Conversation checkpoint handoff", () => {
1732
1752
  expect(decision).toBe("yield");
1733
1753
 
1734
1754
  // Complete the run so the conversation finishes cleanly
1735
- resolveRun(0);
1755
+ await resolveRun(0);
1736
1756
  await p1;
1737
1757
 
1738
1758
  // After yield, the first message should emit generation_handoff
@@ -1747,7 +1767,7 @@ describe("Conversation checkpoint handoff", () => {
1747
1767
 
1748
1768
  // The queued message should now be draining (second run started)
1749
1769
  await waitForPendingRun(2);
1750
- resolveRun(1);
1770
+ await resolveRun(1);
1751
1771
  await new Promise((r) => setTimeout(r, 10));
1752
1772
  });
1753
1773
 
@@ -1775,7 +1795,7 @@ describe("Conversation checkpoint handoff", () => {
1775
1795
  expect(decision).toBe("continue");
1776
1796
 
1777
1797
  // Cleanup
1778
- resolveRun(0);
1798
+ await resolveRun(0);
1779
1799
  await p1;
1780
1800
  });
1781
1801
 
@@ -1798,9 +1818,21 @@ describe("Conversation checkpoint handoff", () => {
1798
1818
  await waitForPendingRun(1);
1799
1819
 
1800
1820
  // Enqueue three sibling passthroughs while msg-1 is mid-turn
1801
- conversation.enqueueMessage("msg-2", [], (e) => events2.push(e), "req-2");
1802
- conversation.enqueueMessage("msg-3", [], (e) => events3.push(e), "req-3");
1803
- conversation.enqueueMessage("msg-4", [], (e) => events4.push(e), "req-4");
1821
+ conversation.enqueueMessage({
1822
+ content: "msg-2",
1823
+ onEvent: (e) => events2.push(e),
1824
+ requestId: "req-2",
1825
+ });
1826
+ conversation.enqueueMessage({
1827
+ content: "msg-3",
1828
+ onEvent: (e) => events3.push(e),
1829
+ requestId: "req-3",
1830
+ });
1831
+ conversation.enqueueMessage({
1832
+ content: "msg-4",
1833
+ onEvent: (e) => events4.push(e),
1834
+ requestId: "req-4",
1835
+ });
1804
1836
  expect(conversation.getQueueDepth()).toBe(3);
1805
1837
 
1806
1838
  // Simulate the agent loop yielding at the checkpoint (first run is mid-tool-use)
@@ -1815,7 +1847,7 @@ describe("Conversation checkpoint handoff", () => {
1815
1847
  expect(decision).toBe("yield");
1816
1848
 
1817
1849
  // Complete first run
1818
- resolveRun(0);
1850
+ await resolveRun(0);
1819
1851
  await p1;
1820
1852
 
1821
1853
  // The yielded drain pulls ALL THREE queued siblings as ONE batched run —
@@ -1829,7 +1861,7 @@ describe("Conversation checkpoint handoff", () => {
1829
1861
  expect(events4.some((e) => e.type === "message_dequeued")).toBe(true);
1830
1862
 
1831
1863
  // Resolve the batched run — message_complete fans out to all three clients.
1832
- resolveRun(1);
1864
+ await resolveRun(1);
1833
1865
  await new Promise((r) => setTimeout(r, 10));
1834
1866
 
1835
1867
  expect(events2.some((e) => e.type === "message_complete")).toBe(true);
@@ -1854,7 +1886,11 @@ describe("Conversation checkpoint handoff", () => {
1854
1886
  await waitForPendingRun(1);
1855
1887
 
1856
1888
  // Enqueue a second message while the first is processing
1857
- conversation.enqueueMessage("msg-2", [], (e) => events2.push(e), "req-2");
1889
+ conversation.enqueueMessage({
1890
+ content: "msg-2",
1891
+ onEvent: (e) => events2.push(e),
1892
+ requestId: "req-2",
1893
+ });
1858
1894
  expect(conversation.hasQueuedMessages()).toBe(true);
1859
1895
 
1860
1896
  // Simulate tool-use turns: the agent loop calls onCheckpoint at each turn boundary.
@@ -1873,7 +1909,7 @@ describe("Conversation checkpoint handoff", () => {
1873
1909
  expect(decision).toBe("yield");
1874
1910
 
1875
1911
  // Complete the run (AgentLoop resolves after yielding)
1876
- resolveRun(0);
1912
+ await resolveRun(0);
1877
1913
  await p1;
1878
1914
 
1879
1915
  // Verify generation_handoff was emitted (not plain message_complete)
@@ -1896,7 +1932,7 @@ describe("Conversation checkpoint handoff", () => {
1896
1932
  expect(events2.some((e) => e.type === "message_dequeued")).toBe(true);
1897
1933
 
1898
1934
  // Complete the second run
1899
- resolveRun(1);
1935
+ await resolveRun(1);
1900
1936
  await new Promise((r) => setTimeout(r, 10));
1901
1937
  });
1902
1938
 
@@ -1926,33 +1962,24 @@ describe("Conversation checkpoint handoff", () => {
1926
1962
  userMessageInterface: iface,
1927
1963
  assistantMessageInterface: iface,
1928
1964
  });
1929
- conversation.enqueueMessage(
1930
- "msg-B",
1931
- [],
1932
- makeHandler("B"),
1933
- "req-B",
1934
- undefined,
1935
- undefined,
1936
- meta("macos"),
1937
- );
1938
- conversation.enqueueMessage(
1939
- "msg-C",
1940
- [],
1941
- makeHandler("C"),
1942
- "req-C",
1943
- undefined,
1944
- undefined,
1945
- meta("cli"),
1946
- );
1947
- conversation.enqueueMessage(
1948
- "msg-D",
1949
- [],
1950
- makeHandler("D"),
1951
- "req-D",
1952
- undefined,
1953
- undefined,
1954
- meta("vellum"),
1955
- );
1965
+ conversation.enqueueMessage({
1966
+ content: "msg-B",
1967
+ onEvent: makeHandler("B"),
1968
+ requestId: "req-B",
1969
+ metadata: meta("macos"),
1970
+ });
1971
+ conversation.enqueueMessage({
1972
+ content: "msg-C",
1973
+ onEvent: makeHandler("C"),
1974
+ requestId: "req-C",
1975
+ metadata: meta("cli"),
1976
+ });
1977
+ conversation.enqueueMessage({
1978
+ content: "msg-D",
1979
+ onEvent: makeHandler("D"),
1980
+ requestId: "req-D",
1981
+ metadata: meta("vellum"),
1982
+ });
1956
1983
  expect(conversation.getQueueDepth()).toBe(3);
1957
1984
 
1958
1985
  // Handoff from A -> B
@@ -1966,7 +1993,7 @@ describe("Conversation checkpoint handoff", () => {
1966
1993
  history: [],
1967
1994
  }),
1968
1995
  ).toBe("yield");
1969
- resolveRun(0);
1996
+ await resolveRun(0);
1970
1997
  await pA;
1971
1998
 
1972
1999
  // B should be draining
@@ -1983,7 +2010,7 @@ describe("Conversation checkpoint handoff", () => {
1983
2010
  history: [],
1984
2011
  }),
1985
2012
  ).toBe("yield");
1986
- resolveRun(1);
2013
+ await resolveRun(1);
1987
2014
  await waitForPendingRun(3);
1988
2015
 
1989
2016
  // Handoff from C -> D
@@ -1998,7 +2025,7 @@ describe("Conversation checkpoint handoff", () => {
1998
2025
  history: [],
1999
2026
  }),
2000
2027
  ).toBe("yield");
2001
- resolveRun(2);
2028
+ await resolveRun(2);
2002
2029
  await waitForPendingRun(4);
2003
2030
 
2004
2031
  // D has no more queued -> checkpoint should return 'continue'
@@ -2013,7 +2040,7 @@ describe("Conversation checkpoint handoff", () => {
2013
2040
  }),
2014
2041
  ).toBe("continue");
2015
2042
 
2016
- resolveRun(3);
2043
+ await resolveRun(3);
2017
2044
  await new Promise((r) => setTimeout(r, 10));
2018
2045
 
2019
2046
  // Verify FIFO dequeue order
@@ -2038,12 +2065,20 @@ describe("Conversation checkpoint handoff", () => {
2038
2065
  await waitForPendingRun(1);
2039
2066
 
2040
2067
  // Enqueue B (empty content — will fail to persist) and C (valid)
2041
- conversation.enqueueMessage("", [], (e) => eventsB.push(e), "req-B");
2042
- conversation.enqueueMessage("msg-C", [], (e) => eventsC.push(e), "req-C");
2068
+ conversation.enqueueMessage({
2069
+ content: "",
2070
+ onEvent: (e) => eventsB.push(e),
2071
+ requestId: "req-B",
2072
+ });
2073
+ conversation.enqueueMessage({
2074
+ content: "msg-C",
2075
+ onEvent: (e) => eventsC.push(e),
2076
+ requestId: "req-C",
2077
+ });
2043
2078
  expect(conversation.getQueueDepth()).toBe(2);
2044
2079
 
2045
2080
  // Complete message A — triggers drain. B should fail, C should proceed.
2046
- resolveRun(0);
2081
+ await resolveRun(0);
2047
2082
  await pA;
2048
2083
 
2049
2084
  // C should have been dequeued and started a new AgentLoop.run
@@ -2060,7 +2095,7 @@ describe("Conversation checkpoint handoff", () => {
2060
2095
  expect(eventsC.some((e) => e.type === "message_dequeued")).toBe(true);
2061
2096
 
2062
2097
  // Complete C's run
2063
- resolveRun(1);
2098
+ await resolveRun(1);
2064
2099
  await new Promise((r) => setTimeout(r, 10));
2065
2100
 
2066
2101
  // C should have completed successfully
@@ -2097,7 +2132,7 @@ describe("Conversation checkpoint handoff", () => {
2097
2132
  expect(pendingRuns[1].onCheckpoint).toBeDefined();
2098
2133
 
2099
2134
  // Complete retry cleanly
2100
- resolveRun(1);
2135
+ await resolveRun(1);
2101
2136
  await p1;
2102
2137
  });
2103
2138
  });
@@ -2120,7 +2155,7 @@ describe("Conversation usage requestId correlation", () => {
2120
2155
  await waitForPendingRun(1);
2121
2156
 
2122
2157
  // Complete the run — this triggers recordUsage with the request's ID
2123
- resolveRun(0);
2158
+ await resolveRun(0);
2124
2159
  await p1;
2125
2160
 
2126
2161
  // The usage event should carry the request ID, not null
@@ -2153,12 +2188,12 @@ describe("Terminal trace events on rejection/failure", () => {
2153
2188
  await waitForPendingRun(1);
2154
2189
 
2155
2190
  // Enqueue empty content (will fail persistUserMessage)
2156
- conversation.enqueueMessage("", [], () => {}, "req-bad");
2191
+ conversation.enqueueMessage({ content: "", requestId: "req-bad" });
2157
2192
  // Enqueue valid message so drain continues
2158
- conversation.enqueueMessage("msg-3", [], () => {}, "req-3");
2193
+ conversation.enqueueMessage({ content: "msg-3", requestId: "req-3" });
2159
2194
 
2160
2195
  // Complete first — triggers drain, empty msg fails persist
2161
- resolveRun(0);
2196
+ await resolveRun(0);
2162
2197
  await p1;
2163
2198
  await waitForPendingRun(2);
2164
2199
 
@@ -2173,7 +2208,7 @@ describe("Terminal trace events on rejection/failure", () => {
2173
2208
  expect(errorTrace).toBeDefined();
2174
2209
 
2175
2210
  // Cleanup
2176
- resolveRun(1);
2211
+ await resolveRun(1);
2177
2212
  await new Promise((r) => setTimeout(r, 10));
2178
2213
  });
2179
2214
  });
@@ -2215,6 +2250,7 @@ describe("Conversation host attachment directives", () => {
2215
2250
  },
2216
2251
  ],
2217
2252
  };
2253
+ await run.onEvent({ type: "llm_call_started" });
2218
2254
  run.onEvent({
2219
2255
  type: "usage",
2220
2256
  inputTokens: 10,
@@ -2284,6 +2320,7 @@ describe("Conversation host attachment directives", () => {
2284
2320
  },
2285
2321
  ],
2286
2322
  };
2323
+ await run.onEvent({ type: "llm_call_started" });
2287
2324
  run.onEvent({
2288
2325
  type: "usage",
2289
2326
  inputTokens: 10,
@@ -2377,6 +2414,7 @@ describe("Conversation attachment event payloads", () => {
2377
2414
  } as any,
2378
2415
  ],
2379
2416
  });
2417
+ await run.onEvent({ type: "llm_call_started" });
2380
2418
  run.onEvent({
2381
2419
  type: "usage",
2382
2420
  inputTokens: 10,
@@ -2423,7 +2461,7 @@ describe("Conversation attachment event payloads", () => {
2423
2461
  await waitForPendingRun(1);
2424
2462
 
2425
2463
  // Queue a second message so the first run yields via checkpoint handoff.
2426
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
2464
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
2427
2465
 
2428
2466
  const run = pendingRuns[0];
2429
2467
  expect(run.onCheckpoint).toBeDefined();
@@ -2452,6 +2490,7 @@ describe("Conversation attachment event payloads", () => {
2452
2490
  } as any,
2453
2491
  ],
2454
2492
  });
2493
+ await run.onEvent({ type: "llm_call_started" });
2455
2494
  run.onEvent({
2456
2495
  type: "usage",
2457
2496
  inputTokens: 10,
@@ -2478,7 +2517,7 @@ describe("Conversation attachment event payloads", () => {
2478
2517
  expect(attachments[0].data).toBe("iVBORw0K");
2479
2518
 
2480
2519
  await waitForPendingRun(2);
2481
- resolveRun(1);
2520
+ await resolveRun(1);
2482
2521
  await new Promise((r) => setTimeout(r, 10));
2483
2522
  });
2484
2523
  });
@@ -2510,7 +2549,7 @@ describe("Regression: cancel semantics and error channel split", () => {
2510
2549
  conversation.abort();
2511
2550
 
2512
2551
  // Resolve the pending run so the abort-check path fires
2513
- resolveRun(0);
2552
+ await resolveRun(0);
2514
2553
  await p1;
2515
2554
 
2516
2555
  // generation_cancelled should be emitted via the per-message callback
@@ -2556,6 +2595,7 @@ describe("Regression: cancel semantics and error channel split", () => {
2556
2595
  } as any,
2557
2596
  ],
2558
2597
  });
2598
+ await run.onEvent({ type: "llm_call_started" });
2559
2599
  run.onEvent({
2560
2600
  type: "usage",
2561
2601
  inputTokens: 10,
@@ -2622,18 +2662,16 @@ describe("Regression: cancel semantics and error channel split", () => {
2622
2662
  );
2623
2663
  await waitForPendingRun(1);
2624
2664
 
2625
- conversation.enqueueMessage(
2626
- "msg-2",
2627
- [],
2628
- (e) => eventsPerMsg[1].push(e),
2629
- "req-2",
2630
- );
2631
- conversation.enqueueMessage(
2632
- "msg-3",
2633
- [],
2634
- (e) => eventsPerMsg[2].push(e),
2635
- "req-3",
2636
- );
2665
+ conversation.enqueueMessage({
2666
+ content: "msg-2",
2667
+ onEvent: (e) => eventsPerMsg[1].push(e),
2668
+ requestId: "req-2",
2669
+ });
2670
+ conversation.enqueueMessage({
2671
+ content: "msg-3",
2672
+ onEvent: (e) => eventsPerMsg[2].push(e),
2673
+ requestId: "req-3",
2674
+ });
2637
2675
 
2638
2676
  conversation.abort();
2639
2677
 
@@ -2677,10 +2715,14 @@ describe("Regression: cancel semantics and error channel split", () => {
2677
2715
  await waitForPendingRun(1);
2678
2716
 
2679
2717
  // Enqueue a second message while the first is processing
2680
- conversation.enqueueMessage("msg-2", [], (e) => events2.push(e), "req-2");
2718
+ conversation.enqueueMessage({
2719
+ content: "msg-2",
2720
+ onEvent: (e) => events2.push(e),
2721
+ requestId: "req-2",
2722
+ });
2681
2723
 
2682
2724
  // Complete the first agent loop run
2683
- resolveRun(0);
2725
+ await resolveRun(0);
2684
2726
 
2685
2727
  // The turn should still complete (timeout fires) and drain the queue
2686
2728
  // even though commitTurnChanges never resolves.
@@ -2701,7 +2743,7 @@ describe("Regression: cancel semantics and error channel split", () => {
2701
2743
 
2702
2744
  // Complete the second run so the test can clean up
2703
2745
  turnCommitHangForever = false;
2704
- resolveRun(1);
2746
+ await resolveRun(1);
2705
2747
  await new Promise((r) => origSetTimeout(r, 10));
2706
2748
  } finally {
2707
2749
  turnCommitHangForever = false;