@voicethere/agent 0.7.2 → 0.7.4

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.
@@ -130,6 +130,24 @@ var SessionSerialQueue = class {
130
130
  var SESSION_START_INIT_DELAY_ENABLED_ENV = "AGENT_SESSION_START_INIT_DELAY_ENABLED";
131
131
  var SESSION_START_INIT_DELAY_MS_ENV = "AGENT_SESSION_START_INIT_DELAY_MS";
132
132
  var DEFAULT_SESSION_START_INIT_DELAY_MS = 500;
133
+ function isPlayAckMessage(value) {
134
+ if (!value || typeof value !== "object")
135
+ return false;
136
+ const msg = value;
137
+ return msg.type === "play_ack" && typeof msg.requestId === "string";
138
+ }
139
+ function isPlayStatusAckMessage(value) {
140
+ if (!value || typeof value !== "object")
141
+ return false;
142
+ const msg = value;
143
+ return msg.type === "play_status_ack" && typeof msg.requestId === "string";
144
+ }
145
+ function isPlayStopAckMessage(value) {
146
+ if (!value || typeof value !== "object")
147
+ return false;
148
+ const msg = value;
149
+ return msg.type === "play_stop_ack" && typeof msg.requestId === "string";
150
+ }
133
151
  function isRecordingControlAckMessage(value) {
134
152
  if (!value || typeof value !== "object")
135
153
  return false;
@@ -190,7 +208,7 @@ function isParentMessage(value) {
190
208
  if (!value || typeof value !== "object")
191
209
  return false;
192
210
  const msg = value;
193
- return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
211
+ return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "play_ack" || msg.type === "play_status_ack" || msg.type === "play_stop_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
194
212
  }
195
213
  function isSessionScopedParentMessage(value) {
196
214
  return isParentMessage(value) && !isWebhookMessage(value);
@@ -207,7 +225,11 @@ var recordingAvailableBySessionId = /* @__PURE__ */ new Map();
207
225
  var mixAvailableBySessionId = /* @__PURE__ */ new Map();
208
226
  var ttsPoseAvailableBySessionId = /* @__PURE__ */ new Map();
209
227
  var endedSessionIds = /* @__PURE__ */ new Set();
228
+ var PLAY_BYTES_MAX_DECODED_LENGTH = 64 * 1024;
210
229
  var pendingRecordingAcks = /* @__PURE__ */ new Map();
230
+ var pendingPlayAcks = /* @__PURE__ */ new Map();
231
+ var pendingPlayStatusAcks = /* @__PURE__ */ new Map();
232
+ var pendingPlayStopAcks = /* @__PURE__ */ new Map();
211
233
  var pendingMixAcks = /* @__PURE__ */ new Map();
212
234
  var pendingSttAcks = /* @__PURE__ */ new Map();
213
235
  function handleRecordingControlAck(message) {
@@ -222,6 +244,46 @@ function handleRecordingControlAck(message) {
222
244
  requestId: message.requestId
223
245
  });
224
246
  }
247
+ function handlePlayAck(message) {
248
+ const pending = pendingPlayAcks.get(message.requestId);
249
+ if (!pending)
250
+ return;
251
+ clearTimeout(pending.timer);
252
+ pendingPlayAcks.delete(message.requestId);
253
+ pending.resolve({
254
+ ok: message.ok,
255
+ ...message.playId ? { playId: message.playId } : {},
256
+ reason: message.reason,
257
+ requestId: message.requestId
258
+ });
259
+ }
260
+ function handlePlayStatusAck(message) {
261
+ const pending = pendingPlayStatusAcks.get(message.requestId);
262
+ if (!pending)
263
+ return;
264
+ clearTimeout(pending.timer);
265
+ pendingPlayStatusAcks.delete(message.requestId);
266
+ pending.resolve({
267
+ ok: message.ok,
268
+ playId: message.playId,
269
+ ...message.status ? { status: message.status } : {},
270
+ reason: message.reason,
271
+ requestId: message.requestId
272
+ });
273
+ }
274
+ function handlePlayStopAck(message) {
275
+ const pending = pendingPlayStopAcks.get(message.requestId);
276
+ if (!pending)
277
+ return;
278
+ clearTimeout(pending.timer);
279
+ pendingPlayStopAcks.delete(message.requestId);
280
+ pending.resolve({
281
+ ok: message.ok,
282
+ playId: message.playId,
283
+ reason: message.reason,
284
+ requestId: message.requestId
285
+ });
286
+ }
225
287
  function handleMixControlAck(message) {
226
288
  const pending = pendingMixAcks.get(message.requestId);
227
289
  if (!pending)
@@ -307,6 +369,10 @@ function sendParentMessage(message) {
307
369
  process.send?.(message);
308
370
  return;
309
371
  }
372
+ if (msgType === "play" || msgType === "play_status" || msgType === "play_stop") {
373
+ process.send?.(message);
374
+ return;
375
+ }
310
376
  const sessionId = message && typeof message === "object" && "sessionId" in message && typeof message.sessionId === "string" ? message.sessionId : void 0;
311
377
  if (!allowOutboundForSession(sessionId))
312
378
  return;
@@ -382,6 +448,16 @@ async function handleWebhookMessage(message, handlers) {
382
448
  });
383
449
  }
384
450
  }
