@absolutejs/voice 0.0.22-beta.93 → 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.
package/dist/vue/index.js CHANGED
@@ -1531,9 +1531,253 @@ var VoiceRoutingStatus = defineComponent5({
1531
1531
  ]);
1532
1532
  }
1533
1533
  });
1534
- // src/vue/VoiceTurnQuality.ts
1534
+ // src/vue/VoiceTurnLatency.ts
1535
1535
  import { computed as computed5, defineComponent as defineComponent6, h as h6 } from "vue";
1536
1536
 
1537
+ // src/client/turnLatency.ts
1538
+ var fetchVoiceTurnLatency = async (path = "/api/turn-latency", options = {}) => {
1539
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1540
+ const response = await fetchImpl(path);
1541
+ if (!response.ok) {
1542
+ throw new Error(`Voice turn latency failed: HTTP ${response.status}`);
1543
+ }
1544
+ return await response.json();
1545
+ };
1546
+ var createVoiceTurnLatencyStore = (path = "/api/turn-latency", options = {}) => {
1547
+ const listeners = new Set;
1548
+ let closed = false;
1549
+ let timer;
1550
+ let snapshot = {
1551
+ error: null,
1552
+ isLoading: false
1553
+ };
1554
+ const emit = () => {
1555
+ for (const listener of listeners) {
1556
+ listener();
1557
+ }
1558
+ };
1559
+ const refresh = async () => {
1560
+ if (closed) {
1561
+ return snapshot.report;
1562
+ }
1563
+ snapshot = { ...snapshot, error: null, isLoading: true };
1564
+ emit();
1565
+ try {
1566
+ const report = await fetchVoiceTurnLatency(path, options);
1567
+ snapshot = {
1568
+ error: null,
1569
+ isLoading: false,
1570
+ report,
1571
+ updatedAt: Date.now()
1572
+ };
1573
+ emit();
1574
+ return report;
1575
+ } catch (error) {
1576
+ snapshot = {
1577
+ ...snapshot,
1578
+ error: error instanceof Error ? error.message : String(error),
1579
+ isLoading: false
1580
+ };
1581
+ emit();
1582
+ throw error;
1583
+ }
1584
+ };
1585
+ const close = () => {
1586
+ closed = true;
1587
+ if (timer) {
1588
+ clearInterval(timer);
1589
+ timer = undefined;
1590
+ }
1591
+ listeners.clear();
1592
+ };
1593
+ if (options.intervalMs && options.intervalMs > 0) {
1594
+ timer = setInterval(() => {
1595
+ refresh().catch(() => {});
1596
+ }, options.intervalMs);
1597
+ }
1598
+ return {
1599
+ close,
1600
+ getServerSnapshot: () => snapshot,
1601
+ getSnapshot: () => snapshot,
1602
+ refresh,
1603
+ subscribe: (listener) => {
1604
+ listeners.add(listener);
1605
+ return () => {
1606
+ listeners.delete(listener);
1607
+ };
1608
+ }
1609
+ };
1610
+ };
1611
+
1612
+ // src/client/turnLatencyWidget.ts
1613
+ var DEFAULT_TITLE5 = "Turn Latency";
1614
+ var DEFAULT_DESCRIPTION5 = "Per-turn timing from first transcript to commit and assistant response start.";
1615
+ var escapeHtml6 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1616
+ var formatMs = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
1617
+ var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
1618
+ const turns = (snapshot.report?.turns ?? []).map((turn) => ({
1619
+ ...turn,
1620
+ label: turn.text || "Empty turn",
1621
+ rows: turn.stages.map((stage) => ({
1622
+ label: stage.label,
1623
+ value: formatMs(stage.valueMs)
1624
+ }))
1625
+ }));
1626
+ const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
1627
+ const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
1628
+ return {
1629
+ description: options.description ?? DEFAULT_DESCRIPTION5,
1630
+ error: snapshot.error,
1631
+ isLoading: snapshot.isLoading,
1632
+ label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} slow` : warningCount > 0 ? `${warningCount} warnings` : `avg ${formatMs(snapshot.report?.averageTotalMs)}` : snapshot.isLoading ? "Checking" : "No turns",
1633
+ status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
1634
+ title: options.title ?? DEFAULT_TITLE5,
1635
+ turns,
1636
+ updatedAt: snapshot.updatedAt
1637
+ };
1638
+ };
1639
+ var renderVoiceTurnLatencyHTML = (snapshot, options = {}) => {
1640
+ const model = createVoiceTurnLatencyViewModel(snapshot, options);
1641
+ 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--${escapeHtml6(turn.status)}">
1642
+ <header>
1643
+ <strong>${escapeHtml6(turn.label)}</strong>
1644
+ <span>${escapeHtml6(turn.status)}</span>
1645
+ </header>
1646
+ <dl>${turn.rows.map((row) => `<div>
1647
+ <dt>${escapeHtml6(row.label)}</dt>
1648
+ <dd>${escapeHtml6(row.value)}</dd>
1649
+ </div>`).join("")}</dl>
1650
+ </article>`).join("")}</div>` : '<p class="absolute-voice-turn-latency__empty">Complete a voice turn to see latency diagnostics.</p>';
1651
+ return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml6(model.status)}">
1652
+ <header class="absolute-voice-turn-latency__header">
1653
+ <span class="absolute-voice-turn-latency__eyebrow">${escapeHtml6(model.title)}</span>
1654
+ <strong class="absolute-voice-turn-latency__label">${escapeHtml6(model.label)}</strong>
1655
+ </header>
1656
+ <p class="absolute-voice-turn-latency__description">${escapeHtml6(model.description)}</p>
1657
+ ${turns}
1658
+ ${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml6(model.error)}</p>` : ""}
1659
+ </section>`;
1660
+ };
1661
+ var mountVoiceTurnLatency = (element, path = "/api/turn-latency", options = {}) => {
1662
+ const store = createVoiceTurnLatencyStore(path, options);
1663
+ const render = () => {
1664
+ element.innerHTML = renderVoiceTurnLatencyHTML(store.getSnapshot(), options);
1665
+ };
1666
+ const unsubscribe = store.subscribe(render);
1667
+ render();
1668
+ store.refresh().catch(() => {});
1669
+ return {
1670
+ close: () => {
1671
+ unsubscribe();
1672
+ store.close();
1673
+ },
1674
+ refresh: store.refresh
1675
+ };
1676
+ };
1677
+ var defineVoiceTurnLatencyElement = (tagName = "absolute-voice-turn-latency") => {
1678
+ if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
1679
+ return;
1680
+ }
1681
+ customElements.define(tagName, class AbsoluteVoiceTurnLatencyElement extends HTMLElement {
1682
+ mounted;
1683
+ connectedCallback() {
1684
+ const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
1685
+ this.mounted = mountVoiceTurnLatency(this, this.getAttribute("path") ?? "/api/turn-latency", {
1686
+ description: this.getAttribute("description") ?? undefined,
1687
+ intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
1688
+ title: this.getAttribute("title") ?? undefined
1689
+ });
1690
+ }
1691
+ disconnectedCallback() {
1692
+ this.mounted?.close();
1693
+ this.mounted = undefined;
1694
+ }
1695
+ });
1696
+ };
1697
+
1698
+ // src/vue/useVoiceTurnLatency.ts
1699
+ import { onUnmounted as onUnmounted6, shallowRef as shallowRef5 } from "vue";
1700
+ var useVoiceTurnLatency = (path = "/api/turn-latency", options = {}) => {
1701
+ const store = createVoiceTurnLatencyStore(path, options);
1702
+ const error = shallowRef5(null);
1703
+ const isLoading = shallowRef5(false);
1704
+ const report = shallowRef5();
1705
+ const updatedAt = shallowRef5(undefined);
1706
+ const sync = () => {
1707
+ const snapshot = store.getSnapshot();
1708
+ error.value = snapshot.error;
1709
+ isLoading.value = snapshot.isLoading;
1710
+ report.value = snapshot.report;
1711
+ updatedAt.value = snapshot.updatedAt;
1712
+ };
1713
+ const unsubscribe = store.subscribe(sync);
1714
+ sync();
1715
+ store.refresh().catch(() => {});
1716
+ onUnmounted6(() => {
1717
+ unsubscribe();
1718
+ store.close();
1719
+ });
1720
+ return { error, isLoading, refresh: store.refresh, report, updatedAt };
1721
+ };
1722
+
1723
+ // src/vue/VoiceTurnLatency.ts
1724
+ var VoiceTurnLatency = defineComponent6({
1725
+ name: "VoiceTurnLatency",
1726
+ props: {
1727
+ class: { default: "", type: String },
1728
+ description: { default: undefined, type: String },
1729
+ intervalMs: { default: 5000, type: Number },
1730
+ path: { default: "/api/turn-latency", type: String },
1731
+ title: { default: undefined, type: String }
1732
+ },
1733
+ setup(props) {
1734
+ const options = {
1735
+ description: props.description,
1736
+ intervalMs: props.intervalMs,
1737
+ title: props.title
1738
+ };
1739
+ const latency = useVoiceTurnLatency(props.path, options);
1740
+ const model = computed5(() => createVoiceTurnLatencyViewModel({
1741
+ error: latency.error.value,
1742
+ isLoading: latency.isLoading.value,
1743
+ report: latency.report.value,
1744
+ updatedAt: latency.updatedAt.value
1745
+ }, options));
1746
+ return () => h6("section", {
1747
+ class: [
1748
+ "absolute-voice-turn-latency",
1749
+ `absolute-voice-turn-latency--${model.value.status}`,
1750
+ props.class
1751
+ ]
1752
+ }, [
1753
+ h6("header", { class: "absolute-voice-turn-latency__header" }, [
1754
+ h6("span", { class: "absolute-voice-turn-latency__eyebrow" }, model.value.title),
1755
+ h6("strong", { class: "absolute-voice-turn-latency__label" }, model.value.label)
1756
+ ]),
1757
+ h6("p", { class: "absolute-voice-turn-latency__description" }, model.value.description),
1758
+ model.value.turns.length ? h6("div", { class: "absolute-voice-turn-latency__turns" }, model.value.turns.map((turn) => h6("article", {
1759
+ class: [
1760
+ "absolute-voice-turn-latency__turn",
1761
+ `absolute-voice-turn-latency__turn--${turn.status}`
1762
+ ],
1763
+ key: `${turn.sessionId}:${turn.turnId}`
1764
+ }, [
1765
+ h6("header", [
1766
+ h6("strong", turn.label),
1767
+ h6("span", turn.status)
1768
+ ]),
1769
+ h6("dl", turn.rows.map((row) => h6("div", { key: row.label }, [
1770
+ h6("dt", row.label),
1771
+ h6("dd", row.value)
1772
+ ])))
1773
+ ]))) : h6("p", { class: "absolute-voice-turn-latency__empty" }, "Complete a voice turn to see latency diagnostics."),
1774
+ model.value.error ? h6("p", { class: "absolute-voice-turn-latency__error" }, model.value.error) : null
1775
+ ]);
1776
+ }
1777
+ });
1778
+ // src/vue/VoiceTurnQuality.ts
1779
+ import { computed as computed6, defineComponent as defineComponent7, h as h7 } from "vue";
1780
+
1537
1781
  // src/client/turnQuality.ts
1538
1782
  var fetchVoiceTurnQuality = async (path = "/api/turn-quality", options = {}) => {
1539
1783
  const fetchImpl = options.fetch ?? globalThis.fetch;
@@ -1614,9 +1858,9 @@ var createVoiceTurnQualityStore = (path = "/api/turn-quality", options = {}) =>
1614
1858
  };
1615
1859
 
1616
1860
  // src/client/turnQualityWidget.ts
1617
- var DEFAULT_TITLE5 = "Turn Quality";
1618
- var DEFAULT_DESCRIPTION5 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
1619
- var escapeHtml6 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1861
+ var DEFAULT_TITLE6 = "Turn Quality";
1862
+ var DEFAULT_DESCRIPTION6 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
1863
+ var escapeHtml7 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1620
1864
  var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
1621
1865
  var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
1622
1866
  var getTurnDetail = (turn) => {
@@ -1654,37 +1898,37 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
1654
1898
  const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
1655
1899
  const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
1656
1900
  return {
1657
- description: options.description ?? DEFAULT_DESCRIPTION5,
1901
+ description: options.description ?? DEFAULT_DESCRIPTION6,
1658
1902
  error: snapshot.error,
1659
1903
  isLoading: snapshot.isLoading,
1660
1904
  label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} failed` : warningCount > 0 ? `${warningCount} warnings` : `${turns.length} healthy` : snapshot.isLoading ? "Checking" : "No turns",
