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

@kuralle-syrinx/stt-core

v4.6.3

Published

Shared streaming-STT lifecycle engine for Syrinx STT adapters — connection, transcript funnel, usage delta-billing

Readme

@kuralle-syrinx/stt-core

The shared streaming-STT lifecycle engine for Syrinx STT adapters — the STT counterpart of @kuralle-syrinx/tts-core. A provider becomes a Syrinx STT plugin by implementing one small, socket-free port (SttWireProtocol); this package owns everything else: the @kuralle-syrinx/ws WebSocketConnection, stt.interim / stt.result emission, the smart-turn-safe final-transcript funnel, usage.recorded{stage:"stt", audioSeconds} delta-billing, and finalize/reconfigure/reset plumbing.

The SttWireProtocol port

export interface SttWireProtocol {
  encodeFinalize(contextId: string): readonly SocketData[];
  decode(data: SocketData, isBinary: boolean): readonly SttEvent[];
  encodeClose?(): readonly SocketData[];
  encodeAudio?(audio: Uint8Array): readonly SocketData[];   // default: raw PCM frame
  onOpen?(): readonly SocketData[];                          // handshake/config on (re)connect
  encodeReconfigure?(partial: SttReconfigurePartial): readonly SocketData[];
  isReady?(): boolean;                                       // gate outbound audio pre-handshake
  onConnectionLost?(): void;
  attach?(host: SttProtocolHost): void;                      // async-emit seam (timers, etc.)
  onFinalizeSent?(contextId: string): void;
}

A provider implements only wire encode/decode. The engine owns context tracking, outbound audio send + finalize, the interim/final funnel, delta-billing, and (optional) eos.turn_complete.

startStreamingSttSession

import { startStreamingSttSession, defaultNodeSocketFactory } from "@kuralle-syrinx/stt-core";

const session = await startStreamingSttSession(bus, {
  protocol: new MyProviderWireProtocol(),
  provider: { name: "my-provider", model: "my-model" },
  url: () => "wss://api.example.com/stt",
  headers: { Authorization: `Bearer ${apiKey}` },
  retry: readProviderRetryConfig(config),
  socketFactory: await defaultNodeSocketFactory(),
  emitEosOnFinal: true, // eos.turn_complete on speechFinal:true results
});

// session.dispose(): Promise<void>
// session.reconfigure(partial: SttReconfigurePartial): void  — mid-turn keyterms/language/etc.
// session.reset(): void                                       — force a transport reconnect

Wires the standard PipelineBus plumbing: stt.audioengine.onAudio (the canonical STT ingress — plugins subscribe to stt.audio only, never user.audio_received, to avoid double-sending/double-billing every frame), stt.finalizeencodeFinalize, turn.change / interrupt.stt → context bookkeeping.

Extension seams (wave 1 + wave 2)

  • encodeAudio / onOpen / encodeReconfigure — optional wire hooks for providers whose audio framing, connect-time handshake, or reconfigure protocol differs from the raw-PCM default.
  • Richer SttEvent vocabularyspeech_started, partial, eos_interim, eos_retracted, in addition to interim / final / error / turn_complete / ignore.
  • Pre-handshake audio buffering — audio that races ahead of a provider handshake (e.g. Grok's transcript.created) is buffered (capped) and flushed on the ready transition instead of dropped.
  • Sent-bytes billing fallback — when a final has no provider duration, usage bills off sent PCM bytes; a later duration-bearing final advances the byte marker so it can't double-bill.
  • SttProtocolHost (attach/emit/reset), onFinalizeSent, Transport.reset, SttEvent.turn_complete (wave 2) — async-emit seams for providers with their own finalize-timeout/fallback/reconnect state machine (e.g. Deepgram nova's Finalize handshake).

Providers built on it

Grok, ElevenLabs, Google, and Deepgram Flux STT are behavior-preserving migrations onto this base. Deepgram nova STT also builds on it, using the wave-2 async-emit seams for its Finalize-timeout state machine (multi-segment accumulation, speech_final/from_finalize gating, UtteranceEnd backstop) — provider-boundary logic stays in the wire protocol; the socket/reconnect/billing/buffer funnel is shared.

See @kuralle-syrinx/grok's GrokSTTPlugin (src/stt.ts) or @kuralle-syrinx/elevenlabs's ElevenLabsSTTPlugin for a minimal real implementation.

Deploy on Cloudflare Workers

Socket-free and transport-injectable like the rest of the Syrinx kernel: pass createWorkersSocket (@kuralle-syrinx/ws/workers) as the socketFactory instead of defaultNodeSocketFactory() to dial outbound provider WebSockets through the fetch-upgrade path.