Skip to content

fix(warm-transfer): capture job context for post-merge caller-room cleanup#1898

Open
toubatbrian wants to merge 1 commit into
1.5.0from
brian/warm-transfer-fix
Open

fix(warm-transfer): capture job context for post-merge caller-room cleanup#1898
toubatbrian wants to merge 1 commit into
1.5.0from
brian/warm-transfer-fix

Conversation

@toubatbrian

@toubatbrian toubatbrian commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #1895: WarmTransferTask's post-merge cleanup throws an unhandled rejection and leaves the 2-party SIP room undeleted.

After mergeCalls(), the task registers a RoomEvent.ParticipantDisconnected listener on the caller room to delete that room once a party hangs up. The handler called getJobContext(), but it runs from a native @livekit/rtc-node FFI callback whose AsyncLocalStorage context is pinned to FfiClient-singleton creation — not the job's context. So by the time the late event fires (the agent has already left, the session is closed), getJobContext() reads an empty/stale store and throws. The caller room is never deleted.

The fix

  • Capture the JobContext eagerly in onEnter() (while the live context is still available) and use jobCtx.deleteRoom() in the late handler. This avoids the dead getJobContext() lookup and uses the job's own API credentials instead of relying on process env vars.

Also in this PR

  • Refactor to AgentTask.create. WarmTransferTask is rebuilt with the functional createWarmTransferTask factory (now exported) for a more JS-native shape, keeping parity with the Python warm_transfer.py behavior without re-introducing the context bug.
  • Python parity. The result and secondary-room naming now match the Python workflow: WarmTransferResult.humanAgentIdentity, participant identity human-agent-sip, and room suffix -human-agent.
  • waitUntilAborted moved to utils.ts (out of generation.ts) so the task can reuse it, plus a pre-aborted short-circuit that swallows the abandoned promise's late rejection.
  • Example hardening. examples/src/warm_transfer.ts pins agentName to force explicit dispatch. Without an explicit name the worker auto-dispatches into every new room — including the human agent room the task creates — putting a second agent on the line with the human agent and producing overlapping voices.

Test plan

  • Warm transfer end-to-end: caller → agent → transfer_to_human → human agent dialed via SIP → connect_to_callermergeCalls().
  • After the bridge, have a party hang up; confirm the caller room is deleted and no unhandled getJobContext() rejection appears in worker logs.
  • Confirm the human agent hears a single agent (no overlapping voices) with the example running under explicit dispatch.
  • pnpm build:agents.

@changeset-bot

changeset-bot Bot commented Jun 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a1cce94

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 35 packages
Name Type
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-did Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hedra Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-perplexity Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugin-soniox Patch
@livekit/agents-plugin-tavus Patch
@livekit/agents-plugins-test Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

WarmTransferTask's post-merge ParticipantDisconnected handler called getJobContext() from an rtc-node FFI callback whose AsyncLocalStorage context is not the job's, so it threw and left the 2-party SIP room undeleted.

Capture the JobContext eagerly in onEnter() and use jobCtx.deleteRoom() in the late handler. Also refactor the task through AgentTask.create, move waitUntilAborted into utils.ts, align human-agent naming with Python, and keep the example on explicit dispatch to avoid auto-dispatching a second agent into the human-agent room.

Co-authored-by: Cursor <cursoragent@cursor.com>
@toubatbrian toubatbrian force-pushed the brian/warm-transfer-fix branch from e19da67 to a1cce94 Compare June 29, 2026 01:42
@chenghao-mou chenghao-mou self-requested a review June 29, 2026 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant