@absolutejs/voice 0.0.22-beta.94 → 0.0.22-beta.95

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.
@@ -1759,9 +1759,266 @@ var VoiceTraceTimeline = ({
1759
1759
  ]
1760
1760
  }, undefined, true, undefined, this);
1761
1761
  };
1762
- // src/react/useVoiceTurnQuality.tsx
1762
+ // src/react/useVoiceTurnLatency.tsx
1763
1763
  import { useEffect as useEffect7, useRef as useRef7, useSyncExternalStore as useSyncExternalStore7 } from "react";
1764
1764
 
1765
+ // src/client/turnLatency.ts
1766
+ var fetchVoiceTurnLatency = async (path = "/api/turn-latency", options = {}) => {
1767
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1768
+ const response = await fetchImpl(path);
1769
+ if (!response.ok) {
1770
+ throw new Error(`Voice turn latency failed: HTTP ${response.status}`);
1771
+ }
1772
+ return await response.json();
1773
+ };
1774
+ var createVoiceTurnLatencyStore = (path = "/api/turn-latency", options = {}) => {
1775
+ const listeners = new Set;
1776
+ let closed = false;
1777
+ let timer;
1778
+ let snapshot = {
1779
+ error: null,
1780
+ isLoading: false
1781
+ };
1782
+ const emit = () => {
1783
+ for (const listener of listeners) {
1784
+ listener();
1785
+ }
1786
+ };
1787
+ const refresh = async () => {
1788
+ if (closed) {
1789
+ return snapshot.report;
1790
+ }
1791
+ snapshot = { ...snapshot, error: null, isLoading: true };
1792
+ emit();
1793
+ try {
1794
+ const report = await fetchVoiceTurnLatency(path, options);
1795
+ snapshot = {
1796
+ error: null,
1797
+ isLoading: false,
1798
+ report,
1799
+ updatedAt: Date.now()
1800
+ };
1801
+ emit();
1802
+ return report;
1803
+ } catch (error) {
1804
+ snapshot = {
1805
+ ...snapshot,
1806
+ error: error instanceof Error ? error.message : String(error),
1807
+ isLoading: false
1808
+ };
1809
+ emit();
1810
+ throw error;
1811
+ }
1812
+ };
1813
+ const close = () => {
1814
+ closed = true;
1815
+ if (timer) {
1816
+ clearInterval(timer);
1817
+ timer = undefined;
1818
+ }
1819
+ listeners.clear();
1820
+ };
1821
+ if (options.intervalMs && options.intervalMs > 0) {
1822
+ timer = setInterval(() => {
1823
+ refresh().catch(() => {});
1824
+ }, options.intervalMs);
1825
+ }
1826
+ return {
1827
+ close,
1828
+ getServerSnapshot: () => snapshot,
1829
+ getSnapshot: () => snapshot,
1830
+ refresh,
1831
+ subscribe: (listener) => {
1832
+ listeners.add(listener);
1833
+ return () => {
1834
+ listeners.delete(listener);
1835
+ };
1836
+ }
1837
+ };
1838
+ };
1839
+
1840
+ // src/react/useVoiceTurnLatency.tsx
1841
+ var useVoiceTurnLatency = (path = "/api/turn-latency", options = {}) => {
1842
+ const storeRef = useRef7(null);
1843
+ if (!storeRef.current) {
1844
+ storeRef.current = createVoiceTurnLatencyStore(path, options);
1845
+ }
1846
+ const store = storeRef.current;
1847
+ useEffect7(() => {
1848
+ store.refresh().catch(() => {});
1849
+ return () => store.close();
1850
+ }, [store]);
1851
+ return {
1852
+ ...useSyncExternalStore7(store.subscribe, store.getSnapshot, store.getServerSnapshot),
1853
+ refresh: store.refresh
1854
+ };
1855
+ };
1856
+
1857
+ // src/client/turnLatencyWidget.ts
1858
+ var DEFAULT_TITLE6 = "Turn Latency";
1859
+ var DEFAULT_DESCRIPTION6 = "Per-turn timing from first transcript to commit and assistant response start.";
1860
+ var escapeHtml7 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1861
+ var formatMs2 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
1862
+ var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
1863
+ const turns = (snapshot.report?.turns ?? []).map((turn) => ({
1864
+ ...turn,
1865
+ label: turn.text || "Empty turn",
1866
+ rows: turn.stages.map((stage) => ({
1867
+ label: stage.label,
1868
+ value: formatMs2(stage.valueMs)
1869
+ }))
1870
+ }));
1871
+ const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
1872
+ const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
1873
+ return {
1874
+ description: options.description ?? DEFAULT_DESCRIPTION6,
1875
+ error: snapshot.error,
1876
+ isLoading: snapshot.isLoading,
1877
+ label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} slow` : warningCount > 0 ? `${warningCount} warnings` : `avg ${formatMs2(snapshot.report?.averageTotalMs)}` : snapshot.isLoading ? "Checking" : "No turns",
1878
+ status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
1879
+ title: options.title ?? DEFAULT_TITLE6,
1880
+ turns,
1881
+ updatedAt: snapshot.updatedAt
1882
+ };
1883
+ };
1884
+ var renderVoiceTurnLatencyHTML = (snapshot, options = {}) => {
1885
+ const model = createVoiceTurnLatencyViewModel(snapshot, options);
1886
+ const turns = model.turns.length ? `<div class="absolute-voice-turn-latency__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-latency__turn absolute-voice-turn-latency__turn--${escapeHtml7(turn.status)}">
1887
+ <header>
1888
+ <strong>${escapeHtml7(turn.label)}</strong>
1889
+ <span>${escapeHtml7(turn.status)}</span>
1890
+ </header>
1891
+ <dl>${turn.rows.map((row) => `<div>
1892
+ <dt>${escapeHtml7(row.label)}</dt>
1893
+ <dd>${escapeHtml7(row.value)}</dd>
1894
+ </div>`).join("")}</dl>
1895
+ </article>`).join("")}</div>` : '<p class="absolute-voice-turn-latency__empty">Complete a voice turn to see latency diagnostics.</p>';
1896
+ return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml7(model.status)}">
1897
+ <header class="absolute-voice-turn-latency__header">
1898
+ <span class="absolute-voice-turn-latency__eyebrow">${escapeHtml7(model.title)}</span>
1899
+ <strong class="absolute-voice-turn-latency__label">${escapeHtml7(model.label)}</strong>
1900
+ </header>
1901
+ <p class="absolute-voice-turn-latency__description">${escapeHtml7(model.description)}</p>
1902
+ ${turns}
1903
+ ${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml7(model.error)}</p>` : ""}
1904
+ </section>`;
1905
+ };
1906
+ var mountVoiceTurnLatency = (element, path = "/api/turn-latency", options = {}) => {
1907
+ const store = createVoiceTurnLatencyStore(path, options);
1908
+ const render = () => {
1909
+ element.innerHTML = renderVoiceTurnLatencyHTML(store.getSnapshot(), options);
1910
+ };
1911
+ const unsubscribe = store.subscribe(render);
1912
+ render();
1913
+ store.refresh().catch(() => {});
1914
+ return {
1915
+ close: () => {
1916
+ unsubscribe();
1917
+ store.close();
1918
+ },
1919
+ refresh: store.refresh
1920
+ };
1921
+ };
1922
+ var defineVoiceTurnLatencyElement = (tagName = "absolute-voice-turn-latency") => {
1923
+ if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
1924
+ return;
1925
+ }
1926
+ customElements.define(tagName, class AbsoluteVoiceTurnLatencyElement extends HTMLElement {
1927
+ mounted;
1928
+ connectedCallback() {
1929
+ const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
1930
+ this.mounted = mountVoiceTurnLatency(this, this.getAttribute("path") ?? "/api/turn-latency", {
1931
+ description: this.getAttribute("description") ?? undefined,
1932
+ intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
1933
+ title: this.getAttribute("title") ?? undefined
1934
+ });
1935
+ }
1936
+ disconnectedCallback() {
1937
+ this.mounted?.close();
1938
+ this.mounted = undefined;
1939
+ }
1940
+ });
1941
+ };
1942
+
1943
+ // src/react/VoiceTurnLatency.tsx
1944
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
1945
+ var VoiceTurnLatency = ({
1946
+ className,
1947
+ path = "/api/turn-latency",
1948
+ ...options
1949
+ }) => {
1950
+ const snapshot = useVoiceTurnLatency(path, options);
1951
+ const model = createVoiceTurnLatencyViewModel(snapshot, options);
1952
+ return /* @__PURE__ */ jsxDEV7("section", {
1953
+ className: [
1954
+ "absolute-voice-turn-latency",
1955
+ `absolute-voice-turn-latency--${model.status}`,
1956
+ className
1957
+ ].filter(Boolean).join(" "),
1958
+ children: [
1959
+ /* @__PURE__ */ jsxDEV7("header", {
1960
+ className: "absolute-voice-turn-latency__header",
1961
+ children: [
1962
+ /* @__PURE__ */ jsxDEV7("span", {
1963
+ className: "absolute-voice-turn-latency__eyebrow",
1964
+ children: model.title
1965
+ }, undefined, false, undefined, this),
1966
+ /* @__PURE__ */ jsxDEV7("strong", {
1967
+ className: "absolute-voice-turn-latency__label",
1968
+ children: model.label
1969
+ }, undefined, false, undefined, this)
1970
+ ]
1971
+ }, undefined, true, undefined, this),
1972
+ /* @__PURE__ */ jsxDEV7("p", {
1973
+ className: "absolute-voice-turn-latency__description",
1974
+ children: model.description
1975
+ }, undefined, false, undefined, this),
1976
+ model.turns.length ? /* @__PURE__ */ jsxDEV7("div", {
1977
+ className: "absolute-voice-turn-latency__turns",
1978
+ children: model.turns.map((turn) => /* @__PURE__ */ jsxDEV7("article", {
1979
+ className: [
1980
+ "absolute-voice-turn-latency__turn",
1981
+ `absolute-voice-turn-latency__turn--${turn.status}`
1982
+ ].join(" "),
1983
+ children: [
1984
+ /* @__PURE__ */ jsxDEV7("header", {
1985
+ children: [
1986
+ /* @__PURE__ */ jsxDEV7("strong", {
1987
+ children: turn.label
1988
+ }, undefined, false, undefined, this),
1989
+ /* @__PURE__ */ jsxDEV7("span", {
1990
+ children: turn.status
1991
+ }, undefined, false, undefined, this)
1992
+ ]
1993
+ }, undefined, true, undefined, this),
1994
+ /* @__PURE__ */ jsxDEV7("dl", {
1995
+ children: turn.rows.map((row) => /* @__PURE__ */ jsxDEV7("div", {
1996
+ children: [
1997
+ /* @__PURE__ */ jsxDEV7("dt", {
1998
+ children: row.label
1999
+ }, undefined, false, undefined, this),
2000
+ /* @__PURE__ */ jsxDEV7("dd", {
2001
+ children: row.value
2002
+ }, undefined, false, undefined, this)
2003
+ ]
2004
+ }, row.label, true, undefined, this))
2005
+ }, undefined, false, undefined, this)
2006
+ ]
2007
+ }, `${turn.sessionId}:${turn.turnId}`, true, undefined, this))
2008
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV7("p", {
2009
+ className: "absolute-voice-turn-latency__empty",
2010
+ children: "Complete a voice turn to see latency diagnostics."
2011
+ }, undefined, false, undefined, this),
2012
+ model.error ? /* @__PURE__ */ jsxDEV7("p", {
2013
+ className: "absolute-voice-turn-latency__error",
2014
+ children: model.error
2015
+ }, undefined, false, undefined, this) : null
2016
+ ]
2017
+ }, undefined, true, undefined, this);
2018
+ };
2019
+ // src/react/useVoiceTurnQuality.tsx
2020
+ import { useEffect as useEffect8, useRef as useRef8, useSyncExternalStore as useSyncExternalStore8 } from "react";
2021
+
1765
2022
  // src/client/turnQuality.ts
1766
2023
  var fetchVoiceTurnQuality = async (path = "/api/turn-quality", options = {}) => {
1767
2024
  const fetchImpl = options.fetch ?? globalThis.fetch;
@@ -1843,25 +2100,25 @@ var createVoiceTurnQualityStore = (path = "/api/turn-quality", options = {}) =>
1843
2100
 
1844
2101
  // src/react/useVoiceTurnQuality.tsx
1845
2102
  var useVoiceTurnQuality = (path = "/api/turn-quality", options = {}) => {
1846
- const storeRef = useRef7(null);
2103
+ const storeRef = useRef8(null);
1847
2104
  if (!storeRef.current) {
1848
2105
  storeRef.current = createVoiceTurnQualityStore(path, options);
1849
2106
  }
1850
2107
  const store = storeRef.current;
1851
- useEffect7(() => {
2108
+ useEffect8(() => {
1852
2109
  store.refresh().catch(() => {});
1853
2110
  return () => store.close();
1854
2111
  }, [store]);
1855
2112
  return {
1856
- ...useSyncExternalStore7(store.subscribe, store.getSnapshot, store.getServerSnapshot),
2113
+ ...useSyncExternalStore8(store.subscribe, store.getSnapshot, store.getServerSnapshot),
1857
2114
  refresh: store.refresh
1858
2115
  };
1859
2116
  };
1860
2117
 
1861
2118
  // src/client/turnQualityWidget.ts
1862
- var DEFAULT_TITLE6 = "Turn Quality";
1863
- var DEFAULT_DESCRIPTION6 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
1864
- var escapeHtml7 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2119
+ var DEFAULT_TITLE7 = "Turn Quality";
2120
+ var DEFAULT_DESCRIPTION7 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
2121
+ var escapeHtml8 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1865
2122
  var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
1866
2123
  var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
1867
2124
  var getTurnDetail = (turn) => {
@@ -1899,37 +2156,37 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
1899
2156
  const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
1900
2157
  const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
1901
2158
  return {
1902
- description: options.description ?? DEFAULT_DESCRIPTION6,
2159
+ description: options.description ?? DEFAULT_DESCRIPTION7,
1903
2160
  error: snapshot.error,
1904
2161
  isLoading: snapshot.isLoading,
1905
2162
  label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} failed` : warningCount > 0 ? `${warningCount} warnings` : `${turns.length} healthy` : snapshot.isLoading ? "Checking" : "No turns",
1906
2163
  status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
1907
- title: options.title ?? DEFAULT_TITLE6,
2164
+ title: options.title ?? DEFAULT_TITLE7,
1908
2165
  turns,
1909
2166
  updatedAt: snapshot.updatedAt
1910
2167
  };
1911
2168
  };
1912
2169
  var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
1913
2170
  const model = createVoiceTurnQualityViewModel(snapshot, options);
1914
- const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${escapeHtml7(turn.status)}">
2171
+ const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${escapeHtml8(turn.status)}">
1915
2172
  <header>
1916
- <strong>${escapeHtml7(turn.label)}</strong>
1917
- <span>${escapeHtml7(turn.status)}</span>
2173
+ <strong>${escapeHtml8(turn.label)}</strong>
2174
+ <span>${escapeHtml8(turn.status)}</span>
1918
2175
  </header>
1919
- <p>${escapeHtml7(turn.detail)}</p>
2176
+ <p>${escapeHtml8(turn.detail)}</p>
1920
2177
  <dl>${turn.rows.map((row) => `<div>
1921
- <dt>${escapeHtml7(row.label)}</dt>
1922
- <dd>${escapeHtml7(row.value)}</dd>
2178
+ <dt>${escapeHtml8(row.label)}</dt>
2179
+ <dd>${escapeHtml8(row.value)}</dd>
1923
2180
  </div>`).join("")}</dl>
1924
2181
  </article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
1925
- return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml7(model.status)}">
2182
+ return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml8(model.status)}">
1926
2183
  <header class="absolute-voice-turn-quality__header">
1927
- <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml7(model.title)}</span>
1928
- <strong class="absolute-voice-turn-quality__label">${escapeHtml7(model.label)}</strong>
2184
+ <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml8(model.title)}</span>
2185
+ <strong class="absolute-voice-turn-quality__label">${escapeHtml8(model.label)}</strong>
1929
2186
  </header>
1930
- <p class="absolute-voice-turn-quality__description">${escapeHtml7(model.description)}</p>
2187
+ <p class="absolute-voice-turn-quality__description">${escapeHtml8(model.description)}</p>
1931
2188
  ${turns}
1932
- ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml7(model.error)}</p>` : ""}
2189
+ ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml8(model.error)}</p>` : ""}
1933
2190
  </section>`;
1934
2191
  };
1935
2192
  var getVoiceTurnQualityCSS = () => `.absolute-voice-turn-quality{border:1px solid #e4d1a3;border-radius:20px;background:#fff9eb;color:#17120a;padding:18px;box-shadow:0 18px 40px rgba(73,48,14,.12);font-family:inherit}.absolute-voice-turn-quality--error,.absolute-voice-turn-quality--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-turn-quality__header,.absolute-voice-turn-quality__turn header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-turn-quality__eyebrow{color:#8a5a0a;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-turn-quality__label{font-size:24px;line-height:1}.absolute-voice-turn-quality__description,.absolute-voice-turn-quality__turn p,.absolute-voice-turn-quality__turn dt,.absolute-voice-turn-quality__empty{color:#5a4930}.absolute-voice-turn-quality__turns{display:grid;gap:12px;margin-top:14px}.absolute-voice-turn-quality__turn{background:#fff;border:1px solid #f0dfba;border-radius:16px;padding:14px}.absolute-voice-turn-quality__turn--pass{border-color:#86efac}.absolute-voice-turn-quality__turn--warn,.absolute-voice-turn-quality__turn--unknown{border-color:#fbbf24}.absolute-voice-turn-quality__turn--fail{border-color:#f2a7a7}.absolute-voice-turn-quality__turn p{margin:10px 0}.absolute-voice-turn-quality__turn dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-turn-quality__turn div{background:#fff9eb;border:1px solid #f0dfba;border-radius:12px;padding:8px}.absolute-voice-turn-quality__turn dt{font-size:12px}.absolute-voice-turn-quality__turn dd{font-weight:800;margin:4px 0 0}.absolute-voice-turn-quality__empty{margin:14px 0 0}.absolute-voice-turn-quality__error{color:#9f1239;font-weight:700}`;
@@ -1971,7 +2228,7 @@ var defineVoiceTurnQualityElement = (tagName = "absolute-voice-turn-quality") =>
1971
2228
  };
1972
2229
 
1973
2230
  // src/react/VoiceTurnQuality.tsx
1974
- import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
2231
+ import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
1975
2232
  var VoiceTurnQuality = ({
1976
2233
  className,
1977
2234
  path = "/api/turn-quality",
@@ -1979,58 +2236,58 @@ var VoiceTurnQuality = ({
1979
2236
  }) => {
1980
2237
  const snapshot = useVoiceTurnQuality(path, options);
1981
2238
  const model = createVoiceTurnQualityViewModel(snapshot, options);
1982
- return /* @__PURE__ */ jsxDEV7("section", {
2239
+ return /* @__PURE__ */ jsxDEV8("section", {
1983
2240
  className: [
1984
2241
  "absolute-voice-turn-quality",
1985
2242
  `absolute-voice-turn-quality--${model.status}`,
1986
2243
  className
1987
2244
  ].filter(Boolean).join(" "),
1988
2245
  children: [
1989
- /* @__PURE__ */ jsxDEV7("header", {
2246
+ /* @__PURE__ */ jsxDEV8("header", {
1990
2247
  className: "absolute-voice-turn-quality__header",
1991
2248
  children: [
1992
- /* @__PURE__ */ jsxDEV7("span", {
2249
+ /* @__PURE__ */ jsxDEV8("span", {
1993
2250
  className: "absolute-voice-turn-quality__eyebrow",
1994
2251
  children: model.title
1995
2252
  }, undefined, false, undefined, this),
1996
- /* @__PURE__ */ jsxDEV7("strong", {
2253
+ /* @__PURE__ */ jsxDEV8("strong", {
1997
2254
  className: "absolute-voice-turn-quality__label",
1998
2255
  children: model.label
1999
2256
  }, undefined, false, undefined, this)
2000
2257
  ]
2001
2258
  }, undefined, true, undefined, this),
2002
- /* @__PURE__ */ jsxDEV7("p", {
2259
+ /* @__PURE__ */ jsxDEV8("p", {
2003
2260
  className: "absolute-voice-turn-quality__description",
2004
2261
  children: model.description
2005
2262
  }, undefined, false, undefined, this),
2006
- model.turns.length ? /* @__PURE__ */ jsxDEV7("div", {
2263
+ model.turns.length ? /* @__PURE__ */ jsxDEV8("div", {
2007
2264
  className: "absolute-voice-turn-quality__turns",
2008
- children: model.turns.map((turn) => /* @__PURE__ */ jsxDEV7("article", {
2265
+ children: model.turns.map((turn) => /* @__PURE__ */ jsxDEV8("article", {
2009
2266
  className: [
2010
2267
  "absolute-voice-turn-quality__turn",
2011
2268
  `absolute-voice-turn-quality__turn--${turn.status}`
2012
2269
  ].join(" "),
2013
2270
  children: [
2014
- /* @__PURE__ */ jsxDEV7("header", {
2271
+ /* @__PURE__ */ jsxDEV8("header", {
2015
2272
  children: [
2016
- /* @__PURE__ */ jsxDEV7("strong", {
2273
+ /* @__PURE__ */ jsxDEV8("strong", {
2017
2274
  children: turn.label
2018
2275
  }, undefined, false, undefined, this),
2019
- /* @__PURE__ */ jsxDEV7("span", {
2276
+ /* @__PURE__ */ jsxDEV8("span", {
2020
2277
  children: turn.status
2021
2278
  }, undefined, false, undefined, this)
2022
2279
  ]
2023
2280
  }, undefined, true, undefined, this),
2024
- /* @__PURE__ */ jsxDEV7("p", {
2281
+ /* @__PURE__ */ jsxDEV8("p", {
2025
2282
  children: turn.detail
2026
2283
  }, undefined, false, undefined, this),
2027
- /* @__PURE__ */ jsxDEV7("dl", {
2028
- children: turn.rows.map((row) => /* @__PURE__ */ jsxDEV7("div", {
2284
+ /* @__PURE__ */ jsxDEV8("dl", {
2285
+ children: turn.rows.map((row) => /* @__PURE__ */ jsxDEV8("div", {
2029
2286
  children: [
2030
- /* @__PURE__ */ jsxDEV7("dt", {
2287
+ /* @__PURE__ */ jsxDEV8("dt", {
2031
2288
  children: row.label
2032
2289
  }, undefined, false, undefined, this),
2033
- /* @__PURE__ */ jsxDEV7("dd", {
2290
+ /* @__PURE__ */ jsxDEV8("dd", {
2034
2291
  children: row.value
2035
2292
  }, undefined, false, undefined, this)
2036
2293
  ]
@@ -2038,11 +2295,11 @@ var VoiceTurnQuality = ({
2038
2295
  }, undefined, false, undefined, this)
2039
2296
  ]
2040
2297
  }, `${turn.sessionId}:${turn.turnId}`, true, undefined, this))
2041
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV7("p", {
2298
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV8("p", {
2042
2299
  className: "absolute-voice-turn-quality__empty",
2043
2300
  children: "Complete a voice turn to see STT quality diagnostics."
2044
2301
  }, undefined, false, undefined, this),
2045
- model.error ? /* @__PURE__ */ jsxDEV7("p", {
2302
+ model.error ? /* @__PURE__ */ jsxDEV8("p", {
2046
2303
  className: "absolute-voice-turn-quality__error",
2047
2304
  children: model.error
2048
2305
  }, undefined, false, undefined, this) : null
@@ -2050,7 +2307,7 @@ var VoiceTurnQuality = ({
2050
2307
  }, undefined, true, undefined, this);
2051
2308
  };
2052
2309
  // src/react/useVoiceStream.tsx
2053
- import { useEffect as useEffect8, useRef as useRef8, useSyncExternalStore as useSyncExternalStore8 } from "react";
2310
+ import { useEffect as useEffect9, useRef as useRef9, useSyncExternalStore as useSyncExternalStore9 } from "react";
2054
2311
 
2055
2312
  // src/client/actions.ts
2056
2313
  var normalizeErrorMessage = (value) => {
@@ -2578,13 +2835,13 @@ var EMPTY_SNAPSHOT = {
2578
2835
  turns: []
2579
2836
  };
2580
2837
  var useVoiceStream = (path, options = {}) => {
2581
- const streamRef = useRef8(null);
2838
+ const streamRef = useRef9(null);
2582
2839
  if (!streamRef.current) {
2583
2840
  streamRef.current = createVoiceStream(path, options);
2584
2841
  }
2585
2842
  const stream = streamRef.current;
2586
- useEffect8(() => () => stream.close(), [stream]);
2587
- const snapshot = useSyncExternalStore8(stream.subscribe, stream.getSnapshot, stream.getServerSnapshot) ?? EMPTY_SNAPSHOT;
2843
+ useEffect9(() => () => stream.close(), [stream]);
2844
+ const snapshot = useSyncExternalStore9(stream.subscribe, stream.getSnapshot, stream.getServerSnapshot) ?? EMPTY_SNAPSHOT;
2588
2845
  return {
2589
2846
  ...snapshot,
2590
2847
  callControl: (message) => stream.callControl(message),
@@ -2594,7 +2851,7 @@ var useVoiceStream = (path, options = {}) => {
2594
2851
  };
2595
2852
  };
2596
2853
  // src/react/useVoiceController.tsx
2597
- import { useEffect as useEffect9, useRef as useRef9, useSyncExternalStore as useSyncExternalStore9 } from "react";
2854
+ import { useEffect as useEffect10, useRef as useRef10, useSyncExternalStore as useSyncExternalStore10 } from "react";
2598
2855
 
2599
2856
  // src/client/htmx.ts
2600
2857
  var DEFAULT_EVENT_NAME = "voice-refresh";
@@ -3247,13 +3504,13 @@ var EMPTY_SNAPSHOT2 = {
3247
3504
  turns: []
3248
3505
  };
3249
3506
  var useVoiceController = (path, options = {}) => {
3250
- const controllerRef = useRef9(null);
3507
+ const controllerRef = useRef10(null);
3251
3508
  if (!controllerRef.current) {
3252
3509
  controllerRef.current = createVoiceController(path, options);
3253
3510
  }
3254
3511
  const controller = controllerRef.current;
3255
- useEffect9(() => () => controller.close(), [controller]);
3256
- const snapshot = useSyncExternalStore9(controller.subscribe, controller.getSnapshot, controller.getServerSnapshot) ?? EMPTY_SNAPSHOT2;
3512
+ useEffect10(() => () => controller.close(), [controller]);
3513
+ const snapshot = useSyncExternalStore10(controller.subscribe, controller.getSnapshot, controller.getServerSnapshot) ?? EMPTY_SNAPSHOT2;
3257
3514
  return {
3258
3515
  ...snapshot,
3259
3516
  bindHTMX: controller.bindHTMX,
@@ -3267,7 +3524,7 @@ var useVoiceController = (path, options = {}) => {
3267
3524
  };
3268
3525
  };
3269
3526
  // src/react/useVoiceWorkflowStatus.tsx
3270
- import { useEffect as useEffect10, useRef as useRef10, useSyncExternalStore as useSyncExternalStore10 } from "react";
3527
+ import { useEffect as useEffect11, useRef as useRef11, useSyncExternalStore as useSyncExternalStore11 } from "react";
3271
3528
 
3272
3529
  // src/client/workflowStatus.ts
3273
3530
  var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
@@ -3350,23 +3607,24 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
3350
3607
 
3351
3608
  // src/react/useVoiceWorkflowStatus.tsx
3352
3609
  var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
3353
- const storeRef = useRef10(null);
3610
+ const storeRef = useRef11(null);
3354
3611
  if (!storeRef.current) {
3355
3612
  storeRef.current = createVoiceWorkflowStatusStore(path, options);
3356
3613
  }
3357
3614
  const store = storeRef.current;
3358
- useEffect10(() => {
3615
+ useEffect11(() => {
3359
3616
  store.refresh().catch(() => {});
3360
3617
  return () => store.close();
3361
3618
  }, [store]);
3362
3619
  return {
3363
- ...useSyncExternalStore10(store.subscribe, store.getSnapshot, store.getServerSnapshot),
3620
+ ...useSyncExternalStore11(store.subscribe, store.getSnapshot, store.getServerSnapshot),
3364
3621
  refresh: store.refresh
3365
3622
  };
3366
3623
  };
3367
3624
  export {
3368
3625
  useVoiceWorkflowStatus,
3369
3626
  useVoiceTurnQuality,
3627
+ useVoiceTurnLatency,
3370
3628
  useVoiceTraceTimeline,
3371
3629
  useVoiceStream,
3372
3630
  useVoiceRoutingStatus,
@@ -3376,6 +3634,7 @@ export {
3376
3634
  useVoiceController,
3377
3635
  useVoiceAppKitStatus,
3378
3636
  VoiceTurnQuality,
3637
+ VoiceTurnLatency,
3379
3638
  VoiceTraceTimeline,
3380
3639
  VoiceRoutingStatus,
3381
3640
  VoiceProviderStatus,
@@ -0,0 +1,8 @@
1
+ import { type VoiceTurnLatencyClientOptions } from '../client/turnLatency';
2
+ export declare const useVoiceTurnLatency: (path?: string, options?: VoiceTurnLatencyClientOptions) => {
3
+ refresh: () => Promise<import("..").VoiceTurnLatencyReport | undefined>;
4
+ error: string | null;
5
+ isLoading: boolean;
6
+ report?: import("..").VoiceTurnLatencyReport;
7
+ updatedAt?: number;
8
+ };
@@ -0,0 +1,10 @@
1
+ import { type VoiceTurnLatencyWidgetOptions } from '../client/turnLatencyWidget';
2
+ export declare const createVoiceTurnLatency: (path?: string, options?: VoiceTurnLatencyWidgetOptions) => {
3
+ getHTML: () => string;
4
+ getViewModel: () => import("../client").VoiceTurnLatencyViewModel;
5
+ close: () => void;
6
+ getServerSnapshot: () => import("../client").VoiceTurnLatencySnapshot;
7
+ getSnapshot: () => import("../client").VoiceTurnLatencySnapshot;
8
+ refresh: () => Promise<import("..").VoiceTurnLatencyReport | undefined>;
9
+ subscribe: (listener: () => void) => () => void;
10
+ };
@@ -6,6 +6,7 @@ export { createVoiceStream } from './createVoiceStream';
6
6
  export { createVoiceProviderStatus } from './createVoiceProviderStatus';
7
7
  export { createVoiceRoutingStatus } from './createVoiceRoutingStatus';
8
8
  export { createVoiceTraceTimeline } from './createVoiceTraceTimeline';
9
+ export { createVoiceTurnLatency } from './createVoiceTurnLatency';
9
10
  export { createVoiceTurnQuality } from './createVoiceTurnQuality';
10
11
  export { createVoiceWorkflowStatus } from './createVoiceWorkflowStatus';
11
12
  export { createVoiceController } from '../client/controller';