@absolutejs/voice 0.0.22-beta.166 → 0.0.22-beta.168

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.
@@ -0,0 +1,8 @@
1
+ import { type VoiceProviderContractsClientOptions } from '../client/providerContracts';
2
+ export declare const useVoiceProviderContracts: <TProvider extends string = string>(path?: string, options?: VoiceProviderContractsClientOptions) => {
3
+ refresh: () => Promise<import("..").VoiceProviderContractMatrixReport<TProvider> | undefined>;
4
+ error: string | null;
5
+ isLoading: boolean;
6
+ report?: import("..").VoiceProviderContractMatrixReport<TProvider> | undefined;
7
+ updatedAt?: number;
8
+ };
@@ -0,0 +1,10 @@
1
+ import { type VoiceProviderContractsWidgetOptions } from '../client/providerContractsWidget';
2
+ export declare const createVoiceProviderContracts: <TProvider extends string = string>(path?: string, options?: VoiceProviderContractsWidgetOptions) => {
3
+ getHTML: () => string;
4
+ getViewModel: () => import("../client").VoiceProviderContractsViewModel<TProvider>;
5
+ close: () => void;
6
+ getServerSnapshot: () => import("../client").VoiceProviderContractsSnapshot<TProvider>;
7
+ getSnapshot: () => import("../client").VoiceProviderContractsSnapshot<TProvider>;
8
+ refresh: () => Promise<import("..").VoiceProviderContractMatrixReport<TProvider> | undefined>;
9
+ subscribe: (listener: () => void) => () => void;
10
+ };
@@ -4,6 +4,7 @@ export { createVoiceOpsActionCenter } from './createVoiceOpsActionCenter';
4
4
  export { createVoiceOpsStatus } from './createVoiceOpsStatus';
5
5
  export { createVoiceProviderSimulationControls } from './createVoiceProviderSimulationControls';
6
6
  export { createVoiceProviderCapabilities } from './createVoiceProviderCapabilities';
7
+ export { createVoiceProviderContracts } from './createVoiceProviderContracts';
7
8
  export { createVoiceStream } from './createVoiceStream';
8
9
  export { createVoiceProviderStatus } from './createVoiceProviderStatus';
9
10
  export { createVoiceRoutingStatus } from './createVoiceRoutingStatus';
@@ -1367,6 +1367,191 @@ var createVoiceProviderCapabilities = (path = "/api/provider-capabilities", opti
1367
1367
  getViewModel: () => createVoiceProviderCapabilitiesViewModel(store.getSnapshot(), options)
1368
1368
  };
1369
1369
  };
