@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.
- package/README.md +10 -9
- package/dist/agent.js +45 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +36 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/dist/runtime.d.ts +21 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +112 -0
- package/dist/runtime.js.map +1 -1
- package/dist/templates/echo/agent.js +54 -5
- package/dist/templates/echo-dc/agent.js +39 -1
- package/dist/templates/game-sync/agent.js +39 -1
- package/dist/templates/positional-tts/agent.js +39 -1
- package/dist/templates/recording-consent/agent.js +51 -13
- package/dist/templates/spatial-showcase/agent.js +39 -1
- package/dist/templates/voice-showcase/agent.js +103 -26
- package/dist/templates/voice-starter/agent.js +39 -1
- package/dist/templates/webhooks/agent.js +39 -1
- package/dist/templates/webhooks-redis/agent.js +39 -1
- package/dist/templates/world-sync/agent.js +39 -1
- package/dist/templates/world-sync-binary/agent.js +39 -1
- package/package.json +1 -1
- package/templates/echo/agent.ts +3 -5
- package/templates/voice-showcase/agent.ts +3 -1
- package/templates/voice-showcase/conversation.ts +9 -5
- package/templates/voice-showcase/delivery.ts +40 -10
|
@@ -10915,6 +10915,12 @@ function isRecordingControlAckMessage(value) {
|
|
|
10915
10915
|
const msg = value;
|
|
10916
10916
|
return msg.type === "recording_control_ack" && typeof msg.requestId === "string";
|
|
10917
10917
|
}
|
|
10918
|
+
function isConversationHistoryControlAckMessage(value) {
|
|
10919
|
+
if (!value || typeof value !== "object")
|
|
10920
|
+
return false;
|
|
10921
|
+
const msg = value;
|
|
10922
|
+
return msg.type === "conversation_history_control_ack" && typeof msg.requestId === "string";
|
|
10923
|
+
}
|
|
10918
10924
|
function isMixControlAckMessage(value) {
|
|
10919
10925
|
if (!value || typeof value !== "object")
|
|
10920
10926
|
return false;
|
|
@@ -10969,7 +10975,7 @@ function isParentMessage(value) {
|
|
|
10969
10975
|
if (!value || typeof value !== "object")
|
|
10970
10976
|
return false;
|
|
10971
10977
|
const msg = value;
|
|
10972
|
-
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";
|
|
10978
|
+
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";
|
|
10973
10979
|
}
|
|
10974
10980
|
function isSessionScopedParentMessage(value) {
|
|
10975
10981
|
return isParentMessage(value) && !isWebhookMessage(value);
|
|
@@ -10983,11 +10989,13 @@ function parseDataChannelPayload(raw) {
|
|
|
10983
10989
|
}
|
|
10984
10990
|
var peerEnvBySessionId = /* @__PURE__ */ new Map();
|
|
10985
10991
|
var recordingAvailableBySessionId = /* @__PURE__ */ new Map();
|
|
10992
|
+
var conversationHistoryAvailableBySessionId = /* @__PURE__ */ new Map();
|
|
10986
10993
|
var mixAvailableBySessionId = /* @__PURE__ */ new Map();
|
|
10987
10994
|
var ttsPoseAvailableBySessionId = /* @__PURE__ */ new Map();
|
|
10988
10995
|
var endedSessionIds = /* @__PURE__ */ new Set();
|
|
10989
10996
|
var PLAY_BYTES_MAX_DECODED_LENGTH = 64 * 1024;
|
|
10990
10997
|
var pendingRecordingAcks = /* @__PURE__ */ new Map();
|
|
10998
|
+
var pendingConversationHistoryAcks = /* @__PURE__ */ new Map();
|
|
10991
10999
|
var pendingPlayAcks = /* @__PURE__ */ new Map();
|
|
10992
11000
|
var pendingPlayStatusAcks = /* @__PURE__ */ new Map();
|
|
10993
11001
|
var pendingPlayStopAcks = /* @__PURE__ */ new Map();
|
|
@@ -11006,6 +11014,18 @@ function handleRecordingControlAck(message) {
|
|
|
11006
11014
|
requestId: message.requestId
|
|
11007
11015
|
});
|
|
11008
11016
|
}
|
|
11017
|
+
function handleConversationHistoryControlAck(message) {
|
|
11018
|
+
const pending = pendingConversationHistoryAcks.get(message.requestId);
|
|
11019
|
+
if (!pending)
|
|
11020
|
+
return;
|
|
11021
|
+
clearTimeout(pending.timer);
|
|
11022
|
+
pendingConversationHistoryAcks.delete(message.requestId);
|
|
11023
|
+
pending.resolve({
|
|
11024
|
+
ok: message.ok,
|
|
11025
|
+
reason: message.reason,
|
|
11026
|
+
requestId: message.requestId
|
|
11027
|
+
});
|
|
11028
|
+
}
|
|
11009
11029
|
function handlePlayAck(message) {
|
|
11010
11030
|
const pending = pendingPlayAcks.get(message.requestId);
|
|
11011
11031
|
if (!pending)
|
|
@@ -11093,6 +11113,15 @@ function clearPendingRecordingAcksForSession(sessionId, reason) {
|
|
|
11093
11113
|
pending.resolve({ ok: false, reason, requestId });
|
|
11094
11114
|
}
|
|
11095
11115
|
}
|
|
11116
|
+
function clearPendingConversationHistoryAcksForSession(sessionId, reason) {
|
|
11117
|
+
for (const [requestId, pending] of pendingConversationHistoryAcks) {
|
|
11118
|
+
if (pending.sessionId !== sessionId)
|
|
11119
|
+
continue;
|
|
11120
|
+
clearTimeout(pending.timer);
|
|
11121
|
+
pendingConversationHistoryAcks.delete(requestId);
|
|
11122
|
+
pending.resolve({ ok: false, reason, requestId });
|
|
11123
|
+
}
|
|
11124
|
+
}
|
|
11096
11125
|
var sessionExecutionContext = new AsyncLocalStorage();
|
|
11097
11126
|
var agentLogSessionContext = new AsyncLocalStorage();
|
|
11098
11127
|
var inboundQueueAuthority = null;
|
|
@@ -11239,6 +11268,7 @@ async function handleParentMessage(message, handlers) {
|
|
|
11239
11268
|
endedSessionIds.delete(message.sessionId);
|
|
11240
11269
|
peerEnvBySessionId.set(message.sessionId, message.env);
|
|
11241
11270
|
recordingAvailableBySessionId.set(message.sessionId, message.recordingAvailable ?? false);
|
|
11271
|
+
conversationHistoryAvailableBySessionId.set(message.sessionId, message.conversationHistoryAvailable ?? false);
|
|
11242
11272
|
mixAvailableBySessionId.set(message.sessionId, message.mixAvailable ?? false);
|
|
11243
11273
|
ttsPoseAvailableBySessionId.set(message.sessionId, message.ttsPoseAvailable ?? false);
|
|
11244
11274
|
const sessionStartInitDelayMs = resolveSessionStartInitDelayMs();
|
|
@@ -11249,6 +11279,7 @@ async function handleParentMessage(message, handlers) {
|
|
|
11249
11279
|
sessionId: message.sessionId,
|
|
11250
11280
|
env: message.env,
|
|
11251
11281
|
recordingAvailable: message.recordingAvailable ?? false,
|
|
11282
|
+
conversationHistoryAvailable: message.conversationHistoryAvailable ?? false,
|
|
11252
11283
|
mixAvailable: message.mixAvailable ?? false,
|
|
11253
11284
|
ttsPoseAvailable: message.ttsPoseAvailable ?? false
|
|
11254
11285
|
});
|
|
@@ -11295,8 +11326,10 @@ async function handleParentMessage(message, handlers) {
|
|
|
11295
11326
|
break;
|
|
11296
11327
|
case "session_end":
|
|
11297
11328
|
clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
|
|
11329
|
+
clearPendingConversationHistoryAcksForSession(message.sessionId, "session_ended");
|
|
11298
11330
|
peerEnvBySessionId.delete(message.sessionId);
|
|
11299
11331
|
recordingAvailableBySessionId.delete(message.sessionId);
|
|
11332
|
+
conversationHistoryAvailableBySessionId.delete(message.sessionId);
|
|
11300
11333
|
mixAvailableBySessionId.delete(message.sessionId);
|
|
11301
11334
|
ttsPoseAvailableBySessionId.delete(message.sessionId);
|
|
11302
11335
|
await (handlers.onClientLeave ?? handlers.onSessionEnd)?.({
|
|
@@ -11318,6 +11351,10 @@ function defineAgent(handlers) {
|
|
|
11318
11351
|
handleRecordingControlAck(message);
|
|
11319
11352
|
return;
|
|
11320
11353
|
}
|
|
11354
|
+
if (isConversationHistoryControlAckMessage(message)) {
|
|
11355
|
+
handleConversationHistoryControlAck(message);
|
|
11356
|
+
return;
|
|
11357
|
+
}
|
|
11321
11358
|
if (isPlayAckMessage(message)) {
|
|
11322
11359
|
handlePlayAck(message);
|
|
11323
11360
|
return;
|
|
@@ -11351,6 +11388,7 @@ function defineAgent(handlers) {
|
|
|
11351
11388
|
if (message.type === "session_end") {
|
|
11352
11389
|
endedSessionIds.add(message.sessionId);
|
|
11353
11390
|
clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
|
|
11391
|
+
clearPendingConversationHistoryAcksForSession(message.sessionId, "session_ended");
|
|
11354
11392
|
inboundBySession.clear(message.sessionId);
|
|
11355
11393
|
}
|
|
11356
11394
|
if (message.type === "session_start") {
|
|
@@ -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") {
|
|
@@ -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") {
|
package/package.json
CHANGED
package/templates/echo/agent.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
defineAgent,
|
|
11
11
|
parseChatText,
|
|
12
12
|
sendToClient,
|
|
13
|
-
|
|
13
|
+
speakAndChat,
|
|
14
14
|
type SpeechEvent,
|
|
15
15
|
} from "@voicethere/agent";
|
|
16
16
|
|
|
@@ -42,16 +42,14 @@ defineAgent({
|
|
|
42
42
|
|
|
43
43
|
onUserSpeechFinal({ sessionId, text }) {
|
|
44
44
|
const reply = formatEcho(text);
|
|
45
|
-
|
|
46
|
-
sendToClient(sessionId, { type: "chat_reply", text: reply });
|
|
45
|
+
speakAndChat(sessionId, reply, { stream: true });
|
|
47
46
|
},
|
|
48
47
|
|
|
49
48
|
onDataChannelMessage(ctx) {
|
|
50
49
|
const text = parseChatText(ctx.message);
|
|
51
50
|
if (!text) return;
|
|
52
51
|
const reply = formatEcho(text);
|
|
53
|
-
|
|
54
|
-
speak(ctx.sessionId, reply);
|
|
52
|
+
speakAndChat(ctx.sessionId, reply, { stream: true });
|
|
55
53
|
},
|
|
56
54
|
|
|
57
55
|
onSessionEnd({ sessionId }) {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
parseChatText,
|
|
11
11
|
sendToClient,
|
|
12
12
|
speak,
|
|
13
|
+
speakAndChat,
|
|
13
14
|
type SpeechEvent,
|
|
14
15
|
} from "@voicethere/agent";
|
|
15
16
|
|
|
@@ -54,7 +55,7 @@ function deliverSpokenThenPlay(
|
|
|
54
55
|
applyOutboundOps(
|
|
55
56
|
sessionId,
|
|
56
57
|
spokenThenPlayOps(result.messages, result.speakLines),
|
|
57
|
-
{ sendToClient, speak },
|
|
58
|
+
{ sendToClient, speak, speakAndChat },
|
|
58
59
|
);
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -88,6 +89,7 @@ defineAgent({
|
|
|
88
89
|
applyOutboundOps(sessionId, greetingOps(sessionId, GREETING), {
|
|
89
90
|
sendToClient,
|
|
90
91
|
speak,
|
|
92
|
+
speakAndChat,
|
|
91
93
|
});
|
|
92
94
|
agentLog("info", `voice-showcase session_start ${sessionId}`);
|
|
93
95
|
},
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Tests import this module directly — no defineAgent dependency.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
|
|
6
8
|
import { formatRecipeSpeech, pickRecipe } from "./recipes.js";
|
|
7
9
|
import { pickFunFact } from "./fun-facts.js";
|
|
8
10
|
import {
|
|
@@ -55,6 +57,8 @@ export interface ConversationState {
|
|
|
55
57
|
export interface OutboundMessage {
|
|
56
58
|
type: "chat_reply" | "menu" | "agent_event";
|
|
57
59
|
text?: string;
|
|
60
|
+
stream?: boolean;
|
|
61
|
+
utteranceId?: string;
|
|
58
62
|
event?: string;
|
|
59
63
|
items?: Array<{ id: number; label: string }>;
|
|
60
64
|
sessionId?: string;
|
|
@@ -91,9 +95,10 @@ function speakAndChat(text: string): {
|
|
|
91
95
|
speakLines: string[];
|
|
92
96
|
messages: OutboundMessage[];
|
|
93
97
|
} {
|
|
98
|
+
const utteranceId = randomUUID();
|
|
94
99
|
return {
|
|
95
100
|
speakLines: [text],
|
|
96
|
-
messages: [{ type: "chat_reply", text }],
|
|
101
|
+
messages: [{ type: "chat_reply", text, stream: true, utteranceId }],
|
|
97
102
|
};
|
|
98
103
|
}
|
|
99
104
|
|
|
@@ -427,16 +432,15 @@ export function handleUtterance(
|
|
|
427
432
|
}
|
|
428
433
|
|
|
429
434
|
if (!country) {
|
|
435
|
+
const ask = speakAndChat("Got it. Which country is that in?");
|
|
430
436
|
return {
|
|
431
437
|
state: {
|
|
432
438
|
...state,
|
|
433
439
|
phase: "weatherAwaitingLocation",
|
|
434
440
|
weatherCity: city,
|
|
435
441
|
},
|
|
436
|
-
speakLines:
|
|
437
|
-
messages:
|
|
438
|
-
{ type: "chat_reply", text: "Got it. Which country is that in?" },
|
|
439
|
-
],
|
|
442
|
+
speakLines: ask.speakLines,
|
|
443
|
+
messages: ask.messages,
|
|
440
444
|
};
|
|
441
445
|
}
|
|
442
446
|
|
|
@@ -3,15 +3,21 @@
|
|
|
3
3
|
* then trigger TTS play. Parent IPC preserves this order.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
|
|
6
8
|
import type { OutboundMessage } from "./conversation.js";
|
|
7
9
|
|
|
8
10
|
export type OutboundOp =
|
|
9
|
-
|
|
10
|
-
| { kind: "play"; text: string };
|
|
11
|
+
{ kind: "send"; message: OutboundMessage } | { kind: "play"; text: string };
|
|
11
12
|
|
|
12
13
|
export interface OutboundDeps {
|
|
13
14
|
sendToClient: (sessionId: string, payload: unknown) => void;
|
|
14
15
|
speak: (sessionId: string, text: string) => void;
|
|
16
|
+
speakAndChat: (
|
|
17
|
+
sessionId: string,
|
|
18
|
+
text: string,
|
|
19
|
+
options?: { stream?: boolean; utteranceId?: string },
|
|
20
|
+
) => void;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
/** All DataChannel payloads, then TTS play commands. */
|
|
@@ -29,14 +35,17 @@ export function spokenThenPlayOps(
|
|
|
29
35
|
return ops;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
|
-
export function greetingOps(
|
|
33
|
-
|
|
34
|
-
greeting: string,
|
|
35
|
-
): OutboundOp[] {
|
|
38
|
+
export function greetingOps(sessionId: string, greeting: string): OutboundOp[] {
|
|
39
|
+
const utteranceId = randomUUID();
|
|
36
40
|
return spokenThenPlayOps(
|
|
37
41
|
[
|
|
38
42
|
{ type: "agent_event", event: "session_start", sessionId },
|
|
39
|
-
{
|
|
43
|
+
{
|
|
44
|
+
type: "chat_reply",
|
|
45
|
+
text: greeting,
|
|
46
|
+
stream: true,
|
|
47
|
+
utteranceId,
|
|
48
|
+
},
|
|
40
49
|
],
|
|
41
50
|
[greeting],
|
|
42
51
|
);
|
|
@@ -47,11 +56,32 @@ export function applyOutboundOps(
|
|
|
47
56
|
ops: OutboundOp[],
|
|
48
57
|
deps: OutboundDeps,
|
|
49
58
|
): void {
|
|
59
|
+
const spokenViaSpeakAndChat = new Set<string>();
|
|
60
|
+
|
|
50
61
|
for (const op of ops) {
|
|
51
62
|
if (op.kind === "send") {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
const message = op.message;
|
|
64
|
+
if (
|
|
65
|
+
message.type === "chat_reply" &&
|
|
66
|
+
message.stream === true &&
|
|
67
|
+
message.text?.trim()
|
|
68
|
+
) {
|
|
69
|
+
const text = message.text.trim();
|
|
70
|
+
deps.speakAndChat(sessionId, text, {
|
|
71
|
+
stream: true,
|
|
72
|
+
utteranceId: message.utteranceId,
|
|
73
|
+
});
|
|
74
|
+
spokenViaSpeakAndChat.add(text);
|
|
75
|
+
} else {
|
|
76
|
+
deps.sendToClient(sessionId, message);
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const playText = op.text.trim();
|
|
82
|
+
if (spokenViaSpeakAndChat.has(playText)) {
|
|
83
|
+
continue;
|
|
55
84
|
}
|
|
85
|
+
deps.speak(sessionId, op.text);
|
|
56
86
|
}
|
|
57
87
|
}
|