@livekit/agents 1.2.0 → 1.2.2

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 (205) hide show
  1. package/dist/_exceptions.cjs.map +1 -1
  2. package/dist/_exceptions.d.ts.map +1 -1
  3. package/dist/_exceptions.js.map +1 -1
  4. package/dist/audio.cjs +10 -0
  5. package/dist/audio.cjs.map +1 -1
  6. package/dist/audio.d.cts +1 -1
  7. package/dist/audio.d.ts +1 -1
  8. package/dist/audio.d.ts.map +1 -1
  9. package/dist/audio.js +10 -0
  10. package/dist/audio.js.map +1 -1
  11. package/dist/beta/workflows/task_group.cjs +7 -4
  12. package/dist/beta/workflows/task_group.cjs.map +1 -1
  13. package/dist/beta/workflows/task_group.d.ts.map +1 -1
  14. package/dist/beta/workflows/task_group.js +7 -4
  15. package/dist/beta/workflows/task_group.js.map +1 -1
  16. package/dist/inference/api_protos.d.cts +26 -26
  17. package/dist/inference/api_protos.d.ts +26 -26
  18. package/dist/inference/interruption/http_transport.cjs.map +1 -1
  19. package/dist/inference/interruption/http_transport.d.cts +3 -1
  20. package/dist/inference/interruption/http_transport.d.ts +3 -1
  21. package/dist/inference/interruption/http_transport.d.ts.map +1 -1
  22. package/dist/inference/interruption/http_transport.js.map +1 -1
  23. package/dist/inference/interruption/ws_transport.cjs +37 -32
  24. package/dist/inference/interruption/ws_transport.cjs.map +1 -1
  25. package/dist/inference/interruption/ws_transport.d.ts.map +1 -1
  26. package/dist/inference/interruption/ws_transport.js +37 -32
  27. package/dist/inference/interruption/ws_transport.js.map +1 -1
  28. package/dist/inference/tts.cjs +14 -1
  29. package/dist/inference/tts.cjs.map +1 -1
  30. package/dist/inference/tts.d.cts +42 -4
  31. package/dist/inference/tts.d.ts +42 -4
  32. package/dist/inference/tts.d.ts.map +1 -1
  33. package/dist/inference/tts.js +24 -3
  34. package/dist/inference/tts.js.map +1 -1
  35. package/dist/inference/tts.test.cjs +72 -0
  36. package/dist/inference/tts.test.cjs.map +1 -1
  37. package/dist/inference/tts.test.js +72 -0
  38. package/dist/inference/tts.test.js.map +1 -1
  39. package/dist/ipc/job_proc_lazy_main.cjs +7 -2
  40. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
  41. package/dist/ipc/job_proc_lazy_main.js +7 -2
  42. package/dist/ipc/job_proc_lazy_main.js.map +1 -1
  43. package/dist/ipc/supervised_proc.cjs +4 -1
  44. package/dist/ipc/supervised_proc.cjs.map +1 -1
  45. package/dist/ipc/supervised_proc.d.ts.map +1 -1
  46. package/dist/ipc/supervised_proc.js +4 -1
  47. package/dist/ipc/supervised_proc.js.map +1 -1
  48. package/dist/ipc/supervised_proc.test.cjs +82 -0
  49. package/dist/ipc/supervised_proc.test.cjs.map +1 -1
  50. package/dist/ipc/supervised_proc.test.js +82 -0
  51. package/dist/ipc/supervised_proc.test.js.map +1 -1
  52. package/dist/job.cjs +2 -1
  53. package/dist/job.cjs.map +1 -1
  54. package/dist/job.d.ts.map +1 -1
  55. package/dist/job.js +2 -1
  56. package/dist/job.js.map +1 -1
  57. package/dist/llm/chat_context.cjs +102 -31
  58. package/dist/llm/chat_context.cjs.map +1 -1
  59. package/dist/llm/chat_context.d.ts.map +1 -1
  60. package/dist/llm/chat_context.js +102 -31
  61. package/dist/llm/chat_context.js.map +1 -1
  62. package/dist/llm/chat_context.test.cjs +123 -5
  63. package/dist/llm/chat_context.test.cjs.map +1 -1
  64. package/dist/llm/chat_context.test.js +123 -5
  65. package/dist/llm/chat_context.test.js.map +1 -1
  66. package/dist/llm/fallback_adapter.cjs +2 -0
  67. package/dist/llm/fallback_adapter.cjs.map +1 -1
  68. package/dist/llm/fallback_adapter.d.ts.map +1 -1
  69. package/dist/llm/fallback_adapter.js +2 -0
  70. package/dist/llm/fallback_adapter.js.map +1 -1
  71. package/dist/llm/index.cjs +2 -0
  72. package/dist/llm/index.cjs.map +1 -1
  73. package/dist/llm/index.d.cts +1 -1
  74. package/dist/llm/index.d.ts +1 -1
  75. package/dist/llm/index.d.ts.map +1 -1
  76. package/dist/llm/index.js +2 -0
  77. package/dist/llm/index.js.map +1 -1
  78. package/dist/llm/utils.cjs +89 -0
  79. package/dist/llm/utils.cjs.map +1 -1
  80. package/dist/llm/utils.d.cts +8 -0
  81. package/dist/llm/utils.d.ts +8 -0
  82. package/dist/llm/utils.d.ts.map +1 -1
  83. package/dist/llm/utils.js +88 -0
  84. package/dist/llm/utils.js.map +1 -1
  85. package/dist/llm/utils.test.cjs +90 -0
  86. package/dist/llm/utils.test.cjs.map +1 -1
  87. package/dist/llm/utils.test.js +98 -2
  88. package/dist/llm/utils.test.js.map +1 -1
  89. package/dist/stt/stt.cjs +8 -0
  90. package/dist/stt/stt.cjs.map +1 -1
  91. package/dist/stt/stt.d.cts +8 -0
  92. package/dist/stt/stt.d.ts +8 -0
  93. package/dist/stt/stt.d.ts.map +1 -1
  94. package/dist/stt/stt.js +8 -0
  95. package/dist/stt/stt.js.map +1 -1
  96. package/dist/tts/fallback_adapter.cjs +6 -0
  97. package/dist/tts/fallback_adapter.cjs.map +1 -1
  98. package/dist/tts/fallback_adapter.d.ts.map +1 -1
  99. package/dist/tts/fallback_adapter.js +6 -0
  100. package/dist/tts/fallback_adapter.js.map +1 -1
  101. package/dist/typed_promise.cjs +48 -0
  102. package/dist/typed_promise.cjs.map +1 -0
  103. package/dist/typed_promise.d.cts +24 -0
  104. package/dist/typed_promise.d.ts +24 -0
  105. package/dist/typed_promise.d.ts.map +1 -0
  106. package/dist/typed_promise.js +28 -0
  107. package/dist/typed_promise.js.map +1 -0
  108. package/dist/utils.cjs +30 -2
  109. package/dist/utils.cjs.map +1 -1
  110. package/dist/utils.d.cts +18 -0
  111. package/dist/utils.d.ts +18 -0
  112. package/dist/utils.d.ts.map +1 -1
  113. package/dist/utils.js +27 -2
  114. package/dist/utils.js.map +1 -1
  115. package/dist/version.cjs +1 -1
  116. package/dist/version.js +1 -1
  117. package/dist/voice/agent_activity.cjs +10 -0
  118. package/dist/voice/agent_activity.cjs.map +1 -1
  119. package/dist/voice/agent_activity.d.ts.map +1 -1
  120. package/dist/voice/agent_activity.js +11 -0
  121. package/dist/voice/agent_activity.js.map +1 -1
  122. package/dist/voice/agent_session.cjs +1 -1
  123. package/dist/voice/agent_session.cjs.map +1 -1
  124. package/dist/voice/agent_session.d.cts +4 -2
  125. package/dist/voice/agent_session.d.ts +4 -2
  126. package/dist/voice/agent_session.d.ts.map +1 -1
  127. package/dist/voice/agent_session.js +1 -1
  128. package/dist/voice/agent_session.js.map +1 -1
  129. package/dist/voice/events.cjs +11 -0
  130. package/dist/voice/events.cjs.map +1 -1
  131. package/dist/voice/events.d.cts +12 -1
  132. package/dist/voice/events.d.ts +12 -1
  133. package/dist/voice/events.d.ts.map +1 -1
  134. package/dist/voice/events.js +10 -0
  135. package/dist/voice/events.js.map +1 -1
  136. package/dist/voice/generation.cjs +23 -4
  137. package/dist/voice/generation.cjs.map +1 -1
  138. package/dist/voice/generation.d.ts.map +1 -1
  139. package/dist/voice/generation.js +32 -5
  140. package/dist/voice/generation.js.map +1 -1
  141. package/dist/voice/generation_tts_timeout.test.cjs +85 -0
  142. package/dist/voice/generation_tts_timeout.test.cjs.map +1 -0
  143. package/dist/voice/generation_tts_timeout.test.js +84 -0
  144. package/dist/voice/generation_tts_timeout.test.js.map +1 -0
  145. package/dist/voice/index.cjs.map +1 -1
  146. package/dist/voice/index.d.cts +1 -1
  147. package/dist/voice/index.d.ts +1 -1
  148. package/dist/voice/index.d.ts.map +1 -1
  149. package/dist/voice/index.js +3 -1
  150. package/dist/voice/index.js.map +1 -1
  151. package/dist/voice/recorder_io/recorder_io.cjs +1 -2
  152. package/dist/voice/recorder_io/recorder_io.cjs.map +1 -1
  153. package/dist/voice/recorder_io/recorder_io.d.ts.map +1 -1
  154. package/dist/voice/recorder_io/recorder_io.js +2 -3
  155. package/dist/voice/recorder_io/recorder_io.js.map +1 -1
  156. package/dist/voice/report.cjs +1 -1
  157. package/dist/voice/report.cjs.map +1 -1
  158. package/dist/voice/report.js +1 -1
  159. package/dist/voice/report.js.map +1 -1
  160. package/dist/voice/report.test.cjs +70 -0
  161. package/dist/voice/report.test.cjs.map +1 -1
  162. package/dist/voice/report.test.js +70 -0
  163. package/dist/voice/report.test.js.map +1 -1
  164. package/dist/voice/room_io/room_io.cjs +5 -1
  165. package/dist/voice/room_io/room_io.cjs.map +1 -1
  166. package/dist/voice/room_io/room_io.d.ts.map +1 -1
  167. package/dist/voice/room_io/room_io.js +5 -1
  168. package/dist/voice/room_io/room_io.js.map +1 -1
  169. package/dist/voice/room_io/room_io.test.cjs +18 -0
  170. package/dist/voice/room_io/room_io.test.cjs.map +1 -0
  171. package/dist/voice/room_io/room_io.test.js +17 -0
  172. package/dist/voice/room_io/room_io.test.js.map +1 -0
  173. package/package.json +4 -2
  174. package/src/_exceptions.ts +5 -0
  175. package/src/audio.ts +12 -1
  176. package/src/beta/workflows/task_group.ts +14 -5
  177. package/src/inference/interruption/http_transport.ts +2 -1
  178. package/src/inference/interruption/ws_transport.ts +44 -34
  179. package/src/inference/tts.test.ts +87 -0
  180. package/src/inference/tts.ts +71 -9
  181. package/src/ipc/job_proc_lazy_main.ts +7 -2
  182. package/src/ipc/supervised_proc.test.ts +96 -0
  183. package/src/ipc/supervised_proc.ts +8 -1
  184. package/src/job.ts +1 -0
  185. package/src/llm/chat_context.test.ts +137 -5
  186. package/src/llm/chat_context.ts +119 -38
  187. package/src/llm/fallback_adapter.ts +5 -2
  188. package/src/llm/index.ts +2 -0
  189. package/src/llm/utils.test.ts +103 -2
  190. package/src/llm/utils.ts +128 -0
  191. package/src/stt/stt.ts +9 -1
  192. package/src/tts/fallback_adapter.ts +9 -2
  193. package/src/typed_promise.ts +67 -0
  194. package/src/utils.ts +45 -2
  195. package/src/voice/agent_activity.ts +11 -0
  196. package/src/voice/agent_session.ts +13 -7
  197. package/src/voice/events.ts +21 -0
  198. package/src/voice/generation.ts +35 -8
  199. package/src/voice/generation_tts_timeout.test.ts +112 -0
  200. package/src/voice/index.ts +6 -1
  201. package/src/voice/recorder_io/recorder_io.ts +2 -7
  202. package/src/voice/report.test.ts +78 -0
  203. package/src/voice/report.ts +1 -1
  204. package/src/voice/room_io/room_io.test.ts +38 -0
  205. package/src/voice/room_io/room_io.ts +7 -2