1370
+ // src/client/providerContracts.ts
1371
+ var fetchVoiceProviderContracts = async (path = "/api/provider-contracts", options = {}) => {
1372
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1373
+ const response = await fetchImpl(path);
1374
+ if (!response.ok) {
1375
+ throw new Error(`Voice provider contracts failed: HTTP ${response.status}`);
1376
+ }
1377
+ return await response.json();
1378
+ };
1379
+ var createVoiceProviderContractsStore = (path = "/api/provider-contracts", options = {}) => {
1380
+ const listeners = new Set;
1381
+ let closed = false;
1382
+ let timer;
1383
+ let snapshot = {
1384
+ error: null,
1385
+ isLoading: false
1386
+ };
1387
+ const emit = () => {
1388
+ for (const listener of listeners) {
1389
+ listener();
1390
+ }
1391
+ };
1392
+ const refresh = async () => {
1393
+ if (closed) {
1394
+ return snapshot.report;
1395
+ }
1396
+ snapshot = { ...snapshot, error: null, isLoading: true };
1397
+ emit();
1398
+ try {
1399
+ const report = await fetchVoiceProviderContracts(path, options);
1400
+ snapshot = {
1401
+ error: null,
1402
+ isLoading: false,
1403
+ report,
1404
+ updatedAt: Date.now()
1405
+ };
1406
+ emit();
1407
+ return report;
1408
+ } catch (error) {
1409
+ snapshot = {
1410
+ ...snapshot,
1411
+ error: error instanceof Error ? error.message : String(error),
1412
+ isLoading: false
1413
+ };
1414
+ emit();
1415
+ throw error;
1416
+ }
1417
+ };
1418
+ const close = () => {
1419
+ closed = true;
1420
+ if (timer) {
1421
+ clearInterval(timer);
1422
+ timer = undefined;
1423
+ }
1424
+ listeners.clear();
1425
+ };
1426
+ if (options.intervalMs && options.intervalMs > 0) {
1427
+ timer = setInterval(() => {
1428
+ refresh().catch(() => {});
1429
+ }, options.intervalMs);
1430
+ }
1431
+ return {
1432
+ close,
1433
+ getServerSnapshot: () => snapshot,
1434
+ getSnapshot: () => snapshot,
1435
+ refresh,
1436
+ subscribe: (listener) => {
1437
+ listeners.add(listener);
1438
+ return () => {
1439
+ listeners.delete(listener);
1440
+ };
1441
+ }
1442
+ };
1443
+ };
1444
+
1445
+ // src/client/providerContractsWidget.ts
1446
+ var DEFAULT_TITLE5 = "Provider Contracts";
1447
+ var DEFAULT_DESCRIPTION5 = "Production contract coverage for provider env, latency, fallback, streaming, and capabilities.";
1448
+ var escapeHtml6 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1449
+ var formatProvider2 = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
1450
+ var formatStatus2 = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
1451
+ var contractDetail = (row) => {
1452
+ const failing = row.checks.filter((check) => check.status !== "pass");
1453
+ if (failing.length === 0) {
1454
+ return "Provider contract is production-ready.";
1455
+ }
1456
+ return failing.map((check) => `${check.label}: ${check.detail ?? check.status}`).join(" ");
1457
+ };
1458
+ var createVoiceProviderContractsViewModel = (snapshot, options = {}) => {
1459
+ const rows = (snapshot.report?.rows ?? []).map((row) => ({
1460
+ ...row,
1461
+ detail: contractDetail(row),
1462
+ label: `${formatProvider2(row.provider)} ${row.kind.toUpperCase()}`,
1463
+ rows: [
1464
+ { label: "Status", value: formatStatus2(row.status) },
1465
+ { label: "Selected", value: row.selected ? "Yes" : "No" },
1466
+ { label: "Configured", value: row.configured ? "Yes" : "No" },
1467
+ {
1468
+ label: "Checks",
1469
+ value: row.checks.map((check) => `${check.label}: ${formatStatus2(check.status)}`).join(", ")
1470
+ }
1471
+ ]
1472
+ }));
1473
+ const warningCount = snapshot.report ? snapshot.report.failed + snapshot.report.warned : rows.filter((row) => row.status !== "pass").length;
1474
+ return {
1475
+ description: options.description ?? DEFAULT_DESCRIPTION5,
1476
+ error: snapshot.error,
1477
+ isLoading: snapshot.isLoading,
1478
+ label: snapshot.error ? "Unavailable" : rows.length ? warningCount > 0 ? `${warningCount} needs attention` : `${rows.length} passing` : snapshot.isLoading ? "Checking" : "No contracts",
1479
+ rows,
1480
+ status: snapshot.error ? "error" : rows.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
1481
+ title: options.title ?? DEFAULT_TITLE5,
1482
+ updatedAt: snapshot.updatedAt
1483
+ };
1484
+ };
1485
+ var renderVoiceProviderContractsHTML = (snapshot, options = {}) => {
1486
+ const model = createVoiceProviderContractsViewModel(snapshot, options);
1487
+ const rows = model.rows.length ? `<div class="absolute-voice-provider-contracts__rows">${model.rows.map((row) => `<article class="absolute-voice-provider-contracts__row absolute-voice-provider-contracts__row--${escapeHtml6(row.status)}">
1488
+ <header>
1489
+ <strong>${escapeHtml6(row.label)}</strong>
1490
+ <span>${escapeHtml6(formatStatus2(row.status))}</span>
1491
+ </header>
1492
+ <p>${escapeHtml6(row.detail)}</p>
1493
+ <dl>${row.rows.map((item) => `<div>
1494
+ <dt>${escapeHtml6(item.label)}</dt>
1495
+ <dd>${escapeHtml6(item.value)}</dd>
1496
+ </div>`).join("")}</dl>
1497
+ </article>`).join("")}</div>` : '<p class="absolute-voice-provider-contracts__empty">Configure provider contracts to see production coverage.</p>';
1498
+ return `<section class="absolute-voice-provider-contracts absolute-voice-provider-contracts--${escapeHtml6(model.status)}">
1499
+ <header class="absolute-voice-provider-contracts__header">
1500
+ <span class="absolute-voice-provider-contracts__eyebrow">${escapeHtml6(model.title)}</span>
1501
+ <strong class="absolute-voice-provider-contracts__label">${escapeHtml6(model.label)}</strong>
1502
+ </header>
1503
+ <p class="absolute-voice-provider-contracts__description">${escapeHtml6(model.description)}</p>
1504
+ ${rows}
1505
+ ${model.error ? `<p class="absolute-voice-provider-contracts__error">${escapeHtml6(model.error)}</p>` : ""}
1506
+ </section>`;
1507
+ };
1508
+ var getVoiceProviderContractsCSS = () => `.absolute-voice-provider-contracts{border:1px solid #b8dcc7;border-radius:20px;background:#f7fff9;color:#09140d;padding:18px;box-shadow:0 18px 40px rgba(21,83,45,.12);font-family:inherit}.absolute-voice-provider-contracts--error,.absolute-voice-provider-contracts--warning{border-color:#f2a7a7;background:#fff7f4}.absolute-voice-provider-contracts__header,.absolute-voice-provider-contracts__row header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-provider-contracts__eyebrow{color:#166534;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-provider-contracts__label{font-size:24px;line-height:1}.absolute-voice-provider-contracts__description,.absolute-voice-provider-contracts__row p,.absolute-voice-provider-contracts__row dt,.absolute-voice-provider-contracts__empty{color:#405448}.absolute-voice-provider-contracts__rows{display:grid;gap:12px;margin-top:14px}.absolute-voice-provider-contracts__row{background:#fff;border:1px solid #d6eadb;border-radius:16px;padding:14px}.absolute-voice-provider-contracts__row--pass{border-color:#86efac}.absolute-voice-provider-contracts__row--warn,.absolute-voice-provider-contracts__row--fail{border-color:#f2a7a7}.absolute-voice-provider-contracts__row p{margin:10px 0}.absolute-voice-provider-contracts__row dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-provider-contracts__row div{background:#f7fff9;border:1px solid #d6eadb;border-radius:12px;padding:8px}.absolute-voice-provider-contracts__row dt{font-size:12px}.absolute-voice-provider-contracts__row dd{font-weight:800;margin:4px 0 0}.absolute-voice-provider-contracts__error{color:#9f1239;font-weight:700}`;
1509
+ var mountVoiceProviderContracts = (element, path = "/api/provider-contracts", options = {}) => {
1510
+ const store = createVoiceProviderContractsStore(path, options);
1511
+ const render = () => {
1512
+ element.innerHTML = renderVoiceProviderContractsHTML(store.getSnapshot(), options);
1513
+ };
1514
+ const unsubscribe = store.subscribe(render);
1515
+ render();
1516
+ store.refresh().catch(() => {});
1517
+ return {
1518
+ close: () => {
1519
+ unsubscribe();
1520
+ store.close();
1521
+ },
1522
+ refresh: store.refresh
1523
+ };
1524
+ };
1525
+ var defineVoiceProviderContractsElement = (tagName = "absolute-voice-provider-contracts") => {
1526
+ if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
1527
+ return;
1528
+ }
1529
+ customElements.define(tagName, class AbsoluteVoiceProviderContractsElement extends HTMLElement {
1530
+ mounted;
1531
+ connectedCallback() {
1532
+ const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
1533
+ this.mounted = mountVoiceProviderContracts(this, this.getAttribute("path") ?? "/api/provider-contracts", {
1534
+ description: this.getAttribute("description") ?? undefined,
1535
+ intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
1536
+ title: this.getAttribute("title") ?? undefined
1537
+ });
1538
+ }
1539
+ disconnectedCallback() {
1540
+ this.mounted?.close();
1541
+ this.mounted = undefined;
1542
+ }
1543
+ });
1544
+ };
1545
+
1546
+ // src/svelte/createVoiceProviderContracts.ts
1547
+ var createVoiceProviderContracts = (path = "/api/provider-contracts", options = {}) => {
1548
+ const store = createVoiceProviderContractsStore(path, options);
1549
+ return {
1550
+ ...store,
1551
+ getHTML: () => renderVoiceProviderContractsHTML(store.getSnapshot(), options),
1552
+ getViewModel: () => createVoiceProviderContractsViewModel(store.getSnapshot(), options)
1553
+ };
1554
+ };
1370
1555
  // src/client/actions.ts
