@azure/ai-voicelive 1.0.0-alpha.20260105.1 → 1.0.0-alpha.20260107.1

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.
Files changed (109) hide show
  1. package/dist/browser/index.d.ts +1 -1
  2. package/dist/browser/index.js +1 -1
  3. package/dist/browser/index.js.map +1 -1
  4. package/dist/browser/models/index.d.ts +1 -1
  5. package/dist/browser/models/index.js +1 -1
  6. package/dist/browser/models/index.js.map +1 -1
  7. package/dist/browser/models/models.d.ts +357 -25
  8. package/dist/browser/models/models.js +361 -56
  9. package/dist/browser/models/models.js.map +1 -1
  10. package/dist/browser/protocol/messageParser.d.ts +1 -1
  11. package/dist/browser/protocol/messageParser.js +19 -76
  12. package/dist/browser/protocol/messageParser.js.map +1 -1
  13. package/dist/browser/voiceLiveSession.d.ts +2 -2
  14. package/dist/browser/voiceLiveSession.js +11 -10
  15. package/dist/browser/voiceLiveSession.js.map +1 -1
  16. package/dist/browser/websocket/websocket-browser.mjs.map +1 -0
  17. package/dist/browser/websocket/{websocketBrowser.d.ts → websocket.d.ts} +8 -2
  18. package/dist/{react-native/websocket/websocketBrowser.js → browser/websocket/websocket.js} +43 -3
  19. package/dist/browser/websocket/websocketFactory.d.ts +0 -11
  20. package/dist/browser/websocket/websocketFactory.js +2 -66
  21. package/dist/browser/websocket/websocketFactory.js.map +1 -1
  22. package/dist/browser/websocket/websocketLike.d.ts +3 -0
  23. package/dist/browser/websocket/websocketLike.js.map +1 -1
  24. package/dist/commonjs/index.d.ts +1 -1
  25. package/dist/commonjs/index.js +6 -1
  26. package/dist/commonjs/index.js.map +1 -1
  27. package/dist/commonjs/models/index.d.ts +1 -1
  28. package/dist/commonjs/models/index.js +6 -1
  29. package/dist/commonjs/models/index.js.map +1 -1
  30. package/dist/commonjs/models/models.d.ts +357 -25
  31. package/dist/commonjs/models/models.js +386 -59
  32. package/dist/commonjs/models/models.js.map +1 -1
  33. package/dist/commonjs/protocol/messageParser.d.ts +1 -1
  34. package/dist/commonjs/protocol/messageParser.js +18 -75
  35. package/dist/commonjs/protocol/messageParser.js.map +1 -1
  36. package/dist/commonjs/voiceLiveSession.d.ts +2 -2
  37. package/dist/commonjs/voiceLiveSession.js +20 -19
  38. package/dist/commonjs/voiceLiveSession.js.map +1 -1
  39. package/dist/{esm/websocket/websocketNode.d.ts → commonjs/websocket/websocket.d.ts} +2 -2
  40. package/dist/commonjs/websocket/{websocketNode.js → websocket.js} +31 -6
  41. package/dist/commonjs/websocket/websocket.js.map +1 -0
  42. package/dist/commonjs/websocket/websocketFactory.d.ts +0 -11
  43. package/dist/commonjs/websocket/websocketFactory.js +2 -68
  44. package/dist/commonjs/websocket/websocketFactory.js.map +1 -1
  45. package/dist/commonjs/websocket/websocketLike.d.ts +3 -0
  46. package/dist/commonjs/websocket/websocketLike.js.map +1 -1
  47. package/dist/esm/index.d.ts +1 -1
  48. package/dist/esm/index.js +1 -1
  49. package/dist/esm/index.js.map +1 -1
  50. package/dist/esm/models/index.d.ts +1 -1
  51. package/dist/esm/models/index.js +1 -1
  52. package/dist/esm/models/index.js.map +1 -1
  53. package/dist/esm/models/models.d.ts +357 -25
  54. package/dist/esm/models/models.js +361 -56
  55. package/dist/esm/models/models.js.map +1 -1
  56. package/dist/esm/protocol/messageParser.d.ts +1 -1
  57. package/dist/esm/protocol/messageParser.js +19 -76
  58. package/dist/esm/protocol/messageParser.js.map +1 -1
  59. package/dist/esm/voiceLiveSession.d.ts +2 -2
  60. package/dist/esm/voiceLiveSession.js +11 -10
  61. package/dist/esm/voiceLiveSession.js.map +1 -1
  62. package/dist/{react-native/websocket/websocketNode.d.ts → esm/websocket/websocket.d.ts} +2 -2
  63. package/dist/{browser/websocket/websocketNode.js → esm/websocket/websocket.js} +29 -4
  64. package/dist/esm/websocket/websocket.js.map +1 -0
  65. package/dist/esm/websocket/websocketFactory.d.ts +0 -11
  66. package/dist/esm/websocket/websocketFactory.js +2 -66
  67. package/dist/esm/websocket/websocketFactory.js.map +1 -1
  68. package/dist/esm/websocket/websocketLike.d.ts +3 -0
  69. package/dist/esm/websocket/websocketLike.js.map +1 -1
  70. package/dist/react-native/index.d.ts +1 -1
  71. package/dist/react-native/index.js +1 -1
  72. package/dist/react-native/index.js.map +1 -1
  73. package/dist/react-native/models/index.d.ts +1 -1
  74. package/dist/react-native/models/index.js +1 -1
  75. package/dist/react-native/models/index.js.map +1 -1
  76. package/dist/react-native/models/models.d.ts +357 -25
  77. package/dist/react-native/models/models.js +361 -56
  78. package/dist/react-native/models/models.js.map +1 -1
  79. package/dist/react-native/protocol/messageParser.d.ts +1 -1
  80. package/dist/react-native/protocol/messageParser.js +19 -76
  81. package/dist/react-native/protocol/messageParser.js.map +1 -1
  82. package/dist/react-native/voiceLiveSession.d.ts +2 -2
  83. package/dist/react-native/voiceLiveSession.js +11 -10
  84. package/dist/react-native/voiceLiveSession.js.map +1 -1
  85. package/dist/{commonjs/websocket/websocketNode.d.ts → react-native/websocket/websocket.d.ts} +2 -2
  86. package/dist/{esm/websocket/websocketNode.js → react-native/websocket/websocket.js} +29 -4
  87. package/dist/react-native/websocket/websocket.js.map +1 -0
  88. package/dist/react-native/websocket/websocketFactory.d.ts +0 -11
  89. package/dist/react-native/websocket/websocketFactory.js +2 -66
  90. package/dist/react-native/websocket/websocketFactory.js.map +1 -1
  91. package/dist/react-native/websocket/websocketLike.d.ts +3 -0
  92. package/dist/react-native/websocket/websocketLike.js.map +1 -1
  93. package/package.json +16 -3
  94. package/dist/browser/websocket/websocketBrowser.js +0 -175
  95. package/dist/browser/websocket/websocketBrowser.js.map +0 -1
  96. package/dist/browser/websocket/websocketNode.d.ts +0 -26
  97. package/dist/browser/websocket/websocketNode.js.map +0 -1
  98. package/dist/commonjs/websocket/websocketBrowser.d.ts +0 -26
  99. package/dist/commonjs/websocket/websocketBrowser.js +0 -179
  100. package/dist/commonjs/websocket/websocketBrowser.js.map +0 -1
  101. package/dist/commonjs/websocket/websocketNode.js.map +0 -1
  102. package/dist/esm/websocket/websocketBrowser.d.ts +0 -26
  103. package/dist/esm/websocket/websocketBrowser.js +0 -175
  104. package/dist/esm/websocket/websocketBrowser.js.map +0 -1
  105. package/dist/esm/websocket/websocketNode.js.map +0 -1
  106. package/dist/react-native/websocket/websocketBrowser.d.ts +0 -26
  107. package/dist/react-native/websocket/websocketBrowser.js.map +0 -1
  108. package/dist/react-native/websocket/websocketNode.js +0 -180
  109. package/dist/react-native/websocket/websocketNode.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import type { ServerEventUnion, ClientEventUnion } from "../models/index.js";
1
+ import { type ServerEventUnion, type ClientEventUnion } from "../models/index.js";
2
2
  /**
3
3
  * Parsed message containing event data and metadata
4
4
  */
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
- import { clientEventUnionSerializer, serverEventUnionDeserializer } from "../models/models.js";
3
+ import { logger } from "../logger.js";
4
+ import { KnownServerEventType, } from "../models/index.js";
5
+ import { clientEventUnionSerializer, KnownClientEventType, serverEventUnionDeserializer, } from "../models/models.js";
4
6
  /**
5
7
  * Parses and serializes Voice Live protocol messages
6
8
  */
@@ -37,8 +39,20 @@ export class VoiceLiveMessageParser {
37
39
  return null; // Unknown message format
38
40
  }
39
41
  catch (error) {
40
- // Invalid JSON or parsing error
41
- return null;
42
+ logger.error("Failed to parse incoming message:", error);
43
+ return {
44
+ type: "server",
45
+ event: {
46
+ type: KnownServerEventType.Error,
47
+ error: {
48
+ type: "error",
49
+ code: "MessageParsingError",
50
+ message: "Failed to parse incoming message data. " +
51
+ (error instanceof Error ? error.message : String(error)),
52
+ },
53
+ },
54
+ raw: data,
55
+ };
42
56
  }
43
57
  }
44
58
  /**
@@ -66,85 +80,14 @@ export class VoiceLiveMessageParser {
66
80
  */
67
81
  _isValidServerEventType(type) {
68
82
  // Based on the comprehensive analysis in EXISTING_TYPES_ANALYSIS.md
69
- const validServerTypes = [
70
- // Error handling
71
- "error",
72
- // Session management
73
- "session.created",
74
- "session.updated",
75
- "session.avatar.connecting",
76
- // Audio buffer events
77
- "input_audio_buffer.committed",
78
- "input_audio_buffer.cleared",
79
- "input_audio_buffer.speech_started",
80
- "input_audio_buffer.speech_stopped",
81
- // Conversation item events
82
- "conversation.item.created",
83
- "conversation.item.truncated",
84
- "conversation.item.deleted",
85
- "conversation.item.retrieved",
86
- "conversation.item.input_audio_transcription.completed",
87
- "conversation.item.input_audio_transcription.failed",
88
- "conversation.item.input_audio_transcription.delta",
89
- // Response lifecycle events
90
- "response.created",
91
- "response.done",
92
- "response.output_item.added",
93
- "response.output_item.done",
94
- // Content streaming events
95
- "response.content_part.added",
96
- "response.content_part.done",
97
- "response.text.delta",
98
- "response.text.done",
99
- // Audio streaming events
100
- "response.audio_transcript.delta",
101
- "response.audio_transcript.done",
102
- "response.audio.delta",
103
- "response.audio.done",
104
- // Animation events
105
- "response.animation.blendshape.delta",
106
- "response.animation.blendshape.done",
107
- "response.animation.viseme.delta",
108
- "response.animation.viseme.done",
109
- // Timestamp events
110
- "response.audio.timestamp.delta",
111
- "response.audio.timestamp.done",
112
- // Function call events
113
- "response.function_call_arguments.delta",
114
- "response.function_call_arguments.done",
115
- ];
116
- return validServerTypes.includes(type);
83
+ return Object.values(KnownServerEventType).includes(type);
117
84
  }
