@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
|
@@ -1112,7 +1112,7 @@ export async function wakeAgentForOpportunity(
|
|
|
1112
1112
|
try {
|
|
1113
1113
|
recordRequestLog(
|
|
1114
1114
|
conversationId,
|
|
1115
|
-
JSON.stringify(record.rawRequest),
|
|
1115
|
+
JSON.stringify(record.rawRequest ?? null),
|
|
1116
1116
|
JSON.stringify(record.rawResponse),
|
|
1117
1117
|
undefined,
|
|
1118
1118
|
record.provider,
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
* Layered approval message composition system.
|
|
3
3
|
*
|
|
4
4
|
* Generates approval prompt text through a priority chain:
|
|
5
|
-
* 1.
|
|
6
|
-
* 2.
|
|
7
|
-
* 3. Deterministic fallback templates (natural, scenario-specific messages)
|
|
5
|
+
* 1. Generator-produced rewrite of deterministic fallback text (when provided by daemon)
|
|
6
|
+
* 2. Deterministic fallback templates (natural, scenario-specific messages)
|
|
8
7
|
*/
|
|
9
8
|
import { getLogger } from "../util/logger.js";
|
|
10
9
|
import type {
|
|
@@ -25,18 +24,10 @@ const log = getLogger("approval-message-composer");
|
|
|
25
24
|
// Public API
|
|
26
25
|
// ---------------------------------------------------------------------------
|
|
27
26
|
|
|
28
|
-
/**
|
|
29
|
-
* Compose an approval message using layered source selection:
|
|
30
|
-
* 1. If an assistant preface is provided and non-empty, return it directly.
|
|
31
|
-
* 2. Otherwise fall back to a deterministic scenario-specific template.
|
|
32
|
-
*/
|
|
27
|
+
/** Compose an approval message from its deterministic scenario template. */
|
|
33
28
|
export function composeApprovalMessage(
|
|
34
29
|
context: ApprovalMessageContext,
|
|
35
30
|
): string {
|
|
36
|
-
if (context.assistantPreface && context.assistantPreface.trim().length > 0) {
|
|
37
|
-
return context.assistantPreface;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
31
|
return getFallbackMessage(context);
|
|
41
32
|
}
|
|
42
33
|
|
|
@@ -99,10 +90,6 @@ export async function composeApprovalMessageGenerative(
|
|
|
99
90
|
options: ComposeApprovalMessageGenerativeOptions = {},
|
|
100
91
|
generator?: ApprovalCopyGenerator,
|
|
101
92
|
): Promise<string> {
|
|
102
|
-
if (context.assistantPreface && context.assistantPreface.trim().length > 0) {
|
|
103
|
-
return context.assistantPreface;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
93
|
const fallbackText =
|
|
107
94
|
options.fallbackText?.trim() || getFallbackMessage(context);
|
|
108
95
|
|
|
@@ -155,45 +142,12 @@ export function getFallbackMessage(context: ApprovalMessageContext): string {
|
|
|
155
142
|
case "reminder_prompt":
|
|
156
143
|
return "There is a pending approval request. Ask a follow-up question or say approve/deny when you are ready.";
|
|
157
144
|
|
|
158
|
-
case "guardian_delivery_failed":
|
|
159
|
-
return context.toolName
|
|
160
|
-
? `Your request to run "${context.toolName}" could not be sent to the guardian for approval. The request has been denied for safety.`
|
|
161
|
-
: "I wasn't able to reach the guardian to request approval. The request has been denied for safety.";
|
|
162
|
-
|
|
163
|
-
case "guardian_request_forwarded":
|
|
164
|
-
return `Your request to use "${
|
|
165
|
-
context.toolName ?? "unknown"
|
|
166
|
-
}" has been forwarded to the guardian for approval. I'll let you know once they decide.`;
|
|
167
|
-
|
|
168
|
-
case "guardian_disambiguation":
|
|
169
|
-
return `There are ${
|
|
170
|
-
context.pendingCount ?? "multiple"
|
|
171
|
-
} pending approval requests. Please use the approval buttons to specify which request you're responding to.`;
|
|
172
|
-
|
|
173
145
|
case "guardian_identity_mismatch":
|
|
174
146
|
return "This approval request can only be handled by the designated guardian.";
|
|
175
147
|
|
|
176
148
|
case "request_pending_guardian":
|
|
177
149
|
return "Your request is pending guardian approval. Please wait for the guardian to respond.";
|
|
178
150
|
|
|
179
|
-
case "guardian_decision_outcome":
|
|
180
|
-
return `The guardian has ${
|
|
181
|
-
context.decision ?? "decided on"
|
|
182
|
-
} your request to use "${context.toolName ?? "unknown"}".`;
|
|
183
|
-
|
|
184
|
-
case "guardian_expired_requester":
|
|
185
|
-
return `The approval request for "${
|
|
186
|
-
context.toolName ?? "unknown"
|
|
187
|
-
}" has expired without a guardian response. The request has been denied.`;
|
|
188
|
-
|
|
189
|
-
case "guardian_expired_guardian":
|
|
190
|
-
return `The approval request from ${
|
|
191
|
-
context.requesterIdentifier ?? "the requester"
|
|
192
|
-
} for "${context.toolName ?? "unknown"}" has expired.`;
|
|
193
|
-
|
|
194
|
-
case "guardian_verify_success":
|
|
195
|
-
return "Guardian verification successful! You are now set as the guardian for this channel.";
|
|
196
|
-
|
|
197
151
|
case "guardian_verify_failed":
|
|
198
152
|
return `Verification failed. ${
|
|
199
153
|
context.failureReason ?? "Please try again."
|
|
@@ -220,29 +174,9 @@ export function getFallbackMessage(context: ApprovalMessageContext): string {
|
|
|
220
174
|
return `To complete guardian verification, send the code: ${code}.`;
|
|
221
175
|
}
|
|
222
176
|
|
|
223
|
-
case "guardian_verify_status_bound":
|
|
224
|
-
return "A guardian is currently active for this channel.";
|
|
225
|
-
|
|
226
|
-
case "guardian_verify_status_unbound":
|
|
227
|
-
return "No guardian is currently configured for this channel.";
|
|
228
|
-
|
|
229
|
-
case "guardian_deny_no_identity":
|
|
230
|
-
return "This action requires approval, but your identity could not be verified. The request has been denied for safety.";
|
|
231
|
-
|
|
232
|
-
case "guardian_deny_no_binding":
|
|
233
|
-
return "This action requires guardian approval, but no guardian has been configured for this channel. The request has been denied for safety.";
|
|
234
|
-
|
|
235
|
-
case "requester_cancel":
|
|
236
|
-
return context.toolName
|
|
237
|
-
? `Your request to use "${context.toolName}" has been cancelled.`
|
|
238
|
-
: "Your pending request has been cancelled.";
|
|
239
|
-
|
|
240
177
|
case "approval_already_resolved":
|
|
241
178
|
return "This approval request has already been resolved.";
|
|
242
179
|
|
|
243
|
-
case "guardian_text_unavailable":
|
|
244
|
-
return "I can't process text replies for approvals right now. Please use the approve/deny buttons above to respond.";
|
|
245
|
-
|
|
246
180
|
default: {
|
|
247
181
|
// Exhaustive check — TypeScript will flag if a scenario is missing.
|
|
248
182
|
const _exhaustive: never = context.scenario;
|
|
@@ -49,10 +49,15 @@ export function capabilityForMessageType(
|
|
|
49
49
|
}
|
|
50
50
|
import type { AssistantEventEnvelope } from "../api/index.js";
|
|
51
51
|
import { forwardEventPublishToDaemon } from "../ipc/events-publish-client.js";
|
|
52
|
+
import {
|
|
53
|
+
type ClientConnectionEventReason,
|
|
54
|
+
recordClientConnectionEvent,
|
|
55
|
+
} from "../persistence/client-connection-events-store.js";
|
|
52
56
|
import { appendEventToStream } from "../signals/event-stream.js";
|
|
53
57
|
import { getLogger } from "../util/logger.js";
|
|
54
58
|
import { buildAssistantEvent } from "./assistant-event.js";
|
|
55
59
|
import type { AssistantEventPublishOptions } from "./assistant-event-publish-options.js";
|
|
60
|
+
import { matchesTargeting } from "./assistant-event-targeting.js";
|
|
56
61
|
import { stampAndBuffer } from "./assistant-stream-state.js";
|
|
57
62
|
import { isMainDaemonProcess } from "./process-role.js";
|
|
58
63
|
|
|
@@ -72,7 +77,7 @@ export type AssistantEventCallback = (
|
|
|
72
77
|
|
|
73
78
|
/** Opaque handle returned by `subscribe`. Call `dispose()` to remove the subscription. */
|
|
74
79
|
export interface AssistantEventSubscription {
|
|
75
|
-
dispose(): void;
|
|
80
|
+
dispose(reason?: ClientConnectionEventReason): void;
|
|
76
81
|
/** True until `dispose()` has been called. */
|
|
77
82
|
readonly active: boolean;
|
|
78
83
|
/**
|
|
@@ -142,6 +147,10 @@ interface ClientEntry extends BaseSubscriberEntry {
|
|
|
142
147
|
* service-token connections that have no principal.
|
|
143
148
|
*/
|
|
144
149
|
actorPrincipalId?: string;
|
|
150
|
+
/** Client-reported build version (e.g. extension manifest version). */
|
|
151
|
+
clientVersion?: string;
|
|
152
|
+
/** Whether the client advertised an SSE idle watchdog on this connection. */
|
|
153
|
+
sseWatchdog?: boolean;
|
|
145
154
|
/**
|
|
146
155
|
* Last desktop presence reported by this client, for clients that report it.
|
|
147
156
|
* In-memory only, so consumers must fail open when it is absent.
|
|
@@ -193,14 +202,22 @@ type SubscriberInput = DistributiveOmit<
|
|
|
193
202
|
* Client connections register as subscribers with metadata and are queryable
|
|
194
203
|
* via `listClients()`, `getMostRecentClientByCapability()`, etc.
|
|
195
204
|
*/
|
|
205
|
+
export type ClientConnectionRecorder = typeof recordClientConnectionEvent;
|
|
206
|
+
|
|
196
207
|
export class AssistantEventHub {
|
|
197
208
|
private readonly subscribers = new Set<SubscriberEntry>();
|
|
198
209
|
private readonly maxSubscribers: number;
|
|
199
210
|
/** Monotonic source for per-connection ids, scoped to this hub. */
|
|
200
211
|
private connectionCounter = 0;
|
|
212
|
+
private readonly recordConnection: ClientConnectionRecorder;
|
|
201
213
|
|
|
202
|
-
constructor(options?: {
|
|
214
|
+
constructor(options?: {
|
|
215
|
+
maxSubscribers?: number;
|
|
216
|
+
recordConnection?: ClientConnectionRecorder;
|
|
217
|
+
}) {
|
|
203
218
|
this.maxSubscribers = options?.maxSubscribers ?? Infinity;
|
|
219
|
+
this.recordConnection =
|
|
220
|
+
options?.recordConnection ?? recordClientConnectionEvent;
|
|
204
221
|
}
|
|
205
222
|
|
|
206
223
|
/**
|
|
@@ -231,6 +248,7 @@ export class AssistantEventHub {
|
|
|
231
248
|
for (const entry of stale) {
|
|
232
249
|
entry.active = false;
|
|
233
250
|
this.subscribers.delete(entry);
|
|
251
|
+
this.recordClientLifecycle(entry, "stale_replaced");
|
|
234
252
|
try {
|
|
235
253
|
entry.onEvict();
|
|
236
254
|
} catch {
|
|
@@ -258,6 +276,7 @@ export class AssistantEventHub {
|
|
|
258
276
|
}
|
|
259
277
|
oldest.active = false;
|
|
260
278
|
this.subscribers.delete(oldest);
|
|
279
|
+
this.recordClientLifecycle(oldest, "cap_evicted");
|
|
261
280
|
try {
|
|
262
281
|
oldest.onEvict();
|
|
263
282
|
} catch {
|
|
@@ -287,6 +306,7 @@ export class AssistantEventHub {
|
|
|
287
306
|
},
|
|
288
307
|
"subscriber registered (client)",
|
|
289
308
|
);
|
|
309
|
+
this.recordClientLifecycle(entry, "sse_open");
|
|
290
310
|
} else {
|
|
291
311
|
log.info({ connectionId }, "subscriber registered (process)");
|
|
292
312
|
}
|
|
@@ -294,11 +314,12 @@ export class AssistantEventHub {
|
|
|
294
314
|
this.subscribers.add(entry);
|
|
295
315
|
|
|
296
316
|
return {
|
|
297
|
-
dispose: () => {
|
|
317
|
+
dispose: (reason) => {
|
|
298
318
|
if (entry.active) {
|
|
299
319
|
entry.active = false;
|
|
300
320
|
this.subscribers.delete(entry);
|
|
301
321
|
if (entry.type === "client") {
|
|
322
|
+
this.recordClientLifecycle(entry, reason ?? "sse_close");
|
|
302
323
|
log.info(
|
|
303
324
|
{
|
|
304
325
|
clientId: entry.clientId,
|
|
@@ -323,21 +344,14 @@ export class AssistantEventHub {
|
|
|
323
344
|
* Publish an event to all matching subscribers.
|
|
324
345
|
*
|
|
325
346
|
* Matching rules:
|
|
326
|
-
* -
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
* -
|
|
331
|
-
*
|
|
332
|
-
* event's conversationId
|
|
333
|
-
*
|
|
334
|
-
* - if `filter.conversationId` is set (and `targetClientId` is not), the
|
|
335
|
-
* `event.conversationId` must equal it
|
|
336
|
-
* - if `targetCapability` is set, only subscribers whose capabilities include
|
|
337
|
-
* it receive the event; untargeted events go to all
|
|
338
|
-
* - if `targetInterfaceId` is set, only client subscribers whose
|
|
339
|
-
* `interfaceId` matches receive the event; process subscribers and
|
|
340
|
-
* non-matching clients are skipped.
|
|
347
|
+
* - the event's targeting (`excludeClientId`, `targetInterfaceId`,
|
|
348
|
+
* `targetActorPrincipalId`, `targetClientId`, `targetCapability`) must
|
|
349
|
+
* admit the subscriber, per {@link matchesTargeting}, the same check
|
|
350
|
+
* replay applies
|
|
351
|
+
* - a subscriber's `filter.conversationId` must equal `event.conversationId`,
|
|
352
|
+
* unless the event names a client with `targetClientId` (a web-origin
|
|
353
|
+
* event's conversationId can differ from the conversation the targeted
|
|
354
|
+
* macOS client subscribed to)
|
|
341
355
|
*
|
|
342
356
|
* Fanout is isolated: a throwing or rejecting subscriber does not abort
|
|
343
357
|
* delivery to remaining subscribers.
|
|
@@ -362,10 +376,6 @@ export class AssistantEventHub {
|
|
|
362
376
|
}
|
|
363
377
|
}
|
|
364
378
|
|
|
365
|
-
const targetCapability = options?.targetCapability;
|
|
366
|
-
const targetClientId = options?.targetClientId;
|
|
367
|
-
const targetInterfaceId = options?.targetInterfaceId;
|
|
368
|
-
const excludeClientId = options?.excludeClientId;
|
|
369
379
|
const snapshot = Array.from(this.subscribers);
|
|
370
380
|
const errors: unknown[] = [];
|
|
371
381
|
|
|
@@ -373,63 +383,18 @@ export class AssistantEventHub {
|
|
|
373
383
|
if (!entry.active) {
|
|
374
384
|
continue;
|
|
375
385
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
// both targeted and untargeted broadcasts.
|
|
386
|
+
if (!matchesTargeting(options, entry)) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
380
389
|
if (
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
entry.
|
|
390
|
+
options?.targetClientId == null &&
|
|
391
|
+
event.conversationId != null &&
|
|
392
|
+
entry.filter.conversationId != null &&
|
|
393
|
+
entry.filter.conversationId !== event.conversationId
|
|
384
394
|
) {
|
|
385
395
|
continue;
|
|
386
396
|
}
|
|
387
397
|
|
|
388
|
-
// Interface targeting: skip any subscriber that is not a client of
|
|
389
|
-
// the requested interface. Composes with `targetClientId` and
|
|
390
|
-
// `targetCapability` below.
|
|
391
|
-
if (targetInterfaceId != null) {
|
|
392
|
-
if (
|
|
393
|
-
entry.type !== "client" ||
|
|
394
|
-
entry.interfaceId !== targetInterfaceId
|
|
395
|
-
) {
|
|
396
|
-
continue;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
if (targetClientId != null) {
|
|
401
|
-
// Targeted: bypass conversation filter, deliver only to the named client.
|
|
402
|
-
if (entry.type !== "client" || entry.clientId !== targetClientId) {
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
if (
|
|
406
|
-
targetCapability != null &&
|
|
407
|
-
!entry.capabilities.includes(targetCapability)
|
|
408
|
-
) {
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
} else {
|
|
412
|
-
// Untargeted: existing conversation-scoped + capability logic.
|
|
413
|
-
if (
|
|
414
|
-
event.conversationId != null &&
|
|
415
|
-
entry.filter.conversationId != null &&
|
|
416
|
-
entry.filter.conversationId !== event.conversationId
|
|
417
|
-
) {
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Capability targeting: targeted events only go to subscribers that
|
|
422
|
-
// declare the required capability.
|
|
423
|
-
if (targetCapability != null) {
|
|
424
|
-
if (
|
|
425
|
-
entry.type !== "client" ||
|
|
426
|
-
!entry.capabilities.includes(targetCapability)
|
|
427
|
-
) {
|
|
428
|
-
continue;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
398
|
try {
|
|
434
399
|
await entry.callback(event);
|
|
435
400
|
} catch (err) {
|
|
@@ -673,6 +638,7 @@ export class AssistantEventHub {
|
|
|
673
638
|
for (const entry of targets) {
|
|
674
639
|
entry.active = false;
|
|
675
640
|
this.subscribers.delete(entry);
|
|
641
|
+
this.recordClientLifecycle(entry, "force_disconnect");
|
|
676
642
|
try {
|
|
677
643
|
entry.onEvict();
|
|
678
644
|
} catch {
|
|
@@ -688,6 +654,29 @@ export class AssistantEventHub {
|
|
|
688
654
|
return targets.length;
|
|
689
655
|
}
|
|
690
656
|
|
|
657
|
+
/**
|
|
658
|
+
* Best-effort history row for a client subscriber. Process subscribers
|
|
659
|
+
* and an unready database are skipped; a write failure never throws.
|
|
660
|
+
*/
|
|
661
|
+
private recordClientLifecycle(
|
|
662
|
+
entry: SubscriberEntry,
|
|
663
|
+
reason: ClientConnectionEventReason,
|
|
664
|
+
): void {
|
|
665
|
+
if (entry.type !== "client") {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
this.recordConnection({
|
|
669
|
+
clientId: entry.clientId,
|
|
670
|
+
interfaceId: entry.interfaceId,
|
|
671
|
+
connectionId: entry.connectionId,
|
|
672
|
+
reason,
|
|
673
|
+
actorPrincipalId: entry.actorPrincipalId,
|
|
674
|
+
clientVersion: entry.clientVersion,
|
|
675
|
+
sseWatchdog: entry.sseWatchdog,
|
|
676
|
+
machineName: entry.machineName,
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
|
|
691
680
|
/** Number of currently active subscribers (useful for tests and caps). */
|
|
692
681
|
subscriberCount(): number {
|
|
693
682
|
return this.subscribers.size;
|
|
@@ -716,6 +705,12 @@ export const assistantEventHub = new AssistantEventHub({ maxSubscribers: 100 });
|
|
|
716
705
|
*/
|
|
717
706
|
let _hubChain = Promise.resolve();
|
|
718
707
|
|
|
708
|
+
/** Targeting a caller of {@link broadcastMessage} may request. */
|
|
709
|
+
export type BroadcastMessageOptions = Pick<
|
|
710
|
+
AssistantEventPublishOptions,
|
|
711
|
+
"targetClientId" | "targetInterfaceId" | "targetActorPrincipalId"
|
|
712
|
+
>;
|
|
713
|
+
|
|
719
714
|
/**
|
|
720
715
|
* Wraps a `AssistantEvent` in an `AssistantEventEnvelope` envelope and publishes it
|
|
721
716
|
* to the process-level hub.
|
|
@@ -735,11 +730,12 @@ let _hubChain = Promise.resolve();
|
|
|
735
730
|
export function broadcastMessage(
|
|
736
731
|
msg: AssistantEvent,
|
|
737
732
|
conversationId?: string,
|
|
738
|
-
options?:
|
|
733
|
+
options?: BroadcastMessageOptions,
|
|
739
734
|
): void {
|
|
740
735
|
const resolvedConversationId = conversationId ?? extractConversationId(msg);
|
|
741
736
|
const targetClientId = options?.targetClientId;
|
|
742
737
|
const targetInterfaceId = options?.targetInterfaceId;
|
|
738
|
+
const targetActorPrincipalId = options?.targetActorPrincipalId;
|
|
743
739
|
|
|
744
740
|
const event = buildAssistantEvent(msg, resolvedConversationId);
|
|
745
741
|
// A reconnect can overlap an older executor with the same device ID.
|
|
@@ -765,11 +761,13 @@ export function broadcastMessage(
|
|
|
765
761
|
targetCapability != null ||
|
|
766
762
|
targetClientId != null ||
|
|
767
763
|
targetInterfaceId != null ||
|
|
764
|
+
targetActorPrincipalId != null ||
|
|
768
765
|
excludeClientId != null
|
|
769
766
|
? {
|
|
770
767
|
targetCapability,
|
|
771
768
|
targetClientId,
|
|
772
769
|
targetInterfaceId,
|
|
770
|
+
targetActorPrincipalId,
|
|
773
771
|
excludeClientId,
|
|
774
772
|
}
|
|
775
773
|
: undefined;
|
|
@@ -15,6 +15,13 @@ export const AssistantEventPublishOptionsSchema = z.object({
|
|
|
15
15
|
targetCapability: z.enum(HOST_PROXY_CAPABILITIES).optional(),
|
|
16
16
|
targetClientId: z.string().optional(),
|
|
17
17
|
targetInterfaceId: z.enum(INTERFACE_IDS).optional(),
|
|
18
|
+
/**
|
|
19
|
+
* Deliver only to client subscribers whose verified `actorPrincipalId`
|
|
20
|
+
* equals this value. Subscribers without a principal never match, so an
|
|
21
|
+
* event scoped to one person fails closed rather than reaching a connection
|
|
22
|
+
* whose identity is unknown.
|
|
23
|
+
*/
|
|
24
|
+
targetActorPrincipalId: z.string().optional(),
|
|
18
25
|
/**
|
|
19
26
|
* Skip the subscriber with this `clientId`. Used for self-echo suppression on
|
|
20
27
|
* `sync_changed`: the route handler echoes the originating tab's
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who receives a targeted assistant event. Live fanout
|
|
3
|
+
* (`AssistantEventHub.publish`) and replay (`getReplayWindow`, reached from
|
|
4
|
+
* the SSE reconnect and the `events/tail` route) both decide delivery with
|
|
5
|
+
* {@link matchesTargeting}, so a targeting rule added here reaches every
|
|
6
|
+
* path at once.
|
|
7
|
+
*
|
|
8
|
+
* Conversation scoping is deliberately not part of this check. Live fanout
|
|
9
|
+
* applies a subscriber's conversation filter only to events that do not name
|
|
10
|
+
* a client; replay scopes to the conversation its caller asked for. Each path
|
|
11
|
+
* applies its own scoping after this check.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { HostProxyCapability, InterfaceId } from "../channels/types.js";
|
|
15
|
+
import type { AssistantEventPublishOptions } from "./assistant-event-publish-options.js";
|
|
16
|
+
|
|
17
|
+
/** The connection an event is being delivered to, live or on replay. */
|
|
18
|
+
export interface SubscriberIdentity {
|
|
19
|
+
type: "client" | "process";
|
|
20
|
+
clientId?: string;
|
|
21
|
+
interfaceId?: InterfaceId;
|
|
22
|
+
capabilities?: readonly HostProxyCapability[];
|
|
23
|
+
actorPrincipalId?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Whether `subscriber` receives an event carrying `targeting`. An event with
|
|
28
|
+
* no targeting reaches every subscriber. Every rule a targeted event sets must
|
|
29
|
+
* hold, and a process subscriber never matches a rule that names a client
|
|
30
|
+
* property.
|
|
31
|
+
*/
|
|
32
|
+
export function matchesTargeting(
|
|
33
|
+
targeting: AssistantEventPublishOptions | undefined,
|
|
34
|
+
subscriber: SubscriberIdentity,
|
|
35
|
+
): boolean {
|
|
36
|
+
if (!targeting) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
const isClient = subscriber.type === "client";
|
|
40
|
+
|
|
41
|
+
// Self-echo suppression: the client that originated a mutation does not
|
|
42
|
+
// receive its own invalidation back.
|
|
43
|
+
if (
|
|
44
|
+
targeting.excludeClientId != null &&
|
|
45
|
+
isClient &&
|
|
46
|
+
subscriber.clientId === targeting.excludeClientId
|
|
47
|
+
) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (
|
|
51
|
+
targeting.targetInterfaceId != null &&
|
|
52
|
+
(!isClient || subscriber.interfaceId !== targeting.targetInterfaceId)
|
|
53
|
+
) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
// An event scoped to one person reaches only that person's connections; a
|
|
57
|
+
// connection with no verified principal never matches.
|
|
58
|
+
if (
|
|
59
|
+
targeting.targetActorPrincipalId != null &&
|
|
60
|
+
(!isClient ||
|
|
61
|
+
subscriber.actorPrincipalId !== targeting.targetActorPrincipalId)
|
|
62
|
+
) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (
|
|
66
|
+
targeting.targetClientId != null &&
|
|
67
|
+
(!isClient || subscriber.clientId !== targeting.targetClientId)
|
|
68
|
+
) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (
|
|
72
|
+
targeting.targetCapability != null &&
|
|
73
|
+
(!isClient ||
|
|
74
|
+
!subscriber.capabilities?.includes(targeting.targetCapability))
|
|
75
|
+
) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
@@ -70,6 +70,11 @@ import {
|
|
|
70
70
|
import type { AssistantEventEnvelope } from "../api/index.js";
|
|
71
71
|
import { getLogger } from "../util/logger.js";
|
|
72
72
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
73
|
+
import type { AssistantEventPublishOptions } from "./assistant-event-publish-options.js";
|
|
74
|
+
import {
|
|
75
|
+
matchesTargeting,
|
|
76
|
+
type SubscriberIdentity,
|
|
77
|
+
} from "./assistant-event-targeting.js";
|
|
73
78
|
|
|
74
79
|
const log = getLogger("assistant-stream-state");
|
|
75
80
|
|
|
@@ -93,39 +98,13 @@ const SEQ_RESERVATION_BLOCK = 1024;
|
|
|
93
98
|
|
|
94
99
|
// ── Types ────────────────────────────────────────────────────────────
|
|
95
100
|
|
|
96
|
-
/**
|
|
97
|
-
* Targeting / exclusion modifiers attached to an event at publish time.
|
|
98
|
-
* Stored on ring entries so replay can re-apply the same delivery
|
|
99
|
-
* filter that the live `publish()` path used.
|
|
100
|
-
*
|
|
101
|
-
* Fields use plain `string` rather than branded channel types so
|
|
102
|
-
* this module stays independent of the `channels/` package.
|
|
103
|
-
*/
|
|
104
|
-
export interface EventTargeting {
|
|
105
|
-
targetCapability?: string;
|
|
106
|
-
targetClientId?: string;
|
|
107
|
-
targetInterfaceId?: string;
|
|
108
|
-
excludeClientId?: string;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Identity of the subscriber requesting a replay window. Replay
|
|
113
|
-
* filtering mirrors the live `publish()` logic in `AssistantEventHub`:
|
|
114
|
-
* targeted entries are only delivered when the subscriber matches.
|
|
115
|
-
*/
|
|
116
|
-
export interface ReplaySubscriber {
|
|
117
|
-
type: "client" | "process";
|
|
118
|
-
clientId?: string;
|
|
119
|
-
interfaceId?: string;
|
|
120
|
-
capabilities?: readonly string[];
|
|
121
|
-
}
|
|
122
|
-
|
|
123
101
|
interface RingEntry {
|
|
124
102
|
seq: number;
|
|
125
103
|
event: AssistantEventEnvelope;
|
|
126
104
|
emittedAt: number;
|
|
127
105
|
sizeBytes: number;
|
|
128
|
-
|
|
106
|
+
/** The publish options the event went out with, re-applied on replay. */
|
|
107
|
+
targeting?: AssistantEventPublishOptions;
|
|
129
108
|
}
|
|
130
109
|
|
|
131
110
|
interface AssistantStreamState {
|
|
@@ -215,7 +194,7 @@ export function isStreamSeqStampingDisabled(): boolean {
|
|
|
215
194
|
*/
|
|
216
195
|
export function stampAndBuffer(
|
|
217
196
|
event: AssistantEventEnvelope,
|
|
218
|
-
options?: { targeting?:
|
|
197
|
+
options?: { targeting?: AssistantEventPublishOptions },
|
|
219
198
|
): void {
|
|
220
199
|
if (stampingDisabled) {
|
|
221
200
|
return;
|
|
@@ -272,9 +251,9 @@ export function stampAndBuffer(
|
|
|
272
251
|
* buffered entry -- callers should fall back to a snapshot resync.
|
|
273
252
|
*
|
|
274
253
|
* When `subscriber` is provided, entries carrying targeting metadata
|
|
275
|
-
* are filtered
|
|
276
|
-
* `AssistantEventHub
|
|
277
|
-
* outside their intended delivery set on reconnect.
|
|
254
|
+
* are filtered with {@link matchesTargeting}, the check the live
|
|
255
|
+
* `publish()` path in `AssistantEventHub` applies, so targeted events do
|
|
256
|
+
* not leak to subscribers outside their intended delivery set on reconnect.
|
|
278
257
|
*
|
|
279
258
|
* When `conversationId` is provided, only that conversation's events are
|
|
280
259
|
* returned -- a conversation-scoped subscription only delivers its own
|
|
@@ -287,7 +266,7 @@ export function stampAndBuffer(
|
|
|
287
266
|
*/
|
|
288
267
|
export function getReplayWindow(
|
|
289
268
|
lastSeenSeq: number,
|
|
290
|
-
subscriber?:
|
|
269
|
+
subscriber?: SubscriberIdentity,
|
|
291
270
|
conversationId?: string,
|
|
292
271
|
): readonly AssistantEventEnvelope[] | null {
|
|
293
272
|
evict();
|
|
@@ -315,7 +294,7 @@ export function getReplayWindow(
|
|
|
315
294
|
entry.seq > lastSeenSeq &&
|
|
316
295
|
(conversationId == null ||
|
|
317
296
|
entry.event.conversationId === conversationId) &&
|
|
318
|
-
(subscriber == null ||
|
|
297
|
+
(subscriber == null || matchesTargeting(entry.targeting, subscriber)),
|
|
319
298
|
)
|
|
320
299
|
.map((entry) => entry.event);
|
|
321
300
|
}
|
|
@@ -518,72 +497,6 @@ function readReservedCeiling(): number {
|
|
|
518
497
|
return 0;
|
|
519
498
|
}
|
|
520
499
|
|
|
521
|
-
/**
|
|
522
|
-
* Mirrors the delivery logic in `AssistantEventHub.publish()`. Returns
|
|
523
|
-
* `true` when `subscriber` would have received the entry during live
|
|
524
|
-
* fanout.
|
|
525
|
-
*/
|
|
526
|
-
function matchesSubscriber(
|
|
527
|
-
entry: RingEntry,
|
|
528
|
-
subscriber: ReplaySubscriber,
|
|
529
|
-
): boolean {
|
|
530
|
-
const t = entry.targeting;
|
|
531
|
-
if (!t) {
|
|
532
|
-
return true;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// Self-echo suppression: the originating client never receives the
|
|
536
|
-
// event back.
|
|
537
|
-
if (
|
|
538
|
-
t.excludeClientId != null &&
|
|
539
|
-
subscriber.type === "client" &&
|
|
540
|
-
subscriber.clientId === t.excludeClientId
|
|
541
|
-
) {
|
|
542
|
-
return false;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// Interface targeting: only clients of the requested interface.
|
|
546
|
-
if (t.targetInterfaceId != null) {
|
|
547
|
-
if (
|
|
548
|
-
subscriber.type !== "client" ||
|
|
549
|
-
subscriber.interfaceId !== t.targetInterfaceId
|
|
550
|
-
) {
|
|
551
|
-
return false;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (t.targetClientId != null) {
|
|
556
|
-
// Client targeting: bypass conversation filter, deliver only to the
|
|
557
|
-
// named client.
|
|
558
|
-
if (
|
|
559
|
-
subscriber.type !== "client" ||
|
|
560
|
-
subscriber.clientId !== t.targetClientId
|
|
561
|
-
) {
|
|
562
|
-
return false;
|
|
563
|
-
}
|
|
564
|
-
if (
|
|
565
|
-
t.targetCapability != null &&
|
|
566
|
-
!subscriber.capabilities?.includes(t.targetCapability)
|
|
567
|
-
) {
|
|
568
|
-
return false;
|
|
569
|
-
}
|
|
570
|
-
return true;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Capability targeting (without client targeting): only subscribers
|
|
574
|
-
// that declare the required capability.
|
|
575
|
-
if (t.targetCapability != null) {
|
|
576
|
-
if (
|
|
577
|
-
subscriber.type !== "client" ||
|
|
578
|
-
!subscriber.capabilities?.includes(t.targetCapability)
|
|
579
|
-
) {
|
|
580
|
-
return false;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
return true;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
500
|
function evict(): void {
|
|
588
501
|
const now = Date.now();
|
|
589
502
|
while (state.ring.length > 0) {
|