1371
1556
  var normalizeErrorMessage = (value) => {
1372
1557
  if (typeof value === "string" && value.trim()) {
@@ -2090,11 +2275,11 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
2090
2275
  };
2091
2276
 
2092
2277
  // src/client/providerStatusWidget.ts
2093
- var DEFAULT_TITLE5 = "Voice Providers";
2094
- var DEFAULT_DESCRIPTION5 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
2095
- var escapeHtml6 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2096
- var formatProvider2 = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
2097
- var formatStatus2 = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
2278
+ var DEFAULT_TITLE6 = "Voice Providers";
2279
+ var DEFAULT_DESCRIPTION6 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
2280
+ var escapeHtml7 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2281
+ var formatProvider3 = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
2282
+ var formatStatus3 = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
2098
2283
  var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
2099
2284
  var formatSuppression = (value) => typeof value === "number" ? `${Math.ceil(value / 1000)}s` : "None";
2100
2285
  var getProviderDetail = (provider) => {
@@ -2120,7 +2305,7 @@ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
2120
2305
  const providers = snapshot.providers.map((provider) => ({
2121
2306
  ...provider,
2122
2307
  detail: getProviderDetail(provider),
2123
- label: `${formatProvider2(provider.provider)}${provider.recommended ? " recommended" : ""}`,
2308
+ label: `${formatProvider3(provider.provider)}${provider.recommended ? " recommended" : ""}`,
2124
2309
  rows: [
2125
2310
  { label: "Runs", value: String(provider.runCount) },
2126
2311
  { label: "Avg latency", value: formatLatency(provider.averageElapsedMs) },
@@ -2136,37 +2321,37 @@ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
2136
2321
  const warningCount = providers.filter((provider) => isWarningStatus2(provider.status)).length;
2137
2322
  const healthyCount = providers.filter((provider) => provider.status === "healthy").length;
2138
2323
  return {
2139
- description: options.description ?? DEFAULT_DESCRIPTION5,
2324
+ description: options.description ?? DEFAULT_DESCRIPTION6,
2140
2325
  error: snapshot.error,
2141
2326
  isLoading: snapshot.isLoading,
2142
2327
  label: snapshot.error ? "Unavailable" : providers.length ? warningCount > 0 ? `${warningCount} needs attention` : `${healthyCount} healthy` : snapshot.isLoading ? "Checking" : "No provider traffic",
2143
2328
  providers,
2144
2329
  status: snapshot.error ? "error" : providers.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
2145
- title: options.title ?? DEFAULT_TITLE5,
2330
+ title: options.title ?? DEFAULT_TITLE6,
2146
2331
  updatedAt: snapshot.updatedAt
2147
2332
  };
2148
2333
  };
2149
2334
  var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
2150
2335
  const model = createVoiceProviderStatusViewModel(snapshot, options);
2151
- 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--${escapeHtml6(provider.status)}">
2336
+ 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--${escapeHtml7(provider.status)}">
2152
2337
  <header>
2153
- <strong>${escapeHtml6(provider.label)}</strong>
2154
- <span>${escapeHtml6(formatStatus2(provider.status))}</span>
2338
+ <strong>${escapeHtml7(provider.label)}</strong>
2339
+ <span>${escapeHtml7(formatStatus3(provider.status))}</span>
2155
2340
  </header>
2156
- <p>${escapeHtml6(provider.detail)}</p>
2341
+ <p>${escapeHtml7(provider.detail)}</p>
2157
2342
  <dl>${provider.rows.map((row) => `<div>
2158
- <dt>${escapeHtml6(row.label)}</dt>
2159
- <dd>${escapeHtml6(row.value)}</dd>
2343
+ <dt>${escapeHtml7(row.label)}</dt>
2344
+ <dd>${escapeHtml7(row.value)}</dd>
2160
2345
  </div>`).join("")}</dl>
2161
2346
  </article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
2162
- return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml6(model.status)}">
2347
+ return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml7(model.status)}">
2163
2348
  <header class="absolute-voice-provider-status__header">
2164
- <span class="absolute-voice-provider-status__eyebrow">${escapeHtml6(model.title)}</span>
2165
- <strong class="absolute-voice-provider-status__label">${escapeHtml6(model.label)}</strong>
2349
+ <span class="absolute-voice-provider-status__eyebrow">${escapeHtml7(model.title)}</span>
2350
+ <strong class="absolute-voice-provider-status__label">${escapeHtml7(model.label)}</strong>
2166
2351
  </header>
2167
- <p class="absolute-voice-provider-status__description">${escapeHtml6(model.description)}</p>
2352
+ <p class="absolute-voice-provider-status__description">${escapeHtml7(model.description)}</p>
2168
2353
  ${providers}
2169
- ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml6(model.error)}</p>` : ""}
2354
+ ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml7(model.error)}</p>` : ""}
2170
2355
  </section>`;
2171
2356
  };
2172
2357
  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}`;
@@ -2297,9 +2482,9 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
2297
2482
  };