118
85
  /**
119
86
  * Validates if the type string represents a valid client event type
120
87
  */
121
88
  _isValidClientEventType(type) {
122
89
  // Based on the comprehensive analysis in EXISTING_TYPES_ANALYSIS.md
123
- const validClientTypes = [
124
- // Session management
125
- "session.update",
126
- "session.avatar.connect",
127
- // Turn-based audio
128
- "input_audio_turn.start",
129
- "input_audio_turn.append",
130
- "input_audio_turn.end",
131
- "input_audio_turn.cancel",
132
- // Buffer-based audio
133
- "input_audio_buffer.append",
134
- "input_audio_buffer.commit",
135
- "input_audio_buffer.clear",
136
- // Audio control
137
- "input_audio.clear",
138
- // Conversation management
139
- "conversation.item.create",
140
- "conversation.item.truncate",
141
- "conversation.item.delete",
142
- "conversation.item.retrieve",
143
- // Response management
144
- "response.create",
145
- "response.cancel",
146
- ];
147
- return validClientTypes.includes(type);
90
+ return Object.values(KnownClientEventType).includes(type);
148
91
  }
149
92
  }
150
93
  //# sourceMappingURL=messageParser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"messageParser.js","sourceRoot":"","sources":["../../../src/protocol/messageParser.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { ServerEventUnion, ClientEventUnion } from \"../models/index.js\";\nimport { clientEventUnionSerializer, serverEventUnionDeserializer } from \"../models/models.js\";\n\n/**\n * Parsed message containing event data and metadata\n */\nexport interface ParsedMessage {\n /** Type of event: client or server */\n type: \"client\" | \"server\";\n /** Parsed event data */\n event: ClientEventUnion | ServerEventUnion;\n /** Original raw data for debugging */\n raw: string | ArrayBuffer;\n}\n\n/**\n * Parses and serializes Voice Live protocol messages\n */\nexport class VoiceLiveMessageParser {\n /**\n * Parses incoming WebSocket message data into typed events\n */\n parseIncomingMessage(data: string | ArrayBuffer): ParsedMessage | null {\n try {\n let messageText: string;\n\n if (data instanceof ArrayBuffer) {\n messageText = new TextDecoder().decode(data);\n } else {\n messageText = data;\n }\n\n const parsedData = JSON.parse(messageText);\n\n // Validate and type the message based on the 'type' field\n if (this._isServerEvent(parsedData)) {\n return {\n type: \"server\",\n event: serverEventUnionDeserializer(parsedData),\n raw: data,\n };\n }\n\n // Handle unexpected client events (shouldn't normally receive these from server)\n if (this._isClientEvent(parsedData)) {\n return {\n type: \"client\",\n event: parsedData as ClientEventUnion,\n raw: data,\n };\n }\n\n return null; // Unknown message format\n } catch (error) {\n // Invalid JSON or parsing error\n return null;\n }\n }\n\n /**\n * Serializes outgoing client events for WebSocket transmission\n */\n serializeOutgoingMessage(event: ClientEventUnion): string {\n return JSON.stringify(clientEventUnionSerializer(event));\n }\n\n /**\n * Checks if the parsed data represents a server event\n */\n private _isServerEvent(data: any): boolean {\n // Check if data matches ServerEventUnion structure\n return data && typeof data.type === \"string\" && this._isValidServerEventType(data.type);\n }\n\n /**\n * Checks if the parsed data represents a client event\n */\n private _isClientEvent(data: any): boolean {\n // Check if data matches ClientEventUnion structure\n return data && typeof data.type === \"string\" && this._isValidClientEventType(data.type);\n }\n\n /**\n * Validates if the type string represents a valid server event type\n */\n private _isValidServerEventType(type: string): boolean {\n // Based on the comprehensive analysis in EXISTING_TYPES_ANALYSIS.md\n const validServerTypes = [\n // Error handling\n \"error\",\n\n // Session management\n \"session.created\",\n \"session.updated\",\n \"session.avatar.connecting\",\n\n // Audio buffer events\n \"input_audio_buffer.committed\",\n \"input_audio_buffer.cleared\",\n \"input_audio_buffer.speech_started\",\n \"input_audio_buffer.speech_stopped\",\n\n // Conversation item events\n \"conversation.item.created\",\n \"conversation.item.truncated\",\n \"conversation.item.deleted\",\n \"conversation.item.retrieved\",\n \"conversation.item.input_audio_transcription.completed\",\n \"conversation.item.input_audio_transcription.failed\",\n \"conversation.item.input_audio_transcription.delta\",\n\n // Response lifecycle events\n \"response.created\",\n \"response.done\",\n \"response.output_item.added\",\n \"response.output_item.done\",\n\n // Content streaming events\n \"response.content_part.added\",\n \"response.content_part.done\",\n \"response.text.delta\",\n \"response.text.done\",\n\n // Audio streaming events\n \"response.audio_transcript.delta\",\n \"response.audio_transcript.done\",\n \"response.audio.delta\",\n \"response.audio.done\",\n\n // Animation events\n \"response.animation.blendshape.delta\",\n \"response.animation.blendshape.done\",\n \"response.animation.viseme.delta\",\n \"response.animation.viseme.done\",\n\n // Timestamp events\n \"response.audio.timestamp.delta\",\n \"response.audio.timestamp.done\",\n\n // Function call events\n \"response.function_call_arguments.delta\",\n \"response.function_call_arguments.done\",\n ];\n\n return validServerTypes.includes(type);\n }\n\n /**\n * Validates if the type string represents a valid client event type\n */\n private _isValidClientEventType(type: string): boolean {\n // Based on the comprehensive analysis in EXISTING_TYPES_ANALYSIS.md\n const validClientTypes = [\n // Session management\n \"session.update\",\n \"session.avatar.connect\",\n\n // Turn-based audio\n \"input_audio_turn.start\",\n \"input_audio_turn.append\",\n \"input_audio_turn.end\",\n \"input_audio_turn.cancel\",\n\n // Buffer-based audio\n \"input_audio_buffer.append\",\n \"input_audio_buffer.commit\",\n \"input_audio_buffer.clear\",\n\n // Audio control\n \"input_audio.clear\",\n\n // Conversation management\n \"conversation.item.create\",\n \"conversation.item.truncate\",\n \"conversation.item.delete\",\n \"conversation.item.retrieve\",\n\n // Response management\n \"response.create\",\n \"response.cancel\",\n ];\n\n return validClientTypes.includes(type);\n }\n}\n"]}
