@absolutejs/voice 0.0.22-beta.90 → 0.0.22-beta.91

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.
@@ -1495,8 +1495,272 @@ var VoiceRoutingStatus = ({
1495
1495
  ]
1496
1496
  }, undefined, true, undefined, this);
1497
1497
  };
1498
- // src/react/useVoiceTurnQuality.tsx
1498
+ // src/client/traceTimeline.ts
1499
+ var fetchVoiceTraceTimeline = async (path = "/api/voice-traces", options = {}) => {
1500
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1501
+ const response = await fetchImpl(path);
1502
+ if (!response.ok) {
1503
+ throw new Error(`Voice trace timeline failed: HTTP ${response.status}`);
1504
+ }
1505
+ return await response.json();
1506
+ };
1507
+ var createVoiceTraceTimelineStore = (path = "/api/voice-traces", options = {}) => {
1508
+ const listeners = new Set;
1509
+ let closed = false;
1510
+ let timer;
1511
+ let snapshot = {
1512
+ error: null,
1513
+ isLoading: false,
1514
+ report: null
1515
+ };
1516
+ const emit = () => {
1517
+ for (const listener of listeners) {
1518
+ listener();
1519
+ }
1520
+ };
1521
+ const refresh = async () => {
1522
+ if (closed) {
1523
+ return snapshot.report;
1524
+ }
1525
+ snapshot = {
1526
+ ...snapshot,
1527
+ error: null,
1528
+ isLoading: true
1529
+ };
1530
+ emit();
1531
+ try {
1532
+ const report = await fetchVoiceTraceTimeline(path, options);
1533
+ snapshot = {
1534
+ error: null,
1535
+ isLoading: false,
1536
+ report,
1537
+ updatedAt: Date.now()
1538
+ };
1539
+ emit();
1540
+ return report;
1541
+ } catch (error) {
1542
+ snapshot = {
1543
+ ...snapshot,
1544
+ error: error instanceof Error ? error.message : String(error),
1545
+ isLoading: false
1546
+ };
1547
+ emit();
1548
+ throw error;
1549
+ }
1550
+ };
1551
+ const close = () => {
1552
+ closed = true;
1553
+ if (timer) {
1554
+ clearInterval(timer);
1555
+ timer = undefined;
1556
+ }
1557
+ listeners.clear();
1558
+ };
1559
+ if (options.intervalMs && options.intervalMs > 0) {
1560
+ timer = setInterval(() => {
1561
+ refresh().catch(() => {});
1562
+ }, options.intervalMs);
1563
+ }
1564
+ return {
1565
+ close,
1566
+ getServerSnapshot: () => snapshot,
1567
+ getSnapshot: () => snapshot,
1568
+ refresh,
1569
+ subscribe: (listener) => {
1570
+ listeners.add(listener);
1571
+ return () => {
1572
+ listeners.delete(listener);
1573
+ };
1574
+ }
1575
+ };
1576
+ };
1577
+
1578
+ // src/client/traceTimelineWidget.ts
1579
+ var DEFAULT_TITLE5 = "Voice Traces";
1580
+ var DEFAULT_DESCRIPTION5 = "Latest call timelines with provider latency, fallbacks, handoffs, and errors from your self-hosted trace store.";
1581
+ var escapeHtml6 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1582
+ var formatMs = (value) => typeof value === "number" ? `${value}ms` : "n/a";
1583
+ var formatProviders = (session) => session.providers.length ? session.providers.map((provider) => provider.provider).join(", ") : "No providers";
1584
+ var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
1585
+ const sessions = (snapshot.report?.sessions ?? []).slice(0, options.limit ?? 3).map((session) => ({
1586
+ ...session,
1587
+ detailHref: `${options.detailBasePath ?? "/traces"}/${encodeURIComponent(session.sessionId)}`,
1588
+ durationLabel: formatMs(session.summary.callDurationMs),
1589
+ label: `${session.summary.eventCount} events / ${session.summary.turnCount} turns`,
1590
+ providerLabel: formatProviders(session)
1591
+ }));
1592
+ const failed = sessions.filter((session) => session.status === "failed").length;
1593
+ const warnings = sessions.filter((session) => session.status === "warning").length;
1594
+ return {
1595
+ description: options.description ?? DEFAULT_DESCRIPTION5,
1596
+ error: snapshot.error,
1597
+ isLoading: snapshot.isLoading,
1598
+ label: snapshot.error ? "Unavailable" : failed > 0 ? `${failed} failed` : warnings > 0 ? `${warnings} warning` : sessions.length ? `${sessions.length} recent` : snapshot.isLoading ? "Checking" : "No traces yet",
1599
+ sessions,
1600
+ status: snapshot.error ? "error" : failed > 0 ? "failed" : warnings > 0 ? "warning" : sessions.length ? "ready" : snapshot.isLoading ? "loading" : "empty",
1601
+ title: options.title ?? DEFAULT_TITLE5,
1602
+ updatedAt: snapshot.updatedAt
1603
+ };
1604
+ };
1605
+ var renderVoiceTraceTimelineWidgetHTML = (snapshot, options = {}) => {
1606
+ const model = createVoiceTraceTimelineViewModel(snapshot, options);
1607
+ const sessions = model.sessions.length ? `<div class="absolute-voice-trace-timeline__sessions">${model.sessions.map((session) => `<article class="absolute-voice-trace-timeline__session absolute-voice-trace-timeline__session--${escapeHtml6(session.status)}">
1608
+ <header>
1609
+ <strong>${escapeHtml6(session.sessionId)}</strong>
1610
+ <span>${escapeHtml6(session.status)}</span>
1611
+ </header>
1612
+ <p>${escapeHtml6(session.label)} \xB7 ${escapeHtml6(session.durationLabel)} \xB7 ${escapeHtml6(session.providerLabel)}</p>
1613
+ <a href="${escapeHtml6(session.detailHref)}">Open timeline</a>
1614
+ </article>`).join("")}</div>` : '<p class="absolute-voice-trace-timeline__empty">Run a voice session to see call timelines.</p>';
1615
+ return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml6(model.status)}">
1616
+ <header class="absolute-voice-trace-timeline__header">
1617
+ <span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml6(model.title)}</span>
1618
+ <strong class="absolute-voice-trace-timeline__label">${escapeHtml6(model.label)}</strong>
1619
+ </header>
1620
+ <p class="absolute-voice-trace-timeline__description">${escapeHtml6(model.description)}</p>
1621
+ ${sessions}
1622
+ ${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml6(model.error)}</p>` : ""}
1623
+ </section>`;
1624
+ };
1625
+ var getVoiceTraceTimelineCSS = () => `.absolute-voice-trace-timeline{border:1px solid #bad7d3;border-radius:20px;background:#f3fffb;color:#09201c;padding:18px;box-shadow:0 18px 40px rgba(9,32,28,.12);font-family:inherit}.absolute-voice-trace-timeline--error,.absolute-voice-trace-timeline--failed{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-trace-timeline--warning{border-color:#fbbf24;background:#fffaf0}.absolute-voice-trace-timeline__header,.absolute-voice-trace-timeline__session header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-trace-timeline__eyebrow{color:#17665b;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-trace-timeline__label{font-size:24px;line-height:1}.absolute-voice-trace-timeline__description,.absolute-voice-trace-timeline__session p,.absolute-voice-trace-timeline__empty{color:#35544f}.absolute-voice-trace-timeline__sessions{display:grid;gap:12px;margin-top:14px}.absolute-voice-trace-timeline__session{background:#fff;border:1px solid #cfe7e2;border-radius:16px;padding:14px}.absolute-voice-trace-timeline__session--failed{border-color:#f2a7a7}.absolute-voice-trace-timeline__session--warning{border-color:#fbbf24}.absolute-voice-trace-timeline__session p{margin:10px 0}.absolute-voice-trace-timeline__session a{color:#0f766e;font-weight:800}.absolute-voice-trace-timeline__empty{margin:14px 0 0}.absolute-voice-trace-timeline__error{color:#9f1239;font-weight:700}`;
1626
+ var mountVoiceTraceTimeline = (element, path = "/api/voice-traces", options = {}) => {
1627
+ const store = createVoiceTraceTimelineStore(path, options);
1628
+ const render = () => {
1629
+ element.innerHTML = renderVoiceTraceTimelineWidgetHTML(store.getSnapshot(), options);
1630
+ };
1631
+ const unsubscribe = store.subscribe(render);
1632
+ render();
1633
+ store.refresh().catch(() => {});
1634
+ return {
1635
+ close: () => {
1636
+ unsubscribe();
1637
+ store.close();
1638
+ },
1639
+ refresh: store.refresh
1640
+ };
1641
+ };
1642
+ var defineVoiceTraceTimelineElement = (tagName = "absolute-voice-trace-timeline") => {
1643
+ if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
1644
+ return;
1645
+ }
1646
+ customElements.define(tagName, class AbsoluteVoiceTraceTimelineElement extends HTMLElement {
1647
+ mounted;
1648
+ connectedCallback() {
1649
+ const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
1650
+ const limit = Number(this.getAttribute("limit") ?? 3);
1651
+ this.mounted = mountVoiceTraceTimeline(this, this.getAttribute("path") ?? "/api/voice-traces", {
1652
+ description: this.getAttribute("description") ?? undefined,
1653
+ detailBasePath: this.getAttribute("detail-base-path") ?? undefined,
1654
+ intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
1655
+ limit: Number.isFinite(limit) ? limit : 3,
1656
+ title: this.getAttribute("title") ?? undefined
1657
+ });
1658
+ }
1659
+ disconnectedCallback() {
1660
+ this.mounted?.close();
1661
+ this.mounted = undefined;
1662
+ }
1663
+ });
1664
+ };
1665
+
1666
+ // src/react/useVoiceTraceTimeline.tsx
1499
1667
  import { useEffect as useEffect6, useRef as useRef6, useSyncExternalStore as useSyncExternalStore6 } from "react";
