@runtypelabs/persona 4.26.1 → 4.27.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 +5 -0
- package/dist/approval-ui.d.ts +5 -0
- package/dist/artifacts-ui.d.cts +5 -0
- package/dist/artifacts-ui.d.ts +5 -0
- package/dist/chunk-3255TNY7.js +1 -0
- package/dist/{client-history-GOZWBDA5.js → client-history-HTRKHBAF.js} +1 -1
- package/dist/client-history.js +1 -1
- package/dist/client-stream-N22JHTLE.js +4 -0
- package/dist/client-stream.js +3 -3
- package/dist/codegen.cjs +1 -1
- package/dist/codegen.js +1 -1
- package/dist/context-mentions.d.cts +5 -0
- package/dist/context-mentions.d.ts +5 -0
- package/dist/event-stream-view.d.cts +5 -0
- package/dist/event-stream-view.d.ts +5 -0
- package/dist/forms-ui.d.cts +7 -0
- package/dist/forms-ui.d.ts +7 -0
- package/dist/index.cjs +57 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +58 -58
- package/dist/index.js.map +1 -1
- package/dist/session-reconnect.d.cts +5 -0
- package/dist/session-reconnect.d.ts +5 -0
- package/dist/smart-dom-reader.d.cts +5 -0
- package/dist/smart-dom-reader.d.ts +5 -0
- package/dist/theme-editor-preview.cjs +59 -59
- package/dist/theme-editor-preview.d.cts +5 -0
- package/dist/theme-editor-preview.d.ts +5 -0
- package/dist/theme-editor-preview.js +31 -31
- package/dist/theme-editor.d.cts +5 -0
- package/dist/theme-editor.d.ts +5 -0
- package/dist/voice-runtime-RHRX4F3T.js +17 -0
- package/dist/voice-runtime.cjs +8 -8
- package/dist/voice-runtime.d.cts +9 -0
- package/dist/voice-runtime.d.ts +9 -0
- package/dist/voice-runtime.js +8 -8
- package/package.json +1 -1
- package/src/client-stream.ts +10 -3
- package/src/client.test.ts +20 -0
- package/src/session.ts +5 -0
- package/src/session.voice.test.ts +59 -3
- package/src/types.ts +5 -0
- package/src/ui.composer-defer-one.test.ts +17 -0
- package/src/ui.ts +49 -12
- package/src/voice/__fixtures__/voice-wire/server/session_end-provider-error.json +6 -0
- package/src/voice/__fixtures__/voice-wire/server/session_end-unknown-reason.json +4 -0
- package/src/voice/__fixtures__/voice-wire/server/session_end.json +5 -0
- package/src/voice/runtype-voice-provider.ts +112 -10
- package/src/voice/voice-delegation.ts +26 -10
- package/src/voice/voice.test.ts +176 -0
- package/dist/chunk-GKPCIQPB.js +0 -1
- package/dist/client-stream-42FOI3EC.js +0 -4
- package/dist/voice-runtime-XLU6YK2N.js +0 -17
|
@@ -99,6 +99,8 @@ const LEVEL_RMS_SCALE = 4;
|
|
|
99
99
|
const RIFF_MAGIC = 0x52494646; // "RIFF"
|
|
100
100
|
/** `prewarm()` warms at most once per this window per provider instance. */
|
|
101
101
|
const PREWARM_THROTTLE_MS = 30_000;
|
|
102
|
+
/** How long a call start waits for a suspended capture context to run. */
|
|
103
|
+
const AUDIO_RESUME_TIMEOUT_MS = 1_000;
|
|
102
104
|
/** How long `startListening()` waits on an attach still handshaking. */
|
|
103
105
|
const ATTACH_HANDSHAKE_TIMEOUT_MS = 5_000;
|
|
104
106
|
/** Attached-socket idle window: server default and clamp range. */
|
|
@@ -142,6 +144,17 @@ const VOICE_PROTOCOL = "runtype-browser-v1";
|
|
|
142
144
|
/** `approvalTimeoutMs` cap: below core's delegation deadline (600 s, restarted by each update). */
|
|
143
145
|
const APPROVAL_TIMEOUT_MAX_MS = 540_000;
|
|
144
146
|
const DISCLOSURE_TEXT = "You're talking to an AI assistant. Voice is processed by OpenAI.";
|
|
147
|
+
/** Status-line text per `session_end.reason` (Amendment 6); unlisted reasons show nothing. */
|
|
148
|
+
const SESSION_END_TEXT = new Map([
|
|
149
|
+
["idle_timeout", "Voice call ended after a quiet period."],
|
|
150
|
+
["max_duration", "This voice call reached its time limit."],
|
|
151
|
+
["provider_ended", "The voice session ended."],
|
|
152
|
+
["quota", "Voice is unavailable right now."],
|
|
153
|
+
["auth_expired", "Voice session expired. Tap the mic to reconnect."],
|
|
154
|
+
]);
|
|
155
|
+
/** Reasons that get one automatic reconnect. */
|
|
156
|
+
const RECONNECT_REASONS = ["provider_error", "server_restart"];
|
|
157
|
+
const CONNECTION_LOST = "Voice connection lost.";
|
|
145
158
|
/** Call-start context frame: total cap, host share, history window, per-message cap. */
|
|
146
159
|
const CONTEXT_MAX_CHARS = 8000;
|
|
147
160
|
const CONTEXT_HOST_MAX_CHARS = 4000;
|
|
@@ -271,6 +284,14 @@ export class RuntypeVoiceProvider implements VoiceProvider {
|
|
|
271
284
|
#cancelledDelegations = new Set<string>();
|
|
272
285
|
// session_config.callId: the server's id for this call, for logs.
|
|
273
286
|
#callId: string | undefined;
|
|
287
|
+
// Amendment 6: why the server ended this call (its last frame), if it said.
|
|
288
|
+
#sessionEnd: { reason: string; message?: string } | null = null;
|
|
289
|
+
// Status-line text for the UI, read once (`takeNotice`).
|
|
290
|
+
#notice: string | null = null;
|
|
291
|
+
// One automatic reconnect per call the visitor started: pending, and spent.
|
|
292
|
+
#reconnectTimer: ReturnType<typeof setTimeout> | undefined;
|
|
293
|
+
#reconnected = false;
|
|
294
|
+
#autoReconnect = false;
|
|
274
295
|
// Call-start context, built at session_config and held until the visitor's
|
|
275
296
|
// first final transcript (sent earlier, the voice model tends to answer it).
|
|
276
297
|
#contextSent = false;
|
|
@@ -467,6 +488,13 @@ export class RuntypeVoiceProvider implements VoiceProvider {
|
|
|
467
488
|
/** Start the call: acquire mic, open the WS, stream PCM until hang-up. */
|
|
468
489
|
async startListening(): Promise<void> {
|
|
469
490
|
if (this.#callLive) return; // idempotent: a call is already live
|
|
491
|
+
// A call the visitor starts gets a fresh reconnect; the automatic one doesn't.
|
|
492
|
+
const auto = this.#autoReconnect;
|
|
493
|
+
if (!auto) {
|
|
494
|
+
this.#reconnected = false;
|
|
495
|
+
this.#notice = null;
|
|
496
|
+
}
|
|
497
|
+
this.#autoReconnect = false;
|
|
470
498
|
|
|
471
499
|
const agentId = this.config?.agentId;
|
|
472
500
|
const token = this.config?.clientToken;
|
|
@@ -499,10 +527,22 @@ export class RuntypeVoiceProvider implements VoiceProvider {
|
|
|
499
527
|
const captureContext: AudioContext = new AudioCtx({
|
|
500
528
|
sampleRate: CAPTURE_SAMPLE_RATE,
|
|
501
529
|
});
|
|
530
|
+
this.#captureContext = captureContext;
|
|
502
531
|
if (captureContext.state === "suspended") {
|
|
503
|
-
|
|
532
|
+
// Outside a click (the automatic reconnect, or a call restored after a
|
|
533
|
+
// page navigation) the autoplay policy can leave audio suspended and
|
|
534
|
+
// `resume()` pending forever: fail fast rather than hang or open a dead call.
|
|
535
|
+
await Promise.race([
|
|
536
|
+
captureContext.resume().catch(() => {}),
|
|
537
|
+
new Promise((resolve) => setTimeout(resolve, auto ? 500 : AUDIO_RESUME_TIMEOUT_MS)),
|
|
538
|
+
]);
|
|
539
|
+
if (generation !== this.#callGeneration) return; // hung up meanwhile
|
|
540
|
+
if ((captureContext.state as string) !== "running") {
|
|
541
|
+
if (auto) throw new Error((this.#notice = `${CONNECTION_LOST} Tap the mic to reconnect.`));
|
|
542
|
+
// A restored call: the widget asks for a tap to resume.
|
|
543
|
+
throw new Error("Audio blocked until a tap");
|
|
544
|
+
}
|
|
504
545
|
}
|
|
505
|
-
this.#captureContext = captureContext;
|
|
506
546
|
|
|
507
547
|
const engine = this.config?.createPlaybackEngine
|
|
508
548
|
? await this.config.createPlaybackEngine()
|
|
@@ -560,7 +600,8 @@ export class RuntypeVoiceProvider implements VoiceProvider {
|
|
|
560
600
|
ws.onmessage = (event) => this.#handleMessage(event, generation);
|
|
561
601
|
|
|
562
602
|
ws.onerror = () => {
|
|
563
|
-
|
|
603
|
+
// After `session_end`, the close that follows reports the server's reason.
|
|
604
|
+
if (generation !== this.#callGeneration || this.#sessionEnd) return;
|
|
564
605
|
this.#emitError(new Error("Voice connection failed"));
|
|
565
606
|
this.#emitStatus("error");
|
|
566
607
|
this.#cleanup();
|
|
@@ -572,17 +613,68 @@ export class RuntypeVoiceProvider implements VoiceProvider {
|
|
|
572
613
|
return;
|
|
573
614
|
}
|
|
574
615
|
if (generation !== this.#callGeneration) return;
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
616
|
+
// Why the server ended it: its `session_end`, or the attach idle close.
|
|
617
|
+
const end = this.#sessionEnd ?? (evt.code === 4408 ? { reason: "idle_timeout" } : null);
|
|
618
|
+
if (!end) {
|
|
619
|
+
// No reason given (a deploy, a dropped connection): as before.
|
|
620
|
+
if (evt.code !== 1000) {
|
|
621
|
+
const codeMsg = evt.code ? ` (code ${evt.code})` : "";
|
|
622
|
+
this.#emitError(new Error(`Voice connection closed${codeMsg}`));
|
|
623
|
+
this.#emitStatus("error");
|
|
624
|
+
} else {
|
|
625
|
+
this.#emitStatus("idle");
|
|
626
|
+
}
|
|
627
|
+
this.#cleanup();
|
|
628
|
+
return;
|
|
581
629
|
}
|
|
582
|
-
this.#
|
|
630
|
+
this.#endCall(end.reason, end.message);
|
|
583
631
|
};
|
|
584
632
|
}
|
|
585
633
|
|
|
634
|
+
/**
|
|
635
|
+
* The server ended the call with a reason (`session_end`, Amendment 6): show
|
|
636
|
+
* its text, and reconnect once after a provider error or a restart. Unknown
|
|
637
|
+
* reasons are an ordinary end (`ended_by_server`).
|
|
638
|
+
*/
|
|
639
|
+
#endCall(reason: string, message?: string): void {
|
|
640
|
+
const retry = RECONNECT_REASONS.includes(reason) && !this.#reconnected;
|
|
641
|
+
this.#cleanup();
|
|
642
|
+
if (retry) {
|
|
643
|
+
// Keep the call "live" while it waits, so the mic button hangs up (and cancels).
|
|
644
|
+
this.#reconnected = true;
|
|
645
|
+
this.#callLive = true;
|
|
646
|
+
this.#notice = reason === "provider_error" ? `${CONNECTION_LOST} Reconnecting…` : "Reconnecting…";
|
|
647
|
+
// A hang-up meanwhile runs #cleanup, which clears this timer.
|
|
648
|
+
this.#reconnectTimer = setTimeout(
|
|
649
|
+
() => {
|
|
650
|
+
this.#callLive = false;
|
|
651
|
+
this.#autoReconnect = true;
|
|
652
|
+
void this.startListening().catch(() => {});
|
|
653
|
+
},
|
|
654
|
+
1000 + Math.random() * 1000,
|
|
655
|
+
);
|
|
656
|
+
this.#emitStatus("idle");
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
if (RECONNECT_REASONS.includes(reason)) {
|
|
660
|
+
// The reconnect already ran: this end is the second failure.
|
|
661
|
+
this.#notice = CONNECTION_LOST;
|
|
662
|
+
this.#emitError(new Error(CONNECTION_LOST));
|
|
663
|
+
return this.#emitStatus("error");
|
|
664
|
+
}
|
|
665
|
+
// `error`: the preceding `error` frame already reported it.
|
|
666
|
+
if (reason === "error") return this.#emitStatus("error");
|
|
667
|
+
this.#notice = (reason === "quota" && message) || SESSION_END_TEXT.get(reason) || null;
|
|
668
|
+
this.#emitStatus("idle");
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/** Status-line text for the call (why it ended, or that it is reconnecting), once. */
|
|
672
|
+
takeNotice(): string | null {
|
|
673
|
+
const notice = this.#notice;
|
|
674
|
+
this.#notice = null;
|
|
675
|
+
return notice;
|
|
676
|
+
}
|
|
677
|
+
|
|
586
678
|
/** The AI-disclosure notice for a live speech-to-speech call (`disclosureText`; `false` hides it). */
|
|
587
679
|
getDisclosure(): string | null {
|
|
588
680
|
const text = this.config?.disclosureText;
|
|
@@ -872,6 +964,14 @@ export class RuntypeVoiceProvider implements VoiceProvider {
|
|
|
872
964
|
this.#emitError(new Error(msg.error || "Voice error"));
|
|
873
965
|
this.#emitStatus("error");
|
|
874
966
|
break;
|
|
967
|
+
|
|
968
|
+
case "session_end":
|
|
969
|
+
// The server's last frame before it closes: the close acts on it.
|
|
970
|
+
this.#sessionEnd = {
|
|
971
|
+
reason: String(msg.reason),
|
|
972
|
+
...(typeof msg.message === "string" && { message: msg.message }),
|
|
973
|
+
};
|
|
974
|
+
break;
|
|
875
975
|
}
|
|
876
976
|
}
|
|
877
977
|
|
|
@@ -1063,6 +1163,8 @@ export class RuntypeVoiceProvider implements VoiceProvider {
|
|
|
1063
1163
|
this.#followUps.clear();
|
|
1064
1164
|
this.#cancelledDelegations.clear();
|
|
1065
1165
|
this.#callId = undefined;
|
|
1166
|
+
this.#sessionEnd = null;
|
|
1167
|
+
clearTimeout(this.#reconnectTimer);
|
|
1066
1168
|
this.#contextSent = false;
|
|
1067
1169
|
this.#pendingContext = null;
|
|
1068
1170
|
this.#contextSend = null;
|
|
@@ -164,16 +164,32 @@ export function createVoiceSessionBridge(host: VoiceDelegationHost): VoiceSessio
|
|
|
164
164
|
};
|
|
165
165
|
|
|
166
166
|
return {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
167
|
+
// Read once at call start, before this call has any captions, so the
|
|
168
|
+
// captions here are earlier calls' (e.g. on the previous page of a
|
|
169
|
+
// multi-page site): spoken conversation the new voice session never heard.
|
|
170
|
+
// An assistant caption followed by a chat answer before the next user turn
|
|
171
|
+
// is a delegated turn's spoken filler ("Let me check."): it would only use
|
|
172
|
+
// up a slot of the context's recent-message window.
|
|
173
|
+
getHistory: () => {
|
|
174
|
+
const settled = host.messages().filter(
|
|
175
|
+
(m) =>
|
|
176
|
+
(m.role === "user" || m.role === "assistant") &&
|
|
177
|
+
!m.variant &&
|
|
178
|
+
!m.voiceProcessing &&
|
|
179
|
+
!m.streaming &&
|
|
180
|
+
m.content,
|
|
181
|
+
);
|
|
182
|
+
let answered = false;
|
|
183
|
+
const kept: Array<{ role: "user" | "assistant"; content: string }> = [];
|
|
184
|
+
for (let i = settled.length - 1; i >= 0; i -= 1) {
|
|
185
|
+
const m = settled[i];
|
|
186
|
+
if (m.role === "user") answered = false;
|
|
187
|
+
else if (!m.voiceCaption) answered = true;
|
|
188
|
+
else if (answered) continue;
|
|
189
|
+
kept.unshift({ role: m.role as "user" | "assistant", content: m.content });
|
|
190
|
+
}
|
|
191
|
+
return kept;
|
|
192
|
+
},
|
|
177
193
|
|
|
178
194
|
dropDelegation(delegationId, expired) {
|
|
179
195
|
// Remembered for a turn still running: when it parks, the same rule applies.
|
package/src/voice/voice.test.ts
CHANGED
|
@@ -1537,6 +1537,182 @@ describe('RuntypeVoiceProvider (realtime streaming)', () => {
|
|
|
1537
1537
|
});
|
|
1538
1538
|
});
|
|
1539
1539
|
});
|
|
1540
|
+
|
|
1541
|
+
describe('session_end (Amendment 6)', () => {
|
|
1542
|
+
const fixture = (name: string) =>
|
|
1543
|
+
JSON.stringify(wireFixtures('server').find((f) => JSON.stringify(f).includes(name)) ?? {});
|
|
1544
|
+
/** A live full-duplex call that collects errors and statuses from here on. */
|
|
1545
|
+
const endableCall = async () => {
|
|
1546
|
+
const call = await startFullDuplexCall();
|
|
1547
|
+
const errors: string[] = [];
|
|
1548
|
+
call.provider.onError((e) => errors.push(e.message));
|
|
1549
|
+
return { ...call, errors };
|
|
1550
|
+
};
|
|
1551
|
+
const end = (ws: MockWebSocket, frame: Record<string, unknown> | string, code: number) => {
|
|
1552
|
+
ws.triggerMessage(typeof frame === 'string' ? frame : JSON.stringify({ type: 'session_end', ...frame }));
|
|
1553
|
+
ws.triggerClose(code);
|
|
1554
|
+
};
|
|
1555
|
+
|
|
1556
|
+
it.each([
|
|
1557
|
+
['idle_timeout', 1000, 'Voice call ended after a quiet period.'],
|
|
1558
|
+
['max_duration', 1000, 'This voice call reached its time limit.'],
|
|
1559
|
+
['provider_ended', 1000, 'The voice session ended.'],
|
|
1560
|
+
['quota', 1008, 'Voice is unavailable right now.'],
|
|
1561
|
+
['auth_expired', 1008, 'Voice session expired. Tap the mic to reconnect.'], // reserved: no auto-reconnect
|
|
1562
|
+
['ended_by_server', 1000, null],
|
|
1563
|
+
['maintenance_window', 1000, null], // unknown: an ordinary server end
|
|
1564
|
+
])('ends on %s with its status text, without an error', async (reason, code, text) => {
|
|
1565
|
+
const { ws, provider, statuses, errors } = await endableCall();
|
|
1566
|
+
end(ws, { reason }, code);
|
|
1567
|
+
expect(provider.takeNotice()).toBe(text);
|
|
1568
|
+
expect(provider.takeNotice()).toBeNull(); // read once
|
|
1569
|
+
expect(errors).toEqual([]);
|
|
1570
|
+
expect(statuses.at(-1)).toBe('idle');
|
|
1571
|
+
expect(provider.isBargeInActive()).toBe(false);
|
|
1572
|
+
await vi.advanceTimersByTimeAsync(5_000);
|
|
1573
|
+
expect(MockWebSocket.instances.at(-1)).toBe(ws); // no reconnect
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
it('shows the quota message the server sent', async () => {
|
|
1577
|
+
const { ws, provider } = await endableCall();
|
|
1578
|
+
end(ws, { reason: 'quota', message: 'Your voice minutes are used up.' }, 1008);
|
|
1579
|
+
expect(provider.takeNotice()).toBe('Your voice minutes are used up.');
|
|
1580
|
+
});
|
|
1581
|
+
|
|
1582
|
+
it('takes the doc fixtures: max_duration, and an unknown reason as ended_by_server', async () => {
|
|
1583
|
+
const first = await endableCall();
|
|
1584
|
+
end(first.ws, fixture('max_duration'), 1000);
|
|
1585
|
+
expect(first.provider.takeNotice()).toBe('This voice call reached its time limit.');
|
|
1586
|
+
const second = await endableCall();
|
|
1587
|
+
end(second.ws, fixture('maintenance_window'), 1000);
|
|
1588
|
+
expect(second.provider.takeNotice()).toBeNull();
|
|
1589
|
+
expect(second.errors).toEqual([]);
|
|
1590
|
+
});
|
|
1591
|
+
|
|
1592
|
+
it('reports a fatal error once: the error frame, not the session_end after it', async () => {
|
|
1593
|
+
const { ws, provider, statuses, errors } = await endableCall();
|
|
1594
|
+
ws.triggerMessage(JSON.stringify({ type: 'error', error: 'Voice engine failed', fatal: true }));
|
|
1595
|
+
end(ws, { reason: 'error' }, 1011);
|
|
1596
|
+
expect(errors).toEqual(['Voice engine failed']);
|
|
1597
|
+
expect(statuses.at(-1)).toBe('error');
|
|
1598
|
+
expect(provider.takeNotice()).toBeNull();
|
|
1599
|
+
});
|
|
1600
|
+
|
|
1601
|
+
it('reconnects once after a provider error, 1-2 s later, and says so', async () => {
|
|
1602
|
+
const { ws, provider, statuses, errors } = await endableCall();
|
|
1603
|
+
end(ws, fixture('provider_error'), 1011);
|
|
1604
|
+
expect(provider.takeNotice()).toBe('Voice connection lost. Reconnecting…');
|
|
1605
|
+
expect(errors).toEqual([]);
|
|
1606
|
+
expect(statuses.at(-1)).toBe('idle');
|
|
1607
|
+
expect(provider.isBargeInActive()).toBe(true); // the mic button hangs up meanwhile
|
|
1608
|
+
await vi.advanceTimersByTimeAsync(999);
|
|
1609
|
+
expect(MockWebSocket.instances.at(-1)).toBe(ws);
|
|
1610
|
+
await vi.advanceTimersByTimeAsync(1_001);
|
|
1611
|
+
const second = lastWs();
|
|
1612
|
+
expect(second).not.toBe(ws);
|
|
1613
|
+
expect(second.protocols).toEqual(['runtype.bearer', 'ct_secret']);
|
|
1614
|
+
|
|
1615
|
+
// A second failure doesn't reconnect again.
|
|
1616
|
+
second.triggerOpen();
|
|
1617
|
+
end(second, { reason: 'provider_error' }, 1011);
|
|
1618
|
+
expect(provider.takeNotice()).toBe('Voice connection lost.');
|
|
1619
|
+
expect(errors).toEqual(['Voice connection lost.']);
|
|
1620
|
+
expect(statuses.at(-1)).toBe('error');
|
|
1621
|
+
await vi.advanceTimersByTimeAsync(5_000);
|
|
1622
|
+
expect(lastWs()).toBe(second);
|
|
1623
|
+
});
|
|
1624
|
+
|
|
1625
|
+
it('reconnects once on server_restart, saying "Reconnecting…"', async () => {
|
|
1626
|
+
const { ws, provider } = await endableCall();
|
|
1627
|
+
end(ws, { reason: 'server_restart' }, 1012);
|
|
1628
|
+
expect(provider.takeNotice()).toBe('Reconnecting…');
|
|
1629
|
+
await vi.advanceTimersByTimeAsync(2_000);
|
|
1630
|
+
expect(lastWs()).not.toBe(ws);
|
|
1631
|
+
});
|
|
1632
|
+
|
|
1633
|
+
it('never reconnects once the visitor hangs up, during the wait or before the close', async () => {
|
|
1634
|
+
const waiting = await endableCall();
|
|
1635
|
+
end(waiting.ws, { reason: 'provider_error' }, 1011);
|
|
1636
|
+
await waiting.provider.stopListening();
|
|
1637
|
+
await vi.advanceTimersByTimeAsync(5_000);
|
|
1638
|
+
expect(lastWs()).toBe(waiting.ws);
|
|
1639
|
+
|
|
1640
|
+
const hungUp = await endableCall();
|
|
1641
|
+
hungUp.ws.triggerMessage(JSON.stringify({ type: 'session_end', reason: 'provider_error' }));
|
|
1642
|
+
await hungUp.provider.stopListening(); // closes the socket itself
|
|
1643
|
+
await vi.advanceTimersByTimeAsync(5_000);
|
|
1644
|
+
expect(lastWs()).toBe(hungUp.ws);
|
|
1645
|
+
expect(hungUp.errors).toEqual([]);
|
|
1646
|
+
});
|
|
1647
|
+
|
|
1648
|
+
it('reconnects on the real failure order: error, audio_end, session_end, close 1011', async () => {
|
|
1649
|
+
const { ws, provider, statuses, errors } = await endableCall();
|
|
1650
|
+
ws.triggerMessage(JSON.stringify({ type: 'error', error: 'Voice engine failed', fatal: true }));
|
|
1651
|
+
ws.triggerMessage(JSON.stringify({ type: 'audio_end' }));
|
|
1652
|
+
end(ws, { reason: 'provider_error', retryable: true }, 1011);
|
|
1653
|
+
expect(errors).toEqual(['Voice engine failed']); // reported once, by the error frame
|
|
1654
|
+
expect(statuses.at(-1)).toBe('idle');
|
|
1655
|
+
expect(provider.takeNotice()).toBe('Voice connection lost. Reconnecting…');
|
|
1656
|
+
expect(provider.isBargeInActive()).toBe(true);
|
|
1657
|
+
await vi.advanceTimersByTimeAsync(2_000);
|
|
1658
|
+
expect(lastWs()).not.toBe(ws);
|
|
1659
|
+
});
|
|
1660
|
+
|
|
1661
|
+
it('still reconnects when a socket error comes between session_end and the close', async () => {
|
|
1662
|
+
const { ws, provider, errors } = await endableCall();
|
|
1663
|
+
ws.triggerMessage(JSON.stringify({ type: 'session_end', reason: 'provider_error' }));
|
|
1664
|
+
ws.triggerError();
|
|
1665
|
+
ws.triggerClose(1011);
|
|
1666
|
+
expect(errors).toEqual([]);
|
|
1667
|
+
expect(provider.takeNotice()).toBe('Voice connection lost. Reconnecting…');
|
|
1668
|
+
await vi.advanceTimersByTimeAsync(2_000);
|
|
1669
|
+
expect(lastWs()).not.toBe(ws);
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1672
|
+
it('ends the automatic reconnect when audio stays suspended outside a click (iOS)', async () => {
|
|
1673
|
+
const { ws, provider, statuses, errors } = await endableCall();
|
|
1674
|
+
end(ws, { reason: 'server_restart' }, 1012);
|
|
1675
|
+
provider.takeNotice();
|
|
1676
|
+
class SuspendedAudioContext extends MockAudioContext {
|
|
1677
|
+
state = 'suspended';
|
|
1678
|
+
resume() {
|
|
1679
|
+
return new Promise<void>(() => {}); // iOS: pending until a gesture
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
(globalThis as any).window.AudioContext = SuspendedAudioContext;
|
|
1683
|
+
try {
|
|
1684
|
+
await vi.advanceTimersByTimeAsync(2_500);
|
|
1685
|
+
} finally {
|
|
1686
|
+
(globalThis as any).window.AudioContext = MockAudioContext;
|
|
1687
|
+
}
|
|
1688
|
+
expect(lastWs()).toBe(ws); // no dead call opened
|
|
1689
|
+
expect(errors).toEqual(['Voice connection lost. Tap the mic to reconnect.']);
|
|
1690
|
+
expect(statuses.at(-1)).toBe('error');
|
|
1691
|
+
expect(provider.takeNotice()).toBe('Voice connection lost. Tap the mic to reconnect.');
|
|
1692
|
+
expect(provider.isBargeInActive()).toBe(false);
|
|
1693
|
+
});
|
|
1694
|
+
|
|
1695
|
+
it('treats the attach idle close (4408) as a quiet end, not an error', async () => {
|
|
1696
|
+
for (const withFrame of [false, true]) {
|
|
1697
|
+
const { ws, provider, statuses, errors } = await endableCall();
|
|
1698
|
+
if (withFrame) ws.triggerMessage(JSON.stringify({ type: 'session_end', reason: 'idle_timeout' }));
|
|
1699
|
+
ws.triggerClose(4408);
|
|
1700
|
+
expect(errors).toEqual([]);
|
|
1701
|
+
expect(statuses.at(-1)).toBe('idle');
|
|
1702
|
+
expect(provider.takeNotice()).toBe('Voice call ended after a quiet period.');
|
|
1703
|
+
}
|
|
1704
|
+
});
|
|
1705
|
+
|
|
1706
|
+
it('keeps an abnormal close without session_end exactly as before', async () => {
|
|
1707
|
+
const { ws, provider, statuses, errors } = await endableCall();
|
|
1708
|
+
ws.triggerClose(1006);
|
|
1709
|
+
expect(errors).toEqual(['Voice connection closed (code 1006)']);
|
|
1710
|
+
expect(statuses.at(-1)).toBe('error');
|
|
1711
|
+
expect(provider.takeNotice()).toBeNull();
|
|
1712
|
+
await vi.advanceTimersByTimeAsync(5_000);
|
|
1713
|
+
expect(lastWs()).toBe(ws);
|
|
1714
|
+
});
|
|
1715
|
+
});
|
|
1540
1716
|
});
|
|
1541
1717
|
});
|
|
1542
1718
|
|
package/dist/chunk-GKPCIQPB.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var t="4.26.1";var n=t;export{n as a};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{c as me,d as ye,e as Ie}from"./chunk-K6HKIP5K.js";import"./chunk-K7NA7G7J.js";import{d as we}from"./chunk-2ANTRKTV.js";import"./chunk-TR2H475E.js";import{e as de,p as z}from"./chunk-NQLDCJAP.js";import"./chunk-76IELU7F.js";async function Te(c,h,m){let u={"Content-Type":"application/json",...c.headers};c.getHeaders&&Object.assign(u,await c.getHeaders());let M={executionId:h.executionId,approvalId:h.approvalId,decision:m,streamResponse:!0},x=(k,p)=>fetch(`${c.clientApiBase()}${k}`,{method:"POST",headers:u,body:JSON.stringify({...p,...M})}),A=`/v1/agents/${h.agentId}/approve`;if(!c.isClientTokenMode())return x(A);let{sessionId:y}=await c.initSession(),S=await x("/v1/client/approve",{sessionId:y});if(S.status===404){let k=await S.clone().json().catch(()=>null);if(!k||k.error==="Not Found")return x(A)}return S}async function Se(c,h,m,u){let M=c.isClientTokenMode(),x=M?c.getClientApiUrl("resume"):`${c.config.apiUrl?.replace(/\/+$/,"")||we}/resume`,A,y=null;if(M){let p=await c.initSession();A=p.sessionId,p.durableRecovery?.enabled===!0&&(y=await c.readVisitorToken())}let S={"Content-Type":"application/json",...y?{"X-Visitor-Token":y}:{},...c.headers};c.getHeaders&&Object.assign(S,await c.getHeaders());let k={executionId:h,toolOutputs:m,streamResponse:u?.streamResponse??!0,...u?.after?{after:u.after}:{}};if(A&&(k.sessionId=A),M&&A){let p=[...Ie(c.config),...await(await c.getWebMcpBridge())?.snapshotForDispatch()??[]],{response:D,commit:ee}=await c.sendWithClientToolsDiff(A,p,d=>{let f={...k,...d};return c.debug&&console.debug("[AgentWidgetClient] client token resume",f),fetch(x,{method:"POST",headers:S,body:JSON.stringify(f),signal:u?.signal})},{emptyMeansReplace:!0});return D.ok&&ee(),D}return fetch(x,{method:"POST",headers:S,body:JSON.stringify(k),signal:u?.signal})}function ke(c){let h=c.toLowerCase(),u={"application/pdf":"pdf","application/json":"json","application/zip":"zip","text/plain":"txt","text/csv":"csv","text/markdown":"md"}[h];if(u)return`attachment.${u}`;let M=h.indexOf("/");if(M>0){let x=h.slice(M+1).split(";")[0]?.trim()??"";if(x&&x!=="octet-stream"&&/^[a-z0-9.+-]+$/i.test(x))return`attachment.${x}`}return"attachment"}var he=c=>c.startsWith("{")||c.startsWith("[")||c.startsWith("<");function We(c,h){if(!c)return h;let m=c.trim(),u=h.trim();if(m.length===0)return h;if(u.length===0)return c;let M=he(m);if(!he(u))return c;if(!M||u===m||u.startsWith(m))return h;let A=z(c);return z(h)!==null&&A===null?h:c}async function be(c,h,m,u,M,x,A){if(!c.parseSSEEvent)return!1;try{let y=await c.parseSSEEvent(h);if(y===null)return!1;let S=p=>{let D={id:`assistant-${Date.now()}-${Math.random().toString(16).slice(2)}`,role:"assistant",content:"",createdAt:new Date().toISOString(),streaming:!0,variant:"assistant",sequence:x(),...p!==void 0&&{partId:p}};return u.current=D,M(D),D},k=p=>u.current?u.current:S(p);if(y.text!==void 0){y.partId!==void 0&&A.current!==null&&y.partId!==A.current&&(u.current&&(u.current.streaming=!1,M(u.current)),S(y.partId)),y.partId!==void 0&&(A.current=y.partId);let p=k(y.partId);y.partId!==void 0&&!p.partId&&(p.partId=y.partId),p.content+=y.text,M(p)}return y.done&&(u.current&&(u.current.streaming=!1,M(u.current)),A.current=null,m({type:"status",status:"idle"})),y.error&&(A.current=null,m({type:"error",error:new Error(y.error)})),!0}catch(y){return typeof console<"u"&&console.error("[AgentWidget] parseSSEEvent error:",y),!1}}async function Ee(c,h,m,u,M){let x=h.getReader(),A=new TextDecoder,y="",S=Date.now(),k=0,p=()=>S+k++,D=n=>{let e=n.reasoning?{...n.reasoning,chunks:[...n.reasoning.chunks]}:void 0,t=n.toolCall?{...n.toolCall,chunks:n.toolCall.chunks?[...n.toolCall.chunks]:void 0}:void 0,o=n.tools?n.tools.map(s=>({...s,chunks:s.chunks?[...s.chunks]:void 0})):void 0;return{...n,reasoning:e,toolCall:t,tools:o}},ee=n=>{if(n.role!=="assistant"||n.variant)return!0;let e=Array.isArray(n.contentParts)&&n.contentParts.length>0,t=typeof n.rawContent=="string"&&n.rawContent.trim()!=="";return typeof n.content=="string"&&n.content.trim()!==""||e||t||!!n.stopReason},d=n=>{ee(n)&&m({type:"message",message:D(n)})},f=null,U=null,O={current:null},Ce={current:null},F=null,E="",$=new Map,v=new Map,te=new Map,Q=new Map,ne=new Map,X=u,ce=!1,oe=()=>{if(f)return f;let n,e="",t=F;return!ce&&X?(n=X,ce=!0,e=M??""):X&&t?n=`${X}_${t}`:n=`assistant-${Date.now()}-${Math.random().toString(16).slice(2)}`,f={id:n,role:"assistant",content:e,createdAt:new Date().toISOString(),streaming:!0,sequence:p()},d(f),f},se=n=>{let e=Q.get(n);if(e)return e;let t={id:`reason-${n}`,role:"assistant",content:"",createdAt:new Date().toISOString(),streaming:!0,variant:"reasoning",sequence:p(),reasoning:{id:n,status:"streaming",chunks:[]}};return Q.set(n,t),d(t),t},H=new Set,re=new Map,ge=new Set,J=new Map,Ae=n=>{if(!n)return!1;let e=n.replace(/_+/g,"_").replace(/^_|_$/g,"");return e==="emit_artifact_markdown"||e==="emit_artifact_component"},L=n=>{let e=ne.get(n);if(e)return e;let t={id:`tool-${n}`,role:"assistant",content:"",createdAt:new Date().toISOString(),streaming:!0,variant:"tool",sequence:p(),toolCall:{id:n,status:"pending"}};return ne.set(n,t),d(t),t},ue=n=>{if(typeof n=="number"&&Number.isFinite(n))return n;if(typeof n=="string"){let e=Number(n);if(!Number.isNaN(e)&&Number.isFinite(e))return e;let t=Date.parse(n);if(!Number.isNaN(t))return t}return Date.now()},ie=n=>{if(typeof n=="string")return n;if(n==null)return"";try{return JSON.stringify(n)}catch{return String(n)}},T=new Map,W=new Map,Me=(n,e)=>{let t=ie(e),o=W.get(n.id),s=We(o,t);n.rawContent=s;let r=T.get(n.id),i=I=>{let g=n.content??"";I.trim()!==""&&(g.trim().length===0||I.startsWith(g)||I.trimStart().startsWith(g.trim()))&&(n.content=I)},l=()=>{if(r){let I=r.close?.();I instanceof Promise&&I.catch(()=>{})}T.delete(n.id),W.delete(n.id),n.streaming=!1,d(n)};if(!r){i(t),l();return}let a=z(s);if(a!==null&&a.trim()!==""){i(a),l();return}let w=I=>{let g=typeof I=="string"?I:I?.text??null;if(g!==null&&g.trim()!=="")return g;let N=r.getExtractedText();return N!==null&&N.trim()!==""?N:t},C;try{C=r.processChunk(s)}catch{i(t),l();return}if(C instanceof Promise){C.then(I=>{i(w(I)),l()}).catch(()=>{i(t),l()});return}i(w(C)),l()},K=null,ae=(n,e,t,o)=>{n.rawContent=e,T.has(n.id)||T.set(n.id,c.createStreamParser());let s=T.get(n.id),r=e.trim().startsWith("{")||e.trim().startsWith("[");if(r&&W.set(n.id,e),s.__isPlainTextParser===!0){n.content=o!==void 0?e:n.content+t,W.delete(n.id),T.delete(n.id),n.rawContent=void 0,d(n);return}let l=s.processChunk(e);if(l instanceof Promise)l.then(a=>{let w=typeof a=="string"?a:a?.text??null;w!==null&&w.trim()!==""?(n.content=w,d(n)):!r&&!e.trim().startsWith("<")&&(n.content=o!==void 0?e:n.content+t,W.delete(n.id),T.delete(n.id),n.rawContent=void 0,d(n))}).catch(()=>{n.content=o!==void 0?e:n.content+t,W.delete(n.id),T.delete(n.id),n.rawContent=void 0,d(n)});else{let a=typeof l=="string"?l:l?.text??null;a!==null&&a.trim()!==""?(n.content=a,d(n)):!r&&!e.trim().startsWith("<")&&(n.content=o!==void 0?e:n.content+t,W.delete(n.id),T.delete(n.id),n.rawContent=void 0,d(n))}},V=(n,e)=>{let t=e??n.content;if(t==null||t===""){n.streaming=!1,d(n);return}let s=W.get(n.id)??ie(t);n.rawContent=s;let r=T.get(n.id),i=null,l=!1;if(r&&(i=r.getExtractedText(),i===null&&(i=z(s)),i===null)){let a=r.processChunk(s);a instanceof Promise?(l=!0,a.then(w=>{let C=typeof w=="string"?w:w?.text??null;C!==null&&(n.content=C,n.streaming=!1,T.delete(n.id),W.delete(n.id),d(n))}).catch(()=>{})):i=typeof a=="string"?a:a?.text??null}if(!l){i!==null&&i.trim()!==""?n.content=i:W.has(n.id)||(n.content=ie(t));let a=T.get(n.id);if(a){let w=a.close?.();w instanceof Promise&&w.catch(()=>{}),T.delete(n.id)}W.delete(n.id),n.streaming=!1,d(n)}},pe=(n,e,t)=>{let o=v.get(n);if(o)return o;let s={id:`nested-${e}-${n}`,role:"assistant",content:"",createdAt:new Date().toISOString(),streaming:!0,sequence:p(),...t?{variant:t}:{},...t==="reasoning"?{reasoning:{id:n,status:"streaming",chunks:[]}}:{},agentMetadata:{parentToolId:e}};return v.set(n,s),d(s),s},G=[],le,Y=new Map,_=0,B="agent",fe=!1,Z=null,j=null,xe=c.config().iterationDisplay??"separate";for(le=()=>{for(let n=0;n<G.length;n++){let e=G[n];if(!fe&&B!=="flow"&&typeof e.stepType=="string"&&(B="flow"),e.type==="reasoning_start"){let t=typeof e.id=="string"?e.id:null,o=typeof e.parentToolCallId=="string"&&e.parentToolCallId?e.parentToolCallId:null;if(t&&o){$.set(t,o),pe(t,o,"reasoning");continue}let s=e.id,r=se(s);r.reasoning=r.reasoning??{id:s,status:"streaming",chunks:[]},r.reasoning.startedAt=r.reasoning.startedAt??Date.now(),r.reasoning.completedAt=void 0,r.reasoning.durationMs=void 0,(e.scope==="loop"||e.scope==="turn")&&(r.reasoning.scope=e.scope),r.streaming=!0,r.reasoning.status="streaming",d(r)}else if(e.type==="reasoning_delta"){let t=typeof e.id=="string"?e.id:null;if(t&&$.has(t)&&v.has(t)){let r=v.get(t),i=e.delta;i&&r.reasoning&&(r.reasoning.chunks.push(String(i)),d(r));continue}let o=e.id,s=se(o);s.reasoning=s.reasoning??{id:o,status:"streaming",chunks:[]},s.reasoning.startedAt=s.reasoning.startedAt??Date.now(),e.delta&&s.reasoning.chunks.push(e.delta),s.reasoning.status="streaming",s.streaming=!0,d(s)}else if(e.type==="reasoning_complete"){let t=typeof e.id=="string"?e.id:null;if(t&&$.has(t)&&v.has(t)){let i=v.get(t);if(i.reasoning){let l=typeof e.text=="string"?e.text:"";l&&i.reasoning.chunks.length===0&&i.reasoning.chunks.push(l),i.reasoning.status="complete",i.streaming=!1,d(i)}$.delete(t),v.delete(t);continue}let o=e.id,s=typeof e.text=="string"?e.text:"";!Q.get(o)&&(s||e.scope==="loop")&&se(o);let r=Q.get(o);if(r?.reasoning){(e.scope==="loop"||e.scope==="turn")&&(r.reasoning.scope=e.scope),s&&r.reasoning.chunks.length===0&&r.reasoning.chunks.push(s),r.reasoning.status="complete",r.reasoning.completedAt=Date.now();let i=r.reasoning.startedAt??Date.now();r.reasoning.durationMs=Math.max(0,(r.reasoning.completedAt??Date.now())-i),r.streaming=!1,d(r)}}else if(e.type==="tool_start"){f&&(f.streaming=!1,d(f),f=null),typeof e.iteration=="number"&&(_=e.iteration);let t=e.toolCallId,o=e.toolName;if(Ae(o)){H.add(t);continue}let s=L(t),r=s.toolCall??{id:t,status:"pending"};r.name=o??r.name,r.status="running",r.success=void 0,r.error=void 0,r.duration=void 0,e.parameters!==void 0&&(r.args=e.parameters),r.startedAt=r.startedAt??ue(e.startedAt),r.completedAt=void 0,r.durationMs=void 0,s.toolCall=r,s.streaming=!0,e.executionId&&(s.agentMetadata={executionId:e.executionId,iteration:e.iteration}),d(s)}else if(e.type==="tool_output_delta"){let t=e.toolCallId;if(H.has(t))continue;let o=L(t),s=o.toolCall??{id:t,status:"running"};s.startedAt=s.startedAt??Date.now();let r=e.delta;r&&(s.chunks=s.chunks??[],s.chunks.push(String(r))),s.status="running",o.toolCall=s,o.streaming=!0,e.executionId&&(o.agentMetadata=o.agentMetadata??{executionId:e.executionId,iteration:_}),d(o)}else if(e.type==="tool_complete"){let t=e.toolCallId;if(H.has(t)){H.delete(t);continue}let o=L(t),s=o.toolCall??{id:t,status:"running"};s.status="complete",e.result!==void 0&&(s.result=e.result),s.name=e.toolName??s.name,s.success=e.success,s.error=e.error,s.completedAt=Date.now();let r=e.executionTime;if(typeof r=="number")s.durationMs=r,s.duration=r;else{let i=s.startedAt??Date.now();s.durationMs=Math.max(0,(s.completedAt??Date.now())-i)}o.toolCall=s,o.streaming=!1,e.executionId&&(o.agentMetadata=o.agentMetadata??{executionId:e.executionId,iteration:e.iteration??_}),d(o)}else if(e.type==="await"&&e.toolName){let t=typeof e.toolCallId=="string"&&e.toolCallId.length>0?e.toolCallId:void 0,o=t??e.toolId??`local-${p()}`,s=L(o),r=e.toolName,i=e.origin==="webmcp"&&!de(r)?`webmcp:${r}`:r,l=de(i),a=s.toolCall??{id:o,status:"pending"};a.name=i,a.args=e.parameters,a.status=l?"running":"complete",a.chunks=a.chunks??[],a.startedAt=a.startedAt??ue(e.awaitedAt),l?(a.completedAt=void 0,a.duration=void 0,a.durationMs=void 0):a.completedAt=a.completedAt??a.startedAt,s.toolCall=a,s.streaming=!1,s.agentMetadata={...s.agentMetadata,executionId:e.executionId??s.agentMetadata?.executionId,awaitingLocalTool:!0,...t?{webMcpToolCallId:t}:{}},d(s)}else if(e.type==="text_start"){let t=typeof e.id=="string"?e.id:null,o=typeof e.parentToolCallId=="string"&&e.parentToolCallId?e.parentToolCallId:null;if(t&&o){$.set(t,o);continue}let s=f;s&&(B==="flow"?(V(s),K=s):(s.streaming=!1,d(s)),f=null),F=typeof e.id=="string"?e.id:F,E=""}else if(e.type==="text_delta"){let t=typeof e.id=="string"?e.id:null,o=t?$.get(t):void 0;if(t&&o){let i=typeof e.delta=="string"?e.delta:"",l=(te.get(t)??"")+i;if(te.set(t,l),l.trim()==="")continue;let a=pe(t,o);a.agentMetadata={...a.agentMetadata,executionId:e.executionId,parentToolId:o},ae(a,l,i,void 0);continue}if(F=typeof e.id=="string"?e.id:F,B==="flow"){let i=typeof e.delta=="string"?e.delta:"";if(E+=i,E.trim()==="")continue;let l=oe();l.agentMetadata={executionId:e.executionId,iteration:_},ae(l,E,i,void 0),U=l;continue}let s=typeof e.delta=="string"?e.delta:"";E+=s;let r=oe();r.agentMetadata={executionId:e.executionId,iteration:_,turnId:Z??void 0,agentName:j?.agentName},ae(r,E,s,void 0),U=r}else if(e.type==="text_complete"){let t=typeof e.id=="string"?e.id:null;if(t&&$.has(t)){let s=v.get(t);s&&V(s),$.delete(t),te.delete(t),v.delete(t);continue}let o=f;o&&(B==="flow"?(V(o),K=o):((o.content??"")===""&&typeof e.text=="string"&&(o.content=e.text),o.streaming=!1,d(o)),f=null),F=null,E=""}else if(e.type==="step_complete"){if(e.stepType==="tool")continue;if(e.success===!1){let t=e.error||"Step failed";m({type:"error",error:new Error(t)});let o=f;o&&o.streaming&&(o.streaming=!1,d(o)),m({type:"status",status:"idle"});continue}{let t=K;K=null;let o=e.stopReason,r=(e.result&&typeof e.result=="object"?e.result:null)?.response;if(t)o&&(t.stopReason=o),r!=null?Me(t,r):t.streaming!==!1&&(T.delete(t.id),W.delete(t.id),t.streaming=!1,d(t));else{let i=r!=null&&r!=="";if(i||o){let l=oe();o&&(l.stopReason=o),i?V(l,r):(l.streaming=!1,d(l))}}continue}}else if(e.type==="execution_start")B=e.kind==="flow"?"flow":"agent",fe=!0,B==="agent"&&(j={executionId:e.executionId,agentId:e.agentId??"virtual",agentName:e.agentName??""});else if(e.type==="turn_start"){let t=typeof e.iteration=="number"?e.iteration:_;if(t!==_){if(xe==="separate"&&t>1){let o=f;o&&(o.streaming=!1,d(o),f=null)}_=t}Z=typeof e.id=="string"?e.id:null,U=null}else if(e.type==="tool_input_delta"){let t=e.toolCallId;if(t){let o=ne.get(t);o?.toolCall&&(o.toolCall.chunks=o.toolCall.chunks??[],o.toolCall.chunks.push(e.delta??""),d(o))}}else if(e.type==="tool_input_complete"){if(H.has(e.toolCallId))continue;let t=L(e.toolCallId),o=t.toolCall;o.args=e.parameters,o.name=e.toolName??o.name,d(t)}else if(e.type==="turn_complete"){let t=e.stopReason,o=f??U;if(t&&o!==null){let s=e.id;(!s||o.agentMetadata?.turnId===s)&&(o.stopReason=t,d(o))}Z===e.id&&(Z=null)}else if(e.type==="media_start"){let t=String(e.id);Y.set(t,{mediaType:typeof e.mediaType=="string"?e.mediaType:void 0,toolCallId:e.toolCallId,parts:[]})}else if(e.type==="media_delta"){let t=Y.get(String(e.id));t&&typeof e.delta=="string"&&t.parts.push(e.delta)}else if(e.type==="media_complete"){let t=String(e.id),o=Y.get(t);Y.delete(t);let s=(typeof e.mediaType=="string"?e.mediaType:void 0)??o?.mediaType??"application/octet-stream",r=typeof e.data=="string"?e.data:void 0,i=typeof e.url=="string"?e.url:o&&o.parts.length>0?o.parts.join(""):void 0,l=null;if(r)l={type:"media",data:r,mediaType:s};else if(i){let I=s.toLowerCase();l={type:I==="image"||I.startsWith("image/")?"image-url":"file-url",url:i,mediaType:s}}let a=e.toolCallId??o?.toolCallId,w=l?[l]:[],C=[];for(let I of w){if(!I||typeof I!="object")continue;let g=I,N=typeof g.type=="string"?g.type:void 0,q=typeof g.mediaType=="string"?g.mediaType.toLowerCase():"",b=null,P="";if(N==="media"){let R=typeof g.data=="string"?g.data:void 0;if(!R)continue;P=q.length>0?q:"application/octet-stream",b=`data:${P};base64,${R}`}else if(N==="image-url"){let R=typeof g.url=="string"?g.url:void 0;if(!R)continue;P=q,b=R}else if(N==="file-url"){let R=typeof g.url=="string"?g.url:void 0;if(!R)continue;P=q,b=R}else continue;if(b)if(N==="image-url"||P.startsWith("image/"))C.push({type:"image",image:b,...P.includes("/")?{mimeType:P}:{}});else if(P.startsWith("audio/"))C.push({type:"audio",audio:b,mimeType:P});else if(P.startsWith("video/"))C.push({type:"video",video:b,mimeType:P});else{let R=P||"application/octet-stream";C.push({type:"file",data:b,mimeType:R,filename:ke(R)})}}if(C.length>0){let I=p(),g=a,q={id:`agent-media-${typeof g=="string"&&g.length>0?`${g}-${I}`:String(I)}`,role:"assistant",content:"",contentParts:C,createdAt:new Date().toISOString(),streaming:!1,sequence:I,agentMetadata:{executionId:e.executionId,iteration:_}};d(q);let b=f;b&&(b.streaming=!1,d(b)),f=null,O.current=null}}else if(e.type==="execution_complete"){let t=f;t&&(e.kind==="flow"&&t.streaming!==!1?V(t):(t.streaming=!1,d(t)),f=null),F=null,E="",K=null,m({type:"status",status:"idle",terminal:!0})}else if(e.type==="execution_error"){let t=typeof e.error=="string"?e.error:e.error?.message??"Execution error";m({type:"error",error:new Error(t)})}else if(e.type!=="ping"){if(e.type==="approval_start"){let t=e.approvalId??`approval-${p()}`,o={id:`approval-${t}`,role:"assistant",content:"",createdAt:new Date().toISOString(),streaming:!1,variant:"approval",sequence:p(),approval:{id:t,status:"pending",agentId:j?.agentId??"virtual",executionId:e.executionId??j?.executionId??"",toolName:e.toolName??"",toolType:e.toolType,description:e.description??`Execute ${e.toolName??"tool"}`,...typeof e.reason=="string"&&e.reason?{reason:e.reason}:{},parameters:e.parameters,...typeof e.toolCallId=="string"&&e.toolCallId?{toolCallId:e.toolCallId}:{}}};d(o)}else if(e.type==="approval_complete"){let t=e.approvalId;if(t){let s={id:`approval-${t}`,role:"assistant",content:"",createdAt:new Date().toISOString(),streaming:!1,variant:"approval",sequence:p(),approval:{id:t,status:e.decision??"approved",agentId:j?.agentId??"virtual",executionId:e.executionId??j?.executionId??"",toolName:"",description:"",resolvedAt:Date.now()}};d(s)}}else if(e.type==="artifact_start"||e.type==="artifact_delta"||e.type==="artifact_update"||e.type==="artifact_complete"){if(e.type==="artifact_start"){let t=e.artifactType,o=String(e.id),s=typeof e.title=="string"?e.title:void 0,r=e.file,i;if(r&&typeof r=="object"&&!Array.isArray(r)&&typeof r.path=="string"&&typeof r.mimeType=="string"&&(i={path:r.path,mimeType:r.mimeType,...typeof r.language=="string"?{language:r.language}:{}}),m({type:"artifact_start",id:o,artifactType:t,title:s,component:typeof e.component=="string"?e.component:void 0,...i?{file:i}:{}}),J.set(o,{markdown:"",title:s,file:i}),!ge.has(o)){ge.add(o);let l=me(c.config().features?.artifacts,{artifactType:t,...i?{file:i}:{}}),a=typeof e.component=="string"?e.component:void 0,w={id:`artifact-ref-${o}`,role:"assistant",content:"",createdAt:new Date().toISOString(),streaming:!0,sequence:p(),rawContent:ye(l,{artifactId:o,title:s,artifactType:t,status:"streaming",...i?{file:i}:{},...a?{component:a}:{}})};re.set(o,w),d(w)}}else if(e.type==="artifact_delta"){let t=String(e.id),o=typeof e.delta=="string"?e.delta:String(e.delta??"");m({type:"artifact_delta",id:t,artDelta:o});let s=J.get(t);s&&(s.markdown+=o)}else if(e.type==="artifact_update"){let t=e.props&&typeof e.props=="object"&&!Array.isArray(e.props)?e.props:{};m({type:"artifact_update",id:String(e.id),props:t,component:typeof e.component=="string"?e.component:void 0});let o=J.get(String(e.id));o&&(o.props={...o.props??{},...t})}else if(e.type==="artifact_complete"){let t=String(e.id);m({type:"artifact_complete",id:t});let o=re.get(t);if(o){o.streaming=!1;try{let s=JSON.parse(o.rawContent??"{}");if(s.props){s.props.status="complete";let r=J.get(t);r?.markdown&&(s.props.markdown=r.markdown),r?.file&&(s.props.file=r.file),s.component==="PersonaArtifactInline"&&r?.props&&Object.keys(r.props).length>0&&(s.props.componentProps=r.props)}o.rawContent=JSON.stringify(s)}catch{}J.delete(t),d(o),re.delete(t)}}}else if(e.type==="error"&&e.recoverable===!1&&e.error!=null&&e.error!==""){let t=typeof e.error=="string"?e.error:e.error?.message!=null?String(e.error.message):"Execution error";m({type:"error",error:new Error(t)});let o=f;o&&o.streaming&&(o.streaming=!1,d(o)),m({type:"status",status:"idle"})}}}G.length=0};;){let{done:n,value:e}=await x.read();if(n)break;y+=A.decode(e,{stream:!0});let t=y.split(`
|
|
2
|
-
|
|
3
|
-
`);y=t.pop()??"";for(let o of t){let s=o.split(`
|
|
4
|
-
`),r="message",i="",l=null;for(let g of s)g.startsWith("event:")?r=g.replace("event:","").trim():g.startsWith("data:")?i+=g.replace("data:","").trim():g.startsWith("id:")&&(l=g.slice(3).trim());let a=()=>{l!==null&&l!==""&&m({type:"cursor",id:l})};if(!i){a();continue}let w;try{w=JSON.parse(i)}catch(g){m({type:"error",error:g instanceof Error?g:new Error("Failed to parse chat stream payload")});continue}let C=w!==null&&typeof w=="object"&&!Array.isArray(w)?w:null,I=r!=="message"?r:typeof C?.type=="string"?C.type:"message";if(c.onSSEEvent?.(I,w),c.parseSSEEvent){O.current=f;let g=await be(c,w,m,O,d,p,Ce);if(O.current&&O.current!==f&&(f=O.current),g){a();continue}}C&&(G.push({...C,type:I}),le()),a()}}le()}export{We as preferFinalStructuredContent,Te as resolveApproval,Se as resumeFlow,Ee as streamResponse};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import{a as Y}from"./chunk-6V3MNEKB.js";import{a as K}from"./chunk-GKPCIQPB.js";var x=16e3,ue=24e3,ge=4096,he=x*2*8,pe=4,fe=1380533830,me=3e4,ve=5e3,ye=3e4,be=3e4,Se=6e5,we="full-duplex-v1",ke=300,Ae=2e3,Me=2e3,Ce="runtype-browser-v1",Te=54e4,Ee="You're talking to an AI assistant. Voice is processed by OpenAI.",Ie=8e3,Pe=4e3,Ve=12,J=2e3;function _e(i,e){let t="Conversation so far:",s=e.slice(0,Pe).trim(),n=Ie-(s?s.length+2:0)-t.length,r=[];for(let l of i.slice(-Ve).reverse()){let a=l.content.replace(/\s+/g," ").trim();if(!a)continue;a.length>J&&(a=`${a.slice(0,J-1)}\u2026`);let o=`${l.role==="user"?"User":"Assistant"}: ${a}`;if(o.length+1>n)break;n-=o.length+1,r.unshift(o)}let d=r.length>0?[`${t}
|
|
2
|
-
${r.join(`
|
|
3
|
-
`)}`]:[];return s&&d.push(s),d.join(`
|
|
4
|
-
|
|
5
|
-
`)}function Re(i){return i.byteLength>=44&&new DataView(i).getUint32(0,!1)===fe?new Uint8Array(i,44):new Uint8Array(i)}function Z(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 Le(i){return Z(i).replace(/^ws/i,"http")}function De(i){return typeof i!="number"||!Number.isFinite(i)?ye:Math.min(Se,Math.max(be,Math.round(i)))}var P=class{constructor(e){this.config=e;this.type="runtype";this.#e=null;this.#d=null;this.#u=[];this.#i=null;this.#r=null;this.#a=null;this.#m=[];this.#v=0;this.#n=null;this.#g=!1;this.#c=!1;this.#s=0;this.#w=!1;this.#F=Number.NEGATIVE_INFINITY;this.#o=null;this.#l=!1;this.#k=!1;this.#A=0;this.#h=0;this.#y=!1;this.bridge=null;this.#C=!1;this.#p=new Set;this.#b=new Map;this.#T=new Set;this.#D=!1;this.#E=null;this.#O=null;this.#N=Promise.resolve();this.#I=new Set;this.#S=!1;this.#P=new Set;this.#V=new Set;this.#z=[];this.#B=[];this.#W=[];this.#U=[];this.#x=[]}#e;#d;#u;#i;#r;#a;#m;#v;#n;#g;#c;#s;#w;#F;#o;#l;#k;#A;#h;#M;#y;#C;#p;#b;#T;#L;#D;#E;#O;#N;#I;#S;#P;#V;#z;#B;#W;#U;#x;async connect(){}setSessionBridge(e){this.bridge=e}prewarm(){if(this.#g||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.#F<me)){this.#F=n;try{this.config?.prewarmMode==="attach"?this.#te(s,e,t):this.#ee(s,e,t)}catch{}}}#ee(e,t,s){if(typeof fetch!="function")return;let n=`${Le(e)}/v1/client/agents/${encodeURIComponent(t)}/voice/prewarm`;fetch(n,{method:"POST",headers:{Authorization:`Bearer ${s}`},keepalive:!0}).catch(()=>{})}#te(e,t,s){let n=new URLSearchParams,r=this.config?.attachIdleMs,d=De(r);r!==void 0&&n.set("attachIdleMs",String(d));let l=new WebSocket(this.#G(e,t,n),["runtype.bearer","runtype.attach",s]);l.binaryType="arraybuffer";let a=()=>{},o=new Promise(u=>{a=u}),c={ws:l,state:"connecting",ready:o,settle:a,openedAt:0,idleTimer:void 0};this.#o=c,l.onopen=()=>{this.#o===c&&(c.state=l.protocol==="runtype.attach"?"attached":"live",c.openedAt=Date.now(),this.#X(c,d),c.state==="attached"&&l.send('{"type":"ping"}'),c.settle())},l.onmessage=u=>{if(this.#o!==c||typeof u.data!="string")return;let p;try{p=JSON.parse(u.data)}catch{return}p.type==="attached"&&typeof p.idleMs=="number"&&this.#X(c,p.idleMs-(Date.now()-c.openedAt))},l.onerror=()=>{},l.onclose=()=>{this.#o===c&&this.#_()}}#X(e,t){clearTimeout(e.idleTimer),e.idleTimer=setTimeout(()=>{this.#o===e&&this.#_()},Math.max(0,t))}#_(){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#se(){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,ve)})]),clearTimeout(t)}return this.#o!==e?null:e.state==="connecting"||e.ws.readyState!==WebSocket.OPEN?(this.#_(),null):(this.#o=null,clearTimeout(e.idleTimer),{ws:e.ws,live:e.state==="live"})}#G(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:Ce,clientVersion:`persona/${K}`,voiceCapabilities:we,clientCapabilities:n.join(",")});return s?.forEach((d,l)=>r.set(l,d)),`${Z(e)}/ws/agents/${encodeURIComponent(t)}/voice?${r}`}async startListening(){if(this.#g)return;let e=this.config?.agentId,t=this.config?.clientToken,s=this.config?.host;if(!e)throw new Error("Runtype voice requires an agentId");if(!t)throw new Error("Runtype voice requires a clientToken");if(!s)throw new Error("Runtype voice requires a host (or widget apiUrl)");let n=++this.#s;this.#w=!1,this.#g=!0;try{let r=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:x,channelCount:1,echoCancellation:!0}});if(n!==this.#s){r.getTracks().forEach(u=>u.stop());return}this.#i=r;let d=window.AudioContext||window.webkitAudioContext,l=new d({sampleRate:x});l.state==="suspended"&&await l.resume().catch(()=>{}),this.#d=l;let a=this.config?.createPlaybackEngine?await this.config.createPlaybackEngine():new Y(ue);if(n!==this.#s){a.destroy(),r.getTracks().forEach(u=>u.stop()),l.close().catch(()=>{});return}this.#n=a,this.#j(),this.#ie(l,r,n);let o=await this.#se();if(n!==this.#s){o?.ws.close(1e3,"client ended call");return}if(o){let{ws:u}=o;this.#e=u,this.#K(u,n),o.live||u.send('{"type":"start"}'),this.#$(u),this.#t("listening");return}let c=new WebSocket(this.#G(s,e),["runtype.bearer",t]);c.binaryType="arraybuffer",this.#e=c,c.onopen=()=>{n===this.#s&&(this.#$(c),this.#t("listening"))},this.#K(c,n)}catch(r){throw this.#f(),this.#R(r),this.#t("error"),r}}#K(e,t){e.onmessage=s=>this.#ne(s,t),e.onerror=()=>{t===this.#s&&(this.#R(new Error("Voice connection failed")),this.#t("error"),this.#f())},e.onclose=s=>{if(this.#w){this.#w=!1;return}if(t===this.#s){if(s.code!==1e3){let n=s.code?` (code ${s.code})`:"";this.#R(new Error(`Voice connection closed${n}`)),this.#t("error")}else this.#t("idle");this.#f()}}}getDisclosure(){let e=this.config?.disclosureText;return this.#g&&this.#l&&e!==!1?e||Ee:null}async stopListening(){this.#f(),this.#t("idle")}async disconnect(){this.#f(),this.#t("disconnected"),this.#z=[],this.#B=[],this.#W=[],this.#U=[],this.#x=[]}stopPlayback(){this.#Q();let e=this.#e;e&&e.readyState===WebSocket.OPEN&&(this.#l&&(this.#A=Date.now()+Ae,e.send('{"type":"cancel"}')),this.#t("listening"))}getInterruptionMode(){return"barge-in"}isBargeInActive(){return this.#g}onLevel(e){this.#u.push(e)}async deactivateBargeIn(){this.#f(),this.#t("idle")}#ie(e,t,s){let n=e.createMediaStreamSource(t);this.#r=n;let r=e.createScriptProcessor(ge,1,1);this.#a=r,r.onaudioprocess=d=>{if(s!==this.#s)return;let l=this.#e,a=l?.readyState===WebSocket.OPEN;if(l&&!a&&l.readyState!==WebSocket.CONNECTING)return;let o=d.inputBuffer.getChannelData(0),c=new Int16Array(o.length),u=0;for(let p=0;p<o.length;p++){let f=Math.max(-1,Math.min(1,o[p]));u+=f*f,c[p]=f<0?f*32768:f*32767}if(l&&a)this.#$(l),l.send(c.buffer);else for(this.#m.push(c.buffer),this.#v+=c.byteLength;this.#v>he;)this.#v-=this.#m.shift().byteLength;if(this.#u.length>0){let p=Math.sqrt(u/o.length),f=Math.max(0,Math.min(1,p*pe));for(let g of this.#u)g(f)}},n.connect(r),r.connect(e.destination)}#$(e){for(let t of this.#m)e.send(t);this.#m=[],this.#v=0}#ne(e,t){if(t!==this.#s)return;if(e.data instanceof ArrayBuffer){this.#ce(e.data,t);return}let s;try{s=JSON.parse(e.data)}catch{return}switch(s.type){case"session_config":s.speechMode&&(this.#l=s.speechMode==="speech_to_speech",this.#n?.setContinuousMode?.(this.#l)),Array.isArray(s.capabilities)&&(this.#p=new Set(s.capabilities)),typeof s.callId=="string"&&(this.#L=s.callId),this.#C=this.#l&&this.#p.has("client_delegation")&&!!this.bridge,this.#l&&!this.#c&&this.#t("listening"),this.#p.has("context")&&!this.#D&&(this.#D=!0,this.#E=this.#oe());break;case"transcript_update":{let n=s.role==="assistant"?"assistant":"user",r=s.utteranceId??s.turnId;if(!r||n==="assistant"&&this.#q())break;let d=String(r);if(n==="user")s.final===!0&&this.#Y(t),this.#S=!1;else{if(this.#p.has("delegation_read_back")){let l=s.delegationId;typeof l=="string"&&this.#I.has(l)&&!this.#P.has(d)&&this.#V.add(d)}else this.#S&&!this.#P.has(d)&&(this.#V.add(d),this.#S=!1);if(this.#P.add(d),this.#V.has(d))break}this.#H(n,s.text??"",s.final===!0,{turnId:d,...typeof s.startMs=="number"&&{startMs:s.startMs},...typeof s.endMs=="number"&&{endMs:s.endMs},...this.#C&&{caption:!0}});break}case"delegation_started":case"delegation_completed":{let n=String(s.delegationId??s.turnId);if(s.type==="delegation_started"){this.#Y(t);let r=s.input;this.#C&&r&&this.#ae(n,{delegationId:n,userText:String(r.text??""),userUtteranceIds:Array.isArray(r.userUtteranceIds)?r.userUtteranceIds.filter(d=>typeof d=="string"):[],messages:Array.isArray(r.messages)?r.messages:[]},t)}else s.speak!==!1&&this.#I.has(n)&&(this.#S=!0);this.#k=s.type==="delegation_started",!this.#c&&!this.#q()&&this.#t(this.#k?"processing":"listening");break}case"delegation_cancelled":this.#J(String(s.delegationId),s.reason!=="session_ending"&&s.reason!=="provider_cancelled");break;case"warning":console.warn(`[Persona voice] ${s.code}: ${s.message??""}`,this.#L??""),s.delegationId&&(s.code==="UNKNOWN_DELEGATION"||s.code==="LATE_RESULT_LIMIT")&&this.#J(String(s.delegationId));break;case"audio_clear":this.#Q(),this.#A=0,this.#t("listening");break;case"transcript_interim":this.#t("listening"),this.#H("user",s.text??"",!1);break;case"transcript_final":{let n=s.role==="assistant"?"assistant":"user";this.#t(n==="user"?"processing":"speaking"),this.#H(n,s.text??"",!0);break}case"audio_end":this.#n?this.#n.markStreamEnd():(this.#c=!1,this.#t("listening"));break;case"metrics":this.#de({llmMs:s.llm_ms,ttsMs:s.tts_ms,firstAudioMs:s.first_audio_ms,totalMs:s.total_ms});break;case"error":this.#R(new Error(s.error||"Voice error")),this.#t("error");break}}async#oe(){let e=this.bridge?.getHistory()??[],t="";try{let s=this.config?.callContext;t=String((typeof s=="function"?await s():s)??"")}catch{}return _e(e,t)}#Y(e){let t=this.#E;if(!t)return;this.#E=null;let s=!1,n=t.then(r=>{let d=this.#e;s||!r||e!==this.#s||d?.readyState!==WebSocket.OPEN||d.send(JSON.stringify({type:"context",text:r}))});this.#O={done:n,drop:()=>s=!0}}async#re(){let e=this.#O;if(!e)return;let t,s=await Promise.race([e.done.then(()=>!1),new Promise(n=>{t=setTimeout(()=>n(!0),Me)})]);clearTimeout(t),s&&e.drop()}#J(e,t=!1){this.#T.add(e),this.bridge?.dropDelegation?.(e,t),this.#b.get(e)?.abort()}#ae(e,t,s){let n=this.bridge,r=(d,l,a)=>{let o=this.#e;return s!==this.#s||o?.readyState!==WebSocket.OPEN||this.#T.has(e)?!1:(o.send(JSON.stringify({type:d,delegationId:e,status:l,text:a})),!0)};this.#N=this.#N.then(async()=>{if(s!==this.#s||this.#T.has(e))return;let d=await n.runDelegatedTurn(t).catch(()=>({status:"failed",text:""}));await this.#re();let l=!!d.followUp,a=l&&this.#p.has("delegation_update");if(!r(a?"delegation_update":"delegation_result",l&&!a?"completed":d.status,d.text)||(d.status!=="failed"&&d.inChat!==!1&&this.#I.add(e),!d.followUp))return;let c=new AbortController;this.#b.set(e,c),d.followUp({signal:c.signal,approvalTimeoutMs:this.config?.approvalTimeoutMs&&Math.min(this.config.approvalTimeoutMs,Te),readBack:a,onUpdate:u=>r("delegation_update","pending_approval",u)}).then(u=>{this.#b.delete(e),a&&u&&!c.signal.aborted&&r("delegation_result",u.status,u.text)})})}#ce(e,t){if(t!==this.#s||!this.#n||this.#q())return;let s=Re(e);s.length!==0&&(this.#c||(this.#c=!0,this.#t("speaking")),this.#j(),this.#n.enqueue(s),this.#l&&this.#le(s.length))}#q(){return Date.now()<this.#A}#j(){if(this.#y||!this.#n)return;this.#y=!0;let e=this.#s;this.#n.onFinished(()=>{e===this.#s&&(this.#y=!1,this.#Z())})}#le(e){let t=Date.now();this.#h=Math.max(t,this.#h)+e/48,clearTimeout(this.#M),this.#M=setTimeout(()=>this.#Z(),this.#h-t+ke)}#Z(){this.#c=!1,this.#h=0,this.#e&&this.#e.readyState===WebSocket.OPEN&&this.#t(this.#k?"processing":"listening")}#Q(){this.#n?.flush(),this.#y=!1,this.#j(),this.#c=!1,this.#h=0,clearTimeout(this.#M)}#f(){this.#s+=1,this.#g=!1,this.#c=!1,this.#_(),this.#l=!1,this.#k=!1,this.#C=!1,this.#p=new Set;for(let e of this.#b.values())e.abort();if(this.#b.clear(),this.#T.clear(),this.#L=void 0,this.#D=!1,this.#E=null,this.#O=null,this.#N=Promise.resolve(),this.#I.clear(),this.#S=!1,this.#P.clear(),this.#V.clear(),this.#y=!1,this.#A=0,this.#h=0,clearTimeout(this.#M),this.#m=[],this.#v=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.#d&&(this.#d.close().catch(()=>{}),this.#d=null),this.#n&&(this.#n.destroy(),this.#n=null),this.#e){this.#w=!0;try{this.#e.close(1e3,"client ended call")}catch{}this.#e=null}}onResult(e){this.#z.push(e)}onError(e){this.#B.push(e)}onStatusChange(e){this.#W.push(e)}onTranscript(e){this.#U.push(e)}onMetrics(e){this.#x.push(e)}#t(e){this.#W.forEach(t=>t(e))}#R(e){this.#B.forEach(t=>t(e))}#H(e,t,s,n){this.#U.forEach(r=>r(e,t,s,n))}#de(e){this.#x.forEach(t=>t(e))}};var C=class{constructor(e={}){this.config=e;this.type="browser";this.#e=null;this.#d=[];this.#u=[];this.#i=[];this.#r=!1;this.#a=typeof window<"u"?window:void 0}#e;#d;#u;#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.#d.forEach(r=>r({text:s,confidence:n?.8:.5,provider:"browser"})),n&&!this.config?.continuous&&this.stopListening()},this.#e.onerror=t=>{this.#u.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.#u.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.#d.push(e)}onError(e){this.#u.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 _(i){switch(i.type){case"runtype":if(!i.runtype)throw new Error("Runtype voice provider requires configuration");return new P(i.runtype);case"browser":if(!C.isSupported())throw new Error("Browser speech recognition not supported");return new C(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 Q(i){if(i?.type==="custom"&&i.custom)return _({type:"custom",custom:i.custom});if(i?.type==="runtype"&&i.runtype)return _({type:"runtype",runtype:i.runtype});if(C.isSupported())return _({type:"browser",browser:i?.browser||{language:"en-US"}});throw new Error("No supported voice providers available")}function Oe(i){try{return Q(i),!0}catch{return!1}}var ee=i=>i.toLowerCase().replace(/[^\p{L}\p{N}]+/gu,""),$=class{constructor(e){this.host=e;this.turns=new Map;this.latestId=null;this.cancelled=new Set}apply(e,t,s,n,r,d){let l=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?l.find(a.userId):void 0;if(o){let c=a.userStartMs===void 0&&r!==void 0;c&&(a.userStartMs=r),l.upsert({...o,content:t,voiceProcessing:!s,...c&&this.placeBefore(this.startedAfter(r))})}else{a.userStartMs=r;let c=a.assistantId?l.find(a.assistantId):void 0;a.userId=l.inject({role:"user",content:t,streaming:!1,voiceProcessing:!s,voiceCaption:d,...this.placeBefore(this.startedAfter(r)??c)}).id}}else if(!this.cancelled.has(n)){a.assistantFinal=s;let o=a.assistantId?l.find(a.assistantId):void 0;if(o){let c=a.assistantStartMs===void 0&&r!==void 0;c&&(a.assistantStartMs=r),l.upsert({...o,content:t,streaming:!s,voiceProcessing:!s,...c&&this.placeBefore(this.startedAfter(r))})}else t&&(a.assistantStartMs=r,a.assistantId=l.inject({role:"assistant",content:t,streaming:!s,voiceProcessing:!s,voiceCaption:d,...this.placeBefore(this.startedAfter(r))}).id);s&&a.assistantId&&l.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=ee(e),d=[...this.turns.values()].reverse().flatMap(l=>{let a=l.userId&&!l.claimed?this.host.find(l.userId):void 0;return a?[{turn:l,text:ee(a.content)}]:[]}).filter(({text:l})=>l&&r);n=(d.find(({text:l})=>l===r)??d.find(({text:l})=>l.startsWith(r)||r.startsWith(l)))?.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 d of s)this.turns.get(d).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 d=r?this.host.find(r):void 0,l=d?.sequence;d?.createdAt===e.createdAt&&l!==void 0&&l>s&&l<t&&(s=l)}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 Ne="I need your answer in the chat before I can continue.";var te="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)",Be=new RegExp(`^(?:${te})(?: (?:${te}))?(?: thanks| thank you)?$`),We=i=>Be.test(i.toLowerCase().replace(/[^a-z' ]+/g," ").replace(/\s+/g," ").trim()),se="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.",ie=()=>new Promise(i=>setTimeout(i,50)),q=(i,e)=>i.length>e?`${i.slice(0,e-1).trimEnd()}\u2026`:i,E=i=>i.replace(/^webmcp:/,"").replace(/([a-z\d])([A-Z])/g,"$1 $2").replace(/[_\-.]+/g," ").trim().toLowerCase();function j(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=>j(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,d])=>{let l=j(d,e+1);return l&&(e?`${E(r)} ${l}`:`${E(r)}: ${l}`)}).filter(Boolean).join(e?", ":"; ")}function ne(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-se.length-2,s=i.map(n=>{let r=[E(n.toolName)];n.description&&r.push(`(${q(n.description.trim(),160)})`);let d=j(n.parameters);return d&&r.push(`with ${d}`),n.reason&&r.push(`because: ${q(n.reason.trim(),160)}`),q(`- ${r.join(" ")}`,Math.floor(t/i.length)-1)});return`${e}
|
|
6
|
-
${s.join(`
|
|
7
|
-
`)}
|
|
8
|
-
|
|
9
|
-
${se}`}var R=i=>i.variant==="approval"&&i.approval?.status==="pending";function Ue(i){let e=null,t=null,s=()=>i.track(e??t),n=[],r=new Map,d=new Map,l=(g,m)=>{r.set(g,[...r.get(g)??[],m]),r.size>32&&r.delete(r.keys().next().value)},a=g=>i.messages().find(m=>m.id===g),o=g=>g.approvals.filter(m=>a(m)?.approval?.status==="pending"),c=g=>E(a(g)?.approval?.toolName??""),u=(g,m,b)=>{g.outcome={status:m,text:b};for(let S of o(g))i.decide(S)},p=g=>g.flatMap(m=>i.messages().find(b=>b.id===m)??[]).filter(m=>!m.variant&&m.content.trim()).map(m=>m.content.trim()).join(`
|
|
10
|
-
|
|
11
|
-
`),f=async g=>{do await ie();while(i.busy()&&!(g&&(i.parked()||g.failed)))};return{getHistory:()=>i.messages().flatMap(g=>(g.role==="user"||g.role==="assistant")&&!g.variant&&!g.voiceProcessing&&!g.voiceCaption&&g.content?[{role:g.role,content:g.content}]:[]),dropDelegation(g,m){d.set(g,!!m),d.size>32&&d.delete(d.keys().next().value);let b=m&&n.find(S=>S.delegationId===g);b&&!b.outcome&&u(b,"timeout","");for(let S of r.get(g)??[])S.dropped=!0,S.ids.length&&i.unspoken(S.ids)},async runDelegatedTurn({delegationId:g,userText:m,userUtteranceIds:b}){let S=n.filter(h=>o(h).length),I=S.flatMap(o);if(I.length===1&&We(m)){let h=c(I[0]);return u(S[0],"denied",""),{status:"denied",text:`Okay, I cancelled the ${h} request. Nothing was done.`,inChat:!1}}let H=i.parked()&&i.messages().every(h=>!R(h)||I.includes(h.id));i.busy()&&!H&&await f();let oe=new Set(i.messages().map(h=>h.id)),L=()=>i.messages().filter(h=>!oe.has(h.id)&&h.variant==="approval"),k={ids:[],failed:!1};l(g,k),e=k,s();try{await i.send(m,i.claim(m,b)??void 0),await f(k)}catch{k.failed=!0}finally{e=null,s()}let re=k.ids.flatMap(h=>i.messages().find(y=>y.id===h)??[]),T=p(k.ids),V=k.failed?[]:L().filter(R),ae=new Set(V.map(h=>E(h.approval.toolName)));for(let h of S){if(h.outcome)continue;for(let w of o(h).filter(D=>ae.has(c(D))))h.replaced.add(w),i.decide(w);let y=c(h.approvals[0]);h.approvals.every(w=>h.replaced.has(w))?h.outcome={status:"cancelled",text:`The earlier ${y} request was replaced by the new one; it was not done.`}:H&&h.approvals.every(w=>a(w)?.approval?.status==="denied")&&(h.outcome={status:"cancelled",text:`The earlier ${y} request was replaced by the new one; it was not done.`})}if(V.length){let h=new Set(V.map(w=>w.id)),y={delegationId:g,approvals:[...h],at:Date.now(),replaced:new Set};return n.push(y),d.get(g)&&u(y,"timeout",""),T=`${T}
|
|
12
|
-
|
|
13
|
-
${ne(V.map(w=>w.approval))}`.trim(),{status:"pending_approval",text:T,followUp:async({signal:w,approvalTimeoutMs:D=3e5,readBack:F=!0,onUpdate:ce})=>{let A={ids:[],failed:!1};r.delete(g),l(g,A);let z=()=>L().filter(v=>h.has(v.id)||A.ids.includes(v.id)),le=()=>L().filter(v=>R(v)&&!h.has(v.id)&&!n.some(M=>M.approvals.includes(v.id))&&(A.ids.includes(v.id)||v.approval?.toolType==="webmcp"&&t===A)),X=()=>{t=A,s()};F&&X();try{for(;!w.aborted&&!y.outcome&&(e||i.busy()||z().some(R));){F&&t!==A&&!o(y).length&&X();let v=le();if(v.length){for(let M of v)h.add(M.id),y.approvals.push(M.id);y.at=Date.now(),ce?.(ne(v.map(M=>M.approval)))}if(Date.now()-y.at>=D&&o(y).length&&(!i.busy()||i.parked())){u(y,"timeout",`That ${c(y.approvals[0])} request expired, so nothing was done.`);break}await ie()}}finally{t===A&&(t=null,s()),n.splice(n.indexOf(y),1)}if(w.aborted)return null;if(y.outcome)return y.outcome;if(A.failed)return{status:"failed",text:`The ${c(y.approvals[0])} request didn't complete.`};let G=z(),O=v=>G.filter(v).map(M=>E(M.approval.toolName)).join(", "),N=G.some(v=>v.approval?.status==="approved"),B=O(v=>v.approval?.status==="denied"&&!y.replaced.has(v.id)),W=O(v=>y.replaced.has(v.id)),U=O(v=>v.approval?.status==="timeout"),de=[N?p(A.ids):"",B&&`The user declined the ${B} request in the chat, so ${N?"that part was not done":"nothing was done"}.`,W&&`The earlier ${W} request was replaced by the new one; it was not done.`,U&&`The ${U} request timed out, so it was not done.`].filter(Boolean).join(`
|
|
14
|
-
|
|
15
|
-
`);return{status:N?"completed":B?"denied":W?"cancelled":U?"timeout":"completed",text:de}}}}return!k.failed&&i.parked()||re.some(h=>h.agentMetadata?.awaitingLocalTool&&!h.agentMetadata.askUserQuestionAnswered&&!h.toolCall?.name?.startsWith("webmcp:"))?{status:k.failed?"failed":"completed",text:`${T}
|
|
16
|
-
|
|
17
|
-
${Ne}`.trim()}:{status:!k.failed&&T?"completed":"failed",text:T}}}}var xe=(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(),d=()=>i.provider()===e&&r===i.generation(),a=(n.config.voiceRecognition??{}).processingErrorText??"Voice processing failed. Please try again.";e.onResult(o=>{d()&&o.provider!=="runtype"&&o.text&&o.text.trim()&&n.sendMessage(o.text,{viaVoice:!0})}),e.onTranscript&&e.onTranscript((o,c,u,p)=>{if(d()){if(p?.turnId){i.keyed?.apply(o,c,u,p.turnId,p.startMs,p.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 f=n.injectMessage({role:"user",content:c,streaming:!1,voiceProcessing:!u});i.pendingUser=f.id}if(u){i.turns().start(p?.turnId),i.pendingUser=null;let f=n.injectMessage({role:"assistant",content:"",streaming:!0,voiceProcessing:!0});i.pendingAssistant=f.id,i.setStreaming(!0)}}else{if(!i.turns().accepts(p?.turnId))return;if(i.pendingAssistant)n.upsertMessage({id:i.pendingAssistant,role:"assistant",content:c,createdAt:new Date().toISOString(),streaming:!u,voiceProcessing:!u});else{let f=n.injectMessage({role:"assistant",content:c,streaming:!u,voiceProcessing:!u});i.pendingAssistant=f.id}u&&(i.pendingAssistant&&n.ttsSpokenMessageIds.add(i.pendingAssistant),i.setStreaming(!1),i.pendingAssistant=null)}}}),e.onLevel&&e.onLevel(o=>{d()&&i.setLevel(Number.isFinite(o)?Math.max(0,Math.min(1,o)):0)}),e.onMetrics&&e.onMetrics(o=>{d()&&n.config.voiceRecognition?.onMetrics?.(o)}),e.onError(o=>{d()&&(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=>{d()&&(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 $e=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 p=i.original?.iconSize??(parseFloat(i.config().voiceRecognition?.iconSize??"")||i.iconFallbackPx),f=i.renderIcon(o,p,c,1.5);f&&a.appendChild(f)},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,p=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 f=a.querySelector("svg");f&&f.setAttribute("stroke",u)}p&&(a.style.borderColor=p),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,p=o.processingIconName??"loader",f=o.processingIconColor??u?.color??"",g=o.processingBackgroundColor??u?.backgroundColor??"",m=o.processingBorderColor??u?.borderColor??"";s(a),a.classList.add("persona-voice-processing"),i.setMicState("processing"),a.style.backgroundColor=g,a.style.borderColor=m;let b=f||"currentColor";a.style.color=b,t(a,p,b),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,p=c==="cancel"?"square":c==="barge-in"?"mic":"volume-2",f=o.speakingIconName??p,g=o.speakingIconColor??(c==="barge-in"?o.recordingIconColor??u?.color??"":u?.color??""),m=o.speakingBackgroundColor??(c==="barge-in"?o.recordingBackgroundColor??"var(--persona-voice-recording-bg, #ef4444)":u?.backgroundColor??""),b=o.speakingBorderColor??(c==="barge-in"?o.recordingBorderColor??"":u?.borderColor??"");s(a),a.classList.add("persona-voice-speaking"),i.setMicState("speaking"),a.style.backgroundColor=m,a.style.borderColor=b;let S=g||"currentColor";a.style.color=S,t(a,f,S);let I=c==="cancel"?"Stop playback and re-record":c==="barge-in"?"Speak to interrupt":"Agent is speaking";a.setAttribute("aria-label",I),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{C as BrowserVoiceProvider,$ as KeyedVoiceTranscript,P as RuntypeVoiceProvider,Q as createBestAvailableVoiceProvider,$e as createMicStateStyles,_ as createVoiceProvider,Ue as createVoiceSessionBridge,Oe as isVoiceSupported,xe as wireSessionVoice};
|