@absolutejs/voice 0.0.22-beta.65 → 0.0.22-beta.66

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
@@ -664,9 +664,310 @@ var VoiceProviderSimulationControls = defineComponent2({
664
664
  ]);
665
665
  }
666
666
  });
667
- // src/vue/VoiceProviderStatus.ts
667
+ // src/vue/VoiceProviderCapabilities.ts
668
668
  import { computed as computed2, defineComponent as defineComponent3, h as h3 } from "vue";
669
669
 
670
+ // src/client/providerCapabilities.ts
671
+ var fetchVoiceProviderCapabilities = async (path = "/api/provider-capabilities", options = {}) => {
672
+ const fetchImpl = options.fetch ?? globalThis.fetch;
673
+ const response = await fetchImpl(path);
674
+ if (!response.ok) {
675
+ throw new Error(`Voice provider capabilities failed: HTTP ${response.status}`);
676
+ }
677
+ return await response.json();
678
+ };
679
+ var createVoiceProviderCapabilitiesStore = (path = "/api/provider-capabilities", options = {}) => {
680
+ const listeners = new Set;
681
+ let closed = false;
682
+ let timer;
683
+ let snapshot = {
684
+ error: null,
685
+ isLoading: false
686
+ };
687
+ const emit = () => {
688
+ for (const listener of listeners) {
689
+ listener();
690
+ }
691
+ };
692
+ const refresh = async () => {
693
+ if (closed) {
694
+ return snapshot.report;
695
+ }
696
+ snapshot = {
697
+ ...snapshot,
698
+ error: null,
699
+ isLoading: true
700
+ };
701
+ emit();
702
+ try {
703
+ const report = await fetchVoiceProviderCapabilities(path, options);
704
+ snapshot = {
705
+ error: null,
706
+ isLoading: false,
707
+ report,
708
+ updatedAt: Date.now()
709
+ };
710
+ emit();
711
+ return report;
712
+ } catch (error) {
713
+ snapshot = {
714
+ ...snapshot,
715
+ error: error instanceof Error ? error.message : String(error),
716
+ isLoading: false
717
+ };
718
+ emit();
719
+ throw error;
720
+ }
721
+ };
722
+ const close = () => {
723
+ closed = true;
724
+ if (timer) {
725
+ clearInterval(timer);
726
+ timer = undefined;
727
+ }
728
+ listeners.clear();
729
+ };
730
+ if (options.intervalMs && options.intervalMs > 0) {
731
+ timer = setInterval(() => {
732
+ refresh().catch(() => {});
733
+ }, options.intervalMs);
734
+ }
735
+ return {
736
+ close,
737
+ getServerSnapshot: () => snapshot,
738
+ getSnapshot: () => snapshot,
739
+ refresh,
740
+ subscribe: (listener) => {
741
+ listeners.add(listener);
742
+ return () => {
743
+ listeners.delete(listener);
744
+ };
745
+ }
746
+ };
747
+ };
748
+
749
+ // src/client/providerCapabilitiesWidget.ts
750
+ var DEFAULT_TITLE2 = "Provider Capabilities";
751
+ var DEFAULT_DESCRIPTION2 = "Configured, selected, and healthy voice providers for this deployment.";
752
+ var escapeHtml3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
753
+ var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
754
+ var formatKind2 = (kind) => kind.toUpperCase();
755
+ var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
756
+ var getCapabilityDetail = (capability) => {
757
+ if (!capability.configured) {
758
+ return "Not configured in this deployment.";
759
+ }
760
+ if (capability.selected) {
761
+ return `Selected ${capability.kind.toUpperCase()} provider for new sessions.`;
762
+ }
763
+ if (capability.health?.status === "healthy") {
764
+ return "Configured and healthy fallback candidate.";
765
+ }
766
+ if (capability.health?.status === "idle") {
767
+ return "Configured; no traffic observed yet.";
768
+ }
769
+ if (capability.health?.lastError) {
770
+ return capability.health.lastError;
771
+ }
772
+ return "Configured and available.";
773
+ };
774
+ var isWarningStatus = (status) => status === "degraded" || status === "rate-limited" || status === "suppressed" || status === "unconfigured";
775
+ var createVoiceProviderCapabilitiesViewModel = (snapshot, options = {}) => {
776
+ const capabilities = (snapshot.report?.capabilities ?? []).map((capability) => ({
777
+ ...capability,
778
+ detail: getCapabilityDetail(capability),
779
+ label: `${formatProvider(capability.provider)} ${formatKind2(capability.kind)}`,
780
+ rows: [
781
+ { label: "Status", value: formatStatus(capability.status) },
782
+ { label: "Selected", value: capability.selected ? "Yes" : "No" },
783
+ { label: "Model", value: capability.model ?? "Default" },
784
+ {
785
+ label: "Features",
786
+ value: capability.features?.join(", ") || "Not specified"
787
+ },
788
+ { label: "Runs", value: String(capability.health?.runCount ?? 0) },
789
+ { label: "Errors", value: String(capability.health?.errorCount ?? 0) }
790
+ ]
791
+ }));
792
+ const warningCount = capabilities.filter((capability) => isWarningStatus(capability.status)).length;
793
+ const selectedCount = snapshot.report?.selected ?? capabilities.filter((capability) => capability.selected).length;
794
+ return {
795
+ capabilities,
796
+ description: options.description ?? DEFAULT_DESCRIPTION2,
797
+ error: snapshot.error,
798
+ isLoading: snapshot.isLoading,
799
+ label: snapshot.error ? "Unavailable" : capabilities.length ? warningCount > 0 ? `${warningCount} needs attention` : `${selectedCount} selected` : snapshot.isLoading ? "Checking" : "No capabilities",
800
+ status: snapshot.error ? "error" : capabilities.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
801
+ title: options.title ?? DEFAULT_TITLE2,
802
+ updatedAt: snapshot.updatedAt
803
+ };
804
+ };
805
+ var renderVoiceProviderCapabilitiesHTML = (snapshot, options = {}) => {
806
+ const model = createVoiceProviderCapabilitiesViewModel(snapshot, options);
807
+ const capabilities = model.capabilities.length ? `<div class="absolute-voice-provider-capabilities__providers">${model.capabilities.map((capability) => `<article class="absolute-voice-provider-capabilities__provider absolute-voice-provider-capabilities__provider--${escapeHtml3(capability.status)}">
808
+ <header>
809
+ <strong>${escapeHtml3(capability.label)}</strong>
810
+ <span>${escapeHtml3(formatStatus(capability.status))}</span>
811
+ </header>
812
+ <p>${escapeHtml3(capability.detail)}</p>
813
+ <dl>${capability.rows.map((row) => `<div>
814
+ <dt>${escapeHtml3(row.label)}</dt>
815
+ <dd>${escapeHtml3(row.value)}</dd>
816
+ </div>`).join("")}</dl>
817
+ </article>`).join("")}</div>` : '<p class="absolute-voice-provider-capabilities__empty">Configure provider capabilities to see deployment coverage.</p>';
818
+ return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${escapeHtml3(model.status)}">
819
+ <header class="absolute-voice-provider-capabilities__header">
820
+ <span class="absolute-voice-provider-capabilities__eyebrow">${escapeHtml3(model.title)}</span>
821
+ <strong class="absolute-voice-provider-capabilities__label">${escapeHtml3(model.label)}</strong>
822
+ </header>
823
+ <p class="absolute-voice-provider-capabilities__description">${escapeHtml3(model.description)}</p>
824
+ ${capabilities}
825
+ ${model.error ? `<p class="absolute-voice-provider-capabilities__error">${escapeHtml3(model.error)}</p>` : ""}
826
+ </section>`;
827
+ };
828
+ var getVoiceProviderCapabilitiesCSS = () => `.absolute-voice-provider-capabilities{border:1px solid #bfd7ea;border-radius:20px;background:#f6fbff;color:#08131f;padding:18px;box-shadow:0 18px 40px rgba(14,51,78,.12);font-family:inherit}.absolute-voice-provider-capabilities--error,.absolute-voice-provider-capabilities--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-provider-capabilities__header,.absolute-voice-provider-capabilities__provider header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-provider-capabilities__eyebrow{color:#255f85;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-provider-capabilities__label{font-size:24px;line-height:1}.absolute-voice-provider-capabilities__description,.absolute-voice-provider-capabilities__provider p,.absolute-voice-provider-capabilities__provider dt,.absolute-voice-provider-capabilities__empty{color:#405467}.absolute-voice-provider-capabilities__providers{display:grid;gap:12px;margin-top:14px}.absolute-voice-provider-capabilities__provider{background:#fff;border:1px solid #d7e7f3;border-radius:16px;padding:14px}.absolute-voice-provider-capabilities__provider--selected,.absolute-voice-provider-capabilities__provider--healthy{border-color:#86efac}.absolute-voice-provider-capabilities__provider--degraded,.absolute-voice-provider-capabilities__provider--rate-limited,.absolute-voice-provider-capabilities__provider--suppressed,.absolute-voice-provider-capabilities__provider--unconfigured{border-color:#f2a7a7}.absolute-voice-provider-capabilities__provider p{margin:10px 0}.absolute-voice-provider-capabilities__provider dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-provider-capabilities__provider div{background:#f6fbff;border:1px solid #d7e7f3;border-radius:12px;padding:8px}.absolute-voice-provider-capabilities__provider dt{font-size:12px}.absolute-voice-provider-capabilities__provider dd{font-weight:800;margin:4px 0 0}.absolute-voice-provider-capabilities__empty{margin:14px 0 0}.absolute-voice-provider-capabilities__error{color:#9f1239;font-weight:700}`;
829
+ var mountVoiceProviderCapabilities = (element, path = "/api/provider-capabilities", options = {}) => {
830
+ const store = createVoiceProviderCapabilitiesStore(path, options);
831
+ const render = () => {
832
+ element.innerHTML = renderVoiceProviderCapabilitiesHTML(store.getSnapshot(), options);
833
+ };
834
+ const unsubscribe = store.subscribe(render);
835
+ render();
836
+ store.refresh().catch(() => {});
837
+ return {
838
+ close: () => {
839
+ unsubscribe();
840
+ store.close();
841
+ },
842
+ refresh: store.refresh
843
+ };
844
+ };
845
+ var defineVoiceProviderCapabilitiesElement = (tagName = "absolute-voice-provider-capabilities") => {
846
+ if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
847
+ return;
848
+ }
849
+ customElements.define(tagName, class AbsoluteVoiceProviderCapabilitiesElement extends HTMLElement {
850
+ mounted;
851
+ connectedCallback() {
852
+ const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
853
+ this.mounted = mountVoiceProviderCapabilities(this, this.getAttribute("path") ?? "/api/provider-capabilities", {
854
+ description: this.getAttribute("description") ?? undefined,
855
+ intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
856
+ title: this.getAttribute("title") ?? undefined
857
+ });
858
+ }
859
+ disconnectedCallback() {
860
+ this.mounted?.close();
861
+ this.mounted = undefined;
862
+ }
863
+ });
864
+ };
865
+
866
+ // src/vue/useVoiceProviderCapabilities.ts
867
+ import { onUnmounted as onUnmounted3, shallowRef as shallowRef2 } from "vue";
868
+ var useVoiceProviderCapabilities = (path = "/api/provider-capabilities", options = {}) => {
869
+ const store = createVoiceProviderCapabilitiesStore(path, options);
870
+ const error = shallowRef2(null);
871
+ const isLoading = shallowRef2(false);
872
+ const report = shallowRef2();
873
+ const updatedAt = shallowRef2(undefined);
874
+ const sync = () => {
875
+ const snapshot = store.getSnapshot();
876
+ error.value = snapshot.error;
877
+ isLoading.value = snapshot.isLoading;
878
+ report.value = snapshot.report;
879
+ updatedAt.value = snapshot.updatedAt;
880
+ };
881
+ const unsubscribe = store.subscribe(sync);
882
+ sync();
883
+ store.refresh().catch(() => {});
884
+ onUnmounted3(() => {
885
+ unsubscribe();
886
+ store.close();
887
+ });
888
+ return {
889
+ error,
890
+ isLoading,
891
+ refresh: store.refresh,
892
+ report,
893
+ updatedAt
894
+ };
895
+ };
896
+
897
+ // src/vue/VoiceProviderCapabilities.ts
898
+ var VoiceProviderCapabilities = defineComponent3({
899
+ name: "VoiceProviderCapabilities",
900
+ props: {
901
+ class: {
902
+ default: "",
903
+ type: String
904
+ },
905
+ description: {
906
+ default: undefined,
907
+ type: String
908
+ },
909
+ intervalMs: {
910
+ default: 5000,
911
+ type: Number
912
+ },
913
+ path: {
914
+ default: "/api/provider-capabilities",
915
+ type: String
916
+ },
917
+ title: {
918
+ default: undefined,
919
+ type: String
920
+ }
921
+ },
922
+ setup(props) {
923
+ const options = {
924
+ description: props.description,
925
+ intervalMs: props.intervalMs,
926
+ title: props.title
927
+ };
928
+ const capabilities = useVoiceProviderCapabilities(props.path, options);
929
+ const model = computed2(() => createVoiceProviderCapabilitiesViewModel({
930
+ error: capabilities.error.value,
931
+ isLoading: capabilities.isLoading.value,
932
+ report: capabilities.report.value,
933
+ updatedAt: capabilities.updatedAt.value
934
+ }, options));
935
+ return () => h3("section", {
936
+ class: [
937
+ "absolute-voice-provider-capabilities",
938
+ `absolute-voice-provider-capabilities--${model.value.status}`,
939
+ props.class
940
+ ]
941
+ }, [
942
+ h3("header", { class: "absolute-voice-provider-capabilities__header" }, [
943
+ h3("span", { class: "absolute-voice-provider-capabilities__eyebrow" }, model.value.title),
944
+ h3("strong", { class: "absolute-voice-provider-capabilities__label" }, model.value.label)
945
+ ]),
946
+ h3("p", { class: "absolute-voice-provider-capabilities__description" }, model.value.description),
947
+ model.value.capabilities.length ? h3("div", { class: "absolute-voice-provider-capabilities__providers" }, model.value.capabilities.map((capability) => h3("article", {
948
+ class: [
949
+ "absolute-voice-provider-capabilities__provider",
950
+ `absolute-voice-provider-capabilities__provider--${capability.status}`
951
+ ],
952
+ key: `${capability.kind}:${capability.provider}`
953
+ }, [
954
+ h3("header", [
955
+ h3("strong", capability.label),
956
+ h3("span", capability.status)
957
+ ]),
958
+ h3("p", capability.detail),
959
+ h3("dl", capability.rows.map((row) => h3("div", { key: row.label }, [
960
+ h3("dt", row.label),
961
+ h3("dd", row.value)
962
+ ])))
963
+ ]))) : h3("p", { class: "absolute-voice-provider-capabilities__empty" }, "Configure provider capabilities to see deployment coverage."),
964
+ model.value.error ? h3("p", { class: "absolute-voice-provider-capabilities__error" }, model.value.error) : null
965
+ ]);
966
+ }
967
+ });
968
+ // src/vue/VoiceProviderStatus.ts
969
+ import { computed as computed3, defineComponent as defineComponent4, h as h4 } from "vue";
970
+
670
971
  // src/client/providerStatus.ts
671
972
  var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
672
973
  const fetchImpl = options.fetch ?? globalThis.fetch;
@@ -748,11 +1049,11 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
748
1049
  };
