@vellumai/assistant 0.12.2 → 0.12.3-staging.2

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 (772) hide show
  1. package/.env.example +2 -0
  2. package/AGENTS.md +1 -1
  3. package/ARCHITECTURE.md +43 -29
  4. package/Dockerfile +63 -0
  5. package/docs/architecture/integrations.md +7 -0
  6. package/docs/architecture/memory.md +17 -2
  7. package/docs/architecture/security.md +10 -5
  8. package/docs/architecture/turn-actor.md +2 -0
  9. package/docs/credential-execution-service.md +1 -1
  10. package/docs/desktop-browser-cli.md +50 -5
  11. package/docs/live-voice-task-outcomes.md +100 -0
  12. package/docs/mode-sessions.md +183 -0
  13. package/docs/voice-input-diagnostics.md +58 -0
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  17. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  18. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  19. package/node_modules/@vellumai/environments/src/shell.test.ts +21 -0
  20. package/node_modules/@vellumai/environments/src/shell.ts +24 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  23. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  24. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  25. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  26. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
  27. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -2
  28. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
  29. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
  30. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
  31. package/node_modules/@vellumai/service-contracts/package.json +2 -1
  32. package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  33. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  34. package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  35. package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  36. package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
  37. package/openapi.yaml +716 -16
  38. package/package.json +1 -1
  39. package/scripts/smoke-desktop-browser-cli.ts +11 -26
  40. package/scripts/smoke-desktop-window-theme.ts +2 -3
  41. package/scripts/sync-llm-catalog.ts +3 -0
  42. package/scripts/voice-ttft-spike.ts +2 -2
  43. package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
  44. package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
  45. package/src/__tests__/agent-loop.test.ts +259 -0
  46. package/src/__tests__/agent-wake-delegation-prompt.test.ts +64 -2
  47. package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
  48. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  49. package/src/__tests__/answered-question-persistence.test.ts +2 -0
  50. package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
  51. package/src/__tests__/anthropic-provider.test.ts +55 -0
  52. package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
  53. package/src/__tests__/approval-message-composer.test.ts +1 -118
  54. package/src/__tests__/approval-routes-http.test.ts +3 -0
  55. package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
  56. package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
  57. package/src/__tests__/assistant-event-targeting.test.ts +186 -0
  58. package/src/__tests__/assistant-stream-state.test.ts +29 -9
  59. package/src/__tests__/attachments-store.test.ts +22 -3
  60. package/src/__tests__/browser-fill-credential.test.ts +11 -22
  61. package/src/__tests__/btw-routes.test.ts +4 -1
  62. package/src/__tests__/build-persisted-content.test.ts +1 -1
  63. package/src/__tests__/call-conversation-messages.test.ts +5 -0
  64. package/src/__tests__/call-recovery.test.ts +7 -2
  65. package/src/__tests__/call-routes-http.test.ts +12 -0
  66. package/src/__tests__/call-store.test.ts +7 -2
  67. package/src/__tests__/channel-approval.test.ts +9 -14
  68. package/src/__tests__/channel-reply-delivery.test.ts +56 -0
  69. package/src/__tests__/channel-retry-sweep.test.ts +9 -0
  70. package/src/__tests__/chat-credential-redaction.test.ts +23 -0
  71. package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
  72. package/src/__tests__/checker.test.ts +11 -3
  73. package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
  74. package/src/__tests__/computer-use-screenshot-attachments.test.ts +411 -0
  75. package/src/__tests__/computer-use-screenshot-selection.test.ts +50 -0
  76. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
  77. package/src/__tests__/computer-use-tools.test.ts +1 -233
  78. package/src/__tests__/config-model-image-provider.test.ts +39 -0
  79. package/src/__tests__/config-schema.test.ts +16 -0
  80. package/src/__tests__/config-watcher.test.ts +24 -0
  81. package/src/__tests__/context-search-agent-runner.test.ts +260 -1
  82. package/src/__tests__/context-search-fanout.test.ts +1 -1
  83. package/src/__tests__/context-search-types.test.ts +7 -7
  84. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
  85. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
  86. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  87. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  88. package/src/__tests__/conversation-agent-loop.test.ts +797 -16
  89. package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
  90. package/src/__tests__/conversation-attachments.test.ts +106 -0
  91. package/src/__tests__/conversation-evictor.test.ts +62 -37
  92. package/src/__tests__/conversation-fork-crud.test.ts +191 -0
  93. package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
  94. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
  95. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  96. package/src/__tests__/conversation-rendered-delegation-state.test.ts +276 -0
  97. package/src/__tests__/conversation-retry-route.test.ts +22 -0
  98. package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
  99. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
  100. package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
  101. package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
  102. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  103. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
  104. package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
  105. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
  106. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
  107. package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
  108. package/src/__tests__/conversation-undo.test.ts +61 -0
  109. package/src/__tests__/credential-execution-client.test.ts +72 -55
  110. package/src/__tests__/credential-prompt-route.test.ts +35 -3
  111. package/src/__tests__/cu-unified-flow.test.ts +82 -1
  112. package/src/__tests__/db-conversation-tool-surface.test.ts +221 -0
  113. package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
  114. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
  115. package/src/__tests__/dm-backfill.test.ts +91 -1
  116. package/src/__tests__/document-create-route.test.ts +88 -0
  117. package/src/__tests__/document-sync-tags.test.ts +27 -0
  118. package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
  119. package/src/__tests__/events-tail-route.test.ts +58 -0
  120. package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
  121. package/src/__tests__/guardian-action-sweep.test.ts +16 -0
  122. package/src/__tests__/guardian-dispatch.test.ts +13 -0
  123. package/src/__tests__/headless-browser-interactions.test.ts +64 -20
  124. package/src/__tests__/heartbeat-service.test.ts +2 -59
  125. package/src/__tests__/helpers/mock-conversation.ts +21 -0
  126. package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
  127. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
  128. package/src/__tests__/history-repair.test.ts +53 -0
  129. package/src/__tests__/host-cu-proxy.test.ts +46 -2
  130. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  131. package/src/__tests__/image-credentials.test.ts +49 -0
  132. package/src/__tests__/image-service-dispatcher.test.ts +45 -0
  133. package/src/__tests__/inference-profile-session-handler.test.ts +26 -0
  134. package/src/__tests__/interrupt-on-send.test.ts +57 -43
  135. package/src/__tests__/interrupt-turn-note.test.ts +5 -6
  136. package/src/__tests__/list-messages-tool-merge.test.ts +85 -1
  137. package/src/__tests__/llm-catalog-parity.test.ts +27 -4
  138. package/src/__tests__/media-generate-image.test.ts +49 -1
  139. package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
  140. package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
  141. package/src/__tests__/mode-session-history.test.ts +245 -0
  142. package/src/__tests__/mode-session-recovery.test.ts +222 -0
  143. package/src/__tests__/mode-session-wire.test.ts +141 -0
  144. package/src/__tests__/notification-guardian-path.test.ts +7 -0
  145. package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
  146. package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
  147. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  148. package/src/__tests__/oauth-commands-routes.test.ts +114 -101
  149. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  150. package/src/__tests__/oauth-provider-serializer.test.ts +13 -0
  151. package/src/__tests__/oauth-providers-routes.test.ts +3 -0
  152. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  153. package/src/__tests__/persist-media-references.test.ts +50 -0
  154. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  155. package/src/__tests__/plugin-api-provider.test.ts +5 -0
  156. package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
  157. package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -1
  158. package/src/__tests__/process-message-display-content.test.ts +56 -0
  159. package/src/__tests__/reaction-persistence.test.ts +5 -0
  160. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
  161. package/src/__tests__/run-conversation-turn-persistence.test.ts +138 -1
  162. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  163. package/src/__tests__/schedule-routes.test.ts +20 -0
  164. package/src/__tests__/scheduler-result-notification.test.ts +23 -4
  165. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  166. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
  167. package/src/__tests__/secret-ingress-http.test.ts +2 -0
  168. package/src/__tests__/secret-routes-platform-proxy.test.ts +86 -31
  169. package/src/__tests__/secret-routes-scrub.test.ts +22 -27
  170. package/src/__tests__/secret-scanner.test.ts +20 -0
  171. package/src/__tests__/secure-keys-managed-failover.test.ts +8 -0
  172. package/src/__tests__/secure-keys.test.ts +50 -4
  173. package/src/__tests__/send-endpoint-busy.test.ts +163 -1
  174. package/src/__tests__/server-history-render.test.ts +91 -1
  175. package/src/__tests__/server-tool-pairing.test.ts +107 -0
  176. package/src/__tests__/settings-routes.test.ts +50 -1
  177. package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
  178. package/src/__tests__/skills.test.ts +5 -4
  179. package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
  180. package/src/__tests__/subagent-manager-notify.test.ts +1 -2
  181. package/src/__tests__/subagent-notify-parent.test.ts +146 -39
  182. package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
  183. package/src/__tests__/subagent-tool-gate-mode.test.ts +226 -0
  184. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
  185. package/src/__tests__/terminal-tools.test.ts +8 -0
  186. package/src/__tests__/thread-backfill.test.ts +53 -0
  187. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  188. package/src/__tests__/tool-result-follow-up.test.ts +99 -0
  189. package/src/__tests__/tool-result-metadata-plumbing.test.ts +66 -1
  190. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  191. package/src/__tests__/twilio-routes.test.ts +1 -0
  192. package/src/__tests__/unicode.test.ts +36 -0
  193. package/src/__tests__/web-search-backend-failure.test.ts +1 -1
  194. package/src/activation/progress-store.test.ts +12 -7
  195. package/src/agent/loop.ts +136 -53
  196. package/src/agent/tool-result-follow-up.ts +70 -0
  197. package/src/api/attachment-provenance.test.ts +68 -0
  198. package/src/api/computer-use-tool.test.ts +55 -0
  199. package/src/api/computer-use-tool.ts +27 -0
  200. package/src/api/events/assistant-outbound-attachment.ts +3 -0
  201. package/src/api/events/assistant-turn-start.ts +3 -0
  202. package/src/api/events/desktop-activity-changed.ts +10 -0
  203. package/src/api/events/generation-handoff.ts +2 -0
  204. package/src/api/events/message-complete.ts +2 -0
  205. package/src/api/events/notification-conversation-created.ts +14 -16
  206. package/src/api/events/notification-intent.ts +4 -2
  207. package/src/api/events/question-request.ts +1 -0
  208. package/src/api/events/tool-result.ts +85 -0
  209. package/src/api/events/tool-use-start.ts +3 -0
  210. package/src/api/events/user-message-echo.ts +3 -0
  211. package/src/api/index.ts +42 -6
  212. package/src/api/mode-session.test.ts +131 -0
  213. package/src/api/mode-session.ts +148 -0
  214. package/src/api/remember-tool.ts +15 -0
  215. package/src/api/responses/conversation-message.ts +9 -0
  216. package/src/approvals/AGENTS.md +4 -2
  217. package/src/approvals/approval-primitive.ts +5 -2
  218. package/src/approvals/scoped-approval-grants.ts +6 -2
  219. package/src/browser/__tests__/operations.test.ts +29 -0
  220. package/src/browser/operation-meta.ts +2 -1
  221. package/src/browser/operations.ts +29 -0
  222. package/src/browser/virtual-desktop-target.ts +2 -9
  223. package/src/bundler/bundle-signer.ts +1 -3
  224. package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
  225. package/src/calls/__tests__/call-controller.test.ts +94 -0
  226. package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
  227. package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
  228. package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
  229. package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
  230. package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
  231. package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
  232. package/src/calls/barge-in-guard.ts +5 -0
  233. package/src/calls/call-controller.ts +196 -1
  234. package/src/calls/call-domain.ts +4 -0
  235. package/src/calls/call-store.ts +87 -2
  236. package/src/calls/guardian-action-sweep.ts +22 -8
  237. package/src/calls/media-stream-audio-transcode.ts +1 -3
  238. package/src/calls/typesafe-noul.ts +156 -0
  239. package/src/calls/voice-control-protocol.ts +6 -0
  240. package/src/calls/voice-escalation-judge.ts +145 -0
  241. package/src/calls/voice-escalation-target.ts +10 -0
  242. package/src/calls/voice-leg-coordinator.ts +121 -3
  243. package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
  244. package/src/calls/voice-session-bridge.ts +256 -112
  245. package/src/calls/voice-triage-escalate.ts +7 -0
  246. package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
  247. package/src/cli/commands/__tests__/cli-test-harness.ts +21 -3
  248. package/src/cli/commands/__tests__/notifications.test.ts +35 -10
  249. package/src/cli/commands/__tests__/plugins.test.ts +20 -1
  250. package/src/cli/commands/__tests__/schedules.test.ts +14 -0
  251. package/src/cli/commands/bash.help.ts +4 -3
  252. package/src/cli/commands/browser.help.ts +9 -6
  253. package/src/cli/commands/browser.ts +0 -1
  254. package/src/cli/commands/clients.help.ts +46 -0
  255. package/src/cli/commands/clients.ts +116 -0
  256. package/src/cli/commands/credentials.help.ts +9 -4
  257. package/src/cli/commands/notifications.help.ts +16 -2
  258. package/src/cli/commands/notifications.ts +30 -8
  259. package/src/cli/commands/oauth/index.help.ts +10 -0
  260. package/src/cli/commands/oauth/providers.ts +12 -0
  261. package/src/cli/commands/oauth/request.test.ts +179 -2
  262. package/src/cli/commands/oauth/request.ts +41 -10
  263. package/src/cli/commands/plugins.ts +12 -5
  264. package/src/cli/commands/roadmap.help.ts +18 -21
  265. package/src/cli/commands/roadmap.ts +4 -21
  266. package/src/cli/commands/schedules.ts +2 -0
  267. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  268. package/src/cli/lib/__tests__/install-from-github.test.ts +41 -0
  269. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
  270. package/src/cli/lib/__tests__/local-plugin-upgrade.test.ts +133 -51
  271. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +8 -2
  272. package/src/cli/lib/bundled-marketplace.json +906 -1
  273. package/src/cli/lib/bundled-plugin-packages.json +783 -1
  274. package/src/cli/lib/inspect-plugin.ts +11 -4
  275. package/src/cli/lib/plugin-marketplace.ts +8 -0
  276. package/src/cli/lib/publish-plugin.ts +1 -2
  277. package/src/cli/lib/upgrade-plugin.ts +19 -11
  278. package/src/cli/output.ts +18 -4
  279. package/src/config/__tests__/default-provider.test.ts +6 -0
  280. package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
  281. package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
  282. package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
  283. package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
  284. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
  285. package/src/config/bundled-skills/schedule/SKILL.md +6 -6
  286. package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
  287. package/src/config/call-site-defaults.ts +9 -0
  288. package/src/config/feature-flag-registry.json +16 -0
  289. package/src/config/loader.ts +9 -17
  290. package/src/config/platform-identity.ts +8 -8
  291. package/src/config/profile-text-generation.test.ts +51 -0
  292. package/src/config/profile-text-generation.ts +51 -0
  293. package/src/config/schemas/__tests__/memory-v3.test.ts +12 -0
  294. package/src/config/schemas/__tests__/tools.test.ts +31 -0
  295. package/src/config/schemas/call-site-catalog.ts +14 -0
  296. package/src/config/schemas/channels.ts +0 -18
  297. package/src/config/schemas/llm.ts +9 -2
  298. package/src/config/schemas/mcp.ts +6 -1
  299. package/src/config/schemas/memory-retrospective.ts +11 -2
  300. package/src/config/schemas/memory-v3.ts +12 -0
  301. package/src/config/schemas/services.ts +15 -6
  302. package/src/config/schemas/tools.ts +10 -0
  303. package/src/config/session-groups-gate.ts +21 -0
  304. package/src/config/skills.ts +2 -6
  305. package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
  306. package/src/conversations/message-consolidation.ts +102 -5
  307. package/src/credential-execution/ces-connect.test.ts +69 -0
  308. package/src/credential-execution/ces-connect.ts +115 -0
  309. package/src/credential-execution/ces-runtime.ts +55 -93
  310. package/src/credential-execution/executable-discovery.ts +34 -142
  311. package/src/credential-execution/process-manager.test.ts +20 -24
  312. package/src/credential-execution/process-manager.ts +24 -19
  313. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
  314. package/src/daemon/__tests__/conversation-tool-setup.test.ts +43 -0
  315. package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
  316. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +36 -0
  317. package/src/daemon/assistant-attachments.ts +20 -12
  318. package/src/daemon/browser-mode-session.test.ts +402 -0
  319. package/src/daemon/browser-mode-session.ts +186 -0
  320. package/src/daemon/chat-credential-redaction.ts +16 -1
  321. package/src/daemon/computer-use-mode-session.test.ts +210 -0
  322. package/src/daemon/computer-use-mode-session.ts +105 -0
  323. package/src/daemon/config-watcher.ts +10 -0
  324. package/src/daemon/conversation-agent-loop-handlers.ts +132 -13
  325. package/src/daemon/conversation-agent-loop.ts +231 -25
  326. package/src/daemon/conversation-attachments.ts +93 -12
  327. package/src/daemon/conversation-evictor.ts +5 -24
  328. package/src/daemon/conversation-history.ts +10 -0
  329. package/src/daemon/conversation-interrupt-repair.ts +4 -43
  330. package/src/daemon/conversation-interrupt.ts +7 -11
  331. package/src/daemon/conversation-messaging.ts +58 -5
  332. package/src/daemon/conversation-mode-session.test.ts +1667 -0
  333. package/src/daemon/conversation-mode-session.ts +1008 -0
  334. package/src/daemon/conversation-process.ts +5 -0
  335. package/src/daemon/conversation-runtime-assembly.ts +10 -20
  336. package/src/daemon/conversation-skill-tools.ts +1 -0
  337. package/src/daemon/conversation-store.ts +4 -4
  338. package/src/daemon/conversation-surface-snapshots.ts +135 -0
  339. package/src/daemon/conversation-surfaces.ts +113 -5
  340. package/src/daemon/conversation-tool-setup.ts +79 -6
  341. package/src/daemon/conversation-turn-finalize.ts +26 -13
  342. package/src/daemon/conversation.ts +215 -58
  343. package/src/daemon/daemon-control.ts +3 -7
  344. package/src/daemon/handlers/config-model.ts +7 -5
  345. package/src/daemon/handlers/shared.ts +55 -16
  346. package/src/daemon/host-cu-proxy.ts +51 -22
  347. package/src/daemon/lifecycle.ts +30 -13
  348. package/src/daemon/mcp-reload-service.ts +1 -1
  349. package/src/daemon/message-protocol.ts +0 -1
  350. package/src/daemon/message-provenance.ts +28 -4
  351. package/src/daemon/message-types/shared.ts +1 -0
  352. package/src/daemon/mode-session-startup-recovery.ts +23 -0
  353. package/src/daemon/mode-session-tracking.ts +54 -0
  354. package/src/daemon/orphan-reaper.ts +4 -3
  355. package/src/daemon/persist-media-references.ts +20 -3
  356. package/src/daemon/process-message.ts +13 -4
  357. package/src/daemon/tool-setup-types.ts +6 -0
  358. package/src/daemon/virtual-desktop-context.ts +21 -0
  359. package/src/daemon/wake-conversation-ops.ts +50 -15
  360. package/src/desktop/__tests__/fake-desktop.ts +1 -0
  361. package/src/desktop/desktop-accessibility-script.ts +88 -0
  362. package/src/desktop/desktop-accessibility.test.ts +160 -0
  363. package/src/desktop/desktop-accessibility.ts +169 -0
  364. package/src/desktop/desktop-automation-lease.test.ts +302 -64
  365. package/src/desktop/desktop-automation-lease.ts +129 -54
  366. package/src/desktop/desktop-browser-endpoint.ts +4 -0
  367. package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
  368. package/src/desktop/desktop-computer-use.test.ts +449 -0
  369. package/src/desktop/desktop-computer-use.ts +502 -0
  370. package/src/desktop/desktop-dependencies.ts +47 -293
  371. package/src/desktop/desktop-display.ts +2 -2
  372. package/src/desktop/desktop-help.ts +15 -0
  373. package/src/desktop/desktop-panel-config.test.ts +13 -3
  374. package/src/desktop/desktop-panel-config.ts +109 -15
  375. package/src/desktop/desktop-session-manager.test.ts +69 -21
  376. package/src/desktop/desktop-session-manager.ts +76 -22
  377. package/src/desktop/desktop-stream-bridge.test.ts +76 -132
  378. package/src/desktop/desktop-stream-bridge.ts +9 -11
  379. package/src/desktop/desktop-wallpaper-renderer.ts +132 -0
  380. package/src/desktop/desktop-wallpaper-worker.ts +17 -0
  381. package/src/desktop/desktop-wallpaper.test.ts +48 -7
  382. package/src/desktop/desktop-wallpaper.ts +34 -102
  383. package/src/desktop/desktop-window-theme.ts +5 -25
  384. package/src/desktop/virtual-desktop-feature.ts +53 -3
  385. package/src/desktop/virtual-desktop-platform.test.ts +5 -9
  386. package/src/documents/document-store.ts +23 -0
  387. package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
  388. package/src/heartbeat/heartbeat-service.ts +5 -33
  389. package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
  390. package/src/ipc/assistant-server.ts +2 -0
  391. package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
  392. package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
  393. package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
  394. package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
  395. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
  396. package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
  397. package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
  398. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
  399. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
  400. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
  401. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
  402. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
  403. package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
  404. package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
  405. package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
  406. package/src/live-voice/__tests__/session-controls.test.ts +23 -0
  407. package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
  408. package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
  409. package/src/live-voice/camera-mode-session.test.ts +265 -0
  410. package/src/live-voice/camera-mode-session.ts +277 -0
  411. package/src/live-voice/continuation-judge.ts +123 -0
  412. package/src/live-voice/live-activity-reporter.ts +115 -6
  413. package/src/live-voice/live-voice-manager.ts +36 -1
  414. package/src/live-voice/live-voice-photo.ts +88 -7
  415. package/src/live-voice/live-voice-session-manager.ts +15 -0
  416. package/src/live-voice/live-voice-session.ts +1443 -747
  417. package/src/live-voice/live-voice-tts.ts +22 -4
  418. package/src/live-voice/microphone-idle-gate.ts +56 -0
  419. package/src/live-voice/protocol.ts +171 -4
  420. package/src/live-voice/session-controls.ts +11 -3
  421. package/src/live-voice/task-outcomes.ts +131 -0
  422. package/src/live-voice/voice-input-diagnostics.ts +117 -0
  423. package/src/mcp/__tests__/manager-tool-caps.test.ts +156 -0
  424. package/src/mcp/__tests__/startup.test.ts +30 -7
  425. package/src/mcp/__tests__/tool-caps.test.ts +134 -0
  426. package/src/mcp/manager.ts +171 -101
  427. package/src/mcp/startup.ts +33 -13
  428. package/src/mcp/tool-caps.ts +184 -0
  429. package/src/media/__tests__/image-models.test.ts +58 -0
  430. package/src/media/image-credentials.ts +5 -0
  431. package/src/media/image-models.ts +52 -0
  432. package/src/media/image-service.ts +8 -0
  433. package/src/media/openrouter-image-service.test.ts +133 -0
  434. package/src/media/openrouter-image-service.ts +153 -0
  435. package/src/media/types.ts +1 -1
  436. package/src/messaging/provider-message-metadata.ts +3 -3
  437. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
  438. package/src/messaging/providers/discord/send.ts +4 -17
  439. package/src/messaging/providers/discord/transport.ts +2 -2
  440. package/src/messaging/providers/outlook/client.ts +16 -22
  441. package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
  442. package/src/messaging/providers/telegram-bot/send.ts +10 -7
  443. package/src/messaging/providers/telegram-bot/transport.ts +3 -10
  444. package/src/messaging/providers/whatsapp/send.test.ts +169 -4
  445. package/src/monitoring/__tests__/control.test.ts +17 -5
  446. package/src/monitoring/control.ts +15 -5
  447. package/src/notifications/AGENTS.md +1 -1
  448. package/src/notifications/README.md +83 -99
  449. package/src/notifications/__tests__/broadcaster.test.ts +1 -1
  450. package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
  451. package/src/notifications/__tests__/copy-composer.test.ts +71 -1
  452. package/src/notifications/__tests__/decision-engine.test.ts +79 -2
  453. package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
  454. package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
  455. package/src/notifications/__tests__/notification-utils.test.ts +18 -0
  456. package/src/notifications/adapters/macos.ts +37 -46
  457. package/src/notifications/broadcaster.ts +5 -6
  458. package/src/notifications/channel-allowlist.ts +44 -0
  459. package/src/notifications/copy-composer.ts +11 -26
  460. package/src/notifications/decision-engine.ts +24 -14
  461. package/src/notifications/deterministic-checks.ts +1 -1
  462. package/src/notifications/emit-signal.ts +123 -37
  463. package/src/notifications/home-feed-side-effect.ts +9 -7
  464. package/src/notifications/notification-utils.ts +19 -0
  465. package/src/notifications/schedule-result-producer.ts +4 -6
  466. package/src/notifications/signal.ts +4 -19
  467. package/src/notifications/types.ts +15 -0
  468. package/src/oauth/AGENTS.md +2 -0
  469. package/src/oauth/__tests__/identity-verifier.test.ts +36 -1
  470. package/src/oauth/connection-resolver.ts +1 -2
  471. package/src/oauth/identity-verifier.ts +24 -0
  472. package/src/oauth/oauth-store.ts +12 -4
  473. package/src/oauth/provider-serializer.ts +8 -0
  474. package/src/oauth/seed-providers.ts +42 -0
  475. package/src/onboarding/onboarding-events-store.ts +99 -12
  476. package/src/permissions/confirmation-guardian-request.test.ts +16 -0
  477. package/src/permissions/confirmation-guardian-request.ts +2 -3
  478. package/src/permissions/question-prompter.test.ts +31 -0
  479. package/src/permissions/question-prompter.ts +2 -0
  480. package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
  481. package/src/persistence/attachments-store.ts +84 -44
  482. package/src/persistence/client-connection-events-store.ts +377 -0
  483. package/src/persistence/conversation-crud.ts +166 -26
  484. package/src/persistence/conversation-mode-sessions.test.ts +424 -0
  485. package/src/persistence/conversation-mode-sessions.ts +460 -0
  486. package/src/persistence/conversation-plugin-facade.ts +30 -0
  487. package/src/persistence/conversation-queries.ts +6 -19
  488. package/src/persistence/conversation-tool-surface.ts +135 -0
  489. package/src/persistence/conversation-types.test.ts +32 -0
  490. package/src/persistence/conversation-types.ts +26 -4
  491. package/src/persistence/llm-usage-store.ts +3 -9
  492. package/src/persistence/message-metadata.ts +25 -0
  493. package/src/persistence/migrations/378-create-conversation-tool-surfaces.test.ts +78 -0
  494. package/src/persistence/migrations/378-create-conversation-tool-surfaces.ts +29 -0
  495. package/src/persistence/migrations/379-oauth-providers-response-ok-field.test.ts +92 -0
  496. package/src/persistence/migrations/379-oauth-providers-response-ok-field.ts +32 -0
  497. package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.test.ts +90 -0
  498. package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.ts +34 -0
  499. package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
  500. package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
  501. package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
  502. package/src/persistence/schema/conversation-tool-surfaces.ts +32 -0
  503. package/src/persistence/schema/conversations.ts +67 -0
  504. package/src/persistence/schema/index.ts +1 -0
  505. package/src/persistence/schema/infrastructure.ts +29 -0
  506. package/src/persistence/schema/oauth.ts +1 -0
  507. package/src/persistence/steps.ts +41 -3
  508. package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
  509. package/src/plugin-api/conversation-turn.ts +31 -7
  510. package/src/plugin-api/effective-context-window.ts +33 -0
  511. package/src/plugin-api/index.ts +35 -6
  512. package/src/plugin-api/plugin-channel-turn-trust.test.ts +133 -0
  513. package/src/plugin-api/plugin-channel-turn-trust.ts +71 -0
  514. package/src/plugin-api/plugin-name-env.ts +125 -0
  515. package/src/plugin-api/resolve-credential.ts +79 -20
  516. package/src/plugin-api/vision-support.test.ts +22 -0
  517. package/src/plugin-api/vision-support.ts +17 -5
  518. package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
  519. package/src/plugins/active-plugin-env.ts +51 -0
  520. package/src/plugins/defaults/index.ts +24 -0
  521. package/src/plugins/defaults/memory/AGENTS.md +39 -5
  522. package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
  523. package/src/plugins/defaults/memory/__tests__/buffer-file.test.ts +365 -0
  524. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +43 -0
  525. package/src/plugins/defaults/memory/__tests__/conversation-memory-purge.test.ts +1 -0
  526. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +2 -0
  527. package/src/plugins/defaults/memory/__tests__/fixtures/buffer-appender.ts +17 -0
  528. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +74 -0
  529. package/src/plugins/defaults/memory/__tests__/memory-run-evidence.test.ts +161 -0
  530. package/src/plugins/defaults/memory/__tests__/relocated-memory-test-rows.ts +10 -0
  531. package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
  532. package/src/plugins/defaults/memory/buffer-file.ts +354 -0
  533. package/src/plugins/defaults/memory/buffer-format.ts +40 -0
  534. package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
  535. package/src/plugins/defaults/memory/context-search/agent-runner.ts +77 -22
  536. package/src/plugins/defaults/memory/context-search/format.ts +53 -8
  537. package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
  538. package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
  539. package/src/plugins/defaults/memory/context-search/search.ts +8 -14
  540. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
  541. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -1
  542. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +2 -1
  543. package/src/plugins/defaults/memory/context-search/types.ts +1 -11
  544. package/src/plugins/defaults/memory/conversation-memory-purge.ts +4 -0
  545. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
  546. package/src/plugins/defaults/memory/graph/capability-seed.ts +1 -2
  547. package/src/plugins/defaults/memory/graph/tool-handlers.ts +13 -63
  548. package/src/plugins/defaults/memory/graph/tools.ts +35 -64
  549. package/src/plugins/defaults/memory/host-utils.ts +0 -10
  550. package/src/plugins/defaults/memory/injectors.ts +4 -3
  551. package/src/plugins/defaults/memory/memory-retrospective-job.ts +72 -183
  552. package/src/plugins/defaults/memory/memory-run-evidence.ts +213 -0
  553. package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
  554. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +6 -1
  555. package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
  556. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +407 -99
  557. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +10 -0
  558. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +107 -7
  559. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +307 -86
  560. package/src/plugins/defaults/memory/substrate/consolidation-tool-surface.ts +34 -0
  561. package/src/plugins/defaults/memory/substrate/page-index.ts +2 -1
  562. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +91 -51
  563. package/src/plugins/defaults/memory/substrate/sweep-job.ts +1 -1
  564. package/src/plugins/defaults/memory/tools.test.ts +124 -9
  565. package/src/plugins/defaults/memory/tools.ts +38 -11
  566. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +2 -2
  567. package/src/plugins/defaults/memory/v1/graph/extraction.ts +2 -1
  568. package/src/plugins/defaults/memory/v1/graph/retriever.ts +1 -1
  569. package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +5 -0
  570. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +5 -2
  571. package/src/plugins/defaults/memory/v2/reranker.ts +2 -1
  572. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +81 -1
  573. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +87 -0
  574. package/src/plugins/defaults/memory/v3/__tests__/plugin-schema.test.ts +12 -0
  575. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +332 -15
  576. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
  577. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +148 -28
  578. package/src/plugins/defaults/memory/v3/card.ts +2 -1
  579. package/src/plugins/defaults/memory/v3/injector.ts +212 -178
  580. package/src/plugins/defaults/memory/v3/orchestrate.ts +159 -32
  581. package/src/plugins/defaults/memory/v3/plugin-schema.ts +54 -4
  582. package/src/plugins/defaults/memory/v3/pool-log-store.ts +275 -37
  583. package/src/plugins/defaults/memory/v3/pool-select.test.ts +47 -5
  584. package/src/plugins/defaults/memory/v3/pool-select.ts +710 -59
  585. package/src/plugins/defaults/memory/v3/sections.ts +2 -1
  586. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +41 -14
  587. package/src/plugins/defaults/memory/worker-control.ts +15 -5
  588. package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
  589. package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
  590. package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
  591. package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
  592. package/src/plugins/defaults/task-progress/package.json +14 -0
  593. package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
  594. package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
  595. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +4 -1
  596. package/src/plugins/defaults/tool-result-truncate/terminal.ts +1 -46
  597. package/src/prompts/__tests__/parallel-tasks-section.test.ts +25 -0
  598. package/src/prompts/delegation-gate.ts +57 -0
  599. package/src/prompts/system-prompt.ts +14 -32
  600. package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
  601. package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
  602. package/src/providers/anthropic/client.ts +10 -1
  603. package/src/providers/anthropic-gateway-shared.ts +6 -0
  604. package/src/providers/fetch-provider-catalog.ts +11 -0
  605. package/src/providers/gemini/client.ts +25 -8
  606. package/src/providers/inference/adapter-factory.ts +6 -0
  607. package/src/providers/jev/client.test.ts +270 -0
  608. package/src/providers/jev/client.ts +534 -0
  609. package/src/providers/model-catalog.ts +59 -3
  610. package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
  611. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
  612. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
  613. package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
  614. package/src/providers/openai/chat-completions-provider.ts +100 -29
  615. package/src/providers/openai/google-thought-signature.ts +0 -6
  616. package/src/providers/openai/responses-provider.ts +23 -1
  617. package/src/providers/ratelimit.ts +1 -3
  618. package/src/providers/search-provider-catalog.ts +24 -0
  619. package/src/providers/server-tool-pairing.ts +16 -7
  620. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
  621. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
  622. package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
  623. package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
  624. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
  625. package/src/providers/types.ts +5 -0
  626. package/src/routes/control.ts +7 -5
  627. package/src/runtime/AGENTS.md +6 -4
  628. package/src/runtime/__tests__/agent-wake.test.ts +94 -1
  629. package/src/runtime/actor-trust-resolver.ts +3 -3
  630. package/src/runtime/agent-wake.ts +32 -5
  631. package/src/runtime/approval-message-composer.ts +3 -69
  632. package/src/runtime/assistant-event-hub.ts +73 -75
  633. package/src/runtime/assistant-event-publish-options.ts +7 -0
  634. package/src/runtime/assistant-event-targeting.ts +79 -0
  635. package/src/runtime/assistant-stream-state.ts +13 -100
  636. package/src/runtime/background-job-runner.ts +5 -5
  637. package/src/runtime/channel-retry-sweep.ts +1 -0
  638. package/src/runtime/guardian-action-message-composer.ts +8 -161
  639. package/src/runtime/guardian-action-service.ts +2 -17
  640. package/src/runtime/guardian-reply-router.ts +29 -40
  641. package/src/runtime/http-server.ts +2 -2
  642. package/src/runtime/http-types.ts +6 -0
  643. package/src/runtime/message-composer-types.ts +3 -61
  644. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
  645. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +89 -0
  646. package/src/runtime/migrations/vbundle-builder.ts +24 -1
  647. package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
  648. package/src/runtime/migrations/vbundle-import-policy.ts +43 -11
  649. package/src/runtime/migrations/vbundle-importer.ts +4 -1
  650. package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
  651. package/src/runtime/migrations/vbundle-validator.ts +1 -0
  652. package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
  653. package/src/runtime/pending-interactions.ts +29 -0
  654. package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
  655. package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
  656. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +24 -2
  657. package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
  658. package/src/runtime/routes/browser-context.ts +31 -11
  659. package/src/runtime/routes/browser-routes.ts +43 -4
  660. package/src/runtime/routes/btw-routes.ts +9 -4
  661. package/src/runtime/routes/channel-route-shared.ts +1 -9
  662. package/src/runtime/routes/client-routes.ts +108 -0
  663. package/src/runtime/routes/conversation-management-routes.ts +4 -1
  664. package/src/runtime/routes/conversation-routes.ts +125 -14
  665. package/src/runtime/routes/credential-prompt-routes.ts +4 -3
  666. package/src/runtime/routes/credential-routes.ts +1 -2
  667. package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
  668. package/src/runtime/routes/desktop-setup-routes.ts +8 -7
  669. package/src/runtime/routes/documents-routes.ts +51 -0
  670. package/src/runtime/routes/email-routes.ts +3 -7
  671. package/src/runtime/routes/events-routes.ts +75 -54
  672. package/src/runtime/routes/guardian-approval-interception.ts +24 -0
  673. package/src/runtime/routes/identity-routes.ts +1 -5
  674. package/src/runtime/routes/image-generation-routes.ts +8 -14
  675. package/src/runtime/routes/inbound-message-handler.ts +37 -41
  676. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
  677. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
  678. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +39 -10
  679. package/src/runtime/routes/inbound-stages/background-dispatch.ts +9 -9
  680. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
  681. package/src/runtime/routes/inference-profile-session-handler.ts +11 -1
  682. package/src/runtime/routes/inference-profiles-routes.ts +9 -0
  683. package/src/runtime/routes/llm-context-normalization.ts +1 -7
  684. package/src/runtime/routes/migration-routes.ts +73 -2
  685. package/src/runtime/routes/notification-routes.ts +10 -0
  686. package/src/runtime/routes/oauth-commands-routes.ts +57 -54
  687. package/src/runtime/routes/oauth-providers.ts +11 -0
  688. package/src/runtime/routes/oauth-request-hints.test.ts +246 -0
  689. package/src/runtime/routes/oauth-request-hints.ts +149 -0
  690. package/src/runtime/routes/plugins-routes.ts +6 -0
  691. package/src/runtime/routes/roadmap-routes.ts +47 -8
  692. package/src/runtime/routes/schedule-routes.ts +2 -0
  693. package/src/runtime/routes/secret-routes.ts +99 -119
  694. package/src/runtime/routes/settings-routes.ts +10 -4
  695. package/src/runtime/routes/watcher-routes.ts +1 -3
  696. package/src/runtime/trust-class.test.ts +7 -6
  697. package/src/runtime/trust-class.ts +9 -17
  698. package/src/schedule/__tests__/worker-control.test.ts +36 -5
  699. package/src/schedule/__tests__/worker-mcp-bootstrap.test.ts +107 -0
  700. package/src/schedule/__tests__/worker-mcp-readiness.test.ts +173 -0
  701. package/src/schedule/__tests__/worker-mcp-tools.test.ts +13 -47
  702. package/src/schedule/run-script.ts +2 -2
  703. package/src/schedule/scheduler.ts +47 -11
  704. package/src/schedule/tool-surface-readiness.ts +65 -0
  705. package/src/schedule/worker-control.ts +15 -5
  706. package/src/schedule/worker-mcp.ts +85 -0
  707. package/src/schedule/worker.ts +9 -31
  708. package/src/security/secure-keys.ts +74 -112
  709. package/src/skills/clawhub.ts +3 -8
  710. package/src/skills/managed-store.ts +1 -5
  711. package/src/stt/types.ts +4 -0
  712. package/src/subagent/notify.ts +54 -34
  713. package/src/subagent/parent-notification.ts +6 -0
  714. package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
  715. package/src/telemetry/phone-call-funnel.ts +144 -0
  716. package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
  717. package/src/tools/ask-question/ask-question-tool.test.ts +167 -5
  718. package/src/tools/ask-question/ask-question-tool.ts +120 -22
  719. package/src/tools/browser/browser-execution.ts +29 -60
  720. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
  721. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
  722. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
  723. package/src/tools/client-os.ts +15 -0
  724. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
  725. package/src/tools/computer-use/target.ts +42 -0
  726. package/src/tools/document/document-tool.ts +13 -18
  727. package/src/tools/execution-target.ts +14 -16
  728. package/src/tools/host-terminal/host-shell.ts +16 -7
  729. package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
  730. package/src/tools/network/__tests__/web-search.test.ts +203 -0
  731. package/src/tools/network/web-fetch.ts +274 -7
  732. package/src/tools/network/web-search.ts +468 -6
  733. package/src/tools/permission-checker.ts +3 -3
  734. package/src/tools/policy-context.ts +3 -1
  735. package/src/tools/shared/filesystem/file-ops-service.ts +1 -31
  736. package/src/tools/shared/shell-output.test.ts +10 -0
  737. package/src/tools/shared/shell-output.ts +14 -2
  738. package/src/tools/skills/load.ts +2 -1
  739. package/src/tools/skills/sandbox-runner.ts +24 -4
  740. package/src/tools/skills/scaffold-managed.ts +2 -1
  741. package/src/tools/skills/skill-script-runner.ts +7 -0
  742. package/src/tools/skills/skill-tool-factory.ts +15 -1
  743. package/src/tools/terminal/__tests__/safe-env.test.ts +44 -4
  744. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -14
  745. package/src/tools/terminal/safe-env.ts +39 -24
  746. package/src/tools/terminal/sanitized-bash.ts +15 -2
  747. package/src/tools/terminal/shell-launch.test.ts +162 -0
  748. package/src/tools/terminal/shell.test.ts +29 -0
  749. package/src/tools/terminal/shell.ts +15 -7
  750. package/src/tools/tool-approval-handler.ts +5 -1
  751. package/src/tools/tool-types.ts +3 -3
  752. package/src/tools/types.ts +8 -2
  753. package/src/util/__tests__/worker-process-command.test.ts +5 -4
  754. package/src/util/abort-reasons.ts +18 -26
  755. package/src/util/browser-human-verification.ts +14 -0
  756. package/src/util/canonicalize-identity.test.ts +21 -0
  757. package/src/util/canonicalize-identity.ts +16 -0
  758. package/src/util/errors.ts +13 -3
  759. package/src/util/host-process.test.ts +17 -1
  760. package/src/util/host-process.ts +24 -0
  761. package/src/util/provider-error-patterns.ts +3 -0
  762. package/src/util/unicode.ts +29 -0
  763. package/src/util/worker-process.ts +77 -35
  764. package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
  765. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
  766. package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
  767. package/src/workspace/migrations/registry.ts +2 -0
  768. package/src/__tests__/computer-use-window-schema.test.ts +0 -24
  769. package/src/api/events/heartbeat-alert.ts +0 -20
  770. package/src/daemon/message-types/web-activity.ts +0 -65
  771. package/src/desktop/desktop-dependencies.test.ts +0 -131
  772. package/src/tools/computer-use/definitions.ts +0 -607