1661
1905
  status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
1662
- title: options.title ?? DEFAULT_TITLE5,
1906
+ title: options.title ?? DEFAULT_TITLE6,
1663
1907
  turns,
1664
1908
  updatedAt: snapshot.updatedAt
1665
1909
  };
1666
1910
  };
1667
1911
  var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
1668
1912
  const model = createVoiceTurnQualityViewModel(snapshot, options);
1669
- 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--${escapeHtml6(turn.status)}">
1913
+ 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)}">
1670
1914
  <header>
1671
- <strong>${escapeHtml6(turn.label)}</strong>
1672
- <span>${escapeHtml6(turn.status)}</span>
1915
+ <strong>${escapeHtml7(turn.label)}</strong>
1916
+ <span>${escapeHtml7(turn.status)}</span>
1673
1917
  </header>
1674
- <p>${escapeHtml6(turn.detail)}</p>
1918
+ <p>${escapeHtml7(turn.detail)}</p>
1675
1919
  <dl>${turn.rows.map((row) => `<div>
1676
- <dt>${escapeHtml6(row.label)}</dt>
1677
- <dd>${escapeHtml6(row.value)}</dd>
1920
+ <dt>${escapeHtml7(row.label)}</dt>
1921
+ <dd>${escapeHtml7(row.value)}</dd>
1678
1922
  </div>`).join("")}</dl>