749
1050
 
750
1051
  // src/client/providerStatusWidget.ts
751
- var DEFAULT_TITLE2 = "Voice Providers";
752
- var DEFAULT_DESCRIPTION2 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
753
- var escapeHtml3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
754
- var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
755
- var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
1052
+ var DEFAULT_TITLE3 = "Voice Providers";
1053
+ var DEFAULT_DESCRIPTION3 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
1054
+ var escapeHtml4 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1055
+ var formatProvider2 = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
1056
+ var formatStatus2 = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
756
1057
  var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
757
1058
  var formatSuppression = (value) => typeof value === "number" ? `${Math.ceil(value / 1000)}s` : "None";
758
1059
  var getProviderDetail = (provider) => {
@@ -773,12 +1074,12 @@ var getProviderDetail = (provider) => {
773
1074
  }
774
1075
  return "No provider traffic observed yet.";
775
1076
  };
776
- var isWarningStatus = (status) => status === "degraded" || status === "rate-limited" || status === "recoverable" || status === "suppressed";
1077
+ var isWarningStatus2 = (status) => status === "degraded" || status === "rate-limited" || status === "recoverable" || status === "suppressed";
777
1078
  var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
778
1079
  const providers = snapshot.providers.map((provider) => ({
779
1080
  ...provider,
780
1081
  detail: getProviderDetail(provider),
781
- label: `${formatProvider(provider.provider)}${provider.recommended ? " recommended" : ""}`,
1082
+ label: `${formatProvider2(provider.provider)}${provider.recommended ? " recommended" : ""}`,
782
1083
  rows: [
783
1084
  { label: "Runs", value: String(provider.runCount) },
784
1085
  { label: "Avg latency", value: formatLatency(provider.averageElapsedMs) },
@@ -791,40 +1092,40 @@ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
791
1092
  }
792
1093
  ]
793
1094
  }));
