@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.
Files changed (180) hide show
  1. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
  2. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
  3. package/node_modules/@mycadet/ces-client/src/http-credentials.ts +18 -1
  4. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
  5. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
  6. package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +0 -20
  7. package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
  8. package/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
  9. package/openapi.yaml +2 -708
  10. package/package.json +2 -3
  11. package/scripts/sync-web-search-catalog.ts +1 -2
  12. package/src/__tests__/cloud-profile-guard.test.ts +41 -5
  13. package/src/__tests__/config-schema.test.ts +6 -38
  14. package/src/__tests__/conversation-queue.test.ts +45 -6
  15. package/src/__tests__/db-readiness-http-gate.test.ts +0 -6
  16. package/src/__tests__/persist-onboarding-artifacts.test.ts +0 -1
  17. package/src/__tests__/persona-resolver.test.ts +0 -51
  18. package/src/__tests__/provider-cloud-proxy-integration.test.ts +2 -110
  19. package/src/__tests__/run-due-schedules.test.ts +0 -4
  20. package/src/__tests__/schedule-store.test.ts +0 -12
  21. package/src/__tests__/stt-stream-session.test.ts +4 -4
  22. package/src/__tests__/telephony-tts-guard.test.ts +2 -50
  23. package/src/__tests__/voice-config-update.test.ts +12 -167
  24. package/src/__tests__/web-search-catalog-parity.test.ts +0 -2
  25. package/src/__tests__/workspace-migration-130-speech-mode-to-provider.test.ts +9 -7
  26. package/src/__tests__/workspace-migration-132-web-search-mode-to-provider.test.ts +6 -6
  27. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -4
  28. package/src/calls/resolve-call-tts-provider.ts +6 -25
  29. package/src/calls/telephony-credential-preflight.ts +0 -10
  30. package/src/calls/telephony-tts-capability.ts +1 -17
  31. package/src/cli/commands/tts.ts +1 -2
  32. package/src/cli/index.help.ts +0 -2
  33. package/src/cli/program.ts +0 -2
  34. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +4 -42
  35. package/src/config/schemas/__tests__/stt.test.ts +10 -14
  36. package/src/config/schemas/llm.ts +3 -5
  37. package/src/config/schemas/services.ts +5 -6
  38. package/src/config/schemas/stt.ts +1 -3
  39. package/src/config/schemas/tts.ts +1 -30
  40. package/src/daemon/daemon-readiness.ts +4 -29
  41. package/src/daemon/lifecycle.ts +0 -31
  42. package/src/daemon/providers-setup.ts +0 -8
  43. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  44. package/src/heartbeat/heartbeat-service.ts +1 -66
  45. package/src/ipc/__tests__/db-readiness-ipc-gate.test.ts +1 -9
  46. package/src/live-voice/__tests__/live-voice-credential-preflight.test.ts +0 -44
  47. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +4 -6
  48. package/src/live-voice/__tests__/live-voice-stt.test.ts +1 -1
  49. package/src/live-voice/live-voice-credential-preflight.ts +9 -33
  50. package/src/live-voice/live-voice-session.ts +10 -6
  51. package/src/live-voice/live-voice-tts.ts +1 -8
  52. package/src/plugins/defaults/memory/worker.ts +3 -3
  53. package/src/prompts/persona-resolver.ts +0 -35
  54. package/src/providers/__tests__/search-provider-catalog.test.ts +5 -8
  55. package/src/providers/__tests__/voice-error-copy.test.ts +9 -10
  56. package/src/providers/search-provider-catalog.ts +7 -39
  57. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +3 -47
  58. package/src/providers/speech-to-text/__tests__/resolve.test.ts +6 -372
  59. package/src/providers/speech-to-text/provider-catalog.ts +2 -73
  60. package/src/providers/speech-to-text/resolve.ts +3 -120
  61. package/src/runtime/http-router-types.ts +1 -3
  62. package/src/runtime/http-server.ts +1 -1
  63. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +13 -11
  64. package/src/runtime/routes/__tests__/tts-routes.test.ts +2 -6
  65. package/src/runtime/routes/conversation-query-routes.ts +0 -96
  66. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +7 -7
  67. package/src/runtime/routes/index.ts +0 -4
  68. package/src/runtime/routes/live-voice-routes.test.ts +4 -11
  69. package/src/runtime/routes/live-voice-routes.ts +8 -11
  70. package/src/runtime/routes/secret-routes.ts +0 -6
  71. package/src/runtime/routes/tts-routes.ts +0 -38
  72. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1 -7
  73. package/src/schedule/__tests__/worker-mcp-readiness.test.ts +0 -4
  74. package/src/schedule/schedule-store.ts +0 -7
  75. package/src/schedule/scheduler.ts +0 -5
  76. package/src/schedule/worker.ts +1 -1
  77. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +2 -10
  78. package/src/stt/daemon-batch-transcriber.ts +4 -38
  79. package/src/stt/types.ts +1 -9
  80. package/src/tools/network/__tests__/web-search.test.ts +3 -250
  81. package/src/tools/network/web-search.ts +4 -165
  82. package/src/tts/__tests__/provider-resolution.test.ts +0 -5
  83. package/src/tts/provider-catalog.ts +0 -4
  84. package/src/tts/types.ts +0 -1
  85. package/src/__tests__/cloud-speech-defaults.test.ts +0 -330
  86. package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1708
  87. package/src/__tests__/migration-export-http.test.ts +0 -557
  88. package/src/__tests__/migration-export-streaming.test.ts +0 -350
  89. package/src/__tests__/migration-export-to-gcs.test.ts +0 -706
  90. package/src/__tests__/migration-import-commit-http.test.ts +0 -1176
  91. package/src/__tests__/migration-import-from-gcs.test.ts +0 -534
  92. package/src/__tests__/migration-import-from-path.test.ts +0 -324
  93. package/src/__tests__/migration-import-from-url.test.ts +0 -588
  94. package/src/__tests__/migration-import-preflight-http.test.ts +0 -739
  95. package/src/__tests__/migration-jobs-status.test.ts +0 -139
  96. package/src/__tests__/migration-parity-persistence.test.ts +0 -1891
  97. package/src/__tests__/migration-transport.test.ts +0 -1089
  98. package/src/__tests__/migration-validate-http.test.ts +0 -634
  99. package/src/__tests__/migration-wizard.test.ts +0 -1395
  100. package/src/__tests__/rebind-secrets-screen.test.ts +0 -876
  101. package/src/__tests__/sanitize-config-for-transfer.test.ts +0 -259
  102. package/src/__tests__/transfer-progress-screen.test.ts +0 -1217
  103. package/src/__tests__/validation-results-screen.test.ts +0 -1130
  104. package/src/__tests__/vbundle-pax-and-symlink.test.ts +0 -147
  105. package/src/background-wake/background-wake-routes.test.ts +0 -906
  106. package/src/background-wake/cloud-client.test.ts +0 -328
  107. package/src/background-wake/cloud-client.ts +0 -196
  108. package/src/background-wake/next-wake.test.ts +0 -335
  109. package/src/background-wake/next-wake.ts +0 -188
  110. package/src/background-wake/publisher.ts +0 -93
  111. package/src/background-wake/wake-intent-hooks.test.ts +0 -234
  112. package/src/cli/commands/__tests__/backup.test.ts +0 -363
  113. package/src/cli/commands/backup.help.ts +0 -180
  114. package/src/cli/commands/backup.ts +0 -398
  115. package/src/cloud/cloud-speech.test.ts +0 -335
  116. package/src/cloud/cloud-speech.ts +0 -300
  117. package/src/config/cloud-speech-defaults.ts +0 -300
  118. package/src/config/sanitize-for-transfer.ts +0 -111
  119. package/src/live-voice/__tests__/live-voice-cloud-speech-fallback.test.ts +0 -250
  120. package/src/providers/speech-to-text/__tests__/cadet-cloud-realtime.test.ts +0 -457
  121. package/src/providers/speech-to-text/__tests__/cadet-cloud.test.ts +0 -271
  122. package/src/providers/speech-to-text/__tests__/cadet-speech-relay-connection.test.ts +0 -126
  123. package/src/providers/speech-to-text/cadet-cloud-flux-realtime.ts +0 -309
  124. package/src/providers/speech-to-text/cadet-cloud-realtime.ts +0 -315
  125. package/src/providers/speech-to-text/cadet-cloud.ts +0 -108
  126. package/src/providers/speech-to-text/cadet-speech-relay-connection.ts +0 -176
  127. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +0 -254
  128. package/src/runtime/migrations/__tests__/job-registry.test.ts +0 -346
  129. package/src/runtime/migrations/__tests__/origin-mode.test.ts +0 -62
  130. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +0 -184
  131. package/src/runtime/migrations/__tests__/staged-import-path.test.ts +0 -110
  132. package/src/runtime/migrations/__tests__/v1-test-helpers.ts +0 -112
  133. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +0 -285
  134. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +0 -126
  135. package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +0 -253
  136. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +0 -208
  137. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +0 -485
  138. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +0 -349
  139. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +0 -196
  140. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +0 -551
  141. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +0 -428
  142. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +0 -220
  143. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +0 -1823
  144. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +0 -598
  145. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +0 -463
  146. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -538
  147. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +0 -515
  148. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +0 -453
  149. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +0 -319
  150. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +0 -240
  151. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +0 -473
  152. package/src/runtime/migrations/gcs-signed-url.ts +0 -190
  153. package/src/runtime/migrations/job-registry.ts +0 -297
  154. package/src/runtime/migrations/migration-transport.ts +0 -612
  155. package/src/runtime/migrations/migration-wizard.ts +0 -747
  156. package/src/runtime/migrations/origin-mode.ts +0 -42
  157. package/src/runtime/migrations/rebind-secrets-screen.ts +0 -412
  158. package/src/runtime/migrations/staged-import-path.ts +0 -116
  159. package/src/runtime/migrations/transfer-progress-screen.ts +0 -323
  160. package/src/runtime/migrations/validation-results-screen.ts +0 -469
  161. package/src/runtime/migrations/vbundle-builder.ts +0 -1382
  162. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -405
  163. package/src/runtime/migrations/vbundle-import-policy.ts +0 -241
  164. package/src/runtime/migrations/vbundle-importer.ts +0 -826
  165. package/src/runtime/migrations/vbundle-metadata-merge.ts +0 -113
  166. package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -2844
  167. package/src/runtime/migrations/vbundle-streaming-validator.ts +0 -426
  168. package/src/runtime/migrations/vbundle-tar-stream.ts +0 -244
  169. package/src/runtime/migrations/vbundle-validator.ts +0 -724
  170. package/src/runtime/routes/__tests__/migration-cadet-metadata-reconcile.test.ts +0 -227
  171. package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +0 -54
  172. package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +0 -70
  173. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +0 -257
  174. package/src/runtime/routes/background-wake-routes.ts +0 -387
  175. package/src/runtime/routes/migration-routes.ts +0 -2584
  176. package/src/tools/network/__tests__/cloud-search-proxy.test.ts +0 -282
  177. package/src/tools/network/cloud-search-proxy.ts +0 -211
  178. package/src/tts/__tests__/cadet-provider.test.ts +0 -253
  179. package/src/tts/providers/cadet-provider.ts +0 -244
  180. package/src/types/tar-stream.d.ts +0 -66
