@vellumai/assistant 0.12.3-staging.1 → 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 (560) hide show
  1. package/ARCHITECTURE.md +43 -29
  2. package/Dockerfile +63 -0
  3. package/docs/architecture/security.md +10 -5
  4. package/docs/architecture/turn-actor.md +2 -0
  5. package/docs/desktop-browser-cli.md +45 -4
  6. package/docs/live-voice-task-outcomes.md +100 -0
  7. package/docs/mode-sessions.md +183 -0
  8. package/docs/voice-input-diagnostics.md +58 -0
  9. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  10. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  15. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
  16. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
  17. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
  18. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
  19. package/node_modules/@vellumai/service-contracts/package.json +2 -1
  20. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  22. package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
  23. package/openapi.yaml +665 -14
  24. package/package.json +1 -1
  25. package/scripts/smoke-desktop-browser-cli.ts +10 -26
  26. package/scripts/smoke-desktop-window-theme.ts +2 -3
  27. package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
  28. package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
  29. package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
  30. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  31. package/src/__tests__/answered-question-persistence.test.ts +2 -0
  32. package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
  33. package/src/__tests__/approval-message-composer.test.ts +1 -118
  34. package/src/__tests__/approval-routes-http.test.ts +3 -0
  35. package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
  36. package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
  37. package/src/__tests__/assistant-event-targeting.test.ts +186 -0
  38. package/src/__tests__/assistant-stream-state.test.ts +29 -9
  39. package/src/__tests__/browser-fill-credential.test.ts +11 -22
  40. package/src/__tests__/btw-routes.test.ts +4 -1
  41. package/src/__tests__/build-persisted-content.test.ts +1 -1
  42. package/src/__tests__/call-conversation-messages.test.ts +5 -0
  43. package/src/__tests__/call-recovery.test.ts +7 -2
  44. package/src/__tests__/call-routes-http.test.ts +12 -0
  45. package/src/__tests__/call-store.test.ts +7 -2
  46. package/src/__tests__/channel-retry-sweep.test.ts +9 -0
  47. package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
  48. package/src/__tests__/checker.test.ts +11 -3
  49. package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
  50. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
  51. package/src/__tests__/computer-use-tools.test.ts +1 -233
  52. package/src/__tests__/config-model-image-provider.test.ts +39 -0
  53. package/src/__tests__/config-schema.test.ts +16 -0
  54. package/src/__tests__/config-watcher.test.ts +24 -0
  55. package/src/__tests__/context-search-agent-runner.test.ts +260 -1
  56. package/src/__tests__/context-search-fanout.test.ts +1 -1
  57. package/src/__tests__/context-search-types.test.ts +7 -7
  58. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
  59. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
  60. package/src/__tests__/conversation-agent-loop.test.ts +546 -16
  61. package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
  62. package/src/__tests__/conversation-evictor.test.ts +62 -37
  63. package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
  64. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
  65. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  66. package/src/__tests__/conversation-retry-route.test.ts +22 -0
  67. package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
  68. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
  69. package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
  70. package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  72. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
  73. package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
  74. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
  75. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
  76. package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
  77. package/src/__tests__/conversation-undo.test.ts +61 -0
  78. package/src/__tests__/credential-execution-client.test.ts +6 -6
  79. package/src/__tests__/credential-prompt-route.test.ts +35 -3
  80. package/src/__tests__/cu-unified-flow.test.ts +82 -1
  81. package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
  82. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
  83. package/src/__tests__/dm-backfill.test.ts +91 -1
  84. package/src/__tests__/document-create-route.test.ts +88 -0
  85. package/src/__tests__/document-sync-tags.test.ts +27 -0
  86. package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
  87. package/src/__tests__/events-tail-route.test.ts +25 -0
  88. package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
  89. package/src/__tests__/guardian-action-sweep.test.ts +16 -0
  90. package/src/__tests__/guardian-dispatch.test.ts +13 -0
  91. package/src/__tests__/headless-browser-interactions.test.ts +64 -20
  92. package/src/__tests__/heartbeat-service.test.ts +2 -59
  93. package/src/__tests__/helpers/mock-conversation.ts +21 -0
  94. package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
  95. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
  96. package/src/__tests__/host-cu-proxy.test.ts +46 -2
  97. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  98. package/src/__tests__/image-credentials.test.ts +49 -0
  99. package/src/__tests__/image-service-dispatcher.test.ts +45 -0
  100. package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
  101. package/src/__tests__/interrupt-on-send.test.ts +57 -43
  102. package/src/__tests__/interrupt-turn-note.test.ts +5 -6
  103. package/src/__tests__/llm-catalog-parity.test.ts +2 -2
  104. package/src/__tests__/media-generate-image.test.ts +49 -1
  105. package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
  106. package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
  107. package/src/__tests__/mode-session-history.test.ts +245 -0
  108. package/src/__tests__/mode-session-recovery.test.ts +222 -0
  109. package/src/__tests__/mode-session-wire.test.ts +141 -0
  110. package/src/__tests__/notification-guardian-path.test.ts +7 -0
  111. package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
  112. package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +12 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  115. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  116. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +5 -0
  118. package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
  119. package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -0
  120. package/src/__tests__/process-message-display-content.test.ts +56 -0
  121. package/src/__tests__/reaction-persistence.test.ts +5 -0
  122. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
  123. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  124. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
  125. package/src/__tests__/secret-ingress-http.test.ts +2 -0
  126. package/src/__tests__/secure-keys.test.ts +43 -1
  127. package/src/__tests__/send-endpoint-busy.test.ts +163 -1
  128. package/src/__tests__/server-history-render.test.ts +91 -1
  129. package/src/__tests__/settings-routes.test.ts +50 -1
  130. package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
  132. package/src/__tests__/subagent-manager-notify.test.ts +1 -2
  133. package/src/__tests__/subagent-notify-parent.test.ts +146 -39
  134. package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
  135. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
  136. package/src/__tests__/thread-backfill.test.ts +53 -0
  137. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  138. package/src/__tests__/tool-result-metadata-plumbing.test.ts +3 -1
  139. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  140. package/src/__tests__/twilio-routes.test.ts +1 -0
  141. package/src/__tests__/web-search-backend-failure.test.ts +1 -1
  142. package/src/activation/progress-store.test.ts +12 -7
  143. package/src/agent/loop.ts +99 -41
  144. package/src/api/events/assistant-turn-start.ts +3 -0
  145. package/src/api/events/generation-handoff.ts +2 -0
  146. package/src/api/events/message-complete.ts +2 -0
  147. package/src/api/events/notification-conversation-created.ts +14 -16
  148. package/src/api/events/notification-intent.ts +4 -2
  149. package/src/api/events/tool-result.ts +85 -0
  150. package/src/api/events/tool-use-start.ts +3 -0
  151. package/src/api/events/user-message-echo.ts +3 -0
  152. package/src/api/index.ts +31 -6
  153. package/src/api/mode-session.test.ts +131 -0
  154. package/src/api/mode-session.ts +148 -0
  155. package/src/api/remember-tool.ts +15 -0
  156. package/src/api/responses/conversation-message.ts +6 -0
  157. package/src/approvals/AGENTS.md +4 -2
  158. package/src/browser/__tests__/operations.test.ts +29 -0
  159. package/src/browser/operation-meta.ts +2 -1
  160. package/src/browser/operations.ts +29 -0
  161. package/src/browser/virtual-desktop-target.ts +2 -8
  162. package/src/bundler/bundle-signer.ts +1 -3
  163. package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
  164. package/src/calls/__tests__/call-controller.test.ts +94 -0
  165. package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
  166. package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
  167. package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
  168. package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
  169. package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
  170. package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
  171. package/src/calls/barge-in-guard.ts +5 -0
  172. package/src/calls/call-controller.ts +196 -1
  173. package/src/calls/call-domain.ts +4 -0
  174. package/src/calls/call-store.ts +87 -2
  175. package/src/calls/guardian-action-sweep.ts +22 -8
  176. package/src/calls/media-stream-audio-transcode.ts +1 -3
  177. package/src/calls/typesafe-noul.ts +156 -0
  178. package/src/calls/voice-control-protocol.ts +6 -0
  179. package/src/calls/voice-escalation-judge.ts +145 -0
  180. package/src/calls/voice-escalation-target.ts +10 -0
  181. package/src/calls/voice-leg-coordinator.ts +121 -3
  182. package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
  183. package/src/calls/voice-session-bridge.ts +256 -112
  184. package/src/calls/voice-triage-escalate.ts +7 -0
  185. package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
  186. package/src/cli/commands/__tests__/notifications.test.ts +35 -10
  187. package/src/cli/commands/browser.help.ts +4 -5
  188. package/src/cli/commands/browser.ts +0 -1
  189. package/src/cli/commands/clients.help.ts +46 -0
  190. package/src/cli/commands/clients.ts +116 -0
  191. package/src/cli/commands/credentials.help.ts +6 -1
  192. package/src/cli/commands/notifications.help.ts +16 -2
  193. package/src/cli/commands/notifications.ts +30 -8
  194. package/src/cli/commands/roadmap.help.ts +18 -21
  195. package/src/cli/commands/roadmap.ts +4 -21
  196. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
  197. package/src/cli/lib/bundled-marketplace.json +84 -34
  198. package/src/cli/lib/plugin-marketplace.ts +8 -0
  199. package/src/cli/lib/publish-plugin.ts +1 -2
  200. package/src/config/__tests__/default-provider.test.ts +3 -1
  201. package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
  202. package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
  203. package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
  204. package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
  205. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
  206. package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
  207. package/src/config/call-site-defaults.ts +9 -0
  208. package/src/config/feature-flag-registry.json +16 -0
  209. package/src/config/profile-text-generation.test.ts +2 -2
  210. package/src/config/schemas/__tests__/tools.test.ts +31 -0
  211. package/src/config/schemas/call-site-catalog.ts +14 -0
  212. package/src/config/schemas/channels.ts +0 -18
  213. package/src/config/schemas/llm.ts +3 -1
  214. package/src/config/schemas/mcp.ts +2 -1
  215. package/src/config/schemas/memory-retrospective.ts +11 -2
  216. package/src/config/schemas/services.ts +15 -6
  217. package/src/config/schemas/tools.ts +10 -0
  218. package/src/config/session-groups-gate.ts +21 -0
  219. package/src/config/skills.ts +2 -6
  220. package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
  221. package/src/conversations/message-consolidation.ts +102 -5
  222. package/src/credential-execution/ces-connect.test.ts +69 -0
  223. package/src/credential-execution/ces-connect.ts +115 -0
  224. package/src/credential-execution/ces-runtime.ts +55 -93
  225. package/src/credential-execution/executable-discovery.ts +1 -11
  226. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
  227. package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
  228. package/src/daemon/browser-mode-session.test.ts +402 -0
  229. package/src/daemon/browser-mode-session.ts +186 -0
  230. package/src/daemon/computer-use-mode-session.test.ts +210 -0
  231. package/src/daemon/computer-use-mode-session.ts +105 -0
  232. package/src/daemon/config-watcher.ts +10 -0
  233. package/src/daemon/conversation-agent-loop-handlers.ts +58 -10
  234. package/src/daemon/conversation-agent-loop.ts +219 -25
  235. package/src/daemon/conversation-evictor.ts +5 -24
  236. package/src/daemon/conversation-history.ts +10 -0
  237. package/src/daemon/conversation-interrupt-repair.ts +4 -43
  238. package/src/daemon/conversation-interrupt.ts +7 -11
  239. package/src/daemon/conversation-messaging.ts +58 -5
  240. package/src/daemon/conversation-mode-session.test.ts +1667 -0
  241. package/src/daemon/conversation-mode-session.ts +1008 -0
  242. package/src/daemon/conversation-process.ts +5 -0
  243. package/src/daemon/conversation-runtime-assembly.ts +10 -20
  244. package/src/daemon/conversation-skill-tools.ts +1 -0
  245. package/src/daemon/conversation-store.ts +4 -4
  246. package/src/daemon/conversation-surface-snapshots.ts +135 -0
  247. package/src/daemon/conversation-surfaces.ts +113 -5
  248. package/src/daemon/conversation-tool-setup.ts +8 -3
  249. package/src/daemon/conversation.ts +143 -41
  250. package/src/daemon/handlers/config-model.ts +7 -5
  251. package/src/daemon/handlers/shared.ts +55 -16
  252. package/src/daemon/host-cu-proxy.ts +51 -22
  253. package/src/daemon/lifecycle.ts +30 -13
  254. package/src/daemon/message-protocol.ts +0 -1
  255. package/src/daemon/message-provenance.ts +28 -4
  256. package/src/daemon/mode-session-startup-recovery.ts +23 -0
  257. package/src/daemon/mode-session-tracking.ts +54 -0
  258. package/src/daemon/process-message.ts +13 -4
  259. package/src/daemon/virtual-desktop-context.ts +21 -0
  260. package/src/desktop/__tests__/fake-desktop.ts +1 -0
  261. package/src/desktop/desktop-accessibility-script.ts +88 -0
  262. package/src/desktop/desktop-accessibility.test.ts +160 -0
  263. package/src/desktop/desktop-accessibility.ts +169 -0
  264. package/src/desktop/desktop-automation-lease.test.ts +76 -68
  265. package/src/desktop/desktop-automation-lease.ts +41 -50
  266. package/src/desktop/desktop-browser-endpoint.ts +4 -0
  267. package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
  268. package/src/desktop/desktop-computer-use.test.ts +449 -0
  269. package/src/desktop/desktop-computer-use.ts +502 -0
  270. package/src/desktop/desktop-dependencies.ts +47 -293
  271. package/src/desktop/desktop-display.ts +2 -2
  272. package/src/desktop/desktop-panel-config.test.ts +13 -3
  273. package/src/desktop/desktop-panel-config.ts +109 -15
  274. package/src/desktop/desktop-session-manager.test.ts +69 -21
  275. package/src/desktop/desktop-session-manager.ts +76 -22
  276. package/src/desktop/desktop-stream-bridge.test.ts +11 -122
  277. package/src/desktop/desktop-stream-bridge.ts +6 -8
  278. package/src/desktop/desktop-window-theme.ts +5 -25
  279. package/src/desktop/virtual-desktop-feature.ts +52 -2
  280. package/src/desktop/virtual-desktop-platform.test.ts +5 -9
  281. package/src/documents/document-store.ts +23 -0
  282. package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
  283. package/src/heartbeat/heartbeat-service.ts +5 -33
  284. package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
  285. package/src/ipc/assistant-server.ts +2 -0
  286. package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
  287. package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
  288. package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
  289. package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
  290. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
  291. package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
  292. package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
  293. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
  294. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
  295. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
  296. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
  297. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
  298. package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
  299. package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
  300. package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
  301. package/src/live-voice/__tests__/session-controls.test.ts +23 -0
  302. package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
  303. package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
  304. package/src/live-voice/camera-mode-session.test.ts +265 -0
  305. package/src/live-voice/camera-mode-session.ts +277 -0
  306. package/src/live-voice/continuation-judge.ts +123 -0
  307. package/src/live-voice/live-activity-reporter.ts +115 -6
  308. package/src/live-voice/live-voice-manager.ts +36 -1
  309. package/src/live-voice/live-voice-photo.ts +88 -7
  310. package/src/live-voice/live-voice-session-manager.ts +15 -0
  311. package/src/live-voice/live-voice-session.ts +1443 -747
  312. package/src/live-voice/live-voice-tts.ts +22 -4
  313. package/src/live-voice/microphone-idle-gate.ts +56 -0
  314. package/src/live-voice/protocol.ts +171 -4
  315. package/src/live-voice/session-controls.ts +11 -3
  316. package/src/live-voice/task-outcomes.ts +131 -0
  317. package/src/live-voice/voice-input-diagnostics.ts +117 -0
  318. package/src/mcp/__tests__/manager-tool-caps.test.ts +63 -18
  319. package/src/mcp/__tests__/tool-caps.test.ts +28 -1
  320. package/src/mcp/manager.ts +3 -0
  321. package/src/mcp/tool-caps.ts +12 -4
  322. package/src/media/__tests__/image-models.test.ts +58 -0
  323. package/src/media/image-credentials.ts +5 -0
  324. package/src/media/image-models.ts +52 -0
  325. package/src/media/image-service.ts +8 -0
  326. package/src/media/openrouter-image-service.test.ts +133 -0
  327. package/src/media/openrouter-image-service.ts +153 -0
  328. package/src/media/types.ts +1 -1
  329. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
  330. package/src/messaging/providers/discord/send.ts +4 -17
  331. package/src/messaging/providers/discord/transport.ts +2 -2
  332. package/src/messaging/providers/outlook/client.ts +16 -22
  333. package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
  334. package/src/messaging/providers/telegram-bot/send.ts +10 -7
  335. package/src/messaging/providers/telegram-bot/transport.ts +3 -10
  336. package/src/messaging/providers/whatsapp/send.test.ts +169 -4
  337. package/src/monitoring/__tests__/control.test.ts +17 -5
  338. package/src/monitoring/control.ts +15 -5
  339. package/src/notifications/AGENTS.md +1 -1
  340. package/src/notifications/README.md +83 -99
  341. package/src/notifications/__tests__/broadcaster.test.ts +1 -1
  342. package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
  343. package/src/notifications/__tests__/copy-composer.test.ts +1 -1
  344. package/src/notifications/__tests__/decision-engine.test.ts +79 -2
  345. package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
  346. package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
  347. package/src/notifications/__tests__/notification-utils.test.ts +18 -0
  348. package/src/notifications/adapters/macos.ts +37 -46
  349. package/src/notifications/broadcaster.ts +5 -6
  350. package/src/notifications/channel-allowlist.ts +44 -0
  351. package/src/notifications/copy-composer.ts +0 -23
  352. package/src/notifications/decision-engine.ts +24 -14
  353. package/src/notifications/deterministic-checks.ts +1 -1
  354. package/src/notifications/emit-signal.ts +123 -37
  355. package/src/notifications/home-feed-side-effect.ts +9 -7
  356. package/src/notifications/notification-utils.ts +19 -0
  357. package/src/notifications/signal.ts +4 -19
  358. package/src/notifications/types.ts +15 -0
  359. package/src/oauth/connection-resolver.ts +1 -2
  360. package/src/oauth/oauth-store.ts +1 -3
  361. package/src/oauth/provider-serializer.ts +7 -0
  362. package/src/oauth/seed-providers.ts +30 -0
  363. package/src/onboarding/onboarding-events-store.ts +99 -12
  364. package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
  365. package/src/persistence/client-connection-events-store.ts +377 -0
  366. package/src/persistence/conversation-crud.ts +55 -6
  367. package/src/persistence/conversation-mode-sessions.test.ts +424 -0
  368. package/src/persistence/conversation-mode-sessions.ts +460 -0
  369. package/src/persistence/conversation-queries.ts +6 -19
  370. package/src/persistence/llm-usage-store.ts +3 -9
  371. package/src/persistence/message-metadata.ts +25 -0
  372. package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
  373. package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
  374. package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
  375. package/src/persistence/schema/conversations.ts +67 -0
  376. package/src/persistence/schema/infrastructure.ts +29 -0
  377. package/src/persistence/steps.ts +17 -0
  378. package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
  379. package/src/plugin-api/effective-context-window.ts +33 -0
  380. package/src/plugin-api/index.ts +24 -5
  381. package/src/plugin-api/plugin-name-env.ts +125 -0
  382. package/src/plugin-api/resolve-credential.ts +79 -20
  383. package/src/plugin-api/vision-support.test.ts +22 -0
  384. package/src/plugin-api/vision-support.ts +17 -5
  385. package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
  386. package/src/plugins/active-plugin-env.ts +51 -0
  387. package/src/plugins/defaults/index.ts +24 -0
  388. package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
  389. package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
  390. package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
  391. package/src/plugins/defaults/memory/context-search/agent-runner.ts +78 -22
  392. package/src/plugins/defaults/memory/context-search/format.ts +51 -7
  393. package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
  394. package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
  395. package/src/plugins/defaults/memory/context-search/search.ts +8 -14
  396. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
  397. package/src/plugins/defaults/memory/context-search/types.ts +1 -11
  398. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
  399. package/src/plugins/defaults/memory/graph/tool-handlers.ts +12 -21
  400. package/src/plugins/defaults/memory/graph/tools.ts +35 -64
  401. package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
  402. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +5 -0
  403. package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
  404. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +2 -2
  405. package/src/plugins/defaults/memory/tools.test.ts +124 -9
  406. package/src/plugins/defaults/memory/tools.ts +38 -11
  407. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +165 -27
  408. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
  409. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +46 -27
  410. package/src/plugins/defaults/memory/v3/orchestrate.ts +78 -38
  411. package/src/plugins/defaults/memory/v3/pool-log-store.ts +65 -79
  412. package/src/plugins/defaults/memory/v3/pool-select.test.ts +14 -5
  413. package/src/plugins/defaults/memory/v3/pool-select.ts +683 -44
  414. package/src/plugins/defaults/memory/worker-control.ts +15 -5
  415. package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
  416. package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
  417. package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
  418. package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
  419. package/src/plugins/defaults/task-progress/package.json +14 -0
  420. package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
  421. package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
  422. package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
  423. package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
  424. package/src/providers/anthropic/client.ts +10 -1
  425. package/src/providers/anthropic-gateway-shared.ts +6 -0
  426. package/src/providers/fetch-provider-catalog.ts +11 -0
  427. package/src/providers/gemini/client.ts +25 -8
  428. package/src/providers/inference/adapter-factory.ts +1 -1
  429. package/src/providers/jev/client.test.ts +10 -0
  430. package/src/providers/jev/client.ts +20 -4
  431. package/src/providers/model-catalog.ts +4 -3
  432. package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
  433. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
  434. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
  435. package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
  436. package/src/providers/openai/chat-completions-provider.ts +100 -29
  437. package/src/providers/openai/google-thought-signature.ts +0 -6
  438. package/src/providers/openai/responses-provider.ts +23 -1
  439. package/src/providers/ratelimit.ts +1 -3
  440. package/src/providers/search-provider-catalog.ts +24 -0
  441. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
  442. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
  443. package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
  444. package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
  445. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
  446. package/src/providers/types.ts +5 -0
  447. package/src/routes/control.ts +7 -5
  448. package/src/runtime/AGENTS.md +4 -2
  449. package/src/runtime/actor-trust-resolver.ts +3 -3
  450. package/src/runtime/agent-wake.ts +1 -1
  451. package/src/runtime/approval-message-composer.ts +3 -69
  452. package/src/runtime/assistant-event-hub.ts +73 -75
  453. package/src/runtime/assistant-event-publish-options.ts +7 -0
  454. package/src/runtime/assistant-event-targeting.ts +79 -0
  455. package/src/runtime/assistant-stream-state.ts +13 -100
  456. package/src/runtime/background-job-runner.ts +5 -5
  457. package/src/runtime/channel-retry-sweep.ts +1 -0
  458. package/src/runtime/guardian-action-message-composer.ts +8 -161
  459. package/src/runtime/guardian-reply-router.ts +28 -32
  460. package/src/runtime/http-types.ts +6 -0
  461. package/src/runtime/message-composer-types.ts +3 -61
  462. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
  463. package/src/runtime/migrations/vbundle-builder.ts +24 -1
  464. package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
  465. package/src/runtime/migrations/vbundle-import-policy.ts +10 -0
  466. package/src/runtime/migrations/vbundle-importer.ts +4 -1
  467. package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
  468. package/src/runtime/migrations/vbundle-validator.ts +1 -0
  469. package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
  470. package/src/runtime/pending-interactions.ts +29 -0
  471. package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
  472. package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
  473. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +1 -1
  474. package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
  475. package/src/runtime/routes/browser-context.ts +31 -11
  476. package/src/runtime/routes/browser-routes.ts +43 -4
  477. package/src/runtime/routes/btw-routes.ts +9 -4
  478. package/src/runtime/routes/client-routes.ts +108 -0
  479. package/src/runtime/routes/conversation-management-routes.ts +4 -1
  480. package/src/runtime/routes/conversation-routes.ts +113 -27
  481. package/src/runtime/routes/credential-prompt-routes.ts +4 -3
  482. package/src/runtime/routes/credential-routes.ts +1 -2
  483. package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
  484. package/src/runtime/routes/desktop-setup-routes.ts +4 -6
  485. package/src/runtime/routes/documents-routes.ts +51 -0
  486. package/src/runtime/routes/email-routes.ts +3 -7
  487. package/src/runtime/routes/events-routes.ts +75 -54
  488. package/src/runtime/routes/identity-routes.ts +1 -5
  489. package/src/runtime/routes/image-generation-routes.ts +8 -14
  490. package/src/runtime/routes/inbound-message-handler.ts +35 -38
  491. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
  492. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
  493. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +38 -9
  494. package/src/runtime/routes/inbound-stages/background-dispatch.ts +2 -5
  495. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
  496. package/src/runtime/routes/llm-context-normalization.ts +1 -7
  497. package/src/runtime/routes/migration-routes.ts +73 -2
  498. package/src/runtime/routes/notification-routes.ts +10 -0
  499. package/src/runtime/routes/oauth-providers.ts +7 -0
  500. package/src/runtime/routes/plugins-routes.ts +6 -0
  501. package/src/runtime/routes/roadmap-routes.ts +47 -8
  502. package/src/runtime/routes/secret-routes.ts +1 -1
  503. package/src/runtime/routes/settings-routes.ts +10 -4
  504. package/src/runtime/routes/watcher-routes.ts +1 -3
  505. package/src/runtime/trust-class.test.ts +7 -6
  506. package/src/runtime/trust-class.ts +9 -17
  507. package/src/schedule/__tests__/worker-control.test.ts +36 -5
  508. package/src/schedule/worker-control.ts +15 -5
  509. package/src/security/secure-keys.ts +53 -103
  510. package/src/skills/clawhub.ts +3 -8
  511. package/src/skills/managed-store.ts +1 -5
  512. package/src/stt/types.ts +4 -0
  513. package/src/subagent/notify.ts +54 -34
  514. package/src/subagent/parent-notification.ts +6 -0
  515. package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
  516. package/src/telemetry/phone-call-funnel.ts +144 -0
  517. package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
  518. package/src/tools/ask-question/ask-question-tool.ts +6 -4
  519. package/src/tools/browser/browser-execution.ts +22 -59
  520. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
  521. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
  522. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
  523. package/src/tools/client-os.ts +15 -0
  524. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
  525. package/src/tools/computer-use/target.ts +42 -0
  526. package/src/tools/document/document-tool.ts +13 -18
  527. package/src/tools/execution-target.ts +14 -16
  528. package/src/tools/host-terminal/host-shell.ts +4 -1
  529. package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
  530. package/src/tools/network/__tests__/web-search.test.ts +203 -0
  531. package/src/tools/network/web-fetch.ts +274 -7
  532. package/src/tools/network/web-search.ts +468 -6
  533. package/src/tools/permission-checker.ts +3 -3
  534. package/src/tools/policy-context.ts +3 -1
  535. package/src/tools/skills/load.ts +2 -1
  536. package/src/tools/skills/sandbox-runner.ts +11 -2
  537. package/src/tools/skills/skill-script-runner.ts +7 -0
  538. package/src/tools/skills/skill-tool-factory.ts +15 -1
  539. package/src/tools/terminal/__tests__/safe-env.test.ts +11 -0
  540. package/src/tools/terminal/safe-env.ts +4 -0
  541. package/src/tools/terminal/shell.ts +2 -0
  542. package/src/tools/tool-approval-handler.ts +5 -1
  543. package/src/tools/tool-types.ts +3 -3
  544. package/src/tools/types.ts +8 -2
  545. package/src/util/__tests__/worker-process-command.test.ts +5 -4
  546. package/src/util/abort-reasons.ts +18 -26
  547. package/src/util/canonicalize-identity.test.ts +21 -0
  548. package/src/util/canonicalize-identity.ts +16 -0
  549. package/src/util/errors.ts +13 -3
  550. package/src/util/provider-error-patterns.ts +3 -0
  551. package/src/util/worker-process.ts +77 -35
  552. package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
  553. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
  554. package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
  555. package/src/workspace/migrations/registry.ts +2 -0
  556. package/src/__tests__/computer-use-window-schema.test.ts +0 -24
  557. package/src/api/events/heartbeat-alert.ts +0 -20
  558. package/src/daemon/message-types/web-activity.ts +0 -65
  559. package/src/desktop/desktop-dependencies.test.ts +0 -131
  560. package/src/tools/computer-use/definitions.ts +0 -607
