@mycadet/assistant 0.12.10 → 0.13.0
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/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/node_modules/@mycadet/ces-client/src/http-credentials.ts +18 -1
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +0 -20
- package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/openapi.yaml +2 -708
- package/package.json +2 -3
- package/scripts/sync-web-search-catalog.ts +1 -2
- package/src/__tests__/cloud-profile-guard.test.ts +41 -5
- package/src/__tests__/config-schema.test.ts +6 -38
- package/src/__tests__/conversation-queue.test.ts +45 -6
- package/src/__tests__/db-readiness-http-gate.test.ts +0 -6
- package/src/__tests__/persist-onboarding-artifacts.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +0 -51
- package/src/__tests__/provider-cloud-proxy-integration.test.ts +2 -110
- package/src/__tests__/run-due-schedules.test.ts +0 -4
- package/src/__tests__/schedule-store.test.ts +0 -12
- package/src/__tests__/stt-stream-session.test.ts +4 -4
- package/src/__tests__/telephony-tts-guard.test.ts +2 -50
- package/src/__tests__/voice-config-update.test.ts +12 -167
- package/src/__tests__/web-search-catalog-parity.test.ts +0 -2
- package/src/__tests__/workspace-migration-130-speech-mode-to-provider.test.ts +9 -7
- package/src/__tests__/workspace-migration-132-web-search-mode-to-provider.test.ts +6 -6
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -4
- package/src/calls/resolve-call-tts-provider.ts +6 -25
- package/src/calls/telephony-credential-preflight.ts +0 -10
- package/src/calls/telephony-tts-capability.ts +1 -17
- package/src/cli/commands/tts.ts +1 -2
- package/src/cli/index.help.ts +0 -2
- package/src/cli/program.ts +0 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +4 -42
- package/src/config/schemas/__tests__/stt.test.ts +10 -14
- package/src/config/schemas/llm.ts +3 -5
- package/src/config/schemas/services.ts +5 -6
- package/src/config/schemas/stt.ts +1 -3
- package/src/config/schemas/tts.ts +1 -30
- package/src/daemon/daemon-readiness.ts +4 -29
- package/src/daemon/lifecycle.ts +0 -31
- package/src/daemon/providers-setup.ts +0 -8
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -66
- package/src/ipc/__tests__/db-readiness-ipc-gate.test.ts +1 -9
- package/src/live-voice/__tests__/live-voice-credential-preflight.test.ts +0 -44
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +4 -6
- package/src/live-voice/__tests__/live-voice-stt.test.ts +1 -1
- package/src/live-voice/live-voice-credential-preflight.ts +9 -33
- package/src/live-voice/live-voice-session.ts +10 -6
- package/src/live-voice/live-voice-tts.ts +1 -8
- package/src/plugins/defaults/memory/worker.ts +3 -3
- package/src/prompts/persona-resolver.ts +0 -35
- package/src/providers/__tests__/search-provider-catalog.test.ts +5 -8
- package/src/providers/__tests__/voice-error-copy.test.ts +9 -10
- package/src/providers/search-provider-catalog.ts +7 -39
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +3 -47
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +6 -372
- package/src/providers/speech-to-text/provider-catalog.ts +2 -73
- package/src/providers/speech-to-text/resolve.ts +3 -120
- package/src/runtime/http-router-types.ts +1 -3
- package/src/runtime/http-server.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +13 -11
- package/src/runtime/routes/__tests__/tts-routes.test.ts +2 -6
- package/src/runtime/routes/conversation-query-routes.ts +0 -96
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +7 -7
- package/src/runtime/routes/index.ts +0 -4
- package/src/runtime/routes/live-voice-routes.test.ts +4 -11
- package/src/runtime/routes/live-voice-routes.ts +8 -11
- package/src/runtime/routes/secret-routes.ts +0 -6
- package/src/runtime/routes/tts-routes.ts +0 -38
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1 -7
- package/src/schedule/__tests__/worker-mcp-readiness.test.ts +0 -4
- package/src/schedule/schedule-store.ts +0 -7
- package/src/schedule/scheduler.ts +0 -5
- package/src/schedule/worker.ts +1 -1
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +2 -10
- package/src/stt/daemon-batch-transcriber.ts +4 -38
- package/src/stt/types.ts +1 -9
- package/src/tools/network/__tests__/web-search.test.ts +3 -250
- package/src/tools/network/web-search.ts +4 -165
- package/src/tts/__tests__/provider-resolution.test.ts +0 -5
- package/src/tts/provider-catalog.ts +0 -4
- package/src/tts/types.ts +0 -1
- package/src/__tests__/cloud-speech-defaults.test.ts +0 -330
- package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1708
- package/src/__tests__/migration-export-http.test.ts +0 -557
- package/src/__tests__/migration-export-streaming.test.ts +0 -350
- package/src/__tests__/migration-export-to-gcs.test.ts +0 -706
- package/src/__tests__/migration-import-commit-http.test.ts +0 -1176
- package/src/__tests__/migration-import-from-gcs.test.ts +0 -534
- package/src/__tests__/migration-import-from-path.test.ts +0 -324
- package/src/__tests__/migration-import-from-url.test.ts +0 -588
- package/src/__tests__/migration-import-preflight-http.test.ts +0 -739
- package/src/__tests__/migration-jobs-status.test.ts +0 -139
- package/src/__tests__/migration-parity-persistence.test.ts +0 -1891
- package/src/__tests__/migration-transport.test.ts +0 -1089
- package/src/__tests__/migration-validate-http.test.ts +0 -634
- package/src/__tests__/migration-wizard.test.ts +0 -1395
- package/src/__tests__/rebind-secrets-screen.test.ts +0 -876
- package/src/__tests__/sanitize-config-for-transfer.test.ts +0 -259
- package/src/__tests__/transfer-progress-screen.test.ts +0 -1217
- package/src/__tests__/validation-results-screen.test.ts +0 -1130
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +0 -147
- package/src/background-wake/background-wake-routes.test.ts +0 -906
- package/src/background-wake/cloud-client.test.ts +0 -328
- package/src/background-wake/cloud-client.ts +0 -196
- package/src/background-wake/next-wake.test.ts +0 -335
- package/src/background-wake/next-wake.ts +0 -188
- package/src/background-wake/publisher.ts +0 -93
- package/src/background-wake/wake-intent-hooks.test.ts +0 -234
- package/src/cli/commands/__tests__/backup.test.ts +0 -363
- package/src/cli/commands/backup.help.ts +0 -180
- package/src/cli/commands/backup.ts +0 -398
- package/src/cloud/cloud-speech.test.ts +0 -335
- package/src/cloud/cloud-speech.ts +0 -300
- package/src/config/cloud-speech-defaults.ts +0 -300
- package/src/config/sanitize-for-transfer.ts +0 -111
- package/src/live-voice/__tests__/live-voice-cloud-speech-fallback.test.ts +0 -250
- package/src/providers/speech-to-text/__tests__/cadet-cloud-realtime.test.ts +0 -457
- package/src/providers/speech-to-text/__tests__/cadet-cloud.test.ts +0 -271
- package/src/providers/speech-to-text/__tests__/cadet-speech-relay-connection.test.ts +0 -126
- package/src/providers/speech-to-text/cadet-cloud-flux-realtime.ts +0 -309
- package/src/providers/speech-to-text/cadet-cloud-realtime.ts +0 -315
- package/src/providers/speech-to-text/cadet-cloud.ts +0 -108
- package/src/providers/speech-to-text/cadet-speech-relay-connection.ts +0 -176
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +0 -254
- package/src/runtime/migrations/__tests__/job-registry.test.ts +0 -346
- package/src/runtime/migrations/__tests__/origin-mode.test.ts +0 -62
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +0 -184
- package/src/runtime/migrations/__tests__/staged-import-path.test.ts +0 -110
- package/src/runtime/migrations/__tests__/v1-test-helpers.ts +0 -112
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +0 -285
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +0 -126
- package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +0 -253
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +0 -208
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +0 -485
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +0 -349
- package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +0 -196
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +0 -551
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +0 -428
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +0 -220
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +0 -1823
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +0 -598
- package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +0 -463
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -538
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +0 -515
- package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +0 -453
- package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +0 -319
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +0 -240
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +0 -473
- package/src/runtime/migrations/gcs-signed-url.ts +0 -190
- package/src/runtime/migrations/job-registry.ts +0 -297
- package/src/runtime/migrations/migration-transport.ts +0 -612
- package/src/runtime/migrations/migration-wizard.ts +0 -747
- package/src/runtime/migrations/origin-mode.ts +0 -42
- package/src/runtime/migrations/rebind-secrets-screen.ts +0 -412
- package/src/runtime/migrations/staged-import-path.ts +0 -116
- package/src/runtime/migrations/transfer-progress-screen.ts +0 -323
- package/src/runtime/migrations/validation-results-screen.ts +0 -469
- package/src/runtime/migrations/vbundle-builder.ts +0 -1382
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -405
- package/src/runtime/migrations/vbundle-import-policy.ts +0 -241
- package/src/runtime/migrations/vbundle-importer.ts +0 -826
- package/src/runtime/migrations/vbundle-metadata-merge.ts +0 -113
- package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -2844
- package/src/runtime/migrations/vbundle-streaming-validator.ts +0 -426
- package/src/runtime/migrations/vbundle-tar-stream.ts +0 -244
- package/src/runtime/migrations/vbundle-validator.ts +0 -724
- package/src/runtime/routes/__tests__/migration-cadet-metadata-reconcile.test.ts +0 -227
- package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +0 -54
- package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +0 -70
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +0 -257
- package/src/runtime/routes/background-wake-routes.ts +0 -387
- package/src/runtime/routes/migration-routes.ts +0 -2584
- package/src/tools/network/__tests__/cloud-search-proxy.test.ts +0 -282
- package/src/tools/network/cloud-search-proxy.ts +0 -211
- package/src/tts/__tests__/cadet-provider.test.ts +0 -253
- package/src/tts/providers/cadet-provider.ts +0 -244
- package/src/types/tar-stream.d.ts +0 -66
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
batchBoundaryGapReason,
|
|
14
14
|
getCredentialProvider,
|
|
15
15
|
getProviderEntry,
|
|
16
|
-
isCloudSttProvider,
|
|
17
16
|
resolveSttCatalogKey,
|
|
18
17
|
supportsBoundary,
|
|
19
18
|
supportsDiarization,
|
|
@@ -37,11 +36,6 @@ const log = getLogger("stt-resolver");
|
|
|
37
36
|
*/
|
|
38
37
|
const MULTILINGUAL_DEFAULT_PROVIDERS: ReadonlySet<SttProviderId> = new Set([
|
|
39
38
|
"deepgram",
|
|
40
|
-
"cadet",
|
|
41
|
-
// Cloud Flux resolves an absent language to `flux-general-en` relay-side,
|
|
42
|
-
// so unset is an English pin here too. With "multi" the relay selects
|
|
43
|
-
// `flux-general-multi` and lets Flux detect and code-switch.
|
|
44
|
-
"cadet-flux",
|
|
45
39
|
] as SttProviderId[]);
|
|
46
40
|
|
|
47
41
|
/**
|
|
@@ -156,16 +150,6 @@ export async function resolveBatchTranscriber(
|
|
|
156
150
|
throw new SttError("provider-error", reason, { userFacing: true });
|
|
157
151
|
}
|
|
158
152
|
|
|
159
|
-
if (provider === "cadet") {
|
|
160
|
-
// Managed batch rides the cloud's speech proxy, which forwards the
|
|
161
|
-
// language to Deepgram server-side. A platform build predating that
|
|
162
|
-
// field ignores it rather than failing, so the daemon can send one
|
|
163
|
-
// before the cloud side deploys.
|
|
164
|
-
return (await sttProviderKeyResolves("cadet"))
|
|
165
|
-
? createDaemonBatchTranscriber(null, "cadet", language)
|
|
166
|
-
: null;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
153
|
const apiKey = await getProviderKeyAsync(credentialProviderName);
|
|
170
154
|
return createDaemonBatchTranscriber(
|
|
171
155
|
apiKey,
|
|
@@ -515,18 +499,8 @@ export async function resolveStreamingTranscriber(
|
|
|
515
499
|
(diarizePreference === "preferred" || diarizePreference === "required") &&
|
|
516
500
|
providerSupportsDiarization;
|
|
517
501
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
// connection resolves.
|
|
521
|
-
const managed = isCloudSttProvider(provider);
|
|
522
|
-
const apiKey = managed
|
|
523
|
-
? null
|
|
524
|
-
: await getProviderKeyAsync(credentialProviderName);
|
|
525
|
-
if (managed) {
|
|
526
|
-
if (!(await sttProviderKeyResolves("cadet"))) {
|
|
527
|
-
return null;
|
|
528
|
-
}
|
|
529
|
-
} else if (!apiKey) {
|
|
502
|
+
const apiKey = await getProviderKeyAsync(credentialProviderName);
|
|
503
|
+
if (!apiKey) {
|
|
530
504
|
return null;
|
|
531
505
|
}
|
|
532
506
|
|
|
@@ -656,82 +630,6 @@ async function createStreamingTranscriber(
|
|
|
656
630
|
...(options.diarize ? { diarize: true } : {}),
|
|
657
631
|
});
|
|
658
632
|
}
|
|
659
|
-
case "cadet": {
|
|
660
|
-
// Cloud speech dials the GATEWAY's speech relay (tunnel contact is
|
|
661
|
-
// gateway-only); the apiKey argument is unused. Diarization is
|
|
662
|
-
// unsupported (not in the relay's param allowlist) and silently
|
|
663
|
-
// ignored, matching Gemini/Whisper — as is utteranceEndMs (the relay
|
|
664
|
-
// allowlist has no utterance_end_ms; boundary finals ride on
|
|
665
|
-
// endpointing alone).
|
|
666
|
-
// The gateway is the credential authority, but the cloud
|
|
667
|
-
// connection (API key + assistant ID) is checkable locally — gate
|
|
668
|
-
// here so preflight reports "connect your account" instead of
|
|
669
|
-
// resolving a transcriber whose dial is doomed.
|
|
670
|
-
const { cadetCloudSpeechAvailable } = await import("./cadet-cloud.js");
|
|
671
|
-
if (!(await cadetCloudSpeechAvailable())) {
|
|
672
|
-
return null;
|
|
673
|
-
}
|
|
674
|
-
const { resolveSpeechRelayConnection } =
|
|
675
|
-
await import("./cadet-speech-relay-connection.js");
|
|
676
|
-
const connection = await resolveSpeechRelayConnection();
|
|
677
|
-
if (!connection) {
|
|
678
|
-
return null;
|
|
679
|
-
}
|
|
680
|
-
const { CadetManagedRealtimeTranscriber } =
|
|
681
|
-
await import("./cadet-cloud-realtime.js");
|
|
682
|
-
return new CadetManagedRealtimeTranscriber(connection, {
|
|
683
|
-
sampleRate: options.sampleRate,
|
|
684
|
-
// `language` IS in the relay's param allowlist, and the relay pins
|
|
685
|
-
// the STT model to nova-3 server-side, so "multi" code-switching
|
|
686
|
-
// needs nothing from the cloud, only this forward. Source:
|
|
687
|
-
// mycadet-platform: tunnel/internal/tunnel/deepgram.go
|
|
688
|
-
// (deepgramSTTParams allowlist; deepgramSTTModel pin).
|
|
689
|
-
...(options.language ? { language: options.language } : {}),
|
|
690
|
-
...(options.utteranceBoundaryFinals
|
|
691
|
-
? { utteranceBoundaryFinals: true }
|
|
692
|
-
: {}),
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
case "cadet-flux": {
|
|
696
|
-
// Cloud Flux: same relay as `cadet`, but the STT v2 endpoint with
|
|
697
|
-
// `contract=flux` so the turn events arrive intact instead of being
|
|
698
|
-
// translated into the released v1 Deepgram dialect. Gated on the local
|
|
699
|
-
// cloud connection for the same reason as `cadet` above.
|
|
700
|
-
const { cadetCloudSpeechAvailable } = await import("./cadet-cloud.js");
|
|
701
|
-
if (!(await cadetCloudSpeechAvailable())) {
|
|
702
|
-
return null;
|
|
703
|
-
}
|
|
704
|
-
// The relay maps the language onto a Flux model and refuses the dial
|
|
705
|
-
// when there is none, so a language outside Flux's roster fails as a
|
|
706
|
-
// relay param error the caller cannot explain. Refuse here instead, on
|
|
707
|
-
// the same rule the BYOK twin above applies, so the reason is named
|
|
708
|
-
// where it can be read.
|
|
709
|
-
const { fluxModelForLanguage: managedFluxModelForLanguage } =
|
|
710
|
-
await import("./deepgram-flux-frames.js");
|
|
711
|
-
if (managedFluxModelForLanguage(options.language) === null) {
|
|
712
|
-
log.warn(
|
|
713
|
-
{ providerId, language: options.language },
|
|
714
|
-
"Cloud Flux has no model for the configured language; refusing rather than transcribing it as another",
|
|
715
|
-
);
|
|
716
|
-
return null;
|
|
717
|
-
}
|
|
718
|
-
const { resolveSpeechRelayConnection } =
|
|
719
|
-
await import("./cadet-speech-relay-connection.js");
|
|
720
|
-
const connection = await resolveSpeechRelayConnection();
|
|
721
|
-
if (!connection) {
|
|
722
|
-
return null;
|
|
723
|
-
}
|
|
724
|
-
const { CadetManagedFluxRealtimeTranscriber } =
|
|
725
|
-
await import("./cadet-cloud-flux-realtime.js");
|
|
726
|
-
return new CadetManagedFluxRealtimeTranscriber(connection, {
|
|
727
|
-
sampleRate: options.sampleRate,
|
|
728
|
-
// The relay picks `flux-general-en` or `flux-general-multi` from
|
|
729
|
-
// this, so it is the only lever over the Flux model from here.
|
|
730
|
-
// `utteranceBoundaryFinals` is a nova-3 concept and the catalog
|
|
731
|
-
// keeps this provider off telephony, so it never arrives.
|
|
732
|
-
...(options.language ? { language: options.language } : {}),
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
633
|
case "deepgram-flux": {
|
|
736
634
|
// Flux is streaming-only and dials Deepgram's /v2/listen conversational
|
|
737
635
|
// endpoint. Turn-detection tuning comes from `liveVoice.flux`, which the
|
|
@@ -775,29 +673,14 @@ async function createStreamingTranscriber(
|
|
|
775
673
|
* need a key-existence check don't import secure-keys directly.
|
|
776
674
|
*/
|
|
777
675
|
/**
|
|
778
|
-
* Human-readable reason for a credential gap
|
|
779
|
-
* providers (cadet) are fixed by connecting the cloud account, not by
|
|
780
|
-
* entering an API key.
|
|
676
|
+
* Human-readable reason for a credential gap.
|
|
781
677
|
*/
|
|
782
678
|
export function sttCredentialGapReason(credentialProviderName: string): string {
|
|
783
|
-
// Called with either the catalog id ("cadet") or its credential-service
|
|
784
|
-
// key ("cadet", the persisted identity family) — both mean cloud speech.
|
|
785
|
-
if (
|
|
786
|
-
credentialProviderName === "cadet" ||
|
|
787
|
-
credentialProviderName === "cadet"
|
|
788
|
-
) {
|
|
789
|
-
return "No Cadet Cloud connection for cloud speech — run 'assistant cloud connect'";
|
|
790
|
-
}
|
|
791
679
|
return `No API key configured for credential provider "${credentialProviderName}"`;
|
|
792
680
|
}
|
|
793
681
|
|
|
794
682
|
export async function sttProviderKeyResolves(
|
|
795
683
|
credentialProviderName: string,
|
|
796
684
|
): Promise<boolean> {
|
|
797
|
-
// cadet has no stored API key — the cloud connection is the credential.
|
|
798
|
-
if (credentialProviderName === "cadet") {
|
|
799
|
-
const { cadetCloudSpeechAvailable } = await import("./cadet-cloud.js");
|
|
800
|
-
return cadetCloudSpeechAvailable();
|
|
801
|
-
}
|
|
802
685
|
return (await getProviderKeyAsync(credentialProviderName)) !== undefined;
|
|
803
686
|
}
|
|
@@ -45,9 +45,7 @@ export type RouteBodySchema = z.ZodType;
|
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Description for a non-200 response variant. Used when an endpoint has
|
|
48
|
-
* meaningful alternate response shapes that clients should handle
|
|
49
|
-
* 502 `fetch_failed` on POST /v1/migrations/import when the URL body path
|
|
50
|
-
* can't reach the upstream bundle host).
|
|
48
|
+
* meaningful alternate response shapes that clients should handle.
|
|
51
49
|
*/
|
|
52
50
|
export interface RouteAdditionalResponse {
|
|
53
51
|
description: string;
|
|
@@ -122,7 +122,7 @@ const log = getLogger("runtime-http");
|
|
|
122
122
|
const DEFAULT_PORT = 7821;
|
|
123
123
|
const DEFAULT_HOSTNAME = "127.0.0.1";
|
|
124
124
|
|
|
125
|
-
/** Global hard cap on request body size (512 MB
|
|
125
|
+
/** Global hard cap on request body size (512 MB, accommodates app package uploads). */
|
|
126
126
|
const MAX_REQUEST_BODY_BYTES = 512 * 1024 * 1024;
|
|
127
127
|
|
|
128
128
|
function dbMigrationUnavailableForEndpoint(endpoint: string): Response | null {
|
|
@@ -1625,7 +1625,7 @@ describe("config GET serves profiles verbatim", () => {
|
|
|
1625
1625
|
});
|
|
1626
1626
|
});
|
|
1627
1627
|
|
|
1628
|
-
describe("provider
|
|
1628
|
+
describe("provider labels follow the open-string Zod schema", () => {
|
|
1629
1629
|
const configPatchRoute = ROUTES.find(
|
|
1630
1630
|
(r) => r.operationId === "config_patch",
|
|
1631
1631
|
)!;
|
|
@@ -1659,21 +1659,23 @@ describe("provider membership at the write choke point", () => {
|
|
|
1659
1659
|
expect(llm.profiles?.work?.provider).toBe("anthropic-work");
|
|
1660
1660
|
});
|
|
1661
1661
|
|
|
1662
|
-
test("
|
|
1662
|
+
test("a new provider label is accepted by the schema", async () => {
|
|
1663
1663
|
rawConfigFixture = { llm: {} };
|
|
1664
1664
|
seedRawConfig();
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1665
|
+
await configPatchRoute.handler({
|
|
1666
|
+
body: {
|
|
1667
|
+
llm: {
|
|
1668
|
+
profiles: {
|
|
1669
|
+
work: {
|
|
1670
|
+
source: "user",
|
|
1671
|
+
provider: "anthropic-work",
|
|
1672
|
+
model: "claude-opus-4-8",
|
|
1672
1673
|
},
|
|
1673
1674
|
},
|
|
1674
1675
|
},
|
|
1675
|
-
}
|
|
1676
|
-
)
|
|
1676
|
+
},
|
|
1677
|
+
});
|
|
1678
|
+
expect(persistedProfile("work").provider).toBe("anthropic-work");
|
|
1677
1679
|
});
|
|
1678
1680
|
});
|
|
1679
1681
|
|
|
@@ -127,17 +127,13 @@ afterEach(() => {
|
|
|
127
127
|
// ---------------------------------------------------------------------------
|
|
128
128
|
|
|
129
129
|
describe("tts-routes", () => {
|
|
130
|
-
test("exports route definitions for tts/providers, tts/
|
|
131
|
-
expect(ROUTES).toHaveLength(
|
|
130
|
+
test("exports route definitions for tts/providers, tts/synthesize, and tts/synthesize-cli", () => {
|
|
131
|
+
expect(ROUTES).toHaveLength(3);
|
|
132
132
|
|
|
133
133
|
const providers = getRoute("tts/providers");
|
|
134
134
|
expect(providers.method).toBe("GET");
|
|
135
135
|
expect(providers.policy?.requiredScopes).toContain("settings.read");
|
|
136
136
|
|
|
137
|
-
const managedVoices = getRoute("tts/managed-voices");
|
|
138
|
-
expect(managedVoices.method).toBe("GET");
|
|
139
|
-
expect(managedVoices.policy?.requiredScopes).toContain("settings.read");
|
|
140
|
-
|
|
141
137
|
const synthesize = getRoute("tts/synthesize");
|
|
142
138
|
expect(synthesize.method).toBe("POST");
|
|
143
139
|
expect(synthesize.policy?.requiredScopes).toContain("chat.read");
|
|
@@ -53,8 +53,6 @@ import {
|
|
|
53
53
|
LLMDefaultConfigSchema,
|
|
54
54
|
LLMSchema,
|
|
55
55
|
ProfileEntry,
|
|
56
|
-
routingIdentityModelIssue,
|
|
57
|
-
unknownLlmProviderIssue,
|
|
58
56
|
} from "../../config/schemas/llm.js";
|
|
59
57
|
import { VALID_MEMORY_EMBEDDING_PROVIDERS } from "../../config/schemas/memory-storage.js";
|
|
60
58
|
import { ServiceModeSchema } from "../../config/schemas/services.js";
|
|
@@ -106,7 +104,6 @@ import { getMemoryRecallLogByMessageIds } from "../../plugins/defaults/memory/me
|
|
|
106
104
|
import { getMemoryV2ActivationLogByMessageIds } from "../../plugins/defaults/memory/v2/activation-log-store.js";
|
|
107
105
|
import { getMemoryV3SelectionForInspectorByMessageIds } from "../../plugins/defaults/memory/v3/selection-log-store.js";
|
|
108
106
|
import { CLOUD_ROUTABLE_PROVIDERS } from "../../providers/cadet-model-routing.js";
|
|
109
|
-
import { writableProfileProviderIssue } from "../../providers/connection-resolution.js";
|
|
110
107
|
import { ROUTING_IDENTITY_PROVIDERS } from "../../providers/inference/auth.js";
|
|
111
108
|
import { PROVIDERS_REQUIRING_BASE_URL_AND_MODELS } from "../../providers/inference/auth.js";
|
|
112
109
|
import {
|
|
@@ -684,98 +681,6 @@ function handleGetConfigSchema({ queryParams = {} }: RouteHandlerArgs) {
|
|
|
684
681
|
};
|
|
685
682
|
}
|
|
686
683
|
|
|
687
|
-
/**
|
|
688
|
-
* Reject writes that would store a routing-identity provider with a missing
|
|
689
|
-
* or unroutable model (see `routingIdentityModelIssue`). saveRawConfig
|
|
690
|
-
* persists without schema validation, so the parse-time rejection alone
|
|
691
|
-
* would let the pair reach disk and be stripped on the next read — a silent
|
|
692
|
-
* no-op where the user expects a saved override.
|
|
693
|
-
*/
|
|
694
|
-
function collectProviderEntries(
|
|
695
|
-
raw: Record<string, unknown>,
|
|
696
|
-
): [string, { provider?: unknown; model?: unknown }][] {
|
|
697
|
-
const llm = raw.llm as
|
|
698
|
-
| {
|
|
699
|
-
default?: { provider?: unknown; model?: unknown } | null;
|
|
700
|
-
profiles?: Record<
|
|
701
|
-
string,
|
|
702
|
-
{ provider?: unknown; model?: unknown } | null | undefined
|
|
703
|
-
>;
|
|
704
|
-
callSites?: Record<
|
|
705
|
-
string,
|
|
706
|
-
{ provider?: unknown; model?: unknown } | null | undefined
|
|
707
|
-
>;
|
|
708
|
-
}
|
|
709
|
-
| undefined;
|
|
710
|
-
const entries: [string, { provider?: unknown; model?: unknown }][] = [];
|
|
711
|
-
// llm.default is a raw compatibility field outside the parsed schema;
|
|
712
|
-
// profile materialization uses the on-disk blob as its fill base, so it is
|
|
713
|
-
// checked like the schema-carried sections.
|
|
714
|
-
if (llm?.default) {
|
|
715
|
-
entries.push(["llm.default", llm.default]);
|
|
716
|
-
}
|
|
717
|
-
for (const section of ["profiles", "callSites"] as const) {
|
|
718
|
-
for (const [name, entry] of Object.entries(llm?.[section] ?? {})) {
|
|
719
|
-
if (entry) {
|
|
720
|
-
entries.push([`llm.${section}.${name}`, entry]);
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
return entries;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
function assertRoutableIdentityEntries(
|
|
728
|
-
preWrite: Record<string, unknown>,
|
|
729
|
-
raw: Record<string, unknown>,
|
|
730
|
-
): void {
|
|
731
|
-
const entries = collectProviderEntries(raw);
|
|
732
|
-
const priorProviders = new Map(
|
|
733
|
-
collectProviderEntries(preWrite).flatMap(([label, entry]) =>
|
|
734
|
-
typeof entry.provider === "string" ? [[label, entry.provider]] : [],
|
|
735
|
-
),
|
|
736
|
-
);
|
|
737
|
-
for (const [label, entry] of entries) {
|
|
738
|
-
if (typeof entry.provider !== "string") {
|
|
739
|
-
continue;
|
|
740
|
-
}
|
|
741
|
-
// The provider schema is an open string (a stored value outside the
|
|
742
|
-
// known set parses instead of stripping its profile), so write-time
|
|
743
|
-
// membership is enforced here and at the profiles route. Scoped to
|
|
744
|
-
// providers this write introduces or changes: a stored value outside
|
|
745
|
-
// the known set is readable and dispatchable by design, and
|
|
746
|
-
// re-validating it on every write would make all later settings saves
|
|
747
|
-
// fail with no in-product repair path. Profiles accept entry names;
|
|
748
|
-
// call-site fragments and the legacy default blob stay vendor-only
|
|
749
|
-
// (overrides become model-only with the entries demolition).
|
|
750
|
-
if (entry.provider !== priorProviders.get(label)) {
|
|
751
|
-
const providerIssue = label.startsWith("llm.profiles.")
|
|
752
|
-
? writableProfileProviderIssue(entry.provider)
|
|
753
|
-
: unknownLlmProviderIssue(entry.provider);
|
|
754
|
-
if (providerIssue) {
|
|
755
|
-
throw new BadRequestError(`${providerIssue} (${label})`);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
// A model-less entry under a code-defined default name is the thin
|
|
759
|
-
// managed stub — its content is code-owned, so the routing-identity
|
|
760
|
-
// model check applies only to entries carrying their own body.
|
|
761
|
-
const profileName = label.startsWith("llm.profiles.")
|
|
762
|
-
? label.slice("llm.profiles.".length)
|
|
763
|
-
: undefined;
|
|
764
|
-
const isManagedName =
|
|
765
|
-
profileName != null && MANAGED_PROFILE_NAMES.has(profileName);
|
|
766
|
-
if (entry.model == null && isManagedName) {
|
|
767
|
-
continue;
|
|
768
|
-
}
|
|
769
|
-
const issue = routingIdentityModelIssue(
|
|
770
|
-
entry.provider,
|
|
771
|
-
typeof entry.model === "string" ? entry.model : undefined,
|
|
772
|
-
);
|
|
773
|
-
if (issue) {
|
|
774
|
-
throw new BadRequestError(`${issue} (${label})`);
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
684
|
export async function commitConfigWrite(
|
|
780
685
|
raw: Record<string, unknown>,
|
|
781
686
|
opLabel: string,
|
|
@@ -792,7 +697,6 @@ export async function commitConfigWrite(
|
|
|
792
697
|
if (!chatSettings.success) {
|
|
793
698
|
throw new BadRequestError(chatSettings.error.message);
|
|
794
699
|
}
|
|
795
|
-
assertRoutableIdentityEntries(preWrite, raw);
|
|
796
700
|
|
|
797
701
|
// Suppress the file-watcher callback for the duration of the debounce
|
|
798
702
|
// window. Without this, the ConfigWatcher detects the config.json write
|
|
@@ -200,17 +200,17 @@ describe("tryTranscribeAudioAttachments", () => {
|
|
|
200
200
|
expect(reason).toContain("Deepgram");
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
-
test("
|
|
203
|
+
test("a userFacing auth failure surfaces its remediation, not the BYOK API-key rewrite", async () => {
|
|
204
204
|
const audio = makeAudioAttachment("a1");
|
|
205
205
|
mockAttachments = [audio];
|
|
206
206
|
const managedMessage =
|
|
207
207
|
"Cloud speech needs a working Cadet Cloud connection — reconnect with 'assistant cloud connect'.";
|
|
208
208
|
mockTranscriber = {
|
|
209
|
-
providerId: "
|
|
209
|
+
providerId: "deepgram",
|
|
210
210
|
boundaryId: "daemon-batch",
|
|
211
211
|
transcribe: async () => {
|
|
212
|
-
//
|
|
213
|
-
//
|
|
212
|
+
// Adapters flag their SttError userFacing so the friendly copy
|
|
213
|
+
// layer passes the message through verbatim.
|
|
214
214
|
throw new SttError("auth", managedMessage, { userFacing: true });
|
|
215
215
|
},
|
|
216
216
|
};
|
|
@@ -220,18 +220,18 @@ describe("tryTranscribeAudioAttachments", () => {
|
|
|
220
220
|
expect(result.status).toBe("error");
|
|
221
221
|
const reason = (result as { reason: string }).reason;
|
|
222
222
|
expect(reason).toBe(managedMessage);
|
|
223
|
-
//
|
|
223
|
+
// The BYOK rewrite must not appear over a userFacing message.
|
|
224
224
|
expect(reason).not.toContain("API key");
|
|
225
225
|
expect(reason).not.toContain("Settings → Voice");
|
|
226
226
|
});
|
|
227
227
|
|
|
228
|
-
test("
|
|
228
|
+
test("a credits-exhausted userFacing failure is not hidden behind generic copy", async () => {
|
|
229
229
|
const audio = makeAudioAttachment("a1");
|
|
230
230
|
mockAttachments = [audio];
|
|
231
231
|
const creditsMessage =
|
|
232
232
|
"Cadet credits are exhausted — add funds to your Cadet account to continue using managed transcription.";
|
|
233
233
|
mockTranscriber = {
|
|
234
|
-
providerId: "
|
|
234
|
+
providerId: "deepgram",
|
|
235
235
|
boundaryId: "daemon-batch",
|
|
236
236
|
transcribe: async () => {
|
|
237
237
|
throw new SttError("provider-error", creditsMessage, {
|
|
@@ -27,7 +27,6 @@ import { ROUTES as AUDIT_ROUTES } from "./audit-routes.js";
|
|
|
27
27
|
import { ROUTES as AUTH_ROUTES } from "./auth-routes.js";
|
|
28
28
|
import { ROUTES as AVATAR_ROUTES } from "./avatar-routes.js";
|
|
29
29
|
import { ROUTES as BACKGROUND_TOOL_ROUTES } from "./background-tool-routes.js";
|
|
30
|
-
import { ROUTES as BACKGROUND_WAKE_ROUTES } from "./background-wake-routes.js";
|
|
31
30
|
import { ROUTES as BOOKMARK_ROUTES } from "./bookmark-routes.js";
|
|
32
31
|
import { ROUTES as BRAIN_GRAPH_ROUTES } from "./brain-graph-routes.js";
|
|
33
32
|
import { ROUTES as BROWSER_ROUTES } from "./browser-routes.js";
|
|
@@ -114,7 +113,6 @@ import { ROUTES as LOG_EXPORT_ROUTES } from "./log-export-routes.js";
|
|
|
114
113
|
import { ROUTES as MCP_AUTH_ROUTES } from "./mcp-auth-routes.js";
|
|
115
114
|
import { ROUTES as MESSAGES_LEXICAL_ROUTES } from "./messages-lexical-routes.js";
|
|
116
115
|
import { ROUTES as MIGRATION_ROLLBACK_ROUTES } from "./migration-rollback-routes.js";
|
|
117
|
-
import { ROUTES as MIGRATION_ROUTES } from "./migration-routes.js";
|
|
118
116
|
import { ROUTES as MONITORING_ROUTES } from "./monitoring-routes.js";
|
|
119
117
|
import { ROUTES as NOTIFICATION_ROUTES } from "./notification-routes.js";
|
|
120
118
|
import { ROUTES as OAUTH_APPS_ROUTES } from "./oauth-apps.js";
|
|
@@ -179,7 +177,6 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
179
177
|
...AUDIT_ROUTES,
|
|
180
178
|
...AUTH_ROUTES,
|
|
181
179
|
...AVATAR_ROUTES,
|
|
182
|
-
...BACKGROUND_WAKE_ROUTES,
|
|
183
180
|
...BACKGROUND_TOOL_ROUTES,
|
|
184
181
|
...BOOKMARK_ROUTES,
|
|
185
182
|
...CACHE_ROUTES,
|
|
@@ -267,7 +264,6 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
267
264
|
...MEMORY_WORKER_ROUTES,
|
|
268
265
|
...MESSAGES_LEXICAL_ROUTES,
|
|
269
266
|
...MIGRATION_ROLLBACK_ROUTES,
|
|
270
|
-
...MIGRATION_ROUTES,
|
|
271
267
|
...NOTIFICATION_ROUTES,
|
|
272
268
|
...OAUTH_APPS_ROUTES,
|
|
273
269
|
...OAUTH_LIFECYCLE_ROUTES,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for the live-voice preflight and session-end routes.
|
|
3
3
|
*
|
|
4
|
-
* The handler
|
|
5
|
-
*
|
|
4
|
+
* The preflight handler returns the live-voice credential readiness verdict
|
|
5
|
+
* verbatim:
|
|
6
6
|
* - ready → { status: "ready" }
|
|
7
7
|
* - not-ready → passes through the missing gaps + userMessage
|
|
8
|
-
* - defaulting is invoked BEFORE readiness is resolved
|
|
9
8
|
*
|
|
10
9
|
* The session-end route is the out-of-band recovery path (LUM-3440): it
|
|
11
10
|
* reports whether it actually reclaimed the daemon's single live-voice slot.
|
|
@@ -23,12 +22,6 @@ const calls: string[] = [];
|
|
|
23
22
|
let readiness: LiveVoiceCredentialReadiness = { status: "ready" };
|
|
24
23
|
let releaseResult: LiveVoiceSessionReleaseResult = { released: false };
|
|
25
24
|
|
|
26
|
-
mock.module("../../config/cloud-speech-defaults.js", () => ({
|
|
27
|
-
maybeDefaultSpeechToCloud: async () => {
|
|
28
|
-
calls.push("default");
|
|
29
|
-
},
|
|
30
|
-
}));
|
|
31
|
-
|
|
32
25
|
mock.module("../../live-voice/live-voice-credential-preflight.js", () => ({
|
|
33
26
|
resolveLiveVoiceCredentialReadiness: async () => {
|
|
34
27
|
calls.push("resolve");
|
|
@@ -80,13 +73,13 @@ describe("live_voice_preflight_post", () => {
|
|
|
80
73
|
expect(result).toEqual(readiness);
|
|
81
74
|
});
|
|
82
75
|
|
|
83
|
-
test("
|
|
76
|
+
test("resolves readiness without touching persisted config", async () => {
|
|
84
77
|
calls.length = 0;
|
|
85
78
|
readiness = { status: "ready" };
|
|
86
79
|
|
|
87
80
|
await preflightRoute.handler({});
|
|
88
81
|
|
|
89
|
-
expect(calls).toEqual(["
|
|
82
|
+
expect(calls).toEqual(["resolve"]);
|
|
90
83
|
});
|
|
91
84
|
});
|
|
92
85
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Transport-agnostic route definitions for live-voice preflight.
|
|
3
3
|
*
|
|
4
|
-
* POST /v1/live-voice/preflight —
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* POST /v1/live-voice/preflight — report whether the daemon can run both
|
|
5
|
+
* audio legs of a live voice session. Lets the web client verify voice is
|
|
6
|
+
* configured BEFORE opening the
|
|
7
7
|
* voice-room WebSocket, instead of opening it and reacting to an error frame.
|
|
8
8
|
*
|
|
9
9
|
* POST /v1/live-voice/session/end releases whatever session holds the
|
|
@@ -22,12 +22,9 @@ import type { RouteDefinition } from "./types.js";
|
|
|
22
22
|
// ---------------------------------------------------------------------------
|
|
23
23
|
|
|
24
24
|
async function handleLiveVoicePreflight() {
|
|
25
|
-
const { maybeDefaultSpeechToCloud } =
|
|
26
|
-
await import("../../config/cloud-speech-defaults.js");
|
|
27
25
|
const { resolveLiveVoiceCredentialReadiness } =
|
|
28
26
|
await import("../../live-voice/live-voice-credential-preflight.js");
|
|
29
27
|
|
|
30
|
-
await maybeDefaultSpeechToCloud();
|
|
31
28
|
return resolveLiveVoiceCredentialReadiness();
|
|
32
29
|
}
|
|
33
30
|
|
|
@@ -51,17 +48,17 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
51
48
|
operationId: "live_voice_preflight_post",
|
|
52
49
|
endpoint: "live-voice/preflight",
|
|
53
50
|
method: "POST",
|
|
54
|
-
// Requires settings.write, not a read scope:
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
51
|
+
// Requires settings.write, not a read scope: preflight reports the
|
|
52
|
+
// assistant's speech credential state, which the caller may act on to
|
|
53
|
+
// rewrite speech config. This matches the secret/settings config
|
|
54
|
+
// writers, which all require settings.write.
|
|
58
55
|
policy: {
|
|
59
56
|
requiredScopes: ["settings.write"],
|
|
60
57
|
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
61
58
|
},
|
|
62
59
|
summary: "Live voice preflight",
|
|
63
60
|
description:
|
|
64
|
-
"
|
|
61
|
+
"Report whether live voice can start with the current speech credentials.",
|
|
65
62
|
tags: ["live-voice"],
|
|
66
63
|
responseBody: z.object({
|
|
67
64
|
status: z.enum(["ready", "not-ready"]),
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
} from "../../acp/acp-credentials.js";
|
|
17
17
|
import { syncAvatarToCloud } from "../../avatar/sync-avatar.js";
|
|
18
18
|
import { syncWorkspaceIdentityToPlatform } from "../../cloud/sync-identity.js";
|
|
19
|
-
import { maybeDefaultSpeechToCloud } from "../../config/cloud-speech-defaults.js";
|
|
20
19
|
import { getCloudAssistantId } from "../../config/env.js";
|
|
21
20
|
import { getConfig } from "../../config/loader.js";
|
|
22
21
|
import { getCesClient } from "../../credential-execution/ces-runtime.js";
|
|
@@ -342,11 +341,6 @@ async function handleAddSecret({ body }: RouteHandlerArgs) {
|
|
|
342
341
|
await refreshProvidersForRotatedCredential(service, field);
|
|
343
342
|
}
|
|
344
343
|
if (field === "assistant_api_key") {
|
|
345
|
-
// Managed-speech availability needs the API key and the assistant
|
|
346
|
-
// ID. Fire on each write that completes a piece of that set; the
|
|
347
|
-
// hook no-ops until the connection is complete.
|
|
348
|
-
// Detached, must not block the response.
|
|
349
|
-
void maybeDefaultSpeechToCloud();
|
|
350
344
|
// Same last-write-wins shape: the startup syncs no-op before live
|
|
351
345
|
// registration, so re-enqueue them here. Both dedup and no-op until
|
|
352
346
|
// the client and assistant id exist.
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
import { sanitizeForTts } from "../../calls/tts-text-sanitizer.js";
|
|
11
|
-
import { managedSpeechVoices } from "../../cloud/cloud-speech.js";
|
|
12
11
|
import { getConfig } from "../../config/loader.js";
|
|
13
12
|
import { listCatalogProvidersForDisplay } from "../../tts/provider-catalog.js";
|
|
14
13
|
import {
|
|
@@ -132,17 +131,6 @@ function handleListTtsProviders() {
|
|
|
132
131
|
return { providers: listCatalogProvidersForDisplay() };
|
|
133
132
|
}
|
|
134
133
|
|
|
135
|
-
async function handleListCloudVoices() {
|
|
136
|
-
const result = await managedSpeechVoices();
|
|
137
|
-
if (!result.ok) {
|
|
138
|
-
if (result.kind === "unavailable") {
|
|
139
|
-
throw new ServiceUnavailableError(result.message);
|
|
140
|
-
}
|
|
141
|
-
throw new BadGatewayError(result.message);
|
|
142
|
-
}
|
|
143
|
-
return result.value;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
134
|
async function handleSynthesizeTts({ body }: RouteHandlerArgs) {
|
|
147
135
|
if (!body?.text || typeof body.text !== "string") {
|
|
148
136
|
throw new BadRequestError("text is required");
|
|
@@ -234,32 +222,6 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
234
222
|
}),
|
|
235
223
|
handler: handleListTtsProviders,
|
|
236
224
|
},
|
|
237
|
-
{
|
|
238
|
-
operationId: "tts_managed_voices",
|
|
239
|
-
endpoint: "tts/managed-voices",
|
|
240
|
-
method: "GET",
|
|
241
|
-
policy: {
|
|
242
|
-
requiredScopes: ["settings.read"],
|
|
243
|
-
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
244
|
-
},
|
|
245
|
-
summary: "List managed TTS voices",
|
|
246
|
-
description:
|
|
247
|
-
"Return the voices offered by Cadet Cloud TTS, fetched live from the cloud. Only currently-usable voices are returned; defaultModel is always one of them (null when none are offered).",
|
|
248
|
-
tags: ["tts"],
|
|
249
|
-
responseBody: z.object({
|
|
250
|
-
voices: z.array(
|
|
251
|
-
z.object({
|
|
252
|
-
model: z.string(),
|
|
253
|
-
label: z.string(),
|
|
254
|
-
description: z.string(),
|
|
255
|
-
sampleUrl: z.string(),
|
|
256
|
-
source: z.string(),
|
|
257
|
-
}),
|
|
258
|
-
),
|
|
259
|
-
defaultModel: z.string().nullable(),
|
|
260
|
-
}),
|
|
261
|
-
handler: handleListCloudVoices,
|
|
262
|
-
},
|
|
263
225
|
{
|
|
264
226
|
operationId: "tts_synthesize",
|
|
265
227
|
endpoint: "tts/synthesize",
|
|
@@ -3,19 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Fixture plugin directories are written under the (per-test-process)
|
|
5
5
|
* workspace plugins dir and converged against the real schedule store; the
|
|
6
|
-
* only
|
|
7
|
-
* background-wake publisher (kept out for hermeticity, mirroring
|
|
8
|
-
* schedule-store.test.ts).
|
|
6
|
+
* only stub is the notification emitter (recorded for assertions).
|
|
9
7
|
*/
|
|
10
8
|
|
|
11
9
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
12
10
|
import { dirname, join } from "node:path";
|
|
13
11
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
14
12
|
|
|
15
|
-
mock.module("../../background-wake/publisher.js", () => ({
|
|
16
|
-
refreshBackgroundWakeIntent: () => {},
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
13
|
const emittedSignals: Array<Record<string, unknown>> = [];
|
|
20
14
|
|
|
21
15
|
/** When set, merged into the next emit results (e.g. a failure reason). */
|
|
@@ -32,10 +32,6 @@ mock.module("../../daemon/process-message.js", () => ({
|
|
|
32
32
|
processMessageInBackground: async () => {},
|
|
33
33
|
}));
|
|
34
34
|
|
|
35
|
-
mock.module("../../background-wake/publisher.js", () => ({
|
|
36
|
-
refreshBackgroundWakeIntent: () => {},
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
35
|
mock.module("../../daemon/disk-pressure-background-gate.js", () => ({
|
|
40
36
|
checkDiskPressureBackgroundGate: () => ({
|
|
41
37
|
action: "allow",
|