npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@telnyx/eva-adapter

v0.1.0

Published

EVA server-native Telnyx media adapter: bridges EVA audio/control to Telnyx Call Control + bidirectional Media Streaming + hosted AI Assistant.

Readme

@telnyx/eva-adapter

Server-native Telnyx media adapter for EVA. A standalone Node.js / TypeScript service that lets an EVA provider wrapper drive a Telnyx AI Assistant call over a Twilio-style media WebSocket — no browser, WebRTC, or Chromium runtime required.

The adapter speaks two protocols:

  • EVA side — a local ws://{host}:{port}/ws endpoint that accepts Twilio Media Streams-style events (start / media / user_speech_start / user_speech_stop / stop) and streams assistant audio back as media frames.
  • Telnyx side — Call Control REST, a public bidirectional Media Streaming WebSocket (/telnyx-media), and a webhook receiver (/telnyx-webhooks).

It bridges μ-law (PCMU, 8 kHz, mono, 160-byte / 20 ms frames) pass-through in both directions, paces playback at 20 ms, and writes the proof artifacts EVA benchmarks need.

Implements VSDK-277. Design source of truth: docs/eva-server-native-telnyx-media-adapter.md (branch feat/eva-local-stream in team-telnyx/telnyx-voice-ai-lib).

Architecture

EVA provider wrapper
  ⇄ ws://localhost:{adapterPort}/ws            # Twilio-style media (EVA side)
  ⇄ TelnyxEvaNodeAdapter
      → Telnyx Call Control REST                # dial/answer, streaming_start, ai_assistant_start
      ⇄ wss://{public}/telnyx-media             # bidirectional Media Streaming (RTP, PCMU)
      ← https://{public}/telnyx-webhooks        # call.answered, streaming.started, …

Session lifecycle, driven by the EVA start event:

EVA start → dial/answer → call.answered
          → streaming_start → media WS connected/start → streaming.started
          → ai_assistant_start (→ conversation id)
          → bidirectional audio bridge
          → cleanup (stop assistant, stop stream, hangup, close sockets, finalize artifacts)

Install & build

Requires Node 20+. Uses npm + TypeScript (ESM, NodeNext) and vitest.

npm ci
npm run build      # tsc -p tsconfig.build.json → dist/
npm run typecheck  # tsc --noEmit (src + tests)
npm run lint       # eslint .
npm test           # vitest run (unit + fake-Telnyx integration)

This package pins the EVA-side beta releases:

Configuration

Copy .env.example and fill in your Telnyx credentials. All variables:

| Variable | Required | Default | Notes | | ------------------------------- | -------- | ------------------ | ---------------------------------------------------- | | TELNYX_API_KEY | yes | — | Telnyx API key | | TELNYX_CONNECTION_ID | yes | — | Call Control connection id | | TELNYX_AI_ASSISTANT_ID | yes | — | Hosted AI Assistant id | | TELNYX_FROM_NUMBER | yes | — | Caller (from) number | | TELNYX_TO_NUMBER | yes | — | Callee (to) number | | TELNYX_PUBLIC_MEDIA_WS_URL | yes | — | Public wss://…/telnyx-media Telnyx connects to | | TELNYX_PUBLIC_WEBHOOK_URL | yes | — | Public https://…/telnyx-webhooks | | TELNYX_STREAM_AUTH_TOKEN | rec. | — | Rejects unauthenticated media upgrades | | TELNYX_WEBHOOK_SIGNING_SECRET | rec. | — | HMAC-SHA256 webhook verification | | TELNYX_EVA_ADAPTER_PORT | no | 8090 | Local EVA /ws port | | TELNYX_EVA_BIND_HOST | no | 127.0.0.1 | EVA /ws bind host | | TELNYX_SETUP_MODE | no | outbound | outbound | inbound | | TELNYX_STREAM_TRACK | no | both_tracks | inbound_track | outbound_track | both_tracks | | TELNYX_EVA_OUTPUT_DIR | no | ./eva-output | Artifact directory | | TELNYX_ENVIRONMENT | no | production | | | TELNYX_EVA_LOG_LEVEL | no | info | debug|info|warn|error|silent | | TELNYX_TIMEOUT_*_MS | no | see .env.example | per-milestone hard timeouts |

The public media + webhook URLs must point at this same process and be reachable by Telnyx. For local development use a tunnel (ngrok / cloudflared) or a deployed staging adapter.

Running

cp .env.example .env   # then edit
npm run build
node dist/cli.js       # or: npm start

