diff --git a/packages/app/src/components/sidebar-agent-list.tsx b/packages/app/src/components/sidebar-agent-list.tsx index 2b0d955ae..95722a295 100644 --- a/packages/app/src/components/sidebar-agent-list.tsx +++ b/packages/app/src/components/sidebar-agent-list.tsx @@ -687,7 +687,7 @@ const styles = StyleSheet.create((theme) => ({ marginBottom: theme.spacing[1], }, agentItemUnselected: { - opacity: 0.75, + opacity: 1, }, agentItemSelected: { backgroundColor: theme.colors.surface2, diff --git a/packages/server/scripts/dev-runner.ts b/packages/server/scripts/dev-runner.ts index 91a7d1526..1f303a1f9 100644 --- a/packages/server/scripts/dev-runner.ts +++ b/packages/server/scripts/dev-runner.ts @@ -2,35 +2,19 @@ import { fileURLToPath } from "url"; import { existsSync } from "node:fs"; import { runSupervisor } from "./supervisor.js"; -function resolveWorkerEntry(): string { - const candidates = [ - fileURLToPath(new URL("../server/server/index.js", import.meta.url)), - fileURLToPath(new URL("../dist/server/server/index.js", import.meta.url)), - fileURLToPath(new URL("../src/server/index.ts", import.meta.url)), - fileURLToPath(new URL("../../src/server/index.ts", import.meta.url)), - ]; - - for (const candidate of candidates) { - if (existsSync(candidate)) { - return candidate; - } - } - - return candidates[0]; -} - -function resolveWorkerExecArgv(): string[] { - const workerEntry = resolveWorkerEntry(); - return workerEntry.endsWith(".ts") ? ["--import", "tsx"] : []; +const WORKER_ENTRY = fileURLToPath(new URL("../src/server/index.ts", import.meta.url)); +if (!existsSync(WORKER_ENTRY)) { + throw new Error(`Dev worker entry not found: ${WORKER_ENTRY}`); } runSupervisor({ name: "DevRunner", startupMessage: "Starting server worker (crash restarts enabled)", - resolveWorkerEntry, + resolveWorkerEntry: () => WORKER_ENTRY, workerArgs: process.argv.slice(2), workerEnv: process.env, - workerExecArgv: resolveWorkerExecArgv(), + // Always run worker with tsx so dev server uses TypeScript sources directly. + workerExecArgv: ["--import", "tsx"], restartOnCrash: true, shutdownReasons: ["cli_shutdown"], }); diff --git a/packages/server/src/server/speech/providers/local/sherpa/sherpa-tts.ts b/packages/server/src/server/speech/providers/local/sherpa/sherpa-tts.ts index 8c05461b8..365a88fa1 100644 --- a/packages/server/src/server/speech/providers/local/sherpa/sherpa-tts.ts +++ b/packages/server/src/server/speech/providers/local/sherpa/sherpa-tts.ts @@ -4,7 +4,7 @@ import { existsSync } from "node:fs"; import type { SpeechStreamResult, TextToSpeechProvider } from "../../../speech-provider.js"; import { chunkBuffer, float32ToPcm16le } from "../../../audio.js"; -import { loadSherpaOnnx } from "./sherpa-onnx-loader.js"; +import { loadSherpaOnnxNode } from "./sherpa-onnx-node-loader.js"; export type SherpaTtsPreset = "kokoro-en-v0_19" | "kitten-nano-en-v0_1-fp16"; @@ -37,7 +37,10 @@ export class SherpaOnnxTTS implements TextToSpeechProvider { this.speakerId = config.speakerId ?? 0; this.speed = config.speed ?? 1.0; - const sherpa = loadSherpaOnnx(); + const sherpa = loadSherpaOnnxNode(); + if (typeof sherpa.OfflineTts !== "function") { + throw new Error("sherpa-onnx-node OfflineTts is unavailable"); + } const modelFile = config.preset === "kokoro-en-v0_19" ? "model.onnx" : "model.fp16.onnx"; const modelPath = `${config.modelDir}/${modelFile}`; @@ -50,30 +53,35 @@ export class SherpaOnnxTTS implements TextToSpeechProvider { assertFileExists(tokensPath, "TTS tokens"); assertFileExists(dataDir, "TTS espeak-ng dataDir"); - const modelConfigKey = + const modelConfig = config.preset === "kokoro-en-v0_19" - ? "offlineTtsKokoroModelConfig" - : "offlineTtsKittenModelConfig"; - - const modelConfig = { - [modelConfigKey]: { - model: modelPath, - voices: voicesPath, - tokens: tokensPath, - dataDir, - lengthScale: config.lengthScale ?? 1.0, - }, - numThreads: config.numThreads ?? 2, - debug: 0, - provider: "cpu", - }; + ? { + kokoro: { + model: modelPath, + voices: voicesPath, + tokens: tokensPath, + dataDir, + lengthScale: config.lengthScale ?? 1.0, + }, + } + : { + kitten: { + model: modelPath, + voices: voicesPath, + tokens: tokensPath, + dataDir, + lengthScale: config.lengthScale ?? 1.0, + }, + }; const offlineTtsConfig = { - offlineTtsModelConfig: modelConfig, + model: modelConfig, + numThreads: config.numThreads ?? 2, + provider: "cpu", maxNumSentences: 1, }; - this.tts = sherpa.createOfflineTts(offlineTtsConfig); + this.tts = new sherpa.OfflineTts(offlineTtsConfig); this.logger.info({ preset: config.preset, modelDir: config.modelDir }, "Sherpa offline TTS initialized"); }