@vellumai/assistant 0.11.2 → 0.11.3-dev.202608080427.92f668a
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 +69 -9
- package/docs/flux-turn-detection-spike.md +243 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +50 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +50 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +50 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +619 -47
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-controller.test.ts +120 -0
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/config-loader-backfill.test.ts +19 -0
- package/src/__tests__/config-schema.test.ts +150 -7
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +54 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +294 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-output.test.ts +175 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/media-stream-stt-session.test.ts +67 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +150 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/tts-text-sanitizer.test.ts +13 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +131 -12
- package/src/calls/__tests__/voice-triage-escalate.test.ts +106 -0
- package/src/calls/call-controller.ts +30 -2
- package/src/calls/call-speech-output.ts +12 -4
- package/src/calls/call-transport.ts +18 -1
- package/src/calls/media-stream-output.ts +53 -5
- package/src/calls/media-stream-server.ts +21 -1
- package/src/calls/media-stream-stt-session.ts +49 -0
- package/src/calls/telephony-synthesis-language.ts +84 -0
- package/src/calls/tts-text-sanitizer.ts +12 -5
- package/src/calls/voice-session-bridge.ts +86 -17
- package/src/calls/voice-triage-escalate.ts +54 -7
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +20 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +37 -1
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +17 -6
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +161 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +60 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +11 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +15 -15
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/call-site-defaults.ts +4 -5
- package/src/config/default-profile-catalog.ts +9 -5
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +75 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/live-voice.test.ts +142 -4
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/live-voice.ts +82 -23
- package/src/config/schemas/llm.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +59 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +18 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/config/schemas/tts.ts +63 -0
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +932 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +12 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +179 -146
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +324 -2
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +131 -2
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-tts.test.ts +148 -2
- package/src/live-voice/__tests__/live-voice-vad.test.ts +313 -32
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/__tests__/progress-phrases.test.ts +167 -0
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +168 -0
- package/src/live-voice/live-voice-session.ts +1137 -351
- package/src/live-voice/live-voice-tts.ts +18 -2
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/progress-phrases.ts +105 -2
- package/src/live-voice/protocol.ts +110 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +196 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/client.ts +14 -7
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +195 -240
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +495 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +463 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +13 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/index.ts +28 -5
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/mcp-servers.test.ts +370 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +64 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mcp-servers.ts +343 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +72 -2
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +620 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +472 -12
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +719 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +283 -1
- package/src/providers/speech-to-text/deepgram-realtime.ts +117 -3
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/provider-catalog.ts +137 -8
- package/src/providers/speech-to-text/resolve.ts +105 -12
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/__tests__/local-actor-identity-force-refresh.test.ts +104 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +110 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +29 -14
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +217 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/sse-actor-principal-heal.test.ts +165 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +2 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +20 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +29 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +141 -57
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +127 -4
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/sse-actor-principal-heal.ts +113 -0
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/oauth2.ts +6 -22
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/language-metadata.test.ts +85 -0
- package/src/stt/__tests__/speech-energy.test.ts +79 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +18 -6
- package/src/stt/language-metadata.ts +65 -0
- package/src/stt/speech-energy.ts +115 -12
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +119 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +303 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/tts/__tests__/provider-adapters.test.ts +147 -0
- package/src/tts/__tests__/speakable-segments.test.ts +470 -0
- package/src/tts/language-voices.ts +23 -0
- package/src/tts/providers/deepgram-provider.ts +3 -1
- package/src/tts/providers/elevenlabs-provider.ts +73 -1
- package/src/tts/providers/xai-provider.ts +28 -2
- package/src/tts/speakable-segments.ts +293 -23
- package/src/tts/synthesis-stream.ts +7 -0
- package/src/tts/types.ts +7 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/language-subtag.test.ts +54 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/language-subtag.ts +43 -0
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/unicode.ts +1 -1
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +116 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/live-voice/__tests__/front-decision.test.ts +0 -525
- package/src/live-voice/front-decision.ts +0 -429
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live-voice session telemetry: the started/ended funnel rows a session
|
|
3
|
+
* records, and the attribution carried on its turns.
|
|
4
|
+
*
|
|
5
|
+
* The recorder is mocked: the outbox and its consent gate are covered by
|
|
6
|
+
* `telemetry-events-outbox.test.ts`. What these tests pin is the wiring that
|
|
7
|
+
* decides whether a row exists at all and what it says, which is where the
|
|
8
|
+
* measurement can silently go wrong: a session that fails before `ready` still
|
|
9
|
+
* has to count, a failed session still has to record an end, and a session's
|
|
10
|
+
* turns have to carry its id or the turn count has nothing to group on.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
14
|
+
|
|
15
|
+
import type { VoiceTurnOptions } from "../../calls/voice-session-bridge.js";
|
|
16
|
+
import type {
|
|
17
|
+
StreamingTranscriber,
|
|
18
|
+
SttStreamServerEvent,
|
|
19
|
+
} from "../../stt/types.js";
|
|
20
|
+
|
|
21
|
+
const recordLiveVoiceSessionStarted = mock(() => null);
|
|
22
|
+
const recordLiveVoiceSessionEnded = mock(() => null);
|
|
23
|
+
|
|
24
|
+
// Spread the real module rather than replacing it: other importers pull
|
|
25
|
+
// `recordActivationEvent` and friends out of it, and a bare replacement makes
|
|
26
|
+
// those imports fail to resolve.
|
|
27
|
+
const onboardingEventsStore =
|
|
28
|
+
await import("../../onboarding/onboarding-events-store.js");
|
|
29
|
+
mock.module("../../onboarding/onboarding-events-store.js", () => ({
|
|
30
|
+
...onboardingEventsStore,
|
|
31
|
+
recordLiveVoiceSessionStarted,
|
|
32
|
+
recordLiveVoiceSessionEnded,
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
// Types are erased, so they import statically regardless of the mock ordering
|
|
36
|
+
// the values below depend on.
|
|
37
|
+
import type { LiveVoiceCredentialReadiness } from "../live-voice-credential-preflight.js";
|
|
38
|
+
import type { LiveVoiceSessionFactoryContext } from "../live-voice-session-manager.js";
|
|
39
|
+
import type {
|
|
40
|
+
LiveVoiceClientStartFrame,
|
|
41
|
+
LiveVoiceServerFrame,
|
|
42
|
+
} from "../protocol.js";
|
|
43
|
+
|
|
44
|
+
// Values load after `mock.module` so the session picks up the mocked recorder.
|
|
45
|
+
const { LiveVoiceSession } = await import("../live-voice-session.js");
|
|
46
|
+
const { LiveVoiceSessionStartupError } =
|
|
47
|
+
await import("../live-voice-session-manager.js");
|
|
48
|
+
const { createLiveVoiceServerFrameSequencer } = await import("../protocol.js");
|
|
49
|
+
|
|
50
|
+
const START_FRAME = {
|
|
51
|
+
type: "start",
|
|
52
|
+
conversationId: "conversation-123",
|
|
53
|
+
audio: { mimeType: "audio/pcm", sampleRate: 24_000, channels: 1 },
|
|
54
|
+
} as const satisfies LiveVoiceClientStartFrame;
|
|
55
|
+
|
|
56
|
+
const NOT_READY: LiveVoiceCredentialReadiness = {
|
|
57
|
+
status: "not-ready",
|
|
58
|
+
missing: [
|
|
59
|
+
{
|
|
60
|
+
kind: "tts",
|
|
61
|
+
providerId: "fish-audio",
|
|
62
|
+
reason: 'TTS provider "fish-audio" is missing credentials',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
userMessage: "Live voice is unavailable because it requires an API key.",
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
class MockStreamingTranscriber implements StreamingTranscriber {
|
|
69
|
+
readonly providerId = "deepgram" as const;
|
|
70
|
+
readonly boundaryId = "daemon-streaming" as const;
|
|
71
|
+
private onEvent: ((event: SttStreamServerEvent) => void) | null = null;
|
|
72
|
+
|
|
73
|
+
constructor(private readonly stopEvents: SttStreamServerEvent[] = []) {}
|
|
74
|
+
|
|
75
|
+
async start(onEvent: (event: SttStreamServerEvent) => void): Promise<void> {
|
|
76
|
+
this.onEvent = onEvent;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
sendAudio(): void {}
|
|
80
|
+
|
|
81
|
+
stop(): void {
|
|
82
|
+
for (const event of this.stopEvents) {
|
|
83
|
+
this.onEvent?.(event);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function createContext(
|
|
89
|
+
startFrame: LiveVoiceClientStartFrame = START_FRAME,
|
|
90
|
+
): LiveVoiceSessionFactoryContext {
|
|
91
|
+
const sequencer = createLiveVoiceServerFrameSequencer();
|
|
92
|
+
const frames: LiveVoiceServerFrame[] = [];
|
|
93
|
+
return {
|
|
94
|
+
sessionId: "session-123",
|
|
95
|
+
startFrame,
|
|
96
|
+
sendFrame: mock(async (payload) => {
|
|
97
|
+
const frame = sequencer.next(payload);
|
|
98
|
+
frames.push(frame);
|
|
99
|
+
return frame;
|
|
100
|
+
}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function readySession(startFrame?: LiveVoiceClientStartFrame) {
|
|
105
|
+
return new LiveVoiceSession(createContext(startFrame), {
|
|
106
|
+
resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
|
|
107
|
+
resolveCredentialReadiness: mock(
|
|
108
|
+
async (): Promise<LiveVoiceCredentialReadiness> => ({ status: "ready" }),
|
|
109
|
+
),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
describe("live-voice session telemetry", () => {
|
|
114
|
+
beforeEach(() => {
|
|
115
|
+
recordLiveVoiceSessionStarted.mockClear();
|
|
116
|
+
recordLiveVoiceSessionEnded.mockClear();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("records a started row for a session that reaches ready", async () => {
|
|
120
|
+
const session = readySession();
|
|
121
|
+
|
|
122
|
+
await session.start();
|
|
123
|
+
|
|
124
|
+
expect(recordLiveVoiceSessionStarted).toHaveBeenCalledWith("session-123");
|
|
125
|
+
|
|
126
|
+
await session.close("client_end");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("records a started row even when the preflight rejects the session", async () => {
|
|
130
|
+
const session = new LiveVoiceSession(createContext(), {
|
|
131
|
+
resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
|
|
132
|
+
resolveCredentialReadiness: mock(async () => NOT_READY),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
await expect(session.start()).rejects.toBeInstanceOf(
|
|
136
|
+
LiveVoiceSessionStartupError,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// The denominator of the failure rate: a session rejected before `ready`
|
|
140
|
+
// is exactly the one that must not go missing from the started count.
|
|
141
|
+
expect(recordLiveVoiceSessionStarted).toHaveBeenCalledWith("session-123");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("records the close reason and a completed outcome on a clean end", async () => {
|
|
145
|
+
const session = readySession();
|
|
146
|
+
await session.start();
|
|
147
|
+
|
|
148
|
+
await session.close("client_end");
|
|
149
|
+
|
|
150
|
+
expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
|
|
151
|
+
sessionId: "session-123",
|
|
152
|
+
screen: "ended_client_end",
|
|
153
|
+
outcome: "completed",
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("a dropped socket ends as completed, not failed", async () => {
|
|
158
|
+
const session = readySession();
|
|
159
|
+
await session.start();
|
|
160
|
+
|
|
161
|
+
await session.close("websocket_close");
|
|
162
|
+
|
|
163
|
+
// The session ran and then stopped; only an error makes it `failed`, and
|
|
164
|
+
// the reason on `screen` is what distinguishes a drop from a hangup.
|
|
165
|
+
expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
|
|
166
|
+
sessionId: "session-123",
|
|
167
|
+
screen: "ended_websocket_close",
|
|
168
|
+
outcome: "completed",
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("a failed session records an ended row carrying the failure code", async () => {
|
|
173
|
+
const session = new LiveVoiceSession(createContext(), {
|
|
174
|
+
resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
|
|
175
|
+
resolveCredentialReadiness: mock(async () => NOT_READY),
|
|
176
|
+
});
|
|
177
|
+
await expect(session.start()).rejects.toBeInstanceOf(
|
|
178
|
+
LiveVoiceSessionStartupError,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
// What the manager does after a startup failure.
|
|
182
|
+
await session.close("error");
|
|
183
|
+
|
|
184
|
+
// A failed session suppresses the client-facing `metrics` frame; the end
|
|
185
|
+
// row must not be suppressed with it.
|
|
186
|
+
expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
|
|
187
|
+
sessionId: "session-123",
|
|
188
|
+
screen: "ended_error:credentials_unavailable",
|
|
189
|
+
outcome: "failed",
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("records exactly one ended row when close is called twice", async () => {
|
|
194
|
+
const session = readySession();
|
|
195
|
+
await session.start();
|
|
196
|
+
|
|
197
|
+
await session.close("client_end");
|
|
198
|
+
await session.close("websocket_close");
|
|
199
|
+
|
|
200
|
+
expect(recordLiveVoiceSessionEnded).toHaveBeenCalledTimes(1);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe("live-voice turn attribution", () => {
|
|
205
|
+
/** Drives one push-to-talk turn and returns the options the bridge saw. */
|
|
206
|
+
async function captureTurnOptions(
|
|
207
|
+
startFrame: LiveVoiceClientStartFrame,
|
|
208
|
+
): Promise<VoiceTurnOptions> {
|
|
209
|
+
let captured: VoiceTurnOptions | undefined;
|
|
210
|
+
const startVoiceTurn = mock(async (options: VoiceTurnOptions) => {
|
|
211
|
+
captured = options;
|
|
212
|
+
return { turnId: "bridge-turn-1", abort: mock() };
|
|
213
|
+
});
|
|
214
|
+
const session = new LiveVoiceSession(createContext(startFrame), {
|
|
215
|
+
resolveTranscriber: mock(
|
|
216
|
+
async () =>
|
|
217
|
+
new MockStreamingTranscriber([
|
|
218
|
+
{ type: "final", text: "hello" },
|
|
219
|
+
{ type: "closed" },
|
|
220
|
+
]),
|
|
221
|
+
),
|
|
222
|
+
startVoiceTurn,
|
|
223
|
+
emitMetrics: false,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
await session.start();
|
|
227
|
+
await session.handleClientFrame({ type: "ptt_release" });
|
|
228
|
+
for (let attempt = 0; attempt < 40 && !captured; attempt += 1) {
|
|
229
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
230
|
+
}
|
|
231
|
+
await session.close("client_end");
|
|
232
|
+
|
|
233
|
+
if (!captured) {
|
|
234
|
+
throw new Error("the live-voice session never started a bridge turn");
|
|
235
|
+
}
|
|
236
|
+
return captured;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
test("carries the session id and originating client onto the turn", async () => {
|
|
240
|
+
const options = await captureTurnOptions({ ...START_FRAME, client: "ios" });
|
|
241
|
+
|
|
242
|
+
expect(options.voiceTelemetry).toEqual({
|
|
243
|
+
sessionId: "session-123",
|
|
244
|
+
client: "ios",
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("leaves the turn's interface id alone while attributing the client", async () => {
|
|
249
|
+
const options = await captureTurnOptions({ ...START_FRAME, client: "ios" });
|
|
250
|
+
|
|
251
|
+
// The load-bearing half of the attribution fix. `userMessageInterface`
|
|
252
|
+
// resolves the turn's channel capabilities, where `macos` is what grants a
|
|
253
|
+
// live-voice turn its dynamic surfaces; reporting the true client here
|
|
254
|
+
// would strip them from every iOS session. Attribution rides
|
|
255
|
+
// `voiceTelemetry` precisely so this can stay put.
|
|
256
|
+
expect(options.userMessageInterface).toBe("macos");
|
|
257
|
+
expect(options.assistantMessageInterface).toBe("macos");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("omits the client when the start frame declares none", async () => {
|
|
261
|
+
const options = await captureTurnOptions(START_FRAME);
|
|
262
|
+
|
|
263
|
+
expect(options.voiceTelemetry).toEqual({ sessionId: "session-123" });
|
|
264
|
+
});
|
|
265
|
+
});
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type LiveVoiceStreamingTranscriberResolver,
|
|
13
13
|
} from "../live-voice-session.js";
|
|
14
14
|
import type { LiveVoiceSessionFactoryContext } from "../live-voice-session-manager.js";
|
|
15
|
+
import type { LiveVoiceTtsOptions } from "../live-voice-tts.js";
|
|
15
16
|
import {
|
|
16
17
|
createLiveVoiceServerFrameSequencer,
|
|
17
18
|
type LiveVoiceClientStartFrame,
|
|
@@ -29,7 +30,9 @@ const START_FRAME = {
|
|
|
29
30
|
} as const satisfies LiveVoiceClientStartFrame;
|
|
30
31
|
|
|
31
32
|
class MockStreamingTranscriber implements StreamingTranscriber {
|
|
32
|
-
|
|
33
|
+
// Configurable: the session gates the language-pin fallback on the DIALED
|
|
34
|
+
// transcriber's providerId (see turnLanguageFor).
|
|
35
|
+
readonly providerId: StreamingTranscriber["providerId"];
|
|
33
36
|
readonly boundaryId = "daemon-streaming" as const;
|
|
34
37
|
readonly audioChunks: Buffer[] = [];
|
|
35
38
|
readonly mimeTypes: string[] = [];
|
|
@@ -37,6 +40,10 @@ class MockStreamingTranscriber implements StreamingTranscriber {
|
|
|
37
40
|
stopped = false;
|
|
38
41
|
private onEvent: ((event: SttStreamServerEvent) => void) | null = null;
|
|
39
42
|
|
|
43
|
+
constructor(providerId: StreamingTranscriber["providerId"] = "deepgram") {
|
|
44
|
+
this.providerId = providerId;
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
async start(onEvent: (event: SttStreamServerEvent) => void): Promise<void> {
|
|
41
48
|
this.started = true;
|
|
42
49
|
this.onEvent = onEvent;
|
|
@@ -135,6 +142,39 @@ function completingVoiceTurnStarter() {
|
|
|
135
142
|
});
|
|
136
143
|
}
|
|
137
144
|
|
|
145
|
+
// A starter whose leg streams one spoken sentence so the turn reaches TTS.
|
|
146
|
+
function speakingVoiceTurnStarter(reply = "Okay.") {
|
|
147
|
+
return mock(async (options: VoiceTurnOptions) => {
|
|
148
|
+
options.callbacks?.assistant_text_delta?.({
|
|
149
|
+
type: "assistant_text_delta",
|
|
150
|
+
text: reply,
|
|
151
|
+
conversationId: options.conversationId,
|
|
152
|
+
});
|
|
153
|
+
options.callbacks?.message_complete?.({
|
|
154
|
+
type: "message_complete",
|
|
155
|
+
conversationId: options.conversationId,
|
|
156
|
+
messageId: "assistant-message-123",
|
|
157
|
+
});
|
|
158
|
+
return { turnId: "bridge-turn-1", abort: mock() };
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Records the exact options object of every TTS synthesis request.
|
|
163
|
+
function recordingTtsStreamer() {
|
|
164
|
+
const ttsCalls: LiveVoiceTtsOptions[] = [];
|
|
165
|
+
const streamTtsAudio = mock(async (options: LiveVoiceTtsOptions) => {
|
|
166
|
+
ttsCalls.push(options);
|
|
167
|
+
return {
|
|
168
|
+
provider: "fish-audio" as const,
|
|
169
|
+
contentType: "audio/pcm",
|
|
170
|
+
sampleRate: 24_000,
|
|
171
|
+
chunks: 0,
|
|
172
|
+
bytes: 0,
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
return { streamTtsAudio, ttsCalls };
|
|
176
|
+
}
|
|
177
|
+
|
|
138
178
|
function createContext(overrides: Partial<LiveVoiceClientStartFrame> = {}): {
|
|
139
179
|
context: LiveVoiceSessionFactoryContext;
|
|
140
180
|
frames: LiveVoiceServerFrame[];
|
|
@@ -203,7 +243,12 @@ describe("LiveVoiceSession STT", () => {
|
|
|
203
243
|
|
|
204
244
|
await session.start();
|
|
205
245
|
|
|
206
|
-
|
|
246
|
+
// The config default is "multi", so the session carries a language into
|
|
247
|
+
// the resolver rather than leaving it to be inferred downstream.
|
|
248
|
+
expect(resolver).toHaveBeenCalledWith({
|
|
249
|
+
sampleRate: 24_000,
|
|
250
|
+
language: "multi",
|
|
251
|
+
});
|
|
207
252
|
expect(transcriber.started).toBe(true);
|
|
208
253
|
expect(frames).toEqual([
|
|
209
254
|
{
|
|
@@ -216,6 +261,20 @@ describe("LiveVoiceSession STT", () => {
|
|
|
216
261
|
]);
|
|
217
262
|
});
|
|
218
263
|
|
|
264
|
+
test("ignores model-integrated turn-detection events", async () => {
|
|
265
|
+
const { frames, session, transcriber } = createSessionWithTranscriber();
|
|
266
|
+
|
|
267
|
+
await session.start();
|
|
268
|
+
transcriber.emit({ type: "turn-start" });
|
|
269
|
+
transcriber.emit({ type: "eager-turn-end", text: "eager" });
|
|
270
|
+
transcriber.emit({ type: "turn-resumed" });
|
|
271
|
+
transcriber.emit({ type: "turn-end", text: "committed", confidence: 0.9 });
|
|
272
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
273
|
+
|
|
274
|
+
expect(frames.map((frame) => frame.type)).toEqual(["ready"]);
|
|
275
|
+
expect(session.finalTranscriptText).toBe("");
|
|
276
|
+
});
|
|
277
|
+
|
|
219
278
|
test("marks transient transcriber errors recoverable while the session continues", async () => {
|
|
220
279
|
const { frames, session, transcriber } = createSessionWithTranscriber();
|
|
221
280
|
|
|
@@ -1332,6 +1391,269 @@ describe("LiveVoiceSession STT", () => {
|
|
|
1332
1391
|
]);
|
|
1333
1392
|
});
|
|
1334
1393
|
|
|
1394
|
+
test("finals tagged with a detected language carry it to TTS and the control prompt", async () => {
|
|
1395
|
+
const transcriber = new MockStreamingTranscriber();
|
|
1396
|
+
const startVoiceTurn = speakingVoiceTurnStarter();
|
|
1397
|
+
const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
|
|
1398
|
+
const { context } = createContext();
|
|
1399
|
+
const session = new LiveVoiceSession(context, {
|
|
1400
|
+
resolveTranscriber: mock(async () => transcriber),
|
|
1401
|
+
startVoiceTurn,
|
|
1402
|
+
streamTtsAudio,
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1405
|
+
await session.start();
|
|
1406
|
+
await session.handleBinaryAudio(new Uint8Array([1]));
|
|
1407
|
+
transcriber.emit({ type: "final", text: "नमस्ते", languages: ["hi"] });
|
|
1408
|
+
await session.handleClientFrame({ type: "ptt_release" });
|
|
1409
|
+
await waitFor(() => ttsCalls.length >= 1);
|
|
1410
|
+
|
|
1411
|
+
expect(ttsCalls[0]?.language).toBe("hi");
|
|
1412
|
+
expect(startVoiceTurn.mock.calls[0]?.[0]?.voiceControlPrompt).toContain(
|
|
1413
|
+
'speaking the language with code "hi"',
|
|
1414
|
+
);
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1417
|
+
test("a tagged partial supplies the turn language when finals carry no tags", async () => {
|
|
1418
|
+
// Speculative turns dispatch from partials before the first tagged
|
|
1419
|
+
// final, so the latest tagged partial must resolve the language.
|
|
1420
|
+
const transcriber = new MockStreamingTranscriber();
|
|
1421
|
+
const startVoiceTurn = speakingVoiceTurnStarter();
|
|
1422
|
+
const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
|
|
1423
|
+
const { context } = createContext();
|
|
1424
|
+
const session = new LiveVoiceSession(context, {
|
|
1425
|
+
resolveTranscriber: mock(async () => transcriber),
|
|
1426
|
+
startVoiceTurn,
|
|
1427
|
+
streamTtsAudio,
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
await session.start();
|
|
1431
|
+
await session.handleBinaryAudio(new Uint8Array([1]));
|
|
1432
|
+
transcriber.emit({ type: "partial", text: "hola", languages: ["es"] });
|
|
1433
|
+
transcriber.emit({ type: "final", text: "hola amigo" });
|
|
1434
|
+
await session.handleClientFrame({ type: "ptt_release" });
|
|
1435
|
+
await waitFor(() => ttsCalls.length >= 1);
|
|
1436
|
+
|
|
1437
|
+
expect(ttsCalls[0]?.language).toBe("es");
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
test("empty finals carrying language tags do not vote", async () => {
|
|
1441
|
+
// Silence frames can carry container-level tags describing no emitted
|
|
1442
|
+
// words; they must not outvote the language of real speech.
|
|
1443
|
+
const transcriber = new MockStreamingTranscriber();
|
|
1444
|
+
const startVoiceTurn = speakingVoiceTurnStarter();
|
|
1445
|
+
const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
|
|
1446
|
+
const { context } = createContext();
|
|
1447
|
+
const session = new LiveVoiceSession(context, {
|
|
1448
|
+
resolveTranscriber: mock(async () => transcriber),
|
|
1449
|
+
startVoiceTurn,
|
|
1450
|
+
streamTtsAudio,
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
await session.start();
|
|
1454
|
+
await session.handleBinaryAudio(new Uint8Array([1]));
|
|
1455
|
+
transcriber.emit({ type: "final", text: "", languages: ["es"] });
|
|
1456
|
+
transcriber.emit({ type: "final", text: "", languages: ["es"] });
|
|
1457
|
+
transcriber.emit({ type: "final", text: "hello there", languages: ["en"] });
|
|
1458
|
+
await session.handleClientFrame({ type: "ptt_release" });
|
|
1459
|
+
await waitFor(() => ttsCalls.length >= 1);
|
|
1460
|
+
|
|
1461
|
+
expect(ttsCalls[0]?.language).toBe("en");
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
test("only each final's dominant language votes", async () => {
|
|
1465
|
+
// `languages` is dominance-ranked per event: a Spanish-dominant segment
|
|
1466
|
+
// tagged ["es", "en"] plus a short English segment tagged ["en"] must
|
|
1467
|
+
// resolve to Spanish (one vote each, tie broken by first appearance),
|
|
1468
|
+
// not English by counting the secondary tag as a full vote.
|
|
1469
|
+
const transcriber = new MockStreamingTranscriber();
|
|
1470
|
+
const startVoiceTurn = speakingVoiceTurnStarter();
|
|
1471
|
+
const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
|
|
1472
|
+
const { context } = createContext();
|
|
1473
|
+
const session = new LiveVoiceSession(context, {
|
|
1474
|
+
resolveTranscriber: mock(async () => transcriber),
|
|
1475
|
+
startVoiceTurn,
|
|
1476
|
+
streamTtsAudio,
|
|
1477
|
+
});
|
|
1478
|
+
|
|
1479
|
+
await session.start();
|
|
1480
|
+
await session.handleBinaryAudio(new Uint8Array([1]));
|
|
1481
|
+
transcriber.emit({
|
|
1482
|
+
type: "final",
|
|
1483
|
+
text: "hola amigo como estas hoy",
|
|
1484
|
+
languages: ["es", "en"],
|
|
1485
|
+
});
|
|
1486
|
+
transcriber.emit({ type: "final", text: "ok", languages: ["en"] });
|
|
1487
|
+
await session.handleClientFrame({ type: "ptt_release" });
|
|
1488
|
+
await waitFor(() => ttsCalls.length >= 1);
|
|
1489
|
+
|
|
1490
|
+
expect(ttsCalls[0]?.language).toBe("es");
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1493
|
+
test("a tagged final outranks an earlier tagged partial", async () => {
|
|
1494
|
+
const transcriber = new MockStreamingTranscriber();
|
|
1495
|
+
const startVoiceTurn = speakingVoiceTurnStarter();
|
|
1496
|
+
const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
|
|
1497
|
+
const { context } = createContext();
|
|
1498
|
+
const session = new LiveVoiceSession(context, {
|
|
1499
|
+
resolveTranscriber: mock(async () => transcriber),
|
|
1500
|
+
startVoiceTurn,
|
|
1501
|
+
streamTtsAudio,
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
await session.start();
|
|
1505
|
+
await session.handleBinaryAudio(new Uint8Array([1]));
|
|
1506
|
+
transcriber.emit({ type: "partial", text: "hola", languages: ["es"] });
|
|
1507
|
+
transcriber.emit({ type: "final", text: "नमस्ते", languages: ["hi"] });
|
|
1508
|
+
await session.handleClientFrame({ type: "ptt_release" });
|
|
1509
|
+
await waitFor(() => ttsCalls.length >= 1);
|
|
1510
|
+
|
|
1511
|
+
expect(ttsCalls[0]?.language).toBe("hi");
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
test("a monolingual services.stt.language pin is the turn language when finals carry no tags", async () => {
|
|
1515
|
+
const originalRaw = loadRawConfig();
|
|
1516
|
+
const rawServices = (originalRaw.services ?? {}) as Record<string, unknown>;
|
|
1517
|
+
saveRawConfig({
|
|
1518
|
+
...originalRaw,
|
|
1519
|
+
services: {
|
|
1520
|
+
...rawServices,
|
|
1521
|
+
stt: {
|
|
1522
|
+
...((rawServices.stt ?? {}) as Record<string, unknown>),
|
|
1523
|
+
language: "ja",
|
|
1524
|
+
},
|
|
1525
|
+
},
|
|
1526
|
+
});
|
|
1527
|
+
const transcriber = new MockStreamingTranscriber();
|
|
1528
|
+
const startVoiceTurn = speakingVoiceTurnStarter();
|
|
1529
|
+
const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
|
|
1530
|
+
const { context } = createContext();
|
|
1531
|
+
const session = new LiveVoiceSession(context, {
|
|
1532
|
+
resolveTranscriber: mock(async () => transcriber),
|
|
1533
|
+
startVoiceTurn,
|
|
1534
|
+
streamTtsAudio,
|
|
1535
|
+
});
|
|
1536
|
+
|
|
1537
|
+
try {
|
|
1538
|
+
await session.start();
|
|
1539
|
+
await session.handleBinaryAudio(new Uint8Array([1]));
|
|
1540
|
+
await session.handleClientFrame({ type: "ptt_release" });
|
|
1541
|
+
await waitFor(() => ttsCalls.length >= 1);
|
|
1542
|
+
|
|
1543
|
+
expect(ttsCalls[0]?.language).toBe("ja");
|
|
1544
|
+
expect(startVoiceTurn.mock.calls[0]?.[0]?.voiceControlPrompt).toContain(
|
|
1545
|
+
'speaking the language with code "ja"',
|
|
1546
|
+
);
|
|
1547
|
+
} finally {
|
|
1548
|
+
await session.close("websocket_close");
|
|
1549
|
+
saveRawConfig(originalRaw);
|
|
1550
|
+
}
|
|
1551
|
+
});
|
|
1552
|
+
|
|
1553
|
+
test("a pin on an auto-detecting provider does not become the turn language", async () => {
|
|
1554
|
+
// google-gemini and openai-whisper ignore services.stt.language, so a
|
|
1555
|
+
// stale persisted pin must not force every turn into that language.
|
|
1556
|
+
const originalRaw = loadRawConfig();
|
|
1557
|
+
const rawServices = (originalRaw.services ?? {}) as Record<string, unknown>;
|
|
1558
|
+
saveRawConfig({
|
|
1559
|
+
...originalRaw,
|
|
1560
|
+
services: {
|
|
1561
|
+
...rawServices,
|
|
1562
|
+
stt: {
|
|
1563
|
+
...((rawServices.stt ?? {}) as Record<string, unknown>),
|
|
1564
|
+
provider: "google-gemini",
|
|
1565
|
+
language: "es",
|
|
1566
|
+
},
|
|
1567
|
+
},
|
|
1568
|
+
});
|
|
1569
|
+
const transcriber = new MockStreamingTranscriber("google-gemini");
|
|
1570
|
+
const startVoiceTurn = speakingVoiceTurnStarter();
|
|
1571
|
+
const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
|
|
1572
|
+
const { context } = createContext();
|
|
1573
|
+
const session = new LiveVoiceSession(context, {
|
|
1574
|
+
resolveTranscriber: mock(async () => transcriber),
|
|
1575
|
+
startVoiceTurn,
|
|
1576
|
+
streamTtsAudio,
|
|
1577
|
+
});
|
|
1578
|
+
|
|
1579
|
+
try {
|
|
1580
|
+
await session.start();
|
|
1581
|
+
await session.handleBinaryAudio(new Uint8Array([1]));
|
|
1582
|
+
await session.handleClientFrame({ type: "ptt_release" });
|
|
1583
|
+
await waitFor(() => ttsCalls.length >= 1);
|
|
1584
|
+
|
|
1585
|
+
expect(ttsCalls[0] && "language" in ttsCalls[0]).toBe(false);
|
|
1586
|
+
} finally {
|
|
1587
|
+
await session.close("websocket_close");
|
|
1588
|
+
saveRawConfig(originalRaw);
|
|
1589
|
+
}
|
|
1590
|
+
});
|
|
1591
|
+
|
|
1592
|
+
test("the pin gate follows the dialed provider, not the configured one", async () => {
|
|
1593
|
+
// A BYOK gemini config without credentials silently dials the managed
|
|
1594
|
+
// vellum transcriber, which DOES honor the pin; the gate must follow
|
|
1595
|
+
// what was dialed, not what was configured.
|
|
1596
|
+
const originalRaw = loadRawConfig();
|
|
1597
|
+
const rawServices = (originalRaw.services ?? {}) as Record<string, unknown>;
|
|
1598
|
+
saveRawConfig({
|
|
1599
|
+
...originalRaw,
|
|
1600
|
+
services: {
|
|
1601
|
+
...rawServices,
|
|
1602
|
+
stt: {
|
|
1603
|
+
...((rawServices.stt ?? {}) as Record<string, unknown>),
|
|
1604
|
+
provider: "google-gemini",
|
|
1605
|
+
language: "es",
|
|
1606
|
+
},
|
|
1607
|
+
},
|
|
1608
|
+
});
|
|
1609
|
+
const transcriber = new MockStreamingTranscriber("vellum");
|
|
1610
|
+
const startVoiceTurn = speakingVoiceTurnStarter();
|
|
1611
|
+
const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
|
|
1612
|
+
const { context } = createContext();
|
|
1613
|
+
const session = new LiveVoiceSession(context, {
|
|
1614
|
+
resolveTranscriber: mock(async () => transcriber),
|
|
1615
|
+
startVoiceTurn,
|
|
1616
|
+
streamTtsAudio,
|
|
1617
|
+
});
|
|
1618
|
+
|
|
1619
|
+
try {
|
|
1620
|
+
await session.start();
|
|
1621
|
+
await session.handleBinaryAudio(new Uint8Array([1]));
|
|
1622
|
+
await session.handleClientFrame({ type: "ptt_release" });
|
|
1623
|
+
await waitFor(() => ttsCalls.length >= 1);
|
|
1624
|
+
|
|
1625
|
+
expect(ttsCalls[0]?.language).toBe("es");
|
|
1626
|
+
} finally {
|
|
1627
|
+
await session.close("websocket_close");
|
|
1628
|
+
saveRawConfig(originalRaw);
|
|
1629
|
+
}
|
|
1630
|
+
});
|
|
1631
|
+
|
|
1632
|
+
test('the default "multi" with tag-less finals leaves every language-aware path untouched', async () => {
|
|
1633
|
+
const transcriber = new MockStreamingTranscriber();
|
|
1634
|
+
const startVoiceTurn = speakingVoiceTurnStarter();
|
|
1635
|
+
const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
|
|
1636
|
+
const { context } = createContext();
|
|
1637
|
+
const session = new LiveVoiceSession(context, {
|
|
1638
|
+
resolveTranscriber: mock(async () => transcriber),
|
|
1639
|
+
startVoiceTurn,
|
|
1640
|
+
streamTtsAudio,
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1643
|
+
await session.start();
|
|
1644
|
+
await session.handleBinaryAudio(new Uint8Array([1]));
|
|
1645
|
+
await session.handleClientFrame({ type: "ptt_release" });
|
|
1646
|
+
await waitFor(() => ttsCalls.length >= 1);
|
|
1647
|
+
|
|
1648
|
+
// With the language unknown the TTS request carries no language key at
|
|
1649
|
+
// all and the control prompt has no per-turn language note: byte-for-byte
|
|
1650
|
+
// the language-blind behavior.
|
|
1651
|
+
expect("language" in ttsCalls[0]!).toBe(false);
|
|
1652
|
+
expect(startVoiceTurn.mock.calls[0]?.[0]?.voiceControlPrompt).not.toContain(
|
|
1653
|
+
"language with code",
|
|
1654
|
+
);
|
|
1655
|
+
});
|
|
1656
|
+
|
|
1335
1657
|
test("uses the production streaming transcriber resolver by default", () => {
|
|
1336
1658
|
const source = readFileSync(
|
|
1337
1659
|
new URL("../live-voice-session.ts", import.meta.url),
|