@@ -2,7 +2,12 @@
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  export { Agent, AgentTask, StopResponse, type AgentOptions, type ModelSettings } from './agent.js';
5
- export { AgentSession, type AgentSessionOptions, type VoiceOptions } from './agent_session.js';
5
+ export {
6
+ AgentSession,
7
+ type AgentSessionOptions,
8
+ type AgentSessionUsage,
9
+ type VoiceOptions,
10
+ } from './agent_session.js';
6
11
  export * from './avatar/index.js';
7
12
  export * from './background_audio.js';
8
13
  export {
@@ -13,7 +13,7 @@ import { TransformStream } from 'node:stream/web';
13
13
  import { log } from '../../log.js';
14
14
  import { isStreamReaderReleaseError } from '../../stream/deferred_stream.js';
15
15
  import { type StreamChannel, createStreamChannel } from '../../stream/stream_channel.js';
16
- import { Future, Task, cancelAndWait, delay } from '../../utils.js';
16
+ import { Future, Task, cancelAndWait, delay, isFfmpegTeardownError } from '../../utils.js';
17
17
  import type { AgentSession } from '../agent_session.js';
18
18
  import { AudioInput, AudioOutput, type PlaybackFinishedEvent } from '../io.js';
19
19
 
@@ -203,12 +203,7 @@ export class RecorderIO {
203
203
  })