@@ -163,7 +163,7 @@ function resetMockPage() {
163
163
  */
164
164
  function defaultCdpHandler(
165
165
  method: string,
166
- _params: Record<string, unknown> | undefined,
166
+ params: Record<string, unknown> | undefined,
167
167
  ): unknown {
168
168
  switch (method) {
169
169
  case "DOM.getDocument":
@@ -177,12 +177,28 @@ function defaultCdpHandler(
177
177
  case "Runtime.evaluate":
178
178
  return { result: { value: { w: 800, h: 600 } } };
179
179
  case "Runtime.callFunctionOn":
180
+ if (
181
+ params?.returnByValue === true &&
182
+ String(params.functionDeclaration).includes("isConnected")
183
+ ) {
184
+ return {
185
+ result: { value: { connected: true, matches: true } },
186
+ };
187
+ }
188
+ if (
189
+ params?.returnByValue === true &&
190
+ String(params.functionDeclaration).includes("clearFirst")
191
+ ) {
192
+ return { result: { value: { needsRefocus: false } } };
193
+ }
180
194
  // executeBrowserSelectOption invokes a function that returns
181
195
  // a `matched` boolean — default to true so wrapper-contract
182
196
  // tests don't need to know the inner select-option matching
183
197
  // shape. Tests that exercise the no-match path override the
184
198
  // handler explicitly.
185
199
  return { result: { value: true } };
200
+ case "Input.insertText":
201
+ return {};
186
202
  default:
187
203
  return {};
188
204
  }
@@ -485,20 +501,21 @@ describe("executeBrowserType", () => {
485
501
  expect(result.content).toContain('Typed into element: element_id "e3"');
486
502
  expect(result.content).toContain("cleared existing content");
487
503
 
488
- // Expected CDP sequence when resolving by backendNodeId + clearFirst:
489
- // DOM.focus → DOM.resolveNode → Runtime.callFunctionOn (clear) →
490
- // DOM.focus → Input.insertText
504
+ // The editor selection precedes insertion, and a live-node read verifies
505
+ // that the requested text persisted.
491
506
  const methods = sendCalls.map((c) => c.method);
492
507
  expect(methods).toEqual([
493
508
  "DOM.focus",
494
509
  "DOM.resolveNode",
495
510
  "Runtime.callFunctionOn",
496
- "DOM.focus",
497
511
  "Input.insertText",
512
+ "Runtime.callFunctionOn",
498
513
  ]);
499
514
  const focusCall = sendCalls[0]!;
500
515
  expect(focusCall.params).toEqual({ backendNodeId: 555 });
501
- const insertCall = sendCalls[sendCalls.length - 1]!;
516
+ const insertCall = sendCalls.find(
517
+ (call) => call.method === "Input.insertText",
518
+ )!;
502
519
  expect(insertCall.params).toEqual({ text: "hello" });
503
520
  });
504
521
 
@@ -524,11 +541,10 @@ describe("executeBrowserType", () => {
524
541
  );
525
542
  expect(result.isError).toBe(false);
526
543
  expect(result.content).not.toContain("cleared");
527
- // clear_first=false skips DOM.resolveNode + Runtime.callFunctionOn
528
- // and the re-focus call, so we should see focus + insertText only.
544
+ // Append mode collapses the editor selection at the end before insertion.
529
545
  const methods = sendCalls.map((c) => c.method);
530
- expect(methods).not.toContain("DOM.resolveNode");
531
- expect(methods).not.toContain("Runtime.callFunctionOn");
546
+ expect(methods).toContain("DOM.resolveNode");
547
+ expect(methods).toContain("Runtime.callFunctionOn");
532
548
  const focusCount = methods.filter((m) => m === "DOM.focus").length;
533
549
  expect(focusCount).toBe(1);
534
550
  expect(methods).toContain("Input.insertText");
@@ -542,22 +558,22 @@ describe("executeBrowserType", () => {
542
558
  expect(result.isError).toBe(false);
543
559
  expect(result.content).toContain("pressed Enter");
544
560
  const methods = sendCalls.map((c) => c.method);
545
- // Input.insertText must come before the Enter keyDown/char/keyUp.
561
+ // Input.insertText must come before the Enter rawKeyDown/char/keyUp.
546
562
  const insertIdx = methods.indexOf("Input.insertText");
547
563
  const keyDownIdx = methods.findIndex(
548
564
  (m, i) =>
549
565
  m === "Input.dispatchKeyEvent" &&
550
- (sendCalls[i]!.params as { type: string }).type === "keyDown",
566
+ (sendCalls[i]!.params as { type: string }).type === "rawKeyDown",
551
567
  );
552
568
  expect(insertIdx).toBeGreaterThanOrEqual(0);
553
569
  expect(keyDownIdx).toBeGreaterThan(insertIdx);
554
- // Enter is text-producing → keyDown + char + keyUp.
570
+ // Enter is text-producing, so it emits rawKeyDown + char + keyUp.
555
571
  const keyEvents = sendCalls.filter(
556
572
  (c) => c.method === "Input.dispatchKeyEvent",
557
573
  );
558
574
  expect(keyEvents).toHaveLength(3);
559
575
  expect((keyEvents[0]!.params as { key: string }).key).toBe("Enter");
560
- expect((keyEvents[0]!.params as { type: string }).type).toBe("keyDown");
576
+ expect((keyEvents[0]!.params as { type: string }).type).toBe("rawKeyDown");
561
577
  expect((keyEvents[1]!.params as { type: string }).type).toBe("char");
562
578
  expect((keyEvents[2]!.params as { type: string }).type).toBe("keyUp");
563
579
  });
@@ -689,7 +705,7 @@ describe("executeBrowserPressKey", () => {
689
705
  expect(result.content).toContain('Pressed "Enter"');
690
706
  // No target => no DOM.focus, no selector resolution. Enter is a
691
707
  // text-producing key (text "\r") so dispatchKeyPress emits
692
- // keyDown + char + keyUp.
708
+ // rawKeyDown + char + keyUp.
693
709
  const methods = sendCalls.map((c) => c.method);
694
710
  expect(methods).toEqual([
695
711
  "Input.dispatchKeyEvent",
@@ -699,7 +715,7 @@ describe("executeBrowserPressKey", () => {
699
715
  const keyDown = sendCalls[0]!.params as Record<string, unknown>;
700
716
  const charEvt = sendCalls[1]!.params as Record<string, unknown>;
701
717
  const keyUp = sendCalls[2]!.params as Record<string, unknown>;
702
- expect(keyDown.type).toBe("keyDown");
718
+ expect(keyDown.type).toBe("rawKeyDown");
703
719
  expect(keyDown.key).toBe("Enter");
704
720
  expect(keyDown.windowsVirtualKeyCode).toBe(13);
705
721
  expect(charEvt.type).toBe("char");
@@ -707,6 +723,36 @@ describe("executeBrowserPressKey", () => {
707
723
  expect(keyUp.key).toBe("Enter");
708
724
  });
709
725
 
726
+ test("presses modifier chords on the focused element", async () => {
727
+ const result = await executeBrowserPressKey({ key: "Meta+v" }, ctx);
728
+ expect(result.isError).toBe(false);
729
+ expect(result.content).toContain('Pressed "Meta+v"');
730
+
731
+ const keyEvents = sendCalls.filter(
732
+ (call) => call.method === "Input.dispatchKeyEvent",
733
+ );
734
+ expect(keyEvents).toHaveLength(4);
735
+ expect(keyEvents[0]!.params).toMatchObject({
736
+ type: "rawKeyDown",
737
+ key: "Meta",
738
+ modifiers: 4,
739
+ });
740
+ expect(keyEvents[1]!.params).toMatchObject({
741
+ type: "rawKeyDown",
742
+ key: "v",
743
+ modifiers: 4,
744
+ });
745
+ expect(keyEvents[2]!.params).toMatchObject({
746
+ type: "keyUp",
747
+ key: "v",
748
+ modifiers: 4,
749
+ });
750
+ expect(keyEvents[3]!.params).toMatchObject({
751
+ type: "keyUp",
752
+ key: "Meta",
753
+ });
754
+ });
755
+
710
756
  test("presses key on targeted element via element_id", async () => {
711
757
  snapshotBackendNodeMaps.set("test-conversation", new Map([["e5", 555]]));
712
758
  const result = await executeBrowserPressKey(
@@ -716,14 +762,12 @@ describe("executeBrowserPressKey", () => {
716
762
  expect(result.isError).toBe(false);
717
763
  expect(result.content).toContain('Pressed "Tab" on element');
718
764
  expect(result.content).toContain('element_id "e5"');
719
- // Backend-resolved path: focus → dispatchKeyEvent × 3 (Tab is
720
- // text-producing so we also dispatch a char event).
765
+ // Backend-resolved path: focus, then Tab key down and key up.
721
766
  const methods = sendCalls.map((c) => c.method);
722
767
  expect(methods).toEqual([
723
768
  "DOM.focus",
724
769
  "Input.dispatchKeyEvent",
725
770
  "Input.dispatchKeyEvent",
726
- "Input.dispatchKeyEvent",
727
771
  ]);
728
772
  expect(sendCalls[0]!.params).toEqual({ backendNodeId: 555 });
729
773
  });
@@ -767,7 +811,7 @@ describe("executeBrowserPressKey", () => {
767
811
 
768
812
  test("surfaces CDP failure as a press-key error", async () => {
769
813
  sendHandler = () => new Error("Key not recognized");
770
- const result = await executeBrowserPressKey({ key: "InvalidKey" }, ctx);
814
+ const result = await executeBrowserPressKey({ key: "Enter" }, ctx);
771
815
  expect(result.isError).toBe(true);
772
816
  expect(result.content).toContain("Press key failed");
773
817
  expect(result.content).toContain("Key not recognized");
@@ -334,11 +334,10 @@ const SCAFFOLD_PERSONA = stripCommentLines(GUARDIAN_PERSONA_TEMPLATE).trim();
334
334
  const { resolveCallSiteConfig } = await import("../config/llm-resolver.js");
335
335
  const { LLMSchema } = await import("../config/schemas/llm.js");
336
336
 
337
- // Capture broadcastMessage so tests can observe the alerts and
338
- // conversation-created events the heartbeat service emits directly.
337
+ // Capture broadcastMessage so tests can observe the conversation-created
338
+ // events the heartbeat service emits directly.
339
339
  type BroadcastedMessage = { type: string; [key: string]: unknown };
340
340
  const broadcastedMessages: BroadcastedMessage[] = [];
341
- let onBroadcast: ((msg: BroadcastedMessage) => void) | null = null;
342
341
 
343
342
  mock.module("../runtime/assistant-event-hub.js", () => ({
344
343
  assistantEventHub: {
@@ -347,7 +346,6 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
347
346
  },
348
347
  broadcastMessage: (msg: BroadcastedMessage) => {
349
348
  broadcastedMessages.push(msg);
350
- onBroadcast?.(msg);
351
349
  },
352
350
  }));
353
351
 
@@ -357,7 +355,6 @@ describe("HeartbeatService", () => {
357
355
  content: string;
358
356
  options?: { callSite?: string };
359
357
  }>;
360
- let alerterCalls: Array<{ type: string; title: string; body: string }>;
361
358
 
362
359
  afterEach(() => {
363
360
  // Clean up workspace files between tests so file-existence tests don't leak
@@ -368,13 +365,7 @@ describe("HeartbeatService", () => {
368
365
 
369
366
  beforeEach(() => {
370
367
  processMessageCalls = [];
371
- alerterCalls = [];
372
368
  broadcastedMessages.length = 0;
373
- onBroadcast = (msg) => {
374
- if (msg.type === "heartbeat_alert") {
375
- alerterCalls.push(msg as { type: string; title: string; body: string });
376
- }
377
- };
378
369
  createdConversations.length = 0;
379
370
  conversationIdCounter = 0;
380
371
  mockStoredMessages.length = 0;
@@ -693,21 +684,6 @@ describe("HeartbeatService", () => {
693
684
  expect(processMessageCalls).toHaveLength(1);
694
685
  });
695
686
 
696
- test("alerts on processMessage failure", async () => {
697
- const service = createService({
698
- processMessage: async () => {
699
- throw new Error("LLM timeout");
700
- },
701
- });
702
-
703
- await service.runOnce();
704
-
705
- expect(alerterCalls).toHaveLength(1);
706
- expect(alerterCalls[0].type).toBe("heartbeat_alert");
707
- expect(alerterCalls[0].title).toBe("Heartbeat Failed");
708
- expect(alerterCalls[0].body).toBe("LLM timeout");
709
- });
710
-
711
687
  test("successful run updates lastRunAt and nextRunAt", async () => {
712
688
  const service = createService();
713
689
  expect(service.lastRunAt).toBeNull();
@@ -724,24 +700,6 @@ describe("HeartbeatService", () => {
724
700
  );
725
701
  });
726
702
 
727
- test("alerts on conversation creation failure", async () => {
728
- // Override createConversation to throw via a fresh import trick:
729
- // Since createConversation is mocked at module level, we simulate
730
- // this by having processMessage throw before it's called — but the
731
- // real fix is that executeRun wraps createConversation in the try/catch.
732
- // We verify by checking that any error in executeRun triggers the alert.
733
- const service = createService({
734
- processMessage: async () => {
735
- throw new Error("DB locked");
736
- },
737
- });
738
-
739
- await service.runOnce();
740
-
741
- expect(alerterCalls).toHaveLength(1);
742
- expect(alerterCalls[0].body).toBe("DB locked");
743
- });
744
-
745
703
  test("resetTimer() pushes nextRunAt forward", () => {
746
704
  const service = createService();
747
705
  service.start();
@@ -1467,21 +1425,6 @@ describe("HeartbeatService", () => {
1467
1425
  });
1468
1426
  });
1469
1427
 
1470
- test("CAS false suppresses failure alerter and feed event", async () => {
1471
- mockCompleteHeartbeatRun.mockImplementation(() => false);
1472
-
1473
- const service = createService({
1474
- processMessage: async () => {
1475
- throw new Error("LLM timeout");
1476
- },
1477
- });
1478
-
1479
- await service.runOnce();
1480
-
1481
- // completeHeartbeatRun returned false, so alerter should NOT be called
1482
- expect(alerterCalls).toHaveLength(0);
1483
- });
1484
-
1485
1428
  test("active-hours skip calls skipHeartbeatRun", async () => {
1486
1429
  setHeartbeatConfig({ activeHoursStart: 9, activeHoursEnd: 17 });
1487
1430
 
@@ -18,6 +18,26 @@
18
18
  import type { Conversation } from "../../daemon/conversation.js";
19
19
  import type { ChannelCapabilities } from "../../daemon/conversation-runtime-assembly.js";
20
20
 
21
+ /** Build a coordinator double representing a turn with no mode owner. */
22
+ export function mockUnownedModeSessions(): Conversation["modeSessions"] {
23
+ return {
24
+ acceptTurn: () => undefined,
25
+ getTurnOwner: () => undefined,
26
+ getTerminalDisposition: () => undefined,
27
+ hasResidentWork: () => false,
28
+ keepsSessionOpenAfterTurn: () => false,
29
+ trackPersistedRow: () => false,
30
+ recordStructuralWait: () => false,
31
+ invalidateStructuralWait: () => false,
32
+ settleStructuralWait: () => false,
33
+ invalidateAllStructuralWaits: () => 0,
34
+ beginDraining: () => false,
35
+ finalizeTurn: () => false,
36
+ releaseTurn: () => {},
37
+ transferTurn: () => undefined,
38
+ } as unknown as Conversation["modeSessions"];
39
+ }
40
+
21
41
  /**
22
42
  * `TExtra` carries test-only fields a double hangs off the same object
23
43
  * (recorded calls, captured messages) through to the return type, so a caller
@@ -38,6 +58,7 @@ export function asConversation<TExtra extends object = object>(
38
58
  merged.currentTurnTrustContext ?? merged.trustContext,
39
59
  isStale: () => false,
40
60
  hasInFlightWork: () => false,
61
+ modeSessions: mockUnownedModeSessions(),
41
62
  ...mock,
42
63
  };
43
64
  return merged as unknown as Conversation & TExtra;
@@ -20,6 +20,7 @@ import type {
20
20
  EventHandlerState,
21
21
  } from "../../daemon/conversation-agent-loop-handlers.js";
22
22
  import { dispatchAgentEvent } from "../../daemon/conversation-agent-loop-handlers.js";
23
+ import { mockUnownedModeSessions } from "./mock-conversation.js";
23
24
 
24
25
  /** A `tool_result` `AssistantEvent` emitted by the handler. */
25
26
  export type ToolResultEvent = Extract<AssistantEvent, { type: "tool_result" }>;
@@ -59,6 +60,7 @@ export function createHandlerDeps(reqId = "req-web-search"): HandlerHarness {
59
60
  emitActivityState: () => {},
60
61
  markWorkspaceTopLevelDirty: () => {},
61
62
  currentTurnSurfaces: [],
63
+ modeSessions: mockUnownedModeSessions(),
62
64
  } as unknown as EventHandlerDeps["ctx"],
63
65
  onEvent: (msg: AssistantEvent) => events.push(msg),
64
66
  reqId,
@@ -276,6 +276,11 @@ export function createOutboundSessionGuarded(
276
276
  }
277
277
  source.status = "revoked";
278
278
  source.updatedAt = Date.now();
279
+ // Like the gateway, the replacement keeps the claimed session's purpose.
280
+ return createOutboundSession({
281
+ ...createParams,
282
+ verificationPurpose: source.verificationPurpose,
283
+ });
279
284
  }
280
285
 
281
286
  return createOutboundSession(createParams);
@@ -643,6 +643,59 @@ describe("repairHistory", () => {
643
643
  ]);
644
644
  });
645
645
 
646
+ test("deferred mixed tail answered by a tool_result plus trailing text gets a synthetic result", () => {
647
+ // Text after the client result closes the assistant turn on the provider
648
+ // side, which then rejects the unanswered search as unpaired. The search
649
+ // is an orphan here, so the synthetic error result keeps the request
650
+ // valid at the cost of that one search. `deepRepairHistory` is the loop's
651
+ // recovery path for that rejection.
652
+ const messages: Message[] = [
653
+ { role: "user", content: [{ type: "text", text: "Check both" }] },
654
+ {
655
+ role: "assistant",
656
+ content: [
657
+ { type: "tool_use", id: "tu_a", name: "web_fetch", input: {} },
658
+ {
659
+ type: "server_tool_use",
660
+ id: "stu_deferred",
661
+ name: "web_search",
662
+ input: { query: "news" },
663
+ },
664
+ ],
665
+ },
666
+ {
667
+ role: "user",
668
+ content: [
669
+ {
670
+ type: "tool_result",
671
+ tool_use_id: "tu_a",
672
+ content: "Error: HTTP 404",
673
+ is_error: true,
674
+ },
675
+ { type: "text", text: "<system_notice>retry</system_notice>" },
676
+ ],
677
+ },
678
+ ];
679
+
680
+ const { messages: repaired, stats } = deepRepairHistory(messages);
681
+
682
+ expect(stats.missingToolResultsInserted).toBe(1);
683
+ expect(repaired).toHaveLength(3);
684
+ expect(repaired[1].content.map((b) => b.type)).toEqual([
685
+ "tool_use",
686
+ "server_tool_use",
687
+ "web_search_tool_result",
688
+ ]);
689
+ expect(repaired[1].content[2]).toMatchObject({
690
+ type: "web_search_tool_result",
691
+ tool_use_id: "stu_deferred",
692
+ });
693
+ expect(repaired[2].content.map((b) => b.type)).toEqual([
694
+ "tool_result",
695
+ "text",
696
+ ]);
697
+ });
698
+
646
699
  test("cross-message server tool pair from a completed deferred execution survives a reload untouched", () => {
647
700
  const messages: Message[] = [
648
701
  { role: "user", content: [{ type: "text", text: "Check both" }] },
@@ -464,6 +464,43 @@ describe("HostCuProxy", () => {
464
464
  const result = await resultPromise;
465
465
  expect(result.isError).toBe(false);
466
466
  });
467
+
468
+ test("runs the lifecycle callback only for a validated dispatch", async () => {
469
+ setup(0);
470
+ const rejectedCallback = mock(() => {});
471
+ proxy.recordAction("computer_use_click", { element_id: 1 });
472
+ const rejected = await proxy.request(
473
+ "computer_use_click",
474
+ { element_id: 1 },
475
+ "session-1",
476
+ proxy.stepCount,
477
+ undefined,
478
+ undefined,
479
+ undefined,
480
+ undefined,
481
+ rejectedCallback,
482
+ );
483
+ expect(rejected.isError).toBe(true);
484
+ expect(rejectedCallback).not.toHaveBeenCalled();
485
+
486
+ setup();
487
+ const acceptedCallback = mock(() => {});
488
+ const pending = proxy.request(
489
+ "computer_use_click",
490
+ { element_id: 1 },
491
+ "session-1",
492
+ 1,
493
+ undefined,
494
+ undefined,
495
+ undefined,
496
+ undefined,
497
+ acceptedCallback,
498
+ );
499
+ expect(acceptedCallback).toHaveBeenCalledTimes(1);
500
+ const requestId = (sentMessages[0] as { requestId: string }).requestId;
501
+ proxy.processObservation(requestId, { executionResult: "Clicked" });
502
+ await pending;
503
+ });
467
504
  });
468
505
 
469
506
  // -------------------------------------------------------------------------
@@ -1333,7 +1370,13 @@ describe("HostCuProxy", () => {
1333
1370
  proxy.processObservation(requestId, { axTree: "Button [1]" });
1334
1371
  await resultPromise;
1335
1372
 
1336
- proxy.endTask("session-1");
1373
+ const sourceId = proxy.sourceId;
1374
+ expect(sourceId).not.toBe("");
1375
+ expect(proxy.resetGeneration).toBe(0);
1376
+
1377
+ expect(proxy.endTask("session-1")).toBe(0);
1378
+ expect(proxy.sourceId).toBe(sourceId);
1379
+ expect(proxy.resetGeneration).toBe(1);
1337
1380
  const cancels = sentMessages.filter(
1338
1381
  (m) => (m as Record<string, unknown>).type === "host_cu_cancel",
1339
1382
  ) as Array<Record<string, unknown>>;
@@ -1341,7 +1384,8 @@ describe("HostCuProxy", () => {
1341
1384
  expect(cancels[0].conversationId).toBe("session-1");
1342
1385
  expect(cancels[0].requestId).not.toBe(requestId);
1343
1386
 
1344
- proxy.endTask("session-1");
1387
+ expect(proxy.endTask("session-1")).toBe(1);
1388
+ expect(proxy.resetGeneration).toBe(2);
1345
1389
  expect(
1346
1390
  sentMessages.filter(
1347
1391
  (m) => (m as Record<string, unknown>).type === "host_cu_cancel",
@@ -161,6 +161,7 @@ mock.module("../runtime/trust-context-resolver.js", () => ({
161
161
  import type { AuthContext } from "../runtime/auth/types.js";
162
162
  import { handleSendMessage } from "../runtime/routes/conversation-routes.js";
163
163
  import { callHandler } from "./helpers/call-route-handler.js";
164
+ import { mockUnownedModeSessions } from "./helpers/mock-conversation.js";
164
165
 
165
166
  const _testAuthContext: AuthContext = {
166
167
  subject: "actor:self:test-actor",
@@ -194,6 +195,7 @@ function makeConversation(overrides: Record<string, unknown> = {}) {
194
195
  setHostCuProxy: () => {},
195
196
  setHostAppControlProxy: () => {},
196
197
  addPreactivatedSkillId: () => {},
198
+ modeSessions: mockUnownedModeSessions(),
197
199
  emitConfirmationStateChanged: () => {},
198
200
  emitActivityState: () => {},
199
201
  setTurnChannelContext: () => {},
@@ -136,6 +136,34 @@ describe("resolveImageGenCredentials", () => {
136
136
  expect(result.errorHint).toBeDefined();
137
137
  expect(result.errorHint).toContain("OpenAI API key");
138
138
  });
139
+
140
+ test("returns direct credentials for openrouter when key is present", async () => {
141
+ mockProviderKey = "openrouter-api-key";
142
+
143
+ const result = await resolveImageGenCredentials({
144
+ provider: "openrouter",
145
+ managed: false,
146
+ });
147
+
148
+ expect(result.errorHint).toBeUndefined();
149
+ expect(result.credentials).toEqual({
150
+ type: "direct",
151
+ apiKey: "openrouter-api-key",
152
+ });
153
+ });
154
+
155
+ test("returns errorHint mentioning 'OpenRouter API key' when no key is set", async () => {
156
+ mockProviderKey = undefined;
157
+
158
+ const result = await resolveImageGenCredentials({
159
+ provider: "openrouter",
160
+ managed: false,
161
+ });
162
+
163
+ expect(result.credentials).toBeUndefined();
164
+ expect(result.errorHint).toBeDefined();
165
+ expect(result.errorHint).toContain("OpenRouter API key");
166
+ });
139
167
  });
140
168
  });
141
169
 
@@ -173,6 +201,27 @@ describe("resolveImageGenRouting", () => {
173
201
  ).toEqual({ backendProvider: "openai", managed: false });
174
202
  });
175
203
 
204
+ test("OpenRouter keeps ownership for google/ and openai/ slugs", () => {
205
+ expect(
206
+ resolveImageGenRouting({
207
+ provider: "openrouter",
208
+ model: "google/gemini-3.1-flash-image-preview",
209
+ }),
210
+ ).toEqual({ backendProvider: "openrouter", managed: false });
211
+ expect(
212
+ resolveImageGenRouting(
213
+ { provider: "openrouter", model: "google/gemini-3.1-flash-image-preview" },
214
+ "openai/gpt-image-2",
215
+ ),
216
+ ).toEqual({ backendProvider: "openrouter", managed: false });
217
+ expect(
218
+ resolveImageGenRouting(
219
+ { provider: "openrouter", model: "google/gemini-3.1-flash-image-preview" },
220
+ "gpt-image-2",
221
+ ),
222
+ ).toEqual({ backendProvider: "openrouter", managed: false });
223
+ });
224
+
176
225
  test("vellum with an unavailable platform is a hard error, not a BYOK fallback", async () => {
177
226
  // Billing rule: an explicit vellum choice never silently spends a stored
178
227
  // provider key.
@@ -17,8 +17,10 @@ interface GenerateCall {
17
17
 
18
18
  let geminiCalls: GenerateCall[] = [];
19
19
  let openaiCalls: GenerateCall[] = [];
20
+ let openrouterCalls: GenerateCall[] = [];
20
21
  let geminiErrorCalls: unknown[] = [];
21
22
  let openaiErrorCalls: unknown[] = [];
23
+ let openrouterErrorCalls: unknown[] = [];
22
24
 
23
25
  let geminiResult: ImageGenerationResult = {
24
26
  images: [],
@@ -28,6 +30,10 @@ let openaiResult: ImageGenerationResult = {
28
30
  images: [],
29
31
  resolvedModel: "openai-mock",
30
32
  };
33
+ let openrouterResult: ImageGenerationResult = {
34
+ images: [],
35
+ resolvedModel: "openrouter-mock",
36
+ };
31
37
 
32
38
  // ---------------------------------------------------------------------------
33
39
  // Mock underlying services — must run before the module under test is imported
@@ -61,6 +67,20 @@ mock.module("../media/openai-image-service.js", () => ({
61
67
  },
62
68
  }));
63
69
 
70
+ mock.module("../media/openrouter-image-service.js", () => ({
71
+ generateImageOpenRouter: async (
72
+ credentials: ImageGenCredentials,
73
+ request: ImageGenerationRequest,
74
+ ): Promise<ImageGenerationResult> => {
75
+ openrouterCalls.push({ credentials, request });
76
+ return openrouterResult;
77
+ },
78
+ mapOpenRouterError: (error: unknown): string => {
79
+ openrouterErrorCalls.push(error);
80
+ return "openrouter-mapped";
81
+ },
82
+ }));
83
+
64
84
  // Import after mocking
65
85
  import {
66
86
  generateImage,
@@ -90,8 +110,10 @@ describe("image-service dispatcher", () => {
90
110
  beforeEach(() => {
91
111
  geminiCalls = [];
92
112
  openaiCalls = [];
113
+ openrouterCalls = [];
93
114
  geminiErrorCalls = [];
94
115
  openaiErrorCalls = [];
116
+ openrouterErrorCalls = [];
95
117
  geminiResult = {
96
118
  images: [{ mimeType: "image/png", dataBase64: "gemini-bytes" }],
97
119
  resolvedModel: "gemini-3.1-flash-image-preview",
@@ -100,6 +122,10 @@ describe("image-service dispatcher", () => {
100
122
  images: [{ mimeType: "image/png", dataBase64: "openai-bytes" }],
101
123
  resolvedModel: "gpt-image-2",
102
124
  };
125
+ openrouterResult = {
126
+ images: [{ mimeType: "image/png", dataBase64: "openrouter-bytes" }],
127
+ resolvedModel: "google/gemini-3.1-flash-image-preview",
128
+ };
103
129
  });
104
130
 
105
131
  test("generateImage('gemini', ...) delegates to the Gemini implementation", async () => {
@@ -131,6 +157,17 @@ describe("image-service dispatcher", () => {
131
157
  expect(mapped).toBe("gemini-mapped");
132
158
  });
133
159
 
160
+ test("generateImage('openrouter', ...) delegates to the OpenRouter implementation", async () => {
161
+ const result = await generateImage("openrouter", directCreds, request);
162
+
163
+ expect(openrouterCalls).toHaveLength(1);
164
+ expect(openrouterCalls[0]?.credentials).toBe(directCreds);
165
+ expect(openrouterCalls[0]?.request).toBe(request);
166
+ expect(geminiCalls).toHaveLength(0);
167
+ expect(openaiCalls).toHaveLength(0);
168
+ expect(result).toEqual(openrouterResult);
169
+ });
170
+
134
171
  test("mapImageGenError('openai', err) delegates to mapOpenAIError", () => {
135
172
  const err = new Error("kapow");
136
173
  const mapped = mapImageGenError("openai", err);
@@ -139,6 +176,14 @@ describe("image-service dispatcher", () => {
139
176
  expect(geminiErrorCalls).toEqual([]);
140
177
  expect(mapped).toBe("openai-mapped");
141
178
  });
179
+
180
+ test("mapImageGenError('openrouter', err) delegates to mapOpenRouterError", () => {
181
+ const err = new Error("or-boom");
182
+ const mapped = mapImageGenError("openrouter", err);
183
+
184
+ expect(openrouterErrorCalls).toEqual([err]);
185
+ expect(mapped).toBe("openrouter-mapped");
186
+ });
142
187
  });
143
188
 
144
189
  describe("providerForModel", () => {