@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.
package/dist/vue/index.js CHANGED
@@ -374,9 +374,299 @@ var VoiceOpsStatus = defineComponent({
374
374
  };
375
375
  }
376
376
  });
377
- // src/vue/VoiceProviderStatus.ts
377
+ // src/vue/VoiceProviderSimulationControls.ts
378
378
  import { computed, defineComponent as defineComponent2, h as h2 } from "vue";
379
379
 
380
+ // src/client/providerSimulationControls.ts
381
+ var postSimulation = async (pathPrefix, mode, provider, fetchImpl) => {
382
+ const response = await fetchImpl(`${pathPrefix}/${mode}?provider=${encodeURIComponent(provider)}`, { method: "POST" });
383
+ const body = await response.json().catch(() => null);
384
+ if (!response.ok) {
385
+ const message = body && typeof body === "object" && "error" in body ? String(body.error) : `Voice provider simulation failed: HTTP ${response.status}`;
386
+ throw new Error(message);
387
+ }
388
+ return body;
389
+ };
390
+ var createVoiceProviderSimulationControlsStore = (options) => {
391
+ const listeners = new Set;
392
+ const fetchImpl = options.fetch ?? globalThis.fetch;
393
+ const pathPrefix = options.pathPrefix ?? `/api/${options.kind ?? "stt"}-simulate`;
394
+ let closed = false;
395
+ let snapshot = {
396
+ error: null,
397
+ isRunning: false,
398
+ lastResult: null,
399
+ mode: null,
400
+ provider: null
401
+ };
402
+ const emit = () => {
403
+ for (const listener of listeners) {
404
+ listener();
405
+ }
406
+ };
407
+ const run = async (provider, mode) => {
408
+ if (closed) {
409
+ return snapshot.lastResult;
410
+ }
411
+ snapshot = {
412
+ ...snapshot,
413
+ error: null,
414
+ isRunning: true,
415
+ mode,
416
+ provider
417
+ };
418
+ emit();
419
+ try {
420
+ const result = await postSimulation(pathPrefix, mode, provider, fetchImpl);
421
+ snapshot = {
422
+ error: null,
423
+ isRunning: false,
424
+ lastResult: result,
425
+ mode,
426
+ provider,
427
+ updatedAt: Date.now()
428
+ };
429
+ emit();
430
+ return result;
431
+ } catch (error) {
432
+ snapshot = {
433
+ ...snapshot,
434
+ error: error instanceof Error ? error.message : String(error),
435
+ isRunning: false
436
+ };
437
+ emit();
438
+ throw error;
439
+ }
440
+ };
441
+ const close = () => {
442
+ closed = true;
443
+ listeners.clear();
444
+ };
445
+ return {
446
+ close,
447
+ getServerSnapshot: () => snapshot,
448
+ getSnapshot: () => snapshot,
449
+ run,
450
+ subscribe: (listener) => {
451
+ listeners.add(listener);
452
+ return () => {
453
+ listeners.delete(listener);
454
+ };
455
+ }
456
+ };
457
+ };
458
+
459
+ // src/client/providerSimulationControlsWidget.ts
460
+ var escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
461
+ var formatKind = (kind) => (kind ?? "stt").toUpperCase();
462
+ var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
463
+ const configuredProviders = options.providers.filter((provider) => provider.configured !== false);
464
+ const fallbackReady = !options.fallbackRequiredProvider || configuredProviders.some((entry) => entry.provider === options.fallbackRequiredProvider);
465
+ const failureProviders = (options.failureProviders ? options.failureProviders.map((provider) => ({ provider })) : configuredProviders).filter((provider) => configuredProviders.some((entry) => entry.provider === provider.provider));
466
+ return {
467
+ canSimulateFailure: configuredProviders.length > 0 && fallbackReady,
468
+ description: options.failureMessage ?? `Simulate ${formatKind(options.kind)} provider failure and recovery without changing credentials.`,
469
+ error: snapshot.error,
470
+ failureProviders,
471
+ isRunning: snapshot.isRunning,
472
+ label: snapshot.isRunning ? `Running ${snapshot.mode ?? "simulation"}` : snapshot.lastResult ? `${snapshot.lastResult.provider} ${snapshot.lastResult.mode} simulated` : configuredProviders.length ? `${configuredProviders.length} configured` : "No configured providers",
473
+ providers: configuredProviders,
474
+ resultText: snapshot.lastResult ? JSON.stringify(snapshot.lastResult, null, 2) : null,
475
+ title: options.title ?? `${formatKind(options.kind)} Failure Simulation`
476
+ };
477
+ };
478
+ var renderVoiceProviderSimulationControlsHTML = (snapshot, options) => {
479
+ const model = createVoiceProviderSimulationControlsViewModel(snapshot, options);
480
+ 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("");
481
+ 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("");
482
+ return `<section class="absolute-voice-provider-simulation absolute-voice-provider-simulation--${snapshot.error ? "error" : snapshot.isRunning ? "running" : "ready"}">
483
+ <header class="absolute-voice-provider-simulation__header">
484
+ <span class="absolute-voice-provider-simulation__eyebrow">${escapeHtml2(model.title)}</span>
485
+ <strong class="absolute-voice-provider-simulation__label">${escapeHtml2(model.label)}</strong>
486
+ </header>
487
+ <p class="absolute-voice-provider-simulation__description">${escapeHtml2(model.description)}</p>
488
+ ${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${escapeHtml2(options.fallbackRequiredMessage ?? "Configure fallback providers before simulating failure.")}</p>`}
489
+ <div class="absolute-voice-provider-simulation__actions">${failureButtons}${recoveryButtons}</div>
490
+ ${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${escapeHtml2(snapshot.error)}</p>` : ""}
491
+ ${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${escapeHtml2(model.resultText)}</pre>` : ""}
492
+ </section>`;
493
+ };
494
+ var bindVoiceProviderSimulationControls = (element, store) => {
495
+ const onClick = (event) => {
496
+ const target = event.target;
497
+ if (!(target instanceof HTMLElement)) {
498
+ return;
499
+ }
500
+ const failProvider = target.getAttribute("data-voice-provider-fail");
501
+ const recoverProvider = target.getAttribute("data-voice-provider-recover");
502
+ if (failProvider) {
503
+ store.run(failProvider, "failure").catch(() => {});
504
+ }
505
+ if (recoverProvider) {
506
+ store.run(recoverProvider, "recovery").catch(() => {});
507
+ }
508
+ };
509
+ element.addEventListener("click", onClick);
510
+ return () => element.removeEventListener("click", onClick);
511
+ };
512
+ var mountVoiceProviderSimulationControls = (element, options) => {
513
+ const store = createVoiceProviderSimulationControlsStore(options);
514
+ const render = () => {
515
+ element.innerHTML = renderVoiceProviderSimulationControlsHTML(store.getSnapshot(), options);
516
+ };
517
+ const unsubscribeStore = store.subscribe(render);
518
+ const unsubscribeDom = bindVoiceProviderSimulationControls(element, store);
519
+ render();
520
+ return {
521
+ close: () => {
522
+ unsubscribeDom();
523
+ unsubscribeStore();
524
+ store.close();
525
+ },
526
+ run: store.run
527
+ };
528
+ };
529
+ var defineVoiceProviderSimulationControlsElement = (tagName = "absolute-voice-provider-simulation") => {
530
+ if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
531
+ return;
532
+ }
533
+ customElements.define(tagName, class AbsoluteVoiceProviderSimulationElement extends HTMLElement {
534
+ mounted;
535
+ connectedCallback() {
536
+ const providers = (this.getAttribute("providers") ?? "").split(",").map((provider) => provider.trim()).filter(Boolean).map((provider) => ({ provider }));
537
+ const failureProviders = (this.getAttribute("failure-providers") ?? "").split(",").map((provider) => provider.trim()).filter(Boolean);
538
+ this.mounted = mountVoiceProviderSimulationControls(this, {
539
+ failureProviders: failureProviders.length ? failureProviders : undefined,
540
+ fallbackRequiredMessage: this.getAttribute("fallback-required-message") ?? undefined,
541
+ fallbackRequiredProvider: this.getAttribute("fallback-required-provider") ?? undefined,
542
+ failureMessage: this.getAttribute("failure-message") ?? undefined,
543
+ kind: this.getAttribute("kind") ?? "stt",
544
+ pathPrefix: this.getAttribute("path-prefix") ?? undefined,
545
+ providers,
546
+ title: this.getAttribute("title") ?? undefined
547
+ });
548
+ }
549
+ disconnectedCallback() {
550
+ this.mounted?.close();
551
+ this.mounted = undefined;
552
+ }
553
+ });
554
+ };
555
+
556
+ // src/vue/useVoiceProviderSimulationControls.ts
557
+ import { onUnmounted as onUnmounted2, ref as ref2 } from "vue";
558
+ var useVoiceProviderSimulationControls = (options) => {
559
+ const store = createVoiceProviderSimulationControlsStore(options);
560
+ const error = ref2(null);
561
+ const isRunning = ref2(false);
562
+ const lastResult = ref2(null);
563
+ const mode = ref2(null);
564
+ const provider = ref2(null);
565
+ const updatedAt = ref2(undefined);
566
+ const sync = () => {
567
+ const snapshot = store.getSnapshot();
568
+ error.value = snapshot.error;
569
+ isRunning.value = snapshot.isRunning;
570
+ lastResult.value = snapshot.lastResult;
571
+ mode.value = snapshot.mode;
572
+ provider.value = snapshot.provider;
573
+ updatedAt.value = snapshot.updatedAt;
574
+ };
575
+ const unsubscribe = store.subscribe(sync);
576
+ sync();
577
+ onUnmounted2(() => {
578
+ unsubscribe();
579
+ store.close();
580
+ });
581
+ return {
582
+ error,
583
+ isRunning,
584
+ lastResult,
585
+ mode,
586
+ provider,
587
+ run: store.run,
588
+ updatedAt
589
+ };
590
+ };
591
+
592
+ // src/vue/VoiceProviderSimulationControls.ts
593
+ var VoiceProviderSimulationControls = defineComponent2({
594
+ name: "VoiceProviderSimulationControls",
595
+ props: {
596
+ class: { default: "", type: String },
597
+ fallbackRequiredMessage: { default: undefined, type: String },
598
+ fallbackRequiredProvider: { default: undefined, type: String },
599
+ failureMessage: { default: undefined, type: String },
600
+ failureProviders: {
601
+ default: undefined,
602
+ type: Array
603
+ },
604
+ kind: { default: "stt", type: String },
605
+ pathPrefix: { default: undefined, type: String },
606
+ providers: {
607
+ required: true,
608
+ type: Array
609
+ },
610
+ title: { default: undefined, type: String }
611
+ },
612
+ setup(props) {
613
+ const options = {
614
+ fallbackRequiredMessage: props.fallbackRequiredMessage,
615
+ fallbackRequiredProvider: props.fallbackRequiredProvider,
616
+ failureMessage: props.failureMessage,
617
+ failureProviders: props.failureProviders,
618
+ kind: props.kind,
619
+ pathPrefix: props.pathPrefix,
620
+ providers: props.providers,
621
+ title: props.title
622
+ };
623
+ const controls = useVoiceProviderSimulationControls(options);
624
+ const model = computed(() => createVoiceProviderSimulationControlsViewModel({
625
+ error: controls.error.value,
626
+ isRunning: controls.isRunning.value,
627
+ lastResult: controls.lastResult.value,
628
+ mode: controls.mode.value,
629
+ provider: controls.provider.value,
630
+ updatedAt: controls.updatedAt.value
631
+ }, options));
632
+ const run = (provider, mode) => {
633
+ controls.run(provider, mode).catch(() => {});
634
+ };
635
+ return () => h2("section", {
636
+ class: [
637
+ "absolute-voice-provider-simulation",
638
+ `absolute-voice-provider-simulation--${controls.error.value ? "error" : controls.isRunning.value ? "running" : "ready"}`,
639
+ props.class
640
+ ]
641
+ }, [
642
+ h2("header", { class: "absolute-voice-provider-simulation__header" }, [
643
+ h2("span", { class: "absolute-voice-provider-simulation__eyebrow" }, model.value.title),
644
+ h2("strong", { class: "absolute-voice-provider-simulation__label" }, model.value.label)
645
+ ]),
646
+ h2("p", { class: "absolute-voice-provider-simulation__description" }, model.value.description),
647
+ model.value.canSimulateFailure ? null : h2("p", { class: "absolute-voice-provider-simulation__empty" }, props.fallbackRequiredMessage ?? "Configure fallback providers before simulating failure."),
648
+ h2("div", { class: "absolute-voice-provider-simulation__actions" }, [
649
+ ...model.value.failureProviders.map((provider) => h2("button", {
650
+ disabled: !model.value.canSimulateFailure || controls.isRunning.value,
651
+ key: `fail-${provider.provider}`,
652
+ onClick: () => run(provider.provider, "failure"),
653
+ type: "button"
654
+ }, `Simulate ${provider.provider} ${props.kind.toUpperCase()} failure`)),
655
+ ...model.value.providers.map((provider) => h2("button", {
656
+ disabled: controls.isRunning.value,
657
+ key: `recover-${provider.provider}`,
658
+ onClick: () => run(provider.provider, "recovery"),
659
+ type: "button"
660
+ }, `Mark ${provider.provider} recovered`))
661
+ ]),
662
+ controls.error.value ? h2("p", { class: "absolute-voice-provider-simulation__error" }, controls.error.value) : null,
663
+ model.value.resultText ? h2("pre", { class: "absolute-voice-provider-simulation__result" }, model.value.resultText) : null
664
+ ]);
665
+ }
666
+ });
667
+ // src/vue/VoiceProviderStatus.ts
668
+ import { computed as computed2, defineComponent as defineComponent3, h as h3 } from "vue";
669
+
380
670
  // src/client/providerStatus.ts
381
671
  var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
382
672
  const fetchImpl = options.fetch ?? globalThis.fetch;
@@ -460,7 +750,7 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
460
750
  // src/client/providerStatusWidget.ts
461
751
  var DEFAULT_TITLE2 = "Voice Providers";
462
752
  var DEFAULT_DESCRIPTION2 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
463
- var escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
753
+ var escapeHtml3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
464
754
  var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
465
755
  var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
466
756
  var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
@@ -516,25 +806,25 @@ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
516
806
  };
517
807
  var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
518
808
  const model = createVoiceProviderStatusViewModel(snapshot, options);
519
- 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)}">
809
+ 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)}">
520
810
  <header>
521
- <strong>${escapeHtml2(provider.label)}</strong>
522
- <span>${escapeHtml2(formatStatus(provider.status))}</span>
811
+ <strong>${escapeHtml3(provider.label)}</strong>
812
+ <span>${escapeHtml3(formatStatus(provider.status))}</span>
523
813
  </header>
524
- <p>${escapeHtml2(provider.detail)}</p>
814
+ <p>${escapeHtml3(provider.detail)}</p>
525
815
  <dl>${provider.rows.map((row) => `<div>