204
204
  .on('error', (err) => {
205
205
  // Ignore errors from intentional stream closure or SIGINT during shutdown
206
- if (
207
- err.message?.includes('Output stream closed') ||
208
- err.message?.includes('received signal 2') ||
209
- err.message?.includes('SIGKILL') ||
210
- err.message?.includes('SIGINT')
211
- ) {
206
+ if (isFfmpegTeardownError(err)) {
212
207
  resolve();
213
208
  } else {
214
209
  this.logger.error({ err }, 'FFmpeg encoding error');
@@ -3,7 +3,10 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { describe, expect, it } from 'vitest';
5
5
  import { ChatContext } from '../llm/chat_context.js';
6
+ import type { ModelUsage } from '../metrics/model_usage.js';
6
7
  import type { AgentSessionOptions, VoiceOptions } from './agent_session.js';
8
+ import { AgentSessionEventTypes, createSessionUsageUpdatedEvent } from './events.js';
9
+ import type { AgentSessionUsage } from './index.js';
7
10
  import { createSessionReport, sessionReportToJSON } from './report.js';
8
11
 
9
12
  type ReportOptions = AgentSessionOptions & Partial<VoiceOptions>;
@@ -133,4 +136,79 @@ describe('sessionReportToJSON', () => {
133
136
  max_tool_steps: 3,
134
137
  });
135
138
  });
139
+
140
+ it('serializes model usage as usage', () => {
141
+ const usage: ModelUsage[] = [
142
+ {
143
+ type: 'tts_usage',
144
+ provider: 'elevenlabs',
145
+ model: 'eleven_flash_v2_5',
146
+ inputTokens: 0,
147
+ outputTokens: 0,
148
+ charactersCount: 42,
149
+ audioDurationMs: 1200,
150
+ },
151
+ ];
152
+
153
+ const report = createSessionReport({
154
+ jobId: 'job',
155
+ roomId: 'room-id',
156
+ room: 'room',
157
+ options: baseOptions(),
158
+ events: [],
159
+ chatHistory: ChatContext.empty(),
160
+ enableRecording: false,
161
+ timestamp: 0,
162
+ startedAt: 0,
163
+ modelUsage: usage,
164
+ });
165
+
166
+ const payload = sessionReportToJSON(report);
167
+ expect(payload.usage).toEqual([
168
+ {
169
+ type: 'tts_usage',
170
+ provider: 'elevenlabs',
171
+ model: 'eleven_flash_v2_5',
172
+ charactersCount: 42,
173
+ audioDurationMs: 1200,
174
+ },
175
+ ]);
176
+ });
177
+
178
+ it('omits session usage update events from serialized events', () => {
179
+ const report = createSessionReport({
180
+ jobId: 'job',
181
+ roomId: 'room-id',
182
+ room: 'room',
183
+ options: baseOptions(),
184
+ events: [
185
+ createSessionUsageUpdatedEvent({
186
+ usage: {
187
+ modelUsage: [
188
+ {
189
+ type: 'tts_usage',
190
+ provider: 'elevenlabs',
191
+ model: 'eleven_flash_v2_5',
192
+ },
193
+ ],
194
+ },
195
+ createdAt: 123,
196
+ }),
197
+ ],
198
+ chatHistory: ChatContext.empty(),
199
+ enableRecording: false,
200
+ timestamp: 0,
201
+ startedAt: 0,
202
+ });
203
+
204
+ const payload = sessionReportToJSON(report);
205
+ expect(payload.events).toEqual([]);
206
+ });
207
+
208
+ it('exports AgentSessionUsage from the voice barrel', () => {
209
+ const usage: AgentSessionUsage = { modelUsage: [] };
210
+ const eventType: AgentSessionEventTypes = AgentSessionEventTypes.SessionUsageUpdated;
211
+ expect(usage.modelUsage).toEqual([]);
212
+ expect(eventType).toBe('session_usage_updated');
213
+ });
136
214
  });