1679
1923
  </article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
1680
- return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml6(model.status)}">
1924
+ return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml7(model.status)}">
1681
1925
  <header class="absolute-voice-turn-quality__header">
1682
- <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml6(model.title)}</span>
1683
- <strong class="absolute-voice-turn-quality__label">${escapeHtml6(model.label)}</strong>
1926
+ <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml7(model.title)}</span>
1927
+ <strong class="absolute-voice-turn-quality__label">${escapeHtml7(model.label)}</strong>
1684
1928
  </header>
1685
- <p class="absolute-voice-turn-quality__description">${escapeHtml6(model.description)}</p>
1929
+ <p class="absolute-voice-turn-quality__description">${escapeHtml7(model.description)}</p>
1686
1930
  ${turns}
1687
- ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml6(model.error)}</p>` : ""}
1931
+ ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml7(model.error)}</p>` : ""}
1688
1932
  </section>`;
1689
1933
  };
1690
1934
  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}`;
@@ -1726,13 +1970,13 @@ var defineVoiceTurnQualityElement = (tagName = "absolute-voice-turn-quality") =>
1726
1970
  };
1727
1971
 
1728
1972
  // src/vue/useVoiceTurnQuality.ts
1729
- import { onUnmounted as onUnmounted6, shallowRef as shallowRef5 } from "vue";
1973
+ import { onUnmounted as onUnmounted7, shallowRef as shallowRef6 } from "vue";
1730
1974
  var useVoiceTurnQuality = (path = "/api/turn-quality", options = {}) => {
1731
1975
  const store = createVoiceTurnQualityStore(path, options);
1732
- const error = shallowRef5(null);
1733
- const isLoading = shallowRef5(false);
1734
- const report = shallowRef5();
1735
- const updatedAt = shallowRef5(undefined);
1976
+ const error = shallowRef6(null);
1977
+ const isLoading = shallowRef6(false);
1978
+ const report = shallowRef6();
1979
+ const updatedAt = shallowRef6(undefined);
1736
1980
  const sync = () => {
1737
1981
  const snapshot = store.getSnapshot();
1738
1982
  error.value = snapshot.error;
@@ -1743,7 +1987,7 @@ var useVoiceTurnQuality = (path = "/api/turn-quality", options = {}) => {
1743
1987
  const unsubscribe = store.subscribe(sync);
1744
1988
  sync();
1745
1989
  store.refresh().catch(() => {});
1746
- onUnmounted6(() => {
1990
+ onUnmounted7(() => {
1747
1991
  unsubscribe();
1748
1992
  store.close();
1749
1993
  });
@@ -1751,7 +1995,7 @@ var useVoiceTurnQuality = (path = "/api/turn-quality", options = {}) => {
1751
1995
  };
1752
1996
 
1753
1997
  // src/vue/VoiceTurnQuality.ts
1754
- var VoiceTurnQuality = defineComponent6({
1998
+ var VoiceTurnQuality = defineComponent7({
1755
1999
  name: "VoiceTurnQuality",
1756
2000
  props: {
1757
2001
  class: { default: "", type: String },
@@ -1767,47 +2011,47 @@ var VoiceTurnQuality = defineComponent6({
1767
2011
  title: props.title
1768
2012
  };
1769
2013
  const quality = useVoiceTurnQuality(props.path, options);
1770
- const model = computed5(() => createVoiceTurnQualityViewModel({
2014
+ const model = computed6(() => createVoiceTurnQualityViewModel({
1771
2015
  error: quality.error.value,
1772
2016
  isLoading: quality.isLoading.value,
1773
2017
  report: quality.report.value,
1774
2018
  updatedAt: quality.updatedAt.value
1775
2019
  }, options));
1776
- return () => h6("section", {
2020
+ return () => h7("section", {
1777
2021
  class: [
1778
2022
  "absolute-voice-turn-quality",
1779
2023
  `absolute-voice-turn-quality--${model.value.status}`,
1780
2024
  props.class
1781
2025
  ]
1782
2026
  }, [
1783
- h6("header", { class: "absolute-voice-turn-quality__header" }, [
1784
- h6("span", { class: "absolute-voice-turn-quality__eyebrow" }, model.value.title),
1785
- h6("strong", { class: "absolute-voice-turn-quality__label" }, model.value.label)
2027
+ h7("header", { class: "absolute-voice-turn-quality__header" }, [
2028
+ h7("span", { class: "absolute-voice-turn-quality__eyebrow" }, model.value.title),
2029
+ h7("strong", { class: "absolute-voice-turn-quality__label" }, model.value.label)
1786
2030
  ]),
1787
- h6("p", { class: "absolute-voice-turn-quality__description" }, model.value.description),
1788
- model.value.turns.length ? h6("div", { class: "absolute-voice-turn-quality__turns" }, model.value.turns.map((turn) => h6("article", {
2031
+ h7("p", { class: "absolute-voice-turn-quality__description" }, model.value.description),
2032
+ model.value.turns.length ? h7("div", { class: "absolute-voice-turn-quality__turns" }, model.value.turns.map((turn) => h7("article", {
1789
2033
  class: [
1790
2034
  "absolute-voice-turn-quality__turn",
1791
2035
  `absolute-voice-turn-quality__turn--${turn.status}`
1792
2036
  ],
1793
2037
  key: `${turn.sessionId}:${turn.turnId}`
1794
2038
  }, [
1795
- h6("header", [
1796
- h6("strong", turn.label),
1797
- h6("span", turn.status)
2039
+ h7("header", [
2040
+ h7("strong", turn.label),
2041
+ h7("span", turn.status)
1798
2042
  ]),
1799
- h6("p", turn.detail),
1800
- h6("dl", turn.rows.map((row) => h6("div", { key: row.label }, [
1801
- h6("dt", row.label),
1802
- h6("dd", row.value)
2043
+ h7("p", turn.detail),
2044
+ h7("dl", turn.rows.map((row) => h7("div", { key: row.label }, [
2045
+ h7("dt", row.label),
2046
+ h7("dd", row.value)
1803
2047
  ])))
1804
- ]))) : h6("p", { class: "absolute-voice-turn-quality__empty" }, "Complete a voice turn to see STT quality diagnostics."),
1805
- model.value.error ? h6("p", { class: "absolute-voice-turn-quality__error" }, model.value.error) : null
2048
+ ]))) : h7("p", { class: "absolute-voice-turn-quality__empty" }, "Complete a voice turn to see STT quality diagnostics."),
2049
+ model.value.error ? h7("p", { class: "absolute-voice-turn-quality__error" }, model.value.error) : null
1806
2050
  ]);
1807
2051
  }
1808
2052
  });
1809
2053
  // src/vue/useVoiceStream.ts
1810
- import { onUnmounted as onUnmounted7, ref as ref5, shallowRef as shallowRef6 } from "vue";
2054
+ import { onUnmounted as onUnmounted8, ref as ref5, shallowRef as shallowRef7 } from "vue";
1811
2055
 
1812
2056
  // src/client/actions.ts
1813
2057
  var normalizeErrorMessage = (value) => {
@@ -2325,15 +2569,15 @@ var createVoiceStream = (path, options = {}) => {
2325
2569
  // src/vue/useVoiceStream.ts
2326
2570
  var useVoiceStream = (path, options = {}) => {
2327
2571
  const stream = createVoiceStream(path, options);
2328
- const assistantAudio = shallowRef6([]);
2329
- const assistantTexts = shallowRef6([]);
2330
- const call = shallowRef6(null);
2572
+ const assistantAudio = shallowRef7([]);
2573
+ const assistantTexts = shallowRef7([]);
2574
+ const call = shallowRef7(null);
2331
2575
  const error = ref5(null);
2332
2576
  const isConnected = ref5(false);
2333
2577
  const partial = ref5("");
2334
2578
  const sessionId = ref5(stream.sessionId);
2335
2579
  const status = ref5(stream.status);
2336
- const turns = shallowRef6([]);
2580
+ const turns = shallowRef7([]);
2337
2581
  const sync = () => {
2338
2582
  assistantAudio.value = [...stream.assistantAudio];
2339
2583
  assistantTexts.value = [...stream.assistantTexts];
@@ -2351,7 +2595,7 @@ var useVoiceStream = (path, options = {}) => {
2351
2595
  unsubscribe();
2352
2596
  stream.close();
2353
2597
  };
2354
- onUnmounted7(destroy);
2598
+ onUnmounted8(destroy);
2355
2599
  return {
2356
2600
  assistantAudio,
2357
2601
  assistantTexts,
@@ -2369,7 +2613,7 @@ var useVoiceStream = (path, options = {}) => {
2369
2613
  };
2370
2614
  };
2371
2615
  // src/vue/useVoiceController.ts
2372
- import { onUnmounted as onUnmounted8, ref as ref6, shallowRef as shallowRef7 } from "vue";
2616
+ import { onUnmounted as onUnmounted9, ref as ref6, shallowRef as shallowRef8 } from "vue";
2373
2617
 
2374
2618
  // src/client/htmx.ts
2375
2619
  var DEFAULT_EVENT_NAME = "voice-refresh";
@@ -3010,8 +3254,8 @@ var createVoiceController = (path, options = {}) => {
3010
3254
  // src/vue/useVoiceController.ts
3011
3255
  var useVoiceController = (path, options = {}) => {
3012
3256
  const controller = createVoiceController(path, options);
3013
- const assistantAudio = shallowRef7([]);
3014
- const assistantTexts = shallowRef7([]);
3257
+ const assistantAudio = shallowRef8([]);
3258
+ const assistantTexts = shallowRef8([]);
3015
3259
  const error = ref6(null);
3016
3260
  const isConnected = ref6(false);
3017
3261
  const isRecording = ref6(false);
@@ -3019,7 +3263,7 @@ var useVoiceController = (path, options = {}) => {
3019
3263
  const recordingError = ref6(null);
3020
3264
  const sessionId = ref6(controller.sessionId);
3021
3265
  const status = ref6(controller.status);
3022
- const turns = shallowRef7([]);
3266
+ const turns = shallowRef8([]);
3023
3267
  const sync = () => {
3024
3268
  assistantAudio.value = [...controller.assistantAudio];
3025
3269
  assistantTexts.value = [...controller.assistantTexts];
@@ -3038,7 +3282,7 @@ var useVoiceController = (path, options = {}) => {
3038
3282
  unsubscribe();
3039
3283
  controller.close();
3040
3284
  };
3041
- onUnmounted8(destroy);
3285
+ onUnmounted9(destroy);
3042
3286
  return {
3043
3287
  assistantAudio,
3044
3288
  assistantTexts,
@@ -3060,7 +3304,7 @@ var useVoiceController = (path, options = {}) => {
3060
3304
  };
3061
3305
  };
3062
3306
  // src/vue/useVoiceTraceTimeline.ts
3063
- import { onUnmounted as onUnmounted9, ref as ref7, shallowRef as shallowRef8 } from "vue";
3307
+ import { onUnmounted as onUnmounted10, ref as ref7, shallowRef as shallowRef9 } from "vue";
3064
3308
 
3065
3309
  // src/client/traceTimeline.ts
3066
3310
  var fetchVoiceTraceTimeline = async (path = "/api/voice-traces", options = {}) => {
@@ -3147,7 +3391,7 @@ var useVoiceTraceTimeline = (path = "/api/voice-traces", options = {}) => {
3147
3391
  const store = createVoiceTraceTimelineStore(path, options);
3148
3392
  const error = ref7(null);
3149
3393
  const isLoading = ref7(false);
3150
- const report = shallowRef8(null);
3394
+ const report = shallowRef9(null);
3151
3395
  const updatedAt = ref7(undefined);
3152
3396
  const sync = () => {
3153
3397
  const snapshot = store.getSnapshot();
@@ -3159,7 +3403,7 @@ var useVoiceTraceTimeline = (path = "/api/voice-traces", options = {}) => {
3159
3403
  const unsubscribe = store.subscribe(sync);
3160
3404
  sync();
3161
3405
  store.refresh().catch(() => {});
3162
- onUnmounted9(() => {
3406
+ onUnmounted10(() => {
3163
3407
  unsubscribe();
3164
3408
  store.close();
3165
3409
  });
@@ -3172,7 +3416,7 @@ var useVoiceTraceTimeline = (path = "/api/voice-traces", options = {}) => {
3172
3416
  };
3173
3417
  };
3174
3418
  // src/vue/useVoiceWorkflowStatus.ts
3175
- import { onUnmounted as onUnmounted10, ref as ref8, shallowRef as shallowRef9 } from "vue";
3419
+ import { onUnmounted as onUnmounted11, ref as ref8, shallowRef as shallowRef10 } from "vue";
3176
3420
 
3177
3421
  // src/client/workflowStatus.ts
3178
3422
  var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
@@ -3258,7 +3502,7 @@ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
3258
3502
  const store = createVoiceWorkflowStatusStore(path, options);
3259
3503
  const error = ref8(null);
3260
3504
  const isLoading = ref8(false);
3261
- const report = shallowRef9(undefined);
3505
+ const report = shallowRef10(undefined);
3262
3506
  const updatedAt = ref8(undefined);
3263
3507
  const sync = () => {
3264
3508
  const snapshot = store.getSnapshot();
@@ -3272,7 +3516,7 @@ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
3272
3516
  if (typeof window !== "undefined") {
3273
3517
  store.refresh().catch(() => {});
3274
3518
  }
3275
- onUnmounted10(() => {
3519
+ onUnmounted11(() => {
3276
3520
  unsubscribe();
3277
3521
  store.close();
3278
3522
  });
@@ -3287,6 +3531,7 @@ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
3287
3531
  export {
3288
3532
  useVoiceWorkflowStatus,
3289
3533
  useVoiceTurnQuality,
3534
+ useVoiceTurnLatency,
3290
3535
  useVoiceTraceTimeline,
3291
3536
  useVoiceStream,
3292
3537
  useVoiceRoutingStatus,
@@ -3296,6 +3541,7 @@ export {
3296
3541
  useVoiceController,
3297
3542
  useVoiceAppKitStatus,
3298
3543
  VoiceTurnQuality,
3544
+ VoiceTurnLatency,
3299
3545
  VoiceRoutingStatus,
3300
3546
  VoiceProviderStatus,
3301
3547
  VoiceProviderSimulationControls,
@@ -0,0 +1,9 @@
1
+ import { type VoiceTurnLatencyClientOptions } from '../client/turnLatency';
2
+ import type { VoiceTurnLatencyReport } from '../turnLatency';
3
+ export declare const useVoiceTurnLatency: (path?: string, options?: VoiceTurnLatencyClientOptions) => {
4
+ error: import("vue").ShallowRef<string | null, string | null>;
5
+ isLoading: import("vue").ShallowRef<boolean, boolean>;
6
+ refresh: () => Promise<VoiceTurnLatencyReport | undefined>;
7
+ report: import("vue").ShallowRef<VoiceTurnLatencyReport | undefined, VoiceTurnLatencyReport | undefined>;
8
+ updatedAt: import("vue").ShallowRef<number | undefined, number | undefined>;
9
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.93",
3
+ "version": "0.0.22-beta.95",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",