@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
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import type { ModeSession, ModeSessionMode } from "../api/mode-session.js";
|
|
2
|
+
import type {
|
|
3
|
+
ConversationModeSessionCoordinator,
|
|
4
|
+
ModeSessionSourceHandle,
|
|
5
|
+
ModeSessionTerminalDisposition,
|
|
6
|
+
} from "../daemon/conversation-mode-session.js";
|
|
7
|
+
import type { LiveVoiceSightSource } from "./protocol.js";
|
|
8
|
+
|
|
9
|
+
type ModeSessionCoordinator = Pick<
|
|
10
|
+
ConversationModeSessionCoordinator,
|
|
11
|
+
| "activateSource"
|
|
12
|
+
| "claimTurn"
|
|
13
|
+
| "recordActivity"
|
|
14
|
+
| "releaseTurn"
|
|
15
|
+
| "retireSource"
|
|
16
|
+
| "trackPersistedRow"
|
|
17
|
+
>;
|
|
18
|
+
|
|
19
|
+
interface CameraRun {
|
|
20
|
+
readonly epoch: number;
|
|
21
|
+
readonly source: LiveVoiceSightSource;
|
|
22
|
+
readonly handle: ModeSessionSourceHandle;
|
|
23
|
+
readonly syntheticTurnId: string;
|
|
24
|
+
pendingFrames: number;
|
|
25
|
+
retired: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CameraFrameToken {
|
|
29
|
+
readonly owner: ModeSessionSourceHandle;
|
|
30
|
+
readonly epoch: number;
|
|
31
|
+
readonly activation: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function modeFor(source: LiveVoiceSightSource): ModeSessionMode {
|
|
35
|
+
return source === "ambient" ? "ambient" : "live_vision";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Maps one voice session's negotiated camera epochs onto durable sessions. */
|
|
39
|
+
export class CameraModeSessionProducer {
|
|
40
|
+
readonly #coordinator: ModeSessionCoordinator;
|
|
41
|
+
readonly #sourceId: string;
|
|
42
|
+
readonly #runs = new Map<number, CameraRun>();
|
|
43
|
+
readonly #pendingFrameTokens = new Set<CameraFrameToken>();
|
|
44
|
+
#activeRun?: CameraRun;
|
|
45
|
+
#highestEpoch = 0;
|
|
46
|
+
|
|
47
|
+
constructor(coordinator: ModeSessionCoordinator, voiceSessionId: string) {
|
|
48
|
+
this.#coordinator = coordinator;
|
|
49
|
+
this.#sourceId = `live-voice-camera:${voiceSessionId}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
start(
|
|
53
|
+
epoch: number,
|
|
54
|
+
source: LiveVoiceSightSource = "live",
|
|
55
|
+
at = Date.now(),
|
|
56
|
+
): ModeSessionSourceHandle | undefined {
|
|
57
|
+
const known = this.#runs.get(epoch);
|
|
58
|
+
if (known) {
|
|
59
|
+
return !known.retired && known.source === source
|
|
60
|
+
? known.handle
|
|
61
|
+
: undefined;
|
|
62
|
+
}
|
|
63
|
+
if (epoch <= this.#highestEpoch) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (this.#activeRun) {
|
|
68
|
+
this.#retire(this.#activeRun, {
|
|
69
|
+
status: "interrupted",
|
|
70
|
+
endReason: "camera_source_replaced",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const handle = this.#coordinator.activateSource({
|
|
75
|
+
sourceId: this.#sourceId,
|
|
76
|
+
generation: epoch,
|
|
77
|
+
mode: modeFor(source),
|
|
78
|
+
sourceStartedAt: at,
|
|
79
|
+
lifetime: "source",
|
|
80
|
+
});
|
|
81
|
+
if (!handle) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const syntheticTurnId = `${this.#sourceId}:${epoch}:${handle.activation}`;
|
|
86
|
+
let owner: ModeSession | undefined;
|
|
87
|
+
try {
|
|
88
|
+
owner = this.#coordinator.claimTurn(syntheticTurnId, handle, at);
|
|
89
|
+
} catch (err) {
|
|
90
|
+
const rollbackError = this.#rollbackStart(handle, syntheticTurnId);
|
|
91
|
+
if (rollbackError) {
|
|
92
|
+
throw new AggregateError(
|
|
93
|
+
[err, rollbackError],
|
|
94
|
+
"Camera session claim and rollback both failed",
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
if (!owner || owner.id !== handle.id) {
|
|
100
|
+
const rollbackError = this.#rollbackStart(handle, syntheticTurnId);
|
|
101
|
+
if (rollbackError) {
|
|
102
|
+
throw rollbackError;
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const run: CameraRun = {
|
|
108
|
+
epoch,
|
|
109
|
+
source,
|
|
110
|
+
handle,
|
|
111
|
+
syntheticTurnId,
|
|
112
|
+
pendingFrames: 0,
|
|
113
|
+
retired: false,
|
|
114
|
+
};
|
|
115
|
+
this.#runs.set(epoch, run);
|
|
116
|
+
this.#highestEpoch = epoch;
|
|
117
|
+
this.#activeRun = run;
|
|
118
|
+
return handle;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#rollbackStart(
|
|
122
|
+
handle: ModeSessionSourceHandle,
|
|
123
|
+
syntheticTurnId: string,
|
|
124
|
+
): unknown {
|
|
125
|
+
let rollbackError: unknown;
|
|
126
|
+
try {
|
|
127
|
+
this.#coordinator.retireSource(handle, {
|
|
128
|
+
status: "interrupted",
|
|
129
|
+
endReason: "camera_start_refused",
|
|
130
|
+
});
|
|
131
|
+
} catch (err) {
|
|
132
|
+
rollbackError = err;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
this.#coordinator.releaseTurn(syntheticTurnId);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
rollbackError ??= err;
|
|
138
|
+
}
|
|
139
|
+
return rollbackError;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
captureTurn(): ModeSessionSourceHandle | undefined {
|
|
143
|
+
return this.#activeRun?.handle;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
holdDelivery(
|
|
147
|
+
turnId: string,
|
|
148
|
+
at = Date.now(),
|
|
149
|
+
): ModeSessionSourceHandle | undefined {
|
|
150
|
+
const handle = this.captureTurn();
|
|
151
|
+
if (!handle) {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
const owner = this.#coordinator.claimTurn(turnId, handle, at);
|
|
155
|
+
return owner?.id === handle.id ? handle : undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
releaseDelivery(turnId: string): void {
|
|
159
|
+
this.#coordinator.releaseTurn(turnId);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
beginFrame(
|
|
163
|
+
epoch: number,
|
|
164
|
+
source: LiveVoiceSightSource | undefined,
|
|
165
|
+
): CameraFrameToken | undefined {
|
|
166
|
+
const run = this.#activeRun;
|
|
167
|
+
if (
|
|
168
|
+
!run ||
|
|
169
|
+
run.retired ||
|
|
170
|
+
run.epoch !== epoch ||
|
|
171
|
+
(source !== undefined && source !== run.source)
|
|
172
|
+
) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
run.pendingFrames += 1;
|
|
176
|
+
const token = {
|
|
177
|
+
owner: run.handle,
|
|
178
|
+
epoch: run.epoch,
|
|
179
|
+
activation: run.handle.activation,
|
|
180
|
+
};
|
|
181
|
+
this.#pendingFrameTokens.add(token);
|
|
182
|
+
return token;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
finishFrame(
|
|
186
|
+
token: CameraFrameToken | undefined,
|
|
187
|
+
persisted?: { messageId: string; at: number },
|
|
188
|
+
): void {
|
|
189
|
+
if (!token || !this.#pendingFrameTokens.delete(token)) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const run = this.#runs.get(token.epoch);
|
|
193
|
+
if (!run || run.handle.activation !== token.activation) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
if (
|
|
198
|
+
persisted &&
|
|
199
|
+
this.#coordinator.recordActivity(run.syntheticTurnId, persisted.at)
|
|
200
|
+
) {
|
|
201
|
+
this.#coordinator.trackPersistedRow(
|
|
202
|
+
run.syntheticTurnId,
|
|
203
|
+
persisted.messageId,
|
|
204
|
+
persisted.at,
|
|
205
|
+
);
|
|
206
|
+
this.#coordinator.recordActivity(run.syntheticTurnId, persisted.at);
|
|
207
|
+
}
|
|
208
|
+
} finally {
|
|
209
|
+
run.pendingFrames = Math.max(0, run.pendingFrames - 1);
|
|
210
|
+
this.#releaseIfSettled(run);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
end(
|
|
215
|
+
epoch: number,
|
|
216
|
+
disposition: ModeSessionTerminalDisposition = {
|
|
217
|
+
status: "completed",
|
|
218
|
+
endReason: "camera_stopped",
|
|
219
|
+
},
|
|
220
|
+
): boolean {
|
|
221
|
+
const run = this.#runs.get(epoch);
|
|
222
|
+
if (!run || run.retired) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
return this.#retire(run, disposition);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
close(disposition: ModeSessionTerminalDisposition): boolean {
|
|
229
|
+
const run = this.#activeRun;
|
|
230
|
+
return run ? this.#retire(run, disposition) : false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
#retire(
|
|
234
|
+
run: CameraRun,
|
|
235
|
+
disposition: ModeSessionTerminalDisposition,
|
|
236
|
+
): boolean {
|
|
237
|
+
if (run.retired) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
let retired = false;
|
|
241
|
+
let retirementError: unknown;
|
|
242
|
+
try {
|
|
243
|
+
retired = this.#coordinator.retireSource(run.handle, disposition);
|
|
244
|
+
} catch (err) {
|
|
245
|
+
retirementError = err;
|
|
246
|
+
}
|
|
247
|
+
run.retired = true;
|
|
248
|
+
if (this.#activeRun === run) {
|
|
249
|
+
this.#activeRun = undefined;
|
|
250
|
+
}
|
|
251
|
+
let releaseError: unknown;
|
|
252
|
+
try {
|
|
253
|
+
this.#releaseIfSettled(run);
|
|
254
|
+
} catch (err) {
|
|
255
|
+
releaseError = err;
|
|
256
|
+
}
|
|
257
|
+
if (retirementError && releaseError) {
|
|
258
|
+
throw new AggregateError(
|
|
259
|
+
[retirementError, releaseError],
|
|
260
|
+
"Camera session retirement and turn release both failed",
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
if (retirementError) {
|
|
264
|
+
throw retirementError;
|
|
265
|
+
}
|
|
266
|
+
if (releaseError) {
|
|
267
|
+
throw releaseError;
|
|
268
|
+
}
|
|
269
|
+
return retired;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
#releaseIfSettled(run: CameraRun): void {
|
|
273
|
+
if (run.retired && run.pendingFrames === 0) {
|
|
274
|
+
this.#coordinator.releaseTurn(run.syntheticTurnId);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decides whether a barged-in request should keep running as a background
|
|
3
|
+
* continuation, from what the caller said when they interrupted.
|
|
4
|
+
*
|
|
5
|
+
* A barge-in hands the interrupted turn's work to a background subagent so
|
|
6
|
+
* it is not lost. That is right when the caller moved on to something else
|
|
7
|
+
* ("oh, also, what's the weather?") or only acknowledged it ("okay, thanks"),
|
|
8
|
+
* and wrong when the interruption cancels or changes the request ("actually,
|
|
9
|
+
* just point it out for me"): the continuation then does work the caller
|
|
10
|
+
* no longer wants, while the foreground turn handles the changed request.
|
|
11
|
+
*
|
|
12
|
+
* The judge runs only when the `voiceContinuationJudge` call site resolves to
|
|
13
|
+
* the TypeSafe provider. Without one, the session never waits on it. Every
|
|
14
|
+
* failure keeps the continuation, which is the behavior without a judge.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
askTypesafeNoul,
|
|
19
|
+
resolveTypesafeProvider,
|
|
20
|
+
} from "../calls/typesafe-noul.js";
|
|
21
|
+
import type { Provider } from "../providers/types.js";
|
|
22
|
+
|
|
23
|
+
export const VOICE_CONTINUATION_JUDGE_CALL_SITE = "voiceContinuationJudge";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Minimum P(yes) that keeps the continuation. Calibrated on labeled
|
|
27
|
+
* interruptions: cancels, corrections, and repeats scored 0.05 to 0.21;
|
|
28
|
+
* unrelated requests, acknowledgements, and progress questions scored 0.56
|
|
29
|
+
* to 0.89.
|
|
30
|
+
*/
|
|
31
|
+
export const CONTINUATION_JUDGE_KEEP_THRESHOLD = 0.4;
|
|
32
|
+
|
|
33
|
+
export const CONTINUATION_JUDGE_TIMEOUT_MS = 1_500;
|
|
34
|
+
|
|
35
|
+
const KEEP_QUESTION =
|
|
36
|
+
"The voice assistant was working on the caller's earlier request when the caller interrupted it. Should the assistant still finish that earlier request in the background, exactly as originally asked? Answer yes if the caller's new words are unrelated to it (a separate question, request, or remark), an acknowledgement, or a question about its progress. Answer no if the new words cancel it, change the caller's mind about it, correct or change what should be done, ask for it differently, or simply repeat it.";
|
|
37
|
+
|
|
38
|
+
export type ContinuationJudgeOutcome =
|
|
39
|
+
| "keep"
|
|
40
|
+
| "drop"
|
|
41
|
+
| "unavailable"
|
|
42
|
+
| "no_interruption"
|
|
43
|
+
| "timeout"
|
|
44
|
+
| "error";
|
|
45
|
+
|
|
46
|
+
export interface ContinuationJudgement {
|
|
47
|
+
keep: boolean;
|
|
48
|
+
outcome: ContinuationJudgeOutcome;
|
|
49
|
+
noul?: number;
|
|
50
|
+
latencyMs: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Judges one barge-in. `interruption` resolves with the caller's interrupting
|
|
55
|
+
* words once the next turn dispatches, or null when none arrived in time;
|
|
56
|
+
* the judge awaits it only when a TypeSafe profile is configured. Never
|
|
57
|
+
* rejects.
|
|
58
|
+
*/
|
|
59
|
+
export type LiveVoiceContinuationJudge = (args: {
|
|
60
|
+
parentConversationId: string;
|
|
61
|
+
interruptedRequest: string;
|
|
62
|
+
interruption: Promise<string | null>;
|
|
63
|
+
signal: AbortSignal;
|
|
64
|
+
}) => Promise<ContinuationJudgement>;
|
|
65
|
+
|
|
66
|
+
export function createContinuationJudge(options?: {
|
|
67
|
+
/** Provider resolver, injectable for tests. */
|
|
68
|
+
resolveProvider?: () => Promise<Provider | null>;
|
|
69
|
+
timeoutMs?: number;
|
|
70
|
+
}): LiveVoiceContinuationJudge {
|
|
71
|
+
const resolveProvider =
|
|
72
|
+
options?.resolveProvider ??
|
|
73
|
+
(() => resolveTypesafeProvider(VOICE_CONTINUATION_JUDGE_CALL_SITE));
|
|
74
|
+
return async (args) => {
|
|
75
|
+
const startedAt = Date.now();
|
|
76
|
+
const kept = (
|
|
77
|
+
outcome: ContinuationJudgeOutcome,
|
|
78
|
+
noul?: number,
|
|
79
|
+
): ContinuationJudgement => ({
|
|
80
|
+
keep: true,
|
|
81
|
+
outcome,
|
|
82
|
+
...(noul !== undefined ? { noul } : {}),
|
|
83
|
+
latencyMs: Date.now() - startedAt,
|
|
84
|
+
});
|
|
85
|
+
let provider: Provider | null;
|
|
86
|
+
try {
|
|
87
|
+
provider = await resolveProvider();
|
|
88
|
+
} catch {
|
|
89
|
+
return kept("error");
|
|
90
|
+
}
|
|
91
|
+
if (!provider || args.interruptedRequest.trim().length === 0) {
|
|
92
|
+
return kept("unavailable");
|
|
93
|
+
}
|
|
94
|
+
const interruption = (await args.interruption)?.trim() ?? "";
|
|
95
|
+
if (interruption.length === 0) {
|
|
96
|
+
return kept("no_interruption");
|
|
97
|
+
}
|
|
98
|
+
const result = await askTypesafeNoul({
|
|
99
|
+
callSite: VOICE_CONTINUATION_JUDGE_CALL_SITE,
|
|
100
|
+
conversationId: args.parentConversationId,
|
|
101
|
+
state: {
|
|
102
|
+
interrupted_request: args.interruptedRequest.trim(),
|
|
103
|
+
caller_then_said: interruption,
|
|
104
|
+
},
|
|
105
|
+
instructions: KEEP_QUESTION,
|
|
106
|
+
timeoutMs: options?.timeoutMs ?? CONTINUATION_JUDGE_TIMEOUT_MS,
|
|
107
|
+
signal: args.signal,
|
|
108
|
+
resolveProvider: async () => provider,
|
|
109
|
+
});
|
|
110
|
+
if (result.outcome !== "answered" || result.noul === undefined) {
|
|
111
|
+
return kept(result.outcome === "answered" ? "error" : result.outcome);
|
|
112
|
+
}
|
|
113
|
+
if (result.noul >= CONTINUATION_JUDGE_KEEP_THRESHOLD) {
|
|
114
|
+
return kept("keep", result.noul);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
keep: false,
|
|
118
|
+
outcome: "drop",
|
|
119
|
+
noul: result.noul,
|
|
120
|
+
latencyMs: Date.now() - startedAt,
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -33,6 +33,9 @@ import type { LiveVoiceServerFramePayload } from "./protocol.js";
|
|
|
33
33
|
|
|
34
34
|
const log = getLogger("live-activity-reporter");
|
|
35
35
|
|
|
36
|
+
/** A stale phase is preferable to letting one request pin every later phase. */
|
|
37
|
+
const LIVE_ACTIVITY_DISPATCH_TIMEOUT_MS = 5_000;
|
|
38
|
+
|
|
36
39
|
/** The phases an iOS Live Activity can render. Mirrors the client's union. */
|
|
37
40
|
export type LiveActivityPhase =
|
|
38
41
|
| "connecting"
|
|
@@ -42,6 +45,12 @@ export type LiveActivityPhase =
|
|
|
42
45
|
| "speaking"
|
|
43
46
|
| "ending";
|
|
44
47
|
|
|
48
|
+
interface PendingDispatch {
|
|
49
|
+
phase: LiveActivityPhase;
|
|
50
|
+
event: "update" | "end";
|
|
51
|
+
detail: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
/**
|
|
46
55
|
* The phase a frame puts the session into, or `null` for frames that do not
|
|
47
56
|
* move it.
|
|
@@ -88,6 +97,10 @@ export function phaseForFrame(
|
|
|
88
97
|
// Unconditional, unlike `stt_final`: the session sends this frame
|
|
89
98
|
// precisely when it has committed to a turn.
|
|
90
99
|
return "thinking";
|
|
100
|
+
case "activity":
|
|
101
|
+
// Keep model-routing detail inside the conversation, while ensuring a
|
|
102
|
+
// silent escalation leaves any preceding speaking phase behind.
|
|
103
|
+
return frame.kind === "escalation" ? "thinking" : null;
|
|
91
104
|
case "tts_audio":
|
|
92
105
|
return "speaking";
|
|
93
106
|
case "tts_done":
|
|
@@ -111,6 +124,11 @@ export class LiveActivityReporter {
|
|
|
111
124
|
private lastPhase: LiveActivityPhase | null = null;
|
|
112
125
|
private lastDetail = "";
|
|
113
126
|
private ended = false;
|
|
127
|
+
private dispatchInFlight = false;
|
|
128
|
+
private pendingDispatch: PendingDispatch | null = null;
|
|
129
|
+
private dispatchIdleWaiters: Array<() => void> = [];
|
|
130
|
+
protected readonly dispatchTimeoutMs: number =
|
|
131
|
+
LIVE_ACTIVITY_DISPATCH_TIMEOUT_MS;
|
|
114
132
|
|
|
115
133
|
constructor(private readonly conversationId: string) {}
|
|
116
134
|
|
|
@@ -128,10 +146,14 @@ export class LiveActivityReporter {
|
|
|
128
146
|
return;
|
|
129
147
|
}
|
|
130
148
|
const phase = phaseForFrame(frame, this.lastPhase);
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
149
|
+
// Tool and approval activity is server-owned wording. Escalation is an
|
|
150
|
+
// in-conversation status, so keep it out of the system-level Live Activity.
|
|
151
|
+
const detail =
|
|
152
|
+
frame.type === "activity"
|
|
153
|
+
? frame.kind === "escalation"
|
|
154
|
+
? ""
|
|
155
|
+
: frame.label
|
|
156
|
+
: this.lastDetail;
|
|
135
157
|
const phaseMoved = phase !== null && phase !== this.lastPhase;
|
|
136
158
|
if (!phaseMoved && detail === this.lastDetail) {
|
|
137
159
|
return;
|
|
@@ -147,7 +169,19 @@ export class LiveActivityReporter {
|
|
|
147
169
|
if (this.lastPhase === null) {
|
|
148
170
|
return;
|
|
149
171
|
}
|
|
150
|
-
|
|
172
|
+
this.enqueueDispatch(this.lastPhase, "update", this.lastDetail);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Move the system surface past escalation bridge speech without replacing a
|
|
177
|
+
* tool or approval line that became active while that speech was draining.
|
|
178
|
+
*/
|
|
179
|
+
restoreThinkingPhase(): void {
|
|
180
|
+
if (this.ended || this.lastPhase === "thinking") {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
this.lastPhase = "thinking";
|
|
184
|
+
this.enqueueDispatch("thinking", "update", this.lastDetail);
|
|
151
185
|
}
|
|
152
186
|
|
|
153
187
|
/**
|
|
@@ -166,7 +200,80 @@ export class LiveActivityReporter {
|
|
|
166
200
|
// No detail on the way out: whatever the turn was doing, it is not doing
|
|
167
201
|
// it any more, and this state is the one that lingers on the Lock Screen
|
|
168
202
|
// through the dismissal window.
|
|
169
|
-
|
|
203
|
+
this.enqueueDispatch("ending", "end", "");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Preserve frame order across asynchronous platform requests without making
|
|
208
|
+
* the session wait for them. A later phase must never arrive before an older
|
|
209
|
+
* request finishes and then be overwritten by that older content state.
|
|
210
|
+
*/
|
|
211
|
+
private enqueueDispatch(
|
|
212
|
+
phase: LiveActivityPhase,
|
|
213
|
+
event: "update" | "end",
|
|
214
|
+
detail: string,
|
|
215
|
+
): void {
|
|
216
|
+
const pending = { phase, event, detail } satisfies PendingDispatch;
|
|
217
|
+
if (this.dispatchInFlight) {
|
|
218
|
+
// ActivityKit renders snapshots, so only the newest state behind the
|
|
219
|
+
// current request matters. In particular, `end` replaces every queued
|
|
220
|
+
// update and is always the next request sent.
|
|
221
|
+
this.pendingDispatch = pending;
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
this.startDispatch(pending);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private startDispatch(pending: PendingDispatch): void {
|
|
228
|
+
this.dispatchInFlight = true;
|
|
229
|
+
const signal = AbortSignal.timeout(this.dispatchTimeoutMs);
|
|
230
|
+
const deadline = new Promise<void>((resolve) => {
|
|
231
|
+
if (signal.aborted) {
|
|
232
|
+
resolve();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
signal.addEventListener("abort", () => resolve(), { once: true });
|
|
236
|
+
});
|
|
237
|
+
// Race the whole operation, including platform-client and credential
|
|
238
|
+
// resolution. The same signal reaches `fetch`, so a timed-out operation
|
|
239
|
+
// that resolves credentials later cannot send its stale snapshot.
|
|
240
|
+
void Promise.race([
|
|
241
|
+
this.dispatch(pending.phase, pending.event, pending.detail, signal),
|
|
242
|
+
deadline,
|
|
243
|
+
])
|
|
244
|
+
.catch((err: unknown) => {
|
|
245
|
+
// `dispatch` contains its own best-effort error boundary. Keep this
|
|
246
|
+
// guard for subclasses and future implementations so one rejection
|
|
247
|
+
// cannot poison every later phase in the queue.
|
|
248
|
+
log.debug(
|
|
249
|
+
{ err, phase: pending.phase, event: pending.event },
|
|
250
|
+
"Live Activity dispatch queue failed",
|
|
251
|
+
);
|
|
252
|
+
})
|
|
253
|
+
.finally(() => {
|
|
254
|
+
const next = this.pendingDispatch;
|
|
255
|
+
this.pendingDispatch = null;
|
|
256
|
+
if (next !== null) {
|
|
257
|
+
this.startDispatch(next);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
this.dispatchInFlight = false;
|
|
261
|
+
const waiters = this.dispatchIdleWaiters;
|
|
262
|
+
this.dispatchIdleWaiters = [];
|
|
263
|
+
for (const resolve of waiters) {
|
|
264
|
+
resolve();
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Resolve after every queued dispatch has settled. */
|
|
270
|
+
protected waitForPendingDispatches(): Promise<void> {
|
|
271
|
+
if (!this.dispatchInFlight) {
|
|
272
|
+
return Promise.resolve();
|
|
273
|
+
}
|
|
274
|
+
return new Promise((resolve) => {
|
|
275
|
+
this.dispatchIdleWaiters.push(resolve);
|
|
276
|
+
});
|
|
170
277
|
}
|
|
171
278
|
|
|
172
279
|
/** `protected` so a test can observe what would be sent without sending it. */
|
|
@@ -174,6 +281,7 @@ export class LiveActivityReporter {
|
|
|
174
281
|
phase: LiveActivityPhase,
|
|
175
282
|
event: "update" | "end",
|
|
176
283
|
detail: string,
|
|
284
|
+
signal: AbortSignal,
|
|
177
285
|
): Promise<void> {
|
|
178
286
|
try {
|
|
179
287
|
const client = await VellumPlatformClient.create();
|
|
@@ -192,6 +300,7 @@ export class LiveActivityReporter {
|
|
|
192
300
|
event,
|
|
193
301
|
detail,
|
|
194
302
|
}),
|
|
303
|
+
signal,
|
|
195
304
|
});
|
|
196
305
|
if (!response.ok) {
|
|
197
306
|
log.debug(
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
import { createRequire } from "node:module";
|
|
13
13
|
|
|
14
|
+
import { isSessionGroupsEnabled } from "../config/session-groups-gate.js";
|
|
15
|
+
import type { SubagentParentNotification } from "../subagent/parent-notification.js";
|
|
14
16
|
import { getLogger } from "../util/logger.js";
|
|
15
17
|
import { LiveVoiceSessionManager } from "./live-voice-session-manager.js";
|
|
16
18
|
|
|
@@ -24,6 +26,15 @@ type LiveVoiceSessionFactory =
|
|
|
24
26
|
let manager: LiveVoiceSessionManager | null = null;
|
|
25
27
|
let bundledSessionFactory: LiveVoiceSessionFactory | null = null;
|
|
26
28
|
|
|
29
|
+
export function deliverSubagentNotificationToLiveVoice(
|
|
30
|
+
conversationId: string,
|
|
31
|
+
notification: SubagentParentNotification,
|
|
32
|
+
): boolean {
|
|
33
|
+
return (
|
|
34
|
+
manager?.deliverSubagentNotification(conversationId, notification) ?? false
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
27
38
|
export function setBundledLiveVoiceSessionFactory(
|
|
28
39
|
factory: LiveVoiceSessionFactory | null,
|
|
29
40
|
): void {
|
|
@@ -50,7 +61,31 @@ export function getLiveVoiceSessionManager(): LiveVoiceSessionManager {
|
|
|
50
61
|
(
|
|
51
62
|
require("./live-voice-session.js") as typeof import("./live-voice-session.js")
|
|
52
63
|
).createLiveVoiceSession;
|
|
53
|
-
|
|
64
|
+
const modeSessionOptions = isSessionGroupsEnabled()
|
|
65
|
+
? {
|
|
66
|
+
acquireModeSessionResidency: async (conversationId: string) => {
|
|
67
|
+
const { ensureConversationExists } =
|
|
68
|
+
require("../persistence/conversation-crud.js") as typeof import("../persistence/conversation-crud.js");
|
|
69
|
+
const { publishConversationListAndMetadataChanged } =
|
|
70
|
+
require("../runtime/sync/resource-sync-events.js") as typeof import("../runtime/sync/resource-sync-events.js");
|
|
71
|
+
if (ensureConversationExists(conversationId, "vellum")) {
|
|
72
|
+
publishConversationListAndMetadataChanged(
|
|
73
|
+
"created",
|
|
74
|
+
conversationId,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const { getOrCreateConversation } =
|
|
78
|
+
require("../daemon/conversation-store.js") as typeof import("../daemon/conversation-store.js");
|
|
79
|
+
const conversation =
|
|
80
|
+
await getOrCreateConversation(conversationId);
|
|
81
|
+
return {
|
|
82
|
+
coordinator: conversation.modeSessions,
|
|
83
|
+
release: conversation.acquireLiveVoiceResidency(),
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
: {};
|
|
88
|
+
return createSession(context, modeSessionOptions);
|
|
54
89
|
},
|
|
55
90
|
// The manager reclaims a slot on its own only when a session stopped
|
|
56
91
|
// behaving: either its teardown overran the close budget, or its client
|