# Native streaming helpers for a Hume migration

These server-side Node.js helpers implement three separate native Oruk protocols. They do not emulate Hume, import its credentials, transfer voices, or establish compatible scores. Choose the route your evaluation actually needs.

| Helper | Raw input | Analysis trigger | Output and billing boundary |
| --- | --- | --- | --- |
| `streamSpectra2` | Mono 16 kHz PCM16LE; 45 ms–60 s; honor the smaller `ready` limit | One explicit `finish` after source EOF | One final transcript and clip-level expression result. A lost response after Finish is uncertain. No result-retrieval promise. |
| `streamResonance2` | Mono 16 kHz finite float32LE; 0.1–120 s | One explicit commit of `[0, sampleCount)` with a caller-supplied ID | Expression scores and axes; no transcript or timestamps. Separate model approval required. Each new overlapping analysis is billed independently, with the documented one-second minimum. |
| `streamRealtime` | Mono 16 kHz PCM16LE; up to the route's 600 s audio limit | Streaming audio, then `input_audio_buffer.commit` at EOF | Phrase/transcript events, followed by usage and a clean close. Processing and metering may begin with the first audio frame, before Commit. |

No WAV header, automatic resampling, PCM16/float32 conversion, voice activity detection, label mapping or score calibration is performed. Use the correct raw format; finite float32 bytes are checked before sending. Digital silence is a transport fixture, not an emotion-quality evaluation.

## Install and run on your server

Use Node.js 22.23+ and install `ws@8.21.3` in your project. Download [hume-native-streams.mjs](https://oruk.ai/examples/hume-native-streams.mjs) beside your script. The module opens no connection on import. It keeps permanent keys in the supported server authorization header, never a URL or browser bundle.

```sh
npm install --save-exact ws@8.21.3
```

Example for Spectra-2, after preparing a permitted PCM16LE recording:

```javascript
import {createReadStream} from 'node:fs'
import {streamSpectra2, MigrationStreamError} from './hume-native-streams.mjs'

const stop = new AbortController()
process.once('SIGINT', () => stop.abort())
// Persist this UUID with the original recording/hash BEFORE making the request.
// Reuse neither a different payload nor a new UUID to resolve a lost response.
const requestId = process.env.ORUK_REQUEST_ID
try {
  const receipt = await streamSpectra2({
    apiKey: process.env.ORUK_API_KEY,
    requestId,
    signal: stop.signal,
    source: signal => createReadStream('permitted.pcm', {highWaterMark: 6400, signal}),
  })
  // This contains transcript/scores. Store it only in your approved private destination.
  console.log(JSON.stringify(receipt))
} catch (error) {
  if (!(error instanceof MigrationStreamError)) throw error
  console.error(JSON.stringify({code: error.code, requestId: error.requestId,
    mayHaveBeenBilled: error.mayHaveBeenBilled, action: error.action}))
  process.exitCode = 1
}
```

For Resonance 2, use `streamResonance2` with raw float32LE input and optional `regime: 'f1' | 'precision'`, `scope: 'clip' | 'live'`. This helper makes one whole-clip commit. It deliberately does not schedule periodic or overlapping commits for you. Choose windows and costs explicitly in a multi-commit application; this helper is not a reusable 256-commit session manager.

For Realtime, use `streamRealtime` and `onEvent: async (event, signal) => { /* consume privately; respect cancellation */ }`. Existing one-argument callbacks remain valid. It enables phrase emotion with the documented 600 ms silence setting, word timestamps and no diarization. Events remain native; phrase failures are delivered as failures, not converted to empty/zero scores. A usage event alone is not success: the helper requires the final transcript and a normal socket close, and catches an error sent after usage. Partial outputs may already have been delivered to your callback when the operation later fails.

## Bounds and lifecycle

- `source(signal)` returns a sync or async iterable of nonempty `Uint8Array` chunks, at most 64,000 bytes each and aligned to the sample width. Respect its abort signal. A microphone adapter must bound its own capture queue as well.
- The helper owns at most one producer chunk and waits for each transport send. It bounds the socket queue at 128 KiB and incoming events at 128 KiB each and 16 pending events. It does not collect a whole recording or a list of transcripts.
- Setup/send waits trigger cancellation at 15 seconds; a stalled producer at 25 seconds; an event callback at 15 seconds; finalization at 45 seconds. The client upload deadline is 70 seconds for Spectra and 590 seconds for Resonance 2/Realtime, measured from connection setup. These are cooperative cancellation budgets, not hard return deadlines or service latency guarantees. Slow capture/upload can reach a deadline before the audio-duration limit. The server may impose a shorter deadline or refuse earlier.
- Supplied request IDs must be stable UUIDs. They are checked against ready/final envelopes where the route supplies them. The helper checks final model, task, result kind, duration against sent PCM, usage and complete native score keys and ranges; it returns native values without remapping. This is not a full semantic or model-quality validator.
- `setupMs`, `uploadMs`, `finalizationMs` and `cleanupMs` measure this caller's stages; `totalMs` includes joined cleanup. Realtime finalization includes the final clean close. They are not isolated inference latency or a time-to-first-usable-signal benchmark.
- Cancellation signals the producer and callback, prevents new sends/callbacks, requests iterator `return()` and terminates the connection. Before settling, the helper joins both processing loops, every original `next()`/send/callback promise, iterator cleanup and the socket's `close` event. A cleanup rejection after an otherwise successful result returns `cleanup_failed`; it cannot silently report success. An earlier failure retains its original safe error code.
- A producer, callback or injected transport that ignores cancellation can keep the helper pending. Use an owned process supervisor if your application requires a hard wall-clock stop. Callbacks and producers must include their work in their returned promises; the helper cannot join detached work they create internally. Cancellation does not undo accepted inference, cancel all backend work, erase retained data or prove a refund. `mayHaveBeenBilled: false` means this helper sent no analysis command/Realtime audio; it is not a live account reconciliation receipt.
- There is **no automatic retry or reconnect**. After an uncertain Spectra/Realtime outcome, retain the original recording and ID and reconcile Usage. Reusing an ID does not make those routes replay-safe. Resonance 2's separately documented shared HTTP/WS replay requires the same key, ID, decoded PCM and regime within its 24-hour window; do not substitute a fresh ID. The replay window is not a physical deletion guarantee.

The optional `connect` function is a transport-injection seam for the local test suite. Production callers should leave it unset so the reviewed `wss://speech-api.oruk.ai` paths, normal TLS validation, authorization headers and redirect prohibition are used.

## Verification and release scope

The repository test exercises actual loopback WebSockets with synthetic audio, plus deterministic congestion/timeout cases. It checks identity, byte framing, one trigger, early/late failure, oversized frames, cancellation and absence of reconnect. No provider inference, live billing, production capacity or customer migration is established by that test. Verify those against a permitted fixture before customer cutover.
