@vellumai/assistant 0.8.5 → 0.8.7-dev.202606052118.34cd356

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 (1323) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +1 -1
  3. package/Dockerfile +21 -4
  4. package/bun.lock +13 -4
  5. package/bunfig.toml +6 -1
  6. package/docker-entrypoint.sh +12 -8
  7. package/docker-init-apt-root.sh +3 -1
  8. package/docker-kata-apt-env.sh +3 -1
  9. package/docker-kata-runtime-family.sh +12 -0
  10. package/docs/architecture/memory.md +1 -1
  11. package/docs/credential-execution-service.md +6 -6
  12. package/docs/plugins.md +114 -86
  13. package/examples/plugins/echo/README.md +13 -12
  14. package/examples/plugins/echo/register.ts +0 -54
  15. package/knip.json +1 -0
  16. package/node_modules/@vellumai/environments/bun.lock +24 -0
  17. package/node_modules/@vellumai/environments/package.json +18 -0
  18. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
  19. package/node_modules/@vellumai/environments/src/index.ts +11 -0
  20. package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
  21. package/node_modules/@vellumai/environments/src/types.ts +70 -0
  22. package/node_modules/@vellumai/environments/tsconfig.json +20 -0
  23. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
  24. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +15 -17
  25. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  26. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +15 -7
  27. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  28. package/openapi.yaml +8748 -589
  29. package/package.json +8 -4
  30. package/scripts/generate-openapi.ts +88 -54
  31. package/src/__tests__/actor-token-service.test.ts +3 -2
  32. package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
  33. package/src/__tests__/agent-loop-exit-reason.test.ts +284 -48
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
  35. package/src/__tests__/agent-loop-override-profile.test.ts +21 -33
  36. package/src/__tests__/agent-loop-provider-error-recording.test.ts +7 -5
  37. package/src/__tests__/agent-loop-thinking.test.ts +17 -12
  38. package/src/__tests__/agent-loop.test.ts +238 -422
  39. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +7 -2
  40. package/src/__tests__/agent-wake-override-profile.test.ts +23 -40
  41. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  42. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  43. package/src/__tests__/annotate-risk-options.test.ts +3 -3
  44. package/src/__tests__/anthropic-provider.test.ts +296 -57
  45. package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
  46. package/src/__tests__/app-control-flow.test.ts +6 -1
  47. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  48. package/src/__tests__/approval-cascade.test.ts +5 -11
  49. package/src/__tests__/approval-routes-http.test.ts +17 -16
  50. package/src/__tests__/assert-not-live-db.ts +79 -0
  51. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  52. package/src/__tests__/assistant-event.test.ts +15 -0
  53. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  54. package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -27
  55. package/src/__tests__/assistant-stream-state.test.ts +645 -0
  56. package/src/__tests__/audit-log-rotation.test.ts +2 -2
  57. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  58. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  59. package/src/__tests__/avatar-e2e.test.ts +7 -37
  60. package/src/__tests__/avatar-generator.test.ts +12 -42
  61. package/src/__tests__/avatar-identity-sync.test.ts +28 -3
  62. package/src/__tests__/background-shell-bash.test.ts +3 -7
  63. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -8
  64. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  65. package/src/__tests__/btw-routes.test.ts +72 -17
  66. package/src/__tests__/build-persisted-content.test.ts +184 -0
  67. package/src/__tests__/call-controller.test.ts +3 -2
  68. package/src/__tests__/call-pointer-messages.test.ts +5 -3
  69. package/src/__tests__/call-site-routing-provider.test.ts +22 -40
  70. package/src/__tests__/catalog-files.test.ts +1 -0
  71. package/src/__tests__/channel-approval-routes.test.ts +52 -23
  72. package/src/__tests__/channel-approvals.test.ts +4 -2
  73. package/src/__tests__/channel-guardian.test.ts +3 -2
  74. package/src/__tests__/channel-invite-transport.test.ts +1 -5
  75. package/src/__tests__/channel-readiness-routes.test.ts +0 -4
  76. package/src/__tests__/channel-readiness-slack-remote.test.ts +170 -0
  77. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  78. package/src/__tests__/channel-retry-sweep.test.ts +388 -79
  79. package/src/__tests__/checker.test.ts +12 -12
  80. package/src/__tests__/clawhub-files.test.ts +1 -0
  81. package/src/__tests__/compaction-circuit.test.ts +258 -0
  82. package/src/__tests__/compaction-direct.test.ts +132 -0
  83. package/src/__tests__/compaction-events.test.ts +6 -17
  84. package/src/__tests__/compaction-trail-store.test.ts +186 -0
  85. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  86. package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
  87. package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
  88. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  89. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
  90. package/src/__tests__/computer-use-tools.test.ts +14 -16
  91. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  92. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  93. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  94. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  95. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  96. package/src/__tests__/config-schema.test.ts +10 -10
  97. package/src/__tests__/config-watcher.test.ts +28 -0
  98. package/src/__tests__/connection-model-compat.test.ts +83 -0
  99. package/src/__tests__/contacts-tools.test.ts +3 -2
  100. package/src/__tests__/context-search-agent-runner.test.ts +6 -3
  101. package/src/__tests__/context-token-estimator.test.ts +56 -0
  102. package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
  103. package/src/__tests__/conversation-abort-tool-results.test.ts +73 -23
  104. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +10 -7
  105. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  106. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +23 -34
  107. package/src/__tests__/conversation-agent-loop-overflow.test.ts +477 -930
  108. package/src/__tests__/conversation-agent-loop.test.ts +1056 -1068
  109. package/src/__tests__/conversation-analysis-routes.test.ts +8 -3
  110. package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
  111. package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -1
  112. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  113. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  114. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  115. package/src/__tests__/conversation-clear-safety.test.ts +20 -10
  116. package/src/__tests__/conversation-confirmation-signals.test.ts +16 -45
  117. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  118. package/src/__tests__/conversation-disk-view.test.ts +10 -17
  119. package/src/__tests__/conversation-error.test.ts +30 -0
  120. package/src/__tests__/conversation-fork-crud.test.ts +132 -157
  121. package/src/__tests__/conversation-fork-route.test.ts +19 -16
  122. package/src/__tests__/conversation-history-web-search.test.ts +12 -1
  123. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  124. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  125. package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
  126. package/src/__tests__/conversation-lifecycle.test.ts +4 -2
  127. package/src/__tests__/conversation-list-source.test.ts +3 -2
  128. package/src/__tests__/conversation-load-history-repair.test.ts +5 -3
  129. package/src/__tests__/conversation-load-history-stripped.test.ts +2 -1
  130. package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
  131. package/src/__tests__/conversation-pairing.test.ts +60 -4
  132. package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
  133. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +36 -7
  134. package/src/__tests__/conversation-process-callsite.test.ts +28 -30
  135. package/src/__tests__/conversation-provider-retry-repair.test.ts +85 -51
  136. package/src/__tests__/conversation-queue.test.ts +605 -455
  137. package/src/__tests__/conversation-routes-disk-view.test.ts +9 -20
  138. package/src/__tests__/conversation-routes-guardian-reply.test.ts +35 -10
  139. package/src/__tests__/conversation-routes-slash-commands.test.ts +40 -8
  140. package/src/__tests__/conversation-runtime-assembly.test.ts +395 -313
  141. package/src/__tests__/conversation-runtime-workspace.test.ts +114 -36
  142. package/src/__tests__/conversation-skill-tools.test.ts +38 -142
  143. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  144. package/src/__tests__/conversation-slash-queue.test.ts +125 -62
  145. package/src/__tests__/conversation-slash-unknown.test.ts +18 -15
  146. package/src/__tests__/conversation-speed-override.test.ts +9 -22
  147. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  148. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +136 -15
  149. package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
  150. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  151. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  152. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  153. package/src/__tests__/conversation-surfaces-state-update.test.ts +8 -5
  154. package/src/__tests__/conversation-surfaces-table-action.test.ts +13 -32
  155. package/src/__tests__/conversation-sync-tags.test.ts +140 -12
  156. package/src/__tests__/conversation-title-service.test.ts +136 -2
  157. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +53 -11
  158. package/src/__tests__/conversation-unread-route.test.ts +14 -2
  159. package/src/__tests__/conversation-usage.test.ts +1 -2
  160. package/src/__tests__/conversation-wipe.test.ts +1 -1
  161. package/src/__tests__/conversation-workspace-cache-state.test.ts +21 -17
  162. package/src/__tests__/conversation-workspace-injection.test.ts +119 -23
  163. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +39 -13
  164. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  165. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  166. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  167. package/src/__tests__/credential-execution-client.test.ts +72 -1
  168. package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
  169. package/src/__tests__/credential-execution-tools.test.ts +1 -2
  170. package/src/__tests__/credential-health-service.test.ts +252 -3
  171. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  172. package/src/__tests__/credential-vault-unit.test.ts +19 -19
  173. package/src/__tests__/credential-vault.test.ts +5 -5
  174. package/src/__tests__/cross-provider-web-search.test.ts +275 -4
  175. package/src/__tests__/cu-unified-flow.test.ts +26 -1
  176. package/src/__tests__/db-acp-history.test.ts +101 -0
  177. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  178. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  179. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  180. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  181. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -0
  182. package/src/__tests__/db-test-helpers.ts +58 -0
  183. package/src/__tests__/disk-pressure-guard.test.ts +119 -36
  184. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  185. package/src/__tests__/disk-pressure-routes.test.ts +9 -35
  186. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  187. package/src/__tests__/dm-persistence.test.ts +38 -43
  188. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  189. package/src/__tests__/document-find-replace.test.ts +3 -2
  190. package/src/__tests__/document-tool-security.test.ts +81 -2
  191. package/src/__tests__/dynamic-page-surface.test.ts +99 -0
  192. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  193. package/src/__tests__/edit-propagation.test.ts +1 -2
  194. package/src/__tests__/empty-response-hook.test.ts +304 -0
  195. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  196. package/src/__tests__/encrypted-store.test.ts +11 -9
  197. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  198. package/src/__tests__/file-write-tool.test.ts +63 -0
  199. package/src/__tests__/filing-service.test.ts +3 -2
  200. package/src/__tests__/first-greeting.test.ts +103 -12
  201. package/src/__tests__/gateway-flag-listener.test.ts +0 -1
  202. package/src/__tests__/gemini-image-service.test.ts +13 -0
  203. package/src/__tests__/gemini-inline-media.test.ts +78 -0
  204. package/src/__tests__/gemini-provider.test.ts +375 -26
  205. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  206. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  207. package/src/__tests__/guardian-outbound-http.test.ts +3 -2
  208. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  209. package/src/__tests__/guardian-routing-state.test.ts +60 -71
  210. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  211. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +11 -8
  212. package/src/__tests__/heartbeat-disk-pressure.test.ts +3 -0
  213. package/src/__tests__/heartbeat-service.test.ts +4 -1
  214. package/src/__tests__/helpers/mock-logger.ts +26 -0
  215. package/src/__tests__/helpers/mock-provider.ts +110 -0
  216. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  217. package/src/__tests__/history-repair-hook.test.ts +162 -0
  218. package/src/__tests__/history-repair-observability.test.ts +1 -1
  219. package/src/__tests__/history-repair.test.ts +2 -1
  220. package/src/__tests__/host-app-control-proxy.test.ts +2 -0
  221. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  222. package/src/__tests__/host-bash-routes.test.ts +1 -0
  223. package/src/__tests__/host-cu-proxy.test.ts +2 -0
  224. package/src/__tests__/host-cu-routes-targeted.test.ts +4 -3
  225. package/src/__tests__/host-file-edit-tool.test.ts +4 -2
  226. package/src/__tests__/host-file-proxy.test.ts +31 -0
  227. package/src/__tests__/host-file-read-tool.test.ts +4 -2
  228. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  229. package/src/__tests__/host-file-write-tool.test.ts +9 -3
  230. package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
  231. package/src/__tests__/host-shell-tool.test.ts +11 -5
  232. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  233. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  234. package/src/__tests__/http-user-message-parity.test.ts +31 -9
  235. package/src/__tests__/identity-intro-cache.test.ts +140 -96
  236. package/src/__tests__/identity-routes.test.ts +248 -7
  237. package/src/__tests__/inbound-slack-persistence.test.ts +56 -75
  238. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  239. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  240. package/src/__tests__/injector-background-turn.test.ts +3 -9
  241. package/src/__tests__/injector-chain.test.ts +139 -275
  242. package/src/__tests__/injector-disk-pressure.test.ts +78 -58
  243. package/src/__tests__/injector-document-comments.test.ts +3 -3
  244. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  245. package/src/__tests__/injector-v3-suppression.test.ts +214 -0
  246. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  247. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  248. package/src/__tests__/list-messages-attachments.test.ts +7 -8
  249. package/src/__tests__/list-messages-hidden-metadata.test.ts +131 -11
  250. package/src/__tests__/list-messages-page-latest.test.ts +60 -1
  251. package/src/__tests__/list-messages-tool-merge.test.ts +56 -6
  252. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  253. package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
  254. package/src/__tests__/llm-resolver.test.ts +346 -39
  255. package/src/__tests__/llm-schema.test.ts +1 -1
  256. package/src/__tests__/llm-usage-store.test.ts +268 -1
  257. package/src/__tests__/log-export-routes.test.ts +59 -0
  258. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
  259. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  260. package/src/__tests__/mcp-abort-signal.test.ts +14 -0
  261. package/src/__tests__/mcp-auth-routes.test.ts +15 -10
  262. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  263. package/src/__tests__/mcp-health-check.test.ts +18 -13
  264. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  265. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  266. package/src/__tests__/messaging-send-tool.test.ts +9 -4
  267. package/src/__tests__/migration-export-http.test.ts +12 -12
  268. package/src/__tests__/migration-import-commit-http.test.ts +8 -8
  269. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  270. package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
  271. package/src/__tests__/migration-validate-http.test.ts +3 -3
  272. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  273. package/src/__tests__/model-intents.test.ts +3 -3
  274. package/src/__tests__/native-web-search.test.ts +235 -22
  275. package/src/__tests__/notification-decision-identity.test.ts +9 -18
  276. package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
  277. package/src/__tests__/notification-deep-link.test.ts +62 -0
  278. package/src/__tests__/oauth-commands-routes.test.ts +38 -1
  279. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  280. package/src/__tests__/oauth-store.test.ts +3 -2
  281. package/src/__tests__/onboarding-template-contract.test.ts +15 -2
  282. package/src/__tests__/openai-image-service.test.ts +17 -0
  283. package/src/__tests__/openai-provider.test.ts +105 -80
  284. package/src/__tests__/openai-responses-provider.test.ts +90 -86
  285. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  286. package/src/__tests__/outbound-slack-persistence.test.ts +48 -2
  287. package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -286
  288. package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
  289. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  290. package/src/__tests__/persistence-secret-redaction.test.ts +86 -13
  291. package/src/__tests__/pipeline-runner.test.ts +31 -43
  292. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  293. package/src/__tests__/plugin-bootstrap.test.ts +72 -63
  294. package/src/__tests__/plugin-registry.test.ts +0 -27
  295. package/src/__tests__/plugin-route-contribution.test.ts +6 -16
  296. package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
  297. package/src/__tests__/plugin-tool-contribution.test.ts +51 -64
  298. package/src/__tests__/plugin-types.test.ts +8 -173
  299. package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
  300. package/src/__tests__/process-message-background-slack.test.ts +38 -32
  301. package/src/__tests__/process-message-display-content.test.ts +55 -66
  302. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  303. package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
  304. package/src/__tests__/provider-error-scenarios.test.ts +7 -6
  305. package/src/__tests__/provider-platform-proxy-integration.test.ts +215 -8
  306. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  307. package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
  308. package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
  309. package/src/__tests__/provider-usage-tracking.test.ts +0 -6
  310. package/src/__tests__/ratelimit.test.ts +9 -4
  311. package/src/__tests__/reaction-persistence.test.ts +1 -1
  312. package/src/__tests__/recording-handler.test.ts +1 -0
  313. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +6 -1
  314. package/src/__tests__/registry.test.ts +82 -76
  315. package/src/__tests__/relay-server.test.ts +30 -23
  316. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  317. package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
  318. package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
  319. package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
  320. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  321. package/src/__tests__/runtime-events-sse-reconnect.test.ts +390 -0
  322. package/src/__tests__/schedule-routes.test.ts +683 -12
  323. package/src/__tests__/schedule-store.test.ts +124 -1
  324. package/src/__tests__/schedule-tools.test.ts +160 -0
  325. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  326. package/src/__tests__/secret-ingress-http.test.ts +7 -3
  327. package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
  328. package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
  329. package/src/__tests__/secret-response-routing.test.ts +13 -11
  330. package/src/__tests__/secure-keys.test.ts +3 -3
  331. package/src/__tests__/send-endpoint-busy.test.ts +87 -44
  332. package/src/__tests__/server-history-render.test.ts +318 -2
  333. package/src/__tests__/shell-observability.test.ts +249 -0
  334. package/src/__tests__/skill-feature-flags-integration.test.ts +52 -21
  335. package/src/__tests__/skill-feature-flags.test.ts +20 -22
  336. package/src/__tests__/skill-load-feature-flag.test.ts +15 -15
  337. package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
  338. package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
  339. package/src/__tests__/skill-tool-factory.test.ts +96 -95
  340. package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
  341. package/src/__tests__/skillssh-files.test.ts +1 -0
  342. package/src/__tests__/slack-channel-config.test.ts +3 -3
  343. package/src/__tests__/starter-task-flow.test.ts +6 -6
  344. package/src/__tests__/strip-memory-injections.test.ts +102 -14
  345. package/src/__tests__/subagent-call-site-routing.test.ts +14 -6
  346. package/src/__tests__/subagent-disposal.test.ts +27 -8
  347. package/src/__tests__/subagent-fork-notifications.test.ts +25 -12
  348. package/src/__tests__/subagent-fork-spawn.test.ts +14 -5
  349. package/src/__tests__/subagent-manager-notify.test.ts +21 -11
  350. package/src/__tests__/subagent-notify-parent.test.ts +6 -7
  351. package/src/__tests__/subagent-spawn-tool-fork.test.ts +59 -1
  352. package/src/__tests__/subagent-tools.test.ts +2 -1
  353. package/src/__tests__/suggestion-routes.test.ts +4 -3
  354. package/src/__tests__/sync-message-contract.test.ts +19 -16
  355. package/src/__tests__/system-prompt.test.ts +112 -0
  356. package/src/__tests__/task-scheduler.test.ts +162 -1
  357. package/src/__tests__/terminal-tools.test.ts +9 -25
  358. package/src/__tests__/test-preload-verifier.ts +68 -0
  359. package/src/__tests__/test-preload.ts +32 -39
  360. package/src/__tests__/thread-backfill.test.ts +4 -9
  361. package/src/__tests__/title-generate-hook.test.ts +319 -0
  362. package/src/__tests__/tool-error-hook.test.ts +278 -0
  363. package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
  364. package/src/__tests__/tool-executor.test.ts +55 -10
  365. package/src/__tests__/tool-preview-lifecycle.test.ts +481 -15
  366. package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
  367. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  368. package/src/__tests__/tool-result-truncation.test.ts +1 -1
  369. package/src/__tests__/tools-audio-read.test.ts +113 -0
  370. package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
  371. package/src/__tests__/turn-events-store.test.ts +11 -7
  372. package/src/__tests__/twilio-routes.test.ts +3 -2
  373. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  374. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  375. package/src/__tests__/usage-routes.test.ts +285 -1
  376. package/src/__tests__/user-plugin-loader.test.ts +2 -2
  377. package/src/__tests__/validate-input.test.ts +381 -0
  378. package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
  379. package/src/__tests__/voice-scoped-grant-consumer.test.ts +10 -7
  380. package/src/__tests__/voice-session-bridge.test.ts +56 -38
  381. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  382. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  383. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  384. package/src/acp/__tests__/agent-process.test.ts +161 -0
  385. package/src/acp/__tests__/client-handler.test.ts +40 -0
  386. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  387. package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
  388. package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
  389. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  390. package/src/acp/__tests__/session-manager-resume.test.ts +695 -0
  391. package/src/acp/agent-process.ts +61 -1
  392. package/src/acp/auto-install.test.ts +125 -0
  393. package/src/acp/auto-install.ts +174 -0
  394. package/src/acp/client-handler.ts +31 -0
  395. package/src/acp/feature-gate.test.ts +48 -0
  396. package/src/acp/feature-gate.ts +34 -0
  397. package/src/acp/prepare-agent-env.ts +52 -11
  398. package/src/acp/resolve-agent.test.ts +147 -6
  399. package/src/acp/resolve-agent.ts +81 -7
  400. package/src/acp/resume-hint.ts +22 -0
  401. package/src/acp/session-manager.ts +492 -77
  402. package/src/agent/compaction-circuit.ts +98 -0
  403. package/src/agent/loop.ts +730 -449
  404. package/src/api/README.md +126 -2
  405. package/src/api/constants/call-sites.ts +27 -0
  406. package/src/api/constants/tool-execution.ts +21 -0
  407. package/src/api/events/assistant-activity-state.ts +75 -0
  408. package/src/api/events/assistant-outbound-attachment.ts +49 -0
  409. package/src/api/events/assistant-text-delta.ts +30 -0
  410. package/src/api/events/assistant-thinking-delta.ts +33 -0
  411. package/src/api/events/assistant-turn-start.ts +31 -0
  412. package/src/api/events/avatar-updated.ts +24 -0
  413. package/src/api/events/compaction-circuit-closed.ts +26 -0
  414. package/src/api/events/compaction-circuit-open.ts +28 -0
  415. package/src/api/events/confirmation-request.ts +114 -0
  416. package/src/api/events/contact-request.ts +33 -0
  417. package/src/api/events/conversation-error.ts +77 -0
  418. package/src/api/events/conversation-list-invalidated.ts +38 -0
  419. package/src/api/events/conversation-title-updated.ts +24 -0
  420. package/src/api/events/disk-pressure-status-changed.ts +61 -0
  421. package/src/api/events/document-comment-created.ts +44 -0
  422. package/src/api/events/document-comment-deleted.ts +22 -0
  423. package/src/api/events/document-comment-reopened.ts +23 -0
  424. package/src/api/events/document-comment-resolved.ts +25 -0
  425. package/src/api/events/document-editor-update.ts +27 -0
  426. package/src/api/events/error.ts +32 -0
  427. package/src/api/events/generation-cancelled.ts +22 -0
  428. package/src/api/events/generation-handoff.ts +39 -0
  429. package/src/api/events/home-feed-updated.ts +26 -0
  430. package/src/api/events/identity-changed.ts +32 -0
  431. package/src/api/events/interaction-resolved.ts +50 -0
  432. package/src/api/events/message-complete.ts +40 -0
  433. package/src/api/events/message-dequeued.ts +21 -0
  434. package/src/api/events/message-queued-deleted.ts +23 -0
  435. package/src/api/events/message-queued.ts +22 -0
  436. package/src/api/events/message-request-complete.ts +29 -0
  437. package/src/api/events/navigate-settings.ts +20 -0
  438. package/src/api/events/notification-intent.ts +33 -0
  439. package/src/api/events/open-url.ts +28 -0
  440. package/src/api/events/question-request.ts +67 -0
  441. package/src/{events → api/events}/relationship-state-updated.ts +6 -8
  442. package/src/api/events/secret-request.ts +42 -0
  443. package/src/api/events/subagent-event.ts +79 -0
  444. package/src/api/events/subagent-spawned.ts +40 -0
  445. package/src/api/events/subagent-status-changed.ts +65 -0
  446. package/src/api/events/sync-changed.ts +29 -0
  447. package/src/api/events/tool-output-chunk.ts +45 -0
  448. package/src/api/events/tool-result.ts +129 -0
  449. package/src/api/events/tool-use-preview-start.ts +32 -0
  450. package/src/api/events/tool-use-start.ts +30 -0
  451. package/src/api/events/trace-event.ts +69 -0
  452. package/src/api/events/turn-profile-auto-routed.ts +28 -0
  453. package/src/api/events/ui-surface-complete.ts +30 -0
  454. package/src/api/events/ui-surface-dismiss.ts +22 -0
  455. package/src/api/events/ui-surface-show.ts +67 -0
  456. package/src/api/events/ui-surface-update.ts +26 -0
  457. package/src/api/events/usage-update.ts +34 -0
  458. package/src/api/events/user-message-echo.ts +35 -0
  459. package/src/api/index.ts +517 -3
  460. package/src/api/requests/dictation.ts +45 -0
  461. package/src/api/responses/conversation-message.ts +374 -0
  462. package/src/api/responses/disk-pressure-status.ts +26 -0
  463. package/src/api/responses/home.ts +217 -0
  464. package/src/api/responses/llm-context-response.ts +41 -0
  465. package/src/api/responses/llm-request-log-entry.ts +93 -0
  466. package/src/api/responses/memory-recall-log.ts +65 -0
  467. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  468. package/src/api/responses/memory-v3-selection-log.ts +50 -0
  469. package/src/api/responses/subagent-detail.ts +48 -0
  470. package/src/approvals/guardian-decision-primitive.ts +7 -15
  471. package/src/approvals/guardian-request-resolvers.ts +7 -10
  472. package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
  473. package/src/avatar/__tests__/avatar-store.test.ts +198 -0
  474. package/src/avatar/avatar-manifest.ts +195 -0
  475. package/src/avatar/avatar-store.ts +113 -0
  476. package/src/avatar/traits-png-sync.ts +8 -2
  477. package/src/background-wake/background-wake-routes.test.ts +687 -52
  478. package/src/background-wake/next-wake.test.ts +31 -1
  479. package/src/background-wake/next-wake.ts +5 -1
  480. package/src/background-wake/platform-client.test.ts +308 -0
  481. package/src/background-wake/platform-client.ts +167 -0
  482. package/src/background-wake/publisher.ts +91 -0
  483. package/src/background-wake/runtime-registry.ts +2 -2
  484. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  485. package/src/calls/call-conversation-messages.ts +6 -4
  486. package/src/calls/guardian-action-sweep.ts +6 -4
  487. package/src/calls/guardian-dispatch.ts +1 -0
  488. package/src/calls/relay-server.ts +12 -8
  489. package/src/calls/voice-session-bridge.ts +17 -31
  490. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  491. package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
  492. package/src/cli/commands/__tests__/notifications.test.ts +242 -54
  493. package/src/cli/commands/avatar.ts +17 -11
  494. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  495. package/src/cli/commands/channels/index.ts +229 -0
  496. package/src/cli/commands/conversations.ts +15 -1
  497. package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
  498. package/src/cli/commands/db/__tests__/status.test.ts +253 -0
  499. package/src/cli/commands/db/format.ts +48 -0
  500. package/src/cli/commands/db/index.ts +29 -0
  501. package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
  502. package/src/cli/commands/db/repair-step-integrity.ts +146 -0
  503. package/src/cli/commands/db/repair-steps.ts +164 -0
  504. package/src/cli/commands/db/repair.ts +141 -0
  505. package/src/cli/commands/db/status.ts +366 -0
  506. package/src/cli/commands/memory-v3.ts +168 -203
  507. package/src/cli/commands/notifications.ts +427 -65
  508. package/src/cli/lib/cli-colors.ts +24 -6
  509. package/src/cli/lib/open-browser.ts +7 -2
  510. package/src/cli/program.ts +6 -5
  511. package/src/config/__tests__/feature-flag-registry-guard.test.ts +4 -4
  512. package/src/config/acp-defaults.test.ts +10 -0
  513. package/src/config/acp-defaults.ts +6 -0
  514. package/src/config/assistant-feature-flags.ts +47 -55
  515. package/src/config/bundled-skills/acp/SKILL.md +64 -30
  516. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  517. package/src/config/bundled-skills/app-builder/SKILL.md +224 -387
  518. package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
  519. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  520. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  521. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  522. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  523. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  524. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  525. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  526. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  527. package/src/config/bundled-skills/document-editor/SKILL.md +33 -24
  528. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  529. package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
  530. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  531. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
  532. package/src/config/bundled-skills/schedule/SKILL.md +2 -2
  533. package/src/config/bundled-skills/schedule/TOOLS.json +10 -2
  534. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  535. package/src/config/bundled-tool-registry.ts +2 -0
  536. package/src/config/call-site-defaults.ts +3 -8
  537. package/src/config/feature-flag-cache.ts +86 -0
  538. package/src/config/feature-flag-registry.json +81 -25
  539. package/src/config/llm-context-resolution.ts +10 -1
  540. package/src/config/llm-resolver.ts +121 -15
  541. package/src/config/loader.ts +4 -5
  542. package/src/config/schemas/__tests__/memory-v2.test.ts +2 -211
  543. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  544. package/src/config/schemas/call-site-catalog.ts +8 -15
  545. package/src/config/schemas/heartbeat.ts +10 -1
  546. package/src/config/schemas/llm.ts +93 -4
  547. package/src/config/schemas/memory-lifecycle.ts +24 -0
  548. package/src/config/schemas/memory-v2.ts +8 -227
  549. package/src/config/schemas/memory-v3.ts +47 -0
  550. package/src/config/schemas/memory.ts +6 -1
  551. package/src/config/schemas/platform.ts +8 -0
  552. package/src/config/schemas/services.ts +6 -2
  553. package/src/config/schemas/timeouts.ts +3 -1
  554. package/src/config/seed-inference-profiles.ts +38 -18
  555. package/src/config/skills.ts +13 -0
  556. package/src/context/compactor.ts +55 -32
  557. package/src/context/strip-injections.ts +128 -0
  558. package/src/context/token-estimator.ts +52 -5
  559. package/src/context/tool-result-truncation.ts +1 -66
  560. package/src/context/window-manager.ts +141 -26
  561. package/src/credential-execution/executable-discovery.ts +56 -0
  562. package/src/credential-execution/process-manager.ts +6 -2
  563. package/src/credential-health/credential-health-service.ts +125 -40
  564. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +9 -6
  565. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +15 -17
  566. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
  567. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  568. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  569. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  570. package/src/daemon/__tests__/native-web-search-metadata.test.ts +11 -8
  571. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  572. package/src/daemon/__tests__/web-search-status-text.test.ts +11 -6
  573. package/src/daemon/approval-generators.ts +4 -4
  574. package/src/daemon/assistant-attachments.ts +1 -1
  575. package/src/daemon/config-watcher.ts +7 -1
  576. package/src/daemon/context-overflow-reducer.ts +0 -1
  577. package/src/daemon/conversation-agent-loop-handlers.ts +1146 -247
  578. package/src/daemon/conversation-agent-loop.ts +570 -1457
  579. package/src/daemon/conversation-error.ts +40 -12
  580. package/src/daemon/conversation-history.ts +27 -10
  581. package/src/daemon/conversation-launch.ts +4 -8
  582. package/src/daemon/conversation-lifecycle.ts +13 -42
  583. package/src/daemon/conversation-messaging.ts +89 -49
  584. package/src/daemon/conversation-notifiers.ts +7 -5
  585. package/src/daemon/conversation-process.ts +183 -130
  586. package/src/daemon/conversation-registry.ts +159 -0
  587. package/src/daemon/conversation-runtime-assembly.ts +209 -362
  588. package/src/daemon/conversation-skill-tools.ts +14 -30
  589. package/src/daemon/conversation-slash.ts +6 -25
  590. package/src/daemon/conversation-store.ts +15 -95
  591. package/src/daemon/conversation-surfaces.ts +346 -107
  592. package/src/daemon/conversation-tool-setup.ts +38 -77
  593. package/src/daemon/conversation-workspace.ts +17 -0
  594. package/src/daemon/conversation.ts +143 -186
  595. package/src/daemon/daemon-control.ts +1 -1
  596. package/src/daemon/daemon-skill-host.ts +7 -4
  597. package/src/daemon/disk-pressure-guard.ts +54 -50
  598. package/src/daemon/external-plugins-bootstrap.ts +53 -32
  599. package/src/daemon/first-greeting.ts +53 -13
  600. package/src/daemon/guardian-action-generators.ts +2 -2
  601. package/src/daemon/handlers/config-a2a.ts +51 -36
  602. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  603. package/src/daemon/handlers/config-telegram.ts +16 -2
  604. package/src/daemon/handlers/conversations.ts +9 -23
  605. package/src/daemon/handlers/shared.ts +158 -77
  606. package/src/daemon/handlers/skills.ts +53 -20
  607. package/src/daemon/host-app-control-proxy.ts +54 -1
  608. package/src/daemon/host-cu-proxy.ts +46 -22
  609. package/src/daemon/host-file-proxy.ts +25 -1
  610. package/src/daemon/host-proxy-preactivation.ts +25 -6
  611. package/src/daemon/lifecycle.ts +65 -67
  612. package/src/daemon/mcp-reload-service.ts +1 -1
  613. package/src/daemon/meet-manifest-loader.ts +10 -17
  614. package/src/daemon/message-protocol.ts +2 -3
  615. package/src/daemon/message-provenance.ts +49 -0
  616. package/src/daemon/message-types/apps.ts +1 -29
  617. package/src/daemon/message-types/contacts.ts +3 -20
  618. package/src/daemon/message-types/conversations.ts +25 -125
  619. package/src/daemon/message-types/document-comments.ts +8 -44
  620. package/src/daemon/message-types/documents.ts +3 -9
  621. package/src/daemon/message-types/home.ts +5 -18
  622. package/src/daemon/message-types/integrations.ts +4 -13
  623. package/src/daemon/message-types/messages.ts +50 -428
  624. package/src/daemon/message-types/notifications.ts +2 -32
  625. package/src/daemon/message-types/settings.ts +3 -8
  626. package/src/daemon/message-types/skills.ts +4 -0
  627. package/src/daemon/message-types/subagents.ts +6 -0
  628. package/src/daemon/message-types/surfaces.ts +138 -3
  629. package/src/daemon/message-types/sync.ts +12 -25
  630. package/src/daemon/message-types/workspace.ts +3 -11
  631. package/src/daemon/now-scratchpad.ts +21 -0
  632. package/src/daemon/orphan-reaper.test.ts +210 -0
  633. package/src/daemon/orphan-reaper.ts +240 -0
  634. package/src/daemon/overflow-reduction-loop.ts +230 -0
  635. package/src/daemon/persist-unsendable-image.ts +117 -0
  636. package/src/daemon/process-message.ts +59 -58
  637. package/src/daemon/providers-setup.ts +1 -1
  638. package/src/daemon/server.ts +30 -0
  639. package/src/daemon/switch-inference-profile-tool.ts +13 -3
  640. package/src/daemon/tool-setup-types.ts +0 -6
  641. package/src/daemon/tool-side-effects.ts +10 -7
  642. package/src/daemon/trace-emitter.ts +6 -4
  643. package/src/daemon/trust-context.ts +32 -0
  644. package/src/daemon/wake-target-adapter.ts +24 -2
  645. package/src/documents/document-store.ts +38 -0
  646. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  647. package/src/heartbeat/__tests__/heartbeat-service.test.ts +34 -0
  648. package/src/heartbeat/heartbeat-run-store.ts +54 -1
  649. package/src/heartbeat/heartbeat-service.ts +105 -0
  650. package/src/home/__tests__/feed-writer.test.ts +161 -0
  651. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  652. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  653. package/src/home/feed-types.ts +36 -221
  654. package/src/home/feed-writer.ts +146 -7
  655. package/src/home/home-greeting-cache.ts +24 -1
  656. package/src/home/suggested-prompts.ts +27 -145
  657. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  658. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  659. package/src/ipc/__tests__/email-ipc.test.ts +0 -9
  660. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  661. package/src/ipc/gateway-client.test.ts +6 -3
  662. package/src/ipc/gateway-client.ts +3 -3
  663. package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
  664. package/src/ipc/routes/route-adapter.ts +45 -6
  665. package/src/ipc/skill-routes/__tests__/memory.test.ts +34 -9
  666. package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
  667. package/src/ipc/skill-routes/__tests__/registries.test.ts +59 -20
  668. package/src/ipc/skill-routes/memory.ts +29 -13
  669. package/src/ipc/skill-routes/providers.ts +5 -6
  670. package/src/ipc/skill-routes/registries.ts +39 -88
  671. package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
  672. package/src/media/gemini-image-service.ts +15 -0
  673. package/src/media/openai-image-service.ts +14 -0
  674. package/src/media/types.ts +34 -0
  675. package/src/memory/__tests__/conversation-queries.test.ts +192 -8
  676. package/src/memory/__tests__/db-maintenance.test.ts +128 -0
  677. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
  678. package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
  679. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +82 -5
  680. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  681. package/src/memory/__tests__/memory-retrospective-job.test.ts +11 -6
  682. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  683. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  684. package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
  685. package/src/memory/auth-fallback-events-store.ts +94 -0
  686. package/src/memory/context-search/agent-runner.ts +2 -4
  687. package/src/memory/conversation-attention-store.ts +17 -3
  688. package/src/memory/conversation-crud.ts +386 -115
  689. package/src/memory/conversation-queries.ts +78 -22
  690. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  691. package/src/memory/conversation-title-service.ts +65 -41
  692. package/src/memory/db-connection.ts +29 -19
  693. package/src/memory/db-init.ts +18 -0
  694. package/src/memory/db-maintenance.ts +18 -2
  695. package/src/memory/db-singleton.ts +77 -0
  696. package/src/memory/delivery-channels.ts +82 -0
  697. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  698. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  699. package/src/memory/graph/consolidation.ts +8 -11
  700. package/src/memory/graph/conversation-graph-memory.ts +106 -8
  701. package/src/memory/graph/extraction.ts +6 -9
  702. package/src/memory/graph/narrative.ts +2 -2
  703. package/src/memory/graph/pattern-scan.ts +2 -2
  704. package/src/memory/graph/retriever.test.ts +3 -3
  705. package/src/memory/graph/retriever.ts +20 -26
  706. package/src/memory/graph/tools.ts +4 -4
  707. package/src/memory/job-handlers/conversation-starters.ts +45 -34
  708. package/src/memory/job-handlers/embedding.test.ts +3 -2
  709. package/src/memory/job-handlers/summarization.ts +1 -2
  710. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  711. package/src/memory/jobs-store.ts +36 -1
  712. package/src/memory/jobs-worker.ts +93 -43
  713. package/src/memory/llm-request-log-source-clickhouse.ts +55 -1
  714. package/src/memory/llm-request-log-source-local.ts +13 -0
  715. package/src/memory/llm-request-log-source.ts +21 -6
  716. package/src/memory/llm-request-log-store.ts +147 -3
  717. package/src/memory/llm-usage-store.ts +234 -50
  718. package/src/memory/memory-marker.ts +17 -0
  719. package/src/memory/memory-retrospective-job.ts +6 -2
  720. package/src/memory/memory-v2-activation-log-store.ts +13 -1
  721. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  722. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  723. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  724. package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
  725. package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
  726. package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
  727. package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
  728. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  729. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  730. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  731. package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
  732. package/src/memory/migrations/index.ts +9 -0
  733. package/src/memory/pkb/autoinject.ts +61 -0
  734. package/src/memory/pkb/context.ts +50 -0
  735. package/src/memory/pkb/types.ts +14 -0
  736. package/src/memory/schedule-attribution-sql.ts +104 -0
  737. package/src/memory/schema/acp.ts +4 -0
  738. package/src/memory/schema/conversations.ts +9 -1
  739. package/src/memory/schema/inference.ts +0 -1
  740. package/src/memory/schema/infrastructure.ts +27 -0
  741. package/src/memory/usage-grouped-buckets.ts +6 -1
  742. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  743. package/src/memory/v2/__tests__/consolidation-job.test.ts +125 -1
  744. package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
  745. package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
  746. package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
  747. package/src/memory/v2/__tests__/migration.test.ts +11 -3
  748. package/src/memory/v2/__tests__/page-index.test.ts +37 -1
  749. package/src/memory/v2/__tests__/router.test.ts +14 -4
  750. package/src/memory/v2/__tests__/sweep-job.test.ts +9 -5
  751. package/src/memory/v2/backfill-jobs.ts +6 -0
  752. package/src/memory/v2/consolidation-job.ts +99 -10
  753. package/src/memory/v2/harness/metrics.ts +5 -1
  754. package/src/memory/v2/harness/replay-input.ts +19 -3
  755. package/src/memory/v2/harness/runner.ts +6 -0
  756. package/src/memory/v2/harness/trace.ts +6 -0
  757. package/src/memory/v2/migration.ts +5 -3
  758. package/src/memory/v2/page-index.ts +11 -0
  759. package/src/memory/v2/router.ts +8 -11
  760. package/src/memory/v2/sweep-job.ts +8 -11
  761. package/src/memory/v2/types.ts +1 -0
  762. package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
  763. package/src/messaging/providers/slack/render-transcript.ts +2 -2
  764. package/src/messaging/style-analyzer.ts +8 -11
  765. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  766. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  767. package/src/notifications/adapters/slack.ts +45 -11
  768. package/src/notifications/broadcaster.ts +114 -63
  769. package/src/notifications/conversation-pairing.ts +23 -8
  770. package/src/notifications/decision-engine.ts +16 -16
  771. package/src/notifications/decisions-store.ts +32 -1
  772. package/src/notifications/deliveries-store.ts +45 -0
  773. package/src/notifications/edit-notification.ts +201 -0
  774. package/src/notifications/emit-signal.ts +11 -1
  775. package/src/notifications/home-feed-side-effect.ts +12 -1
  776. package/src/notifications/preference-extractor.ts +11 -14
  777. package/src/notifications/signal.ts +10 -0
  778. package/src/notifications/types.ts +37 -0
  779. package/src/oauth/byo-connection.test.ts +67 -3
  780. package/src/oauth/byo-connection.ts +32 -5
  781. package/src/oauth/connect-orchestrator.ts +9 -0
  782. package/src/oauth/connection-resolver.test.ts +76 -0
  783. package/src/oauth/connection-resolver.ts +49 -10
  784. package/src/oauth/manual-token-connection.ts +51 -3
  785. package/src/oauth/seed-providers.ts +3 -0
  786. package/src/permissions/approval-policy.test.ts +19 -5
  787. package/src/permissions/approval-policy.ts +14 -3
  788. package/src/permissions/checker.ts +21 -8
  789. package/src/permissions/prompter.ts +46 -36
  790. package/src/permissions/question-prompter.test.ts +35 -26
  791. package/src/permissions/question-prompter.ts +6 -10
  792. package/src/platform/client.test.ts +24 -1
  793. package/src/platform/client.ts +8 -0
  794. package/src/platform/feature-gate.ts +15 -0
  795. package/src/plugin-api/constants.ts +4 -0
  796. package/src/plugin-api/index.ts +10 -1
  797. package/src/plugin-api/types.ts +176 -4
  798. package/src/plugins/defaults/compaction/compact.ts +59 -0
  799. package/src/plugins/defaults/compaction/package.json +15 -0
  800. package/src/plugins/defaults/compaction/register.ts +24 -0
  801. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  802. package/src/plugins/defaults/empty-response/package.json +15 -0
  803. package/src/plugins/defaults/empty-response/register.ts +23 -0
  804. package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
  805. package/src/plugins/defaults/history-repair/package.json +15 -0
  806. package/src/plugins/defaults/history-repair/register.ts +24 -0
  807. package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
  808. package/src/plugins/defaults/index.ts +22 -49
  809. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
  810. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  811. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  812. package/src/plugins/defaults/{injectors.ts → memory-retrieval/injectors.ts} +297 -120
  813. package/src/plugins/defaults/memory-v3-shadow/__tests__/assign.test.ts +242 -0
  814. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +118 -0
  815. package/src/plugins/defaults/memory-v3-shadow/__tests__/core.test.ts +39 -0
  816. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +36 -0
  817. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +37 -0
  818. package/src/plugins/defaults/memory-v3-shadow/__tests__/health.test.ts +219 -0
  819. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +330 -0
  820. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +288 -0
  821. package/src/plugins/defaults/memory-v3-shadow/__tests__/needle.test.ts +107 -0
  822. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +436 -0
  823. package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
  824. package/src/plugins/defaults/memory-v3-shadow/__tests__/reconcile.test.ts +274 -0
  825. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +61 -0
  826. package/src/plugins/defaults/memory-v3-shadow/__tests__/router.test.ts +332 -0
  827. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +179 -0
  828. package/src/plugins/defaults/memory-v3-shadow/__tests__/selector.test.ts +470 -0
  829. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +432 -0
  830. package/src/plugins/defaults/memory-v3-shadow/__tests__/snapshot.test.ts +168 -0
  831. package/src/plugins/defaults/memory-v3-shadow/__tests__/tree.test.ts +192 -0
  832. package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +54 -0
  833. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +106 -0
  834. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +44 -0
  835. package/src/plugins/defaults/memory-v3-shadow/assign.ts +268 -0
  836. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +124 -0
  837. package/src/plugins/defaults/memory-v3-shadow/core.ts +26 -0
  838. package/src/plugins/defaults/memory-v3-shadow/data/README.md +84 -0
  839. package/src/plugins/defaults/memory-v3-shadow/data/assignments.json +5 -0
  840. package/src/plugins/defaults/memory-v3-shadow/data/core.json +1 -0
  841. package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-a/topic-x.md +9 -0
  842. package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-a/topic-y.md +9 -0
  843. package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-b/topic-z.md +9 -0
  844. package/src/plugins/defaults/memory-v3-shadow/health.ts +0 -0
  845. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  846. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  847. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  848. package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
  849. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +314 -0
  850. package/src/plugins/defaults/memory-v3-shadow/needle.ts +115 -0
  851. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +126 -0
  852. package/src/plugins/defaults/memory-v3-shadow/package.json +15 -0
  853. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +34 -0
  854. package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
  855. package/src/plugins/defaults/memory-v3-shadow/reconcile.ts +523 -0
  856. package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
  857. package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +32 -0
  858. package/src/plugins/defaults/memory-v3-shadow/router.ts +190 -0
  859. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +84 -0
  860. package/src/plugins/defaults/memory-v3-shadow/selector.ts +226 -0
  861. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +349 -0
  862. package/src/plugins/defaults/memory-v3-shadow/snapshot.ts +209 -0
  863. package/src/plugins/defaults/memory-v3-shadow/tree.ts +174 -0
  864. package/src/plugins/defaults/memory-v3-shadow/types.ts +59 -0
  865. package/src/plugins/defaults/memory-v3-shadow/working-set.ts +88 -0
  866. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  867. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  868. package/src/plugins/defaults/title-generate/package.json +15 -0
  869. package/src/plugins/defaults/title-generate/register.ts +35 -0
  870. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  871. package/src/plugins/defaults/tool-error/package.json +15 -0
  872. package/src/plugins/defaults/tool-error/register.ts +23 -0
  873. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  874. package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
  875. package/src/plugins/defaults/tool-result-truncate/register.ts +24 -0
  876. package/src/plugins/defaults/tool-result-truncate/terminal.ts +132 -0
  877. package/src/plugins/external-plugin-loader.ts +2 -2
  878. package/src/plugins/pipeline.ts +8 -35
  879. package/src/plugins/registry.ts +8 -25
  880. package/src/plugins/types.ts +62 -677
  881. package/src/plugins/user-loader.ts +4 -3
  882. package/src/proactive-artifact/aux-message-injector.ts +4 -5
  883. package/src/proactive-artifact/job.test.ts +29 -21
  884. package/src/proactive-artifact/job.ts +3 -1
  885. package/src/prompts/__tests__/system-prompt.test.ts +46 -4
  886. package/src/prompts/sections.ts +20 -7
  887. package/src/prompts/system-prompt.ts +38 -40
  888. package/src/prompts/template-detection.ts +10 -4
  889. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
  890. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
  891. package/src/prompts/templates/BOOTSTRAP.md +12 -12
  892. package/src/prompts/templates/IDENTITY.md +0 -2
  893. package/src/prompts/templates/system-sections.ts +21 -0
  894. package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
  895. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  896. package/src/providers/__tests__/retry-callsite.test.ts +25 -25
  897. package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
  898. package/src/providers/anthropic/client.ts +61 -34
  899. package/src/providers/call-site-routing.ts +34 -18
  900. package/src/providers/connection-model-compat.ts +23 -0
  901. package/src/providers/connection-resolution.ts +39 -20
  902. package/src/providers/fireworks/client.ts +1 -0
  903. package/src/providers/gemini/client.ts +176 -37
  904. package/src/providers/gemini/inline-media.ts +74 -0
  905. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  906. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  907. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  908. package/src/providers/inference/auth.ts +0 -8
  909. package/src/providers/inference/connections.ts +3 -66
  910. package/src/providers/inference/resolve-auth.ts +2 -3
  911. package/src/providers/model-catalog.ts +35 -1
  912. package/src/providers/model-intents.ts +3 -3
  913. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  914. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +269 -7
  915. package/src/providers/openai/chat-completions-provider.ts +155 -16
  916. package/src/providers/openai/codex-models.ts +2 -0
  917. package/src/providers/openai/responses-provider.ts +54 -57
  918. package/src/providers/openrouter/client.ts +15 -14
  919. package/src/providers/placeholder-sentinels.ts +35 -0
  920. package/src/providers/provider-send-message.ts +23 -14
  921. package/src/providers/ratelimit.ts +1 -9
  922. package/src/providers/registry.ts +48 -8
  923. package/src/providers/retry.ts +16 -9
  924. package/src/providers/search-provider-catalog.ts +17 -9
  925. package/src/providers/types.ts +20 -2
  926. package/src/providers/usage-tracking.ts +1 -9
  927. package/src/runtime/__tests__/agent-wake.test.ts +142 -32
  928. package/src/runtime/__tests__/background-job-runner.test.ts +2 -3
  929. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  930. package/src/runtime/access-request-helper.ts +1 -0
  931. package/src/runtime/agent-wake.ts +95 -23
  932. package/src/runtime/assistant-event-hub.ts +38 -8
  933. package/src/runtime/assistant-stream-state.ts +368 -0
  934. package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
  935. package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
  936. package/src/runtime/auth/route-policy.ts +42 -1069
  937. package/src/runtime/background-job-runner.ts +1 -4
  938. package/src/runtime/btw-sidechain.ts +3 -1
  939. package/src/runtime/channel-approvals.ts +4 -15
  940. package/src/runtime/channel-invite-transport.ts +5 -6
  941. package/src/runtime/channel-readiness-service.ts +70 -5
  942. package/src/runtime/channel-reply-delivery.ts +23 -0
  943. package/src/runtime/channel-retry-sweep.ts +59 -30
  944. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  945. package/src/runtime/http-router.ts +35 -43
  946. package/src/runtime/http-types.ts +23 -71
  947. package/src/runtime/interactive-ui.ts +1 -1
  948. package/src/runtime/invite-instruction-generator.ts +3 -3
  949. package/src/runtime/migrations/vbundle-builder.ts +3 -2
  950. package/src/runtime/pending-interactions.ts +3 -2
  951. package/src/runtime/routes/__tests__/acp-routes.test.ts +253 -55
  952. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
  953. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  954. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  955. package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
  956. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +436 -0
  957. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
  958. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +129 -1
  959. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  960. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  961. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +26 -72
  962. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  963. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +58 -5
  964. package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
  965. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  966. package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
  967. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
  968. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  969. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +297 -0
  970. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  971. package/src/runtime/routes/__tests__/tts-routes.test.ts +9 -5
  972. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  973. package/src/runtime/routes/acp-routes.test.ts +186 -100
  974. package/src/runtime/routes/acp-routes.ts +110 -35
  975. package/src/runtime/routes/app-management-routes.ts +175 -106
  976. package/src/runtime/routes/app-routes.ts +38 -20
  977. package/src/runtime/routes/approval-routes.ts +17 -5
  978. package/src/runtime/routes/attachment-routes.ts +51 -16
  979. package/src/runtime/routes/audio-routes.ts +1 -0
  980. package/src/runtime/routes/audit-routes.ts +5 -0
  981. package/src/runtime/routes/auth-routes.ts +5 -0
  982. package/src/runtime/routes/avatar-routes.ts +264 -59
  983. package/src/runtime/routes/background-tool-routes.ts +9 -0
  984. package/src/runtime/routes/background-wake-routes.ts +201 -23
  985. package/src/runtime/routes/backup-routes.ts +45 -0
  986. package/src/runtime/routes/bookmark-routes.ts +13 -0
  987. package/src/runtime/routes/brain-graph-routes.ts +9 -0
  988. package/src/runtime/routes/browser-routes.ts +6 -1
  989. package/src/runtime/routes/browser-tabs-routes.ts +11 -10
  990. package/src/runtime/routes/btw-routes.ts +37 -27
  991. package/src/runtime/routes/cache-routes.ts +13 -0
  992. package/src/runtime/routes/call-routes.ts +21 -10
  993. package/src/runtime/routes/channel-availability-routes.ts +5 -1
  994. package/src/runtime/routes/channel-readiness-routes.ts +37 -4
  995. package/src/runtime/routes/channel-route-definitions.ts +21 -0
  996. package/src/runtime/routes/channel-verification-routes.ts +21 -0
  997. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
  998. package/src/runtime/routes/client-routes.ts +9 -0
  999. package/src/runtime/routes/consolidation-routes.ts +133 -25
  1000. package/src/runtime/routes/contact-prompt-routes.ts +9 -0
  1001. package/src/runtime/routes/contact-routes.ts +90 -23
  1002. package/src/runtime/routes/content-source-routes.ts +5 -1
  1003. package/src/runtime/routes/conversation-analysis-routes.ts +11 -1
  1004. package/src/runtime/routes/conversation-attention-routes.ts +5 -0
  1005. package/src/runtime/routes/conversation-cli-routes.ts +54 -7
  1006. package/src/runtime/routes/conversation-compaction-routes.ts +292 -0
  1007. package/src/runtime/routes/conversation-list-routes.ts +225 -9
  1008. package/src/runtime/routes/conversation-management-routes.ts +96 -28
  1009. package/src/runtime/routes/conversation-query-routes.ts +150 -51
  1010. package/src/runtime/routes/conversation-routes.ts +615 -327
  1011. package/src/runtime/routes/conversation-starter-routes.ts +35 -20
  1012. package/src/runtime/routes/conversations-import-routes.ts +48 -13
  1013. package/src/runtime/routes/credential-prompt-routes.ts +5 -0
  1014. package/src/runtime/routes/credential-routes.ts +25 -6
  1015. package/src/runtime/routes/debug-bash-routes.ts +5 -0
  1016. package/src/runtime/routes/debug-routes.ts +11 -2
  1017. package/src/runtime/routes/defer-routes.ts +13 -0
  1018. package/src/runtime/routes/diagnostics-routes.ts +37 -46
  1019. package/src/runtime/routes/disk-pressure-routes.ts +17 -31
  1020. package/src/runtime/routes/document-comments-routes.ts +46 -27
  1021. package/src/runtime/routes/documents-routes.ts +35 -11
  1022. package/src/runtime/routes/domain-routes.ts +98 -51
  1023. package/src/runtime/routes/email-routes.ts +33 -0
  1024. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  1025. package/src/runtime/routes/events-routes.ts +107 -8
  1026. package/src/runtime/routes/filing-routes.ts +9 -4
  1027. package/src/runtime/routes/gateway-log-routes.ts +31 -4
  1028. package/src/runtime/routes/global-search-routes.ts +53 -50
  1029. package/src/runtime/routes/group-routes.ts +32 -5
  1030. package/src/runtime/routes/guardian-action-routes.ts +9 -0
  1031. package/src/runtime/routes/guardian-approval-interception.ts +0 -31
  1032. package/src/runtime/routes/heartbeat-routes.ts +57 -21
  1033. package/src/runtime/routes/home-feed-routes.ts +149 -16
  1034. package/src/runtime/routes/home-state-routes.ts +8 -40
  1035. package/src/runtime/routes/host-app-control-routes.ts +6 -1
  1036. package/src/runtime/routes/host-bash-routes.ts +5 -0
  1037. package/src/runtime/routes/host-browser-routes.ts +13 -0
  1038. package/src/runtime/routes/host-cu-routes.ts +6 -1
  1039. package/src/runtime/routes/host-file-routes.ts +26 -6
  1040. package/src/runtime/routes/host-transfer-routes.ts +13 -2
  1041. package/src/runtime/routes/http-adapter.ts +1 -2
  1042. package/src/runtime/routes/identity-intro-cache.ts +72 -39
  1043. package/src/runtime/routes/identity-routes.ts +257 -20
  1044. package/src/runtime/routes/image-generation-routes.ts +45 -2
  1045. package/src/runtime/routes/inbound-message-handler.ts +16 -12
  1046. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +524 -12
  1047. package/src/runtime/routes/inbound-stages/background-dispatch.ts +72 -27
  1048. package/src/runtime/routes/index.ts +4 -0
  1049. package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
  1050. package/src/runtime/routes/inference-provider-connection-routes.ts +26 -31
  1051. package/src/runtime/routes/inference-send-routes.ts +11 -11
  1052. package/src/runtime/routes/integrations/a2a.ts +32 -7
  1053. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  1054. package/src/runtime/routes/integrations/slack/channel.ts +23 -3
  1055. package/src/runtime/routes/integrations/slack/share.ts +36 -8
  1056. package/src/runtime/routes/integrations/telegram.ts +34 -9
  1057. package/src/runtime/routes/integrations/twilio.ts +77 -7
  1058. package/src/runtime/routes/integrations/vercel.ts +18 -3
  1059. package/src/runtime/routes/internal-oauth-routes.ts +5 -0
  1060. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  1061. package/src/runtime/routes/internal-twilio-routes.ts +13 -0
  1062. package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
  1063. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  1064. package/src/runtime/routes/log-export-routes.ts +36 -10
  1065. package/src/runtime/routes/mcp-auth-routes.ts +25 -0
  1066. package/src/runtime/routes/memory-item-routes.ts +21 -10
  1067. package/src/runtime/routes/memory-v2-routes.ts +105 -44
  1068. package/src/runtime/routes/memory-v3-routes.ts +313 -257
  1069. package/src/runtime/routes/migration-rollback-routes.ts +5 -1
  1070. package/src/runtime/routes/migration-routes.ts +49 -13
  1071. package/src/runtime/routes/notification-routes.ts +80 -2
  1072. package/src/runtime/routes/oauth-apps.ts +99 -23
  1073. package/src/runtime/routes/oauth-commands-routes.ts +43 -15
  1074. package/src/runtime/routes/oauth-connect-routes.ts +9 -0
  1075. package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
  1076. package/src/runtime/routes/oauth-providers.ts +79 -15
  1077. package/src/runtime/routes/platform-routes.ts +102 -5
  1078. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +9 -6
  1079. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
  1080. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
  1081. package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
  1082. package/src/runtime/routes/playground/force-compact.ts +2 -2
  1083. package/src/runtime/routes/playground/helpers.ts +1 -2
  1084. package/src/runtime/routes/playground/inject-failures.ts +13 -8
  1085. package/src/runtime/routes/playground/reset-circuit.ts +14 -9
  1086. package/src/runtime/routes/playground/seed-conversation.ts +1 -1
  1087. package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
  1088. package/src/runtime/routes/playground/state.ts +4 -3
  1089. package/src/runtime/routes/plugins-routes.ts +22 -19
  1090. package/src/runtime/routes/profiler-routes.ts +17 -4
  1091. package/src/runtime/routes/ps-routes.ts +5 -0
  1092. package/src/runtime/routes/publish-routes.ts +13 -3
  1093. package/src/runtime/routes/question-routes.ts +5 -0
  1094. package/src/runtime/routes/recording-routes.ts +25 -12
  1095. package/src/runtime/routes/rename-conversation-routes.ts +10 -0
  1096. package/src/runtime/routes/sanity-routes.ts +9 -2
  1097. package/src/runtime/routes/schedule-routes.ts +288 -88
  1098. package/src/runtime/routes/secret-routes.ts +31 -6
  1099. package/src/runtime/routes/sequence-routes.ts +33 -0
  1100. package/src/runtime/routes/settings-routes.ts +65 -19
  1101. package/src/runtime/routes/skills-routes.ts +166 -73
  1102. package/src/runtime/routes/slack-channel-routes.ts +5 -0
  1103. package/src/runtime/routes/stt-routes.ts +13 -6
  1104. package/src/runtime/routes/subagents-routes.ts +24 -18
  1105. package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
  1106. package/src/runtime/routes/surface-action-routes.ts +10 -38
  1107. package/src/runtime/routes/surface-content-routes.ts +21 -6
  1108. package/src/runtime/routes/surface-conversation-resolver.ts +66 -0
  1109. package/src/runtime/routes/task-routes.ts +37 -0
  1110. package/src/runtime/routes/telemetry-routes.ts +9 -0
  1111. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  1112. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  1113. package/src/runtime/routes/trace-event-routes.ts +42 -1
  1114. package/src/runtime/routes/trust-rules-routes.ts +31 -2
  1115. package/src/runtime/routes/tts-routes.ts +48 -6
  1116. package/src/runtime/routes/types.ts +83 -16
  1117. package/src/runtime/routes/ui-request-routes.ts +5 -0
  1118. package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
  1119. package/src/runtime/routes/usage-routes.ts +118 -42
  1120. package/src/runtime/routes/user-routes-cli.ts +9 -0
  1121. package/src/runtime/routes/user-routes.ts +5 -1
  1122. package/src/runtime/routes/wake-conversation-routes.ts +5 -0
  1123. package/src/runtime/routes/watcher-routes.ts +21 -0
  1124. package/src/runtime/routes/webhook-routes.ts +50 -2
  1125. package/src/runtime/routes/wipe-conversation-routes.ts +8 -0
  1126. package/src/runtime/routes/work-items-routes.ts +49 -23
  1127. package/src/runtime/routes/workspace-commit-routes.ts +5 -0
  1128. package/src/runtime/routes/workspace-routes.test.ts +42 -0
  1129. package/src/runtime/routes/workspace-routes.ts +124 -9
  1130. package/src/runtime/services/__tests__/analyze-conversation.test.ts +10 -4
  1131. package/src/runtime/services/analyze-conversation.ts +5 -8
  1132. package/src/runtime/services/conversation-serializer.ts +24 -2
  1133. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  1134. package/src/runtime/sync/resource-sync-events.ts +17 -3
  1135. package/src/runtime/sync/sync-publisher.ts +2 -2
  1136. package/src/runtime/tool-grant-request-helper.ts +1 -0
  1137. package/src/schedule/run-script.ts +28 -3
  1138. package/src/schedule/schedule-store.ts +36 -2
  1139. package/src/schedule/schedule-usage-store.ts +83 -0
  1140. package/src/schedule/scheduler.ts +126 -21
  1141. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  1142. package/src/security/encrypted-store.ts +7 -16
  1143. package/src/security/store-path-override.ts +61 -0
  1144. package/src/signals/cancel.ts +2 -4
  1145. package/src/signals/user-message.ts +10 -16
  1146. package/src/skills/catalog-files.ts +4 -1
  1147. package/src/skills/catalog-install.ts +3 -0
  1148. package/src/skills/categories-cache.ts +118 -0
  1149. package/src/skills/clawhub-files.ts +1 -0
  1150. package/src/skills/skillssh-files.ts +1 -0
  1151. package/src/skills/validate-input.ts +177 -0
  1152. package/src/subagent/manager.ts +33 -24
  1153. package/src/subagent/types.ts +6 -0
  1154. package/src/tasks/tool-sanitizer.ts +2 -2
  1155. package/src/telemetry/types.ts +55 -1
  1156. package/src/telemetry/usage-telemetry-reporter.test.ts +250 -4
  1157. package/src/telemetry/usage-telemetry-reporter.ts +88 -2
  1158. package/src/tools/acp/context.ts +20 -0
  1159. package/src/tools/acp/list-agents.test.ts +7 -1
  1160. package/src/tools/acp/spawn.test.ts +198 -93
  1161. package/src/tools/acp/spawn.ts +32 -70
  1162. package/src/tools/acp/steer.test.ts +105 -8
  1163. package/src/tools/acp/steer.ts +48 -17
  1164. package/src/tools/apps/definitions.ts +42 -24
  1165. package/src/tools/apps/executors.ts +13 -8
  1166. package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
  1167. package/src/tools/ask-question/ask-question-tool.ts +85 -90
  1168. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  1169. package/src/tools/computer-use/definitions.ts +295 -289
  1170. package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
  1171. package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
  1172. package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
  1173. package/src/tools/credentials/vault.ts +112 -111
  1174. package/src/tools/document/document-tool.ts +131 -8
  1175. package/src/tools/execution-target.ts +3 -6
  1176. package/src/tools/execution-timeout.ts +3 -4
  1177. package/src/tools/executor.ts +18 -107
  1178. package/src/tools/filesystem/edit.ts +45 -42
  1179. package/src/tools/filesystem/list.ts +33 -30
  1180. package/src/tools/filesystem/read.ts +54 -35
  1181. package/src/tools/filesystem/write.ts +69 -32
  1182. package/src/tools/host-filesystem/edit.test.ts +1 -0
  1183. package/src/tools/host-filesystem/edit.ts +44 -42
  1184. package/src/tools/host-filesystem/read.test.ts +1 -0
  1185. package/src/tools/host-filesystem/read.ts +49 -35
  1186. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  1187. package/src/tools/host-filesystem/transfer.ts +121 -108
  1188. package/src/tools/host-filesystem/write.test.ts +1 -0
  1189. package/src/tools/host-filesystem/write.ts +33 -31
  1190. package/src/tools/host-terminal/host-shell.ts +50 -48
  1191. package/src/tools/mcp/mcp-tool-factory.ts +0 -2
  1192. package/src/tools/memory/register.ts +23 -24
  1193. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  1194. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  1195. package/src/tools/network/__tests__/web-search.test.ts +222 -6
  1196. package/src/tools/network/managed-search-proxy.ts +183 -0
  1197. package/src/tools/network/web-fetch.ts +49 -46
  1198. package/src/tools/network/web-search-error.test.ts +248 -0
  1199. package/src/tools/network/web-search-error.ts +267 -0
  1200. package/src/tools/network/web-search.ts +414 -97
  1201. package/src/tools/policy-context.ts +3 -1
  1202. package/src/tools/registry.ts +184 -118
  1203. package/src/tools/schedule/create.ts +14 -1
  1204. package/src/tools/schedule/update.ts +16 -0
  1205. package/src/tools/shared/filesystem/audio-read.ts +122 -0
  1206. package/src/tools/shared/filesystem/image-read.ts +1 -1
  1207. package/src/tools/skills/execute.ts +34 -31
  1208. package/src/tools/skills/load.ts +29 -23
  1209. package/src/tools/skills/skill-tool-factory.ts +17 -36
  1210. package/src/tools/subagent/notify-parent.ts +35 -32
  1211. package/src/tools/subagent/spawn.ts +2 -1
  1212. package/src/tools/system/avatar-generator.ts +13 -22
  1213. package/src/tools/system/request-permission.ts +30 -27
  1214. package/src/tools/terminal/safe-env.ts +10 -1
  1215. package/src/tools/terminal/shell.ts +190 -61
  1216. package/src/tools/tool-approval-handler.ts +10 -4
  1217. package/src/tools/tool-defaults.ts +20 -9
  1218. package/src/tools/tool-manifest.ts +4 -4
  1219. package/src/tools/tool-name-aliases.ts +72 -14
  1220. package/src/tools/types.ts +86 -33
  1221. package/src/tools/ui-surface/definitions.ts +196 -95
  1222. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  1223. package/src/tts/provider-catalog.ts +76 -1
  1224. package/src/types/onboarding-context.ts +6 -0
  1225. package/src/usage/attribution.ts +32 -1
  1226. package/src/usage/types.ts +10 -0
  1227. package/src/util/browser.ts +7 -2
  1228. package/src/util/errors.ts +2 -2
  1229. package/src/util/map-limit.ts +27 -0
  1230. package/src/util/mutex.ts +47 -0
  1231. package/src/util/platform.ts +15 -12
  1232. package/src/work-items/work-item-runner.ts +7 -2
  1233. package/src/workspace/git-service.ts +1 -42
  1234. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
  1235. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  1236. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  1237. package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
  1238. package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
  1239. package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
  1240. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  1241. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  1242. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
  1243. package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
  1244. package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
  1245. package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
  1246. package/src/workspace/migrations/registry.ts +16 -0
  1247. package/src/workspace/provider-commit-message-generator.ts +15 -17
  1248. package/tsconfig.json +4 -1
  1249. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  1250. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  1251. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  1252. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -262
  1253. package/src/__tests__/empty-response-pipeline.test.ts +0 -301
  1254. package/src/__tests__/history-repair-pipeline.test.ts +0 -396
  1255. package/src/__tests__/llm-call-pipeline.test.ts +0 -281
  1256. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  1257. package/src/__tests__/persistence-pipeline.test.ts +0 -376
  1258. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  1259. package/src/__tests__/token-estimate-pipeline.test.ts +0 -481
  1260. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  1261. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  1262. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -344
  1263. package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
  1264. package/src/cli/commands/memory-v3-render.ts +0 -344
  1265. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  1266. package/src/daemon/message-types/disk-pressure.ts +0 -9
  1267. package/src/email/feature-gate.ts +0 -23
  1268. package/src/gallery/default-gallery.ts +0 -1359
  1269. package/src/gallery/gallery-manifest.ts +0 -28
  1270. package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
  1271. package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -468
  1272. package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
  1273. package/src/memory/v3/__tests__/edges.test.ts +0 -563
  1274. package/src/memory/v3/__tests__/filter.test.ts +0 -512
  1275. package/src/memory/v3/__tests__/gate.test.ts +0 -574
  1276. package/src/memory/v3/__tests__/index-composition.test.ts +0 -233
  1277. package/src/memory/v3/__tests__/loop.test.ts +0 -530
  1278. package/src/memory/v3/__tests__/retriever.test.ts +0 -226
  1279. package/src/memory/v3/__tests__/scouts.test.ts +0 -440
  1280. package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -312
  1281. package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
  1282. package/src/memory/v3/__tests__/traversal.test.ts +0 -469
  1283. package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
  1284. package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
  1285. package/src/memory/v3/__tests__/tree-walk.test.ts +0 -707
  1286. package/src/memory/v3/__tests__/validate.test.ts +0 -245
  1287. package/src/memory/v3/auto-edges.ts +0 -223
  1288. package/src/memory/v3/coactivation-store.ts +0 -124
  1289. package/src/memory/v3/consolidation-job.ts +0 -323
  1290. package/src/memory/v3/edge-learning-job.ts +0 -160
  1291. package/src/memory/v3/edges.ts +0 -249
  1292. package/src/memory/v3/filter.ts +0 -281
  1293. package/src/memory/v3/gate.ts +0 -334
  1294. package/src/memory/v3/index-composition.ts +0 -113
  1295. package/src/memory/v3/llm-capture.ts +0 -46
  1296. package/src/memory/v3/loop.ts +0 -382
  1297. package/src/memory/v3/maintenance.ts +0 -144
  1298. package/src/memory/v3/prompt-context.ts +0 -33
  1299. package/src/memory/v3/prompts/consolidation.ts +0 -458
  1300. package/src/memory/v3/prompts/system-prompts.ts +0 -196
  1301. package/src/memory/v3/retriever.ts +0 -33
  1302. package/src/memory/v3/scouts.ts +0 -420
  1303. package/src/memory/v3/shadow-middleware.ts +0 -305
  1304. package/src/memory/v3/traversal.ts +0 -206
  1305. package/src/memory/v3/tree-index.ts +0 -237
  1306. package/src/memory/v3/tree-store.ts +0 -394
  1307. package/src/memory/v3/tree-walk.ts +0 -351
  1308. package/src/memory/v3/types.ts +0 -65
  1309. package/src/memory/v3/validate.ts +0 -300
  1310. package/src/plugins/defaults/circuit-breaker.ts +0 -141
  1311. package/src/plugins/defaults/compaction.ts +0 -141
  1312. package/src/plugins/defaults/empty-response.ts +0 -124
  1313. package/src/plugins/defaults/history-repair.ts +0 -83
  1314. package/src/plugins/defaults/llm-call.ts +0 -77
  1315. package/src/plugins/defaults/memory-retrieval.ts +0 -219
  1316. package/src/plugins/defaults/overflow-reduce.ts +0 -185
  1317. package/src/plugins/defaults/persistence.ts +0 -127
  1318. package/src/plugins/defaults/title-generate.ts +0 -90
  1319. package/src/plugins/defaults/token-estimate.ts +0 -101
  1320. package/src/plugins/defaults/tool-error.ts +0 -119
  1321. package/src/plugins/defaults/tool-execute.ts +0 -87
  1322. package/src/plugins/defaults/tool-result-truncate.ts +0 -84
  1323. package/src/skills/category-inference.ts +0 -111