526
- <dt>${escapeHtml2(row.label)}</dt>
527
- <dd>${escapeHtml2(row.value)}</dd>
816
+ <dt>${escapeHtml3(row.label)}</dt>
817
+ <dd>${escapeHtml3(row.value)}</dd>
528
818
  </div>`).join("")}</dl>
529
819
  </article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
530
- return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml2(model.status)}">
820
+ return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml3(model.status)}">
531
821
  <header class="absolute-voice-provider-status__header">
532
- <span class="absolute-voice-provider-status__eyebrow">${escapeHtml2(model.title)}</span>
533
- <strong class="absolute-voice-provider-status__label">${escapeHtml2(model.label)}</strong>
822
+ <span class="absolute-voice-provider-status__eyebrow">${escapeHtml3(model.title)}</span>
823
+ <strong class="absolute-voice-provider-status__label">${escapeHtml3(model.label)}</strong>
534
824
  </header>
535
- <p class="absolute-voice-provider-status__description">${escapeHtml2(model.description)}</p>
825
+ <p class="absolute-voice-provider-status__description">${escapeHtml3(model.description)}</p>
536
826
  ${providers}
537
- ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml2(model.error)}</p>` : ""}
827
+ ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml3(model.error)}</p>` : ""}
538
828
  </section>`;