451
+ function resolveUserLanguageCode(event) {
452
+ const withLanguage = event;
453
+ const candidates = [withLanguage.language, event.text];
454
+ for (const value of candidates) {
455
+ if (typeof value === "string" && value.trim()) {
456
+ return value.trim();
457
+ }
458
+ }
459
+ return void 0;
460
+ }
385
461
  async function handleParentMessage(message, handlers) {
386
462
  switch (message.type) {
387
463
  case "session_start":
@@ -414,6 +490,15 @@ async function handleParentMessage(message, handlers) {
414
490
  text: message.event.text.trim()
415
491
  });
416
492
  }
493
+ if (message.event.type === "user_language") {
494
+ const language = resolveUserLanguageCode(message.event);
495
+ if (language) {
496
+ await handlers.onUserLanguage?.({
497
+ sessionId: message.sessionId,
498
+ language
499
+ });
500
+ }
501
+ }
417
502
  break;
418
503
  case "data_channel_message":
419
504
  await handlers.onDataChannelMessage?.({
@@ -458,6 +543,18 @@ function defineAgent(handlers) {
458
543
  handleRecordingControlAck(message);
459
544
  return;
460
545
  }
546
+ if (isPlayAckMessage(message)) {
547
+ handlePlayAck(message);
548
+ return;
549
+ }
550
+ if (isPlayStatusAckMessage(message)) {
551
+ handlePlayStatusAck(message);
552
+ return;
553
+ }
554
+ if (isPlayStopAckMessage(message)) {
555
+ handlePlayStopAck(message);
556
+ return;
557
+ }
461
558
  if (isMixControlAckMessage(message)) {
462
559
  handleMixControlAck(message);
463
560
  return;
@@ -130,6 +130,24 @@ var SessionSerialQueue = class {
130
130
  var SESSION_START_INIT_DELAY_ENABLED_ENV = "AGENT_SESSION_START_INIT_DELAY_ENABLED";
131
131
  var SESSION_START_INIT_DELAY_MS_ENV = "AGENT_SESSION_START_INIT_DELAY_MS";
132
132
  var DEFAULT_SESSION_START_INIT_DELAY_MS = 500;
133
+ function isPlayAckMessage(value) {
134
+ if (!value || typeof value !== "object")
135
+ return false;
136
+ const msg = value;
137
+ return msg.type === "play_ack" && typeof msg.requestId === "string";
138
+ }
139
+ function isPlayStatusAckMessage(value) {
140
+ if (!value || typeof value !== "object")
141
+ return false;
142
+ const msg = value;
143
+ return msg.type === "play_status_ack" && typeof msg.requestId === "string";
144
+ }
145
+ function isPlayStopAckMessage(value) {
146
+ if (!value || typeof value !== "object")
147
+ return false;
148
+ const msg = value;
149
+ return msg.type === "play_stop_ack" && typeof msg.requestId === "string";
150
+ }
133
151
  function isRecordingControlAckMessage(value) {
134
152
  if (!value || typeof value !== "object")
135
153
  return false;
@@ -190,7 +208,7 @@ function isParentMessage(value) {
190
208
  if (!value || typeof value !== "object")
191
209
  return false;
192
210
  const msg = value;
193
- return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
211
+ return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "play_ack" || msg.type === "play_status_ack" || msg.type === "play_stop_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
194
212
  }
195
213
  function isSessionScopedParentMessage(value) {
196
214
  return isParentMessage(value) && !isWebhookMessage(value);
@@ -207,7 +225,11 @@ var recordingAvailableBySessionId = /* @__PURE__ */ new Map();
207
225
  var mixAvailableBySessionId = /* @__PURE__ */ new Map();
208
226
  var ttsPoseAvailableBySessionId = /* @__PURE__ */ new Map();
209
227
  var endedSessionIds = /* @__PURE__ */ new Set();
228
+ var PLAY_BYTES_MAX_DECODED_LENGTH = 64 * 1024;
210
229
  var pendingRecordingAcks = /* @__PURE__ */ new Map();
230
+ var pendingPlayAcks = /* @__PURE__ */ new Map();
231
+ var pendingPlayStatusAcks = /* @__PURE__ */ new Map();
232
+ var pendingPlayStopAcks = /* @__PURE__ */ new Map();
211
233
  var pendingMixAcks = /* @__PURE__ */ new Map();
212
234
  var pendingSttAcks = /* @__PURE__ */ new Map();
213
235
  function handleRecordingControlAck(message) {
@@ -222,6 +244,46 @@ function handleRecordingControlAck(message) {
222
244
  requestId: message.requestId
223
245
  });
224
246
  }
247
+ function handlePlayAck(message) {
248
+ const pending = pendingPlayAcks.get(message.requestId);
249
+ if (!pending)
250
+ return;
251
+ clearTimeout(pending.timer);
252
+ pendingPlayAcks.delete(message.requestId);
253
+ pending.resolve({
254
+ ok: message.ok,
255
+ ...message.playId ? { playId: message.playId } : {},
256
+ reason: message.reason,
257
+ requestId: message.requestId
258
+ });
259
+ }
260
+ function handlePlayStatusAck(message) {
261
+ const pending = pendingPlayStatusAcks.get(message.requestId);
262
+ if (!pending)
263
+ return;
264
+ clearTimeout(pending.timer);
265
+ pendingPlayStatusAcks.delete(message.requestId);
266
+ pending.resolve({
267
+ ok: message.ok,
268
+ playId: message.playId,
269
+ ...message.status ? { status: message.status } : {},
270
+ reason: message.reason,
271
+ requestId: message.requestId
272
+ });
273
+ }
274
+ function handlePlayStopAck(message) {
275
+ const pending = pendingPlayStopAcks.get(message.requestId);
276
+ if (!pending)
277
+ return;
278
+ clearTimeout(pending.timer);
279
+ pendingPlayStopAcks.delete(message.requestId);
280
+ pending.resolve({
281
+ ok: message.ok,
282
+ playId: message.playId,
283
+ reason: message.reason,
284
+ requestId: message.requestId
285
+ });
286
+ }
225
287
  function handleMixControlAck(message) {
226
288
  const pending = pendingMixAcks.get(message.requestId);
227
289
  if (!pending)
@@ -307,6 +369,10 @@ function sendParentMessage(message) {
307
369
  process.send?.(message);
308
370
  return;
309
371
  }
372
+ if (msgType === "play" || msgType === "play_status" || msgType === "play_stop") {
373
+ process.send?.(message);
374
+ return;
375
+ }
310
376
  const sessionId = message && typeof message === "object" && "sessionId" in message && typeof message.sessionId === "string" ? message.sessionId : void 0;
311
377
  if (!allowOutboundForSession(sessionId))
312
378
  return;
@@ -382,6 +448,16 @@ async function handleWebhookMessage(message, handlers) {
382
448
  });
383
449
  }
384
450
  }
451
+ function resolveUserLanguageCode(event) {
452
+ const withLanguage = event;
453
+ const candidates = [withLanguage.language, event.text];
454
+ for (const value of candidates) {
455
+ if (typeof value === "string" && value.trim()) {
456
+ return value.trim();
457
+ }
458
+ }
459
+ return void 0;
460
+ }
385
461
  async function handleParentMessage(message, handlers) {
386
462
  switch (message.type) {
387
463
  case "session_start":
@@ -414,6 +490,15 @@ async function handleParentMessage(message, handlers) {
414
490
  text: message.event.text.trim()
415
491
  });
416
492
  }
493
+ if (message.event.type === "user_language") {
494
+ const language = resolveUserLanguageCode(message.event);
495
+ if (language) {
496
+ await handlers.onUserLanguage?.({
497
+ sessionId: message.sessionId,
498
+ language
499
+ });
500
+ }
501
+ }
417
502
  break;
418
503
  case "data_channel_message":
419
504
  await handlers.onDataChannelMessage?.({
@@ -458,6 +543,18 @@ function defineAgent(handlers) {
458
543
  handleRecordingControlAck(message);
459
544
  return;
460
545
  }
546
+ if (isPlayAckMessage(message)) {
547
+ handlePlayAck(message);
548
+ return;
549
+ }
550
+ if (isPlayStatusAckMessage(message)) {
551
+ handlePlayStatusAck(message);
552
+ return;
553
+ }
554
+ if (isPlayStopAckMessage(message)) {
555
+ handlePlayStopAck(message);
556
+ return;
557
+ }
461
558
  if (isMixControlAckMessage(message)) {
462
559
  handleMixControlAck(message);
463
560
  return;
@@ -674,6 +771,8 @@ function peerState(sessionId) {
674
771
  return state;
675
772
  }
676
773
  function formatSpeechDetail(speech) {
774
+ const language = speech.language;
775
+ if (language) return language;
677
776
  if (speech.text) return `"${speech.text}"`;
678
777
  if (speech.error) return speech.error;
679
778
  return "";
@@ -681,6 +780,10 @@ function formatSpeechDetail(speech) {
681
780
  function handleSpeechEvent(sessionId, speech) {
682
781
  const state = peerState(sessionId);
683
782
  const detail = formatSpeechDetail(speech);
783
+ if (speech.type === "user_language") {
784
+ agentLog("info", `[${sessionId}] user_language ${detail}`);
785
+ return;
786
+ }
684
787
  switch (speech.type) {
685
788
  // --- User turn (VAD + STT) ---
686
789
  case "user_speaking_start":
@@ -767,6 +870,9 @@ defineAgent({
767
870
  onUserSpeechFinal({ sessionId, text }) {
768
871
  speak(sessionId, `You said: ${text}`);
769
872
  },
873
+ onUserLanguage({ sessionId, language }) {
874
+ agentLog("info", `user_language ${sessionId} ${language}`);
875
+ },
770
876
  onSessionEnd({ sessionId }) {
771
877
  peers.delete(sessionId);
772
878
  agentLog("info", `session_end ${sessionId}`);
@@ -133,6 +133,24 @@ var SessionSerialQueue = class {
133
133
  var SESSION_START_INIT_DELAY_ENABLED_ENV = "AGENT_SESSION_START_INIT_DELAY_ENABLED";
134
134
  var SESSION_START_INIT_DELAY_MS_ENV = "AGENT_SESSION_START_INIT_DELAY_MS";
135
135
  var DEFAULT_SESSION_START_INIT_DELAY_MS = 500;
136
+ function isPlayAckMessage(value) {
137
+ if (!value || typeof value !== "object")
138
+ return false;
139
+ const msg = value;
140
+ return msg.type === "play_ack" && typeof msg.requestId === "string";
141
+ }
142
+ function isPlayStatusAckMessage(value) {
143
+ if (!value || typeof value !== "object")
144
+ return false;
145
+ const msg = value;
146
+ return msg.type === "play_status_ack" && typeof msg.requestId === "string";
147
+ }
148
+ function isPlayStopAckMessage(value) {
149
+ if (!value || typeof value !== "object")
150
+ return false;
151
+ const msg = value;
152
+ return msg.type === "play_stop_ack" && typeof msg.requestId === "string";
153
+ }
136
154
  function isRecordingControlAckMessage(value) {
137
155
  if (!value || typeof value !== "object")
138
156
  return false;
@@ -193,7 +211,7 @@ function isParentMessage(value) {
193
211
  if (!value || typeof value !== "object")
194
212
  return false;
195
213
  const msg = value;
196
- return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
214
+ return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "play_ack" || msg.type === "play_status_ack" || msg.type === "play_stop_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
197
215
  }
198
216
  function isSessionScopedParentMessage(value) {
199
217
  return isParentMessage(value) && !isWebhookMessage(value);
@@ -210,7 +228,11 @@ var recordingAvailableBySessionId = /* @__PURE__ */ new Map();
210
228
  var mixAvailableBySessionId = /* @__PURE__ */ new Map();
211
229
  var ttsPoseAvailableBySessionId = /* @__PURE__ */ new Map();
212
230
  var endedSessionIds = /* @__PURE__ */ new Set();
231
+ var PLAY_BYTES_MAX_DECODED_LENGTH = 64 * 1024;
213
232
  var pendingRecordingAcks = /* @__PURE__ */ new Map();
233
+ var pendingPlayAcks = /* @__PURE__ */ new Map();
234
+ var pendingPlayStatusAcks = /* @__PURE__ */ new Map();
235
+ var pendingPlayStopAcks = /* @__PURE__ */ new Map();
214
236
  var pendingMixAcks = /* @__PURE__ */ new Map();
215
237
  var pendingSttAcks = /* @__PURE__ */ new Map();
216
238
  function handleRecordingControlAck(message) {
@@ -225,6 +247,46 @@ function handleRecordingControlAck(message) {
225
247
  requestId: message.requestId
226
248
  });
227
249
  }
250
+ function handlePlayAck(message) {
251
+ const pending = pendingPlayAcks.get(message.requestId);
252
+ if (!pending)
253
+ return;
254
+ clearTimeout(pending.timer);
255
+ pendingPlayAcks.delete(message.requestId);
256
+ pending.resolve({
257
+ ok: message.ok,
258
+ ...message.playId ? { playId: message.playId } : {},
259
+ reason: message.reason,
260
+ requestId: message.requestId
261
+ });
262
+ }
263
+ function handlePlayStatusAck(message) {
264
+ const pending = pendingPlayStatusAcks.get(message.requestId);
265
+ if (!pending)
266
+ return;
267
+ clearTimeout(pending.timer);
268
+ pendingPlayStatusAcks.delete(message.requestId);
269
+ pending.resolve({
270
+ ok: message.ok,
271
+ playId: message.playId,
272
+ ...message.status ? { status: message.status } : {},
273
+ reason: message.reason,
274
+ requestId: message.requestId
275
+ });
276
+ }
277
+ function handlePlayStopAck(message) {
278
+ const pending = pendingPlayStopAcks.get(message.requestId);
279
+ if (!pending)
280
+ return;
281
+ clearTimeout(pending.timer);
282
+ pendingPlayStopAcks.delete(message.requestId);
283
+ pending.resolve({
284
+ ok: message.ok,
285
+ playId: message.playId,
286
+ reason: message.reason,
287
+ requestId: message.requestId
288
+ });
289
+ }
228
290
  function handleMixControlAck(message) {
229
291
  const pending = pendingMixAcks.get(message.requestId);
230
292
  if (!pending)
@@ -310,6 +372,10 @@ function sendParentMessage(message) {
310
372
  process.send?.(message);
311
373
  return;
312
374
  }
375
+ if (msgType === "play" || msgType === "play_status" || msgType === "play_stop") {
376
+ process.send?.(message);
377
+ return;
378
+ }
313
379
  const sessionId = message && typeof message === "object" && "sessionId" in message && typeof message.sessionId === "string" ? message.sessionId : void 0;
314
380
  if (!allowOutboundForSession(sessionId))
315
381
  return;
@@ -385,6 +451,16 @@ async function handleWebhookMessage(message, handlers) {
385
451
  });
386
452
  }
387
453
  }
454
+ function resolveUserLanguageCode(event) {
455
+ const withLanguage = event;
456
+ const candidates = [withLanguage.language, event.text];
457
+ for (const value of candidates) {
458
+ if (typeof value === "string" && value.trim()) {
459
+ return value.trim();
460
+ }
461
+ }
462
+ return void 0;
463
+ }
388
464
  async function handleParentMessage(message, handlers) {
389
465
  switch (message.type) {
390
466
  case "session_start":
@@ -417,6 +493,15 @@ async function handleParentMessage(message, handlers) {
417
493
  text: message.event.text.trim()
418
494
  });
419
495
  }
496
+ if (message.event.type === "user_language") {
497
+ const language = resolveUserLanguageCode(message.event);
498
+ if (language) {
499
+ await handlers.onUserLanguage?.({
500
+ sessionId: message.sessionId,
501
+ language
502
+ });
503
+ }
504
+ }
420
505
  break;
421
506
  case "data_channel_message":
422
507
  await handlers.onDataChannelMessage?.({
@@ -461,6 +546,18 @@ function defineAgent(handlers) {
461
546
  handleRecordingControlAck(message);
462
547
  return;
463
548
  }
549
+ if (isPlayAckMessage(message)) {
550
+ handlePlayAck(message);
551
+ return;
552
+ }
553
+ if (isPlayStatusAckMessage(message)) {
554
+ handlePlayStatusAck(message);
555
+ return;
556
+ }
557
+ if (isPlayStopAckMessage(message)) {
558
+ handlePlayStopAck(message);
559
+ return;
560
+ }
464
561
  if (isMixControlAckMessage(message)) {
465
562
  handleMixControlAck(message);
466
563
  return;
@@ -10885,6 +10885,24 @@ var SessionSerialQueue = class {
10885
10885
  var SESSION_START_INIT_DELAY_ENABLED_ENV = "AGENT_SESSION_START_INIT_DELAY_ENABLED";
10886
10886
  var SESSION_START_INIT_DELAY_MS_ENV = "AGENT_SESSION_START_INIT_DELAY_MS";
10887
10887
  var DEFAULT_SESSION_START_INIT_DELAY_MS = 500;
10888
+ function isPlayAckMessage(value) {
10889
+ if (!value || typeof value !== "object")
10890
+ return false;
10891
+ const msg = value;
10892
+ return msg.type === "play_ack" && typeof msg.requestId === "string";
10893
+ }
10894
+ function isPlayStatusAckMessage(value) {
10895
+ if (!value || typeof value !== "object")
10896
+ return false;
10897
+ const msg = value;
10898
+ return msg.type === "play_status_ack" && typeof msg.requestId === "string";
10899
+ }
10900
+ function isPlayStopAckMessage(value) {
10901
+ if (!value || typeof value !== "object")
10902
+ return false;
10903
+ const msg = value;
10904
+ return msg.type === "play_stop_ack" && typeof msg.requestId === "string";
10905
+ }
10888
10906
  function isRecordingControlAckMessage(value) {
10889
10907
  if (!value || typeof value !== "object")
10890
10908
  return false;
@@ -10945,7 +10963,7 @@ function isParentMessage(value) {
10945
10963
  if (!value || typeof value !== "object")
10946
10964
  return false;
10947
10965
  const msg = value;
10948
- return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
10966
+ return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "play_ack" || msg.type === "play_status_ack" || msg.type === "play_stop_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
10949
10967
  }
10950
10968
  function isSessionScopedParentMessage(value) {
10951
10969
  return isParentMessage(value) && !isWebhookMessage(value);
@@ -10962,7 +10980,11 @@ var recordingAvailableBySessionId = /* @__PURE__ */ new Map();
10962
10980
  var mixAvailableBySessionId = /* @__PURE__ */ new Map();
10963
10981
  var ttsPoseAvailableBySessionId = /* @__PURE__ */ new Map();
10964
10982
  var endedSessionIds = /* @__PURE__ */ new Set();
10983
+ var PLAY_BYTES_MAX_DECODED_LENGTH = 64 * 1024;
10965
10984
  var pendingRecordingAcks = /* @__PURE__ */ new Map();
10985
+ var pendingPlayAcks = /* @__PURE__ */ new Map();
10986
+ var pendingPlayStatusAcks = /* @__PURE__ */ new Map();
10987
+ var pendingPlayStopAcks = /* @__PURE__ */ new Map();
10966
10988
  var pendingMixAcks = /* @__PURE__ */ new Map();
10967
10989
  var pendingSttAcks = /* @__PURE__ */ new Map();
10968
10990
  function handleRecordingControlAck(message) {
@@ -10977,6 +10999,46 @@ function handleRecordingControlAck(message) {
10977
10999
  requestId: message.requestId
10978
11000
  });
10979
11001
  }
11002
+ function handlePlayAck(message) {
11003
+ const pending = pendingPlayAcks.get(message.requestId);
11004
+ if (!pending)
11005
+ return;
11006
+ clearTimeout(pending.timer);
11007
+ pendingPlayAcks.delete(message.requestId);
11008
+ pending.resolve({
11009
+ ok: message.ok,
11010
+ ...message.playId ? { playId: message.playId } : {},
11011
+ reason: message.reason,
11012
+ requestId: message.requestId
11013
+ });
11014
+ }
11015
+ function handlePlayStatusAck(message) {
11016
+ const pending = pendingPlayStatusAcks.get(message.requestId);
11017
+ if (!pending)
11018
+ return;
11019
+ clearTimeout(pending.timer);
11020
+ pendingPlayStatusAcks.delete(message.requestId);
11021
+ pending.resolve({
11022
+ ok: message.ok,
11023
+ playId: message.playId,
11024
+ ...message.status ? { status: message.status } : {},
11025
+ reason: message.reason,
11026
+ requestId: message.requestId
11027
+ });
11028
+ }
11029
+ function handlePlayStopAck(message) {
11030
+ const pending = pendingPlayStopAcks.get(message.requestId);
11031
+ if (!pending)
11032
+ return;
11033
+ clearTimeout(pending.timer);
11034
+ pendingPlayStopAcks.delete(message.requestId);
11035
+ pending.resolve({
11036
+ ok: message.ok,
11037
+ playId: message.playId,
11038
+ reason: message.reason,
11039
+ requestId: message.requestId
11040
+ });
11041
+ }
10980
11042
  function handleMixControlAck(message) {
10981
11043
  const pending = pendingMixAcks.get(message.requestId);
10982
11044
  if (!pending)
@@ -11062,6 +11124,10 @@ function sendParentMessage(message) {
11062
11124
  process.send?.(message);
11063
11125
  return;
11064
11126
  }
11127
+ if (msgType === "play" || msgType === "play_status" || msgType === "play_stop") {
11128
+ process.send?.(message);
11129
+ return;
11130
+ }
11065
11131
  const sessionId = message && typeof message === "object" && "sessionId" in message && typeof message.sessionId === "string" ? message.sessionId : void 0;
11066
11132
  if (!allowOutboundForSession(sessionId))
11067
11133
  return;
@@ -11137,6 +11203,16 @@ async function handleWebhookMessage(message, handlers) {
11137
11203
  });
11138
11204
  }
11139
11205
  }
11206
+ function resolveUserLanguageCode(event) {
11207
+ const withLanguage = event;
11208
+ const candidates = [withLanguage.language, event.text];
11209
+ for (const value of candidates) {
11210
+ if (typeof value === "string" && value.trim()) {
11211
+ return value.trim();
11212
+ }
11213
+ }
11214
+ return void 0;
11215
+ }
11140
11216
  async function handleParentMessage(message, handlers) {
11141
11217
  switch (message.type) {
11142
11218
  case "session_start":
@@ -11169,6 +11245,15 @@ async function handleParentMessage(message, handlers) {
11169
11245
  text: message.event.text.trim()
11170
11246
  });
11171
11247
  }
11248
+ if (message.event.type === "user_language") {
11249
+ const language = resolveUserLanguageCode(message.event);
11250
+ if (language) {
11251
+ await handlers.onUserLanguage?.({
11252
+ sessionId: message.sessionId,
11253
+ language
11254
+ });
11255
+ }
11256
+ }
11172
11257
  break;
11173
11258
  case "data_channel_message":
11174
11259
  await handlers.onDataChannelMessage?.({
@@ -11213,6 +11298,18 @@ function defineAgent(handlers) {
11213
11298
  handleRecordingControlAck(message);
11214
11299
  return;
11215
11300
  }
11301
+ if (isPlayAckMessage(message)) {
11302
+ handlePlayAck(message);
11303
+ return;
11304
+ }
11305
+ if (isPlayStatusAckMessage(message)) {
11306
+ handlePlayStatusAck(message);
11307
+ return;
11308
+ }
11309
+ if (isPlayStopAckMessage(message)) {
11310
+ handlePlayStopAck(message);
11311
+ return;
11312
+ }
11216
11313
  if (isMixControlAckMessage(message)) {
11217
11314
  handleMixControlAck(message);
11218
11315
  return;
@@ -2,7 +2,7 @@ import type { ChildToParentMessage } from "../protocol.js";
2
2
  export declare const VERIFY_SESSION_ID = "local-verify";
3
3
  export declare const DEFAULT_VERIFY_BUNDLE = "dist/agent.js";
4
4
  export declare const DEFAULT_VERIFY_ENTRY = "agent.ts";
5
- export declare const VERIFY_CALLBACK_KEYS: readonly ["onSpeechEvent", "onUserSpeechFinal", "onDataChannelMessage", "onDataChannelBinary", "onWebhook", "onSessionStart"];
5
+ export declare const VERIFY_CALLBACK_KEYS: readonly ["onSpeechEvent", "onUserSpeechFinal", "onUserLanguage", "onDataChannelMessage", "onDataChannelBinary", "onWebhook", "onSessionStart"];
6
6
  export type VerifyCallbackKey = (typeof VERIFY_CALLBACK_KEYS)[number];
7
7
  export declare function parseBundleArg(argv: string[], env?: NodeJS.ProcessEnv, cwd?: string): string;
8
8
  export declare function waitForSpeak(messages: ChildToParentMessage[], sessionId: string, timeoutMs: number): Promise<Extract<ChildToParentMessage, {
@@ -1 +1 @@
1
- {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/verify/lib.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAChD,eAAO,MAAM,qBAAqB,kBAAkB,CAAC;AACrD,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAC/C,eAAO,MAAM,oBAAoB,+HAOvB,CAAC;AACX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,GAAG,GAAE,MAAsB,GAC1B,MAAM,CAWR;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,oBAAoB,EAAE,EAChC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,CAuB3D;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,oBAAoB,EAAE,EAChC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAAC,CAyBvE;AAED,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,GACnB,iBAAiB,EAAE,CAqBrB;AAED,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAExE"}
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/verify/lib.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAChD,eAAO,MAAM,qBAAqB,kBAAkB,CAAC;AACrD,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAC/C,eAAO,MAAM,oBAAoB,iJAQvB,CAAC;AACX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,GAAG,GAAE,MAAsB,GAC1B,MAAM,CAWR;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,oBAAoB,EAAE,EAChC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,CAuB3D;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,oBAAoB,EAAE,EAChC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAAC,CAyBvE;AAED,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,GACnB,iBAAiB,EAAE,CAqBrB;AAED,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAExE"}
@@ -5,6 +5,7 @@ export const DEFAULT_VERIFY_ENTRY = "agent.ts";
5
5
  export const VERIFY_CALLBACK_KEYS = [
6
6
  "onSpeechEvent",
7
7
  "onUserSpeechFinal",
8
+ "onUserLanguage",
8
9
  "onDataChannelMessage",
9
10
  "onDataChannelBinary",
10
11
  "onWebhook",