assemblyai 4.34.5 → 4.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.34.6]
4
+
5
+ - Add `keepAlive()` method to `StreamingTranscriber` — sends a `KeepAlive` message to reset the server's inactivity timer when `inactivityTimeout` is configured
6
+
3
7
  ## [4.33.0]
4
8
 
5
9
  - Add streaming parameters to match the Python SDK:
package/README.md CHANGED
@@ -16,9 +16,6 @@ The AssemblyAI JavaScript SDK provides an easy-to-use interface for interacting
16
16
  which supports async and streaming transcription.
17
17
  It is written primarily for Node.js in TypeScript with all types exported, but also [compatible with other runtimes](./docs/compat.md).
18
18
 
19
- > **⚠️ WARNING**
20
- > This SDK is intended for **testing and light usage only**. It is not recommended for use at scale or with production traffic. For best results, we recommend calling the AssemblyAI API directly via HTTP request. See our [official documentation](https://www.assemblyai.com/docs) for more information, including HTTP code examples.
21
-
22
19
  ## Claude Code
23
20
 
24
21
  This repository includes a [`CLAUDE.md`](CLAUDE.md) file that provides context to Claude Code about this SDK — key APIs, common patterns, and gotchas. When you open this repo in Claude Code, it automatically reads this file to give better assistance.
@@ -592,6 +592,24 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
592
592
  }
593
593
  const defaultStreamingUrl = "wss://streaming.assemblyai.com/v3/ws";
594
594
  const terminateSessionMessage = `{"type":"Terminate"}`;
595
+ const DEFAULT_CONNECT_TIMEOUT_MS = 1000;
596
+ const DEFAULT_MAX_CONNECTION_RETRIES = 2;
597
+ const DEFAULT_CONNECTION_RETRY_DELAY_MS = 500;
598
+ /**
599
+ * Close/error codes that signal a permanent client-side problem (auth,
600
+ * billing, malformed config). A retry would hit the same failure, so the
601
+ * connection is never retried on these.
602
+ */
603
+ const NON_RETRYABLE_CLOSE_CODES = new Set([
604
+ StreamingErrorType.BadSampleRate,
605
+ StreamingErrorType.AuthFailed,
606
+ StreamingErrorType.InsufficientFunds,
607
+ StreamingErrorType.FreeTierUser,
608
+ StreamingErrorType.BadSchema,
609
+ ]);
610
+ function isRetryableCloseCode(code) {
611
+ return code !== 1000 && !NON_RETRYABLE_CLOSE_CODES.has(code);
612
+ }
595
613
  /**
596
614
  * Per-send chunk cap in milliseconds for the dual-channel mixer. The streaming
597
615
  * server rejects audio messages longer than 1000 ms (`Input Duration Error`).
@@ -798,12 +816,85 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
798
816
  on(event, listener) {
799
817
  this.listeners[event] = listener;
800
818
  }
819
+ /**
820
+ * Open the streaming session.
821
+ *
822
+ * Resolves with the server's `Begin` event once the handshake completes. A
823
+ * single attempt is bounded by `connectTimeout` (default 1000ms); transient
824
+ * failures (timeout, network drop, unexpected close) are retried up to
825
+ * `maxConnectionRetries` times (default 2), waiting `connectionRetryDelay`
826
+ * (default 500ms) between attempts. Permanent failures (auth, insufficient
827
+ * funds, malformed config) are not retried.
828
+ *
829
+ * Unlike previously, a failed connection now rejects this promise rather
830
+ * than only invoking the `error` listener — necessary for the caller (and
831
+ * the retry loop) to observe the failure.
832
+ */
801
833
  connect() {
802
- return new Promise((resolve) => {
834
+ return __awaiter(this, void 0, void 0, function* () {
835
+ var _a, _b;
803
836
  if (this.socket) {
804
837
  throw new Error("Already connected");
805
838
  }
839
+ const maxRetries = (_a = this.params.maxConnectionRetries) !== null && _a !== void 0 ? _a : DEFAULT_MAX_CONNECTION_RETRIES;
840
+ const retryDelay = (_b = this.params.connectionRetryDelay) !== null && _b !== void 0 ? _b : DEFAULT_CONNECTION_RETRY_DELAY_MS;
841
+ let lastError;
842
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
843
+ try {
844
+ return yield this.connectOnce();
845
+ }
846
+ catch (err) {
847
+ lastError = err;
848
+ const retryable = err.retryable === true;
849
+ if (!retryable || attempt === maxRetries) {
850
+ throw err;
851
+ }
852
+ console.warn(`Streaming connect attempt ${attempt + 1}/${maxRetries + 1} failed (${err.message}); retrying`);
853
+ if (retryDelay > 0) {
854
+ yield new Promise((resolve) => setTimeout(resolve, retryDelay));
855
+ }
856
+ }
857
+ }
858
+ // The loop above always returns or throws; this only satisfies the type
859
+ // checker that a value is produced on every path.
860
+ throw lastError !== null && lastError !== void 0 ? lastError : new Error("Failed to connect to streaming server");
861
+ });
862
+ }
863
+ connectOnce() {
864
+ return new Promise((resolve, reject) => {
865
+ var _a;
806
866
  const url = this.connectionUrl();
867
+ const timeoutMs = (_a = this.params.connectTimeout) !== null && _a !== void 0 ? _a : DEFAULT_CONNECT_TIMEOUT_MS;
868
+ // `settled` flips once this attempt has resolved (`Begin`) or rejected
869
+ // (timeout / pre-`Begin` close / error). Before it flips the socket
870
+ // handlers drive this promise; after it flips they revert to normal
871
+ // runtime dispatch (close / error / message listeners).
872
+ let settled = false;
873
+ let timer;
874
+ const failAttempt = (error) => {
875
+ if (settled)
876
+ return;
877
+ settled = true;
878
+ if (timer)
879
+ clearTimeout(timer);
880
+ this.discardPendingSocket();
881
+ reject(error);
882
+ };
883
+ const succeed = (begin) => {
884
+ if (settled)
885
+ return;
886
+ settled = true;
887
+ if (timer)
888
+ clearTimeout(timer);
889
+ resolve(begin);
890
+ };
891
+ if (timeoutMs > 0) {
892
+ timer = setTimeout(() => {
893
+ const err = new StreamingError(`Streaming connection timed out after ${timeoutMs}ms`);
894
+ err.retryable = true;
895
+ failAttempt(err);
896
+ }, timeoutMs);
897
+ }
807
898
  if (this.token) {
808
899
  this.socket = factory(url.toString());
809
900
  }
@@ -825,6 +916,15 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
825
916
  reason = StreamingErrorMessages[code];
826
917
  }
827
918
  }
919
+ // A close before `Begin` is a failed connection attempt — reject so
920
+ // connect() can retry (or surface a permanent failure).
921
+ if (!settled) {
922
+ const err = new StreamingError(reason || `Streaming connection closed (code=${code})`);
923
+ err.code = code;
924
+ err.retryable = isRetryableCloseCode(code);
925
+ failAttempt(err);
926
+ return;
927
+ }
828
928
  // Stop the flush timer when the socket is gone (server-initiated close,
829
929
  // network drop, etc.) — otherwise subsequent ticks call send() on a
830
930
  // closed socket and spam the error listener.
@@ -835,11 +935,15 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
835
935
  (_b = (_a = this.listeners).close) === null || _b === void 0 ? void 0 : _b.call(_a, code, reason);
836
936
  };
837
937
  this.socket.onerror = (event) => {
838
- var _a, _b, _c, _d;
839
- if (event.error)
840
- (_b = (_a = this.listeners).error) === null || _b === void 0 ? void 0 : _b.call(_a, event.error);
841
- else
842
- (_d = (_c = this.listeners).error) === null || _d === void 0 ? void 0 : _d.call(_c, new Error(event.message));
938
+ var _a, _b, _c;
939
+ const error = (_a = event.error) !== null && _a !== void 0 ? _a : new Error(event.message);
940
+ // A socket error before `Begin` is a failed attempt reject/retry.
941
+ if (!settled) {
942
+ error.retryable = true;
943
+ failAttempt(error);
944
+ return;
945
+ }
946
+ (_c = (_b = this.listeners).error) === null || _c === void 0 ? void 0 : _c.call(_b, error);
843
947
  };
844
948
  this.socket.onmessage = ({ data }) => {
845
949
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
@@ -847,15 +951,23 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
847
951
  if ("error" in message) {
848
952
  const err = new StreamingError(message.error);
849
953
  if ("error_code" in message) {
850
- err.code =
851
- message.error_code;
954
+ err.code = message.error_code;
955
+ }
956
+ // A server error frame before `Begin` fails the attempt; the code
957
+ // decides whether a retry is worthwhile.
958
+ if (!settled) {
959
+ const attemptErr = err;
960
+ attemptErr.retryable =
961
+ err.code === undefined ? true : isRetryableCloseCode(err.code);
962
+ failAttempt(attemptErr);
963
+ return;
852
964
  }
853
965
  (_b = (_a = this.listeners).error) === null || _b === void 0 ? void 0 : _b.call(_a, err);
854
966
  return;
855
967
  }
856
968
  switch (message.type) {
857
969
  case "Begin": {
858
- resolve(message);
970
+ succeed(message);
859
971
  (_d = (_c = this.listeners).open) === null || _d === void 0 ? void 0 : _d.call(_c, message);
860
972
  break;
861
973
  }
@@ -902,6 +1014,20 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
902
1014
  };
903
1015
  });
904
1016
  }
1017
+ /** Tear down a half-open socket from a failed connection attempt. */
1018
+ discardPendingSocket() {
1019
+ if (!this.socket)
1020
+ return;
1021
+ try {
1022
+ if (this.socket.removeAllListeners)
1023
+ this.socket.removeAllListeners();
1024
+ this.socket.close();
1025
+ }
1026
+ catch (_a) {
1027
+ // Best-effort cleanup; a half-open socket may throw on close.
1028
+ }
1029
+ this.socket = undefined;
1030
+ }
905
1031
  /**
906
1032
  * Returns a WritableStream that pumps PCM chunks into `sendAudio`. Single-channel
907
1033
  * only — in dual-channel mode use `sendAudio(pcm, { channel })` directly, since
@@ -1175,6 +1301,16 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
1175
1301
  };
1176
1302
  this.send(JSON.stringify(message));
1177
1303
  }
1304
+ /**
1305
+ * Reset the server's inactivity timer. Only needed when the session was
1306
+ * created with `inactivityTimeout` and no audio is being sent.
1307
+ */
1308
+ keepAlive() {
1309
+ const message = {
1310
+ type: "KeepAlive",
1311
+ };
1312
+ this.send(JSON.stringify(message));
1313
+ }
1178
1314
  send(data) {
1179
1315
  if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
1180
1316
  throw new Error("Socket is not open for communication");
@@ -1233,7 +1369,7 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
1233
1369
  defaultUserAgentString += navigator.userAgent;
1234
1370
  }
1235
1371
  const defaultUserAgent = {
1236
- sdk: { name: "JavaScript", version: "4.34.5" },
1372
+ sdk: { name: "JavaScript", version: "4.35.0" },
1237
1373
  };
