Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions agents/src/voice/agent_session.ts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Captured activity local variable not used for close(), defeating the purpose of the race-condition fix

The PR captures this.activity into a local activity variable at line 1529 to protect against concurrent mutation during the multiple await points in closeImplInner. However, line 1564 still uses this.activity?.close() instead of activity?.close(). A concurrent _updateActivity call can set this.activity = undefined at agent_session.ts:1176 when it sees this.closing = true (set at line 1524). If that happens between lines 1529 and 1564, the original activity captured in the local variable will never have close() called on it — resulting in a resource leak — while this.activity?.close() becomes a no-op.

(Refers to line 1564)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Original file line number Diff line number Diff line change
Expand Up @@ -1526,25 +1526,26 @@ export class AgentSession<
this._onAecWarmupExpired();
this.off(AgentSessionEventTypes.UserInputTranscribed, this._onUserInputTranscribed);

if (this.activity) {
const activity = this.activity;
if (activity) {
if (!drain) {
try {
await this.activity.interrupt({ force: true }).await;
await activity.interrupt({ force: true }).await;
} catch (error) {
this.logger.warn({ error }, 'Error interrupting activity');
}
}

await this.activity.drain();
await activity.drain();
// wait any uninterruptible speech to finish
await this.activity.currentSpeech?.waitForPlayout();
await activity.currentSpeech?.waitForPlayout();

if (reason !== CloseReason.ERROR) {
this.activity.commitUserTurn({ audioDetached: true, throwIfNotReady: false });
activity.commitUserTurn({ audioDetached: true, throwIfNotReady: false });
}

try {
this.activity.detachAudioInput();
activity.detachAudioInput();
} catch (error) {
// Ignore detach errors during cleanup - source may not have been set
}
Expand Down