2298
2483
 
2299
2484
  // src/client/routingStatusWidget.ts
2300
- var DEFAULT_TITLE6 = "Voice Routing";
2301
- var DEFAULT_DESCRIPTION6 = "Latest provider routing decision from the self-hosted trace store.";
2302
- var escapeHtml7 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2485
+ var DEFAULT_TITLE7 = "Voice Routing";
2486
+ var DEFAULT_DESCRIPTION7 = "Latest provider routing decision from the self-hosted trace store.";
2487
+ var escapeHtml8 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2303
2488
  var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
2304
2489
  var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
2305
2490
  const decision = snapshot.decision;
@@ -2323,30 +2508,30 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
2323
2508
  ] : [];
2324
2509
  return {
2325
2510
  decision,
2326
- description: options.description ?? DEFAULT_DESCRIPTION6,
2511
+ description: options.description ?? DEFAULT_DESCRIPTION7,
2327
2512
  error: snapshot.error,
2328
2513
  isLoading: snapshot.isLoading,
2329
2514
  label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
2330
2515
  rows,
2331
2516
  status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
2332
- title: options.title ?? DEFAULT_TITLE6,
2517
+ title: options.title ?? DEFAULT_TITLE7,
2333
2518
  updatedAt: snapshot.updatedAt
2334
2519
  };