1238
1374
  if (typeof process !== "undefined") {
1239
1375
  if (process.versions.node && defaultUserAgentString.indexOf("Node") === -1) {
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).assemblyai={})}(this,(function(e){"use strict";class t extends Error{constructor(e="DualChannelCapture requires a browser environment (AudioContext is undefined)."){super(e),this.name="BrowserOnlyError"}}function s(e,t,s,n){return new(s||(s=Promise))((function(i,r){function o(e){try{l(n.next(e))}catch(e){r(e)}}function a(e){try{l(n.throw(e))}catch(e){r(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof s?t:new s((function(e){e(t)}))).then(o,a)}l((n=n.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;const{WritableStream:n}="undefined"!=typeof window?window:"undefined"!=typeof global?global:globalThis;var i,r;const o=null!==(r=null!==(i=null!==WebSocket&&void 0!==WebSocket?WebSocket:null===global||void 0===global?void 0:global.WebSocket)&&void 0!==i?i:null===window||void 0===window?void 0:window.WebSocket)&&void 0!==r?r:null===self||void 0===self?void 0:self.WebSocket,a=(e,t)=>t?new o(e,t):new o(e),l={[4e3]:"Sample rate must be a positive integer",[4001]:"Not Authorized",[4002]:"Insufficient funds",[4003]:"This feature is paid-only and requires you to add a credit card. Please visit https://app.assemblyai.com/ to add a credit card to your account.",[4004]:"Session ID does not exist",[4008]:"Session has expired",[4010]:"Session is closed",[4029]:"Rate limited",[4030]:"Unique session violation",[4031]:"Session Timeout",[4032]:"Audio too short",[4033]:"Audio too long",[4034]:"Audio too small to transcode",[4100]:"Bad JSON",[4101]:"Bad schema",[4102]:"Too many streams",[4103]:"This session has been reconnected. This WebSocket is no longer valid.",[1013]:"Reconnect attempts exhausted",[4104]:"Could not parse word boost parameter"};class h extends Error{}const c={[3005]:"Server error",[3006]:"Input validation error",[3007]:"Audio chunk duration violation",[3008]:"Session expired: maximum session duration exceeded",[3009]:"Too many concurrent sessions",[4e3]:"Sample rate must be a positive integer",[4001]:"Not Authorized",[4002]:"Insufficient funds",[4003]:"This feature is paid-only and requires you to add a credit card. Please visit https://app.assemblyai.com/ to add a credit card to your account.",[4004]:"Session ID does not exist",[4008]:"Session has expired",[4010]:"Session is closed",[4029]:"Rate limited",[4030]:"Unique session violation",[4031]:"Session Timeout",[4032]:"Audio too short",[4033]:"Audio too long",[4034]:"Audio too small to transcode",[4101]:"Bad schema",[4102]:"Too many streams",[4103]:"This session has been reconnected. This WebSocket is no longer valid."};class d extends Error{}const u='{"terminate_session":true}';class m{constructor(e){var t,s;if(this.listeners={},this.realtimeUrl=null!==(t=e.realtimeUrl)&&void 0!==t?t:"wss://api.assemblyai.com/v2/realtime/ws",this.sampleRate=null!==(s=e.sampleRate)&&void 0!==s?s:16e3,this.wordBoost=e.wordBoost,this.encoding=e.encoding,this.endUtteranceSilenceThreshold=e.endUtteranceSilenceThreshold,this.disablePartialTranscripts=e.disablePartialTranscripts,"token"in e&&e.token&&(this.token=e.token),"apiKey"in e&&e.apiKey&&(this.apiKey=e.apiKey),!this.token&&!this.apiKey)throw new Error("API key or temporary token is required.")}connectionUrl(){const e=new URL(this.realtimeUrl);if("wss:"!==e.protocol)throw new Error("Invalid protocol, must be wss");const t=new URLSearchParams;return this.token&&t.set("token",this.token),t.set("sample_rate",this.sampleRate.toString()),this.wordBoost&&this.wordBoost.length>0&&t.set("word_boost",JSON.stringify(this.wordBoost)),this.encoding&&t.set("encoding",this.encoding),t.set("enable_extra_session_information","true"),this.disablePartialTranscripts&&t.set("disable_partial_transcripts",this.disablePartialTranscripts.toString()),e.search=t.toString(),e}on(e,t){this.listeners[e]=t}connect(){return new Promise((e=>{if(this.socket)throw new Error("Already connected");const t=this.connectionUrl();this.token?this.socket=a(t.toString()):(console.warn("API key authentication is not supported for the RealtimeTranscriber in browser environment. Use temporary token authentication instead.\nLearn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/compat.md#browser-compatibility."),this.socket=a(t.toString(),{headers:{Authorization:this.apiKey}})),this.socket.binaryType="arraybuffer",this.socket.onopen=()=>{void 0!==this.endUtteranceSilenceThreshold&&null!==this.endUtteranceSilenceThreshold&&this.configureEndUtteranceSilenceThreshold(this.endUtteranceSilenceThreshold)},this.socket.onclose=({code:e,reason:t})=>{var s,n;t||e in l&&(t=l[e]),null===(n=(s=this.listeners).close)||void 0===n||n.call(s,e,t)},this.socket.onerror=e=>{var t,s,n,i;e.error?null===(s=(t=this.listeners).error)||void 0===s||s.call(t,e.error):null===(i=(n=this.listeners).error)||void 0===i||i.call(n,new Error(e.message))},this.socket.onmessage=({data:t})=>{var s,n,i,r,o,a,l,c,d,u,m,p,f,v,g;const w=JSON.parse(t.toString());if("error"in w)null===(n=(s=this.listeners).error)||void 0===n||n.call(s,new h(w.error));else switch(w.message_type){case"SessionBegins":{const t={sessionId:w.session_id,expiresAt:new Date(w.expires_at)};e(t),null===(r=(i=this.listeners).open)||void 0===r||r.call(i,t);break}case"PartialTranscript":w.created=new Date(w.created),null===(a=(o=this.listeners).transcript)||void 0===a||a.call(o,w),null===(c=(l=this.listeners)["transcript.partial"])||void 0===c||c.call(l,w);break;case"FinalTranscript":w.created=new Date(w.created),null===(u=(d=this.listeners).transcript)||void 0===u||u.call(d,w),null===(p=(m=this.listeners)["transcript.final"])||void 0===p||p.call(m,w);break;case"SessionInformation":null===(v=(f=this.listeners).session_information)||void 0===v||v.call(f,w);break;case"SessionTerminated":null===(g=this.sessionTerminatedResolve)||void 0===g||g.call(this)}}}))}sendAudio(e){this.send(e)}stream(){return new n({write:e=>{this.sendAudio(e)}})}forceEndUtterance(){this.send('{"force_end_utterance":true}')}configureEndUtteranceSilenceThreshold(e){this.send(`{"end_utterance_silence_threshold":${e}}`)}send(e){if(!this.socket||this.socket.readyState!==this.socket.OPEN)throw new Error("Socket is not open for communication");this.socket.send(e)}close(){return s(this,arguments,void 0,(function*(e=!0){var t;if(this.socket){if(this.socket.readyState===this.socket.OPEN)if(e){const e=new Promise((e=>{this.sessionTerminatedResolve=e}));this.socket.send(u),yield e}else this.socket.send(u);(null===(t=this.socket)||void 0===t?void 0:t.removeAllListeners)&&this.socket.removeAllListeners(),this.socket.close()}this.listeners={},this.socket=void 0}))}}class p{constructor(e={}){var t,s,n,i;this.hangoverRemaining=0,this.thresholdRatio=null!==(t=e.thresholdRatio)&&void 0!==t?t:3,this.noiseFloorAlpha=null!==(s=e.noiseFloorAlpha)&&void 0!==s?s:.05,this.hangoverFrames=null!==(n=e.hangoverFrames)&&void 0!==n?n:10,this.initialNoiseFloor=null!==(i=e.initialNoiseFloor)&&void 0!==i?i:1e-4,this.noiseFloor=this.initialNoiseFloor}process(e){let t=0;for(let s=0;s<e.length;s++)t+=e[s]*e[s];const s=e.length>0?Math.sqrt(t/e.length):0;let n=s>this.noiseFloor*this.thresholdRatio;return n?this.hangoverRemaining=this.hangoverFrames:this.hangoverRemaining>0?(this.hangoverRemaining--,n=!0):this.noiseFloor=this.noiseFloor*(1-this.noiseFloorAlpha)+s*this.noiseFloorAlpha,{active:n,energy:s}}reset(){this.noiseFloor=this.initialNoiseFloor,this.hangoverRemaining=0}}class f{constructor(e){this.windowMs=e,this.frames=[],this.head=0}pushFrame(e){this.frames.push(e);const t=e.ts-this.windowMs;for(;this.head<this.frames.length&&this.frames[this.head].ts<t;)this.head++;this.head>1024&&2*this.head>this.frames.length&&(this.frames=this.frames.slice(this.head),this.head=0)}framesInWindow(e,t){const s=[];for(let n=this.head;n<this.frames.length;n++){const i=this.frames[n];if(!(i.ts<e)){if(i.ts>t)break;s.push(i)}}return s}clear(){this.frames=[],this.head=0}}function v(e,t,s){const n=function(e){var t;const s=new Map;for(const n of e)n.active&&s.set(n.channel,(null!==(t=s.get(n.channel))&&void 0!==t?t:0)+n.rms);return s}(t.framesInWindow(e.start,e.end));if(0===n.size)return"unknown";const i=[...n.entries()].sort(((e,t)=>t[1]-e[1]));if(1===i.length)return i[0][0];const[r,o]=i[0],[a,l]=i[1];return o>=s.dominanceRatio*l||o>l?r:l>o?a:"unknown"}function g(e){var t;const s=new Map;for(const n of e){if(!n.channel||"unknown"===n.channel)continue;const e=Math.max(0,n.end-n.start);s.set(n.channel,(null!==(t=s.get(n.channel))&&void 0!==t?t:0)+e)}if(0===s.size)return"unknown";const n=[...s.entries()].sort(((e,t)=>t[1]-e[1]));if(1===n.length)return n[0][0];const[i,r]=n[0],[,o]=n[1];return r===o?"unknown":i}function w(e,t,s){for(const n of e.words)n.channel=v(n,t,s);e.channel=g(e.words)}const k='{"type":"Terminate"}';class y{constructor(e){var t,s,n,i,r,o,a,l,h;if(this.listeners={},this.isDualChannel=!1,this.vadFrameSamples=0,this.minChunkSamples=0,this.maxChunkSamples=0,this.params=Object.assign(Object.assign({},e),{websocketBaseUrl:e.websocketBaseUrl||"wss://streaming.assemblyai.com/v3/ws"}),"token"in e&&e.token&&(this.token=e.token),"apiKey"in e&&e.apiKey&&(this.apiKey=e.apiKey),!this.token&&!this.apiKey)throw new Error("API key or temporary token is required.");if(e.channels){if(2!==e.channels.length)throw new Error("StreamingTranscriber.channels must have exactly 2 entries.");const c=e.channels.map((e=>e.name));if(new Set(c).size!==c.length)throw new Error("StreamingTranscriber.channels names must be unique.");this.isDualChannel=!0,this.channelNames=c;const d=null!==(t=e.channelAttribution)&&void 0!==t?t:{};this.attributionParams={dominanceRatio:null!==(s=d.dominanceRatio)&&void 0!==s?s:4,timelineWindowMs:null!==(n=d.timelineWindowMs)&&void 0!==n?n:3e4,createVad:null!==(i=d.createVad)&&void 0!==i?i:()=>new p,flushIntervalMs:null!==(r=d.flushIntervalMs)&&void 0!==r?r:50,resolveUnknownChannelsMethod:null!==(o=d.resolveUnknownChannelsMethod)&&void 0!==o?o:"window",resolutionWindowWords:null!==(a=d.resolutionWindowWords)&&void 0!==a?a:2,speakerHistoryMinRmsEvidence:null!==(l=d.speakerHistoryMinRmsEvidence)&&void 0!==l?l:.5,speakerHistoryDominanceRatio:null!==(h=d.speakerHistoryDominanceRatio)&&void 0!==h?h:3},"speaker-history"===this.attributionParams.resolveUnknownChannelsMethod&&(this.speakerHistory=new Map),this.vadFrameSamples=Math.max(1,Math.round(.02*e.sampleRate)),this.minChunkSamples=Math.max(1,Math.round(.05*e.sampleRate)),this.maxChunkSamples=Math.max(this.minChunkSamples,Math.round(.2*e.sampleRate)),this.channelBuffers=new Map(c.map((e=>[e,[]]))),this.channelSamplesReceived=new Map(c.map((e=>[e,0]))),this.channelVadFloatBuffers=new Map(c.map((e=>[e,new Float32Array(this.vadFrameSamples)]))),this.channelVadBufferIdx=new Map(c.map((e=>[e,0]))),this.channelVads=new Map(c.map((e=>[e,this.attributionParams.createVad(e)]))),this.timeline=new f(this.attributionParams.timelineWindowMs)}}connectionUrl(){var e,t;const s=new URL(null!==(e=this.params.websocketBaseUrl)&&void 0!==e?e:"");if("wss:"!==s.protocol)throw new Error("Invalid protocol, must be wss");const n=new URLSearchParams;this.token&&n.set("token",this.token),n.set("sample_rate",this.params.sampleRate.toString()),this.params.endOfTurnConfidenceThreshold&&n.set("end_of_turn_confidence_threshold",this.params.endOfTurnConfidenceThreshold.toString()),void 0!==this.params.minEndOfTurnSilenceWhenConfident&&(void 0!==this.params.minTurnSilence?console.warn("[Deprecation Warning] Both `minEndOfTurnSilenceWhenConfident` and `minTurnSilence` are set. Using `minTurnSilence`; `minEndOfTurnSilenceWhenConfident` is deprecated."):console.warn("[Deprecation Warning] `minEndOfTurnSilenceWhenConfident` is deprecated and will be removed in a future release. Please use `minTurnSilence` instead."));const i=null!==(t=this.params.minTurnSilence)&&void 0!==t?t:this.params.minEndOfTurnSilenceWhenConfident;return void 0!==i&&n.set("min_turn_silence",i.toString()),this.params.maxTurnSilence&&n.set("max_turn_silence",this.params.maxTurnSilence.toString()),void 0!==this.params.vadThreshold&&n.set("vad_threshold",this.params.vadThreshold.toString()),this.params.formatTurns&&n.set("format_turns",this.params.formatTurns.toString()),this.params.encoding&&n.set("encoding",this.params.encoding.toString()),this.params.keytermsPrompt?n.set("keyterms_prompt",JSON.stringify(this.params.keytermsPrompt)):this.params.keyterms&&(console.warn("[Deprecation Warning] `keyterms` is deprecated and will be removed in a future release. Please use `keytermsPrompt` instead."),n.set("keyterms_prompt",JSON.stringify(this.params.keyterms))),this.params.prompt&&n.set("prompt",this.params.prompt),this.params.agentContext&&n.set("agent_context",this.params.agentContext),this.params.filterProfanity&&n.set("filter_profanity",this.params.filterProfanity.toString()),"u3-pro"===this.params.speechModel&&console.warn("[Deprecation Warning] The speech model `u3-pro` is deprecated and will be removed in a future release. Please use `u3-rt-pro` instead."),void 0!==this.params.speechModel&&n.set("speech_model",this.params.speechModel.toString()),void 0!==this.params.languageCode&&n.set("language_code",this.params.languageCode),void 0!==this.params.languageDetection&&n.set("language_detection",this.params.languageDetection.toString()),this.params.domain&&n.set("domain",this.params.domain),void 0!==this.params.inactivityTimeout&&n.set("inactivity_timeout",this.params.inactivityTimeout.toString()),void 0!==this.params.speakerLabels&&n.set("speaker_labels",this.params.speakerLabels.toString()),void 0!==this.params.maxSpeakers&&n.set("max_speakers",this.params.maxSpeakers.toString()),this.params.voiceFocus&&n.set("voice_focus",this.params.voiceFocus),void 0!==this.params.voiceFocusThreshold&&n.set("voice_focus_threshold",this.params.voiceFocusThreshold.toString()),void 0!==this.params.continuousPartials&&n.set("continuous_partials",this.params.continuousPartials.toString()),void 0!==this.params.interruptionDelay&&n.set("interruption_delay",this.params.interruptionDelay.toString()),void 0!==this.params.turnLeftPadMs&&n.set("turn_left_pad_ms",this.params.turnLeftPadMs.toString()),this.params.customerSupportAudioCapture&&(console.warn("`customerSupportAudioCapture=true` will record session audio. Only enable this when explicitly coordinating with AssemblyAI support."),n.set("_customer_support_audio_capture",this.params.customerSupportAudioCapture.toString())),this.params.webhookUrl&&n.set("webhook_url",this.params.webhookUrl),this.params.webhookAuthHeaderName&&n.set("webhook_auth_header_name",this.params.webhookAuthHeaderName),this.params.webhookAuthHeaderValue&&n.set("webhook_auth_header_value",this.params.webhookAuthHeaderValue),void 0!==this.params.includePartialTurns&&n.set("include_partial_turns",this.params.includePartialTurns.toString()),void 0!==this.params.redactPii&&n.set("redact_pii",this.params.redactPii.toString()),void 0!==this.params.redactPiiPolicies&&n.set("redact_pii_policies",JSON.stringify(this.params.redactPiiPolicies)),void 0!==this.params.redactPiiSub&&n.set("redact_pii_sub",this.params.redactPiiSub),void 0!==this.params.mode&&n.set("mode",this.params.mode),void 0!==this.params.llmGateway&&n.set("llm_gateway",JSON.stringify(this.params.llmGateway)),s.search=n.toString(),s}on(e,t){this.listeners[e]=t}connect(){return new Promise((e=>{if(this.socket)throw new Error("Already connected");const t=this.connectionUrl();this.token?this.socket=a(t.toString()):(console.warn("API key authentication is not supported for the StreamingTranscriber in browser environment. Use temporary token authentication instead.\nLearn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/compat.md#browser-compatibility."),this.socket=a(t.toString(),{headers:{Authorization:this.apiKey}})),this.socket.binaryType="arraybuffer",this.socket.onopen=()=>{},this.socket.onclose=({code:e,reason:t})=>{var s,n;t||e in c&&(t=c[e]),this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=void 0),null===(n=(s=this.listeners).close)||void 0===n||n.call(s,e,t)},this.socket.onerror=e=>{var t,s,n,i;e.error?null===(s=(t=this.listeners).error)||void 0===s||s.call(t,e.error):null===(i=(n=this.listeners).error)||void 0===i||i.call(n,new Error(e.message))},this.socket.onmessage=({data:t})=>{var s,n,i,r,o,a,l,h,c,u,m,p,f,v,g;const k=JSON.parse(t.toString());if("error"in k){const e=new d(k.error);return"error_code"in k&&(e.code=k.error_code),void(null===(n=(s=this.listeners).error)||void 0===n||n.call(s,e))}switch(k.type){case"Begin":e(k),null===(r=(i=this.listeners).open)||void 0===r||r.call(i,k);break;case"Turn":if(this.isDualChannel&&this.timeline&&this.attributionParams)switch(w(k,this.timeline,{dominanceRatio:this.attributionParams.dominanceRatio}),this.attributionParams.resolveUnknownChannelsMethod){case"window":this.resolveUnknownChannelsByWindow(k);break;case"speaker-history":this.resolveUnknownChannelsBySpeakerHistory(k)}null===(a=(o=this.listeners).turn)||void 0===a||a.call(o,k);break;case"SpeechStarted":null===(h=(l=this.listeners).speechStarted)||void 0===h||h.call(l,k);break;case"LLMGatewayResponse":null===(u=(c=this.listeners).llmGatewayResponse)||void 0===u||u.call(c,k);break;case"SpeakerRevision":null===(p=(m=this.listeners).speakerRevision)||void 0===p||p.call(m,k);break;case"Warning":{const e=k;console.warn(`Streaming warning (code=${e.warning_code}): ${e.warning}`),null===(v=(f=this.listeners).warning)||void 0===v||v.call(f,e);break}case"Termination":null===(g=this.sessionTerminatedResolve)||void 0===g||g.call(this)}}}))}stream(){return new n({write:e=>{this.sendAudio(e)}})}sendAudio(e,t){if(this.isDualChannel){if(!(null==t?void 0:t.channel))throw new Error("StreamingTranscriber is in dual-channel mode; sendAudio requires { channel }.");if(!this.channelNames.includes(t.channel))throw new Error(`Unknown channel "${t.channel}"; declared channels: ${this.channelNames.join(", ")}.`);this.ingestChannelAudio(t.channel,e)}else this.send(e)}ingestChannelAudio(e,t){var s,n;const i=function(e){if(e instanceof Int16Array)return e;if(ArrayBuffer.isView(e)){const t=e;return new Int16Array(t.buffer,t.byteOffset,Math.floor(t.byteLength/2))}return new Int16Array(e)}(t),r=this.channelBuffers.get(e),o=this.channelVadFloatBuffers.get(e);let a=this.channelVadBufferIdx.get(e),l=this.channelSamplesReceived.get(e);const h=this.channelVads.get(e),c=this.params.sampleRate,d=this.vadFrameSamples;for(let t=0;t<i.length;t++){const u=i[t];if(r.push(u),o[a++]=u/32768,l++,a===d){const t=h.process(o),i={ts:l/c*1e3,channel:e,active:t.active,rms:t.energy};this.timeline.pushFrame(i),null===(n=(s=this.listeners).vad)||void 0===n||n.call(s,i),a=0}}this.channelVadBufferIdx.set(e,a),this.channelSamplesReceived.set(e,l),this.flushTimer||this.startFlushTimer()}startFlushTimer(){this.flushTimer=setInterval((()=>this.flushMix()),this.attributionParams.flushIntervalMs)}flushMix(e=!1){var t,s;if(!this.channelNames||!this.channelBuffers)return;const n=this.channelNames.map((e=>this.channelBuffers.get(e))),i=n.length;for(;;){let r=1/0;for(const e of n)e.length<r&&(r=e.length);if(!Number.isFinite(r)||0===r)return;if(!e&&r<this.minChunkSamples)return;r>this.maxChunkSamples&&(r=this.maxChunkSamples);const o=new Int16Array(r);for(let e=0;e<r;e++){let t=0;for(let s=0;s<i;s++)t+=n[s][e];const s=Math.round(t/i);o[e]=s<-32768?-32768:s>32767?32767:s}for(const e of n)e.splice(0,r);try{this.send(o.buffer)}catch(e){return void(null===(s=(t=this.listeners).error)||void 0===s||s.call(t,e))}}}resolveUnknownChannelsByWindow(e){var t;if(!this.attributionParams)return;const s=this.attributionParams.resolutionWindowWords,n=e.words;let i=!1;for(let e=0;e<n.length;e++){if("unknown"!==n[e].channel)continue;const r=new Map,o=Math.max(0,e-s),a=Math.min(n.length-1,e+s);for(let s=o;s<=a;s++){if(s===e)continue;const i=n[s].channel;i&&"unknown"!==i&&r.set(i,(null!==(t=r.get(i))&&void 0!==t?t:0)+1)}if(0===r.size)continue;let l,h=0,c=!1;for(const[e,t]of r)t>h?(l=e,h=t,c=!1):t===h&&(c=!0);l&&!c&&(n[e].channel=l,n[e].channelResolved=!0,i=!0)}i&&(e.channel=g(n))}resolveUnknownChannelsBySpeakerHistory(e){var t;if(!this.timeline||!this.attributionParams||!this.speakerHistory)return;const s=this.attributionParams.speakerHistoryMinRmsEvidence,n=this.attributionParams.speakerHistoryDominanceRatio;for(const s of e.words){if(!s.speaker)continue;const e=this.timeline.framesInWindow(s.start,s.end);let n=this.speakerHistory.get(s.speaker);n||(n=new Map,this.speakerHistory.set(s.speaker,n));for(const s of e)s.active&&n.set(s.channel,(null!==(t=n.get(s.channel))&&void 0!==t?t:0)+s.rms)}let i=!1;for(const t of e.words){if("unknown"!==t.channel||!t.speaker)continue;const e=this.speakerHistory.get(t.speaker);if(!e||0===e.size)continue;let r,o=0,a=0,l=0;for(const[t,s]of e)o+=s,s>a?(l=a,a=s,r=t):s>l&&(l=s);o<s||(l>0&&a<n*l||r&&(t.channel=r,t.channelResolved=!0,i=!0))}i&&(e.channel=g(e.words))}updateConfiguration(e){const{min_end_of_turn_silence_when_confident:t,min_turn_silence:s}=e,n=function(e,t){var s={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.indexOf(n)<0&&(s[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(n=Object.getOwnPropertySymbols(e);i<n.length;i++)t.indexOf(n[i])<0&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(s[n[i]]=e[n[i]])}return s}(e,["min_end_of_turn_silence_when_confident","min_turn_silence"]);void 0!==t&&(void 0!==s?console.warn("[Deprecation Warning] Both `min_end_of_turn_silence_when_confident` and `min_turn_silence` are set. Using `min_turn_silence`; `min_end_of_turn_silence_when_confident` is deprecated."):console.warn("[Deprecation Warning] `min_end_of_turn_silence_when_confident` is deprecated and will be removed in a future release. Please use `min_turn_silence` instead."));const i=null!=s?s:t,r=Object.assign(Object.assign({type:"UpdateConfiguration"},n),void 0!==i?{min_turn_silence:i}:{});this.send(JSON.stringify(r))}forceEndpoint(){this.send(JSON.stringify({type:"ForceEndpoint"}))}send(e){if(!this.socket||this.socket.readyState!==this.socket.OPEN)throw new Error("Socket is not open for communication");this.socket.send(e)}close(){return s(this,arguments,void 0,(function*(e=!0){var t;if(this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=void 0,this.flushMix(!0)),this.socket){if(this.socket.readyState===this.socket.OPEN)if(e){const e=new Promise((e=>{this.sessionTerminatedResolve=e}));this.socket.send(k),yield e}else this.socket.send(k);(null===(t=this.socket)||void 0===t?void 0:t.removeAllListeners)&&this.socket.removeAllListeners(),this.socket.close()}this.listeners={},this.socket=void 0}))}}const S={cache:"no-store"};let b="";"undefined"!=typeof navigator&&navigator.userAgent&&(b+=navigator.userAgent);const _={sdk:{name:"JavaScript",version:"4.34.5"}};"undefined"!=typeof process&&(process.versions.node&&-1===b.indexOf("Node")&&(_.runtime_env={name:"Node",version:process.versions.node}),process.versions.bun&&-1===b.indexOf("Bun")&&(_.runtime_env={name:"Bun",version:process.versions.bun})),"undefined"!=typeof Deno&&process.versions.bun&&-1===b.indexOf("Deno")&&(_.runtime_env={name:"Deno",version:Deno.version.deno});class T{constructor(e){var t;this.params=e,!1===e.userAgent?this.userAgent=void 0:this.userAgent=(t=e.userAgent||{},b+(!1===t?"":" AssemblyAI/1.0 ("+Object.entries(Object.assign(Object.assign({},_),t)).map((([e,t])=>t?`${e}=${t.name}/${t.version}`:"")).join(" ")+")"))}fetch(e,t){return s(this,void 0,void 0,(function*(){t=Object.assign(Object.assign({},S),t);let s={Authorization:this.params.apiKey,"Content-Type":"application/json"};(null==S?void 0:S.headers)&&(s=Object.assign(Object.assign({},s),S.headers)),(null==t?void 0:t.headers)&&(s=Object.assign(Object.assign({},s),t.headers)),this.userAgent&&(s["User-Agent"]=this.userAgent,"undefined"!=typeof window&&"chrome"in window&&(s["AssemblyAI-Agent"]=this.userAgent)),t.headers=s,e.startsWith("http")||(e=this.params.baseUrl+e);const n=yield fetch(e,t);if(n.status>=400){let e;const t=yield n.text();if(t){try{e=JSON.parse(t)}catch(e){}if(null==e?void 0:e.error)throw new Error(e.error);throw new Error(t)}throw new Error(`HTTP Error: ${n.status} ${n.statusText}`)}return n}))}fetchJson(e,t){return s(this,void 0,void 0,(function*(){return(yield this.fetch(e,t)).json()}))}}class A extends T{constructor(e){super(e),this.baseServiceParams=e}transcriber(e){const t=Object.assign({},e);return t.token||t.apiKey||(t.apiKey=this.baseServiceParams.apiKey),new y(t)}createTemporaryToken(e){return s(this,void 0,void 0,(function*(){const t=new URLSearchParams;Object.entries(e).forEach((([e,s])=>{null!=s&&t.append(e,String(s))}));const s=t.toString(),n=s?`/v3/token?${s}`:"/v3/token";return(yield this.fetchJson(n,{method:"GET"})).token}))}}e.BrowserOnlyError=t,e.DualChannelCapture=class{constructor(e){var s;if(this.running=!1,void 0===globalThis.AudioContext)throw new t;this.params={micStream:e.micStream,systemStream:e.systemStream,transcriber:e.transcriber,targetSampleRate:null!==(s=e.targetSampleRate)&&void 0!==s?s:16e3}}on(e,t){"error"===e&&(this.errorListener=t)}start(){return s(this,void 0,void 0,(function*(){if(this.running)throw new Error("DualChannelCapture already started");this.context=new AudioContext;const e=new Blob(['\nclass Pcm16EncoderProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n const opts = (options && options.processorOptions) || {};\n this.targetRate = opts.targetRate || 16000;\n this.chunkMs = opts.chunkMs || 50;\n this.ratio = sampleRate / this.targetRate;\n this.chunkSize = Math.round(this.targetRate * this.chunkMs / 1000);\n this.buffer = new Int16Array(this.chunkSize);\n this.bufferIdx = 0;\n this.samplesSent = 0;\n this.lastSample = 0;\n this.fractional = 0;\n }\n\n process(inputs) {\n const input = inputs[0];\n if (!input || input.length === 0 || !input[0] || input[0].length === 0) {\n return true;\n }\n const mono = input[0];\n let pos = this.fractional;\n while (pos < mono.length) {\n const i = Math.floor(pos);\n const frac = pos - i;\n const a = i === 0 ? this.lastSample : mono[i - 1];\n const b = mono[i];\n const sample = a + (b - a) * frac;\n const clamped = sample < -1 ? -1 : sample > 1 ? 1 : sample;\n this.buffer[this.bufferIdx++] = clamped < 0 ? clamped * 0x8000 : clamped * 0x7fff;\n if (this.bufferIdx === this.chunkSize) {\n const out = new Int16Array(this.chunkSize);\n out.set(this.buffer);\n this.samplesSent += this.chunkSize;\n this.port.postMessage(\n { pcm: out.buffer, samplesSent: this.samplesSent },\n [out.buffer],\n );\n this.bufferIdx = 0;\n }\n pos += this.ratio;\n }\n this.lastSample = mono[mono.length - 1];\n this.fractional = pos - mono.length;\n return true;\n }\n}\nregisterProcessor("aai-pcm16-encoder", Pcm16EncoderProcessor);\n'],{type:"application/javascript"}),t=URL.createObjectURL(e);try{yield this.context.audioWorklet.addModule(t)}finally{URL.revokeObjectURL(t)}this.micSource=this.context.createMediaStreamSource(this.params.micStream),this.sysSource=this.context.createMediaStreamSource(this.params.systemStream),this.micEncoder=this.makeEncoder("mic"),this.sysEncoder=this.makeEncoder("system"),this.micSource.connect(this.micEncoder),this.sysSource.connect(this.sysEncoder),this.running=!0}))}makeEncoder(e){const t=new AudioWorkletNode(this.context,"aai-pcm16-encoder",{numberOfInputs:1,numberOfOutputs:0,channelCount:1,channelCountMode:"explicit",channelInterpretation:"speakers",processorOptions:{targetRate:this.params.targetSampleRate,chunkMs:50}});return t.port.onmessage=t=>{var s;try{this.params.transcriber.sendAudio(t.data.pcm,{channel:e})}catch(e){null===(s=this.errorListener)||void 0===s||s.call(this,e)}},t}stop(){return s(this,void 0,void 0,(function*(){var e,t,s,n,i,r;if(this.running){this.running=!1;try{null===(e=this.micEncoder)||void 0===e||e.port.close(),null===(t=this.sysEncoder)||void 0===t||t.port.close(),null===(s=this.micEncoder)||void 0===s||s.disconnect(),null===(n=this.sysEncoder)||void 0===n||n.disconnect(),null===(i=this.micSource)||void 0===i||i.disconnect(),null===(r=this.sysSource)||void 0===r||r.disconnect()}catch(e){}this.context&&"closed"!==this.context.state&&(yield this.context.close()),this.context=void 0,this.micSource=void 0,this.sysSource=void 0,this.micEncoder=void 0,this.sysEncoder=void 0}}))}},e.EnergyVad=p,e.LinearResampler=class{constructor(e,t){if(this.sourceRate=e,this.targetRate=t,this.lastSample=0,this.fractional=0,e<=0||t<=0)throw new Error("sourceRate and targetRate must be positive");this.ratio=e/t}process(e){var t;if(this.sourceRate===this.targetRate)return e;const s=new Float32Array(Math.ceil(e.length/this.ratio)+1);let n=0,i=this.fractional;for(;i<e.length;){const t=Math.floor(i),r=i-t,o=0===t?this.lastSample:e[t-1],a=e[t];s[n++]=o+(a-o)*r,i+=this.ratio}return this.lastSample=null!==(t=e[e.length-1])&&void 0!==t?t:this.lastSample,this.fractional=i-e.length,s.subarray(0,n)}reset(){this.lastSample=0,this.fractional=0}},e.RealtimeService=class extends m{},e.RealtimeTranscriber=m,e.StreamingServiceFactory=class extends A{},e.StreamingTranscriber=y,e.StreamingTranscriberFactory=A,e.VadTimeline=f,e.attributeTurn=w,e.attributeWord=v,e.float32ToPcm16=function(e){const t=new ArrayBuffer(2*e.length),s=new DataView(t);for(let t=0;t<e.length;t++){const n=Math.max(-1,Math.min(1,e[t]));s.setInt16(2*t,n<0?32768*n:32767*n,!0)}return t},e.rollUpTurnChannel=g}));
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).assemblyai={})}(this,(function(e){"use strict";class t extends Error{constructor(e="DualChannelCapture requires a browser environment (AudioContext is undefined)."){super(e),this.name="BrowserOnlyError"}}function s(e,t,s,n){return new(s||(s=Promise))((function(i,r){function o(e){try{l(n.next(e))}catch(e){r(e)}}function a(e){try{l(n.throw(e))}catch(e){r(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof s?t:new s((function(e){e(t)}))).then(o,a)}l((n=n.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;const{WritableStream:n}="undefined"!=typeof window?window:"undefined"!=typeof global?global:globalThis;var i,r;const o=null!==(r=null!==(i=null!==WebSocket&&void 0!==WebSocket?WebSocket:null===global||void 0===global?void 0:global.WebSocket)&&void 0!==i?i:null===window||void 0===window?void 0:window.WebSocket)&&void 0!==r?r:null===self||void 0===self?void 0:self.WebSocket,a=(e,t)=>t?new o(e,t):new o(e),l={[4e3]:"Sample rate must be a positive integer",[4001]:"Not Authorized",[4002]:"Insufficient funds",[4003]:"This feature is paid-only and requires you to add a credit card. Please visit https://app.assemblyai.com/ to add a credit card to your account.",[4004]:"Session ID does not exist",[4008]:"Session has expired",[4010]:"Session is closed",[4029]:"Rate limited",[4030]:"Unique session violation",[4031]:"Session Timeout",[4032]:"Audio too short",[4033]:"Audio too long",[4034]:"Audio too small to transcode",[4100]:"Bad JSON",[4101]:"Bad schema",[4102]:"Too many streams",[4103]:"This session has been reconnected. This WebSocket is no longer valid.",[1013]:"Reconnect attempts exhausted",[4104]:"Could not parse word boost parameter"};class c extends Error{}const h=4e3,d=4001,u=4002,m=4003,p=4101,f={[3005]:"Server error",[3006]:"Input validation error",[3007]:"Audio chunk duration violation",[3008]:"Session expired: maximum session duration exceeded",[3009]:"Too many concurrent sessions",[h]:"Sample rate must be a positive integer",[d]:"Not Authorized",[u]:"Insufficient funds",[m]:"This feature is paid-only and requires you to add a credit card. Please visit https://app.assemblyai.com/ to add a credit card to your account.",[4004]:"Session ID does not exist",[4008]:"Session has expired",[4010]:"Session is closed",[4029]:"Rate limited",[4030]:"Unique session violation",[4031]:"Session Timeout",[4032]:"Audio too short",[4033]:"Audio too long",[4034]:"Audio too small to transcode",[p]:"Bad schema",[4102]:"Too many streams",[4103]:"This session has been reconnected. This WebSocket is no longer valid."};class v extends Error{}const g='{"terminate_session":true}';class w{constructor(e){var t,s;if(this.listeners={},this.realtimeUrl=null!==(t=e.realtimeUrl)&&void 0!==t?t:"wss://api.assemblyai.com/v2/realtime/ws",this.sampleRate=null!==(s=e.sampleRate)&&void 0!==s?s:16e3,this.wordBoost=e.wordBoost,this.encoding=e.encoding,this.endUtteranceSilenceThreshold=e.endUtteranceSilenceThreshold,this.disablePartialTranscripts=e.disablePartialTranscripts,"token"in e&&e.token&&(this.token=e.token),"apiKey"in e&&e.apiKey&&(this.apiKey=e.apiKey),!this.token&&!this.apiKey)throw new Error("API key or temporary token is required.")}connectionUrl(){const e=new URL(this.realtimeUrl);if("wss:"!==e.protocol)throw new Error("Invalid protocol, must be wss");const t=new URLSearchParams;return this.token&&t.set("token",this.token),t.set("sample_rate",this.sampleRate.toString()),this.wordBoost&&this.wordBoost.length>0&&t.set("word_boost",JSON.stringify(this.wordBoost)),this.encoding&&t.set("encoding",this.encoding),t.set("enable_extra_session_information","true"),this.disablePartialTranscripts&&t.set("disable_partial_transcripts",this.disablePartialTranscripts.toString()),e.search=t.toString(),e}on(e,t){this.listeners[e]=t}connect(){return new Promise((e=>{if(this.socket)throw new Error("Already connected");const t=this.connectionUrl();this.token?this.socket=a(t.toString()):(console.warn("API key authentication is not supported for the RealtimeTranscriber in browser environment. Use temporary token authentication instead.\nLearn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/compat.md#browser-compatibility."),this.socket=a(t.toString(),{headers:{Authorization:this.apiKey}})),this.socket.binaryType="arraybuffer",this.socket.onopen=()=>{void 0!==this.endUtteranceSilenceThreshold&&null!==this.endUtteranceSilenceThreshold&&this.configureEndUtteranceSilenceThreshold(this.endUtteranceSilenceThreshold)},this.socket.onclose=({code:e,reason:t})=>{var s,n;t||e in l&&(t=l[e]),null===(n=(s=this.listeners).close)||void 0===n||n.call(s,e,t)},this.socket.onerror=e=>{var t,s,n,i;e.error?null===(s=(t=this.listeners).error)||void 0===s||s.call(t,e.error):null===(i=(n=this.listeners).error)||void 0===i||i.call(n,new Error(e.message))},this.socket.onmessage=({data:t})=>{var s,n,i,r,o,a,l,h,d,u,m,p,f,v,g;const w=JSON.parse(t.toString());if("error"in w)null===(n=(s=this.listeners).error)||void 0===n||n.call(s,new c(w.error));else switch(w.message_type){case"SessionBegins":{const t={sessionId:w.session_id,expiresAt:new Date(w.expires_at)};e(t),null===(r=(i=this.listeners).open)||void 0===r||r.call(i,t);break}case"PartialTranscript":w.created=new Date(w.created),null===(a=(o=this.listeners).transcript)||void 0===a||a.call(o,w),null===(h=(l=this.listeners)["transcript.partial"])||void 0===h||h.call(l,w);break;case"FinalTranscript":w.created=new Date(w.created),null===(u=(d=this.listeners).transcript)||void 0===u||u.call(d,w),null===(p=(m=this.listeners)["transcript.final"])||void 0===p||p.call(m,w);break;case"SessionInformation":null===(v=(f=this.listeners).session_information)||void 0===v||v.call(f,w);break;case"SessionTerminated":null===(g=this.sessionTerminatedResolve)||void 0===g||g.call(this)}}}))}sendAudio(e){this.send(e)}stream(){return new n({write:e=>{this.sendAudio(e)}})}forceEndUtterance(){this.send('{"force_end_utterance":true}')}configureEndUtteranceSilenceThreshold(e){this.send(`{"end_utterance_silence_threshold":${e}}`)}send(e){if(!this.socket||this.socket.readyState!==this.socket.OPEN)throw new Error("Socket is not open for communication");this.socket.send(e)}close(){return s(this,arguments,void 0,(function*(e=!0){var t;if(this.socket){if(this.socket.readyState===this.socket.OPEN)if(e){const e=new Promise((e=>{this.sessionTerminatedResolve=e}));this.socket.send(g),yield e}else this.socket.send(g);(null===(t=this.socket)||void 0===t?void 0:t.removeAllListeners)&&this.socket.removeAllListeners(),this.socket.close()}this.listeners={},this.socket=void 0}))}}class y{constructor(e={}){var t,s,n,i;this.hangoverRemaining=0,this.thresholdRatio=null!==(t=e.thresholdRatio)&&void 0!==t?t:3,this.noiseFloorAlpha=null!==(s=e.noiseFloorAlpha)&&void 0!==s?s:.05,this.hangoverFrames=null!==(n=e.hangoverFrames)&&void 0!==n?n:10,this.initialNoiseFloor=null!==(i=e.initialNoiseFloor)&&void 0!==i?i:1e-4,this.noiseFloor=this.initialNoiseFloor}process(e){let t=0;for(let s=0;s<e.length;s++)t+=e[s]*e[s];const s=e.length>0?Math.sqrt(t/e.length):0;let n=s>this.noiseFloor*this.thresholdRatio;return n?this.hangoverRemaining=this.hangoverFrames:this.hangoverRemaining>0?(this.hangoverRemaining--,n=!0):this.noiseFloor=this.noiseFloor*(1-this.noiseFloorAlpha)+s*this.noiseFloorAlpha,{active:n,energy:s}}reset(){this.noiseFloor=this.initialNoiseFloor,this.hangoverRemaining=0}}class k{constructor(e){this.windowMs=e,this.frames=[],this.head=0}pushFrame(e){this.frames.push(e);const t=e.ts-this.windowMs;for(;this.head<this.frames.length&&this.frames[this.head].ts<t;)this.head++;this.head>1024&&2*this.head>this.frames.length&&(this.frames=this.frames.slice(this.head),this.head=0)}framesInWindow(e,t){const s=[];for(let n=this.head;n<this.frames.length;n++){const i=this.frames[n];if(!(i.ts<e)){if(i.ts>t)break;s.push(i)}}return s}clear(){this.frames=[],this.head=0}}function S(e,t,s){const n=function(e){var t;const s=new Map;for(const n of e)n.active&&s.set(n.channel,(null!==(t=s.get(n.channel))&&void 0!==t?t:0)+n.rms);return s}(t.framesInWindow(e.start,e.end));if(0===n.size)return"unknown";const i=[...n.entries()].sort(((e,t)=>t[1]-e[1]));if(1===i.length)return i[0][0];const[r,o]=i[0],[a,l]=i[1];return o>=s.dominanceRatio*l||o>l?r:l>o?a:"unknown"}function b(e){var t;const s=new Map;for(const n of e){if(!n.channel||"unknown"===n.channel)continue;const e=Math.max(0,n.end-n.start);s.set(n.channel,(null!==(t=s.get(n.channel))&&void 0!==t?t:0)+e)}if(0===s.size)return"unknown";const n=[...s.entries()].sort(((e,t)=>t[1]-e[1]));if(1===n.length)return n[0][0];const[i,r]=n[0],[,o]=n[1];return r===o?"unknown":i}function _(e,t,s){for(const n of e.words)n.channel=S(n,t,s);e.channel=b(e.words)}const T='{"type":"Terminate"}',A=new Set([h,d,u,m,p]);function x(e){return 1e3!==e&&!A.has(e)}class P{constructor(e){var t,s,n,i,r,o,a,l,c;if(this.listeners={},this.isDualChannel=!1,this.vadFrameSamples=0,this.minChunkSamples=0,this.maxChunkSamples=0,this.params=Object.assign(Object.assign({},e),{websocketBaseUrl:e.websocketBaseUrl||"wss://streaming.assemblyai.com/v3/ws"}),"token"in e&&e.token&&(this.token=e.token),"apiKey"in e&&e.apiKey&&(this.apiKey=e.apiKey),!this.token&&!this.apiKey)throw new Error("API key or temporary token is required.");if(e.channels){if(2!==e.channels.length)throw new Error("StreamingTranscriber.channels must have exactly 2 entries.");const h=e.channels.map((e=>e.name));if(new Set(h).size!==h.length)throw new Error("StreamingTranscriber.channels names must be unique.");this.isDualChannel=!0,this.channelNames=h;const d=null!==(t=e.channelAttribution)&&void 0!==t?t:{};this.attributionParams={dominanceRatio:null!==(s=d.dominanceRatio)&&void 0!==s?s:4,timelineWindowMs:null!==(n=d.timelineWindowMs)&&void 0!==n?n:3e4,createVad:null!==(i=d.createVad)&&void 0!==i?i:()=>new y,flushIntervalMs:null!==(r=d.flushIntervalMs)&&void 0!==r?r:50,resolveUnknownChannelsMethod:null!==(o=d.resolveUnknownChannelsMethod)&&void 0!==o?o:"window",resolutionWindowWords:null!==(a=d.resolutionWindowWords)&&void 0!==a?a:2,speakerHistoryMinRmsEvidence:null!==(l=d.speakerHistoryMinRmsEvidence)&&void 0!==l?l:.5,speakerHistoryDominanceRatio:null!==(c=d.speakerHistoryDominanceRatio)&&void 0!==c?c:3},"speaker-history"===this.attributionParams.resolveUnknownChannelsMethod&&(this.speakerHistory=new Map),this.vadFrameSamples=Math.max(1,Math.round(.02*e.sampleRate)),this.minChunkSamples=Math.max(1,Math.round(.05*e.sampleRate)),this.maxChunkSamples=Math.max(this.minChunkSamples,Math.round(.2*e.sampleRate)),this.channelBuffers=new Map(h.map((e=>[e,[]]))),this.channelSamplesReceived=new Map(h.map((e=>[e,0]))),this.channelVadFloatBuffers=new Map(h.map((e=>[e,new Float32Array(this.vadFrameSamples)]))),this.channelVadBufferIdx=new Map(h.map((e=>[e,0]))),this.channelVads=new Map(h.map((e=>[e,this.attributionParams.createVad(e)]))),this.timeline=new k(this.attributionParams.timelineWindowMs)}}connectionUrl(){var e,t;const s=new URL(null!==(e=this.params.websocketBaseUrl)&&void 0!==e?e:"");if("wss:"!==s.protocol)throw new Error("Invalid protocol, must be wss");const n=new URLSearchParams;this.token&&n.set("token",this.token),n.set("sample_rate",this.params.sampleRate.toString()),this.params.endOfTurnConfidenceThreshold&&n.set("end_of_turn_confidence_threshold",this.params.endOfTurnConfidenceThreshold.toString()),void 0!==this.params.minEndOfTurnSilenceWhenConfident&&(void 0!==this.params.minTurnSilence?console.warn("[Deprecation Warning] Both `minEndOfTurnSilenceWhenConfident` and `minTurnSilence` are set. Using `minTurnSilence`; `minEndOfTurnSilenceWhenConfident` is deprecated."):console.warn("[Deprecation Warning] `minEndOfTurnSilenceWhenConfident` is deprecated and will be removed in a future release. Please use `minTurnSilence` instead."));const i=null!==(t=this.params.minTurnSilence)&&void 0!==t?t:this.params.minEndOfTurnSilenceWhenConfident;return void 0!==i&&n.set("min_turn_silence",i.toString()),this.params.maxTurnSilence&&n.set("max_turn_silence",this.params.maxTurnSilence.toString()),void 0!==this.params.vadThreshold&&n.set("vad_threshold",this.params.vadThreshold.toString()),this.params.formatTurns&&n.set("format_turns",this.params.formatTurns.toString()),this.params.encoding&&n.set("encoding",this.params.encoding.toString()),this.params.keytermsPrompt?n.set("keyterms_prompt",JSON.stringify(this.params.keytermsPrompt)):this.params.keyterms&&(console.warn("[Deprecation Warning] `keyterms` is deprecated and will be removed in a future release. Please use `keytermsPrompt` instead."),n.set("keyterms_prompt",JSON.stringify(this.params.keyterms))),this.params.prompt&&n.set("prompt",this.params.prompt),this.params.agentContext&&n.set("agent_context",this.params.agentContext),this.params.filterProfanity&&n.set("filter_profanity",this.params.filterProfanity.toString()),"u3-pro"===this.params.speechModel&&console.warn("[Deprecation Warning] The speech model `u3-pro` is deprecated and will be removed in a future release. Please use `u3-rt-pro` instead."),void 0!==this.params.speechModel&&n.set("speech_model",this.params.speechModel.toString()),void 0!==this.params.languageCode&&n.set("language_code",this.params.languageCode),void 0!==this.params.languageDetection&&n.set("language_detection",this.params.languageDetection.toString()),this.params.domain&&n.set("domain",this.params.domain),void 0!==this.params.inactivityTimeout&&n.set("inactivity_timeout",this.params.inactivityTimeout.toString()),void 0!==this.params.speakerLabels&&n.set("speaker_labels",this.params.speakerLabels.toString()),void 0!==this.params.maxSpeakers&&n.set("max_speakers",this.params.maxSpeakers.toString()),this.params.voiceFocus&&n.set("voice_focus",this.params.voiceFocus),void 0!==this.params.voiceFocusThreshold&&n.set("voice_focus_threshold",this.params.voiceFocusThreshold.toString()),void 0!==this.params.continuousPartials&&n.set("continuous_partials",this.params.continuousPartials.toString()),void 0!==this.params.interruptionDelay&&n.set("interruption_delay",this.params.interruptionDelay.toString()),void 0!==this.params.turnLeftPadMs&&n.set("turn_left_pad_ms",this.params.turnLeftPadMs.toString()),this.params.customerSupportAudioCapture&&(console.warn("`customerSupportAudioCapture=true` will record session audio. Only enable this when explicitly coordinating with AssemblyAI support."),n.set("_customer_support_audio_capture",this.params.customerSupportAudioCapture.toString())),this.params.webhookUrl&&n.set("webhook_url",this.params.webhookUrl),this.params.webhookAuthHeaderName&&n.set("webhook_auth_header_name",this.params.webhookAuthHeaderName),this.params.webhookAuthHeaderValue&&n.set("webhook_auth_header_value",this.params.webhookAuthHeaderValue),void 0!==this.params.includePartialTurns&&n.set("include_partial_turns",this.params.includePartialTurns.toString()),void 0!==this.params.redactPii&&n.set("redact_pii",this.params.redactPii.toString()),void 0!==this.params.redactPiiPolicies&&n.set("redact_pii_policies",JSON.stringify(this.params.redactPiiPolicies)),void 0!==this.params.redactPiiSub&&n.set("redact_pii_sub",this.params.redactPiiSub),void 0!==this.params.mode&&n.set("mode",this.params.mode),void 0!==this.params.llmGateway&&n.set("llm_gateway",JSON.stringify(this.params.llmGateway)),s.search=n.toString(),s}on(e,t){this.listeners[e]=t}connect(){return s(this,void 0,void 0,(function*(){var e,t;if(this.socket)throw new Error("Already connected");const s=null!==(e=this.params.maxConnectionRetries)&&void 0!==e?e:2,n=null!==(t=this.params.connectionRetryDelay)&&void 0!==t?t:500;let i;for(let e=0;e<=s;e++)try{return yield this.connectOnce()}catch(t){i=t;if(!(!0===t.retryable)||e===s)throw t;console.warn(`Streaming connect attempt ${e+1}/${s+1} failed (${t.message}); retrying`),n>0&&(yield new Promise((e=>setTimeout(e,n))))}throw null!=i?i:new Error("Failed to connect to streaming server")}))}connectOnce(){return new Promise(((e,t)=>{var s;const n=this.connectionUrl(),i=null!==(s=this.params.connectTimeout)&&void 0!==s?s:1e3;let r,o=!1;const l=e=>{o||(o=!0,r&&clearTimeout(r),this.discardPendingSocket(),t(e))};i>0&&(r=setTimeout((()=>{const e=new v(`Streaming connection timed out after ${i}ms`);e.retryable=!0,l(e)}),i)),this.token?this.socket=a(n.toString()):(console.warn("API key authentication is not supported for the StreamingTranscriber in browser environment. Use temporary token authentication instead.\nLearn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/compat.md#browser-compatibility."),this.socket=a(n.toString(),{headers:{Authorization:this.apiKey}})),this.socket.binaryType="arraybuffer",this.socket.onopen=()=>{},this.socket.onclose=({code:e,reason:t})=>{var s,n;if(t||e in f&&(t=f[e]),!o){const s=new v(t||`Streaming connection closed (code=${e})`);return s.code=e,s.retryable=x(e),void l(s)}this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=void 0),null===(n=(s=this.listeners).close)||void 0===n||n.call(s,e,t)},this.socket.onerror=e=>{var t,s,n;const i=null!==(t=e.error)&&void 0!==t?t:new Error(e.message);if(!o)return i.retryable=!0,void l(i);null===(n=(s=this.listeners).error)||void 0===n||n.call(s,i)},this.socket.onmessage=({data:t})=>{var s,n,i,a,c,h,d,u,m,p,f,g,w,y,k;const S=JSON.parse(t.toString());if("error"in S){const e=new v(S.error);if("error_code"in S&&(e.code=S.error_code),!o){const t=e;return t.retryable=void 0===e.code||x(e.code),void l(t)}null===(n=(s=this.listeners).error)||void 0===n||n.call(s,e)}else{switch(S.type){case"Begin":b=S,o||(o=!0,r&&clearTimeout(r),e(b)),null===(a=(i=this.listeners).open)||void 0===a||a.call(i,S);break;case"Turn":if(this.isDualChannel&&this.timeline&&this.attributionParams)switch(_(S,this.timeline,{dominanceRatio:this.attributionParams.dominanceRatio}),this.attributionParams.resolveUnknownChannelsMethod){case"window":this.resolveUnknownChannelsByWindow(S);break;case"speaker-history":this.resolveUnknownChannelsBySpeakerHistory(S)}null===(h=(c=this.listeners).turn)||void 0===h||h.call(c,S);break;case"SpeechStarted":null===(u=(d=this.listeners).speechStarted)||void 0===u||u.call(d,S);break;case"LLMGatewayResponse":null===(p=(m=this.listeners).llmGatewayResponse)||void 0===p||p.call(m,S);break;case"SpeakerRevision":null===(g=(f=this.listeners).speakerRevision)||void 0===g||g.call(f,S);break;case"Warning":{const e=S;console.warn(`Streaming warning (code=${e.warning_code}): ${e.warning}`),null===(y=(w=this.listeners).warning)||void 0===y||y.call(w,e);break}case"Termination":null===(k=this.sessionTerminatedResolve)||void 0===k||k.call(this)}var b}}}))}discardPendingSocket(){if(this.socket){try{this.socket.removeAllListeners&&this.socket.removeAllListeners(),this.socket.close()}catch(e){}this.socket=void 0}}stream(){return new n({write:e=>{this.sendAudio(e)}})}sendAudio(e,t){if(this.isDualChannel){if(!(null==t?void 0:t.channel))throw new Error("StreamingTranscriber is in dual-channel mode; sendAudio requires { channel }.");if(!this.channelNames.includes(t.channel))throw new Error(`Unknown channel "${t.channel}"; declared channels: ${this.channelNames.join(", ")}.`);this.ingestChannelAudio(t.channel,e)}else this.send(e)}ingestChannelAudio(e,t){var s,n;const i=function(e){if(e instanceof Int16Array)return e;if(ArrayBuffer.isView(e)){const t=e;return new Int16Array(t.buffer,t.byteOffset,Math.floor(t.byteLength/2))}return new Int16Array(e)}(t),r=this.channelBuffers.get(e),o=this.channelVadFloatBuffers.get(e);let a=this.channelVadBufferIdx.get(e),l=this.channelSamplesReceived.get(e);const c=this.channelVads.get(e),h=this.params.sampleRate,d=this.vadFrameSamples;for(let t=0;t<i.length;t++){const u=i[t];if(r.push(u),o[a++]=u/32768,l++,a===d){const t=c.process(o),i={ts:l/h*1e3,channel:e,active:t.active,rms:t.energy};this.timeline.pushFrame(i),null===(n=(s=this.listeners).vad)||void 0===n||n.call(s,i),a=0}}this.channelVadBufferIdx.set(e,a),this.channelSamplesReceived.set(e,l),this.flushTimer||this.startFlushTimer()}startFlushTimer(){this.flushTimer=setInterval((()=>this.flushMix()),this.attributionParams.flushIntervalMs)}flushMix(e=!1){var t,s;if(!this.channelNames||!this.channelBuffers)return;const n=this.channelNames.map((e=>this.channelBuffers.get(e))),i=n.length;for(;;){let r=1/0;for(const e of n)e.length<r&&(r=e.length);if(!Number.isFinite(r)||0===r)return;if(!e&&r<this.minChunkSamples)return;r>this.maxChunkSamples&&(r=this.maxChunkSamples);const o=new Int16Array(r);for(let e=0;e<r;e++){let t=0;for(let s=0;s<i;s++)t+=n[s][e];const s=Math.round(t/i);o[e]=s<-32768?-32768:s>32767?32767:s}for(const e of n)e.splice(0,r);try{this.send(o.buffer)}catch(e){return void(null===(s=(t=this.listeners).error)||void 0===s||s.call(t,e))}}}resolveUnknownChannelsByWindow(e){var t;if(!this.attributionParams)return;const s=this.attributionParams.resolutionWindowWords,n=e.words;let i=!1;for(let e=0;e<n.length;e++){if("unknown"!==n[e].channel)continue;const r=new Map,o=Math.max(0,e-s),a=Math.min(n.length-1,e+s);for(let s=o;s<=a;s++){if(s===e)continue;const i=n[s].channel;i&&"unknown"!==i&&r.set(i,(null!==(t=r.get(i))&&void 0!==t?t:0)+1)}if(0===r.size)continue;let l,c=0,h=!1;for(const[e,t]of r)t>c?(l=e,c=t,h=!1):t===c&&(h=!0);l&&!h&&(n[e].channel=l,n[e].channelResolved=!0,i=!0)}i&&(e.channel=b(n))}resolveUnknownChannelsBySpeakerHistory(e){var t;if(!this.timeline||!this.attributionParams||!this.speakerHistory)return;const s=this.attributionParams.speakerHistoryMinRmsEvidence,n=this.attributionParams.speakerHistoryDominanceRatio;for(const s of e.words){if(!s.speaker)continue;const e=this.timeline.framesInWindow(s.start,s.end);let n=this.speakerHistory.get(s.speaker);n||(n=new Map,this.speakerHistory.set(s.speaker,n));for(const s of e)s.active&&n.set(s.channel,(null!==(t=n.get(s.channel))&&void 0!==t?t:0)+s.rms)}let i=!1;for(const t of e.words){if("unknown"!==t.channel||!t.speaker)continue;const e=this.speakerHistory.get(t.speaker);if(!e||0===e.size)continue;let r,o=0,a=0,l=0;for(const[t,s]of e)o+=s,s>a?(l=a,a=s,r=t):s>l&&(l=s);o<s||(l>0&&a<n*l||r&&(t.channel=r,t.channelResolved=!0,i=!0))}i&&(e.channel=b(e.words))}updateConfiguration(e){const{min_end_of_turn_silence_when_confident:t,min_turn_silence:s}=e,n=function(e,t){var s={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.indexOf(n)<0&&(s[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(n=Object.getOwnPropertySymbols(e);i<n.length;i++)t.indexOf(n[i])<0&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(s[n[i]]=e[n[i]])}return s}(e,["min_end_of_turn_silence_when_confident","min_turn_silence"]);void 0!==t&&(void 0!==s?console.warn("[Deprecation Warning] Both `min_end_of_turn_silence_when_confident` and `min_turn_silence` are set. Using `min_turn_silence`; `min_end_of_turn_silence_when_confident` is deprecated."):console.warn("[Deprecation Warning] `min_end_of_turn_silence_when_confident` is deprecated and will be removed in a future release. Please use `min_turn_silence` instead."));const i=null!=s?s:t,r=Object.assign(Object.assign({type:"UpdateConfiguration"},n),void 0!==i?{min_turn_silence:i}:{});this.send(JSON.stringify(r))}forceEndpoint(){this.send(JSON.stringify({type:"ForceEndpoint"}))}keepAlive(){this.send(JSON.stringify({type:"KeepAlive"}))}send(e){if(!this.socket||this.socket.readyState!==this.socket.OPEN)throw new Error("Socket is not open for communication");this.socket.send(e)}close(){return s(this,arguments,void 0,(function*(e=!0){var t;if(this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=void 0,this.flushMix(!0)),this.socket){if(this.socket.readyState===this.socket.OPEN)if(e){const e=new Promise((e=>{this.sessionTerminatedResolve=e}));this.socket.send(T),yield e}else this.socket.send(T);(null===(t=this.socket)||void 0===t?void 0:t.removeAllListeners)&&this.socket.removeAllListeners(),this.socket.close()}this.listeners={},this.socket=void 0}))}}const R={cache:"no-store"};let E="";"undefined"!=typeof navigator&&navigator.userAgent&&(E+=navigator.userAgent);const O={sdk:{name:"JavaScript",version:"4.35.0"}};"undefined"!=typeof process&&(process.versions.node&&-1===E.indexOf("Node")&&(O.runtime_env={name:"Node",version:process.versions.node}),process.versions.bun&&-1===E.indexOf("Bun")&&(O.runtime_env={name:"Bun",version:process.versions.bun})),"undefined"!=typeof Deno&&process.versions.bun&&-1===E.indexOf("Deno")&&(O.runtime_env={name:"Deno",version:Deno.version.deno});class M{constructor(e){var t;this.params=e,!1===e.userAgent?this.userAgent=void 0:this.userAgent=(t=e.userAgent||{},E+(!1===t?"":" AssemblyAI/1.0 ("+Object.entries(Object.assign(Object.assign({},O),t)).map((([e,t])=>t?`${e}=${t.name}/${t.version}`:"")).join(" ")+")"))}fetch(e,t){return s(this,void 0,void 0,(function*(){t=Object.assign(Object.assign({},R),t);let s={Authorization:this.params.apiKey,"Content-Type":"application/json"};(null==R?void 0:R.headers)&&(s=Object.assign(Object.assign({},s),R.headers)),(null==t?void 0:t.headers)&&(s=Object.assign(Object.assign({},s),t.headers)),this.userAgent&&(s["User-Agent"]=this.userAgent,"undefined"!=typeof window&&"chrome"in window&&(s["AssemblyAI-Agent"]=this.userAgent)),t.headers=s,e.startsWith("http")||(e=this.params.baseUrl+e);const n=yield fetch(e,t);if(n.status>=400){let e;const t=yield n.text();if(t){try{e=JSON.parse(t)}catch(e){}if(null==e?void 0:e.error)throw new Error(e.error);throw new Error(t)}throw new Error(`HTTP Error: ${n.status} ${n.statusText}`)}return n}))}fetchJson(e,t){return s(this,void 0,void 0,(function*(){return(yield this.fetch(e,t)).json()}))}}class C extends M{constructor(e){super(e),this.baseServiceParams=e}transcriber(e){const t=Object.assign({},e);return t.token||t.apiKey||(t.apiKey=this.baseServiceParams.apiKey),new P(t)}createTemporaryToken(e){return s(this,void 0,void 0,(function*(){const t=new URLSearchParams;Object.entries(e).forEach((([e,s])=>{null!=s&&t.append(e,String(s))}));const s=t.toString(),n=s?`/v3/token?${s}`:"/v3/token";return(yield this.fetchJson(n,{method:"GET"})).token}))}}e.BrowserOnlyError=t,e.DualChannelCapture=class{constructor(e){var s;if(this.running=!1,void 0===globalThis.AudioContext)throw new t;this.params={micStream:e.micStream,systemStream:e.systemStream,transcriber:e.transcriber,targetSampleRate:null!==(s=e.targetSampleRate)&&void 0!==s?s:16e3}}on(e,t){"error"===e&&(this.errorListener=t)}start(){return s(this,void 0,void 0,(function*(){if(this.running)throw new Error("DualChannelCapture already started");this.context=new AudioContext;const e=new Blob(['\nclass Pcm16EncoderProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n const opts = (options && options.processorOptions) || {};\n this.targetRate = opts.targetRate || 16000;\n this.chunkMs = opts.chunkMs || 50;\n this.ratio = sampleRate / this.targetRate;\n this.chunkSize = Math.round(this.targetRate * this.chunkMs / 1000);\n this.buffer = new Int16Array(this.chunkSize);\n this.bufferIdx = 0;\n this.samplesSent = 0;\n this.lastSample = 0;\n this.fractional = 0;\n }\n\n process(inputs) {\n const input = inputs[0];\n if (!input || input.length === 0 || !input[0] || input[0].length === 0) {\n return true;\n }\n const mono = input[0];\n let pos = this.fractional;\n while (pos < mono.length) {\n const i = Math.floor(pos);\n const frac = pos - i;\n const a = i === 0 ? this.lastSample : mono[i - 1];\n const b = mono[i];\n const sample = a + (b - a) * frac;\n const clamped = sample < -1 ? -1 : sample > 1 ? 1 : sample;\n this.buffer[this.bufferIdx++] = clamped < 0 ? clamped * 0x8000 : clamped * 0x7fff;\n if (this.bufferIdx === this.chunkSize) {\n const out = new Int16Array(this.chunkSize);\n out.set(this.buffer);\n this.samplesSent += this.chunkSize;\n this.port.postMessage(\n { pcm: out.buffer, samplesSent: this.samplesSent },\n [out.buffer],\n );\n this.bufferIdx = 0;\n }\n pos += this.ratio;\n }\n this.lastSample = mono[mono.length - 1];\n this.fractional = pos - mono.length;\n return true;\n }\n}\nregisterProcessor("aai-pcm16-encoder", Pcm16EncoderProcessor);\n'],{type:"application/javascript"}),t=URL.createObjectURL(e);try{yield this.context.audioWorklet.addModule(t)}finally{URL.revokeObjectURL(t)}this.micSource=this.context.createMediaStreamSource(this.params.micStream),this.sysSource=this.context.createMediaStreamSource(this.params.systemStream),this.micEncoder=this.makeEncoder("mic"),this.sysEncoder=this.makeEncoder("system"),this.micSource.connect(this.micEncoder),this.sysSource.connect(this.sysEncoder),this.running=!0}))}makeEncoder(e){const t=new AudioWorkletNode(this.context,"aai-pcm16-encoder",{numberOfInputs:1,numberOfOutputs:0,channelCount:1,channelCountMode:"explicit",channelInterpretation:"speakers",processorOptions:{targetRate:this.params.targetSampleRate,chunkMs:50}});return t.port.onmessage=t=>{var s;try{this.params.transcriber.sendAudio(t.data.pcm,{channel:e})}catch(e){null===(s=this.errorListener)||void 0===s||s.call(this,e)}},t}stop(){return s(this,void 0,void 0,(function*(){var e,t,s,n,i,r;if(this.running){this.running=!1;try{null===(e=this.micEncoder)||void 0===e||e.port.close(),null===(t=this.sysEncoder)||void 0===t||t.port.close(),null===(s=this.micEncoder)||void 0===s||s.disconnect(),null===(n=this.sysEncoder)||void 0===n||n.disconnect(),null===(i=this.micSource)||void 0===i||i.disconnect(),null===(r=this.sysSource)||void 0===r||r.disconnect()}catch(e){}this.context&&"closed"!==this.context.state&&(yield this.context.close()),this.context=void 0,this.micSource=void 0,this.sysSource=void 0,this.micEncoder=void 0,this.sysEncoder=void 0}}))}},e.EnergyVad=y,e.LinearResampler=class{constructor(e,t){if(this.sourceRate=e,this.targetRate=t,this.lastSample=0,this.fractional=0,e<=0||t<=0)throw new Error("sourceRate and targetRate must be positive");this.ratio=e/t}process(e){var t;if(this.sourceRate===this.targetRate)return e;const s=new Float32Array(Math.ceil(e.length/this.ratio)+1);let n=0,i=this.fractional;for(;i<e.length;){const t=Math.floor(i),r=i-t,o=0===t?this.lastSample:e[t-1],a=e[t];s[n++]=o+(a-o)*r,i+=this.ratio}return this.lastSample=null!==(t=e[e.length-1])&&void 0!==t?t:this.lastSample,this.fractional=i-e.length,s.subarray(0,n)}reset(){this.lastSample=0,this.fractional=0}},e.RealtimeService=class extends w{},e.RealtimeTranscriber=w,e.StreamingServiceFactory=class extends C{},e.StreamingTranscriber=P,e.StreamingTranscriberFactory=C,e.VadTimeline=k,e.attributeTurn=_,e.attributeWord=S,e.float32ToPcm16=function(e){const t=new ArrayBuffer(2*e.length),s=new DataView(t);for(let t=0;t<e.length;t++){const n=Math.max(-1,Math.min(1,e[t]));s.setInt16(2*t,n<0?32768*n:32767*n,!0)}return t},e.rollUpTurnChannel=b}));
@@ -78,7 +78,7 @@
78
78
  defaultUserAgentString += navigator.userAgent;
79
79
  }
80
80
  const defaultUserAgent = {
81
- sdk: { name: "JavaScript", version: "4.34.5" },
81
+ sdk: { name: "JavaScript", version: "4.35.0" },
82
82
  };
83
83
  if (typeof process !== "undefined") {
84
84
  if (process.versions.node && defaultUserAgentString.indexOf("Node") === -1) {
@@ -1090,6 +1090,24 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
1090
1090
  }
1091
1091
  const defaultStreamingUrl$1 = "wss://streaming.assemblyai.com/v3/ws";
1092
1092
  const terminateSessionMessage = `{"type":"Terminate"}`;
1093
+ const DEFAULT_CONNECT_TIMEOUT_MS = 1000;
1094
+ const DEFAULT_MAX_CONNECTION_RETRIES = 2;
1095
+ const DEFAULT_CONNECTION_RETRY_DELAY_MS = 500;
1096
+ /**
1097
+ * Close/error codes that signal a permanent client-side problem (auth,
1098
+ * billing, malformed config). A retry would hit the same failure, so the
1099
+ * connection is never retried on these.
1100
+ */
1101
+ const NON_RETRYABLE_CLOSE_CODES = new Set([
1102
+ StreamingErrorType.BadSampleRate,
1103
+ StreamingErrorType.AuthFailed,
1104
+ StreamingErrorType.InsufficientFunds,
1105
+ StreamingErrorType.FreeTierUser,
1106
+ StreamingErrorType.BadSchema,
1107
+ ]);
1108
+ function isRetryableCloseCode(code) {
1109
+ return code !== 1000 && !NON_RETRYABLE_CLOSE_CODES.has(code);
1110
+ }
1093
1111
  /**
1094
1112
  * Per-send chunk cap in milliseconds for the dual-channel mixer. The streaming
1095
1113
  * server rejects audio messages longer than 1000 ms (`Input Duration Error`).
@@ -1296,12 +1314,85 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
1296
1314
  on(event, listener) {
1297
1315
  this.listeners[event] = listener;
1298
1316
  }
1317
+ /**
1318
+ * Open the streaming session.
1319
+ *
1320
+ * Resolves with the server's `Begin` event once the handshake completes. A
1321
+ * single attempt is bounded by `connectTimeout` (default 1000ms); transient
1322
+ * failures (timeout, network drop, unexpected close) are retried up to
1323
+ * `maxConnectionRetries` times (default 2), waiting `connectionRetryDelay`
1324
+ * (default 500ms) between attempts. Permanent failures (auth, insufficient
1325
+ * funds, malformed config) are not retried.
1326
+ *
1327
+ * Unlike previously, a failed connection now rejects this promise rather
1328
+ * than only invoking the `error` listener — necessary for the caller (and
1329
+ * the retry loop) to observe the failure.
1330
+ */
1299
1331
  connect() {
1300
- return new Promise((resolve) => {
1332
+ return __awaiter(this, void 0, void 0, function* () {
1333
+ var _a, _b;
1301
1334
  if (this.socket) {
1302
1335
  throw new Error("Already connected");
1303
1336
  }
1337
+ const maxRetries = (_a = this.params.maxConnectionRetries) !== null && _a !== void 0 ? _a : DEFAULT_MAX_CONNECTION_RETRIES;
1338
+ const retryDelay = (_b = this.params.connectionRetryDelay) !== null && _b !== void 0 ? _b : DEFAULT_CONNECTION_RETRY_DELAY_MS;
1339
+ let lastError;
1340
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1341
+ try {
1342
+ return yield this.connectOnce();
1343
+ }
1344
+ catch (err) {
1345
+ lastError = err;
1346
+ const retryable = err.retryable === true;
1347
+ if (!retryable || attempt === maxRetries) {
1348
+ throw err;
1349
+ }
1350
+ console.warn(`Streaming connect attempt ${attempt + 1}/${maxRetries + 1} failed (${err.message}); retrying`);
1351
+ if (retryDelay > 0) {
1352
+ yield new Promise((resolve) => setTimeout(resolve, retryDelay));
1353
+ }
1354
+ }
1355
+ }
1356
+ // The loop above always returns or throws; this only satisfies the type
1357
+ // checker that a value is produced on every path.
1358
+ throw lastError !== null && lastError !== void 0 ? lastError : new Error("Failed to connect to streaming server");
1359
+ });
1360
+ }
1361
+ connectOnce() {
1362
+ return new Promise((resolve, reject) => {
1363
+ var _a;
1304
1364
  const url = this.connectionUrl();
1365
+ const timeoutMs = (_a = this.params.connectTimeout) !== null && _a !== void 0 ? _a : DEFAULT_CONNECT_TIMEOUT_MS;
1366
+ // `settled` flips once this attempt has resolved (`Begin`) or rejected
1367
+ // (timeout / pre-`Begin` close / error). Before it flips the socket
1368
+ // handlers drive this promise; after it flips they revert to normal
1369
+ // runtime dispatch (close / error / message listeners).
1370
+ let settled = false;
1371
+ let timer;
1372
+ const failAttempt = (error) => {
1373
+ if (settled)
1374
+ return;
1375
+ settled = true;
1376
+ if (timer)
1377
+ clearTimeout(timer);
1378
+ this.discardPendingSocket();
1379
+ reject(error);
1380
+ };
1381
+ const succeed = (begin) => {
1382
+ if (settled)
1383
+ return;
1384
+ settled = true;
1385
+ if (timer)
1386
+ clearTimeout(timer);
1387
+ resolve(begin);
1388
+ };
1389
+ if (timeoutMs > 0) {
1390
+ timer = setTimeout(() => {
1391
+ const err = new StreamingError(`Streaming connection timed out after ${timeoutMs}ms`);
1392
+ err.retryable = true;
1393
+ failAttempt(err);
1394
+ }, timeoutMs);
1395
+ }
1305
1396
  if (this.token) {
1306
1397
  this.socket = factory(url.toString());
1307
1398
  }
@@ -1323,6 +1414,15 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
1323
1414
  reason = StreamingErrorMessages[code];
1324
1415
  }
1325
1416
  }
1417
+ // A close before `Begin` is a failed connection attempt — reject so
1418
+ // connect() can retry (or surface a permanent failure).
1419
+ if (!settled) {
1420
+ const err = new StreamingError(reason || `Streaming connection closed (code=${code})`);
1421
+ err.code = code;
1422
+ err.retryable = isRetryableCloseCode(code);
1423
+ failAttempt(err);
1424
+ return;
1425
+ }
1326
1426
  // Stop the flush timer when the socket is gone (server-initiated close,
1327
1427
  // network drop, etc.) — otherwise subsequent ticks call send() on a
1328
1428
  // closed socket and spam the error listener.
@@ -1333,11 +1433,15 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
1333
1433
  (_b = (_a = this.listeners).close) === null || _b === void 0 ? void 0 : _b.call(_a, code, reason);
1334
1434
  };
1335
1435
  this.socket.onerror = (event) => {
1336
- var _a, _b, _c, _d;
1337
- if (event.error)
1338
- (_b = (_a = this.listeners).error) === null || _b === void 0 ? void 0 : _b.call(_a, event.error);
1339
- else
1340
- (_d = (_c = this.listeners).error) === null || _d === void 0 ? void 0 : _d.call(_c, new Error(event.message));
1436
+ var _a, _b, _c;
1437
+ const error = (_a = event.error) !== null && _a !== void 0 ? _a : new Error(event.message);
1438
+ // A socket error before `Begin` is a failed attempt reject/retry.
1439
+ if (!settled) {
1440
+ error.retryable = true;
1441
+ failAttempt(error);
1442
+ return;
1443
+ }
1444
+ (_c = (_b = this.listeners).error) === null || _c === void 0 ? void 0 : _c.call(_b, error);
1341
1445
  };
1342
1446
  this.socket.onmessage = ({ data }) => {
1343
1447
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
@@ -1345,15 +1449,23 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
1345
1449
  if ("error" in message) {
1346
1450
  const err = new StreamingError(message.error);
1347
1451
  if ("error_code" in message) {
1348
- err.code =
1349
- message.error_code;
1452
+ err.code = message.error_code;
1453
+ }
1454
+ // A server error frame before `Begin` fails the attempt; the code
1455
+ // decides whether a retry is worthwhile.
1456
+ if (!settled) {
1457
+ const attemptErr = err;
1458
+ attemptErr.retryable =
1459
+ err.code === undefined ? true : isRetryableCloseCode(err.code);
1460
+ failAttempt(attemptErr);
1461
+ return;
1350
1462
  }
1351
1463
  (_b = (_a = this.listeners).error) === null || _b === void 0 ? void 0 : _b.call(_a, err);
1352
1464
  return;
1353
1465
  }
1354
1466
  switch (message.type) {
1355
1467
  case "Begin": {
1356
- resolve(message);
1468
+ succeed(message);
1357
1469
  (_d = (_c = this.listeners).open) === null || _d === void 0 ? void 0 : _d.call(_c, message);
1358
1470
  break;
1359
1471
  }
@@ -1400,6 +1512,20 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
1400
1512
  };
1401
1513
  });
1402
1514
  }
1515
+ /** Tear down a half-open socket from a failed connection attempt. */
1516
+ discardPendingSocket() {
1517
+ if (!this.socket)
1518
+ return;
1519
+ try {
1520
+ if (this.socket.removeAllListeners)
1521
+ this.socket.removeAllListeners();
1522
+ this.socket.close();
1523
+ }
1524
+ catch (_a) {
1525
+ // Best-effort cleanup; a half-open socket may throw on close.
1526
+ }
1527
+ this.socket = undefined;
1528
+ }
1403
1529
  /**
1404
1530
  * Returns a WritableStream that pumps PCM chunks into `sendAudio`. Single-channel
1405
1531
  * only — in dual-channel mode use `sendAudio(pcm, { channel })` directly, since
@@ -1673,6 +1799,16 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
1673
1799
  };
1674
1800
  this.send(JSON.stringify(message));
1675
1801
  }
1802
+ /**
1803
+ * Reset the server's inactivity timer. Only needed when the session was
1804
+ * created with `inactivityTimeout` and no audio is being sent.
1805
+ */
1806
+ keepAlive() {
1807
+ const message = {
1808
+ type: "KeepAlive",
1809
+ };
1810
+ this.send(JSON.stringify(message));
1811
+ }
1676
1812
  send(data) {
1677
1813
  if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
1678
1814
  throw new Error("Socket is not open for communication");