@mycadet/assistant 0.12.11 → 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 +2 -2
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +0 -20
- package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- 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 +2 -2
- 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/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__/tts-routes.test.ts +2 -6
- 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
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
let mockSynthesizeResult: unknown;
|
|
4
|
-
let synthesizeCalls: Array<{
|
|
5
|
-
text: string;
|
|
6
|
-
format: string;
|
|
7
|
-
signal?: AbortSignal;
|
|
8
|
-
}> = [];
|
|
9
|
-
|
|
10
|
-
mock.module("../../cloud/cloud-speech.js", () => ({
|
|
11
|
-
managedSpeechSynthesize: async (input: {
|
|
12
|
-
text: string;
|
|
13
|
-
format: string;
|
|
14
|
-
signal?: AbortSignal;
|
|
15
|
-
}) => {
|
|
16
|
-
synthesizeCalls.push(input);
|
|
17
|
-
return mockSynthesizeResult;
|
|
18
|
-
},
|
|
19
|
-
}));
|
|
20
|
-
|
|
21
|
-
let mockRelayConnection: {
|
|
22
|
-
wsBaseUrl: string;
|
|
23
|
-
httpBaseUrl: string;
|
|
24
|
-
mintServiceToken: () => string;
|
|
25
|
-
} | null = null;
|
|
26
|
-
|
|
27
|
-
mock.module(
|
|
28
|
-
"../../providers/speech-to-text/cadet-speech-relay-connection.js",
|
|
29
|
-
() => ({
|
|
30
|
-
resolveSpeechRelayConnection: async () => mockRelayConnection,
|
|
31
|
-
mapTunnelError: (e: { code: string }) => ({
|
|
32
|
-
category: "provider-error",
|
|
33
|
-
message: `mapped:${e.code}`,
|
|
34
|
-
}),
|
|
35
|
-
probeTunnelRejection: async () => mockProbeRejection,
|
|
36
|
-
}),
|
|
37
|
-
);
|
|
38
|
-
let mockProbeRejection: { code: string; detail?: string } | null = null;
|
|
39
|
-
|
|
40
|
-
let socketCalls: Array<{ url: string; text: string }> = [];
|
|
41
|
-
let mockSocketResult: () => Promise<Buffer> = async () => Buffer.from("pcm");
|
|
42
|
-
|
|
43
|
-
mock.module("../providers/cadet-tts-socket.js", () => ({
|
|
44
|
-
synthesizeOverCadetTtsSocket: async (opts: {
|
|
45
|
-
url: string;
|
|
46
|
-
text: string;
|
|
47
|
-
onChunk?: (c: Uint8Array) => void;
|
|
48
|
-
}) => {
|
|
49
|
-
socketCalls.push({ url: opts.url, text: opts.text });
|
|
50
|
-
const audio = await mockSocketResult();
|
|
51
|
-
opts.onChunk?.(audio);
|
|
52
|
-
return audio;
|
|
53
|
-
},
|
|
54
|
-
}));
|
|
55
|
-
|
|
56
|
-
import {
|
|
57
|
-
cadetTtsProviderDefinition,
|
|
58
|
-
createCadetProvider,
|
|
59
|
-
} from "../providers/cadet-provider.js";
|
|
60
|
-
import type { TtsSynthesisRequest } from "../types.js";
|
|
61
|
-
|
|
62
|
-
const MP3_BYTES = Buffer.from([0x49, 0x44, 0x33]);
|
|
63
|
-
|
|
64
|
-
function request(
|
|
65
|
-
overrides: Partial<TtsSynthesisRequest> = {},
|
|
66
|
-
): TtsSynthesisRequest {
|
|
67
|
-
return {
|
|
68
|
-
text: "hello",
|
|
69
|
-
useCase: "message-playback",
|
|
70
|
-
...overrides,
|
|
71
|
-
} as TtsSynthesisRequest;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
describe("cadet TTS adapter", () => {
|
|
75
|
-
beforeEach(() => {
|
|
76
|
-
synthesizeCalls = [];
|
|
77
|
-
socketCalls = [];
|
|
78
|
-
mockProbeRejection = null;
|
|
79
|
-
mockSocketResult = async () => Buffer.from("pcm");
|
|
80
|
-
mockRelayConnection = {
|
|
81
|
-
wsBaseUrl: "ws://gateway.test",
|
|
82
|
-
httpBaseUrl: "http://gateway.test",
|
|
83
|
-
mintServiceToken: () => "tok-1",
|
|
84
|
-
};
|
|
85
|
-
mockSynthesizeResult = {
|
|
86
|
-
ok: true,
|
|
87
|
-
value: { audio: MP3_BYTES, contentType: "audio/mpeg" },
|
|
88
|
-
};
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("message playback synthesizes mp3 and round-trips audio", async () => {
|
|
92
|
-
const provider = createCadetProvider();
|
|
93
|
-
const result = await provider.synthesize(request());
|
|
94
|
-
|
|
95
|
-
expect(synthesizeCalls).toEqual([{ text: "hello", format: "mp3" }]);
|
|
96
|
-
expect(Buffer.compare(result.audio, MP3_BYTES)).toBe(0);
|
|
97
|
-
expect(result.contentType).toBe("audio/mpeg");
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("PCM hint maps to the cloud's fixed 16 kHz PCM format", async () => {
|
|
101
|
-
const provider = createCadetProvider();
|
|
102
|
-
await provider.synthesize(request({ outputFormat: "pcm" }));
|
|
103
|
-
|
|
104
|
-
expect(synthesizeCalls[0].format).toBe("pcm_16000");
|
|
105
|
-
expect(
|
|
106
|
-
provider.resolveOutputSampleRateHz?.(request({ outputFormat: "pcm" })),
|
|
107
|
-
).toBe(16000);
|
|
108
|
-
// Contract: undefined for non-PCM output.
|
|
109
|
-
expect(provider.resolveOutputSampleRateHz?.(request())).toBeUndefined();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test("forwards the abort signal to the cloud call", async () => {
|
|
113
|
-
const controller = new AbortController();
|
|
114
|
-
const provider = createCadetProvider();
|
|
115
|
-
await provider.synthesize(request({ signal: controller.signal }));
|
|
116
|
-
|
|
117
|
-
expect(synthesizeCalls[0].signal).toBe(controller.signal);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test("insufficient balance throws a top-up message", async () => {
|
|
121
|
-
mockSynthesizeResult = {
|
|
122
|
-
ok: false,
|
|
123
|
-
kind: "platform-error",
|
|
124
|
-
status: 402,
|
|
125
|
-
code: "insufficient_balance",
|
|
126
|
-
message: "balance",
|
|
127
|
-
};
|
|
128
|
-
const provider = createCadetProvider();
|
|
129
|
-
await expect(provider.synthesize(request())).rejects.toThrow(
|
|
130
|
-
/Cadet credits/,
|
|
131
|
-
);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test("unavailable platform throws a connect-your-account message", async () => {
|
|
135
|
-
mockSynthesizeResult = {
|
|
136
|
-
ok: false,
|
|
137
|
-
kind: "unavailable",
|
|
138
|
-
message: "No Cadet Cloud connection.",
|
|
139
|
-
};
|
|
140
|
-
const provider = createCadetProvider();
|
|
141
|
-
await expect(provider.synthesize(request())).rejects.toThrow(
|
|
142
|
-
/cloud connect/,
|
|
143
|
-
);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
describe("cadet TTS streaming", () => {
|
|
148
|
-
beforeEach(() => {
|
|
149
|
-
synthesizeCalls = [];
|
|
150
|
-
socketCalls = [];
|
|
151
|
-
mockProbeRejection = null;
|
|
152
|
-
mockSocketResult = async () => Buffer.from("pcm");
|
|
153
|
-
mockRelayConnection = {
|
|
154
|
-
wsBaseUrl: "ws://gateway.test",
|
|
155
|
-
httpBaseUrl: "http://gateway.test",
|
|
156
|
-
mintServiceToken: () => "tok-1",
|
|
157
|
-
};
|
|
158
|
-
mockSynthesizeResult = {
|
|
159
|
-
ok: true,
|
|
160
|
-
value: { audio: MP3_BYTES, contentType: "audio/mpeg" },
|
|
161
|
-
};
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test("PCM streaming dials the gateway relay with token and audio params", async () => {
|
|
165
|
-
const provider = createCadetProvider();
|
|
166
|
-
const chunks: Uint8Array[] = [];
|
|
167
|
-
|
|
168
|
-
const result = await provider.synthesizeStream!(
|
|
169
|
-
request({ outputFormat: "pcm" }),
|
|
170
|
-
(c) => chunks.push(c),
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
expect(socketCalls).toHaveLength(1);
|
|
174
|
-
const url = new URL(socketCalls[0]!.url);
|
|
175
|
-
expect(url.origin).toBe("ws://gateway.test");
|
|
176
|
-
expect(url.pathname).toBe("/v1/speech/tts/stream");
|
|
177
|
-
expect(url.searchParams.get("key")).toBe("tok-1");
|
|
178
|
-
expect(url.searchParams.get("encoding")).toBe("linear16");
|
|
179
|
-
expect(url.searchParams.get("sample_rate")).toBe("16000");
|
|
180
|
-
// Raw PCM, not Deepgram's default WAV container — RIFF headers would
|
|
181
|
-
// play as static on the headerless media-stream path.
|
|
182
|
-
expect(url.searchParams.get("container")).toBe("none");
|
|
183
|
-
expect(result.contentType).toBe("audio/pcm");
|
|
184
|
-
expect(chunks).toHaveLength(1);
|
|
185
|
-
// Batch path untouched.
|
|
186
|
-
expect(synthesizeCalls).toHaveLength(0);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test("non-PCM streaming delegates to the batch path with one chunk", async () => {
|
|
190
|
-
const provider = createCadetProvider();
|
|
191
|
-
const chunks: Uint8Array[] = [];
|
|
192
|
-
|
|
193
|
-
const result = await provider.synthesizeStream!(request(), (c) =>
|
|
194
|
-
chunks.push(c),
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
expect(socketCalls).toHaveLength(0);
|
|
198
|
-
expect(synthesizeCalls).toEqual([{ text: "hello", format: "mp3" }]);
|
|
199
|
-
expect(result.contentType).toBe("audio/mpeg");
|
|
200
|
-
expect(chunks).toHaveLength(1);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test("a rejected dial surfaces the relay's mapped rejection via the probe", async () => {
|
|
204
|
-
mockSocketResult = async () => {
|
|
205
|
-
throw new Error("socket closed before Flushed");
|
|
206
|
-
};
|
|
207
|
-
mockProbeRejection = { code: "insufficient_balance" };
|
|
208
|
-
const provider = createCadetProvider();
|
|
209
|
-
|
|
210
|
-
await expect(
|
|
211
|
-
provider.synthesizeStream!(request({ outputFormat: "pcm" }), () => {}),
|
|
212
|
-
).rejects.toThrow("mapped:insufficient_balance");
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
test("no relay connection throws a connect-your-account message", async () => {
|
|
216
|
-
mockRelayConnection = null;
|
|
217
|
-
const provider = createCadetProvider();
|
|
218
|
-
|
|
219
|
-
await expect(
|
|
220
|
-
provider.synthesizeStream!(request({ outputFormat: "pcm" }), () => {}),
|
|
221
|
-
).rejects.toThrow(/cloud connect/);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
test("streaming capability is advertised", () => {
|
|
225
|
-
expect(createCadetProvider().capabilities.supportsStreaming).toBe(true);
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
describe("cadet TTS definition", () => {
|
|
230
|
-
test("declares streaming synthesized-play with PCM media-stream playback", () => {
|
|
231
|
-
expect(cadetTtsProviderDefinition.capabilities.supportsStreaming).toBe(
|
|
232
|
-
true,
|
|
233
|
-
);
|
|
234
|
-
expect(cadetTtsProviderDefinition.callMode).toBe("synthesized-play");
|
|
235
|
-
expect(cadetTtsProviderDefinition.allowNativeFallback).toBe(false);
|
|
236
|
-
expect(cadetTtsProviderDefinition.mediaStreamPlayback.outputFormat).toBe(
|
|
237
|
-
"pcm",
|
|
238
|
-
);
|
|
239
|
-
// True since cloud voice selection: clients gate their voice dropdown
|
|
240
|
-
// on this flag (old daemons report false and show no selector).
|
|
241
|
-
expect(cadetTtsProviderDefinition.supportsVoiceSelection).toBe(true);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
test("requires only the cloud connection credential", () => {
|
|
245
|
-
expect(cadetTtsProviderDefinition.secretRequirements).toEqual([
|
|
246
|
-
{
|
|
247
|
-
credentialStoreKey: "credential/cadet/assistant_api_key",
|
|
248
|
-
displayName: "Cadet account connection",
|
|
249
|
-
setCommand: "assistant cloud connect",
|
|
250
|
-
},
|
|
251
|
-
]);
|
|
252
|
-
});
|
|
253
|
-
});
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cadet Cloud TTS: synthesis through the cloud's cloud speech
|
|
3
|
-
* surfaces (Cadet-held Deepgram key, billed to Cadet credits). No provider
|
|
4
|
-
* API key exists on this machine — the cloud connection is the credential.
|
|
5
|
-
*
|
|
6
|
-
* Two paths:
|
|
7
|
-
* - Batch `synthesize` (read-aloud, mp3 surfaces) via the Django endpoint.
|
|
8
|
-
* - Streaming `synthesizeStream` (live voice, telephony PCM) via the
|
|
9
|
-
* gateway's speech relay (gateway → tunnel → Deepgram; tunnel contact is
|
|
10
|
-
* gateway-only — the daemon holds no tunnel address and no speech
|
|
11
|
-
* credential).
|
|
12
|
-
*
|
|
13
|
-
* `allowNativeFallback: false` means synthesis failures propagate to the
|
|
14
|
-
* caller's error handler, so failures here throw with user-actionable
|
|
15
|
-
* messages rather than returning silence.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
type CloudSpeechResult,
|
|
20
|
-
type CloudSpeechTtsFormat,
|
|
21
|
-
managedSpeechSynthesize,
|
|
22
|
-
} from "../../cloud/cloud-speech.js";
|
|
23
|
-
import { getConfig } from "../../config/loader.js";
|
|
24
|
-
import {
|
|
25
|
-
mapTunnelError,
|
|
26
|
-
probeTunnelRejection,
|
|
27
|
-
resolveSpeechRelayConnection,
|
|
28
|
-
} from "../../providers/speech-to-text/cadet-speech-relay-connection.js";
|
|
29
|
-
import { getLogger } from "../../util/logger.js";
|
|
30
|
-
import type { TtsProviderDefinition } from "../provider-definition.js";
|
|
31
|
-
import type {
|
|
32
|
-
TtsProvider,
|
|
33
|
-
TtsProviderCapabilities,
|
|
34
|
-
TtsSynthesisRequest,
|
|
35
|
-
TtsSynthesisResult,
|
|
36
|
-
} from "../types.js";
|
|
37
|
-
import { synthesizeOverCadetTtsSocket } from "./cadet-tts-socket.js";
|
|
38
|
-
|
|
39
|
-
const log = getLogger("cadet-tts");
|
|
40
|
-
|
|
41
|
-
const TTS_STREAM_PATH = "/v1/speech/tts/stream";
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* The cloud's PCM format is fixed at 16 kHz, 16-bit LE mono — the
|
|
45
|
-
* media-stream transcoder needs to know the rate regardless of hints.
|
|
46
|
-
*/
|
|
47
|
-
const CADET_PCM_SAMPLE_RATE_HZ = 16_000;
|
|
48
|
-
|
|
49
|
-
type CloudSpeechFailure = Extract<CloudSpeechResult<never>, { ok: false }>;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Map the synthesis request onto the cloud's format vocabulary.
|
|
53
|
-
*
|
|
54
|
-
* The PCM hint (media-stream playback) maps to raw 16 kHz PCM for mu-law
|
|
55
|
-
* transcoding; every other use case gets mp3.
|
|
56
|
-
*/
|
|
57
|
-
function resolveManagedFormat(
|
|
58
|
-
request: TtsSynthesisRequest,
|
|
59
|
-
): CloudSpeechTtsFormat {
|
|
60
|
-
return request.outputFormat === "pcm" ? "pcm_16000" : "mp3";
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* The configured cloud voice, if any. A request-level voiceId wins over
|
|
65
|
-
* config; undefined means the cloud's default voice.
|
|
66
|
-
*/
|
|
67
|
-
function resolveCloudVoice(request: TtsSynthesisRequest): string | undefined {
|
|
68
|
-
return request.voiceId ?? getConfig().services.tts.providers.cadet.model;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function synthesisError(failure: CloudSpeechFailure): Error {
|
|
72
|
-
if (failure.code === "insufficient_balance") {
|
|
73
|
-
return new Error(
|
|
74
|
-
"Cadet credits are exhausted — add funds to your Cadet account to continue using cloud speech.",
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
if (failure.kind === "unavailable") {
|
|
78
|
-
return new Error(
|
|
79
|
-
`Cloud speech is unavailable: ${failure.message} Run 'assistant cloud connect' to connect your Cadet account.`,
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
return new Error(failure.message);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function performSynthesis(
|
|
86
|
-
request: TtsSynthesisRequest,
|
|
87
|
-
): Promise<TtsSynthesisResult> {
|
|
88
|
-
const result = await managedSpeechSynthesize({
|
|
89
|
-
text: request.text,
|
|
90
|
-
format: resolveManagedFormat(request),
|
|
91
|
-
model: resolveCloudVoice(request),
|
|
92
|
-
signal: request.signal,
|
|
93
|
-
});
|
|
94
|
-
if (!result.ok) {
|
|
95
|
-
throw synthesisError(result);
|
|
96
|
-
}
|
|
97
|
-
return {
|
|
98
|
-
audio: result.value.audio,
|
|
99
|
-
contentType: result.value.contentType,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Stream one PCM utterance through the gateway's speech relay. Non-PCM
|
|
105
|
-
* requests delegate to the batch path — streaming exists for realtime PCM
|
|
106
|
-
* consumers (live voice, telephony), and the relay's format is pinned to
|
|
107
|
-
* linear16.
|
|
108
|
-
*/
|
|
109
|
-
async function performStreamingSynthesis(
|
|
110
|
-
request: TtsSynthesisRequest,
|
|
111
|
-
onChunk: (chunk: Uint8Array) => void,
|
|
112
|
-
): Promise<TtsSynthesisResult> {
|
|
113
|
-
if (request.outputFormat !== "pcm") {
|
|
114
|
-
const result = await performSynthesis(request);
|
|
115
|
-
onChunk(result.audio);
|
|
116
|
-
return result;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const connection = await resolveSpeechRelayConnection();
|
|
120
|
-
if (!connection) {
|
|
121
|
-
throw new Error(
|
|
122
|
-
"Cloud speech is unavailable: the assistant cannot reach its speech relay. Run 'assistant cloud connect' to connect your Cadet account.",
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const params = new URLSearchParams({
|
|
127
|
-
key: connection.mintServiceToken(),
|
|
128
|
-
encoding: "linear16",
|
|
129
|
-
sample_rate: String(CADET_PCM_SAMPLE_RATE_HZ),
|
|
130
|
-
// Deepgram defaults linear16 to a WAV container; the media-stream path
|
|
131
|
-
// consumes headerless PCM, so RIFF header bytes would play as static.
|
|
132
|
-
container: "none",
|
|
133
|
-
});
|
|
134
|
-
const voice = resolveCloudVoice(request);
|
|
135
|
-
if (voice) {
|
|
136
|
-
params.set("model", voice);
|
|
137
|
-
}
|
|
138
|
-
const url = `${connection.wsBaseUrl}${TTS_STREAM_PATH}?${params.toString()}`;
|
|
139
|
-
|
|
140
|
-
log.info(
|
|
141
|
-
{ textLength: request.text.length },
|
|
142
|
-
"Starting managed streaming TTS synthesis",
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
const audio = await synthesizeOverCadetTtsSocket({
|
|
147
|
-
url,
|
|
148
|
-
text: request.text,
|
|
149
|
-
onChunk,
|
|
150
|
-
signal: request.signal,
|
|
151
|
-
makeTimeoutError: (timeoutMs) =>
|
|
152
|
-
new Error(`Managed streaming TTS timed out after ${timeoutMs}ms`),
|
|
153
|
-
makeStreamError: (detail) =>
|
|
154
|
-
new Error(`Managed streaming TTS failed: ${detail}`),
|
|
155
|
-
makeEmptyError: () =>
|
|
156
|
-
new Error("Managed streaming TTS returned no audio"),
|
|
157
|
-
makeRelayError: (code, detail) =>
|
|
158
|
-
new Error(mapTunnelError({ code, detail }).message),
|
|
159
|
-
});
|
|
160
|
-
return {
|
|
161
|
-
audio,
|
|
162
|
-
contentType: "audio/pcm",
|
|
163
|
-
};
|
|
164
|
-
} catch (err) {
|
|
165
|
-
if (request.signal?.aborted) {
|
|
166
|
-
throw err;
|
|
167
|
-
}
|
|
168
|
-
// A rejected upgrade hides the relay's {code, detail}; replay the gate
|
|
169
|
-
// as a plain GET (the gateway probes tunnel's own gate too) so auth and
|
|
170
|
-
// balance failures surface with their mapped messages. The probe must
|
|
171
|
-
// carry the same voice model — a model-specific rejection (e.g.
|
|
172
|
-
// missing_price) only reproduces when the probe asks for that model.
|
|
173
|
-
const probeParams = new URLSearchParams({
|
|
174
|
-
key: connection.mintServiceToken(),
|
|
175
|
-
});
|
|
176
|
-
if (voice) {
|
|
177
|
-
probeParams.set("model", voice);
|
|
178
|
-
}
|
|
179
|
-
const rejection = await probeTunnelRejection(
|
|
180
|
-
`${connection.httpBaseUrl}${TTS_STREAM_PATH}?${probeParams.toString()}`,
|
|
181
|
-
);
|
|
182
|
-
if (rejection) {
|
|
183
|
-
throw new Error(mapTunnelError(rejection).message);
|
|
184
|
-
}
|
|
185
|
-
throw err;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function createCadetProvider(): TtsProvider {
|
|
190
|
-
const capabilities: TtsProviderCapabilities = {
|
|
191
|
-
supportsStreaming: true,
|
|
192
|
-
supportedFormats: ["mp3", "pcm"],
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
id: "cadet",
|
|
197
|
-
capabilities,
|
|
198
|
-
// Per the TtsProvider contract this returns undefined for non-PCM
|
|
199
|
-
// output; the cloud's PCM format is pinned to 16 kHz.
|
|
200
|
-
resolveOutputSampleRateHz: (request) =>
|
|
201
|
-
request.outputFormat === "pcm" ? CADET_PCM_SAMPLE_RATE_HZ : undefined,
|
|
202
|
-
synthesize: performSynthesis,
|
|
203
|
-
synthesizeStream: performStreamingSynthesis,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* The complete Cadet Cloud provider definition — catalog metadata plus
|
|
209
|
-
* the runtime adapter — assembled into the canonical catalog by
|
|
210
|
-
* `provider-catalog.ts`.
|
|
211
|
-
*/
|
|
212
|
-
export const cadetTtsProviderDefinition: TtsProviderDefinition = {
|
|
213
|
-
id: "cadet",
|
|
214
|
-
displayName: "Cadet",
|
|
215
|
-
subtitle:
|
|
216
|
-
"Text-to-speech through your Cadet account — billed to Cadet credits, no separate API key needed.",
|
|
217
|
-
// Advertises cloud voice selection (`services.tts.providers.cadet.model`)
|
|
218
|
-
// to clients; web gates its voice dropdown on this flag, so daemons that
|
|
219
|
-
// predate the field never show a selector whose writes they would ignore.
|
|
220
|
-
supportsVoiceSelection: true,
|
|
221
|
-
apiKeyPlaceholder: "Connected via your Cadet account",
|
|
222
|
-
credentialsGuide: {
|
|
223
|
-
description:
|
|
224
|
-
"Connect this assistant to your Cadet account; cloud speech uses that connection instead of a provider API key.",
|
|
225
|
-
url: "https://cloud.mycadet.ai/",
|
|
226
|
-
linkLabel: "Open Cadet Cloud",
|
|
227
|
-
},
|
|
228
|
-
callMode: "synthesized-play",
|
|
229
|
-
allowNativeFallback: false,
|
|
230
|
-
capabilities: {
|
|
231
|
-
supportsStreaming: true,
|
|
232
|
-
supportedFormats: ["mp3", "pcm"],
|
|
233
|
-
},
|
|
234
|
-
// The adapter honours the PCM hint via the cloud's pcm_16000 format.
|
|
235
|
-
mediaStreamPlayback: { outputFormat: "pcm" },
|
|
236
|
-
secretRequirements: [
|
|
237
|
-
{
|
|
238
|
-
credentialStoreKey: "credential/cadet/assistant_api_key",
|
|
239
|
-
displayName: "Cadet account connection",
|
|
240
|
-
setCommand: "assistant cloud connect",
|
|
241
|
-
},
|
|
242
|
-
],
|
|
243
|
-
adapter: createCadetProvider(),
|
|
244
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal ambient types for `tar-stream` v3, which ships no type definitions.
|
|
3
|
-
*
|
|
4
|
-
* We only declare the surface used by `runtime/migrations/vbundle-tar-stream.ts`
|
|
5
|
-
* (the `extract()` factory and its `"entry"` event shape). If additional usage
|
|
6
|
-
* is needed later, extend this file rather than adding `@types/tar-stream` —
|
|
7
|
-
* the DT types target v2 and do not match v3's streamx-based extractor.
|
|
8
|
-
*/
|
|
9
|
-
declare module "tar-stream" {
|
|
10
|
-
import type { Readable, Writable } from "node:stream";
|
|
11
|
-
|
|
12
|
-
export interface TarHeader {
|
|
13
|
-
name: string;
|
|
14
|
-
size: number;
|
|
15
|
-
type:
|
|
16
|
-
| "file"
|
|
17
|
-
| "link"
|
|
18
|
-
| "symlink"
|
|
19
|
-
| "character-device"
|
|
20
|
-
| "block-device"
|
|
21
|
-
| "directory"
|
|
22
|
-
| "fifo"
|
|
23
|
-
| "contiguous-file"
|
|
24
|
-
| "pax-header"
|
|
25
|
-
| "pax-global-header"
|
|
26
|
-
| "gnu-long-path"
|
|
27
|
-
| "gnu-long-link-path"
|
|
28
|
-
| "other"
|
|
29
|
-
| string;
|
|
30
|
-
linkname?: string | null;
|
|
31
|
-
mode?: number;
|
|
32
|
-
uid?: number;
|
|
33
|
-
gid?: number;
|
|
34
|
-
mtime?: Date;
|
|
35
|
-
pax?: Record<string, string> | null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface ExtractOptions {
|
|
39
|
-
filenameEncoding?: string;
|
|
40
|
-
allowUnknownFormat?: boolean;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export type EntryCallback = (err?: Error | null) => void;
|
|
44
|
-
|
|
45
|
-
export interface Extract extends Writable {
|
|
46
|
-
on(
|
|
47
|
-
event: "entry",
|
|
48
|
-
listener: (
|
|
49
|
-
header: TarHeader,
|
|
50
|
-
stream: Readable,
|
|
51
|
-
next: EntryCallback,
|
|
52
|
-
) => void,
|
|
53
|
-
): this;
|
|
54
|
-
on(event: "finish", listener: () => void): this;
|
|
55
|
-
on(event: "error", listener: (err: Error) => void): this;
|
|
56
|
-
on(event: "close", listener: () => void): this;
|
|
57
|
-
on(event: string, listener: (...args: unknown[]) => void): this;
|
|
58
|
-
destroy(err?: Error): void;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function extract(opts?: ExtractOptions): Extract;
|
|
62
|
-
|
|
63
|
-
// `pack` exists but is unused by the runtime migrations code; declare it
|
|
64
|
-
// permissively so importers that reach for it still type-check.
|
|
65
|
-
export function pack(opts?: unknown): Writable;
|
|
66
|
-
}
|