@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
package/ARCHITECTURE.md CHANGED
@@ -96,7 +96,7 @@ All HTTP API requests use a single `Authorization: Bearer <jwt>` header for auth
96
96
 
97
97
  **Credential storage:** Only hashed tokens are persisted. Access token hashes go in `credential_records`; refresh token hashes in `refresh_token_records`. Raw tokens are returned once and never stored server-side.
98
98
 
99
- **Notification scoping:** Guardian-sensitive notifications are annotated with `targetGuardianPrincipalId` for identity-scoped delivery.
99
+ **Notification scoping:** Guardian-sensitive notifications (`notification_intent`, `notification_conversation_created`) are published with `targetActorPrincipalId`, so the event hub delivers them, live, on reconnect replay, and from the `events/tail` recovery route, only to SSE connections whose verified `actorPrincipalId` is the guardian's. Connections without a principal never receive them. The payload's `targetGuardianPrincipalId` records the scoping.
100
100
 
101
101
  **Key source files:**
102
102
 
@@ -289,32 +289,35 @@ The WhatsApp channel enables inbound and outbound messaging via the Meta WhatsAp
289
289
 
290
290
  **Ingress** (`GET /webhooks/whatsapp` — verification, `POST /webhooks/whatsapp` — messages):
291
291
 
292
- 1. **Webhook verification**: Meta sends a `GET` with `hub.mode=subscribe`, `hub.verify_token`, and `hub.challenge`. The gateway compares `hub.verify_token` against `WHATSAPP_WEBHOOK_VERIFY_TOKEN` and echoes `hub.challenge` as plain text.
293
- 2. On `POST`, the gateway verifies the `X-Hub-Signature-256` header (HMAC-SHA256 of the raw request body using `WHATSAPP_APP_SECRET`) when the app secret is configured. Fail-closed: requests are rejected when the secret is set but the signature fails.
292
+ 1. **Webhook verification**: Meta sends a `GET` with `hub.mode=subscribe`, `hub.verify_token`, and `hub.challenge`. The gateway compares `hub.verify_token` against the `webhook_verify_token` credential and echoes `hub.challenge` as plain text.
293
+ 2. On `POST`, the gateway verifies the `X-Hub-Signature-256` header (HMAC-SHA256 of the raw request body using the `app_secret` credential). Fail-closed: a request is rejected with 500 when no app secret is configured, and with 403 when the signature does not match.
294
294
  3. **Normalization**: Text and media messages (image, audio, video, document, sticker) from `messages` change fields are forwarded. Delivery receipts, read receipts, and unsupported message types (contacts, location) are silently acknowledged with `{ ok: true }`. Media attachments are downloaded from the WhatsApp Cloud API, uploaded to the runtime attachment store, and their IDs are passed alongside the message content.
295
295
  4. **`/new` command**: When the message body is `/new` (case-insensitive), the gateway resolves routing, resets the conversation, and sends a confirmation message without forwarding to the runtime.
296
296
  5. The payload is normalized into a `GatewayInboundEvent` with `sourceChannel: "whatsapp"` and `conversationExternalId` set to the sender's WhatsApp phone number (E.164).
297
297
  6. WhatsApp message IDs are deduplicated via `StringDedupCache` (24-hour TTL).
298
298
  7. The gateway marks each inbound message as read (best-effort, fire-and-forget).
299
- 8. The event is forwarded to the runtime via `POST /channels/inbound` with WhatsApp-specific transport hints and a `replyCallbackUrl` pointing to `/deliver/whatsapp`.
299
+ 8. The event is forwarded to the runtime via `POST /channels/inbound` with WhatsApp-specific transport hints and a `replyCallbackUrl` of `<gatewayInternalBaseUrl>/deliver/whatsapp`. The gateway serves no route at that path: the daemon uses the URL only to address the reply (see Egress).
300
300
 
301
- **Egress** (`POST /deliver/whatsapp`):
301
+ **Egress** (daemon WhatsApp transport, `src/messaging/providers/whatsapp/`):
302
302
 
303
- 1. The runtime calls the gateway's `/deliver/whatsapp` endpoint with `{ to, text }` or `{ chatId, text }` (alias).
304
- 2. The gateway authenticates the request via bearer token (same fail-closed model as other deliver endpoints).
305
- 3. The gateway sends the message via the WhatsApp Cloud API `/{phoneNumberId}/messages` endpoint using the configured access token.
306
- 4. Text is split at 4096 characters if needed.
303
+ 1. Replies, approval prompts, and proactive sends go out through the daemon's `whatsappTransport` (`transport.ts`), which calls the Meta Cloud API itself. `isDirectDelivery()` in `src/messaging/providers/index.ts` resolves the `/deliver/whatsapp` callback URL to this transport through `channelForCallback()` (`src/messaging/providers/callback-routing.ts`), so the daemon never posts back to the gateway or dials the URL's host and port. The gateway's own replies to invite and verification codes it intercepts at ingress reach this transport the same way: `deliverVerificationReply` (`gateway/src/verification/reply-delivery.ts`) hands the reply and the inbound message's callback URL to the daemon's IPC-only `deliver_gateway_reply` method (`src/ipc/routes/channel-reply-ipc-routes.ts`), which resolves the transport from that URL.
304
+ 2. `api.ts` reads `phone_number_id` and `access_token` from the secure store and sends through the Cloud API `/{phoneNumberId}/messages` endpoint.
305
+ 3. `send.ts` splits text into 4096-character chunks, preferring newline and then whitespace boundaries.
306
+ 4. An approval prompt renders as an interactive message with up to three reply buttons whose ids follow the shared `apr:<requestId>:<action>` convention. When the cap forces a cut, the reject and block actions are kept. The gateway normalizes the guardian's `button_reply` back into callback data on ingress.
307
+ 5. Attachments up to 25 MB are uploaded to the Cloud API media endpoint and sent as image, video, or document messages. Any that fail are listed in a follow-up text notice.
307
308
 
308
- **Required credentials**:
309
+ The gateway sends only its own notices to WhatsApp (the `/new` confirmation and routing-rejection notices), through `gateway/src/whatsapp/send.ts`.
309
310
 
