@voicethere/agent 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -160,6 +160,12 @@ function isRecordingControlAckMessage(value) {
160
160
  const msg = value;
161
161
  return msg.type === "recording_control_ack" && typeof msg.requestId === "string";
162
162
  }
163
+ function isConversationHistoryControlAckMessage(value) {
164
+ if (!value || typeof value !== "object")
165
+ return false;
166
+ const msg = value;
167
+ return msg.type === "conversation_history_control_ack" && typeof msg.requestId === "string";
168
+ }
163
169
  function isMixControlAckMessage(value) {
164
170
  if (!value || typeof value !== "object")
165
171
  return false;
@@ -214,7 +220,7 @@ function isParentMessage(value) {
214
220
  if (!value || typeof value !== "object")
215
221
  return false;
216
222
  const msg = value;
217
- 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 === "play_pose_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
223
+ 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 === "conversation_history_control_ack" || msg.type === "play_ack" || msg.type === "play_status_ack" || msg.type === "play_stop_ack" || msg.type === "play_pose_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
218
224
  }
219
225
  function isSessionScopedParentMessage(value) {
220
226
  return isParentMessage(value) && !isWebhookMessage(value);
@@ -228,11 +234,13 @@ function parseDataChannelPayload(raw) {
228
234
  }
229
235
  var peerEnvBySessionId = /* @__PURE__ */ new Map();
230
236
  var recordingAvailableBySessionId = /* @__PURE__ */ new Map();
237
+ var conversationHistoryAvailableBySessionId = /* @__PURE__ */ new Map();
231
238
  var mixAvailableBySessionId = /* @__PURE__ */ new Map();
232
239
  var ttsPoseAvailableBySessionId = /* @__PURE__ */ new Map();
233
240
  var endedSessionIds = /* @__PURE__ */ new Set();
234
241
  var PLAY_BYTES_MAX_DECODED_LENGTH = 64 * 1024;
235
242
  var pendingRecordingAcks = /* @__PURE__ */ new Map();
243
+ var pendingConversationHistoryAcks = /* @__PURE__ */ new Map();
236
244
  var pendingPlayAcks = /* @__PURE__ */ new Map();
237
245
  var pendingPlayStatusAcks = /* @__PURE__ */ new Map();
238
246
  var pendingPlayStopAcks = /* @__PURE__ */ new Map();
@@ -251,6 +259,18 @@ function handleRecordingControlAck(message) {
251
259
  requestId: message.requestId
252
260
  });
253
261
  }
262
+ function handleConversationHistoryControlAck(message) {
263
+ const pending = pendingConversationHistoryAcks.get(message.requestId);
264
+ if (!pending)
265
+ return;
266
+ clearTimeout(pending.timer);
267
+ pendingConversationHistoryAcks.delete(message.requestId);
268
+ pending.resolve({
269
+ ok: message.ok,
270
+ reason: message.reason,
271
+ requestId: message.requestId
272
+ });
273
+ }
254
274
  function handlePlayAck(message) {
255
275
  const pending = pendingPlayAcks.get(message.requestId);
256
276
  if (!pending)
@@ -338,6 +358,15 @@ function clearPendingRecordingAcksForSession(sessionId, reason) {
338
358
  pending.resolve({ ok: false, reason, requestId });
339
359
  }
340
360
  }
361
+ function clearPendingConversationHistoryAcksForSession(sessionId, reason) {
362
+ for (const [requestId, pending] of pendingConversationHistoryAcks) {
363
+ if (pending.sessionId !== sessionId)
364
+ continue;
365
+ clearTimeout(pending.timer);
366
+ pendingConversationHistoryAcks.delete(requestId);
367
+ pending.resolve({ ok: false, reason, requestId });
368
+ }
369
+ }
341
370
  var sessionExecutionContext = new AsyncLocalStorage();
342
371
  var agentLogSessionContext = new AsyncLocalStorage();
343
372
  var inboundQueueAuthority = null;