539
829
  };
540
830
  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}`;
@@ -576,13 +866,13 @@ var defineVoiceProviderStatusElement = (tagName = "absolute-voice-provider-statu
576
866
  };
577
867
 
578
868
  // src/vue/useVoiceProviderStatus.ts
579
- import { onUnmounted as onUnmounted2, ref as ref2, shallowRef as shallowRef2 } from "vue";
869
+ import { onUnmounted as onUnmounted3, ref as ref3, shallowRef as shallowRef2 } from "vue";
580
870
  var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
581
871
  const store = createVoiceProviderStatusStore(path, options);
582
- const error = ref2(null);
583
- const isLoading = ref2(false);
872
+ const error = ref3(null);
873
+ const isLoading = ref3(false);
584
874
  const providers = shallowRef2([]);
585
- const updatedAt = ref2(undefined);
875
+ const updatedAt = ref3(undefined);
586
876
  const sync = () => {
587
877
  const snapshot = store.getSnapshot();
588
878
  error.value = snapshot.error;
@@ -593,7 +883,7 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
593
883
  const unsubscribe = store.subscribe(sync);
594
884
  sync();
595
885
  store.refresh().catch(() => {});
596
- onUnmounted2(() => {
886
+ onUnmounted3(() => {
597
887
  unsubscribe();
598
888
  store.close();
599
889
  });
@@ -607,7 +897,7 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
607
897
  };
608
898
 
609
899
  // src/vue/VoiceProviderStatus.ts
610
- var VoiceProviderStatus = defineComponent2({
900
+ var VoiceProviderStatus = defineComponent3({
611
901
  name: "VoiceProviderStatus",
612
902
  props: {
613
903
  class: {
@@ -638,47 +928,47 @@ var VoiceProviderStatus = defineComponent2({
638
928
  title: props.title
639
929
  };
640
930
  const status = useVoiceProviderStatus(props.path, options);
641
- const model = computed(() => createVoiceProviderStatusViewModel({
931
+ const model = computed2(() => createVoiceProviderStatusViewModel({
642
932
  error: status.error.value,
643
933
  isLoading: status.isLoading.value,
644
934
  providers: status.providers.value,
645
935
  updatedAt: status.updatedAt.value
646
936
  }, options));
647
- return () => h2("section", {
937
+ return () => h3("section", {
648
938
  class: [
649
939
  "absolute-voice-provider-status",
650
940
  `absolute-voice-provider-status--${model.value.status}`,
651
941
  props.class
652
942
  ]
653
943
  }, [
654
- h2("header", { class: "absolute-voice-provider-status__header" }, [
655
- h2("span", { class: "absolute-voice-provider-status__eyebrow" }, model.value.title),
656
- h2("strong", { class: "absolute-voice-provider-status__label" }, model.value.label)
944
+ h3("header", { class: "absolute-voice-provider-status__header" }, [
945
+ h3("span", { class: "absolute-voice-provider-status__eyebrow" }, model.value.title),
946
+ h3("strong", { class: "absolute-voice-provider-status__label" }, model.value.label)
657
947
  ]),
658
- h2("p", { class: "absolute-voice-provider-status__description" }, model.value.description),
659
- model.value.providers.length ? h2("div", { class: "absolute-voice-provider-status__providers" }, model.value.providers.map((provider) => h2("article", {
948
+ h3("p", { class: "absolute-voice-provider-status__description" }, model.value.description),
949
+ model.value.providers.length ? h3("div", { class: "absolute-voice-provider-status__providers" }, model.value.providers.map((provider) => h3("article", {
660
950
  class: [
661
951
  "absolute-voice-provider-status__provider",
662
952
  `absolute-voice-provider-status__provider--${provider.status}`
663
953
  ],
664
954
  key: provider.provider
665
955
  }, [
666
- h2("header", [
667
- h2("strong", provider.label),
668
- h2("span", provider.status)
956
+ h3("header", [
957
+ h3("strong", provider.label),
958
+ h3("span", provider.status)
669
959
  ]),
670
- h2("p", provider.detail),
671
- h2("dl", provider.rows.map((row) => h2("div", { key: row.label }, [
672
- h2("dt", row.label),
673
- h2("dd", row.value)
960
+ h3("p", provider.detail),
961
+ h3("dl", provider.rows.map((row) => h3("div", { key: row.label }, [
962
+ h3("dt", row.label),
963
+ h3("dd", row.value)
674
964
  ])))
675
- ]))) : h2("p", { class: "absolute-voice-provider-status__empty" }, "Run voice traffic to see provider health."),
676
- model.value.error ? h2("p", { class: "absolute-voice-provider-status__error" }, model.value.error) : null
965
+ ]))) : h3("p", { class: "absolute-voice-provider-status__empty" }, "Run voice traffic to see provider health."),
966
+ model.value.error ? h3("p", { class: "absolute-voice-provider-status__error" }, model.value.error) : null
677
967
  ]);
678
968
  }
679
969
  });
680
970
  // src/vue/VoiceRoutingStatus.ts
681
- import { computed as computed2, defineComponent as defineComponent3, h as h3 } from "vue";
971
+ import { computed as computed3, defineComponent as defineComponent4, h as h4 } from "vue";
682
972
 
683
973
  // src/client/routingStatus.ts
684
974
  var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
@@ -763,7 +1053,7 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
763
1053
  // src/client/routingStatusWidget.ts
764
1054
  var DEFAULT_TITLE3 = "Voice Routing";
765
1055
  var DEFAULT_DESCRIPTION3 = "Latest provider routing decision from the self-hosted trace store.";
766
- var escapeHtml3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1056
+ var escapeHtml4 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
767
1057
  var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
768
1058
  var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
769
1059
  const decision = snapshot.decision;
@@ -800,17 +1090,17 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
800
1090
  var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
801
1091
  const model = createVoiceRoutingStatusViewModel(snapshot, options);
802
1092
  const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
803
- <span>${escapeHtml3(row.label)}</span>
804
- <strong>${escapeHtml3(row.value)}</strong>
1093
+ <span>${escapeHtml4(row.label)}</span>
1094
+ <strong>${escapeHtml4(row.value)}</strong>
805
1095
  </div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
806
- return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml3(model.status)}">
1096
+ return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml4(model.status)}">
807
1097
  <header class="absolute-voice-routing-status__header">
808
- <span class="absolute-voice-routing-status__eyebrow">${escapeHtml3(model.title)}</span>
809
- <strong class="absolute-voice-routing-status__label">${escapeHtml3(model.label)}</strong>
1098
+ <span class="absolute-voice-routing-status__eyebrow">${escapeHtml4(model.title)}</span>
1099
+ <strong class="absolute-voice-routing-status__label">${escapeHtml4(model.label)}</strong>
810
1100
  </header>
811
- <p class="absolute-voice-routing-status__description">${escapeHtml3(model.description)}</p>
1101
+ <p class="absolute-voice-routing-status__description">${escapeHtml4(model.description)}</p>
812
1102
  ${rows}
813
- ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml3(model.error)}</p>` : ""}
1103
+ ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml4(model.error)}</p>` : ""}
814
1104
  </section>`;
