Skip to content

feat(twilio): a flow can make the relay wait quietly while the caller is busy on their phone - #1492

Merged
yileicn merged 1 commit into
SciSharp:masterfrom
yuyixg:features/relay-quiet-wait
Nov 13, 2026
Merged

yileicn merged 1 commit into
SciSharp:masterfrom
yuyixg:features/relay-quiet-wait

Conversation

@yuyixg

@yuyixg yuyixg commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Why

A phone flow that asks the caller to do something on their phone (check that a text arrived, fill in a form it texted them) expects silence. The idle prompts read that silence as a dead line: "Sorry, I didn't catch that." five seconds in, then "Are you still there?", then a hang-up about twenty-five seconds in, before the flow could say what was still due, such as a confirmation number.

What

Two conversation states a flow sets, read as each turn's reply comes in (a reply with nothing to say still updates them, so a flow that answers the caller's asides with nothing keeps the wait):

State Effect
twilio_relay_quiet_wait = "true" The idle watcher says only a soft check-in (prompt quiet-wait-check-in, default "I'm still here whenever you're ready.") each QuietWaitSeconds (default 60) and hangs up after MaxQuietWaitPrompts (default 4) of them. No "say it again", no "Are you still there?".
twilio_relay_goodbye_on_silence = text Said as the session hangs up on a caller who stayed quiet, in place of the idle-goodbye prompt, even when IdleAsksAgain is off.

QuietWaitSeconds and MaxQuietWaitPrompts can be set per agent (ConversationRelay:Agents). Without either state, the idle prompts work as before.

Tests

Session tests over a fake WebSocket (not in the repo, which has no Twilio test project):

  • no quiet wait: asked again, then the idle goodbye and the hang-up, as before;
  • quiet wait: only the two check-ins, then the flow's goodbye; the hang-up takes the quiet-wait time, not the idle timeouts; lines kept with their labels;
  • a turn with an empty reply keeps the wait, and a turn that clears it brings the idle prompts back;
  • the flow's goodbye is said on an ordinary idle hang-up even with IdleAsksAgain off.

🤖 Generated with Claude Code

… is busy on their phone

A phone flow that asks the caller to do something on their phone (check a text arrived, fill in a form it texted them)
expects silence, but the idle prompts read it as a dead line: "Sorry, I didn't catch that." five seconds in, a hang-up
some twenty-five seconds in, before the flow could say what was still due (a confirmation number).

Two conversation states, read as each turn's reply comes in (a reply with nothing to say still updates them):
- twilio_relay_quiet_wait: "true" while silence is expected. The idle watcher then says only a soft check-in
  (prompt quiet-wait-check-in, "I'm still here whenever you're ready.") each QuietWaitSeconds (60), and hangs up after
  MaxQuietWaitPrompts (4) of them; both settings can be set per agent.
- twilio_relay_goodbye_on_silence: what is said as the session hangs up on a caller who stayed quiet, in place of the
  idle-goodbye prompt, and said even when IdleAsksAgain is off.

Without either state the idle prompts are as before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@yileicn

yileicn commented Nov 13, 2026

Copy link
Copy Markdown
Member

✅ AegisCode Code Review Report: Pass

Counted: 6 Low. None of them blocks this change.
Full report · review 11502 · commit b71e22cd

Low · .../BotSharp.Plugin.Twilio/Models/ConversationRelayStates.cs:27 · Constant naming: Constant 'QuietWait' (line 27) is not UPPER_CASE

Constant 'QuietWait' (line 27) is not UPPER_CASE

Suggested fix: [Validated by static analysis] Rename 'QuietWait' to 'QUIET_WAIT'

Low · .../BotSharp.Plugin.Twilio/Models/ConversationRelayStates.cs:34 · Constant naming: Constant 'GoodbyeOnSilence' (line 34) is not UPPER_CASE

Constant 'GoodbyeOnSilence' (line 34) is not UPPER_CASE