310
- - `WHATSAPP_PHONE_NUMBER_ID` — the numeric WhatsApp Business phone number ID from Meta
311
- - `WHATSAPP_ACCESS_TOKEN` — System User or temporary access token
312
- - `WHATSAPP_APP_SECRET` — App secret for webhook signature verification
313
- - `WHATSAPP_WEBHOOK_VERIFY_TOKEN` — Token for the Meta webhook subscription handshake
311
+ **Required credentials** (credential vault, `whatsapp` service):
314
312
 
315
- These can be set via environment variables or stored in the credential vault (CES / encrypted store) under the `whatsapp` service prefix.
313
+ - `phone_number_id`: the numeric WhatsApp Business phone number ID from Meta
314
+ - `access_token`: System User or temporary access token
315
+ - `app_secret`: app secret for webhook signature verification
316
+ - `webhook_verify_token`: token for the Meta webhook subscription handshake
316
317
 
317
- **Limitations (v1)**: Rich approval UI (inline buttons) is not supported. Contacts and location message types are acknowledged but not forwarded.
318
+ The daemon reads `phone_number_id` and `access_token` to send. The gateway reads all four: the last two for ingress, and the first two for its own notices.
319
+
320
+ **Limitations**: Contacts and location message types are acknowledged but not forwarded.
318
321
 
319
322
  **Channel Readiness**: The channel readiness HTTP endpoints (`GET /v1/channels/readiness`, `POST /v1/channels/readiness/refresh`) backed by `ChannelReadinessService` in `src/runtime/channel-readiness-service.ts` provide a unified readiness subsystem for all channels. Each channel registers a `ChannelProbe` that runs synchronous local checks (credential presence, ingress config) and optional async remote checks with a 5-minute TTL cache. Built-in probes: Telegram (bot token, webhook secret, ingress). The GET endpoint returns cached snapshots; the refresh endpoint invalidates the cache first. Unknown channels return `unsupported_channel`. Route handlers live in `src/runtime/routes/channel-readiness-routes.ts`.
320
323
 
@@ -614,6 +617,8 @@ Transcription mode is selected once per session in `media-stream-stt-session.ts`
614
617
 
