@@ -77,6 +77,7 @@ export interface VoiceOptions {
7777 maxToolSteps : number ;
7878 preemptiveGeneration : boolean ;
7979 userAwayTimeout ?: number | null ;
80+ aecWarmupDuration : number | null ;
8081 useTtsAlignedTranscript : boolean ;
8182}
8283
@@ -90,6 +91,7 @@ const defaultVoiceOptions: VoiceOptions = {
9091 maxToolSteps : 3 ,
9192 preemptiveGeneration : false ,
9293 userAwayTimeout : 15.0 ,
94+ aecWarmupDuration : 3000 ,
9395 useTtsAlignedTranscript : true ,
9496} as const ;
9597
@@ -158,6 +160,8 @@ export class AgentSession<
158160 private closingTask : Promise < void > | null = null ;
159161 private userAwayTimer : NodeJS . Timeout | null = null ;
160162
163+ private _aecWarmupTimer : NodeJS . Timeout | null = null ;
164+
161165 // Connection options for STT, LLM, and TTS
162166 private _connOptions : ResolvedSessionConnectOptions ;
163167
@@ -169,6 +173,9 @@ export class AgentSession<
169173 private userSpeakingSpan ?: Span ;
170174 private agentSpeakingSpan ?: Span ;
171175
176+ /** @internal */
177+ _aecWarmupRemaining = 0 ;
178+
172179 /** @internal */
173180 _recorderIO ?: RecorderIO ;
174181
@@ -241,6 +248,7 @@ export class AgentSession<
241248 // This is the "global" chat context, it holds the entire conversation history
242249 this . _chatCtx = ChatContext . empty ( ) ;
243250 this . options = { ...defaultVoiceOptions , ...voiceOptions } ;
251+ this . _aecWarmupRemaining = this . options . aecWarmupDuration ?? 0 ;
244252
245253 this . _onUserInputTranscribed = this . _onUserInputTranscribed . bind ( this ) ;
246254 this . on ( AgentSessionEventTypes . UserInputTranscribed , this . _onUserInputTranscribed ) ;
@@ -845,6 +853,14 @@ export class AgentSession<
845853 this . agentSpeakingSpan = undefined ;
846854 }
847855
856+ if ( state === 'speaking' && this . _aecWarmupRemaining > 0 && this . _aecWarmupTimer === null ) {
857+ this . _aecWarmupTimer = setTimeout ( ( ) => this . _onAecWarmupExpired ( ) , this . _aecWarmupRemaining ) ;
858+ this . logger . debug (
859+ { warmupDurationMs : this . _aecWarmupRemaining } ,
860+ 'aec warmup active, disabling interruptions' ,
861+ ) ;
862+ }
863+
848864 const oldState = this . _agentState ;
849865 this . _agentState = state ;
850866
@@ -938,6 +954,19 @@ export class AgentSession<
938954 }
939955 }
940956
957+ /** @internal */
958+ _onAecWarmupExpired ( ) : void {
959+ if ( this . _aecWarmupRemaining > 0 ) {
960+ this . logger . debug ( 'aec warmup expired, re-enabling interruptions' ) ;
961+ }
962+
963+ this . _aecWarmupRemaining = 0 ;
964+ if ( this . _aecWarmupTimer !== null ) {
965+ clearTimeout ( this . _aecWarmupTimer ) ;
966+ this . _aecWarmupTimer = null ;
967+ }
968+ }
969+
941970 private _onUserInputTranscribed ( ev : UserInputTranscribedEvent ) : void {
942971 if ( this . userState === 'away' && ev . isFinal ) {
943972 this . logger . debug ( 'User returned from away state due to speech input' ) ;
@@ -969,6 +998,7 @@ export class AgentSession<
969998 }
970999
9711000 this . _cancelUserAwayTimer ( ) ;
1001+ this . _onAecWarmupExpired ( ) ;
9721002 this . off ( AgentSessionEventTypes . UserInputTranscribed , this . _onUserInputTranscribed ) ;
9731003
9741004 if ( this . activity ) {
0 commit comments