@mycadet/assistant 0.12.10 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/node_modules/@mycadet/ces-client/src/http-credentials.ts +18 -1
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +0 -20
- package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/openapi.yaml +2 -708
- package/package.json +2 -3
- package/scripts/sync-web-search-catalog.ts +1 -2
- package/src/__tests__/cloud-profile-guard.test.ts +41 -5
- package/src/__tests__/config-schema.test.ts +6 -38
- package/src/__tests__/conversation-queue.test.ts +45 -6
- package/src/__tests__/db-readiness-http-gate.test.ts +0 -6
- package/src/__tests__/persist-onboarding-artifacts.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +0 -51
- package/src/__tests__/provider-cloud-proxy-integration.test.ts +2 -110
- package/src/__tests__/run-due-schedules.test.ts +0 -4
- package/src/__tests__/schedule-store.test.ts +0 -12
- package/src/__tests__/stt-stream-session.test.ts +4 -4
- package/src/__tests__/telephony-tts-guard.test.ts +2 -50
- package/src/__tests__/voice-config-update.test.ts +12 -167
- package/src/__tests__/web-search-catalog-parity.test.ts +0 -2
- package/src/__tests__/workspace-migration-130-speech-mode-to-provider.test.ts +9 -7
- package/src/__tests__/workspace-migration-132-web-search-mode-to-provider.test.ts +6 -6
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -4
- package/src/calls/resolve-call-tts-provider.ts +6 -25
- package/src/calls/telephony-credential-preflight.ts +0 -10
- package/src/calls/telephony-tts-capability.ts +1 -17
- package/src/cli/commands/tts.ts +1 -2
- package/src/cli/index.help.ts +0 -2
- package/src/cli/program.ts +0 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +4 -42
- package/src/config/schemas/__tests__/stt.test.ts +10 -14
- package/src/config/schemas/llm.ts +3 -5
- package/src/config/schemas/services.ts +5 -6
- package/src/config/schemas/stt.ts +1 -3
- package/src/config/schemas/tts.ts +1 -30
- package/src/daemon/daemon-readiness.ts +4 -29
- package/src/daemon/lifecycle.ts +0 -31
- package/src/daemon/providers-setup.ts +0 -8
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -66
- package/src/ipc/__tests__/db-readiness-ipc-gate.test.ts +1 -9
- package/src/live-voice/__tests__/live-voice-credential-preflight.test.ts +0 -44
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +4 -6
- package/src/live-voice/__tests__/live-voice-stt.test.ts +1 -1
- package/src/live-voice/live-voice-credential-preflight.ts +9 -33
- package/src/live-voice/live-voice-session.ts +10 -6
- package/src/live-voice/live-voice-tts.ts +1 -8
- package/src/plugins/defaults/memory/worker.ts +3 -3
- package/src/prompts/persona-resolver.ts +0 -35
- package/src/providers/__tests__/search-provider-catalog.test.ts +5 -8
- package/src/providers/__tests__/voice-error-copy.test.ts +9 -10
- package/src/providers/search-provider-catalog.ts +7 -39
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +3 -47
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +6 -372
- package/src/providers/speech-to-text/provider-catalog.ts +2 -73
- package/src/providers/speech-to-text/resolve.ts +3 -120
- package/src/runtime/http-router-types.ts +1 -3
- package/src/runtime/http-server.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +13 -11
- package/src/runtime/routes/__tests__/tts-routes.test.ts +2 -6
- package/src/runtime/routes/conversation-query-routes.ts +0 -96
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +7 -7
- package/src/runtime/routes/index.ts +0 -4
- package/src/runtime/routes/live-voice-routes.test.ts +4 -11
- package/src/runtime/routes/live-voice-routes.ts +8 -11
- package/src/runtime/routes/secret-routes.ts +0 -6
- package/src/runtime/routes/tts-routes.ts +0 -38
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1 -7
- package/src/schedule/__tests__/worker-mcp-readiness.test.ts +0 -4
- package/src/schedule/schedule-store.ts +0 -7
- package/src/schedule/scheduler.ts +0 -5
- package/src/schedule/worker.ts +1 -1
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +2 -10
- package/src/stt/daemon-batch-transcriber.ts +4 -38
- package/src/stt/types.ts +1 -9
- package/src/tools/network/__tests__/web-search.test.ts +3 -250
- package/src/tools/network/web-search.ts +4 -165
- package/src/tts/__tests__/provider-resolution.test.ts +0 -5
- package/src/tts/provider-catalog.ts +0 -4
- package/src/tts/types.ts +0 -1
- package/src/__tests__/cloud-speech-defaults.test.ts +0 -330
- package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1708
- package/src/__tests__/migration-export-http.test.ts +0 -557
- package/src/__tests__/migration-export-streaming.test.ts +0 -350
- package/src/__tests__/migration-export-to-gcs.test.ts +0 -706
- package/src/__tests__/migration-import-commit-http.test.ts +0 -1176
- package/src/__tests__/migration-import-from-gcs.test.ts +0 -534
- package/src/__tests__/migration-import-from-path.test.ts +0 -324
- package/src/__tests__/migration-import-from-url.test.ts +0 -588
- package/src/__tests__/migration-import-preflight-http.test.ts +0 -739
- package/src/__tests__/migration-jobs-status.test.ts +0 -139
- package/src/__tests__/migration-parity-persistence.test.ts +0 -1891
- package/src/__tests__/migration-transport.test.ts +0 -1089
- package/src/__tests__/migration-validate-http.test.ts +0 -634
- package/src/__tests__/migration-wizard.test.ts +0 -1395
- package/src/__tests__/rebind-secrets-screen.test.ts +0 -876
- package/src/__tests__/sanitize-config-for-transfer.test.ts +0 -259
- package/src/__tests__/transfer-progress-screen.test.ts +0 -1217
- package/src/__tests__/validation-results-screen.test.ts +0 -1130
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +0 -147
- package/src/background-wake/background-wake-routes.test.ts +0 -906
- package/src/background-wake/cloud-client.test.ts +0 -328
- package/src/background-wake/cloud-client.ts +0 -196
- package/src/background-wake/next-wake.test.ts +0 -335
- package/src/background-wake/next-wake.ts +0 -188
- package/src/background-wake/publisher.ts +0 -93
- package/src/background-wake/wake-intent-hooks.test.ts +0 -234
- package/src/cli/commands/__tests__/backup.test.ts +0 -363
- package/src/cli/commands/backup.help.ts +0 -180
- package/src/cli/commands/backup.ts +0 -398
- package/src/cloud/cloud-speech.test.ts +0 -335
- package/src/cloud/cloud-speech.ts +0 -300
- package/src/config/cloud-speech-defaults.ts +0 -300
- package/src/config/sanitize-for-transfer.ts +0 -111
- package/src/live-voice/__tests__/live-voice-cloud-speech-fallback.test.ts +0 -250
- package/src/providers/speech-to-text/__tests__/cadet-cloud-realtime.test.ts +0 -457
- package/src/providers/speech-to-text/__tests__/cadet-cloud.test.ts +0 -271
- package/src/providers/speech-to-text/__tests__/cadet-speech-relay-connection.test.ts +0 -126
- package/src/providers/speech-to-text/cadet-cloud-flux-realtime.ts +0 -309
- package/src/providers/speech-to-text/cadet-cloud-realtime.ts +0 -315
- package/src/providers/speech-to-text/cadet-cloud.ts +0 -108
- package/src/providers/speech-to-text/cadet-speech-relay-connection.ts +0 -176
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +0 -254
- package/src/runtime/migrations/__tests__/job-registry.test.ts +0 -346
- package/src/runtime/migrations/__tests__/origin-mode.test.ts +0 -62
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +0 -184
- package/src/runtime/migrations/__tests__/staged-import-path.test.ts +0 -110
- package/src/runtime/migrations/__tests__/v1-test-helpers.ts +0 -112
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +0 -285
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +0 -126
- package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +0 -253
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +0 -208
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +0 -485
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +0 -349
- package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +0 -196
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +0 -551
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +0 -428
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +0 -220
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +0 -1823
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +0 -598
- package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +0 -463
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -538
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +0 -515
- package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +0 -453
- package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +0 -319
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +0 -240
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +0 -473
- package/src/runtime/migrations/gcs-signed-url.ts +0 -190
- package/src/runtime/migrations/job-registry.ts +0 -297
- package/src/runtime/migrations/migration-transport.ts +0 -612
- package/src/runtime/migrations/migration-wizard.ts +0 -747
- package/src/runtime/migrations/origin-mode.ts +0 -42
- package/src/runtime/migrations/rebind-secrets-screen.ts +0 -412
- package/src/runtime/migrations/staged-import-path.ts +0 -116
- package/src/runtime/migrations/transfer-progress-screen.ts +0 -323
- package/src/runtime/migrations/validation-results-screen.ts +0 -469
- package/src/runtime/migrations/vbundle-builder.ts +0 -1382
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -405
- package/src/runtime/migrations/vbundle-import-policy.ts +0 -241
- package/src/runtime/migrations/vbundle-importer.ts +0 -826
- package/src/runtime/migrations/vbundle-metadata-merge.ts +0 -113
- package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -2844
- package/src/runtime/migrations/vbundle-streaming-validator.ts +0 -426
- package/src/runtime/migrations/vbundle-tar-stream.ts +0 -244
- package/src/runtime/migrations/vbundle-validator.ts +0 -724
- package/src/runtime/routes/__tests__/migration-cadet-metadata-reconcile.test.ts +0 -227
- package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +0 -54
- package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +0 -70
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +0 -257
- package/src/runtime/routes/background-wake-routes.ts +0 -387
- package/src/runtime/routes/migration-routes.ts +0 -2584
- package/src/tools/network/__tests__/cloud-search-proxy.test.ts +0 -282
- package/src/tools/network/cloud-search-proxy.ts +0 -211
- package/src/tts/__tests__/cadet-provider.test.ts +0 -253
- package/src/tts/providers/cadet-provider.ts +0 -244
- package/src/types/tar-stream.d.ts +0 -66
|
@@ -69,9 +69,9 @@ describe("SttServiceSchema", () => {
|
|
|
69
69
|
test("language defaults to multilingual rather than staying absent", () => {
|
|
70
70
|
// Every config carries a real language, so nothing downstream has to
|
|
71
71
|
// infer one from an absent key. Omitting the param entirely is not a
|
|
72
|
-
// neutral state on Deepgram
|
|
73
|
-
//
|
|
74
|
-
const parsed = SttServiceSchema.parse({ provider: "
|
|
72
|
+
// neutral state on Deepgram: it decodes as English, which is what this
|
|
73
|
+
// default exists to stop.
|
|
74
|
+
const parsed = SttServiceSchema.parse({ provider: "deepgram" });
|
|
75
75
|
expect(parsed.language).toBe("multi");
|
|
76
76
|
});
|
|
77
77
|
|
|
@@ -86,7 +86,8 @@ describe("SttServiceSchema", () => {
|
|
|
86
86
|
|
|
87
87
|
test("language round-trips a code and the multi code-switching mode", () => {
|
|
88
88
|
expect(
|
|
89
|
-
SttServiceSchema.parse({ provider: "
|
|
89
|
+
SttServiceSchema.parse({ provider: "deepgram", language: "multi" })
|
|
90
|
+
.language,
|
|
90
91
|
).toBe("multi");
|
|
91
92
|
expect(
|
|
92
93
|
SttServiceSchema.parse({ provider: "deepgram", language: "hi" }).language,
|
|
@@ -95,30 +96,25 @@ describe("SttServiceSchema", () => {
|
|
|
95
96
|
|
|
96
97
|
test("language is trimmed and rejects blank strings", () => {
|
|
97
98
|
expect(
|
|
98
|
-
SttServiceSchema.parse({ provider: "
|
|
99
|
+
SttServiceSchema.parse({ provider: "deepgram", language: " multi " })
|
|
99
100
|
.language,
|
|
100
101
|
).toBe("multi");
|
|
101
102
|
expect(() =>
|
|
102
|
-
SttServiceSchema.parse({ provider: "
|
|
103
|
+
SttServiceSchema.parse({ provider: "deepgram", language: " " }),
|
|
103
104
|
).toThrow(/must not be empty/);
|
|
104
105
|
});
|
|
105
106
|
});
|
|
106
107
|
|
|
107
|
-
describe("
|
|
108
|
-
test("accepts cadet as an ordinary provider choice", () => {
|
|
109
|
-
const parsed = SttServiceSchema.parse({ provider: "cadet" });
|
|
110
|
-
expect(parsed.provider).toBe("cadet");
|
|
111
|
-
});
|
|
112
|
-
|
|
108
|
+
describe("provider parsing", () => {
|
|
113
109
|
// Migration 130 folds mode into provider; a stale key must not resurrect
|
|
114
110
|
// the second axis or fail the parse.
|
|
115
111
|
test("ignores a legacy mode key", () => {
|
|
116
112
|
const parsed = SttServiceSchema.parse({
|
|
117
113
|
mode: "managed",
|
|
118
|
-
provider: "
|
|
114
|
+
provider: "deepgram",
|
|
119
115
|
});
|
|
120
116
|
expect(parsed).toEqual({
|
|
121
|
-
provider: "
|
|
117
|
+
provider: "deepgram",
|
|
122
118
|
language: "multi",
|
|
123
119
|
providers: {},
|
|
124
120
|
roles: {},
|
|
@@ -54,9 +54,8 @@ const ALWAYS_RESOLVING_MANAGED_ONLY_PROFILE_KEYS =
|
|
|
54
54
|
* an open string so a stored provider outside this list parses instead of
|
|
55
55
|
* stripping its profile; dispatch resolves such a label as a connection
|
|
56
56
|
* entry name and fails with an explainable resolution error when no row
|
|
57
|
-
* carries it.
|
|
58
|
-
*
|
|
59
|
-
* is where entry names unlock when the entries model enables them.
|
|
57
|
+
* carries it. The generic config routes follow this open-string schema;
|
|
58
|
+
* dedicated profile routes apply their own provider selection rules.
|
|
60
59
|
*/
|
|
61
60
|
export const KNOWN_LLM_PROVIDERS = [
|
|
62
61
|
"anthropic",
|
|
@@ -153,8 +152,7 @@ export function isByokDefaultProviderChoice(
|
|
|
153
152
|
* on that profile/call site deterministically — a call-site fragment naming
|
|
154
153
|
* an identity without a model would inherit whatever model the winning
|
|
155
154
|
* profile carries, which the identity may not serve. Enforced at parse time
|
|
156
|
-
* (LLMSchema.superRefine)
|
|
157
|
-
* (commitConfigWrite), and by the profile write route.
|
|
155
|
+
* (LLMSchema.superRefine) and by the profile creation route.
|
|
158
156
|
*/
|
|
159
157
|
export function routingIdentityModelIssue(
|
|
160
158
|
provider: string,
|
|
@@ -71,12 +71,11 @@ const ImageGenerationServiceSchema = z.object({
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* Web-search carries no `mode`: `provider` is the only axis.
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* by an older client is stripped at parse.
|
|
74
|
+
* Web-search carries no `mode`: `provider` is the only axis.
|
|
75
|
+
* `"inference-provider-native"` prefers the inference model's own hosted
|
|
76
|
+
* search, falling back to the user's own keys; any other provider uses the
|
|
77
|
+
* user's own API key. A `mode` key sent by an older client is stripped at
|
|
78
|
+
* parse.
|
|
80
79
|
*/
|
|
81
80
|
const WebSearchServiceSchema = z.object({
|
|
82
81
|
provider: z
|
|
@@ -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:
|
|
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:
|
|
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.
|
|
68
|
-
*
|
|
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
|
-
|
|
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 {
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -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
|
|
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
|
|
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: "
|
|
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: "
|
|
1540
|
+
{ providerId: "deepgram" as const, audio: LOUD_CHUNK, interrupts: false },
|
|
1543
1541
|
{
|
|
1544
|
-
providerId: "
|
|
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 = "
|
|
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("
|
|
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
|
|
15
|
-
*
|
|
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 {
|
|
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
|
|
82
|
-
|
|
83
|
-
});
|
|
79
|
+
const config = getConfig();
|
|
80
|
+
const effectiveStt = sttCatalogKeyForRole(config.services.stt, "liveVoice");
|
|
84
81
|
const gaps = (
|
|
85
82
|
await Promise.all([
|
|
86
|
-
resolveSttGap(
|
|
87
|
-
resolveTtsGap(
|
|
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 {
|