@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
@@ -31,6 +31,10 @@ import {
31
31
  type ConversationCreatedInfo,
32
32
  NotificationBroadcaster,
33
33
  } from "./broadcaster.js";
34
+ import {
35
+ intersectChannelAllowlist,
36
+ readChannelAllowlist,
37
+ } from "./channel-allowlist.js";
34
38
  import { enforceRoutingIntent, evaluateSignal } from "./decision-engine.js";
35
39
  import { updateDecision } from "./decisions-store.js";
36
40
  import {
@@ -51,6 +55,7 @@ import type {
51
55
  import type {
52
56
  NotificationChannel,
53
57
  NotificationDeliveryResult,
58
+ NotificationReceiptClass,
54
59
  } from "./types.js";
55
60
 
56
61
  const log = getLogger("emit-signal");
@@ -69,20 +74,27 @@ export function getBroadcaster(): NotificationBroadcaster {
69
74
  new PlatformPushAdapter(),
70
75
  ]);
71
76
 
72
- // Wire the conversation-created callback so the macOS client is notified
73
- // immediately when a vellum notification conversation is paired — before
74
- // slower channel deliveries (e.g. Telegram) delay the push.
77
+ // Wire the conversation-created callback so the event goes out as soon
78
+ // as a vellum notification conversation is paired, before slower channel
79
+ // deliveries (e.g. Telegram) run.
80
+ // A guardian-sensitive conversation's title reaches only the guardian's
81
+ // own connections, the same scoping the vellum adapter applies to the
82
+ // notification itself.
75
83
  broadcasterInstance.setOnConversationCreated((info) => {
76
- broadcastMessage({
77
- type: "notification_conversation_created",
78
- conversationId: info.conversationId,
79
- title: info.title,
80
- sourceEventName: info.sourceEventName,
81
- targetGuardianPrincipalId: info.targetGuardianPrincipalId,
82
- groupId: info.groupId,
83
- source: info.source,
84
- silent: info.silent,
85
- });
84
+ broadcastMessage(
85
+ {
86
+ type: "notification_conversation_created",
87
+ conversationId: info.conversationId,
88
+ title: info.title,
89
+ sourceEventName: info.sourceEventName,
90
+ targetGuardianPrincipalId: info.targetGuardianPrincipalId,
91
+ groupId: info.groupId,
92
+ source: info.source,
93
+ silent: info.silent,
94
+ },
95
+ undefined,
96
+ { targetActorPrincipalId: info.targetGuardianPrincipalId },
97
+ );
86
98
  log.info(
87
99
  {
88
100
  conversationId: info.conversationId,
@@ -240,7 +252,9 @@ export interface EmitSignalResult {
240
252
  deduplicated: boolean;
241
253
  dispatched: boolean;
242
254
  reason: string;
255
+ selectedChannels: NotificationChannel[];
243
256
  deliveryResults: NotificationDeliveryResult[];
257
+ receiptClass: NotificationReceiptClass;
244
258
  /**
245
259
  * True when the pipeline threw before reaching a verdict. Dispatch, dedupe,
246
260
  * suppression, and a blocked deterministic check are all verdicts. Callers
@@ -250,6 +264,55 @@ export interface EmitSignalResult {
250
264
  pipelineFailed: boolean;
251
265
  }
252
266
 
267
+ const PROVIDER_RECEIPT_CHANNELS = new Set(["telegram", "slack", "discord"]);
268
+
269
+ /**
270
+ * Strongest proof the emit path can report from adapter results. A vellum
271
+ * `sent` row means the assistant broadcast a `notification_intent`; it is
272
+ * not a client ACK that the OS posted a banner.
273
+ */
274
+ export function classifyNotificationReceipt(
275
+ results: readonly NotificationDeliveryResult[],
276
+ ): NotificationReceiptClass {
277
+ const accepted = results.filter((result) => {
278
+ return result.status === "sent" || result.status === "pending";
279
+ });
280
+ if (
281
+ accepted.some((result) => {
282
+ return PROVIDER_RECEIPT_CHANNELS.has(result.channel);
283
+ })
284
+ ) {
285
+ return "provider_accepted";
286
+ }
287
+ if (
288
+ accepted.some((result) => {
289
+ return result.channel === "platform";
290
+ })
291
+ ) {
292
+ return "gateway_accepted";
293
+ }
294
+ return "unknown";
295
+ }
296
+
297
+ function emitOutcome(
298
+ partial: Omit<
299
+ EmitSignalResult,
300
+ "deliveryResults" | "selectedChannels" | "receiptClass"
301
+ > & {
302
+ deliveryResults?: NotificationDeliveryResult[];
303
+ selectedChannels?: NotificationChannel[];
304
+ },
305
+ ): EmitSignalResult {
306
+ const deliveryResults = partial.deliveryResults ?? [];
307
+ const selectedChannels = partial.selectedChannels ?? [];
308
+ return {
309
+ ...partial,
310
+ selectedChannels,
311
+ deliveryResults,
312
+ receiptClass: classifyNotificationReceipt(deliveryResults),
313
+ };
314
+ }
315
+
253
316
  /**
254
317
  * True when at least one channel adapter has already been handed the signal.
255
318
  * `sent` is a completed delivery; `pending` is the platform push that passed
@@ -333,14 +396,13 @@ export async function emitNotificationSignal<TEventName extends string>(
333
396
  { signalId, dedupeKey: params.dedupeKey },
334
397
  "Signal deduplicated at event store level",
335
398
  );
336
- return {
399
+ return emitOutcome({
337
400
  signalId,
338
401
  deduplicated: true,
339
402
  dispatched: false,
340
403
  reason: "Signal deduplicated at event store level",
341
- deliveryResults: [],
342
404
  pipelineFailed: false,
343
- };
405
+ });
344
406
  }
345
407
  eventPersisted = true;
346
408
 
@@ -357,14 +419,13 @@ export async function emitNotificationSignal<TEventName extends string>(
357
419
  { signalId, reason: sourceActiveCheck.reason },
358
420
  "Signal suppressed before decision stage (source-active)",
359
421
  );
360
- return {
422
+ return emitOutcome({
361
423
  signalId,
362
424
  deduplicated: false,
363
425
  dispatched: false,
364
426
  reason: `Signal suppressed: ${sourceActiveCheck.reason}`,
365
- deliveryResults: [],
366
427
  pipelineFailed: false,
367
- };
428
+ });
368
429
  }
369
430
 
370
431
  // Step 2: Evaluate the signal through the decision engine
@@ -398,8 +459,31 @@ export async function emitNotificationSignal<TEventName extends string>(
398
459
  // dispatch rank, so selection order only matters to single_channel
399
460
  // enforcement's first-selected fallback (step 2.5b), which must keep
400
461
  // the in-app banner rather than a push the server may skip.
462
+ const exclusiveAllowlist = readChannelAllowlist(signal.contextPayload);
401
463
  const urgency = signal.attentionHints.urgency;
402
- if (
464
+ if (exclusiveAllowlist) {
465
+ // Exclusive allowlist wins over the default set, urgent force, and
466
+ // routing-intent expansion. Delivery is the allowlist intersected
467
+ // with connected channels.
468
+ const allowed = intersectChannelAllowlist(
469
+ exclusiveAllowlist,
470
+ connectedChannels,
471
+ );
472
+ const alreadyMatches =
473
+ allowed.length === decision.selectedChannels.length &&
474
+ allowed.every((channel) => {
475
+ return decision.selectedChannels.includes(channel);
476
+ }) &&
477
+ decision.shouldNotify === allowed.length > 0;
478
+ if (!alreadyMatches) {
479
+ decision = {
480
+ ...decision,
481
+ selectedChannels: allowed,
482
+ shouldNotify: allowed.length > 0,
483
+ reasoningSummary: `${decision.reasoningSummary} (channelAllowlist: ${allowed.join(", ") || "none available"})`,
484
+ };
485
+ }
486
+ } else if (
403
487
  (urgency === "high" || urgency === "critical") &&
404
488
  decision.shouldNotify
405
489
  ) {
@@ -427,13 +511,16 @@ export async function emitNotificationSignal<TEventName extends string>(
427
511
  }
428
512
  }
429
513
 
430
- // Step 2.5b: Enforce routing intent policy (fire-time guard)
431
- decision = enforceRoutingIntent(
432
- decision,
433
- signal.routingIntent,
434
- connectedChannels,
435
- signal.sourceChannel,
436
- );
514
+ // Step 2.5b: Enforce routing intent policy (fire-time guard).
515
+ // An exclusive allowlist already closed the channel set.
516
+ if (!exclusiveAllowlist) {
517
+ decision = enforceRoutingIntent(
518
+ decision,
519
+ signal.routingIntent,
520
+ connectedChannels,
521
+ signal.sourceChannel,
522
+ );
523
+ }
437
524
 
438
525
  // Step 2.5c: Access-request signals carry a decisionable canonical
439
526
  // guardian request created before this emit, and that row suppresses
@@ -509,22 +596,21 @@ export async function emitNotificationSignal<TEventName extends string>(
509
596
  { signalId, reason: checkResult.reason },
510
597
  "Signal blocked by deterministic checks",
511
598
  );
512
- return {
599
+ return emitOutcome({
513
600
  signalId,
514
601
  deduplicated: false,
515
602
  dispatched: false,
516
603
  reason: `Signal blocked by deterministic checks: ${checkResult.reason}`,
517
- deliveryResults: [],
518
604
  pipelineFailed: false,
519
- };
605
+ });
520
606
  }
521
607
  }
522
608
 
523
609
  // Step 4: Dispatch through the broadcaster
524
610
  // Note: notification_conversation_created events are emitted eagerly inside
525
611
  // the broadcaster as soon as vellum conversation pairing succeeds, rather
526
- // than after all channel deliveries complete. This avoids a race where
527
- // slow Telegram delivery delays the push past the macOS deep-link retry.
612
+ // than after all channel deliveries complete, so a slow Telegram delivery
613
+ // cannot delay it.
528
614
  const broadcaster = getBroadcaster();
529
615
  const dispatchResult = await dispatchDecision(
530
616
  signal,
@@ -562,14 +648,15 @@ export async function emitNotificationSignal<TEventName extends string>(
562
648
  },
563
649
  "Signal pipeline complete",
564
650
  );
565
- return {
651
+ return emitOutcome({
566
652
  signalId,
567
653
  deduplicated: false,
568
654
  dispatched: dispatchResult.dispatched,
569
655
  reason: dispatchResult.reason,
656
+ selectedChannels: decision.selectedChannels,
570
657
  deliveryResults: dispatchResult.deliveryResults,
571
658
  pipelineFailed: false,
572
- };
659
+ });
573
660
  } catch (err) {
574
661
  const errMsg = err instanceof Error ? err.message : String(err);
575
662
  log.error(
@@ -599,13 +686,12 @@ export async function emitNotificationSignal<TEventName extends string>(
599
686
  if (params.throwOnError) {
600
687
  throw err instanceof Error ? err : new Error(errMsg);
601
688
  }
602
- return {
689
+ return emitOutcome({
603
690
  signalId,
604
691
  deduplicated: false,
605
692
  dispatched: false,
606
693
  reason: `Signal pipeline failed: ${errMsg}`,
607
- deliveryResults: [],
608
694
  pipelineFailed: true,
609
- };
695
+ });
610
696
  }
611
697
  }
@@ -30,6 +30,7 @@ import {
30
30
  import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
31
31
  import { getLogger } from "../util/logger.js";
32
32
  import { normalizeTitle, stripMarkdown } from "../util/short-title.js";
33
+ import { readChannelAllowlist } from "./channel-allowlist.js";
33
34
  import { isConversationSeedSane } from "./conversation-seed-composer.js";
34
35
  import { deriveTitle } from "./copy-composer.js";
35
36
  import {
@@ -459,12 +460,10 @@ const EVENT_CATEGORY_MAP: Record<string, FeedItemCategory> = {
459
460
 
460
461
  /**
461
462
  * Map a signal's source event to a feed category, or nothing when the event
462
- * has no entry. An unmapped event used to land in `system`, a bucket named
463
- * for our architecture rather than the user's world, and every deliberate
464
- * assistant notification (`user.send_notification`) ended up there. The
465
- * category is optional on the wire, so an event without a home simply
466
- * carries none: readers that filter by category skip it, and nothing has
467
- * to guess.
463
+ * has no entry. An unmapped event, such as a deliberate assistant
464
+ * notification (`assistant.share`), carries no category rather than a
465
+ * catch-all bucket named for our architecture: readers that filter by
466
+ * category skip it, and nothing has to guess.
468
467
  */
469
468
  function deriveCategory(
470
469
  signal: NotificationSignal,
@@ -558,7 +557,10 @@ function resolveHomeFeedMirror(
558
557
  signal.sourceChannel === "assistant_tool" ||
559
558
  signal.sourceEventName === "chat.assistant_reply"
560
559
  ) {
561
- return { mirror: true, sourceConversationId, sourceScheduleJobId };
560
+ const allowlist = readChannelAllowlist(signal.contextPayload);
561
+ if (!allowlist || allowlist.includes("vellum")) {
562
+ return { mirror: true, sourceConversationId, sourceScheduleJobId };
563
+ }
562
564
  }
563
565
  if (signal.attentionHints.isAsyncBackground) {
564
566
  return { mirror: true, sourceConversationId, sourceScheduleJobId };
@@ -71,6 +71,25 @@ export function readPayloadObject(
71
71
  return isPlainObject(value) ? value : undefined;
72
72
  }
73
73
 
74
+ /**
75
+ * Safely read a string-array property from an unknown-typed payload object.
76
+ * Non-string entries are dropped. Returns `undefined` when the payload is
77
+ * not an object or the key does not hold an array.
78
+ */
79
+ export function readPayloadStringArray(
80
+ payload: unknown,
81
+ key: string,
82
+ ): string[] | undefined {
83
+ if (!isPlainObject(payload)) {
84
+ return undefined;
85
+ }
86
+ const value = payload[key];
87
+ if (!Array.isArray(value)) {
88
+ return undefined;
89
+ }
90
+ return value.filter((entry): entry is string => typeof entry === "string");
91
+ }
92
+
74
93
  /** Truncate `text` to `maxLength`, appending "…" when exceeded. */
75
94
  export function truncate(text: string, maxLength: number): string {
76
95
  if (text.length <= maxLength) {
@@ -59,8 +59,9 @@ export function isNotificationSourceChannel(
59
59
 
60
60
  export const NOTIFICATION_SOURCE_EVENT_NAMES = [
61
61
  {
62
- id: "user.send_notification",
63
- description: "User-initiated notification via assistant tool",
62
+ id: "assistant.share",
63
+ description:
64
+ "Assistant chose to tell the user something (`assistant notifications send`)",
64
65
  },
65
66
  {
66
67
  id: "schedule.notify",
@@ -113,28 +114,12 @@ export const NOTIFICATION_SOURCE_EVENT_NAMES = [
113
114
  id: "watcher.notification",
114
115
  description: "Watcher detected a notable event",
115
116
  },
116
- {
117
- id: "watcher.escalation",
118
- description: "Watcher event requiring immediate attention",
119
- },
120
- {
121
- id: "tool_confirmation.required_action",
122
- description: "Tool requires user confirmation before executing",
123
- },
124
117
  { id: "activity.complete", description: "Background activity finished" },
125
118
  {
126
119
  id: "activity.failed",
127
120
  description:
128
121
  "Background job execution failed (model_provider, exception, or timeout)",
129
122
  },
130
- {
131
- id: "quick_chat.response_ready",
132
- description: "Quick chat response ready for review",
133
- },
134
- {
135
- id: "voice.response_ready",
136
- description: "Voice response ready for playback",
137
- },
138
123
  {
139
124
  id: "credential.health_alert",
140
125
  description:
@@ -251,7 +236,7 @@ export interface NotificationSignal<TEventName extends string = string> {
251
236
  createdAt: number; // epoch ms
252
237
  sourceChannel: NotificationSourceChannel; // see NOTIFICATION_SOURCE_CHANNELS registry
253
238
  sourceContextId: string;
254
- sourceEventName: TEventName; // free-form: 'reminder_fired', 'guardian_question', etc.
239
+ sourceEventName: TEventName; // free-form; see NOTIFICATION_SOURCE_EVENT_NAMES
255
240
  contextPayload: NotificationContextPayload<TEventName>;
256
241
  attentionHints: AttentionHints;
257
242
  /**
@@ -46,6 +46,21 @@ export type NotificationDeliveryResult = z.infer<
46
46
  typeof NotificationDeliveryResultSchema
47
47
  >;
48
48
 
49
+ /**
50
+ * Strongest delivery proof the emit path can report. These are receipts,
51
+ * not intents: `client_os_posted` is reserved for a client ACK that the
52
+ * OS banner was posted, which this return path does not observe.
53
+ */
54
+ export const NotificationReceiptClassSchema = z.enum([
55
+ "provider_accepted",
56
+ "gateway_accepted",
57
+ "client_os_posted",
58
+ "unknown",
59
+ ]);
60
+ export type NotificationReceiptClass = z.infer<
61
+ typeof NotificationReceiptClassSchema
62
+ >;
63
+
49
64
  // -- Channel adapter data shapes ----------------------------------------------
50
65
 
51
66
  export const DeliveryResultSchema = z.object({
@@ -467,12 +467,11 @@ async function fetchPlatformConnections(options: {
467
467
  }
468
468
 
469
469
  const body = (await response.json()) as unknown;
470
- const connections = (
470
+ return (
471
471
  Array.isArray(body)
472
472
  ? body
473
473
  : ((body as Record<string, unknown>).results ?? [])
474
474
  ) as PlatformConnectionEntry[];
475
- return connections;
476
475
  }
477
476
 
478
477
  /**
@@ -678,8 +678,6 @@ export async function upsertApp(
678
678
  );
679
679
  }
680
680
 
681
- const defaultCredPath = (appId: string) => oauthAppClientSecretPath(appId);
682
-
683
681
  // Verify the credential path points to an existing secret.
684
682
  if (clientSecretCredentialPath) {
685
683
  const existing = await getSecureKeyAsync(clientSecretCredentialPath);
@@ -739,7 +737,7 @@ export async function upsertApp(
739
737
 
740
738
  const now = Date.now();
741
739
  const id = uuid();
742
- const credPath = clientSecretCredentialPath ?? defaultCredPath(id);
740
+ const credPath = clientSecretCredentialPath ?? oauthAppClientSecretPath(id);
743
741
 
744
742
  const row = {
745
743
  id,
@@ -19,6 +19,7 @@ export type SerializedProvider = ReturnType<typeof serializeProvider> &
19
19
  Record<string, unknown>;
20
20
 
21
21
  import { isChannelBotProvider } from "@vellumai/service-contracts/channels";
22
+ import type { IntegrationCategory } from "@vellumai/service-contracts/integration-categories";
22
23
 
23
24
  import { PROVIDER_SEED_DATA } from "./seed-providers.js";
24
25
 
@@ -38,6 +39,11 @@ export interface SerializedProviderSummary {
38
39
  supports_managed_mode: boolean;
39
40
  managed_service_is_paid: boolean;
40
41
  feature_flag: string | null;
42
+ /**
43
+ * Where the integrations catalog files the provider. `null` for a row with
44
+ * no seed entry, such as a provider a user registered by hand.
45
+ */
46
+ category: IntegrationCategory | null;
41
47
  /**
42
48
  * Per-tenant providers only (Shopify): what a client must collect before
43
49
  * starting a managed connect, since the provider's OAuth endpoints live on
@@ -173,6 +179,7 @@ export function serializeProviderSummary(
173
179
  supports_managed_mode: !!row.managedServiceConfigKey,
174
180
  managed_service_is_paid: !!row.managedServiceIsPaid,
175
181
  feature_flag: row.featureFlag ?? null,
182
+ category: PROVIDER_SEED_DATA[row.provider]?.category ?? null,
176
183
  tenant_host: PROVIDER_SEED_DATA[row.provider]?.tenantHost ?? null,
177
184
  acts_as: isChannelBotProvider(row.provider) ? "assistant" : "user",
178
185
  };