1
+ {"version":3,"file":"messageParser.js","sourceRoot":"","sources":["../../../src/protocol/messageParser.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAGL,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EAGpB,4BAA4B,GAC7B,MAAM,qBAAqB,CAAC;AAc7B;;GAEG;AACH,MAAM,OAAO,sBAAsB;IACjC;;OAEG;IACH,oBAAoB,CAAC,IAA0B;QAC7C,IAAI,CAAC;YACH,IAAI,WAAmB,CAAC;YAExB,IAAI,IAAI,YAAY,WAAW,EAAE,CAAC;gBAChC,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC;iBAAM,CAAC;gBACN,WAAW,GAAG,IAAI,CAAC;YACrB,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAE3C,0DAA0D;YAC1D,IAAI,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpC,OAAO;oBACL,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,4BAA4B,CAAC,UAAU,CAAC;oBAC/C,GAAG,EAAE,IAAI;iBACV,CAAC;YACJ,CAAC;YAED,iFAAiF;YACjF,IAAI,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpC,OAAO;oBACL,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,UAA8B;oBACrC,GAAG,EAAE,IAAI;iBACV,CAAC;YACJ,CAAC;YAED,OAAO,IAAI,CAAC,CAAC,yBAAyB;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;YACzD,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE;oBACL,IAAI,EAAE,oBAAoB,CAAC,KAAK;oBAChC,KAAK,EAAE;wBACL,IAAI,EAAE,OAAO;wBACb,IAAI,EAAE,qBAAqB;wBAC3B,OAAO,EACL,yCAAyC;4BACzC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;qBAChC;iBACT;gBACrB,GAAG,EAAE,IAAI;aACV,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,wBAAwB,CAAC,KAAuB;QAC9C,OAAO,IAAI,CAAC,SAAS,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,IAAS;QAC9B,mDAAmD;QACnD,OAAO,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1F,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,IAAS;QAC9B,mDAAmD;QACnD,OAAO,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1F,CAAC;IAED;;OAEG;IACK,uBAAuB,CAAC,IAAY;QAC1C,oEAAoE;QACpE,OAAO,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC,IAA4B,CAAC,CAAC;IACpF,CAAC;IAED;;OAEG;IACK,uBAAuB,CAAC,IAAY;QAC1C,oEAAoE;QACpE,OAAO,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC,IAA4B,CAAC,CAAC;IACpF,CAAC;CACF","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { logger } from \"../logger.js\";\nimport {\n type ServerEventUnion,\n type ClientEventUnion,\n KnownServerEventType,\n} from \"../models/index.js\";\nimport {\n clientEventUnionSerializer,\n KnownClientEventType,\n type ServerEventError,\n type ServerEventErrorDetails,\n serverEventUnionDeserializer,\n} from \"../models/models.js\";\n\n/**\n * Parsed message containing event data and metadata\n */\nexport interface ParsedMessage {\n /** Type of event: client or server */\n type: \"client\" | \"server\";\n /** Parsed event data */\n event: ClientEventUnion | ServerEventUnion;\n /** Original raw data for debugging */\n raw: string | ArrayBuffer;\n}\n\n/**\n * Parses and serializes Voice Live protocol messages\n */\nexport class VoiceLiveMessageParser {\n /**\n * Parses incoming WebSocket message data into typed events\n */\n parseIncomingMessage(data: string | ArrayBuffer): ParsedMessage | null {\n try {\n let messageText: string;\n\n if (data instanceof ArrayBuffer) {\n messageText = new TextDecoder().decode(data);\n } else {\n messageText = data;\n }\n\n const parsedData = JSON.parse(messageText);\n\n // Validate and type the message based on the 'type' field\n if (this._isServerEvent(parsedData)) {\n return {\n type: \"server\",\n event: serverEventUnionDeserializer(parsedData),\n raw: data,\n };\n }\n\n // Handle unexpected client events (shouldn't normally receive these from server)\n if (this._isClientEvent(parsedData)) {\n return {\n type: \"client\",\n event: parsedData as ClientEventUnion,\n raw: data,\n };\n }\n\n return null; // Unknown message format\n } catch (error) {\n logger.error(\"Failed to parse incoming message:\", error);\n return {\n type: \"server\",\n event: {\n type: KnownServerEventType.Error,\n error: {\n type: \"error\",\n code: \"MessageParsingError\",\n message:\n \"Failed to parse incoming message data. \" +\n (error instanceof Error ? error.message : String(error)),\n } as ServerEventErrorDetails,\n } as ServerEventError,\n raw: data,\n };\n }\n }\n\n /**\n * Serializes outgoing client events for WebSocket transmission\n */\n serializeOutgoingMessage(event: ClientEventUnion): string {\n return JSON.stringify(clientEventUnionSerializer(event));\n }\n\n /**\n * Checks if the parsed data represents a server event\n */\n private _isServerEvent(data: any): boolean {\n // Check if data matches ServerEventUnion structure\n return data && typeof data.type === \"string\" && this._isValidServerEventType(data.type);\n }\n\n /**\n * Checks if the parsed data represents a client event\n */\n private _isClientEvent(data: any): boolean {\n // Check if data matches ClientEventUnion structure\n return data && typeof data.type === \"string\" && this._isValidClientEventType(data.type);\n }\n\n /**\n * Validates if the type string represents a valid server event type\n */\n private _isValidServerEventType(type: string): boolean {\n // Based on the comprehensive analysis in EXISTING_TYPES_ANALYSIS.md\n return Object.values(KnownServerEventType).includes(type as KnownServerEventType);\n }\n\n /**\n * Validates if the type string represents a valid client event type\n */\n private _isValidClientEventType(type: string): boolean {\n // Based on the comprehensive analysis in EXISTING_TYPES_ANALYSIS.md\n return Object.values(KnownClientEventType).includes(type as KnownClientEventType);\n }\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { KeyCredential, TokenCredential } from "@azure/core-auth";
2
2
  import type { AbortSignalLike } from "@azure/abort-controller";
3
- import type { RequestSession, ClientEventUnion, ConversationRequestItem } from "./models/index.js";
3
+ import { type RequestSession, type ClientEventUnion, type ConversationRequestItem } from "./models/index.js";
4
4
  import { ConnectionState } from "./websocket/connectionManager.js";
5
5
  import type { VoiceLiveSessionHandlers, VoiceLiveSubscription } from "./handlers/sessionHandlers.js";
6
6
  export interface VoiceLiveSessionOptions {
@@ -45,6 +45,7 @@ export declare class VoiceLiveSession {
45
45
  private readonly _options;
46
46
  private readonly _messageParser;
47
47
  private readonly _model;
48
+ private readonly _apiVersion;
48
49
  private _connectionManager?;
49
50
  private _sessionId?;
50
51
  private _activeTurnId?;
@@ -70,7 +71,6 @@ export declare class VoiceLiveSession {
70
71
  disconnect(): Promise<void>;
71
72
  /**
72
73
  * Subscribe to VoiceLive session events using strongly-typed handlers.
73
- * This follows the Azure SDK pattern used by EventHub, Service Bus, etc.
74
74
  *
75
75
  * @param handlers - Handler functions for different types of events
76
76
  * @returns A subscription object that can be used to stop receiving events
@@ -1,5 +1,6 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
+ import { KnownClientEventType, KnownServerEventType, } from "./models/index.js";
3
4
  import { ConnectionManager, ConnectionState } from "./websocket/connectionManager.js";
4
5
  import { VoiceLiveWebSocketFactory } from "./websocket/websocketFactory.js";
5
6
  import { VoiceLiveMessageParser } from "./protocol/messageParser.js";
@@ -19,6 +20,7 @@ export class VoiceLiveSession {
19
20
  _options;
20
21
  _messageParser;
21
22
  _model;
23
+ _apiVersion;
22
24
  _connectionManager;
23
25
  _sessionId;
24
26
  _activeTurnId;
@@ -40,6 +42,7 @@ export class VoiceLiveSession {
40
42
  this._options = this._buildDefaultOptions(options);
41
43
  this._messageParser = new VoiceLiveMessageParser();
42
44
  this._model = model;
45
+ this._apiVersion = apiVersion;
43
46
  // Initialize handler-based subscription management
44
47
  this._subscriptionManager = new SubscriptionManager();
45
48
  logger.info("VoiceLiveSession created", {
@@ -64,8 +67,7 @@ export class VoiceLiveSession {
64
67
  model: this._model,
65
68
  });
66
69
  // Get WebSocket URL with authentication and model
67
- const wsUrl = await this._credentialHandler.getWebSocketUrl(this._endpoint, "2025-10-01", // TODO: make this configurable through constructor
68
- this._model);
70
+ const wsUrl = await this._credentialHandler.getWebSocketUrl(this._endpoint, this._apiVersion, this._model);
69
71
  const authHeaders = await this._credentialHandler.getAuthHeaders();
70
72
  // Create connection manager
71
73
  const websocketFactory = new VoiceLiveWebSocketFactory();
@@ -117,7 +119,6 @@ export class VoiceLiveSession {
117
119
  }
118
120
  /**
119
121
  * Subscribe to VoiceLive session events using strongly-typed handlers.
120
- * This follows the Azure SDK pattern used by EventHub, Service Bus, etc.
121
122
  *
122
123
  * @param handlers - Handler functions for different types of events
123
124
  * @returns A subscription object that can be used to stop receiving events
@@ -140,7 +141,7 @@ export class VoiceLiveSession {
140
141
  async updateSession(session, options = {}) {
141
142
  this._ensureConnected();
142
143
  const updateEvent = {
143
- type: "session.update",
144
+ type: KnownClientEventType.SessionUpdate,
144
145
  session: session,
145
146
  eventId: this._generateEventId(),
146
147
  };
@@ -155,7 +156,7 @@ export class VoiceLiveSession {
155
156
  if (options.turnId) {
156
157
  // Turn-based audio
157
158
  const appendEvent = {
158
- type: "input_audio.turn.append",
159
+ type: KnownClientEventType.InputAudioTurnAppend,
159
160
  audio: audioBase64,
160
161
  turnId: options.turnId,
161
162
  eventId: this._generateEventId(),
@@ -165,7 +166,7 @@ export class VoiceLiveSession {
165
166
  else {
166
167
  // Buffer-based audio (VAD mode)
167
168
  const bufferEvent = {
168
- type: "input_audio_buffer.append",
169
+ type: KnownClientEventType.InputAudioBufferAppend,
169
170
  audio: audioBase64,
170
171
  eventId: this._generateEventId(),
171
172
  };
@@ -180,7 +181,7 @@ export class VoiceLiveSession {
180
181
  const turnId = options.turnId || this._generateTurnId();
181
182
  this._activeTurnId = turnId;
182
183
  const startEvent = {
183
- type: "input_audio.turn.start",
184
+ type: KnownClientEventType.InputAudioTurnStart,
184
185
  turnId: turnId,
185
186
  eventId: this._generateEventId(),
186
187
  };
@@ -197,7 +198,7 @@ export class VoiceLiveSession {
197
198
  throw new VoiceLiveConnectionError("No active audio turn to end", "INVALID_STATE");
198
199
  }
199
200
  const endEvent = {
200
- type: "input_audio.turn.end",
201
+ type: KnownClientEventType.InputAudioTurnEnd,
201
202
  turnId: targetTurnId,
202
203
  eventId: this._generateEventId(),
203
204
  };
@@ -212,7 +213,7 @@ export class VoiceLiveSession {
212
213
  async addConversationItem(item, options = {}) {
213
214
  this._ensureConnected();
214
215
  const createEvent = {
215
- type: "conversation.item.create",
216
+ type: KnownClientEventType.ConversationItemCreate,
216
217
  item: item,
217
218
  eventId: this._generateEventId(),
218
219
  };
@@ -348,7 +349,7 @@ export class VoiceLiveSession {
348
349
  }
349
350
  _handleServerEvent(event) {
350
351
  // Extract session information from events
351
- if (event.type === "session.created" && event.session?.id) {
352
+ if (event.type === KnownServerEventType.SessionCreated && event.session?.id) {
352
353
  this._sessionId = event.session.id;
353
354
  logger.info("Session created", { sessionId: this._sessionId });
354
355
  }
@@ -1 +1 @@
1
- {"version":3,"file":"voiceLiveSession.js","sourceRoot":"","sources":["../../src/voiceLiveSession.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAgBlC,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACtF,OAAO,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAUrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAoCxE;;;;;GAKG;AACH,MAAM,OAAO,gBAAgB;IACV,SAAS,CAAS;IAClB,kBAAkB,CAAoB;IACtC,QAAQ,CAAoC;IAC5C,cAAc,CAAyB;IACvC,MAAM,CAAS;IACxB,kBAAkB,CAAqB;IACvC,UAAU,CAAU;IACpB,aAAa,CAAU;IACvB,SAAS,GAAG,KAAK,CAAC;IAE1B,wCAAwC;IACvB,oBAAoB,CAAsB;IAE3D;;;;;;;;OAQG;IACH,YACE,QAAgB,EAChB,UAA2C,EAC3C,UAAkB,EAClB,KAAa,EACb,UAAmC,EAAE;QAErC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,CAAC,kBAAkB,GAAG,IAAI,iBAAiB,CAAC,UAAU,CAAC,CAAC;QAC5D,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,cAAc,GAAG,IAAI,sBAAsB,EAAE,CAAC;QACnD,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QAEpB,mDAAmD;QACnD,IAAI,CAAC,oBAAoB,GAAG,IAAI,mBAAmB,EAAE,CAAC;QAEtD,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE;YACtC,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,KAAK,EAAE,IAAI,CAAC,MAAM;YAClB,UAAU,EAAE,UAAU;YACtB,kBAAkB,EAAE,IAAI,CAAC,QAAQ,CAAC,kBAAkB;SACrD,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,UAA0B,EAAE;QACxC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,kCAAkC,EAAE;gBAC9C,QAAQ,EAAE,IAAI,CAAC,SAAS;gBACxB,KAAK,EAAE,IAAI,CAAC,MAAM;aACnB,CAAC,CAAC;YAEH,kDAAkD;YAClD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,eAAe,CACzD,IAAI,CAAC,SAAS,EACd,YAAY,EAAE,mDAAmD;YACjE,IAAI,CAAC,MAAM,CACZ,CAAC;YACF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,EAAE,CAAC;YAEnE,4BAA4B;YAC5B,MAAM,gBAAgB,GAAG,IAAI,yBAAyB,EAAE,CAAC;YACzD,IAAI,CAAC,kBAAkB,GAAG,IAAI,iBAAiB,CAC7C,GAAG,EAAE,CACH,gBAAgB,CAAC,MAAM,CAAC;gBACtB,OAAO,EAAE,EAAE,GAAG,WAAW,EAAE;gBAC3B,qBAAqB,EAAE,IAAI,CAAC,QAAQ,CAAC,qBAAqB;gBAC1D,WAAW,EAAE,IAAI;aAClB,CAAC,EACJ;gBACE,QAAQ,EAAE,KAAK;gBACf,iBAAiB,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,qBAAqB;aAC9E,CACF,CAAC;YAEF,kCAAkC;YAClC,IAAI,CAAC,6BAA6B,EAAE,CAAC;YAErC,qCAAqC;YACrC,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAE3D,MAAM,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,yCAAyC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAEnE,2BAA2B;YAC3B,IAAI,KAAK,YAAY,wBAAwB,EAAE,CAAC;gBAC9C,MAAM,KAAK,CAAC;YACd,CAAC;iBAAM,CAAC;gBACN,MAAM,uBAAuB,CAAC,KAAK,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC7B,OAAO;QACT,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;QAErD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE,CAAC;QAC7C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACrD,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;YACpC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;YAC5B,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,SAAS,CAAC,QAAkC;QAC1C,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1B,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAChE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS,CAAC,KAAuB,EAAE,UAA4B,EAAE;QACrE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,OAAuB,EAAE,UAA4B,EAAE;QACzE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,MAAM,WAAW,GAA6B;YAC5C,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,OAAO;YAChB,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE;SACjC,CAAC;QAEF,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS,CACb,SAAmC,EACnC,UAA8B,EAAE;QAEhC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,MAAM,WAAW,GAAG,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAEzD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,mBAAmB;YACnB,MAAM,WAAW,GAAoC;gBACnD,IAAI,EAAE,yBAAyB;gBAC/B,KAAK,EAAE,WAAW;gBAClB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE;aACjC,CAAC;YACF,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,gCAAgC;YAChC,MAAM,WAAW,GAAsC;gBACrD,IAAI,EAAE,2BAA2B;gBACjC,KAAK,EAAE,WAAW;gBAClB,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE;aACjC,CAAC;YACF,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,UAAuB,EAAE;QAC5C,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACxD,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC;QAE5B,MAAM,UAAU,GAAmC;YACjD,IAAI,EAAE,wBAAwB;YAC9B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE;SACjC,CAAC;QAEF,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC3C,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,MAAe,EAAE,UAA4B,EAAE;QAChE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,MAAM,YAAY,GAAG,MAAM,IAAI,IAAI,CAAC,aAAa,CAAC;QAClD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,wBAAwB,CAAC,6BAA6B,EAAE,eAAe,CAAC,CAAC;QACrF,CAAC;QAED,MAAM,QAAQ,GAAiC;YAC7C,IAAI,EAAE,sBAAsB;YAC5B,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE;SACjC,CAAC;QAEF,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEzC,IAAI,YAAY,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC;YACxC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;QACjC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,mBAAmB,CACvB,IAA6B,EAC7B,UAA4B,EAAE;QAE9B,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,MAAM,WAAW,GAAsC;YACrD,IAAI,EAAE,0BAA0B;YAChC,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE;SACjC,CAAC;QAEF,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,kBAAkB,EAAE,WAAW,IAAI,KAAK,CAAC;IACvD,CAAC;IAED;;OAEG;IACH,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,kBAAkB,EAAE,KAAK,IAAI,eAAe,CAAC,YAAY,CAAC;IACxE,CAAC;IAED;;OAEG;IACH,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAE1C,IAAI,CAAC;YACH,gCAAgC;YAChC,MAAM,IAAI,CAAC,oBAAoB,CAAC,QAAQ,EAAE,CAAC;YAE3C,kBAAkB;YAClB,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,kBAAkB;IAEV,oBAAoB,CAC1B,OAAgC;QAEhC,OAAO;YACL,qBAAqB,EAAE,OAAO,CAAC,qBAAqB,IAAI,KAAK;YAC7D,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,IAAI,KAAK;SACxD,CAAC;IACJ,CAAC;IAEO,kBAAkB,CAAC,QAAgB;QACzC,sCAAsC;QACtC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YACxE,QAAQ,GAAG,WAAW,QAAQ,EAAE,CAAC;QACnC,CAAC;QAED,wBAAwB;QACxB,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrC,CAAC;IAEO,6BAA6B;QACnC,IAAI,CAAC,IAAI,CAAC,kBAAkB;YAAE,OAAO;QAErC,IAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAAC;YAC1C,aAAa,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE;gBACtC,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;gBAElE,kEAAkE;gBAClE,IAAI,KAAK,KAAK,eAAe,CAAC,SAAS,IAAI,aAAa,KAAK,eAAe,CAAC,UAAU,EAAE,CAAC;oBACxF,IAAI,CAAC,sBAAsB,CAAC,WAAW,EAAE;wBACvC,YAAY,EAAE,QAAQ,IAAI,CAAC,GAAG,EAAE,EAAE;wBAClC,SAAS,EAAE,IAAI,IAAI,EAAE;qBACtB,CAAC,CAAC;gBACL,CAAC;qBAAM,IACL,KAAK,KAAK,eAAe,CAAC,YAAY;oBACtC,aAAa,KAAK,eAAe,CAAC,SAAS,EAC3C,CAAC;oBACD,IAAI,CAAC,sBAAsB,CAAC,cAAc,EAAE;wBAC1C,IAAI,EAAE,IAAI,EAAE,mBAAmB;wBAC/B,MAAM,EAAE,gCAAgC;wBACxC,QAAQ,EAAE,KAAK;wBACf,SAAS,EAAE,IAAI,IAAI,EAAE;qBACtB,CAAC,CAAC;oBACH,IAAI,CAAC,kBAAkB,CACrB,kEAAkE,CACnE,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;gBAClB,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACjB,MAAM,CAAC,KAAK,CAAC,4CAA4C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBACtE,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE;oBACnC,KAAK,EAAE,KAAK;oBACZ,OAAO,EAAE,4BAA4B;oBACrC,WAAW,EAAE,KAAK;oBAClB,SAAS,EAAE,IAAI,IAAI,EAAE;iBACtB,CAAC,CAAC;gBACH,IAAI,CAAC,kBAAkB,CAAC,qBAAqB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAChE,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAEO,kBAAkB,CAAC,MAAc;QACvC,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAE3B,MAAM,CAAC,KAAK,CAAC,oCAAoC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAE/D,0CAA0C;QAC1C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QAEtB,8BAA8B;QAC9B,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;QACpC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;IACjC,CAAC;IAEO,sBAAsB,CAAC,IAA0B;QACvD,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE;gBAC9B,IAAI,EAAE,OAAO,IAAI;gBACjB,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU;aAC/D,CAAC,CAAC;YAEH,gCAAgC;YAChC,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAC9D,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACvC,uBAAuB;gBACvB,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAEO,kBAAkB,CAAC,KAAU;QACnC,0CAA0C;QAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,CAAC;YAC1D,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,qCAAqC;QACrC,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAEO,sBAAsB,CAC5B,SAAiD,EACjD,IAAiE;QAEjE,MAAM,OAAO,GAAsB;YACjC,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,KAAK,EAAE,IAAI,CAAC,MAAM;SACnB,CAAC;QAEF,kDAAkD;QAClD,IAAI,CAAC,oBAAoB,CAAC,sBAAsB,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACzF,MAAM,CAAC,KAAK,CAAC,+CAA+C,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACtF,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,kBAAkB,CAAC,KAAuB;QAChD,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,kDAAkD;YAClD,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAmB;YAC9B,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,KAAK,EAAE,IAAI,CAAC,MAAM;YAClB,cAAc,EAAE,SAAS,EAAE,wCAAwC;SACpE,CAAC;QAEF,kDAAkD;QAClD,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAC3E,MAAM,CAAC,KAAK,CAAC,2CAA2C,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9F,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,KAAuB,EAAE,OAAyB;QACzE,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,WAAW,EAAE,CAAC;YAC1C,MAAM,IAAI,wBAAwB,CAAC,qCAAqC,EAAE,eAAe,CAAC,CAAC;QAC7F,CAAC;QAED,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;YAEpE,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAG,KAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACnF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,wBAAwB,EAAE,CAAC;gBAC9C,MAAM,KAAK,CAAC;YACd,CAAC;YAED,MAAM,uBAAuB,CAAC,KAAK,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAEO,gBAAgB;QACtB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,wBAAwB,CAChC,yCAAyC,EACzC,eAAe,CAChB,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,kBAAkB;QACxB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,MAAM,IAAI,wBAAwB,CAChC,mFAAmF,EACnF,cAAc,CACf,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,gBAAgB;QACtB,OAAO,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAC5E,CAAC;IAEO,eAAe;QACrB,OAAO,QAAQ,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAC7E,CAAC;IAEO,oBAAoB,CAAC,MAAgC;QAC3D,MAAM,KAAK,GAAG,MAAM,YAAY,WAAW,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAC9E,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;CACF","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { KeyCredential, TokenCredential } from \"@azure/core-auth\";\nimport type { AbortSignalLike } from \"@azure/abort-controller\";\nimport type {\n RequestSession,\n ClientEventSessionUpdate,\n ClientEventUnion,\n ClientEventInputAudioBufferAppend,\n ClientEventInputAudioTurnStart,\n ClientEventInputAudioTurnAppend,\n ClientEventInputAudioTurnEnd,\n ConversationRequestItem,\n ClientEventConversationItemCreate,\n ServerEventUnion,\n} from \"./models/index.js\";\nimport { ConnectionManager, ConnectionState } from \"./websocket/connectionManager.js\";\nimport { VoiceLiveWebSocketFactory } from \"./websocket/websocketFactory.js\";\nimport { VoiceLiveMessageParser } from \"./protocol/messageParser.js\";\nimport { CredentialHandler } from \"./auth/credentialHandler.js\";\nimport { VoiceLiveConnectionError, classifyConnectionError } from \"./errors/index.js\";\nimport { logger } from \"./logger.js\";\nimport type {\n VoiceLiveSessionHandlers,\n VoiceLiveSubscription,\n ConnectionContext,\n SessionContext,\n ConnectedEventArgs,\n DisconnectedEventArgs,\n ErrorEventArgs,\n} from \"./handlers/sessionHandlers.js\";\nimport { SubscriptionManager } from \"./handlers/subscriptionManager.js\";\n\nexport interface VoiceLiveSessionOptions {\n /** Connection timeout in milliseconds */\n connectionTimeoutInMs?: number;\n /** Enable debug logging for development */\n enableDebugLogging?: boolean;\n}\n\nexport interface CreateSessionOptions extends VoiceLiveSessionOptions {}\n\nexport interface StartSessionOptions extends VoiceLiveSessionOptions {}\nexport interface ConnectOptions {\n /** Abort signal to cancel connection attempt */\n abortSignal?: AbortSignalLike;\n /** Override connection timeout for this operation */\n timeoutInMs?: number;\n}\n\nexport interface SendEventOptions {\n /** Abort signal to cancel send operation */\n abortSignal?: AbortSignalLike;\n /** Timeout for send operation */\n timeoutInMs?: number;\n}\n\nexport interface AudioStreamOptions extends SendEventOptions {\n /** Turn ID for turn-based audio (if not provided, uses buffer mode) */\n turnId?: string;\n}\n\nexport interface TurnOptions extends SendEventOptions {\n /** Custom turn ID (if not provided, one will be generated) */\n turnId?: string;\n}\n\n/**\n * Represents a WebSocket-based session for real-time voice communication with the Azure VoiceLive service.\n *\n * This class manages the connection, handles real-time communication, and provides access to all\n * interactive features including audio streaming, conversation management, and avatar control.\n */\nexport class VoiceLiveSession {\n private readonly _endpoint: string;\n private readonly _credentialHandler: CredentialHandler;\n private readonly _options: Required<VoiceLiveSessionOptions>;\n private readonly _messageParser: VoiceLiveMessageParser;\n private readonly _model: string;\n private _connectionManager?: ConnectionManager;\n private _sessionId?: string;\n private _activeTurnId?: string;\n private _disposed = false;\n\n // Handler-based subscription management\n private readonly _subscriptionManager: SubscriptionManager;\n\n /**\n * Creates an instance of VoiceLiveSession.\n *\n * @param endpoint - The Voice Live service endpoint URL\n * @param credential - Azure TokenCredential or KeyCredential for authentication\n * @param apiVersion - API version to use for the Voice Live service\n * @param model - The model name to use for the session\n * @param options - Optional configuration for the session\n */\n constructor(\n endpoint: string,\n credential: TokenCredential | KeyCredential,\n apiVersion: string,\n model: string,\n options: VoiceLiveSessionOptions = {},\n ) {\n this._endpoint = this._normalizeEndpoint(endpoint);\n this._credentialHandler = new CredentialHandler(credential);\n this._options = this._buildDefaultOptions(options);\n this._messageParser = new VoiceLiveMessageParser();\n this._model = model;\n\n // Initialize handler-based subscription management\n this._subscriptionManager = new SubscriptionManager();\n\n logger.info(\"VoiceLiveSession created\", {\n endpoint: this._endpoint,\n model: this._model,\n apiVersion: apiVersion,\n enableDebugLogging: this._options.enableDebugLogging,\n });\n }\n\n /**\n * Establishes connection to the Voice Live service with authentication.\n */\n async connect(options: ConnectOptions = {}): Promise<void> {\n this._ensureNotDisposed();\n\n if (this.isConnected) {\n logger.info(\"VoiceLiveSession already connected\");\n return;\n }\n\n try {\n logger.info(\"Connecting to Voice Live service\", {\n endpoint: this._endpoint,\n model: this._model,\n });\n\n // Get WebSocket URL with authentication and model\n const wsUrl = await this._credentialHandler.getWebSocketUrl(\n this._endpoint,\n \"2025-10-01\", // TODO: make this configurable through constructor\n this._model,\n );\n const authHeaders = await this._credentialHandler.getAuthHeaders();\n\n // Create connection manager\n const websocketFactory = new VoiceLiveWebSocketFactory();\n this._connectionManager = new ConnectionManager(\n () =>\n websocketFactory.create({\n headers: { ...authHeaders },\n connectionTimeoutInMs: this._options.connectionTimeoutInMs,\n compression: true,\n }),\n {\n endpoint: wsUrl,\n connectionTimeout: options.timeoutInMs || this._options.connectionTimeoutInMs,\n },\n );\n\n // Setup connection event handlers\n this._setupConnectionEventHandlers();\n\n // Connect with proper error handling\n await this._connectionManager.connect(options.abortSignal);\n\n logger.info(\"Successfully connected to Voice Live service\");\n } catch (error) {\n logger.error(\"Failed to connect to Voice Live service\", { error });\n\n // Use error classification\n if (error instanceof VoiceLiveConnectionError) {\n throw error;\n } else {\n throw classifyConnectionError(error);\n }\n }\n }\n\n /**\n * Disconnects from the Voice Live service and cleans up resources.\n */\n async disconnect(): Promise<void> {\n if (!this._connectionManager) {\n return;\n }\n\n logger.info(\"Disconnecting from Voice Live service\");\n\n try {\n await this._connectionManager.disconnect();\n } catch (error) {\n logger.error(\"Error during disconnect\", { error });\n } finally {\n this._connectionManager = undefined;\n this._sessionId = undefined;\n this._activeTurnId = undefined;\n logger.info(\"Disconnected from Voice Live service\");\n }\n }\n\n /**\n * Subscribe to VoiceLive session events using strongly-typed handlers.\n * This follows the Azure SDK pattern used by EventHub, Service Bus, etc.\n *\n * @param handlers - Handler functions for different types of events\n * @returns A subscription object that can be used to stop receiving events\n */\n subscribe(handlers: VoiceLiveSessionHandlers): VoiceLiveSubscription {\n this._ensureNotDisposed();\n\n logger.info(\"Creating VoiceLive session subscription\");\n return this._subscriptionManager.createSubscription(handlers);\n }\n\n /**\n * Sends a custom client event to the service.\n */\n async sendEvent(event: ClientEventUnion, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n await this._sendEvent(event, options);\n }\n\n /**\n * Updates the session configuration with the service.\n */\n async updateSession(session: RequestSession, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n\n const updateEvent: ClientEventSessionUpdate = {\n type: \"session.update\",\n session: session,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(updateEvent, options);\n }\n\n /**\n * Sends audio data to the service using turn-based or buffer-based approach.\n */\n async sendAudio(\n audioData: ArrayBuffer | Uint8Array,\n options: AudioStreamOptions = {},\n ): Promise<void> {\n this._ensureConnected();\n\n const audioBase64 = this._arrayBufferToBase64(audioData);\n\n if (options.turnId) {\n // Turn-based audio\n const appendEvent: ClientEventInputAudioTurnAppend = {\n type: \"input_audio.turn.append\",\n audio: audioBase64,\n turnId: options.turnId,\n eventId: this._generateEventId(),\n };\n await this._sendEvent(appendEvent, options);\n } else {\n // Buffer-based audio (VAD mode)\n const bufferEvent: ClientEventInputAudioBufferAppend = {\n type: \"input_audio_buffer.append\",\n audio: audioBase64,\n eventId: this._generateEventId(),\n };\n await this._sendEvent(bufferEvent, options);\n }\n }\n\n /**\n * Starts a new audio turn for turn-based audio input.\n */\n async startAudioTurn(options: TurnOptions = {}): Promise<string> {\n this._ensureConnected();\n\n const turnId = options.turnId || this._generateTurnId();\n this._activeTurnId = turnId;\n\n const startEvent: ClientEventInputAudioTurnStart = {\n type: \"input_audio.turn.start\",\n turnId: turnId,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(startEvent, options);\n return turnId;\n }\n\n /**\n * Ends the current audio turn.\n */\n async endAudioTurn(turnId?: string, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n\n const targetTurnId = turnId || this._activeTurnId;\n if (!targetTurnId) {\n throw new VoiceLiveConnectionError(\"No active audio turn to end\", \"INVALID_STATE\");\n }\n\n const endEvent: ClientEventInputAudioTurnEnd = {\n type: \"input_audio.turn.end\",\n turnId: targetTurnId,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(endEvent, options);\n\n if (targetTurnId === this._activeTurnId) {\n this._activeTurnId = undefined;\n }\n }\n\n /**\n * Adds a conversation item (message) to the conversation.\n */\n async addConversationItem(\n item: ConversationRequestItem,\n options: SendEventOptions = {},\n ): Promise<void> {\n this._ensureConnected();\n\n const createEvent: ClientEventConversationItemCreate = {\n type: \"conversation.item.create\",\n item: item,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(createEvent, options);\n }\n\n /**\n * Indicates whether the session is currently connected to the Voice Live service.\n */\n get isConnected(): boolean {\n return this._connectionManager?.isConnected || false;\n }\n\n /**\n * Gets the current connection state of the session.\n */\n get connectionState(): ConnectionState {\n return this._connectionManager?.state || ConnectionState.Disconnected;\n }\n\n /**\n * Gets the current session ID.\n */\n get sessionId(): string | undefined {\n return this._sessionId;\n }\n\n /**\n * Gets the current active audio turn ID.\n */\n get activeTurnId(): string | undefined {\n return this._activeTurnId;\n }\n\n /**\n * Disposes the session and cleans up resources.\n */\n async dispose(): Promise<void> {\n if (this._disposed) {\n return;\n }\n\n logger.info(\"Disposing VoiceLiveSession\");\n\n try {\n // Close all subscriptions first\n await this._subscriptionManager.closeAll();\n\n // Then disconnect\n await this.disconnect();\n } catch (error) {\n logger.error(\"Error during session disposal\", { error });\n } finally {\n this._disposed = true;\n logger.info(\"VoiceLiveSession disposed\");\n }\n }\n\n // Private methods\n\n private _buildDefaultOptions(\n options: VoiceLiveSessionOptions,\n ): Required<VoiceLiveSessionOptions> {\n return {\n connectionTimeoutInMs: options.connectionTimeoutInMs || 30000,\n enableDebugLogging: options.enableDebugLogging ?? false,\n };\n }\n\n private _normalizeEndpoint(endpoint: string): string {\n // Ensure endpoint has proper protocol\n if (!endpoint.startsWith(\"http://\") && !endpoint.startsWith(\"https://\")) {\n endpoint = `https://${endpoint}`;\n }\n\n // Remove trailing slash\n return endpoint.replace(/\\/$/, \"\");\n }\n\n private _setupConnectionEventHandlers(): void {\n if (!this._connectionManager) return;\n\n this._connectionManager.updateEventHandlers({\n onStateChange: (state, previousState) => {\n logger.info(\"Connection state changed\", { state, previousState });\n\n // Handle connection state changes for handler-based subscriptions\n if (state === ConnectionState.Connected && previousState === ConnectionState.Connecting) {\n this._notifyConnectionEvent(\"connected\", {\n connectionId: `conn_${Date.now()}`,\n timestamp: new Date(),\n });\n } else if (\n state === ConnectionState.Disconnected &&\n previousState === ConnectionState.Connected\n ) {\n this._notifyConnectionEvent(\"disconnected\", {\n code: 1006, // Abnormal closure\n reason: \"Connection lost during session\",\n wasClean: false,\n timestamp: new Date(),\n });\n this._markSessionAsDead(\n \"Connection lost during session - session is permanently unusable\",\n );\n }\n },\n onMessage: (data) => {\n this._handleIncomingMessage(data);\n },\n onError: (error) => {\n logger.error(\"Connection error - marking session as dead\", { error });\n this._notifyConnectionEvent(\"error\", {\n error: error,\n context: \"WebSocket connection error\",\n recoverable: false,\n timestamp: new Date(),\n });\n this._markSessionAsDead(`Connection error: ${error.message}`);\n },\n });\n }\n\n private _markSessionAsDead(reason: string): void {\n if (this._disposed) return;\n\n logger.error(\"Session marked as permanently dead\", { reason });\n\n // Mark as disposed to prevent further use\n this._disposed = true;\n\n // Clean up connection manager\n this._connectionManager = undefined;\n this._sessionId = undefined;\n this._activeTurnId = undefined;\n }\n\n private _handleIncomingMessage(data: string | ArrayBuffer): void {\n try {\n logger.info(\"Message received\", {\n type: typeof data,\n size: typeof data === \"string\" ? data.length : data.byteLength,\n });\n\n // Parse and process the message\n const parsed = this._messageParser.parseIncomingMessage(data);\n if (parsed && parsed.type === \"server\") {\n // Handle server events\n this._handleServerEvent(parsed.event);\n }\n } catch (error) {\n logger.error(\"Error handling incoming message\", { error });\n }\n }\n\n private _handleServerEvent(event: any): void {\n // Extract session information from events\n if (event.type === \"session.created\" && event.session?.id) {\n this._sessionId = event.session.id;\n logger.info(\"Session created\", { sessionId: this._sessionId });\n }\n\n // Notify handler-based subscriptions\n this._notifyServerEvent(event);\n }\n\n private _notifyConnectionEvent(\n eventType: \"connected\" | \"disconnected\" | \"error\",\n args: ConnectedEventArgs | DisconnectedEventArgs | ErrorEventArgs,\n ): void {\n const context: ConnectionContext = {\n endpoint: this._endpoint,\n sessionId: this._sessionId,\n timestamp: new Date(),\n model: this._model,\n };\n\n // Fire and forget - don't await to avoid blocking\n this._subscriptionManager.processConnectionEvent(eventType, args, context).catch((error) => {\n logger.error(\"Error processing connection event in handlers\", { eventType, error });\n });\n }\n\n private _notifyServerEvent(event: ServerEventUnion): void {\n if (!this._sessionId) {\n // Can't notify server events without a session ID\n return;\n }\n\n const context: SessionContext = {\n endpoint: this._endpoint,\n sessionId: this._sessionId,\n timestamp: new Date(),\n model: this._model,\n conversationId: undefined, // Could extract from event if available\n };\n\n // Fire and forget - don't await to avoid blocking\n this._subscriptionManager.processServerEvent(event, context).catch((error) => {\n logger.error(\"Error processing server event in handlers\", { eventType: event.type, error });\n });\n }\n\n private async _sendEvent(event: ClientEventUnion, options: SendEventOptions): Promise<void> {\n if (!this._connectionManager?.isConnected) {\n throw new VoiceLiveConnectionError(\"Not connected to Voice Live service\", \"NOT_CONNECTED\");\n }\n\n try {\n const serialized = this._messageParser.serializeOutgoingMessage(event);\n await this._connectionManager.send(serialized, options.abortSignal);\n\n logger.info(\"Sent event\", { type: event.type, eventId: (event as any).eventId });\n } catch (error) {\n if (error instanceof VoiceLiveConnectionError) {\n throw error;\n }\n\n throw classifyConnectionError(error);\n }\n }\n\n private _ensureConnected(): void {\n this._ensureNotDisposed();\n if (!this.isConnected) {\n throw new VoiceLiveConnectionError(\n \"Must be connected to Voice Live service\",\n \"NOT_CONNECTED\",\n );\n }\n }\n\n private _ensureNotDisposed(): void {\n if (this._disposed) {\n throw new VoiceLiveConnectionError(\n \"Session is permanently dead and cannot be used. Create a new session to continue.\",\n \"SESSION_DEAD\",\n );\n }\n }\n\n private _generateEventId(): string {\n return `evt_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private _generateTurnId(): string {\n return `turn_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private _arrayBufferToBase64(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;\n let binary = \"\";\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n}\n"]}
1
+ {"version":3,"file":"voiceLiveSession.js","sourceRoot":"","sources":["../../src/voiceLiveSession.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { KeyCredential, TokenCredential } from \"@azure/core-auth\";\nimport type { AbortSignalLike } from \"@azure/abort-controller\";\nimport {\n type RequestSession,\n type ClientEventSessionUpdate,\n type ClientEventUnion,\n type ClientEventInputAudioBufferAppend,\n type ClientEventInputAudioTurnStart,\n type ClientEventInputAudioTurnAppend,\n type ClientEventInputAudioTurnEnd,\n type ConversationRequestItem,\n type ClientEventConversationItemCreate,\n type ServerEventUnion,\n KnownClientEventType,\n KnownServerEventType,\n} from \"./models/index.js\";\nimport { ConnectionManager, ConnectionState } from \"./websocket/connectionManager.js\";\nimport { VoiceLiveWebSocketFactory } from \"./websocket/websocketFactory.js\";\nimport { VoiceLiveMessageParser } from \"./protocol/messageParser.js\";\nimport { CredentialHandler } from \"./auth/credentialHandler.js\";\nimport { VoiceLiveConnectionError, classifyConnectionError } from \"./errors/index.js\";\nimport { logger } from \"./logger.js\";\nimport type {\n VoiceLiveSessionHandlers,\n VoiceLiveSubscription,\n ConnectionContext,\n SessionContext,\n ConnectedEventArgs,\n DisconnectedEventArgs,\n ErrorEventArgs,\n} from \"./handlers/sessionHandlers.js\";\nimport { SubscriptionManager } from \"./handlers/subscriptionManager.js\";\n\nexport interface VoiceLiveSessionOptions {\n /** Connection timeout in milliseconds */\n connectionTimeoutInMs?: number;\n /** Enable debug logging for development */\n enableDebugLogging?: boolean;\n}\n\nexport interface CreateSessionOptions extends VoiceLiveSessionOptions {}\n\nexport interface StartSessionOptions extends VoiceLiveSessionOptions {}\nexport interface ConnectOptions {\n /** Abort signal to cancel connection attempt */\n abortSignal?: AbortSignalLike;\n /** Override connection timeout for this operation */\n timeoutInMs?: number;\n}\n\nexport interface SendEventOptions {\n /** Abort signal to cancel send operation */\n abortSignal?: AbortSignalLike;\n /** Timeout for send operation */\n timeoutInMs?: number;\n}\n\nexport interface AudioStreamOptions extends SendEventOptions {\n /** Turn ID for turn-based audio (if not provided, uses buffer mode) */\n turnId?: string;\n}\n\nexport interface TurnOptions extends SendEventOptions {\n /** Custom turn ID (if not provided, one will be generated) */\n turnId?: string;\n}\n\n/**\n * Represents a WebSocket-based session for real-time voice communication with the Azure VoiceLive service.\n *\n * This class manages the connection, handles real-time communication, and provides access to all\n * interactive features including audio streaming, conversation management, and avatar control.\n */\nexport class VoiceLiveSession {\n private readonly _endpoint: string;\n private readonly _credentialHandler: CredentialHandler;\n private readonly _options: Required<VoiceLiveSessionOptions>;\n private readonly _messageParser: VoiceLiveMessageParser;\n private readonly _model: string;\n private readonly _apiVersion: string;\n private _connectionManager?: ConnectionManager;\n private _sessionId?: string;\n private _activeTurnId?: string;\n private _disposed = false;\n // Handler-based subscription management\n private readonly _subscriptionManager: SubscriptionManager;\n\n /**\n * Creates an instance of VoiceLiveSession.\n *\n * @param endpoint - The Voice Live service endpoint URL\n * @param credential - Azure TokenCredential or KeyCredential for authentication\n * @param apiVersion - API version to use for the Voice Live service\n * @param model - The model name to use for the session\n * @param options - Optional configuration for the session\n */\n constructor(\n endpoint: string,\n credential: TokenCredential | KeyCredential,\n apiVersion: string,\n model: string,\n options: VoiceLiveSessionOptions = {},\n ) {\n this._endpoint = this._normalizeEndpoint(endpoint);\n this._credentialHandler = new CredentialHandler(credential);\n this._options = this._buildDefaultOptions(options);\n this._messageParser = new VoiceLiveMessageParser();\n this._model = model;\n this._apiVersion = apiVersion;\n\n // Initialize handler-based subscription management\n this._subscriptionManager = new SubscriptionManager();\n\n logger.info(\"VoiceLiveSession created\", {\n endpoint: this._endpoint,\n model: this._model,\n apiVersion: apiVersion,\n enableDebugLogging: this._options.enableDebugLogging,\n });\n }\n\n /**\n * Establishes connection to the Voice Live service with authentication.\n */\n async connect(options: ConnectOptions = {}): Promise<void> {\n this._ensureNotDisposed();\n\n if (this.isConnected) {\n logger.info(\"VoiceLiveSession already connected\");\n return;\n }\n\n try {\n logger.info(\"Connecting to Voice Live service\", {\n endpoint: this._endpoint,\n model: this._model,\n });\n\n // Get WebSocket URL with authentication and model\n const wsUrl = await this._credentialHandler.getWebSocketUrl(\n this._endpoint,\n this._apiVersion,\n this._model,\n );\n const authHeaders = await this._credentialHandler.getAuthHeaders();\n\n // Create connection manager\n const websocketFactory = new VoiceLiveWebSocketFactory();\n this._connectionManager = new ConnectionManager(\n () =>\n websocketFactory.create({\n headers: { ...authHeaders },\n connectionTimeoutInMs: this._options.connectionTimeoutInMs,\n compression: true,\n }),\n {\n endpoint: wsUrl,\n connectionTimeout: options.timeoutInMs || this._options.connectionTimeoutInMs,\n },\n );\n\n // Setup connection event handlers\n this._setupConnectionEventHandlers();\n\n // Connect with proper error handling\n await this._connectionManager.connect(options.abortSignal);\n\n logger.info(\"Successfully connected to Voice Live service\");\n } catch (error) {\n logger.error(\"Failed to connect to Voice Live service\", { error });\n\n // Use error classification\n if (error instanceof VoiceLiveConnectionError) {\n throw error;\n } else {\n throw classifyConnectionError(error);\n }\n }\n }\n\n /**\n * Disconnects from the Voice Live service and cleans up resources.\n */\n async disconnect(): Promise<void> {\n if (!this._connectionManager) {\n return;\n }\n\n logger.info(\"Disconnecting from Voice Live service\");\n\n try {\n await this._connectionManager.disconnect();\n } catch (error) {\n logger.error(\"Error during disconnect\", { error });\n } finally {\n this._connectionManager = undefined;\n this._sessionId = undefined;\n this._activeTurnId = undefined;\n logger.info(\"Disconnected from Voice Live service\");\n }\n }\n\n /**\n * Subscribe to VoiceLive session events using strongly-typed handlers.\n *\n * @param handlers - Handler functions for different types of events\n * @returns A subscription object that can be used to stop receiving events\n */\n subscribe(handlers: VoiceLiveSessionHandlers): VoiceLiveSubscription {\n this._ensureNotDisposed();\n\n logger.info(\"Creating VoiceLive session subscription\");\n return this._subscriptionManager.createSubscription(handlers);\n }\n\n /**\n * Sends a custom client event to the service.\n */\n async sendEvent(event: ClientEventUnion, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n await this._sendEvent(event, options);\n }\n\n /**\n * Updates the session configuration with the service.\n */\n async updateSession(session: RequestSession, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n\n const updateEvent: ClientEventSessionUpdate = {\n type: KnownClientEventType.SessionUpdate,\n session: session,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(updateEvent, options);\n }\n\n /**\n * Sends audio data to the service using turn-based or buffer-based approach.\n */\n async sendAudio(\n audioData: ArrayBuffer | Uint8Array,\n options: AudioStreamOptions = {},\n ): Promise<void> {\n this._ensureConnected();\n\n const audioBase64 = this._arrayBufferToBase64(audioData);\n\n if (options.turnId) {\n // Turn-based audio\n const appendEvent: ClientEventInputAudioTurnAppend = {\n type: KnownClientEventType.InputAudioTurnAppend,\n audio: audioBase64,\n turnId: options.turnId,\n eventId: this._generateEventId(),\n };\n await this._sendEvent(appendEvent, options);\n } else {\n // Buffer-based audio (VAD mode)\n const bufferEvent: ClientEventInputAudioBufferAppend = {\n type: KnownClientEventType.InputAudioBufferAppend,\n audio: audioBase64,\n eventId: this._generateEventId(),\n };\n await this._sendEvent(bufferEvent, options);\n }\n }\n\n /**\n * Starts a new audio turn for turn-based audio input.\n */\n async startAudioTurn(options: TurnOptions = {}): Promise<string> {\n this._ensureConnected();\n\n const turnId = options.turnId || this._generateTurnId();\n this._activeTurnId = turnId;\n\n const startEvent: ClientEventInputAudioTurnStart = {\n type: KnownClientEventType.InputAudioTurnStart,\n turnId: turnId,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(startEvent, options);\n return turnId;\n }\n\n /**\n * Ends the current audio turn.\n */\n async endAudioTurn(turnId?: string, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n\n const targetTurnId = turnId || this._activeTurnId;\n if (!targetTurnId) {\n throw new VoiceLiveConnectionError(\"No active audio turn to end\", \"INVALID_STATE\");\n }\n\n const endEvent: ClientEventInputAudioTurnEnd = {\n type: KnownClientEventType.InputAudioTurnEnd,\n turnId: targetTurnId,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(endEvent, options);\n\n if (targetTurnId === this._activeTurnId) {\n this._activeTurnId = undefined;\n }\n }\n\n /**\n * Adds a conversation item (message) to the conversation.\n */\n async addConversationItem(\n item: ConversationRequestItem,\n options: SendEventOptions = {},\n ): Promise<void> {\n this._ensureConnected();\n\n const createEvent: ClientEventConversationItemCreate = {\n type: KnownClientEventType.ConversationItemCreate,\n item: item,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(createEvent, options);\n }\n\n /**\n * Indicates whether the session is currently connected to the Voice Live service.\n */\n get isConnected(): boolean {\n return this._connectionManager?.isConnected || false;\n }\n\n /**\n * Gets the current connection state of the session.\n */\n get connectionState(): ConnectionState {\n return this._connectionManager?.state || ConnectionState.Disconnected;\n }\n\n /**\n * Gets the current session ID.\n */\n get sessionId(): string | undefined {\n return this._sessionId;\n }\n\n /**\n * Gets the current active audio turn ID.\n */\n get activeTurnId(): string | undefined {\n return this._activeTurnId;\n }\n\n /**\n * Disposes the session and cleans up resources.\n */\n async dispose(): Promise<void> {\n if (this._disposed) {\n return;\n }\n\n logger.info(\"Disposing VoiceLiveSession\");\n\n try {\n // Close all subscriptions first\n await this._subscriptionManager.closeAll();\n\n // Then disconnect\n await this.disconnect();\n } catch (error) {\n logger.error(\"Error during session disposal\", { error });\n } finally {\n this._disposed = true;\n logger.info(\"VoiceLiveSession disposed\");\n }\n }\n\n // Private methods\n\n private _buildDefaultOptions(\n options: VoiceLiveSessionOptions,\n ): Required<VoiceLiveSessionOptions> {\n return {\n connectionTimeoutInMs: options.connectionTimeoutInMs || 30000,\n enableDebugLogging: options.enableDebugLogging ?? false,\n };\n }\n\n private _normalizeEndpoint(endpoint: string): string {\n // Ensure endpoint has proper protocol\n if (!endpoint.startsWith(\"http://\") && !endpoint.startsWith(\"https://\")) {\n endpoint = `https://${endpoint}`;\n }\n\n // Remove trailing slash\n return endpoint.replace(/\\/$/, \"\");\n }\n\n private _setupConnectionEventHandlers(): void {\n if (!this._connectionManager) return;\n\n this._connectionManager.updateEventHandlers({\n onStateChange: (state, previousState) => {\n logger.info(\"Connection state changed\", { state, previousState });\n\n // Handle connection state changes for handler-based subscriptions\n if (state === ConnectionState.Connected && previousState === ConnectionState.Connecting) {\n this._notifyConnectionEvent(\"connected\", {\n connectionId: `conn_${Date.now()}`,\n timestamp: new Date(),\n });\n } else if (\n state === ConnectionState.Disconnected &&\n previousState === ConnectionState.Connected\n ) {\n this._notifyConnectionEvent(\"disconnected\", {\n code: 1006, // Abnormal closure\n reason: \"Connection lost during session\",\n wasClean: false,\n timestamp: new Date(),\n });\n this._markSessionAsDead(\n \"Connection lost during session - session is permanently unusable\",\n );\n }\n },\n onMessage: (data) => {\n this._handleIncomingMessage(data);\n },\n onError: (error) => {\n logger.error(\"Connection error - marking session as dead\", { error });\n this._notifyConnectionEvent(\"error\", {\n error: error,\n context: \"WebSocket connection error\",\n recoverable: false,\n timestamp: new Date(),\n });\n this._markSessionAsDead(`Connection error: ${error.message}`);\n },\n });\n }\n\n private _markSessionAsDead(reason: string): void {\n if (this._disposed) return;\n\n logger.error(\"Session marked as permanently dead\", { reason });\n\n // Mark as disposed to prevent further use\n this._disposed = true;\n\n // Clean up connection manager\n this._connectionManager = undefined;\n this._sessionId = undefined;\n this._activeTurnId = undefined;\n }\n\n private _handleIncomingMessage(data: string | ArrayBuffer): void {\n try {\n logger.info(\"Message received\", {\n type: typeof data,\n size: typeof data === \"string\" ? data.length : data.byteLength,\n });\n\n // Parse and process the message\n const parsed = this._messageParser.parseIncomingMessage(data);\n if (parsed && parsed.type === \"server\") {\n // Handle server events\n this._handleServerEvent(parsed.event);\n }\n } catch (error) {\n logger.error(\"Error handling incoming message\", { error });\n }\n }\n\n private _handleServerEvent(event: any): void {\n // Extract session information from events\n if (event.type === KnownServerEventType.SessionCreated && event.session?.id) {\n this._sessionId = event.session.id;\n logger.info(\"Session created\", { sessionId: this._sessionId });\n }\n\n // Notify handler-based subscriptions\n this._notifyServerEvent(event);\n }\n\n private _notifyConnectionEvent(\n eventType: \"connected\" | \"disconnected\" | \"error\",\n args: ConnectedEventArgs | DisconnectedEventArgs | ErrorEventArgs,\n ): void {\n const context: ConnectionContext = {\n endpoint: this._endpoint,\n sessionId: this._sessionId,\n timestamp: new Date(),\n model: this._model,\n };\n\n // Fire and forget - don't await to avoid blocking\n this._subscriptionManager.processConnectionEvent(eventType, args, context).catch((error) => {\n logger.error(\"Error processing connection event in handlers\", { eventType, error });\n });\n }\n\n private _notifyServerEvent(event: ServerEventUnion): void {\n if (!this._sessionId) {\n // Can't notify server events without a session ID\n return;\n }\n\n const context: SessionContext = {\n endpoint: this._endpoint,\n sessionId: this._sessionId,\n timestamp: new Date(),\n model: this._model,\n conversationId: undefined, // Could extract from event if available\n };\n\n // Fire and forget - don't await to avoid blocking\n this._subscriptionManager.processServerEvent(event, context).catch((error) => {\n logger.error(\"Error processing server event in handlers\", { eventType: event.type, error });\n });\n }\n\n private async _sendEvent(event: ClientEventUnion, options: SendEventOptions): Promise<void> {\n if (!this._connectionManager?.isConnected) {\n throw new VoiceLiveConnectionError(\"Not connected to Voice Live service\", \"NOT_CONNECTED\");\n }\n\n try {\n const serialized = this._messageParser.serializeOutgoingMessage(event);\n await this._connectionManager.send(serialized, options.abortSignal);\n\n logger.info(\"Sent event\", { type: event.type, eventId: (event as any).eventId });\n } catch (error) {\n if (error instanceof VoiceLiveConnectionError) {\n throw error;\n }\n\n throw classifyConnectionError(error);\n }\n }\n\n private _ensureConnected(): void {\n this._ensureNotDisposed();\n if (!this.isConnected) {\n throw new VoiceLiveConnectionError(\n \"Must be connected to Voice Live service\",\n \"NOT_CONNECTED\",\n );\n }\n }\n\n private _ensureNotDisposed(): void {\n if (this._disposed) {\n throw new VoiceLiveConnectionError(\n \"Session is permanently dead and cannot be used. Create a new session to continue.\",\n \"SESSION_DEAD\",\n );\n }\n }\n\n private _generateEventId(): string {\n return `evt_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private _generateTurnId(): string {\n return `turn_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private _arrayBufferToBase64(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;\n let binary = \"\";\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket-browser.mjs","sourceRoot":"","sources":["../../../src/websocket/websocket-browser.mts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { AbortSignalLike } from \"@azure/abort-controller\";\nimport {\n WebSocketState,\n type VoiceLiveWebSocketLike,\n type WebSocketFactoryOptions,\n} from \"./websocketLike.js\";\nimport { VoiceLiveConnectionError, VoiceLiveErrorCodes } from \"../errors/connectionErrors.js\";\n\n/**\n * Browser WebSocket implementation using native WebSocket API\n */\nexport class VoiceLiveWebSocket implements VoiceLiveWebSocketLike {\n private _ws?: WebSocket;\n private _url?: string;\n private _options: WebSocketFactoryOptions;\n\n private _onOpen?: () => void;\n private _onClose?: (code: number, reason: string) => void;\n private _onMessage?: (data: string | ArrayBuffer) => void;\n private _onError?: (error: Error) => void;\n\n constructor(options: WebSocketFactoryOptions = {}) {\n this._options = {\n connectionTimeoutInMs: 30000,\n maxMessageSize: 1024 * 1024, // 1MB\n ...options,\n };\n }\n\n async connect(url: string, protocols?: string[], abortSignal?: AbortSignalLike): Promise<void> {\n if (this._ws && this._ws.readyState !== WebSocket.CLOSED) {\n throw new VoiceLiveConnectionError(\n \"WebSocket is already connected or connecting\",\n VoiceLiveErrorCodes.AlreadyConnected,\n );\n }\n\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(\n new VoiceLiveConnectionError(\n `WebSocket connection timeout after ${this._options.connectionTimeoutInMs}ms`,\n VoiceLiveErrorCodes.ConnectionTimeout,\n ),\n );\n }, this._options.connectionTimeoutInMs);\n // Handle abort signal\n const abortHandler = (): void => {\n clearTimeout(timeoutId);\n if (this._ws) {\n this._ws.close();\n }\n reject(\n new VoiceLiveConnectionError(\n \"WebSocket connection aborted\",\n VoiceLiveErrorCodes.OperationCancelled,\n ),\n );\n };\n\n if (abortSignal) {\n if (abortSignal.aborted) {\n abortHandler();\n return;\n }\n abortSignal.addEventListener(\"abort\", abortHandler);\n }\n\n try {\n // Browser WebSocket API doesn't support custom headers, so we need to pass\n // authentication and other headers as query parameters\n const urlWithHeaders = this._addHeadersToUrl(url, this._options.headers);\n\n this._ws = new WebSocket(urlWithHeaders, protocols);\n this._ws.binaryType = \"arraybuffer\";\n this._url = url;\n\n this._ws.addEventListener(\"open\", () => {\n clearTimeout(timeoutId);\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n this._onOpen?.();\n resolve();\n });\n\n this._ws.addEventListener(\"close\", (event: CloseEvent) => {\n clearTimeout(timeoutId);\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n this._onClose?.(event.code, event.reason);\n });\n\n this._ws.addEventListener(\"message\", (event: MessageEvent) => {\n if (typeof event.data === \"string\") {\n this._onMessage?.(event.data);\n } else if (event.data instanceof ArrayBuffer) {\n this._onMessage?.(event.data);\n } else if (event.data instanceof Blob) {\n // Convert Blob to ArrayBuffer\n const reader = new FileReader();\n reader.onload = () => {\n if (reader.result instanceof ArrayBuffer) {\n this._onMessage?.(reader.result);\n }\n };\n reader.onerror = () => {\n this._onError?.(\n new VoiceLiveConnectionError(\n \"Failed to read blob data\",\n VoiceLiveErrorCodes.WebSocketError,\n \"blob_read\",\n ),\n );\n };\n reader.readAsArrayBuffer(event.data);\n }\n });\n\n this._ws.addEventListener(\"error\", () => {\n clearTimeout(timeoutId);\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n const error = new VoiceLiveConnectionError(\n \"WebSocket connection failed\",\n VoiceLiveErrorCodes.WebSocketError,\n \"websocket_connection\",\n true,\n );\n this._onError?.(error);\n reject(error);\n });\n } catch (error) {\n clearTimeout(timeoutId);\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n reject(\n new VoiceLiveConnectionError(\n `Failed to create WebSocket: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n VoiceLiveErrorCodes.ConnectionFailed,\n \"websocket_creation\",\n false,\n error instanceof Error ? error : new Error(String(error)),\n ),\n );\n }\n });\n }\n\n async disconnect(code: number = 1000, reason?: string): Promise<void> {\n if (!this._ws || this._ws.readyState === WebSocket.CLOSED) {\n return;\n }\n\n return new Promise((resolve) => {\n const closeHandler = (): void => {\n resolve();\n };\n\n if (this._ws!.readyState === WebSocket.CLOSING) {\n this._ws!.addEventListener(\"close\", closeHandler, { once: true });\n } else {\n this._ws!.addEventListener(\"close\", closeHandler, { once: true });\n this._ws!.close(code, reason);\n }\n });\n }\n\n async send(data: string | ArrayBuffer, abortSignal?: AbortSignalLike): Promise<void> {\n if (!this._ws || this._ws.readyState !== WebSocket.OPEN) {\n throw new VoiceLiveConnectionError(\n \"WebSocket is not connected\",\n VoiceLiveErrorCodes.NotConnected,\n );\n }\n\n if (abortSignal?.aborted) {\n throw new VoiceLiveConnectionError(\n \"Send operation aborted\",\n VoiceLiveErrorCodes.OperationCancelled,\n );\n }\n\n return new Promise((resolve, reject) => {\n const abortHandler = (): void => {\n reject(\n new VoiceLiveConnectionError(\n \"Send operation aborted\",\n VoiceLiveErrorCodes.OperationCancelled,\n ),\n );\n };\n\n if (abortSignal) {\n abortSignal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n\n try {\n this._ws!.send(data);\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n resolve();\n } catch (error) {\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n reject(\n new VoiceLiveConnectionError(\n `Failed to send WebSocket message: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n VoiceLiveErrorCodes.WebSocketError,\n \"message_send\",\n true,\n error instanceof Error ? error : new Error(String(error)),\n ),\n );\n }\n });\n }\n\n onOpen(handler: () => void): void {\n this._onOpen = handler;\n }\n\n onClose(handler: (code: number, reason: string) => void): void {\n this._onClose = handler;\n }\n\n onMessage(handler: (data: string | ArrayBuffer) => void): void {\n this._onMessage = handler;\n }\n\n onError(handler: (error: Error) => void): void {\n this._onError = handler;\n }\n\n get isConnected(): boolean {\n return this._ws?.readyState === WebSocket.OPEN;\n }\n\n get readyState(): WebSocketState {\n if (!this._ws) return WebSocketState.Closed;\n return this._ws.readyState as WebSocketState;\n }\n\n get url(): string | undefined {\n return this._url;\n }\n\n /**\n * Converts headers to query parameters for browser WebSocket connections.\n * Browser WebSocket API doesn't support custom headers, so authentication\n * and other headers need to be passed as query parameters.\n */\n private _addHeadersToUrl(url: string, headers?: Record<string, string>): string {\n if (!headers || Object.keys(headers).length === 0) {\n return url;\n }\n\n const urlObj = new URL(url);\n\n // Convert known authentication headers to appropriate query parameters\n for (const [key, value] of Object.entries(headers)) {\n const lowerKey = key.toLowerCase();\n\n if (lowerKey === \"authorization\") {\n // Convert Bearer token to authorization query parameter\n urlObj.searchParams.set(\"authorization\", value);\n } else if (lowerKey === \"api-key\") {\n // API key goes to api-key query parameter\n urlObj.searchParams.set(\"api-key\", value);\n } else if (lowerKey === \"x-ms-client-request-id\") {\n // Client request ID as query parameter\n urlObj.searchParams.set(\"client-request-id\", value);\n } else if (lowerKey === \"user-agent\") {\n // User-Agent cannot be set in WebSocket, skip it\n continue;\n } else {\n // For other headers, convert to query parameters with 'h-' prefix\n // to avoid conflicts with existing query parameters\n urlObj.searchParams.set(`h-${lowerKey}`, value);\n }\n }\n\n return urlObj.toString();\n }\n}\n"]}
@@ -3,7 +3,7 @@ import { WebSocketState, type VoiceLiveWebSocketLike, type WebSocketFactoryOptio
3
3
  /**
4
4
  * Browser WebSocket implementation using native WebSocket API
5
5
  */
6
- export declare class VoiceLiveWebSocketBrowser implements VoiceLiveWebSocketLike {
6
+ export declare class VoiceLiveWebSocket implements VoiceLiveWebSocketLike {
7
7
  private _ws?;
8
8
  private _url?;
9
9
  private _options;
@@ -22,5 +22,11 @@ export declare class VoiceLiveWebSocketBrowser implements VoiceLiveWebSocketLike
22
22
  get isConnected(): boolean;
23
23
  get readyState(): WebSocketState;
24
24
  get url(): string | undefined;
25
+ /**
26
+ * Converts headers to query parameters for browser WebSocket connections.
27
+ * Browser WebSocket API doesn't support custom headers, so authentication
28
+ * and other headers need to be passed as query parameters.
29
+ */
30
+ private _addHeadersToUrl;
25
31
  }
26
- //# sourceMappingURL=websocketBrowser.d.ts.map
32
+ //# sourceMappingURL=websocket-browser.d.mts.map
@@ -5,7 +5,7 @@ import { VoiceLiveConnectionError, VoiceLiveErrorCodes } from "../errors/connect
5
5
  /**
6
6
  * Browser WebSocket implementation using native WebSocket API
7
7
  */
8
- export class VoiceLiveWebSocketBrowser {
8
+ export class VoiceLiveWebSocket {
9
9
  _ws;
10
10
  _url;
11
11
  _options;
@@ -44,7 +44,10 @@ export class VoiceLiveWebSocketBrowser {
44
44
  abortSignal.addEventListener("abort", abortHandler);
45
45
  }
46
46
  try {
47
- this._ws = new WebSocket(url, protocols);
47
+ // Browser WebSocket API doesn't support custom headers, so we need to pass
48
+ // authentication and other headers as query parameters
49
+ const urlWithHeaders = this._addHeadersToUrl(url, this._options.headers);
50
+ this._ws = new WebSocket(urlWithHeaders, protocols);
48
51
  this._ws.binaryType = "arraybuffer";
49
52
  this._url = url;
50
53
  this._ws.addEventListener("open", () => {
@@ -171,5 +174,42 @@ export class VoiceLiveWebSocketBrowser {
171
174
  get url() {
172
175
  return this._url;
173
176
  }
177
+ /**
178
+ * Converts headers to query parameters for browser WebSocket connections.
179
+ * Browser WebSocket API doesn't support custom headers, so authentication
180
+ * and other headers need to be passed as query parameters.
181
+ */
182
+ _addHeadersToUrl(url, headers) {
183
+ if (!headers || Object.keys(headers).length === 0) {
184
+ return url;
185
+ }
186
+ const urlObj = new URL(url);
187
+ // Convert known authentication headers to appropriate query parameters
188
+ for (const [key, value] of Object.entries(headers)) {
189
+ const lowerKey = key.toLowerCase();
190
+ if (lowerKey === "authorization") {
191
+ // Convert Bearer token to authorization query parameter
192
+ urlObj.searchParams.set("authorization", value);
193
+ }
194
+ else if (lowerKey === "api-key") {
195
+ // API key goes to api-key query parameter
196
+ urlObj.searchParams.set("api-key", value);
197
+ }
198
+ else if (lowerKey === "x-ms-client-request-id") {
199
+ // Client request ID as query parameter
200
+ urlObj.searchParams.set("client-request-id", value);
201
+ }
202
+ else if (lowerKey === "user-agent") {
203
+ // User-Agent cannot be set in WebSocket, skip it
204
+ continue;
205
+ }
206
+ else {
207
+ // For other headers, convert to query parameters with 'h-' prefix
208
+ // to avoid conflicts with existing query parameters
209
+ urlObj.searchParams.set(`h-${lowerKey}`, value);
210
+ }
211
+ }
212
+ return urlObj.toString();
213
+ }
174
214
  }
175
- //# sourceMappingURL=websocketBrowser.js.map
215
+ //# sourceMappingURL=websocket-browser.mjs.map
@@ -4,18 +4,7 @@ import type { VoiceLiveWebSocketLike, VoiceLiveWebSocketFactoryLike, WebSocketFa
4
4
  */
5
5
  export declare class VoiceLiveWebSocketFactory implements VoiceLiveWebSocketFactoryLike {
6
6
  create(options?: WebSocketFactoryOptions): VoiceLiveWebSocketLike;
7
- private _createBrowserWebSocket;
8
- private _createNodeWebSocket;
9
- private _detectPlatform;
10
7
  }
11
- /**
12
- * Platform detection utility
13
- */
14
- export declare function detectPlatform(): "browser" | "node" | "unknown";
15
- /**
16
- * Check if platform supports WebSocket
17
- */
18
- export declare function isWebSocketSupported(): boolean;
19
8
  /**
20
9
  * Default factory instance
21
10
  */