794
- const warningCount = providers.filter((provider) => isWarningStatus(provider.status)).length;
1095
+ const warningCount = providers.filter((provider) => isWarningStatus2(provider.status)).length;
795
1096
  const healthyCount = providers.filter((provider) => provider.status === "healthy").length;
796
1097
  return {
797
- description: options.description ?? DEFAULT_DESCRIPTION2,
1098
+ description: options.description ?? DEFAULT_DESCRIPTION3,
798
1099
  error: snapshot.error,
799
1100
  isLoading: snapshot.isLoading,
800
1101
  label: snapshot.error ? "Unavailable" : providers.length ? warningCount > 0 ? `${warningCount} needs attention` : `${healthyCount} healthy` : snapshot.isLoading ? "Checking" : "No provider traffic",
801
1102
  providers,
802
1103
  status: snapshot.error ? "error" : providers.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
803
- title: options.title ?? DEFAULT_TITLE2,
1104
+ title: options.title ?? DEFAULT_TITLE3,
804
1105
  updatedAt: snapshot.updatedAt
805
1106
  };
806
1107
  };
807
1108
  var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
808
1109
  const model = createVoiceProviderStatusViewModel(snapshot, options);
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)}">
1110
+ 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--${escapeHtml4(provider.status)}">
810
1111
  <header>
