Skip to content

Releases: wiringai/mod_earshot

v0.4.1

Choose a tag to compare

@xpertvoip xpertvoip released this 04 Oct 14:54
5956c14

Fixed

  • pause now pauses. It stops caller audio and DTMF going to the agent, holds queued
    agent audio and suspends barge-in until resume (mod_audio_stream semantics, which
    controllers rely on for "pause until pickCall"). Before, it only closed the ready-gate: the
    agent kept hearing the caller while "paused", and in the default ready=firstframe mode the
    next agent frame reopened the gate. status reports paused.
  • Agent audio frames of any size are played whole. The native/Deepgram/Vapi binary path
    (clamped at 8 KB: 256 ms of L16 at 16 kHz), the pipecat protobuf path and the base64 JSON
    paths (16 KB) decoded into one fixed buffer and silently dropped the rest of a larger frame.
    Large frames are now decoded in pieces. Regression test: test/test_proto_audio.c
    (native L16/PCMU, pipecat protobuf, OpenAI base64 delta).
  • mask on|off covers every stream on the channel, including fan-out transcription or
    supervisor streams, from the operator API and from the agent's mask command, and a stream
    started inside an open window starts masked before its media bug goes live. Before, it
    masked only the stream it was addressed to, so a side stream kept receiving the card number.
  • Twilio marks are echoed at their position in the audio, after the audio queued before
    each mark has played, in order. Before, every pending mark was echoed only once the whole
    play buffer drained. Marks pinned to audio discarded by a flush/clear are still echoed at
    once (Twilio reports cleared marks too).
  • The caller hears FreeSWITCH's own audio while the agent is silent. WRITE_REPLACE used to
    replace every outbound frame with silence when no agent audio was queued, which muted the
    controller's ringback (uuid_broadcast) and hold music until the agent's first word. The
    frame is now left untouched unless agent audio is being played; for 300 ms after the last
    agent frame the line stays silent, so gaps between chunks of one utterance do not let the
    underlying audio blip through. A barge-in hands the line back at once.
  • Cross-thread control flags (ready, paused, masking) are read and written with
    atomics instead of plain loads and stores.

Changed

  • es_ws.h no longer calls itself a reviewed draft; the header describes the shared
    service-thread pool that has been in place since 0.1.0.
  • Docs: new docs/MODULE-VS-AGENT.md explaining what module-side VAD is (energy endpointing, not a
    turn model), when to leave it off (agents with their own VAD), and why edge latency metrics differ
    from agent-side metrics; README/FEATURES/WHY say "VAD endpointing" instead of "turn detection".

Binaries

Built by CI from this tag on Debian 12 (bookworm), amd64, against the SignalWire FreeSWITCH packages:

File FreeSWITCH
mod-earshot_0.4.1_fs1.11_debian12_amd64.deb / .so 1.11.x
mod-earshot_0.4.1_fs1.10_debian12_amd64.deb / .so 1.10.x

Needs libwebsockets (apt install libwebsockets16 on bookworm). Checksums in SHA256SUMS. Other distributions: build from source, see the README quickstart.

v0.4.0

Choose a tag to compare

@xpertvoip xpertvoip released this 03 Oct 06:21
dbeb523

[0.4.0] — 2026-10-03

Added

  • The uuid_audio_stream compat API now speaks the mod_audio_stream agent dialect, so
    agents and controllers written for that module run unchanged behind Earshot:
    {"type":"streamAudio",...} audio is played to the caller (L16 at the stated rate,
    resampled to the channel rate when they differ); {"control":{"command":...}}
    messages are surfaced as the mod_audio_stream::json event, with clear also
    acting as a barge-in; clear_playback drops queued agent audio; the metadata after
    <rate> is sent to the agent as the first text frame; connect failures and closes
    are mirrored as mod_audio_stream::error / mod_audio_stream::disconnect.

Changed

  • README: removed the commercial support section.

mod_earshot 0.3.0

Choose a tag to compare

@xpertvoip xpertvoip released this 15 Aug 05:28
ee10e1d

Security hardening of the agent control channel, a concurrency fix that keeps audio smooth under load, and a batch of reliability + robustness fixes.

⚠️ Security — control channel. If you use the opt-in control channel (commands=), upgrade: this release closes a command-injection → RCE class. Default deployments (no commands=) were never exposed.

Security

  • Control-channel argument validation (es_cmdguard): rejects FreeSWITCH's app::args exec form, the inline dialplan, .. path traversal, whitespace positional-injection, and exec-triggering variables; blocked attempts are recorded in the earshot::command audit.
  • Per-action commands= allowlist (commands=play,hangup) — enabling one action no longer grants transfer/setvar/record.
  • setvar is fail-closed behind a setvars=<name,...> name allowlist (nothing settable by default).

Changed

  • Control-channel commands run on a worker pool, not the WebSocket service thread — a blocking uuid_transfer/bridge/record/broadcast no longer stalls audio for other calls sharing that thread. A call's commands keep their submission order.

Fixed

  • Use-after-free racing the command result-echo against media-bug teardown (per-stream lock + fresh session relocation).
  • WebSocket receive path now gates on suppress_events, closing an orphan-teardown window where a late frame could touch a freed play buffer.
  • Negative reconnect jitter (unsigned modulo).
  • Mark-queue leak when the 64-deep queue is full.

