# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.3.2] - 2026-09-25

### Added

- Added `auth: { type: "jwt", token }` to direct-endpoint mode, exclusive with `api_key` ([ADR 0002](./docs/adr/0002-exclusive-auth-strategy.md)). The JWT is sent as the first WebSocket subprotocol and as `Authorization: Bearer <token>` on the batch, batch reprocess and audit ingestion requests. The SDK adds the `Bearer ` prefix; final upload stays without credentials.
- Added the `INVALID_JWT` (empty token or `Bearer ` prefix) and `INVALID_JWT_CHARACTERS` (realtime token that is not an RFC 6455 token) `SofyaAuthError` codes, thrown before connecting.

### Changed

- Debug output redacts `auth.token`.

### Deprecated

- `config.token` in direct mode: use `auth: { type: "jwt", token }`. It keeps working until 1.0.0, logs a deprecation warning once per instance, and `auth.token` wins when both are set.

## [0.3.1] - 2026-09-18

### Added

- Added the optional `auth` config option for direct-endpoint mode (`{ provider, endpoint, config }`): `{ type: "none" }` (default) or `{ type: "api_key", key, transport }` with `transport` defaulting to `"subprotocol"`. Available on `SofyaSpeechConfig` and `SofyaBatchConfig`; `apiKey` mode is unchanged.
- Added the `subprotocol` transport, which sends the key as the extra `Sec-WebSocket-Protocol` token `x-api-key.<key>`, always appended after the configured protocols (falling back to the `sofya-stt.v1` companion token when none is configured, because the server rejects a key token sent alone).
- Added the `query` transport, which appends `x-api-key=<key>` to the realtime URL and leaves the configured protocols untouched.
- Added `SofyaAuthError` (exported), thrown before connecting when the key is empty or cannot be sent as an RFC 6455 subprotocol token, and emitted through `error` when the server rejects the key with close code `4401`.
- Added tests covering URL/subprotocol construction for every transport, reconnection reuse, key validation and key redaction in debug output.

### Changed

- The SDK now sends the `x-api-key` header on the HTTP calls it makes (batch reprocess and audit ingestion) when `auth.type` is `"api_key"` and the header is not already configured.
- A `4401` close no longer triggers the reconnection loop: the SDK emits `error` plus `disconnected` and stops retrying the rejected key.
- Debug output redacts the API key in the `auth` config, in the `x-api-key.*` subprotocol token, in the `x-api-key` header and in the `x-api-key` query parameter.

## [0.3.0] - 2026-09-18

Stable release consolidating the `0.3.0-beta.1` through `0.3.0-beta.17` series below.

## [0.3.0-beta.17] - 2026-09-18

### Added

- Added the `record` config flag for Whisper providers. `true`/`false` is sent as the `record` query parameter on every realtime connection (reconnections included); `undefined` omits the parameter so the server environment default applies.
- Added the `stt_session` event, emitted when the server `session_id` (the STT connection identifier carried by every `partial`/`final` message) first appears or changes on reconnection, with `{ sttSessionId, externalId, connectionAttempt }`.
- Added `getSttSessionId()` (current connection) and `getSttSessionIds()` (all connections since `startTranscription()`, in order) to `SofyaTranscriber` and the public typings. Non-Whisper providers and servers without `session_id` return `null`/`[]`.
- Added `stt_session_ids` to the STT audit ingestion payload and `sttSessionIds` to the exported debug audit file.
- Added tests covering `record`/`x-external-id` URL propagation and reconnection, and `session_id` on transcript messages.

### Changed

- Documented the `external_id` contract rules (opaque value, up to 512 characters, `[A-Za-z0-9._-]` starting with a letter or digit and up to 64 characters to be used as-is in the bucket path, fixed `x-external-id` field name, same value on every reconnection so the recordings of one consultation are grouped together).

## [0.3.0-beta.14] - 2026-04-12

### Added

- Added configurable `auditIngestion` support for Whisper providers to automatically POST the final debug audit payload on `stopTranscription()`.
- Added a typed `stt_audit_ingestion_warning` event and public warning payload contract for skipped/failed audit ingestion flows.
- Added STT audit ingestion guide documentation with request mapping, retry behavior, and operational integration checklist.

### Changed

- Reused batch-style connection resolution for STT audit ingestion (base URL derivation, inherited headers, and bearer auth fallback), with endpoint/header overrides.
- Added transient retry behavior for audit ingestion (`network`/`5xx` only) using exponential delays (`300ms`, `600ms`) while keeping `stopTranscription()` non-blocking on ingestion failure.

## [0.3.0-beta.13] - 2026-04-10

### Added