811
- <strong>${escapeHtml3(provider.label)}</strong>
812
- <span>${escapeHtml3(formatStatus(provider.status))}</span>
1112
+ <strong>${escapeHtml4(provider.label)}</strong>
1113
+ <span>${escapeHtml4(formatStatus2(provider.status))}</span>
813
1114
  </header>
814
- <p>${escapeHtml3(provider.detail)}</p>
1115
+ <p>${escapeHtml4(provider.detail)}</p>
815
1116
  <dl>${provider.rows.map((row) => `<div>
816
- <dt>${escapeHtml3(row.label)}</dt>
817
- <dd>${escapeHtml3(row.value)}</dd>
1117
+ <dt>${escapeHtml4(row.label)}</dt>
1118
+ <dd>${escapeHtml4(row.value)}</dd>
818
1119
  </div>`).join("")}</dl>
819
1120
  </article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
820
- return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml3(model.status)}">
1121
+ return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml4(model.status)}">
821
1122
  <header class="absolute-voice-provider-status__header">
822
- <span class="absolute-voice-provider-status__eyebrow">${escapeHtml3(model.title)}</span>
823
- <strong class="absolute-voice-provider-status__label">${escapeHtml3(model.label)}</strong>
1123
+ <span class="absolute-voice-provider-status__eyebrow">${escapeHtml4(model.title)}</span>
1124
+ <strong class="absolute-voice-provider-status__label">${escapeHtml4(model.label)}</strong>
824
1125
  </header>
