@livekit/agents 1.0.17 → 1.0.19

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 (216) hide show
  1. package/dist/index.cjs +3 -0
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +2 -1
  4. package/dist/index.d.ts +2 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/inference/api_protos.d.cts +12 -12
  9. package/dist/inference/api_protos.d.ts +12 -12
  10. package/dist/inference/llm.cjs +35 -13
  11. package/dist/inference/llm.cjs.map +1 -1
  12. package/dist/inference/llm.d.cts +10 -5
  13. package/dist/inference/llm.d.ts +10 -5
  14. package/dist/inference/llm.d.ts.map +1 -1
  15. package/dist/inference/llm.js +35 -13
  16. package/dist/inference/llm.js.map +1 -1
  17. package/dist/inference/tts.cjs +1 -1
  18. package/dist/inference/tts.cjs.map +1 -1
  19. package/dist/inference/tts.js +1 -1
  20. package/dist/inference/tts.js.map +1 -1
  21. package/dist/ipc/job_proc_lazy_main.cjs +6 -2
  22. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
  23. package/dist/ipc/job_proc_lazy_main.js +6 -2
  24. package/dist/ipc/job_proc_lazy_main.js.map +1 -1
  25. package/dist/job.cjs +31 -0
  26. package/dist/job.cjs.map +1 -1
  27. package/dist/job.d.cts +6 -0
  28. package/dist/job.d.ts +6 -0
  29. package/dist/job.d.ts.map +1 -1
  30. package/dist/job.js +31 -0
  31. package/dist/job.js.map +1 -1
  32. package/dist/llm/chat_context.cjs +33 -0
  33. package/dist/llm/chat_context.cjs.map +1 -1
  34. package/dist/llm/chat_context.d.cts +22 -2
  35. package/dist/llm/chat_context.d.ts +22 -2
  36. package/dist/llm/chat_context.d.ts.map +1 -1
  37. package/dist/llm/chat_context.js +32 -0
  38. package/dist/llm/chat_context.js.map +1 -1
  39. package/dist/llm/index.cjs +2 -0
  40. package/dist/llm/index.cjs.map +1 -1
  41. package/dist/llm/index.d.cts +1 -1
  42. package/dist/llm/index.d.ts +1 -1
  43. package/dist/llm/index.d.ts.map +1 -1
  44. package/dist/llm/index.js +2 -0
  45. package/dist/llm/index.js.map +1 -1
  46. package/dist/llm/llm.cjs.map +1 -1
  47. package/dist/llm/llm.d.cts +1 -1
  48. package/dist/llm/llm.d.ts +1 -1
  49. package/dist/llm/llm.d.ts.map +1 -1
  50. package/dist/llm/llm.js.map +1 -1
  51. package/dist/llm/provider_format/google.cjs.map +1 -1
  52. package/dist/llm/provider_format/google.d.cts +1 -1
  53. package/dist/llm/provider_format/google.d.ts +1 -1
  54. package/dist/llm/provider_format/google.d.ts.map +1 -1
  55. package/dist/llm/provider_format/google.js.map +1 -1
  56. package/dist/llm/provider_format/google.test.cjs +48 -0
  57. package/dist/llm/provider_format/google.test.cjs.map +1 -1
  58. package/dist/llm/provider_format/google.test.js +54 -1
  59. package/dist/llm/provider_format/google.test.js.map +1 -1
  60. package/dist/llm/provider_format/index.d.cts +1 -1
  61. package/dist/llm/provider_format/index.d.ts +1 -1
  62. package/dist/llm/provider_format/index.d.ts.map +1 -1
  63. package/dist/llm/provider_format/openai.cjs +1 -2
  64. package/dist/llm/provider_format/openai.cjs.map +1 -1
  65. package/dist/llm/provider_format/openai.js +1 -2
  66. package/dist/llm/provider_format/openai.js.map +1 -1
  67. package/dist/llm/provider_format/openai.test.cjs +32 -0
  68. package/dist/llm/provider_format/openai.test.cjs.map +1 -1
  69. package/dist/llm/provider_format/openai.test.js +38 -1
  70. package/dist/llm/provider_format/openai.test.js.map +1 -1
  71. package/dist/llm/realtime.cjs.map +1 -1
  72. package/dist/llm/realtime.d.cts +4 -0
  73. package/dist/llm/realtime.d.ts +4 -0
  74. package/dist/llm/realtime.d.ts.map +1 -1
  75. package/dist/llm/realtime.js.map +1 -1
  76. package/dist/llm/utils.cjs +2 -2
  77. package/dist/llm/utils.cjs.map +1 -1
  78. package/dist/llm/utils.d.cts +1 -1
  79. package/dist/llm/utils.d.ts +1 -1
  80. package/dist/llm/utils.d.ts.map +1 -1
  81. package/dist/llm/utils.js +2 -2
  82. package/dist/llm/utils.js.map +1 -1
  83. package/dist/llm/zod-utils.cjs +6 -3
  84. package/dist/llm/zod-utils.cjs.map +1 -1
  85. package/dist/llm/zod-utils.d.cts +1 -1
  86. package/dist/llm/zod-utils.d.ts +1 -1
  87. package/dist/llm/zod-utils.d.ts.map +1 -1
  88. package/dist/llm/zod-utils.js +6 -3
  89. package/dist/llm/zod-utils.js.map +1 -1
  90. package/dist/llm/zod-utils.test.cjs +83 -0
  91. package/dist/llm/zod-utils.test.cjs.map +1 -1
  92. package/dist/llm/zod-utils.test.js +83 -0
  93. package/dist/llm/zod-utils.test.js.map +1 -1
  94. package/dist/log.cjs.map +1 -1
  95. package/dist/log.d.ts.map +1 -1
  96. package/dist/log.js.map +1 -1
  97. package/dist/telemetry/index.cjs +51 -0
  98. package/dist/telemetry/index.cjs.map +1 -0
  99. package/dist/telemetry/index.d.cts +4 -0
  100. package/dist/telemetry/index.d.ts +4 -0
  101. package/dist/telemetry/index.d.ts.map +1 -0
  102. package/dist/telemetry/index.js +12 -0
  103. package/dist/telemetry/index.js.map +1 -0
  104. package/dist/telemetry/trace_types.cjs +191 -0
  105. package/dist/telemetry/trace_types.cjs.map +1 -0
  106. package/dist/telemetry/trace_types.d.cts +56 -0
  107. package/dist/telemetry/trace_types.d.ts +56 -0
  108. package/dist/telemetry/trace_types.d.ts.map +1 -0
  109. package/dist/telemetry/trace_types.js +113 -0
  110. package/dist/telemetry/trace_types.js.map +1 -0
  111. package/dist/telemetry/traces.cjs +196 -0
  112. package/dist/telemetry/traces.cjs.map +1 -0
  113. package/dist/telemetry/traces.d.cts +97 -0
  114. package/dist/telemetry/traces.d.ts +97 -0
  115. package/dist/telemetry/traces.d.ts.map +1 -0
  116. package/dist/telemetry/traces.js +173 -0
  117. package/dist/telemetry/traces.js.map +1 -0
  118. package/dist/telemetry/utils.cjs +86 -0
  119. package/dist/telemetry/utils.cjs.map +1 -0
  120. package/dist/telemetry/utils.d.cts +5 -0
  121. package/dist/telemetry/utils.d.ts +5 -0
  122. package/dist/telemetry/utils.d.ts.map +1 -0
  123. package/dist/telemetry/utils.js +51 -0
  124. package/dist/telemetry/utils.js.map +1 -0
  125. package/dist/tts/tts.cjs.map +1 -1
  126. package/dist/tts/tts.d.ts.map +1 -1
  127. package/dist/tts/tts.js.map +1 -1
  128. package/dist/utils.cjs.map +1 -1
  129. package/dist/utils.d.cts +7 -0
  130. package/dist/utils.d.ts +7 -0
  131. package/dist/utils.d.ts.map +1 -1
  132. package/dist/utils.js.map +1 -1
  133. package/dist/voice/agent.cjs +15 -0
  134. package/dist/voice/agent.cjs.map +1 -1
  135. package/dist/voice/agent.d.cts +4 -1
  136. package/dist/voice/agent.d.ts +4 -1
  137. package/dist/voice/agent.d.ts.map +1 -1
  138. package/dist/voice/agent.js +15 -0
  139. package/dist/voice/agent.js.map +1 -1
  140. package/dist/voice/agent_activity.cjs +71 -20
  141. package/dist/voice/agent_activity.cjs.map +1 -1
  142. package/dist/voice/agent_activity.d.ts.map +1 -1
  143. package/dist/voice/agent_activity.js +71 -20
  144. package/dist/voice/agent_activity.js.map +1 -1
  145. package/dist/voice/agent_session.cjs +69 -2
  146. package/dist/voice/agent_session.cjs.map +1 -1
  147. package/dist/voice/agent_session.d.cts +11 -2
  148. package/dist/voice/agent_session.d.ts +11 -2
  149. package/dist/voice/agent_session.d.ts.map +1 -1
  150. package/dist/voice/agent_session.js +70 -3
  151. package/dist/voice/agent_session.js.map +1 -1
  152. package/dist/voice/audio_recognition.cjs.map +1 -1
  153. package/dist/voice/audio_recognition.d.ts.map +1 -1
  154. package/dist/voice/audio_recognition.js.map +1 -1
  155. package/dist/voice/generation.cjs.map +1 -1
  156. package/dist/voice/generation.d.ts.map +1 -1
  157. package/dist/voice/generation.js.map +1 -1
  158. package/dist/voice/index.cjs +2 -0
  159. package/dist/voice/index.cjs.map +1 -1
  160. package/dist/voice/index.d.cts +1 -0
  161. package/dist/voice/index.d.ts +1 -0
  162. package/dist/voice/index.d.ts.map +1 -1
  163. package/dist/voice/index.js +1 -0
  164. package/dist/voice/index.js.map +1 -1
  165. package/dist/voice/interruption_detection.test.cjs +114 -0
  166. package/dist/voice/interruption_detection.test.cjs.map +1 -0
  167. package/dist/voice/interruption_detection.test.js +113 -0
  168. package/dist/voice/interruption_detection.test.js.map +1 -0
  169. package/dist/voice/report.cjs +69 -0
  170. package/dist/voice/report.cjs.map +1 -0
  171. package/dist/voice/report.d.cts +26 -0
  172. package/dist/voice/report.d.ts +26 -0
  173. package/dist/voice/report.d.ts.map +1 -0
  174. package/dist/voice/report.js +44 -0
  175. package/dist/voice/report.js.map +1 -0
  176. package/dist/voice/room_io/room_io.cjs +3 -0
  177. package/dist/voice/room_io/room_io.cjs.map +1 -1
  178. package/dist/voice/room_io/room_io.d.cts +1 -0
  179. package/dist/voice/room_io/room_io.d.ts +1 -0
  180. package/dist/voice/room_io/room_io.d.ts.map +1 -1
  181. package/dist/voice/room_io/room_io.js +3 -0
  182. package/dist/voice/room_io/room_io.js.map +1 -1
  183. package/package.json +12 -5
  184. package/src/index.ts +2 -1
  185. package/src/inference/llm.ts +53 -21
  186. package/src/inference/tts.ts +1 -1
  187. package/src/ipc/job_proc_lazy_main.ts +10 -2
  188. package/src/job.ts +48 -0
  189. package/src/llm/__snapshots__/zod-utils.test.ts.snap +218 -0
  190. package/src/llm/chat_context.ts +53 -1
  191. package/src/llm/index.ts +1 -0
  192. package/src/llm/llm.ts +3 -1
  193. package/src/llm/provider_format/google.test.ts +72 -1
  194. package/src/llm/provider_format/google.ts +4 -4
  195. package/src/llm/provider_format/openai.test.ts +55 -1
  196. package/src/llm/provider_format/openai.ts +3 -2
  197. package/src/llm/realtime.ts +8 -1
  198. package/src/llm/utils.ts +7 -2
  199. package/src/llm/zod-utils.test.ts +101 -0
  200. package/src/llm/zod-utils.ts +12 -3
  201. package/src/log.ts +1 -0
  202. package/src/telemetry/index.ts +10 -0
  203. package/src/telemetry/trace_types.ts +88 -0
  204. package/src/telemetry/traces.ts +266 -0
  205. package/src/telemetry/utils.ts +61 -0
  206. package/src/tts/tts.ts +4 -0
  207. package/src/utils.ts +17 -0
  208. package/src/voice/agent.ts +22 -0
  209. package/src/voice/agent_activity.ts +102 -24
  210. package/src/voice/agent_session.ts +98 -1
  211. package/src/voice/audio_recognition.ts +2 -0
  212. package/src/voice/generation.ts +3 -0
  213. package/src/voice/index.ts +1 -0
  214. package/src/voice/interruption_detection.test.ts +151 -0
  215. package/src/voice/report.ts +77 -0
  216. package/src/voice/room_io/room_io.ts +4 -0
