@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
@@ -0,0 +1,131 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ ModeSessionSchema,
5
+ ModeSessionStatusSchema,
6
+ ModeSessionSummarySchema,
7
+ } from "./mode-session.js";
8
+
9
+ describe("mode session API schemas", () => {
10
+ test("accepts canonical membership", () => {
11
+ expect(
12
+ ModeSessionSchema.parse({ id: "session-123", mode: "browser" }),
13
+ ).toEqual({ id: "session-123", mode: "browser" });
14
+ });
15
+
16
+ test("keeps persisted status to the three lifecycle states", () => {
17
+ expect(ModeSessionStatusSchema.options).toEqual([
18
+ "active",
19
+ "completed",
20
+ "interrupted",
21
+ ]);
22
+ expect(ModeSessionStatusSchema.safeParse("waiting").success).toBe(false);
23
+ expect(ModeSessionStatusSchema.safeParse("draining").success).toBe(false);
24
+ });
25
+
26
+ test("accepts an interrupted summary with unknown end time", () => {
27
+ const summary = ModeSessionSummarySchema.parse({
28
+ id: "session-123",
29
+ conversationId: "conv-123",
30
+ mode: "computer_use",
31
+ status: "interrupted",
32
+ sourceStartedAt: 100,
33
+ firstIncludedAt: 110,
34
+ firstIncludedMessageId: "message-123",
35
+ lastActivityAt: 125,
36
+ lastOwnedMessageId: "message-456",
37
+ endedAt: null,
38
+ endReason: "assistant_restarted",
39
+ revision: 4,
40
+ });
41
+
42
+ expect(summary.endedAt).toBeNull();
43
+ });
44
+
45
+ test("rejects unknown historic modes and invalid revisions", () => {
46
+ const base = {
47
+ id: "session-123",
48
+ conversationId: "conv-123",
49
+ status: "active",
50
+ sourceStartedAt: 100,
51
+ firstIncludedAt: null,
52
+ firstIncludedMessageId: null,
53
+ lastActivityAt: 100,
54
+ lastOwnedMessageId: null,
55
+ endedAt: null,
56
+ endReason: null,
57
+ };
58
+ expect(
59
+ ModeSessionSummarySchema.safeParse({
60
+ ...base,
61
+ mode: "unknown_mode",
62
+ revision: 1,
63
+ }).success,
64
+ ).toBe(false);
65
+ expect(
66
+ ModeSessionSummarySchema.safeParse({
67
+ ...base,
68
+ mode: "ambient",
69
+ revision: 0,
70
+ }).success,
71
+ ).toBe(false);
72
+ });
73
+
74
+ test("enforces status-specific terminal timing", () => {
75
+ const base = {
76
+ id: "session-123",
77
+ conversationId: "conv-123",
78
+ mode: "browser",
79
+ sourceStartedAt: 100,
80
+ firstIncludedAt: null,
81
+ firstIncludedMessageId: null,
82
+ lastActivityAt: 125,
83
+ lastOwnedMessageId: null,
84
+ revision: 1,
85
+ };
86
+
87
+ expect(
88
+ ModeSessionSummarySchema.safeParse({
89
+ ...base,
90
+ status: "active",
91
+ endedAt: 150,
92
+ endReason: "settled",
93
+ }).success,
94
+ ).toBe(false);
95
+ expect(
96
+ ModeSessionSummarySchema.safeParse({
97
+ ...base,
98
+ status: "completed",
99
+ endedAt: null,
100
+ endReason: "settled",
101
+ }).success,
102
+ ).toBe(false);
103
+ expect(
104
+ ModeSessionSummarySchema.safeParse({
105
+ ...base,
106
+ status: "completed",
107
+ endedAt: 120,
108
+ endReason: "settled",
109
+ }).success,
110
+ ).toBe(false);
111
+ });
112
+
113
+ test("keeps display boundary fields paired", () => {
114
+ expect(
115
+ ModeSessionSummarySchema.safeParse({
116
+ id: "session-123",
117
+ conversationId: "conv-123",
118
+ mode: "ambient",
119
+ status: "active",
120
+ sourceStartedAt: 100,
121
+ firstIncludedAt: 110,
122
+ firstIncludedMessageId: null,
123
+ lastActivityAt: 125,
124
+ lastOwnedMessageId: null,
125
+ endedAt: null,
126
+ endReason: null,
127
+ revision: 1,
128
+ }).success,
129
+ ).toBe(false);
130
+ });
131
+ });
@@ -0,0 +1,148 @@
1
+ import { z } from "zod";
2
+
3
+ export const ModeSessionModeSchema = z.enum([
4
+ "computer_use",
5
+ "browser",
6
+ "live_vision",
7
+ "ambient",
8
+ ]);
9
+ export type ModeSessionMode = z.infer<typeof ModeSessionModeSchema>;
10
+
11
+ /** Whether a stamped row may start the user-visible span for this mode. */
12
+ export function modeSessionRowStartsDisplay(
13
+ mode: ModeSessionMode,
14
+ startsDisplayBoundary: boolean,
15
+ ): boolean {
16
+ return mode === "live_vision" || startsDisplayBoundary;
17
+ }
18
+
19
+ export const ModeSessionStatusSchema = z.enum([
20
+ "active",
21
+ "completed",
22
+ "interrupted",
23
+ ]);
24
+ export type ModeSessionStatus = z.infer<typeof ModeSessionStatusSchema>;
25
+
26
+ /** Immutable ownership stamped onto transcript message metadata. */
27
+ export const ModeSessionSchema = z.object({
28
+ mode: ModeSessionModeSchema,
29
+ id: z.string().min(1),
30
+ });
31
+ export type ModeSession = z.infer<typeof ModeSessionSchema>;
32
+
33
+ export function parseModeSession(value: unknown): ModeSession | undefined {
34
+ const parsed = ModeSessionSchema.safeParse(value);
35
+ return parsed.success ? parsed.data : undefined;
36
+ }
37
+
38
+ /** Historic or caller-authored invalid stamps behave like an absent stamp. */
39
+ export const TolerantModeSessionSchema = z.preprocess(
40
+ parseModeSession,
41
+ ModeSessionSchema.optional(),
42
+ );
43
+
44
+ const MAX_DATE_MS = 8_640_000_000_000_000;
45
+
46
+ export const ModeSessionActivitySchema = z
47
+ .object({
48
+ firstAt: z.number().int().nonnegative().max(MAX_DATE_MS),
49
+ lastAt: z.number().int().nonnegative().max(MAX_DATE_MS),
50
+ })
51
+ .refine((activity) => activity.lastAt >= activity.firstAt, {
52
+ message: "Mode session activity cannot end before it starts",
53
+ path: ["lastAt"],
54
+ });
55
+ export type ModeSessionActivity = z.infer<typeof ModeSessionActivitySchema>;
56
+
57
+ const ModeSessionSummaryBaseSchema = z.object({
58
+ id: z.string().min(1),
59
+ conversationId: z.string().min(1),
60
+ mode: ModeSessionModeSchema,
61
+ sourceStartedAt: z.number().int().nonnegative(),
62
+ firstIncludedAt: z.number().int().nonnegative().nullable(),
63
+ firstIncludedMessageId: z.string().min(1).nullable(),
64
+ lastActivityAt: z.number().int().nonnegative(),
65
+ lastOwnedMessageId: z.string().min(1).nullable(),
66
+ revision: z.number().int().positive(),
67
+ });
68
+
69
+ /** Durable lifecycle summary for one conversation-owned presentation run. */
70
+ export const ModeSessionSummarySchema = z
71
+ .discriminatedUnion("status", [
72
+ ModeSessionSummaryBaseSchema.extend({
73
+ status: z.literal("active"),
74
+ endedAt: z.null(),
75
+ endReason: z.null(),
76
+ }),
77
+ ModeSessionSummaryBaseSchema.extend({
78
+ status: z.literal("completed"),
79
+ endedAt: z.number().int().nonnegative(),
80
+ endReason: z.string().min(1),
81
+ }),
82
+ ModeSessionSummaryBaseSchema.extend({
83
+ status: z.literal("interrupted"),
84
+ endedAt: z.number().int().nonnegative().nullable(),
85
+ endReason: z.string().min(1),
86
+ }),
87
+ ])
88
+ .superRefine((summary, ctx) => {
89
+ if (
90
+ (summary.firstIncludedAt === null) !==
91
+ (summary.firstIncludedMessageId === null)
92
+ ) {
93
+ ctx.addIssue({
94
+ code: "custom",
95
+ message: "First included time and message ID must be set together",
96
+ });
97
+ }
98
+ if (summary.endedAt !== null && summary.endedAt < summary.lastActivityAt) {
99
+ ctx.addIssue({
100
+ code: "custom",
101
+ message: "Mode session end cannot precede its last activity",
102
+ path: ["endedAt"],
103
+ });
104
+ }
105
+ if (summary.lastActivityAt < summary.sourceStartedAt) {
106
+ ctx.addIssue({
107
+ code: "custom",
108
+ message: "Last activity cannot precede source start",
109
+ path: ["lastActivityAt"],
110
+ });
111
+ }
112
+ if (
113
+ summary.firstIncludedAt !== null &&
114
+ summary.firstIncludedAt > summary.lastActivityAt
115
+ ) {
116
+ ctx.addIssue({
117
+ code: "custom",
118
+ message: "First included time cannot follow last activity",
119
+ path: ["firstIncludedAt"],
120
+ });
121
+ }
122
+ });
123
+ export type ModeSessionSummary = z.infer<typeof ModeSessionSummarySchema>;
124
+
125
+ export const ModeSessionRuntimeStateSchema = z.enum(["waiting", "finishing"]);
126
+ export type ModeSessionRuntimeState = z.infer<
127
+ typeof ModeSessionRuntimeStateSchema
128
+ >;
129
+
130
+ /** Durable lifecycle truth plus optional process-local presentation state. */
131
+ export const ModeSessionDescriptorSchema = z
132
+ .object({
133
+ summary: ModeSessionSummarySchema,
134
+ runtimeState: ModeSessionRuntimeStateSchema.optional(),
135
+ })
136
+ .superRefine((descriptor, ctx) => {
137
+ if (
138
+ descriptor.runtimeState !== undefined &&
139
+ descriptor.summary.status !== "active"
140
+ ) {
141
+ ctx.addIssue({
142
+ code: "custom",
143
+ message: "Only active mode sessions can expose runtime state",
144
+ path: ["runtimeState"],
145
+ });
146
+ }
147
+ });
148
+ export type ModeSessionDescriptor = z.infer<typeof ModeSessionDescriptorSchema>;
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * The `remember` tool's input: what the daemon parses a call with, and what a
5
+ * client reads a recorded call's input with. `content` is one fact or a list
6
+ * of them. `finish_turn` yields the turn only when it is `true`; any other
7
+ * value reads as absent. Unknown keys, such as the injected `activity`, pass
8
+ * through.
9
+ */
10
+ export const RememberInputSchema = z.looseObject({
11
+ content: z.union([z.string(), z.array(z.string()).min(1)]),
12
+ finish_turn: z.boolean().optional().catch(undefined),
13
+ });
14
+
15
+ export type RememberInput = z.infer<typeof RememberInputSchema>;
@@ -33,6 +33,10 @@ import {
33
33
  import { AnsweredQuestionSchema } from "../events/question-answered.js";
34
34
  import { QuestionEntrySchema } from "../events/question-request.js";
35
35
  import { ToolActivityMetadataSchema } from "../events/tool-result.js";
36
+ import {
37
+ ModeSessionActivitySchema,
38
+ ModeSessionSchema,
39
+ } from "../mode-session.js";
36
40
 
37
41
  // ---------------------------------------------------------------------------
38
42
  // Attachment metadata
@@ -492,6 +496,8 @@ export type ConversationContentBlock = z.infer<
492
496
  */
493
497
  export const ConversationMessageSchema = z.object({
494
498
  id: z.string(),
499
+ modeSession: ModeSessionSchema.optional(),
500
+ modeSessionActivity: ModeSessionActivitySchema.optional(),
495
501
  /**
496
502
  * Server message ids folded into this display row when consecutive
497
503
  * assistant messages were consolidated for history rendering.
@@ -11,7 +11,9 @@ The end-to-end lifecycle of interactive guardian requests (approvals, questions)
11
11
 
12
12
  ## Single-Guardian Invariant
13
13
 
14
- Each assistant instance serves exactly one guardian. Multi-guardian is not supported and will never be. All connections, browser sessions, approval channels, and trust contexts within a single assistant process belong to the same guardian principal. Do not introduce guardian-keyed maps, per-guardian routing logic, or multi-guardian multiplexing — they add complexity without a real use case and create the false impression that cross-guardian isolation is required.
14
+ Each assistant instance serves exactly one guardian. Multi-guardian is not supported and will never be. Only that guardian's principal decides guardian requests: `applyGuardianDecision()` requires it exactly. Do not introduce guardian-keyed maps, per-guardian routing logic, or multi-guardian multiplexing; they add complexity without a real use case and create the false impression that cross-guardian isolation is required.
15
+
16
+ One guardian does not mean one person connected. The event stream (`GET /v1/events`) admits any actor principal with `chat.read`, so a non-guardian actor can hold a valid connection. Routes that must be guardian-only set `requireGuardian: true`, which `routes/http-adapter.ts` enforces with `requireBoundGuardian` (`runtime/auth/require-bound-guardian.ts`). Content meant only for the guardian is scoped to the guardian's principal at delivery (`targetActorPrincipalId` on the event hub), never assumed safe because it reached a connection.
15
17
 
16
18
  ## Guardian Verification Invariant
17
19
 
@@ -21,7 +23,7 @@ Conversational guardian verification control-plane invocation is guardian-only.
21
23
 
22
24
  ## Memory Provenance Invariant
23
25
 
24
- All memory retrieval decisions must consider actor-role provenance. Untrusted actors (non-guardian, unverified_channel) must not receive memory recall results. This invariant is enforced in `indexer.ts` (write gate) and the memory plugin's `injectors.ts` (read gate), which admits personal-memory content only for a turn whose trust context passes `isPersonalMemoryAllowed`.
26
+ Memory access follows the acting actor's capabilities: `resolveCapabilities(trustClass).canAccessMemory`, which only `guardian` holds. An actor without it neither reads nor writes the guardian's memory. The memory plugin's `injectors.ts` admits personal-memory content only for a turn whose trust context passes `isPersonalMemoryAllowed`; the `recall`, `remember`, and `delete_memory_page` tools refuse such a turn (`plugins/defaults/memory/tools.ts`); and extraction from conversation messages runs only for guardian-provenance rows, or legacy rows with none (`plugins/defaults/memory/indexer.ts`).
25
27
 
26
28
  ## Guardian Privilege Isolation Invariant
27
29
 
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
2
2
 
3
3
  import {
4
4
  BROWSER_OPERATION_META,
5
+ browserOperationLifecycle,
5
6
  executeBrowserOperation,
6
7
  } from "../operations.js";
7
8
  import { BROWSER_OPERATIONS, type BrowserOperation } from "../types.js";
@@ -19,6 +20,34 @@ describe("browser operations contract", () => {
19
20
  expect(BROWSER_OPERATION_META).toHaveLength(BROWSER_OPERATIONS.length);
20
21
  });
21
22
 
23
+ test("classifies every typed operation for session lifecycle", () => {
24
+ const classifications = Object.fromEntries(
25
+ BROWSER_OPERATIONS.map((operation) => [
26
+ operation,
27
+ browserOperationLifecycle(operation),
28
+ ]),
29
+ );
30
+ expect(classifications).toEqual({
31
+ navigate: "action",
32
+ snapshot: "action",
33
+ screenshot: "action",
34
+ close: "terminal",
35
+ attach: "action",
36
+ detach: "terminal",
37
+ click: "action",
38
+ type: "action",
39
+ press_key: "action",
40
+ scroll: "action",
41
+ select_option: "action",
42
+ hover: "action",
43
+ wait_for: "action",
44
+ extract: "action",
45
+ wait_for_download: "action",
46
+ fill_credential: "action",
47
+ status: "status",
48
+ });
49
+ });
50
+
22
51
  // ── Every operation has a dispatch handler ─────────────────────────
23
52
 
24
53
  test("every operation has a dispatch handler (rejects unknown)", async () => {
@@ -216,7 +216,7 @@ Examples:
216
216
  name: "key",
217
217
  type: "string",
218
218
  description:
219
- 'The key to press (e.g. "Enter", "Escape", "Tab", "ArrowDown").',
219
+ 'The key or modifier chord to press (e.g. "Enter", "Meta+v", "Control+a").',
220
220
  required: true,
221
221
  },
222
222
  {
@@ -238,6 +238,7 @@ the key is dispatched to that element; otherwise to the focused element.
238
238
  Examples:
239
239
  $ assistant browser press-key --key Enter
240
240
  $ assistant browser press-key --key Tab --element-id e5
241
+ $ assistant browser press-key --key "Meta+v"
241
242
  $ assistant browser press-key --key Escape`,
242
243
  },
243
244
  {
@@ -36,6 +36,35 @@ import type { ToolExecutionResult } from "../tools/types.js";
36
36
  import type { BrowserOperationContext as ToolContext } from "./types.js";
37
37
  import type { BrowserOperation } from "./types.js";
38
38
 
39
+ export type BrowserOperationLifecycle = "action" | "terminal" | "status";
40
+
41
+ const OPERATION_LIFECYCLE: Record<BrowserOperation, BrowserOperationLifecycle> =
42
+ {
43
+ navigate: "action",
44
+ snapshot: "action",
45
+ screenshot: "action",
46
+ close: "terminal",
47
+ attach: "action",
48
+ detach: "terminal",
49
+ click: "action",
50
+ type: "action",
51
+ press_key: "action",
52
+ scroll: "action",
53
+ select_option: "action",
54
+ hover: "action",
55
+ wait_for: "action",
56
+ extract: "action",
57
+ wait_for_download: "action",
58
+ fill_credential: "action",
59
+ status: "status",
60
+ };
61
+
62
+ export function browserOperationLifecycle(
63
+ operation: BrowserOperation,
64
+ ): BrowserOperationLifecycle {
65
+ return OPERATION_LIFECYCLE[operation];
66
+ }
67
+
39
68
  // ── Dispatch handlers ────────────────────────────────────────────────
40
69
 
41
70
  /**
@@ -1,4 +1,4 @@
1
- import { isVirtualDesktopEnabled } from "../desktop/virtual-desktop-feature.js";
1
+ import { shouldUseVirtualDesktop } from "../desktop/virtual-desktop-feature.js";
2
2
  import { browserManager } from "../tools/browser/browser-manager.js";
3
3
  import { normalizeBrowserMode } from "../tools/browser/browser-mode.js";
4
4
  import { getPinnedTab } from "../tools/browser/pinned-tabs.js";
@@ -18,16 +18,10 @@ export function shouldUseVirtualDesktopBrowser(
18
18
  mode.mode !== "auto" ||
19
19
  input.target_client_id ||
20
20
  input.use_active_tab ||
21
- context.transportInterface !== "web" ||
22
- context.clientOs === "macos" ||
23
- context.clientOs === "windows" ||
24
- context.clientOs === "linux" ||
25
- context.trustClass !== "guardian" ||
26
- !context.sourceActorPrincipalId ||
27
21
  browserManager.getPreferredBackendKind(context.conversationId) !== null ||
28
22
  getPinnedTab(context.conversationId)
29
23
  ) {
30
24
  return false;
31
25
  }
32
- return isVirtualDesktopEnabled();
26
+ return shouldUseVirtualDesktop(context);
33
27
  }
@@ -115,7 +115,7 @@ export async function signBundle(
115
115
  const { signature, keyId } = await requestSignature(canonicalPayload);
116
116
 
117
117
  // 5. Build SignatureJson
118
- const signatureJson: SignatureJson = {
118
+ return {
119
119
  algorithm: "ed25519",
120
120
  signer: {
121
121
  key_id: keyId,
@@ -124,6 +124,4 @@ export async function signBundle(
124
124
  content_hashes: contentHashes,
125
125
  signature,
126
126
  };
127
-
128
- return signatureJson;
129
127
  }
@@ -12,13 +12,16 @@ describe("createBargeInGuard", () => {
12
12
  expect(guard.track("speech", 100)).toBe("pending");
13
13
  expect(guard.track("speech", 100)).toBe("pending");
14
14
  expect(guard.speechMs).toBe(200);
15
+ expect(guard.fired).toBe(false);
15
16
  });
16
17
 
17
18
  test("sustained speech reaching the threshold fires once and stays fired", () => {
18
19
  const guard = createBargeInGuard(250);
19
20
  guard.track("speech", 200);
20
21
  expect(guard.track("speech", 50)).toBe("fired");
22
+ expect(guard.fired).toBe(true);
21
23
  expect(guard.track("silence", 1_000)).toBe("fired");
24
+ expect(guard.fired).toBe(true);
22
25
  });
23
26
 
24
27
  test("a brief sub-threshold gap does not reset the run", () => {
@@ -421,6 +421,7 @@ function setupController(
421
421
  ) {
422
422
  ensureConversation("conv-ctrl-test");
423
423
  const session = createCallSession({
424
+ direction: "inbound",
424
425
  conversationId: "conv-ctrl-test",
425
426
  provider: "twilio",
426
427
  fromNumber: "+15551111111",
@@ -471,6 +472,7 @@ function setupControllerWithOrigin(task?: string) {
471
472
  ensureConversation("conv-ctrl-voice");
472
473
  ensureConversation("conv-ctrl-origin");
473
474
  const session = createCallSession({
475
+ direction: "inbound",
474
476
  conversationId: "conv-ctrl-voice",
475
477
  provider: "twilio",
476
478
  fromNumber: "+15551111111",
@@ -5028,4 +5030,96 @@ describe("call-controller", () => {
5028
5030
  await turnPromise.catch(() => {});
5029
5031
  });
5030
5032
  });
5033
+ describe("turn metrics", () => {
5034
+ test("a caller turn records the marks the latency numbers are built from", async () => {
5035
+ const { controller } = setupController();
5036
+
5037
+ await controller.handleCallerUtterance("What is the weather?");
5038
+
5039
+ const snapshot = controller.getMetricsSnapshot();
5040
+ expect(snapshot.recentTurns.length).toBe(1);
5041
+ const turn = snapshot.recentTurns[0];
5042
+ expect(turn.status).toBe("completed");
5043
+ // The transcript mark is seeded from when the caller stopped talking,
5044
+ // the dispatch mark from when the leg went out, and the delta mark from
5045
+ // the first token back: the three anchors every phone latency number is
5046
+ // subtracted from.
5047
+ expect(turn.timestamps.finalTranscriptAtMs).not.toBeNull();
5048
+ expect(turn.timestamps.assistantDispatchAtMs).not.toBeNull();
5049
+ expect(turn.timestamps.firstAssistantDeltaAtMs).not.toBeNull();
5050
+ expect(turn.durations.dispatchToFirstAssistantDeltaMs).not.toBeNull();
5051
+ expect(turn.durations.totalTurnDurationMs).not.toBeNull();
5052
+ // The provider's final doubles as the speech-end anchor, so the turn's
5053
+ // round trip is measurable on phone as it is in live voice.
5054
+ expect(turn.timestamps.utteranceEndAtMs).not.toBeNull();
5055
+ expect(turn.durations.roundTripMs).not.toBeNull();
5056
+
5057
+ controller.destroy();
5058
+ });
5059
+
5060
+ test("the transcript mark predates the dispatch it triggered", async () => {
5061
+ // Seeding backdates the turn to the caller's own boundary; a turn that
5062
+ // opened at dispatch time would report a felt latency that starts after
5063
+ // the caller already finished speaking.
5064
+ const { controller } = setupController();
5065
+
5066
+ await controller.handleCallerUtterance("Hello there");
5067
+
5068
+ const turn = controller.getMetricsSnapshot().recentTurns[0];
5069
+ const finalTranscriptAtMs = turn.timestamps.finalTranscriptAtMs;
5070
+ const assistantDispatchAtMs = turn.timestamps.assistantDispatchAtMs;
5071
+ expect(finalTranscriptAtMs).not.toBeNull();
5072
+ expect(assistantDispatchAtMs).not.toBeNull();
5073
+ expect(finalTranscriptAtMs!).toBeLessThanOrEqual(assistantDispatchAtMs!);
5074
+ expect(turn.timestamps.startedAtMs).toBeLessThanOrEqual(
5075
+ finalTranscriptAtMs!,
5076
+ );
5077
+
5078
+ controller.destroy();
5079
+ });
5080
+
5081
+ test("a barge-in ends the turn once, as cancelled", async () => {
5082
+ // One interruption is one cancelled turn: the count downstream reads as
5083
+ // "turns the caller cut off", so a second settle would double it.
5084
+ // A turn that never emits or completes holds the controller in the
5085
+ // pre-speech phase, which a sustained barge-in can still cut off.
5086
+ mockStartVoiceTurn.mockImplementation(
5087
+ async (opts: { onComplete: () => void }) => ({
5088
+ turnId: "run-metrics-slow",
5089
+ abort: () => opts.onComplete(),
5090
+ }),
5091
+ );
5092
+ const { controller } = setupController();
5093
+ const turnPromise = controller.handleCallerUtterance("Tell me a story");
5094
+ for (let i = 0; i < 5; i++) {
5095
+ await Promise.resolve();
5096
+ }
5097
+ expect(controller.getState()).toBe("processing");
5098
+
5099
+ expect(controller.handleBargeIn()).toBe(true);
5100
+ await turnPromise.catch(() => {});
5101
+ controller.destroy();
5102
+
5103
+ const turns = controller.getMetricsSnapshot().recentTurns;
5104
+ expect(turns.length).toBe(1);
5105
+ expect(turns[0].status).toBe("cancelled");
5106
+ expect(turns[0].cancellationReason).toBe("interrupted");
5107
+ expect(turns[0].timestamps.bargeInAtMs).not.toBeNull();
5108
+ });
5109
+
5110
+ test("no turn is invented between turns", async () => {
5111
+ // Every mark opens a turn when none is active, so a barge-in on an idle
5112
+ // controller must leave the collector empty rather than start a turn
5113
+ // that never settles.
5114
+ const { controller } = setupController();
5115
+
5116
+ expect(controller.handleBargeIn()).toBe(false);
5117
+
5118
+ const snapshot = controller.getMetricsSnapshot();
5119
+ expect(snapshot.recentTurns.length).toBe(0);
5120
+ expect(snapshot.activeTurn).toBeNull();
5121
+
5122
+ controller.destroy();
5123
+ });
5124
+ });
5031
5125
  });