@vellumai/assistant 0.12.3-staging.1 → 0.12.3-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (560) hide show
  1. package/ARCHITECTURE.md +43 -29
  2. package/Dockerfile +63 -0
  3. package/docs/architecture/security.md +10 -5
  4. package/docs/architecture/turn-actor.md +2 -0
  5. package/docs/desktop-browser-cli.md +45 -4
  6. package/docs/live-voice-task-outcomes.md +100 -0
  7. package/docs/mode-sessions.md +183 -0
  8. package/docs/voice-input-diagnostics.md +58 -0
  9. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  10. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  15. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
  16. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
  17. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
  18. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
  19. package/node_modules/@vellumai/service-contracts/package.json +2 -1
  20. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  22. package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
  23. package/openapi.yaml +665 -14
  24. package/package.json +1 -1
  25. package/scripts/smoke-desktop-browser-cli.ts +10 -26
  26. package/scripts/smoke-desktop-window-theme.ts +2 -3
  27. package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
  28. package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
  29. package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
  30. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  31. package/src/__tests__/answered-question-persistence.test.ts +2 -0
  32. package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
  33. package/src/__tests__/approval-message-composer.test.ts +1 -118
  34. package/src/__tests__/approval-routes-http.test.ts +3 -0
  35. package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
  36. package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
  37. package/src/__tests__/assistant-event-targeting.test.ts +186 -0
  38. package/src/__tests__/assistant-stream-state.test.ts +29 -9
  39. package/src/__tests__/browser-fill-credential.test.ts +11 -22
  40. package/src/__tests__/btw-routes.test.ts +4 -1
  41. package/src/__tests__/build-persisted-content.test.ts +1 -1
  42. package/src/__tests__/call-conversation-messages.test.ts +5 -0
  43. package/src/__tests__/call-recovery.test.ts +7 -2
  44. package/src/__tests__/call-routes-http.test.ts +12 -0
  45. package/src/__tests__/call-store.test.ts +7 -2
  46. package/src/__tests__/channel-retry-sweep.test.ts +9 -0
  47. package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
  48. package/src/__tests__/checker.test.ts +11 -3
  49. package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
  50. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
  51. package/src/__tests__/computer-use-tools.test.ts +1 -233
  52. package/src/__tests__/config-model-image-provider.test.ts +39 -0
  53. package/src/__tests__/config-schema.test.ts +16 -0
  54. package/src/__tests__/config-watcher.test.ts +24 -0
  55. package/src/__tests__/context-search-agent-runner.test.ts +260 -1
  56. package/src/__tests__/context-search-fanout.test.ts +1 -1
  57. package/src/__tests__/context-search-types.test.ts +7 -7
  58. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
  59. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
  60. package/src/__tests__/conversation-agent-loop.test.ts +546 -16
  61. package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
  62. package/src/__tests__/conversation-evictor.test.ts +62 -37
  63. package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
  64. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
  65. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  66. package/src/__tests__/conversation-retry-route.test.ts +22 -0
  67. package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
  68. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
  69. package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
  70. package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  72. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
  73. package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
  74. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
  75. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
  76. package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
  77. package/src/__tests__/conversation-undo.test.ts +61 -0
  78. package/src/__tests__/credential-execution-client.test.ts +6 -6
  79. package/src/__tests__/credential-prompt-route.test.ts +35 -3
  80. package/src/__tests__/cu-unified-flow.test.ts +82 -1
  81. package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
  82. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
  83. package/src/__tests__/dm-backfill.test.ts +91 -1
  84. package/src/__tests__/document-create-route.test.ts +88 -0
  85. package/src/__tests__/document-sync-tags.test.ts +27 -0
  86. package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
  87. package/src/__tests__/events-tail-route.test.ts +25 -0
  88. package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
  89. package/src/__tests__/guardian-action-sweep.test.ts +16 -0
  90. package/src/__tests__/guardian-dispatch.test.ts +13 -0
  91. package/src/__tests__/headless-browser-interactions.test.ts +64 -20
  92. package/src/__tests__/heartbeat-service.test.ts +2 -59
  93. package/src/__tests__/helpers/mock-conversation.ts +21 -0
  94. package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
  95. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
  96. package/src/__tests__/host-cu-proxy.test.ts +46 -2
  97. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  98. package/src/__tests__/image-credentials.test.ts +49 -0
  99. package/src/__tests__/image-service-dispatcher.test.ts +45 -0
  100. package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
  101. package/src/__tests__/interrupt-on-send.test.ts +57 -43
  102. package/src/__tests__/interrupt-turn-note.test.ts +5 -6
  103. package/src/__tests__/llm-catalog-parity.test.ts +2 -2
  104. package/src/__tests__/media-generate-image.test.ts +49 -1
  105. package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
  106. package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
  107. package/src/__tests__/mode-session-history.test.ts +245 -0
  108. package/src/__tests__/mode-session-recovery.test.ts +222 -0
  109. package/src/__tests__/mode-session-wire.test.ts +141 -0
  110. package/src/__tests__/notification-guardian-path.test.ts +7 -0
  111. package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
  112. package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +12 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  115. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  116. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +5 -0
  118. package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
  119. package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -0
  120. package/src/__tests__/process-message-display-content.test.ts +56 -0
  121. package/src/__tests__/reaction-persistence.test.ts +5 -0
  122. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
  123. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  124. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
  125. package/src/__tests__/secret-ingress-http.test.ts +2 -0
  126. package/src/__tests__/secure-keys.test.ts +43 -1
  127. package/src/__tests__/send-endpoint-busy.test.ts +163 -1
  128. package/src/__tests__/server-history-render.test.ts +91 -1
  129. package/src/__tests__/settings-routes.test.ts +50 -1
  130. package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
  132. package/src/__tests__/subagent-manager-notify.test.ts +1 -2
  133. package/src/__tests__/subagent-notify-parent.test.ts +146 -39
  134. package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
  135. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
  136. package/src/__tests__/thread-backfill.test.ts +53 -0
  137. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  138. package/src/__tests__/tool-result-metadata-plumbing.test.ts +3 -1
  139. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  140. package/src/__tests__/twilio-routes.test.ts +1 -0
  141. package/src/__tests__/web-search-backend-failure.test.ts +1 -1
  142. package/src/activation/progress-store.test.ts +12 -7
  143. package/src/agent/loop.ts +99 -41
  144. package/src/api/events/assistant-turn-start.ts +3 -0
  145. package/src/api/events/generation-handoff.ts +2 -0
  146. package/src/api/events/message-complete.ts +2 -0
  147. package/src/api/events/notification-conversation-created.ts +14 -16
  148. package/src/api/events/notification-intent.ts +4 -2
  149. package/src/api/events/tool-result.ts +85 -0
  150. package/src/api/events/tool-use-start.ts +3 -0
  151. package/src/api/events/user-message-echo.ts +3 -0
  152. package/src/api/index.ts +31 -6
  153. package/src/api/mode-session.test.ts +131 -0
  154. package/src/api/mode-session.ts +148 -0
  155. package/src/api/remember-tool.ts +15 -0
  156. package/src/api/responses/conversation-message.ts +6 -0
  157. package/src/approvals/AGENTS.md +4 -2
  158. package/src/browser/__tests__/operations.test.ts +29 -0
  159. package/src/browser/operation-meta.ts +2 -1
  160. package/src/browser/operations.ts +29 -0
  161. package/src/browser/virtual-desktop-target.ts +2 -8
  162. package/src/bundler/bundle-signer.ts +1 -3
  163. package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
  164. package/src/calls/__tests__/call-controller.test.ts +94 -0
  165. package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
  166. package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
  167. package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
  168. package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
  169. package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
  170. package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
  171. package/src/calls/barge-in-guard.ts +5 -0
  172. package/src/calls/call-controller.ts +196 -1
  173. package/src/calls/call-domain.ts +4 -0
  174. package/src/calls/call-store.ts +87 -2
  175. package/src/calls/guardian-action-sweep.ts +22 -8
  176. package/src/calls/media-stream-audio-transcode.ts +1 -3
  177. package/src/calls/typesafe-noul.ts +156 -0
  178. package/src/calls/voice-control-protocol.ts +6 -0
  179. package/src/calls/voice-escalation-judge.ts +145 -0
  180. package/src/calls/voice-escalation-target.ts +10 -0
  181. package/src/calls/voice-leg-coordinator.ts +121 -3
  182. package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
  183. package/src/calls/voice-session-bridge.ts +256 -112
  184. package/src/calls/voice-triage-escalate.ts +7 -0
  185. package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
  186. package/src/cli/commands/__tests__/notifications.test.ts +35 -10
  187. package/src/cli/commands/browser.help.ts +4 -5
  188. package/src/cli/commands/browser.ts +0 -1
  189. package/src/cli/commands/clients.help.ts +46 -0
  190. package/src/cli/commands/clients.ts +116 -0
  191. package/src/cli/commands/credentials.help.ts +6 -1
  192. package/src/cli/commands/notifications.help.ts +16 -2
  193. package/src/cli/commands/notifications.ts +30 -8
  194. package/src/cli/commands/roadmap.help.ts +18 -21
  195. package/src/cli/commands/roadmap.ts +4 -21
  196. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
  197. package/src/cli/lib/bundled-marketplace.json +84 -34
  198. package/src/cli/lib/plugin-marketplace.ts +8 -0
  199. package/src/cli/lib/publish-plugin.ts +1 -2
  200. package/src/config/__tests__/default-provider.test.ts +3 -1
  201. package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
  202. package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
  203. package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
  204. package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
  205. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
  206. package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
  207. package/src/config/call-site-defaults.ts +9 -0
  208. package/src/config/feature-flag-registry.json +16 -0
  209. package/src/config/profile-text-generation.test.ts +2 -2
  210. package/src/config/schemas/__tests__/tools.test.ts +31 -0
  211. package/src/config/schemas/call-site-catalog.ts +14 -0
  212. package/src/config/schemas/channels.ts +0 -18
  213. package/src/config/schemas/llm.ts +3 -1
  214. package/src/config/schemas/mcp.ts +2 -1
  215. package/src/config/schemas/memory-retrospective.ts +11 -2
  216. package/src/config/schemas/services.ts +15 -6
  217. package/src/config/schemas/tools.ts +10 -0
  218. package/src/config/session-groups-gate.ts +21 -0
  219. package/src/config/skills.ts +2 -6
  220. package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
  221. package/src/conversations/message-consolidation.ts +102 -5
  222. package/src/credential-execution/ces-connect.test.ts +69 -0
  223. package/src/credential-execution/ces-connect.ts +115 -0
  224. package/src/credential-execution/ces-runtime.ts +55 -93
  225. package/src/credential-execution/executable-discovery.ts +1 -11
  226. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
  227. package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
  228. package/src/daemon/browser-mode-session.test.ts +402 -0
  229. package/src/daemon/browser-mode-session.ts +186 -0
  230. package/src/daemon/computer-use-mode-session.test.ts +210 -0
  231. package/src/daemon/computer-use-mode-session.ts +105 -0
  232. package/src/daemon/config-watcher.ts +10 -0
  233. package/src/daemon/conversation-agent-loop-handlers.ts +58 -10
  234. package/src/daemon/conversation-agent-loop.ts +219 -25
  235. package/src/daemon/conversation-evictor.ts +5 -24
  236. package/src/daemon/conversation-history.ts +10 -0
  237. package/src/daemon/conversation-interrupt-repair.ts +4 -43
  238. package/src/daemon/conversation-interrupt.ts +7 -11
  239. package/src/daemon/conversation-messaging.ts +58 -5
  240. package/src/daemon/conversation-mode-session.test.ts +1667 -0
  241. package/src/daemon/conversation-mode-session.ts +1008 -0
  242. package/src/daemon/conversation-process.ts +5 -0
  243. package/src/daemon/conversation-runtime-assembly.ts +10 -20
  244. package/src/daemon/conversation-skill-tools.ts +1 -0
  245. package/src/daemon/conversation-store.ts +4 -4
  246. package/src/daemon/conversation-surface-snapshots.ts +135 -0
  247. package/src/daemon/conversation-surfaces.ts +113 -5
  248. package/src/daemon/conversation-tool-setup.ts +8 -3
  249. package/src/daemon/conversation.ts +143 -41
  250. package/src/daemon/handlers/config-model.ts +7 -5
  251. package/src/daemon/handlers/shared.ts +55 -16
  252. package/src/daemon/host-cu-proxy.ts +51 -22
  253. package/src/daemon/lifecycle.ts +30 -13
  254. package/src/daemon/message-protocol.ts +0 -1
  255. package/src/daemon/message-provenance.ts +28 -4
  256. package/src/daemon/mode-session-startup-recovery.ts +23 -0
  257. package/src/daemon/mode-session-tracking.ts +54 -0
  258. package/src/daemon/process-message.ts +13 -4
  259. package/src/daemon/virtual-desktop-context.ts +21 -0
  260. package/src/desktop/__tests__/fake-desktop.ts +1 -0
  261. package/src/desktop/desktop-accessibility-script.ts +88 -0
  262. package/src/desktop/desktop-accessibility.test.ts +160 -0
  263. package/src/desktop/desktop-accessibility.ts +169 -0
  264. package/src/desktop/desktop-automation-lease.test.ts +76 -68
  265. package/src/desktop/desktop-automation-lease.ts +41 -50
  266. package/src/desktop/desktop-browser-endpoint.ts +4 -0
  267. package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
  268. package/src/desktop/desktop-computer-use.test.ts +449 -0
  269. package/src/desktop/desktop-computer-use.ts +502 -0
  270. package/src/desktop/desktop-dependencies.ts +47 -293
  271. package/src/desktop/desktop-display.ts +2 -2
  272. package/src/desktop/desktop-panel-config.test.ts +13 -3
  273. package/src/desktop/desktop-panel-config.ts +109 -15
  274. package/src/desktop/desktop-session-manager.test.ts +69 -21
  275. package/src/desktop/desktop-session-manager.ts +76 -22
  276. package/src/desktop/desktop-stream-bridge.test.ts +11 -122
  277. package/src/desktop/desktop-stream-bridge.ts +6 -8
  278. package/src/desktop/desktop-window-theme.ts +5 -25
  279. package/src/desktop/virtual-desktop-feature.ts +52 -2
  280. package/src/desktop/virtual-desktop-platform.test.ts +5 -9
  281. package/src/documents/document-store.ts +23 -0
  282. package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
  283. package/src/heartbeat/heartbeat-service.ts +5 -33
  284. package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
  285. package/src/ipc/assistant-server.ts +2 -0
  286. package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
  287. package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
  288. package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
  289. package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
  290. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
  291. package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
  292. package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
  293. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
  294. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
  295. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
  296. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
  297. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
  298. package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
  299. package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
  300. package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
  301. package/src/live-voice/__tests__/session-controls.test.ts +23 -0
  302. package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
  303. package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
  304. package/src/live-voice/camera-mode-session.test.ts +265 -0
  305. package/src/live-voice/camera-mode-session.ts +277 -0
  306. package/src/live-voice/continuation-judge.ts +123 -0
  307. package/src/live-voice/live-activity-reporter.ts +115 -6
  308. package/src/live-voice/live-voice-manager.ts +36 -1
  309. package/src/live-voice/live-voice-photo.ts +88 -7
  310. package/src/live-voice/live-voice-session-manager.ts +15 -0
  311. package/src/live-voice/live-voice-session.ts +1443 -747
  312. package/src/live-voice/live-voice-tts.ts +22 -4
  313. package/src/live-voice/microphone-idle-gate.ts +56 -0
  314. package/src/live-voice/protocol.ts +171 -4
  315. package/src/live-voice/session-controls.ts +11 -3
  316. package/src/live-voice/task-outcomes.ts +131 -0
  317. package/src/live-voice/voice-input-diagnostics.ts +117 -0
  318. package/src/mcp/__tests__/manager-tool-caps.test.ts +63 -18
  319. package/src/mcp/__tests__/tool-caps.test.ts +28 -1
  320. package/src/mcp/manager.ts +3 -0
  321. package/src/mcp/tool-caps.ts +12 -4
  322. package/src/media/__tests__/image-models.test.ts +58 -0
  323. package/src/media/image-credentials.ts +5 -0
  324. package/src/media/image-models.ts +52 -0
  325. package/src/media/image-service.ts +8 -0
  326. package/src/media/openrouter-image-service.test.ts +133 -0
  327. package/src/media/openrouter-image-service.ts +153 -0
  328. package/src/media/types.ts +1 -1
  329. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
  330. package/src/messaging/providers/discord/send.ts +4 -17
  331. package/src/messaging/providers/discord/transport.ts +2 -2
  332. package/src/messaging/providers/outlook/client.ts +16 -22
  333. package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
  334. package/src/messaging/providers/telegram-bot/send.ts +10 -7
  335. package/src/messaging/providers/telegram-bot/transport.ts +3 -10
  336. package/src/messaging/providers/whatsapp/send.test.ts +169 -4
  337. package/src/monitoring/__tests__/control.test.ts +17 -5
  338. package/src/monitoring/control.ts +15 -5
  339. package/src/notifications/AGENTS.md +1 -1
  340. package/src/notifications/README.md +83 -99
  341. package/src/notifications/__tests__/broadcaster.test.ts +1 -1
  342. package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
  343. package/src/notifications/__tests__/copy-composer.test.ts +1 -1
  344. package/src/notifications/__tests__/decision-engine.test.ts +79 -2
  345. package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
  346. package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
  347. package/src/notifications/__tests__/notification-utils.test.ts +18 -0
  348. package/src/notifications/adapters/macos.ts +37 -46
  349. package/src/notifications/broadcaster.ts +5 -6
  350. package/src/notifications/channel-allowlist.ts +44 -0
  351. package/src/notifications/copy-composer.ts +0 -23
  352. package/src/notifications/decision-engine.ts +24 -14
  353. package/src/notifications/deterministic-checks.ts +1 -1
  354. package/src/notifications/emit-signal.ts +123 -37
  355. package/src/notifications/home-feed-side-effect.ts +9 -7
  356. package/src/notifications/notification-utils.ts +19 -0
  357. package/src/notifications/signal.ts +4 -19
  358. package/src/notifications/types.ts +15 -0
  359. package/src/oauth/connection-resolver.ts +1 -2
  360. package/src/oauth/oauth-store.ts +1 -3
  361. package/src/oauth/provider-serializer.ts +7 -0
  362. package/src/oauth/seed-providers.ts +30 -0
  363. package/src/onboarding/onboarding-events-store.ts +99 -12
  364. package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
  365. package/src/persistence/client-connection-events-store.ts +377 -0
  366. package/src/persistence/conversation-crud.ts +55 -6
  367. package/src/persistence/conversation-mode-sessions.test.ts +424 -0
  368. package/src/persistence/conversation-mode-sessions.ts +460 -0
  369. package/src/persistence/conversation-queries.ts +6 -19
  370. package/src/persistence/llm-usage-store.ts +3 -9
  371. package/src/persistence/message-metadata.ts +25 -0
  372. package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
  373. package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
  374. package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
  375. package/src/persistence/schema/conversations.ts +67 -0
  376. package/src/persistence/schema/infrastructure.ts +29 -0
  377. package/src/persistence/steps.ts +17 -0
  378. package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
  379. package/src/plugin-api/effective-context-window.ts +33 -0
  380. package/src/plugin-api/index.ts +24 -5
  381. package/src/plugin-api/plugin-name-env.ts +125 -0
  382. package/src/plugin-api/resolve-credential.ts +79 -20
  383. package/src/plugin-api/vision-support.test.ts +22 -0
  384. package/src/plugin-api/vision-support.ts +17 -5
  385. package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
  386. package/src/plugins/active-plugin-env.ts +51 -0
  387. package/src/plugins/defaults/index.ts +24 -0
  388. package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
  389. package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
  390. package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
  391. package/src/plugins/defaults/memory/context-search/agent-runner.ts +78 -22
  392. package/src/plugins/defaults/memory/context-search/format.ts +51 -7
  393. package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
  394. package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
  395. package/src/plugins/defaults/memory/context-search/search.ts +8 -14
  396. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
  397. package/src/plugins/defaults/memory/context-search/types.ts +1 -11
  398. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
  399. package/src/plugins/defaults/memory/graph/tool-handlers.ts +12 -21
  400. package/src/plugins/defaults/memory/graph/tools.ts +35 -64
  401. package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
  402. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +5 -0
  403. package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
  404. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +2 -2
  405. package/src/plugins/defaults/memory/tools.test.ts +124 -9
  406. package/src/plugins/defaults/memory/tools.ts +38 -11
  407. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +165 -27
  408. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
  409. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +46 -27
  410. package/src/plugins/defaults/memory/v3/orchestrate.ts +78 -38
  411. package/src/plugins/defaults/memory/v3/pool-log-store.ts +65 -79
  412. package/src/plugins/defaults/memory/v3/pool-select.test.ts +14 -5
  413. package/src/plugins/defaults/memory/v3/pool-select.ts +683 -44
  414. package/src/plugins/defaults/memory/worker-control.ts +15 -5
  415. package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
  416. package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
  417. package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
  418. package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
  419. package/src/plugins/defaults/task-progress/package.json +14 -0
  420. package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
  421. package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
  422. package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
  423. package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
  424. package/src/providers/anthropic/client.ts +10 -1
  425. package/src/providers/anthropic-gateway-shared.ts +6 -0
  426. package/src/providers/fetch-provider-catalog.ts +11 -0
  427. package/src/providers/gemini/client.ts +25 -8
  428. package/src/providers/inference/adapter-factory.ts +1 -1
  429. package/src/providers/jev/client.test.ts +10 -0
  430. package/src/providers/jev/client.ts +20 -4
  431. package/src/providers/model-catalog.ts +4 -3
  432. package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
  433. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
  434. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
  435. package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
  436. package/src/providers/openai/chat-completions-provider.ts +100 -29
  437. package/src/providers/openai/google-thought-signature.ts +0 -6
  438. package/src/providers/openai/responses-provider.ts +23 -1
  439. package/src/providers/ratelimit.ts +1 -3
  440. package/src/providers/search-provider-catalog.ts +24 -0
  441. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
  442. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
  443. package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
  444. package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
  445. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
  446. package/src/providers/types.ts +5 -0
  447. package/src/routes/control.ts +7 -5
  448. package/src/runtime/AGENTS.md +4 -2
  449. package/src/runtime/actor-trust-resolver.ts +3 -3
  450. package/src/runtime/agent-wake.ts +1 -1
  451. package/src/runtime/approval-message-composer.ts +3 -69
  452. package/src/runtime/assistant-event-hub.ts +73 -75
  453. package/src/runtime/assistant-event-publish-options.ts +7 -0
  454. package/src/runtime/assistant-event-targeting.ts +79 -0
  455. package/src/runtime/assistant-stream-state.ts +13 -100
  456. package/src/runtime/background-job-runner.ts +5 -5
  457. package/src/runtime/channel-retry-sweep.ts +1 -0
  458. package/src/runtime/guardian-action-message-composer.ts +8 -161
  459. package/src/runtime/guardian-reply-router.ts +28 -32
  460. package/src/runtime/http-types.ts +6 -0
  461. package/src/runtime/message-composer-types.ts +3 -61
  462. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
  463. package/src/runtime/migrations/vbundle-builder.ts +24 -1
  464. package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
  465. package/src/runtime/migrations/vbundle-import-policy.ts +10 -0
  466. package/src/runtime/migrations/vbundle-importer.ts +4 -1
  467. package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
  468. package/src/runtime/migrations/vbundle-validator.ts +1 -0
  469. package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
  470. package/src/runtime/pending-interactions.ts +29 -0
  471. package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
  472. package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
  473. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +1 -1
  474. package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
  475. package/src/runtime/routes/browser-context.ts +31 -11
  476. package/src/runtime/routes/browser-routes.ts +43 -4
  477. package/src/runtime/routes/btw-routes.ts +9 -4
  478. package/src/runtime/routes/client-routes.ts +108 -0
  479. package/src/runtime/routes/conversation-management-routes.ts +4 -1
  480. package/src/runtime/routes/conversation-routes.ts +113 -27
  481. package/src/runtime/routes/credential-prompt-routes.ts +4 -3
  482. package/src/runtime/routes/credential-routes.ts +1 -2
  483. package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
  484. package/src/runtime/routes/desktop-setup-routes.ts +4 -6
  485. package/src/runtime/routes/documents-routes.ts +51 -0
  486. package/src/runtime/routes/email-routes.ts +3 -7
  487. package/src/runtime/routes/events-routes.ts +75 -54
  488. package/src/runtime/routes/identity-routes.ts +1 -5
  489. package/src/runtime/routes/image-generation-routes.ts +8 -14
  490. package/src/runtime/routes/inbound-message-handler.ts +35 -38
  491. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
  492. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
  493. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +38 -9
  494. package/src/runtime/routes/inbound-stages/background-dispatch.ts +2 -5
  495. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
  496. package/src/runtime/routes/llm-context-normalization.ts +1 -7
  497. package/src/runtime/routes/migration-routes.ts +73 -2
  498. package/src/runtime/routes/notification-routes.ts +10 -0
  499. package/src/runtime/routes/oauth-providers.ts +7 -0
  500. package/src/runtime/routes/plugins-routes.ts +6 -0
  501. package/src/runtime/routes/roadmap-routes.ts +47 -8
  502. package/src/runtime/routes/secret-routes.ts +1 -1
  503. package/src/runtime/routes/settings-routes.ts +10 -4
  504. package/src/runtime/routes/watcher-routes.ts +1 -3
  505. package/src/runtime/trust-class.test.ts +7 -6
  506. package/src/runtime/trust-class.ts +9 -17
  507. package/src/schedule/__tests__/worker-control.test.ts +36 -5
  508. package/src/schedule/worker-control.ts +15 -5
  509. package/src/security/secure-keys.ts +53 -103
  510. package/src/skills/clawhub.ts +3 -8
  511. package/src/skills/managed-store.ts +1 -5
  512. package/src/stt/types.ts +4 -0
  513. package/src/subagent/notify.ts +54 -34
  514. package/src/subagent/parent-notification.ts +6 -0
  515. package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
  516. package/src/telemetry/phone-call-funnel.ts +144 -0
  517. package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
  518. package/src/tools/ask-question/ask-question-tool.ts +6 -4
  519. package/src/tools/browser/browser-execution.ts +22 -59
  520. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
  521. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
  522. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
  523. package/src/tools/client-os.ts +15 -0
  524. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
  525. package/src/tools/computer-use/target.ts +42 -0
  526. package/src/tools/document/document-tool.ts +13 -18
  527. package/src/tools/execution-target.ts +14 -16
  528. package/src/tools/host-terminal/host-shell.ts +4 -1
  529. package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
  530. package/src/tools/network/__tests__/web-search.test.ts +203 -0
  531. package/src/tools/network/web-fetch.ts +274 -7
  532. package/src/tools/network/web-search.ts +468 -6
  533. package/src/tools/permission-checker.ts +3 -3
  534. package/src/tools/policy-context.ts +3 -1
  535. package/src/tools/skills/load.ts +2 -1
  536. package/src/tools/skills/sandbox-runner.ts +11 -2
  537. package/src/tools/skills/skill-script-runner.ts +7 -0
  538. package/src/tools/skills/skill-tool-factory.ts +15 -1
  539. package/src/tools/terminal/__tests__/safe-env.test.ts +11 -0
  540. package/src/tools/terminal/safe-env.ts +4 -0
  541. package/src/tools/terminal/shell.ts +2 -0
  542. package/src/tools/tool-approval-handler.ts +5 -1
  543. package/src/tools/tool-types.ts +3 -3
  544. package/src/tools/types.ts +8 -2
  545. package/src/util/__tests__/worker-process-command.test.ts +5 -4
  546. package/src/util/abort-reasons.ts +18 -26
  547. package/src/util/canonicalize-identity.test.ts +21 -0
  548. package/src/util/canonicalize-identity.ts +16 -0
  549. package/src/util/errors.ts +13 -3
  550. package/src/util/provider-error-patterns.ts +3 -0
  551. package/src/util/worker-process.ts +77 -35
  552. package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
  553. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
  554. package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
  555. package/src/workspace/migrations/registry.ts +2 -0
  556. package/src/__tests__/computer-use-window-schema.test.ts +0 -24
  557. package/src/api/events/heartbeat-alert.ts +0 -20
  558. package/src/daemon/message-types/web-activity.ts +0 -65
  559. package/src/desktop/desktop-dependencies.test.ts +0 -131
  560. package/src/tools/computer-use/definitions.ts +0 -607
