@runtypelabs/persona 4.27.0 → 4.28.0
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/dist/approval-ui.d.cts +13 -0
- package/dist/approval-ui.d.ts +13 -0
- package/dist/artifacts-ui.d.cts +13 -0
- package/dist/artifacts-ui.d.ts +13 -0
- package/dist/chunk-AALCT54S.js +1 -0
- package/dist/{client-history-HTRKHBAF.js → client-history-LV3KHEFA.js} +1 -1
- package/dist/client-history.js +1 -1
- package/dist/codegen.cjs +1 -1
- package/dist/codegen.js +1 -1
- package/dist/context-mentions.d.cts +13 -0
- package/dist/context-mentions.d.ts +13 -0
- package/dist/event-stream-view.d.cts +13 -0
- package/dist/event-stream-view.d.ts +13 -0
- package/dist/forms-ui.d.cts +19 -0
- package/dist/forms-ui.d.ts +19 -0
- package/dist/index.cjs +33 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +27 -27
- package/dist/index.js.map +1 -1
- package/dist/session-reconnect.d.cts +13 -0
- package/dist/session-reconnect.d.ts +13 -0
- package/dist/smart-dom-reader.d.cts +13 -0
- package/dist/smart-dom-reader.d.ts +13 -0
- package/dist/theme-editor-preview.cjs +63 -63
- package/dist/theme-editor-preview.d.cts +13 -0
- package/dist/theme-editor-preview.d.ts +13 -0
- package/dist/theme-editor-preview.js +5 -5
- package/dist/theme-editor.d.cts +13 -0
- package/dist/theme-editor.d.ts +13 -0
- package/dist/voice-runtime-LVUMPUDS.js +17 -0
- package/dist/voice-runtime.cjs +8 -8
- package/dist/voice-runtime.d.cts +24 -0
- package/dist/voice-runtime.d.ts +24 -0
- package/dist/voice-runtime.js +8 -8
- package/package.json +1 -1
- package/src/client.interrupt.test.ts +18 -0
- package/src/client.ts +7 -0
- package/src/session.ts +11 -3
- package/src/session.voice.test.ts +140 -0
- package/src/types.ts +19 -0
- package/src/voice/runtype-voice-provider.ts +93 -11
- package/src/voice/voice-clause-buffer.test.ts +143 -0
- package/src/voice/voice-clause-buffer.ts +187 -0
- package/src/voice/voice-delegation.ts +85 -3
- package/src/voice/voice.test.ts +257 -2
- package/dist/chunk-3255TNY7.js +0 -1
- package/dist/voice-runtime-RHRX4F3T.js +0 -17
package/src/voice/voice.test.ts
CHANGED
|
@@ -896,7 +896,7 @@ describe('RuntypeVoiceProvider (realtime streaming)', () => {
|
|
|
896
896
|
|
|
897
897
|
it('declares client_delegation (never the kebab form) only with a bridge and the switch on', async () => {
|
|
898
898
|
const { ws } = await startDelegatedCall();
|
|
899
|
-
expect(ws.url).toBe(voiceUrl('wss://api.example.com', 'a1', [...BASE_CAPS, 'client_delegation', 'delegation_update', 'delegation_read_back']));
|
|
899
|
+
expect(ws.url).toBe(voiceUrl('wss://api.example.com', 'a1', [...BASE_CAPS, 'client_delegation', 'delegation_update', 'delegation_read_back', 'delegation_stream']));
|
|
900
900
|
expect(ws.url).not.toContain('client-delegation');
|
|
901
901
|
await startDelegatedCall({ clientDelegation: false });
|
|
902
902
|
expect(lastWs().url).toBe(voiceUrl('wss://api.example.com', 'a1', BASE_CAPS));
|
|
@@ -1268,6 +1268,261 @@ describe('RuntypeVoiceProvider (realtime streaming)', () => {
|
|
|
1268
1268
|
expect(sentJson(ws)).toEqual([]);
|
|
1269
1269
|
});
|
|
1270
1270
|
|
|
1271
|
+
describe('with delegation_stream negotiated', () => {
|
|
1272
|
+
const STREAM_CAPS = [...ALL_CAPS, 'delegation_stream', 'delegation_read_back'];
|
|
1273
|
+
const progress = (ws: MockWebSocket, delegationId: string, text = 'chunk') =>
|
|
1274
|
+
ws.triggerMessage(JSON.stringify({ type: 'delegation_progress', delegationId, text }));
|
|
1275
|
+
|
|
1276
|
+
it('sends delegation_delta pieces in order, then the result with streamedChars', async () => {
|
|
1277
|
+
const { ws, calls, pending } = await startDelegatedCall({}, { capabilities: STREAM_CAPS });
|
|
1278
|
+
delegate(ws, 'd1', 'hours?');
|
|
1279
|
+
await flush();
|
|
1280
|
+
expect(typeof calls[0].stream).toBe('function');
|
|
1281
|
+
calls[0].stream!('We open at eight. ');
|
|
1282
|
+
calls[0].stream!('We close at six.');
|
|
1283
|
+
await flush();
|
|
1284
|
+
expect(sentJson(ws)).toEqual([
|
|
1285
|
+
{ type: 'delegation_delta', delegationId: 'd1', text: 'We open at eight. ' },
|
|
1286
|
+
{ type: 'delegation_delta', delegationId: 'd1', text: 'We close at six.' },
|
|
1287
|
+
]);
|
|
1288
|
+
pending[0]({ status: 'completed', text: 'We open at eight. We close at six.', streamedChars: 34 });
|
|
1289
|
+
await flush();
|
|
1290
|
+
expect(sentJson(ws).at(-1)).toEqual({
|
|
1291
|
+
type: 'delegation_result',
|
|
1292
|
+
delegationId: 'd1',
|
|
1293
|
+
status: 'completed',
|
|
1294
|
+
text: 'We open at eight. We close at six.',
|
|
1295
|
+
streamedChars: 34,
|
|
1296
|
+
});
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
it('omits streamedChars when nothing streamed, and puts it on a parked turn\'s update', async () => {
|
|
1300
|
+
const { ws, calls, pending } = await startDelegatedCall({}, { capabilities: STREAM_CAPS });
|
|
1301
|
+
delegate(ws, 'd1', 'q');
|
|
1302
|
+
await flush();
|
|
1303
|
+
pending[0]({ status: 'completed', text: 'Answer.', streamedChars: 0 });
|
|
1304
|
+
await flush();
|
|
1305
|
+
expect(sentJson(ws)).toEqual([{ type: 'delegation_result', delegationId: 'd1', status: 'completed', text: 'Answer.' }]);
|
|
1306
|
+
|
|
1307
|
+
delegate(ws, 'd2', 'order');
|
|
1308
|
+
await flush();
|
|
1309
|
+
calls[1].stream!('I can place that order. ');
|
|
1310
|
+
pending[1]({
|
|
1311
|
+
status: 'pending_approval',
|
|
1312
|
+
text: 'I can place that order.\n\nThis action needs approval.',
|
|
1313
|
+
streamedChars: 23,
|
|
1314
|
+
followUp: () => new Promise(() => {}),
|
|
1315
|
+
});
|
|
1316
|
+
await flush();
|
|
1317
|
+
expect(sentJson(ws).slice(1)).toEqual([
|
|
1318
|
+
{ type: 'delegation_delta', delegationId: 'd2', text: 'I can place that order. ' },
|
|
1319
|
+
{
|
|
1320
|
+
type: 'delegation_update',
|
|
1321
|
+
delegationId: 'd2',
|
|
1322
|
+
status: 'pending_approval',
|
|
1323
|
+
text: 'I can place that order.\n\nThis action needs approval.',
|
|
1324
|
+
streamedChars: 23,
|
|
1325
|
+
},
|
|
1326
|
+
]);
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1329
|
+
it('does not stream when delegation_read_back is missing (a multi-utterance read-back could not stay folded)', async () => {
|
|
1330
|
+
const { ws, calls, pending } = await startDelegatedCall({}, { capabilities: [...ALL_CAPS, 'delegation_stream'] });
|
|
1331
|
+
delegate(ws, 'd1', 'q');
|
|
1332
|
+
await flush();
|
|
1333
|
+
expect(calls[0]).not.toHaveProperty('stream');
|
|
1334
|
+
pending[0]({ status: 'completed', text: 'Answer.', streamedChars: 7 });
|
|
1335
|
+
await flush();
|
|
1336
|
+
expect(sentJson(ws)).toEqual([{ type: 'delegation_result', delegationId: 'd1', status: 'completed', text: 'Answer.' }]);
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
it('a cancellation after progress disarms that delegation\'s fold, not another\'s', async () => {
|
|
1340
|
+
const { ws, calls, transcripts } = await startDelegatedCall({}, { capabilities: STREAM_CAPS });
|
|
1341
|
+
delegate(ws, 'd1', 'q');
|
|
1342
|
+
await flush();
|
|
1343
|
+
calls[0].stream!('It is sunny. ');
|
|
1344
|
+
await flush();
|
|
1345
|
+
progress(ws, 'd1');
|
|
1346
|
+
ws.triggerMessage(JSON.stringify({ type: 'delegation_cancelled', delegationId: 'dx', reason: 'deadline' }));
|
|
1347
|
+
ws.triggerMessage(JSON.stringify({ type: 'delegation_cancelled', delegationId: 'd1', reason: 'provider_cancelled' }));
|
|
1348
|
+
ws.triggerMessage(update('assistant', 'Something unrelated.', 'x1')); // read-back never started: renders
|
|
1349
|
+
progress(ws, 'd1'); // cancelled: no frames, and no re-arm
|
|
1350
|
+
ws.triggerMessage(update('assistant', 'Still here.', 'x2'));
|
|
1351
|
+
expect(transcripts.map((t) => t[1])).toEqual(['Something unrelated.', 'Still here.']);
|
|
1352
|
+
});
|
|
1353
|
+
|
|
1354
|
+
it('a refusing warning after progress disarms the fold too', async () => {
|
|
1355
|
+
const { ws, calls, transcripts } = await startDelegatedCall({}, { capabilities: STREAM_CAPS });
|
|
1356
|
+
delegate(ws, 'd1', 'q');
|
|
1357
|
+
await flush();
|
|
1358
|
+
calls[0].stream!('It is sunny. ');
|
|
1359
|
+
await flush();
|
|
1360
|
+
progress(ws, 'd1');
|
|
1361
|
+
ws.triggerMessage(JSON.stringify({ type: 'warning', code: 'UNKNOWN_DELEGATION', delegationId: 'd1' }));
|
|
1362
|
+
ws.triggerMessage(update('assistant', 'Something unrelated.', 'x1'));
|
|
1363
|
+
expect(transcripts.map((t) => t[1])).toEqual(['Something unrelated.']);
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
it('streams nothing (and sends no streamedChars) when the server did not grant it', async () => {
|
|
1367
|
+
const { ws, calls, pending } = await startDelegatedCall();
|
|
1368
|
+
delegate(ws, 'd1', 'q');
|
|
1369
|
+
await flush();
|
|
1370
|
+
expect(calls[0]).not.toHaveProperty('stream');
|
|
1371
|
+
pending[0]({ status: 'completed', text: 'Answer.', streamedChars: 7 });
|
|
1372
|
+
await flush();
|
|
1373
|
+
expect(sentJson(ws)).toEqual([{ type: 'delegation_result', delegationId: 'd1', status: 'completed', text: 'Answer.' }]);
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
it('sends deltas after a slow call context, and nothing once the delegation is cancelled', async () => {
|
|
1377
|
+
let resolveContext!: (text: string) => void;
|
|
1378
|
+
const { ws, calls, pending } = await startDelegatedCall(
|
|
1379
|
+
{ callContext: () => new Promise<string>((resolve) => (resolveContext = resolve)) },
|
|
1380
|
+
{ capabilities: STREAM_CAPS },
|
|
1381
|
+
);
|
|
1382
|
+
delegate(ws, 'd1', 'q');
|
|
1383
|
+
await flush();
|
|
1384
|
+
calls[0].stream!('First piece. ');
|
|
1385
|
+
await flush();
|
|
1386
|
+
expect(sentJson(ws)).toEqual([]);
|
|
1387
|
+
resolveContext('On /docs.');
|
|
1388
|
+
await flush();
|
|
1389
|
+
expect(sentJson(ws).map((f) => f.type)).toEqual(['context', 'delegation_delta']);
|
|
1390
|
+
ws.triggerMessage(JSON.stringify({ type: 'delegation_cancelled', delegationId: 'd1', reason: 'provider_cancelled' }));
|
|
1391
|
+
calls[0].stream!('Second piece.');
|
|
1392
|
+
pending[0]({ status: 'completed', text: 'First piece. Second piece.', streamedChars: 26 });
|
|
1393
|
+
await flush();
|
|
1394
|
+
expect(sentJson(ws).map((f) => f.type)).toEqual(['context', 'delegation_delta']);
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
it('folds the read-back on the first delegation_progress, once per spoken phase', async () => {
|
|
1398
|
+
const { provider, ws, calls, transcripts, pending } = await startDelegatedCall({}, { capabilities: STREAM_CAPS });
|
|
1399
|
+
const statuses: string[] = [];
|
|
1400
|
+
provider.onStatusChange((s) => statuses.push(s));
|
|
1401
|
+
ws.triggerMessage(update('assistant', 'Let me check.', 'f1'));
|
|
1402
|
+
delegate(ws, 'd1', 'q');
|
|
1403
|
+
await flush();
|
|
1404
|
+
calls[0].stream!('It is sunny. ');
|
|
1405
|
+
await flush();
|
|
1406
|
+
progress(ws, 'd1', 'It is sunny.');
|
|
1407
|
+
ws.triggerMessage(update('assistant', 'Let me check. Okay.', 'f1')); // the filler keeps updating
|
|
1408
|
+
ws.triggerMessage(update('assistant', 'It is sunny.', 'r1')); // read-back: folded
|
|
1409
|
+
progress(ws, 'd1', 'And warm.'); // a later chunk of the same phase
|
|
1410
|
+
ws.triggerMessage(update('assistant', 'It is sunny. And warm.', 'r1'));
|
|
1411
|
+
pending[0]({ status: 'completed', text: 'It is sunny. And warm.', streamedChars: 13 });
|
|
1412
|
+
await flush();
|
|
1413
|
+
completed(ws, 'd1'); // the phase ends: no second fold
|
|
1414
|
+
ws.triggerMessage(update('assistant', 'Anything else?', 'a2'));
|
|
1415
|
+
expect(transcripts.map((t) => t[1])).toEqual(['Let me check.', 'Let me check. Okay.', 'Anything else?']);
|
|
1416
|
+
// Progress keeps the call "processing" until delegation_completed.
|
|
1417
|
+
expect(statuses.filter((s) => s !== 'processing')).toEqual(['listening']);
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
it('folds a single-shot result\'s read-back on progress, and ignores progress for delegations it did not answer', async () => {
|
|
1421
|
+
const { ws, transcripts, pending } = await startDelegatedCall({}, { capabilities: STREAM_CAPS });
|
|
1422
|
+
progress(ws, 'dx');
|
|
1423
|
+
ws.triggerMessage(update('assistant', 'Hello there.', 'g1')); // unrelated: renders
|
|
1424
|
+
delegate(ws, 'd1', 'q');
|
|
1425
|
+
await flush();
|
|
1426
|
+
pending[0]({ status: 'completed', text: 'It is sunny.' });
|
|
1427
|
+
await flush();
|
|
1428
|
+
progress(ws, 'd1');
|
|
1429
|
+
ws.triggerMessage(update('assistant', 'It is sunny.', 'r1'));
|
|
1430
|
+
ws.triggerMessage(update('user', 'thanks', 'u2'));
|
|
1431
|
+
ws.triggerMessage(update('assistant', 'You are welcome.', 'a2'));
|
|
1432
|
+
expect(transcripts.map((t) => t[1])).toEqual(['Hello there.', 'thanks', 'You are welcome.']);
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
it('folds again for a later spoken phase (the approval outcome)', async () => {
|
|
1436
|
+
const { ws, transcripts, pending } = await startDelegatedCall({}, { capabilities: STREAM_CAPS });
|
|
1437
|
+
let settle!: (f: VoiceDelegationFollowUp | null) => void;
|
|
1438
|
+
delegate(ws, 'd1', 'order');
|
|
1439
|
+
await flush();
|
|
1440
|
+
pending[0]({ status: 'pending_approval', text: 'Approve it.', followUp: () => new Promise((r) => (settle = r)) });
|
|
1441
|
+
await flush();
|
|
1442
|
+
progress(ws, 'd1');
|
|
1443
|
+
ws.triggerMessage(update('assistant', 'Please approve it.', 'r1'));
|
|
1444
|
+
completed(ws, 'd1', false);
|
|
1445
|
+
settle({ status: 'completed', text: 'Order placed.' });
|
|
1446
|
+
await flush();
|
|
1447
|
+
progress(ws, 'd1');
|
|
1448
|
+
ws.triggerMessage(update('assistant', 'Your order is placed.', 'r2'));
|
|
1449
|
+
completed(ws, 'd1');
|
|
1450
|
+
ws.triggerMessage(update('assistant', 'Anything else?', 'a3'));
|
|
1451
|
+
expect(transcripts.map((t) => t[1])).toEqual(['Anything else?']);
|
|
1452
|
+
});
|
|
1453
|
+
|
|
1454
|
+
it('folds an untagged read-back after delegation_progress on a tag-negotiated server (the cut-filler continuation)', async () => {
|
|
1455
|
+
const { ws, calls, transcripts, pending } = await startDelegatedCall({}, { capabilities: STREAM_CAPS });
|
|
1456
|
+
const frame = (turnId: string, text: string, final: boolean, delegationId?: string) =>
|
|
1457
|
+
ws.triggerMessage(
|
|
1458
|
+
JSON.stringify({ type: 'transcript_update', role: 'assistant', text, utteranceId: turnId, final, delegationId }),
|
|
1459
|
+
);
|
|
1460
|
+
// GPT-Live's filler A is streaming when the delegation starts.
|
|
1461
|
+
frame('a1', 'Let me check', false);
|
|
1462
|
+
delegate(ws, 'd1', 'hours?');
|
|
1463
|
+
await flush();
|
|
1464
|
+
calls[0].stream!("We're open Monday to Friday from 8 to 6. ");
|
|
1465
|
+
await flush();
|
|
1466
|
+
expect(sentJson(ws).map((f) => f.type)).toEqual(['delegation_delta']);
|
|
1467
|
+
// Core cuts A (its final carries the start of the answer), reports progress...
|
|
1468
|
+
frame('a1', "Let me check on that for you. We're open Monday", true);
|
|
1469
|
+
progress(ws, 'd1');
|
|
1470
|
+
// ...and GPT-Live continues in a NEW utterance B that nobody tagged: folded.
|
|
1471
|
+
frame('b1', 'to Friday from 8 to 6.', false);
|
|
1472
|
+
frame('b1', 'to Friday from 8 to 6. Saturday 9 to 4.', true);
|
|
1473
|
+
pending[0]({ status: 'completed', text: "We're open Monday to Friday from 8 to 6.", streamedChars: 40 });
|
|
1474
|
+
await flush();
|
|
1475
|
+
completed(ws, 'd1'); // the tag server's completion arms nothing
|
|
1476
|
+
frame('n1', 'Anything else?', true); // renders
|
|
1477
|
+
// A later read-back tagged with the delegation folds by its tag.
|
|
1478
|
+
ws.triggerMessage(update('user', 'and sundays', 'u2'));
|
|
1479
|
+
frame('c1', 'Closed on Sundays.', true, 'd1');
|
|
1480
|
+
expect(transcripts.map((t) => t[1])).toEqual([
|
|
1481
|
+
'Let me check',
|
|
1482
|
+
"Let me check on that for you. We're open Monday",
|
|
1483
|
+
'Anything else?',
|
|
1484
|
+
'and sundays',
|
|
1485
|
+
]);
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
it('a user transcript clears a progress-armed fold, and a tag fold consumes it (tag-negotiated server)', async () => {
|
|
1489
|
+
const { ws, calls, transcripts, pending } = await startDelegatedCall({}, { capabilities: STREAM_CAPS });
|
|
1490
|
+
const frame = (turnId: string, text: string, delegationId?: string) =>
|
|
1491
|
+
ws.triggerMessage(
|
|
1492
|
+
JSON.stringify({ type: 'transcript_update', role: 'assistant', text, utteranceId: turnId, final: true, delegationId }),
|
|
1493
|
+
);
|
|
1494
|
+
delegate(ws, 'd1', 'order');
|
|
1495
|
+
await flush();
|
|
1496
|
+
calls[0].stream!('I can do that. ');
|
|
1497
|
+
await flush();
|
|
1498
|
+
progress(ws, 'd1');
|
|
1499
|
+
ws.triggerMessage(update('user', 'wait', 'u1')); // the visitor spoke first: disarmed
|
|
1500
|
+
frame('x1', 'Sure, go ahead.'); // renders
|
|
1501
|
+
progress(ws, 'd1'); // same phase: no re-arm
|
|
1502
|
+
frame('x2', 'Still here.'); // renders
|
|
1503
|
+
// The approval ask ends that phase; the outcome's phase arms again.
|
|
1504
|
+
pending[0]({ status: 'pending_approval', text: 'I can do that.\n\nApprove it.', streamedChars: 14, followUp: () => new Promise(() => {}) });
|
|
1505
|
+
await flush();
|
|
1506
|
+
completed(ws, 'd1', false);
|
|
1507
|
+
progress(ws, 'd1');
|
|
1508
|
+
frame('r2', 'Your order is placed.', 'd1'); // folded by its tag, consuming the armed fold
|
|
1509
|
+
frame('x3', 'Anything else?'); // renders
|
|
1510
|
+
expect(transcripts.map((t) => t[1])).toEqual(['wait', 'Sure, go ahead.', 'Still here.', 'Anything else?']);
|
|
1511
|
+
});
|
|
1512
|
+
|
|
1513
|
+
it('folds a tagged read-back that starts while the answer is still streaming (delegation_read_back)', async () => {
|
|
1514
|
+
const { ws, calls, transcripts } = await startDelegatedCall({}, { capabilities: STREAM_CAPS });
|
|
1515
|
+
delegate(ws, 'd1', 'q');
|
|
1516
|
+
await flush();
|
|
1517
|
+
calls[0].stream!('It is sunny. ');
|
|
1518
|
+
await flush();
|
|
1519
|
+
ws.triggerMessage(
|
|
1520
|
+
JSON.stringify({ type: 'transcript_update', role: 'assistant', text: 'It is sunny.', utteranceId: 'r1', final: false, delegationId: 'd1' }),
|
|
1521
|
+
);
|
|
1522
|
+
expect(transcripts).toEqual([]);
|
|
1523
|
+
});
|
|
1524
|
+
});
|
|
1525
|
+
|
|
1271
1526
|
describe('approval lifecycle (delegation_update, then one terminal delegation_result)', () => {
|
|
1272
1527
|
/** A parked result whose follow-up the test settles, recording the options it got. */
|
|
1273
1528
|
const parked = () => {
|
|
@@ -1838,7 +2093,7 @@ describe('RuntypeVoiceProvider prewarm', () => {
|
|
|
1838
2093
|
provider.setSessionBridge({ getHistory: () => [], runDelegatedTurn: async () => ({ status: 'completed', text: '' }) });
|
|
1839
2094
|
provider.prewarm();
|
|
1840
2095
|
expect(lastWs().url).toBe(
|
|
1841
|
-
voiceUrl('wss://api.example.com', 'agent%2F1', ['attach', ...BASE_CAPS, 'client_delegation', 'delegation_update', 'delegation_read_back']),
|
|
2096
|
+
voiceUrl('wss://api.example.com', 'agent%2F1', ['attach', ...BASE_CAPS, 'client_delegation', 'delegation_update', 'delegation_read_back', 'delegation_stream']),
|
|
1842
2097
|
);
|
|
1843
2098
|
});
|
|
1844
2099
|
|
package/dist/chunk-3255TNY7.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var t="4.27.0";var n=t;export{n as a};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import{a as J}from"./chunk-6V3MNEKB.js";import{a as Y}from"./chunk-3255TNY7.js";var $=16e3,he=24e3,pe=4096,fe=$*2*8,me=4,ve=1380533830,ye=3e4,be=1e3,Se=5e3,we=3e4,ke=3e4,Ae=6e5,Ce="full-duplex-v1",Me=300,Te=2e3,Ee=2e3,_e="runtype-browser-v1",Ie=54e4,Pe="You're talking to an AI assistant. Voice is processed by OpenAI.",Ve=new Map([["idle_timeout","Voice call ended after a quiet period."],["max_duration","This voice call reached its time limit."],["provider_ended","The voice session ended."],["quota","Voice is unavailable right now."],["auth_expired","Voice session expired. Tap the mic to reconnect."]]),Z=["provider_error","server_restart"],V="Voice connection lost.",Re=8e3,Le=4e3,Oe=12,Q=2e3;function Ne(i,e){let t="Conversation so far:",s=e.slice(0,Le).trim(),n=Re-(s?s.length+2:0)-t.length,r=[];for(let d of i.slice(-Oe).reverse()){let a=d.content.replace(/\s+/g," ").trim();if(!a)continue;a.length>Q&&(a=`${a.slice(0,Q-1)}\u2026`);let o=`${d.role==="user"?"User":"Assistant"}: ${a}`;if(o.length+1>n)break;n-=o.length+1,r.unshift(o)}let l=r.length>0?[`${t}
|
|
2
|
-
${r.join(`
|
|
3
|
-
`)}`]:[];return s&&l.push(s),l.join(`
|
|
4
|
-
|
|
5
|
-
`)}function De(i){return i.byteLength>=44&&new DataView(i).getUint32(0,!1)===ve?new Uint8Array(i,44):new Uint8Array(i)}function ee(i){let e=i.replace(/\/+$/,"");return/^wss?:\/\//i.test(e)?e:/^https?:\/\//i.test(e)?e.replace(/^http/i,"ws"):`${typeof window<"u"&&window.location?.protocol==="https:"?"wss:":"ws:"}//${e}`}function Ue(i){return ee(i).replace(/^ws/i,"http")}function xe(i){return typeof i!="number"||!Number.isFinite(i)?we:Math.min(Ae,Math.max(ke,Math.round(i)))}var I=class{constructor(e){this.config=e;this.type="runtype";this.#e=null;this.#u=null;this.#g=[];this.#i=null;this.#r=null;this.#a=null;this.#v=[];this.#y=0;this.#n=null;this.#c=!1;this.#l=!1;this.#s=0;this.#A=!1;this.#K=Number.NEGATIVE_INFINITY;this.#o=null;this.#d=!1;this.#C=!1;this.#M=0;this.#f=0;this.#b=!1;this.bridge=null;this.#E=!1;this.#m=new Set;this.#S=new Map;this.#_=new Set;this.#I=null;this.#h=null;this.#D=!1;this.#U=!1;this.#x=!1;this.#P=null;this.#B=null;this.#W=Promise.resolve();this.#V=new Set;this.#w=!1;this.#R=new Set;this.#L=new Set;this.#J=[];this.#$=[];this.#q=[];this.#j=[];this.#H=[]}#e;#u;#g;#i;#r;#a;#v;#y;#n;#c;#l;#s;#A;#K;#o;#d;#C;#M;#f;#T;#b;#E;#m;#S;#_;#N;#I;#h;#Y;#D;#U;#x;#P;#B;#W;#V;#w;#R;#L;#J;#$;#q;#j;#H;async connect(){}setSessionBridge(e){this.bridge=e}prewarm(){if(this.#c||this.#o)return;let e=this.config?.agentId,t=this.config?.clientToken,s=this.config?.host;if(!e||!t||!s)return;let n=Date.now();if(!(n-this.#K<ye)){this.#K=n;try{this.config?.prewarmMode==="attach"?this.#re(s,e,t):this.#oe(s,e,t)}catch{}}}#oe(e,t,s){if(typeof fetch!="function")return;let n=`${Ue(e)}/v1/client/agents/${encodeURIComponent(t)}/voice/prewarm`;fetch(n,{method:"POST",headers:{Authorization:`Bearer ${s}`},keepalive:!0}).catch(()=>{})}#re(e,t,s){let n=new URLSearchParams,r=this.config?.attachIdleMs,l=xe(r);r!==void 0&&n.set("attachIdleMs",String(l));let d=new WebSocket(this.#Q(e,t,n),["runtype.bearer","runtype.attach",s]);d.binaryType="arraybuffer";let a=()=>{},o=new Promise(u=>{a=u}),c={ws:d,state:"connecting",ready:o,settle:a,openedAt:0,idleTimer:void 0};this.#o=c,d.onopen=()=>{this.#o===c&&(c.state=d.protocol==="runtype.attach"?"attached":"live",c.openedAt=Date.now(),this.#Z(c,l),c.state==="attached"&&d.send('{"type":"ping"}'),c.settle())},d.onmessage=u=>{if(this.#o!==c||typeof u.data!="string")return;let g;try{g=JSON.parse(u.data)}catch{return}g.type==="attached"&&typeof g.idleMs=="number"&&this.#Z(c,g.idleMs-(Date.now()-c.openedAt))},d.onerror=()=>{},d.onclose=()=>{this.#o===c&&this.#O()}}#Z(e,t){clearTimeout(e.idleTimer),e.idleTimer=setTimeout(()=>{this.#o===e&&this.#O()},Math.max(0,t))}#O(){let e=this.#o;if(!e)return;this.#o=null,clearTimeout(e.idleTimer);let{ws:t}=e;if(t.onopen=null,t.onmessage=null,t.onerror=null,t.onclose=null,e.settle(),t.readyState===WebSocket.CONNECTING||t.readyState===WebSocket.OPEN)try{t.close(1e3,"prewarm released")}catch{}}async#ae(){let e=this.#o;if(!e)return null;if(e.state==="connecting"){let t;await Promise.race([e.ready,new Promise(s=>{t=setTimeout(s,Se)})]),clearTimeout(t)}return this.#o!==e?null:e.state==="connecting"||e.ws.readyState!==WebSocket.OPEN?(this.#O(),null):(this.#o=null,clearTimeout(e.idleTimer),{ws:e.ws,live:e.state==="live"})}#Q(e,t,s){let n=["partial_transcript","context"];this.bridge&&this.config?.clientDelegation!==!1&&n.push("client_delegation","delegation_update","delegation_read_back"),s&&n.unshift("attach");let r=new URLSearchParams({voiceProtocol:_e,clientVersion:`persona/${Y}`,voiceCapabilities:Ce,clientCapabilities:n.join(",")});return s?.forEach((l,d)=>r.set(d,l)),`${ee(e)}/ws/agents/${encodeURIComponent(t)}/voice?${r}`}async startListening(){if(this.#c)return;let e=this.#U;e||(this.#D=!1,this.#h=null),this.#U=!1;let t=this.config?.agentId,s=this.config?.clientToken,n=this.config?.host;if(!t)throw new Error("Runtype voice requires an agentId");if(!s)throw new Error("Runtype voice requires a clientToken");if(!n)throw new Error("Runtype voice requires a host (or widget apiUrl)");let r=++this.#s;this.#A=!1,this.#c=!0;try{let l=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:$,channelCount:1,echoCancellation:!0}});if(r!==this.#s){l.getTracks().forEach(g=>g.stop());return}this.#i=l;let d=window.AudioContext||window.webkitAudioContext,a=new d({sampleRate:$});if(this.#u=a,a.state==="suspended"){if(await Promise.race([a.resume().catch(()=>{}),new Promise(g=>setTimeout(g,e?500:be))]),r!==this.#s)return;if(a.state!=="running")throw e?new Error(this.#h=`${V} Tap the mic to reconnect.`):new Error("Audio blocked until a tap")}let o=this.config?.createPlaybackEngine?await this.config.createPlaybackEngine():new J(he);if(r!==this.#s){o.destroy(),l.getTracks().forEach(g=>g.stop()),a.close().catch(()=>{});return}this.#n=o,this.#X(),this.#le(a,l,r);let c=await this.#ae();if(r!==this.#s){c?.ws.close(1e3,"client ended call");return}if(c){let{ws:g}=c;this.#e=g,this.#ee(g,r),c.live||g.send('{"type":"start"}'),this.#F(g),this.#t("listening");return}let u=new WebSocket(this.#Q(n,t),["runtype.bearer",s]);u.binaryType="arraybuffer",this.#e=u,u.onopen=()=>{r===this.#s&&(this.#F(u),this.#t("listening"))},this.#ee(u,r)}catch(l){throw this.#p(),this.#k(l),this.#t("error"),l}}#ee(e,t){e.onmessage=s=>this.#de(s,t),e.onerror=()=>{t!==this.#s||this.#I||(this.#k(new Error("Voice connection failed")),this.#t("error"),this.#p())},e.onclose=s=>{if(this.#A){this.#A=!1;return}if(t!==this.#s)return;let n=this.#I??(s.code===4408?{reason:"idle_timeout"}:null);if(!n){if(s.code!==1e3){let r=s.code?` (code ${s.code})`:"";this.#k(new Error(`Voice connection closed${r}`)),this.#t("error")}else this.#t("idle");this.#p();return}this.#ce(n.reason,n.message)}}#ce(e,t){let s=Z.includes(e)&&!this.#D;if(this.#p(),s){this.#D=!0,this.#c=!0,this.#h=e==="provider_error"?`${V} Reconnecting\u2026`:"Reconnecting\u2026",this.#Y=setTimeout(()=>{this.#c=!1,this.#U=!0,this.startListening().catch(()=>{})},1e3+Math.random()*1e3),this.#t("idle");return}if(Z.includes(e))return this.#h=V,this.#k(new Error(V)),this.#t("error");if(e==="error")return this.#t("error");this.#h=e==="quota"&&t||Ve.get(e)||null,this.#t("idle")}takeNotice(){let e=this.#h;return this.#h=null,e}getDisclosure(){let e=this.config?.disclosureText;return this.#c&&this.#d&&e!==!1?e||Pe:null}async stopListening(){this.#p(),this.#t("idle")}async disconnect(){this.#p(),this.#t("disconnected"),this.#J=[],this.#$=[],this.#q=[],this.#j=[],this.#H=[]}stopPlayback(){this.#ne();let e=this.#e;e&&e.readyState===WebSocket.OPEN&&(this.#d&&(this.#M=Date.now()+Te,e.send('{"type":"cancel"}')),this.#t("listening"))}getInterruptionMode(){return"barge-in"}isBargeInActive(){return this.#c}onLevel(e){this.#g.push(e)}async deactivateBargeIn(){this.#p(),this.#t("idle")}#le(e,t,s){let n=e.createMediaStreamSource(t);this.#r=n;let r=e.createScriptProcessor(pe,1,1);this.#a=r,r.onaudioprocess=l=>{if(s!==this.#s)return;let d=this.#e,a=d?.readyState===WebSocket.OPEN;if(d&&!a&&d.readyState!==WebSocket.CONNECTING)return;let o=l.inputBuffer.getChannelData(0),c=new Int16Array(o.length),u=0;for(let g=0;g<o.length;g++){let m=Math.max(-1,Math.min(1,o[g]));u+=m*m,c[g]=m<0?m*32768:m*32767}if(d&&a)this.#F(d),d.send(c.buffer);else for(this.#v.push(c.buffer),this.#y+=c.byteLength;this.#y>fe;)this.#y-=this.#v.shift().byteLength;if(this.#g.length>0){let g=Math.sqrt(u/o.length),m=Math.max(0,Math.min(1,g*me));for(let h of this.#g)h(m)}},n.connect(r),r.connect(e.destination)}#F(e){for(let t of this.#v)e.send(t);this.#v=[],this.#y=0}#de(e,t){if(t!==this.#s)return;if(e.data instanceof ArrayBuffer){this.#pe(e.data,t);return}let s;try{s=JSON.parse(e.data)}catch{return}switch(s.type){case"session_config":s.speechMode&&(this.#d=s.speechMode==="speech_to_speech",this.#n?.setContinuousMode?.(this.#d)),Array.isArray(s.capabilities)&&(this.#m=new Set(s.capabilities)),typeof s.callId=="string"&&(this.#N=s.callId),this.#E=this.#d&&this.#m.has("client_delegation")&&!!this.bridge,this.#d&&!this.#l&&this.#t("listening"),this.#m.has("context")&&!this.#x&&(this.#x=!0,this.#P=this.#ue());break;case"transcript_update":{let n=s.role==="assistant"?"assistant":"user",r=s.utteranceId??s.turnId;if(!r||n==="assistant"&&this.#z())break;let l=String(r);if(n==="user")s.final===!0&&this.#te(t),this.#w=!1;else{if(this.#m.has("delegation_read_back")){let d=s.delegationId;typeof d=="string"&&this.#V.has(d)&&!this.#R.has(l)&&this.#L.add(l)}else this.#w&&!this.#R.has(l)&&(this.#L.add(l),this.#w=!1);if(this.#R.add(l),this.#L.has(l))break}this.#G(n,s.text??"",s.final===!0,{turnId:l,...typeof s.startMs=="number"&&{startMs:s.startMs},...typeof s.endMs=="number"&&{endMs:s.endMs},...this.#E&&{caption:!0}});break}case"delegation_started":case"delegation_completed":{let n=String(s.delegationId??s.turnId);if(s.type==="delegation_started"){this.#te(t);let r=s.input;this.#E&&r&&this.#he(n,{delegationId:n,userText:String(r.text??""),userUtteranceIds:Array.isArray(r.userUtteranceIds)?r.userUtteranceIds.filter(l=>typeof l=="string"):[],messages:Array.isArray(r.messages)?r.messages:[]},t)}else s.speak!==!1&&this.#V.has(n)&&(this.#w=!0);this.#C=s.type==="delegation_started",!this.#l&&!this.#z()&&this.#t(this.#C?"processing":"listening");break}case"delegation_cancelled":this.#se(String(s.delegationId),s.reason!=="session_ending"&&s.reason!=="provider_cancelled");break;case"warning":console.warn(`[Persona voice] ${s.code}: ${s.message??""}`,this.#N??""),s.delegationId&&(s.code==="UNKNOWN_DELEGATION"||s.code==="LATE_RESULT_LIMIT")&&this.#se(String(s.delegationId));break;case"audio_clear":this.#ne(),this.#M=0,this.#t("listening");break;case"transcript_interim":this.#t("listening"),this.#G("user",s.text??"",!1);break;case"transcript_final":{let n=s.role==="assistant"?"assistant":"user";this.#t(n==="user"?"processing":"speaking"),this.#G(n,s.text??"",!0);break}case"audio_end":this.#n?this.#n.markStreamEnd():(this.#l=!1,this.#t("listening"));break;case"metrics":this.#me({llmMs:s.llm_ms,ttsMs:s.tts_ms,firstAudioMs:s.first_audio_ms,totalMs:s.total_ms});break;case"error":this.#k(new Error(s.error||"Voice error")),this.#t("error");break;case"session_end":this.#I={reason:String(s.reason),...typeof s.message=="string"&&{message:s.message}};break}}async#ue(){let e=this.bridge?.getHistory()??[],t="";try{let s=this.config?.callContext;t=String((typeof s=="function"?await s():s)??"")}catch{}return Ne(e,t)}#te(e){let t=this.#P;if(!t)return;this.#P=null;let s=!1,n=t.then(r=>{let l=this.#e;s||!r||e!==this.#s||l?.readyState!==WebSocket.OPEN||l.send(JSON.stringify({type:"context",text:r}))});this.#B={done:n,drop:()=>s=!0}}async#ge(){let e=this.#B;if(!e)return;let t,s=await Promise.race([e.done.then(()=>!1),new Promise(n=>{t=setTimeout(()=>n(!0),Ee)})]);clearTimeout(t),s&&e.drop()}#se(e,t=!1){this.#_.add(e),this.bridge?.dropDelegation?.(e,t),this.#S.get(e)?.abort()}#he(e,t,s){let n=this.bridge,r=(l,d,a)=>{let o=this.#e;return s!==this.#s||o?.readyState!==WebSocket.OPEN||this.#_.has(e)?!1:(o.send(JSON.stringify({type:l,delegationId:e,status:d,text:a})),!0)};this.#W=this.#W.then(async()=>{if(s!==this.#s||this.#_.has(e))return;let l=await n.runDelegatedTurn(t).catch(()=>({status:"failed",text:""}));await this.#ge();let d=!!l.followUp,a=d&&this.#m.has("delegation_update");if(!r(a?"delegation_update":"delegation_result",d&&!a?"completed":l.status,l.text)||(l.status!=="failed"&&l.inChat!==!1&&this.#V.add(e),!l.followUp))return;let c=new AbortController;this.#S.set(e,c),l.followUp({signal:c.signal,approvalTimeoutMs:this.config?.approvalTimeoutMs&&Math.min(this.config.approvalTimeoutMs,Ie),readBack:a,onUpdate:u=>r("delegation_update","pending_approval",u)}).then(u=>{this.#S.delete(e),a&&u&&!c.signal.aborted&&r("delegation_result",u.status,u.text)})})}#pe(e,t){if(t!==this.#s||!this.#n||this.#z())return;let s=De(e);s.length!==0&&(this.#l||(this.#l=!0,this.#t("speaking")),this.#X(),this.#n.enqueue(s),this.#d&&this.#fe(s.length))}#z(){return Date.now()<this.#M}#X(){if(this.#b||!this.#n)return;this.#b=!0;let e=this.#s;this.#n.onFinished(()=>{e===this.#s&&(this.#b=!1,this.#ie())})}#fe(e){let t=Date.now();this.#f=Math.max(t,this.#f)+e/48,clearTimeout(this.#T),this.#T=setTimeout(()=>this.#ie(),this.#f-t+Me)}#ie(){this.#l=!1,this.#f=0,this.#e&&this.#e.readyState===WebSocket.OPEN&&this.#t(this.#C?"processing":"listening")}#ne(){this.#n?.flush(),this.#b=!1,this.#X(),this.#l=!1,this.#f=0,clearTimeout(this.#T)}#p(){this.#s+=1,this.#c=!1,this.#l=!1,this.#O(),this.#d=!1,this.#C=!1,this.#E=!1,this.#m=new Set;for(let e of this.#S.values())e.abort();if(this.#S.clear(),this.#_.clear(),this.#N=void 0,this.#I=null,clearTimeout(this.#Y),this.#x=!1,this.#P=null,this.#B=null,this.#W=Promise.resolve(),this.#V.clear(),this.#w=!1,this.#R.clear(),this.#L.clear(),this.#b=!1,this.#M=0,this.#f=0,clearTimeout(this.#T),this.#v=[],this.#y=0,this.#a&&(this.#a.onaudioprocess=null,this.#a.disconnect(),this.#a=null),this.#r&&(this.#r.disconnect(),this.#r=null),this.#i&&(this.#i.getTracks().forEach(e=>e.stop()),this.#i=null),this.#u&&(this.#u.close().catch(()=>{}),this.#u=null),this.#n&&(this.#n.destroy(),this.#n=null),this.#e){this.#A=!0;try{this.#e.close(1e3,"client ended call")}catch{}this.#e=null}}onResult(e){this.#J.push(e)}onError(e){this.#$.push(e)}onStatusChange(e){this.#q.push(e)}onTranscript(e){this.#j.push(e)}onMetrics(e){this.#H.push(e)}#t(e){this.#q.forEach(t=>t(e))}#k(e){this.#$.forEach(t=>t(e))}#G(e,t,s,n){this.#j.forEach(r=>r(e,t,s,n))}#me(e){this.#H.forEach(t=>t(e))}};var T=class{constructor(e={}){this.config=e;this.type="browser";this.#e=null;this.#u=[];this.#g=[];this.#i=[];this.#r=!1;this.#a=typeof window<"u"?window:void 0}#e;#u;#g;#i;#r;#a;async connect(){this.#i.forEach(e=>e("connected"))}async startListening(){try{if(this.#r)throw new Error("Already listening");if(!this.#a)throw new Error("Window object not available");let e=this.#a.SpeechRecognition||this.#a.webkitSpeechRecognition;if(!e)throw new Error("Browser speech recognition not supported");this.#e=new e,this.#e.lang=this.config?.language||"en-US",this.#e.continuous=this.config?.continuous||!1,this.#e.interimResults=!0,this.#e.onresult=t=>{let s=Array.from(t.results).map(r=>r[0]).map(r=>r.transcript).join(""),n=t.results[t.results.length-1].isFinal;this.#u.forEach(r=>r({text:s,confidence:n?.8:.5,provider:"browser"})),n&&!this.config?.continuous&&this.stopListening()},this.#e.onerror=t=>{this.#g.forEach(s=>s(new Error(t.error))),this.#i.forEach(s=>s("error"))},this.#e.onstart=()=>{this.#r=!0,this.#i.forEach(t=>t("listening"))},this.#e.onend=()=>{this.#r=!1,this.#i.forEach(t=>t("idle"))},this.#e.start()}catch(e){throw this.#g.forEach(t=>t(e)),this.#i.forEach(t=>t("error")),e}}async stopListening(){this.#e&&(this.#e.stop(),this.#e=null),this.#r=!1,this.#i.forEach(e=>e("idle"))}onResult(e){this.#u.push(e)}onError(e){this.#g.push(e)}onStatusChange(e){this.#i.push(e)}async disconnect(){await this.stopListening(),this.#i.forEach(e=>e("disconnected"))}static isSupported(){return"SpeechRecognition"in window||"webkitSpeechRecognition"in window}};function R(i){switch(i.type){case"runtype":if(!i.runtype)throw new Error("Runtype voice provider requires configuration");return new I(i.runtype);case"browser":if(!T.isSupported())throw new Error("Browser speech recognition not supported");return new T(i.browser||{});case"custom":{let e=i.custom;if(!e)throw new Error("Custom voice provider requires a `custom` provider instance or factory");let t=typeof e=="function"?e():e;if(!t||typeof t.startListening!="function")throw new Error("Custom voice provider `custom` must be a VoiceProvider (or a factory returning one)");return t}default:throw new Error(`Unknown voice provider type: ${i.type}`)}}function te(i){if(i?.type==="custom"&&i.custom)return R({type:"custom",custom:i.custom});if(i?.type==="runtype"&&i.runtype)return R({type:"runtype",runtype:i.runtype});if(T.isSupported())return R({type:"browser",browser:i?.browser||{language:"en-US"}});throw new Error("No supported voice providers available")}function Be(i){try{return te(i),!0}catch{return!1}}var se=i=>i.toLowerCase().replace(/[^\p{L}\p{N}]+/gu,""),q=class{constructor(e){this.host=e;this.turns=new Map;this.latestId=null;this.cancelled=new Set}apply(e,t,s,n,r,l){let d=this.host;t=t.trim();let a=this.turns.get(n);if(a||(a={},this.turns.set(n,a),this.latestId=n),e==="user"){a.userFinal=s;let o=a.userId?d.find(a.userId):void 0;if(o){let c=a.userStartMs===void 0&&r!==void 0;c&&(a.userStartMs=r),d.upsert({...o,content:t,voiceProcessing:!s,...c&&this.placeBefore(this.startedAfter(r))})}else{a.userStartMs=r;let c=a.assistantId?d.find(a.assistantId):void 0;a.userId=d.inject({role:"user",content:t,streaming:!1,voiceProcessing:!s,voiceCaption:l,...this.placeBefore(this.startedAfter(r)??c)}).id}}else if(!this.cancelled.has(n)){a.assistantFinal=s;let o=a.assistantId?d.find(a.assistantId):void 0;if(o){let c=a.assistantStartMs===void 0&&r!==void 0;c&&(a.assistantStartMs=r),d.upsert({...o,content:t,streaming:!s,voiceProcessing:!s,...c&&this.placeBefore(this.startedAfter(r))})}else t&&(a.assistantStartMs=r,a.assistantId=d.inject({role:"assistant",content:t,streaming:!s,voiceProcessing:!s,voiceCaption:l,...this.placeBefore(this.startedAfter(r))}).id);s&&a.assistantId&&d.markSpoken(a.assistantId)}this.sync()}claimUserTurn(e,t=[]){let s=t[t.length-1];for(let r of t)r!==s&&(this.turns.has(r)||this.turns.set(r,{}),this.turns.get(r).claimed=!0);let n;if(s){if(n=this.turns.get(s),n||(n={},this.turns.set(s,n)),n.claimed)return null;(!n.userId||!this.host.find(n.userId))&&(n.userFinal=!0,n.userId=this.host.inject({role:"user",content:e.trim()}).id)}else{let r=se(e),l=[...this.turns.values()].reverse().flatMap(d=>{let a=d.userId&&!d.claimed?this.host.find(d.userId):void 0;return a?[{turn:d,text:se(a.content)}]:[]}).filter(({text:d})=>d&&r);n=(l.find(({text:d})=>d===r)??l.find(({text:d})=>d.startsWith(r)||r.startsWith(d)))?.turn}return n?(n.claimed=!0,n.userId):null}cancel(){let e=this.inFlight();if(e.length===0){let t=this.awaiting();if(!t)return;e=[t]}for(let t of e)this.cancelled.add(t);this.closeReplies(e),this.sync()}fail(e){let t=this.awaiting(),s=this.inFlight();if(!t&&s.length===0)return;for(let l of s)this.turns.get(l).assistantFinal=!0;this.closeReplies(s);let n=this.host.inject({role:"assistant",content:e,streaming:!1,voiceProcessing:!1}),r=t?this.turns.get(t):void 0;r&&(r.assistantId=n.id,r.assistantFinal=!0),this.sync()}settle(){if(this.turns.size===0)return;let e=this.pending(),t=new Set;for(let s of this.turns.values())s.userId&&t.add(s.userId),s.assistantId&&(t.add(s.assistantId),this.host.markSpoken(s.assistantId));this.turns.clear(),this.latestId=null,this.host.settle(t),e&&this.host.setStreaming(!1)}startedAfter(e){if(e===void 0)return;let t;for(let s of this.turns.values())for(let[n,r]of[[s.userId,s.userStartMs],[s.assistantId,s.assistantStartMs]])n&&r!==void 0&&r>e&&(!t||r<t.start)&&(t={start:r,id:n});return t&&this.host.find(t.id)}placeBefore(e){if(!e)return;let t=e.sequence??0,s=t-1;for(let n of this.turns.values())for(let r of[n.userId,n.assistantId]){let l=r?this.host.find(r):void 0,d=l?.sequence;l?.createdAt===e.createdAt&&d!==void 0&&d>s&&d<t&&(s=d)}return{createdAt:e.createdAt,sequence:(s+t)/2}}closeReplies(e){let t=new Set;for(let s of e){let n=this.turns.get(s)?.assistantId;n&&(t.add(n),this.host.markSpoken(n))}t.size>0&&this.host.settle(t)}inFlight(){let e=[];for(let[t,s]of this.turns)s.assistantId&&!s.assistantFinal&&!this.cancelled.has(t)&&e.push(t);return e}awaiting(){let e=this.latestId;if(!e)return null;let t=this.turns.get(e);return t?.userFinal&&!t.assistantId&&!t.claimed&&!this.cancelled.has(e)?e:null}pending(){return this.awaiting()!==null||this.inFlight().length>0}sync(){this.host.setStreaming(this.pending())}};var We="I need your answer in the chat before I can continue.";var ie="no|nope|never ?mind|decline(?: it| that)?|cancel(?: it| that| this)?(?: (?:the|my|that|this) (?:[a-z]+ )?(?:order|request))?|don'?t (?:do|place) (?:it|that|the (?:[a-z]+ )?order)",$e=new RegExp(`^(?:${ie})(?: (?:${ie}))?(?: thanks| thank you)?$`),qe=i=>$e.test(i.toLowerCase().replace(/[^a-z' ]+/g," ").replace(/\s+/g," ").trim()),ne="Briefly tell the user what you're about to do and ask them to approve or decline it in the chat. Don't claim it's done.",oe=()=>new Promise(i=>setTimeout(i,50)),j=(i,e)=>i.length>e?`${i.slice(0,e-1).trimEnd()}\u2026`:i,_=i=>i.replace(/^webmcp:/,"").replace(/([a-z\d])([A-Z])/g,"$1 $2").replace(/[_\-.]+/g," ").trim().toLowerCase();function H(i,e=0){if(i==null||i==="")return"";if(typeof i!="object")return String(i);if(e>2)return"";if(Array.isArray(i))return i.map(r=>H(r,e+1)).filter(Boolean).join(", ");let t=i,s=t.quantity??t.qty??t.count,n=t.name??t.item??t.product??t.title;return s!=null&&n!=null&&typeof n!="object"?`${s} ${n}`:Object.entries(t).filter(([r])=>!r.startsWith("_")).map(([r,l])=>{let d=H(l,e+1);return d&&(e?`${_(r)} ${d}`:`${_(r)}: ${d}`)}).filter(Boolean).join(e?", ":"; ")}function re(i){let e=i.length>1?"These actions need the user's approval in the chat before they happen:":"This action needs the user's approval in the chat before it happens:",t=1e3-e.length-ne.length-2,s=i.map(n=>{let r=[_(n.toolName)];n.description&&r.push(`(${j(n.description.trim(),160)})`);let l=H(n.parameters);return l&&r.push(`with ${l}`),n.reason&&r.push(`because: ${j(n.reason.trim(),160)}`),j(`- ${r.join(" ")}`,Math.floor(t/i.length)-1)});return`${e}
|
|
6
|
-
${s.join(`
|
|
7
|
-
`)}
|
|
8
|
-
|
|
9
|
-
${ne}`}var L=i=>i.variant==="approval"&&i.approval?.status==="pending";function je(i){let e=null,t=null,s=()=>i.track(e??t),n=[],r=new Map,l=new Map,d=(h,f)=>{r.set(h,[...r.get(h)??[],f]),r.size>32&&r.delete(r.keys().next().value)},a=h=>i.messages().find(f=>f.id===h),o=h=>h.approvals.filter(f=>a(f)?.approval?.status==="pending"),c=h=>_(a(h)?.approval?.toolName??""),u=(h,f,S)=>{h.outcome={status:f,text:S};for(let v of o(h))i.decide(v)},g=h=>h.flatMap(f=>i.messages().find(S=>S.id===f)??[]).filter(f=>!f.variant&&f.content.trim()).map(f=>f.content.trim()).join(`
|
|
10
|
-
|
|
11
|
-
`),m=async h=>{do await oe();while(i.busy()&&!(h&&(i.parked()||h.failed)))};return{getHistory:()=>{let h=i.messages().filter(v=>(v.role==="user"||v.role==="assistant")&&!v.variant&&!v.voiceProcessing&&!v.streaming&&v.content),f=!1,S=[];for(let v=h.length-1;v>=0;v-=1){let k=h[v];if(k.role==="user")f=!1;else if(!k.voiceCaption)f=!0;else if(f)continue;S.unshift({role:k.role,content:k.content})}return S},dropDelegation(h,f){l.set(h,!!f),l.size>32&&l.delete(l.keys().next().value);let S=f&&n.find(v=>v.delegationId===h);S&&!S.outcome&&u(S,"timeout","");for(let v of r.get(h)??[])v.dropped=!0,v.ids.length&&i.unspoken(v.ids)},async runDelegatedTurn({delegationId:h,userText:f,userUtteranceIds:S}){let v=n.filter(p=>o(p).length),k=v.flatMap(o);if(k.length===1&&qe(f)){let p=c(k[0]);return u(v[0],"denied",""),{status:"denied",text:`Okay, I cancelled the ${p} request. Nothing was done.`,inChat:!1}}let F=i.parked()&&i.messages().every(p=>!L(p)||k.includes(p.id));i.busy()&&!F&&await m();let ae=new Set(i.messages().map(p=>p.id)),O=()=>i.messages().filter(p=>!ae.has(p.id)&&p.variant==="approval"),A={ids:[],failed:!1};d(h,A),e=A,s();try{await i.send(f,i.claim(f,S)??void 0),await m(A)}catch{A.failed=!0}finally{e=null,s()}let ce=A.ids.flatMap(p=>i.messages().find(b=>b.id===p)??[]),E=g(A.ids),P=A.failed?[]:O().filter(L),le=new Set(P.map(p=>_(p.approval.toolName)));for(let p of v){if(p.outcome)continue;for(let w of o(p).filter(N=>le.has(c(N))))p.replaced.add(w),i.decide(w);let b=c(p.approvals[0]);p.approvals.every(w=>p.replaced.has(w))?p.outcome={status:"cancelled",text:`The earlier ${b} request was replaced by the new one; it was not done.`}:F&&p.approvals.every(w=>a(w)?.approval?.status==="denied")&&(p.outcome={status:"cancelled",text:`The earlier ${b} request was replaced by the new one; it was not done.`})}if(P.length){let p=new Set(P.map(w=>w.id)),b={delegationId:h,approvals:[...p],at:Date.now(),replaced:new Set};return n.push(b),l.get(h)&&u(b,"timeout",""),E=`${E}
|
|
12
|
-
|
|
13
|
-
${re(P.map(w=>w.approval))}`.trim(),{status:"pending_approval",text:E,followUp:async({signal:w,approvalTimeoutMs:N=3e5,readBack:z=!0,onUpdate:de})=>{let C={ids:[],failed:!1};r.delete(h),d(h,C);let X=()=>O().filter(y=>p.has(y.id)||C.ids.includes(y.id)),ue=()=>O().filter(y=>L(y)&&!p.has(y.id)&&!n.some(M=>M.approvals.includes(y.id))&&(C.ids.includes(y.id)||y.approval?.toolType==="webmcp"&&t===C)),G=()=>{t=C,s()};z&&G();try{for(;!w.aborted&&!b.outcome&&(e||i.busy()||X().some(L));){z&&t!==C&&!o(b).length&&G();let y=ue();if(y.length){for(let M of y)p.add(M.id),b.approvals.push(M.id);b.at=Date.now(),de?.(re(y.map(M=>M.approval)))}if(Date.now()-b.at>=N&&o(b).length&&(!i.busy()||i.parked())){u(b,"timeout",`That ${c(b.approvals[0])} request expired, so nothing was done.`);break}await oe()}}finally{t===C&&(t=null,s()),n.splice(n.indexOf(b),1)}if(w.aborted)return null;if(b.outcome)return b.outcome;if(C.failed)return{status:"failed",text:`The ${c(b.approvals[0])} request didn't complete.`};let K=X(),D=y=>K.filter(y).map(M=>_(M.approval.toolName)).join(", "),U=K.some(y=>y.approval?.status==="approved"),x=D(y=>y.approval?.status==="denied"&&!b.replaced.has(y.id)),B=D(y=>b.replaced.has(y.id)),W=D(y=>y.approval?.status==="timeout"),ge=[U?g(C.ids):"",x&&`The user declined the ${x} request in the chat, so ${U?"that part was not done":"nothing was done"}.`,B&&`The earlier ${B} request was replaced by the new one; it was not done.`,W&&`The ${W} request timed out, so it was not done.`].filter(Boolean).join(`
|
|
14
|
-
|
|
15
|
-
`);return{status:U?"completed":x?"denied":B?"cancelled":W?"timeout":"completed",text:ge}}}}return!A.failed&&i.parked()||ce.some(p=>p.agentMetadata?.awaitingLocalTool&&!p.agentMetadata.askUserQuestionAnswered&&!p.toolCall?.name?.startsWith("webmcp:"))?{status:A.failed?"failed":"completed",text:`${E}
|
|
16
|
-
|
|
17
|
-
${We}`.trim()}:{status:!A.failed&&E?"completed":"failed",text:E}}}}var He=(i,e,t,s)=>{let n=i.session;try{i.keyed=t?new t({find:o=>n.messages.find(c=>c.id===o),inject:o=>n.injectMessage(o),upsert:o=>n.upsertMessage(o),settle:o=>{n.messages=n.messages.map(c=>o.has(c.id)?{...c,streaming:!1,voiceProcessing:!1}:c),n.callbacks.onMessagesChanged([...n.messages])},setStreaming:o=>{(o||!i.busy())&&i.setStreaming(o)},markSpoken:o=>{n.ttsSpokenMessageIds.add(o)}}):null,s&&e.setSessionBridge&&e.setSessionBridge(s({messages:()=>n.messages,busy:()=>i.busy(),parked:()=>n.webMcpApprovalResolvers.size>0,claim:(o,c)=>i.keyed?.claimUserTurn(o,c)??null,send:(o,c)=>n.sendMessage(o,{viaVoice:!0,voiceTurn:{userMessageId:c}}),track:o=>{i.setDelegation(o)},unspoken:o=>{for(let c of o)n.ttsSpokenMessageIds.delete(c);n.streaming||n.speakLatestAssistantMessage(o)},decide:o=>{if(n.webMcpApprovalResolvers.has(o))return n.resolveWebMcpApproval(o,"denied");let c=n.messages.find(u=>u.id===o)?.approval;c?.status==="pending"&&n.resolveApproval(c,"denied")}}));let r=i.generation(),l=()=>i.provider()===e&&r===i.generation(),a=(n.config.voiceRecognition??{}).processingErrorText??"Voice processing failed. Please try again.";e.onResult(o=>{l()&&o.provider!=="runtype"&&o.text&&o.text.trim()&&n.sendMessage(o.text,{viaVoice:!0})}),e.onTranscript&&e.onTranscript((o,c,u,g)=>{if(l()){if(g?.turnId){i.keyed?.apply(o,c,u,g.turnId,g.startMs,g.caption);return}if(o==="user"){if(i.pendingUser)n.upsertMessage({id:i.pendingUser,role:"user",content:c,createdAt:new Date().toISOString(),streaming:!1,voiceProcessing:!u});else{let m=n.injectMessage({role:"user",content:c,streaming:!1,voiceProcessing:!u});i.pendingUser=m.id}if(u){i.turns().start(g?.turnId),i.pendingUser=null;let m=n.injectMessage({role:"assistant",content:"",streaming:!0,voiceProcessing:!0});i.pendingAssistant=m.id,i.setStreaming(!0)}}else{if(!i.turns().accepts(g?.turnId))return;if(i.pendingAssistant)n.upsertMessage({id:i.pendingAssistant,role:"assistant",content:c,createdAt:new Date().toISOString(),streaming:!u,voiceProcessing:!u});else{let m=n.injectMessage({role:"assistant",content:c,streaming:!u,voiceProcessing:!u});i.pendingAssistant=m.id}u&&(i.pendingAssistant&&n.ttsSpokenMessageIds.add(i.pendingAssistant),i.setStreaming(!1),i.pendingAssistant=null)}}}),e.onLevel&&e.onLevel(o=>{l()&&i.setLevel(Number.isFinite(o)?Math.max(0,Math.min(1,o)):0)}),e.onMetrics&&e.onMetrics(o=>{l()&&n.config.voiceRecognition?.onMetrics?.(o)}),e.onError(o=>{l()&&(console.error("Voice error:",o),i.pendingAssistant&&(n.upsertMessage({id:i.pendingAssistant,role:"assistant",content:a,createdAt:new Date().toISOString(),streaming:!1,voiceProcessing:!1}),i.setStreaming(!1),i.pendingUser=null,i.pendingAssistant=null),i.keyed?.fail(a))}),e.onStatusChange(o=>{l()&&(i.setStatus(o),(o==="listening"||o==="idle"||o==="disconnected")&&i.settlePending(o!=="listening"),(o==="idle"||o==="disconnected")&&i.keyed?.settle(),n.callbacks.onVoiceStatusChanged?.(o))}),e.connect()}catch(r){console.error("Failed to setup voice:",r)}};var Fe=i=>{let e=a=>{if(i.original)return;let o=i.config().voiceRecognition??{};i.original={backgroundColor:a.style.backgroundColor,color:a.style.color,borderColor:a.style.borderColor,iconName:o.iconName??"mic",iconSize:parseFloat(o.iconSize??"")||i.iconFallbackPx}},t=(a,o,c)=>{let u=a.querySelector("svg");u&&u.remove();let g=i.original?.iconSize??(parseFloat(i.config().voiceRecognition?.iconSize??"")||i.iconFallbackPx),m=i.renderIcon(o,g,c,1.5);m&&a.appendChild(m)},s=a=>{a.classList.remove("persona-voice-recording","persona-voice-processing","persona-voice-speaking")};return{recording:()=>{let a=i.mic();if(!a)return;e(a);let o=i.config().voiceRecognition??{},c=o.recordingBackgroundColor,u=o.recordingIconColor,g=o.recordingBorderColor;if(s(a),a.classList.add("persona-voice-recording"),i.setMicState("recording"),a.style.backgroundColor=c??"var(--persona-voice-recording-bg, #ef4444)",a.style.color=u??"var(--persona-voice-recording-indicator, #ffffff)",u){let m=a.querySelector("svg");m&&m.setAttribute("stroke",u)}g&&(a.style.borderColor=g),a.setAttribute("aria-label","Stop voice recognition")},processing:()=>{let a=i.mic();if(!a)return;e(a);let o=i.config().voiceRecognition??{},c=i.session().getVoiceInterruptionMode(),u=i.original,g=o.processingIconName??"loader",m=o.processingIconColor??u?.color??"",h=o.processingBackgroundColor??u?.backgroundColor??"",f=o.processingBorderColor??u?.borderColor??"";s(a),a.classList.add("persona-voice-processing"),i.setMicState("processing"),a.style.backgroundColor=h,a.style.borderColor=f;let S=m||"currentColor";a.style.color=S,t(a,g,S),a.setAttribute("aria-label","Processing voice input"),c==="none"&&(a.style.cursor="default")},speaking:()=>{let a=i.mic();if(!a)return;e(a);let o=i.config().voiceRecognition??{},c=i.session().getVoiceInterruptionMode(),u=i.original,g=c==="cancel"?"square":c==="barge-in"?"mic":"volume-2",m=o.speakingIconName??g,h=o.speakingIconColor??(c==="barge-in"?o.recordingIconColor??u?.color??"":u?.color??""),f=o.speakingBackgroundColor??(c==="barge-in"?o.recordingBackgroundColor??"var(--persona-voice-recording-bg, #ef4444)":u?.backgroundColor??""),S=o.speakingBorderColor??(c==="barge-in"?o.recordingBorderColor??"":u?.borderColor??"");s(a),a.classList.add("persona-voice-speaking"),i.setMicState("speaking"),a.style.backgroundColor=f,a.style.borderColor=S;let v=h||"currentColor";a.style.color=v,t(a,m,v);let k=c==="cancel"?"Stop playback and re-record":c==="barge-in"?"Speak to interrupt":"Agent is speaking";a.setAttribute("aria-label",k),c==="none"&&(a.style.cursor="default"),c==="barge-in"&&a.classList.add("persona-voice-recording")},reset:()=>{let a=i.mic();if(!a)return;s(a),i.setMicState("idle");let o=i.original;o&&(a.style.backgroundColor=o.backgroundColor??"",a.style.color=o.color??"",a.style.borderColor=o.borderColor??"",t(a,o.iconName,o.color||"currentColor"),i.original=null),a.style.cursor="",a.setAttribute("aria-label","Start voice recognition")}}};export{T as BrowserVoiceProvider,q as KeyedVoiceTranscript,I as RuntypeVoiceProvider,te as createBestAvailableVoiceProvider,Fe as createMicStateStyles,R as createVoiceProvider,je as createVoiceSessionBridge,Be as isVoiceSupported,He as wireSessionVoice};
|