Added

  • Start-option robustness — ES_MAX_ARGV 16 → 48 so the full documented option set parses (a list past the 13th option was silently mis-parsed), plus a token-limit warning.
  • Warnings on unrecognized start options (key only, never the value) and on unsupported stereo/mixed audio_stream mix-types.
  • Memory & capacity guidance in docs/ARCHITECTURE.md (per-stream play buffer ≤ 2 MB, per-session greeting PCM ≤ ~1.4 MB).

Build / CI

  • Module now compiles against real FreeSWITCH on same-repo PRs; the .deb/module build job fixed; malformed-input fuzzing for es_proto_on_text.

Artifacts: mod_earshot.so + mod-earshot_0.3.0_amd64.deb for FreeSWITCH 1.10 and 1.11.

Full details in CHANGELOG.md.

v0.2.0 — the adapter release

Choose a tag to compare

@xpertvoip xpertvoip released this 06 Aug 16:25
36e8417

Every protocol adapter is now live-validated against its real vendor — plus a barge-in
policy engine, caller-context + welcome-greeting, and box-level mTLS.

Adapters

  • New: vapi (WebSocket transport), assemblyai + cartesia (streaming STT → new
    earshot::transcript event).
  • Fixed to work against current APIs: elevenlabs (required convai subprotocol +
    respects codec/rate), gemini (JSON-over-binary frames + realtimeInput.audio +
    current model).
  • All ten adapters validated: native · twilio · openai · deepgram · elevenlabs · gemini ·
    pipecat · vapi · assemblyai · cartesia.

Features

  • Barge-in policy engine — interruptible=none|dtmf|speech|any, sensitivity,
    ignore_backchannel, barge_min_ms, barge_fade_ms.
  • Caller context + welcome greeting — EARSHOT_META → X-Earshot-Meta header;
    greeting= / EARSHOT_GREETING.
  • Box-level mTLS + custom CA — EARSHOT_TLS_CLIENT_CERT / _KEY / EARSHOT_TLS_CA.
  • New event earshot::transcript (STT).

Full details in CHANGELOG.md.

Full changelog: v0.1.0...v0.2.0

v0.1.0 — Within Earshot (first public release)

Choose a tag to compare

@xpertvoip xpertvoip released this 05 Aug 08:25

mod_earshot 0.1.0 — first public release

A FreeSWITCH module that bridges live call audio to your AI voice agent over one WebSocket — full-duplex, with the agent's voice played back reliably on the same leg. A drop-in mod_audio_stream alternative whose playback actually works, with seven protocol adapters, module-side turn-taking, agent-driven call control, PCI masking, multi-stream fan-out, and typed ESL events.

The transport, full-duplex audio path, and playout are validated end-to-end on real SIP calls with real audio — live OpenAI Realtime and Deepgram Voice Agent sessions (Deepgram from a hardware SIP phone), plus a shared-pool concurrency + soak load test.

Highlights

  • Playback that works — the WRITE_REPLACE agent→caller path validated with real audio, full duplex, on one leg. This is the wedge the incumbent module never delivered.
  • 7 protocol adapters — native, twilio, openai (Realtime), deepgram (Voice Agent), elevenlabs, gemini (Live), pipecat. Swap vendors with one word (proto=).
  • Turn-taking for any agent — module-side VAD → speech_started/speech_stopped, a ready-gate (no "answered into silence"), and speech-triggered barge-in.
  • Agent drives the call — opt-in, whitelisted control channel (transfer / hangup / DTMF / play / record / hold / bridge / setvar), every action audited via earshot::command.
  • PCI/PII masking — mute caller audio + redact DTMF to the agent during card entry, while digits still reach a secure collector.
  • Multi-stream fan-out — agent + live transcription + supervisor on one call.
  • Scales with sessions, not threads — all streams share a small pool of libwebsockets service contexts (~1 MB/session vs ~17 MB thread-per-stream), so a small box holds thousands of sessions where thread-per-stream hit a wall near ~420. Validated to 150 concurrent + a 12-minute flat-memory soak, mass-teardown/RST-storm without a crash, and a ThreadSanitizer-clean fuzz run.
  • Observable — typed earshot:: custom events over the FreeSWITCH event socket (ESL) + latency KPIs (time-to-first-audio, per-turn response, WebSocket RTT), keyed to a two-key trace (SIP Call-ID + channel UUID).
  • Drop-in migration — uuid_audio_stream / audio_stream compat runs existing mod_audio_stream dialplans unchanged.

Adapter validation (stated per vendor, not blanket)

native · openai · deepgram — live-validated on a real call · pipecat — against Pipecat's official protobuf schema · twilio — WebSocket echo round-trip · elevenlabs · gemini — implemented, not yet live-validated (see the adapter status table in the README).

Install

Needs FreeSWITCH dev headers + libwebsockets-dev:

cmake -S . -B build && cmake --build build
sudo cmake --install build        # -> /usr/lib/freeswitch/mod/mod_earshot.so
fs_cli -x "load mod_earshot"

Quickstart recipes in QUICKSTART.md, full option/verb/event reference in docs/API.md.

License: MIT · Full changelog: CHANGELOG.md