@absolutejs/voice 0.0.22-beta.60 → 0.0.22-beta.62

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.
@@ -387,8 +387,277 @@ var VoiceOpsStatus = ({
387
387
  ]
388
388
  }, undefined, true, undefined, this);
389
389
  };
390
- // src/react/useVoiceProviderStatus.tsx
390
+ // src/client/providerSimulationControls.ts
391
+ var postSimulation = async (pathPrefix, mode, provider, fetchImpl) => {
392
+ const response = await fetchImpl(`${pathPrefix}/${mode}?provider=${encodeURIComponent(provider)}`, { method: "POST" });
393
+ const body = await response.json().catch(() => null);
394
+ if (!response.ok) {
395
+ const message = body && typeof body === "object" && "error" in body ? String(body.error) : `Voice provider simulation failed: HTTP ${response.status}`;
396
+ throw new Error(message);
397
+ }
398
+ return body;
399
+ };
400
+ var createVoiceProviderSimulationControlsStore = (options) => {
401
+ const listeners = new Set;
402
+ const fetchImpl = options.fetch ?? globalThis.fetch;
403
+ const pathPrefix = options.pathPrefix ?? `/api/${options.kind ?? "stt"}-simulate`;
404
+ let closed = false;
405
+ let snapshot = {
406
+ error: null,
407
+ isRunning: false,
408
+ lastResult: null,
409
+ mode: null,
410
+ provider: null
411
+ };
412
+ const emit = () => {
413
+ for (const listener of listeners) {
414
+ listener();
415
+ }
416
+ };
417
+ const run = async (provider, mode) => {
418
+ if (closed) {
419
+ return snapshot.lastResult;
420
+ }
421
+ snapshot = {
422
+ ...snapshot,
423
+ error: null,
424
+ isRunning: true,
425
+ mode,
426
+ provider
427
+ };
428
+ emit();
429
+ try {
430
+ const result = await postSimulation(pathPrefix, mode, provider, fetchImpl);
431
+ snapshot = {
432
+ error: null,
433
+ isRunning: false,
434
+ lastResult: result,
435
+ mode,
436
+ provider,
437
+ updatedAt: Date.now()
438
+ };
439
+ emit();
440
+ return result;
441
+ } catch (error) {
442
+ snapshot = {
443
+ ...snapshot,
444
+ error: error instanceof Error ? error.message : String(error),
445
+ isRunning: false
446
+ };
447
+ emit();
448
+ throw error;
449
+ }
450
+ };
451
+ const close = () => {
452
+ closed = true;
453
+ listeners.clear();
454
+ };
455
+ return {
456
+ close,
457
+ getServerSnapshot: () => snapshot,
458
+ getSnapshot: () => snapshot,
459
+ run,
460
+ subscribe: (listener) => {
461
+ listeners.add(listener);
462
+ return () => {
463
+ listeners.delete(listener);
464
+ };
465
+ }
466
+ };
467
+ };
468
+
469
+ // src/client/providerSimulationControlsWidget.ts
470
+ var escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
471
+ var formatKind = (kind) => (kind ?? "stt").toUpperCase();
472
+ var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
473
+ const configuredProviders = options.providers.filter((provider) => provider.configured !== false);
474
+ const fallbackReady = !options.fallbackRequiredProvider || configuredProviders.some((entry) => entry.provider === options.fallbackRequiredProvider);
475
+ const failureProviders = (options.failureProviders ? options.failureProviders.map((provider) => ({ provider })) : configuredProviders).filter((provider) => configuredProviders.some((entry) => entry.provider === provider.provider));
476
+ return {
477
+ canSimulateFailure: configuredProviders.length > 0 && fallbackReady,
478
+ description: options.failureMessage ?? `Simulate ${formatKind(options.kind)} provider failure and recovery without changing credentials.`,
479
+ error: snapshot.error,
480
+ failureProviders,
481
+ isRunning: snapshot.isRunning,
482
+ label: snapshot.isRunning ? `Running ${snapshot.mode ?? "simulation"}` : snapshot.lastResult ? `${snapshot.lastResult.provider} ${snapshot.lastResult.mode} simulated` : configuredProviders.length ? `${configuredProviders.length} configured` : "No configured providers",
483
+ providers: configuredProviders,
484
+ resultText: snapshot.lastResult ? JSON.stringify(snapshot.lastResult, null, 2) : null,
485
+ title: options.title ?? `${formatKind(options.kind)} Failure Simulation`
486
+ };
487
+ };
488
+ var renderVoiceProviderSimulationControlsHTML = (snapshot, options) => {
489
+ const model = createVoiceProviderSimulationControlsViewModel(snapshot, options);
490
+ const failureButtons = model.failureProviders.map((provider) => `<button type="button" data-voice-provider-fail="${escapeHtml2(provider.provider)}"${!model.canSimulateFailure || snapshot.isRunning ? " disabled" : ""}>Simulate ${escapeHtml2(provider.provider)} ${escapeHtml2(formatKind(options.kind))} failure</button>`).join("");
491
+ const recoveryButtons = model.providers.map((provider) => `<button type="button" data-voice-provider-recover="${escapeHtml2(provider.provider)}"${snapshot.isRunning ? " disabled" : ""}>Mark ${escapeHtml2(provider.provider)} recovered</button>`).join("");
492
+ return `<section class="absolute-voice-provider-simulation absolute-voice-provider-simulation--${snapshot.error ? "error" : snapshot.isRunning ? "running" : "ready"}">
493
+ <header class="absolute-voice-provider-simulation__header">
494
+ <span class="absolute-voice-provider-simulation__eyebrow">${escapeHtml2(model.title)}</span>
495
+ <strong class="absolute-voice-provider-simulation__label">${escapeHtml2(model.label)}</strong>
496
+ </header>
497
+ <p class="absolute-voice-provider-simulation__description">${escapeHtml2(model.description)}</p>
498
+ ${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${escapeHtml2(options.fallbackRequiredMessage ?? "Configure fallback providers before simulating failure.")}</p>`}
499
+ <div class="absolute-voice-provider-simulation__actions">${failureButtons}${recoveryButtons}</div>
500
+ ${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${escapeHtml2(snapshot.error)}</p>` : ""}
501
+ ${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${escapeHtml2(model.resultText)}</pre>` : ""}
502
+ </section>`;
503
+ };
504
+ var bindVoiceProviderSimulationControls = (element, store) => {
505
+ const onClick = (event) => {
506
+ const target = event.target;
507
+ if (!(target instanceof HTMLElement)) {
508
+ return;
509
+ }
510
+ const failProvider = target.getAttribute("data-voice-provider-fail");
511
+ const recoverProvider = target.getAttribute("data-voice-provider-recover");
512
+ if (failProvider) {
513
+ store.run(failProvider, "failure").catch(() => {});
514
+ }
515
+ if (recoverProvider) {
516
+ store.run(recoverProvider, "recovery").catch(() => {});
517
+ }
518
+ };
519
+ element.addEventListener("click", onClick);
520
+ return () => element.removeEventListener("click", onClick);
521
+ };
522
+ var mountVoiceProviderSimulationControls = (element, options) => {
523
+ const store = createVoiceProviderSimulationControlsStore(options);
524
+ const render = () => {
525
+ element.innerHTML = renderVoiceProviderSimulationControlsHTML(store.getSnapshot(), options);
526
+ };
527
+ const unsubscribeStore = store.subscribe(render);
528
+ const unsubscribeDom = bindVoiceProviderSimulationControls(element, store);
529
+ render();
530
+ return {
531
+ close: () => {
532
+ unsubscribeDom();
533
+ unsubscribeStore();
534
+ store.close();
535
+ },
536
+ run: store.run
537
+ };
538
+ };
539
+ var defineVoiceProviderSimulationControlsElement = (tagName = "absolute-voice-provider-simulation") => {
540
+ if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
541
+ return;
542
+ }
543
+ customElements.define(tagName, class AbsoluteVoiceProviderSimulationElement extends HTMLElement {
544
+ mounted;
545
+ connectedCallback() {
546
+ const providers = (this.getAttribute("providers") ?? "").split(",").map((provider) => provider.trim()).filter(Boolean).map((provider) => ({ provider }));
547
+ const failureProviders = (this.getAttribute("failure-providers") ?? "").split(",").map((provider) => provider.trim()).filter(Boolean);
548
+ this.mounted = mountVoiceProviderSimulationControls(this, {
549
+ failureProviders: failureProviders.length ? failureProviders : undefined,
550
+ fallbackRequiredMessage: this.getAttribute("fallback-required-message") ?? undefined,
551
+ fallbackRequiredProvider: this.getAttribute("fallback-required-provider") ?? undefined,
552
+ failureMessage: this.getAttribute("failure-message") ?? undefined,
553
+ kind: this.getAttribute("kind") ?? "stt",
554
+ pathPrefix: this.getAttribute("path-prefix") ?? undefined,
555
+ providers,
556
+ title: this.getAttribute("title") ?? undefined
557
+ });
558
+ }
559
+ disconnectedCallback() {
560
+ this.mounted?.close();
561
+ this.mounted = undefined;
562
+ }
563
+ });
564
+ };
565
+
566
+ // src/react/useVoiceProviderSimulationControls.tsx
391
567
  import { useEffect as useEffect2, useRef as useRef2, useSyncExternalStore as useSyncExternalStore2 } from "react";
568
+ var useVoiceProviderSimulationControls = (options) => {
569
+ const storeRef = useRef2(null);
570
+ if (!storeRef.current) {
571
+ storeRef.current = createVoiceProviderSimulationControlsStore(options);
572
+ }
573
+ const store = storeRef.current;
574
+ useEffect2(() => () => store.close(), [store]);
575
+ return {
576
+ ...useSyncExternalStore2(store.subscribe, store.getSnapshot, store.getServerSnapshot),
577
+ run: store.run
578
+ };
579
+ };
580
+
581
+ // src/react/VoiceProviderSimulationControls.tsx
582
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
583
+ var VoiceProviderSimulationControls = ({
584
+ className,
585
+ ...options
586
+ }) => {
587
+ const snapshot = useVoiceProviderSimulationControls(options);
588
+ const model = createVoiceProviderSimulationControlsViewModel(snapshot, options);
589
+ const run = (provider, mode) => {
590
+ snapshot.run(provider, mode).catch(() => {});
591
+ };
592
+ return /* @__PURE__ */ jsxDEV2("section", {
593
+ className: [
594
+ "absolute-voice-provider-simulation",
595
+ `absolute-voice-provider-simulation--${snapshot.error ? "error" : snapshot.isRunning ? "running" : "ready"}`,
596
+ className
597
+ ].filter(Boolean).join(" "),
598
+ children: [
599
+ /* @__PURE__ */ jsxDEV2("header", {
600
+ className: "absolute-voice-provider-simulation__header",
601
+ children: [
602
+ /* @__PURE__ */ jsxDEV2("span", {
603
+ className: "absolute-voice-provider-simulation__eyebrow",
604
+ children: model.title
605
+ }, undefined, false, undefined, this),
606
+ /* @__PURE__ */ jsxDEV2("strong", {
607
+ className: "absolute-voice-provider-simulation__label",
608
+ children: model.label
609
+ }, undefined, false, undefined, this)
610
+ ]
611
+ }, undefined, true, undefined, this),
612
+ /* @__PURE__ */ jsxDEV2("p", {
613
+ className: "absolute-voice-provider-simulation__description",
614
+ children: model.description
615
+ }, undefined, false, undefined, this),
616
+ model.canSimulateFailure ? null : /* @__PURE__ */ jsxDEV2("p", {
617
+ className: "absolute-voice-provider-simulation__empty",
618
+ children: options.fallbackRequiredMessage ?? "Configure fallback providers before simulating failure."
619
+ }, undefined, false, undefined, this),
620
+ /* @__PURE__ */ jsxDEV2("div", {
621
+ className: "absolute-voice-provider-simulation__actions",
622
+ children: [
623
+ model.failureProviders.map((provider) => /* @__PURE__ */ jsxDEV2("button", {
624
+ disabled: !model.canSimulateFailure || snapshot.isRunning,
625
+ onClick: () => run(provider.provider, "failure"),
626
+ type: "button",
627
+ children: [
628
+ "Simulate ",
629
+ provider.provider,
630
+ " ",
631
+ (options.kind ?? "stt").toUpperCase(),
632
+ " ",
633
+ "failure"
634
+ ]
635
+ }, `fail-${provider.provider}`, true, undefined, this)),
636
+ model.providers.map((provider) => /* @__PURE__ */ jsxDEV2("button", {
637
+ disabled: snapshot.isRunning,
638
+ onClick: () => run(provider.provider, "recovery"),
639
+ type: "button",
640
+ children: [
641
+ "Mark ",
642
+ provider.provider,
643
+ " recovered"
644
+ ]
645
+ }, `recover-${provider.provider}`, true, undefined, this))
646
+ ]
647
+ }, undefined, true, undefined, this),
648
+ snapshot.error ? /* @__PURE__ */ jsxDEV2("p", {
649
+ className: "absolute-voice-provider-simulation__error",
650
+ children: snapshot.error
651
+ }, undefined, false, undefined, this) : null,
652
+ model.resultText ? /* @__PURE__ */ jsxDEV2("pre", {
653
+ className: "absolute-voice-provider-simulation__result",
654
+ children: model.resultText
655
+ }, undefined, false, undefined, this) : null
656
+ ]
657
+ }, undefined, true, undefined, this);
658
+ };
659
+ // src/react/useVoiceProviderStatus.tsx
660
+ import { useEffect as useEffect3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore3 } from "react";
392
661
 
393
662
  // src/client/providerStatus.ts
394
663
  var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
@@ -472,17 +741,17 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
472
741
 
473
742
  // src/react/useVoiceProviderStatus.tsx
474
743
  var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
475
- const storeRef = useRef2(null);
744
+ const storeRef = useRef3(null);
476
745
  if (!storeRef.current) {
477
746
  storeRef.current = createVoiceProviderStatusStore(path, options);
478
747
  }
479
748
  const store = storeRef.current;
480
- useEffect2(() => {
749
+ useEffect3(() => {
481
750
  store.refresh().catch(() => {});
482
751
  return () => store.close();
483
752
  }, [store]);
484
753
  return {
485
- ...useSyncExternalStore2(store.subscribe, store.getSnapshot, store.getServerSnapshot),
754
+ ...useSyncExternalStore3(store.subscribe, store.getSnapshot, store.getServerSnapshot),
486
755
  refresh: store.refresh
487
756
  };
488
757
  };
@@ -490,7 +759,7 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
490
759
  // src/client/providerStatusWidget.ts
491
760
  var DEFAULT_TITLE2 = "Voice Providers";
492
761
  var DEFAULT_DESCRIPTION2 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
493
- var escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
762
+ var escapeHtml3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
494
763
  var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
495
764
  var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
496
765
  var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
@@ -546,25 +815,25 @@ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
546
815
  };
547
816
  var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
548
817
  const model = createVoiceProviderStatusViewModel(snapshot, options);
549
- const providers = model.providers.length ? `<div class="absolute-voice-provider-status__providers">${model.providers.map((provider) => `<article class="absolute-voice-provider-status__provider absolute-voice-provider-status__provider--${escapeHtml2(provider.status)}">
818
+ const providers = model.providers.length ? `<div class="absolute-voice-provider-status__providers">${model.providers.map((provider) => `<article class="absolute-voice-provider-status__provider absolute-voice-provider-status__provider--${escapeHtml3(provider.status)}">
550
819
  <header>
551
- <strong>${escapeHtml2(provider.label)}</strong>
552
- <span>${escapeHtml2(formatStatus(provider.status))}</span>
820
+ <strong>${escapeHtml3(provider.label)}</strong>
821
+ <span>${escapeHtml3(formatStatus(provider.status))}</span>
553
822
  </header>
554
- <p>${escapeHtml2(provider.detail)}</p>
823
+ <p>${escapeHtml3(provider.detail)}</p>
555
824
  <dl>${provider.rows.map((row) => `<div>
556
- <dt>${escapeHtml2(row.label)}</dt>
557
- <dd>${escapeHtml2(row.value)}</dd>
825
+ <dt>${escapeHtml3(row.label)}</dt>
826
+ <dd>${escapeHtml3(row.value)}</dd>
558
827
  </div>`).join("")}</dl>
559
828
  </article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
560
- return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml2(model.status)}">
829
+ return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml3(model.status)}">
561
830
  <header class="absolute-voice-provider-status__header">
562
- <span class="absolute-voice-provider-status__eyebrow">${escapeHtml2(model.title)}</span>
563
- <strong class="absolute-voice-provider-status__label">${escapeHtml2(model.label)}</strong>
831
+ <span class="absolute-voice-provider-status__eyebrow">${escapeHtml3(model.title)}</span>
832
+ <strong class="absolute-voice-provider-status__label">${escapeHtml3(model.label)}</strong>
564
833
  </header>
565
- <p class="absolute-voice-provider-status__description">${escapeHtml2(model.description)}</p>
834
+ <p class="absolute-voice-provider-status__description">${escapeHtml3(model.description)}</p>
566
835
  ${providers}
567
- ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml2(model.error)}</p>` : ""}
836
+ ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml3(model.error)}</p>` : ""}
568
837
  </section>`;
569
838
  };
570
839
  var getVoiceProviderStatusCSS = () => `.absolute-voice-provider-status{border:1px solid #d8d2c4;border-radius:20px;background:#fffaf0;color:#16130d;padding:18px;box-shadow:0 18px 40px rgba(47,37,18,.12);font-family:inherit}.absolute-voice-provider-status--error,.absolute-voice-provider-status--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-provider-status__header,.absolute-voice-provider-status__provider header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-provider-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-provider-status__label{font-size:24px;line-height:1}.absolute-voice-provider-status__description,.absolute-voice-provider-status__provider p,.absolute-voice-provider-status__provider dt,.absolute-voice-provider-status__empty{color:#514733}.absolute-voice-provider-status__providers{display:grid;gap:12px;margin-top:14px}.absolute-voice-provider-status__provider{background:#fff;border:1px solid #eee4d2;border-radius:16px;padding:14px}.absolute-voice-provider-status__provider--degraded,.absolute-voice-provider-status__provider--rate-limited,.absolute-voice-provider-status__provider--suppressed{border-color:#f2a7a7}.absolute-voice-provider-status__provider--recoverable{border-color:#fbbf24}.absolute-voice-provider-status__provider p{margin:10px 0}.absolute-voice-provider-status__provider dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-provider-status__provider div{background:#fffaf0;border:1px solid #eee4d2;border-radius:12px;padding:8px}.absolute-voice-provider-status__provider dt{font-size:12px}.absolute-voice-provider-status__provider dd{font-weight:800;margin:4px 0 0}.absolute-voice-provider-status__empty{margin:14px 0 0}.absolute-voice-provider-status__error{color:#9f1239;font-weight:700}`;
@@ -606,7 +875,7 @@ var defineVoiceProviderStatusElement = (tagName = "absolute-voice-provider-statu
606
875
  };
607
876
 
608
877
  // src/react/VoiceProviderStatus.tsx
609
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
878
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
610
879
  var VoiceProviderStatus = ({
611
880
  className,
612
881
  path = "/api/provider-status",
@@ -614,58 +883,58 @@ var VoiceProviderStatus = ({
614
883
  }) => {
615
884
  const snapshot = useVoiceProviderStatus(path, options);
616
885
  const model = createVoiceProviderStatusViewModel(snapshot, options);
617
- return /* @__PURE__ */ jsxDEV2("section", {
886
+ return /* @__PURE__ */ jsxDEV3("section", {
618
887
  className: [
619
888
  "absolute-voice-provider-status",
620
889
  `absolute-voice-provider-status--${model.status}`,
621
890
  className
622
891
  ].filter(Boolean).join(" "),
623
892
  children: [
624
- /* @__PURE__ */ jsxDEV2("header", {
893
+ /* @__PURE__ */ jsxDEV3("header", {
625
894
  className: "absolute-voice-provider-status__header",
626
895
  children: [
627
- /* @__PURE__ */ jsxDEV2("span", {
896
+ /* @__PURE__ */ jsxDEV3("span", {
628
897
  className: "absolute-voice-provider-status__eyebrow",
629
898
  children: model.title
630
899
  }, undefined, false, undefined, this),
631
- /* @__PURE__ */ jsxDEV2("strong", {
900
+ /* @__PURE__ */ jsxDEV3("strong", {
632
901
  className: "absolute-voice-provider-status__label",
633
902
  children: model.label
634
903
  }, undefined, false, undefined, this)
635
904
  ]
636
905
  }, undefined, true, undefined, this),
637
- /* @__PURE__ */ jsxDEV2("p", {
906
+ /* @__PURE__ */ jsxDEV3("p", {
638
907
  className: "absolute-voice-provider-status__description",
639
908
  children: model.description
640
909
  }, undefined, false, undefined, this),
641
- model.providers.length ? /* @__PURE__ */ jsxDEV2("div", {
910
+ model.providers.length ? /* @__PURE__ */ jsxDEV3("div", {
642
911
  className: "absolute-voice-provider-status__providers",
643
- children: model.providers.map((provider) => /* @__PURE__ */ jsxDEV2("article", {
912
+ children: model.providers.map((provider) => /* @__PURE__ */ jsxDEV3("article", {
644
913
  className: [
645
914
  "absolute-voice-provider-status__provider",
646
915
  `absolute-voice-provider-status__provider--${provider.status}`
647
916
  ].join(" "),
648
917
  children: [
649
- /* @__PURE__ */ jsxDEV2("header", {
918
+ /* @__PURE__ */ jsxDEV3("header", {
650
919
  children: [
651
- /* @__PURE__ */ jsxDEV2("strong", {
920
+ /* @__PURE__ */ jsxDEV3("strong", {
652
921
  children: provider.label
653
922
  }, undefined, false, undefined, this),
654
- /* @__PURE__ */ jsxDEV2("span", {
923
+ /* @__PURE__ */ jsxDEV3("span", {
655
924
  children: provider.status
656
925
  }, undefined, false, undefined, this)
657
926
  ]
658
927
  }, undefined, true, undefined, this),
659
- /* @__PURE__ */ jsxDEV2("p", {
928
+ /* @__PURE__ */ jsxDEV3("p", {
660
929
  children: provider.detail
661
930
  }, undefined, false, undefined, this),
662
- /* @__PURE__ */ jsxDEV2("dl", {
663
- children: provider.rows.map((row) => /* @__PURE__ */ jsxDEV2("div", {
931
+ /* @__PURE__ */ jsxDEV3("dl", {
932
+ children: provider.rows.map((row) => /* @__PURE__ */ jsxDEV3("div", {
664
933
  children: [
665
- /* @__PURE__ */ jsxDEV2("dt", {
934
+ /* @__PURE__ */ jsxDEV3("dt", {
666
935
  children: row.label
667
936
  }, undefined, false, undefined, this),
668
- /* @__PURE__ */ jsxDEV2("dd", {
937
+ /* @__PURE__ */ jsxDEV3("dd", {
669
938
  children: row.value
670
939
  }, undefined, false, undefined, this)
671
940
  ]
@@ -673,11 +942,11 @@ var VoiceProviderStatus = ({
673
942
  }, undefined, false, undefined, this)
674
943
  ]
675
944
  }, provider.provider, true, undefined, this))
676
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV2("p", {
945
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV3("p", {
677
946
  className: "absolute-voice-provider-status__empty",
678
947
  children: "Run voice traffic to see provider health."
679
948
  }, undefined, false, undefined, this),
680
- model.error ? /* @__PURE__ */ jsxDEV2("p", {
949
+ model.error ? /* @__PURE__ */ jsxDEV3("p", {
681
950
  className: "absolute-voice-provider-status__error",
682
951
  children: model.error
683
952
  }, undefined, false, undefined, this) : null
@@ -685,7 +954,7 @@ var VoiceProviderStatus = ({
685
954
  }, undefined, true, undefined, this);
686
955
  };
687
956
  // src/react/useVoiceRoutingStatus.tsx
688
- import { useEffect as useEffect3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore3 } from "react";
957
+ import { useEffect as useEffect4, useRef as useRef4, useSyncExternalStore as useSyncExternalStore4 } from "react";
689
958
 
690
959
  // src/client/routingStatus.ts
691
960
  var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
@@ -769,17 +1038,17 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
769
1038
 
770
1039
  // src/react/useVoiceRoutingStatus.tsx
771
1040
  var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
772
- const storeRef = useRef3(null);
1041
+ const storeRef = useRef4(null);
773
1042
  if (!storeRef.current) {
774
1043
  storeRef.current = createVoiceRoutingStatusStore(path, options);
775
1044
  }
776
1045
  const store = storeRef.current;
777
- useEffect3(() => {
1046
+ useEffect4(() => {
778
1047
  store.refresh().catch(() => {});
779
1048
  return () => store.close();
780
1049
  }, [store]);
781
1050
  return {
782
- ...useSyncExternalStore3(store.subscribe, store.getSnapshot, store.getServerSnapshot),
1051
+ ...useSyncExternalStore4(store.subscribe, store.getSnapshot, store.getServerSnapshot),
783
1052
  refresh: store.refresh
784
1053
  };
785
1054
  };
@@ -787,7 +1056,7 @@ var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
787
1056
  // src/client/routingStatusWidget.ts
788
1057
  var DEFAULT_TITLE3 = "Voice Routing";
789
1058
  var DEFAULT_DESCRIPTION3 = "Latest provider routing decision from the self-hosted trace store.";
790
- var escapeHtml3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1059
+ var escapeHtml4 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
791
1060
  var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
792
1061
  var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
793
1062
  const decision = snapshot.decision;
@@ -824,17 +1093,17 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
824
1093
  var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
825
1094
  const model = createVoiceRoutingStatusViewModel(snapshot, options);
826
1095
  const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
827
- <span>${escapeHtml3(row.label)}</span>
828
- <strong>${escapeHtml3(row.value)}</strong>
1096
+ <span>${escapeHtml4(row.label)}</span>
1097
+ <strong>${escapeHtml4(row.value)}</strong>
829
1098
  </div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
830
- return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml3(model.status)}">
1099
+ return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml4(model.status)}">
831
1100
  <header class="absolute-voice-routing-status__header">
832
- <span class="absolute-voice-routing-status__eyebrow">${escapeHtml3(model.title)}</span>
833
- <strong class="absolute-voice-routing-status__label">${escapeHtml3(model.label)}</strong>
1101
+ <span class="absolute-voice-routing-status__eyebrow">${escapeHtml4(model.title)}</span>
1102
+ <strong class="absolute-voice-routing-status__label">${escapeHtml4(model.label)}</strong>
834
1103
  </header>
835
- <p class="absolute-voice-routing-status__description">${escapeHtml3(model.description)}</p>
1104
+ <p class="absolute-voice-routing-status__description">${escapeHtml4(model.description)}</p>
836
1105
  ${rows}
837
- ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml3(model.error)}</p>` : ""}
1106
+ ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml4(model.error)}</p>` : ""}
838
1107
  </section>`;
839
1108
  };
840
1109
  var getVoiceRoutingStatusCSS = () => `.absolute-voice-routing-status{border:1px solid #d8d2c4;border-radius:20px;background:#fffaf0;color:#16130d;padding:18px;box-shadow:0 18px 40px rgba(47,37,18,.12);font-family:inherit}.absolute-voice-routing-status--error{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-routing-status__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-routing-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-routing-status__label{font-size:24px;line-height:1}.absolute-voice-routing-status__description{color:#514733;margin:12px 0 0}.absolute-voice-routing-status__grid{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin-top:14px}.absolute-voice-routing-status__grid div{background:#fff;border:1px solid #eee4d2;border-radius:14px;padding:10px 12px}.absolute-voice-routing-status__grid span{color:#655944;display:block;font-size:12px;margin-bottom:4px}.absolute-voice-routing-status__grid strong{overflow-wrap:anywhere}.absolute-voice-routing-status__empty{color:#655944;margin:14px 0 0}.absolute-voice-routing-status__error{color:#9f1239;font-weight:700}`;
@@ -876,7 +1145,7 @@ var defineVoiceRoutingStatusElement = (tagName = "absolute-voice-routing-status"
876
1145
  };
877
1146
 
878
1147
  // src/react/VoiceRoutingStatus.tsx
879
- import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
1148
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
880
1149
  var VoiceRoutingStatus = ({
881
1150
  className,
882
1151
  path = "/api/routing/latest",
@@ -884,47 +1153,47 @@ var VoiceRoutingStatus = ({
884
1153
  }) => {
885
1154
  const snapshot = useVoiceRoutingStatus(path, options);
886
1155
  const model = createVoiceRoutingStatusViewModel(snapshot, options);
887
- return /* @__PURE__ */ jsxDEV3("section", {
1156
+ return /* @__PURE__ */ jsxDEV4("section", {
888
1157
  className: [
889
1158
  "absolute-voice-routing-status",
890
1159
  `absolute-voice-routing-status--${model.status}`,
891
1160
  className
892
1161
  ].filter(Boolean).join(" "),
893
1162
  children: [
894
- /* @__PURE__ */ jsxDEV3("header", {
1163
+ /* @__PURE__ */ jsxDEV4("header", {
895
1164
  className: "absolute-voice-routing-status__header",
896
1165
  children: [
897
- /* @__PURE__ */ jsxDEV3("span", {
1166
+ /* @__PURE__ */ jsxDEV4("span", {
898
1167
  className: "absolute-voice-routing-status__eyebrow",
899
1168
  children: model.title
900
1169
  }, undefined, false, undefined, this),
901
- /* @__PURE__ */ jsxDEV3("strong", {
1170
+ /* @__PURE__ */ jsxDEV4("strong", {
902
1171
  className: "absolute-voice-routing-status__label",
903
1172
  children: model.label
904
1173
  }, undefined, false, undefined, this)
905
1174
  ]
906
1175
  }, undefined, true, undefined, this),
907
- /* @__PURE__ */ jsxDEV3("p", {
1176
+ /* @__PURE__ */ jsxDEV4("p", {
908
1177
  className: "absolute-voice-routing-status__description",
909
1178
  children: model.description
910
1179
  }, undefined, false, undefined, this),
911
- model.rows.length ? /* @__PURE__ */ jsxDEV3("div", {
1180
+ model.rows.length ? /* @__PURE__ */ jsxDEV4("div", {
912
1181
  className: "absolute-voice-routing-status__grid",
913
- children: model.rows.map((row) => /* @__PURE__ */ jsxDEV3("div", {
1182
+ children: model.rows.map((row) => /* @__PURE__ */ jsxDEV4("div", {
914
1183
  children: [
915
- /* @__PURE__ */ jsxDEV3("span", {
1184
+ /* @__PURE__ */ jsxDEV4("span", {
916
1185
  children: row.label
917
1186
  }, undefined, false, undefined, this),
918
- /* @__PURE__ */ jsxDEV3("strong", {
1187
+ /* @__PURE__ */ jsxDEV4("strong", {
919
1188
  children: row.value
920
1189
  }, undefined, false, undefined, this)
921
1190
  ]
922
1191
  }, row.label, true, undefined, this))
923
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV3("p", {
1192
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV4("p", {
924
1193
  className: "absolute-voice-routing-status__empty",
925
1194
  children: "Start a voice session to see the selected provider."
926
1195
  }, undefined, false, undefined, this),
927
- model.error ? /* @__PURE__ */ jsxDEV3("p", {
1196
+ model.error ? /* @__PURE__ */ jsxDEV4("p", {
928
1197
  className: "absolute-voice-routing-status__error",
929
1198
  children: model.error
930
1199
  }, undefined, false, undefined, this) : null
@@ -932,7 +1201,7 @@ var VoiceRoutingStatus = ({
932
1201
  }, undefined, true, undefined, this);
933
1202
  };
934
1203
  // src/react/useVoiceStream.tsx
935
- import { useEffect as useEffect4, useRef as useRef4, useSyncExternalStore as useSyncExternalStore4 } from "react";
1204
+ import { useEffect as useEffect5, useRef as useRef5, useSyncExternalStore as useSyncExternalStore5 } from "react";
936
1205
 
937
1206
  // src/client/actions.ts
938
1207
  var normalizeErrorMessage = (value) => {
@@ -1460,13 +1729,13 @@ var EMPTY_SNAPSHOT = {
1460
1729
  turns: []
1461
1730
  };
1462
1731
  var useVoiceStream = (path, options = {}) => {
1463
- const streamRef = useRef4(null);
1732
+ const streamRef = useRef5(null);
1464
1733
  if (!streamRef.current) {
1465
1734
  streamRef.current = createVoiceStream(path, options);
1466
1735
  }
1467
1736
  const stream = streamRef.current;
1468
- useEffect4(() => () => stream.close(), [stream]);
1469
- const snapshot = useSyncExternalStore4(stream.subscribe, stream.getSnapshot, stream.getServerSnapshot) ?? EMPTY_SNAPSHOT;
1737
+ useEffect5(() => () => stream.close(), [stream]);
1738
+ const snapshot = useSyncExternalStore5(stream.subscribe, stream.getSnapshot, stream.getServerSnapshot) ?? EMPTY_SNAPSHOT;
1470
1739
  return {
1471
1740
  ...snapshot,
1472
1741
  callControl: (message) => stream.callControl(message),
@@ -1476,7 +1745,7 @@ var useVoiceStream = (path, options = {}) => {
1476
1745
  };
1477
1746
  };
1478
1747
  // src/react/useVoiceController.tsx
1479
- import { useEffect as useEffect5, useRef as useRef5, useSyncExternalStore as useSyncExternalStore5 } from "react";
1748
+ import { useEffect as useEffect6, useRef as useRef6, useSyncExternalStore as useSyncExternalStore6 } from "react";
1480
1749
 
1481
1750
  // src/client/htmx.ts
1482
1751
  var DEFAULT_EVENT_NAME = "voice-refresh";
@@ -2123,13 +2392,13 @@ var EMPTY_SNAPSHOT2 = {
2123
2392
  turns: []
2124
2393
  };
2125
2394
  var useVoiceController = (path, options = {}) => {
2126
- const controllerRef = useRef5(null);
2395
+ const controllerRef = useRef6(null);
2127
2396
  if (!controllerRef.current) {
2128
2397
  controllerRef.current = createVoiceController(path, options);
2129
2398
  }
2130
2399
  const controller = controllerRef.current;
2131
- useEffect5(() => () => controller.close(), [controller]);
2132
- const snapshot = useSyncExternalStore5(controller.subscribe, controller.getSnapshot, controller.getServerSnapshot) ?? EMPTY_SNAPSHOT2;
2400
+ useEffect6(() => () => controller.close(), [controller]);
2401
+ const snapshot = useSyncExternalStore6(controller.subscribe, controller.getSnapshot, controller.getServerSnapshot) ?? EMPTY_SNAPSHOT2;
2133
2402
  return {
2134
2403
  ...snapshot,
2135
2404
  bindHTMX: controller.bindHTMX,
@@ -2143,7 +2412,7 @@ var useVoiceController = (path, options = {}) => {
2143
2412
  };
2144
2413
  };
2145
2414
  // src/react/useVoiceWorkflowStatus.tsx
2146
- import { useEffect as useEffect6, useRef as useRef6, useSyncExternalStore as useSyncExternalStore6 } from "react";
2415
+ import { useEffect as useEffect7, useRef as useRef7, useSyncExternalStore as useSyncExternalStore7 } from "react";
2147
2416
 
2148
2417
  // src/client/workflowStatus.ts
2149
2418
  var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
@@ -2226,17 +2495,17 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
2226
2495
 
2227
2496
  // src/react/useVoiceWorkflowStatus.tsx
2228
2497
  var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
2229
- const storeRef = useRef6(null);
2498
+ const storeRef = useRef7(null);
2230
2499
  if (!storeRef.current) {
2231
2500
  storeRef.current = createVoiceWorkflowStatusStore(path, options);
2232
2501
  }
2233
2502
  const store = storeRef.current;
2234
- useEffect6(() => {
2503
+ useEffect7(() => {
2235
2504
  store.refresh().catch(() => {});
2236
2505
  return () => store.close();
2237
2506
  }, [store]);
2238
2507
  return {
2239
- ...useSyncExternalStore6(store.subscribe, store.getSnapshot, store.getServerSnapshot),
2508
+ ...useSyncExternalStore7(store.subscribe, store.getSnapshot, store.getServerSnapshot),
2240
2509
  refresh: store.refresh
2241
2510
  };
2242
2511
  };
@@ -2245,9 +2514,11 @@ export {
2245
2514
  useVoiceStream,
2246
2515
  useVoiceRoutingStatus,
2247
2516
  useVoiceProviderStatus,
2517
+ useVoiceProviderSimulationControls,
2248
2518
  useVoiceController,
2249
2519
  useVoiceAppKitStatus,
2250
2520
  VoiceRoutingStatus,
2251
2521
  VoiceProviderStatus,
2522
+ VoiceProviderSimulationControls,
2252
2523
  VoiceOpsStatus
2253
2524
  };