815
1105
  };
816
1106
  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}`;
@@ -852,13 +1142,13 @@ var defineVoiceRoutingStatusElement = (tagName = "absolute-voice-routing-status"
852
1142
  };
853
1143
 
854
1144
  // src/vue/useVoiceRoutingStatus.ts
855
- import { onUnmounted as onUnmounted3, ref as ref3, shallowRef as shallowRef3 } from "vue";
1145
+ import { onUnmounted as onUnmounted4, ref as ref4, shallowRef as shallowRef3 } from "vue";
856
1146
  var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
857
1147
  const store = createVoiceRoutingStatusStore(path, options);
858
1148
  const decision = shallowRef3(null);
859
- const error = ref3(null);
860
- const isLoading = ref3(false);
861
- const updatedAt = ref3(undefined);
1149
+ const error = ref4(null);
1150
+ const isLoading = ref4(false);
1151
+ const updatedAt = ref4(undefined);
862
1152
  const sync = () => {
863
1153
  const snapshot = store.getSnapshot();
864
1154
  decision.value = snapshot.decision;
@@ -869,7 +1159,7 @@ var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
869
1159
  const unsubscribe = store.subscribe(sync);
870
1160
  sync();
871
1161
  store.refresh().catch(() => {});
872
- onUnmounted3(() => {
1162
+ onUnmounted4(() => {
873
1163
  unsubscribe();
874
1164
  store.close();
875
1165
  });
@@ -883,7 +1173,7 @@ var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
883
1173
  };
884
1174
 
885
1175
  // src/vue/VoiceRoutingStatus.ts
886
- var VoiceRoutingStatus = defineComponent3({
1176
+ var VoiceRoutingStatus = defineComponent4({
887
1177
  name: "VoiceRoutingStatus",
888
1178
  props: {
889
1179
  class: {
@@ -914,34 +1204,34 @@ var VoiceRoutingStatus = defineComponent3({
914
1204
  title: props.title
915
1205
  };
916
1206
  const status = useVoiceRoutingStatus(props.path, options);
917
- const model = computed2(() => createVoiceRoutingStatusViewModel({
1207
+ const model = computed3(() => createVoiceRoutingStatusViewModel({
918
1208
  decision: status.decision.value,
919
1209
  error: status.error.value,
920
1210
  isLoading: status.isLoading.value,
921
1211
  updatedAt: status.updatedAt.value
922
1212
  }, options));
923
- return () => h3("section", {
1213
+ return () => h4("section", {
924
1214
  class: [
925
1215
  "absolute-voice-routing-status",
926
1216
  `absolute-voice-routing-status--${model.value.status}`,
927
1217
  props.class
928
1218
  ]
929
1219
  }, [
930
- h3("header", { class: "absolute-voice-routing-status__header" }, [
931
- h3("span", { class: "absolute-voice-routing-status__eyebrow" }, model.value.title),
932
- h3("strong", { class: "absolute-voice-routing-status__label" }, model.value.label)
1220
+ h4("header", { class: "absolute-voice-routing-status__header" }, [
1221
+ h4("span", { class: "absolute-voice-routing-status__eyebrow" }, model.value.title),
1222
+ h4("strong", { class: "absolute-voice-routing-status__label" }, model.value.label)
933
1223
  ]),
934
- h3("p", { class: "absolute-voice-routing-status__description" }, model.value.description),
935
- model.value.rows.length ? h3("div", { class: "absolute-voice-routing-status__grid" }, model.value.rows.map((row) => h3("div", { key: row.label }, [
936
- h3("span", row.label),
937
- h3("strong", row.value)
938
- ]))) : h3("p", { class: "absolute-voice-routing-status__empty" }, "Start a voice session to see the selected provider."),
939
- model.value.error ? h3("p", { class: "absolute-voice-routing-status__error" }, model.value.error) : null
1224
+ h4("p", { class: "absolute-voice-routing-status__description" }, model.value.description),
1225
+ model.value.rows.length ? h4("div", { class: "absolute-voice-routing-status__grid" }, model.value.rows.map((row) => h4("div", { key: row.label }, [
1226
+ h4("span", row.label),
1227
+ h4("strong", row.value)
1228
+ ]))) : h4("p", { class: "absolute-voice-routing-status__empty" }, "Start a voice session to see the selected provider."),
1229
+ model.value.error ? h4("p", { class: "absolute-voice-routing-status__error" }, model.value.error) : null
940
1230
  ]);
941
1231
  }
942
1232
  });
943
1233
  // src/vue/useVoiceStream.ts
944
- import { onUnmounted as onUnmounted4, ref as ref4, shallowRef as shallowRef4 } from "vue";
1234
+ import { onUnmounted as onUnmounted5, ref as ref5, shallowRef as shallowRef4 } from "vue";
945
1235
 
946
1236
  // src/client/actions.ts
947
1237
  var normalizeErrorMessage = (value) => {
@@ -1462,11 +1752,11 @@ var useVoiceStream = (path, options = {}) => {
1462
1752
  const assistantAudio = shallowRef4([]);
1463
1753
  const assistantTexts = shallowRef4([]);
1464
1754
  const call = shallowRef4(null);
1465
- const error = ref4(null);
1466
- const isConnected = ref4(false);
1467
- const partial = ref4("");
1468
- const sessionId = ref4(stream.sessionId);
1469
- const status = ref4(stream.status);
1755
+ const error = ref5(null);
1756
+ const isConnected = ref5(false);
1757
+ const partial = ref5("");
1758
+ const sessionId = ref5(stream.sessionId);
1759
+ const status = ref5(stream.status);
1470
1760
  const turns = shallowRef4([]);
1471
1761
  const sync = () => {
1472
1762
  assistantAudio.value = [...stream.assistantAudio];
@@ -1485,7 +1775,7 @@ var useVoiceStream = (path, options = {}) => {
1485
1775
  unsubscribe();
1486
1776
  stream.close();
1487
1777
  };
1488
- onUnmounted4(destroy);
1778
+ onUnmounted5(destroy);
1489
1779
  return {
1490
1780
  assistantAudio,
1491
1781
  assistantTexts,
@@ -1503,7 +1793,7 @@ var useVoiceStream = (path, options = {}) => {
1503
1793
  };
1504
1794
  };
1505
1795
  // src/vue/useVoiceController.ts
1506
- import { onUnmounted as onUnmounted5, ref as ref5, shallowRef as shallowRef5 } from "vue";
1796
+ import { onUnmounted as onUnmounted6, ref as ref6, shallowRef as shallowRef5 } from "vue";
1507
1797
 
1508
1798
  // src/client/htmx.ts
1509
1799
  var DEFAULT_EVENT_NAME = "voice-refresh";
@@ -2140,13 +2430,13 @@ var useVoiceController = (path, options = {}) => {
2140
2430
  const controller = createVoiceController(path, options);
2141
2431
  const assistantAudio = shallowRef5([]);
2142
2432
  const assistantTexts = shallowRef5([]);
2143
- const error = ref5(null);
2144
- const isConnected = ref5(false);
2145
- const isRecording = ref5(false);
2146
- const partial = ref5("");
2147
- const recordingError = ref5(null);
2148
- const sessionId = ref5(controller.sessionId);
2149
- const status = ref5(controller.status);
2433
+ const error = ref6(null);
2434
+ const isConnected = ref6(false);
2435
+ const isRecording = ref6(false);
2436
+ const partial = ref6("");
2437
+ const recordingError = ref6(null);
2438
+ const sessionId = ref6(controller.sessionId);
2439
+ const status = ref6(controller.status);
2150
2440
  const turns = shallowRef5([]);
2151
2441
  const sync = () => {
2152
2442
  assistantAudio.value = [...controller.assistantAudio];
@@ -2166,7 +2456,7 @@ var useVoiceController = (path, options = {}) => {
2166
2456
  unsubscribe();
2167
2457
  controller.close();
2168
2458
  };
2169
- onUnmounted5(destroy);
2459
+ onUnmounted6(destroy);
2170
2460
  return {
2171
2461
  assistantAudio,
2172
2462
  assistantTexts,
@@ -2188,7 +2478,7 @@ var useVoiceController = (path, options = {}) => {
2188
2478
  };
2189
2479
  };
2190
2480
  // src/vue/useVoiceWorkflowStatus.ts
2191
- import { onUnmounted as onUnmounted6, ref as ref6, shallowRef as shallowRef6 } from "vue";
2481
+ import { onUnmounted as onUnmounted7, ref as ref7, shallowRef as shallowRef6 } from "vue";
2192
2482
 
2193
2483
  // src/client/workflowStatus.ts
2194
2484
  var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
@@ -2272,10 +2562,10 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
2272
2562
  // src/vue/useVoiceWorkflowStatus.ts
2273
2563
  var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
2274
2564
  const store = createVoiceWorkflowStatusStore(path, options);
2275
- const error = ref6(null);
2276
- const isLoading = ref6(false);
2565
+ const error = ref7(null);
2566
+ const isLoading = ref7(false);
2277
2567
  const report = shallowRef6(undefined);
2278
- const updatedAt = ref6(undefined);
2568
+ const updatedAt = ref7(undefined);
2279
2569
  const sync = () => {
2280
2570
  const snapshot = store.getSnapshot();
2281
2571
  error.value = snapshot.error;
@@ -2288,7 +2578,7 @@ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
2288
2578
  if (typeof window !== "undefined") {
2289
2579
  store.refresh().catch(() => {});
2290
2580
  }
2291
- onUnmounted6(() => {
2581
+ onUnmounted7(() => {
2292
2582
  unsubscribe();
2293
2583
  store.close();
2294
2584
  });
@@ -2305,9 +2595,11 @@ export {
2305
2595
  useVoiceStream,
2306
2596
  useVoiceRoutingStatus,
2307
2597
  useVoiceProviderStatus,
2598
+ useVoiceProviderSimulationControls,
2308
2599
  useVoiceController,
2309
2600
  useVoiceAppKitStatus,
2310
2601
  VoiceRoutingStatus,
2311
2602
  VoiceProviderStatus,
2603
+ VoiceProviderSimulationControls,
2312
2604
  VoiceOpsStatus
2313
2605
  };