@vellumai/assistant 0.11.2 → 0.11.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 +58 -3
- 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 +39 -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 +39 -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 +39 -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 +589 -30
- package/package.json +1 -1
- 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-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- 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__/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 +57 -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 +183 -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-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -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__/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/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__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- 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 +4 -2
- 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/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 +122 -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 +18 -0
- package/src/cli.ts +1 -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/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- 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 +2 -2
- 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/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -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__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- 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/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-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-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- 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-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- 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 +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -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 +12 -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/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -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/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 +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -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 +67 -0
- 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__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- 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/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -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 +11 -9
- 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 +173 -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__/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-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 +23 -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 +28 -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/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- 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/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/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/daemon-batch-transcriber.ts +8 -6
- 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 +302 -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/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -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/schedule-source-key.ts +23 -0
- 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/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -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/notifications/deferred-emit.ts +0 -147
|
@@ -163,6 +163,26 @@ describe("createLiveVoiceConnection", () => {
|
|
|
163
163
|
});
|
|
164
164
|
});
|
|
165
165
|
|
|
166
|
+
test("names the rejected frame on an unknown_type error", async () => {
|
|
167
|
+
// A client that sends more than one optional frame gets the same
|
|
168
|
+
// `unknown_type` for any of them. Without the name it has to guess which,
|
|
169
|
+
// and guessing wrong is silent: a photo is dropped with nothing said, or
|
|
170
|
+
// the voice-room settings quietly stop applying for the session.
|
|
171
|
+
installFakeManager();
|
|
172
|
+
const { connection, frames } = createConnection();
|
|
173
|
+
|
|
174
|
+
await connection.handleMessage(START_MESSAGE);
|
|
175
|
+
await connection.handleMessage(
|
|
176
|
+
JSON.stringify({ type: "attach_image_from_the_future" }),
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
expect(frames.at(-1)).toMatchObject({
|
|
180
|
+
type: "error",
|
|
181
|
+
code: "unknown_type",
|
|
182
|
+
frameType: "attach_image_from_the_future",
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
166
186
|
test("errors on a non-start frame before start", async () => {
|
|
167
187
|
installFakeManager();
|
|
168
188
|
const { connection, frames } = createConnection();
|
|
@@ -736,9 +736,9 @@ describe("live-voice audio archiving default (JARVIS-1283)", () => {
|
|
|
736
736
|
|
|
737
737
|
test("liveVoice.archiveAudio=true wires the default archiver through the factory", async () => {
|
|
738
738
|
// The real linkers write to the DB; these are role-less inputs, so stub
|
|
739
|
-
// them
|
|
740
|
-
//
|
|
741
|
-
//
|
|
739
|
+
// them with a valid archived result per role. The point under test is the
|
|
740
|
+
// config→default-archiver wiring, not the DB write, which the
|
|
741
|
+
// injected-archiver tests above already cover.
|
|
742
742
|
const audioInput = {
|
|
743
743
|
sessionId: "session-123",
|
|
744
744
|
turnId: "live-turn-1",
|
|
@@ -751,11 +751,13 @@ describe("live-voice audio archiving default (JARVIS-1283)", () => {
|
|
|
751
751
|
const userSpy = spyOn(
|
|
752
752
|
liveVoiceArchive,
|
|
753
753
|
"linkLiveVoiceUserUtteranceAudioToMessage",
|
|
754
|
-
).
|
|
754
|
+
).mockResolvedValue(makeArchiveResult({ ...audioInput, role: "user" }));
|
|
755
755
|
const assistantSpy = spyOn(
|
|
756
756
|
liveVoiceArchive,
|
|
757
757
|
"linkLiveVoiceAssistantResponseAudioToMessage",
|
|
758
|
-
).
|
|
758
|
+
).mockResolvedValue(
|
|
759
|
+
makeArchiveResult({ ...audioInput, role: "assistant" }),
|
|
760
|
+
);
|
|
759
761
|
const originalRaw = loadRawConfig();
|
|
760
762
|
saveRawConfig({ ...originalRaw, liveVoice: { archiveAudio: true } });
|
|
761
763
|
|
|
@@ -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
|
+
});
|
|
@@ -203,7 +203,12 @@ describe("LiveVoiceSession STT", () => {
|
|
|
203
203
|
|
|
204
204
|
await session.start();
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
// The config default is "multi", so the session carries a language into
|
|
207
|
+
// the resolver rather than leaving it to be inferred downstream.
|
|
208
|
+
expect(resolver).toHaveBeenCalledWith({
|
|
209
|
+
sampleRate: 24_000,
|
|
210
|
+
language: "multi",
|
|
211
|
+
});
|
|
207
212
|
expect(transcriber.started).toBe(true);
|
|
208
213
|
expect(frames).toEqual([
|
|
209
214
|
{
|
|
@@ -103,9 +103,13 @@ class MockStreamingTranscriber implements StreamingTranscriber {
|
|
|
103
103
|
readonly received: Buffer[] = [];
|
|
104
104
|
stopped = false;
|
|
105
105
|
private onEvent: ((event: SttStreamServerEvent) => void) | null = null;
|
|
106
|
+
private flushed = false;
|
|
107
|
+
// Claimed on the first audio chunk, so the scripted transcript is spent in
|
|
108
|
+
// the order cycles hear speech (see takeScriptedFinal).
|
|
109
|
+
private scriptedFinal: string | null = null;
|
|
106
110
|
|
|
107
111
|
constructor(
|
|
108
|
-
private readonly
|
|
112
|
+
private readonly takeScriptedFinal: () => string,
|
|
109
113
|
private readonly holdStopEvents = false,
|
|
110
114
|
) {}
|
|
111
115
|
|
|
@@ -114,6 +118,7 @@ class MockStreamingTranscriber implements StreamingTranscriber {
|
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
sendAudio(chunk: Buffer): void {
|
|
121
|
+
this.scriptedFinal ??= this.takeScriptedFinal();
|
|
117
122
|
this.received.push(Buffer.from(chunk));
|
|
118
123
|
}
|
|
119
124
|
|
|
@@ -128,19 +133,20 @@ class MockStreamingTranscriber implements StreamingTranscriber {
|
|
|
128
133
|
}
|
|
129
134
|
|
|
130
135
|
flushStopEvents(): void {
|
|
136
|
+
if (this.flushed) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
this.flushed = true;
|
|
131
140
|
// A stream that was never sent audio has nothing to transcribe, so a real
|
|
132
|
-
// provider closes without a final. Scripting one anyway
|
|
133
|
-
//
|
|
134
|
-
// post-cancel re-arm
|
|
135
|
-
//
|
|
141
|
+
// provider closes without a final. Scripting one anyway lets a cycle that
|
|
142
|
+
// arms and tears down in the same breath (a client interrupt racing the
|
|
143
|
+
// post-cancel re-arm) hand the session a transcript it never heard, which
|
|
144
|
+
// launches a turn nothing will ever finalize and wedges the
|
|
136
145
|
// one-turn-at-a-time gate for every later utterance.
|
|
137
|
-
|
|
138
|
-
this.
|
|
139
|
-
? this.stopEvents
|
|
140
|
-
: this.stopEvents.filter((event) => event.type !== "final");
|
|
141
|
-
for (const event of events) {
|
|
142
|
-
this.onEvent?.(event);
|
|
146
|
+
if (this.scriptedFinal !== null) {
|
|
147
|
+
this.onEvent?.({ type: "final", text: this.scriptedFinal });
|
|
143
148
|
}
|
|
149
|
+
this.onEvent?.({ type: "closed" });
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
// Provider-initiated event (e.g. an idle-timeout close), no stop() needed.
|
|
@@ -192,12 +198,23 @@ function createHarness(options: {
|
|
|
192
198
|
};
|
|
193
199
|
|
|
194
200
|
const finals = options.finals ?? ["hello world"];
|
|
201
|
+
// Scripted finals are handed out in the order cycles first receive audio,
|
|
202
|
+
// not in the order they arm. Server VAD arms and discards audio-free cycles
|
|
203
|
+
// on wall-clock timers (the trailing-silence timer, the post-cancel re-arm),
|
|
204
|
+
// so how many cycles have armed by a given point depends on how fast the
|
|
205
|
+
// event loop is, while the order the test feeds audio in does not. Keying
|
|
206
|
+
// off audio keeps `finals` reading as "what the user says, in order".
|
|
207
|
+
let scriptedFinalIndex = 0;
|
|
208
|
+
const takeScriptedFinal = (): string => {
|
|
209
|
+
const text =
|
|
210
|
+
finals[scriptedFinalIndex] ?? `utterance ${scriptedFinalIndex + 1}`;
|
|
211
|
+
scriptedFinalIndex += 1;
|
|
212
|
+
return text;
|
|
213
|
+
};
|
|
195
214
|
const transcribers: MockStreamingTranscriber[] = [];
|
|
196
215
|
const resolveTranscriber = mock(async () => {
|
|
197
|
-
const text =
|
|
198
|
-
finals[transcribers.length] ?? `utterance ${transcribers.length + 1}`;
|
|
199
216
|
const transcriber = new MockStreamingTranscriber(
|
|
200
|
-
|
|
217
|
+
takeScriptedFinal,
|
|
201
218
|
options.holdStopEventsFor?.includes(transcribers.length) ?? false,
|
|
202
219
|
);
|
|
203
220
|
transcribers.push(transcriber);
|
|
@@ -2929,8 +2946,11 @@ describe("LiveVoiceSession server VAD", () => {
|
|
|
2929
2946
|
|
|
2930
2947
|
test("an idle transcriber close before speech re-arms capture for the next utterance", async () => {
|
|
2931
2948
|
const { startVoiceTurn, calls } = makeAutoCompletingTurnStarter(["Hi."]);
|
|
2949
|
+
// The first transcriber closes before it is sent any audio, so it never
|
|
2950
|
+
// claims a scripted final: the one entry belongs to the cycle that speech
|
|
2951
|
+
// actually reaches.
|
|
2932
2952
|
const { frames, session, transcribers } = createHarness({
|
|
2933
|
-
finals: ["
|
|
2953
|
+
finals: ["hello after close"],
|
|
2934
2954
|
startVoiceTurn,
|
|
2935
2955
|
});
|
|
2936
2956
|
|
|
@@ -305,6 +305,58 @@ describe("parseLiveVoiceClientTextFrame", () => {
|
|
|
305
305
|
expect("turnDetection" in result.frame).toBe(false);
|
|
306
306
|
});
|
|
307
307
|
|
|
308
|
+
test("parses the originating client from the start frame", () => {
|
|
309
|
+
const result = parseLiveVoiceClientTextFrame(
|
|
310
|
+
JSON.stringify({
|
|
311
|
+
type: "start",
|
|
312
|
+
client: "ios",
|
|
313
|
+
audio: { mimeType: "audio/pcm", sampleRate: 24000, channels: 1 },
|
|
314
|
+
}),
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
expect(result.ok).toBe(true);
|
|
318
|
+
if (!result.ok) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
expect(result.frame).toMatchObject({ type: "start", client: "ios" });
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("omits client when absent from the start frame", () => {
|
|
326
|
+
const result = parseLiveVoiceClientTextFrame(
|
|
327
|
+
JSON.stringify({
|
|
328
|
+
type: "start",
|
|
329
|
+
audio: { mimeType: "audio/pcm", sampleRate: 24000, channels: 1 },
|
|
330
|
+
}),
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
expect(result.ok).toBe(true);
|
|
334
|
+
if (!result.ok) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
expect("client" in result.frame).toBe(false);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("drops an unrecognized client rather than rejecting the session", () => {
|
|
342
|
+
const result = parseLiveVoiceClientTextFrame(
|
|
343
|
+
JSON.stringify({
|
|
344
|
+
type: "start",
|
|
345
|
+
client: "commodore-64",
|
|
346
|
+
audio: { mimeType: "audio/pcm", sampleRate: 24000, channels: 1 },
|
|
347
|
+
}),
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// The field is an analytics dimension. Failing startup over it would
|
|
351
|
+
// trade a gap in a chart for a user who cannot talk to their assistant.
|
|
352
|
+
expect(result.ok).toBe(true);
|
|
353
|
+
if (!result.ok) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
expect("client" in result.frame).toBe(false);
|
|
358
|
+
});
|
|
359
|
+
|
|
308
360
|
test("returns typed protocol errors for invalid turnDetection values", () => {
|
|
309
361
|
const result = validateLiveVoiceClientFrame({
|
|
310
362
|
type: "start",
|
|
@@ -327,9 +327,12 @@ describe("RuntimeHttpServer live voice WebSocket shell", () => {
|
|
|
327
327
|
conversationId: "conversation-ready",
|
|
328
328
|
});
|
|
329
329
|
expect(typeof ready.sessionId).toBe("string");
|
|
330
|
+
// The config default is "multi", so the session carries a language into
|
|
331
|
+
// the resolver rather than leaving it to be inferred downstream.
|
|
330
332
|
expect(resolveStreamingTranscriberMock).toHaveBeenCalledWith({
|
|
331
333
|
sampleRate: 24_000,
|
|
332
334
|
providerId: "deepgram",
|
|
335
|
+
language: "multi",
|
|
333
336
|
});
|
|
334
337
|
expect(resolvedTranscribers).toHaveLength(1);
|
|
335
338
|
expect(resolvedTranscribers[0]?.started).toBe(true);
|
|
@@ -468,9 +468,9 @@ function artifactFromAttachment(input: {
|
|
|
468
468
|
};
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
-
export function archiveLiveVoiceAudioArtifact(
|
|
471
|
+
export async function archiveLiveVoiceAudioArtifact(
|
|
472
472
|
input: ArchiveLiveVoiceAudioInput,
|
|
473
|
-
): LiveVoiceAudioArchiveResult {
|
|
473
|
+
): Promise<LiveVoiceAudioArchiveResult> {
|
|
474
474
|
const validated = validateInput(input);
|
|
475
475
|
if ("type" in validated) {
|
|
476
476
|
return validated;
|
|
@@ -529,7 +529,7 @@ export function archiveLiveVoiceAudioArtifact(
|
|
|
529
529
|
const position = input.position ?? nextAttachmentPosition(input.messageId);
|
|
530
530
|
const stored =
|
|
531
531
|
input.audio.type === "base64"
|
|
532
|
-
? attachInlineAttachmentToMessage(
|
|
532
|
+
? await attachInlineAttachmentToMessage(
|
|
533
533
|
input.messageId,
|
|
534
534
|
position,
|
|
535
535
|
validated.filename,
|
|
@@ -614,15 +614,15 @@ interface LinkLiveVoiceAudioArtifactInput {
|
|
|
614
614
|
position?: number;
|
|
615
615
|
}
|
|
616
616
|
|
|
617
|
-
export function archiveLiveVoiceUserUtteranceAudio(
|
|
617
|
+
export async function archiveLiveVoiceUserUtteranceAudio(
|
|
618
618
|
input: ArchiveLiveVoiceRolelessAudioInput,
|
|
619
|
-
): LiveVoiceAudioArchiveResult {
|
|
619
|
+
): Promise<LiveVoiceAudioArchiveResult> {
|
|
620
620
|
return archiveLiveVoiceAudioArtifact({ ...input, role: "user" });
|
|
621
621
|
}
|
|
622
622
|
|
|
623
|
-
export function archiveLiveVoiceAssistantResponseAudio(
|
|
623
|
+
export async function archiveLiveVoiceAssistantResponseAudio(
|
|
624
624
|
input: ArchiveLiveVoiceRolelessAudioInput,
|
|
625
|
-
): LiveVoiceAudioArchiveResult {
|
|
625
|
+
): Promise<LiveVoiceAudioArchiveResult> {
|
|
626
626
|
return archiveLiveVoiceAudioArtifact({ ...input, role: "assistant" });
|
|
627
627
|
}
|
|
628
628
|
|
|
@@ -630,11 +630,11 @@ function normalizeMessageId(messageId: string | null | undefined): string {
|
|
|
630
630
|
return messageId?.trim() ?? "";
|
|
631
631
|
}
|
|
632
632
|
|
|
633
|
-
function linkLiveVoiceAudioToMessage(
|
|
633
|
+
async function linkLiveVoiceAudioToMessage(
|
|
634
634
|
input: LinkLiveVoiceRolelessAudioInput & {
|
|
635
635
|
role: LiveVoiceAudioArchiveRole;
|
|
636
636
|
},
|
|
637
|
-
): LiveVoiceAudioArchiveResult {
|
|
637
|
+
): Promise<LiveVoiceAudioArchiveResult> {
|
|
638
638
|
const messageId = normalizeMessageId(input.messageId);
|
|
639
639
|
if (!messageId) {
|
|
640
640
|
return resultUnlinked(
|
|
@@ -651,15 +651,15 @@ function linkLiveVoiceAudioToMessage(
|
|
|
651
651
|
});
|
|
652
652
|
}
|
|
653
653
|
|
|
654
|
-
export function linkLiveVoiceUserUtteranceAudioToMessage(
|
|
654
|
+
export async function linkLiveVoiceUserUtteranceAudioToMessage(
|
|
655
655
|
input: LinkLiveVoiceRolelessAudioInput,
|
|
656
|
-
): LiveVoiceAudioArchiveResult {
|
|
656
|
+
): Promise<LiveVoiceAudioArchiveResult> {
|
|
657
657
|
return linkLiveVoiceAudioToMessage({ ...input, role: "user" });
|
|
658
658
|
}
|
|
659
659
|
|
|
660
|
-
export function linkLiveVoiceAssistantResponseAudioToMessage(
|
|
660
|
+
export async function linkLiveVoiceAssistantResponseAudioToMessage(
|
|
661
661
|
input: LinkLiveVoiceRolelessAudioInput,
|
|
662
|
-
): LiveVoiceAudioArchiveResult {
|
|
662
|
+
): Promise<LiveVoiceAudioArchiveResult> {
|
|
663
663
|
return linkLiveVoiceAudioToMessage({ ...input, role: "assistant" });
|
|
664
664
|
}
|
|
665
665
|
|
|
@@ -207,13 +207,19 @@ class LiveVoiceConnectionImpl implements LiveVoiceConnection {
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
private sendError(
|
|
210
|
-
error: Pick<LiveVoiceProtocolError, "code" | "message">,
|
|
210
|
+
error: Pick<LiveVoiceProtocolError, "code" | "message" | "frameType">,
|
|
211
211
|
): void {
|
|
212
212
|
this.sendFrame({
|
|
213
213
|
type: "error",
|
|
214
214
|
seq: this.lastSeq + 1,
|
|
215
215
|
code: error.code,
|
|
216
216
|
message: error.message,
|
|
217
|
+
// Which frame was rejected, when the parser knows. Without it an
|
|
218
|
+
// `unknown_type` is unattributable: a client that sends more than one
|
|
219
|
+
// optional frame cannot tell which of them an older daemon refused, and
|
|
220
|
+
// has to guess. Guessing wrong is silent, which for a rejected photo
|
|
221
|
+
// means the user is told nothing at all.
|
|
222
|
+
...(error.frameType ? { frameType: error.frameType } : {}),
|
|
217
223
|
});
|
|
218
224
|
}
|
|
219
225
|
|