Files
jambonz-feature-server/test
Hoan Luu HuuandClaude Opus 5.5 287883c148 feat(elevenlabs): forward DTMF to the agent and support llm:update (#1592)
* feat(elevenlabs): forward DTMF to the agent and support llm:update

Adds opt-in llmOptions.dtmf {enable, interDigitTimeout, finishOnKey}.
Digits are buffered until finishOnKey, the inter-digit timeout or 50
digits, then sent as a user_message with source_medium "dtmf". Each
key press also sends user_activity so the agent turn timeout does not
fire while the caller is typing.

llm:update now forwards user_message, user_activity and
contextual_update client events instead of ignoring them.

Client events escape "|" so payloads containing it are no longer
split by the api argument delimiter (this also fixes tool results).

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

* feat(elevenlabs): send DTMF per digit like ultravox, drop llmOptions.dtmf

Match the ultravox adapter: DTMF is always forwarded, with no llmOptions
setting. Each digit is sent as soon as it arrives, as a user_message with
source_medium "dtmf". Digit buffering, the inter-digit timeout and
finishOnKey are left to the agent's dtmf_input_settings.

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

* feat(elevenlabs): buffer DTMF behind llmOptions.dtmf again

Per-digit sending does not work for ElevenLabs. Over the agent
WebSocket each user_message becomes its own turn: "1234#" produced 5
turns, and the agent answered "Are you still there?". The same digits
sent as a single message produce one dtmf turn, "1234".

- restores the opt-in llmOptions.dtmf {enable, interDigitTimeout, finishOnKey}
- digits are buffered and sent as one user_message, source_medium "dtmf"

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

* feat: shared DtmfInput util with gather dtmf semantics

Any llm or agent vendor can turn caller DTMF into whole entries without
copying gather or touching it.

- lib/utils/dtmf-input.js uses gather options: finishOnKey, numDigits,
  minDigits, maxDigits, interDigitTimeout, timeout (15s default, 0 = off)
- no invented defaults: no implicit "#", no 2s timeout, no 50-digit cap
- minDigits defaults to 1; in gather an unset minDigits never arms the
  inter-digit timer
- stop() removes only its own dtmf listener
- elevenlabs uses it behind llmOptions.dtmf.enable

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

* fix(dtmf): review fixes for DtmfInput and elevenlabs

- inter-digit timer replaces the overall timeout while typing, as in gather
- warn when no option can ever complete an entry
- no DtmfInput when the session connects after kill (listener leak)
- llm:update rejects user_message/contextual_update without string text,
  since an ElevenLabs error event ends the conversation
- unit tests use mock timers instead of wall-clock waits

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:23:11 -04:00
..
2025-06-28 15:01:09 -04:00
2025-06-28 15:01:09 -04:00
2023-06-03 08:16:05 -04:00
2020-01-07 10:34:03 -05:00
2023-12-28 14:59:59 -05:00
2023-06-03 09:09:49 -04:00
2023-06-09 14:54:53 -04:00
2023-06-03 08:16:05 -04:00
2023-02-06 08:06:41 -05:00