- Added `getBatchReprocessRemainingTime()` to the Whisper batch reprocess surface so consumers can read the remaining retained-audio TTL directly in milliseconds.
- Added a typed `REPROCESS_IN_PROGRESS` batch error so concurrent `reprocessAudio()` calls fail explicitly while the active request is still running.

### Changed

- Increased retained consultation audio TTL from 5 minutes to 30 minutes for post-stop batch reprocessing.
- Kept retained IndexedDB consultation audio alive after successful `reprocessAudio()` calls so repeated sequential batch retries remain available until TTL cleanup.

## [0.3.0-beta.12] - 2026-04-06

### Added

- Added the typed `createTranscriber()` factory so durable Whisper providers expose batch reprocessing capability in IntelliSense while non-Whisper providers stay on the common transcription surface.
- Added `BatchReprocessStatus`, `BatchReprocessError`, `getBatchReprocessStatus()`, and the `batch_reprocess_status` event to support UI availability state, TTL display, and typed error handling for retained-audio batch flows.
- Added focused runtime, typing, and request-shape coverage for batch reprocess inheritance, provider support, TTL expiration, and custom batch response parsing.

### Changed

- Centralized realtime connection resolution so batch reprocessing now derives its default POST URL from the realtime host (`/api/transcriber`) and automatically inherits authorization, extra headers, `x-external-id`, `transcription_language`, and `translation_language`.
- Changed durable consultation status reads after stop to avoid recreating empty sessions once retained audio has expired, allowing batch availability state to reflect the real TTL window.
- Expanded public docs and JSDoc coverage for provider-aware connection types, Whisper-only batch configuration, browser CORS expectations, and retained-audio lifecycle rules.

## [0.3.0-beta.11] - 2026-04-02

### Added

- Added end-of-session `reprocessAudio()` support for Whisper-backed sessions, exporting retained consultation audio as WAV and returning the parsed batch transcription payload.

### Changed

- Changed batch reprocessing requests to inherit realtime query parameters and authorization while uploading multipart form data with field name `file` for the batch endpoint contract.
- Changed durable consultation export behavior to fail clearly when retained audio has already expired instead of recreating an empty session.
- Added a Blob/FileReader fallback when reading stored IndexedDB payloads in environments without `blob.arrayBuffer()`.

## [0.3.0-beta.10] - 2026-04-01

### Added

- Added temporary factory-level websocket configuration logging so applications can inspect the provider/config payload reaching realtime connection setup.

## [0.3.0-beta.9] - 2026-04-01

### Added

- Added optional `external_id` configuration so Whisper websocket connections can include an external identifier in the realtime URL query string as `x-external-id`.

## [0.3.0-beta.8] - 2026-04-01

### Fixed

- Fixed published package entrypoints to reference generated `dist` declarations and runtime files so TypeScript consumers can resolve IntelliSense correctly.
- Excluded generated `.tgz` artifacts from npm packaging so release tarballs no longer recursively include previous packed outputs.

## [0.3.0-beta.7] - 2026-04-01

### Added

- Added optional `protocols` configuration so Whisper websocket clients can pass a string or string array for websocket subprotocol negotiation (`Sec-WebSocket-Protocol`).

## [0.3.0-beta.6] - 2026-04-01

### Added

- Added consultation storage lifecycle coverage for reset-on-start, post-stop TTL cleanup, database reopen recovery, and durable write backpressure.
- Added `EventEmitter.listenerCount()` coverage so listener ownership assertions can be verified in shared emitter and transcriber tests.

### Changed

- Reset IndexedDB-backed consultation storage when a new durable audio session starts, and schedule automatic consultation storage cleanup shortly after stop finalization.
- Serialized durable append/flush persistence, added manifest write coalescing, and applied backpressure when IndexedDB writes stall so in-memory backlog stays bounded.
- Reopened durable consultation storage automatically after `DatabaseClosedError` conditions and throttled consultation storage status refreshes during replay/archive updates.
- Added periodic resilience status logging at the transcriber layer and extra listener/adapter lifecycle diagnostics to help investigate repeated start/stop flows.

## [0.3.0-beta.5] - 2026-03-31

### Changed

- Stabilized Whisper adapter lifecycle behavior to reduce race conditions across repeated start/stop flows.
- Updated Whisper adapter unit coverage and TypeScript project configuration to align with lifecycle hardening changes.

## [0.3.0-beta.4] - 2026-03-31

### Added

- Added explicit JSDoc lifecycle guidance for emitter listener ownership (`on`, `off`, `once`, and `removeAllListeners`) to clarify client-side teardown responsibilities.

### Changed

- Updated event emitter documentation to make listener cleanup guarantees and mutation-safe dispatch behavior explicit for integrators.

