@langwatch/scenario
    Preparing search index...

    Class PipecatAgentAdapter

    Adapter that drives a running Pipecat bot over the Twilio Media Streams WS protocol. Default audio format = µ-law 8 kHz mono, which is what Pipecat's TwilioFrameSerializer expects.

    Hierarchy (View Summary)

    Index
    _voiceTurnContext?: Context

    Live OTel context of the CURRENT voice.turn, published by defaultVoiceCall for background-receive-loop adapters (Pipecat/Twilio) to parent their detached-callback recv spans under the turn (#774 — the reusable pattern Twilio PR5 inherits). undefined between turns, so a callback firing outside a turn skips its span rather than parenting under a closed turn. Internal (underscore) — not a public API.

    agentHungUp: boolean = false

    SET when the AGENT deliberately ended the call (e.g. an ElevenLabs hosted agent invoking the end_call system tool), as opposed to the transport dropping. A scripted turn arriving after this concludes the conversation instead of failing the run — the agent behaved as designed. Assertions and judges can read it to reason about WHO ended the call.

    agentSpeakingEvent?: AgentSpeakingEvent

    Set when the adapter has emitted its first agent audio chunk for the current turn — gates timing-based barge-in. Concrete adapters expose this so scenario.interrupt can wait for real speech before firing the interruption. Optional: adapters without server-VAD-style interrupt sequencing can leave it undefined.

    audioFormat: string
    callSid?: string
    capabilities: AdapterCapabilities = ...

    Declaration of what this adapter can and cannot do. Concrete subclasses MUST publish a non-default value; the base instance defaults to "nothing supported" so capability-gated steps fail safely when an adapter forgets to declare.

    name?: string
    responseMaxDuration: number = 30.0

    Hard cap on a single agent turn's audio. Prevents runaway loops if a transport never signals end-of-stream. 30s = a long sentence.

    responseTailSilence: number = 0.6

    Tail silence: once the first agent chunk arrives, keep draining receiveAudio until no chunk shows up within this many seconds — that's how we detect the agent finished talking.

    responseTimeout: number = 60.0

    Seconds to wait for agent audio after sending user audio: the STT + LLM + TTS budget for one agent turn. Kept identical to Python's VoiceAgentAdapter.response_timeout so the same scenario passes or fails the same way in both SDKs.

    Raise it for an agent that runs a tool call or a retrieval step before it speaks:

    const agent = elevenLabsAgent({ agentId, apiKey });
    agent.responseTimeout = 180; // wait up to 3 minutes
    role: AgentRole = AgentRole.AGENT
    sampleRate: number
    signalingUrl?: string
    streamingTranscript?: string

    Incremental transcript text emitted while the agent speaks. Populated by adapters that advertise capabilities.streamingTranscripts. Read by scenario.interrupt when afterWords: N is set.

    streamSid?: string
    transcriptGraceWait: number = 2.0

    Bounded grace-wait (seconds) for the agent turn's transcript AFTER audio drains (#734). Audio silence closes the turn (responseTailSilence), but a live voice agent (hosted ElevenLabs) delivers the turn's text on a SEPARATE socket event (agent_responselastAgentTranscript). When that event lands after the audio-silence boundary, snapshotting lastAgentTranscript at drain-close reads null and the turn reaches the text-only simulator as a bare [audio message] — the simulator then fabricates.

    The default call() flow (defaultVoiceCall) polls this field up to this ceiling for a pending transcript before reading it. It short-circuits the INSTANT lastAgentTranscript is already set (zero added latency on the happy path — the common case where the transcript won the race) and only elapses when the transcript genuinely never arrives, so a real ElevenLabs drop still terminates the turn. Set to 0 to disable the wait.

    transport: PipecatTransport
    url?: string
    • Send a Twilio clear frame and truncate the JS-side receive buffer.

      Side effects:

      1. inbox.queue cleared — discards buffered PCM chunks not yet consumed by drainAgentResponse.
      2. mulawChunks cleared — discards partially-accumulated µ-law that hasn't been resampled yet.
      3. Active receiveAudio waiter (if any) woken with an empty chunk so drainAgentResponse breaks its loop immediately without a timeout.
      4. interruptPhase set to "interrupted" — the NEXT receiveAudio call returns an empty sentinel (stops the drain loop after the waiter wake), and bufferMulaw discards late-arriving WS frames until the recovery turn's sendAudio resets the phase to "idle".

      Returns Promise<void>