2335
2520
  };
2336
2521
  var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
2337
2522
  const model = createVoiceRoutingStatusViewModel(snapshot, options);
2338
2523
  const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
2339
- <span>${escapeHtml7(row.label)}</span>
2340
- <strong>${escapeHtml7(row.value)}</strong>
2524
+ <span>${escapeHtml8(row.label)}</span>
2525
+ <strong>${escapeHtml8(row.value)}</strong>
2341
2526
  </div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
2342
- return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml7(model.status)}">
2527
+ return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml8(model.status)}">
2343
2528
  <header class="absolute-voice-routing-status__header">
2344
- <span class="absolute-voice-routing-status__eyebrow">${escapeHtml7(model.title)}</span>
2345
- <strong class="absolute-voice-routing-status__label">${escapeHtml7(model.label)}</strong>
2529
+ <span class="absolute-voice-routing-status__eyebrow">${escapeHtml8(model.title)}</span>
2530
+ <strong class="absolute-voice-routing-status__label">${escapeHtml8(model.label)}</strong>
2346
2531
  </header>
2347
- <p class="absolute-voice-routing-status__description">${escapeHtml7(model.description)}</p>
2532
+ <p class="absolute-voice-routing-status__description">${escapeHtml8(model.description)}</p>
2348
2533
  ${rows}
2349
- ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml7(model.error)}</p>` : ""}
2534
+ ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml8(model.error)}</p>` : ""}
2350
2535
  </section>`;
2351
2536
  };
2352
2537
  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}`;
@@ -2477,9 +2662,9 @@ var createVoiceTraceTimelineStore = (path = "/api/voice-traces", options = {}) =
2477
2662
  };
2478
2663
 
2479
2664
  // src/client/traceTimelineWidget.ts
2480
- var DEFAULT_TITLE7 = "Voice Traces";
2481
- var DEFAULT_DESCRIPTION7 = "Latest call timelines with provider latency, fallbacks, handoffs, and errors from your self-hosted trace store.";
2482
- var escapeHtml8 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2665
+ var DEFAULT_TITLE8 = "Voice Traces";
2666
+ var DEFAULT_DESCRIPTION8 = "Latest call timelines with provider latency, fallbacks, handoffs, and errors from your self-hosted trace store.";
2667
+ var escapeHtml9 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2483
2668
  var formatMs = (value) => typeof value === "number" ? `${value}ms` : "n/a";
2484
2669
  var formatProviders = (session) => session.providers.length ? session.providers.map((provider) => provider.provider).join(", ") : "No providers";
2485
2670
  var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
@@ -2493,34 +2678,34 @@ var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
2493
2678
  const failed = sessions.filter((session) => session.status === "failed").length;
2494
2679
  const warnings = sessions.filter((session) => session.status === "warning").length;
2495
2680
  return {
2496
- description: options.description ?? DEFAULT_DESCRIPTION7,
2681
+ description: options.description ?? DEFAULT_DESCRIPTION8,
2497
2682
  error: snapshot.error,
2498
2683
  isLoading: snapshot.isLoading,
2499
2684
  label: snapshot.error ? "Unavailable" : failed > 0 ? `${failed} failed` : warnings > 0 ? `${warnings} warning` : sessions.length ? `${sessions.length} recent` : snapshot.isLoading ? "Checking" : "No traces yet",
2500
2685
  sessions,
2501
2686
  status: snapshot.error ? "error" : failed > 0 ? "failed" : warnings > 0 ? "warning" : sessions.length ? "ready" : snapshot.isLoading ? "loading" : "empty",
2502
- title: options.title ?? DEFAULT_TITLE7,
2687
+ title: options.title ?? DEFAULT_TITLE8,
2503
2688
  updatedAt: snapshot.updatedAt
2504
2689
  };
