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

@usetuner.ai/livekit-sdk

v0.5.0

Published

Automatically ingest LiveKit Agents session data into the Tuner observability API

Readme

@usetuner.ai/livekit-sdk

Automatically ingest LiveKit Agents session data into the Tuner observability API.

Node.js / TypeScript port of tuner-livekit-sdk (Python).

Install

npm install @usetuner.ai/livekit-sdk

Peer dependencies: @livekit/agents >= 1.4.0, @livekit/rtc-node >= 0.13.0. Node 18+.

Quickstart (2 lines)

import { TunerPlugin } from '@usetuner.ai/livekit-sdk';
import { voice, type JobContext } from '@livekit/agents';

export default async function entry(ctx: JobContext) {
  const session = new voice.AgentSession({ /* ... */ });
  new TunerPlugin(session, ctx);             // wires itself automatically
  await session.start({ agent, room: ctx.room });
}

Set the env vars:

TUNER_API_KEY=tr_api_...
TUNER_WORKSPACE_ID=123
TUNER_AGENT_ID=my-agent

The plugin subscribes to metrics_collected, close, and participantConnected/Disconnected events, captures SIP correlation data, and submits the session payload on shutdown.

Configuration

Every option can be passed as a constructor argument or as an environment variable. Constructor arguments take precedence.

| Option | Env var | Required | Description | | ----------------------- | --------------------- | -------- | --------------------------------------------------------------------------- | | apiKey | TUNER_API_KEY | yes | Bearer token issued by Tuner. | | workspaceId | TUNER_WORKSPACE_ID | yes | Integer workspace ID. | | agentId | TUNER_AGENT_ID | yes | Stable identifier for this agent across sessions. | | baseUrl | TUNER_BASE_URL | no | Defaults to https://api.usetuner.ai. | | agentVersion | AGENT_VERSION | no | Free-form version string. Useful for A/B and regression analysis. | | callType | — | no | "phone_call", "web_call", or any custom label. | | recordingUrlResolver | — | no | async (roomName, jobId) => string \| null. Returns a playback URL. | | costCalculator | — | no | (usage) => number. Compute per-call cost in USD. | | sipCorrelationId | — | no | Override the auto-captured sip.callIDFull. See "Simulations" below. | | extraMetadata | — | no | Free-form metadata merged into the call payload. | | enabled | — | no | false disables the plugin entirely. Defaults to true. | | timeoutSeconds | — | no | Per-attempt HTTP timeout. Defaults to 30. | | maxRetries | — | no | Max retries on retryable HTTP errors. Defaults to 3. |

Full example

import { TunerPlugin } from '@usetuner.ai/livekit-sdk';
import type { metrics } from '@livekit/agents';

function calculateCost(usage: metrics.UsageSummary): number {
  return (
    usage.llmPromptTokens * 0.000_003 +
    usage.llmCompletionTokens * 0.000_015 +
    usage.ttsCharactersCount * 0.000_030
  );
}

async function getRecordingUrl(roomName: string, jobId: string): Promise<string> {
  return (await myStorage.getUrl(jobId)) ?? 'pending';
}

new TunerPlugin(session, ctx, {
  apiKey: process.env.TUNER_API_KEY,
  workspaceId: Number.parseInt(process.env.TUNER_WORKSPACE_ID!, 10),
  agentId: process.env.TUNER_AGENT_ID,
  callType: 'phone_call',
  recordingUrlResolver: getRecordingUrl,
  costCalculator: calculateCost,
  extraMetadata: { env: 'prod', region: 'us-east-1' },
  agentVersion: 42,
  timeoutSeconds: 20,
  maxRetries: 3,
  enabled: process.env.NODE_ENV === 'production',
});

Simulation correlation (SIP)

Tuner simulations dial into your agent through the same SIP trunk that handles production phone calls. To match a simulation run with the session your agent submits, the SDK forwards LiveKit's sip.callIDFull attribute as a sipCorrelationId.

This section covers the SDK wiring only. For LiveKit platform setup (SIP URI, inbound trunk, dispatch rule, Tuner SIP settings), see:

docs.usetuner.ai/docs/api-and-integrations/connecting-to-livekit/simulation-setup

Step 1 — The extractSipCorrelationId helper

import { ParticipantKind } from '@livekit/rtc-node';
import type { JobContext } from '@livekit/agents';

function extractSipCorrelationId(ctx: JobContext): string | undefined {
  for (const participant of ctx.room.remoteParticipants.values()) {
    if (participant.kind !== ParticipantKind.SIP) continue;
    const sipCallIdFull = participant.attributes?.['sip.callIDFull'];
    if (typeof sipCallIdFull === 'string' && sipCallIdFull) return sipCallIdFull;
  }
  return undefined;
}

Step 2 — Pass it to TunerPlugin

export default async function entry(ctx: JobContext) {
  const session = new voice.AgentSession({ /* ... */ });

  await ctx.connect();
  const sipCorrelationId = extractSipCorrelationId(ctx);

  new TunerPlugin(session, ctx, {
    sipCorrelationId,
    // ...other options
  });

  await session.start({ agent, room: ctx.room });
}

⚠️ Order matters: ctx.room.remoteParticipants is empty until await ctx.connect() completes. If you call the helper too early it will always return undefined and you'll silently lose correlation for every simulation — no error, just missing data in Tuner. Always: build AgentSessionawait ctx.connect() → extract ID → attach plugin → await session.start(...).

API surface

import {
  TunerPlugin,
  TunerConfig,
  DisconnectReason,
  VERSION,
  type TunerConfigInput,
  type RecordingUrlResolver,
  type CostCalculator,
} from '@usetuner.ai/livekit-sdk';

Behavior notes

A few intentional differences vs. the Python SDK; nothing affects the wire format:

  • Shutdown reason: @livekit/agents (Node) currently invokes shutdown callbacks with no arguments, so this SDK passes a hardcoded 'shutdown' to state.finalize(...). Python receives the real reason from JobContext. If LiveKit's Node SDK adds the parameter, see the comment in src/plugin.ts — it's a one-line change.
  • agentId URL-encoding: this SDK percent-encodes agentId when building the submit URL; the Python SDK does not. Use URL-safe agent IDs to stay portable.
  • Timestamp normalization: the mapper accepts createdAt in either seconds or milliseconds (auto-detects by magnitude) for forward-compatibility with @livekit/agents releases.

Development

pnpm install
pnpm typecheck
pnpm test
pnpm build

Publishing to npm

The package is published to the public npm registry as @usetuner.ai/livekit-sdk.

First-time setup

# Create an account at npmjs.com, then authenticate
npm login

# Confirm you have access to the @usetuner.ai org on npm
npm org ls usetuner.ai

Releasing a new version

Pick the release type based on what changed:

# Bug fixes / non-breaking internal changes
npm run release:patch   # e.g. 0.1.0 → 0.1.1

# New backwards-compatible features
npm run release:minor   # e.g. 0.1.0 → 0.2.0

# Breaking API changes
npm run release:major   # e.g. 0.1.0 → 1.0.0

Each command automatically:

  1. Bumps the version in package.json and creates a git tag
  2. Runs prepublishOnly — typecheck → tests → build (publish is blocked if any step fails)
  3. Publishes the built dist/ to npm

After publishing, push the version commit and tag:

git push && git push --tags

What gets published

Only the files listed in "files" are included in the npm package:

  • dist/ — compiled ESM + CJS + type declarations
  • README.md
  • LICENSE

Source files, tests, and config are excluded.

License

MIT