Skip to content

Twilio ConversationRelay: report a caller hanging up, and a cut-in on text spoken after the turn - #1475

Open
yuyixg wants to merge 2 commits into
SciSharp:masterfrom
yuyixg:features/relay-caller-hangup-hook
Open

yuyixg wants to merge 2 commits into
SciSharp:masterfrom
yuyixg:features/relay-caller-hangup-hook

Conversation

@yuyixg

@yuyixg yuyixg commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Two additions to the Twilio ConversationRelay plugin, for agents that need to know how a call ended and whether the caller heard a line in full.

The problems

  1. A caller hanging up reaches no hook. ITwilioSessionHook reports the agent hanging up (OnAgentHangUp) and transferring (OnAgentTransferring). When the caller hangs up, the session ends with no hook at all, so an agent can't act on the end of most calls, for example to keep the transcript once the call is over.
  2. A cut-in on text spoken after the turn goes unnoticed. Text a tool gives is spoken once the turn is over (GetUnstreamedText). When the caller cuts into it, the history has already kept it whole, and only streamed text gets the cut-off mark. So a flow that reads a line the caller must hear in full, such as a disclaimer they agree to, has to make it uninterruptible.

What this does

  • ITwilioSessionHook.OnCallerHangUp (default no-op) fires from the action callback when the session ends with the call completed and no handoff from the agent.
  • The session notes text said after a turn, and an interrupt that lands on it. The next turn gets ConversationRelayStates.LastReplyCutOff (twilio_relay_last_reply_cut_off) set to "true", and the state is removed otherwise. The flow can then say the line again.

Testing

Covered downstream, where the relay session runs against a fake WebSocket:

  • a caller cutting into text spoken after the turn sets the state at the next turn;
  • text played to the end leaves the state unset.

The caller-hangup path was checked by build only; it has not been run on a real call. There is no test project here that covers the Twilio plugin.

🤖 Generated with Claude Code

yuyixg and others added 2 commits November 2, 2026 17:37
The relay action callback told hooks when the agent hung up or transferred, but a caller hanging up
ended the call with no hook at all, so an agent had no way to act on the end of most calls (e.g. to
keep the transcript once the call is over). OnCallerHangUp fires when the session ends with the
call completed and no handoff from the agent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ter the last one

Text a tool gives is spoken once the turn is over, so when the caller cuts into it the history
has already kept it whole and nothing marks what they missed. A flow that reads a line the
caller must hear in full (a disclaimer they agree to) had to keep it uninterrupted for that
reason.

The session now notes text said after a turn, and an interrupt that lands on it; the next turn
gets twilio_relay_last_reply_cut_off set to "true" (removed otherwise), so the flow can say the
line again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@qodo-code-review

qodo-code-review Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

Grey Divider

Sorry, something went wrong

We weren't able to complete the code review on our side. Please try again manually by commenting /agentic_review on this PR.

Grey Divider

Qodo Logo

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Report ConversationRelay caller hang-ups and interrupted post-turn speech

✨ Enhancement 🕐 20-40 Minutes

Grey Divider

AI Description

• Notify session hooks when a caller hangs up without an agent handoff.
• Tell the next turn when the caller interrupted text spoken after the previous turn.
• Clear the interruption state on turns without a post-turn cut-in.
Diagram

graph TD
  R["Twilio Relay"] --> A["Action callback"] --> H["Caller hang-up hook"]
  R --> S["Relay session"] --> C["Turn callbacks"] --> P["Conversation processor"] --> T["Conversation state"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Record post-turn playback cut-offs in conversation history
  • ➕ Keeps the interruption alongside the transcript for later readers.
  • ➖ Requires revising text already stored when playback occurs after the turn.
  • ➖ Adds history-mutation complexity to a signal the next turn needs immediately.

Recommendation: Use the proposed next-turn state for flow decisions: it avoids retroactively changing stored text, while the action callback provides call-status context for the hang-up hook. Review the timing of the playback flag against WebSocket interrupts, since that determines whether the signal accurately means the caller missed text.

Files changed (6) +58 / -1

Enhancement (6) +58 / -1
TwilioConversationRelayController.csEmit a hook for completed calls without an agent hang-up +5/-0

Emit a hook for completed calls without an agent hang-up

• The relay action callback now emits OnCallerHangUp when the call is completed and the agent-hang-up branch was not taken. Transfer and reconnect handling remain ahead of this branch.

src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioConversationRelayController.cs

ITwilioSessionHook.csExpose an optional caller-hang-up hook +9/-0

Expose an optional caller-hang-up hook

• Adds OnCallerHangUp with a default no-op implementation, allowing existing hook implementations to remain compatible.

src/Plugins/BotSharp.Plugin.Twilio/Interfaces/ITwilioSessionHook.cs

AgentTurnCallbacks.csCarry the previous post-turn cut-off into an agent turn +6/-0

Carry the previous post-turn cut-off into an agent turn

• Adds a boolean callback property that conveys whether the caller cut into text spoken after the preceding turn.

src/Plugins/BotSharp.Plugin.Twilio/Models/AgentTurnCallbacks.cs

ConversationRelayStates.csDefine the last-reply cut-off state key +7/-0

Define the last-reply cut-off state key

• Adds twilio_relay_last_reply_cut_off for flows that need to detect missed post-turn speech.

src/Plugins/BotSharp.Plugin.Twilio/Models/ConversationRelayStates.cs

ConversationRelaySession.csTrack interruptions of text spoken after a turn +23/-1

Track interruptions of text spoken after a turn

• Records when unstreamed reply text is about to be spoken and whether a subsequent interrupt occurs. Passes that result to the next turn's callbacks, then resets the tracking flags.

src/Plugins/BotSharp.Plugin.Twilio/Services/ConversationRelaySession.cs

TwilioConversationProcessor.csExpose the cut-off marker to the next conversation turn +8/-0

Expose the cut-off marker to the next conversation turn

• Sets the application conversation state to "true" when the callback reports a post-turn cut-off; otherwise removes the state before the turn proceeds.

src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioConversationProcessor.cs

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