2505
2690
  };
2506
2691
  var renderVoiceTraceTimelineWidgetHTML = (snapshot, options = {}) => {
2507
2692
  const model = createVoiceTraceTimelineViewModel(snapshot, options);
2508
- 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--${escapeHtml8(session.status)}">
2693
+ 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--${escapeHtml9(session.status)}">
2509
2694
  <header>
2510
- <strong>${escapeHtml8(session.sessionId)}</strong>
2511
- <span>${escapeHtml8(session.status)}</span>
2695
+ <strong>${escapeHtml9(session.sessionId)}</strong>
2696
+ <span>${escapeHtml9(session.status)}</span>
2512
2697
  </header>
2513
- <p>${escapeHtml8(session.label)} \xB7 ${escapeHtml8(session.durationLabel)} \xB7 ${escapeHtml8(session.providerLabel)}</p>
2514
- <a href="${escapeHtml8(session.detailHref)}">Open timeline</a>
2698
+ <p>${escapeHtml9(session.label)} \xB7 ${escapeHtml9(session.durationLabel)} \xB7 ${escapeHtml9(session.providerLabel)}</p>
2699
+ <a href="${escapeHtml9(session.detailHref)}">Open timeline</a>
2515
2700
  </article>`).join("")}</div>` : '<p class="absolute-voice-trace-timeline__empty">Run a voice session to see call timelines.</p>';
2516
- return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml8(model.status)}">
2701
+ return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml9(model.status)}">
2517
2702
  <header class="absolute-voice-trace-timeline__header">
2518
- <span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml8(model.title)}</span>
2519
- <strong class="absolute-voice-trace-timeline__label">${escapeHtml8(model.label)}</strong>
2703
+ <span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml9(model.title)}</span>
2704
+ <strong class="absolute-voice-trace-timeline__label">${escapeHtml9(model.label)}</strong>
2520
2705
  </header>
2521
- <p class="absolute-voice-trace-timeline__description">${escapeHtml8(model.description)}</p>
2706
+ <p class="absolute-voice-trace-timeline__description">${escapeHtml9(model.description)}</p>
2522
2707
  ${sessions}
2523
- ${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml8(model.error)}</p>` : ""}
2708
+ ${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml9(model.error)}</p>` : ""}
2524
2709
  </section>`;
2525
2710
  };
2526
2711
  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}`;
@@ -2677,10 +2862,10 @@ var createVoiceTurnLatencyStore = (path = "/api/turn-latency", options = {}) =>
2677
2862
  };
2678
2863
 
2679
2864
  // src/client/turnLatencyWidget.ts
2680
- var DEFAULT_TITLE8 = "Turn Latency";
2681
- var DEFAULT_DESCRIPTION8 = "Per-turn timing from first transcript to commit and assistant response start.";
2865
+ var DEFAULT_TITLE9 = "Turn Latency";
2866
+ var DEFAULT_DESCRIPTION9 = "Per-turn timing from first transcript to commit and assistant response start.";
2682
2867
  var DEFAULT_PROOF_LABEL = "Run latency proof";
2683
- var escapeHtml9 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2868
+ var escapeHtml10 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2684
2869
  var formatMs2 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
2685
2870
  var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