The process starts the EVA /ws server and the public media+webhook server, then waits for the EVA wrapper to connect and send start.

EVA integration

The EVA provider wrapper owns only the thin EVA-specific layer. It should:

  1. Launch / reach this adapter and connect to adapter_local_ws_url (ws://localhost:8090/ws).
  2. Send a Twilio-style start, then stream caller audio as media events with base64 μ-law (8 kHz, 160 bytes / 20 ms).
  3. Emit user_speech_start / user_speech_stop markers so the adapter can compute model_response latency.
  4. Receive assistant audio back as media events and play it to EVA.
  5. Send stop to end the session.

Suggested EVA s2s_params:

{
  "provider": "telnyx",
  "agent_id": "assistant-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "connection_id": "7267xxxxxxxxxxxxxx",
  "from": "+15555550100",
  "to": "+15555550199",
  "adapter_local_ws_url": "ws://localhost:8090/ws",
  "public_media_ws_url": "wss://adapter.example.com/telnyx-media",
  "public_webhook_url": "https://adapter.example.com/telnyx-webhooks",
  "stream_codec": "PCMU",
  "stream_sample_rate": 8000,
  "stream_track": "both_tracks",
  "environment": "production"
}

Only one EVA conversation is served per process; extra connections are rejected with WebSocket close code 1013.

Artifacts

On finalize the adapter writes, under TELNYX_EVA_OUTPUT_DIR:

  • audio_user.wav — caller (EVA → Telnyx) audio
  • audio_assistant.wav — assistant (Telnyx → EVA) audio
  • audio_mixed.wav — combined stream
  • transcript.jsonl — transcript entries ({role, content, timestamp})
  • metrics.json — latency/quality metrics
  • audit_log.json — ordered lifecycle audit events
  • adapter.log — framework log

metrics.json includes model_response latency, computed as firstAssistantAudioAt − latestUserSpeechStopAt (first non-silent assistant frame after the latest user_speech_stop).

Proof criteria (VSDK-277)

A run is considered a pass when all of:

  • call.answered observed.
  • Media WebSocket receives connected then start.
  • streaming.started webhook observed.
  • ai_assistant_start returns success and a conversation id.
  • The injected phrase reaches the hosted assistant (visible in transcript / insights, or the assistant clearly reacts).
  • Assistant audio streams back; audio_assistant.wav is non-empty and time-aligned.
  • model_response latency is computable from user_speech_stop → first assistant audio.

Critical risk: injected bidirectional media may not reach hosted AI Assistant STT (it may only play to the participant). If, after controlled retries, injected audio is not consumed by assistant STT — or assistant audio can't be isolated — fall back to the Node WebRTC bridge variant described in the design doc, keeping the same EVA wrapper contract.

Project layout

src/
  audio/            μ-law codec, frame repacketization, WAV writer, 20ms pacer
  protocol/         EVA, Telnyx media, and client-state message types/parsers
  telnyx-call-control-client.ts   Call Control REST (dial/stream/assistant/cleanup)
  telnyx-webhook-receiver.ts      webhook parse + signature verify + waitFor()
  telnyx-media-endpoint.ts        public media WS (noServer; host wires upgrades)
  public-server.ts                single HTTP server: media upgrades + webhooks + /health
  eva-server.ts                   EVA-facing /ws server
  audio-bridge.ts                 bidirectional bridge + WAV capture + reorder buffer
  artifacts.ts                    audit/transcript/metrics writers
  telnyx-eva-node-adapter.ts      lifecycle orchestrator
  config.ts  logger.ts  index.ts  cli.ts
tests/              unit tests + fake-Telnyx full-lifecycle integration test

Security & reliability

  • Authenticate media upgrades with stream_auth_token; reject otherwise.
  • Verify Telnyx webhook signatures before trusting payloads.
  • Correlate by call_control_id, call_session_id, and client_state; unique command_id per command for idempotency.
  • API keys, bearer tokens, and phone numbers are redacted in logs.
  • Hard timeouts on every milestone; best-effort cleanup always runs (stop assistant, stop stream, hangup, close sockets, finalize artifacts).
  • Audio artifacts stay local to the configured output dir.

Scope

This is the server-native first implementation: no React, browser, WebRTC, or Playwright/Chromium runtime dependencies. The pinned @telnyx/* beta packages are declared for EVA-side compatibility; transcript output mirrors @telnyx/ai-agent-lib's TranscriptItem shape (toTranscriptEntry) without coupling to its React-oriented, extensionless ESM type barrel (which does not resolve cleanly under this package's NodeNext module resolution).