@@ -484,6 +513,7 @@ async function handleParentMessage(message, handlers) {
484
513
  endedSessionIds.delete(message.sessionId);
485
514
  peerEnvBySessionId.set(message.sessionId, message.env);
486
515
  recordingAvailableBySessionId.set(message.sessionId, message.recordingAvailable ?? false);
516
+ conversationHistoryAvailableBySessionId.set(message.sessionId, message.conversationHistoryAvailable ?? false);
487
517
  mixAvailableBySessionId.set(message.sessionId, message.mixAvailable ?? false);
488
518
  ttsPoseAvailableBySessionId.set(message.sessionId, message.ttsPoseAvailable ?? false);
489
519
  const sessionStartInitDelayMs = resolveSessionStartInitDelayMs();
@@ -494,6 +524,7 @@ async function handleParentMessage(message, handlers) {
494
524
  sessionId: message.sessionId,
495
525
  env: message.env,
496
526
  recordingAvailable: message.recordingAvailable ?? false,
527
+ conversationHistoryAvailable: message.conversationHistoryAvailable ?? false,
497
528
  mixAvailable: message.mixAvailable ?? false,
498
529
  ttsPoseAvailable: message.ttsPoseAvailable ?? false
499
530
  });
@@ -540,8 +571,10 @@ async function handleParentMessage(message, handlers) {
540
571
  break;
541
572
  case "session_end":
542
573
  clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
574
+ clearPendingConversationHistoryAcksForSession(message.sessionId, "session_ended");
543
575
  peerEnvBySessionId.delete(message.sessionId);
544
576
  recordingAvailableBySessionId.delete(message.sessionId);
577
+ conversationHistoryAvailableBySessionId.delete(message.sessionId);
545
578
  mixAvailableBySessionId.delete(message.sessionId);
546
579
  ttsPoseAvailableBySessionId.delete(message.sessionId);
547
580
  await (handlers.onClientLeave ?? handlers.onSessionEnd)?.({
@@ -563,6 +596,10 @@ function defineAgent(handlers) {
563
596
  handleRecordingControlAck(message);
564
597
  return;
565
598
  }
599
+ if (isConversationHistoryControlAckMessage(message)) {
600
+ handleConversationHistoryControlAck(message);
601
+ return;
602
+ }
566
603
  if (isPlayAckMessage(message)) {
567
604
  handlePlayAck(message);
568
605
  return;
@@ -596,6 +633,7 @@ function defineAgent(handlers) {
596
633
  if (message.type === "session_end") {
597
634
  endedSessionIds.add(message.sessionId);
598
635
  clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
636
+ clearPendingConversationHistoryAcksForSession(message.sessionId, "session_ended");
599
637
  inboundBySession.clear(message.sessionId);
600
638
  }
601
639
  if (message.type === "session_start") {
@@ -723,6 +761,19 @@ function buildIdleEnv(sessionId) {
723
761
  function speak(sessionId, text) {
724
762
  sendParentMessage({ type: "speak", sessionId, text });
725
763
  }
764
+ function speakAndChat(sessionId, text, options) {
765
+ const trimmed = text.trim();
766
+ if (!trimmed) {
767
+ return;
768
+ }
769
+ const payload = { type: "chat_reply", text: trimmed };
770
+ if (options?.stream) {
771
+ payload.stream = true;
772
+ payload.utteranceId = options.utteranceId ?? randomUUID();
773
+ }
774
+ sendToClient(sessionId, payload);
775
+ speak(sessionId, trimmed);
776
+ }
726
777
  function sendToClient(sessionId, payload) {
727
778
  sendParentMessage({ type: "send_to_client", sessionId, payload });
728
779
  }
@@ -791,6 +842,9 @@ function parseChatText(message) {
791
842
  return trimmed.length > 0 ? trimmed : null;
792
843
  }
793
844
 
845
+ // templates/voice-showcase/conversation.ts
846
+ import { randomUUID as randomUUID2 } from "node:crypto";
847
+
794
848
  // templates/voice-showcase/recipes.ts
795
849
  var RECIPES = [
796
850
  {
@@ -1134,10 +1188,11 @@ function buildMenuMessages() {
1134
1188
  }
1135
1189
  ];
1136
1190
  }
1137
- function speakAndChat(text) {
1191
+ function speakAndChat2(text) {
1192
+ const utteranceId = randomUUID2();
1138
1193
  return {
1139
1194
  speakLines: [text],
1140
- messages: [{ type: "chat_reply", text }]
1195
+ messages: [{ type: "chat_reply", text, stream: true, utteranceId }]
1141
1196
  };
1142
1197
  }
1143
1198
  function isHumanEscalation(utterance) {
@@ -1214,15 +1269,15 @@ function transitionAfterName(state, name, declined) {
1214
1269
  const lines = [];
1215
1270
  const messages = [];
1216
1271
  if (declined) {
1217
- const decline = speakAndChat(NAME_DECLINE_REPLY);
1272
+ const decline = speakAndChat2(NAME_DECLINE_REPLY);
1218
1273
  lines.push(...decline.speakLines);
1219
1274
  messages.push(...decline.messages);
1220
1275
  } else if (name) {
1221
- const thanks = speakAndChat(`Great, thank you ${name}`);
1276
+ const thanks = speakAndChat2(`Great, thank you ${name}`);
1222
1277
  lines.push(...thanks.speakLines);
1223
1278
  messages.push(...thanks.messages);
1224
1279
  }
1225
- const hello = speakAndChat(helloAfterName(next));
1280
+ const hello = speakAndChat2(helloAfterName(next));
1226
1281
  lines.push(...hello.speakLines);
1227
1282
  messages.push(...hello.messages);
1228
1283
  messages.push(...buildMenuMessages());
@@ -1281,7 +1336,7 @@ function formatCountingSpeech(n) {
1281
1336
  return parts.join(", ");
1282
1337
  }
1283
1338
  function resendMenu(state) {
1284
- const menu = speakAndChat("Here is the menu again.");
1339
+ const menu = speakAndChat2("Here is the menu again.");
1285
1340
  return {
1286
1341
  state: { ...state, phase: "awaitingMenuChoice" },
1287
1342
  speakLines: menu.speakLines,
@@ -1289,7 +1344,7 @@ function resendMenu(state) {
1289
1344
  };
1290
1345
  }
1291
1346
  function returnToMenu(state, line) {
1292
- const spoken = speakAndChat(line);
1347
+ const spoken = speakAndChat2(line);
1293
1348
  return {
1294
1349
  state: {
1295
1350
  ...state,
@@ -1309,7 +1364,7 @@ function handleUtterance(state, utterance) {
1309
1364
  return { state, speakLines: [], messages: [] };
1310
1365
  }
1311
1366
  if (isHumanEscalation(text)) {
1312
- const reply = speakAndChat(HUMAN_ESCALATION_REPLY);
1367
+ const reply = speakAndChat2(HUMAN_ESCALATION_REPLY);
1313
1368
  return {
1314
1369
  state,
1315
1370
  speakLines: reply.speakLines,
@@ -1338,7 +1393,7 @@ function handleUtterance(state, utterance) {
1338
1393
  const choice = parseMenuChoice(text);
1339
1394
  if (choice === "menu") return resendMenu(state);
1340
1395
  if (choice === "weather") {
1341
- const ask = speakAndChat(
1396
+ const ask = speakAndChat2(
1342
1397
  "Sure. Please tell me a city or ZIP code and the country."
1343
1398
  );
1344
1399
  return {
@@ -1354,7 +1409,7 @@ function handleUtterance(state, utterance) {
1354
1409
  };
1355
1410
  }
1356
1411
  if (choice === "count") {
1357
- const ask = speakAndChat(
1412
+ const ask = speakAndChat2(
1358
1413
  "Pick a number from 1 to 10 and I will count up to it."
1359
1414
  );
1360
1415
  return {
@@ -1368,7 +1423,7 @@ function handleUtterance(state, utterance) {
1368
1423
  };
1369
1424
  }
1370
1425
  if (choice === "recipe") {
1371
- const ask = speakAndChat(
1426
+ const ask = speakAndChat2(
1372
1427
  "What do you fancy? Try pasta, soup, breakfast, cookies, or salad."
1373
1428
  );
1374
1429
  return {
@@ -1381,7 +1436,7 @@ function handleUtterance(state, utterance) {
1381
1436
  const fact = pickFunFact();
1382
1437
  return returnToMenu(state, `Here is a fun fact. ${fact}`);
1383
1438
  }
1384
- const retry = speakAndChat(
1439
+ const retry = speakAndChat2(
1385
1440
  "I did not catch that. Pick 1 through 4 from the menu, or say weather, count, recipe, or fun fact."
1386
1441
  );
1387
1442
  return {
@@ -1402,7 +1457,7 @@ function handleUtterance(state, utterance) {
1402
1457
  }
1403
1458
  }
1404
1459
  if (!city) {
1405
- const ask = speakAndChat(
1460
+ const ask = speakAndChat2(
1406
1461
  "Please tell me a city or ZIP code and the country."
1407
1462
  );
1408
1463
  return {
@@ -1412,16 +1467,15 @@ function handleUtterance(state, utterance) {
1412
1467
  };
1413
1468
  }
1414
1469
  if (!country) {
1470
+ const ask = speakAndChat2("Got it. Which country is that in?");
1415
1471
  return {
1416
1472
  state: {
1417
1473
  ...state,
1418
1474
  phase: "weatherAwaitingLocation",
1419
1475
  weatherCity: city
1420
1476
  },
1421
- speakLines: ["Got it. Which country is that in?"],
1422
- messages: [
1423
- { type: "chat_reply", text: "Got it. Which country is that in?" }
1424
- ]
1477
+ speakLines: ask.speakLines,
1478
+ messages: ask.messages
1425
1479
  };
1426
1480
  }
1427
1481
  return {
@@ -1438,7 +1492,7 @@ function handleUtterance(state, utterance) {
1438
1492
  if (failures >= 2) {
1439
1493
  return returnToMenu(state, "Sorry, I cannot do this.");
1440
1494
  }
1441
- const retry = speakAndChat(
1495
+ const retry = speakAndChat2(
1442
1496
  "I did not understand you. Please say a number from 1 to 10."
1443
1497
  );
1444
1498
  return {
@@ -1471,7 +1525,7 @@ function applyWeatherFailure(state) {
1471
1525
  "Sorry, I could not look up the weather right now."
1472
1526
  );
1473
1527
  }
1474
- const retry = speakAndChat(
1528
+ const retry = speakAndChat2(
1475
1529
  "I could not find that location. Please try again with a city or ZIP and country."
1476
1530
  );
1477
1531
  return {
@@ -1499,6 +1553,7 @@ async function resolveWeatherTurn(state, city, country, fetchFn) {
1499
1553
  }
1500
1554
 
1501
1555
  // templates/voice-showcase/delivery.ts
1556
+ import { randomUUID as randomUUID3 } from "node:crypto";
1502
1557
  function spokenThenPlayOps(messages, speakLines) {
1503
1558
  const ops = [];
1504
1559
  for (const message of messages) {
@@ -1510,21 +1565,42 @@ function spokenThenPlayOps(messages, speakLines) {
1510
1565
  return ops;
1511
1566
  }
1512
1567
  function greetingOps(sessionId, greeting) {
1568
+ const utteranceId = randomUUID3();
1513
1569
  return spokenThenPlayOps(
1514
1570
  [
1515
1571
  { type: "agent_event", event: "session_start", sessionId },
1516
- { type: "chat_reply", text: greeting }
1572
+ {
1573
+ type: "chat_reply",
1574
+ text: greeting,
1575
+ stream: true,
1576
+ utteranceId
1577
+ }
1517
1578
  ],
1518
1579
  [greeting]
1519
1580
  );
1520
1581
  }
1521
1582
  function applyOutboundOps(sessionId, ops, deps) {
1583
+ const spokenViaSpeakAndChat = /* @__PURE__ */ new Set();
1522
1584
  for (const op of ops) {
1523
1585
  if (op.kind === "send") {
1524
- deps.sendToClient(sessionId, op.message);
1525
- } else {
1526
- deps.speak(sessionId, op.text);
1586
+ const message = op.message;
1587
+ if (message.type === "chat_reply" && message.stream === true && message.text?.trim()) {
1588
+ const text = message.text.trim();
1589
+ deps.speakAndChat(sessionId, text, {
1590
+ stream: true,
1591
+ utteranceId: message.utteranceId
1592
+ });
1593
+ spokenViaSpeakAndChat.add(text);
1594
+ } else {
1595
+ deps.sendToClient(sessionId, message);
1596
+ }
1597
+ continue;
1598
+ }
1599
+ const playText = op.text.trim();
1600
+ if (spokenViaSpeakAndChat.has(playText)) {
1601
+ continue;
1527
1602
  }
1603
+ deps.speak(sessionId, op.text);
1528
1604
  }
1529
1605
  }
1530
1606
 
@@ -1550,7 +1626,7 @@ function deliverSpokenThenPlay(sessionId, result) {
1550
1626
  applyOutboundOps(
1551
1627
  sessionId,
1552
1628
  spokenThenPlayOps(result.messages, result.speakLines),
1553
- { sendToClient, speak }
1629
+ { sendToClient, speak, speakAndChat }
1554
1630
  );
1555
1631
  }
1556
1632
  async function applyTurn(sessionId, result) {
@@ -1576,7 +1652,8 @@ defineAgent({
1576
1652
  sessions.set(sessionId, createInitialState());
1577
1653
  applyOutboundOps(sessionId, greetingOps(sessionId, GREETING), {
1578
1654
  sendToClient,
1579
- speak
1655
+ speak,
1656
+ speakAndChat
1580
1657
  });
1581
1658
  agentLog("info", `voice-showcase session_start ${sessionId}`);
1582
1659
  },
@@ -160,6 +160,12 @@ function isRecordingControlAckMessage(value) {
160
160
  const msg = value;
161
161
  return msg.type === "recording_control_ack" && typeof msg.requestId === "string";
162
162
  }
163
+ function isConversationHistoryControlAckMessage(value) {
164
+ if (!value || typeof value !== "object")
165
+ return false;
166
+ const msg = value;
167
+ return msg.type === "conversation_history_control_ack" && typeof msg.requestId === "string";
168
+ }
163
169
  function isMixControlAckMessage(value) {
164
170
  if (!value || typeof value !== "object")
165
171
  return false;
@@ -214,7 +220,7 @@ function isParentMessage(value) {
214
220
  if (!value || typeof value !== "object")
215
221
  return false;
216
222
  const msg = value;
217
- 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 === "play_pose_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
223
+ 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 === "conversation_history_control_ack" || msg.type === "play_ack" || msg.type === "play_status_ack" || msg.type === "play_stop_ack" || msg.type === "play_pose_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
218
224
  }
219
225
  function isSessionScopedParentMessage(value) {
220
226
  return isParentMessage(value) && !isWebhookMessage(value);
@@ -228,11 +234,13 @@ function parseDataChannelPayload(raw) {
228
234
  }
229
235
  var peerEnvBySessionId = /* @__PURE__ */ new Map();
230
236
  var recordingAvailableBySessionId = /* @__PURE__ */ new Map();
237
+ var conversationHistoryAvailableBySessionId = /* @__PURE__ */ new Map();
231
238
  var mixAvailableBySessionId = /* @__PURE__ */ new Map();
232
239
  var ttsPoseAvailableBySessionId = /* @__PURE__ */ new Map();
233
240
  var endedSessionIds = /* @__PURE__ */ new Set();
234
241
  var PLAY_BYTES_MAX_DECODED_LENGTH = 64 * 1024;
235
242
  var pendingRecordingAcks = /* @__PURE__ */ new Map();
243
+ var pendingConversationHistoryAcks = /* @__PURE__ */ new Map();
236
244
  var pendingPlayAcks = /* @__PURE__ */ new Map();
237
245
  var pendingPlayStatusAcks = /* @__PURE__ */ new Map();
238
246
  var pendingPlayStopAcks = /* @__PURE__ */ new Map();
@@ -251,6 +259,18 @@ function handleRecordingControlAck(message) {
251
259
  requestId: message.requestId
252
260
  });
253
261
  }
262
+ function handleConversationHistoryControlAck(message) {
263
+ const pending = pendingConversationHistoryAcks.get(message.requestId);
264
+ if (!pending)
265
+ return;
266
+ clearTimeout(pending.timer);
267
+ pendingConversationHistoryAcks.delete(message.requestId);
268
+ pending.resolve({
269
+ ok: message.ok,
270
+ reason: message.reason,
271
+ requestId: message.requestId
272
+ });
273
+ }
254
274
  function handlePlayAck(message) {
255
275
  const pending = pendingPlayAcks.get(message.requestId);
256
276
  if (!pending)
@@ -338,6 +358,15 @@ function clearPendingRecordingAcksForSession(sessionId, reason) {
338
358
  pending.resolve({ ok: false, reason, requestId });
339
359
  }
340
360
  }
361
+ function clearPendingConversationHistoryAcksForSession(sessionId, reason) {
362
+ for (const [requestId, pending] of pendingConversationHistoryAcks) {
363
+ if (pending.sessionId !== sessionId)
364
+ continue;
365
+ clearTimeout(pending.timer);
366
+ pendingConversationHistoryAcks.delete(requestId);
367
+ pending.resolve({ ok: false, reason, requestId });
368
+ }
369
+ }
341
370
  var sessionExecutionContext = new AsyncLocalStorage();
342
371
  var agentLogSessionContext = new AsyncLocalStorage();
343
372
  var inboundQueueAuthority = null;
@@ -484,6 +513,7 @@ async function handleParentMessage(message, handlers) {
484
513
  endedSessionIds.delete(message.sessionId);
485
514
  peerEnvBySessionId.set(message.sessionId, message.env);
486
515
  recordingAvailableBySessionId.set(message.sessionId, message.recordingAvailable ?? false);
516
+ conversationHistoryAvailableBySessionId.set(message.sessionId, message.conversationHistoryAvailable ?? false);
487
517
  mixAvailableBySessionId.set(message.sessionId, message.mixAvailable ?? false);
488
518
  ttsPoseAvailableBySessionId.set(message.sessionId, message.ttsPoseAvailable ?? false);
489
519
  const sessionStartInitDelayMs = resolveSessionStartInitDelayMs();
@@ -494,6 +524,7 @@ async function handleParentMessage(message, handlers) {
494
524
  sessionId: message.sessionId,
495
525
  env: message.env,
496
526
  recordingAvailable: message.recordingAvailable ?? false,
527
+ conversationHistoryAvailable: message.conversationHistoryAvailable ?? false,
497
528
  mixAvailable: message.mixAvailable ?? false,
498
529
  ttsPoseAvailable: message.ttsPoseAvailable ?? false
499
530
  });
@@ -540,8 +571,10 @@ async function handleParentMessage(message, handlers) {
540
571
  break;
541
572
  case "session_end":
542
573
  clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
574
+ clearPendingConversationHistoryAcksForSession(message.sessionId, "session_ended");
543
575
  peerEnvBySessionId.delete(message.sessionId);
544
576
  recordingAvailableBySessionId.delete(message.sessionId);
577
+ conversationHistoryAvailableBySessionId.delete(message.sessionId);
545
578
  mixAvailableBySessionId.delete(message.sessionId);
546
579
  ttsPoseAvailableBySessionId.delete(message.sessionId);
547
580
  await (handlers.onClientLeave ?? handlers.onSessionEnd)?.({
@@ -563,6 +596,10 @@ function defineAgent(handlers) {
563
596
  handleRecordingControlAck(message);
564
597
  return;
565
598
  }
599
+ if (isConversationHistoryControlAckMessage(message)) {
600
+ handleConversationHistoryControlAck(message);
601
+ return;
602
+ }
566
603
  if (isPlayAckMessage(message)) {
567
604
  handlePlayAck(message);
568
605
  return;
@@ -596,6 +633,7 @@ function defineAgent(handlers) {
596
633
  if (message.type === "session_end") {
597
634
  endedSessionIds.add(message.sessionId);
598
635
  clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
636
+ clearPendingConversationHistoryAcksForSession(message.sessionId, "session_ended");
599
637
  inboundBySession.clear(message.sessionId);
600
638
  }
601
639
  if (message.type === "session_start") {
@@ -163,6 +163,12 @@ function isRecordingControlAckMessage(value) {
163
163
  const msg = value;
164
164
  return msg.type === "recording_control_ack" && typeof msg.requestId === "string";
165
165
  }
166
+ function isConversationHistoryControlAckMessage(value) {
167
+ if (!value || typeof value !== "object")
168
+ return false;
169
+ const msg = value;
170
+ return msg.type === "conversation_history_control_ack" && typeof msg.requestId === "string";
171
+ }
166
172
  function isMixControlAckMessage(value) {
167
173
  if (!value || typeof value !== "object")
168
174
  return false;
@@ -217,7 +223,7 @@ function isParentMessage(value) {
217
223
  if (!value || typeof value !== "object")
218
224
  return false;
219
225
  const msg = value;
220
- 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 === "play_pose_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
226
+ 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 === "conversation_history_control_ack" || msg.type === "play_ack" || msg.type === "play_status_ack" || msg.type === "play_stop_ack" || msg.type === "play_pose_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
221
227
  }
222
228
  function isSessionScopedParentMessage(value) {
223
229
  return isParentMessage(value) && !isWebhookMessage(value);
@@ -231,11 +237,13 @@ function parseDataChannelPayload(raw) {
231
237
  }
232
238
  var peerEnvBySessionId = /* @__PURE__ */ new Map();
233
239
  var recordingAvailableBySessionId = /* @__PURE__ */ new Map();
240
+ var conversationHistoryAvailableBySessionId = /* @__PURE__ */ new Map();
234
241
  var mixAvailableBySessionId = /* @__PURE__ */ new Map();
235
242
  var ttsPoseAvailableBySessionId = /* @__PURE__ */ new Map();
236
243
  var endedSessionIds = /* @__PURE__ */ new Set();
237
244
  var PLAY_BYTES_MAX_DECODED_LENGTH = 64 * 1024;
238
245
  var pendingRecordingAcks = /* @__PURE__ */ new Map();
246
+ var pendingConversationHistoryAcks = /* @__PURE__ */ new Map();
239
247
  var pendingPlayAcks = /* @__PURE__ */ new Map();
240
248
  var pendingPlayStatusAcks = /* @__PURE__ */ new Map();
241
249
  var pendingPlayStopAcks = /* @__PURE__ */ new Map();
@@ -254,6 +262,18 @@ function handleRecordingControlAck(message) {
254
262
  requestId: message.requestId
255
263
  });
256
264
  }
265
+ function handleConversationHistoryControlAck(message) {
266
+ const pending = pendingConversationHistoryAcks.get(message.requestId);
267
+ if (!pending)
268
+ return;
269
+ clearTimeout(pending.timer);
270
+ pendingConversationHistoryAcks.delete(message.requestId);
271
+ pending.resolve({
272
+ ok: message.ok,
273
+ reason: message.reason,
274
+ requestId: message.requestId
275
+ });
276
+ }
257
277
  function handlePlayAck(message) {
258
278
  const pending = pendingPlayAcks.get(message.requestId);
259
279
  if (!pending)
@@ -341,6 +361,15 @@ function clearPendingRecordingAcksForSession(sessionId, reason) {
341
361
  pending.resolve({ ok: false, reason, requestId });
342
362
  }
343
363
  }
364
+ function clearPendingConversationHistoryAcksForSession(sessionId, reason) {
365
+ for (const [requestId, pending] of pendingConversationHistoryAcks) {
366
+ if (pending.sessionId !== sessionId)
367
+ continue;
368
+ clearTimeout(pending.timer);
369
+ pendingConversationHistoryAcks.delete(requestId);
370
+ pending.resolve({ ok: false, reason, requestId });
371
+ }
372
+ }
344
373
  var sessionExecutionContext = new AsyncLocalStorage();
345
374
  var agentLogSessionContext = new AsyncLocalStorage();
346
375
  var inboundQueueAuthority = null;
@@ -487,6 +516,7 @@ async function handleParentMessage(message, handlers) {
487
516
  endedSessionIds.delete(message.sessionId);
488
517
  peerEnvBySessionId.set(message.sessionId, message.env);
489
518
  recordingAvailableBySessionId.set(message.sessionId, message.recordingAvailable ?? false);
519
+ conversationHistoryAvailableBySessionId.set(message.sessionId, message.conversationHistoryAvailable ?? false);
490
520
  mixAvailableBySessionId.set(message.sessionId, message.mixAvailable ?? false);
491
521
  ttsPoseAvailableBySessionId.set(message.sessionId, message.ttsPoseAvailable ?? false);
492
522
  const sessionStartInitDelayMs = resolveSessionStartInitDelayMs();
@@ -497,6 +527,7 @@ async function handleParentMessage(message, handlers) {
497
527
  sessionId: message.sessionId,
498
528
  env: message.env,
499
529
  recordingAvailable: message.recordingAvailable ?? false,
530
+ conversationHistoryAvailable: message.conversationHistoryAvailable ?? false,
500
531
  mixAvailable: message.mixAvailable ?? false,
501
532
  ttsPoseAvailable: message.ttsPoseAvailable ?? false
502
533
  });
@@ -543,8 +574,10 @@ async function handleParentMessage(message, handlers) {
543
574
  break;
544
575
  case "session_end":
545
576
  clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
577
+ clearPendingConversationHistoryAcksForSession(message.sessionId, "session_ended");
546
578
  peerEnvBySessionId.delete(message.sessionId);
547
579
  recordingAvailableBySessionId.delete(message.sessionId);
580
+ conversationHistoryAvailableBySessionId.delete(message.sessionId);
548
581
  mixAvailableBySessionId.delete(message.sessionId);
549
582
  ttsPoseAvailableBySessionId.delete(message.sessionId);
550
583
  await (handlers.onClientLeave ?? handlers.onSessionEnd)?.({
@@ -566,6 +599,10 @@ function defineAgent(handlers) {
566
599
  handleRecordingControlAck(message);
567
600
  return;
568
601
  }
602
+ if (isConversationHistoryControlAckMessage(message)) {
603
+ handleConversationHistoryControlAck(message);
604
+ return;
605
+ }
569
606
  if (isPlayAckMessage(message)) {
570
607
  handlePlayAck(message);
571
608
  return;
@@ -599,6 +636,7 @@ function defineAgent(handlers) {
599
636
  if (message.type === "session_end") {
600
637
  endedSessionIds.add(message.sessionId);
601
638
  clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
639
+ clearPendingConversationHistoryAcksForSession(message.sessionId, "session_ended");
602
640
  inboundBySession.clear(message.sessionId);
603
641
  }
604
642
  if (message.type === "session_start") {