2686
2871
  const turns = (snapshot.report?.turns ?? []).map((turn) => ({
@@ -2694,39 +2879,39 @@ var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
2694
2879
  const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
2695
2880
  const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
2696
2881
  return {
2697
- description: options.description ?? DEFAULT_DESCRIPTION8,
2882
+ description: options.description ?? DEFAULT_DESCRIPTION9,
2698
2883
  error: snapshot.error,
2699
2884
  isLoading: snapshot.isLoading,
2700
2885
  label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} slow` : warningCount > 0 ? `${warningCount} warnings` : `avg ${formatMs2(snapshot.report?.averageTotalMs)}` : snapshot.isLoading ? "Checking" : "No turns",
2701
2886
  proofLabel: options.proofPath ? options.proofLabel ?? DEFAULT_PROOF_LABEL : undefined,
2702
2887
  showProofAction: Boolean(options.proofPath),
2703
2888
  status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
2704
- title: options.title ?? DEFAULT_TITLE8,
2889
+ title: options.title ?? DEFAULT_TITLE9,
2705
2890
  turns,
2706
2891
  updatedAt: snapshot.updatedAt
2707
2892
  };
2708
2893
  };
2709
2894
  var renderVoiceTurnLatencyHTML = (snapshot, options = {}) => {
2710
2895
  const model = createVoiceTurnLatencyViewModel(snapshot, options);
2711
- 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--${escapeHtml9(turn.status)}">
2896
+ 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--${escapeHtml10(turn.status)}">
2712
2897
  <header>
2713
- <strong>${escapeHtml9(turn.label)}</strong>
2714
- <span>${escapeHtml9(turn.status)}</span>
2898
+ <strong>${escapeHtml10(turn.label)}</strong>
2899
+ <span>${escapeHtml10(turn.status)}</span>
2715
2900
  </header>
2716
2901
  <dl>${turn.rows.map((row) => `<div>
2717
- <dt>${escapeHtml9(row.label)}</dt>
2718
- <dd>${escapeHtml9(row.value)}</dd>
2902
+ <dt>${escapeHtml10(row.label)}</dt>
2903
+ <dd>${escapeHtml10(row.value)}</dd>
2719
2904
  </div>`).join("")}</dl>
2720
2905
  </article>`).join("")}</div>` : '<p class="absolute-voice-turn-latency__empty">Complete a voice turn to see latency diagnostics.</p>';
2721
- return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml9(model.status)}">
2906
+ return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml10(model.status)}">
2722
2907
  <header class="absolute-voice-turn-latency__header">
2723
- <span class="absolute-voice-turn-latency__eyebrow">${escapeHtml9(model.title)}</span>
2724
- <strong class="absolute-voice-turn-latency__label">${escapeHtml9(model.label)}</strong>
2908
+ <span class="absolute-voice-turn-latency__eyebrow">${escapeHtml10(model.title)}</span>
2909
+ <strong class="absolute-voice-turn-latency__label">${escapeHtml10(model.label)}</strong>
2725
2910
  </header>
2726
- <p class="absolute-voice-turn-latency__description">${escapeHtml9(model.description)}</p>
2727
- ${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${escapeHtml9(model.proofLabel ?? DEFAULT_PROOF_LABEL)}</button>` : ""}
2911
+ <p class="absolute-voice-turn-latency__description">${escapeHtml10(model.description)}</p>
2912
+ ${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${escapeHtml10(model.proofLabel ?? DEFAULT_PROOF_LABEL)}</button>` : ""}
2728
2913
  ${turns}
2729
- ${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml9(model.error)}</p>` : ""}
2914
+ ${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml10(model.error)}</p>` : ""}
2730
2915
  </section>`;
2731
2916
  };
2732
2917
  var mountVoiceTurnLatency = (element, path = "/api/turn-latency", options = {}) => {
@@ -2865,9 +3050,9 @@ var createVoiceTurnQualityStore = (path = "/api/turn-quality", options = {}) =>
2865
3050
  };
2866
3051
 
2867
3052
  // src/client/turnQualityWidget.ts
2868
- var DEFAULT_TITLE9 = "Turn Quality";
2869
- var DEFAULT_DESCRIPTION9 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
2870
- var escapeHtml10 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3053
+ var DEFAULT_TITLE10 = "Turn Quality";
3054
+ var DEFAULT_DESCRIPTION10 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
3055
+ var escapeHtml11 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2871
3056
  var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
2872
3057
  var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