@@ -9,6 +9,7 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
9
9
  import type { AssistantEvent } from "../api/index.js";
10
10
  import type { Conversation } from "../daemon/conversation.js";
11
11
  import type { SecretPromptResult } from "../permissions/secret-prompt-types.js";
12
+ import { mockUnownedModeSessions } from "./helpers/mock-conversation.js";
12
13
 
13
14
  mock.module("../config/env.js", () => ({
14
15
  isHttpAuthDisabled: () => true,
@@ -120,6 +121,7 @@ function makeIdleSession(opts?: {
120
121
  addPreactivatedSkillId: () => {},
121
122
  enqueueMessage: () => ({ queued: false, requestId: "noop" }),
122
123
  hasAnyPendingConfirmation: () => false,
124
+ modeSessions: mockUnownedModeSessions(),
123
125
  runAgentLoop: async (
124
126
  _content: string,
125
127
  _messageId: string,
@@ -183,6 +185,7 @@ function makeConfirmationEmittingSession(opts?: {
183
185
  addPreactivatedSkillId: () => {},
184
186
  enqueueMessage: () => ({ queued: false, requestId: "noop" }),
185
187
  hasAnyPendingConfirmation: () => false,
188
+ modeSessions: mockUnownedModeSessions(),
186
189
  runAgentLoop: async (
187
190
  _content: string,
188
191
  _messageId: string,
@@ -0,0 +1,111 @@
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
+
3
+ import type { ClientConnectionEventInput } from "../persistence/client-connection-events-store.js";
4
+ import { AssistantEventHub } from "../runtime/assistant-event-hub.js";
5
+
6
+ describe("AssistantEventHub connection history", () => {
7
+ const recorded: Array<Pick<ClientConnectionEventInput, "clientId" | "reason">> =
8
+ [];
9
+
10
+ beforeEach(() => {
11
+ recorded.length = 0;
12
+ });
13
+
14
+ function hub(options?: { maxSubscribers?: number }): AssistantEventHub {
15
+ return new AssistantEventHub({
16
+ ...options,
17
+ recordConnection: (input) => {
18
+ recorded.push({ clientId: input.clientId, reason: input.reason });
19
+ return null;
20
+ },
21
+ });
22
+ }
23
+
24
+ test("records sse_open on client subscribe and sse_close on dispose", () => {
25
+ const sub = hub().subscribe({
26
+ type: "client",
27
+ clientId: "client-123",
28
+ interfaceId: "chrome-extension",
29
+ capabilities: ["host_browser"],
30
+ callback: () => {},
31
+ });
32
+
33
+ expect(recorded.map((row) => row.reason)).toEqual(["sse_open"]);
34
+ sub.dispose();
35
+ expect(recorded.map((row) => row.reason)).toEqual(["sse_open", "sse_close"]);
36
+ });
37
+
38
+ test("records stale_replaced then sse_open on reconnect", () => {
39
+ const instance = hub();
40
+ instance.subscribe({
41
+ type: "client",
42
+ clientId: "client-123",
43
+ interfaceId: "chrome-extension",
44
+ capabilities: ["host_browser"],
45
+ callback: () => {},
46
+ });
47
+ instance.subscribe({
48
+ type: "client",
49
+ clientId: "client-123",
50
+ interfaceId: "chrome-extension",
51
+ capabilities: ["host_browser"],
52
+ callback: () => {},
53
+ });
54
+
55
+ expect(recorded.map((row) => row.reason)).toEqual([
56
+ "sse_open",
57
+ "stale_replaced",
58
+ "sse_open",
59
+ ]);
60
+ });
61
+
62
+ test("records force_disconnect from disposeClient", () => {
63
+ const instance = hub();
64
+ instance.subscribe({
65
+ type: "client",
66
+ clientId: "client-123",
67
+ interfaceId: "chrome-extension",
68
+ capabilities: ["host_browser"],
69
+ callback: () => {},
70
+ });
71
+ recorded.length = 0;
72
+
73
+ expect(instance.disposeClient("client-123")).toBe(1);
74
+ expect(recorded.map((row) => row.reason)).toEqual(["force_disconnect"]);
75
+ });
76
+
77
+ test("records cap_evicted when the oldest subscriber is shed", () => {
78
+ const instance = hub({ maxSubscribers: 1 });
79
+ instance.subscribe({
80
+ type: "client",
81
+ clientId: "client-old",
82
+ interfaceId: "web",
83
+ capabilities: [],
84
+ callback: () => {},
85
+ });
86
+ recorded.length = 0;
87
+
88
+ instance.subscribe({
89
+ type: "client",
90
+ clientId: "client-new",
91
+ interfaceId: "web",
92
+ capabilities: [],
93
+ callback: () => {},
94
+ });
95
+
96
+ expect(recorded.map((row) => row.reason)).toEqual([
97
+ "cap_evicted",
98
+ "sse_open",
99
+ ]);
100
+ expect(recorded[0].clientId).toBe("client-old");
101
+ });
102
+
103
+ test("does not record process subscribers", () => {
104
+ const sub = hub().subscribe({
105
+ type: "process",
106
+ callback: () => {},
107
+ });
108
+ sub.dispose();
109
+ expect(recorded).toEqual([]);
110
+ });
111
+ });
@@ -257,3 +257,53 @@ describe("AssistantEventHub — getClientById()", () => {
257
257
  expect(hub.getClientById("client-x")).toBeUndefined();
258
258
  });
259
259
  });
260
+
261
+ // ── Principal targeting ───────────────────────────────────────────────────────
262
+
263
+ describe("AssistantEventHub principal targeting (targetActorPrincipalId)", () => {
264
+ test("delivers only to clients authenticated as that principal", async () => {
265
+ const hub = new AssistantEventHub();
266
+ const received: Record<string, number> = {
267
+ guardianMac: 0,
268
+ guardianWeb: 0,
269
+ otherUser: 0,
270
+ noPrincipal: 0,
271
+ process: 0,
272
+ };
273
+ const client = (
274
+ clientId: string,
275
+ key: string,
276
+ actorPrincipalId: string | undefined,
277
+ ) =>
278
+ hub.subscribe({
279
+ type: "client",
280
+ clientId,
281
+ interfaceId: "web",
282
+ capabilities: [],
283
+ ...(actorPrincipalId ? { actorPrincipalId } : {}),
284
+ callback: () => {
285
+ received[key]! += 1;
286
+ },
287
+ });
288
+ client("mac", "guardianMac", "principal-g");
289
+ client("web", "guardianWeb", "principal-g");
290
+ client("other", "otherUser", "principal-x");
291
+ client("legacy", "noPrincipal", undefined);
292
+ hub.subscribe({
293
+ type: "process",
294
+ callback: () => {
295
+ received.process! += 1;
296
+ },
297
+ });
298
+
299
+ await hub.publish(makeEvent(), { targetActorPrincipalId: "principal-g" });
300
+
301
+ expect(received).toEqual({
302
+ guardianMac: 1,
303
+ guardianWeb: 1,
304
+ otherUser: 0,
305
+ noPrincipal: 0,
306
+ process: 0,
307
+ });
308
+ });
309
+ });
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Tests for `matchesTargeting`, the one targeting check behind live fanout
3
+ * (`AssistantEventHub.publish`) and replay (`getReplayWindow`).
4
+ *
5
+ * The parity block runs every case through both paths and requires them to
6
+ * agree, so a rule added to one path alone fails here.
7
+ */
8
+ import { beforeEach, describe, expect, test } from "bun:test";
9
+
10
+ import type { AssistantEventEnvelope } from "../api/index.js";
11
+ import type { HostProxyCapability, InterfaceId } from "../channels/types.js";
12
+ import { AssistantEventHub } from "../runtime/assistant-event-hub.js";
13
+ import type { AssistantEventPublishOptions } from "../runtime/assistant-event-publish-options.js";
14
+ import { matchesTargeting } from "../runtime/assistant-event-targeting.js";
15
+ import {
16
+ _resetStreamStateForTesting,
17
+ getReplayWindow,
18
+ stampAndBuffer,
19
+ } from "../runtime/assistant-stream-state.js";
20
+
21
+ const CONV = "conv-targeting";
22
+
23
+ interface ClientIdentity {
24
+ type: "client";
25
+ clientId: string;
26
+ interfaceId: InterfaceId;
27
+ capabilities: HostProxyCapability[];
28
+ actorPrincipalId?: string;
29
+ }
30
+
31
+ const MAC: ClientIdentity = {
32
+ type: "client",
33
+ clientId: "mac-1",
34
+ interfaceId: "macos",
35
+ capabilities: ["host_bash", "host_browser"],
36
+ actorPrincipalId: "principal-g",
37
+ };
38
+ const WEB: ClientIdentity = {
39
+ type: "client",
40
+ clientId: "web-1",
41
+ interfaceId: "web",
42
+ capabilities: [],
43
+ actorPrincipalId: "principal-g",
44
+ };
45
+ const OTHER_USER: ClientIdentity = {
46
+ type: "client",
47
+ clientId: "web-2",
48
+ interfaceId: "web",
49
+ capabilities: [],
50
+ actorPrincipalId: "principal-x",
51
+ };
52
+ const NO_PRINCIPAL: ClientIdentity = {
53
+ type: "client",
54
+ clientId: "legacy-1",
55
+ interfaceId: "web",
56
+ capabilities: [],
57
+ };
58
+ interface ProcessIdentity {
59
+ type: "process";
60
+ }
61
+
62
+ const PROCESS: ProcessIdentity = { type: "process" };
63
+
64
+ const SUBSCRIBERS: Array<[string, ClientIdentity | ProcessIdentity]> = [
65
+ ["mac", MAC],
66
+ ["web", WEB],
67
+ ["other user", OTHER_USER],
68
+ ["no principal", NO_PRINCIPAL],
69
+ ["process", PROCESS],
70
+ ];
71
+
72
+ /** Each targeting case with the subscribers it must reach, by label. */
73
+ const CASES: Array<{
74
+ name: string;
75
+ targeting: AssistantEventPublishOptions | undefined;
76
+ reaches: string[];
77
+ }> = [
78
+ {
79
+ name: "untargeted",
80
+ targeting: undefined,
81
+ reaches: ["mac", "web", "other user", "no principal", "process"],
82
+ },
83
+ {
84
+ name: "self-echo exclusion",
85
+ targeting: { excludeClientId: "web-1" },
86
+ reaches: ["mac", "other user", "no principal", "process"],
87
+ },
88
+ {
89
+ name: "interface",
90
+ targeting: { targetInterfaceId: "web" },
91
+ reaches: ["web", "other user", "no principal"],
92
+ },
93
+ {
94
+ name: "principal",
95
+ targeting: { targetActorPrincipalId: "principal-g" },
96
+ reaches: ["mac", "web"],
97
+ },
98
+ {
99
+ name: "client",
100
+ targeting: { targetClientId: "mac-1" },
101
+ reaches: ["mac"],
102
+ },
103
+ {
104
+ name: "capability",
105
+ targeting: { targetCapability: "host_browser" },
106
+ reaches: ["mac"],
107
+ },
108
+ {
109
+ name: "client without the capability",
110
+ targeting: { targetClientId: "web-1", targetCapability: "host_browser" },
111
+ reaches: [],
112
+ },
113
+ {
114
+ name: "principal and interface together",
115
+ targeting: {
116
+ targetActorPrincipalId: "principal-g",
117
+ targetInterfaceId: "web",
118
+ },
119
+ reaches: ["web"],
120
+ },
121
+ ];
122
+
123
+ function makeEvent(): AssistantEventEnvelope {
124
+ return {
125
+ id: `evt-${Math.random().toString(36).slice(2, 10)}`,
126
+ conversationId: CONV,
127
+ emittedAt: new Date().toISOString(),
128
+ message: { type: "assistant_text_delta", conversationId: CONV, text: "x" },
129
+ };
130
+ }
131
+
132
+ async function reachedLive(
133
+ targeting: AssistantEventPublishOptions | undefined,
134
+ ): Promise<string[]> {
135
+ const hub = new AssistantEventHub();
136
+ const reached: string[] = [];
137
+ for (const [label, identity] of SUBSCRIBERS) {
138
+ const callback = () => {
139
+ reached.push(label);
140
+ };
141
+ if (identity.type === "process") {
142
+ hub.subscribe({ type: "process", callback });
143
+ } else {
144
+ hub.subscribe({ ...identity, callback });
145
+ }
146
+ }
147
+ await hub.publish(makeEvent(), targeting);
148
+ return reached.sort();
149
+ }
150
+
151
+ function reachedOnReplay(
152
+ targeting: AssistantEventPublishOptions | undefined,
153
+ ): string[] {
154
+ _resetStreamStateForTesting();
155
+ stampAndBuffer(makeEvent(), targeting ? { targeting } : undefined);
156
+ return SUBSCRIBERS.filter(
157
+ ([, identity]) => (getReplayWindow(0, identity) ?? []).length > 0,
158
+ )
159
+ .map(([label]) => label)
160
+ .sort();
161
+ }
162
+
163
+ describe("matchesTargeting", () => {
164
+ for (const { name, targeting, reaches } of CASES) {
165
+ test(`${name}: reaches exactly its subscribers`, () => {
166
+ const reached = SUBSCRIBERS.filter(([, identity]) =>
167
+ matchesTargeting(targeting, identity),
168
+ ).map(([label]) => label);
169
+ expect(reached.sort()).toEqual([...reaches].sort());
170
+ });
171
+ }
172
+ });
173
+
174
+ describe("live fanout and replay agree", () => {
175
+ beforeEach(() => {
176
+ _resetStreamStateForTesting();
177
+ });
178
+
179
+ for (const { name, targeting, reaches } of CASES) {
180
+ test(`${name}: both paths deliver to the same subscribers`, async () => {
181
+ const expected = [...reaches].sort();
182
+ expect(await reachedLive(targeting)).toEqual(expected);
183
+ expect(reachedOnReplay(targeting)).toEqual(expected);
184
+ });
185
+ }
186
+ });
@@ -9,10 +9,8 @@ import { dirname, join } from "node:path";
9
9
  import { beforeEach, describe, expect, test } from "bun:test";
10
10
 
11
11
  import type { AssistantEventEnvelope } from "../api/index.js";
12
- import type {
13
- EventTargeting,
14
- ReplaySubscriber,
15
- } from "../runtime/assistant-stream-state.js";
12
+ import type { AssistantEventPublishOptions } from "../runtime/assistant-event-publish-options.js";
13
+ import type { SubscriberIdentity } from "../runtime/assistant-event-targeting.js";
16
14
  import {
17
15
  _peekStreamForTesting,
18
16
  _resetStreamStateForTesting,
@@ -103,7 +101,7 @@ describe("assistant-stream-state", () => {
103
101
  /** Targeted events stay in the ring so replay can filter them. */
104
102
 
105
103
  // GIVEN a targeting modifier
106
- const targeting: EventTargeting = {
104
+ const targeting: AssistantEventPublishOptions = {
107
105
  targetCapability: "host_bash",
108
106
  };
109
107
 
@@ -334,28 +332,28 @@ describe("assistant-stream-state", () => {
334
332
  });
335
333
 
336
334
  describe("getReplayWindow — targeting filter", () => {
337
- const MACOS_CLIENT: ReplaySubscriber = {
335
+ const MACOS_CLIENT: SubscriberIdentity = {
338
336
  type: "client",
339
337
  clientId: "mac-1",
340
338
  interfaceId: "macos",
341
339
  capabilities: ["host_bash", "host_file", "host_cu", "host_browser"],
342
340
  };
343
341
 
344
- const WEB_CLIENT: ReplaySubscriber = {
342
+ const WEB_CLIENT: SubscriberIdentity = {
345
343
  type: "client",
346
344
  clientId: "web-1",
347
345
  interfaceId: "web",
348
346
  capabilities: [],
349
347
  };
350
348
 
351
- const CHROME_EXT_CLIENT: ReplaySubscriber = {
349
+ const CHROME_EXT_CLIENT: SubscriberIdentity = {
352
350
  type: "client",
353
351
  clientId: "ext-1",
354
352
  interfaceId: "chrome-extension",
355
353
  capabilities: ["host_browser"],
356
354
  };
357
355
 
358
- const PROCESS_SUB: ReplaySubscriber = { type: "process" };
356
+ const PROCESS_SUB: SubscriberIdentity = { type: "process" };
359
357
 
360
358
  test("untargeted events are replayed to all subscriber types", () => {
361
359
  /** Events without targeting metadata go to everyone. */
@@ -491,6 +489,28 @@ describe("assistant-stream-state", () => {
491
489
  expect(procReplay).toEqual([]);
492
490
  });
493
491
 
492
+ test("principal-targeted events only replay to that principal's clients", () => {
493
+ stampAndBuffer(mkEvent(), {
494
+ targeting: { targetActorPrincipalId: "principal-g" },
495
+ });
496
+
497
+ const guardian = getReplayWindow(0, {
498
+ ...WEB_CLIENT,
499
+ actorPrincipalId: "principal-g",
500
+ });
501
+ const other = getReplayWindow(0, {
502
+ ...WEB_CLIENT,
503
+ actorPrincipalId: "principal-x",
504
+ });
505
+ const noPrincipal = getReplayWindow(0, WEB_CLIENT);
506
+ const proc = getReplayWindow(0, PROCESS_SUB);
507
+
508
+ expect(guardian!.map((e) => e.seq)).toEqual([1]);
509
+ expect(other).toEqual([]);
510
+ expect(noPrincipal).toEqual([]);
511
+ expect(proc).toEqual([]);
512
+ });
513
+
494
514
  test("mixed targeting across events filters per-entry", () => {
495
515
  /**
496
516
  * A ring with untargeted, capability-targeted, and excluded events
@@ -244,42 +244,32 @@ describe("executeBrowserFillCredential", () => {
244
244
  expect(detachCalls).toBe(1);
245
245
  });
246
246
 
247
- test("clears pre-populated field BEFORE inserting credential text", async () => {
248
- // Regression: previously, fillCredential called focus + insertText
249
- // directly, which APPENDED the credential to any existing value
250
- // (autofill, prior typing, etc.) — corrupting the password and
251
- // leaking partial state. The fix routes through the shared
252
- // clearAndInsertText helper.
247
+ test("selects pre-populated content before inserting credential text", async () => {
253
248
  snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
254
249
  await executeBrowserFillCredential(
255
250
  { service: "gmail", field: "password", element_id: "e1" },
256
251
  ctx,
257
252
  );
258
253
 
259
- // The clear must happen BEFORE the insertText. Specifically, the
260
- // Runtime.callFunctionOn that runs the clearing function declaration
261
- // must precede Input.insertText.
254
+ // Selection must happen before Input.insertText so the credential replaces
255
+ // existing content without mutating framework-owned editor state directly.
262
256
  const methodSeq = sendCalls.map((c) => c.method);
263
257
  const clearIdx = methodSeq.indexOf("Runtime.callFunctionOn");
264
258
  const insertIdx = methodSeq.indexOf("Input.insertText");
265
259
  expect(clearIdx).toBeGreaterThanOrEqual(0);
266
260
  expect(insertIdx).toBeGreaterThan(clearIdx);
267
261
 
268
- // The clearing function must reset both `value` and
269
- // `textContent` so input/textarea AND contenteditable targets
270
- // are handled.
262
+ // Form fields and contenteditable targets use their native selection APIs.
271
263
  const clearCall = sendCalls[clearIdx]!;
272
264
  const fnDecl = (clearCall.params as { functionDeclaration: string })
273
265
  .functionDeclaration;
274
- expect(fnDecl).toContain('this.value = ""');
275
- expect(fnDecl).toContain("this.textContent");
276
- expect(fnDecl).toContain('new Event("input"');
266
+ expect(fnDecl).toContain("this.select()");
267
+ expect(fnDecl).toContain("range.selectNodeContents(this)");
268
+ expect(fnDecl).not.toContain('this.value = ""');
269
+ expect(fnDecl).not.toContain("this.textContent =");
277
270
 
278
- // After the clear, the helper re-focuses the element (some sites
279
- // blur on programmatic value reset) before inserting text — so we
280
- // expect at least 2 DOM.focus calls in total.
281
271
  const focusCount = methodSeq.filter((m) => m === "DOM.focus").length;
282
- expect(focusCount).toBeGreaterThanOrEqual(2);
272
+ expect(focusCount).toBe(1);
283
273
  });
284
274
 
285
275
  test("fills credential by CSS selector", async () => {
@@ -364,14 +354,13 @@ describe("executeBrowserFillCredential", () => {
364
354
  expect(result.isError).toBe(false);
365
355
  const methods = sendCalls.map((c) => c.method);
366
356
  expect(methods).toContain("Input.insertText");
367
- // dispatchKeyPress for "Enter" dispatches keyDown + char + keyUp
368
- // (Enter has text "\r" so it now produces a char event too).
357
+ // Enter dispatches rawKeyDown + char + keyUp.
369
358
  const keyEvents = sendCalls.filter(
370
359
  (c) => c.method === "Input.dispatchKeyEvent",
371
360
  );
372
361
  expect(keyEvents).toHaveLength(3);
373
362
  expect((keyEvents[0]!.params as { type: string; key: string }).type).toBe(
374
- "keyDown",
363
+ "rawKeyDown",
375
364
  );
376
365
  expect((keyEvents[0]!.params as { type: string; key: string }).key).toBe(
377
366
  "Enter",
@@ -338,7 +338,7 @@ describe("POST /v1/btw", () => {
338
338
  expect(options!.config!.modelIntent).toBeUndefined();
339
339
  });
340
340
 
341
- test("greeting requests pass callSite: 'emptyStateGreeting'", async () => {
341
+ test("greeting requests pass callSite: 'emptyStateGreeting' and send no tools", async () => {
342
342
  const provider = makeMockProvider();
343
343
  const session = makeMockSession(provider);
344
344
  mockGetOrCreateConversation.mockImplementationOnce(async () => session);
@@ -352,6 +352,9 @@ describe("POST /v1/btw", () => {
352
352
  expect(provider.sendMessage).toHaveBeenCalledTimes(1);
353
353
  const [, options] = provider.sendMessage.mock.calls[0];
354
354
  expect(options!.config!.callSite).toBe("emptyStateGreeting");
355
+ // The greeting targets no real conversation, so there is no cache prefix
356
+ // for tool definitions to share; they would only cost tokens.
357
+ expect(options!.tools).toEqual([]);
355
358
  });
356
359
 
357
360
  test("greeting requests include fresh turn context using the client timezone", async () => {
@@ -29,12 +29,12 @@ mock.module("../persistence/llm-request-log-store.js", () => ({
29
29
  // ── Imports (after mocks) ─────────────────────────────────────────────────────
30
30
  import { REDACTED_SENTINEL_OPEN } from "@vellumai/service-contracts/redacted-credential";
31
31
 
32
+ import type { ToolActivityMetadata } from "../api/events/tool-result.js";
32
33
  import type { ResolvedRevealCandidate } from "../daemon/chat-credential-redaction.js";
33
34
  import {
34
35
  buildPersistedAssistantContent,
35
36
  stampThinkingTiming,
36
37
  } from "../daemon/conversation-agent-loop-handlers.js";
37
- import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
38
38
  import type { ContentBlock } from "../providers/types.js";
39
39
  import {
40
40
  OPENAI_PROJECT_KEY_REDACTION_MARKER,
@@ -102,6 +102,7 @@ describe("call-conversation-messages", () => {
102
102
  const conversationId = "conv-call-msg-failed";
103
103
  ensureConversation(conversationId);
104
104
  const session = createCallSession({
105
+ direction: "inbound",
105
106
  conversationId,
106
107
  provider: "twilio",
107
108
  fromNumber: "+15550001111",
@@ -122,6 +123,7 @@ describe("call-conversation-messages", () => {
122
123
  const conversationId = "conv-call-msg-cancelled";
123
124
  ensureConversation(conversationId);
124
125
  const session = createCallSession({
126
+ direction: "inbound",
125
127
  conversationId,
126
128
  provider: "twilio",
127
129
  fromNumber: "+15550001111",
@@ -142,6 +144,7 @@ describe("call-conversation-messages", () => {
142
144
  const conversationId = "conv-call-msg-completed";
143
145
  ensureConversation(conversationId);
144
146
  const session = createCallSession({
147
+ direction: "inbound",
145
148
  conversationId,
146
149
  provider: "twilio",
147
150
  fromNumber: "+15550001111",
@@ -171,6 +174,7 @@ describe("call-conversation-messages", () => {
171
174
  const conversationId = "conv-call-msg-fallback";
172
175
  ensureConversation(conversationId);
173
176
  const session = createCallSession({
177
+ direction: "inbound",
174
178
  conversationId,
175
179
  provider: "twilio",
176
180
  fromNumber: "+12025550101",
@@ -208,6 +212,7 @@ describe("call-conversation-messages", () => {
208
212
  const conversationId = "conv-call-msg-schema";
209
213
  ensureConversation(conversationId);
210
214
  const session = createCallSession({
215
+ direction: "inbound",
211
216
  conversationId,
212
217
  provider: "twilio",
213
218
  fromNumber: "+12025550101",
@@ -54,9 +54,14 @@ function resetTables() {
54
54
  ensuredConvIds = new Set();
55
55
  }
56
56
 
57
- function createTestCallSession(opts: Parameters<typeof createCallSession>[0]) {
57
+ function createTestCallSession(
58
+ opts: Omit<Parameters<typeof createCallSession>[0], "direction"> & {
59
+ direction?: Parameters<typeof createCallSession>[0]["direction"];
60
+ },
61
+ ) {
58
62
  ensureConversation(opts.conversationId);
59
- return createCallSession(opts);
63
+ // Direction only shapes the funnel stamp; these suites test other things.
64
+ return createCallSession({ direction: "inbound", ...opts });
60
65
  }
61
66
 
62
67
  /** Backdate a session's createdAt so it appears older than the grace period. */