1668
+ var useVoiceTraceTimeline = (path = "/api/voice-traces", options = {}) => {
1669
+ const storeRef = useRef6(null);
1670
+ if (!storeRef.current) {
1671
+ storeRef.current = createVoiceTraceTimelineStore(path, options);
1672
+ }
1673
+ const store = storeRef.current;
1674
+ useEffect6(() => {
1675
+ store.refresh().catch(() => {});
1676
+ return () => store.close();
1677
+ }, [store]);
1678
+ return {
1679
+ ...useSyncExternalStore6(store.subscribe, store.getSnapshot, store.getServerSnapshot),
1680
+ refresh: store.refresh
1681
+ };
1682
+ };
1683
+
1684
+ // src/react/VoiceTraceTimeline.tsx
1685
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
1686
+ var VoiceTraceTimeline = ({
1687
+ className,
1688
+ path = "/api/voice-traces",
1689
+ ...options
1690
+ }) => {
1691
+ const snapshot = useVoiceTraceTimeline(path, options);
1692
+ const model = createVoiceTraceTimelineViewModel(snapshot, options);
1693
+ return /* @__PURE__ */ jsxDEV6("section", {
1694
+ className: [
1695
+ "absolute-voice-trace-timeline",
1696
+ `absolute-voice-trace-timeline--${model.status}`,
1697
+ className
1698
+ ].filter(Boolean).join(" "),
1699
+ children: [
1700
+ /* @__PURE__ */ jsxDEV6("header", {
1701
+ className: "absolute-voice-trace-timeline__header",
1702
+ children: [
1703
+ /* @__PURE__ */ jsxDEV6("span", {
1704
+ className: "absolute-voice-trace-timeline__eyebrow",
1705
+ children: model.title
1706
+ }, undefined, false, undefined, this),
1707
+ /* @__PURE__ */ jsxDEV6("strong", {
1708
+ className: "absolute-voice-trace-timeline__label",
1709
+ children: model.label
1710
+ }, undefined, false, undefined, this)
1711
+ ]
1712
+ }, undefined, true, undefined, this),
1713
+ /* @__PURE__ */ jsxDEV6("p", {
1714
+ className: "absolute-voice-trace-timeline__description",
1715
+ children: model.description
1716
+ }, undefined, false, undefined, this),
1717
+ model.sessions.length ? /* @__PURE__ */ jsxDEV6("div", {
1718
+ className: "absolute-voice-trace-timeline__sessions",
1719
+ children: model.sessions.map((session) => /* @__PURE__ */ jsxDEV6("article", {
1720
+ className: [
1721
+ "absolute-voice-trace-timeline__session",
1722
+ `absolute-voice-trace-timeline__session--${session.status}`
1723
+ ].join(" "),
1724
+ children: [
1725
+ /* @__PURE__ */ jsxDEV6("header", {
1726
+ children: [
1727
+ /* @__PURE__ */ jsxDEV6("strong", {
1728
+ children: session.sessionId
1729
+ }, undefined, false, undefined, this),
1730
+ /* @__PURE__ */ jsxDEV6("span", {
1731
+ children: session.status
1732
+ }, undefined, false, undefined, this)
1733
+ ]
1734
+ }, undefined, true, undefined, this),
1735
+ /* @__PURE__ */ jsxDEV6("p", {
1736
+ children: [
1737
+ session.label,
1738
+ " \xB7 ",
1739
+ session.durationLabel,
1740
+ " \xB7",
1741
+ " ",
1742
+ session.providerLabel
1743
+ ]
1744
+ }, undefined, true, undefined, this),
1745
+ /* @__PURE__ */ jsxDEV6("a", {
1746
+ href: session.detailHref,
1747
+ children: "Open timeline"
1748
+ }, undefined, false, undefined, this)
1749
+ ]
1750
+ }, session.sessionId, true, undefined, this))
1751
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV6("p", {
1752
+ className: "absolute-voice-trace-timeline__empty",
1753
+ children: "Run a voice session to see call timelines."
1754
+ }, undefined, false, undefined, this),
1755
+ model.error ? /* @__PURE__ */ jsxDEV6("p", {
1756
+ className: "absolute-voice-trace-timeline__error",
1757
+ children: model.error
1758
+ }, undefined, false, undefined, this) : null
1759
+ ]
1760
+ }, undefined, true, undefined, this);
1761
+ };
1762
+ // src/react/useVoiceTurnQuality.tsx
1763
+ import { useEffect as useEffect7, useRef as useRef7, useSyncExternalStore as useSyncExternalStore7 } from "react";
1500
1764
 
1501
1765
  // src/client/turnQuality.ts
1502
1766
  var fetchVoiceTurnQuality = async (path = "/api/turn-quality", options = {}) => {
@@ -1579,25 +1843,25 @@ var createVoiceTurnQualityStore = (path = "/api/turn-quality", options = {}) =>
1579
1843
 
1580
1844
  // src/react/useVoiceTurnQuality.tsx
1581
1845
  var useVoiceTurnQuality = (path = "/api/turn-quality", options = {}) => {
1582
- const storeRef = useRef6(null);
1846
+ const storeRef = useRef7(null);
1583
1847
  if (!storeRef.current) {
1584
1848
  storeRef.current = createVoiceTurnQualityStore(path, options);
1585
1849
  }
1586
1850
  const store = storeRef.current;
1587
- useEffect6(() => {
1851
+ useEffect7(() => {
1588
1852
  store.refresh().catch(() => {});
1589
1853
  return () => store.close();
1590
1854
  }, [store]);
1591
1855
  return {
1592
- ...useSyncExternalStore6(store.subscribe, store.getSnapshot, store.getServerSnapshot),
1856
+ ...useSyncExternalStore7(store.subscribe, store.getSnapshot, store.getServerSnapshot),
1593
1857
  refresh: store.refresh
1594
1858
  };
1595
1859
  };
1596
1860
 
1597
1861
  // src/client/turnQualityWidget.ts
1598
- var DEFAULT_TITLE5 = "Turn Quality";
1599
- var DEFAULT_DESCRIPTION5 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
1600
- var escapeHtml6 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
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;");
1601
1865
  var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
1602
1866
  var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
1603
1867
  var getTurnDetail = (turn) => {
@@ -1635,37 +1899,37 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
1635
1899
  const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
1636
1900
  const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
1637
1901
  return {
1638
- description: options.description ?? DEFAULT_DESCRIPTION5,
1902
+ description: options.description ?? DEFAULT_DESCRIPTION6,
1639
1903
  error: snapshot.error,
1640
1904
  isLoading: snapshot.isLoading,
1641
1905
  label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} failed` : warningCount > 0 ? `${warningCount} warnings` : `${turns.length} healthy` : snapshot.isLoading ? "Checking" : "No turns",
1642
1906
  status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
1643
- title: options.title ?? DEFAULT_TITLE5,
1907
+ title: options.title ?? DEFAULT_TITLE6,
1644
1908
  turns,
1645
1909
  updatedAt: snapshot.updatedAt
1646
1910
  };
1647
1911
  };
1648
1912
  var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
1649
1913
  const model = createVoiceTurnQualityViewModel(snapshot, options);
1650
- 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)}">
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)}">
1651
1915
  <header>
1652
- <strong>${escapeHtml6(turn.label)}</strong>
1653
- <span>${escapeHtml6(turn.status)}</span>
1916
+ <strong>${escapeHtml7(turn.label)}</strong>
1917
+ <span>${escapeHtml7(turn.status)}</span>
1654
1918
  </header>
1655
- <p>${escapeHtml6(turn.detail)}</p>
1919
+ <p>${escapeHtml7(turn.detail)}</p>
1656
1920
  <dl>${turn.rows.map((row) => `<div>
1657
- <dt>${escapeHtml6(row.label)}</dt>
1658
- <dd>${escapeHtml6(row.value)}</dd>
1921
+ <dt>${escapeHtml7(row.label)}</dt>
1922
+ <dd>${escapeHtml7(row.value)}</dd>
1659
1923
  </div>`).join("")}</dl>
1660
1924
  </article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
1661
- return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml6(model.status)}">
1925
+ return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml7(model.status)}">
1662
1926
  <header class="absolute-voice-turn-quality__header">
1663
- <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml6(model.title)}</span>
1664
- <strong class="absolute-voice-turn-quality__label">${escapeHtml6(model.label)}</strong>
1927
+ <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml7(model.title)}</span>
1928
+ <strong class="absolute-voice-turn-quality__label">${escapeHtml7(model.label)}</strong>
1665
1929
  </header>
1666
- <p class="absolute-voice-turn-quality__description">${escapeHtml6(model.description)}</p>
1930
+ <p class="absolute-voice-turn-quality__description">${escapeHtml7(model.description)}</p>
1667
1931
  ${turns}
1668
- ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml6(model.error)}</p>` : ""}
1932
+ ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml7(model.error)}</p>` : ""}
1669
1933
  </section>`;
1670
1934
  };
1671
1935
  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}`;
@@ -1707,7 +1971,7 @@ var defineVoiceTurnQualityElement = (tagName = "absolute-voice-turn-quality") =>
1707
1971
  };
1708
1972
 
1709
1973
  // src/react/VoiceTurnQuality.tsx
1710
- import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
1974
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
1711
1975
  var VoiceTurnQuality = ({
1712
1976
  className,
1713
1977
  path = "/api/turn-quality",
@@ -1715,58 +1979,58 @@ var VoiceTurnQuality = ({
1715
1979
  }) => {
1716
1980
  const snapshot = useVoiceTurnQuality(path, options);
1717
1981
  const model = createVoiceTurnQualityViewModel(snapshot, options);
1718
- return /* @__PURE__ */ jsxDEV6("section", {
1982
+ return /* @__PURE__ */ jsxDEV7("section", {
1719
1983
  className: [
1720
1984
  "absolute-voice-turn-quality",
1721
1985
  `absolute-voice-turn-quality--${model.status}`,
1722
1986
  className
1723
1987
  ].filter(Boolean).join(" "),
1724
1988
  children: [
1725
- /* @__PURE__ */ jsxDEV6("header", {
1989
+ /* @__PURE__ */ jsxDEV7("header", {
1726
1990
  className: "absolute-voice-turn-quality__header",
1727
1991
  children: [
1728
- /* @__PURE__ */ jsxDEV6("span", {
1992
+ /* @__PURE__ */ jsxDEV7("span", {
1729
1993
  className: "absolute-voice-turn-quality__eyebrow",
1730
1994
  children: model.title
1731
1995
  }, undefined, false, undefined, this),
1732
- /* @__PURE__ */ jsxDEV6("strong", {
1996
+ /* @__PURE__ */ jsxDEV7("strong", {
1733
1997
  className: "absolute-voice-turn-quality__label",
1734
1998
  children: model.label
1735
1999
  }, undefined, false, undefined, this)
1736
2000
  ]
1737
2001
  }, undefined, true, undefined, this),
1738
- /* @__PURE__ */ jsxDEV6("p", {
2002
+ /* @__PURE__ */ jsxDEV7("p", {
1739
2003
  className: "absolute-voice-turn-quality__description",
1740
2004
  children: model.description
1741
2005
  }, undefined, false, undefined, this),
1742
- model.turns.length ? /* @__PURE__ */ jsxDEV6("div", {
2006
+ model.turns.length ? /* @__PURE__ */ jsxDEV7("div", {
1743
2007
  className: "absolute-voice-turn-quality__turns",
1744
- children: model.turns.map((turn) => /* @__PURE__ */ jsxDEV6("article", {
2008
+ children: model.turns.map((turn) => /* @__PURE__ */ jsxDEV7("article", {
1745
2009
  className: [
1746
2010
  "absolute-voice-turn-quality__turn",
1747
2011
  `absolute-voice-turn-quality__turn--${turn.status}`
1748
2012
  ].join(" "),
1749
2013
  children: [
1750
- /* @__PURE__ */ jsxDEV6("header", {
2014
+ /* @__PURE__ */ jsxDEV7("header", {
1751
2015
  children: [
1752
- /* @__PURE__ */ jsxDEV6("strong", {
2016
+ /* @__PURE__ */ jsxDEV7("strong", {
1753
2017
  children: turn.label
1754
2018
  }, undefined, false, undefined, this),
1755
- /* @__PURE__ */ jsxDEV6("span", {
2019
+ /* @__PURE__ */ jsxDEV7("span", {
1756
2020
  children: turn.status
1757
2021
  }, undefined, false, undefined, this)
1758
2022
  ]
1759
2023
  }, undefined, true, undefined, this),
1760
- /* @__PURE__ */ jsxDEV6("p", {
2024
+ /* @__PURE__ */ jsxDEV7("p", {
1761
2025
  children: turn.detail
1762
2026
  }, undefined, false, undefined, this),
1763
- /* @__PURE__ */ jsxDEV6("dl", {
1764
- children: turn.rows.map((row) => /* @__PURE__ */ jsxDEV6("div", {
2027
+ /* @__PURE__ */ jsxDEV7("dl", {
2028
+ children: turn.rows.map((row) => /* @__PURE__ */ jsxDEV7("div", {
1765
2029
  children: [
1766
- /* @__PURE__ */ jsxDEV6("dt", {
2030
+ /* @__PURE__ */ jsxDEV7("dt", {
1767
2031
  children: row.label
1768
2032
  }, undefined, false, undefined, this),
1769
- /* @__PURE__ */ jsxDEV6("dd", {
2033
+ /* @__PURE__ */ jsxDEV7("dd", {
1770
2034
  children: row.value
1771
2035
  }, undefined, false, undefined, this)
1772
2036
  ]
@@ -1774,11 +2038,11 @@ var VoiceTurnQuality = ({
1774
2038
  }, undefined, false, undefined, this)
1775
2039
  ]
1776
2040
  }, `${turn.sessionId}:${turn.turnId}`, true, undefined, this))
1777
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV6("p", {
2041
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV7("p", {
1778
2042
  className: "absolute-voice-turn-quality__empty",
1779
2043
  children: "Complete a voice turn to see STT quality diagnostics."
1780
2044
  }, undefined, false, undefined, this),
1781
- model.error ? /* @__PURE__ */ jsxDEV6("p", {
2045
+ model.error ? /* @__PURE__ */ jsxDEV7("p", {
1782
2046
  className: "absolute-voice-turn-quality__error",
1783
2047
  children: model.error
1784
2048
  }, undefined, false, undefined, this) : null
@@ -1786,7 +2050,7 @@ var VoiceTurnQuality = ({
1786
2050
  }, undefined, true, undefined, this);
1787
2051
  };
1788
2052
  // src/react/useVoiceStream.tsx
1789
- import { useEffect as useEffect7, useRef as useRef7, useSyncExternalStore as useSyncExternalStore7 } from "react";
2053
+ import { useEffect as useEffect8, useRef as useRef8, useSyncExternalStore as useSyncExternalStore8 } from "react";
1790
2054
 
1791
2055
  // src/client/actions.ts
1792
2056
  var normalizeErrorMessage = (value) => {
@@ -2314,13 +2578,13 @@ var EMPTY_SNAPSHOT = {
2314
2578
  turns: []
2315
2579
  };
2316
2580
  var useVoiceStream = (path, options = {}) => {
2317
- const streamRef = useRef7(null);
2581
+ const streamRef = useRef8(null);
2318
2582
  if (!streamRef.current) {
2319
2583
  streamRef.current = createVoiceStream(path, options);
2320
2584
  }
2321
2585
  const stream = streamRef.current;
2322
- useEffect7(() => () => stream.close(), [stream]);
2323
- const snapshot = useSyncExternalStore7(stream.subscribe, stream.getSnapshot, stream.getServerSnapshot) ?? EMPTY_SNAPSHOT;
2586
+ useEffect8(() => () => stream.close(), [stream]);
2587
+ const snapshot = useSyncExternalStore8(stream.subscribe, stream.getSnapshot, stream.getServerSnapshot) ?? EMPTY_SNAPSHOT;
2324
2588
  return {
2325
2589
  ...snapshot,
2326
2590
  callControl: (message) => stream.callControl(message),
@@ -2330,7 +2594,7 @@ var useVoiceStream = (path, options = {}) => {
2330
2594
  };
2331
2595
  };
2332
2596
  // src/react/useVoiceController.tsx
2333
- import { useEffect as useEffect8, useRef as useRef8, useSyncExternalStore as useSyncExternalStore8 } from "react";
2597
+ import { useEffect as useEffect9, useRef as useRef9, useSyncExternalStore as useSyncExternalStore9 } from "react";
2334
2598
 
2335
2599
  // src/client/htmx.ts
2336
2600
  var DEFAULT_EVENT_NAME = "voice-refresh";
@@ -2977,13 +3241,13 @@ var EMPTY_SNAPSHOT2 = {
2977
3241
  turns: []
2978
3242
  };
2979
3243
  var useVoiceController = (path, options = {}) => {
2980
- const controllerRef = useRef8(null);
3244
+ const controllerRef = useRef9(null);
2981
3245
  if (!controllerRef.current) {
2982
3246
  controllerRef.current = createVoiceController(path, options);
2983
3247
  }
2984
3248
  const controller = controllerRef.current;
2985
- useEffect8(() => () => controller.close(), [controller]);
2986
- const snapshot = useSyncExternalStore8(controller.subscribe, controller.getSnapshot, controller.getServerSnapshot) ?? EMPTY_SNAPSHOT2;
3249
+ useEffect9(() => () => controller.close(), [controller]);
3250
+ const snapshot = useSyncExternalStore9(controller.subscribe, controller.getSnapshot, controller.getServerSnapshot) ?? EMPTY_SNAPSHOT2;
2987
3251
  return {
2988
3252
  ...snapshot,
2989
3253
  bindHTMX: controller.bindHTMX,
@@ -2997,7 +3261,7 @@ var useVoiceController = (path, options = {}) => {
2997
3261
  };
2998
3262
  };
2999
3263
  // src/react/useVoiceWorkflowStatus.tsx
3000
- import { useEffect as useEffect9, useRef as useRef9, useSyncExternalStore as useSyncExternalStore9 } from "react";
3264
+ import { useEffect as useEffect10, useRef as useRef10, useSyncExternalStore as useSyncExternalStore10 } from "react";
3001
3265
 
3002
3266
  // src/client/workflowStatus.ts
3003
3267
  var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
@@ -3080,23 +3344,24 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
3080
3344
 
3081
3345
  // src/react/useVoiceWorkflowStatus.tsx
3082
3346
  var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
3083
- const storeRef = useRef9(null);
3347
+ const storeRef = useRef10(null);
3084
3348
  if (!storeRef.current) {
3085
3349
  storeRef.current = createVoiceWorkflowStatusStore(path, options);
3086
3350
  }
3087
3351
  const store = storeRef.current;
3088
- useEffect9(() => {
3352
+ useEffect10(() => {
3089
3353
  store.refresh().catch(() => {});
3090
3354
  return () => store.close();
3091
3355
  }, [store]);
3092
3356
  return {
3093
- ...useSyncExternalStore9(store.subscribe, store.getSnapshot, store.getServerSnapshot),
3357
+ ...useSyncExternalStore10(store.subscribe, store.getSnapshot, store.getServerSnapshot),
3094
3358
  refresh: store.refresh
3095
3359
  };
3096
3360
  };
3097
3361
  export {
3098
3362
  useVoiceWorkflowStatus,
3099
3363
  useVoiceTurnQuality,
3364
+ useVoiceTraceTimeline,
3100
3365
  useVoiceStream,
3101
3366
  useVoiceRoutingStatus,
3102
3367
  useVoiceProviderStatus,
@@ -3105,6 +3370,7 @@ export {
3105
3370
  useVoiceController,
3106
3371
  useVoiceAppKitStatus,
3107
3372
  VoiceTurnQuality,
3373
+ VoiceTraceTimeline,
3108
3374
  VoiceRoutingStatus,
3109
3375
  VoiceProviderStatus,
3110
3376
  VoiceProviderSimulationControls,
@@ -0,0 +1,8 @@
1
+ import { type VoiceTraceTimelineClientOptions } from '../client/traceTimeline';
2
+ export declare const useVoiceTraceTimeline: (path?: string, options?: VoiceTraceTimelineClientOptions) => {
3
+ refresh: () => Promise<import("..").VoiceTraceTimelineReport | null>;
4
+ error: string | null;
5
+ isLoading: boolean;
6
+ report: import("..").VoiceTraceTimelineReport | null;
7
+ updatedAt?: number;
8
+ };
@@ -0,0 +1,10 @@
1
+ import { type VoiceTraceTimelineWidgetOptions } from '../client/traceTimelineWidget';
2
+ export declare const createVoiceTraceTimeline: (path?: string, options?: VoiceTraceTimelineWidgetOptions) => {
3
+ getHTML: () => string;
4
+ getViewModel: () => import("../client").VoiceTraceTimelineViewModel;
5
+ close: () => void;
6
+ getServerSnapshot: () => import("../client").VoiceTraceTimelineSnapshot;
7
+ getSnapshot: () => import("../client").VoiceTraceTimelineSnapshot;
8
+ refresh: () => Promise<import("..").VoiceTraceTimelineReport | null>;
9
+ subscribe: (listener: () => void) => () => void;
10
+ };
@@ -5,6 +5,7 @@ export { createVoiceProviderCapabilities } from './createVoiceProviderCapabiliti
5
5
  export { createVoiceStream } from './createVoiceStream';
6
6
  export { createVoiceProviderStatus } from './createVoiceProviderStatus';
7
7
  export { createVoiceRoutingStatus } from './createVoiceRoutingStatus';
8
+ export { createVoiceTraceTimeline } from './createVoiceTraceTimeline';
8
9
  export { createVoiceTurnQuality } from './createVoiceTurnQuality';
9
10
  export { createVoiceWorkflowStatus } from './createVoiceWorkflowStatus';
10
11
  export { createVoiceController } from '../client/controller';