@@ -99,7 +99,6 @@ const fakeCatalog: FakeCatalogEntry[] = [
99
99
  makeEntry("fish-audio", "synthesized-play", "pcm", true),
100
100
  makeEntry("deepgram", "synthesized-play", "pcm", false),
101
101
  makeEntry("compressed-only", "synthesized-play", "none", false),
102
- makeEntry("cadet", "synthesized-play", "pcm", false),
103
102
  ];
104
103
 
105
104
  // The catalog is also the adapter-resolution point (`getTtsProvider`), so the
@@ -192,12 +191,6 @@ mock.module("../calls/audio-store.js", () => ({
192
191
  },
193
192
  }));
194
193
 
195
- let mockCloudSpeechAvailable = true;
196
-
197
- mock.module("../cloud/cloud-speech.js", () => ({
198
- managedSpeechAvailable: async () => mockCloudSpeechAvailable,
199
- }));
200
-
201
194
  mock.module("../inbound/public-ingress-urls.js", () => ({
202
195
  ...realPublicIngressUrlsModule,
203
196
  getPublicBaseUrl: (
@@ -470,35 +463,14 @@ describe("resolveCallTtsProvider with requiresPcmAudio", () => {
470
463
  expect(result.audioFormat).toBe("mp3");
471
464
  });
472
465
 
473
- test("cloud speech stands in for a configured provider whose credential is missing", async () => {
466
+ test("a credential-less configured provider is kept", async () => {
474
467
  testConfig.services.tts.provider = "deepgram";
475
- // No deepgram key, cloud speech available: the phone speaks through
476
- // cloud speech, the same substitution live voice makes.
477
468
  registerStubProvider("deepgram", async () => {
478
- throw new Error("should not be used");
479
- });
480
- registerStubProvider("cadet", async () => {
481
469
  return { audio: Buffer.from("x"), contentType: "audio/mpeg" };
482
470
  });
483
471
 
484
472
  const result = await resolveCallTtsProvider();
485
- expect(result.provider?.id).toBe("cadet");
486
- expect(result.useSynthesizedPath).toBe(true);
487
- });
488
-
489
- test("without cloud speech a credential-less configured provider is kept", async () => {
490
- mockCloudSpeechAvailable = false;
491
- try {
492
- testConfig.services.tts.provider = "deepgram";
493
- registerStubProvider("deepgram", async () => {
494
- return { audio: Buffer.from("x"), contentType: "audio/mpeg" };
495
- });
496
-
497
- const result = await resolveCallTtsProvider();
498
- expect(result.provider?.id).toBe("deepgram");
499
- } finally {
500
- mockCloudSpeechAvailable = true;
501
- }
473
+ expect(result.provider?.id).toBe("deepgram");
502
474
  });
503
475
  });
504
476
 
@@ -793,23 +765,3 @@ describe("speakSystemPrompt aborted synthesis", () => {
793
765
  ]);
794
766
  });
795
767
  });