@@ -1,10 +1,13 @@
1
1
  import { rmSync, writeFileSync } from "node:fs";
2
2
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
3
3
 
4
+ import { CompactionCircuit } from "../agent/compaction-circuit.js";
4
5
  import type {
5
6
  AgentEvent,
7
+ AgentLoopRunResult,
6
8
  CheckpointDecision,
7
9
  CheckpointInfo,
10
+ ExitReason,
8
11
  } from "../agent/loop.js";
9
12
  import type { ServerMessage } from "../daemon/message-protocol.js";
10
13
  import type { Message, ProviderResponse } from "../providers/types.js";
@@ -175,7 +178,7 @@ mock.module("../memory/conversation-crud.js", () => ({
175
178
  _convId: string,
176
179
  role: string,
177
180
  content: string,
178
- metadata?: Record<string, unknown>,
181
+ options?: { metadata?: Record<string, unknown> },
179
182
  ) => {
180
183
  // Simulate a persist failure for tests that need to exercise the
181
184
  // tail-persist-failed path in drainBatch. Triggered by matching any
@@ -186,13 +189,20 @@ mock.module("../memory/conversation-crud.js", () => ({
186
189
  }
187
190
  }
188
191
  const id = `msg-${Date.now()}-${capturedAddMessages.length}`;
189
- capturedAddMessages.push({ id, role, content, metadata });
192
+ capturedAddMessages.push({
193
+ id,
194
+ role,
195
+ content,
196
+ metadata: options?.metadata,
197
+ });
190
198
  return { id };
191
199
  },
192
200
  updateConversationUsage: () => {},
193
201
  updateConversationTitle: () => {},
194
202
  getMessageById: () => null,
195
203
  getLastUserTimestampBefore: () => 0,
204
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
205
+ updateMessageContent: mock(() => {}),
196
206
  }));
