@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
@@ -18,7 +18,6 @@ export const VALID_STT_PROVIDERS = [
18
18
  "google-gemini",
19
19
  "openai-whisper",
20
20
  "xai",
21
- "cadet",
22
21
  ] as const;
23
22
 
24
23
  /**
@@ -183,8 +182,7 @@ export { STT_ROLE_REQUIREMENTS, STT_ROLES, type SttRole };
183
182
  /**
184
183
  * Canonical STT service configuration.
185
184
  *
186
- * `provider` is the only axis: `"cadet"` transcribes through the cloud,
187
- * billed to Cadet credits; any other provider uses the user's own API key.
185
+ * `provider` is the only axis: every provider uses the user's own API key.
188
186
  */
189
187
  export const SttServiceSchema = z
190
188
  .object({
@@ -271,31 +271,6 @@ export const TtsXaiProviderConfigSchema = z
271
271
 
272
272
  export type TtsXaiProviderConfig = z.infer<typeof TtsXaiProviderConfigSchema>;
273
273
 
274
- /**
275
- * Cadet Cloud provider configuration under `services.tts.providers`.
276
- */
277
- const TtsCadetProviderConfigSchema = z
278
- .object({
279
- model: z
280
- .string()
281
- .optional()
282
- .describe(
283
- "Managed TTS voice model (e.g. aura-2-thalia-en). Unset means the cloud's default voice. The cloud rejects voices it does not offer.",
284
- ),
285
- languageVoices: languageVoicesSchema(
286
- "cadet",
287
- "values are managed TTS voice models (e.g. aura-2-thalia-en) and must " +
288
- "be voices offered by the cloud rate card: an unpriced model is " +
289
- "rejected by the relay (missing_price), the same contract as " +
290
- "services.tts.providers.cadet.model.",
291
- ),
292
- })
293
- .describe("Cadet Cloud provider configuration under services.tts");
294
-
295
- export type TtsCadetProviderConfig = z.infer<
296
- typeof TtsCadetProviderConfigSchema
297
- >;
298
-
299
274
  const TtsProvidersSchema = z.object({
300
275
  elevenlabs: TtsElevenLabsProviderConfigSchema.default(
301
276
  TtsElevenLabsProviderConfigSchema.parse({}),
@@ -307,9 +282,6 @@ const TtsProvidersSchema = z.object({
307
282
  TtsDeepgramProviderConfigSchema.parse({}),
308
283
  ),
309
284
  xai: TtsXaiProviderConfigSchema.default(TtsXaiProviderConfigSchema.parse({})),
310
- cadet: TtsCadetProviderConfigSchema.default(
311
- TtsCadetProviderConfigSchema.parse({}),
312
- ),
313
285
  });
314
286
  export type TtsProviders = z.infer<typeof TtsProvidersSchema>;
315
287
 
@@ -344,8 +316,7 @@ for (const id of schemaKeys) {
344
316
  /**
345
317
  * Canonical TTS service configuration.
346
318
  *
347
- * `provider` is the only axis: `"cadet"` synthesizes through the cloud,
348
- * billed to Cadet credits; any other provider uses the user's own API key.
319
+ * `provider` is the only axis: every provider uses the user's own API key.
349
320
  */
350
321
  export const TtsServiceSchema = z
351
322
  .object({
@@ -64,32 +64,13 @@ export const DB_MIGRATION_READINESS_EXEMPT_OPERATIONS: ReadonlySet<string> =
64
64
  /**
65
65
  * The migration-repair surface: operations additionally allowed while DB
66
66
  * migrations are in the terminal FAILED state — and only then, never while
67
- * they are still running. Rolling back migrations and importing a backup are
68
- * exactly the remedies for a failed migration (the upgrade CLI's
69
- * rollback/restore path), so gating them in that state would make recovery
70
- * impossible. Contains both the HTTP endpoint form and the IPC operationId
67
+ * they are still running. Rollback repairs a failed migration without racing
68
+ * an active migration runner. Contains both the HTTP endpoint form and the IPC operationId
71
69
  * form of each route. Bypasses only the migration gate — route policies
72
70
  * (gateway-principal scopes, guardian auth) still apply.
73
71
  */
74
72
  export const DB_MIGRATION_FAILED_STATE_EXEMPT_OPERATIONS: ReadonlySet<string> =
75
- new Set([
76
- "admin/rollback-migrations",
77
- "admin_rollbackmigrations_post",
78
- "migrations/import",
79
- "migrations_import_post",
80
- // Restore has three transports; all must stay repair-capable. The
81
- // preflights are read-only analysis; the GCS import is the cloud
82
- // platform's restore path and is job-based, so its job-status GET (a
83
- // parameterized route, matched by prefix in isDbMigrationGateBypassed)
84
- // must answer too.
85
- "migrations/import-preflight",
86
- "migrations_importpreflight_post",
87
- "migrations/import-from-gcs",
88
- "migrations_importfromgcs_post",
89
- "migrations/preflight-from-gcs",
90
- "migrations_preflightfromgcs_post",
91
- "migrations_jobs_by_job_id_get",
92
- ]);
73
+ new Set(["admin/rollback-migrations", "admin_rollbackmigrations_post"]);
93
74
 
94
75
  /**
95
76
  * Whether an operation may proceed despite unready DB migrations: always for
@@ -103,13 +84,7 @@ export function isDbMigrationGateBypassed(operation: string): boolean {
103
84
  if (dbMigrationReadiness.state !== "failed") {
104
85
  return false;
105
86
  }
106
- // The HTTP gate passes the raw path after /v1/, so the parameterized
107
- // job-status route (migrations/jobs/:job_id) is matched by prefix; the
108
- // IPC form is the exact operationId in the set above.
109
- return (
110
- DB_MIGRATION_FAILED_STATE_EXEMPT_OPERATIONS.has(operation) ||
111
- operation.startsWith("migrations/jobs/")
112
- );
87
+ return DB_MIGRATION_FAILED_STATE_EXEMPT_OPERATIONS.has(operation);
113
88
  }
114
89
 
115
90
  export function setDbReady(v: boolean): void {
@@ -49,7 +49,6 @@ import {
49
49
  startRuntimeHttpServerBackgroundSweeps,
50
50
  } from "../runtime/http-server.js";
51
51
  import { warmLocalGuardianPrincipalCache } from "../runtime/local-actor-identity.js";
52
- import { recoverInterruptedImport } from "../runtime/migrations/vbundle-streaming-importer.js";
53
52
  import { markCurrentProcessAsMainDaemon } from "../runtime/process-role.js";
54
53
  import { publishConfigChanged } from "../runtime/sync/resource-sync-events.js";
55
54
  import { reconcilePluginSchedules } from "../schedule/plugin-schedule-reconciler.js";
@@ -148,36 +147,6 @@ export async function runDaemon(): Promise<void> {
148
147
  // after it.
149
148
  recordDaemonBootTime(startupStartedAt);
150
149
 
151
- // Recover from any streaming `.vbundle` import that was interrupted by a
152
- // crash or SIGKILL. If the previous process died between
153
- // `carryOverPreservedPaths` and the atomic workspace swap, the live
154
- // workspace may be missing `data/db` / `data/qdrant` / etc. The marker
155
- // at `<workspaceDir>.import-marker.json` (persisted before any rename
156
- // runs) tells us where the orphaned preserved paths landed; the
157
- // recovery helper moves them back into the live workspace and cleans
158
- // up the temp tree. Running this BEFORE `initializeDb()` ensures the
159
- // DB singleton opens against the fully-restored `assistant.db`.
160
- try {
161
- const recoveryResult = await recoverInterruptedImport(getWorkspaceDir());
162
- if (!recoveryResult.ok) {
163
- // Rollback is intentionally unresolved — backup/temp/marker are
164
- // preserved on disk so an operator (or a later retry) can finish
165
- // the recovery. Log loudly so ops sees it, but don't block start-up:
166
- // the daemon still needs to come up for diagnostics. The next
167
- // `streamCommitImport` will refuse to start a new import until the
168
- // marker is resolved.
169
- log.error(
170
- { failedCount: recoveryResult.failedCount },
171
- "Interrupted-import recovery is INCOMPLETE; leftover .pre-import-* / .import-* scratch dirs remain in the workspace. Manual intervention may be required before the next import can run.",
172
- );
173
- }
174
- } catch (err) {
175
- log.warn(
176
- { err },
177
- "recoverInterruptedImport threw during daemon startup; continuing",
178
- );
179
- }
180
-
181
150
  // Load (or generate + persist) the auth signing key so tokens survive
182
151
  // daemon restarts.
183
152
  const signingKey = resolveSigningKey();
@@ -1,5 +1,4 @@
1
1
  import { ensureCloudIdentityIds } from "../config/cloud-identity.js";
2
- import { maybeDefaultSpeechToCloud } from "../config/cloud-speech-defaults.js";
3
2
  import type { AssistantConfig } from "../config/types.js";
4
3
  import { getMcpServerManager } from "../mcp/manager.js";
5
4
  import { startConfiguredMcpServers } from "../mcp/startup.js";
@@ -34,13 +33,6 @@ export async function initializeProvidersAndTools(
34
33
  // overrides are normally only set by the secret-routes handlers at runtime.
35
34
  await ensureCloudIdentityIds();
36
35
 
37
- // Speech defaulting normally fires when the cloud credentials land
38
- // (secret-routes); evaluating it once per boot also covers assistants whose
39
- // connection predates that trigger, so voice works for every connected
40
- // assistant with no configured speech credentials. Idempotent, and detached
41
- // per the startup philosophy — must not block boot.
42
- void maybeDefaultSpeechToCloud();
43
-
44
36
  await initializeProviders(config);
45
37
  // initializeTools() also loads workspace tool overrides from
46
38
  // `<workspaceDir>/tools/` once core tools have settled, so they own
@@ -58,7 +58,6 @@ mock.module("../../prompts/persona-resolver.js", () => ({
58
58
  resolveGuardianPersona: () => null,
59
59
  resolveGuardianPersonaPath: () => null,
60
60
  resolveGuardianPersonaStrict: () => null,
61
- isGuardianPersonaCustomized: () => false,
62
61
  resolveUserSlug: () => null,
63
62
  resolveUserPersona: () => null,
64
63
  resolveChannelPersona: () => null,
@@ -124,16 +124,6 @@ function recordReengagementTimestamp(): void {
124
124
  }
125
125
  }
126
126
 
127
- function refreshBackgroundWakeIntentSoon(reason: string): void {
128
- void import("../background-wake/publisher.js")
129
- .then(({ refreshBackgroundWakeIntent }) =>
130
- refreshBackgroundWakeIntent(reason),
131
- )
132
- .catch((err) =>
133
- log.warn({ err, reason }, "Failed to queue background wake refresh"),
134
- );
135
- }
136
-
137
127
  export interface HeartbeatDeps {
138
128
  /** Override for current hour (0-23), for testing. Used when no timezone is resolved. */
139
129
  getCurrentHour?: () => number;
@@ -141,19 +131,6 @@ export interface HeartbeatDeps {
141
131
  now?: () => Date;
142
132
  }
143
133
 
144
- export interface ManagedWakeHeartbeatRunOptions {
145
- now?: number;
146
- toleranceMs?: number;
147
- assumeDue?: boolean;
148
- scheduledFor?: number;
149
- }
150
-
151
- export interface ManagedWakeHeartbeatRunResult {
152
- due: boolean;
153
- completed: number;
154
- skipped: number;
155
- }
156
-
157
134
  export class HeartbeatService {
158
135
  private static instance?: HeartbeatService;
159
136
 
@@ -236,47 +213,12 @@ export class HeartbeatService {
236
213
  return countCompletedRunsToday() >= config.maxDailyRuns;
237
214
  }
238
215
 
239
- async runCloudWakeIfDue(
240
- options: ManagedWakeHeartbeatRunOptions = {},
241
- ): Promise<ManagedWakeHeartbeatRunResult> {
242
- const now = options.now ?? Date.now();
243
- const toleranceMs = options.toleranceMs ?? 0;
244
- const dueByLocalTimer =
245
- this._nextRunAt != null && this._nextRunAt <= now + toleranceMs;
246
-
247
- if (!dueByLocalTimer && options.assumeDue !== true) {
248
- return { due: false, completed: 0, skipped: 0 };
249
- }
250
-
251
- if (!dueByLocalTimer) {
252
- if (this._pendingRunId) {
253
- supersedePendingRun(this._pendingRunId);
254
- this._pendingRunId = null;
255
- }
256
- this._nextRunAt = options.scheduledFor ?? now;
257
- this._pendingRunId = insertPendingHeartbeatRun(this._nextRunAt);
258
- }
259
-
260
- const completed = await this.runOnce({ force: false });
261
-
262
- if (this.cronMode && !this.stopped) {
263
- this.scheduleNextCronRun(getConfig().heartbeat);
264
- }
265
-
266
- return {
267
- due: true,
268
- completed: completed ? 1 : 0,
269
- skipped: completed ? 0 : 1,
270
- };
271
- }
272
-
273
216
  start(): void {
274
217
  this.stopped = false;
275
218
  const config = getConfig().heartbeat;
276
219
  if (!config.enabled) {
277
220
  log.info("Heartbeat disabled by config");
278
221
  this._nextRunAt = null;
279
- refreshBackgroundWakeIntentSoon("heartbeat-disabled");
280
222
  return;
281
223
  }
282
224
  if (this.timer) {
@@ -405,7 +347,6 @@ export class HeartbeatService {
405
347
  { nextRunAt: new Date(nextRunAt).toISOString(), delayMs },
406
348
  "Heartbeat cron run scheduled",
407
349
  );
408
- refreshBackgroundWakeIntentSoon("heartbeat-cron-scheduled");
409
350
  } catch (err) {
410
351
  log.warn(
411
352
  { err },
@@ -432,7 +373,6 @@ export class HeartbeatService {
432
373
  this._nextRunAt = null;
433
374
  this.cronMode = false;
434
375
  this.start();
435
- refreshBackgroundWakeIntentSoon("heartbeat-reconfigured");
436
376
  }
437
377
 
438
378
  /**
@@ -449,7 +389,6 @@ export class HeartbeatService {
449
389
  supersedePendingRun(this._pendingRunId);
450
390
  this._pendingRunId = null;
451
391
  }
452
- refreshBackgroundWakeIntentSoon("heartbeat-counter-reset");
453
392
  if (!this.timer) {
454
393
  return;
455
394
  }
@@ -519,7 +458,6 @@ export class HeartbeatService {
519
458
  if (runId) {
520
459
  skipHeartbeatRun(runId, "disabled");
521
460
  }
522
- refreshBackgroundWakeIntentSoon("heartbeat-disabled");
523
461
  return false;
524
462
  }
525
463
 
@@ -680,7 +618,6 @@ export class HeartbeatService {
680
618
  if (!this.cronMode) {
681
619
  this.scheduleNextRun(getConfig().heartbeat.intervalMs);
682
620
  }
683
- refreshBackgroundWakeIntentSoon("heartbeat-run-complete");
684
621
  }
685
622
  return true;
686
623
  }
@@ -691,7 +628,6 @@ export class HeartbeatService {
691
628
  }
692
629
  this._nextRunAt = Date.now() + intervalMs;
693
630
  this._pendingRunId = insertPendingHeartbeatRun(this._nextRunAt);
694
- refreshBackgroundWakeIntentSoon("heartbeat-interval-scheduled");
695
631
  }
696
632
 
697
633
  /**
@@ -964,8 +900,7 @@ This is one of your first heartbeats. Your user hasn't heard from you yet and ma
964
900
  }
965
901
 
966
902
  /**
967
- * Construct and start the heartbeat service singleton, returning it so callers
968
- * can wire it into the background-wake runtime. start() self-gates on
903
+ * Construct and start the heartbeat service singleton. start() self-gates on
969
904
  * `heartbeat.enabled` and logs its own status.
970
905
  */
971
906
  export function startHeartbeatService(): HeartbeatService {
@@ -97,23 +97,15 @@ describe("AssistantIpcServer DB migration readiness gate", () => {
97
97
  });
98
98
 
99
99
  test("migration-repair surface is allowed only in the failed state", () => {
100
- // Rollback/import are the remedies for failed migrations — gating them in
100
+ // Rollback is the remedy for failed migrations — gating them in
101
101
  // that state would make recovery impossible (the upgrade CLI's
102
102
  // rollback/restore path). While migrations are merely RUNNING they stay
103
103
  // gated: a rollback would race the in-flight migration runner.
104
104
  setDbMigrating();
105
105
  expect(gate("admin_rollbackmigrations_post")?.statusCode).toBe(503);
106
- expect(gate("migrations_import_post")?.statusCode).toBe(503);
107
106
 
108
107
  setDbMigrationFailed(new Error("boom"));
109
108
  expect(gate("admin_rollbackmigrations_post")).toBeNull();
110
- expect(gate("migrations_import_post")).toBeNull();
111
- // All restore transports are repair-capable: preflights, the cloud
112
- // platform's GCS path, and its job-status polling.
113
- expect(gate("migrations_importpreflight_post")).toBeNull();
114
- expect(gate("migrations_importfromgcs_post")).toBeNull();
115
- expect(gate("migrations_preflightfromgcs_post")).toBeNull();
116
- expect(gate("migrations_jobs_by_job_id_get")).toBeNull();
117
109
  // Everything else stays gated in the failed state.
118
110
  expect(gate("db_proxy")?.statusCode).toBe(503);
119
111
  });
@@ -40,10 +40,6 @@ const realSttResolveModule = {
40
40
  const realSecureKeysModule = {
41
41
  ...(await import("../../security/secure-keys.js")),
42
42
  };
43
- const realCloudSpeechModule = {
44
- ...(await import("../../cloud/cloud-speech.js")),
45
- };
46
-
47
43
  // -- Mutable stub state --------------------------------------------------------
48
44
 
49
45
  import type { StreamingTranscriber } from "../../stt/types.js";
@@ -74,16 +70,6 @@ mock.module("../../security/secure-keys.js", () => ({
74
70
  : realSecureKeysModule.getSecureKeyAsync(account),
75
71
  }));
76
72
 
77
- let managedAvailable = false;
78
-
79
- mock.module("../../cloud/cloud-speech.js", () => ({
80
- ...realCloudSpeechModule,
81
- managedSpeechAvailable: async () =>
82
- preflightMocksActive
83
- ? managedAvailable
84
- : realCloudSpeechModule.managedSpeechAvailable(),
85
- }));
86
-
87
73
  import { setConfig } from "../../__tests__/helpers/set-config.js";
88
74
  import { getConfig } from "../../config/loader.js";
89
75
  import {
@@ -135,7 +121,6 @@ beforeEach(() => {
135
121
  sttProvider = "deepgram";
136
122
  ttsProvider = "fish-audio";
137
123
  ttsProviders = { "fish-audio": { referenceId: "ref-123" } };
138
- managedAvailable = false;
139
124
  });
140
125
 
141
126
  function expectNotReady(
@@ -284,32 +269,3 @@ describe("resolveLiveVoiceCredentialReadiness", () => {
284
269
  expect(readiness.userMessage.endsWith(".")).toBe(true);
285
270
  });
286
271
  });
287
-
288
- describe("resolveLiveVoiceCredentialReadiness — managed (cadet) TTS", () => {
289
- test("cadet TTS with a full platform connection → ready", async () => {
290
- ttsProvider = "cadet";
291
- ttsProviders = {};
292
- managedAvailable = true;
293
-
294
- const readiness = await runReadiness();
295
- expect(readiness).toEqual({ status: "ready" });
296
- });
297
-
298
- test("cadet TTS without a cloud connection → connection copy, not API-key copy", async () => {
299
- ttsProvider = "cadet";
300
- ttsProviders = {};
301
- managedAvailable = false;
302
-
303
- const readiness = expectNotReady(await runReadiness());
304
- expect(readiness.missing).toEqual([
305
- {
306
- kind: "tts",
307
- providerId: "cadet",
308
- reason:
309
- 'TTS provider "cadet" needs a Cadet Cloud connection for cloud speech',
310
- },
311
- ]);
312
- expect(readiness.userMessage).toContain("assistant cloud connect");
313
- expect(readiness.userMessage).not.toContain("API key");
314
- });
315
- });
@@ -1170,8 +1170,6 @@ describe("LiveVoiceSession Flux end-of-turn", () => {
1170
1170
  test.each([
1171
1171
  { providerId: "deepgram-flux" as const, flagOn: true },
1172
1172
  { providerId: "deepgram-flux" as const, flagOn: false },
1173
- { providerId: "cadet-flux" as const, flagOn: true },
1174
- { providerId: "cadet-flux" as const, flagOn: false },
1175
1173
  ])(
1176
1174
  "only provider speech starts interrupt playback: %j",
1177
1175
  async ({ providerId, flagOn }) => {
@@ -1518,7 +1516,7 @@ describe("LiveVoiceSession Flux end-of-turn during the STT dial", () => {
1518
1516
  async (flagOn) => {
1519
1517
  const gate = createDialGate();
1520
1518
  const { frames, session } = createHarness({
1521
- providerId: "cadet",
1519
+ providerId: "deepgram",
1522
1520
  fluxConfig: flagOn ? FLUX_ON : FLUX_OFF,
1523
1521
  silenceThresholdMs: 10_000,
1524
1522
  resolveGate: gate.promise,
@@ -1539,9 +1537,9 @@ describe("LiveVoiceSession Flux end-of-turn during the STT dial", () => {
1539
1537
  );
1540
1538
 
1541
1539
  test.each([
1542
- { providerId: "cadet" as const, audio: LOUD_CHUNK, interrupts: false },
1540
+ { providerId: "deepgram" as const, audio: LOUD_CHUNK, interrupts: false },
1543
1541
  {
1544
- providerId: "cadet" as const,
1542
+ providerId: "deepgram" as const,
1545
1543
  audio: SUSTAINED_LOUD_CHUNK,
1546
1544
  interrupts: true,
1547
1545
  },
@@ -1616,7 +1614,7 @@ describe("LiveVoiceSession Flux end-of-turn during the STT dial", () => {
1616
1614
  // The first input is released but cannot dispatch until its final arrives.
1617
1615
  await session.handleBinaryAudio(SUSTAINED_LOUD_CHUNK);
1618
1616
  await sleep(PAST_SILENCE_BOUNDARY_MS);
1619
- options.providerId = "cadet";
1617
+ options.providerId = "deepgram";
1620
1618
  first.flushStopEvents();
1621
1619
  await waitFor(() => countFrames(frames, "thinking") === 1);
1622
1620
  expect(countFrames(frames, "turn_cancelled")).toBe(0);
@@ -1609,7 +1609,7 @@ describe("LiveVoiceSession STT", () => {
1609
1609
  },
1610
1610
  },
1611
1611
  });
1612
- const transcriber = new MockStreamingTranscriber("cadet");
1612
+ const transcriber = new MockStreamingTranscriber("deepgram");
1613
1613
  const startVoiceTurn = speakingVoiceTurnStarter();
1614
1614
  const { streamTtsAudio, ttsCalls } = recordingTtsStreamer();
1615
1615
  const { context } = createContext();
@@ -11,24 +11,22 @@
11
11
  * Mirrors `calls/telephony-credential-preflight.ts` for the phone-call
12
12
  * transport.
13
13
  *
14
- * Both legs are checked against the effective providers
15
- * (`resolveEffectiveSpeechProviders`), which is what the transcriber and
16
- * synthesis paths resolve too — so a `ready` verdict earned on managed
17
- * speech is a verdict the runtime can honour.
14
+ * Both legs are checked against the providers the transcriber and synthesis
15
+ * paths resolve, so a `ready` verdict is one the runtime can honour.
18
16
  */
19
17
 
20
18
  import {
21
19
  fishAudioReferenceIdConfigured,
22
20
  ttsSecretResolves,
23
21
  } from "../calls/telephony-tts-capability.js";
24
- import { managedSpeechAvailable } from "../cloud/cloud-speech.js";
25
- import { resolveEffectiveSpeechProviders } from "../config/cloud-speech-defaults.js";
22
+ import { getConfig } from "../config/loader.js";
26
23
  import { getProviderEntry } from "../providers/speech-to-text/provider-catalog.js";
27
24
  import {
28
25
  resolveStreamingTranscriber,
29
26
  sttCredentialGapReason,
30
27
  sttProviderKeyResolves,
31
28
  } from "../providers/speech-to-text/resolve.js";
29
+ import { sttCatalogKeyForRole } from "../stt/roles.js";
32
30
  import type { SttProviderId } from "../stt/types.js";
33
31
  import type { TtsProviderCatalogEntry } from "../tts/provider-catalog.js";
34
32
  import { getCatalogProvider, getTtsProvider } from "../tts/provider-catalog.js";
@@ -78,13 +76,12 @@ export type LiveVoiceCredentialReadiness =
78
76
  export async function resolveLiveVoiceCredentialReadiness(): Promise<LiveVoiceCredentialReadiness> {
79
77
  // Readiness must ask about the same provider the session will dial, so it
80
78
  // reads the live-voice role rather than the global setting.
81
- const effective = await resolveEffectiveSpeechProviders(undefined, {
82
- role: "liveVoice",
83
- });
79
+ const config = getConfig();
80
+ const effectiveStt = sttCatalogKeyForRole(config.services.stt, "liveVoice");
84
81
  const gaps = (
85
82
  await Promise.all([
86
- resolveSttGap(effective.stt),
87
- resolveTtsGap(effective.tts),
83
+ resolveSttGap(effectiveStt),
84
+ resolveTtsGap(config.services.tts.provider),
88
85
  ])
89
86
  ).filter((gap) => gap !== null);
90
87
 
@@ -142,10 +139,7 @@ async function resolveSttGap(
142
139
  providerId: entry.id,
143
140
  reason: sttCredentialGapReason(entry.credentialProvider),
144
141
  },
145
- clause:
146
- entry.credentialProvider === "cadet"
147
- ? `a Cadet Cloud connection for cloud speech (run 'assistant cloud connect')`
148
- : `an API key for the speech-to-text provider "${entry.id}"`,
142
+ clause: `an API key for the speech-to-text provider "${entry.id}"`,
149
143
  };
150
144
  }
151
145
 
@@ -197,24 +191,6 @@ async function resolveTtsGap(
197
191
  };
198
192
  }
199
193
 
200
- // Cloud speech authenticates via the cloud connection — full
201
- // availability (API key + assistant ID) is the credential, so the
202
- // stored-secret loop below would pass half-connected states that
203
- // synthesis rejects.
204
- if (entry.id === "cadet") {
205
- if (!(await managedSpeechAvailable())) {
206
- return {
207
- gap: {
208
- kind: "tts",
209
- providerId: entry.id,
210
- reason: `TTS provider "${entry.id}" needs a Cadet Cloud connection for cloud speech`,
211
- },
212
- clause: `a Cadet Cloud connection for cloud speech (run 'assistant cloud connect')`,
213
- };
214
- }
215
- return null;
216
- }
217
-
218
194
  for (const secret of entry.secretRequirements) {
219
195
  if (!(await ttsSecretResolves(secret.credentialStoreKey))) {
220
196
  return {
@@ -8262,14 +8262,18 @@ export async function defaultSpawnBackgroundContinuation(args: {
8262
8262
  async function defaultResolveStreamingTranscriber(
8263
8263
  options: ResolveStreamingTranscriberOptions,
8264
8264
  ): Promise<StreamingTranscriber | null> {
8265
- const { resolveEffectiveSpeechProviders } =
8266
- await import("../config/cloud-speech-defaults.js");
8267
8265
  const { resolveStreamingTranscriber } =
8268
8266
  await import("../providers/speech-to-text/resolve.js");
8269
- const { stt } = await resolveEffectiveSpeechProviders(undefined, {
8270
- role: "liveVoice",
8271
- });
8272
- return resolveStreamingTranscriber({ ...options, providerId: stt });
8267
+ const { sttCatalogKeyForRole } = await import("../stt/roles.js");
8268
+ const { getConfig } = await import("../config/loader.js");
8269
+ // The live-voice role's configured provider is the one that gets dialed —
8270
+ // the session resolves it here (rather than trusting a caller's override)
8271
+ // so readiness and transcription agree.
8272
+ const providerId = sttCatalogKeyForRole(
8273
+ getConfig().services.stt,
8274
+ "liveVoice",
8275
+ );
8276
+ return resolveStreamingTranscriber({ ...options, providerId });
8273
8277
  }
8274
8278
 
8275
8279
  async function defaultResolveLiveVoiceCredentialReadiness(): Promise<LiveVoiceCredentialReadiness> {
@@ -220,14 +220,7 @@ async function resolveLiveVoiceStreamingTtsProvider(
220
220
  configOverride?: LiveVoiceTtsConfig,
221
221
  ): Promise<ResolvedStreamingTtsProvider> {
222
222
  const config = configOverride ?? (await loadAssistantConfig());
223
- const { resolveEffectiveSpeechProviders } =
224
- await import("../config/cloud-speech-defaults.js");
225
- const { tts: effectiveProviderId } =
226
- await resolveEffectiveSpeechProviders(config);
227
- const { provider: providerId, providerConfig } = resolveTtsConfig(
228
- config,
229
- effectiveProviderId,
230
- );
223
+ const { provider: providerId, providerConfig } = resolveTtsConfig(config);
231
224
 
232
225
  let provider: TtsProvider;
233
226
  try {
@@ -60,9 +60,9 @@ async function main(): Promise<void> {
60
60
  // standalone process must do it itself so getCloudBaseUrl() resolves to
61
61
  // the persisted platform environment instead of the CADET_ENVIRONMENT
62
62
  // default. Retrospective and consolidation passes wake real agent
63
- // conversations whose inference and background-wake requests go through the
64
- // cloud proxy — without rehydration those requests hit the wrong platform
65
- // and are rejected.
63
+ // conversations whose inference requests go through the cloud proxy —
64
+ // without rehydration those requests hit the wrong platform and are
65
+ // rejected.
66
66
  await ensureCloudIdentityIds();
67
67
 
68
68
  // Jobs in this process wake real agent conversations, so it registers the
@@ -385,41 +385,6 @@ export function ensureGuardianPersonaFile(userFile: string): void {
385
385
  log.debug({ path: filePath }, "Wrote guardian persona scaffold");
386
386
  }
387
387
 
388
- /**
389
- * Return `true` when the persona file at `filePath` has been edited by
390
- * the user (its stripped content differs from the bare scaffold
391
- * template). Returns `false` when the file is missing, unreadable,
392
- * empty after stripping, or byte-identical to the template after
393
- * stripping comment lines.
394
- *
395
- * Used by the vbundle importer to decide whether a legacy
396
- * `prompts/USER.md` entry may safely overwrite `users/<slug>.md`.
397
- */
398
- export function isGuardianPersonaCustomized(filePath: string): boolean {
399
- if (!existsSync(filePath)) {
400
- return false;
401
- }
402
-
403
- let content: string;
404
- try {
405
- content = readFileSync(filePath, "utf-8");
406
- } catch (err) {
407
- log.warn(
408
- { err, path: filePath },
409
- "Failed to read persona file while checking customization",
410
- );
411
- return false;
412
- }
413
-
414
- const stripped = stripCommentLines(content);
415
- if (stripped.length === 0) {
416
- return false;
417
- }
418
-
419
- const templateStripped = stripCommentLines(GUARDIAN_PERSONA_TEMPLATE);
420
- return stripped !== templateStripped;
421
- }
422
-
423
388
  // ── Onboarding section writer ────────────────────────────────────
424
389
 
425
390
  const ONBOARDING_HEADING = "## Onboarding Context";