615
618
  Every phone turn runs the same two-leg triage as live voice through `startVoiceTurn` (`src/calls/voice-session-bridge.ts`): `call-controller.ts` opens on a toolless front-door leg (`routingLeg: "front-door"`, the `voiceFrontDoor` call site) and drives it through the shared `createFrontDoorLegCoordinator` (`src/calls/voice-leg-coordinator.ts`), which reads the stream through the verdict machine and sequences the hand-off (pause narration, abort the leg, resolve and speak the bridge, mark it as the floor holder, start the escalated leg pinned to the conversation's own model, re-arm narration); each driver supplies only a host for how text and the bridge are spoken, how a leg is started or aborted, and (live voice only) the speculative hold and commit. Phone has no partial transcripts, so the hold verdict is never taught and routing is escalate-only. The controller also passes the bridge's turn callbacks (tool activity is recorded as `tool_use_started` / `tool_use_completed` call events, persisted row ids ride the `assistant_spoke` event), `launchedAtMs` for dispatch timing, and a `voiceTelemetry` bag keyed by the call session with a `phone_inbound` / `phone_outbound` entry. Both drivers share the spoken progress narration cadence (`src/calls/voice-progress-cadence.ts`, tuned by `voice.frontModel.progress`): the cadence owns the tool-activity log, the triggers (an ops burst, a long operation completing, a full interval of audible silence with news, the `maxSilenceMs` heartbeat) and the generated or static phrase, while each driver supplies its own view of audible silence (live voice from its TTS queue and playback-tail estimate; the media-stream transport from `isPlaybackIdle()` and a running sum of sent frame durations) and how to speak a phrase.
616
619
 
620
+ Both drivers also share the per-turn latency marks (`src/calls/voice-metrics.ts`). The controller opens a turn's marks when it dispatches, seeded with when the caller's transcript arrived, and stamps the leg dispatch, the first assistant delta, the first audible audio, a barge-in, and how the turn settled. Live voice streams a snapshot to its client on every mark; a call has no client on the line, so the aggregate is logged once per turn, cancelled turns included. Each call also records a `phone_call_started` / `phone_call_ended` pair on the onboarding telemetry substrate (`src/telemetry/phone-call-funnel.ts`), written by the call store at session creation and at the single terminal status transition: duration is the gap between the two rows, and a call that took no caller turn carries why on the end row (`silent_no_connect` when it never connected, `silent_no_turn` when it did and nobody spoke).
621
+
617
622
  A credential preflight (`resolveTelephonyCredentialReadiness()` in `src/calls/telephony-credential-preflight.ts`) gates every call: it requires a credentialed, telephony-capable STT provider **and** a media-stream-playable TTS provider (the configured one or a credentialed playable fallback). Inbound calls that fail the preflight receive `<Say>` setup-required copy plus `<Hangup/>` instead of a doomed stream; outbound placement fails before dialing via `preflightVoiceIngress()` with the same user-facing message.
618
623
 
619
624
  Key modules:
@@ -623,6 +628,7 @@ Key modules:
623
628
  | `src/calls/twilio-routes.ts` | Voice webhook handler; generates `<Connect><Stream>` TwiML, enforces the inbound credential preflight |
624
629
  | `src/calls/telephony-credential-preflight.ts` | Combined STT + TTS credential-readiness resolver |
625
630
  | `src/calls/media-stream-parser.ts` | Twilio Media Streams protocol parser |
631
+ | `src/calls/voice-metrics.ts` | Per-turn latency marks, shared with live voice |
626
632
  | `src/calls/media-turn-detector.ts` | Energy-based VAD turn detector for raw audio (batch mode) |
627
633
  | `src/calls/media-stream-stt-session.ts` | STT session — streaming/batch mode selection and transcription via `services.stt` |
628
634
  | `src/calls/media-stream-audio-transcode.ts` | Mu-law ↔ PCM16 codecs and resampling |
@@ -729,7 +735,7 @@ The assistant-side live voice module is intentionally bounded under `src/live-vo
729
735
  | `live-voice-session.ts` | Session orchestration: streaming STT, push-to-talk release, voice turn bridge callbacks, assistant text deltas, TTS, archive, metrics, interrupt, and close |
730
736
  | `live-voice-tts.ts` | Streaming TTS helper that resolves `services.tts`, requires `TtsProvider.synthesizeStream()`, and forwards audio chunks as `tts_audio` frames |
731
737
  | `live-voice-archive.ts` | Audio artifact creation/linking for user utterance and assistant response message IDs |
732
- | `live-voice-metrics.ts` | Per-session and per-turn latency snapshots emitted as `metrics` frames |
738
+ | `../calls/voice-metrics.ts` | Shared per-session and per-turn latency marks; live voice emits each snapshot as a `metrics` frame, phone calls log the per-turn aggregate |
733
739
 
734
740
  Live voice STT uses the same `resolveStreamingTranscriber()` path as conversation streaming, dialed with the provider the `liveVoice` STT role resolves to after managed-speech defaulting (`resolveEffectiveSpeechProviders`, see `config/managed-speech-defaults.ts`). For V1 latency-sensitive behavior, that provider must resolve to a `daemon-streaming` transcriber whose catalog entry has `conversationStreamingMode: "realtime-ws"` and usable credentials. Providers that only support batch or incremental-batch transcription remain valid for other voice surfaces, but do not satisfy live voice's streaming STT requirement.
735
741
 
@@ -1924,12 +1930,14 @@ Keep-alive heartbeats (every 30 s by default):
1924
1930
 
1925
1931
  ### Key Source Files
1926
1932
 
1927
- | File | Role |
1928
- | ----------------------------------------------- | ------------------------------------------------------------------------------ |
1929
- | `assistant/src/runtime/assistant-event.ts` | `AssistantEvent` type, `buildAssistantEvent()` factory, SSE framing helpers |
1930
- | `assistant/src/runtime/assistant-event-hub.ts` | `AssistantEventHub` class and process-level singleton |
1931
- | `assistant/src/runtime/routes/events-routes.ts` | `handleSubscribeAssistantEvents()` — SSE route handler |
1932
- | `assistant/src/daemon/server.ts` | Session event paths that publish to the hub (`send` → `publishAssistantEvent`) |
1933
+ | File | Role |
1934
+ | ---------------------------------------------------- | ------------------------------------------------------------------------------------- |
1935
+ | `assistant/src/runtime/assistant-event.ts` | `AssistantEvent` type, `buildAssistantEvent()` factory, SSE framing helpers |
1936
+ | `assistant/src/runtime/assistant-event-hub.ts` | `AssistantEventHub` class and process-level singleton |
1937
+ | `assistant/src/runtime/assistant-event-targeting.ts` | `matchesTargeting()`, the targeting check live fanout and replay share |
1938
+ | `assistant/src/runtime/assistant-stream-state.ts` | Per-assistant `seq` counter and ring buffer behind reconnect and `events/tail` replay |
1939
+ | `assistant/src/runtime/routes/events-routes.ts` | `handleSubscribeAssistantEvents()`, the SSE route handler |
1940
+ | `assistant/src/daemon/server.ts` | Session event paths that publish to the hub (`send` → `publishAssistantEvent`) |
1933
1941
 
1934
1942
  ---
1935
1943
 
@@ -1990,7 +1998,7 @@ An SSE push event surfaces new conversations in the macOS client sidebar:
1990
1998
 
1991
1999
  - **`notification_conversation_created`** — Emitted by `broadcaster.ts` when a notification delivery **creates** a new vellum conversation (strategy `start_new_conversation`, `createdNewConversation: true`). **Not** emitted when a conversation is reused. Payload: `{ conversationId, title, sourceEventName }`.
1992
2000
 
1993
- The event follows this pattern: the daemon creates a server-side conversation, persists an initial message, and broadcasts the SSE event so the macOS `ConversationManager` can create a visible conversation in the sidebar.
2001
+ The daemon persists the conversation and its initial message before broadcasting, so a client that receives the event can fetch the conversation immediately. A guardian-sensitive conversation is announced only to the guardian's own connections.
1994
2002
 
1995
2003
  ### Conversation Routing Decision Flow
1996
2004
 
@@ -2028,12 +2036,15 @@ Reminders carry optional `routingIntent` (`single_channel` | `multi_channel` | `
2028
2036
 
2029
2037
  ### Channel Delivery
2030
2038
 
2031
- Notifications are delivered to three channel types:
2039
+ Notifications are delivered to five channel types. The daemon sends to external channels itself, calling each provider's API through the send modules in `src/messaging/providers/<channel>/`; nothing goes through the gateway.
2032
2040
 
2033
2041
  - **Vellum (always connected)**: SSE via the daemon's broadcast mechanism. The `VellumAdapter` emits a `notification_intent` message with rendered copy and optional `deepLinkMetadata` (includes `conversationId` for conversation navigation and `messageId` for message-level scroll anchoring).
2034
- - **Telegram (when guardian binding exists)**: HTTP POST to the gateway's `/deliver/telegram` endpoint. Requires an active guardian binding for the assistant.
2042
+ - **Platform (always connected)**: the `PlatformPushAdapter` posts the notification to the platform's `/v1/assistants/{id}/push/dispatch/` endpoint for native mobile push. Without platform credentials the delivery is recorded as failed.
2043
+ - **Telegram (when the guardian has a chat ID)**: the `TelegramAdapter` calls the Telegram Bot API through `telegram-bot/send.ts`. Approval cards carry inline keyboard buttons and fall back to plain text with typed-reply instructions if the rich send fails.
2044
+ - **Slack (when the guardian's chat is a DM)**: the `SlackAdapter` posts through `slack/send.ts`. Only `D`-prefixed DM channels qualify, so a binding made from a shared channel never receives notifications.
2045
+ - **Discord (when the guardian binding names a user)**: the `DiscordAdapter` opens the guardian's DM through the Discord REST API and sends through `discord/send.ts`.
2035
2046
 
2036
- Connected channels are resolved at signal emission time: vellum is always included, and binding-based channels (Telegram) are included only when an active guardian binding exists for the assistant.
2047
+ Connected channels are resolved at signal emission time by `getConnectedChannels()` in `emit-signal.ts`, from the guardian delivery list (`getGuardianDelivery()`): vellum and platform are always included, and Telegram, Slack, and Discord are included only when that list gives the guardian a deliverable endpoint on the channel.
2037
2048
 
2038
2049
  **Key modules:**
2039
2050
 
@@ -2047,8 +2058,11 @@ Connected channels are resolved at signal emission time: vellum is always includ
2047
2058
  | `assistant/src/notifications/conversation-pairing.ts` | Materializes conversation + message per delivery; executes conversation reuse decisions |
2048
2059
  | `assistant/src/notifications/conversation-candidates.ts` | Builds per-channel candidate set of recent conversations for the decision engine |
2049
2060
  | `assistant/src/notifications/adapters/macos.ts` | Vellum adapter — broadcasts `notification_intent` via SSE with deep-link metadata |
2050
- | `assistant/src/notifications/adapters/telegram.ts` | Telegram adapter — POSTs to gateway `/deliver/telegram` |
2051
- | `assistant/src/notifications/destination-resolver.ts` | Resolves per-channel endpoints (vellum SSE, Telegram chat ID from guardian binding) |
2061
+ | `assistant/src/notifications/adapters/platform.ts` | Platform adapter: posts to the platform push-dispatch endpoint for native mobile push |
2062
+ | `assistant/src/notifications/adapters/telegram.ts` | Telegram adapter: calls the Telegram Bot API through `messaging/providers/telegram-bot/send.ts` |
2063
+ | `assistant/src/notifications/adapters/slack.ts` | Slack adapter: posts to the guardian's DM through `messaging/providers/slack/send.ts` |
2064
+ | `assistant/src/notifications/adapters/discord.ts` | Discord adapter: sends to the guardian's DM through `messaging/providers/discord/send.ts` |
2065
+ | `assistant/src/notifications/destination-resolver.ts` | Resolves per-channel endpoints from the guardian delivery list (Telegram and Slack chat ID, Discord user ID) |
2052
2066
  | `assistant/src/notifications/copy-composer.ts` | Template-based fallback copy when LLM copy is unavailable |
2053
2067
  | `assistant/src/notifications/preference-extractor.ts` | Detects preference statements in conversation messages |
2054
2068
  | `assistant/src/notifications/preferences-store.ts` | CRUD for user notification preferences |
package/Dockerfile CHANGED
@@ -101,6 +101,69 @@ RUN apt-get update && apt-get install -y \
101
101
  xdg-utils \
102
102
  && rm -rf /var/lib/apt/lists/*
103
103
 
104
+ # Desktop system packages persist through container replacement as image layers.
105
+ RUN set -eu; \
106
+ wezterm_version=20240203-110809-5046fc22; \
107
+ case "$(dpkg --print-architecture)" in \
108
+ amd64) wezterm_suffix=Debian12.deb; wezterm_sha256=d3a5c97093fbc0a87e8f9616e44efc4c9503cfc495fd1cfe4ffeff88578d15f8 ;; \
109
+ arm64) wezterm_suffix=Debian12.arm64.deb; wezterm_sha256=351f8791c6e9561d687afa63834a2132de84800dad6e7c3d035cc6518d6c3744 ;; \
110
+ *) echo "Unsupported desktop architecture" >&2; exit 1 ;; \
111
+ esac; \
112
+ curl --fail --location --proto '=https' --proto-redir '=https' \
113
+ --retry 2 --max-time 300 --output /tmp/wezterm.deb \
114
+ "https://github.com/wezterm/wezterm/releases/download/${wezterm_version}/wezterm-${wezterm_version}.${wezterm_suffix}"; \
115
+ printf '%s /tmp/wezterm.deb\n' "$wezterm_sha256" | sha256sum --check -; \
116
+ apt-get update; \
117
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
118
+ adwaita-icon-theme \
119
+ at-spi2-core \
120
+ bamfdaemon \
121
+ dbus-daemon \
122
+ dbus-x11 \
123
+ feh \
124
+ fonts-liberation \
125
+ gnome-mines \
126
+ gvfs \
127
+ libcurl4 \
128
+ libegl1 \
129
+ libgl1-mesa-dri \
130
+ libgtk-3-0 \
131
+ libvulkan1 \
132
+ openbox \
133
+ plank \
134
+ python3 \
135
+ python3-dbus \
136
+ scrot \
137
+ thunar \
138
+ tigervnc-common \
139
+ tigervnc-standalone-server \
140
+ tumbler \
141
+ xauth \
142
+ xcompmgr \
143
+ xdotool \
144
+ xfonts-base \
145
+ xterm \
146
+ /tmp/wezterm.deb; \
147
+ wezterm --version; \
148
+ rm -rf /tmp/wezterm.deb /var/lib/apt/lists/* /var/cache/apt/archives/*.deb
149
+
150
+ # Extract Chrome without running package scripts or registering its repository.
151
+ RUN set -eu; \
152
+ chrome_version=153.0.8010.36-1; \
153
+ chrome_arch="$(dpkg --print-architecture)"; \
154
+ case "$chrome_arch" in \
155
+ amd64) chrome_sha256=9bb44e33031c2f2857cf36b4343051a12f93058e4b781e3c76313df87f6c8d32 ;; \
156
+ arm64) chrome_sha256=1fcf6ec51a9d52e26ff1ad807225f725be55069ad2b68c5d69361a2746665188 ;; \
157
+ *) echo "Unsupported Chrome architecture" >&2; exit 1 ;; \
158
+ esac; \
159
+ curl --fail --location --proto '=https' --proto-redir '=https' \
160
+ --retry 2 --max-time 300 --output /tmp/chrome.deb \
161
+ "https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${chrome_version}_${chrome_arch}.deb"; \
162
+ printf '%s /tmp/chrome.deb\n' "$chrome_sha256" | sha256sum --check -; \
163
+ dpkg-deb --extract /tmp/chrome.deb /; \
164
+ /opt/google/chrome/chrome --version; \
165
+ rm /tmp/chrome.deb
166
+
104
167
  # Copy bun binary from builder instead of re-installing
105
168
  COPY --from=builder /root/.bun/bin/bun /usr/local/bin/bun
106
169
  RUN ln -sf /usr/local/bin/bun /usr/local/bin/bunx
@@ -176,6 +176,11 @@ sequenceDiagram
176
176
 
177
177
  Model->>Route: assistant credentials prompt --service --field --label
178
178
  Route->>Prompter: requestSecretStandalone(service, field, label, ...)
179
+ alt No surface can render the card (no conversation, or a channel without dynamic UI)
180
+ Prompter->>Route: {value: null, error: "unsupported_channel", collectionUrl?}
181
+ Route->>Model: "Share this one-time link ..." (pending, nothing stored yet)
182
+ Note over Route: The recipient submits the value on the gateway's<br/>credential entry page; the gateway stores it at redemption
183
+ end
179
184
  Prompter->>HTTP: secret_request {requestId, service, field, label, allowOneTimeSend}
180
185
  HTTP->>UI: Show secret prompt
181
186
  UI->>UI: User enters value in a masked field
@@ -226,11 +231,11 @@ User messages are scanned at ingress (`secret-ingress.ts`) against known credent
226
231
 
227
232
  ### Storage Layout
228
233
 
229
- | Component | Location | What it stores |
230
- | ------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
231
- | Secret values | CES credential store or encrypted file store | Encrypted credential values keyed as `credential/{service}/{field}`. Stored via CES RPC (primary), CES HTTP (containerized), or encrypted file store (fallback). |
232
- | Credential records | CES `<cesDataRoot>/metadata.json` | Service, field, alias, policy (`allowedTools`, `allowedDomains`, `injectionTemplates`), timestamps. Served over CES RPC/HTTP. |
233
- | Config | `$VELLUM_WORKSPACE_DIR/config.*` | `secretDetection` settings: enabled, blockIngress, allowOneTimeSend, blockTokenShapedMessages (default `true` — whole-message token-shape heuristic) |
234
+ | Component | Location | What it stores |
235
+ | ------------------ | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
236
+ | Secret values | CES credential store or encrypted file store | Encrypted credential values keyed as `credential/{service}/{field}`. Stored via CES RPC (primary), CES HTTP (containerized), or encrypted file store (fallback). |
237
+ | Credential records | CES `<cesDataRoot>/metadata.json` | Service, field, alias, policy (`allowedTools`, `allowedDomains`, `injectionTemplates`), timestamps. Served over CES RPC/HTTP. |
238
+ | Config | `$VELLUM_WORKSPACE_DIR/config.*` | `secretDetection` settings: enabled, blockIngress, allowOneTimeSend, blockTokenShapedMessages (default `true` — whole-message token-shape heuristic) |
234
239
 
235
240
  ### Key Files
236
241
 
@@ -29,6 +29,8 @@ Do not read `trustContext` or `currentTurnTrustContext` directly. Call sites han
29
29
 
30
30
  **Use `getTurnOrRestingTrust()`** for provenance stamped onto persisted rows (see `provenanceFromTrustContext`). Provenance is read back by the memory indexer, which runs extraction only for guardian rows, and by the transcript assembly, which wraps non-guardian user content before it reaches the model. The turn's actor is correct when a turn is running; rows persisted with no turn in flight (wake notices) must keep the owner's class, because stamping `"unknown"` there silently stops memory extraction for the owner's own flows.
31
31
 
32
+ That provenance describes the turn, so it is stamped on every row the turn writes, the assistant's replies and tool results included. Who wrote a row is a separate fact: `provenanceContactId` names the author's contact and needs positive evidence. It is added only where a person's own inbound words or actions are persisted (`actorAuthorProvenance`): a message channel ingress or its retry replay relays with an explicit `author`, unless that row is scripted or hidden, and the actor's reaction row. The shared user-message writer never infers an author from the conversation's trust, because machine-authored rows (ACP and subagent notifications, pointer turns) persist through it too. Rows the assistant writes during someone's turn never carry it. Slack backfill imports other people's messages, so the turn's actor says nothing about them; each backfilled sender is classified by the same gateway verdict live ingress uses (`resolve_inbound_trust`, see `inbound-stages/backfill-sender-provenance.ts`), which supplies both its trust class and its contact id. When that lookup returns no usable verdict, the row falls back to the guardian-address comparison and is marked `provenanceLookupFailed`, so a failed lookup is never stored as the same fact as a sender the gateway resolved as a stranger. Live ingress needs no such marker: it denies a turn whose verdict failed.
33
+
32
34
  **Use `getTrustContext()`** when there is no turn: HTTP routes reporting conversation state, hydration, and persistence of conversation-level options. Also when a caller deliberately wants the conversation's owner rather than whoever is currently acting; that intent should be obvious from the call site, and if it is not, it is probably the wrong accessor.
33
35
 
34
36
  ## When the acting actor is unknown
@@ -1,6 +1,8 @@
1
1
  # Virtual desktop browser CLI
2
2
 
3
- `assistant browser --virtual-desktop` uses the browser CLI's shared operation handlers against the Chrome process owned by `DesktopSessionManager`. It requires a platform-hosted container, the `assistant-desktop` flag, completed automatic desktop installation and an identified guardian conversation. Availability is checked before starting or reusing control and after asynchronous startup; loss of the flag or installation readiness cancels active control. The first eligible browser command waits for the shared installation job, starts Chrome, and executes the original action exactly once. Cancellation and feature revocation stop the pending action while shared installation can finish for other viewers. Browser CLI requests allow ten minutes, with an eight-minute setup wait limit. Eligible web conversations receive browser guidance in the bash tool description, including the configured shell timeout; native clients retain their existing tool description. Opening the picture-in-picture panel also starts setup and shows progress automatically; failed installs retain a retry action. The persistent profile remains `data/desktop-profile`; no data migration or extension installation is required.
3
+ `assistant browser --virtual-desktop` uses the browser CLI's shared operation handlers against the Chrome process owned by `DesktopSessionManager`. It requires a platform-hosted container, the `assistant-desktop` flag, image-provided desktop components and an identified guardian conversation. Availability is checked before starting or reusing control and after asynchronous startup; losing readiness or the feature flag cancels active control. The first eligible browser command starts Chrome and executes its action without downloading or installing dependencies. Opening the picture-in-picture panel checks readiness and starts the viewer directly. The persistent profile remains `data/desktop-profile`.
4
+
5
+ The assistant Dockerfile installs desktop system packages, pinned WezTerm and pinned Google Chrome before Bun and application source. Architecture-specific SHA-256 checksums verify both downloads. Chrome is extracted to `/opt/google/chrome` without running package scripts or registering its repository. Missing components require replacing the assistant image. Runtime code only checks readiness. `POST /v1/desktop/setup` remains a read-only compatibility endpoint for released clients; current clients only use GET.
4
6
 
5
7
  Automatic browser selection in platform-hosted web conversations uses the streamed Chrome when the desktop flag is enabled and the actor is an identified guardian. The macOS and Windows apps keep their existing browser selection and fallback behavior; they use the streamed desktop only with explicit `--virtual-desktop`. Their shared renderer reports a `web` transport, so selection also reads the active turn's frozen `clientOs`; it does not change transport identity or host capability checks. Explicit `--virtual-desktop`, `--browser-mode`, `--target-client-id` and active-tab requests override the default. Existing personal-browser sessions and tab pins remain on their selected browser. Tab commands use the same streamed-browser default as page commands. `--browser-mode local` selects assistant-side Playwright, not the user's Chrome.
6
8
 
@@ -16,6 +18,10 @@ flowchart LR
16
18
  Chrome --> Stream[Existing desktop stream]
17
19
  ```
18
20
 
21
+ The Terminal dock launcher opens [WezTerm](https://wezterm.org/), with a plain dark native title bar, minimize/maximize/close controls, a new-tab button and split panes. Use Ctrl+Shift+T for a tab, Ctrl+Shift+D to split side by side, and Ctrl+Shift+E to split top and bottom. The desktop seeds an editable `data/desktop-panel/wezterm/wezterm.lua` configuration with software rendering and a dark theme. The managed launcher keeps its stable `data/desktop-panel/applications/xterm.desktop` path so existing dock pins remain valid. The image includes an exact-version, SHA-256-verified WezTerm package for Linux x64 or ARM64, plus its rendering libraries.
22
+
23
+ Chrome and WezTerm use explicit desktop window classes shared with their launcher entries. The desktop session bus and Plank share `XDG_DATA_HOME` so the D-Bus-activated window matcher can resolve the generated launchers and group running windows under their pinned icons.
24
+
19
25
  Chrome and its dock launcher share the managed profile and loopback debug port. For Chrome reopened from the dock, the profile singleton lock identifies a candidate PID, validated against the installed executable, profile and loopback debug arguments. Discovery checks `/proc` socket ownership against that browser PID, refuses redirects and validates the returned browser WebSocket endpoint. The connection stays within the container. No CDP endpoint or token is exposed to the renderer.
20
26
 
21
27
  The desktop client bypasses personal-browser discovery, extension reconnect waits and backend fallback. It reuses the existing AX snapshot, DOM element resolution, mouse, keyboard, extraction and credential-fill implementations. Operation-scoped clients borrow the lease's connection; disposing one does not release the lease. `detach`, `close`, turn completion, cancellation, errors and idle expiry release control. Turn completion clears the activity indicator immediately and queues browser cleanup before the next automation session; it leaves Chrome and the current page open for the user. Only `tabs close` closes a Chrome tab.
@@ -24,14 +30,49 @@ The desktop client bypasses personal-browser discovery, extension reconnect wait
24
30
 
25
31
  Tab IDs are ephemeral numeric aliases for this managed browser's CDP target IDs. They are never personal extension tab IDs. Initial attachment selects an existing HTTP(S) page or opens a blank tab. Use `tabs list` and `tabs select --tab-id <id>` to choose explicitly. Navigation, tab selection, document replacement and release clear the desktop snapshot map. Personal-browser snapshots use a separate namespace.
26
32
 
27
- CDP mouse events use page viewport CSS coordinates. Before dispatching them, the client animates a purple arrow overlay to the same point. The overlay is excluded from accessibility and hit testing. It appears in the existing desktop stream and is removed on release. It does not move the OS pointer, appear in browser toolbar UI or visualize every programmatic DOM operation. Native dialogs and other applications are outside the browser CLI; users can interact with them directly in the expanded desktop. The picture-in-picture preview is view-only. Direct user interaction does not pause automation. For logins, the assistant uses saved credentials first and securely collects missing credentials with `assistant credentials prompt`, then fills the login form itself. Every CAPTCHA and bot-detection challenge, including sliders and press-and-hold checks, is handed to the user before the assistant attempts it. When human interaction is needed, the assistant calls `ask_question` with `desktopHelp: { message, doneLabel, skipLabel }` in the user's language. The tool releases held browser input and reserves the desktop before publishing a question with `presentation: "virtual_desktop"`. The live viewer mounts only in the attended Electron window, with browser focus as a fallback on older shells and the web, so conversations release it when focus changes. A busy viewer offers Reconnect if the previous connection has not finished closing. The compact card lives in the scrolling message transcript, with expandable instructions and a preview capped at 384 pixels wide. It hosts the shared live viewer, with Step In opening its full desktop modal and Done or Skip resolving the existing question response. The assistant retains an exclusive human-help reservation while waiting. Done resumes the same conversation's automation lease for its fresh snapshot; Skip, closure, timeout and cancellation release it. The browser idle timer does not expire a pending human-help reservation. The live preview connects after Show live preview or Step In, and reuses an already-open picture-in-picture viewer. Other devices receiving the card do not automatically open a stream. When the request resolves, its interactive viewer closes or returns to the read-only PiP session that was open before the request. Skip does not imply the obstacle was resolved. Older clients show the same request as a normal Done/Skip question, with model-localized labels retained in history. Channels without dynamic UI return guidance to continue in the app rather than waiting on an unusable card.
33
+ CDP mouse events use page viewport CSS coordinates. Before dispatching them, the client animates a purple arrow overlay to the same point. The overlay is excluded from accessibility and hit testing. It appears in the existing desktop stream and is removed on release. It does not move the OS pointer, appear in browser toolbar UI or visualize every programmatic DOM operation. Native dialogs and other applications use the computer-use skill or direct interaction in the expanded desktop. The picture-in-picture preview is view-only. Direct user interaction does not pause automation. For logins, the assistant uses saved credentials first and securely collects missing credentials with `assistant credentials prompt`, then fills the login form itself. Every CAPTCHA and bot-detection challenge, including sliders and press-and-hold checks, is handed to the user before the assistant attempts it. When human interaction is needed, the assistant calls `ask_question` with `desktopHelp: { message, doneLabel, skipLabel }` in the user's language. The tool releases held browser input and reserves the desktop before publishing a question with `presentation: "virtual_desktop"`. The live viewer mounts only in the attended Electron window, with browser focus as a fallback on older shells and the web, so conversations release it when focus changes. A busy viewer offers Reconnect if the previous connection has not finished closing. The compact card lives in the scrolling message transcript, with expandable instructions and a preview capped at 384 pixels wide. It hosts the shared live viewer, with Step In opening its full desktop modal and Done or Skip resolving the existing question response. The assistant retains an exclusive human-help reservation while waiting. Done resumes the same conversation's automation lease for its fresh snapshot; Skip, closure, timeout and cancellation release it. The browser idle timer does not expire a pending human-help reservation. The live preview connects after Show live preview or Step In, and reuses an already-open picture-in-picture viewer. Other devices receiving the card do not automatically open a stream. When the request resolves, its interactive viewer closes or returns to the read-only PiP session that was open before the request. Skip does not imply the obstacle was resolved. Older clients show the same request as a normal Done/Skip question, with model-localized labels retained in history. Channels without dynamic UI return guidance to continue in the app rather than waiting on an unusable card.
28
34
 
29
35
  The client records key and mouse presses before dispatch. On release it opens a fresh bounded cleanup connection, attaches to the same live targets, releases uncertain held input and removes overlays. A failed cleanup preserves state and the automation slot for retry. Turn-triggered cancellation retries cleanup automatically until it succeeds or ownership changes. Dispatched actions are never automatically retried. Closed targets need no input cleanup. Browser-process loss disposes the client, and later requests discover the replacement process.
30
36
 
31
37
  `--use-active-tab` and personal browser targeting are rejected with `--virtual-desktop`. Download waiting is unsupported. Browser operations are bounded to two minutes and share the desktop lease's action budget and idle expiry.
32
38
 
33
- Validation: focused client tests exercise shared snapshot/click behavior, namespace isolation, stale references, target changes, cancellation and uncertain-input cleanup. Lease tests cover browser ownership, cancellation and cleanup independently of native input. The Linux smoke script exercises real Chrome, the CLI and visible pointer feedback.
39
+ Validation: focused client tests exercise shared snapshot/click behavior, namespace isolation, stale references, target changes, cancellation and uncertain-input cleanup. Lease tests cover browser ownership, cancellation and cleanup independently of native input. The Linux smoke script exercises real Chrome, the CLI and visible pointer feedback. For image replacement coverage, run `scripts/smoke-desktop-browser-cli.ts` in two disposable containers with the same workspace volume and networking disabled from first use. Both runs require baked components to be ready before navigation and assert that no installation progress is emitted. Each run needs a temporary `ASSISTANT_IPC_SOCKET_DIR`.
34
40
 
35
- The desktop header icon pulses in the assistant's avatar accent color while a browser automation lease is active, including between browser commands. It shares the progress indicator's accent and neutral fallback. Reduced-motion clients show a solid accent. Setup status exposes the optional `automationActive` field; `desktop_activity_changed` events refresh it on acquisition and cancellation or release. Installation progress retains its `assistant:self:desktop` sync invalidations. The indicator reads status without starting installation, and reconnects refetch the current lease state.
41
+ The desktop header icon pulses in the assistant's avatar accent color while a browser automation lease is active, including between browser commands. It shares the progress indicator's accent and neutral fallback. Reduced-motion clients show a solid accent. Setup status exposes the optional `automationActive` field; `desktop_activity_changed` events refresh it on acquisition and cancellation or release. The indicator reads status, and reconnects refetch the current lease state.
36
42
 
37
43
  Desktop streaming checks the current in-memory gateway feature flags during connection startup. Once connected, frame and drain callbacks do not check feature flags or load workspace configuration. Turning the flag off prevents new connections; an existing stream continues until it closes or the desktop stops.
44
+
45
+ ## Computer use on the same desktop
46
+
47
+ The existing `computer-use` skill defaults to this desktop in platform-hosted
48
+ web conversations. Native desktop clients, including the Mac app connected to a
49
+ platform-hosted assistant, keep the connected-computer default and require
50
+ `target: "assistant-desktop"` to select the virtual desktop explicitly.
51
+ Non-platform assistants keep their connected-computer behavior.
52
+
53
+ Explicit `target: "connected-computer"` or `target_client_id` selects a connected
54
+ computer on every client. The selected target never falls back to another
55
+ computer when unavailable. The virtual desktop uses a fixed 1440x810 display.
56
+ Observations return full-screen screenshots and mouse actions use the same
57
+ screen coordinates.
58
+ Viewer resizing scales the canvas locally without changing the desktop layout.
59
+
60
+ Observe first and pass the returned `observation_id` with each native action.
61
+ Browser commands, user handoff, errors, and interruption require a fresh
62
+ observation before returning to native input. Browser commands and computer use
63
+ share session ownership, cancellation, and the user-help reservation. Use
64
+ browser commands for page-level operations and computer use for desktop UI.
65
+
66
+ ## File explorer
67
+
68
+ Open **Files**, between Chrome and Terminal in the virtual desktop dock, to browse the assistant workspace in
69
+ Thunar, with a Workspace sidebar shortcut and a dark theme. It supports tabs,
70
+ split views, search, hidden files, drag and drop,
71
+ copy/paste, bulk renaming, and file properties. GVfs provides Trash support and
72
+ Tumbler provides thumbnails. Use Ctrl+L to enter a path, Ctrl+T for a new tab,
73
+ Ctrl+H to toggle hidden files, and F3 for split view.
74
+
75
+ Files manages the assistant's files. Files on your personal computer must first
76
+ be uploaded or otherwise copied into the assistant workspace. Existing desktops
77
+ receive the Files dock shortcut once during upgrade; removing or rearranging it
78
+ is preserved across restarts.
@@ -0,0 +1,100 @@
1
+ # Background task outcomes in live voice
2
+
3
+ The conversation's active live-voice session owns delivery of subagent updates
4
+ while the call is open. `injectMessageIntoParent` offers updates carrying
5
+ `subagentNotification` metadata to the existing `LiveVoiceSessionManager` before
6
+ enqueuing a generic parent turn. The manager only routes to its session with the
7
+ matching conversation ID. A closing session holds incoming updates for its
8
+ teardown handoff. Other conversations and fully closed calls retain the ordinary
9
+ parent delivery path.
10
+
11
+ The session keeps one outcome queue for explicit subagents and completed
12
+ interrupted-turn continuations. A newer update from the same task replaces its
13
+ pending payload and preserves its delivery state; independent tasks keep separate
14
+ entries. Success, failure, abort and mid-run updates all follow this route.
15
+ Silent/advisor instructions remain in the update so the assistant can use findings
16
+ internally. Cancelling a running continuation does not discard finished outcomes.
17
+
18
+ A user turn may read pending findings as context without consuming them. Its
19
+ completed reply playback provides evidence for the next announcement decision,
20
+ so an answer that actually covered the findings can be skipped and an unrelated
21
+ answer leaves them due. Speculative dispatches and rollbacks do not own delivery.
22
+
23
+ Announcements use the shared voice floor check: user speech, an active response,
24
+ and queued audio playback delay delivery. A hidden, tool-capable voice turn
25
+ processes the update with current conversation context and produces speech through
26
+ the normal TTS pipeline. The original notification metadata and cron attribution
27
+ travel with the turn. No synthetic user bubble is echoed. The assistant can read
28
+ additional output or resolve a blocker without starting a competing text turn.
29
+
30
+ The assistant decides whether an update adds useful information. It speaks the
31
+ new outcome before optional visual-summary tools, or outputs the internal
32
+ `[TASK_UPDATE:SILENT]` marker for routine progress or findings already heard.
33
+ The marker is stripped from speech and captions; a completed silent decision
34
+ acknowledges the update without audio.
35
+
36
+ Spoken results require forwarded reply audio and remain pending through the
37
+ estimated playback tail. Progress narration alone, empty speech synthesis, and
38
+ failed speech synthesis leave them pending. Announcement prompts carry explicit
39
+ interruption state and the latest reply with completed playback for that task.
40
+ Generated conversation text and a displayed card are not playback receipts.
41
+ Interruption state survives a progress update being replaced by its final report.
42
+ A barge-in preserves the interrupted outcome without creating another worker.
43
+ A failed launch or an
44
+ explicit stop defers retry until the next user turn or task update. Hang-up returns
45
+ undelivered updates to the ordinary conversation path after pending voice starts
46
+ and the voice turn's teardown settle, since its abort clears the parent queue.
47
+ The handoff bypasses voice routing so a replacement call cannot reclaim it during
48
+ teardown. All parent deliveries enqueue even when idle and trigger the existing
49
+ queue drain after turn finalization, so simultaneous outcomes share its processing
50
+ lock and retry behavior.
51
+
52
+ PCM is framed into at most 100ms chunks, with the first available samples emitted
53
+ immediately. Socket emission runs roughly 500ms ahead of estimated playback,
54
+ keeping prefetched synthesis from flooding the relay queue. Pacing waits stay
55
+ outside the shared outbound-frame queue and abort with the turn, so cancellation
56
+ and control frames are not delayed. Assistant audio recordings contain only
57
+ frames successfully sent to the transport. Hands-free clients reconnect on retryable
58
+ relay closes, including legacy backpressure code 4013.
59
+
60
+ The pending queue is session-local; this does not add a durable notification ledger
61
+ or client playback acknowledgements. Task records and worker output retain their
62
+ existing persistence. Completed-playback receipts mean that reply audio was sent
63
+ and its estimated playback tail elapsed without interruption; they do not prove
64
+ that the speaker was audible. The queue keeps the latest completed reply receipt
65
+ per task until the call closes. Automatic announcements follow the current
66
+ exchange; they do not modify an already-running response.
67
+
68
+ ## macOS companion QA
69
+
70
+ Start a fresh conversation from the companion and use a new investigation topic
71
+ so an earlier report cannot satisfy the request. Ask explicitly for a background
72
+ agent. Use ordinary conversational pauses; timed silence is not required.
73
+ Continuous Flux input and the diagnostics described in
74
+ [voice input diagnostics](voice-input-diagnostics.md) apply to these calls.
75
+
76
+ 1. **Ordinary completion.** Ask for a small background investigation and wait.
77
+ Confirm the task actually starts, the assistant acknowledges the request, and
78
+ useful findings are spoken without asking for them. A completion-only notice
79
+ or visual summary without an audible explanation is not sufficient.
80
+ 2. **Keep talking.** Start another investigation, then ask an unrelated question.
81
+ Include a natural pause partway through the question. The question should stay
82
+ together, the work should survive, and its results should wait for the current
83
+ exchange to finish before being announced. Repeat with an interrupted request
84
+ that continues in the background, not only an explicitly spawned agent.
85
+ 3. **Interrupt the announcement.** Speak while findings are being announced.
86
+ The assistant should yield, answer the interruption, and retain the unheard
87
+ result for appropriate delivery. Interrupting an announcement should not spawn
88
+ another copy of the investigation. Let the full report arrive after an
89
+ interrupted progress announcement; the final report must retain that delivery
90
+ state and briefly resume useful unheard findings.
91
+ 4. **Two tasks.** Request two independent investigations. Keep chatting while
92
+ they run, then leave an opening. Both useful outcomes should arrive without
93
+ overlapping audio, repetitive status announcements, or a disconnect.
94
+ 5. **Hang up before delivery.** End the call with work still running or a result
95
+ waiting to be heard. Confirm the outcome reaches the conversation afterward.
96
+
97
+ Record approximate times for requests, interruptions, missing results, and any
98
+ disconnect. Export support logs after the call. Distinguish the request being
99
+ acknowledged, the worker starting, and the result being heard; they exercise
100
+ different parts of the flow.