## [0.3.0-beta.3] - 2026-03-30

### Added

- Added a vendor-agnostic telemetry integration connector that dispatches audit-derived payloads through a single configured provider.
- Added initial Dynatrace telemetry provider support via global runtime integration (`window.dtrum` by default) with configurable global key and action name.
- Added a new public event `telemetry_integration_warning` for non-fatal integration warnings (for example, missing Dynatrace runtime object).
- Added connector and provider unit coverage for provider resolution, dispatch deduplication, payload shaping, and no-transcript-text outbound mapping.

### Changed

- Changed `BaseConfig` to include optional `telemetry` configuration (`enabled`, `provider`, provider-specific options) while keeping integration opt-in.
- Changed `SofyaTranscriber` lifecycle wiring to auto-dispatch audit payloads on terminal flows (`stop`, `error`, and terminal `disconnected`).
- Changed Dynatrace mapping output to normalized lowercase property keys and typed property maps for action/session properties.

## [0.3.0-beta.2] - 2026-03-30

### Added

- Added `debug.advancedMetrics` to explicitly control advanced telemetry collection.

### Changed

- Changed telemetry behavior to keep essential audit/flow metrics always available while gating advanced audio-capture diagnostics behind debug mode.
- Changed `getDebugAudit()` behavior to return an essential audit snapshot even when debug persistence is disabled.
- Updated tests and documentation for the new essential-vs-advanced telemetry model.

## [0.3.0-beta.1] - 2026-03-30

### Added

- Added telemetry v2 public APIs: `getTelemetrySnapshot()`, `getTelemetryRows()`, `clearTelemetryRows()`, and `resetTelemetry()`.
- Added telemetry events `telemetry` and `telemetry_row` for snapshot updates and row streaming.
- Added an explicit OpenTelemetry bridge (`attachOpenTelemetryBridge`) to mirror telemetry rows to OTel instruments when configured by the host app.
- Added telemetry v2 unit coverage for ring-buffer retention, derived metrics, snapshot memoization behavior, and OTel bridge no-op safety.

### Changed

- Replaced snapshot-heavy metrics internals with a bounded in-memory telemetry core using map-backed counters/gauges/histograms and a fixed ring buffer (default `2000` rows).
- Switched runtime signal output to flat telemetry metric names and streaming histogram summaries (`count`, `sum`, `min`, `max`, `avg`, `p50`, `p95`, `p99`) without raw sample retention.
- Versioned debug audit/report output to schema `2` and updated report builders to consume telemetry v2.
- Updated Whisper/Oracle adapters and service interfaces to telemetry v2 contracts, including null-safe unsupported-provider snapshots.

### Removed

- Removed metrics v1 APIs/events from the public surface, including `getMetrics()`, grouped metric getters, and the `metrics` event.

## [0.2.0-beta.19] - 2026-03-29

### Changed

- Changed transcription listener bridging to bind per start and detach on stop, preventing listener accumulation across repeated sessions.
- Changed Whisper runtime ownership to use a shared `AudioContext` lease model with idle shutdown and one-time worklet module loading per active context.
- Changed adapter stop cleanup to release media and recognizer references deterministically so sessions do not retain stale runtime objects.
- Added an app-side memory leak investigation context guide in `docs/APP_SIDE_MEMORY_LEAK_CONTEXT.md` with repro, heap clues, and cleanup checklist.

## [0.2.0-beta.18] - 2026-03-27

### Added

- Added a derived session report builder and dashboard-ready debug audit format so exported audit JSON can be used directly by investigation views and report interfaces.
- Added a metrics and report usage guide describing how to map live metrics to user feedback and end-of-session dashboards.

### Changed

- Changed `getDebugAudit()` and `downloadDebugAudit()` to export only final interface-ready report data instead of raw telemetry arrays, sampled snapshots, or series payloads.
- Hardened runtime cleanup and bounded internal audit and metric retention so long or failed sessions do not leak memory through unbounded diagnostic buffers.

## [0.2.0-beta.17] - 2026-03-26

### Added

- Added a client-side metrics snapshot API with `getMetrics()` and a `metrics` event so applications can build UI around session, connection, recovery, buffering, browser network, and capture telemetry.
- Added grouped metric getters such as `getConnectionMetrics()`, `getRecoveryMetrics()`, and `getAudioCaptureMetrics()` so applications can read only the metric slice they need.

## [0.2.0-beta.16] - 2026-03-25

### Added

- Added a configurable `preRecoveryReplayMs` resilience option that prepends a short rolling replay window to buffered audio after delayed outage detection so the SDK can prefer duplicate transcript overlap over missing boundary words.

### Changed