@@ -38,7 +38,8 @@ const defaultVoiceOptions = {
38
38
  minEndpointingDelay: 500,
39
39
  maxEndpointingDelay: 6e3,
40
40
  maxToolSteps: 3,
41
- preemptiveGeneration: false
41
+ preemptiveGeneration: false,
42
+ userAwayTimeout: 15
42
43
  };
43
44
  class AgentSession extends import_node_events.EventEmitter {
44
45
  vad;
@@ -60,6 +61,9 @@ class AgentSession extends import_node_events.EventEmitter {
60
61
  _input;
61
62
  _output;
62
63
  closingTask = null;
64
+ userAwayTimer = null;
65
+ /** @internal */
66
+ _recordedEvents = [];
63
67
  constructor(opts) {
64
68
  super();
65
69
  const {
@@ -93,6 +97,12 @@ class AgentSession extends import_node_events.EventEmitter {
93
97
  this._output = new import_io.AgentOutput(this.onAudioOutputChanged, this.onTextOutputChanged);
94
98
  this._chatCtx = import_chat_context.ChatContext.empty();
95
99
  this.options = { ...defaultVoiceOptions, ...voiceOptions };
100
+ this.on(import_events.AgentSessionEventTypes.UserInputTranscribed, this._onUserInputTranscribed.bind(this));
101
+ }
102
+ emit(event, ...args) {
103
+ const eventData = args[0];
104
+ this._recordedEvents.push(eventData);
105
+ return super.emit(event, ...args);
96
106
  }
97
107
  get input() {
98
108
  return this._input;
@@ -113,10 +123,14 @@ class AgentSession extends import_node_events.EventEmitter {
113
123
  this._userData = value;
114
124
  }
115
125
  async start({
126
+ // TODO(brian): PR2 - Add setupCloudTracer() call if on LiveKit Cloud with recording enabled
127
+ // TODO(brian): PR3 - Add span: this._sessionSpan = tracer.startSpan('agent_session'), store as instance property
128
+ // TODO(brian): PR4 - Add setupCloudLogger() call in setupCloudTracer() to setup OTEL logging with Pino bridge
116
129
  agent,
117
130
  room,
118
131
  inputOptions,
119
- outputOptions
132
+ outputOptions,
133
+ record = true
120
134
  }) {
121
135
  if (this.started) {
122
136
  return;
@@ -149,6 +163,15 @@ class AgentSession extends import_node_events.EventEmitter {
149
163
  this.logger.debug("Auto-connecting to room via job context");
150
164
  tasks.push(ctx.connect());
151
165
  }
166
+ if (record) {
167
+ if (ctx._primaryAgentSession === void 0) {
168
+ ctx._primaryAgentSession = this;
169
+ } else {
170
+ throw new Error(
171
+ "Only one `AgentSession` can be the primary at a time. If you want to ignore primary designation, use session.start(record=False)."
172
+ );
173
+ }
174
+ }
152
175
  tasks.push(this.updateActivity(this.agent));
153
176
  await Promise.allSettled(tasks);
154
177
  this.logger.debug(
@@ -208,12 +231,20 @@ class AgentSession extends import_node_events.EventEmitter {
208
231
  }
209
232
  async updateActivity(agent) {
210
233
  this.nextActivity = new import_agent_activity.AgentActivity(agent, this);
234
+ const previousActivity = this.activity;
211
235
  if (this.activity) {
212
236
  await this.activity.drain();
213
237
  await this.activity.close();
214
238
  }
215
239
  this.activity = this.nextActivity;
216
240
  this.nextActivity = void 0;
241
+ this._chatCtx.insert(
242
+ new import_chat_context.AgentHandoffItem({
243
+ oldAgentId: previousActivity == null ? void 0 : previousActivity.agent.id,
244
+ newAgentId: agent.id
245
+ })
246
+ );
247
+ this.logger.debug({ previousActivity, agent }, "Agent handoff inserted into chat context");
217
248
  await this.activity.start();
218
249
  if (this._input.audio) {
219
250
  this.activity.attachAudioInput(this._input.audio.stream);
@@ -269,6 +300,11 @@ class AgentSession extends import_node_events.EventEmitter {
269
300
  }
270
301
  const oldState = this._agentState;
271
302
  this._agentState = state;
303
+ if (state === "listening" && this.userState === "listening") {
304
+ this._setUserAwayTimer();
305
+ } else {
306
+ this._cancelUserAwayTimer();
307
+ }
272
308
  this.emit(
273
309
  import_events.AgentSessionEventTypes.AgentStateChanged,
274
310
  (0, import_events.createAgentStateChangedEvent)(oldState, state)
@@ -281,6 +317,11 @@ class AgentSession extends import_node_events.EventEmitter {
281
317
  }
282
318
  const oldState = this.userState;
283
319
  this.userState = state;
320
+ if (state === "listening" && this._agentState === "listening") {
321
+ this._setUserAwayTimer();
322
+ } else {
323
+ this._cancelUserAwayTimer();
324
+ }
284
325
  this.emit(
285
326
  import_events.AgentSessionEventTypes.UserStateChanged,
286
327
  (0, import_events.createUserStateChangedEvent)(oldState, state)
@@ -299,11 +340,37 @@ class AgentSession extends import_node_events.EventEmitter {
299
340
  }
300
341
  onTextOutputChanged() {
301
342
  }
343
+ _setUserAwayTimer() {
344
+ this._cancelUserAwayTimer();
345
+ if (this.options.userAwayTimeout === null || this.options.userAwayTimeout === void 0) {
346
+ return;
347
+ }
348
+ if (this.roomIO && !this.roomIO.isParticipantAvailable) {
349
+ return;
350
+ }
351
+ this.userAwayTimer = setTimeout(() => {
352
+ this.logger.debug("User away timeout triggered");
353
+ this._updateUserState("away");
354
+ }, this.options.userAwayTimeout * 1e3);
355
+ }
356
+ _cancelUserAwayTimer() {
357
+ if (this.userAwayTimer !== null) {
358
+ clearTimeout(this.userAwayTimer);
359
+ this.userAwayTimer = null;
360
+ }
361
+ }
362
+ _onUserInputTranscribed(ev) {
363
+ if (this.userState === "away" && ev.isFinal) {
364
+ this.logger.debug("User returned from away state due to speech input");
365
+ this._updateUserState("listening");
366
+ }
367
+ }
302
368
  async closeImpl(reason, error = null, drain = false) {
303
369
  var _a, _b, _c;
304
370
  if (!this.started) {
305
371
  return;
306
372
  }
373
+ this._cancelUserAwayTimer();
307
374
  if (this.activity) {
308
375
  if (!drain) {
309
376
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/voice/agent_session.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame, Room } from '@livekit/rtc-node';\nimport type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';\nimport { EventEmitter } from 'node:events';\nimport type { ReadableStream } from 'node:stream/web';\nimport {\n LLM as InferenceLLM,\n STT as InferenceSTT,\n TTS as InferenceTTS,\n type LLMModels,\n type STTModelString,\n type TTSModelString,\n} from '../inference/index.js';\nimport { getJobContext } from '../job.js';\nimport { ChatContext, ChatMessage } from '../llm/chat_context.js';\nimport type { LLM, RealtimeModel, RealtimeModelError, ToolChoice } from '../llm/index.js';\nimport type { LLMError } from '../llm/llm.js';\nimport { log } from '../log.js';\nimport type { STT } from '../stt/index.js';\nimport type { STTError } from '../stt/stt.js';\nimport type { TTS, TTSError } from '../tts/tts.js';\nimport type { VAD } from '../vad.js';\nimport type { Agent } from './agent.js';\nimport { AgentActivity } from './agent_activity.js';\nimport type { _TurnDetector } from './audio_recognition.js';\nimport {\n AgentSessionEventTypes,\n type AgentState,\n type AgentStateChangedEvent,\n type CloseEvent,\n CloseReason,\n type ConversationItemAddedEvent,\n type ErrorEvent,\n type FunctionToolsExecutedEvent,\n type MetricsCollectedEvent,\n type SpeechCreatedEvent,\n type UserInputTranscribedEvent,\n type UserState,\n type UserStateChangedEvent,\n createAgentStateChangedEvent,\n createCloseEvent,\n createConversationItemAddedEvent,\n createUserStateChangedEvent,\n} from './events.js';\nimport { AgentInput, AgentOutput } from './io.js';\nimport { RoomIO, type RoomInputOptions, type RoomOutputOptions } from './room_io/index.js';\nimport type { UnknownUserData } from './run_context.js';\nimport type { SpeechHandle } from './speech_handle.js';\n\nexport interface VoiceOptions {\n allowInterruptions: boolean;\n discardAudioIfUninterruptible: boolean;\n minInterruptionDuration: number;\n minInterruptionWords: number;\n minEndpointingDelay: number;\n maxEndpointingDelay: number;\n maxToolSteps: number;\n preemptiveGeneration: boolean;\n}\n\nconst defaultVoiceOptions: VoiceOptions = {\n allowInterruptions: true,\n discardAudioIfUninterruptible: true,\n minInterruptionDuration: 500,\n minInterruptionWords: 0,\n minEndpointingDelay: 500,\n maxEndpointingDelay: 6000,\n maxToolSteps: 3,\n preemptiveGeneration: false,\n} as const;\n\nexport type TurnDetectionMode = 'stt' | 'vad' | 'realtime_llm' | 'manual' | _TurnDetector;\n\nexport type AgentSessionCallbacks = {\n [AgentSessionEventTypes.UserInputTranscribed]: (ev: UserInputTranscribedEvent) => void;\n [AgentSessionEventTypes.AgentStateChanged]: (ev: AgentStateChangedEvent) => void;\n [AgentSessionEventTypes.UserStateChanged]: (ev: UserStateChangedEvent) => void;\n [AgentSessionEventTypes.ConversationItemAdded]: (ev: ConversationItemAddedEvent) => void;\n [AgentSessionEventTypes.FunctionToolsExecuted]: (ev: FunctionToolsExecutedEvent) => void;\n [AgentSessionEventTypes.MetricsCollected]: (ev: MetricsCollectedEvent) => void;\n [AgentSessionEventTypes.SpeechCreated]: (ev: SpeechCreatedEvent) => void;\n [AgentSessionEventTypes.Error]: (ev: ErrorEvent) => void;\n [AgentSessionEventTypes.Close]: (ev: CloseEvent) => void;\n};\n\nexport type AgentSessionOptions<UserData = UnknownUserData> = {\n turnDetection?: TurnDetectionMode;\n stt?: STT | STTModelString;\n vad?: VAD;\n llm?: LLM | RealtimeModel | LLMModels;\n tts?: TTS | TTSModelString;\n userData?: UserData;\n voiceOptions?: Partial<VoiceOptions>;\n};\n\nexport class AgentSession<\n UserData = UnknownUserData,\n> extends (EventEmitter as new () => TypedEmitter<AgentSessionCallbacks>) {\n vad?: VAD;\n stt?: STT;\n llm?: LLM | RealtimeModel;\n tts?: TTS;\n turnDetection?: TurnDetectionMode;\n\n readonly options: VoiceOptions;\n\n private agent?: Agent;\n private activity?: AgentActivity;\n private nextActivity?: AgentActivity;\n private started = false;\n private userState: UserState = 'listening';\n\n private roomIO?: RoomIO;\n private logger = log();\n\n private _chatCtx: ChatContext;\n private _userData: UserData | undefined;\n private _agentState: AgentState = 'initializing';\n\n private _input: AgentInput;\n private _output: AgentOutput;\n\n private closingTask: Promise<void> | null = null;\n\n constructor(opts: AgentSessionOptions<UserData>) {\n super();\n\n const {\n vad,\n stt,\n llm,\n tts,\n turnDetection,\n userData,\n voiceOptions = defaultVoiceOptions,\n } = opts;\n\n this.vad = vad;\n\n if (typeof stt === 'string') {\n this.stt = InferenceSTT.fromModelString(stt);\n } else {\n this.stt = stt;\n }\n\n if (typeof llm === 'string') {\n this.llm = InferenceLLM.fromModelString(llm);\n } else {\n this.llm = llm;\n }\n\n if (typeof tts === 'string') {\n this.tts = InferenceTTS.fromModelString(tts);\n } else {\n this.tts = tts;\n }\n\n this.turnDetection = turnDetection;\n this._userData = userData;\n\n // configurable IO\n this._input = new AgentInput(this.onAudioInputChanged);\n this._output = new AgentOutput(this.onAudioOutputChanged, this.onTextOutputChanged);\n\n // This is the \"global\" chat context, it holds the entire conversation history\n this._chatCtx = ChatContext.empty();\n this.options = { ...defaultVoiceOptions, ...voiceOptions };\n }\n\n get input(): AgentInput {\n return this._input;\n }\n\n get output(): AgentOutput {\n return this._output;\n }\n\n get userData(): UserData {\n if (this._userData === undefined) {\n throw new Error('Voice agent userData is not set');\n }\n\n return this._userData;\n }\n\n get history(): ChatContext {\n return this._chatCtx;\n }\n\n set userData(value: UserData) {\n this._userData = value;\n }\n\n async start({\n agent,\n room,\n inputOptions,\n outputOptions,\n }: {\n agent: Agent;\n room: Room;\n inputOptions?: Partial<RoomInputOptions>;\n outputOptions?: Partial<RoomOutputOptions>;\n }): Promise<void> {\n if (this.started) {\n return;\n }\n\n this.agent = agent;\n this._updateAgentState('initializing');\n\n const tasks: Promise<void>[] = [];\n // Check for existing input/output configuration and warn if needed\n if (this.input.audio && inputOptions?.audioEnabled !== false) {\n this.logger.warn('RoomIO audio input is enabled but input.audio is already set, ignoring..');\n }\n\n if (this.output.audio && outputOptions?.audioEnabled !== false) {\n this.logger.warn(\n 'RoomIO audio output is enabled but output.audio is already set, ignoring..',\n );\n }\n\n if (this.output.transcription && outputOptions?.transcriptionEnabled !== false) {\n this.logger.warn(\n 'RoomIO transcription output is enabled but output.transcription is already set, ignoring..',\n );\n }\n\n this.roomIO = new RoomIO({\n agentSession: this,\n room,\n inputOptions,\n outputOptions,\n });\n this.roomIO.start();\n\n const ctx = getJobContext();\n if (ctx && ctx.room === room && !room.isConnected) {\n this.logger.debug('Auto-connecting to room via job context');\n tasks.push(ctx.connect());\n }\n // TODO(AJS-265): add shutdown callback to job context\n tasks.push(this.updateActivity(this.agent));\n\n await Promise.allSettled(tasks);\n\n // Log used IO configuration\n this.logger.debug(\n `using audio io: ${this.input.audio ? '`' + this.input.audio.constructor.name + '`' : '(none)'} -> \\`AgentSession\\` -> ${this.output.audio ? '`' + this.output.audio.constructor.name + '`' : '(none)'}`,\n );\n\n this.logger.debug(\n `using transcript io: \\`AgentSession\\` -> ${this.output.transcription ? '`' + this.output.transcription.constructor.name + '`' : '(none)'}`,\n );\n\n this.started = true;\n this._updateAgentState('listening');\n }\n\n updateAgent(agent: Agent): void {\n this.agent = agent;\n\n if (this.started) {\n this.updateActivity(agent);\n }\n }\n\n commitUserTurn() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n this.activity.commitUserTurn();\n }\n\n clearUserTurn() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n this.activity.clearUserTurn();\n }\n\n say(\n text: string | ReadableStream<string>,\n options?: {\n audio?: ReadableStream<AudioFrame>;\n allowInterruptions?: boolean;\n addToChatCtx?: boolean;\n },\n ): SpeechHandle {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n return this.activity.say(text, options);\n }\n\n interrupt() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n return this.activity.interrupt();\n }\n\n generateReply(options?: {\n userInput?: string;\n instructions?: string;\n toolChoice?: ToolChoice;\n allowInterruptions?: boolean;\n }): SpeechHandle {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n const userMessage = options?.userInput\n ? new ChatMessage({\n role: 'user',\n content: options.userInput,\n })\n : undefined;\n\n if (this.activity.draining) {\n if (!this.nextActivity) {\n throw new Error('AgentSession is closing, cannot use generateReply()');\n }\n return this.nextActivity.generateReply({ userMessage, ...options });\n }\n\n return this.activity.generateReply({ userMessage, ...options });\n }\n\n private async updateActivity(agent: Agent): Promise<void> {\n // TODO(AJS-129): add lock to agent activity core lifecycle\n this.nextActivity = new AgentActivity(agent, this);\n\n if (this.activity) {\n await this.activity.drain();\n await this.activity.close();\n }\n\n this.activity = this.nextActivity;\n this.nextActivity = undefined;\n\n await this.activity.start();\n\n if (this._input.audio) {\n this.activity.attachAudioInput(this._input.audio.stream);\n }\n }\n\n get chatCtx(): ChatContext {\n return this._chatCtx.copy();\n }\n\n get agentState(): AgentState {\n return this._agentState;\n }\n\n get currentAgent(): Agent {\n if (!this.agent) {\n throw new Error('AgentSession is not running');\n }\n\n return this.agent;\n }\n\n async close(): Promise<void> {\n await this.closeImpl(CloseReason.USER_INITIATED);\n }\n\n /** @internal */\n _closeSoon({\n reason,\n drain = false,\n error = null,\n }: {\n reason: CloseReason;\n drain?: boolean;\n error?: RealtimeModelError | STTError | TTSError | LLMError | null;\n }): void {\n if (this.closingTask) {\n return;\n }\n this.closeImpl(reason, error, drain);\n }\n\n /** @internal */\n _onError(error: RealtimeModelError | STTError | TTSError | LLMError): void {\n if (this.closingTask || error.recoverable) {\n return;\n }\n\n this.logger.error(error, 'AgentSession is closing due to unrecoverable error');\n\n this.closingTask = (async () => {\n await this.closeImpl(CloseReason.ERROR, error);\n })().then(() => {\n this.closingTask = null;\n });\n }\n\n /** @internal */\n _conversationItemAdded(item: ChatMessage): void {\n this._chatCtx.insert(item);\n this.emit(AgentSessionEventTypes.ConversationItemAdded, createConversationItemAddedEvent(item));\n }\n\n /** @internal */\n _updateAgentState(state: AgentState) {\n if (this._agentState === state) {\n return;\n }\n\n const oldState = this._agentState;\n this._agentState = state;\n this.emit(\n AgentSessionEventTypes.AgentStateChanged,\n createAgentStateChangedEvent(oldState, state),\n );\n }\n\n /** @internal */\n _updateUserState(state: UserState, _lastSpeakingTime?: number) {\n if (this.userState === state) {\n return;\n }\n\n const oldState = this.userState;\n this.userState = state;\n this.emit(\n AgentSessionEventTypes.UserStateChanged,\n createUserStateChangedEvent(oldState, state),\n );\n }\n\n // -- User changed input/output streams/sinks --\n private onAudioInputChanged(): void {\n if (!this.started) {\n return;\n }\n\n if (this.activity && this._input.audio) {\n this.activity.attachAudioInput(this._input.audio.stream);\n }\n }\n\n private onAudioOutputChanged(): void {}\n\n private onTextOutputChanged(): void {}\n\n private async closeImpl(\n reason: CloseReason,\n error: RealtimeModelError | LLMError | TTSError | STTError | null = null,\n drain: boolean = false,\n ): Promise<void> {\n if (!this.started) {\n return;\n }\n\n if (this.activity) {\n if (!drain) {\n try {\n this.activity.interrupt();\n } catch (error) {\n // uninterruptible speech [copied from python]\n // TODO(shubhra): force interrupt or wait for it to finish?\n // it might be an audio played from the error callback\n }\n }\n await this.activity.drain();\n // wait any uninterruptible speech to finish\n await this.activity.currentSpeech?.waitForPlayout();\n this.activity.detachAudioInput();\n }\n\n // detach the inputs and outputs\n this.input.audio = null;\n this.output.audio = null;\n this.output.transcription = null;\n\n await this.roomIO?.close();\n this.roomIO = undefined;\n\n await this.activity?.close();\n this.activity = undefined;\n\n this.started = false;\n\n this.emit(AgentSessionEventTypes.Close, createCloseEvent(reason, error));\n\n this.userState = 'listening';\n this._agentState = 'initializing';\n\n this.logger.info({ reason, error }, 'AgentSession closed');\n }\n}\n"],"mappings":"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","names":["InferenceSTT","InferenceLLM","InferenceTTS","error"]}
1
+ {"version":3,"sources":["../../src/voice/agent_session.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame, Room } from '@livekit/rtc-node';\nimport type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';\nimport { EventEmitter } from 'node:events';\nimport type { ReadableStream } from 'node:stream/web';\nimport {\n LLM as InferenceLLM,\n STT as InferenceSTT,\n TTS as InferenceTTS,\n type LLMModels,\n type STTModelString,\n type TTSModelString,\n} from '../inference/index.js';\nimport { getJobContext } from '../job.js';\nimport { AgentHandoffItem, ChatContext, ChatMessage } from '../llm/chat_context.js';\nimport type { LLM, RealtimeModel, RealtimeModelError, ToolChoice } from '../llm/index.js';\nimport type { LLMError } from '../llm/llm.js';\nimport { log } from '../log.js';\nimport type { STT } from '../stt/index.js';\nimport type { STTError } from '../stt/stt.js';\nimport type { TTS, TTSError } from '../tts/tts.js';\nimport type { VAD } from '../vad.js';\nimport type { Agent } from './agent.js';\nimport { AgentActivity } from './agent_activity.js';\nimport type { _TurnDetector } from './audio_recognition.js';\nimport {\n type AgentEvent,\n AgentSessionEventTypes,\n type AgentState,\n type AgentStateChangedEvent,\n type CloseEvent,\n CloseReason,\n type ConversationItemAddedEvent,\n type ErrorEvent,\n type FunctionToolsExecutedEvent,\n type MetricsCollectedEvent,\n type SpeechCreatedEvent,\n type UserInputTranscribedEvent,\n type UserState,\n type UserStateChangedEvent,\n createAgentStateChangedEvent,\n createCloseEvent,\n createConversationItemAddedEvent,\n createUserStateChangedEvent,\n} from './events.js';\nimport { AgentInput, AgentOutput } from './io.js';\nimport { RoomIO, type RoomInputOptions, type RoomOutputOptions } from './room_io/index.js';\nimport type { UnknownUserData } from './run_context.js';\nimport type { SpeechHandle } from './speech_handle.js';\n\nexport interface VoiceOptions {\n allowInterruptions: boolean;\n discardAudioIfUninterruptible: boolean;\n minInterruptionDuration: number;\n minInterruptionWords: number;\n minEndpointingDelay: number;\n maxEndpointingDelay: number;\n maxToolSteps: number;\n preemptiveGeneration: boolean;\n userAwayTimeout?: number | null;\n}\n\nconst defaultVoiceOptions: VoiceOptions = {\n allowInterruptions: true,\n discardAudioIfUninterruptible: true,\n minInterruptionDuration: 500,\n minInterruptionWords: 0,\n minEndpointingDelay: 500,\n maxEndpointingDelay: 6000,\n maxToolSteps: 3,\n preemptiveGeneration: false,\n userAwayTimeout: 15.0,\n} as const;\n\nexport type TurnDetectionMode = 'stt' | 'vad' | 'realtime_llm' | 'manual' | _TurnDetector;\n\nexport type AgentSessionCallbacks = {\n [AgentSessionEventTypes.UserInputTranscribed]: (ev: UserInputTranscribedEvent) => void;\n [AgentSessionEventTypes.AgentStateChanged]: (ev: AgentStateChangedEvent) => void;\n [AgentSessionEventTypes.UserStateChanged]: (ev: UserStateChangedEvent) => void;\n [AgentSessionEventTypes.ConversationItemAdded]: (ev: ConversationItemAddedEvent) => void;\n [AgentSessionEventTypes.FunctionToolsExecuted]: (ev: FunctionToolsExecutedEvent) => void;\n [AgentSessionEventTypes.MetricsCollected]: (ev: MetricsCollectedEvent) => void;\n [AgentSessionEventTypes.SpeechCreated]: (ev: SpeechCreatedEvent) => void;\n [AgentSessionEventTypes.Error]: (ev: ErrorEvent) => void;\n [AgentSessionEventTypes.Close]: (ev: CloseEvent) => void;\n};\n\nexport type AgentSessionOptions<UserData = UnknownUserData> = {\n turnDetection?: TurnDetectionMode;\n stt?: STT | STTModelString;\n vad?: VAD;\n llm?: LLM | RealtimeModel | LLMModels;\n tts?: TTS | TTSModelString;\n userData?: UserData;\n voiceOptions?: Partial<VoiceOptions>;\n};\n\nexport class AgentSession<\n UserData = UnknownUserData,\n> extends (EventEmitter as new () => TypedEmitter<AgentSessionCallbacks>) {\n vad?: VAD;\n stt?: STT;\n llm?: LLM | RealtimeModel;\n tts?: TTS;\n turnDetection?: TurnDetectionMode;\n\n readonly options: VoiceOptions;\n\n private agent?: Agent;\n private activity?: AgentActivity;\n private nextActivity?: AgentActivity;\n private started = false;\n private userState: UserState = 'listening';\n\n private roomIO?: RoomIO;\n private logger = log();\n\n private _chatCtx: ChatContext;\n private _userData: UserData | undefined;\n private _agentState: AgentState = 'initializing';\n\n private _input: AgentInput;\n private _output: AgentOutput;\n\n private closingTask: Promise<void> | null = null;\n private userAwayTimer: NodeJS.Timeout | null = null;\n\n /** @internal */\n _recordedEvents: AgentEvent[] = [];\n\n constructor(opts: AgentSessionOptions<UserData>) {\n super();\n\n const {\n vad,\n stt,\n llm,\n tts,\n turnDetection,\n userData,\n voiceOptions = defaultVoiceOptions,\n } = opts;\n\n this.vad = vad;\n\n if (typeof stt === 'string') {\n this.stt = InferenceSTT.fromModelString(stt);\n } else {\n this.stt = stt;\n }\n\n if (typeof llm === 'string') {\n this.llm = InferenceLLM.fromModelString(llm);\n } else {\n this.llm = llm;\n }\n\n if (typeof tts === 'string') {\n this.tts = InferenceTTS.fromModelString(tts);\n } else {\n this.tts = tts;\n }\n\n this.turnDetection = turnDetection;\n this._userData = userData;\n\n // configurable IO\n this._input = new AgentInput(this.onAudioInputChanged);\n this._output = new AgentOutput(this.onAudioOutputChanged, this.onTextOutputChanged);\n\n // This is the \"global\" chat context, it holds the entire conversation history\n this._chatCtx = ChatContext.empty();\n this.options = { ...defaultVoiceOptions, ...voiceOptions };\n\n this.on(AgentSessionEventTypes.UserInputTranscribed, this._onUserInputTranscribed.bind(this));\n }\n\n emit<K extends keyof AgentSessionCallbacks>(\n event: K,\n ...args: Parameters<AgentSessionCallbacks[K]>\n ): boolean {\n const eventData = args[0] as AgentEvent;\n this._recordedEvents.push(eventData);\n return super.emit(event, ...args);\n }\n\n get input(): AgentInput {\n return this._input;\n }\n\n get output(): AgentOutput {\n return this._output;\n }\n\n get userData(): UserData {\n if (this._userData === undefined) {\n throw new Error('Voice agent userData is not set');\n }\n\n return this._userData;\n }\n\n get history(): ChatContext {\n return this._chatCtx;\n }\n\n set userData(value: UserData) {\n this._userData = value;\n }\n\n async start({\n // TODO(brian): PR2 - Add setupCloudTracer() call if on LiveKit Cloud with recording enabled\n // TODO(brian): PR3 - Add span: this._sessionSpan = tracer.startSpan('agent_session'), store as instance property\n // TODO(brian): PR4 - Add setupCloudLogger() call in setupCloudTracer() to setup OTEL logging with Pino bridge\n agent,\n room,\n inputOptions,\n outputOptions,\n record = true,\n }: {\n agent: Agent;\n room: Room;\n inputOptions?: Partial<RoomInputOptions>;\n outputOptions?: Partial<RoomOutputOptions>;\n record?: boolean;\n }): Promise<void> {\n if (this.started) {\n return;\n }\n\n this.agent = agent;\n this._updateAgentState('initializing');\n\n const tasks: Promise<void>[] = [];\n // Check for existing input/output configuration and warn if needed\n if (this.input.audio && inputOptions?.audioEnabled !== false) {\n this.logger.warn('RoomIO audio input is enabled but input.audio is already set, ignoring..');\n }\n\n if (this.output.audio && outputOptions?.audioEnabled !== false) {\n this.logger.warn(\n 'RoomIO audio output is enabled but output.audio is already set, ignoring..',\n );\n }\n\n if (this.output.transcription && outputOptions?.transcriptionEnabled !== false) {\n this.logger.warn(\n 'RoomIO transcription output is enabled but output.transcription is already set, ignoring..',\n );\n }\n\n this.roomIO = new RoomIO({\n agentSession: this,\n room,\n inputOptions,\n outputOptions,\n });\n this.roomIO.start();\n\n const ctx = getJobContext();\n if (ctx && ctx.room === room && !room.isConnected) {\n this.logger.debug('Auto-connecting to room via job context');\n tasks.push(ctx.connect());\n }\n\n if (record) {\n if (ctx._primaryAgentSession === undefined) {\n ctx._primaryAgentSession = this;\n } else {\n throw new Error(\n 'Only one `AgentSession` can be the primary at a time. If you want to ignore primary designation, use session.start(record=False).',\n );\n }\n }\n\n // TODO(AJS-265): add shutdown callback to job context\n tasks.push(this.updateActivity(this.agent));\n\n await Promise.allSettled(tasks);\n\n // Log used IO configuration\n this.logger.debug(\n `using audio io: ${this.input.audio ? '`' + this.input.audio.constructor.name + '`' : '(none)'} -> \\`AgentSession\\` -> ${this.output.audio ? '`' + this.output.audio.constructor.name + '`' : '(none)'}`,\n );\n\n this.logger.debug(\n `using transcript io: \\`AgentSession\\` -> ${this.output.transcription ? '`' + this.output.transcription.constructor.name + '`' : '(none)'}`,\n );\n\n this.started = true;\n this._updateAgentState('listening');\n }\n\n updateAgent(agent: Agent): void {\n this.agent = agent;\n\n if (this.started) {\n this.updateActivity(agent);\n }\n }\n\n commitUserTurn() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n this.activity.commitUserTurn();\n }\n\n clearUserTurn() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n this.activity.clearUserTurn();\n }\n\n say(\n text: string | ReadableStream<string>,\n options?: {\n audio?: ReadableStream<AudioFrame>;\n allowInterruptions?: boolean;\n addToChatCtx?: boolean;\n },\n ): SpeechHandle {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n return this.activity.say(text, options);\n }\n\n interrupt() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n return this.activity.interrupt();\n }\n\n generateReply(options?: {\n userInput?: string;\n instructions?: string;\n toolChoice?: ToolChoice;\n allowInterruptions?: boolean;\n }): SpeechHandle {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n const userMessage = options?.userInput\n ? new ChatMessage({\n role: 'user',\n content: options.userInput,\n })\n : undefined;\n\n if (this.activity.draining) {\n if (!this.nextActivity) {\n throw new Error('AgentSession is closing, cannot use generateReply()');\n }\n return this.nextActivity.generateReply({ userMessage, ...options });\n }\n\n return this.activity.generateReply({ userMessage, ...options });\n }\n\n private async updateActivity(agent: Agent): Promise<void> {\n // TODO(AJS-129): add lock to agent activity core lifecycle\n this.nextActivity = new AgentActivity(agent, this);\n\n const previousActivity = this.activity;\n\n if (this.activity) {\n await this.activity.drain();\n await this.activity.close();\n }\n\n this.activity = this.nextActivity;\n this.nextActivity = undefined;\n\n this._chatCtx.insert(\n new AgentHandoffItem({\n oldAgentId: previousActivity?.agent.id,\n newAgentId: agent.id,\n }),\n );\n this.logger.debug({ previousActivity, agent }, 'Agent handoff inserted into chat context');\n\n await this.activity.start();\n\n if (this._input.audio) {\n this.activity.attachAudioInput(this._input.audio.stream);\n }\n }\n\n get chatCtx(): ChatContext {\n return this._chatCtx.copy();\n }\n\n get agentState(): AgentState {\n return this._agentState;\n }\n\n get currentAgent(): Agent {\n if (!this.agent) {\n throw new Error('AgentSession is not running');\n }\n\n return this.agent;\n }\n\n async close(): Promise<void> {\n await this.closeImpl(CloseReason.USER_INITIATED);\n }\n\n /** @internal */\n _closeSoon({\n reason,\n drain = false,\n error = null,\n }: {\n reason: CloseReason;\n drain?: boolean;\n error?: RealtimeModelError | STTError | TTSError | LLMError | null;\n }): void {\n if (this.closingTask) {\n return;\n }\n this.closeImpl(reason, error, drain);\n }\n\n /** @internal */\n _onError(error: RealtimeModelError | STTError | TTSError | LLMError): void {\n if (this.closingTask || error.recoverable) {\n return;\n }\n\n this.logger.error(error, 'AgentSession is closing due to unrecoverable error');\n\n this.closingTask = (async () => {\n await this.closeImpl(CloseReason.ERROR, error);\n })().then(() => {\n this.closingTask = null;\n });\n }\n\n /** @internal */\n _conversationItemAdded(item: ChatMessage): void {\n this._chatCtx.insert(item);\n this.emit(AgentSessionEventTypes.ConversationItemAdded, createConversationItemAddedEvent(item));\n }\n\n /** @internal */\n _updateAgentState(state: AgentState) {\n if (this._agentState === state) {\n return;\n }\n\n // TODO(brian): PR3 - Add span: if state === 'speaking' && !this._agentSpeakingSpan, create tracer.startSpan('agent_speaking') with participant attributes\n // TODO(brian): PR3 - Add span: if state !== 'speaking' && this._agentSpeakingSpan, end and clear this._agentSpeakingSpan\n const oldState = this._agentState;\n this._agentState = state;\n\n // Handle user away timer based on state changes\n if (state === 'listening' && this.userState === 'listening') {\n this._setUserAwayTimer();\n } else {\n this._cancelUserAwayTimer();\n }\n\n this.emit(\n AgentSessionEventTypes.AgentStateChanged,\n createAgentStateChangedEvent(oldState, state),\n );\n }\n\n /** @internal */\n _updateUserState(state: UserState, _lastSpeakingTime?: number) {\n if (this.userState === state) {\n return;\n }\n\n // TODO(brian): PR3 - Add span: if state === 'speaking' && !this._userSpeakingSpan, create tracer.startSpan('user_speaking') with participant attributes\n // TODO(brian): PR3 - Add span: if state !== 'speaking' && this._userSpeakingSpan, end and clear this._userSpeakingSpan\n const oldState = this.userState;\n this.userState = state;\n\n // Handle user away timer based on state changes\n if (state === 'listening' && this._agentState === 'listening') {\n this._setUserAwayTimer();\n } else {\n this._cancelUserAwayTimer();\n }\n\n this.emit(\n AgentSessionEventTypes.UserStateChanged,\n createUserStateChangedEvent(oldState, state),\n );\n }\n\n // -- User changed input/output streams/sinks --\n private onAudioInputChanged(): void {\n if (!this.started) {\n return;\n }\n\n if (this.activity && this._input.audio) {\n this.activity.attachAudioInput(this._input.audio.stream);\n }\n }\n\n private onAudioOutputChanged(): void {}\n\n private onTextOutputChanged(): void {}\n\n private _setUserAwayTimer(): void {\n this._cancelUserAwayTimer();\n\n if (this.options.userAwayTimeout === null || this.options.userAwayTimeout === undefined) {\n return;\n }\n\n if (this.roomIO && !this.roomIO.isParticipantAvailable) {\n return;\n }\n\n this.userAwayTimer = setTimeout(() => {\n this.logger.debug('User away timeout triggered');\n this._updateUserState('away');\n }, this.options.userAwayTimeout * 1000);\n }\n\n private _cancelUserAwayTimer(): void {\n if (this.userAwayTimer !== null) {\n clearTimeout(this.userAwayTimer);\n this.userAwayTimer = null;\n }\n }\n\n private _onUserInputTranscribed(ev: UserInputTranscribedEvent): void {\n if (this.userState === 'away' && ev.isFinal) {\n this.logger.debug('User returned from away state due to speech input');\n this._updateUserState('listening');\n }\n }\n\n private async closeImpl(\n reason: CloseReason,\n error: RealtimeModelError | LLMError | TTSError | STTError | null = null,\n drain: boolean = false,\n ): Promise<void> {\n if (!this.started) {\n return;\n }\n\n this._cancelUserAwayTimer();\n\n if (this.activity) {\n if (!drain) {\n try {\n this.activity.interrupt();\n } catch (error) {\n // uninterruptible speech [copied from python]\n // TODO(shubhra): force interrupt or wait for it to finish?\n // it might be an audio played from the error callback\n }\n }\n await this.activity.drain();\n // wait any uninterruptible speech to finish\n await this.activity.currentSpeech?.waitForPlayout();\n this.activity.detachAudioInput();\n }\n\n // detach the inputs and outputs\n this.input.audio = null;\n this.output.audio = null;\n this.output.transcription = null;\n\n await this.roomIO?.close();\n this.roomIO = undefined;\n\n await this.activity?.close();\n this.activity = undefined;\n\n this.started = false;\n\n this.emit(AgentSessionEventTypes.Close, createCloseEvent(reason, error));\n\n this.userState = 'listening';\n this._agentState = 'initializing';\n\n this.logger.info({ reason, error }, 'AgentSession closed');\n }\n}\n"],"mappings":"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","names":["InferenceSTT","InferenceLLM","InferenceTTS","error"]}
@@ -12,7 +12,7 @@ import type { TTS, TTSError } from '../tts/tts.js';
12
12
  import type { VAD } from '../vad.js';
13
13
  import type { Agent } from './agent.js';
14
14
  import type { _TurnDetector } from './audio_recognition.js';
15
- import { AgentSessionEventTypes, type AgentState, type AgentStateChangedEvent, type CloseEvent, CloseReason, type ConversationItemAddedEvent, type ErrorEvent, type FunctionToolsExecutedEvent, type MetricsCollectedEvent, type SpeechCreatedEvent, type UserInputTranscribedEvent, type UserState, type UserStateChangedEvent } from './events.js';
15
+ import { type AgentEvent, AgentSessionEventTypes, type AgentState, type AgentStateChangedEvent, type CloseEvent, CloseReason, type ConversationItemAddedEvent, type ErrorEvent, type FunctionToolsExecutedEvent, type MetricsCollectedEvent, type SpeechCreatedEvent, type UserInputTranscribedEvent, type UserState, type UserStateChangedEvent } from './events.js';
16
16
  import { AgentInput, AgentOutput } from './io.js';
17
17
  import { type RoomInputOptions, type RoomOutputOptions } from './room_io/index.js';
18
18
  import type { UnknownUserData } from './run_context.js';
@@ -26,6 +26,7 @@ export interface VoiceOptions {
26
26
  maxEndpointingDelay: number;
27
27
  maxToolSteps: number;
28
28
  preemptiveGeneration: boolean;
29
+ userAwayTimeout?: number | null;
29
30
  }
30
31
  export type TurnDetectionMode = 'stt' | 'vad' | 'realtime_llm' | 'manual' | _TurnDetector;
31
32
  export type AgentSessionCallbacks = {
@@ -69,17 +70,22 @@ export declare class AgentSession<UserData = UnknownUserData> extends AgentSessi
69
70
  private _input;
70
71
  private _output;
71
72
  private closingTask;
73
+ private userAwayTimer;
74
+ /** @internal */
75
+ _recordedEvents: AgentEvent[];
72
76
  constructor(opts: AgentSessionOptions<UserData>);
77
+ emit<K extends keyof AgentSessionCallbacks>(event: K, ...args: Parameters<AgentSessionCallbacks[K]>): boolean;
73
78
  get input(): AgentInput;
74
79
  get output(): AgentOutput;
75
80
  get userData(): UserData;
76
81
  get history(): ChatContext;
77
82
  set userData(value: UserData);
78
- start({ agent, room, inputOptions, outputOptions, }: {
83
+ start({ agent, room, inputOptions, outputOptions, record, }: {
79
84
  agent: Agent;
80
85
  room: Room;
81
86
  inputOptions?: Partial<RoomInputOptions>;
82
87
  outputOptions?: Partial<RoomOutputOptions>;
88
+ record?: boolean;
83
89
  }): Promise<void>;
84
90
  updateAgent(agent: Agent): void;
85
91
  commitUserTurn(): void;
@@ -118,6 +124,9 @@ export declare class AgentSession<UserData = UnknownUserData> extends AgentSessi
118
124
  private onAudioInputChanged;
119
125
  private onAudioOutputChanged;
120
126
  private onTextOutputChanged;
127
+ private _setUserAwayTimer;
128
+ private _cancelUserAwayTimer;
129
+ private _onUserInputTranscribed;
121
130
  private closeImpl;
122
131
  }
123
132
  export {};
@@ -12,7 +12,7 @@ import type { TTS, TTSError } from '../tts/tts.js';
12
12
  import type { VAD } from '../vad.js';
13
13
  import type { Agent } from './agent.js';
14
14
  import type { _TurnDetector } from './audio_recognition.js';
15
- import { AgentSessionEventTypes, type AgentState, type AgentStateChangedEvent, type CloseEvent, CloseReason, type ConversationItemAddedEvent, type ErrorEvent, type FunctionToolsExecutedEvent, type MetricsCollectedEvent, type SpeechCreatedEvent, type UserInputTranscribedEvent, type UserState, type UserStateChangedEvent } from './events.js';
15
+ import { type AgentEvent, AgentSessionEventTypes, type AgentState, type AgentStateChangedEvent, type CloseEvent, CloseReason, type ConversationItemAddedEvent, type ErrorEvent, type FunctionToolsExecutedEvent, type MetricsCollectedEvent, type SpeechCreatedEvent, type UserInputTranscribedEvent, type UserState, type UserStateChangedEvent } from './events.js';
16
16
  import { AgentInput, AgentOutput } from './io.js';
17
17
  import { type RoomInputOptions, type RoomOutputOptions } from './room_io/index.js';
18
18
  import type { UnknownUserData } from './run_context.js';
@@ -26,6 +26,7 @@ export interface VoiceOptions {
26
26
  maxEndpointingDelay: number;
27
27
  maxToolSteps: number;
28
28
  preemptiveGeneration: boolean;
29
+ userAwayTimeout?: number | null;
29
30
  }
30
31
  export type TurnDetectionMode = 'stt' | 'vad' | 'realtime_llm' | 'manual' | _TurnDetector;
31
32
  export type AgentSessionCallbacks = {
@@ -69,17 +70,22 @@ export declare class AgentSession<UserData = UnknownUserData> extends AgentSessi
69
70
  private _input;
70
71
  private _output;
71
72
  private closingTask;
73
+ private userAwayTimer;
74
+ /** @internal */
75
+ _recordedEvents: AgentEvent[];
72
76
  constructor(opts: AgentSessionOptions<UserData>);
77
+ emit<K extends keyof AgentSessionCallbacks>(event: K, ...args: Parameters<AgentSessionCallbacks[K]>): boolean;
73
78
  get input(): AgentInput;
74
79
  get output(): AgentOutput;
75
80
  get userData(): UserData;
76
81
  get history(): ChatContext;
77
82
  set userData(value: UserData);
78
- start({ agent, room, inputOptions, outputOptions, }: {
83
+ start({ agent, room, inputOptions, outputOptions, record, }: {
79
84
  agent: Agent;
80
85
  room: Room;
81
86
  inputOptions?: Partial<RoomInputOptions>;
82
87
  outputOptions?: Partial<RoomOutputOptions>;
88
+ record?: boolean;
83
89
  }): Promise<void>;
84
90
  updateAgent(agent: Agent): void;
85
91
  commitUserTurn(): void;
@@ -118,6 +124,9 @@ export declare class AgentSession<UserData = UnknownUserData> extends AgentSessi
118
124
  private onAudioInputChanged;
119
125
  private onAudioOutputChanged;
120
126
  private onTextOutputChanged;
127
+ private _setUserAwayTimer;
128
+ private _cancelUserAwayTimer;
129
+ private _onUserInputTranscribed;
121
130
  private closeImpl;
122
131
  }
123
132
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"agent_session.d.ts","sourceRoot":"","sources":["../../src/voice/agent_session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent_session.d.ts","sourceRoot":"","sources":["../../src/voice/agent_session.ts"],"names":[],"mappings":";AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAoB,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACpF,OAAO,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EACL,KAAK,UAAU,EACf,sBAAsB,EACtB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,UAAU,EACf,WAAW,EACX,KAAK,0BAA0B,EAC/B,KAAK,UAAU,EACf,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,SAAS,EACd,KAAK,qBAAqB,EAK3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAU,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,6BAA6B,EAAE,OAAO,CAAC;IACvC,uBAAuB,EAAE,MAAM,CAAC;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAcD,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,KAAK,GAAG,cAAc,GAAG,QAAQ,GAAG,aAAa,CAAC;AAE1F,MAAM,MAAM,qBAAqB,GAAG;IAClC,CAAC,sBAAsB,CAAC,oBAAoB,CAAC,EAAE,CAAC,EAAE,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACvF,CAAC,sBAAsB,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAAE,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACjF,CAAC,sBAAsB,CAAC,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC/E,CAAC,sBAAsB,CAAC,qBAAqB,CAAC,EAAE,CAAC,EAAE,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACzF,CAAC,sBAAsB,CAAC,qBAAqB,CAAC,EAAE,CAAC,EAAE,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACzF,CAAC,sBAAsB,CAAC,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC/E,CAAC,sBAAsB,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACzE,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,IAAI,CAAC;IACzD,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,IAAI,CAAC;CAC1D,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,QAAQ,GAAG,eAAe,IAAI;IAC5D,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,GAAG,CAAC,EAAE,GAAG,GAAG,cAAc,CAAC;IAC3B,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,GAAG,CAAC,EAAE,GAAG,GAAG,aAAa,GAAG,SAAS,CAAC;IACtC,GAAG,CAAC,EAAE,GAAG,GAAG,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CACtC,CAAC;2CAImC,aAAa,qBAAqB,CAAC;AAFxE,qBAAa,YAAY,CACvB,QAAQ,GAAG,eAAe,CAC1B,SAAQ,iBAA+D;IACvE,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,GAAG,CAAC,EAAE,GAAG,GAAG,aAAa,CAAC;IAC1B,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAElC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAE/B,OAAO,CAAC,KAAK,CAAC,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAAC,CAAgB;IACjC,OAAO,CAAC,YAAY,CAAC,CAAgB;IACrC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAA0B;IAE3C,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAA8B;IAEjD,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAc;IAE7B,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,aAAa,CAA+B;IAEpD,gBAAgB;IAChB,eAAe,EAAE,UAAU,EAAE,CAAM;gBAEvB,IAAI,EAAE,mBAAmB,CAAC,QAAQ,CAAC;IA+C/C,IAAI,CAAC,CAAC,SAAS,MAAM,qBAAqB,EACxC,KAAK,EAAE,CAAC,EACR,GAAG,IAAI,EAAE,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,GAC5C,OAAO;IAMV,IAAI,KAAK,IAAI,UAAU,CAEtB;IAED,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,IAAI,QAAQ,IAAI,QAAQ,CAMvB;IAED,IAAI,OAAO,IAAI,WAAW,CAEzB;IAED,IAAI,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAE3B;IAEK,KAAK,CAAC,EAIV,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,aAAa,EACb,MAAa,GACd,EAAE;QACD,KAAK,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,IAAI,CAAC;QACX,YAAY,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACzC,aAAa,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoEjB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAQ/B,cAAc;IAQd,aAAa;IAOb,GAAG,CACD,IAAI,EAAE,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,EACrC,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;QACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,GACA,YAAY;IAQf,SAAS;IAOT,aAAa,CAAC,OAAO,CAAC,EAAE;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,UAAU,CAAC;QACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;KAC9B,GAAG,YAAY;YAsBF,cAAc;IA6B5B,IAAI,OAAO,IAAI,WAAW,CAEzB;IAED,IAAI,UAAU,IAAI,UAAU,CAE3B;IAED,IAAI,YAAY,IAAI,KAAK,CAMxB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,gBAAgB;IAChB,UAAU,CAAC,EACT,MAAM,EACN,KAAa,EACb,KAAY,GACb,EAAE;QACD,MAAM,EAAE,WAAW,CAAC;QACpB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,KAAK,CAAC,EAAE,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAC;KACpE,GAAG,IAAI;IAOR,gBAAgB;IAChB,QAAQ,CAAC,KAAK,EAAE,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI;IAc1E,gBAAgB;IAChB,sBAAsB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAK/C,gBAAgB;IAChB,iBAAiB,CAAC,KAAK,EAAE,UAAU;IAuBnC,gBAAgB;IAChB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,MAAM;IAwB7D,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,oBAAoB;IAE5B,OAAO,CAAC,mBAAmB;IAE3B,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,uBAAuB;YAOjB,SAAS;CA+CxB"}
@@ -5,7 +5,7 @@ import {
5
5
  TTS as InferenceTTS
6
6
  } from "../inference/index.js";
7
7
  import { getJobContext } from "../job.js";
8
- import { ChatContext, ChatMessage } from "../llm/chat_context.js";
8
+ import { AgentHandoffItem, ChatContext, ChatMessage } from "../llm/chat_context.js";
9
9
  import { log } from "../log.js";
10
10
  import { AgentActivity } from "./agent_activity.js";
11
11
  import {
@@ -26,7 +26,8 @@ const defaultVoiceOptions = {
26
26
  minEndpointingDelay: 500,
27
27
  maxEndpointingDelay: 6e3,
28
28
  maxToolSteps: 3,
29
- preemptiveGeneration: false
29
+ preemptiveGeneration: false,
30
+ userAwayTimeout: 15
30
31
  };
31
32
  class AgentSession extends EventEmitter {
32
33
  vad;
@@ -48,6 +49,9 @@ class AgentSession extends EventEmitter {
48
49
  _input;
49
50
  _output;
50
51
  closingTask = null;
52
+ userAwayTimer = null;
53
+ /** @internal */
54
+ _recordedEvents = [];
51
55
  constructor(opts) {
52
56
  super();
53
57
  const {
@@ -81,6 +85,12 @@ class AgentSession extends EventEmitter {
81
85
  this._output = new AgentOutput(this.onAudioOutputChanged, this.onTextOutputChanged);
82
86
  this._chatCtx = ChatContext.empty();
83
87
  this.options = { ...defaultVoiceOptions, ...voiceOptions };
88
+ this.on(AgentSessionEventTypes.UserInputTranscribed, this._onUserInputTranscribed.bind(this));
89
+ }
90
+ emit(event, ...args) {
91
+ const eventData = args[0];
92
+ this._recordedEvents.push(eventData);
93
+ return super.emit(event, ...args);
84
94
  }
85
95
  get input() {
86
96
  return this._input;
@@ -101,10 +111,14 @@ class AgentSession extends EventEmitter {
101
111
  this._userData = value;
102
112
  }
103
113
  async start({
114
+ // TODO(brian): PR2 - Add setupCloudTracer() call if on LiveKit Cloud with recording enabled
115
+ // TODO(brian): PR3 - Add span: this._sessionSpan = tracer.startSpan('agent_session'), store as instance property
116
+ // TODO(brian): PR4 - Add setupCloudLogger() call in setupCloudTracer() to setup OTEL logging with Pino bridge
104
117
  agent,
105
118
  room,
106
119
  inputOptions,
107
- outputOptions
120
+ outputOptions,
121
+ record = true
108
122
  }) {
109
123
  if (this.started) {
110
124
  return;
@@ -137,6 +151,15 @@ class AgentSession extends EventEmitter {
137
151
  this.logger.debug("Auto-connecting to room via job context");
138
152
  tasks.push(ctx.connect());
139
153
  }
154
+ if (record) {
155
+ if (ctx._primaryAgentSession === void 0) {
156
+ ctx._primaryAgentSession = this;
157
+ } else {
158
+ throw new Error(
159
+ "Only one `AgentSession` can be the primary at a time. If you want to ignore primary designation, use session.start(record=False)."
160
+ );
161
+ }
162
+ }
140
163
  tasks.push(this.updateActivity(this.agent));
141
164
  await Promise.allSettled(tasks);
142
165
  this.logger.debug(
@@ -196,12 +219,20 @@ class AgentSession extends EventEmitter {
196
219
  }
197
220
  async updateActivity(agent) {
198
221
  this.nextActivity = new AgentActivity(agent, this);
222
+ const previousActivity = this.activity;
199
223
  if (this.activity) {
200
224
  await this.activity.drain();
201
225
  await this.activity.close();
202
226
  }
203
227
  this.activity = this.nextActivity;
204
228
  this.nextActivity = void 0;
229
+ this._chatCtx.insert(
230
+ new AgentHandoffItem({
231
+ oldAgentId: previousActivity == null ? void 0 : previousActivity.agent.id,
232
+ newAgentId: agent.id
233
+ })
234
+ );
235
+ this.logger.debug({ previousActivity, agent }, "Agent handoff inserted into chat context");
205
236
  await this.activity.start();
206
237
  if (this._input.audio) {
207
238
  this.activity.attachAudioInput(this._input.audio.stream);
@@ -257,6 +288,11 @@ class AgentSession extends EventEmitter {
257
288
  }
258
289
  const oldState = this._agentState;
259
290
  this._agentState = state;
291
+ if (state === "listening" && this.userState === "listening") {
292
+ this._setUserAwayTimer();
293
+ } else {
294
+ this._cancelUserAwayTimer();
295
+ }
260
296
  this.emit(
261
297
  AgentSessionEventTypes.AgentStateChanged,
262
298
  createAgentStateChangedEvent(oldState, state)
@@ -269,6 +305,11 @@ class AgentSession extends EventEmitter {
269
305
  }
270
306
  const oldState = this.userState;
271
307
  this.userState = state;
308
+ if (state === "listening" && this._agentState === "listening") {
309
+ this._setUserAwayTimer();
310
+ } else {
311
+ this._cancelUserAwayTimer();
312
+ }
272
313
  this.emit(
273
314
  AgentSessionEventTypes.UserStateChanged,
274
315
  createUserStateChangedEvent(oldState, state)
@@ -287,11 +328,37 @@ class AgentSession extends EventEmitter {
287
328
  }
288
329
  onTextOutputChanged() {
289
330
  }
331
+ _setUserAwayTimer() {
332
+ this._cancelUserAwayTimer();
333
+ if (this.options.userAwayTimeout === null || this.options.userAwayTimeout === void 0) {
334
+ return;
335
+ }
336
+ if (this.roomIO && !this.roomIO.isParticipantAvailable) {
337
+ return;
338
+ }
339
+ this.userAwayTimer = setTimeout(() => {
340
+ this.logger.debug("User away timeout triggered");
341
+ this._updateUserState("away");
342
+ }, this.options.userAwayTimeout * 1e3);
343
+ }
344
+ _cancelUserAwayTimer() {
345
+ if (this.userAwayTimer !== null) {
346
+ clearTimeout(this.userAwayTimer);
347
+ this.userAwayTimer = null;
348
+ }
349
+ }
350
+ _onUserInputTranscribed(ev) {
351
+ if (this.userState === "away" && ev.isFinal) {
352
+ this.logger.debug("User returned from away state due to speech input");
353
+ this._updateUserState("listening");
354
+ }
355
+ }
290
356
  async closeImpl(reason, error = null, drain = false) {
291
357
  var _a, _b, _c;
292
358
  if (!this.started) {
293
359
  return;
294
360
  }
361
+ this._cancelUserAwayTimer();
295
362
  if (this.activity) {
296
363
  if (!drain) {
297
364
  try {