@@ -111,7 +111,7 @@ export function sessionReportToJSON(report: SessionReport): Record<string, unkno
111
111
  options.voiceOptions?.maxEndpointingDelay;
112
112
 
113
113
  for (const event of report.events) {
114
- if (event.type === 'metrics_collected') {
114
+ if (event.type === 'metrics_collected' || event.type === 'session_usage_updated') {
115
115
  continue; // metrics are too noisy, Cloud is using the chat_history as the source of truth
116
116
  }
117
117
 
@@ -0,0 +1,38 @@
1
+ // SPDX-FileCopyrightText: 2026 LiveKit, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import { describe, expect, it } from 'vitest';
5
+ import { IdentityTransform } from '../../stream/identity_transform.js';
6
+
7
+ /**
8
+ * Regression tests proving WritableStream.close() rejects when the writer is
9
+ * already closed or errored — the exact scenario RoomIO.close() guards against
10
+ * with a try/catch.
11
+ *
12
+ * RoomIO holds a WritableStreamDefaultWriter for user transcript forwarding.
13
+ * During teardown, the writer may already be closed or errored (e.g. a
14
+ * concurrent write failed during speech interruption). Without the guard,
15
+ * close() throws ERR_INVALID_STATE and crashes teardown.
16
+ */
17
+ describe('RoomIO WritableStream close guard', () => {
18
+ it('should reject when closing an already-closed writer', async () => {
19
+ const transform = new IdentityTransform<string>();
20
+ const writer = transform.writable.getWriter();
21
+
22
+ await writer.close();
23
+
24
+ // Proves the bug: second close() rejects — RoomIO.close() must guard this.
25
+ await expect(writer.close()).rejects.toThrow();
26
+ });
27
+
28
+ it('should reject when closing a writer on an errored stream', async () => {
29
+ const transform = new IdentityTransform<string>();
30
+ const writer = transform.writable.getWriter();
31
+
32
+ // Force the stream into an errored state
33
+ await writer.abort(new Error('simulated write failure'));
34
+
35
+ // Proves the bug: close() on errored writer rejects — RoomIO.close() must guard this.
36
+ await expect(writer.close()).rejects.toThrow();
37
+ });
38
+ });
@@ -529,8 +529,13 @@ export class RoomIO {
529
529
  await this.initTask?.cancelAndWait();
530
530
 
531
531
  // Close stream FIRST so reader.read() in forwardUserTranscript can exit.
532
- // This is a workaround for a race condition in the stream API.
533
- this.userTranscriptWriter.close();
532
+ // Writer may already be closed or errored if a concurrent write failed
533
+ // during session teardown (e.g. speech interruption race). Safe to ignore.
534
+ try {
535
+ await this.userTranscriptWriter.close();
536
+ } catch (e) {
537
+ this.logger.debug({ error: e }, 'userTranscriptWriter already closed or errored');
538
+ }
534
539
  await this.forwardUserTranscriptTask?.cancelAndWait();
535
540
 
536
541
  await this.audioInput?.close();