Suggested fix: [Validated by static analysis] Rename 'GoodbyeOnSilence' to 'GOODBYE_ON_SILENCE'

Low · .../BotSharp.Plugin.Twilio/Services/ConversationRelaySession.cs:23 · Constant naming: Constant 'DefaultQuietWaitCheckInText' (line 23) is not UPPER_CASE

Constant 'DefaultQuietWaitCheckInText' (line 23) is not UPPER_CASE

Suggested fix: [Validated by static analysis] Rename 'DefaultQuietWaitCheckInText' to 'DEFAULT_QUIET_WAIT_CHECK_IN_TEXT'

Low · .../BotSharp.Plugin.Twilio/Services/ConversationRelaySession.cs:861 · Line length: This line is 135 characters long; the maximum is 125

This line is 135 characters long; the maximum is 125

Suggested fix: [Validated by static analysis] Wrap the line so that no line exceeds 125 characters

Low · .../BotSharp.Plugin.Twilio/Services/ConversationRelaySession.cs:880 · Line length: This line is 137 characters long; the maximum is 125

This line is 137 characters long; the maximum is 125

Suggested fix: [Validated by static analysis] Wrap the line so that no line exceeds 125 characters

Low · .../BotSharp.Plugin.Twilio/Services/TwilioConversationProcessor.cs:172 · Line length: This line is 132 characters long; the maximum is 125

This line is 132 characters long; the maximum is 125

Suggested fix: [Validated by static analysis] Wrap the line so that no line exceeds 125 characters

@yileicn
yileicn requested a lite review from Copilot November 13, 2026 08:42
@yileicn
yileicn merged commit cd1df97 into SciSharp:master Nov 13, 2026
4 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The change still has an active issue because new quiet-wait timeout, state-transition, and custom-goodbye branches lack the requested fake-WebSocket test coverage.

1 open issue
What changed in this PR

Adds flow-controlled quiet waiting for Twilio ConversationRelay calls, allowing callers time to complete phone-based tasks without triggering normal idle prompts or premature hang-ups.

Changes:

  • Adds global and per-agent quiet-wait timing and prompt-count settings.
  • Propagates quiet-wait and custom goodbye states from conversation flows into session handling.
  • Updates idle monitoring to issue soft check-ins and support flow-provided goodbye text.
File Description
src/​Plugins/​BotSharp.Plugin.Twilio/​Settings/​ConversationRelaySetting.cs Defines quiet-wait defaults and per-agent setting resolution.
src/​Plugins/​BotSharp.Plugin.Twilio/​Settings/​ConversationRelayAgentSetting.cs Adds nullable per-agent quiet-wait overrides.
src/​Plugins/​BotSharp.Plugin.Twilio/​Services/​TwilioConversationProcessor.cs Captures quiet-wait and silence-goodbye states on assistant replies.
src/​Plugins/​BotSharp.Plugin.Twilio/​Services/​ConversationRelaySession.cs Implements quiet-wait idle prompts, timing, limits, and custom goodbye handling.
src/​Plugins/​BotSharp.Plugin.Twilio/​Models/​ConversationRelayStates.cs Defines flow state keys for quiet waiting and silence goodbye text.
src/​Plugins/​BotSharp.Plugin.Twilio/​Models/​ConversationRelayLabels.cs Updates idle-prompt documentation for quiet-wait check-ins.
src/​Plugins/​BotSharp.Plugin.Twilio/​Models/​AssistantMessage.cs Carries quiet-wait metadata between processing and session layers.

Review effort: Lite

Comment on lines +856 to +861
if (_idlePrompts < (quietWait ? relay.MaxQuietWaitPrompts : relay.MaxIdlePrompts))
{
_idlePrompts++;
askAgain = true;
activity = _activity;
question = relay.IdleAsksAgain && stage == 0 ? ConversationRelayLines.LastQuestion(_lastLine) : null;
question = !quietWait && relay.IdleAsksAgain && stage == 0 ? ConversationRelayLines.LastQuestion(_lastLine) : null;
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.

3 participants