diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 2d69fc42a..246399693 100644 Binary files a/.claude/CLAUDE.md and b/.claude/CLAUDE.md differ diff --git a/packages/voice-assistant/src/server/agent/tts-manager.ts b/packages/voice-assistant/src/server/agent/tts-manager.ts index 6223a07af..431991988 100644 --- a/packages/voice-assistant/src/server/agent/tts-manager.ts +++ b/packages/voice-assistant/src/server/agent/tts-manager.ts @@ -25,20 +25,49 @@ export class TTSManager { */ public async generateAndWaitForPlayback( text: string, - emitMessage: (msg: SessionOutboundMessage) => void + emitMessage: (msg: SessionOutboundMessage) => void, + abortSignal?: AbortSignal ): Promise { + // Check if already aborted + if (abortSignal?.aborted) { + throw new Error("TTS playback aborted"); + } + // Generate TTS audio const { audio, format } = await synthesizeSpeech(text); // Create unique ID for this audio segment const audioId = uuidv4(); + // Store abort handler reference outside Promise constructor + let onAbort: (() => void) | undefined; + // Create promise that will be resolved when client confirms playback const playbackPromise = new Promise((resolve, reject) => { // Store handlers (no timeout - will resolve when client confirms or connection closes) this.pendingPlaybacks.set(audioId, { resolve, reject }); + + // Handle abort signal + if (abortSignal) { + onAbort = () => { + // Clean up pending playback + this.pendingPlaybacks.delete(audioId); + // Reject with abort error + reject(new Error("TTS playback aborted")); + }; + + // Listen for abort (once: true for auto-cleanup if abort fires) + abortSignal.addEventListener("abort", onAbort, { once: true }); + } }); + // Clean up abort listener when promise settles (in case abort never fires) + if (onAbort) { + playbackPromise.finally(() => { + abortSignal!.removeEventListener("abort", onAbort!); + }); + } + // Emit audio output message emitMessage({ type: "audio_output", diff --git a/packages/voice-assistant/src/server/session.ts b/packages/voice-assistant/src/server/session.ts index 3cdeb8390..4db3ec4b7 100644 --- a/packages/voice-assistant/src/server/session.ts +++ b/packages/voice-assistant/src/server/session.ts @@ -332,7 +332,8 @@ export class Session { if (enableTTS) { pendingTTS = this.ttsManager.generateAndWaitForPlayback( textBuffer, - (msg) => this.emit(msg) + (msg) => this.emit(msg), + this.abortController.signal ); }