2873
3058
  var getTurnDetail = (turn) => {
@@ -2905,37 +3090,37 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
2905
3090
  const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
2906
3091
  const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
2907
3092
  return {
2908
- description: options.description ?? DEFAULT_DESCRIPTION9,
3093
+ description: options.description ?? DEFAULT_DESCRIPTION10,
2909
3094
  error: snapshot.error,
2910
3095
  isLoading: snapshot.isLoading,
2911
3096
  label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} failed` : warningCount > 0 ? `${warningCount} warnings` : `${turns.length} healthy` : snapshot.isLoading ? "Checking" : "No turns",
2912
3097
  status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
2913
- title: options.title ?? DEFAULT_TITLE9,
3098
+ title: options.title ?? DEFAULT_TITLE10,
2914
3099
  turns,
2915
3100
  updatedAt: snapshot.updatedAt
2916
3101
  };
2917
3102
  };
2918
3103
  var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
2919
3104
  const model = createVoiceTurnQualityViewModel(snapshot, options);
2920
- 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--${escapeHtml10(turn.status)}">
3105
+ 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--${escapeHtml11(turn.status)}">
2921
3106
  <header>
2922
- <strong>${escapeHtml10(turn.label)}</strong>
2923
- <span>${escapeHtml10(turn.status)}</span>
3107
+ <strong>${escapeHtml11(turn.label)}</strong>
3108
+ <span>${escapeHtml11(turn.status)}</span>
2924
3109
  </header>
2925
- <p>${escapeHtml10(turn.detail)}</p>
3110
+ <p>${escapeHtml11(turn.detail)}</p>
2926
3111
  <dl>${turn.rows.map((row) => `<div>
2927
- <dt>${escapeHtml10(row.label)}</dt>
2928
- <dd>${escapeHtml10(row.value)}</dd>
3112
+ <dt>${escapeHtml11(row.label)}</dt>
3113
+ <dd>${escapeHtml11(row.value)}</dd>
2929
3114
  </div>`).join("")}</dl>
2930
3115
  </article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
2931
- return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml10(model.status)}">
3116
+ return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml11(model.status)}">
2932
3117
  <header class="absolute-voice-turn-quality__header">
2933
- <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml10(model.title)}</span>
2934
- <strong class="absolute-voice-turn-quality__label">${escapeHtml10(model.label)}</strong>
3118
+ <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml11(model.title)}</span>
3119
+ <strong class="absolute-voice-turn-quality__label">${escapeHtml11(model.label)}</strong>
2935
3120
  </header>
2936
- <p class="absolute-voice-turn-quality__description">${escapeHtml10(model.description)}</p>
3121
+ <p class="absolute-voice-turn-quality__description">${escapeHtml11(model.description)}</p>
2937
3122
  ${turns}
2938
- ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml10(model.error)}</p>` : ""}
3123
+ ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml11(model.error)}</p>` : ""}
2939
3124
  </section>`;
2940
3125
  };
2941
3126
  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}`;
@@ -3715,6 +3900,7 @@ export {
3715
3900
  createVoiceRoutingStatus,
3716
3901
  createVoiceProviderStatus,
3717
3902
  createVoiceProviderSimulationControls,
3903
+ createVoiceProviderContracts,
3718
3904
  createVoiceProviderCapabilities,
3719
3905
  createVoiceOpsStatus,
3720
3906
  createVoiceOpsActionCenter,
@@ -0,0 +1,21 @@
1
+ export declare const VoiceProviderContracts: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
2
+ description: StringConstructor;
3
+ intervalMs: NumberConstructor;
4
+ path: {
5
+ default: string;
6
+ type: StringConstructor;
7
+ };
8
+ title: StringConstructor;
9
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
10
+ [key: string]: any;
11
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
12
+ description: StringConstructor;
13
+ intervalMs: NumberConstructor;
14
+ path: {
15
+ default: string;
16
+ type: StringConstructor;
17
+ };
18
+ title: StringConstructor;
19
+ }>> & Readonly<{}>, {
20
+ path: string;
21
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -3,6 +3,7 @@ export { VoiceOpsActionCenter } from './VoiceOpsActionCenter';
3
3
  export { VoiceDeliveryRuntime } from './VoiceDeliveryRuntime';
4
4
  export { VoiceProviderSimulationControls } from './VoiceProviderSimulationControls';
5
5
  export { VoiceProviderCapabilities } from './VoiceProviderCapabilities';
6
+ export { VoiceProviderContracts } from './VoiceProviderContracts';
6
7
  export { VoiceProviderStatus } from './VoiceProviderStatus';
7
8
  export { VoiceRoutingStatus } from './VoiceRoutingStatus';
8
9
  export { VoiceTurnLatency } from './VoiceTurnLatency';
@@ -15,6 +16,7 @@ export { useVoiceStream } from './useVoiceStream';
15
16
  export { useVoiceController } from './useVoiceController';
16
17
  export { useVoiceProviderStatus } from './useVoiceProviderStatus';
17
18
  export { useVoiceProviderCapabilities } from './useVoiceProviderCapabilities';
19
+ export { useVoiceProviderContracts } from './useVoiceProviderContracts';
18
20
  export { useVoiceProviderSimulationControls } from './useVoiceProviderSimulationControls';
19
21
  export { useVoiceRoutingStatus } from './useVoiceRoutingStatus';
20
22
  export { useVoiceTraceTimeline } from './useVoiceTraceTimeline';