From 4437576acc3bc0120fb8fb19e58ec8ba4ae7af4f Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Thu, 26 Feb 2026 21:45:47 +0100
Subject: [PATCH] Add playback acknowledgement from bot to gateway.
Send ttsPlaybackAck events (queued/completed/failed) from bot and forward them as ttsDeliveryStatus SSE updates for session diagnostics.
Made-with: Cursor
---
src/bot/orchestrator.ts | 31 +++++++++++++++++++++++++++++--
src/types/index.ts | 14 +++++++++++++-
2 files changed, 42 insertions(+), 3 deletions(-)
diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts
index 458b0de..37631c1 100644
--- a/src/bot/orchestrator.ts
+++ b/src/bot/orchestrator.ts
@@ -7,7 +7,7 @@ import WebSocket from 'ws';
import { config } from '../config';
import { createSessionLogger } from '../utils/logger';
-import { BotSession, BotState, TranscriptEntry, StatusMessage, TranscriptMessage, PlayAudioMessage, ChatMessage, SendChatMessage, AudioChunkMessage } from '../types';
+import { BotSession, BotState, TranscriptEntry, StatusMessage, TranscriptMessage, PlayAudioMessage, ChatMessage, SendChatMessage, AudioChunkMessage, TtsPlaybackAckMessage } from '../types';
import { JoinProcedure } from './joinProcedure';
import { CaptionsProcedure } from './captionsProcedure';
import { AudioProcedure } from './audioProcedure';
@@ -843,7 +843,34 @@ export class BotOrchestrator {
}
}
- await this._audioProcedure.playAudio(audioData, format);
+ this._sendTtsPlaybackAck('queued', format, audioData.length, 'Audio queued for playback');
+ try {
+ await this._audioProcedure.playAudio(audioData, format);
+ this._sendTtsPlaybackAck('completed', format, audioData.length, 'Audio playback completed');
+ } catch (error) {
+ this._sendTtsPlaybackAck('failed', format, audioData.length, String(error));
+ throw error;
+ }
+ }
+
+ private _sendTtsPlaybackAck(
+ status: 'queued' | 'completed' | 'failed',
+ format: 'mp3' | 'wav' | 'pcm',
+ bytesBase64?: number,
+ message?: string,
+ ): void {
+ const ack: TtsPlaybackAckMessage = {
+ type: 'ttsPlaybackAck',
+ sessionId: this._sessionId,
+ playback: {
+ status,
+ format,
+ bytesBase64,
+ message,
+ timestamp: new Date().toISOString(),
+ },
+ };
+ this._sendToGateway(ack);
}
/**
diff --git a/src/types/index.ts b/src/types/index.ts
index 5912172..3d71dfb 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -73,8 +73,20 @@ export interface AudioChunkMessage {
};
}
+export interface TtsPlaybackAckMessage {
+ type: 'ttsPlaybackAck';
+ sessionId: string;
+ playback: {
+ status: 'queued' | 'completed' | 'failed';
+ format: 'mp3' | 'pcm' | 'wav';
+ bytesBase64?: number;
+ message?: string;
+ timestamp: string;
+ };
+}
+
export type GatewayToBot = PlayAudioMessage | JoinMeetingMessage | LeaveMeetingMessage | SendChatMessage;
-export type BotToGateway = TranscriptMessage | StatusMessage | ChatMessage | AudioChunkMessage;
+export type BotToGateway = TranscriptMessage | StatusMessage | ChatMessage | AudioChunkMessage | TtsPlaybackAckMessage;
// Bot State
export type BotState =