796
-
797
- describe("cadet managed playability", () => {
798
- test("playable only when the cloud identity is fully provisioned", async () => {
799
- // The stored secret resolves, but availability decides.
800
- storedKeys["cadet"] = "stored";
801
- mockCloudSpeechAvailable = true;
802
- const playable = await evaluateTelephonyTtsPlayability("cadet");
803
- expect(playable.status).toBe("playable");
804
- });
805
-
806
- test("half-connected platform (secret without identity) is not playable", async () => {
807
- storedKeys["cadet"] = "stored";
808
- mockCloudSpeechAvailable = false;
809
- const playable = await evaluateTelephonyTtsPlayability("cadet");
810
- expect(playable).toMatchObject({
811
- status: "not-playable",
812
- reason: "missing-cloud-connection",
813
- });
814
- });
815
- });
@@ -1,15 +1,6 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
4
-
5
- import * as actualCloudSpeech from "../cloud/cloud-speech.js";
6
-
7
- let mockCloudSpeechAvailable = false;
8
-
9
- mock.module("../cloud/cloud-speech.js", () => ({
10
- ...actualCloudSpeech,
11
- managedSpeechAvailable: async () => mockCloudSpeechAvailable,
12
- }));
3
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
13
4
 
14
5
  // ---------------------------------------------------------------------------