@@ -9,6 +9,7 @@ import { z } from "zod";
9
9
 
10
10
  import type { HostProxyCapability } from "../../channels/types.js";
11
11
  import { isHttpAuthDisabled } from "../../config/env.js";
12
+ import { listClientConnectionHistory } from "../../persistence/client-connection-events-store.js";
12
13
  import { datesToISO } from "../../util/json.js";
13
14
  import { getLogger } from "../../util/logger.js";
14
15
  import {
@@ -112,6 +113,8 @@ export const ROUTES: RouteDefinition[] = [
112
113
  interfaceId: c.interfaceId,
113
114
  capabilities: c.capabilities,
114
115
  machineName: c.machineName,
116
+ clientVersion: c.clientVersion,
117
+ sseWatchdog: c.sseWatchdog,
115
118
  connectedAt: c.connectedAt,
116
119
  lastActiveAt: c.lastActiveAt,
117
120
  degraded: isClientDegraded(
@@ -151,6 +154,79 @@ export const ROUTES: RouteDefinition[] = [
151
154
  return { disconnected: count };
152
155
  },
153
156
  },
157
+ {
158
+ operationId: "list_client_history",
159
+ endpoint: "clients/history",
160
+ method: "GET",
161
+ policy: {
162
+ requiredScopes: ["settings.read"],
163
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
164
+ },
165
+ summary: "List client connection history",
166
+ description:
167
+ "Return persisted client subscribe/dispose events and coalesced sessions. Reconnects shorter than 60 seconds are flaps, not outages.",
168
+ tags: ["clients"],
169
+ queryParams: [
170
+ {
171
+ name: "clientId",
172
+ type: "string",
173
+ required: false,
174
+ description: "Restrict history to one client UUID.",
175
+ },
176
+ {
177
+ name: "interfaceId",
178
+ type: "string",
179
+ required: false,
180
+ description: "Restrict history to one interface (e.g. chrome-extension).",
181
+ },
182
+ {
183
+ name: "since",
184
+ type: "string",
185
+ required: false,
186
+ description:
187
+ "ISO-8601 or epoch-ms lower bound. Sessions already open at this time are included.",
188
+ },
189
+ {
190
+ name: "limit",
191
+ type: "string",
192
+ required: false,
193
+ description: "Max coalesced sessions to return (1-500, default 50).",
194
+ },
195
+ ],
196
+ responseBody: z.object({
197
+ sessions: z.array(z.object({}).passthrough()),
198
+ events: z.array(z.object({}).passthrough()),
199
+ }),
200
+ handler: ({ queryParams, headers }) => {
201
+ const since = parseHistorySince(queryParams?.since);
202
+ const limit = parseHistoryLimit(queryParams?.limit);
203
+ const callerPrincipalId = headers?.["x-vellum-actor-principal-id"];
204
+ const history = listClientConnectionHistory({
205
+ clientId: queryParams?.clientId?.trim() || undefined,
206
+ interfaceId: queryParams?.interfaceId?.trim() || undefined,
207
+ actorPrincipalId: isHttpAuthDisabled()
208
+ ? undefined
209
+ : callerPrincipalId || "__none__",
210
+ since,
211
+ limit,
212
+ });
213
+
214
+ return {
215
+ sessions: history.sessions.map((session) => ({
216
+ ...session,
217
+ startedAt: new Date(session.startedAt).toISOString(),
218
+ endedAt:
219
+ session.endedAt == null
220
+ ? null
221
+ : new Date(session.endedAt).toISOString(),
222
+ })),
223
+ events: history.events.map((event) => ({
224
+ ...event,
225
+ occurredAt: new Date(event.occurredAt).toISOString(),
226
+ })),
227
+ };
228
+ },
229
+ },
154
230
  {
155
231
  operationId: "report_client_presence",
156
232
  endpoint: "clients/presence",
@@ -342,3 +418,35 @@ export const ROUTES: RouteDefinition[] = [
342
418
  },
343
419
  },
344
420
  ];
421
+
422
+ function parseHistorySince(raw: string | undefined): number | undefined {
423
+ if (!raw?.trim()) {
424
+ return undefined;
425
+ }
426
+ const trimmed = raw.trim();
427
+ if (/^\d+$/.test(trimmed)) {
428
+ const epoch = Number(trimmed);
429
+ if (!Number.isFinite(epoch) || epoch < 0) {
430
+ throw new BadRequestError("since must be a non-negative epoch-ms value");
431
+ }
432
+ return epoch;
433
+ }
434
+ const parsed = Date.parse(trimmed);
435
+ if (Number.isNaN(parsed)) {
436
+ throw new BadRequestError(
437
+ "since must be an ISO-8601 timestamp or epoch milliseconds",
438
+ );
439
+ }
440
+ return parsed;
441
+ }
442
+
443
+ function parseHistoryLimit(raw: string | undefined): number | undefined {
444
+ if (!raw?.trim()) {
445
+ return undefined;
446
+ }
447
+ const parsed = Number(raw.trim());
448
+ if (!Number.isInteger(parsed) || parsed < 1) {
449
+ throw new BadRequestError("limit must be a positive integer");
450
+ }
451
+ return parsed;
452
+ }
@@ -19,7 +19,6 @@
19
19
  * POST /v1/conversations/:id/retry — retry the last assistant turn
20
20
  * POST /v1/conversations/reorder — reorder / pin conversations
21
21
  */
22
-
23
22
  import { v7 as uuidv7 } from "uuid";
24
23
  import { z } from "zod";
25
24
 
@@ -43,6 +42,7 @@ import {
43
42
  undoLastMessage,
44
43
  } from "../../daemon/handlers/conversations.js";
45
44
  import { normalizeConversationType } from "../../daemon/message-types/shared.js";
45
+ import { bestEffortModeSessionTracking } from "../../daemon/mode-session-tracking.js";
46
46
  import { stripConversationIds } from "../../home/feed-writer.js";
47
47
  import {
48
48
  archiveConversation,
@@ -732,6 +732,9 @@ async function handleRetryLastAssistantTurn({
732
732
  if (!discarded) {
733
733
  throw new UnprocessableEntityError("No user message to retry from");
734
734
  }
735
+ bestEffortModeSessionTracking("conversation wait invalidation", () =>
736
+ conversation.modeSessions.invalidateAllStructuralWaits(),
737
+ );
735
738
  } catch (err) {
736
739
  if (conversation.abortController === abortController) {
737
740
  conversation.abortController = null;
@@ -18,6 +18,7 @@ import {
18
18
  createUserMessage,
19
19
  } from "../../agent/message-types.js";
20
20
  import type { AssistantEvent } from "../../api/index.js";
21
+ import { ModeSessionDescriptorSchema } from "../../api/mode-session.js";
21
22
  import {
22
23
  BackgroundToolCompletionSchema,
23
24
  type ConversationContentBlock,
@@ -44,10 +45,8 @@ import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-fl
44
45
  import { getUserSelectableProfilesForProvider } from "../../config/default-profile-catalog.js";
45
46
  import { isHttpAuthDisabled } from "../../config/env.js";
46
47
  import { getConfig } from "../../config/loader.js";
47
- import {
48
- mergeConsecutiveAssistantMessages,
49
- mergeToolResultsIntoAssistantMessages,
50
- } from "../../conversations/message-consolidation.js";
48
+ import { isModeSessionRecoveryHealthy } from "../../config/session-groups-gate.js";
49
+ import { consolidateMessageRows } from "../../conversations/message-consolidation.js";
51
50
  import { resolveTurnCommitWaitMs } from "../../daemon/abort-watchdog.js";
52
51
  import { createApprovalConversationGenerator } from "../../daemon/approval-generators.js";
53
52
  import type { Conversation } from "../../daemon/conversation.js";
@@ -140,6 +139,7 @@ import {
140
139
  getConversationByKey,
141
140
  getOrCreateConversation,
142
141
  } from "../../persistence/conversation-key-store.js";
142
+ import { listConversationModeSessionsByIds } from "../../persistence/conversation-mode-sessions.js";
143
143
  import { searchConversations } from "../../persistence/conversation-queries.js";
144
144
  import {
145
145
  computerUseScreenshotAttachmentIdsFromMetadata,
@@ -147,6 +147,10 @@ import {
147
147
  messageMetadataIsAmbientSightKeep,
148
148
  } from "../../persistence/conversation-types.js";
149
149
  import { linkRequestLogsToMessage } from "../../persistence/llm-request-log-store.js";
150
+ import {
151
+ mergeMessageMetadata,
152
+ readModeSessionMetadata,
153
+ } from "../../persistence/message-metadata.js";
150
154
  import { assistantTextVisibilityOf } from "../../persistence/user-facing-content.js";
151
155
  import { MEMORY_RETROSPECTIVE_FORK_SOURCE } from "../../plugins/defaults/memory/memory-retrospective-constants.js";
152
156
  import { normalizeOnboardingContext } from "../../prompts/normalize-onboarding.js";
@@ -881,10 +885,31 @@ function displayRowFilter(conversationId: string): (m: MessageRow) => boolean {
881
885
  function consolidateDisplayTurns(rows: MessageRow[]): {
882
886
  messages: MessageRow[];
883
887
  mergedIdMap: Map<string, string[]>;
888
+ modeSessionActivityMap: Map<string, { firstAt: number; lastAt: number }>;
884
889
  } {
885
- return mergeConsecutiveAssistantMessages(
886
- mergeToolResultsIntoAssistantMessages(rows),
887
- );
890
+ return consolidateMessageRows(rows);
891
+ }
892
+
893
+ const MAX_REQUESTED_MODE_SESSION_IDS = 100;
894
+ const MAX_MODE_SESSION_ID_LENGTH = 200;
895
+
896
+ function parseRequestedModeSessionIds(raw: unknown): string[] {
897
+ if (raw === undefined) {
898
+ return [];
899
+ }
900
+ if (typeof raw !== "string") {
901
+ throw new BadRequestError(
902
+ "modeSessionIds must be a comma-separated string",
903
+ );
904
+ }
905
+ const ids = [...new Set(raw.split(",").map((id) => id.trim()))];
906
+ if (
907
+ ids.length > MAX_REQUESTED_MODE_SESSION_IDS ||
908
+ ids.some((id) => id.length === 0 || id.length > MAX_MODE_SESSION_ID_LENGTH)
909
+ ) {
910
+ throw new BadRequestError("modeSessionIds contains an invalid session ID");
911
+ }
912
+ return ids;
888
913
  }
889
914
 
890
915
  export async function handleListMessages({
@@ -920,6 +945,9 @@ export async function handleListMessages({
920
945
  const beforeTimestampRaw = queryParams?.beforeTimestamp;
921
946
  const limitRaw = queryParams?.limit;
922
947
  const pageRaw = queryParams?.page;
948
+ const requestedModeSessionIds = parseRequestedModeSessionIds(
949
+ queryParams?.modeSessionIds,
950
+ );
923
951
 
924
952
  // Validate: reject NaN values with 400
925
953
  if (beforeTimestampRaw != null && isNaN(Number(beforeTimestampRaw))) {
@@ -1023,8 +1051,47 @@ export async function handleListMessages({
1023
1051
  // consolidation is deferred to compaction for prefix-cache stability.
1024
1052
  // Merge here at query time so renderHistoryContent produces the same
1025
1053
  // contentOrder shape as streaming (consecutive tool refs grouped together).
1026
- const { messages: consolidatedMessages, mergedIdMap } =
1027
- consolidateDisplayTurns(rawMessages);
1054
+ const pageModeSessionIds = rawMessages.flatMap((message) => {
1055
+ const owner = readModeSessionMetadata(message.metadata);
1056
+ return owner ? [owner.id] : [];
1057
+ });
1058
+ const summaries = listConversationModeSessionsByIds(resolvedConversationId, [
1059
+ ...new Set([...pageModeSessionIds, ...requestedModeSessionIds]),
1060
+ ]);
1061
+ const validOwners = new Map(
1062
+ summaries.map((summary) => [summary.id, summary.mode]),
1063
+ );
1064
+ const projectionMessages = rawMessages.map((message) => {
1065
+ const owner = readModeSessionMetadata(message.metadata);
1066
+ if (!owner || validOwners.get(owner.id) === owner.mode) {
1067
+ return message;
1068
+ }
1069
+ return {
1070
+ ...message,
1071
+ metadata: mergeMessageMetadata(message.metadata, {
1072
+ modeSession: undefined,
1073
+ }),
1074
+ };
1075
+ });
1076
+ const {
1077
+ messages: consolidatedMessages,
1078
+ mergedIdMap,
1079
+ modeSessionActivityMap,
1080
+ } = consolidateDisplayTurns(projectionMessages);
1081
+ const liveConversation = findConversation(resolvedConversationId);
1082
+ const modeSessions = summaries
1083
+ .filter(
1084
+ (summary) =>
1085
+ isModeSessionRecoveryHealthy() || summary.status !== "active",
1086
+ )
1087
+ .flatMap((summary) => {
1088
+ const descriptor = liveConversation?.modeSessions
1089
+ ? liveConversation.modeSessions.describeSummary(summary)
1090
+ : summary.status !== "active"
1091
+ ? { summary }
1092
+ : undefined;
1093
+ return descriptor ? [descriptor] : [];
1094
+ });
1028
1095
  const assistantSlackDisplayName = getAssistantName()?.trim() || undefined;
1029
1096
 
1030
1097
  // Parse each row's stored content and per-message metadata. Rendering is
@@ -1152,6 +1219,7 @@ export async function handleListMessages({
1152
1219
  deletedAt,
1153
1220
  clientMessageId: msg.clientMessageId ?? undefined,
1154
1221
  assistantTextVisibility: assistantTextVisibilityOf(msg.metadata),
1222
+ modeSession: readModeSessionMetadata(msg.metadata),
1155
1223
  // The row's raw stored envelope, carried to the render pass below so it
1156
1224
  // can read the row's own `assistantTextVisibility` marker. Never part of
1157
1225
  // the wire payload, which the serializer builds field by field.
@@ -1172,6 +1240,9 @@ export async function handleListMessages({
1172
1240
  const messages: RuntimeMessagePayload[] = await Promise.all(
1173
1241
  parsed.map(async (m) => {
1174
1242
  const mergedMessageIds = m.id ? (mergedIdMap.get(m.id) ?? []) : [];
1243
+ const modeSessionActivity = m.id
1244
+ ? modeSessionActivityMap.get(m.id)
1245
+ : undefined;
1175
1246
 
1176
1247
  // Hydrate the row's attachments from the DB. A metadata-only query avoids
1177
1248
  // loading large base64 blobs for non-image attachments (documents, audio);
@@ -1183,26 +1254,18 @@ export async function handleListMessages({
1183
1254
  if (m.id) {
1184
1255
  const idsToQuery = [m.id, ...mergedMessageIds];
1185
1256
  const linkedRows = getAttachmentMetadataForMessages(idsToQuery);
1186
- const computerUseScreenshotIdsByMessage = new Map<
1187
- string,
1188
- Set<string>
1189
- >();
1257
+ const computerUseScreenshotIds = new Set(
1258
+ linkedRows.flatMap((row) =>
1259
+ computerUseScreenshotAttachmentIdsFromMetadata(
1260
+ parseMessageMetadata(row.messageMetadata),
1261
+ ),
1262
+ ),
1263
+ );
1190
1264
  const linked = linkedRows.map((row) => {
1191
- let screenshotIds = computerUseScreenshotIdsByMessage.get(
1192
- row.messageId,
1193
- );
1194
- if (!screenshotIds) {
1195
- screenshotIds = new Set(
1196
- computerUseScreenshotAttachmentIdsFromMetadata(
1197
- parseMessageMetadata(row.messageMetadata),
1198
- ),
1199
- );
1200
- computerUseScreenshotIdsByMessage.set(row.messageId, screenshotIds);
1201
- }
1202
1265
  return {
1203
1266
  ...row.attachment,
1204
1267
  computerUseScreenshot:
1205
- screenshotIds.has(row.attachment.id) || undefined,
1268
+ computerUseScreenshotIds.has(row.attachment.id) || undefined,
1206
1269
  };
1207
1270
  });
1208
1271
  if (linked.length > 0) {
@@ -1358,6 +1421,8 @@ export async function handleListMessages({
1358
1421
  return {
1359
1422
  id: m.id ?? "",
1360
1423
  ...(mergedMessageIds.length > 0 ? { mergedMessageIds } : {}),
1424
+ ...(m.modeSession ? { modeSession: m.modeSession } : {}),
1425
+ ...(modeSessionActivity ? { modeSessionActivity } : {}),
1361
1426
  ...(m.clientMessageId ? { clientMessageId: m.clientMessageId } : {}),
1362
1427
  role: m.role,
1363
1428
  timestamp: new Date(displayTimestamp).toISOString(),
@@ -1447,6 +1512,7 @@ export async function handleListMessages({
1447
1512
  oldestMessageId: oldestMessageId ?? null,
1448
1513
  seq: persistedSeq,
1449
1514
  processing,
1515
+ ...(modeSessions.length > 0 ? { modeSessions } : {}),
1450
1516
  };
1451
1517
  }
1452
1518
 
@@ -1457,10 +1523,16 @@ export async function handleListMessages({
1457
1523
  ...(oldestMessageId != null ? { oldestMessageId } : {}),
1458
1524
  seq: persistedSeq,
1459
1525
  processing,
1526
+ ...(modeSessions.length > 0 ? { modeSessions } : {}),
1460
1527
  };
1461
1528
  }
1462
1529
 
1463
- return { messages, seq: persistedSeq, processing };
1530
+ return {
1531
+ messages,
1532
+ seq: persistedSeq,
1533
+ processing,
1534
+ ...(modeSessions.length > 0 ? { modeSessions } : {}),
1535
+ };
1464
1536
  }
1465
1537
 
1466
1538
  /**
@@ -2192,7 +2264,7 @@ export async function handleSendMessage(
2192
2264
  publishConversationMessagesChanged(conversationId, originClientId);
2193
2265
  },
2194
2266
  afterRelease: () => {
2195
- conversation.warmPromptCache();
2267
+ void conversation.warmPromptCache();
2196
2268
  },
2197
2269
  });
2198
2270
 
@@ -3052,6 +3124,7 @@ export async function handleSendMessage(
3052
3124
  messageId,
3053
3125
  requestId,
3054
3126
  clientMessageId,
3127
+ modeSession: conversation.modeSessions.getTurnOwner(requestId),
3055
3128
  });
3056
3129
  // The row this echo announces was durably persisted above, so advance
3057
3130
  // the snapshot↔stream anchor to the echo's seq (stamped inline by
@@ -3676,6 +3749,13 @@ export const ROUTES: RouteDefinition[] = [
3676
3749
  required: false,
3677
3750
  description: "Maximum number of messages to return.",
3678
3751
  },
3752
+ {
3753
+ name: "modeSessionIds",
3754
+ type: "string",
3755
+ required: false,
3756
+ description:
3757
+ "Comma-separated mode-session IDs to include with summaries for loaded active groups outside this page.",
3758
+ },
3679
3759
  ],
3680
3760
  responseBody: z.object({
3681
3761
  messages: z
@@ -3710,6 +3790,12 @@ export const ROUTES: RouteDefinition[] = [
3710
3790
  .describe(
3711
3791
  "Whether the agent is currently mid-turn for this conversation, sourced authoritatively from the persisted `processing_started_at` column. `true` means a turn is in flight; `false` means the conversation is idle. Clients use this to recover from a dropped SSE stream: if a turn appears to be running locally but the server reports `processing: false`, the turn has ended (or died) and the UI should stop waiting rather than spin indefinitely. Absent on older daemons that predate this field.",
3712
3792
  ),
3793
+ modeSessions: z
3794
+ .array(ModeSessionDescriptorSchema)
3795
+ .optional()
3796
+ .describe(
3797
+ "Conversation-owned mode-session summaries referenced by this page or explicitly requested by ID.",
3798
+ ),
3713
3799
  }),
3714
3800
  handler: (args) => handleListMessages(args),
3715
3801
  },
@@ -177,13 +177,14 @@ async function handleCredentialPrompt({ body = {} }: RouteHandlerArgs) {
177
177
  expiresAt: result.collectionExpiresAt,
178
178
  service: validated.service,
179
179
  field: validated.field,
180
- message: `This channel does not support secure input. The credential has NOT been stored yet. Share this one-time link with the user to collect it securely (single-use${expiresInMinutes ? `, expires in ${expiresInMinutes} minutes` : ""}): ${result.collectionUrl}`,
180
+ message: `${validated.conversationId ? "This channel does not support secure input." : "No conversation is attached to this invocation, so no secure prompt can be shown."} The credential has NOT been stored yet. Share this one-time link with the user to collect it securely (single-use${expiresInMinutes ? `, expires in ${expiresInMinutes} minutes` : ""}): ${result.collectionUrl}`,
181
181
  };
182
182
  }
183
183
  return {
184
184
  ok: false,
185
- error:
186
- "This conversation's channel does not support secure credential entry",
185
+ error: validated.conversationId
186
+ ? "This conversation's channel does not support secure credential entry"
187
+ : "No conversation is attached to this invocation, so no secure prompt can be shown, and a one-time collection link could not be created. Ask the user to add the credential in the app under Settings > Credentials. Never ask them to paste it into chat.",
187
188
  };
188
189
  }
189
190
  // An explicit user cancel is a valid flow, not a failure. Keep it distinct
@@ -607,8 +607,7 @@ async function handleCredentialsDelete({ body }: RouteHandlerArgs) {
607
607
  }
608
608
 
609
609
  async function handleCredentialsStatus() {
610
- const info = await getActiveBackendInfoAsync();
611
- return info;
610
+ return getActiveBackendInfoAsync();
612
611
  }
613
612
 
614
613
  // ---------------------------------------------------------------------------
@@ -9,25 +9,22 @@ import {
9
9
  } from "bun:test";
10
10
 
11
11
  import { setOverridesForTesting } from "../../__tests__/feature-flag-test-helpers.js";
12
- import { desktopDependencyInstaller } from "../../desktop/desktop-dependencies.js";
12
+ import { desktopDependencies } from "../../desktop/desktop-dependencies.js";
13
13
  import { ROUTES } from "./desktop-setup-routes.js";
14
14
  import { NotFoundError } from "./errors.js";
15
15
 
16
16
  const originalContainerized = process.env.IS_CONTAINERIZED;
17
17
  const originalPlatform = process.env.IS_PLATFORM;
18
- const status = spyOn(desktopDependencyInstaller, "getStatus");
19
- const start = spyOn(desktopDependencyInstaller, "start");
18
+ const status = spyOn(desktopDependencies, "getStatus");
20
19
 
21
20
  afterAll(() => {
22
21
  status.mockRestore();
23
- start.mockRestore();
24
22
  });
25
23
 
26
24
  beforeEach(() => {
27
25
  process.env.IS_CONTAINERIZED = "true";
28
26
  process.env.IS_PLATFORM = "true";
29
- status.mockReturnValue({ state: "required" });
30
- start.mockReturnValue({ state: "installing", stage: "packages" });
27
+ status.mockReturnValue({ state: "ready" });
31
28
  });
32
29
 
33
30
  afterEach(() => {
@@ -43,7 +40,6 @@ afterEach(() => {
43
40
  }
44
41
  setOverridesForTesting({});
45
42
  status.mockClear();
46
- start.mockClear();
47
43
  });
48
44
 
49
45
  describe("desktop setup route feature gate", () => {
@@ -55,7 +51,6 @@ describe("desktop setup route feature gate", () => {
55
51
  );
56
52
  expect(() => route.handler({})).toThrow(NotFoundError);
57
53
  expect(status).not.toHaveBeenCalled();
58
- expect(start).not.toHaveBeenCalled();
59
54
  });
60
55
  }
61
56
 
@@ -64,26 +59,22 @@ describe("desktop setup route feature gate", () => {
64
59
  setOverridesForTesting({ "assistant-desktop": true });
65
60
  expect(() => route.handler({})).toThrow(NotFoundError);
66
61
  expect(status).not.toHaveBeenCalled();
67
- expect(start).not.toHaveBeenCalled();
68
62
  });
69
63
 
70
- test(`${route.method} rejects self-hosted Docker assistants without inspecting or installing dependencies`, () => {
64
+ test(`${route.method} rejects self-hosted Docker assistants without inspecting dependencies`, () => {
71
65
  process.env.IS_PLATFORM = "false";
72
66
  setOverridesForTesting({ "assistant-desktop": true });
73
67
  expect(() => route.handler({})).toThrow(NotFoundError);
74
68
  expect(status).not.toHaveBeenCalled();
75
- expect(start).not.toHaveBeenCalled();
76
69
  });
77
70
 
78
71
  test(`${route.method} serves setup only for enabled platform containers`, () => {
79
72
  setOverridesForTesting({ "assistant-desktop": true });
80
- expect(route.handler({})).toEqual(
81
- route.method === "GET"
82
- ? { state: "required", automationActive: false }
83
- : { state: "installing", stage: "packages", automationActive: false },
84
- );
85
- expect(route.method === "GET" ? status : start).toHaveBeenCalledTimes(1);
86
- expect(route.method === "GET" ? start : status).not.toHaveBeenCalled();
73
+ expect(route.handler({})).toEqual({
74
+ state: "ready",
75
+ automationActive: false,
76
+ });
77
+ expect(status).toHaveBeenCalledTimes(1);
87
78
  });
88
79
  }
89
80
  });
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  import { desktopAutomationLease } from "../../desktop/desktop-automation-lease.js";
4
- import { desktopDependencyInstaller } from "../../desktop/desktop-dependencies.js";
4
+ import { desktopDependencies } from "../../desktop/desktop-dependencies.js";
5
5
  import { isVirtualDesktopEnabled } from "../../desktop/virtual-desktop-feature.js";
6
6
  import { GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
7
7
  import { NotFoundError } from "./errors.js";
@@ -13,6 +13,7 @@ const statusSchema = z.object({
13
13
  stage: z.enum(["packages", "chrome", "checking"]).optional(),
14
14
  });
15
15
 
16
+ // POST and its wire schema support released clients; both methods only read readiness.
16
17
  export const ROUTES: RouteDefinition[] = ["GET", "POST"].map((method) => ({
17
18
  operationId:
18
19
  method === "GET" ? "desktop_setup_status" : "desktop_setup_install",
@@ -25,16 +26,13 @@ export const ROUTES: RouteDefinition[] = ["GET", "POST"].map((method) => ({
25
26
  "Virtual desktop is available only on enabled platform-hosted assistants",
26
27
  );
27
28
  }
28
- const status =
29
- method === "GET"
30
- ? desktopDependencyInstaller.getStatus()
31
- : desktopDependencyInstaller.start();
29
+ const status = desktopDependencies.getStatus();
32
30
  return { ...status, automationActive: desktopAutomationLease.isActive };
33
31
  },
34
32
  summary:
35
33
  method === "GET"
36
34
  ? "Get virtual desktop setup status"
37
- : "Install virtual desktop components",
35
+ : "Check virtual desktop readiness (legacy POST)",
38
36
  tags: ["desktop"],
39
37
  responseBody: statusSchema,
40
38
  }));
@@ -8,10 +8,13 @@ import { z } from "zod";
8
8
 
9
9
  import {
10
10
  addDocumentConversation,
11
+ createDocument,
12
+ DEFAULT_DOCUMENT_TITLE,
11
13
  getDocumentById,
12
14
  getDocumentsForConversation,
13
15
  saveDocument,
14
16
  } from "../../documents/document-store.js";
17
+ import { getConversation } from "../../persistence/conversation-crud.js";
15
18
  import { rawAll } from "../../persistence/raw-query.js";
16
19
  import { getLogger } from "../../util/logger.js";
17
20
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
@@ -214,6 +217,54 @@ export const ROUTES: RouteDefinition[] = [
214
217
  },
215
218
  },
216
219
 
220
+ {
221
+ operationId: "createDocument",
222
+ endpoint: "documents/create",
223
+ method: "POST",
224
+ policy: {
225
+ requiredScopes: ["settings.write"],
226
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
227
+ },
228
+ summary: "Create a document",
229
+ description:
230
+ "Create an empty document owned by a conversation and return it with its new surface ID.",
231
+ tags: ["documents"],
232
+ requestBody: z.object({
233
+ conversationId: z.string().describe("Owning conversation"),
234
+ title: z
235
+ .string()
236
+ .optional()
237
+ .describe(`Document title. Defaults to "${DEFAULT_DOCUMENT_TITLE}".`),
238
+ }),
239
+ responseBody: documentPayloadSchema,
240
+ handler: ({ body, headers }) => {
241
+ const { conversationId, title } = (body ?? {}) as {
242
+ conversationId?: string;
243
+ title?: string;
244
+ };
245
+ if (!conversationId || typeof conversationId !== "string") {
246
+ throw new BadRequestError("conversationId is required");
247
+ }
248
+ if (title !== undefined && typeof title !== "string") {
249
+ throw new BadRequestError("title must be a string");
250
+ }
251
+ if (!getConversation(conversationId)) {
252
+ throw new NotFoundError("Conversation not found");
253
+ }
254
+
255
+ const result = createDocument({ conversationId, title: title?.trim() });
256
+ if (!result.success) {
257
+ throw new InternalError(result.error);
258
+ }
259
+ const doc = getDocumentById(result.surfaceId);
260
+ if (!doc) {
261
+ throw new InternalError("Created document could not be read back");
262
+ }
263
+ publishDocumentsChanged(getOriginClientId(headers));
264
+ return { success: true, ...doc };
265
+ },
266
+ },
267
+
217
268
  {
218
269
  operationId: "linkDocumentConversation",
219
270
  endpoint: "documents/:id/conversations",
@@ -169,7 +169,7 @@ async function handleEmailStatus(_args: RouteHandlerArgs) {
169
169
  );
170
170
  }
171
171
 
172
- const statusData = (await statusResponse.json()) as {
172
+ return (await statusResponse.json()) as {
173
173
  address: string;
174
174
  status: string;
175
175
  created_at: string;
@@ -181,8 +181,6 @@ async function handleEmailStatus(_args: RouteHandlerArgs) {
181
181
  received_this_month: number;
182
182
  };
183
183
  };
184
-
185
- return statusData;
186
184
  }
187
185
 
188
186
  async function handleEmailList({ queryParams = {} }: RouteHandlerArgs) {
@@ -239,8 +237,7 @@ async function handleEmailDownload({ queryParams = {} }: RouteHandlerArgs) {
239
237
  throw new RouteError(String(detail), "DOWNLOAD_FAILED", response.status);
240
238
  }
241
239
 
242
- const msg = await response.json();
243
- return msg;
240
+ return response.json();
244
241
  }
245
242
 
246
243
  async function handleEmailSend({ body = {} }: RouteHandlerArgs) {
@@ -323,11 +320,10 @@ async function handleEmailSend({ body = {} }: RouteHandlerArgs) {
323
320
  throw new BadRequestError(String(detail));
324
321
  }
325
322
 
326
- const data = (await response.json()) as {
323
+ return (await response.json()) as {
327
324
  delivery_id: string;
328
325
  status: string;
329
326
  };
330
- return data;
331
327
  }
332
328
 
333
329
  // ── Attachment handlers ───────────────────────────────────────────────