Environment: livekit-agents 1.6.4 (Python)
Description
livekit.agents.beta.tools.send_dtmf_events resolves its publish target via the job context:
job_ctx = get_job_context()
await job_ctx.room.local_participant.publish_dtmf(code=code, digit=event.value)
The job's room is always the original caller room. But the natural use of this tool with WarmTransferTask — WarmTransferTask(tools=[send_dtmf_events], ...) so the consult agent can navigate an IVR/phone tree on the human-agent leg — executes the tool on the consult session, which runs in a separate {caller_room}-human-agent room.
Result on a live call: the DTMF was relayed out through the caller room's SIP participant, so the caller on hold heard carrier-regenerated beeps over the hold music, and the IVR on the human-agent leg never received the digits (the consult agent then fell back to speaking at the menu, which does nothing).
Steps to reproduce
- Agent on a SIP call with a caller; trigger a
WarmTransferTask whose sip_call_to answers with a DTMF menu ("press 1 for sales…").
- Pass
tools=[send_dtmf_events] (plus instructions to use it) to the task.
- The consult agent calls the tool. Observe: digits arrive on the caller leg; the IVR never advances.
Expected behavior
DTMF should be relayed to the SIP participant(s) in the room of the session executing the tool.
Suggested fix
Resolve the room from the RunContext instead of the job context:
room = context.session.room_io.room
await room.local_participant.publish_dtmf(code=code, digit=event.value)
This is behavior-preserving for the primary ivr_detection=True case (session room == job room) and correct inside WarmTransferTask. We're running exactly this as a local replacement tool and confirmed on live calls that the menu advances.
Related observation
WarmTransferTask._dial_human_agent builds the consult AgentSession copying only vad/llm/stt/tts/turn_detection, so session-level ivr_detection (and its injected tools) never reaches the leg that actually encounters phone trees — passing tools=[send_dtmf_events] explicitly is currently the only public way to get DTMF there, which is how this surfaces. A ? in the docs or an ivr_detection-style option on the task might be worth considering.
Environment: livekit-agents 1.6.4 (Python)
Description
livekit.agents.beta.tools.send_dtmf_eventsresolves its publish target via the job context:The job's room is always the original caller room. But the natural use of this tool with
WarmTransferTask—WarmTransferTask(tools=[send_dtmf_events], ...)so the consult agent can navigate an IVR/phone tree on the human-agent leg — executes the tool on the consult session, which runs in a separate{caller_room}-human-agentroom.Result on a live call: the DTMF was relayed out through the caller room's SIP participant, so the caller on hold heard carrier-regenerated beeps over the hold music, and the IVR on the human-agent leg never received the digits (the consult agent then fell back to speaking at the menu, which does nothing).
Steps to reproduce
WarmTransferTaskwhosesip_call_toanswers with a DTMF menu ("press 1 for sales…").tools=[send_dtmf_events](plus instructions to use it) to the task.Expected behavior
DTMF should be relayed to the SIP participant(s) in the room of the session executing the tool.
Suggested fix
Resolve the room from the
RunContextinstead of the job context:This is behavior-preserving for the primary
ivr_detection=Truecase (session room == job room) and correct insideWarmTransferTask. We're running exactly this as a local replacement tool and confirmed on live calls that the menu advances.Related observation
WarmTransferTask._dial_human_agentbuilds the consultAgentSessioncopying onlyvad/llm/stt/tts/turn_detection, so session-levelivr_detection(and its injected tools) never reaches the leg that actually encounters phone trees — passingtools=[send_dtmf_events]explicitly is currently the only public way to get DTMF there, which is how this surfaces. A?in the docs or anivr_detection-style option on the task might be worth considering.