@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
@@ -281,7 +281,9 @@ describe("writeHomeFeedItemForSignal", () => {
281
281
  "The deployment completed successfully.",
282
282
  );
283
283
  expect(appendCalls[0]!.conversationId).toBe("conv-source-1");
284
- expect(appendCalls[0]!.metadata?.notificationConversationMessageId).toBeUndefined();
284
+ expect(
285
+ appendCalls[0]!.metadata?.notificationConversationMessageId,
286
+ ).toBeUndefined();
285
287
  expect(messageAppends).toEqual([]);
286
288
  expect(messagesInvalidated).toEqual([]);
287
289
  expect(conversationLookups).toEqual(["conv-source-1", "conv-source-1"]);
@@ -352,6 +354,93 @@ describe("writeHomeFeedItemForSignal", () => {
352
354
  expect(conversationLookups).toEqual(["cli-12345"]);
353
355
  });
354
356
 
357
+ test("assistant_tool source skips the automatic Home mirror when channelAllowlist omits vellum", async () => {
358
+ conversationRow = null;
359
+ const signal = makeSignal({
360
+ sourceChannel: "assistant_tool",
361
+ sourceEventName: "assistant.share",
362
+ sourceContextId: "cli-12345",
363
+ contextPayload: {
364
+ title: "Telegram only",
365
+ channelAllowlist: ["telegram"],
366
+ },
367
+ attentionHints: {
368
+ requiresAction: false,
369
+ urgency: "critical",
370
+ isAsyncBackground: false,
371
+ visibleInSourceNow: false,
372
+ },
373
+ });
374
+ const decision = makeDecision({
375
+ selectedChannels: ["telegram"],
376
+ renderedCopy: {
377
+ telegram: { title: "Telegram only", body: "Alarm body" },
378
+ },
379
+ });
380
+
381
+ const item = await writeHomeFeedItemForSignal(signal, decision);
382
+
383
+ expect(item).toBeNull();
384
+ expect(appendCalls).toHaveLength(0);
385
+ });
386
+
387
+ test("assistant_tool source still mirrors when channelAllowlist includes vellum", async () => {
388
+ conversationRow = null;
389
+ const signal = makeSignal({
390
+ sourceChannel: "assistant_tool",
391
+ sourceEventName: "assistant.share",
392
+ sourceContextId: "cli-12345",
393
+ contextPayload: {
394
+ title: "Inbox and telegram",
395
+ channelAllowlist: ["vellum", "telegram"],
396
+ },
397
+ });
398
+ const decision = makeDecision({
399
+ selectedChannels: ["vellum", "telegram"],
400
+ renderedCopy: {
401
+ vellum: { title: "Inbox and telegram", body: "Shared body" },
402
+ },
403
+ });
404
+
405
+ const item = await writeHomeFeedItemForSignal(signal, decision);
406
+
407
+ expect(item).not.toBeNull();
408
+ expect(appendCalls).toHaveLength(1);
409
+ });
410
+
411
+ test("assistant_tool exclusive allowlist still mirrors when isAsyncBackground is set", async () => {
412
+ conversationRow = null;
413
+ const signal = makeSignal({
414
+ sourceChannel: "assistant_tool",
415
+ sourceEventName: "assistant.share",
416
+ sourceContextId: "cli-12345",
417
+ contextPayload: {
418
+ title: "Background telegram",
419
+ channelAllowlist: ["telegram"],
420
+ },
421
+ attentionHints: {
422
+ requiresAction: false,
423
+ urgency: "low",
424
+ isAsyncBackground: true,
425
+ visibleInSourceNow: false,
426
+ },
427
+ });
428
+ const decision = makeDecision({
429
+ selectedChannels: ["telegram"],
430
+ renderedCopy: {
431
+ telegram: { title: "Background telegram", body: "Still a feed item" },
432
+ },
433
+ });
434
+
435
+ const item = await writeHomeFeedItemForSignal(signal, decision);
436
+
437
+ expect(item).not.toBeNull();
438
+ expect(appendCalls).toHaveLength(1);
439
+ expect(appendCalls[0]!.noteworthy).toBe(true);
440
+ expect(appendCalls[0]!.conversationId).toBeUndefined();
441
+ expect(conversationLookups).toEqual(["cli-12345"]);
442
+ });
443
+
355
444
  test("source conversation id does not propagate when the lookup misses", async () => {
356
445
  // When `sourceContextId` does not resolve to a real conversation row
357
446
  // (e.g. scheduler job id, watcher event id), the item is still mirrored
@@ -956,7 +1045,7 @@ describe("writeHomeFeedItemForSignal", () => {
956
1045
  conversationRow = { conversationType: "background" };
957
1046
  const signal = makeSignal({
958
1047
  sourceChannel: "assistant_tool",
959
- sourceEventName: "user.send_notification",
1048
+ sourceEventName: "assistant.share",
960
1049
  contextPayload: { title: "Tool share", body: "Body" },
961
1050
  });
962
1051
 
@@ -970,7 +1059,7 @@ describe("writeHomeFeedItemForSignal", () => {
970
1059
  conversationRow = { conversationType: "background" };
971
1060
  const signal = makeSignal({
972
1061
  sourceChannel: "assistant_tool",
973
- sourceEventName: "user.send_notification",
1062
+ sourceEventName: "assistant.share",
974
1063
  contextPayload: { title: "Tool share", body: "Body" },
975
1064
  });
976
1065
 
@@ -1126,7 +1215,7 @@ describe("writeHomeFeedItemForSignal", () => {
1126
1215
  conversationRow = { conversationType: "background" };
1127
1216
  const signal = makeSignal({
1128
1217
  sourceChannel: "assistant_tool",
1129
- sourceEventName: "user.send_notification",
1218
+ sourceEventName: "assistant.share",
1130
1219
  contextPayload: { title: "Tool share", body: "Body" },
1131
1220
  });
1132
1221
 
@@ -5,6 +5,7 @@ import {
5
5
  decodeLiteralLineBreaks,
6
6
  describeMedia,
7
7
  mediaEmbeds,
8
+ readPayloadStringArray,
8
9
  sanitizeMultilineMessagePreview,
9
10
  stripMarkdownForPreview,
10
11
  } from "../notification-utils.js";
@@ -280,6 +281,23 @@ describe("describeMedia", () => {
280
281
  });
281
282
  });
282
283
 
284
+ describe("readPayloadStringArray", () => {
285
+ test("returns string entries and drops non-strings", () => {
286
+ expect(
287
+ readPayloadStringArray(
288
+ { channelAllowlist: ["telegram", 1, "slack"] },
289
+ "channelAllowlist",
290
+ ),
291
+ ).toEqual(["telegram", "slack"]);
292
+ });
293
+
294
+ test("returns undefined when the key is missing or not an array", () => {
295
+ expect(readPayloadStringArray({ channelAllowlist: "telegram" }, "channelAllowlist")).toBeUndefined();
296
+ expect(readPayloadStringArray({}, "channelAllowlist")).toBeUndefined();
297
+ expect(readPayloadStringArray(null, "channelAllowlist")).toBeUndefined();
298
+ });
299
+ });
300
+
283
301
  describe("decodeLiteralLineBreaks", () => {
284
302
  test("turns literal newline escapes into real line breaks", () => {
285
303
  expect(decodeLiteralLineBreaks("Line one\\n\\nLine two")).toBe(
@@ -2,26 +2,26 @@
2
2
  * Vellum channel adapter — delivers notifications to connected desktop
3
3
  * and mobile clients via the daemon's event broadcast mechanism.
4
4
  *
5
- * The adapter broadcasts a `notification_intent` message that the Vellum
6
- * client uses for two distinct purposes: paired-conversation bookkeeping
7
- * (mark-unseen + history catch-up, fallback dedup) and posting an OS
8
- * banner via `UNUserNotificationCenter`. The banner posting is gated by
9
- * the `silent` flag — set to true for non-urgent (`low`/`medium`) signals
10
- * so the notification center inbox still receives the entry but the OS
11
- * does not surface a push banner. Urgent signals (`high`/`critical`)
12
- * broadcast with `silent: false` and fire the banner.
5
+ * The adapter broadcasts a `notification_intent` message that the client
6
+ * turns into an OS notification (`use-notification-intent-sync.ts` in the
7
+ * web client, which every first-party app runs). The `silent` flag is true
8
+ * for non-urgent (`low`/`medium`) signals, and the client posts nothing for
9
+ * those: they reach their conversation (and the home feed, for background
10
+ * work) without a banner.
11
+ * Urgent signals (`high`/`critical`) broadcast with `silent: false` and
12
+ * banner.
13
13
  *
14
14
  * Guardian-sensitive notifications (approval requests, access requests)
15
- * are annotated with `targetGuardianPrincipalId` so that only clients
16
- * bound to the guardian identity display them. Non-guardian clients
17
- * should ignore notifications with a `targetGuardianPrincipalId` that
18
- * does not match their own identity.
15
+ * are delivered only to connections authenticated as the guardian: the hub
16
+ * matches `targetActorPrincipalId` against each connection's verified
17
+ * principal, so no other connection ever receives the title and body. The
18
+ * payload's `targetGuardianPrincipalId` records that scoping for clients.
19
19
  */
20
20
 
21
21
  import type { AssistantEvent } from "../../api/index.js";
22
- import type { InterfaceId } from "../../channels/types.js";
23
22
  import { getAssistantName } from "../../daemon/identity-helpers.js";
24
23
  import { updateMessageContent } from "../../persistence/conversation-crud.js";
24
+ import type { BroadcastMessageOptions } from "../../runtime/assistant-event-hub.js";
25
25
  import { publishConversationMessagesChanged } from "../../runtime/sync/resource-sync-events.js";
26
26
  import { getLogger } from "../../util/logger.js";
27
27
  import type {
@@ -36,28 +36,16 @@ import type {
36
36
 
37
37
  const log = getLogger("notif-adapter-vellum");
38
38
 
39
- /**
40
- * Optional targeting/filtering applied at the hub when a broadcast is
41
- * emitted. Mirrors the third argument of
42
- * `broadcastMessage()` in `runtime/assistant-event-hub.ts`. Callers can
43
- * use `targetInterfaceId` to scope a legacy message to a single client
44
- * surface (e.g. macOS) during a migration window.
45
- */
46
- export interface BroadcastFnOptions {
47
- targetClientId?: string;
48
- targetInterfaceId?: InterfaceId;
49
- }
50
-
51
39
  export type BroadcastFn = (
52
40
  msg: AssistantEvent,
53
41
  conversationId?: string,
54
- options?: BroadcastFnOptions,
42
+ options?: BroadcastMessageOptions,
55
43
  ) => void;
56
44
 
57
45
  /**
58
46
  * Event name prefixes that carry guardian-sensitive content (approval
59
- * requests, access requests). Notifications for these events are scoped
60
- * to bound guardian devices via `targetGuardianPrincipalId`.
47
+ * requests, access requests). Notifications for these events reach only
48
+ * the guardian's own connections.
61
49
  */
62
50
  const GUARDIAN_SENSITIVE_EVENT_PREFIXES = [
63
51
  "guardian.question",
@@ -86,10 +74,9 @@ export class VellumAdapter implements ChannelAdapter {
86
74
  destination: ChannelDestination,
87
75
  ): Promise<DeliveryResult> {
88
76
  try {
89
- // For guardian-sensitive events, annotate the outbound message with
90
- // the target guardian identity so clients can filter. The
91
- // guardianPrincipalId comes from the vellum binding resolved by
92
- // the destination resolver.
77
+ // For guardian-sensitive events, deliver only to the guardian's own
78
+ // connections. The guardianPrincipalId comes from the vellum binding
79
+ // resolved by the destination resolver.
93
80
  const guardianPrincipalId =
94
81
  typeof destination.metadata?.guardianPrincipalId === "string"
95
82
  ? destination.metadata.guardianPrincipalId
@@ -104,20 +91,24 @@ export class VellumAdapter implements ChannelAdapter {
104
91
  payload.urgency !== "high" && payload.urgency !== "critical";
105
92
  const assistantName = getAssistantName()?.trim() || undefined;
106
93
 
107
- this.broadcast({
108
- type: "notification_intent",
109
- deliveryId: payload.deliveryId,
110
- correlationId: payload.correlationId,
111
- sourceEventName: payload.sourceEventName,
112
- ...(assistantName ? { assistantName } : {}),
113
- title: payload.copy.title,
114
- body: payload.copy.body,
115
- deepLinkMetadata: payload.deepLinkTarget,
116
- targetGuardianPrincipalId,
117
- silent,
118
- remotePushDispatched: payload.remotePushDispatched,
119
- remotePushPlatforms: payload.remotePushPlatforms,
120
- } as AssistantEvent);
94
+ this.broadcast(
95
+ {
96
+ type: "notification_intent",
97
+ deliveryId: payload.deliveryId,
98
+ correlationId: payload.correlationId,
99
+ sourceEventName: payload.sourceEventName,
100
+ ...(assistantName ? { assistantName } : {}),
101
+ title: payload.copy.title,
102
+ body: payload.copy.body,
103
+ deepLinkMetadata: payload.deepLinkTarget,
104
+ targetGuardianPrincipalId,
105
+ silent,
106
+ remotePushDispatched: payload.remotePushDispatched,
107
+ remotePushPlatforms: payload.remotePushPlatforms,
108
+ },
109
+ undefined,
110
+ { targetActorPrincipalId: targetGuardianPrincipalId },
111
+ );
121
112
 
122
113
  log.info(
123
114
  {
@@ -241,8 +241,8 @@ export interface ConversationCreatedInfo {
241
241
  /** Semantic source from the signal producer (e.g. "schedule", "reminder"). */
242
242
  source?: string;
243
243
  /**
244
- * Mirrors the vellum adapter's `silent` flag. When true the client
245
- * must skip the fallback OS banner — the sidebar entry still appears.
244
+ * Mirrors the vellum adapter's `silent` flag (true for low- and
245
+ * medium-urgency signals).
246
246
  */
247
247
  silent: boolean;
248
248
  }
@@ -354,10 +354,9 @@ export class NotificationBroadcaster {
354
354
 
355
355
  // Ensure vellum is processed first so the notification_conversation_created
356
356
  // event fires immediately, before slower channel sends (e.g. Telegram 30s
357
- // timeout) can delay it past the macOS deep-link retry window. Platform
358
- // sorts second: the vellum intent carries remotePushDispatched, so its
359
- // deferred send (below) flushes right after the platform outcome is known
360
- // instead of waiting behind slower channels.
357
+ // timeout) can delay it. Platform sorts second: the vellum intent carries
358
+ // remotePushDispatched, so its deferred send (below) flushes right after
359
+ // the platform outcome is known instead of waiting behind slower channels.
361
360
  const dispatchRank = (channel: NotificationChannel): number => {
362
361
  if (channel === "vellum") {
363
362
  return 0;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Exclusive channel allowlist carried on a signal's `contextPayload`.
3
+ *
4
+ * When `channelAllowlist` is a non-empty string array, delivery is restricted
5
+ * to that set intersected with connected channels. Urgent force and
6
+ * routing-intent expansion do not add channels outside the allowlist.
7
+ * `preferredChannels` stays additive and is ignored when an allowlist is
8
+ * present.
9
+ */
10
+
11
+ import { readPayloadStringArray } from "./notification-utils.js";
12
+ import type { NotificationChannel } from "./types.js";
13
+
14
+ export function readChannelAllowlist(payload: unknown): string[] | undefined {
15
+ const raw = readPayloadStringArray(payload, "channelAllowlist");
16
+ if (!raw) {
17
+ return undefined;
18
+ }
19
+ const channels = raw.map((entry) => entry.trim()).filter((entry) => {
20
+ return entry.length > 0;
21
+ });
22
+ return channels.length > 0 ? channels : undefined;
23
+ }
24
+
25
+ export function hasExclusiveChannelAllowlist(payload: unknown): boolean {
26
+ return readChannelAllowlist(payload) !== undefined;
27
+ }
28
+
29
+ export function intersectChannelAllowlist(
30
+ allowlist: readonly string[],
31
+ available: readonly NotificationChannel[],
32
+ ): NotificationChannel[] {
33
+ const availableSet = new Set<string>(available);
34
+ const seen = new Set<string>();
35
+ const selected: NotificationChannel[] = [];
36
+ for (const channel of allowlist) {
37
+ if (!availableSet.has(channel) || seen.has(channel)) {
38
+ continue;
39
+ }
40
+ seen.add(channel);
41
+ selected.push(channel as NotificationChannel);
42
+ }
43
+ return selected;
44
+ }
@@ -331,19 +331,6 @@ const TEMPLATES: Partial<Record<NotificationSourceEventName, CopyTemplate>> = {
331
331
  body: str(payload.body, "A watcher event occurred"),
332
332
  }),
333
333
 
334
- "watcher.escalation": (payload) => ({
335
- title: str(payload.title, "Watcher Escalation"),
336
- body: str(payload.body, "A watcher event requires your attention"),
337
- }),
338
-
339
- "tool_confirmation.required_action": (payload) => {
340
- const toolName = str(payload.toolName, "A tool");
341
- return {
342
- title: `${toolName} needs your confirmation`,
343
- body: `${toolName} requires your confirmation`,
344
- };
345
- },
346
-
347
334
  // Titled by what was done rather than by the kind of event: the summary's
348
335
  // first sentence is the outcome ("Finished the fuel-system diagnostic app"),
349
336
  // which is what a reader scanning the bell wants to see. A producer that
@@ -385,16 +372,6 @@ const TEMPLATES: Partial<Record<NotificationSourceEventName, CopyTemplate>> = {
385
372
  body: summary !== "" ? summary : describeUnclassifiedFailure(payload),
386
373
  };
387
374
  },
388
-
389
- "quick_chat.response_ready": (payload) => ({
390
- title: "Quick chat reply ready",
391
- body: str(payload.preview, "Your quick chat response is ready"),
392
- }),
393
-
394
- "voice.response_ready": (payload) => ({
395
- title: "Voice reply ready",
396
- body: str(payload.preview, "A voice response is ready"),
397
- }),
398
375
  };
399
376
 
400
377
  /**
@@ -35,6 +35,10 @@ import {
35
35
  buildAccessRequestSeedContentBlocks,
36
36
  buildToolApprovalSeedContentBlocks,
37
37
  } from "./approval-card-data.js";
38
+ import {
39
+ intersectChannelAllowlist,
40
+ readChannelAllowlist,
41
+ } from "./channel-allowlist.js";
38
42
  import {
39
43
  buildConversationCandidates,
40
44
  type ConversationCandidateSet,
@@ -52,6 +56,7 @@ import {
52
56
  nonEmpty,
53
57
  readPayloadObject,
54
58
  readPayloadString,
59
+ readPayloadStringArray,
55
60
  } from "./notification-utils.js";
56
61
  import { getPreferenceSummary } from "./preference-summary.js";
57
62
  import type { NotificationSignal, RoutingIntent } from "./signal.js";
@@ -887,22 +892,27 @@ export async function evaluateSignal(
887
892
  signal.attentionHints.urgency,
888
893
  availableChannels,
889
894
  );
890
- // Honor `--preferred-channels` as ADDITIVE push targets on top of
891
- // the default channel set. The notification center (vellum) is the
892
- // always-on canonical inbox; preferred channels add push surfaces
893
- // on top, they never replace vellum. Disconnected channels are
894
- // filtered out so we never try to deliver on something unavailable.
895
- const preferredChannelsRaw = Array.isArray(payload.preferredChannels)
896
- ? (payload.preferredChannels as unknown[]).filter(
897
- (c): c is string => typeof c === "string",
898
- )
899
- : undefined;
895
+ // `channelAllowlist` is exclusive: only those available channels are
896
+ // selected. `preferredChannels` is additive on top of the default set
897
+ // (vellum stays the canonical inbox) and is ignored when an allowlist
898
+ // is present. Disconnected names are dropped so we never deliver on
899
+ // something unavailable.
900
+ const exclusiveAllowlist = readChannelAllowlist(payload);
901
+ const preferredChannelsRaw = readPayloadStringArray(
902
+ payload,
903
+ "preferredChannels",
904
+ );
900
905
  let selectedChannels = defaultChannels;
901
- if (preferredChannelsRaw && preferredChannelsRaw.length > 0) {
906
+ if (exclusiveAllowlist) {
907
+ selectedChannels = intersectChannelAllowlist(
908
+ exclusiveAllowlist,
909
+ availableChannels,
910
+ );
911
+ } else if (preferredChannelsRaw && preferredChannelsRaw.length > 0) {
902
912
  const availableSet = new Set<string>(availableChannels);
903
- const preferredAvailable = preferredChannelsRaw.filter((c) =>
904
- availableSet.has(c),
905
- ) as NotificationChannel[];
913
+ const preferredAvailable = preferredChannelsRaw.filter((c) => {
914
+ return availableSet.has(c);
915
+ }) as NotificationChannel[];
906
916
  if (preferredAvailable.length > 0) {
907
917
  selectedChannels = Array.from(
908
918
  new Set<NotificationChannel>([
@@ -254,7 +254,7 @@ function checkDedupe(
254
254
  /**
255
255
  * Fail-closed check that the rendered copy is real text and not an
256
256
  * accidental fallback leak (empty body, or body that is just the raw
257
- * source event name like "user.send_notification").
257
+ * source event name like "assistant.share").
258
258
  *
259
259
  * Only validates channels that the decision engine actually emitted
260
260
  * copy for. Channels appended after the decision (urgency-forced