- Changed recovery shutdown to wait for buffered audio to drain before finalization when backlog exists, and updated the resilience audit to confirm backlog drain completion across the recovery scenarios.

## [0.2.0-beta.15] - 2026-03-25

### Added

- Added an optional debug audit mode to SofyaTranscriber that captures client-side lifecycle, transcript, and resilience telemetry and can download the audit JSON automatically after stopTranscription().

## [0.2.0-beta.14] - 2026-03-25

### Changed

- Changed stop finalization to wait briefly for buffered audio to drain before sending finish, reducing transcript loss during recovery shutdown.

## [0.2.0-beta.13] - 2026-03-25

### Added

- Added a committed resilience audit baseline workflow with regression comparison, summary reporting, and optional baseline enforcement.

## [0.2.0-beta.12] - 2026-03-25

### Added

- Added resilience lifecycle telemetry for buffer and drain progress plus stop finalization so applications can inspect recovery progress in more detail.

## [0.2.0-beta.11] - 2026-03-25

### Changed

- Changed the resilience E2E suite to use separate production-like merge-gating and stress-audit execution profiles.

## [0.2.0-beta.10] - 2026-03-25

### Added

- Added degraded-network fault injection and a browser resilience scenario covering delayed, dropped, and stalled outbound delivery.

## [0.2.0-beta.9] - 2026-03-25

### Fixed

- Fixed stop finalization after transport recovery so the SDK sends a single finish action once the websocket reopens.

## [0.2.0-beta.8] - 2026-03-25

### Fixed

- Fixed buffered-audio recovery so reconnect drains now flush pending audio immediately, including small in-memory batches that were never persisted as segments.

## [0.2.0-beta.7] - 2026-03-25

### Changed

- Changed browser-offline websocket drops to stay in recovery mode and automatically resume reconnecting when the browser comes back online.

## [0.2.0-beta.6] - 2026-03-25

### Changed

- Added a dedicated drainStallTimeoutMs resilience option so offline-buffer drain stalls no longer reuse the websocket connect timeout.

## [0.2.0-beta.5] - 2026-03-25

### Added

- Added a browser resilience audit matrix that runs scripted recovery scenarios and records audit metrics to test-results/resilience-audit.json.

## [0.2.0-beta.4] - 2026-03-25

### Changed

- Stopped treating `pauseTranscription()` as a websocket reset and kept the microphone pause local to the audio capture graph.
- Re-armed the Whisper offline audio gate on `resumeTranscription()` so resumed audio is buffered immediately until the current transport proves stable again.

## [0.2.0-beta.3] - 2026-03-25

### Added

- Added a Playwright-based resilience test scaffold with a local harness page, a scripted websocket backend, and reusable browser-test fixtures.
- Added the first browser-level resilience scenarios for the happy path baseline and a short offline recovery flow.
- Added a dedicated resilience E2E suite specification documenting the full recovery scenario matrix and acceptance criteria.

### Changed

- Updated the package scripts and development dependencies to support running the new browser resilience suite locally and in CI.
- Linked the README resilience section to the browser-level resilience test strategy and scenario matrix.

## [0.2.0-beta.2] - 2026-03-25

### Changed

- Terminal Whisper transport disconnects now emit an `error` before teardown so non-user failures are not treated like a normal stop.
- Retry exhaustion now runs the same full cleanup flow as an explicit stop, including transport teardown and offline-buffer cleanup.
- Offline audio buffering now stops growing once the transport has officially reached the terminal disconnected state.

## [0.2.0-beta.1] - 2026-03-25

### Changed

- Kept offline audio buffering armed after `resumeTranscription()` by refreshing the Whisper websocket before live audio is trusted again.
- Cleared persisted offline audio segments after a successful backlog upload and when a new user-started transcription session begins.

## [0.2.0-beta.0] - 2026-03-24

### Added

- Added `getResilienceStatus()` so developers can query the current transport resilience snapshot at runtime.
- Added the `resilience_status` event so applications can react to realtime resilience state changes as they happen.

### Changed

- Expanded the resilience runtime data model to include connection phase, websocket phase, retry progress, buffered-audio totals, and the latest disconnect context.
- Updated the resilience documentation and examples to describe how applications can expose recovery state to end users and build logic on top of transport recovery.

## [0.1.0-beta.0] - 2026-03-24

### Changed

- Changed the default `maxReconnectAttempts` to `96`, which maps to roughly 15 minutes of retries with the current reconnect backoff configuration.
- Updated the resilience documentation to reflect the retry-window-oriented default reconnect behavior.

### Removed

- Removed `maxEnqueuedMessages` from the public resilience configuration because realtime audio buffering is handled by the SDK's offline buffer store rather than the websocket queue.