825
- <p class="absolute-voice-provider-status__description">${escapeHtml3(model.description)}</p>
1126
+ <p class="absolute-voice-provider-status__description">${escapeHtml4(model.description)}</p>
826
1127
  ${providers}
827
- ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml3(model.error)}</p>` : ""}
1128
+ ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml4(model.error)}</p>` : ""}
828
1129
  </section>`;
829
1130
  };
830
1131
  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}`;
@@ -866,12 +1167,12 @@ var defineVoiceProviderStatusElement = (tagName = "absolute-voice-provider-statu
866
1167
  };
867
1168
 
868
1169
  // src/vue/useVoiceProviderStatus.ts
869
- import { onUnmounted as onUnmounted3, ref as ref3, shallowRef as shallowRef2 } from "vue";
1170
+ import { onUnmounted as onUnmounted4, ref as ref3, shallowRef as shallowRef3 } from "vue";
870
1171
  var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
871
1172
  const store = createVoiceProviderStatusStore(path, options);
872
1173
  const error = ref3(null);
873
1174
  const isLoading = ref3(false);
874
- const providers = shallowRef2([]);
1175
+ const providers = shallowRef3([]);
875
1176
  const updatedAt = ref3(undefined);
876
1177
  const sync = () => {
877
1178
  const snapshot = store.getSnapshot();
@@ -883,7 +1184,7 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
883
1184
  const unsubscribe = store.subscribe(sync);
884
1185
  sync();
885
1186
  store.refresh().catch(() => {});
886
- onUnmounted3(() => {
1187
+ onUnmounted4(() => {
887
1188
  unsubscribe();
888
1189
  store.close();
889
1190
  });
@@ -897,7 +1198,7 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
897
1198
  };
898
1199
 
899
1200
  // src/vue/VoiceProviderStatus.ts
900
- var VoiceProviderStatus = defineComponent3({
1201
+ var VoiceProviderStatus = defineComponent4({
901
1202
  name: "VoiceProviderStatus",
902
1203
  props: {
903
1204
  class: {
@@ -928,47 +1229,47 @@ var VoiceProviderStatus = defineComponent3({
928
1229
  title: props.title
929
1230
  };
930
1231
  const status = useVoiceProviderStatus(props.path, options);
931
- const model = computed2(() => createVoiceProviderStatusViewModel({
1232
+ const model = computed3(() => createVoiceProviderStatusViewModel({
932
1233
  error: status.error.value,
933
1234
  isLoading: status.isLoading.value,
934
1235
  providers: status.providers.value,
935
1236
  updatedAt: status.updatedAt.value
936
1237
  }, options));
937
- return () => h3("section", {
1238
+ return () => h4("section", {
938
1239
  class: [
939
1240
  "absolute-voice-provider-status",
940
1241
  `absolute-voice-provider-status--${model.value.status}`,
941
1242
  props.class
942
1243
  ]
943
1244
  }, [
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)
1245
+ h4("header", { class: "absolute-voice-provider-status__header" }, [
1246
+ h4("span", { class: "absolute-voice-provider-status__eyebrow" }, model.value.title),
1247
+ h4("strong", { class: "absolute-voice-provider-status__label" }, model.value.label)
947
1248
  ]),
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", {
1249
+ h4("p", { class: "absolute-voice-provider-status__description" }, model.value.description),
1250
+ model.value.providers.length ? h4("div", { class: "absolute-voice-provider-status__providers" }, model.value.providers.map((provider) => h4("article", {
950
1251
  class: [
951
1252
  "absolute-voice-provider-status__provider",
952
1253
  `absolute-voice-provider-status__provider--${provider.status}`
953
1254
  ],
954
1255
  key: provider.provider
955
1256
  }, [
956
- h3("header", [
957
- h3("strong", provider.label),
958
- h3("span", provider.status)
1257
+ h4("header", [
1258
+ h4("strong", provider.label),
1259
+ h4("span", provider.status)
959
1260
  ]),
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)
1261
+ h4("p", provider.detail),
1262
+ h4("dl", provider.rows.map((row) => h4("div", { key: row.label }, [
1263
+ h4("dt", row.label),
1264
+ h4("dd", row.value)
964
1265
  ])))
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
1266
+ ]))) : h4("p", { class: "absolute-voice-provider-status__empty" }, "Run voice traffic to see provider health."),
1267
+ model.value.error ? h4("p", { class: "absolute-voice-provider-status__error" }, model.value.error) : null
967
1268
  ]);
968
1269
  }
969
1270
  });
970
1271
  // src/vue/VoiceRoutingStatus.ts
971
- import { computed as computed3, defineComponent as defineComponent4, h as h4 } from "vue";
1272
+ import { computed as computed4, defineComponent as defineComponent5, h as h5 } from "vue";
972
1273
 
973
1274
  // src/client/routingStatus.ts
974
1275
  var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
@@ -1051,9 +1352,9 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
1051
1352
  };
1052
1353
 
1053
1354
  // src/client/routingStatusWidget.ts
1054
- var DEFAULT_TITLE3 = "Voice Routing";
1055
- var DEFAULT_DESCRIPTION3 = "Latest provider routing decision from the self-hosted trace store.";
1056
- var escapeHtml4 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1355
+ var DEFAULT_TITLE4 = "Voice Routing";
1356
+ var DEFAULT_DESCRIPTION4 = "Latest provider routing decision from the self-hosted trace store.";
1357
+ var escapeHtml5 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1057
1358
  var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
1058
1359
  var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
1059
1360
  const decision = snapshot.decision;
@@ -1077,30 +1378,30 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
1077
1378
  ] : [];
1078
1379
  return {
1079
1380
  decision,
1080
- description: options.description ?? DEFAULT_DESCRIPTION3,
1381
+ description: options.description ?? DEFAULT_DESCRIPTION4,
1081
1382
  error: snapshot.error,
1082
1383
  isLoading: snapshot.isLoading,
1083
1384
  label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
1084
1385
  rows,
1085
1386
  status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
1086
- title: options.title ?? DEFAULT_TITLE3,
1387
+ title: options.title ?? DEFAULT_TITLE4,
1087
1388
  updatedAt: snapshot.updatedAt
1088
1389
  };
1089
1390
  };
1090
1391
  var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
1091
1392
  const model = createVoiceRoutingStatusViewModel(snapshot, options);
1092
1393
  const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
1093
- <span>${escapeHtml4(row.label)}</span>
1094
- <strong>${escapeHtml4(row.value)}</strong>
1394
+ <span>${escapeHtml5(row.label)}</span>
1395
+ <strong>${escapeHtml5(row.value)}</strong>
1095
1396
  </div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
1096
- return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml4(model.status)}">
1397
+ return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml5(model.status)}">
1097
1398
  <header class="absolute-voice-routing-status__header">
1098
- <span class="absolute-voice-routing-status__eyebrow">${escapeHtml4(model.title)}</span>
1099
- <strong class="absolute-voice-routing-status__label">${escapeHtml4(model.label)}</strong>
1399
+ <span class="absolute-voice-routing-status__eyebrow">${escapeHtml5(model.title)}</span>
1400
+ <strong class="absolute-voice-routing-status__label">${escapeHtml5(model.label)}</strong>
1100
1401
  </header>
1101
- <p class="absolute-voice-routing-status__description">${escapeHtml4(model.description)}</p>
1402
+ <p class="absolute-voice-routing-status__description">${escapeHtml5(model.description)}</p>
1102
1403
  ${rows}
1103
- ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml4(model.error)}</p>` : ""}
1404
+ ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml5(model.error)}</p>` : ""}
1104
1405
  </section>`;
1105
1406
  };
1106
1407
  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}`;
@@ -1142,10 +1443,10 @@ var defineVoiceRoutingStatusElement = (tagName = "absolute-voice-routing-status"
1142
1443
  };
1143
1444
 
1144
1445
  // src/vue/useVoiceRoutingStatus.ts
1145
- import { onUnmounted as onUnmounted4, ref as ref4, shallowRef as shallowRef3 } from "vue";
1446
+ import { onUnmounted as onUnmounted5, ref as ref4, shallowRef as shallowRef4 } from "vue";
1146
1447
  var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
1147
1448
  const store = createVoiceRoutingStatusStore(path, options);
1148
- const decision = shallowRef3(null);
1449
+ const decision = shallowRef4(null);
1149
1450
  const error = ref4(null);
1150
1451
  const isLoading = ref4(false);
1151
1452
  const updatedAt = ref4(undefined);
@@ -1159,7 +1460,7 @@ var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
1159
1460
  const unsubscribe = store.subscribe(sync);
1160
1461
  sync();
1161
1462
  store.refresh().catch(() => {});
1162
- onUnmounted4(() => {
1463
+ onUnmounted5(() => {
1163
1464
  unsubscribe();
1164
1465
  store.close();
1165
1466
  });
@@ -1173,7 +1474,7 @@ var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
1173
1474
  };
1174
1475
 
1175
1476
  // src/vue/VoiceRoutingStatus.ts
1176
- var VoiceRoutingStatus = defineComponent4({
1477
+ var VoiceRoutingStatus = defineComponent5({
1177
1478
  name: "VoiceRoutingStatus",
1178
1479
  props: {
1179
1480
  class: {
@@ -1204,34 +1505,34 @@ var VoiceRoutingStatus = defineComponent4({
1204
1505
  title: props.title
1205
1506
  };
1206
1507
  const status = useVoiceRoutingStatus(props.path, options);
1207
- const model = computed3(() => createVoiceRoutingStatusViewModel({
1508
+ const model = computed4(() => createVoiceRoutingStatusViewModel({
1208
1509
  decision: status.decision.value,
1209
1510
  error: status.error.value,
1210
1511
  isLoading: status.isLoading.value,
1211
1512
  updatedAt: status.updatedAt.value
1212
1513
  }, options));
1213
- return () => h4("section", {
1514
+ return () => h5("section", {
1214
1515
  class: [
1215
1516
  "absolute-voice-routing-status",
1216
1517
  `absolute-voice-routing-status--${model.value.status}`,
1217
1518
  props.class
1218
1519
  ]
1219
1520
  }, [
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)
1521
+ h5("header", { class: "absolute-voice-routing-status__header" }, [
1522
+ h5("span", { class: "absolute-voice-routing-status__eyebrow" }, model.value.title),
1523
+ h5("strong", { class: "absolute-voice-routing-status__label" }, model.value.label)
1223
1524
  ]),
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
1525
+ h5("p", { class: "absolute-voice-routing-status__description" }, model.value.description),
1526
+ model.value.rows.length ? h5("div", { class: "absolute-voice-routing-status__grid" }, model.value.rows.map((row) => h5("div", { key: row.label }, [
1527
+ h5("span", row.label),
1528
+ h5("strong", row.value)
1529
+ ]))) : h5("p", { class: "absolute-voice-routing-status__empty" }, "Start a voice session to see the selected provider."),
1530
+ model.value.error ? h5("p", { class: "absolute-voice-routing-status__error" }, model.value.error) : null
1230
1531
  ]);
1231
1532
  }
1232
1533
  });
1233
1534
  // src/vue/useVoiceStream.ts
1234
- import { onUnmounted as onUnmounted5, ref as ref5, shallowRef as shallowRef4 } from "vue";
1535
+ import { onUnmounted as onUnmounted6, ref as ref5, shallowRef as shallowRef5 } from "vue";
1235
1536
 
1236
1537
  // src/client/actions.ts
1237
1538
  var normalizeErrorMessage = (value) => {
@@ -1749,15 +2050,15 @@ var createVoiceStream = (path, options = {}) => {
1749
2050
  // src/vue/useVoiceStream.ts
1750
2051
  var useVoiceStream = (path, options = {}) => {
1751
2052
  const stream = createVoiceStream(path, options);
1752
- const assistantAudio = shallowRef4([]);
1753
- const assistantTexts = shallowRef4([]);
1754
- const call = shallowRef4(null);
2053
+ const assistantAudio = shallowRef5([]);
2054
+ const assistantTexts = shallowRef5([]);
2055
+ const call = shallowRef5(null);
1755
2056
  const error = ref5(null);
1756
2057
  const isConnected = ref5(false);
1757
2058
  const partial = ref5("");
1758
2059
  const sessionId = ref5(stream.sessionId);
1759
2060
  const status = ref5(stream.status);
1760
- const turns = shallowRef4([]);
2061
+ const turns = shallowRef5([]);
1761
2062
  const sync = () => {
1762
2063
  assistantAudio.value = [...stream.assistantAudio];
1763
2064
  assistantTexts.value = [...stream.assistantTexts];
@@ -1775,7 +2076,7 @@ var useVoiceStream = (path, options = {}) => {
1775
2076
  unsubscribe();
1776
2077
  stream.close();
1777
2078
  };
1778
- onUnmounted5(destroy);
2079
+ onUnmounted6(destroy);
1779
2080
  return {
1780
2081
  assistantAudio,
1781
2082
  assistantTexts,
@@ -1793,7 +2094,7 @@ var useVoiceStream = (path, options = {}) => {
1793
2094
  };
1794
2095
  };
1795
2096
  // src/vue/useVoiceController.ts
1796
- import { onUnmounted as onUnmounted6, ref as ref6, shallowRef as shallowRef5 } from "vue";
2097
+ import { onUnmounted as onUnmounted7, ref as ref6, shallowRef as shallowRef6 } from "vue";
1797
2098
 
1798
2099
  // src/client/htmx.ts
1799
2100
  var DEFAULT_EVENT_NAME = "voice-refresh";
@@ -2428,8 +2729,8 @@ var createVoiceController = (path, options = {}) => {
2428
2729
  // src/vue/useVoiceController.ts
2429
2730
  var useVoiceController = (path, options = {}) => {
2430
2731
  const controller = createVoiceController(path, options);
2431
- const assistantAudio = shallowRef5([]);
2432
- const assistantTexts = shallowRef5([]);
2732
+ const assistantAudio = shallowRef6([]);
2733
+ const assistantTexts = shallowRef6([]);
2433
2734
  const error = ref6(null);
2434
2735
  const isConnected = ref6(false);
2435
2736
  const isRecording = ref6(false);
@@ -2437,7 +2738,7 @@ var useVoiceController = (path, options = {}) => {
2437
2738
  const recordingError = ref6(null);
2438
2739
  const sessionId = ref6(controller.sessionId);
2439
2740
  const status = ref6(controller.status);
2440
- const turns = shallowRef5([]);
2741
+ const turns = shallowRef6([]);
2441
2742
  const sync = () => {
2442
2743
  assistantAudio.value = [...controller.assistantAudio];
2443
2744
  assistantTexts.value = [...controller.assistantTexts];
@@ -2456,7 +2757,7 @@ var useVoiceController = (path, options = {}) => {
2456
2757
  unsubscribe();
2457
2758
  controller.close();
2458
2759
  };
2459
- onUnmounted6(destroy);
2760
+ onUnmounted7(destroy);
2460
2761
  return {
2461
2762
  assistantAudio,
2462
2763
  assistantTexts,
@@ -2478,7 +2779,7 @@ var useVoiceController = (path, options = {}) => {
2478
2779
  };
2479
2780
  };
2480
2781
  // src/vue/useVoiceWorkflowStatus.ts
2481
- import { onUnmounted as onUnmounted7, ref as ref7, shallowRef as shallowRef6 } from "vue";
2782
+ import { onUnmounted as onUnmounted8, ref as ref7, shallowRef as shallowRef7 } from "vue";
2482
2783
 
2483
2784
  // src/client/workflowStatus.ts
2484
2785
  var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
@@ -2564,7 +2865,7 @@ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
2564
2865
  const store = createVoiceWorkflowStatusStore(path, options);
2565
2866
  const error = ref7(null);
2566
2867
  const isLoading = ref7(false);
2567
- const report = shallowRef6(undefined);
2868
+ const report = shallowRef7(undefined);
2568
2869
  const updatedAt = ref7(undefined);
2569
2870
  const sync = () => {
2570
2871
  const snapshot = store.getSnapshot();
@@ -2578,7 +2879,7 @@ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
2578
2879
  if (typeof window !== "undefined") {
2579
2880
  store.refresh().catch(() => {});
2580
2881
  }
2581
- onUnmounted7(() => {
2882
+ onUnmounted8(() => {
2582
2883
  unsubscribe();
2583
2884
  store.close();
2584
2885
  });
@@ -2596,10 +2897,12 @@ export {
2596
2897
  useVoiceRoutingStatus,
2597
2898
  useVoiceProviderStatus,
2598
2899
  useVoiceProviderSimulationControls,
2900
+ useVoiceProviderCapabilities,
2599
2901
  useVoiceController,
2600
2902
  useVoiceAppKitStatus,
2601
2903
  VoiceRoutingStatus,
2602
2904
  VoiceProviderStatus,
2603
2905
  VoiceProviderSimulationControls,
2906
+ VoiceProviderCapabilities,
2604
2907
  VoiceOpsStatus
2605
2908
  };