@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.
- package/ARCHITECTURE.md +43 -29
- package/Dockerfile +63 -0
- package/docs/architecture/security.md +10 -5
- package/docs/architecture/turn-actor.md +2 -0
- package/docs/desktop-browser-cli.md +45 -4
- package/docs/live-voice-task-outcomes.md +100 -0
- package/docs/mode-sessions.md +183 -0
- package/docs/voice-input-diagnostics.md +58 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
- package/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
- package/openapi.yaml +665 -14
- package/package.json +1 -1
- package/scripts/smoke-desktop-browser-cli.ts +10 -26
- package/scripts/smoke-desktop-window-theme.ts +2 -3
- package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
- package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/answered-question-persistence.test.ts +2 -0
- package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
- package/src/__tests__/approval-message-composer.test.ts +1 -118
- package/src/__tests__/approval-routes-http.test.ts +3 -0
- package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
- package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
- package/src/__tests__/assistant-event-targeting.test.ts +186 -0
- package/src/__tests__/assistant-stream-state.test.ts +29 -9
- package/src/__tests__/browser-fill-credential.test.ts +11 -22
- package/src/__tests__/btw-routes.test.ts +4 -1
- package/src/__tests__/build-persisted-content.test.ts +1 -1
- package/src/__tests__/call-conversation-messages.test.ts +5 -0
- package/src/__tests__/call-recovery.test.ts +7 -2
- package/src/__tests__/call-routes-http.test.ts +12 -0
- package/src/__tests__/call-store.test.ts +7 -2
- package/src/__tests__/channel-retry-sweep.test.ts +9 -0
- package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
- package/src/__tests__/checker.test.ts +11 -3
- package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
- package/src/__tests__/computer-use-tools.test.ts +1 -233
- package/src/__tests__/config-model-image-provider.test.ts +39 -0
- package/src/__tests__/config-schema.test.ts +16 -0
- package/src/__tests__/config-watcher.test.ts +24 -0
- package/src/__tests__/context-search-agent-runner.test.ts +260 -1
- package/src/__tests__/context-search-fanout.test.ts +1 -1
- package/src/__tests__/context-search-types.test.ts +7 -7
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +546 -16
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
- package/src/__tests__/conversation-evictor.test.ts +62 -37
- package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
- package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-retry-route.test.ts +22 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
- package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
- package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
- package/src/__tests__/conversation-undo.test.ts +61 -0
- package/src/__tests__/credential-execution-client.test.ts +6 -6
- package/src/__tests__/credential-prompt-route.test.ts +35 -3
- package/src/__tests__/cu-unified-flow.test.ts +82 -1
- package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
- package/src/__tests__/dm-backfill.test.ts +91 -1
- package/src/__tests__/document-create-route.test.ts +88 -0
- package/src/__tests__/document-sync-tags.test.ts +27 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
- package/src/__tests__/events-tail-route.test.ts +25 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
- package/src/__tests__/guardian-action-sweep.test.ts +16 -0
- package/src/__tests__/guardian-dispatch.test.ts +13 -0
- package/src/__tests__/headless-browser-interactions.test.ts +64 -20
- package/src/__tests__/heartbeat-service.test.ts +2 -59
- package/src/__tests__/helpers/mock-conversation.ts +21 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
- package/src/__tests__/host-cu-proxy.test.ts +46 -2
- package/src/__tests__/http-user-message-parity.test.ts +2 -0
- package/src/__tests__/image-credentials.test.ts +49 -0
- package/src/__tests__/image-service-dispatcher.test.ts +45 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
- package/src/__tests__/interrupt-on-send.test.ts +57 -43
- package/src/__tests__/interrupt-turn-note.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +49 -1
- package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
- package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
- package/src/__tests__/mode-session-history.test.ts +245 -0
- package/src/__tests__/mode-session-recovery.test.ts +222 -0
- package/src/__tests__/mode-session-wire.test.ts +141 -0
- package/src/__tests__/notification-guardian-path.test.ts +7 -0
- package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
- package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +12 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/plugin-api-provider.test.ts +5 -0
- package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -0
- package/src/__tests__/process-message-display-content.test.ts +56 -0
- package/src/__tests__/reaction-persistence.test.ts +5 -0
- package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
- package/src/__tests__/secret-ingress-http.test.ts +2 -0
- package/src/__tests__/secure-keys.test.ts +43 -1
- package/src/__tests__/send-endpoint-busy.test.ts +163 -1
- package/src/__tests__/server-history-render.test.ts +91 -1
- package/src/__tests__/settings-routes.test.ts +50 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
- package/src/__tests__/subagent-manager-notify.test.ts +1 -2
- package/src/__tests__/subagent-notify-parent.test.ts +146 -39
- package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
- package/src/__tests__/thread-backfill.test.ts +53 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +3 -1
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/twilio-routes.test.ts +1 -0
- package/src/__tests__/web-search-backend-failure.test.ts +1 -1
- package/src/activation/progress-store.test.ts +12 -7
- package/src/agent/loop.ts +99 -41
- package/src/api/events/assistant-turn-start.ts +3 -0
- package/src/api/events/generation-handoff.ts +2 -0
- package/src/api/events/message-complete.ts +2 -0
- package/src/api/events/notification-conversation-created.ts +14 -16
- package/src/api/events/notification-intent.ts +4 -2
- package/src/api/events/tool-result.ts +85 -0
- package/src/api/events/tool-use-start.ts +3 -0
- package/src/api/events/user-message-echo.ts +3 -0
- package/src/api/index.ts +31 -6
- package/src/api/mode-session.test.ts +131 -0
- package/src/api/mode-session.ts +148 -0
- package/src/api/remember-tool.ts +15 -0
- package/src/api/responses/conversation-message.ts +6 -0
- package/src/approvals/AGENTS.md +4 -2
- package/src/browser/__tests__/operations.test.ts +29 -0
- package/src/browser/operation-meta.ts +2 -1
- package/src/browser/operations.ts +29 -0
- package/src/browser/virtual-desktop-target.ts +2 -8
- package/src/bundler/bundle-signer.ts +1 -3
- package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
- package/src/calls/__tests__/call-controller.test.ts +94 -0
- package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
- package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
- package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
- package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
- package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
- package/src/calls/barge-in-guard.ts +5 -0
- package/src/calls/call-controller.ts +196 -1
- package/src/calls/call-domain.ts +4 -0
- package/src/calls/call-store.ts +87 -2
- package/src/calls/guardian-action-sweep.ts +22 -8
- package/src/calls/media-stream-audio-transcode.ts +1 -3
- package/src/calls/typesafe-noul.ts +156 -0
- package/src/calls/voice-control-protocol.ts +6 -0
- package/src/calls/voice-escalation-judge.ts +145 -0
- package/src/calls/voice-escalation-target.ts +10 -0
- package/src/calls/voice-leg-coordinator.ts +121 -3
- package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
- package/src/calls/voice-session-bridge.ts +256 -112
- package/src/calls/voice-triage-escalate.ts +7 -0
- package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
- package/src/cli/commands/__tests__/notifications.test.ts +35 -10
- package/src/cli/commands/browser.help.ts +4 -5
- package/src/cli/commands/browser.ts +0 -1
- package/src/cli/commands/clients.help.ts +46 -0
- package/src/cli/commands/clients.ts +116 -0
- package/src/cli/commands/credentials.help.ts +6 -1
- package/src/cli/commands/notifications.help.ts +16 -2
- package/src/cli/commands/notifications.ts +30 -8
- package/src/cli/commands/roadmap.help.ts +18 -21
- package/src/cli/commands/roadmap.ts +4 -21
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
- package/src/cli/lib/bundled-marketplace.json +84 -34
- package/src/cli/lib/plugin-marketplace.ts +8 -0
- package/src/cli/lib/publish-plugin.ts +1 -2
- package/src/config/__tests__/default-provider.test.ts +3 -1
- package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
- package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
- package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
- package/src/config/call-site-defaults.ts +9 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/profile-text-generation.test.ts +2 -2
- package/src/config/schemas/__tests__/tools.test.ts +31 -0
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -18
- package/src/config/schemas/llm.ts +3 -1
- package/src/config/schemas/mcp.ts +2 -1
- package/src/config/schemas/memory-retrospective.ts +11 -2
- package/src/config/schemas/services.ts +15 -6
- package/src/config/schemas/tools.ts +10 -0
- package/src/config/session-groups-gate.ts +21 -0
- package/src/config/skills.ts +2 -6
- package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
- package/src/conversations/message-consolidation.ts +102 -5
- package/src/credential-execution/ces-connect.test.ts +69 -0
- package/src/credential-execution/ces-connect.ts +115 -0
- package/src/credential-execution/ces-runtime.ts +55 -93
- package/src/credential-execution/executable-discovery.ts +1 -11
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
- package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
- package/src/daemon/browser-mode-session.test.ts +402 -0
- package/src/daemon/browser-mode-session.ts +186 -0
- package/src/daemon/computer-use-mode-session.test.ts +210 -0
- package/src/daemon/computer-use-mode-session.ts +105 -0
- package/src/daemon/config-watcher.ts +10 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +58 -10
- package/src/daemon/conversation-agent-loop.ts +219 -25
- package/src/daemon/conversation-evictor.ts +5 -24
- package/src/daemon/conversation-history.ts +10 -0
- package/src/daemon/conversation-interrupt-repair.ts +4 -43
- package/src/daemon/conversation-interrupt.ts +7 -11
- package/src/daemon/conversation-messaging.ts +58 -5
- package/src/daemon/conversation-mode-session.test.ts +1667 -0
- package/src/daemon/conversation-mode-session.ts +1008 -0
- package/src/daemon/conversation-process.ts +5 -0
- package/src/daemon/conversation-runtime-assembly.ts +10 -20
- package/src/daemon/conversation-skill-tools.ts +1 -0
- package/src/daemon/conversation-store.ts +4 -4
- package/src/daemon/conversation-surface-snapshots.ts +135 -0
- package/src/daemon/conversation-surfaces.ts +113 -5
- package/src/daemon/conversation-tool-setup.ts +8 -3
- package/src/daemon/conversation.ts +143 -41
- package/src/daemon/handlers/config-model.ts +7 -5
- package/src/daemon/handlers/shared.ts +55 -16
- package/src/daemon/host-cu-proxy.ts +51 -22
- package/src/daemon/lifecycle.ts +30 -13
- package/src/daemon/message-protocol.ts +0 -1
- package/src/daemon/message-provenance.ts +28 -4
- package/src/daemon/mode-session-startup-recovery.ts +23 -0
- package/src/daemon/mode-session-tracking.ts +54 -0
- package/src/daemon/process-message.ts +13 -4
- package/src/daemon/virtual-desktop-context.ts +21 -0
- package/src/desktop/__tests__/fake-desktop.ts +1 -0
- package/src/desktop/desktop-accessibility-script.ts +88 -0
- package/src/desktop/desktop-accessibility.test.ts +160 -0
- package/src/desktop/desktop-accessibility.ts +169 -0
- package/src/desktop/desktop-automation-lease.test.ts +76 -68
- package/src/desktop/desktop-automation-lease.ts +41 -50
- package/src/desktop/desktop-browser-endpoint.ts +4 -0
- package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
- package/src/desktop/desktop-computer-use.test.ts +449 -0
- package/src/desktop/desktop-computer-use.ts +502 -0
- package/src/desktop/desktop-dependencies.ts +47 -293
- package/src/desktop/desktop-display.ts +2 -2
- package/src/desktop/desktop-panel-config.test.ts +13 -3
- package/src/desktop/desktop-panel-config.ts +109 -15
- package/src/desktop/desktop-session-manager.test.ts +69 -21
- package/src/desktop/desktop-session-manager.ts +76 -22
- package/src/desktop/desktop-stream-bridge.test.ts +11 -122
- package/src/desktop/desktop-stream-bridge.ts +6 -8
- package/src/desktop/desktop-window-theme.ts +5 -25
- package/src/desktop/virtual-desktop-feature.ts +52 -2
- package/src/desktop/virtual-desktop-platform.test.ts +5 -9
- package/src/documents/document-store.ts +23 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
- package/src/heartbeat/heartbeat-service.ts +5 -33
- package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
- package/src/ipc/assistant-server.ts +2 -0
- package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
- package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
- package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
- package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
- package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
- package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
- package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
- package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
- package/src/live-voice/__tests__/session-controls.test.ts +23 -0
- package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
- package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
- package/src/live-voice/camera-mode-session.test.ts +265 -0
- package/src/live-voice/camera-mode-session.ts +277 -0
- package/src/live-voice/continuation-judge.ts +123 -0
- package/src/live-voice/live-activity-reporter.ts +115 -6
- package/src/live-voice/live-voice-manager.ts +36 -1
- package/src/live-voice/live-voice-photo.ts +88 -7
- package/src/live-voice/live-voice-session-manager.ts +15 -0
- package/src/live-voice/live-voice-session.ts +1443 -747
- package/src/live-voice/live-voice-tts.ts +22 -4
- package/src/live-voice/microphone-idle-gate.ts +56 -0
- package/src/live-voice/protocol.ts +171 -4
- package/src/live-voice/session-controls.ts +11 -3
- package/src/live-voice/task-outcomes.ts +131 -0
- package/src/live-voice/voice-input-diagnostics.ts +117 -0
- package/src/mcp/__tests__/manager-tool-caps.test.ts +63 -18
- package/src/mcp/__tests__/tool-caps.test.ts +28 -1
- package/src/mcp/manager.ts +3 -0
- package/src/mcp/tool-caps.ts +12 -4
- package/src/media/__tests__/image-models.test.ts +58 -0
- package/src/media/image-credentials.ts +5 -0
- package/src/media/image-models.ts +52 -0
- package/src/media/image-service.ts +8 -0
- package/src/media/openrouter-image-service.test.ts +133 -0
- package/src/media/openrouter-image-service.ts +153 -0
- package/src/media/types.ts +1 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
- package/src/messaging/providers/discord/send.ts +4 -17
- package/src/messaging/providers/discord/transport.ts +2 -2
- package/src/messaging/providers/outlook/client.ts +16 -22
- package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
- package/src/messaging/providers/telegram-bot/send.ts +10 -7
- package/src/messaging/providers/telegram-bot/transport.ts +3 -10
- package/src/messaging/providers/whatsapp/send.test.ts +169 -4
- package/src/monitoring/__tests__/control.test.ts +17 -5
- package/src/monitoring/control.ts +15 -5
- package/src/notifications/AGENTS.md +1 -1
- package/src/notifications/README.md +83 -99
- package/src/notifications/__tests__/broadcaster.test.ts +1 -1
- package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
- package/src/notifications/__tests__/copy-composer.test.ts +1 -1
- package/src/notifications/__tests__/decision-engine.test.ts +79 -2
- package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
- package/src/notifications/__tests__/notification-utils.test.ts +18 -0
- package/src/notifications/adapters/macos.ts +37 -46
- package/src/notifications/broadcaster.ts +5 -6
- package/src/notifications/channel-allowlist.ts +44 -0
- package/src/notifications/copy-composer.ts +0 -23
- package/src/notifications/decision-engine.ts +24 -14
- package/src/notifications/deterministic-checks.ts +1 -1
- package/src/notifications/emit-signal.ts +123 -37
- package/src/notifications/home-feed-side-effect.ts +9 -7
- package/src/notifications/notification-utils.ts +19 -0
- package/src/notifications/signal.ts +4 -19
- package/src/notifications/types.ts +15 -0
- package/src/oauth/connection-resolver.ts +1 -2
- package/src/oauth/oauth-store.ts +1 -3
- package/src/oauth/provider-serializer.ts +7 -0
- package/src/oauth/seed-providers.ts +30 -0
- package/src/onboarding/onboarding-events-store.ts +99 -12
- package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
- package/src/persistence/client-connection-events-store.ts +377 -0
- package/src/persistence/conversation-crud.ts +55 -6
- package/src/persistence/conversation-mode-sessions.test.ts +424 -0
- package/src/persistence/conversation-mode-sessions.ts +460 -0
- package/src/persistence/conversation-queries.ts +6 -19
- package/src/persistence/llm-usage-store.ts +3 -9
- package/src/persistence/message-metadata.ts +25 -0
- package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
- package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
- package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
- package/src/persistence/schema/conversations.ts +67 -0
- package/src/persistence/schema/infrastructure.ts +29 -0
- package/src/persistence/steps.ts +17 -0
- package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
- package/src/plugin-api/effective-context-window.ts +33 -0
- package/src/plugin-api/index.ts +24 -5
- package/src/plugin-api/plugin-name-env.ts +125 -0
- package/src/plugin-api/resolve-credential.ts +79 -20
- package/src/plugin-api/vision-support.test.ts +22 -0
- package/src/plugin-api/vision-support.ts +17 -5
- package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
- package/src/plugins/active-plugin-env.ts +51 -0
- package/src/plugins/defaults/index.ts +24 -0
- package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
- package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
- package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +78 -22
- package/src/plugins/defaults/memory/context-search/format.ts +51 -7
- package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
- package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
- package/src/plugins/defaults/memory/context-search/search.ts +8 -14
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
- package/src/plugins/defaults/memory/context-search/types.ts +1 -11
- package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +12 -21
- package/src/plugins/defaults/memory/graph/tools.ts +35 -64
- package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
- package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +5 -0
- package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +2 -2
- package/src/plugins/defaults/memory/tools.test.ts +124 -9
- package/src/plugins/defaults/memory/tools.ts +38 -11
- package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +165 -27
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +46 -27
- package/src/plugins/defaults/memory/v3/orchestrate.ts +78 -38
- package/src/plugins/defaults/memory/v3/pool-log-store.ts +65 -79
- package/src/plugins/defaults/memory/v3/pool-select.test.ts +14 -5
- package/src/plugins/defaults/memory/v3/pool-select.ts +683 -44
- package/src/plugins/defaults/memory/worker-control.ts +15 -5
- package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
- package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
- package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
- package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
- package/src/plugins/defaults/task-progress/package.json +14 -0
- package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
- package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
- package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
- package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
- package/src/providers/anthropic/client.ts +10 -1
- package/src/providers/anthropic-gateway-shared.ts +6 -0
- package/src/providers/fetch-provider-catalog.ts +11 -0
- package/src/providers/gemini/client.ts +25 -8
- package/src/providers/inference/adapter-factory.ts +1 -1
- package/src/providers/jev/client.test.ts +10 -0
- package/src/providers/jev/client.ts +20 -4
- package/src/providers/model-catalog.ts +4 -3
- package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
- package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
- package/src/providers/openai/chat-completions-provider.ts +100 -29
- package/src/providers/openai/google-thought-signature.ts +0 -6
- package/src/providers/openai/responses-provider.ts +23 -1
- package/src/providers/ratelimit.ts +1 -3
- package/src/providers/search-provider-catalog.ts +24 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
- package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
- package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
- package/src/providers/types.ts +5 -0
- package/src/routes/control.ts +7 -5
- package/src/runtime/AGENTS.md +4 -2
- package/src/runtime/actor-trust-resolver.ts +3 -3
- package/src/runtime/agent-wake.ts +1 -1
- package/src/runtime/approval-message-composer.ts +3 -69
- package/src/runtime/assistant-event-hub.ts +73 -75
- package/src/runtime/assistant-event-publish-options.ts +7 -0
- package/src/runtime/assistant-event-targeting.ts +79 -0
- package/src/runtime/assistant-stream-state.ts +13 -100
- package/src/runtime/background-job-runner.ts +5 -5
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/guardian-action-message-composer.ts +8 -161
- package/src/runtime/guardian-reply-router.ts +28 -32
- package/src/runtime/http-types.ts +6 -0
- package/src/runtime/message-composer-types.ts +3 -61
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
- package/src/runtime/migrations/vbundle-builder.ts +24 -1
- package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
- package/src/runtime/migrations/vbundle-import-policy.ts +10 -0
- package/src/runtime/migrations/vbundle-importer.ts +4 -1
- package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
- package/src/runtime/migrations/vbundle-validator.ts +1 -0
- package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
- package/src/runtime/pending-interactions.ts +29 -0
- package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
- package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
- package/src/runtime/routes/browser-context.ts +31 -11
- package/src/runtime/routes/browser-routes.ts +43 -4
- package/src/runtime/routes/btw-routes.ts +9 -4
- package/src/runtime/routes/client-routes.ts +108 -0
- package/src/runtime/routes/conversation-management-routes.ts +4 -1
- package/src/runtime/routes/conversation-routes.ts +113 -27
- package/src/runtime/routes/credential-prompt-routes.ts +4 -3
- package/src/runtime/routes/credential-routes.ts +1 -2
- package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
- package/src/runtime/routes/desktop-setup-routes.ts +4 -6
- package/src/runtime/routes/documents-routes.ts +51 -0
- package/src/runtime/routes/email-routes.ts +3 -7
- package/src/runtime/routes/events-routes.ts +75 -54
- package/src/runtime/routes/identity-routes.ts +1 -5
- package/src/runtime/routes/image-generation-routes.ts +8 -14
- package/src/runtime/routes/inbound-message-handler.ts +35 -38
- package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
- package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +38 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +2 -5
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +73 -2
- package/src/runtime/routes/notification-routes.ts +10 -0
- package/src/runtime/routes/oauth-providers.ts +7 -0
- package/src/runtime/routes/plugins-routes.ts +6 -0
- package/src/runtime/routes/roadmap-routes.ts +47 -8
- package/src/runtime/routes/secret-routes.ts +1 -1
- package/src/runtime/routes/settings-routes.ts +10 -4
- package/src/runtime/routes/watcher-routes.ts +1 -3
- package/src/runtime/trust-class.test.ts +7 -6
- package/src/runtime/trust-class.ts +9 -17
- package/src/schedule/__tests__/worker-control.test.ts +36 -5
- package/src/schedule/worker-control.ts +15 -5
- package/src/security/secure-keys.ts +53 -103
- package/src/skills/clawhub.ts +3 -8
- package/src/skills/managed-store.ts +1 -5
- package/src/stt/types.ts +4 -0
- package/src/subagent/notify.ts +54 -34
- package/src/subagent/parent-notification.ts +6 -0
- package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
- package/src/telemetry/phone-call-funnel.ts +144 -0
- package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
- package/src/tools/ask-question/ask-question-tool.ts +6 -4
- package/src/tools/browser/browser-execution.ts +22 -59
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
- package/src/tools/client-os.ts +15 -0
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
- package/src/tools/computer-use/target.ts +42 -0
- package/src/tools/document/document-tool.ts +13 -18
- package/src/tools/execution-target.ts +14 -16
- package/src/tools/host-terminal/host-shell.ts +4 -1
- package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
- package/src/tools/network/__tests__/web-search.test.ts +203 -0
- package/src/tools/network/web-fetch.ts +274 -7
- package/src/tools/network/web-search.ts +468 -6
- package/src/tools/permission-checker.ts +3 -3
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/skills/load.ts +2 -1
- package/src/tools/skills/sandbox-runner.ts +11 -2
- package/src/tools/skills/skill-script-runner.ts +7 -0
- package/src/tools/skills/skill-tool-factory.ts +15 -1
- package/src/tools/terminal/__tests__/safe-env.test.ts +11 -0
- package/src/tools/terminal/safe-env.ts +4 -0
- package/src/tools/terminal/shell.ts +2 -0
- package/src/tools/tool-approval-handler.ts +5 -1
- package/src/tools/tool-types.ts +3 -3
- package/src/tools/types.ts +8 -2
- package/src/util/__tests__/worker-process-command.test.ts +5 -4
- package/src/util/abort-reasons.ts +18 -26
- package/src/util/canonicalize-identity.test.ts +21 -0
- package/src/util/canonicalize-identity.ts +16 -0
- package/src/util/errors.ts +13 -3
- package/src/util/provider-error-patterns.ts +3 -0
- package/src/util/worker-process.ts +77 -35
- package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
- package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/computer-use-window-schema.test.ts +0 -24
- package/src/api/events/heartbeat-alert.ts +0 -20
- package/src/daemon/message-types/web-activity.ts +0 -65
- package/src/desktop/desktop-dependencies.test.ts +0 -131
- package/src/tools/computer-use/definitions.ts +0 -607
|
@@ -20,9 +20,10 @@
|
|
|
20
20
|
|
|
21
21
|
import { type IntervalHistogram, monitorEventLoopDelay } from "node:perf_hooks";
|
|
22
22
|
|
|
23
|
+
import { sanitizeClientMetadataValue } from "@vellumai/service-contracts/client-metadata";
|
|
23
24
|
import { z } from "zod";
|
|
24
25
|
|
|
25
|
-
import type { HostProxyCapability } from "../../channels/types.js";
|
|
26
|
+
import type { HostProxyCapability, InterfaceId } from "../../channels/types.js";
|
|
26
27
|
import { parseInterfaceId, supportsHostProxy } from "../../channels/types.js";
|
|
27
28
|
import { notifyContactsChanged } from "../../contacts/notify-contacts-changed.js";
|
|
28
29
|
import { getConversation } from "../../persistence/conversation-crud.js";
|
|
@@ -38,7 +39,7 @@ import {
|
|
|
38
39
|
AssistantEventHub,
|
|
39
40
|
assistantEventHub,
|
|
40
41
|
} from "../assistant-event-hub.js";
|
|
41
|
-
import type {
|
|
42
|
+
import type { SubscriberIdentity } from "../assistant-event-targeting.js";
|
|
42
43
|
import { getReplayWindow } from "../assistant-stream-state.js";
|
|
43
44
|
import { ACTOR_PRINCIPALS, GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
|
|
44
45
|
import { DEFAULT_HEARTBEAT_INTERVAL_MS } from "../client-health.js";
|
|
@@ -248,12 +249,14 @@ const defaultSseShedReporter: SseShedReporter = (reason, inst) => {
|
|
|
248
249
|
* conversations for this assistant.
|
|
249
250
|
*
|
|
250
251
|
* Headers (optional):
|
|
251
|
-
* X-Vellum-Client-Id
|
|
252
|
-
* X-Vellum-Interface-Id
|
|
252
|
+
* X-Vellum-Client-Id -- stable per-install UUID identifying this client.
|
|
253
|
+
* X-Vellum-Interface-Id -- interface type (e.g. "macos", "ios", "web").
|
|
254
|
+
* X-Vellum-Client-Version -- optional build/manifest version fingerprint.
|
|
255
|
+
* X-Vellum-Sse-Watchdog -- optional "1" when the client has an SSE idle watchdog.
|
|
253
256
|
*
|
|
254
|
-
* When both are present, the subscriber is registered as a
|
|
255
|
-
* event hub with metadata (interfaceId, capabilities). The hub
|
|
256
|
-
* lifecycle
|
|
257
|
+
* When both identity headers are present, the subscriber is registered as a
|
|
258
|
+
* client in the event hub with metadata (interfaceId, capabilities). The hub
|
|
259
|
+
* handles lifecycle. dispose() unregisters the client automatically.
|
|
257
260
|
*
|
|
258
261
|
* Options (for testing):
|
|
259
262
|
* hub -- override the event hub (defaults to process singleton).
|
|
@@ -300,25 +303,18 @@ export function handleSubscribeAssistantEvents(
|
|
|
300
303
|
}
|
|
301
304
|
|
|
302
305
|
// ── Client identity from headers ──────────────────────────────────────
|
|
303
|
-
const rawClientId = headers?.["x-vellum-client-id"];
|
|
304
|
-
const rawInterfaceId = headers?.["x-vellum-interface-id"];
|
|
305
306
|
const rawMachineName = headers?.["x-vellum-machine-name"];
|
|
306
307
|
const rawActorPrincipalId = headers?.["x-vellum-actor-principal-id"];
|
|
307
|
-
const clientId
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
// Verified by RuntimeHttpServer and forwarded by the http-adapter from the
|
|
312
|
-
// bearer token's AuthContext. May be absent for legacy / service-token
|
|
313
|
-
// connections that have no principal. See `resolveActorPrincipalId` for the
|
|
314
|
-
// dev-bypass translation rationale.
|
|
315
|
-
const actorPrincipalId = resolveActorPrincipalIdForLocalGuardianSync(
|
|
316
|
-
rawActorPrincipalId?.trim() || undefined,
|
|
308
|
+
const { clientId, interfaceId, actorPrincipalId } =
|
|
309
|
+
readClientIdentity(headers);
|
|
310
|
+
const clientVersion = sanitizeClientMetadataValue(
|
|
311
|
+
headers?.["x-vellum-client-version"],
|
|
317
312
|
);
|
|
313
|
+
const sseWatchdog = headers?.["x-vellum-sse-watchdog"]?.trim() === "1";
|
|
318
314
|
|
|
319
315
|
if (clientId && !interfaceId) {
|
|
320
316
|
log.error(
|
|
321
|
-
{ clientId, rawInterfaceId },
|
|
317
|
+
{ clientId, rawInterfaceId: headers?.["x-vellum-interface-id"] },
|
|
322
318
|
"client registration failed: invalid or missing X-Vellum-Interface-Id",
|
|
323
319
|
);
|
|
324
320
|
throw new BadRequestError(
|
|
@@ -410,7 +406,7 @@ export function handleSubscribeAssistantEvents(
|
|
|
410
406
|
try {
|
|
411
407
|
if (controller.desiredSize != null && controller.desiredSize <= 0) {
|
|
412
408
|
shedReporter("callback_backpressure", instrumentation);
|
|
413
|
-
sub.dispose();
|
|
409
|
+
sub.dispose("shed_backpressure");
|
|
414
410
|
cleanup();
|
|
415
411
|
return;
|
|
416
412
|
}
|
|
@@ -455,6 +451,8 @@ export function handleSubscribeAssistantEvents(
|
|
|
455
451
|
],
|
|
456
452
|
machineName: rawMachineName?.trim() || undefined,
|
|
457
453
|
actorPrincipalId,
|
|
454
|
+
clientVersion,
|
|
455
|
+
sseWatchdog: sseWatchdog || undefined,
|
|
458
456
|
})
|
|
459
457
|
: hub.subscribe({
|
|
460
458
|
...subscriberBase,
|
|
@@ -521,20 +519,9 @@ export function handleSubscribeAssistantEvents(
|
|
|
521
519
|
// The client detects the gap from the seq jump on its first
|
|
522
520
|
// live event and refetches via the existing messages API.
|
|
523
521
|
if (lastSeenSeq != null) {
|
|
524
|
-
const replaySubscriber: ReplaySubscriber =
|
|
525
|
-
clientId && interfaceId
|
|
526
|
-
? {
|
|
527
|
-
type: "client",
|
|
528
|
-
clientId,
|
|
529
|
-
interfaceId,
|
|
530
|
-
capabilities: ALL_CAPABILITIES.filter((cap) =>
|
|
531
|
-
supportsHostProxy(interfaceId, cap),
|
|
532
|
-
),
|
|
533
|
-
}
|
|
534
|
-
: { type: "process" };
|
|
535
522
|
const window = getReplayWindow(
|
|
536
523
|
lastSeenSeq,
|
|
537
|
-
|
|
524
|
+
replaySubscriberFor(clientId, interfaceId, actorPrincipalId),
|
|
538
525
|
filter.conversationId,
|
|
539
526
|
);
|
|
540
527
|
if (window !== null) {
|
|
@@ -555,7 +542,7 @@ export function handleSubscribeAssistantEvents(
|
|
|
555
542
|
try {
|
|
556
543
|
if (controller.desiredSize != null && controller.desiredSize <= 0) {
|
|
557
544
|
shedReporter("heartbeat_backpressure", instrumentation);
|
|
558
|
-
sub.dispose();
|
|
545
|
+
sub.dispose("shed_backpressure");
|
|
559
546
|
cleanup();
|
|
560
547
|
return;
|
|
561
548
|
}
|
|
@@ -590,6 +577,53 @@ export function handleSubscribeAssistantEvents(
|
|
|
590
577
|
return stream;
|
|
591
578
|
}
|
|
592
579
|
|
|
580
|
+
/**
|
|
581
|
+
* The caller's client identity, read the same way by the SSE subscribe route
|
|
582
|
+
* and the tail route. The principal is verified by RuntimeHttpServer and
|
|
583
|
+
* forwarded by the http-adapter from the bearer token's AuthContext; it is
|
|
584
|
+
* absent for legacy and service-token connections, and a dev-bypass header
|
|
585
|
+
* resolves to the local guardian (see `resolveActorPrincipalIdForLocalGuardianSync`).
|
|
586
|
+
*/
|
|
587
|
+
function readClientIdentity(headers: Record<string, string> | undefined): {
|
|
588
|
+
clientId: string | null;
|
|
589
|
+
interfaceId: InterfaceId | null;
|
|
590
|
+
actorPrincipalId: string | undefined;
|
|
591
|
+
} {
|
|
592
|
+
const clientId = headers?.["x-vellum-client-id"]?.trim() || null;
|
|
593
|
+
const interfaceId = clientId
|
|
594
|
+
? parseInterfaceId(headers?.["x-vellum-interface-id"]?.trim())
|
|
595
|
+
: null;
|
|
596
|
+
const actorPrincipalId = resolveActorPrincipalIdForLocalGuardianSync(
|
|
597
|
+
headers?.["x-vellum-actor-principal-id"]?.trim() || undefined,
|
|
598
|
+
);
|
|
599
|
+
return { clientId, interfaceId, actorPrincipalId };
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* The identity a replay filter checks targeted events against: the client a
|
|
604
|
+
* live SSE subscription with these headers would register, or a process
|
|
605
|
+
* subscriber when the caller names no client. Shared by the reconnect replay
|
|
606
|
+
* and the tail route so both deliver exactly what live fanout would have.
|
|
607
|
+
*/
|
|
608
|
+
function replaySubscriberFor(
|
|
609
|
+
clientId: string | null,
|
|
610
|
+
interfaceId: InterfaceId | null,
|
|
611
|
+
actorPrincipalId: string | undefined,
|
|
612
|
+
): SubscriberIdentity {
|
|
613
|
+
if (!clientId || !interfaceId) {
|
|
614
|
+
return { type: "process" };
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
type: "client",
|
|
618
|
+
clientId,
|
|
619
|
+
interfaceId,
|
|
620
|
+
capabilities: ALL_CAPABILITIES.filter((cap) =>
|
|
621
|
+
supportsHostProxy(interfaceId, cap),
|
|
622
|
+
),
|
|
623
|
+
actorPrincipalId,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
593
627
|
/**
|
|
594
628
|
* Replay-by-request companion to the SSE `lastSeenSeq` resume: return the
|
|
595
629
|
* ring-buffered event tail for one conversation with `seq > fromSeq`.
|
|
@@ -641,26 +675,13 @@ function handleEventsTail({
|
|
|
641
675
|
toSeq = parsed;
|
|
642
676
|
}
|
|
643
677
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const subscriber: ReplaySubscriber | undefined =
|
|
652
|
-
clientId && interfaceId
|
|
653
|
-
? {
|
|
654
|
-
type: "client",
|
|
655
|
-
clientId,
|
|
656
|
-
interfaceId,
|
|
657
|
-
capabilities: ALL_CAPABILITIES.filter((cap) =>
|
|
658
|
-
supportsHostProxy(interfaceId, cap),
|
|
659
|
-
),
|
|
660
|
-
}
|
|
661
|
-
: undefined;
|
|
662
|
-
|
|
663
|
-
const window = getReplayWindow(fromSeq, subscriber, conversationId);
|
|
678
|
+
const { clientId, interfaceId, actorPrincipalId } =
|
|
679
|
+
readClientIdentity(headers);
|
|
680
|
+
const window = getReplayWindow(
|
|
681
|
+
fromSeq,
|
|
682
|
+
replaySubscriberFor(clientId, interfaceId, actorPrincipalId),
|
|
683
|
+
conversationId,
|
|
684
|
+
);
|
|
664
685
|
if (window === null) {
|
|
665
686
|
return { events: [], complete: false, frontier: null };
|
|
666
687
|
}
|
|
@@ -228,7 +228,7 @@ function getIdentity() {
|
|
|
228
228
|
|
|
229
229
|
const version = APP_VERSION;
|
|
230
230
|
|
|
231
|
-
const createdAt =
|
|
231
|
+
const createdAt = resolveHatchedAtReadOnly(identityPath);
|
|
232
232
|
|
|
233
233
|
return {
|
|
234
234
|
name: fields.name ?? "",
|
|
@@ -241,10 +241,6 @@ function getIdentity() {
|
|
|
241
241
|
};
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
function resolveIdentityCreatedAt(identityPath: string): string | undefined {
|
|
245
|
-
return resolveHatchedAtReadOnly(identityPath);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
244
|
// ---------------------------------------------------------------------------
|
|
249
245
|
// Zod schemas for profiler health metadata
|
|
250
246
|
// ---------------------------------------------------------------------------
|
|
@@ -5,10 +5,7 @@ import {
|
|
|
5
5
|
resolveImageGenCredentials,
|
|
6
6
|
resolveImageGenRouting,
|
|
7
7
|
} from "../../media/image-credentials.js";
|
|
8
|
-
import {
|
|
9
|
-
describeImageModels,
|
|
10
|
-
resolveImageModel,
|
|
11
|
-
} from "../../media/image-models.js";
|
|
8
|
+
import { resolveRequestedImageModel } from "../../media/image-models.js";
|
|
12
9
|
import { generateImage, mapImageGenError } from "../../media/image-service.js";
|
|
13
10
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
14
11
|
import {
|
|
@@ -80,18 +77,15 @@ async function handleImageGenerationGenerate(
|
|
|
80
77
|
const config = getConfig();
|
|
81
78
|
const svc = config.services["image-generation"];
|
|
82
79
|
|
|
83
|
-
// Resolve
|
|
84
|
-
//
|
|
85
|
-
// error is self-describing rather than a stale enum.
|
|
80
|
+
// Resolve aliases and OpenRouter slugs against the configured provider.
|
|
81
|
+
// Built-in providers still list the current catalog on unknown values.
|
|
86
82
|
let resolvedModel = model as string | undefined;
|
|
87
83
|
if (typeof resolvedModel === "string" && resolvedModel) {
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
throw new BadRequestError(
|
|
91
|
-
`Unknown model "${resolvedModel}". Available models and aliases:\n${describeImageModels()}`,
|
|
92
|
-
);
|
|
84
|
+
const resolved = resolveRequestedImageModel(resolvedModel, svc.provider);
|
|
85
|
+
if (resolved.error) {
|
|
86
|
+
throw new BadRequestError(resolved.error);
|
|
93
87
|
}
|
|
94
|
-
resolvedModel =
|
|
88
|
+
resolvedModel = resolved.model;
|
|
95
89
|
}
|
|
96
90
|
|
|
97
91
|
// Backend and managed-ness resolve together; an explicit model override
|
|
@@ -154,7 +148,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
154
148
|
},
|
|
155
149
|
summary: "Generate or edit images using AI",
|
|
156
150
|
description:
|
|
157
|
-
"Calls the configured image-generation provider (Gemini or
|
|
151
|
+
"Calls the configured image-generation provider (Gemini, OpenAI, or OpenRouter) to produce one or more images.",
|
|
158
152
|
tags: ["image-generation"],
|
|
159
153
|
requestBody: ImageGenerationRequestSchema,
|
|
160
154
|
responseBody: ImageGenerationResponseSchema,
|
|
@@ -106,7 +106,10 @@ import { markProcessed } from "../../persistence/delivery-status.js";
|
|
|
106
106
|
import { upsertBinding } from "../../persistence/external-conversation-store.js";
|
|
107
107
|
import type { ContentBlock } from "../../providers/types.js";
|
|
108
108
|
import { checkIngressForSecrets } from "../../security/secret-ingress.js";
|
|
109
|
-
import {
|
|
109
|
+
import {
|
|
110
|
+
canonicalizeInboundIdentity,
|
|
111
|
+
inboundIdentitiesMatch,
|
|
112
|
+
} from "../../util/canonicalize-identity.js";
|
|
110
113
|
import { safeParseRecord } from "../../util/json.js";
|
|
111
114
|
import { getLogger } from "../../util/logger.js";
|
|
112
115
|
import { truncate } from "../../util/truncate.js";
|
|
@@ -126,6 +129,11 @@ import {
|
|
|
126
129
|
enforceIngressAcl,
|
|
127
130
|
} from "./inbound-stages/acl-enforcement.js";
|
|
128
131
|
import { enforceAdmissionPolicy } from "./inbound-stages/admission-policy.js";
|
|
132
|
+
import {
|
|
133
|
+
type BackfilledSenderProvenance,
|
|
134
|
+
type BackfilledSenderProvenanceResolver,
|
|
135
|
+
createBackfilledSenderProvenanceResolver,
|
|
136
|
+
} from "./inbound-stages/backfill-sender-provenance.js";
|
|
129
137
|
import { processChannelMessageInBackground } from "./inbound-stages/background-dispatch.js";
|
|
130
138
|
import { handleBootstrapIntercept } from "./inbound-stages/bootstrap-intercept.js";
|
|
131
139
|
import { handleEditIntercept } from "./inbound-stages/edit-intercept.js";
|
|
@@ -1835,6 +1843,7 @@ async function persistBackfilledSlackMessage(params: {
|
|
|
1835
1843
|
message: ProviderMessage;
|
|
1836
1844
|
account?: string;
|
|
1837
1845
|
guardianExternalUserId?: string;
|
|
1846
|
+
resolveSenderProvenance: BackfilledSenderProvenanceResolver;
|
|
1838
1847
|
}): Promise<boolean> {
|
|
1839
1848
|
const { message } = params;
|
|
1840
1849
|
|
|
@@ -1872,10 +1881,19 @@ async function persistBackfilledSlackMessage(params: {
|
|
|
1872
1881
|
message.metadata,
|
|
1873
1882
|
"actorTimezoneLabel",
|
|
1874
1883
|
);
|
|
1875
|
-
const
|
|
1884
|
+
const role = (await isBackfilledSlackAssistantMessage(
|
|
1876
1885
|
message,
|
|
1877
|
-
params.
|
|
1878
|
-
)
|
|
1886
|
+
params.account,
|
|
1887
|
+
))
|
|
1888
|
+
? "assistant"
|
|
1889
|
+
: "user";
|
|
1890
|
+
// The assistant's own posts are replayed as assistant history, not as any
|
|
1891
|
+
// person's words, so only a person's row asks who its sender is.
|
|
1892
|
+
const provenance: BackfilledSenderProvenance =
|
|
1893
|
+
role === "user"
|
|
1894
|
+
? await params.resolveSenderProvenance(actorExternalUserId)
|
|
1895
|
+
: { provenanceTrustClass: "unknown" };
|
|
1896
|
+
const isGuardian = provenance.provenanceTrustClass === "guardian";
|
|
1879
1897
|
const slackTranscriptTimestampTimezone =
|
|
1880
1898
|
resolveSlackTranscriptTimestampTimezone();
|
|
1881
1899
|
const slackTimezoneFields = buildSlackTimezoneMetadata({
|
|
@@ -1899,13 +1917,6 @@ async function persistBackfilledSlackMessage(params: {
|
|
|
1899
1917
|
...(slackFiles.length > 0 ? { slackFiles } : {}),
|
|
1900
1918
|
};
|
|
1901
1919
|
|
|
1902
|
-
const role = (await isBackfilledSlackAssistantMessage(
|
|
1903
|
-
message,
|
|
1904
|
-
params.account,
|
|
1905
|
-
))
|
|
1906
|
-
? "assistant"
|
|
1907
|
-
: "user";
|
|
1908
|
-
|
|
1909
1920
|
const rawText = message.text ?? "";
|
|
1910
1921
|
|
|
1911
1922
|
// `sentAt` is when the message was sent; the row's `createdAt` is when the
|
|
@@ -1944,7 +1955,7 @@ async function persistBackfilledSlackMessage(params: {
|
|
|
1944
1955
|
metadata: {
|
|
1945
1956
|
...envelope,
|
|
1946
1957
|
...(sentAt !== undefined ? { sentAt } : {}),
|
|
1947
|
-
|
|
1958
|
+
...provenance,
|
|
1948
1959
|
provenanceSourceChannel: "slack",
|
|
1949
1960
|
...(params.guardianExternalUserId
|
|
1950
1961
|
? { provenanceGuardianExternalUserId: params.guardianExternalUserId }
|
|
@@ -2063,22 +2074,6 @@ async function buildBackfilledSlackContentBlocks(
|
|
|
2063
2074
|
return blocks;
|
|
2064
2075
|
}
|
|
2065
2076
|
|
|
2066
|
-
function isBackfilledSlackGuardianMessage(
|
|
2067
|
-
message: ProviderMessage,
|
|
2068
|
-
guardianExternalUserId: string | undefined,
|
|
2069
|
-
): boolean {
|
|
2070
|
-
const rawSenderId = message.sender?.id?.trim();
|
|
2071
|
-
if (!rawSenderId || !guardianExternalUserId) {
|
|
2072
|
-
return false;
|
|
2073
|
-
}
|
|
2074
|
-
const normalizedSender =
|
|
2075
|
-
canonicalizeInboundIdentity("slack", rawSenderId) ?? rawSenderId;
|
|
2076
|
-
const normalizedGuardian =
|
|
2077
|
-
canonicalizeInboundIdentity("slack", guardianExternalUserId) ??
|
|
2078
|
-
guardianExternalUserId.trim();
|
|
2079
|
-
return normalizedSender === normalizedGuardian;
|
|
2080
|
-
}
|
|
2081
|
-
|
|
2082
2077
|
const SLACK_ASSISTANT_THREAD_PLACEHOLDER_TEXT = "New Assistant Thread";
|
|
2083
2078
|
|
|
2084
2079
|
async function isSlackAssistantThreadPlaceholder(
|
|
@@ -2115,7 +2110,7 @@ async function isBackfilledSlackAssistantMessage(
|
|
|
2115
2110
|
return false;
|
|
2116
2111
|
}
|
|
2117
2112
|
|
|
2118
|
-
if (rawSenderId &&
|
|
2113
|
+
if (rawSenderId && inboundIdentitiesMatch("slack", rawSenderId, botUserId)) {
|
|
2119
2114
|
return true;
|
|
2120
2115
|
}
|
|
2121
2116
|
|
|
@@ -2131,7 +2126,7 @@ async function isBackfilledSlackAssistantMessage(
|
|
|
2131
2126
|
const resolvedBotUserId = await resolveSlackBotUserId(account, rawBotId);
|
|
2132
2127
|
return (
|
|
2133
2128
|
typeof resolvedBotUserId === "string" &&
|
|
2134
|
-
|
|
2129
|
+
inboundIdentitiesMatch("slack", resolvedBotUserId, botUserId)
|
|
2135
2130
|
);
|
|
2136
2131
|
} catch (err) {
|
|
2137
2132
|
log.warn(
|
|
@@ -2142,14 +2137,6 @@ async function isBackfilledSlackAssistantMessage(
|
|
|
2142
2137
|
}
|
|
2143
2138
|
}
|
|
2144
2139
|
|
|
2145
|
-
function slackIdentityMatches(left: string, right: string): boolean {
|
|
2146
|
-
const canonicalSender =
|
|
2147
|
-
canonicalizeInboundIdentity("slack", left) ?? left.trim();
|
|
2148
|
-
const canonicalBot =
|
|
2149
|
-
canonicalizeInboundIdentity("slack", right) ?? right.trim();
|
|
2150
|
-
return canonicalSender === canonicalBot;
|
|
2151
|
-
}
|
|
2152
|
-
|
|
2153
2140
|
/**
|
|
2154
2141
|
* Transient view of `slackFiles` that preserves the download URLs added by
|
|
2155
2142
|
* `mapSlackFiles` on the in-flight `ProviderMessage`. These URLs never reach
|
|
@@ -2290,6 +2277,10 @@ async function runBackfillSlackDmIfCold(params: {
|
|
|
2290
2277
|
// and a transcript that reads correctly when the renderer joins on
|
|
2291
2278
|
// monotonic createdAt.
|
|
2292
2279
|
const ordered = [...fetched].reverse();
|
|
2280
|
+
const resolveSenderProvenance = createBackfilledSenderProvenanceResolver(
|
|
2281
|
+
params.guardianExternalUserId,
|
|
2282
|
+
ordered,
|
|
2283
|
+
);
|
|
2293
2284
|
for (const message of ordered) {
|
|
2294
2285
|
if (seen.has(message.id)) {
|
|
2295
2286
|
continue;
|
|
@@ -2309,6 +2300,7 @@ async function runBackfillSlackDmIfCold(params: {
|
|
|
2309
2300
|
...(params.guardianExternalUserId
|
|
2310
2301
|
? { guardianExternalUserId: params.guardianExternalUserId }
|
|
2311
2302
|
: {}),
|
|
2303
|
+
resolveSenderProvenance,
|
|
2312
2304
|
});
|
|
2313
2305
|
seen.add(message.id);
|
|
2314
2306
|
if (stored) {
|
|
@@ -2890,6 +2882,10 @@ export async function triggerSlackThreadBackfillIfNeeded(params: {
|
|
|
2890
2882
|
}
|
|
2891
2883
|
|
|
2892
2884
|
let persisted = 0;
|
|
2885
|
+
const resolveSenderProvenance = createBackfilledSenderProvenanceResolver(
|
|
2886
|
+
guardianExternalUserId,
|
|
2887
|
+
fetched,
|
|
2888
|
+
);
|
|
2893
2889
|
for (const message of fetched) {
|
|
2894
2890
|
if (!message.id) {
|
|
2895
2891
|
continue;
|
|
@@ -2907,6 +2903,7 @@ export async function triggerSlackThreadBackfillIfNeeded(params: {
|
|
|
2907
2903
|
message,
|
|
2908
2904
|
...(account ? { account } : {}),
|
|
2909
2905
|
...(guardianExternalUserId ? { guardianExternalUserId } : {}),
|
|
2906
|
+
resolveSenderProvenance,
|
|
2910
2907
|
});
|
|
2911
2908
|
threadState.storedChannelTs.add(message.id);
|
|
2912
2909
|
if (stored) {
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { afterAll, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { TrustVerdict } from "@vellumai/gateway-client";
|
|
4
|
+
|
|
5
|
+
import type { InboundTrustReadResult } from "../../../calls/inbound-trust-reader.js";
|
|
6
|
+
import * as inboundTrustReader from "../../../calls/inbound-trust-reader.js";
|
|
7
|
+
import type { Message as ProviderMessage } from "../../../messaging/provider-types.js";
|
|
8
|
+
import { createBackfilledSenderProvenanceResolver } from "./backfill-sender-provenance.js";
|
|
9
|
+
|
|
10
|
+
const readsBySender = new Map<string, InboundTrustReadResult>();
|
|
11
|
+
const readInboundTrustMock = spyOn(
|
|
12
|
+
inboundTrustReader,
|
|
13
|
+
"readInboundTrust",
|
|
14
|
+
).mockImplementation(
|
|
15
|
+
async (input) =>
|
|
16
|
+
readsBySender.get(input.actorExternalId ?? "") ?? { ok: false },
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
readInboundTrustMock.mockRestore();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const GUARDIAN_ID = "U_GUARDIAN";
|
|
24
|
+
|
|
25
|
+
function memberVerdict(
|
|
26
|
+
trustClass: TrustVerdict["trustClass"],
|
|
27
|
+
contactId: string,
|
|
28
|
+
status: string,
|
|
29
|
+
): TrustVerdict {
|
|
30
|
+
return {
|
|
31
|
+
trustClass,
|
|
32
|
+
canonicalSenderId: `U_${contactId}`,
|
|
33
|
+
contactId,
|
|
34
|
+
channelId: `ch-${contactId}`,
|
|
35
|
+
status,
|
|
36
|
+
policy: "allow",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function readOf(verdict: TrustVerdict): InboundTrustReadResult {
|
|
41
|
+
return { ok: true, verdict, admissionPolicy: null };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
readsBySender.clear();
|
|
46
|
+
readInboundTrustMock.mockClear();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("createBackfilledSenderProvenanceResolver", () => {
|
|
50
|
+
test("stamps the gateway verdict's trust class and contact id", async () => {
|
|
51
|
+
readsBySender.set(
|
|
52
|
+
"U_ALICE",
|
|
53
|
+
readOf(memberVerdict("trusted_contact", "c-alice", "active")),
|
|
54
|
+
);
|
|
55
|
+
readsBySender.set(
|
|
56
|
+
"U_BOB",
|
|
57
|
+
readOf(memberVerdict("unverified_contact", "c-bob", "unverified")),
|
|
58
|
+
);
|
|
59
|
+
const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
|
|
60
|
+
|
|
61
|
+
expect(await resolve("U_ALICE")).toEqual({
|
|
62
|
+
provenanceTrustClass: "trusted_contact",
|
|
63
|
+
provenanceContactId: "c-alice",
|
|
64
|
+
});
|
|
65
|
+
expect(await resolve("U_BOB")).toEqual({
|
|
66
|
+
provenanceTrustClass: "unverified_contact",
|
|
67
|
+
provenanceContactId: "c-bob",
|
|
68
|
+
});
|
|
69
|
+
expect(readInboundTrustMock).toHaveBeenCalledWith({
|
|
70
|
+
channelType: "slack",
|
|
71
|
+
actorExternalId: "U_ALICE",
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("a stranger verdict carries no contact id and no lookup-failed marker", async () => {
|
|
76
|
+
readsBySender.set(
|
|
77
|
+
"U_STRANGER",
|
|
78
|
+
readOf({ trustClass: "unknown", canonicalSenderId: "U_STRANGER" }),
|
|
79
|
+
);
|
|
80
|
+
const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
|
|
81
|
+
|
|
82
|
+
expect(await resolve("U_STRANGER")).toEqual({
|
|
83
|
+
provenanceTrustClass: "unknown",
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("an unreadable verdict falls back to the guardian address comparison and marks the failed lookup", async () => {
|
|
88
|
+
const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
|
|
89
|
+
|
|
90
|
+
expect(await resolve(GUARDIAN_ID)).toEqual({
|
|
91
|
+
provenanceTrustClass: "guardian",
|
|
92
|
+
provenanceLookupFailed: true,
|
|
93
|
+
});
|
|
94
|
+
expect(await resolve("U_ALICE")).toEqual({
|
|
95
|
+
provenanceTrustClass: "unknown",
|
|
96
|
+
provenanceLookupFailed: true,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("a resolver-failure verdict is not trusted for class or contact id", async () => {
|
|
101
|
+
readsBySender.set(
|
|
102
|
+
"U_ALICE",
|
|
103
|
+
readOf({
|
|
104
|
+
...memberVerdict("trusted_contact", "c-alice", "active"),
|
|
105
|
+
resolutionFailed: true,
|
|
106
|
+
}),
|
|
107
|
+
);
|
|
108
|
+
const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
|
|
109
|
+
|
|
110
|
+
expect(await resolve("U_ALICE")).toEqual({
|
|
111
|
+
provenanceTrustClass: "unknown",
|
|
112
|
+
provenanceLookupFailed: true,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("reads the gateway once per distinct sender", async () => {
|
|
117
|
+
readsBySender.set(
|
|
118
|
+
"U_ALICE",
|
|
119
|
+
readOf(memberVerdict("trusted_contact", "c-alice", "active")),
|
|
120
|
+
);
|
|
121
|
+
const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
|
|
122
|
+
|
|
123
|
+
await Promise.all([
|
|
124
|
+
resolve("U_ALICE"),
|
|
125
|
+
resolve(" U_ALICE "),
|
|
126
|
+
resolve("U_BOB"),
|
|
127
|
+
]);
|
|
128
|
+
await resolve("U_ALICE");
|
|
129
|
+
|
|
130
|
+
expect(readInboundTrustMock).toHaveBeenCalledTimes(2);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("a row with no sender id is unknown without a gateway read or a lookup-failed marker", async () => {
|
|
134
|
+
const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
|
|
135
|
+
|
|
136
|
+
expect(await resolve(undefined)).toEqual({
|
|
137
|
+
provenanceTrustClass: "unknown",
|
|
138
|
+
});
|
|
139
|
+
expect(readInboundTrustMock).not.toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("starts every sender's read up front and together, bots included", () => {
|
|
143
|
+
const message = (id: string, senderId: string, isBot = false) =>
|
|
144
|
+
({
|
|
145
|
+
id,
|
|
146
|
+
conversationId: "C1",
|
|
147
|
+
sender: { id: senderId, name: senderId },
|
|
148
|
+
text: "hi",
|
|
149
|
+
timestamp: 0,
|
|
150
|
+
platform: "slack",
|
|
151
|
+
...(isBot ? { metadata: { isBot: true } } : {}),
|
|
152
|
+
}) satisfies ProviderMessage;
|
|
153
|
+
|
|
154
|
+
createBackfilledSenderProvenanceResolver(GUARDIAN_ID, [
|
|
155
|
+
message("1", "U_ALICE"),
|
|
156
|
+
message("2", "B_THIRD_PARTY", true),
|
|
157
|
+
message("3", "U_ALICE"),
|
|
158
|
+
message("4", "U_BOB"),
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
// Every read is in flight before any of them has settled.
|
|
162
|
+
expect(readInboundTrustMock.mock.calls.map(([input]) => input)).toEqual([
|
|
163
|
+
{ channelType: "slack", actorExternalId: "U_ALICE" },
|
|
164
|
+
{ channelType: "slack", actorExternalId: "B_THIRD_PARTY" },
|
|
165
|
+
{ channelType: "slack", actorExternalId: "U_BOB" },
|
|
166
|
+
]);
|
|
167
|
+
});
|
|
168
|
+
});
|