197
207
 
198
208
  mock.module("../memory/conversation-queries.js", () => ({
@@ -315,16 +325,24 @@ interface PendingRun {
315
325
  resolve: (history: Message[]) => void;
316
326
  reject: (err: Error) => void;
317
327
  messages: Message[];
318
- onEvent: (event: AgentEvent) => void;
328
+ onEvent: (event: AgentEvent) => void | Promise<void>;
319
329
  onCheckpoint?: (
320
330
  checkpoint: CheckpointInfo,
321
331
  ) => CheckpointDecision | Promise<CheckpointDecision>;
332
+ /**
333
+ * Pause-reason recorded from the most recent `onCheckpoint` call, mirroring
334
+ * how the production loop carries it back via {@link AgentLoopRunResult}.
335
+ * `resolve(history)` packages this into the run result so the orchestrator
336
+ * derives its handoff bookkeeping the same way it does against the real loop.
337
+ */
338
+ exitReason: ExitReason | null;
322
339
  }
323
340
 
324
341
  let pendingRuns: PendingRun[] = [];
325
342
 
326
343
  mock.module("../agent/loop.js", () => ({
327
344
  AgentLoop: class {
345
+ compactionCircuit = new CompactionCircuit("test-conv");
328
346
  constructor() {}
329
347
  getToolTokenBudget() {
330
348
  return 0;
@@ -337,15 +355,35 @@ mock.module("../agent/loop.js", () => ({
337
355
  }
338
356
  async run(
339
357
  messages: Message[],
340
- onEvent: (event: AgentEvent) => void,
341
- _signal?: AbortSignal,
342
- _requestId?: string,
343
- onCheckpoint?: (
344
- checkpoint: CheckpointInfo,
345
- ) => CheckpointDecision | Promise<CheckpointDecision>,
346
- ): Promise<Message[]> {
347
- return new Promise<Message[]>((resolve, reject) => {
348
- pendingRuns.push({ resolve, reject, messages, onEvent, onCheckpoint });
358
+ onEvent: (event: AgentEvent) => void | Promise<void>,
359
+ options?: {
360
+ onCheckpoint?: (
361
+ checkpoint: CheckpointInfo,
362
+ ) => CheckpointDecision | Promise<CheckpointDecision>;
363
+ },
364
+ ): Promise<AgentLoopRunResult> {
365
+ return new Promise<AgentLoopRunResult>((resolveResult, reject) => {
366
+ const pending: PendingRun = {
367
+ resolve: (history: Message[]) =>
368
+ resolveResult({
369
+ history,
370
+ exitReason: pending.exitReason,
371
+ appendedNewMessages: history.length > messages.length,
372
+ newMessages: history.slice(messages.length),
373
+ }),
374
+ reject,
375
+ messages,
376
+ onEvent,
377
+ exitReason: null,
378
+ onCheckpoint: options?.onCheckpoint
379
+ ? async (checkpoint) => {
380
+ const decision = await options.onCheckpoint!(checkpoint);
381
+ pending.exitReason = decision === "continue" ? null : decision;
382
+ return decision;
383
+ }
384
+ : undefined,
385
+ };
386
+ pendingRuns.push(pending);
349
387
  });
350
388
  }
351
389
  },
@@ -409,9 +447,9 @@ function makeConversation(
409
447
  "conv-1",
410
448
  provider,
411
449
  "system prompt",
412
- 4096,
413
450
  sendToClient ?? (() => {}),
414
451
  "/tmp",
452
+ { maxTokens: 4096 },
415
453
  );
416
454
  const conversationWithWorkspaceDeps =
417
455
  conversationObj as ConversationWithWorkspaceDeps;
@@ -471,7 +509,7 @@ async function waitForCondition(
471
509
  * that `runAgentLoop` expects (usage + message_complete) so the conversation
472
510
  * cleanly transitions out of its processing state.
473
511
  */
474
- function resolveRun(index: number) {
512
+ async function resolveRun(index: number) {
475
513
  const run = pendingRuns[index];
476
514
  if (!run) throw new Error(`No pending run at index ${index}`);
477
515
  // Emit the events runAgentLoop expects
@@ -479,14 +517,17 @@ function resolveRun(index: number) {
479
517
  role: "assistant",
480
518
  content: [{ type: "text", text: `reply-${index}` }],
481
519
  };
482
- run.onEvent({
520
+ // Prime the assistant row anchor — production code emits this from
521
+ // `AgentLoop.run` just before `provider.sendMessage`.
522
+ await run.onEvent({ type: "llm_call_started" });
523
+ await run.onEvent({
483
524
  type: "usage",
484
525
  inputTokens: 10,
485
526
  outputTokens: 5,
486
527
  model: "mock",
487
528
  providerDurationMs: 100,
488
529
  });
489
- run.onEvent({ type: "message_complete", message: assistantMsg });
530
+ await run.onEvent({ type: "message_complete", message: assistantMsg });
490
531
  // Return updated history with the assistant message appended
491
532
  run.resolve([...run.messages, assistantMsg]);
492
533
  }
@@ -519,12 +560,12 @@ describe("Conversation message queue", () => {
519
560
  const events2: ServerMessage[] = [];
520
561
 
521
562
  // Start first message — this will block on AgentLoop.run
522
- const p1 = conversation.processMessage(
523
- "msg-1",
524
- [],
525
- (e) => events1.push(e),
526
- "req-1",
527
- );
563
+ const p1 = conversation.processMessage({
564
+ content: "msg-1",
565
+ attachments: [],
566
+ onEvent: (e) => events1.push(e),
567
+ requestId: "req-1",
568
+ });
528
569
 
529
570
  // Wait for the first AgentLoop.run to be registered
530
571
  await waitForPendingRun(1);
@@ -533,18 +574,17 @@ describe("Conversation message queue", () => {
533
574
  expect(conversation.isProcessing()).toBe(true);
534
575
 
535
576
  // Enqueue second message — should NOT throw
536
- const result = conversation.enqueueMessage(
537
- "msg-2",
538
- [],
539
- (e) => events2.push(e),
540
- "req-2",
541
- );
577
+ const result = conversation.enqueueMessage({
578
+ content: "msg-2",
579
+ onEvent: (e) => events2.push(e),
580
+ requestId: "req-2",
581
+ });
542
582
  expect(result.queued).toBe(true);
543
583
  expect(result.requestId).toBe("req-2");
544
584
  expect(conversation.getQueueDepth()).toBe(1);
545
585
 
546
586
  // Complete the first message
547
- resolveRun(0);
587
+ await resolveRun(0);
548
588
  await p1;
549
589
 
550
590
  // After the first run resolves, the queue drains and triggers a second run.
@@ -557,7 +597,7 @@ describe("Conversation message queue", () => {
557
597
  expect(pendingRuns.length).toBe(2);
558
598
 
559
599
  // Complete the second run
560
- resolveRun(1);
600
+ await resolveRun(1);
561
601
  await new Promise((r) => setTimeout(r, 10));
562
602
  });
563
603
 
@@ -570,21 +610,29 @@ describe("Conversation message queue", () => {
570
610
  const events3: ServerMessage[] = [];
571
611
 
572
612
  // Start first message
573
- const p1 = conversation.processMessage(
574
- "msg-1",
575
- [],
576
- (e) => events1.push(e),
577
- "req-1",
578
- );
613
+ const p1 = conversation.processMessage({
614
+ content: "msg-1",
615
+ attachments: [],
616
+ onEvent: (e) => events1.push(e),
617
+ requestId: "req-1",
618
+ });
579
619
  await waitForPendingRun(1);
580
620
 
581
621
  // 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");
622
+ conversation.enqueueMessage({
623
+ content: "msg-2",
624
+ onEvent: (e) => events2.push(e),
625
+ requestId: "req-2",
626
+ });
627
+ conversation.enqueueMessage({
628
+ content: "msg-3",
629
+ onEvent: (e) => events3.push(e),
630
+ requestId: "req-3",
631
+ });
584
632
  expect(conversation.getQueueDepth()).toBe(2);
585
633
 
586
634
  // Complete run 0 → drain pulls msg-2 and msg-3 into ONE batched run.
587
- resolveRun(0);
635
+ await resolveRun(0);
588
636
  await p1;
589
637
  await waitForPendingRun(2);
590
638
 
@@ -623,7 +671,7 @@ describe("Conversation message queue", () => {
623
671
  expect(combinedUserText).toContain("msg-3");
624
672
 
625
673
  // Resolve the batched run; message_complete must fan out to both clients.
626
- resolveRun(1);
674
+ await resolveRun(1);
627
675
  await new Promise((r) => setTimeout(r, 10));
628
676
 
629
677
  expect(events2.some((e) => e.type === "message_complete")).toBe(true);
@@ -637,20 +685,23 @@ describe("Conversation message queue", () => {
637
685
  const events2: ServerMessage[] = [];
638
686
 
639
687
  // Start first message
640
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
688
+ const p1 = conversation.processMessage({
689
+ content: "msg-1",
690
+ attachments: [],
691
+ requestId: "req-1",
692
+ });
641
693
  await waitForPendingRun(1);
642
694
 
643
695
  // Enqueue second — simulating what handleUserMessage does
644
- const result = conversation.enqueueMessage(
645
- "msg-2",
646
- [],
647
- (e) => events2.push(e),
648
- "req-2",
649
- );
696
+ const result = conversation.enqueueMessage({
697
+ content: "msg-2",
698
+ onEvent: (e) => events2.push(e),
699
+ requestId: "req-2",
700
+ });
650
701
  expect(result.queued).toBe(true);
651
702
 
652
703
  // Complete first
653
- resolveRun(0);
704
+ await resolveRun(0);
654
705
  await p1;
655
706
  await waitForPendingRun(2);
656
707
 
@@ -664,7 +715,7 @@ describe("Conversation message queue", () => {
664
715
  });
665
716
 
666
717
  // Complete second run so the conversation finishes cleanly
667
- resolveRun(1);
718
+ await resolveRun(1);
668
719
  await new Promise((r) => setTimeout(r, 10));
669
720
  });
670
721
 
@@ -676,12 +727,24 @@ describe("Conversation message queue", () => {
676
727
  const events3: ServerMessage[] = [];
677
728
 
678
729
  // Start first message
679
- conversation.processMessage("msg-1", [], () => {}, "req-1");
730
+ conversation.processMessage({
731
+ content: "msg-1",
732
+ attachments: [],
733
+ requestId: "req-1",
734
+ });
680
735
  await waitForPendingRun(1);
681
736
 
682
737
  // 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");
738
+ conversation.enqueueMessage({
739
+ content: "msg-2",
740
+ onEvent: (e) => events2.push(e),
741
+ requestId: "req-2",
742
+ });
743
+ conversation.enqueueMessage({
744
+ content: "msg-3",
745
+ onEvent: (e) => events3.push(e),
746
+ requestId: "req-3",
747
+ });
685
748
  expect(conversation.getQueueDepth()).toBe(2);
686
749
 
687
750
  // Abort
@@ -727,12 +790,12 @@ describe("Conversation message queue", () => {
727
790
  const events: ServerMessage[] = [];
728
791
 
729
792
  // Start a message — blocks on AgentLoop.run
730
- const p1 = conversation.processMessage(
731
- "msg-1",
732
- [],
733
- (e) => events.push(e),
734
- "req-1",
735
- );
793
+ const p1 = conversation.processMessage({
794
+ content: "msg-1",
795
+ attachments: [],
796
+ onEvent: (e) => events.push(e),
797
+ requestId: "req-1",
798
+ });
736
799
  await waitForPendingRun(1);
737
800
 
738
801
  // Reject the AgentLoop.run() with a provider error to trigger the
@@ -754,23 +817,27 @@ describe("Conversation message queue", () => {
754
817
  await conversation.loadFromDb();
755
818
 
756
819
  // Start first message
757
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
820
+ const p1 = conversation.processMessage({
821
+ content: "msg-1",
822
+ attachments: [],
823
+ requestId: "req-1",
824
+ });
758
825
  await waitForPendingRun(1);
759
826
 
760
827
  expect(conversation.getQueueDepth()).toBe(0);
761
828
 
762
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
829
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
763
830
  expect(conversation.getQueueDepth()).toBe(1);
764
831
 
765
- conversation.enqueueMessage("msg-3", [], () => {}, "req-3");
832
+ conversation.enqueueMessage({ content: "msg-3", requestId: "req-3" });
766
833
  expect(conversation.getQueueDepth()).toBe(2);
767
834
 
768
- conversation.enqueueMessage("msg-4", [], () => {}, "req-4");
835
+ conversation.enqueueMessage({ content: "msg-4", requestId: "req-4" });
769
836
  expect(conversation.getQueueDepth()).toBe(3);
770
837
 
771
838
  // Complete first → drain pulls all three same-interface passthroughs
772
839
  // into a single batched run (depth → 0, runs → 2 total).
773
- resolveRun(0);
840
+ await resolveRun(0);
774
841
  await p1;
775
842
  await waitForPendingRun(2);
776
843
 
@@ -778,7 +845,7 @@ describe("Conversation message queue", () => {
778
845
  expect(pendingRuns.length).toBe(2);
779
846
 
780
847
  // Complete the batched run; conversation finishes cleanly.
781
- resolveRun(1);
848
+ await resolveRun(1);
782
849
  await new Promise((r) => setTimeout(r, 10));
783
850
  });
784
851
 
@@ -791,23 +858,31 @@ describe("Conversation message queue", () => {
791
858
  const events3: ServerMessage[] = [];
792
859
 
793
860
  // Start first message — blocks on AgentLoop.run
794
- const p1 = conversation.processMessage(
795
- "msg-1",
796
- [],
797
- (e) => events1.push(e),
798
- "req-1",
799
- );
861
+ const p1 = conversation.processMessage({
862
+ content: "msg-1",
863
+ attachments: [],
864
+ onEvent: (e) => events1.push(e),
865
+ requestId: "req-1",
866
+ });
800
867
  await waitForPendingRun(1);
801
868
 
802
869
  // Enqueue a message with empty content (will fail persistUserMessage)
803
- conversation.enqueueMessage("", [], (e) => events2.push(e), "req-2");
870
+ conversation.enqueueMessage({
871
+ content: "",
872
+ onEvent: (e) => events2.push(e),
873
+ requestId: "req-2",
874
+ });
804
875
  // Enqueue a valid message after the bad one
805
- conversation.enqueueMessage("msg-3", [], (e) => events3.push(e), "req-3");
876
+ conversation.enqueueMessage({
877
+ content: "msg-3",
878
+ onEvent: (e) => events3.push(e),
879
+ requestId: "req-3",
880
+ });
806
881
  expect(conversation.getQueueDepth()).toBe(2);
807
882
 
808
883
  // Complete first message — triggers drain. The empty message should fail
809
884
  // to persist, but the drain should continue to msg-3.
810
- resolveRun(0);
885
+ await resolveRun(0);
811
886
  await p1;
812
887
 
813
888
  // msg-3 should have been dequeued and started a new AgentLoop.run
@@ -824,7 +899,7 @@ describe("Conversation message queue", () => {
824
899
  expect(events3.some((e) => e.type === "message_dequeued")).toBe(true);
825
900
 
826
901
  // Complete the third message's run
827
- resolveRun(1);
902
+ await resolveRun(1);
828
903
  await new Promise((r) => setTimeout(r, 10));
829
904
 
830
905
  // msg-3 should have completed successfully
@@ -851,7 +926,11 @@ describe("Batched drain", () => {
851
926
  const events5: ServerMessage[] = [];
852
927
 
853
928
  // Start in-flight message (msg-1)
854
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
929
+ const p1 = conversation.processMessage({
930
+ content: "msg-1",
931
+ attachments: [],
932
+ requestId: "req-1",
933
+ });
855
934
  await waitForPendingRun(1);
856
935
 
857
936
  // Enqueue 4 messages with interfaces [macos, macos, cli, macos].
@@ -860,46 +939,34 @@ describe("Batched drain", () => {
860
939
  userMessageInterface: iface,
861
940
  assistantMessageInterface: iface,
862
941
  });
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
- );
942
+ conversation.enqueueMessage({
943
+ content: "msg-2",
944
+ onEvent: (e) => events2.push(e),
945
+ requestId: "req-2",
946
+ metadata: meta("macos"),
947
+ });
948
+ conversation.enqueueMessage({
949
+ content: "msg-3",
950
+ onEvent: (e) => events3.push(e),
951
+ requestId: "req-3",
952
+ metadata: meta("macos"),
953
+ });
954
+ conversation.enqueueMessage({
955
+ content: "msg-4",
956
+ onEvent: (e) => events4.push(e),
957
+ requestId: "req-4",
958
+ metadata: meta("cli"),
959
+ });
960
+ conversation.enqueueMessage({
961
+ content: "msg-5",
962
+ onEvent: (e) => events5.push(e),
963
+ requestId: "req-5",
964
+ metadata: meta("macos"),
965
+ });
899
966
  expect(conversation.getQueueDepth()).toBe(4);
900
967
 
901
968
  // Resolve msg-1 → batched run pulls macos msg-2 + msg-3.
902
- resolveRun(0);
969
+ await resolveRun(0);
903
970
  await p1;
904
971
  await waitForPendingRun(2);
905
972
 
@@ -927,7 +994,7 @@ describe("Batched drain", () => {
927
994
  );
928
995
 
929
996
  // Resolve the batched run → drain pulls the cli single-message run.
930
- resolveRun(1);
997
+ await resolveRun(1);
931
998
  await waitForPendingRun(3);
932
999
 
933
1000
  // cli run contains msg-4 as a single-message run.
@@ -942,7 +1009,7 @@ describe("Batched drain", () => {
942
1009
  );
943
1010
 
944
1011
  // Resolve the cli run → drain pulls the final macos single-message run.
945
- resolveRun(2);
1012
+ await resolveRun(2);
946
1013
  await waitForPendingRun(4);
947
1014
  const finalHistory = pendingRuns[3].messages;
948
1015
  const finalUserText = finalHistory
@@ -957,7 +1024,7 @@ describe("Batched drain", () => {
957
1024
  // Four total runs: msg-1, batched [msg-2, msg-3], msg-4, msg-5.
958
1025
  expect(pendingRuns.length).toBe(4);
959
1026
 
960
- resolveRun(3);
1027
+ await resolveRun(3);
961
1028
  await new Promise((r) => setTimeout(r, 10));
962
1029
  });
963
1030
 
@@ -970,36 +1037,37 @@ describe("Batched drain", () => {
970
1037
  const eventsWorld: ServerMessage[] = [];
971
1038
 
972
1039
  // Start in-flight message
973
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
1040
+ const p1 = conversation.processMessage({
1041
+ content: "msg-1",
1042
+ attachments: [],
1043
+ requestId: "req-1",
1044
+ });
974
1045
  await waitForPendingRun(1);
975
1046
 
976
1047
  // Enqueue ["hello", "/compact", "world"]. /compact resolves to a non-
977
1048
  // passthrough slash, so the batch builder stops at "hello" (length 1),
978
1049
  // then /compact takes the single-message /compact short-circuit path
979
1050
  // (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
- );
1051
+ conversation.enqueueMessage({
1052
+ content: "hello",
1053
+ onEvent: (e) => eventsHello.push(e),
1054
+ requestId: "req-hello",
1055
+ });
1056
+ conversation.enqueueMessage({
1057
+ content: "/compact",
1058
+ onEvent: (e) => eventsSlash.push(e),
1059
+ requestId: "req-slash",
1060
+ });
1061
+ conversation.enqueueMessage({
1062
+ content: "world",
1063
+ onEvent: (e) => eventsWorld.push(e),
1064
+ requestId: "req-world",
1065
+ });
998
1066
  expect(conversation.getQueueDepth()).toBe(3);
999
1067
 
1000
1068
  // Resolve msg-1 → drain pulls "hello" as its own run (batch stops at
1001
1069
  // /compact boundary).
1002
- resolveRun(0);
1070
+ await resolveRun(0);
1003
1071
  await p1;
1004
1072
  await waitForPendingRun(2);
1005
1073
 
@@ -1010,7 +1078,7 @@ describe("Batched drain", () => {
1010
1078
 
1011
1079
  // Resolve "hello" → drain pops /compact via the builder-rejected path,
1012
1080
  // runs its short-circuit (no new runAgentLoop), then drains "world".
1013
- resolveRun(1);
1081
+ await resolveRun(1);
1014
1082
  await waitForPendingRun(3);
1015
1083
 
1016
1084
  // /compact should have emitted its own message_complete via the short-
@@ -1019,7 +1087,7 @@ describe("Batched drain", () => {
1019
1087
  expect(eventsWorld.some((e) => e.type === "message_dequeued")).toBe(true);
1020
1088
  expect(pendingRuns.length).toBe(3);
1021
1089
 
1022
- resolveRun(2);
1090
+ await resolveRun(2);
1023
1091
  await new Promise((r) => setTimeout(r, 10));
1024
1092
  });
1025
1093
 
@@ -1042,7 +1110,11 @@ describe("Batched drain", () => {
1042
1110
  const eventsPlainB: ServerMessage[] = [];
1043
1111
 
1044
1112
  // Start in-flight message
1045
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
1113
+ const p1 = conversation.processMessage({
1114
+ content: "msg-1",
1115
+ attachments: [],
1116
+ requestId: "req-1",
1117
+ });
1046
1118
  await waitForPendingRun(1);
1047
1119
 
1048
1120
  // Enqueue ["plain-a", "/status", "plain-b"]. /status resolves to a non-
@@ -1051,29 +1123,26 @@ describe("Batched drain", () => {
1051
1123
  // unknown-slash short-circuit path (no new runAgentLoop invocation — it
1052
1124
  // emits assistant_text_delta + message_complete inline), then "plain-b"
1053
1125
  // 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
- );
1126
+ conversation.enqueueMessage({
1127
+ content: "plain-a",
1128
+ onEvent: (e) => eventsPlainA.push(e),
1129
+ requestId: "req-plain-a",
1130
+ });
1131
+ conversation.enqueueMessage({
1132
+ content: "/status",
1133
+ onEvent: (e) => eventsSlash.push(e),
1134
+ requestId: "req-slash",
1135
+ });
1136
+ conversation.enqueueMessage({
1137
+ content: "plain-b",
1138
+ onEvent: (e) => eventsPlainB.push(e),
1139
+ requestId: "req-plain-b",
1140
+ });
1072
1141
  expect(conversation.getQueueDepth()).toBe(3);
1073
1142
 
1074
1143
  // Resolve msg-1 → drain pulls "plain-a" as its own run (batch stops at
1075
1144
  // the /status boundary).
1076
- resolveRun(0);
1145
+ await resolveRun(0);
1077
1146
  await p1;
1078
1147
  await waitForPendingRun(2);
1079
1148
 
@@ -1086,7 +1155,7 @@ describe("Batched drain", () => {
1086
1155
  // runs its unknown-slash short-circuit (no new runAgentLoop, emits
1087
1156
  // assistant_text_delta + message_complete inline), then drains "plain-b"
1088
1157
  // as its own run.
1089
- resolveRun(1);
1158
+ await resolveRun(1);
1090
1159
  await waitForPendingRun(3);
1091
1160
 
1092
1161
  // /status should have emitted its own assistant_text_delta + message_complete
@@ -1100,7 +1169,7 @@ describe("Batched drain", () => {
1100
1169
  // without a runAgentLoop invocation.
1101
1170
  expect(pendingRuns.length).toBe(3);
1102
1171
 
1103
- resolveRun(2);
1172
+ await resolveRun(2);
1104
1173
  await new Promise((r) => setTimeout(r, 10));
1105
1174
  });
1106
1175
 
@@ -1110,7 +1179,11 @@ describe("Batched drain", () => {
1110
1179
  await conversation.loadFromDb();
1111
1180
 
1112
1181
  // Start in-flight message
1113
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
1182
+ const p1 = conversation.processMessage({
1183
+ content: "msg-1",
1184
+ attachments: [],
1185
+ requestId: "req-1",
1186
+ });
1114
1187
  await waitForPendingRun(1);
1115
1188
 
1116
1189
  // Two sibling messages, each with a distinct image attachment.
@@ -1132,11 +1205,19 @@ describe("Batched drain", () => {
1132
1205
  filePath: "/tmp/b.png",
1133
1206
  },
1134
1207
  ];
1135
- conversation.enqueueMessage("with-A", attachA, () => {}, "req-A");
1136
- conversation.enqueueMessage("with-B", attachB, () => {}, "req-B");
1208
+ conversation.enqueueMessage({
1209
+ content: "with-A",
1210
+ attachments: attachA,
1211
+ requestId: "req-A",
1212
+ });
1213
+ conversation.enqueueMessage({
1214
+ content: "with-B",
1215
+ attachments: attachB,
1216
+ requestId: "req-B",
1217
+ });
1137
1218
  expect(conversation.getQueueDepth()).toBe(2);
1138
1219
 
1139
- resolveRun(0);
1220
+ await resolveRun(0);
1140
1221
  await p1;
1141
1222
  await waitForPendingRun(2);
1142
1223
 
@@ -1170,7 +1251,7 @@ describe("Batched drain", () => {
1170
1251
  expect(allText).toContain("a.png");
1171
1252
  expect(allText).toContain("b.png");
1172
1253
 
1173
- resolveRun(1);
1254
+ await resolveRun(1);
1174
1255
  await new Promise((r) => setTimeout(r, 10));
1175
1256
  });
1176
1257
 
@@ -1185,34 +1266,33 @@ describe("Batched drain", () => {
1185
1266
  new MessageQueue(budget);
1186
1267
 
1187
1268
  // Start in-flight so subsequent enqueues are queued (not processed).
1188
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
1269
+ const p1 = conversation.processMessage({
1270
+ content: "msg-1",
1271
+ attachments: [],
1272
+ requestId: "req-1",
1273
+ });
1189
1274
  await waitForPendingRun(1);
1190
1275
 
1191
1276
  // 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
- );
1277
+ const accepted1 = conversation.enqueueMessage({
1278
+ content: "x".repeat(500),
1279
+ requestId: "req-big-1",
1280
+ });
1281
+ const accepted2 = conversation.enqueueMessage({
1282
+ content: "y".repeat(500),
1283
+ requestId: "req-big-2",
1284
+ });
1204
1285
  expect(accepted1.queued).toBe(true);
1205
1286
  expect(accepted2.queued).toBe(true);
1206
1287
  // A third would push the queue over budget → rejected. Capture its
1207
1288
  // onEvent callback so we can verify the queue_full error event reaches
1208
1289
  // the rejected caller (not just the synchronous return value).
1209
1290
  const rejectedEvents: ServerMessage[] = [];
1210
- const rejected = conversation.enqueueMessage(
1211
- "z".repeat(500),
1212
- [],
1213
- (e) => rejectedEvents.push(e),
1214
- "req-over",
1215
- );
1291
+ const rejected = conversation.enqueueMessage({
1292
+ content: "z".repeat(500),
1293
+ onEvent: (e) => rejectedEvents.push(e),
1294
+ requestId: "req-over",
1295
+ });
1216
1296
  expect(rejected.queued).toBe(false);
1217
1297
  expect(rejected.rejected).toBe(true);
1218
1298
  expect(conversation.getQueueDepth()).toBe(2);
@@ -1231,33 +1311,41 @@ describe("Batched drain", () => {
1231
1311
  }
1232
1312
 
1233
1313
  // Complete in-flight → drain pulls both queued passthroughs as ONE batched run.
1234
- resolveRun(0);
1314
+ await resolveRun(0);
1235
1315
  await p1;
1236
1316
  await waitForPendingRun(2);
1237
1317
  expect(conversation.getQueueDepth()).toBe(0);
1238
1318
 
1239
1319
  // Resolve the batched run.
1240
- resolveRun(1);
1320
+ await resolveRun(1);
1241
1321
  await new Promise((r) => setTimeout(r, 10));
1242
1322
 
1243
1323
  // After the full drain, the byte budget must be fully reclaimed — a fresh
1244
1324
  // round of enqueues up to the budget should succeed again. Spin up another
1245
1325
  // in-flight message to reach the queueing state.
1246
- const p2 = conversation.processMessage("msg-2", [], () => {}, "req-2");
1326
+ const p2 = conversation.processMessage({
1327
+ content: "msg-2",
1328
+ attachments: [],
1329
+ requestId: "req-2",
1330
+ });
1247
1331
  await waitForPendingRun(3);
1248
1332
  expect(
1249
- conversation.enqueueMessage("a".repeat(500), [], () => {}, "req-a")
1250
- .queued,
1333
+ conversation.enqueueMessage({
1334
+ content: "a".repeat(500),
1335
+ requestId: "req-a",
1336
+ }).queued,
1251
1337
  ).toBe(true);
1252
1338
  expect(
1253
- conversation.enqueueMessage("b".repeat(500), [], () => {}, "req-b")
1254
- .queued,
1339
+ conversation.enqueueMessage({
1340
+ content: "b".repeat(500),
1341
+ requestId: "req-b",
1342
+ }).queued,
1255
1343
  ).toBe(true);
1256
1344
 
1257
- resolveRun(2);
1345
+ await resolveRun(2);
1258
1346
  await p2;
1259
1347
  await waitForPendingRun(4);
1260
- resolveRun(3);
1348
+ await resolveRun(3);
1261
1349
  await new Promise((r) => setTimeout(r, 10));
1262
1350
  });
1263
1351
  });
@@ -1281,7 +1369,11 @@ describe("Batched drain correctness fixes", () => {
1281
1369
  const eventsRegular: ServerMessage[] = [];
1282
1370
 
1283
1371
  // Start in-flight message
1284
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
1372
+ const p1 = conversation.processMessage({
1373
+ content: "msg-1",
1374
+ attachments: [],
1375
+ requestId: "req-1",
1376
+ });
1285
1377
  await waitForPendingRun(1);
1286
1378
 
1287
1379
  // Enqueue a surface-action message (activeSurfaceId set + tracked in
@@ -1289,25 +1381,23 @@ describe("Batched drain correctness fixes", () => {
1289
1381
  // same interface. The batch builder must reject the surface-action head
1290
1382
  // so each drains as its own run.
1291
1383
  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
- );
1384
+ conversation.enqueueMessage({
1385
+ content: "surface action response",
1386
+ onEvent: (e) => eventsSurface.push(e),
1387
+ requestId: "req-surface",
1388
+ activeSurfaceId: "surface-1",
1389
+ });
1390
+ conversation.enqueueMessage({
1391
+ content: "regular follow-up",
1392
+ onEvent: (e) => eventsRegular.push(e),
1393
+ requestId: "req-regular",
1394
+ });
1305
1395
  expect(conversation.getQueueDepth()).toBe(2);
1306
1396
 
1307
1397
  // Complete run 0 → drain must NOT batch the surface-action with the
1308
1398
  // regular passthrough. Expect the surface-action to drain as a single
1309
1399
  // run first.
1310
- resolveRun(0);
1400
+ await resolveRun(0);
1311
1401
  await p1;
1312
1402
  await waitForPendingRun(2);
1313
1403
 
@@ -1322,7 +1412,7 @@ describe("Batched drain correctness fixes", () => {
1322
1412
 
1323
1413
  // Complete the surface-action run; drain pulls the regular passthrough
1324
1414
  // as its own separate run.
1325
- resolveRun(1);
1415
+ await resolveRun(1);
1326
1416
  await waitForPendingRun(3);
1327
1417
  expect(pendingRuns.length).toBe(3);
1328
1418
  expect(
@@ -1331,7 +1421,7 @@ describe("Batched drain correctness fixes", () => {
1331
1421
 
1332
1422
  // Total runs = 3: msg-1, surface-action, regular — NOT 2 (would mean
1333
1423
  // they were batched).
1334
- resolveRun(2);
1424
+ await resolveRun(2);
1335
1425
  await new Promise((r) => setTimeout(r, 10));
1336
1426
  });
1337
1427
 
@@ -1345,12 +1435,12 @@ describe("Batched drain correctness fixes", () => {
1345
1435
  const events4: ServerMessage[] = [];
1346
1436
 
1347
1437
  // Start in-flight message
1348
- const p1 = conversation.processMessage(
1349
- "msg-1",
1350
- [],
1351
- (e) => events1.push(e),
1352
- "req-1",
1353
- );
1438
+ const p1 = conversation.processMessage({
1439
+ content: "msg-1",
1440
+ attachments: [],
1441
+ onEvent: (e) => events1.push(e),
1442
+ requestId: "req-1",
1443
+ });
1354
1444
  await waitForPendingRun(1);
1355
1445
 
1356
1446
  // Enqueue three sibling passthroughs (msg-2 = head, msg-3 = mid,
@@ -1360,7 +1450,11 @@ describe("Batched drain correctness fixes", () => {
1360
1450
  // fresh one). Calling abort() now aborts that fresh controller, and
1361
1451
  // the drainBatch loop's abort check after msg-3's persist will break,
1362
1452
  // so msg-4 never persists.
1363
- conversation.enqueueMessage("msg-2", [], (e) => events2.push(e), "req-2");
1453
+ conversation.enqueueMessage({
1454
+ content: "msg-2",
1455
+ onEvent: (e) => events2.push(e),
1456
+ requestId: "req-2",
1457
+ });
1364
1458
 
1365
1459
  // Install a one-shot abort trigger on msg-3's dequeue event. We do
1366
1460
  // this before enqueueing so the wrapped callback is what drainBatch
@@ -1373,8 +1467,16 @@ describe("Batched drain correctness fixes", () => {
1373
1467
  conversation.abort();
1374
1468
  }
1375
1469
  };
1376
- conversation.enqueueMessage("msg-3", [], onMsg3Event, "req-3");
1377
- conversation.enqueueMessage("msg-4", [], (e) => events4.push(e), "req-4");
1470
+ conversation.enqueueMessage({
1471
+ content: "msg-3",
1472
+ onEvent: onMsg3Event,
1473
+ requestId: "req-3",
1474
+ });
1475
+ conversation.enqueueMessage({
1476
+ content: "msg-4",
1477
+ onEvent: (e) => events4.push(e),
1478
+ requestId: "req-4",
1479
+ });
1378
1480
  expect(conversation.getQueueDepth()).toBe(3);
1379
1481
 
1380
1482
  const persistedUserRowCountBefore = capturedAddMessages.filter(
@@ -1382,7 +1484,7 @@ describe("Batched drain correctness fixes", () => {
1382
1484
  ).length;
1383
1485
 
1384
1486
  // Complete run 0 → drain pulls the sibling batch.
1385
- resolveRun(0);
1487
+ await resolveRun(0);
1386
1488
  await p1;
1387
1489
 
1388
1490
  // Give the drain loop a chance to iterate. Abort happens on msg-3's
@@ -1412,12 +1514,12 @@ describe("Batched drain correctness fixes", () => {
1412
1514
  const events4: ServerMessage[] = [];
1413
1515
 
1414
1516
  // Start in-flight message
1415
- const p1 = conversation.processMessage(
1416
- "msg-1",
1417
- [],
1418
- (e) => events1.push(e),
1419
- "req-1",
1420
- );
1517
+ const p1 = conversation.processMessage({
1518
+ content: "msg-1",
1519
+ attachments: [],
1520
+ onEvent: (e) => events1.push(e),
1521
+ requestId: "req-1",
1522
+ });
1421
1523
  await waitForPendingRun(1);
1422
1524
 
1423
1525
  // Enqueue three siblings. Configure addMessage to throw for the second
@@ -1426,28 +1528,25 @@ describe("Batched drain correctness fixes", () => {
1426
1528
  // msg-tail's requestId (the LAST successful persist), not msg-mid's.
1427
1529
  addMessageShouldThrowForContent.add("msg-mid-unique-marker");
1428
1530
 
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
- );
1531
+ conversation.enqueueMessage({
1532
+ content: "msg-head",
1533
+ onEvent: (e) => events2.push(e),
1534
+ requestId: "req-head",
1535
+ });
1536
+ conversation.enqueueMessage({
1537
+ content: "msg-mid-unique-marker",
1538
+ onEvent: (e) => events3.push(e),
1539
+ requestId: "req-mid",
1540
+ });
1541
+ conversation.enqueueMessage({
1542
+ content: "msg-tail",
1543
+ onEvent: (e) => events4.push(e),
1544
+ requestId: "req-tail",
1545
+ });
1447
1546
  expect(conversation.getQueueDepth()).toBe(3);
1448
1547
 
1449
1548
  // Complete run 0 → batched drain.
1450
- resolveRun(0);
1549
+ await resolveRun(0);
1451
1550
  await p1;
1452
1551
  await waitForPendingRun(2);
1453
1552
 
@@ -1464,7 +1563,7 @@ describe("Batched drain correctness fixes", () => {
1464
1563
  ).toBe("req-tail");
1465
1564
 
1466
1565
  // Cleanup: resolve the batched run.
1467
- resolveRun(1);
1566
+ await resolveRun(1);
1468
1567
  await new Promise((r) => setTimeout(r, 20));
1469
1568
  });
1470
1569
 
@@ -1477,12 +1576,12 @@ describe("Batched drain correctness fixes", () => {
1477
1576
  const events3: ServerMessage[] = [];
1478
1577
  const events4: ServerMessage[] = [];
1479
1578
 
1480
- const p1 = conversation.processMessage(
1481
- "msg-1",
1482
- [],
1483
- (e) => events1.push(e),
1484
- "req-1",
1485
- );
1579
+ const p1 = conversation.processMessage({
1580
+ content: "msg-1",
1581
+ attachments: [],
1582
+ onEvent: (e) => events1.push(e),
1583
+ requestId: "req-1",
1584
+ });
1486
1585
  await waitForPendingRun(1);
1487
1586
 
1488
1587
  // Mid tail will fail to persist. After the batched run resolves,
@@ -1492,31 +1591,28 @@ describe("Batched drain correctness fixes", () => {
1492
1591
  // desync the client.
1493
1592
  addMessageShouldThrowForContent.add("fanout-mid-marker");
1494
1593
 
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
- );
1594
+ conversation.enqueueMessage({
1595
+ content: "fanout-head",
1596
+ onEvent: (e) => events2.push(e),
1597
+ requestId: "req-fanout-head",
1598
+ });
1599
+ conversation.enqueueMessage({
1600
+ content: "fanout-mid-marker",
1601
+ onEvent: (e) => events3.push(e),
1602
+ requestId: "req-fanout-mid",
1603
+ });
1604
+ conversation.enqueueMessage({
1605
+ content: "fanout-tail",
1606
+ onEvent: (e) => events4.push(e),
1607
+ requestId: "req-fanout-tail",
1608
+ });
1513
1609
 
1514
- resolveRun(0);
1610
+ await resolveRun(0);
1515
1611
  await p1;
1516
1612
  await waitForPendingRun(2);
1517
1613
 
1518
1614
  // Drive the batched run to emit message_complete via fanOutOnEvent.
1519
- resolveRun(1);
1615
+ await resolveRun(1);
1520
1616
  await new Promise((r) => setTimeout(r, 20));
1521
1617
 
1522
1618
  expect(events3.find((e) => e.type === "error")).toBeDefined();
@@ -1536,7 +1632,11 @@ describe("Batched drain correctness fixes", () => {
1536
1632
  await conversation.loadFromDb();
1537
1633
 
1538
1634
  // Start in-flight message
1539
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
1635
+ const p1 = conversation.processMessage({
1636
+ content: "msg-1",
1637
+ attachments: [],
1638
+ requestId: "req-1",
1639
+ });
1540
1640
  await waitForPendingRun(1);
1541
1641
 
1542
1642
  // Snapshot the count before drain so we only compare batch-emitted
@@ -1544,12 +1644,12 @@ describe("Batched drain correctness fixes", () => {
1544
1644
  const baseline = activityStates.length;
1545
1645
 
1546
1646
  // 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");
1647
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
1648
+ conversation.enqueueMessage({ content: "msg-3", requestId: "req-3" });
1649
+ conversation.enqueueMessage({ content: "msg-4", requestId: "req-4" });
1550
1650
 
1551
1651
  // Complete run 0 → drain pulls the batched siblings as ONE run.
1552
- resolveRun(0);
1652
+ await resolveRun(0);
1553
1653
  await p1;
1554
1654
  await waitForPendingRun(2);
1555
1655
 
@@ -1569,7 +1669,7 @@ describe("Batched drain correctness fixes", () => {
1569
1669
  requestId: "req-2", // head's requestId, per the fix
1570
1670
  });
1571
1671
 
1572
- resolveRun(1);
1672
+ await resolveRun(1);
1573
1673
  await new Promise((r) => setTimeout(r, 10));
1574
1674
  });
1575
1675
 
@@ -1610,17 +1710,21 @@ describe("Conversation queue policy helpers", () => {
1610
1710
  await conversation.loadFromDb();
1611
1711
 
1612
1712
  // Start processing to make the session busy
1613
- conversation.processMessage("msg-1", [], () => {}, "req-1");
1713
+ conversation.processMessage({
1714
+ content: "msg-1",
1715
+ attachments: [],
1716
+ requestId: "req-1",
1717
+ });
1614
1718
  await waitForPendingRun(1);
1615
1719
 
1616
1720
  // Enqueue a message while processing
1617
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
1721
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
1618
1722
  expect(conversation.hasQueuedMessages()).toBe(true);
1619
1723
 
1620
1724
  // Cleanup: resolve the pending run
1621
- resolveRun(0);
1725
+ await resolveRun(0);
1622
1726
  await waitForPendingRun(2);
1623
- resolveRun(1);
1727
+ await resolveRun(1);
1624
1728
  await new Promise((r) => setTimeout(r, 10));
1625
1729
  });
1626
1730
 
@@ -1637,7 +1741,11 @@ describe("Conversation queue policy helpers", () => {
1637
1741
  await conversation.loadFromDb();
1638
1742
 
1639
1743
  // Start processing — but don't enqueue anything
1640
- conversation.processMessage("msg-1", [], () => {}, "req-1");
1744
+ conversation.processMessage({
1745
+ content: "msg-1",
1746
+ attachments: [],
1747
+ requestId: "req-1",
1748
+ });
1641
1749
  await waitForPendingRun(1);
1642
1750
 
1643
1751
  expect(conversation.isProcessing()).toBe(true);
@@ -1645,7 +1753,7 @@ describe("Conversation queue policy helpers", () => {
1645
1753
  expect(conversation.canHandoffAtCheckpoint()).toBe(false);
1646
1754
 
1647
1755
  // Cleanup
1648
- resolveRun(0);
1756
+ await resolveRun(0);
1649
1757
  await new Promise((r) => setTimeout(r, 10));
1650
1758
  });
1651
1759
 
@@ -1654,20 +1762,24 @@ describe("Conversation queue policy helpers", () => {
1654
1762
  await conversation.loadFromDb();
1655
1763
 
1656
1764
  // Start processing
1657
- conversation.processMessage("msg-1", [], () => {}, "req-1");
1765
+ conversation.processMessage({
1766
+ content: "msg-1",
1767
+ attachments: [],
1768
+ requestId: "req-1",
1769
+ });
1658
1770
  await waitForPendingRun(1);
1659
1771
 
1660
1772
  // Enqueue a message
1661
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
1773
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
1662
1774
 
1663
1775
  expect(conversation.isProcessing()).toBe(true);
1664
1776
  expect(conversation.hasQueuedMessages()).toBe(true);
1665
1777
  expect(conversation.canHandoffAtCheckpoint()).toBe(true);
1666
1778
 
1667
1779
  // Cleanup
1668
- resolveRun(0);
1780
+ await resolveRun(0);
1669
1781
  await waitForPendingRun(2);
1670
- resolveRun(1);
1782
+ await resolveRun(1);
1671
1783
  await new Promise((r) => setTimeout(r, 10));
1672
1784
  });
1673
1785
 
@@ -1705,16 +1817,16 @@ describe("Conversation checkpoint handoff", () => {
1705
1817
  const events1: ServerMessage[] = [];
1706
1818
 
1707
1819
  // Start processing first message
1708
- const p1 = conversation.processMessage(
1709
- "msg-1",
1710
- [],
1711
- (e) => events1.push(e),
1712
- "req-1",
1713
- );
1820
+ const p1 = conversation.processMessage({
1821
+ content: "msg-1",
1822
+ attachments: [],
1823
+ onEvent: (e) => events1.push(e),
1824
+ requestId: "req-1",
1825
+ });
1714
1826
  await waitForPendingRun(1);
1715
1827
 
1716
1828
  // Enqueue a second message while the first is processing
1717
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
1829
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
1718
1830
  expect(conversation.hasQueuedMessages()).toBe(true);
1719
1831
 
1720
1832
  // The pending run should have received an onCheckpoint callback.
@@ -1728,11 +1840,11 @@ describe("Conversation checkpoint handoff", () => {
1728
1840
  history: [],
1729
1841
  });
1730
1842
 
1731
- // Because there is a queued message, the callback should return 'yield'
1732
- expect(decision).toBe("yield");
1843
+ // Because there is a queued message, the callback should yield for handoff
1844
+ expect(decision).toEqual("handoff");
1733
1845
 
1734
1846
  // Complete the run so the conversation finishes cleanly
1735
- resolveRun(0);
1847
+ await resolveRun(0);
1736
1848
  await p1;
1737
1849
 
1738
1850
  // After yield, the first message should emit generation_handoff
@@ -1747,7 +1859,7 @@ describe("Conversation checkpoint handoff", () => {
1747
1859
 
1748
1860
  // The queued message should now be draining (second run started)
1749
1861
  await waitForPendingRun(2);
1750
- resolveRun(1);
1862
+ await resolveRun(1);
1751
1863
  await new Promise((r) => setTimeout(r, 10));
1752
1864
  });
1753
1865
 
@@ -1756,7 +1868,11 @@ describe("Conversation checkpoint handoff", () => {
1756
1868
  await conversation.loadFromDb();
1757
1869
 
1758
1870
  // Start processing — no enqueued messages
1759
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
1871
+ const p1 = conversation.processMessage({
1872
+ content: "msg-1",
1873
+ attachments: [],
1874
+ requestId: "req-1",
1875
+ });
1760
1876
  await waitForPendingRun(1);
1761
1877
 
1762
1878
  expect(conversation.hasQueuedMessages()).toBe(false);
@@ -1775,7 +1891,7 @@ describe("Conversation checkpoint handoff", () => {
1775
1891
  expect(decision).toBe("continue");
1776
1892
 
1777
1893
  // Cleanup
1778
- resolveRun(0);
1894
+ await resolveRun(0);
1779
1895
  await p1;
1780
1896
  });
1781
1897
 
@@ -1789,18 +1905,30 @@ describe("Conversation checkpoint handoff", () => {
1789
1905
  const events4: ServerMessage[] = [];
1790
1906
 
1791
1907
  // Start first message (mid-tool-use — will yield at the next checkpoint)
1792
- const p1 = conversation.processMessage(
1793
- "msg-1",
1794
- [],
1795
- (e) => events1.push(e),
1796
- "req-1",
1797
- );
1908
+ const p1 = conversation.processMessage({
1909
+ content: "msg-1",
1910
+ attachments: [],
1911
+ onEvent: (e) => events1.push(e),
1912
+ requestId: "req-1",
1913
+ });
1798
1914
  await waitForPendingRun(1);
1799
1915
 
1800
1916
  // 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");
1917
+ conversation.enqueueMessage({
1918
+ content: "msg-2",
1919
+ onEvent: (e) => events2.push(e),
1920
+ requestId: "req-2",
1921
+ });
1922
+ conversation.enqueueMessage({
1923
+ content: "msg-3",
1924
+ onEvent: (e) => events3.push(e),
1925
+ requestId: "req-3",
1926
+ });
1927
+ conversation.enqueueMessage({
1928
+ content: "msg-4",
1929
+ onEvent: (e) => events4.push(e),
1930
+ requestId: "req-4",
1931
+ });
1804
1932
  expect(conversation.getQueueDepth()).toBe(3);
1805
1933
 
1806
1934
  // Simulate the agent loop yielding at the checkpoint (first run is mid-tool-use)
@@ -1812,10 +1940,10 @@ describe("Conversation checkpoint handoff", () => {
1812
1940
  hasToolUse: true,
1813
1941
  history: [],
1814
1942
  });
1815
- expect(decision).toBe("yield");
1943
+ expect(decision).toEqual("handoff");
1816
1944
 
1817
1945
  // Complete first run
1818
- resolveRun(0);
1946
+ await resolveRun(0);
1819
1947
  await p1;
1820
1948
 
1821
1949
  // The yielded drain pulls ALL THREE queued siblings as ONE batched run —
@@ -1829,7 +1957,7 @@ describe("Conversation checkpoint handoff", () => {
1829
1957
  expect(events4.some((e) => e.type === "message_dequeued")).toBe(true);
1830
1958
 
1831
1959
  // Resolve the batched run — message_complete fans out to all three clients.
1832
- resolveRun(1);
1960
+ await resolveRun(1);
1833
1961
  await new Promise((r) => setTimeout(r, 10));
1834
1962
 
1835
1963
  expect(events2.some((e) => e.type === "message_complete")).toBe(true);
@@ -1845,20 +1973,24 @@ describe("Conversation checkpoint handoff", () => {
1845
1973
  const events2: ServerMessage[] = [];
1846
1974
 
1847
1975
  // Start processing first message
1848
- const p1 = conversation.processMessage(
1849
- "msg-1",
1850
- [],
1851
- (e) => events1.push(e),
1852
- "req-1",
1853
- );
1976
+ const p1 = conversation.processMessage({
1977
+ content: "msg-1",
1978
+ attachments: [],
1979
+ onEvent: (e) => events1.push(e),
1980
+ requestId: "req-1",
1981
+ });
1854
1982
  await waitForPendingRun(1);
1855
1983
 
1856
1984
  // Enqueue a second message while the first is processing
1857
- conversation.enqueueMessage("msg-2", [], (e) => events2.push(e), "req-2");
1985
+ conversation.enqueueMessage({
1986
+ content: "msg-2",
1987
+ onEvent: (e) => events2.push(e),
1988
+ requestId: "req-2",
1989
+ });
1858
1990
  expect(conversation.hasQueuedMessages()).toBe(true);
1859
1991
 
1860
1992
  // Simulate tool-use turns: the agent loop calls onCheckpoint at each turn boundary.
1861
- // Because there is a queued message, the callback should return 'yield'.
1993
+ // Because there is a queued message, the callback should yield for handoff.
1862
1994
  const run = pendingRuns[0];
1863
1995
  expect(run.onCheckpoint).toBeDefined();
1864
1996
 
@@ -1870,10 +2002,10 @@ describe("Conversation checkpoint handoff", () => {
1870
2002
  hasToolUse: true,
1871
2003
  history: [],
1872
2004
  });
1873
- expect(decision).toBe("yield");
2005
+ expect(decision).toEqual("handoff");
1874
2006
 
1875
2007
  // Complete the run (AgentLoop resolves after yielding)
1876
- resolveRun(0);
2008
+ await resolveRun(0);
1877
2009
  await p1;
1878
2010
 
1879
2011
  // Verify generation_handoff was emitted (not plain message_complete)
@@ -1896,7 +2028,7 @@ describe("Conversation checkpoint handoff", () => {
1896
2028
  expect(events2.some((e) => e.type === "message_dequeued")).toBe(true);
1897
2029
 
1898
2030
  // Complete the second run
1899
- resolveRun(1);
2031
+ await resolveRun(1);
1900
2032
  await new Promise((r) => setTimeout(r, 10));
1901
2033
  });
1902
2034
 
@@ -1912,12 +2044,12 @@ describe("Conversation checkpoint handoff", () => {
1912
2044
  };
1913
2045
 
1914
2046
  // Start processing message A
1915
- const pA = conversation.processMessage(
1916
- "msg-A",
1917
- [],
1918
- (e) => eventsA.push(e),
1919
- "req-A",
1920
- );
2047
+ const pA = conversation.processMessage({
2048
+ content: "msg-A",
2049
+ attachments: [],
2050
+ onEvent: (e) => eventsA.push(e),
2051
+ requestId: "req-A",
2052
+ });
1921
2053
  await waitForPendingRun(1);
1922
2054
 
1923
2055
  // Enqueue messages B, C, D — each on a distinct userMessageInterface so the
@@ -1926,33 +2058,24 @@ describe("Conversation checkpoint handoff", () => {
1926
2058
  userMessageInterface: iface,
1927
2059
  assistantMessageInterface: iface,
1928
2060
  });
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
- );
2061
+ conversation.enqueueMessage({
2062
+ content: "msg-B",
2063
+ onEvent: makeHandler("B"),
2064
+ requestId: "req-B",
2065
+ metadata: meta("macos"),
2066
+ });
2067
+ conversation.enqueueMessage({
2068
+ content: "msg-C",
2069
+ onEvent: makeHandler("C"),
2070
+ requestId: "req-C",
2071
+ metadata: meta("cli"),
2072
+ });
2073
+ conversation.enqueueMessage({
2074
+ content: "msg-D",
2075
+ onEvent: makeHandler("D"),
2076
+ requestId: "req-D",
2077
+ metadata: meta("vellum"),
2078
+ });
1956
2079
  expect(conversation.getQueueDepth()).toBe(3);
1957
2080
 
1958
2081
  // Handoff from A -> B
@@ -1965,8 +2088,8 @@ describe("Conversation checkpoint handoff", () => {
1965
2088
  hasToolUse: true,
1966
2089
  history: [],
1967
2090
  }),
1968
- ).toBe("yield");
1969
- resolveRun(0);
2091
+ ).toEqual("handoff");
2092
+ await resolveRun(0);
1970
2093
  await pA;
1971
2094
 
1972
2095
  // B should be draining
@@ -1982,8 +2105,8 @@ describe("Conversation checkpoint handoff", () => {
1982
2105
  hasToolUse: true,
1983
2106
  history: [],
1984
2107
  }),
1985
- ).toBe("yield");
1986
- resolveRun(1);
2108
+ ).toEqual("handoff");
2109
+ await resolveRun(1);
1987
2110
  await waitForPendingRun(3);
1988
2111
 
1989
2112
  // Handoff from C -> D
@@ -1997,8 +2120,8 @@ describe("Conversation checkpoint handoff", () => {
1997
2120
  hasToolUse: true,
1998
2121
  history: [],
1999
2122
  }),
2000
- ).toBe("yield");
2001
- resolveRun(2);
2123
+ ).toEqual("handoff");
2124
+ await resolveRun(2);
2002
2125
  await waitForPendingRun(4);
2003
2126
 
2004
2127
  // D has no more queued -> checkpoint should return 'continue'
@@ -2013,7 +2136,7 @@ describe("Conversation checkpoint handoff", () => {
2013
2136
  }),
2014
2137
  ).toBe("continue");
2015
2138
 
2016
- resolveRun(3);
2139
+ await resolveRun(3);
2017
2140
  await new Promise((r) => setTimeout(r, 10));
2018
2141
 
2019
2142
  // Verify FIFO dequeue order
@@ -2029,21 +2152,29 @@ describe("Conversation checkpoint handoff", () => {
2029
2152
  const eventsC: ServerMessage[] = [];
2030
2153
 
2031
2154
  // Start processing message A
2032
- const pA = conversation.processMessage(
2033
- "msg-A",
2034
- [],
2035
- (e) => eventsA.push(e),
2036
- "req-A",
2037
- );
2155
+ const pA = conversation.processMessage({
2156
+ content: "msg-A",
2157
+ attachments: [],
2158
+ onEvent: (e) => eventsA.push(e),
2159
+ requestId: "req-A",
2160
+ });
2038
2161
  await waitForPendingRun(1);
2039
2162
 
2040
2163
  // 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");
2164
+ conversation.enqueueMessage({
2165
+ content: "",
2166
+ onEvent: (e) => eventsB.push(e),
2167
+ requestId: "req-B",
2168
+ });
2169
+ conversation.enqueueMessage({
2170
+ content: "msg-C",
2171
+ onEvent: (e) => eventsC.push(e),
2172
+ requestId: "req-C",
2173
+ });
2043
2174
  expect(conversation.getQueueDepth()).toBe(2);
2044
2175
 
2045
2176
  // Complete message A — triggers drain. B should fail, C should proceed.
2046
- resolveRun(0);
2177
+ await resolveRun(0);
2047
2178
  await pA;
2048
2179
 
2049
2180
  // C should have been dequeued and started a new AgentLoop.run
@@ -2060,7 +2191,7 @@ describe("Conversation checkpoint handoff", () => {
2060
2191
  expect(eventsC.some((e) => e.type === "message_dequeued")).toBe(true);
2061
2192
 
2062
2193
  // Complete C's run
2063
- resolveRun(1);
2194
+ await resolveRun(1);
2064
2195
  await new Promise((r) => setTimeout(r, 10));
2065
2196
 
2066
2197
  // C should have completed successfully
@@ -2072,7 +2203,11 @@ describe("Conversation checkpoint handoff", () => {
2072
2203
  await conversation.loadFromDb();
2073
2204
 
2074
2205
  // Start processing
2075
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
2206
+ const p1 = conversation.processMessage({
2207
+ content: "msg-1",
2208
+ attachments: [],
2209
+ requestId: "req-1",
2210
+ });
2076
2211
  await waitForPendingRun(1);
2077
2212
 
2078
2213
  // The first run should have onCheckpoint
@@ -2097,7 +2232,7 @@ describe("Conversation checkpoint handoff", () => {
2097
2232
  expect(pendingRuns[1].onCheckpoint).toBeDefined();
2098
2233
 
2099
2234
  // Complete retry cleanly
2100
- resolveRun(1);
2235
+ await resolveRun(1);
2101
2236
  await p1;
2102
2237
  });
2103
2238
  });
@@ -2116,11 +2251,15 @@ describe("Conversation usage requestId correlation", () => {
2116
2251
  const conversation = makeConversation();
2117
2252
  await conversation.loadFromDb();
2118
2253
 
2119
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-42");
2254
+ const p1 = conversation.processMessage({
2255
+ content: "msg-1",
2256
+ attachments: [],
2257
+ requestId: "req-42",
2258
+ });
2120
2259
  await waitForPendingRun(1);
2121
2260
 
2122
2261
  // Complete the run — this triggers recordUsage with the request's ID
2123
- resolveRun(0);
2262
+ await resolveRun(0);
2124
2263
  await p1;
2125
2264
 
2126
2265
  // The usage event should carry the request ID, not null
@@ -2149,16 +2288,20 @@ describe("Terminal trace events on rejection/failure", () => {
2149
2288
  await conversation.loadFromDb();
2150
2289
 
2151
2290
  // Start first message
2152
- const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
2291
+ const p1 = conversation.processMessage({
2292
+ content: "msg-1",
2293
+ attachments: [],
2294
+ requestId: "req-1",
2295
+ });
2153
2296
  await waitForPendingRun(1);
2154
2297
 
2155
2298
  // Enqueue empty content (will fail persistUserMessage)
2156
- conversation.enqueueMessage("", [], () => {}, "req-bad");
2299
+ conversation.enqueueMessage({ content: "", requestId: "req-bad" });
2157
2300
  // Enqueue valid message so drain continues
2158
- conversation.enqueueMessage("msg-3", [], () => {}, "req-3");
2301
+ conversation.enqueueMessage({ content: "msg-3", requestId: "req-3" });
2159
2302
 
2160
2303
  // Complete first — triggers drain, empty msg fails persist
2161
- resolveRun(0);
2304
+ await resolveRun(0);
2162
2305
  await p1;
2163
2306
  await waitForPendingRun(2);
2164
2307
 
@@ -2173,7 +2316,7 @@ describe("Terminal trace events on rejection/failure", () => {
2173
2316
  expect(errorTrace).toBeDefined();
2174
2317
 
2175
2318
  // Cleanup
2176
- resolveRun(1);
2319
+ await resolveRun(1);
2177
2320
  await new Promise((r) => setTimeout(r, 10));
2178
2321
  });
2179
2322
  });
@@ -2197,12 +2340,12 @@ describe("Conversation host attachment directives", () => {
2197
2340
  const conversation = makeConversation((msg) => clientEvents.push(msg));
2198
2341
  await conversation.loadFromDb();
2199
2342
 
2200
- const p1 = conversation.processMessage(
2201
- "msg-1",
2202
- [],
2203
- (e) => events.push(e),
2204
- "req-1",
2205
- );
2343
+ const p1 = conversation.processMessage({
2344
+ content: "msg-1",
2345
+ attachments: [],
2346
+ onEvent: (e) => events.push(e),
2347
+ requestId: "req-1",
2348
+ });
2206
2349
  await waitForPendingRun(1);
2207
2350
 
2208
2351
  const run = pendingRuns[0];
@@ -2215,6 +2358,7 @@ describe("Conversation host attachment directives", () => {
2215
2358
  },
2216
2359
  ],
2217
2360
  };
2361
+ await run.onEvent({ type: "llm_call_started" });
2218
2362
  run.onEvent({
2219
2363
  type: "usage",
2220
2364
  inputTokens: 10,
@@ -2266,12 +2410,12 @@ describe("Conversation host attachment directives", () => {
2266
2410
  const conversation = makeConversation((msg) => clientEvents.push(msg));
2267
2411
  await conversation.loadFromDb();
2268
2412
 
2269
- const p1 = conversation.processMessage(
2270
- "msg-1",
2271
- [],
2272
- (e) => events.push(e),
2273
- "req-1",
2274
- );
2413
+ const p1 = conversation.processMessage({
2414
+ content: "msg-1",
2415
+ attachments: [],
2416
+ onEvent: (e) => events.push(e),
2417
+ requestId: "req-1",
2418
+ });
2275
2419
  await waitForPendingRun(1);
2276
2420
 
2277
2421
  const run = pendingRuns[0];
@@ -2284,6 +2428,7 @@ describe("Conversation host attachment directives", () => {
2284
2428
  },
2285
2429
  ],
2286
2430
  };
2431
+ await run.onEvent({ type: "llm_call_started" });
2287
2432
  run.onEvent({
2288
2433
  type: "usage",
2289
2434
  inputTokens: 10,
@@ -2352,12 +2497,12 @@ describe("Conversation attachment event payloads", () => {
2352
2497
  const conversation = makeConversation();
2353
2498
  await conversation.loadFromDb();
2354
2499
 
2355
- const p1 = conversation.processMessage(
2356
- "msg-1",
2357
- [],
2358
- (e) => events.push(e),
2359
- "req-1",
2360
- );
2500
+ const p1 = conversation.processMessage({
2501
+ content: "msg-1",
2502
+ attachments: [],
2503
+ onEvent: (e) => events.push(e),
2504
+ requestId: "req-1",
2505
+ });
2361
2506
  await waitForPendingRun(1);
2362
2507
 
2363
2508
  const run = pendingRuns[0];
@@ -2377,6 +2522,7 @@ describe("Conversation attachment event payloads", () => {
2377
2522
  } as any,
2378
2523
  ],
2379
2524
  });
2525
+ await run.onEvent({ type: "llm_call_started" });
2380
2526
  run.onEvent({
2381
2527
  type: "usage",
2382
2528
  inputTokens: 10,
@@ -2414,16 +2560,16 @@ describe("Conversation attachment event payloads", () => {
2414
2560
  const conversation = makeConversation();
2415
2561
  await conversation.loadFromDb();
2416
2562
 
2417
- const p1 = conversation.processMessage(
2418
- "msg-1",
2419
- [],
2420
- (e) => events1.push(e),
2421
- "req-1",
2422
- );
2563
+ const p1 = conversation.processMessage({
2564
+ content: "msg-1",
2565
+ attachments: [],
2566
+ onEvent: (e) => events1.push(e),
2567
+ requestId: "req-1",
2568
+ });
2423
2569
  await waitForPendingRun(1);
2424
2570
 
2425
2571
  // Queue a second message so the first run yields via checkpoint handoff.
2426
- conversation.enqueueMessage("msg-2", [], () => {}, "req-2");
2572
+ conversation.enqueueMessage({ content: "msg-2", requestId: "req-2" });
2427
2573
 
2428
2574
  const run = pendingRuns[0];
2429
2575
  expect(run.onCheckpoint).toBeDefined();
@@ -2434,7 +2580,7 @@ describe("Conversation attachment event payloads", () => {
2434
2580
  hasToolUse: true,
2435
2581
  history: [],
2436
2582
  }),
2437
- ).toBe("yield");
2583
+ ).toEqual("handoff");
2438
2584
 
2439
2585
  const assistantMsg: Message = {
2440
2586
  role: "assistant",
@@ -2452,6 +2598,7 @@ describe("Conversation attachment event payloads", () => {
2452
2598
  } as any,
2453
2599
  ],
2454
2600
  });
2601
+ await run.onEvent({ type: "llm_call_started" });
2455
2602
  run.onEvent({
2456
2603
  type: "usage",
2457
2604
  inputTokens: 10,
@@ -2478,7 +2625,7 @@ describe("Conversation attachment event payloads", () => {
2478
2625
  expect(attachments[0].data).toBe("iVBORw0K");
2479
2626
 
2480
2627
  await waitForPendingRun(2);
2481
- resolveRun(1);
2628
+ await resolveRun(1);
2482
2629
  await new Promise((r) => setTimeout(r, 10));
2483
2630
  });
2484
2631
  });
@@ -2498,19 +2645,19 @@ describe("Regression: cancel semantics and error channel split", () => {
2498
2645
  await conversation.loadFromDb();
2499
2646
 
2500
2647
  // Start processing a message — collect events from the per-message callback
2501
- const p1 = conversation.processMessage(
2502
- "msg-1",
2503
- [],
2504
- (e) => msgEvents.push(e),
2505
- "req-1",
2506
- );
2648
+ const p1 = conversation.processMessage({
2649
+ content: "msg-1",
2650
+ attachments: [],
2651
+ onEvent: (e) => msgEvents.push(e),
2652
+ requestId: "req-1",
2653
+ });
2507
2654
  await waitForPendingRun(1);
2508
2655
 
2509
2656
  // User cancels — sets the abort signal
2510
2657
  conversation.abort();
2511
2658
 
2512
2659
  // Resolve the pending run so the abort-check path fires
2513
- resolveRun(0);
2660
+ await resolveRun(0);
2514
2661
  await p1;
2515
2662
 
2516
2663
  // generation_cancelled should be emitted via the per-message callback
@@ -2532,12 +2679,12 @@ describe("Regression: cancel semantics and error channel split", () => {
2532
2679
  await conversation.loadFromDb();
2533
2680
  linkAttachmentShouldThrow = true;
2534
2681
 
2535
- const p1 = conversation.processMessage(
2536
- "msg-1",
2537
- [],
2538
- (e) => events.push(e),
2539
- "req-1",
2540
- );
2682
+ const p1 = conversation.processMessage({
2683
+ content: "msg-1",
2684
+ attachments: [],
2685
+ onEvent: (e) => events.push(e),
2686
+ requestId: "req-1",
2687
+ });
2541
2688
  await waitForPendingRun(1);
2542
2689
  const run = pendingRuns[0];
2543
2690
  const assistantMsg: Message = {
@@ -2556,6 +2703,7 @@ describe("Regression: cancel semantics and error channel split", () => {
2556
2703
  } as any,
2557
2704
  ],
2558
2705
  });
2706
+ await run.onEvent({ type: "llm_call_started" });
2559
2707
  run.onEvent({
2560
2708
  type: "usage",
2561
2709
  inputTokens: 10,
@@ -2585,12 +2733,12 @@ describe("Regression: cancel semantics and error channel split", () => {
2585
2733
  const conversation = makeConversation();
2586
2734
  await conversation.loadFromDb();
2587
2735
 
2588
- const p1 = conversation.processMessage(
2589
- "msg-1",
2590
- [],
2591
- (e) => allEvents.push(e),
2592
- "req-1",
2593
- );
2736
+ const p1 = conversation.processMessage({
2737
+ content: "msg-1",
2738
+ attachments: [],
2739
+ onEvent: (e) => allEvents.push(e),
2740
+ requestId: "req-1",
2741
+ });
2594
2742
  await waitForPendingRun(1);
2595
2743
 
2596
2744
  // Simulate a provider failure
@@ -2614,26 +2762,24 @@ describe("Regression: cancel semantics and error channel split", () => {
2614
2762
 
2615
2763
  const eventsPerMsg: ServerMessage[][] = [[], [], []];
2616
2764
 
2617
- conversation.processMessage(
2618
- "msg-1",
2619
- [],
2620
- (e) => eventsPerMsg[0].push(e),
2621
- "req-1",
2622
- );
2765
+ conversation.processMessage({
2766
+ content: "msg-1",
2767
+ attachments: [],
2768
+ onEvent: (e) => eventsPerMsg[0].push(e),
2769
+ requestId: "req-1",
2770
+ });
2623
2771
  await waitForPendingRun(1);
2624
2772
 
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
- );
2773
+ conversation.enqueueMessage({
2774
+ content: "msg-2",
2775
+ onEvent: (e) => eventsPerMsg[1].push(e),
2776
+ requestId: "req-2",
2777
+ });
2778
+ conversation.enqueueMessage({
2779
+ content: "msg-3",
2780
+ onEvent: (e) => eventsPerMsg[2].push(e),
2781
+ requestId: "req-3",
2782
+ });
2637
2783
 
2638
2784
  conversation.abort();
2639
2785
 
@@ -2668,19 +2814,23 @@ describe("Regression: cancel semantics and error channel split", () => {
2668
2814
  const events2: ServerMessage[] = [];
2669
2815
 
2670
2816
  // Start first message (promise intentionally not awaited — we test queue drain behavior)
2671
- const _p1 = conversation.processMessage(
2672
- "msg-1",
2673
- [],
2674
- (e) => events1.push(e),
2675
- "req-1",
2676
- );
2817
+ const _p1 = conversation.processMessage({
2818
+ content: "msg-1",
2819
+ attachments: [],
2820
+ onEvent: (e) => events1.push(e),
2821
+ requestId: "req-1",
2822
+ });
2677
2823
  await waitForPendingRun(1);
2678
2824
 
2679
2825
  // Enqueue a second message while the first is processing
2680
- conversation.enqueueMessage("msg-2", [], (e) => events2.push(e), "req-2");
2826
+ conversation.enqueueMessage({
2827
+ content: "msg-2",
2828
+ onEvent: (e) => events2.push(e),
2829
+ requestId: "req-2",
2830
+ });
2681
2831
 
2682
2832
  // Complete the first agent loop run
2683
- resolveRun(0);
2833
+ await resolveRun(0);
2684
2834
 
2685
2835
  // The turn should still complete (timeout fires) and drain the queue
2686
2836
  // even though commitTurnChanges never resolves.
@@ -2701,7 +2851,7 @@ describe("Regression: cancel semantics and error channel split", () => {
2701
2851
 
2702
2852
  // Complete the second run so the test can clean up
2703
2853
  turnCommitHangForever = false;
2704
- resolveRun(1);
2854
+ await resolveRun(1);
2705
2855
  await new Promise((r) => origSetTimeout(r, 10));
2706
2856
  } finally {
2707
2857
  turnCommitHangForever = false;