@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.
Files changed (176) hide show
  1. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
  2. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
  3. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
  4. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
  5. package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +0 -20
  6. package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
  7. package/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
  8. package/openapi.yaml +2 -708
  9. package/package.json +2 -3
  10. package/scripts/sync-web-search-catalog.ts +1 -2
  11. package/src/__tests__/cloud-profile-guard.test.ts +2 -2
  12. package/src/__tests__/config-schema.test.ts +6 -38
  13. package/src/__tests__/conversation-queue.test.ts +45 -6
  14. package/src/__tests__/db-readiness-http-gate.test.ts +0 -6
  15. package/src/__tests__/persist-onboarding-artifacts.test.ts +0 -1
  16. package/src/__tests__/persona-resolver.test.ts +0 -51
  17. package/src/__tests__/provider-cloud-proxy-integration.test.ts +2 -110
  18. package/src/__tests__/run-due-schedules.test.ts +0 -4
  19. package/src/__tests__/schedule-store.test.ts +0 -12
  20. package/src/__tests__/stt-stream-session.test.ts +4 -4
  21. package/src/__tests__/telephony-tts-guard.test.ts +2 -50
  22. package/src/__tests__/voice-config-update.test.ts +12 -167
  23. package/src/__tests__/web-search-catalog-parity.test.ts +0 -2
  24. package/src/__tests__/workspace-migration-130-speech-mode-to-provider.test.ts +9 -7
  25. package/src/__tests__/workspace-migration-132-web-search-mode-to-provider.test.ts +6 -6
  26. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -4
  27. package/src/calls/resolve-call-tts-provider.ts +6 -25
  28. package/src/calls/telephony-credential-preflight.ts +0 -10
  29. package/src/calls/telephony-tts-capability.ts +1 -17
  30. package/src/cli/commands/tts.ts +1 -2
  31. package/src/cli/index.help.ts +0 -2
  32. package/src/cli/program.ts +0 -2
  33. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +4 -42
  34. package/src/config/schemas/__tests__/stt.test.ts +10 -14
  35. package/src/config/schemas/services.ts +5 -6
  36. package/src/config/schemas/stt.ts +1 -3
  37. package/src/config/schemas/tts.ts +1 -30
  38. package/src/daemon/daemon-readiness.ts +4 -29
  39. package/src/daemon/lifecycle.ts +0 -31
  40. package/src/daemon/providers-setup.ts +0 -8
  41. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  42. package/src/heartbeat/heartbeat-service.ts +1 -66
  43. package/src/ipc/__tests__/db-readiness-ipc-gate.test.ts +1 -9
  44. package/src/live-voice/__tests__/live-voice-credential-preflight.test.ts +0 -44
  45. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +4 -6
  46. package/src/live-voice/__tests__/live-voice-stt.test.ts +1 -1
  47. package/src/live-voice/live-voice-credential-preflight.ts +9 -33
  48. package/src/live-voice/live-voice-session.ts +10 -6
  49. package/src/live-voice/live-voice-tts.ts +1 -8
  50. package/src/plugins/defaults/memory/worker.ts +3 -3
  51. package/src/prompts/persona-resolver.ts +0 -35
  52. package/src/providers/__tests__/search-provider-catalog.test.ts +5 -8
  53. package/src/providers/__tests__/voice-error-copy.test.ts +9 -10
  54. package/src/providers/search-provider-catalog.ts +7 -39
  55. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +3 -47
  56. package/src/providers/speech-to-text/__tests__/resolve.test.ts +6 -372
  57. package/src/providers/speech-to-text/provider-catalog.ts +2 -73
  58. package/src/providers/speech-to-text/resolve.ts +3 -120
  59. package/src/runtime/http-router-types.ts +1 -3
  60. package/src/runtime/http-server.ts +1 -1
  61. package/src/runtime/routes/__tests__/tts-routes.test.ts +2 -6
  62. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +7 -7
  63. package/src/runtime/routes/index.ts +0 -4
  64. package/src/runtime/routes/live-voice-routes.test.ts +4 -11
  65. package/src/runtime/routes/live-voice-routes.ts +8 -11
  66. package/src/runtime/routes/secret-routes.ts +0 -6
  67. package/src/runtime/routes/tts-routes.ts +0 -38
  68. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1 -7
  69. package/src/schedule/__tests__/worker-mcp-readiness.test.ts +0 -4
  70. package/src/schedule/schedule-store.ts +0 -7
  71. package/src/schedule/scheduler.ts +0 -5
  72. package/src/schedule/worker.ts +1 -1
  73. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +2 -10
  74. package/src/stt/daemon-batch-transcriber.ts +4 -38
  75. package/src/stt/types.ts +1 -9
  76. package/src/tools/network/__tests__/web-search.test.ts +3 -250
  77. package/src/tools/network/web-search.ts +4 -165
  78. package/src/tts/__tests__/provider-resolution.test.ts +0 -5
  79. package/src/tts/provider-catalog.ts +0 -4
  80. package/src/tts/types.ts +0 -1
  81. package/src/__tests__/cloud-speech-defaults.test.ts +0 -330
  82. package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1708
  83. package/src/__tests__/migration-export-http.test.ts +0 -557
  84. package/src/__tests__/migration-export-streaming.test.ts +0 -350
  85. package/src/__tests__/migration-export-to-gcs.test.ts +0 -706
  86. package/src/__tests__/migration-import-commit-http.test.ts +0 -1176
  87. package/src/__tests__/migration-import-from-gcs.test.ts +0 -534
  88. package/src/__tests__/migration-import-from-path.test.ts +0 -324
  89. package/src/__tests__/migration-import-from-url.test.ts +0 -588
  90. package/src/__tests__/migration-import-preflight-http.test.ts +0 -739
  91. package/src/__tests__/migration-jobs-status.test.ts +0 -139
  92. package/src/__tests__/migration-parity-persistence.test.ts +0 -1891
  93. package/src/__tests__/migration-transport.test.ts +0 -1089
  94. package/src/__tests__/migration-validate-http.test.ts +0 -634
  95. package/src/__tests__/migration-wizard.test.ts +0 -1395
  96. package/src/__tests__/rebind-secrets-screen.test.ts +0 -876
  97. package/src/__tests__/sanitize-config-for-transfer.test.ts +0 -259
  98. package/src/__tests__/transfer-progress-screen.test.ts +0 -1217
  99. package/src/__tests__/validation-results-screen.test.ts +0 -1130
  100. package/src/__tests__/vbundle-pax-and-symlink.test.ts +0 -147
  101. package/src/background-wake/background-wake-routes.test.ts +0 -906
  102. package/src/background-wake/cloud-client.test.ts +0 -328
  103. package/src/background-wake/cloud-client.ts +0 -196
  104. package/src/background-wake/next-wake.test.ts +0 -335
  105. package/src/background-wake/next-wake.ts +0 -188
  106. package/src/background-wake/publisher.ts +0 -93
  107. package/src/background-wake/wake-intent-hooks.test.ts +0 -234
  108. package/src/cli/commands/__tests__/backup.test.ts +0 -363
  109. package/src/cli/commands/backup.help.ts +0 -180
  110. package/src/cli/commands/backup.ts +0 -398
  111. package/src/cloud/cloud-speech.test.ts +0 -335
  112. package/src/cloud/cloud-speech.ts +0 -300
  113. package/src/config/cloud-speech-defaults.ts +0 -300
  114. package/src/config/sanitize-for-transfer.ts +0 -111
  115. package/src/live-voice/__tests__/live-voice-cloud-speech-fallback.test.ts +0 -250
  116. package/src/providers/speech-to-text/__tests__/cadet-cloud-realtime.test.ts +0 -457
  117. package/src/providers/speech-to-text/__tests__/cadet-cloud.test.ts +0 -271
  118. package/src/providers/speech-to-text/__tests__/cadet-speech-relay-connection.test.ts +0 -126
  119. package/src/providers/speech-to-text/cadet-cloud-flux-realtime.ts +0 -309
  120. package/src/providers/speech-to-text/cadet-cloud-realtime.ts +0 -315
  121. package/src/providers/speech-to-text/cadet-cloud.ts +0 -108
  122. package/src/providers/speech-to-text/cadet-speech-relay-connection.ts +0 -176
  123. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +0 -254
  124. package/src/runtime/migrations/__tests__/job-registry.test.ts +0 -346
  125. package/src/runtime/migrations/__tests__/origin-mode.test.ts +0 -62
  126. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +0 -184
  127. package/src/runtime/migrations/__tests__/staged-import-path.test.ts +0 -110
  128. package/src/runtime/migrations/__tests__/v1-test-helpers.ts +0 -112
  129. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +0 -285
  130. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +0 -126
  131. package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +0 -253
  132. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +0 -208
  133. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +0 -485
  134. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +0 -349
  135. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +0 -196
  136. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +0 -551
  137. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +0 -428
  138. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +0 -220
  139. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +0 -1823
  140. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +0 -598
  141. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +0 -463
  142. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -538
  143. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +0 -515
  144. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +0 -453
  145. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +0 -319
  146. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +0 -240
  147. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +0 -473
  148. package/src/runtime/migrations/gcs-signed-url.ts +0 -190
  149. package/src/runtime/migrations/job-registry.ts +0 -297
  150. package/src/runtime/migrations/migration-transport.ts +0 -612
  151. package/src/runtime/migrations/migration-wizard.ts +0 -747
  152. package/src/runtime/migrations/origin-mode.ts +0 -42
  153. package/src/runtime/migrations/rebind-secrets-screen.ts +0 -412
  154. package/src/runtime/migrations/staged-import-path.ts +0 -116
  155. package/src/runtime/migrations/transfer-progress-screen.ts +0 -323
  156. package/src/runtime/migrations/validation-results-screen.ts +0 -469
  157. package/src/runtime/migrations/vbundle-builder.ts +0 -1382
  158. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -405
  159. package/src/runtime/migrations/vbundle-import-policy.ts +0 -241
  160. package/src/runtime/migrations/vbundle-importer.ts +0 -826
  161. package/src/runtime/migrations/vbundle-metadata-merge.ts +0 -113
  162. package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -2844
  163. package/src/runtime/migrations/vbundle-streaming-validator.ts +0 -426
  164. package/src/runtime/migrations/vbundle-tar-stream.ts +0 -244
  165. package/src/runtime/migrations/vbundle-validator.ts +0 -724
  166. package/src/runtime/routes/__tests__/migration-cadet-metadata-reconcile.test.ts +0 -227
  167. package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +0 -54
  168. package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +0 -70
  169. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +0 -257
  170. package/src/runtime/routes/background-wake-routes.ts +0 -387
  171. package/src/runtime/routes/migration-routes.ts +0 -2584
  172. package/src/tools/network/__tests__/cloud-search-proxy.test.ts +0 -282
  173. package/src/tools/network/cloud-search-proxy.ts +0 -211
  174. package/src/tts/__tests__/cadet-provider.test.ts +0 -253
  175. package/src/tts/providers/cadet-provider.ts +0 -244
  176. 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
- }