15
6
  // Mocks — declared before imports that depend on platform/logger
@@ -71,7 +62,6 @@ beforeEach(() => {
71
62
  ensureTestDir();
72
63
  writeConfig({});
73
64
  invalidateConfigCache();
74
- mockCloudSpeechAvailable = false;
75
65
  });
76
66
 
77
67
  afterEach(() => {
@@ -144,18 +134,6 @@ describe("voice_config_update — tts_provider", () => {
144
134
  expect(tts).not.toHaveProperty("mode");
145
135
  });
146
136
 
147
- test("tts_provider cadet persists when a cloud connection is available", async () => {
148
- mockCloudSpeechAvailable = true;
149
-
150
- const result = await run(
151
- { setting: "tts_provider", value: "cadet" },
152
- makeContext(),
153
- );
154
-
155
- expect(result.isError).toBe(false);
156
- expect((readConfig().services as any).tts.provider).toBe("cadet");
157
- });
158
-
159
137
  test("broadcasts ttsProvider to client", async () => {
160
138
  const messages: any[] = [];
161
139
  const ctx = makeContext({
@@ -220,130 +198,36 @@ describe("voice_config_update — tts_voice_id", () => {
220
198
  // Tests: tts_voice_id targets the active provider's voice field
221
199
  // ---------------------------------------------------------------------------
222
200
 
223
- describe("voice_config_update — tts_voice_id (managed/cadet active)", () => {
224
- function makeCadetActive(): void {
225
- writeConfig({ services: { tts: { provider: "cadet" } } });
201
+ describe("voice_config_update — tts_voice_id", () => {
202
+ test("writes the voice id and broadcasts the ttsVoiceId key", async () => {
203
+ writeConfig({ services: { tts: { provider: "elevenlabs" } } });
226
204
  invalidateConfigCache();
227
- }
228
-
229
- test("writes a Deepgram Aura model id to services.tts.providers.cadet.model", async () => {
230
- makeCadetActive();
231
-
232
- const result = await run(
233
- { setting: "tts_voice_id", value: "aura-2-zeus-en" },
234
- makeContext(),
235
- );
236
-
237
- expect(result.isError).toBe(false);
238
- const config = readConfig();
239
- expect((config.services as any)?.tts?.providers?.cadet?.model).toBe(
240
- "aura-2-zeus-en",
241
- );
242
- // The ElevenLabs field is never touched when cadet is active.
243
- expect(
244
- (config.services as any)?.tts?.providers?.elevenlabs?.voiceId,
245
- ).toBeUndefined();
246
- });
247
-
248
- test("accepts an ElevenLabs voice id as a cloud model (managed serves ElevenLabs voices)", async () => {
249
- makeCadetActive();
250
-
251
- const result = await run(
252
- { setting: "tts_voice_id", value: "EXAVITQu4vr4xnSDxMaL" },
253
- makeContext(),
254
- );
255
-
256
- expect(result.isError).toBe(false);
257
- expect((readConfig().services as any)?.tts?.providers?.cadet?.model).toBe(
258
- "EXAVITQu4vr4xnSDxMaL",
259
- );
260
- });
261
-
262
- test("does not broadcast the ElevenLabs ttsVoiceId key for a cloud voice", async () => {
263
- makeCadetActive();
264
205
  const messages: any[] = [];
265
206
  const ctx = makeContext({
266
207
  sendToClient: (msg: any) => messages.push(msg),
267
208
  });
268
209
 
269
- const result = await run(
270
- { setting: "tts_voice_id", value: "aura-2-zeus-en" },
271
- ctx,
272
- );
273
-
274
- expect(result.isError).toBe(false);
275
- expect(messages).toHaveLength(0);
276
- expect(result.content).not.toContain("broadcast");
277
- });
278
-
279
- test("hints the inline picker for the next cloud voice change", async () => {
280
- makeCadetActive();
281
-
282
- const result = await run(
283
- { setting: "tts_voice_id", value: "aura-2-zeus-en" },
284
- makeContext(),
285
- );
210
+ const result = await run({ setting: "tts_voice_id", value: "abc123" }, ctx);
286
211
 
287
212
  expect(result.isError).toBe(false);
288
- expect(result.content).toStartWith("Cadet Cloud voice updated to");
289
- expect(result.content).toContain(
290
- 'ui_show { surface_type: "voice_picker", data: {} }',
291
- );
213
+ expect(messages).toHaveLength(1);
214
+ expect(messages[0].key).toBe("ttsVoiceId");
215
+ expect(messages[0].value).toBe("abc123");
292
216
  });
293
217
 
294
- test("does not hint the picker when the voice is not managed", async () => {
218
+ test("rejects a value with characters invalid for any voice id", async () => {
295
219
  writeConfig({ services: { tts: { provider: "elevenlabs" } } });
296
220
  invalidateConfigCache();
297
221
 
298
- const result = await run(
299
- { setting: "tts_voice_id", value: "abc123" },
300
- makeContext(),
301
- );
302
-
303
- expect(result.isError).toBe(false);
304
- expect(result.content).not.toContain("voice_picker");
305
- });
306
-
307
- test("does not hint the picker for a non-voice setting", async () => {
308
- makeCadetActive();
309
-
310
- const result = await run(
311
- { setting: "conversation_timeout", value: 30 },
312
- makeContext(),
313
- );
314
-
315
- expect(result.isError).toBe(false);
316
- expect(result.content).not.toContain("voice_picker");
317
- });
318
-
319
- test("rejects a value with characters invalid for any voice id", async () => {
320
- makeCadetActive();
321
-
322
222
  const result = await run(
323
223
  { setting: "tts_voice_id", value: "a british man" },
324
224
  makeContext(),
325
225
  );
326
226
 
327
227
  expect(result.isError).toBe(true);
328
- expect((readConfig().services as any)?.tts?.providers?.cadet?.model).toBe(
329
- undefined,
330
- );
331
- });
332
-
333
- test("broadcasts the ElevenLabs ttsVoiceId key when elevenlabs is active", async () => {
334
- writeConfig({ services: { tts: { provider: "elevenlabs" } } });
335
- invalidateConfigCache();
336
- const messages: any[] = [];
337
- const ctx = makeContext({
338
- sendToClient: (msg: any) => messages.push(msg),
339
- });
340
-
341
- const result = await run({ setting: "tts_voice_id", value: "abc123" }, ctx);
342
-
343
- expect(result.isError).toBe(false);
344
- expect(messages).toHaveLength(1);
345
- expect(messages[0].key).toBe("ttsVoiceId");
346
- expect(messages[0].value).toBe("abc123");
228
+ expect(
229
+ (readConfig().services as any)?.tts?.providers?.elevenlabs?.voiceId,
230
+ ).toBeUndefined();
347
231
  });
348
232
  });
349
233
 
@@ -536,31 +420,6 @@ describe("voice_config_update — stt_provider", () => {
536
420
  );
537
421
  });
538
422
 
539
- test("persists the cadet provider when a cloud connection is available", async () => {
540
- mockCloudSpeechAvailable = true;
541
-
542
- const result = await run(
543
- { setting: "stt_provider", value: "cadet" },
544
- makeContext(),
545
- );
546
-
547
- expect(result.isError).toBe(false);
548
- expect((readConfig().services as any)?.stt?.provider).toBe("cadet");
549
- });
550
-
551
- test("rejects the cadet provider without a cloud connection", async () => {
552
- mockCloudSpeechAvailable = false;
553
-
554
- const result = await run(
555
- { setting: "stt_provider", value: "cadet" },
556
- makeContext(),
557
- );
558
-
559
- expect(result.isError).toBe(true);
560
- expect(result.content).toContain("assistant cloud connect");
561
- expect((readConfig().services as any)?.stt?.provider).toBeUndefined();
562
- });
563
-
564
423
  test("stt_model persists under the active provider", async () => {
565
424
  // Flux stays reachable from this surface after ceasing to be a provider
566
425
  // id: it is a model family on whichever provider is active.
@@ -916,22 +775,8 @@ describe("voice_config_update - stt_language", () => {
916
775
  // ---------------------------------------------------------------------------
917
776
 
918
777
  describe("voice_config_update — catalog-driven provider validation", () => {
919
- test("rejects tts_provider cadet without a cloud connection", async () => {
920
- mockCloudSpeechAvailable = false;
921
-
922
- const result = await run(
923
- { setting: "tts_provider", value: "cadet" },
924
- makeContext(),
925
- );
926
-
927
- expect(result.isError).toBe(true);
928
- expect(result.content).toContain("assistant cloud connect");
929
- expect((readConfig().services as any)?.tts?.provider).toBeUndefined();
930
- });
931
-
932
778
  test("accepts every provider ID in the catalog", async () => {
933
779
  // The catalog includes cadet, which is gated on a cloud connection.
934
- mockCloudSpeechAvailable = true;
935
780
  const catalogIds = listCatalogProviderIds();
936
781
  expect(catalogIds.length).toBeGreaterThanOrEqual(1);
937
782
 
@@ -23,7 +23,6 @@ interface JsonCatalogEntry {
23
23
  id: string;
24
24
  displayName: string;
25
25
  displayNameLong?: string;
26
- kind: "managed" | "byok";
27
26
  apiKeyPrefix?: string;
28
27
  envVar?: string;
29
28
  secretKey?: string;
@@ -61,7 +60,6 @@ function entryToPlain(
61
60
  if (entry.displayNameLong !== undefined) {
62
61
  out.displayNameLong = entry.displayNameLong;
63
62
  }
64
- out.kind = entry.kind;
65
63
  if (entry.apiKeyPrefix !== undefined) {
66
64
  out.apiKeyPrefix = entry.apiKeyPrefix;
67
65
  }
@@ -9,7 +9,6 @@ import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
11
 
12
- import { AssistantConfigSchema } from "../config/schema.js";
13
12
  import { speechModeToProviderMigration } from "../workspace/migrations/130-speech-mode-to-provider.js";
14
13
  import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
15
14
  import { getLastWorkspaceMigrationId } from "../workspace/migrations/runner.js";
@@ -146,9 +145,10 @@ describe("130-speech-mode-to-provider", () => {
146
145
  expect(() => speechModeToProviderMigration.run(workspaceDir)).not.toThrow();
147
146
  });
148
147
 
149
- // The migrated shape is what the daemon actually parses, so a cloud user
150
- // must land on cadet rather than falling back to a keyless BYOK provider.
151
- test("migrated output parses to the cadet provider", () => {
148
+ // The migrated shape is frozen (the migration never reruns), asserted raw:
149
+ // the current config schema no longer knows the cadet provider id, so a
150
+ // schema parse of a migrated cloud config is no longer a valid invariant.
151
+ test("migrated output folds managed mode into the cadet provider", () => {
152
152
  writeConfig({
153
153
  services: {
154
154
  stt: { mode: "managed", provider: "deepgram", providers: {} },
@@ -157,10 +157,12 @@ describe("130-speech-mode-to-provider", () => {
157
157
  });
158
158
 
159
159
  speechModeToProviderMigration.run(workspaceDir);
160
- const parsed = AssistantConfigSchema.parse(readConfig());
161
160
 
162
- expect(parsed.services.stt.provider).toBe("cadet");
163
- expect(parsed.services.tts.provider).toBe("cadet");
161
+ expect(services(readConfig()).stt).toEqual({
162
+ provider: "cadet",
163
+ providers: {},
164
+ });
165
+ expect(services(readConfig()).tts).toEqual({ provider: "cadet" });
164
166
  });
165
167
 
166
168
  describe("down", () => {
@@ -9,7 +9,6 @@ import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
11
 
12
- import { AssistantConfigSchema } from "../config/schema.js";
13
12
  import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
14
13
  import { getLastWorkspaceMigrationId } from "../workspace/migrations/runner.js";
15
14
 
@@ -163,17 +162,18 @@ describe("132-web-search-mode-to-provider", () => {
163
162
  ).not.toThrow();
164
163
  });
165
164
 
166
- // The migrated shape is what the daemon actually parses, so a cloud user
167
- // must land on cadet rather than falling back to a keyless BYOK provider.
168
- test("migrated output parses to the cadet provider", () => {
165
+ // The migrated shape is frozen (the migration never reruns), asserted raw:
166
+ // the current config schema no longer knows the cadet provider id, so a
167
+ // schema parse of a migrated cloud config is no longer a valid invariant —
168
+ // the config loader's recovery ladder owns any on-disk 'cadet' values.
169
+ test("migrated output folds managed mode into the cadet provider", () => {
169
170
  writeConfig({
170
171
  services: { "web-search": { mode: "managed", provider: "brave" } },
171
172
  });
172
173
 
173
174
  webSearchModeToProviderMigration.run(workspaceDir);
174
- const parsed = AssistantConfigSchema.parse(readConfig());
175
175
 
176
- expect(parsed.services["web-search"].provider).toBe("cadet");
176
+ expect(webSearch(readConfig())).toEqual({ provider: "cadet" });
177
177
  });
178
178
 
179
179
  describe("down", () => {
@@ -1091,16 +1091,13 @@ describe("default call protocol numbered rules", () => {
1091
1091
 
1092
1092
  test("a monolingual listening language becomes the pre-speech fallback", () => {
1093
1093
  // An assistant pinned to services.stt.language = "es" on a provider that
1094
- // honors the pin (deepgram, cadet) is already transcribing Spanish, so
1094
+ // honors the pin (deepgram) is already transcribing Spanish, so
1095
1095
  // the opener must not default to English. The default test config runs
1096
1096
  // the auto-detect branch ("multi"), so the pinned branch is covered at
1097
1097
  // the fragment level.
1098
1098
  expect(preSpeechLanguageRuleFragment("es", "deepgram")).toContain(
1099
1099
  'configured listening language ("es")',
1100
1100
  );
1101
- expect(preSpeechLanguageRuleFragment("es", "cadet")).toContain(
1102
- "default to English only when neither gives a language",
1103
- );
1104
1101
  for (const autoDetect of ["multi", "", " ", undefined]) {
1105
1102
  expect(preSpeechLanguageRuleFragment(autoDetect, "deepgram")).toBe(
1106
1103
  "use the language the Task context implies, if any; otherwise default to English",
@@ -7,7 +7,6 @@
7
7
  * native-vs-synthesized strategy decision stay in one implementation.
8
8
  */
9
9
 
10
- import { resolveEffectiveSpeechProviders } from "../config/cloud-speech-defaults.js";
11
10
  import { getConfig } from "../config/loader.js";
12
11
  import {
13
12
  getCatalogProvider,
@@ -74,13 +73,6 @@ export interface ResolveCallTtsOptions {
74
73
  /**
75
74
  * Resolve the active TTS provider via the global provider abstraction.
76
75
  *
77
- * The provider is the effective one after cloud-speech defaulting
78
- * ({@link resolveEffectiveSpeechProviders}): a configured BYOK provider
79
- * whose credential does not resolve is stood in for by cloud speech
80
- * while the cloud connection is usable, the same substitution live
81
- * voice makes, so a fresh Cadet connection speaks on the phone with zero
82
- * TTS configuration.
83
- *
84
76
  * The native-vs-synthesized decision is driven by the catalog's
85
77
  * `callMode` field via {@link resolveCallStrategy}. Providers with
86
78
  * `callMode: "synthesized-play"` have their audio streamed through the
@@ -185,25 +177,14 @@ export async function resolveCallTtsProvider(
185
177
  }
186
178
 
187
179
  /**
188
- * The TTS provider the call actually uses after cloud-speech defaulting.
189
- * A failure inside the cloud lookup (platform client, credential store)
190
- * keeps the configured provider: a cloud-speech hiccup must not demote a
191
- * working BYOK provider, and the outer resolver's own fallback is the
192
- * silent native path.
180
+ * The TTS provider the call actually uses — the configured one. Kept as a
181
+ * named seam because the outer resolver's own fallback is the silent
182
+ * native path when the config has no services.tts block.
193
183
  */
194
- async function effectiveTtsProviderId(
184
+ function effectiveTtsProviderId(
195
185
  config: ReturnType<typeof getConfig>,
196
- ): Promise<TtsProviderId | undefined> {
197
- try {
198
- const { tts } = await resolveEffectiveSpeechProviders(config);
199
- return tts;
200
- } catch (err) {
201
- log.warn(
202
- { err },
203
- "Managed-speech defaulting failed for call TTS; keeping the configured provider",
204
- );
205
- return undefined;
206
- }
186
+ ): TtsProviderId | undefined {
187
+ return config.services.tts?.provider;
207
188
  }
208
189
 
209
190
  /**
@@ -140,16 +140,6 @@ function ttsGap(
140
140
  clause: `an API key for the text-to-speech provider "${providerId}" (no fallback provider has usable credentials)`,
141
141
  };
142
142
  }
143
- case "missing-cloud-connection": {
144
- return {
145
- gap: {
146
- kind: "tts",
147
- providerId,
148
- reason: `Managed TTS has no usable Cadet Cloud connection and no playable fallback provider is available`,
149
- },
150
- clause: `a Cadet Cloud connection for cloud speech (run 'assistant cloud connect'; no fallback provider has usable credentials)`,
151
- };
152
- }
153
143
  case "missing-fish-audio-reference-id": {
154
144
  return {
155
145
  gap: {
@@ -37,8 +37,7 @@ import { resolveTtsConfig } from "../tts/tts-config-resolver.js";
37
37
  export type TelephonyTtsNotPlayableReason =
38
38
  | "unsupported-format"
39
39
  | "missing-credentials"
40
- | "missing-fish-audio-reference-id"
41
- | "missing-cloud-connection";
40
+ | "missing-fish-audio-reference-id";
42
41
 
43
42
  /**
44
43
  * Result of resolving whether a TTS provider is playable over the
@@ -113,21 +112,6 @@ export async function evaluateTelephonyTtsPlayability(
113
112
  }
114
113
  }
115
114
 
116
- // The cadet secret alone doesn't prove usability: cloud speech also
117
- // needs the cloud assistant identity, or synthesis fails before any
118
- // request is made — with allowNativeFallback false, that would mean
119
- // silent call failures instead of a BYOK fallback.
120
- if (entry.id === "cadet") {
121
- const { managedSpeechAvailable } = await import("../cloud/cloud-speech.js");
122
- if (!(await managedSpeechAvailable())) {
123
- return {
124
- status: "not-playable",
125
- providerId: entry.id,
126
- reason: "missing-cloud-connection",
127
- };
128
- }
129
- }
130
-
131
115
  if (entry.id === "fish-audio" && !fishAudioReferenceIdConfigured()) {
132
116
  return {
133
117
  status: "not-playable",
@@ -222,8 +222,7 @@ export function registerTtsCommand(program: Command): void {
222
222
  }
223
223
 
224
224
  // Read the active TTS provider so the voice lands on the field that
225
- // provider actually reads. Writing the ElevenLabs field while the
226
- // active provider is `cadet` is a silent no-op.
225
+ // provider actually reads.
227
226
  const cfg = await cliIpcCall<{
228
227
  services?: { tts?: { provider?: string } };
229
228
  }>("config_get");
@@ -16,7 +16,6 @@ import { attachmentHelp } from "./commands/attachment.help.js";
16
16
  import { auditHelp } from "./commands/audit.help.js";
17
17
  import { authHelp } from "./commands/auth.help.js";
18
18
  import { avatarHelp } from "./commands/avatar.help.js";
19
- import { backupHelp } from "./commands/backup.help.js";
20
19
  import { bashHelp } from "./commands/bash.help.js";
21
20
  import { browserHelp } from "./commands/browser.help.js";
22
21
  import { cacheHelp } from "./commands/cache.help.js";
@@ -67,7 +66,6 @@ export const CLI_COMMAND_HELP: readonly CliCommandHelp[] = [
67
66
  auditHelp,
68
67
  authHelp,
69
68
  avatarHelp,
70
- backupHelp,
71
69
  bashHelp,
72
70
  browserHelp,
73
71
  cacheHelp,
@@ -10,7 +10,6 @@ import { registerAttachmentCommand } from "./commands/attachment.js";
10
10
  import { registerAuditCommand } from "./commands/audit.js";
11
11
  import { registerAuthCommand } from "./commands/auth.js";
12
12
  import { registerAvatarCommand } from "./commands/avatar.js";
13
- import { registerBackupCommand } from "./commands/backup.js";
14
13
  import { registerBashCommand } from "./commands/bash.js";
15
14
  import { registerBrowserCommand } from "./commands/browser.js";
16
15
  import { registerCacheCommand } from "./commands/cache.js";
@@ -108,7 +107,6 @@ Examples:
108
107
  registerAuditCommand(program);
109
108
  registerAuthCommand(program);
110
109
  registerAvatarCommand(program);
111
- registerBackupCommand(program);
112
110
  registerBashCommand(program);
113
111
  registerBrowserCommand(program);
114
112
  registerCacheCommand(program);
@@ -1,13 +1,9 @@
1
- import { managedSpeechAvailable } from "../../../../cloud/cloud-speech.js";
2
1
  import { normalizeActivationKey } from "../../../../daemon/handlers/config-voice.js";
3
2
  import {
4
3
  DEEPGRAM_MULTI_LANGUAGE_CODES,
5
4
  DEEPGRAM_NOVA3_MONOLINGUAL_CODES,
6
5
  } from "../../../../providers/speech-to-text/deepgram.js";
7
- import {
8
- isCloudSttProvider,
9
- listProviderModelFamilies,
10
- } from "../../../../providers/speech-to-text/provider-catalog.js";
6
+ import { listProviderModelFamilies } from "../../../../providers/speech-to-text/provider-catalog.js";
11
7
  import type { SttProviderId } from "../../../../stt/types.js";
12
8
  import { throwIfCancelled } from "../../../../tools/shared/abort.js";
13
9
  import type {
@@ -28,7 +24,6 @@ import {
28
24
  normalizeSttProviderId,
29
25
  VALID_STT_PROVIDERS,
30
26
  } from "../../../schemas/stt.js";
31
- import { voicePickerHint } from "./shared.js";
32
27
 
33
28
  /**
34
29
  * Valid voice config settings and their UserDefaults key mappings.
@@ -199,19 +194,6 @@ const STT_LANGUAGE_ALIASES: Record<
199
194
  "code-switching": "multi",
200
195
  };
201
196
 
202
- /**
203
- * Same steer `navigate_settings_tab` puts on the Voice tab, applied to the
204
- * strongest competing affordance: this tool's description coaches the model to
205
- * read the cloud catalog and set `tts_voice_id` itself, which is exactly the
206
- * "assistant names voices in prose" outcome the inline picker exists to
207
- * replace. Managed only, because that is the catalog the picker renders. The
208
- * write still happens and the result stays non-error; this only redirects the
209
- * next request.
210
- */
211
- const MANAGED_VOICE_PICKER_HINT = voicePickerHint(
212
- "which lets them hear and pick from the cloud catalog in the conversation. Set the id here only when the user named a specific voice.",
213
- );
214
-
215
197
  function validateSetting(
216
198
  setting: string,
217
199
  value: unknown,
@@ -469,24 +451,8 @@ export async function run(
469
451
  return { content: `Error: ${validation.error}`, isError: true };
470
452
  }
471
453
 
472
- // Cloud speech has more than one STT id, so the gate reads the catalog
473
- // rather than a literal: without this, setting a cloud provider other
474
- // than "cadet" on a disconnected assistant persists happily and then
475
- // resolves no transcriber at all.
476
- const wantsCloudSpeech =
477
- (setting === "stt_provider" &&
478
- isCloudSttProvider(validation.coerced as SttProviderId)) ||
479
- (setting === "tts_provider" && validation.coerced === "cadet");
480
- if (wantsCloudSpeech && !(await managedSpeechAvailable())) {
481
- return {
482
- content:
483
- "Error: cloud speech requires a Cadet Cloud connection. Run 'assistant cloud connect' first.",
484
- isError: true,
485
- };
486
- }
487
-
488
- // Recheck: the cloud-speech reachability probe above is an await, and the
489
- // client broadcast and config write below are what the user would notice.
454
+ // Recheck: the reachability probe above is an await, and the client
455
+ // broadcast and config write below are what the user would notice.
490
456
  throwIfCancelled(context);
491
457
 
492
458
  const meta: VoiceSettingMeta = VOICE_SETTINGS[setting as VoiceSettingName];
@@ -618,14 +584,10 @@ export async function run(
618
584
  AUTO_DETECT_STT_PROVIDERS.has(activeSttProviderId)
619
585
  ? ` Note: the configured STT provider (${activeSttProviderId}) auto-detects the spoken language natively and ignores this setting.`
620
586
  : "";
621
- const pickerNote =
622
- setting === "tts_voice_id" && activeTtsProviderId === "cadet"
623
- ? ` ${MANAGED_VOICE_PICKER_HINT}`
624
- : "";
625
587
  return {
626
588
  content: `${friendlyName} updated to ${JSON.stringify(
627
589
  validation.coerced,
628
- )}.${broadcastNote}${autoDetectNote}${pickerNote}`,
590
+ )}.${broadcastNote}${autoDetectNote}`,
629
591
  isError: false,
630
592
  };
631
593
  }