@absolutejs/voice 0.0.22-beta.154 → 0.0.22-beta.156

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.
@@ -2063,6 +2063,23 @@ var createVoiceOpsStatusStore = (path = "/api/voice/ops-status", options = {}) =
2063
2063
  };
2064
2064
  };
2065
2065
  // src/client/opsActionCenter.ts
2066
+ var recordVoiceOpsActionResult = async (result, options = {}) => {
2067
+ if (options.auditPath === false) {
2068
+ return;
2069
+ }
2070
+ const path = options.auditPath ?? "/api/voice/ops-actions/audit";
2071
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2072
+ const response = await fetchImpl(path, {
2073
+ body: JSON.stringify(result),
2074
+ headers: {
2075
+ "Content-Type": "application/json"
2076
+ },
2077
+ method: "POST"
2078
+ });
2079
+ if (!response.ok) {
2080
+ throw new Error(`Voice ops action audit failed: HTTP ${response.status}`);
2081
+ }
2082
+ };
2066
2083
  var createVoiceOpsActionCenterActions = (options = {}) => {
2067
2084
  const deliveryRuntimePath = options.deliveryRuntimePath ?? "/api/voice-delivery-runtime";
2068
2085
  const actions = [];
@@ -2175,6 +2192,8 @@ var createVoiceOpsActionCenterStore = (options = {}) => {
2175
2192
  emit();
2176
2193
  try {
2177
2194
  const result = await runVoiceOpsAction(action, options);
2195
+ await options.onActionResult?.(result);
2196
+ await recordVoiceOpsActionResult(result, options);
2178
2197
  snapshot = {
2179
2198
  ...snapshot,
2180
2199
  error: null,
@@ -2186,6 +2205,16 @@ var createVoiceOpsActionCenterStore = (options = {}) => {
2186
2205
  emit();
2187
2206
  return result;
2188
2207
  } catch (error) {
2208
+ const result = {
2209
+ actionId: action.id,
2210
+ body: null,
2211
+ error: error instanceof Error ? error.message : String(error),
2212
+ ok: false,
2213
+ ranAt: Date.now(),
2214
+ status: 0
2215
+ };
2216
+ await options.onActionResult?.(result);
2217
+ await recordVoiceOpsActionResult(result, options).catch(() => {});
2189
2218
  snapshot = {
2190
2219
  ...snapshot,
2191
2220
  error: error instanceof Error ? error.message : String(error),
@@ -2223,6 +2252,80 @@ var createVoiceOpsActionCenterStore = (options = {}) => {
2223
2252
  }
2224
2253
  };
2225
2254
  };
2255
+ // src/client/opsActionHistory.ts
2256
+ var fetchVoiceOpsActionHistory = async (path = "/api/voice/ops-actions/history", options = {}) => {
2257
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2258
+ const response = await fetchImpl(path);
2259
+ if (!response.ok) {
2260
+ throw new Error(`Voice ops action history failed: HTTP ${response.status}`);
2261
+ }
2262
+ return await response.json();
2263
+ };
2264
+ var createVoiceOpsActionHistoryStore = (path = "/api/voice/ops-actions/history", options = {}) => {
2265
+ const listeners = new Set;
2266
+ let closed = false;
2267
+ let timer;
2268
+ let snapshot = {
2269
+ error: null,
2270
+ isLoading: false
2271
+ };
2272
+ const emit = () => {
2273
+ for (const listener of listeners) {
2274
+ listener();
2275
+ }
2276
+ };
2277
+ const refresh = async () => {
2278
+ if (closed) {
2279
+ return snapshot.report;
2280
+ }
2281
+ snapshot = { ...snapshot, error: null, isLoading: true };
2282
+ emit();
2283
+ try {
2284
+ const report = await fetchVoiceOpsActionHistory(path, options);
2285
+ snapshot = {
2286
+ error: null,
2287
+ isLoading: false,
2288
+ report,
2289
+ updatedAt: Date.now()
2290
+ };
2291
+ emit();
2292
+ return report;
2293
+ } catch (error) {
2294
+ snapshot = {
2295
+ ...snapshot,
2296
+ error: error instanceof Error ? error.message : String(error),
2297
+ isLoading: false
2298
+ };
2299
+ emit();
2300
+ throw error;
2301
+ }
2302
+ };
2303
+ const close = () => {
2304
+ closed = true;
2305
+ if (timer) {
2306
+ clearInterval(timer);
2307
+ timer = undefined;
2308
+ }
2309
+ listeners.clear();
2310
+ };
2311
+ if (options.intervalMs && options.intervalMs > 0) {
2312
+ timer = setInterval(() => {
2313
+ refresh().catch(() => {});
2314
+ }, options.intervalMs);
2315
+ }
2316
+ return {
2317
+ close,
2318
+ getServerSnapshot: () => snapshot,
2319
+ getSnapshot: () => snapshot,
2320
+ refresh,
2321
+ subscribe: (listener) => {
2322
+ listeners.add(listener);
2323
+ return () => {
2324
+ listeners.delete(listener);
2325
+ };
2326
+ }
2327
+ };
2328
+ };
2226
2329
  // src/client/deliveryRuntime.ts
2227
2330
  var getDefaultActionPath = (path, action, options) => {
2228
2331
  if (action === "tick") {
@@ -2570,10 +2673,40 @@ var defineVoiceOpsActionCenterElement = (tagName = "absolute-voice-ops-action-ce
2570
2673
  }
2571
2674
  });
2572
2675
  };
2676
+ // src/client/opsActionHistoryWidget.ts
2677
+ var escapeHtml3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2678
+ var renderVoiceOpsActionHistoryWidgetHTML = (snapshot, options = {}) => {
2679
+ const report = snapshot.report;
2680
+ const entries = (report?.entries ?? []).slice(0, options.limit ?? 5);
2681
+ const rows = entries.map((entry) => `<li class="absolute-voice-ops-action-history__entry absolute-voice-ops-action-history__entry--${entry.ok ? "success" : "error"}"><span>${escapeHtml3(entry.actionId)}</span><strong>${escapeHtml3(entry.ok ? "Success" : "Failed")}</strong><small>${escapeHtml3(new Date(entry.at).toLocaleString())}${entry.status ? ` \xB7 HTTP ${String(entry.status)}` : ""}</small></li>`).join("");
2682
+ return `<section class="absolute-voice-ops-action-history">
2683
+ <header><span>Operator proof</span><strong>${escapeHtml3(options.title ?? "Action History")}</strong></header>
2684
+ <p>${String(report?.total ?? 0)} action(s), ${String(report?.failed ?? 0)} failed.</p>
2685
+ <ul>${rows || "<li>No operator actions recorded yet.</li>"}</ul>
2686
+ ${snapshot.error ? `<p class="absolute-voice-ops-action-history__error">${escapeHtml3(snapshot.error)}</p>` : ""}
2687
+ </section>`;
2688
+ };
2689
+ var getVoiceOpsActionHistoryCSS = () => `.absolute-voice-ops-action-history{border:1px solid #d8d2c4;border-radius:20px;background:#fffaf0;color:#16130d;padding:18px;font-family:inherit}.absolute-voice-ops-action-history header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-ops-action-history header span{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-ops-action-history header strong{font-size:24px}.absolute-voice-ops-action-history p{color:#514733}.absolute-voice-ops-action-history ul{display:grid;gap:8px;list-style:none;margin:12px 0 0;padding:0}.absolute-voice-ops-action-history__entry{background:#fff;border:1px solid #eee4d2;border-radius:14px;display:grid;gap:3px;padding:10px 12px}.absolute-voice-ops-action-history__entry--error{border-color:#f2a7a7}.absolute-voice-ops-action-history__entry span{font-weight:800}.absolute-voice-ops-action-history__entry small{color:#655944}.absolute-voice-ops-action-history__error{color:#9f1239;font-weight:700}`;
2690
+ var mountVoiceOpsActionHistory = (element, path = "/api/voice/ops-actions/history", options = {}) => {
2691
+ const store = createVoiceOpsActionHistoryStore(path, options);
2692
+ const render = () => {
2693
+ element.innerHTML = renderVoiceOpsActionHistoryWidgetHTML(store.getSnapshot(), options);
2694
+ };
2695
+ const unsubscribe = store.subscribe(render);
2696
+ render();
2697
+ store.refresh().catch(() => {});
2698
+ return {
2699
+ close: () => {
2700
+ unsubscribe();
2701
+ store.close();
2702
+ },
2703
+ refresh: store.refresh
2704
+ };
2705
+ };
2573
2706
  // src/client/deliveryRuntimeWidget.ts
2574
2707
  var DEFAULT_TITLE3 = "Voice Delivery Runtime";
2575
2708
  var DEFAULT_DESCRIPTION3 = "Audit and trace delivery worker health from your AbsoluteJS voice app.";
2576
- var escapeHtml3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2709
+ var escapeHtml4 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2577
2710
  var createSurface = (id, summary) => {
2578
2711
  if (!summary) {
2579
2712
  return {
@@ -2622,26 +2755,26 @@ var createVoiceDeliveryRuntimeViewModel = (snapshot, options = {}) => {
2622
2755
  };
2623
2756
  var renderVoiceDeliveryRuntimeHTML = (snapshot, options = {}) => {
2624
2757
  const model = createVoiceDeliveryRuntimeViewModel(snapshot, options);
2625
- const surfaces = model.surfaces.map((surface) => `<li class="absolute-voice-delivery-runtime__surface absolute-voice-delivery-runtime__surface--${escapeHtml3(surface.status)}">
2626
- <span>${escapeHtml3(surface.label)}</span>
2627
- <strong>${escapeHtml3(surface.detail)}</strong>
2758
+ const surfaces = model.surfaces.map((surface) => `<li class="absolute-voice-delivery-runtime__surface absolute-voice-delivery-runtime__surface--${escapeHtml4(surface.status)}">
2759
+ <span>${escapeHtml4(surface.label)}</span>
2760
+ <strong>${escapeHtml4(surface.detail)}</strong>
2628
2761
  <small>${String(surface.failed)} failed &middot; ${String(surface.deadLettered)} dead-lettered</small>
2629
2762
  </li>`).join("");
2630
2763
  const actions = options.includeActions === false ? "" : `<div class="absolute-voice-delivery-runtime__actions">
2631
2764
  <button type="button" data-absolute-voice-delivery-runtime-action="tick">${model.actionStatus === "running" ? "Working..." : "Tick workers"}</button>
2632
2765
  <button type="button" data-absolute-voice-delivery-runtime-action="requeue-dead-letters"${model.surfaces.some((surface) => surface.deadLettered > 0) ? "" : " disabled"}>Requeue dead letters</button>
2633
2766
  </div>`;
2634
- const actionError = model.actionError ? `<p class="absolute-voice-delivery-runtime__error">${escapeHtml3(model.actionError)}</p>` : "";
2635
- return `<section class="absolute-voice-delivery-runtime absolute-voice-delivery-runtime--${escapeHtml3(model.status)}">
2767
+ const actionError = model.actionError ? `<p class="absolute-voice-delivery-runtime__error">${escapeHtml4(model.actionError)}</p>` : "";
2768
+ return `<section class="absolute-voice-delivery-runtime absolute-voice-delivery-runtime--${escapeHtml4(model.status)}">
2636
2769
  <header class="absolute-voice-delivery-runtime__header">
2637
- <span class="absolute-voice-delivery-runtime__eyebrow">${escapeHtml3(model.title)}</span>
2638
- <strong class="absolute-voice-delivery-runtime__label">${escapeHtml3(model.label)}</strong>
2770
+ <span class="absolute-voice-delivery-runtime__eyebrow">${escapeHtml4(model.title)}</span>
2771
+ <strong class="absolute-voice-delivery-runtime__label">${escapeHtml4(model.label)}</strong>
2639
2772
  </header>
2640
- <p class="absolute-voice-delivery-runtime__description">${escapeHtml3(model.description)}</p>
2773
+ <p class="absolute-voice-delivery-runtime__description">${escapeHtml4(model.description)}</p>
2641
2774
  <ul class="absolute-voice-delivery-runtime__surfaces">${surfaces}</ul>
2642
2775
  ${actions}
2643
2776
  ${actionError}
2644
- ${model.error ? `<p class="absolute-voice-delivery-runtime__error">${escapeHtml3(model.error)}</p>` : ""}
2777
+ ${model.error ? `<p class="absolute-voice-delivery-runtime__error">${escapeHtml4(model.error)}</p>` : ""}
2645
2778
  </section>`;
2646
2779
  };
2647
2780
  var getVoiceDeliveryRuntimeCSS = () => `.absolute-voice-delivery-runtime{border:1px solid #c9d8cf;border-radius:20px;background:#f6fff9;color:#0d1b12;padding:18px;box-shadow:0 18px 40px rgba(19,55,35,.12);font-family:inherit}.absolute-voice-delivery-runtime--warn,.absolute-voice-delivery-runtime--error{border-color:#f2b56b;background:#fff9ed}.absolute-voice-delivery-runtime__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-delivery-runtime__eyebrow{color:#4e6b59;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-delivery-runtime__label{font-size:28px;line-height:1}.absolute-voice-delivery-runtime__description{color:#33483b;margin:12px 0 0}.absolute-voice-delivery-runtime__surfaces{display:grid;gap:8px;list-style:none;margin:16px 0 0;padding:0}.absolute-voice-delivery-runtime__surface{background:#fff;border:1px solid #d9eadf;border-radius:14px;display:grid;gap:4px;padding:10px 12px}.absolute-voice-delivery-runtime__surface--warn{border-color:#f2b56b}.absolute-voice-delivery-runtime__surface--disabled{opacity:.72}.absolute-voice-delivery-runtime__surface span,.absolute-voice-delivery-runtime__surface small{color:#587063}.absolute-voice-delivery-runtime__actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}.absolute-voice-delivery-runtime__actions button{background:#134e2d;border:0;border-radius:999px;color:#f6fff9;cursor:pointer;font:inherit;font-weight:800;padding:8px 12px}.absolute-voice-delivery-runtime__actions button:disabled{cursor:not-allowed;opacity:.48}.absolute-voice-delivery-runtime__error{color:#9f1239;font-weight:700}`;
@@ -2779,7 +2912,7 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
2779
2912
  // src/client/routingStatusWidget.ts
2780
2913
  var DEFAULT_TITLE4 = "Voice Routing";
2781
2914
  var DEFAULT_DESCRIPTION4 = "Latest provider routing decision from the self-hosted trace store.";
2782
- var escapeHtml4 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2915
+ var escapeHtml5 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2783
2916
  var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
2784
2917
  var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
2785
2918
  const decision = snapshot.decision;
@@ -2816,17 +2949,17 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
2816
2949
  var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
2817
2950
  const model = createVoiceRoutingStatusViewModel(snapshot, options);
2818
2951
  const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
2819
- <span>${escapeHtml4(row.label)}</span>
2820
- <strong>${escapeHtml4(row.value)}</strong>
2952
+ <span>${escapeHtml5(row.label)}</span>
2953
+ <strong>${escapeHtml5(row.value)}</strong>
2821
2954
  </div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
2822
- return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml4(model.status)}">
2955
+ return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml5(model.status)}">
2823
2956
  <header class="absolute-voice-routing-status__header">
2824
- <span class="absolute-voice-routing-status__eyebrow">${escapeHtml4(model.title)}</span>
2825
- <strong class="absolute-voice-routing-status__label">${escapeHtml4(model.label)}</strong>
2957
+ <span class="absolute-voice-routing-status__eyebrow">${escapeHtml5(model.title)}</span>
2958
+ <strong class="absolute-voice-routing-status__label">${escapeHtml5(model.label)}</strong>
2826
2959
  </header>
2827
- <p class="absolute-voice-routing-status__description">${escapeHtml4(model.description)}</p>
2960
+ <p class="absolute-voice-routing-status__description">${escapeHtml5(model.description)}</p>
2828
2961
  ${rows}
2829
- ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml4(model.error)}</p>` : ""}
2962
+ ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml5(model.error)}</p>` : ""}
2830
2963
  </section>`;
2831
2964
  };
2832
2965
  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}`;
@@ -3478,7 +3611,7 @@ var createVoiceProviderSimulationControlsStore = (options) => {
3478
3611
  };
3479
3612
  };
3480
3613
  // src/client/providerSimulationControlsWidget.ts
3481
- var escapeHtml5 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3614
+ var escapeHtml6 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3482
3615
  var formatKind = (kind) => (kind ?? "stt").toUpperCase();
3483
3616
  var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
3484
3617
  const configuredProviders = options.providers.filter((provider) => provider.configured !== false);
@@ -3498,18 +3631,18 @@ var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
3498
3631
  };
3499
3632
  var renderVoiceProviderSimulationControlsHTML = (snapshot, options) => {
3500
3633
  const model = createVoiceProviderSimulationControlsViewModel(snapshot, options);
3501
- const failureButtons = model.failureProviders.map((provider) => `<button type="button" data-voice-provider-fail="${escapeHtml5(provider.provider)}"${!model.canSimulateFailure || snapshot.isRunning ? " disabled" : ""}>Simulate ${escapeHtml5(provider.provider)} ${escapeHtml5(formatKind(options.kind))} failure</button>`).join("");
3502
- const recoveryButtons = model.providers.map((provider) => `<button type="button" data-voice-provider-recover="${escapeHtml5(provider.provider)}"${snapshot.isRunning ? " disabled" : ""}>Mark ${escapeHtml5(provider.provider)} recovered</button>`).join("");
3634
+ const failureButtons = model.failureProviders.map((provider) => `<button type="button" data-voice-provider-fail="${escapeHtml6(provider.provider)}"${!model.canSimulateFailure || snapshot.isRunning ? " disabled" : ""}>Simulate ${escapeHtml6(provider.provider)} ${escapeHtml6(formatKind(options.kind))} failure</button>`).join("");
3635
+ const recoveryButtons = model.providers.map((provider) => `<button type="button" data-voice-provider-recover="${escapeHtml6(provider.provider)}"${snapshot.isRunning ? " disabled" : ""}>Mark ${escapeHtml6(provider.provider)} recovered</button>`).join("");
3503
3636
  return `<section class="absolute-voice-provider-simulation absolute-voice-provider-simulation--${snapshot.error ? "error" : snapshot.isRunning ? "running" : "ready"}">
3504
3637
  <header class="absolute-voice-provider-simulation__header">
3505
- <span class="absolute-voice-provider-simulation__eyebrow">${escapeHtml5(model.title)}</span>
3506
- <strong class="absolute-voice-provider-simulation__label">${escapeHtml5(model.label)}</strong>
3638
+ <span class="absolute-voice-provider-simulation__eyebrow">${escapeHtml6(model.title)}</span>
3639
+ <strong class="absolute-voice-provider-simulation__label">${escapeHtml6(model.label)}</strong>
3507
3640
  </header>
3508
- <p class="absolute-voice-provider-simulation__description">${escapeHtml5(model.description)}</p>
3509
- ${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${escapeHtml5(options.fallbackRequiredMessage ?? "Configure fallback providers before simulating failure.")}</p>`}
3641
+ <p class="absolute-voice-provider-simulation__description">${escapeHtml6(model.description)}</p>
3642
+ ${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${escapeHtml6(options.fallbackRequiredMessage ?? "Configure fallback providers before simulating failure.")}</p>`}
3510
3643
  <div class="absolute-voice-provider-simulation__actions">${failureButtons}${recoveryButtons}</div>
3511
- ${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${escapeHtml5(snapshot.error)}</p>` : ""}
3512
- ${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${escapeHtml5(model.resultText)}</pre>` : ""}
3644
+ ${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${escapeHtml6(snapshot.error)}</p>` : ""}
3645
+ ${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${escapeHtml6(model.resultText)}</pre>` : ""}
3513
3646
  </section>`;
3514
3647
  };
3515
3648
  var bindVoiceProviderSimulationControls = (element, store) => {
@@ -3576,7 +3709,7 @@ var defineVoiceProviderSimulationControlsElement = (tagName = "absolute-voice-pr
3576
3709
  // src/client/providerStatusWidget.ts
3577
3710
  var DEFAULT_TITLE5 = "Voice Providers";
3578
3711
  var DEFAULT_DESCRIPTION5 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
3579
- var escapeHtml6 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3712
+ var escapeHtml7 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3580
3713
  var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
3581
3714
  var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
3582
3715
  var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
@@ -3632,25 +3765,25 @@ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
3632
3765
  };
3633
3766
  var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
3634
3767
  const model = createVoiceProviderStatusViewModel(snapshot, options);
3635
- 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)}">
3768
+ 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)}">
3636
3769
  <header>
3637
- <strong>${escapeHtml6(provider.label)}</strong>
3638
- <span>${escapeHtml6(formatStatus(provider.status))}</span>
3770
+ <strong>${escapeHtml7(provider.label)}</strong>
3771
+ <span>${escapeHtml7(formatStatus(provider.status))}</span>
3639
3772
  </header>
3640
- <p>${escapeHtml6(provider.detail)}</p>
3773
+ <p>${escapeHtml7(provider.detail)}</p>
3641
3774
  <dl>${provider.rows.map((row) => `<div>
3642
- <dt>${escapeHtml6(row.label)}</dt>
3643
- <dd>${escapeHtml6(row.value)}</dd>
3775
+ <dt>${escapeHtml7(row.label)}</dt>
3776
+ <dd>${escapeHtml7(row.value)}</dd>
3644
3777
  </div>`).join("")}</dl>
3645
3778
  </article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
3646
- return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml6(model.status)}">
3779
+ return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml7(model.status)}">
3647
3780
  <header class="absolute-voice-provider-status__header">
3648
- <span class="absolute-voice-provider-status__eyebrow">${escapeHtml6(model.title)}</span>
3649
- <strong class="absolute-voice-provider-status__label">${escapeHtml6(model.label)}</strong>
3781
+ <span class="absolute-voice-provider-status__eyebrow">${escapeHtml7(model.title)}</span>
3782
+ <strong class="absolute-voice-provider-status__label">${escapeHtml7(model.label)}</strong>
3650
3783
  </header>
3651
- <p class="absolute-voice-provider-status__description">${escapeHtml6(model.description)}</p>
3784
+ <p class="absolute-voice-provider-status__description">${escapeHtml7(model.description)}</p>
3652
3785
  ${providers}
3653
- ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml6(model.error)}</p>` : ""}
3786
+ ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml7(model.error)}</p>` : ""}
3654
3787
  </section>`;
3655
3788
  };
3656
3789
  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}`;
@@ -3693,7 +3826,7 @@ var defineVoiceProviderStatusElement = (tagName = "absolute-voice-provider-statu
3693
3826
  // src/client/providerCapabilitiesWidget.ts
3694
3827
  var DEFAULT_TITLE6 = "Provider Capabilities";
3695
3828
  var DEFAULT_DESCRIPTION6 = "Configured, selected, and healthy voice providers for this deployment.";
3696
- var escapeHtml7 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3829
+ var escapeHtml8 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3697
3830
  var formatProvider2 = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
3698
3831
  var formatKind2 = (kind) => kind.toUpperCase();
3699
3832
  var formatStatus2 = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
@@ -3748,25 +3881,25 @@ var createVoiceProviderCapabilitiesViewModel = (snapshot, options = {}) => {
3748
3881
  };
3749
3882
  var renderVoiceProviderCapabilitiesHTML = (snapshot, options = {}) => {
3750
3883
  const model = createVoiceProviderCapabilitiesViewModel(snapshot, options);
3751
- const capabilities = model.capabilities.length ? `<div class="absolute-voice-provider-capabilities__providers">${model.capabilities.map((capability) => `<article class="absolute-voice-provider-capabilities__provider absolute-voice-provider-capabilities__provider--${escapeHtml7(capability.status)}">
3884
+ const capabilities = model.capabilities.length ? `<div class="absolute-voice-provider-capabilities__providers">${model.capabilities.map((capability) => `<article class="absolute-voice-provider-capabilities__provider absolute-voice-provider-capabilities__provider--${escapeHtml8(capability.status)}">
3752
3885
  <header>
3753
- <strong>${escapeHtml7(capability.label)}</strong>
3754
- <span>${escapeHtml7(formatStatus2(capability.status))}</span>
3886
+ <strong>${escapeHtml8(capability.label)}</strong>
3887
+ <span>${escapeHtml8(formatStatus2(capability.status))}</span>
3755
3888
  </header>
3756
- <p>${escapeHtml7(capability.detail)}</p>
3889
+ <p>${escapeHtml8(capability.detail)}</p>
3757
3890
  <dl>${capability.rows.map((row) => `<div>
3758
- <dt>${escapeHtml7(row.label)}</dt>
3759
- <dd>${escapeHtml7(row.value)}</dd>
3891
+ <dt>${escapeHtml8(row.label)}</dt>
3892
+ <dd>${escapeHtml8(row.value)}</dd>
3760
3893
  </div>`).join("")}</dl>
3761
3894
  </article>`).join("")}</div>` : '<p class="absolute-voice-provider-capabilities__empty">Configure provider capabilities to see deployment coverage.</p>';
3762
- return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${escapeHtml7(model.status)}">
3895
+ return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${escapeHtml8(model.status)}">
3763
3896
  <header class="absolute-voice-provider-capabilities__header">
3764
- <span class="absolute-voice-provider-capabilities__eyebrow">${escapeHtml7(model.title)}</span>
3765
- <strong class="absolute-voice-provider-capabilities__label">${escapeHtml7(model.label)}</strong>
3897
+ <span class="absolute-voice-provider-capabilities__eyebrow">${escapeHtml8(model.title)}</span>
3898
+ <strong class="absolute-voice-provider-capabilities__label">${escapeHtml8(model.label)}</strong>
3766
3899
  </header>
3767
- <p class="absolute-voice-provider-capabilities__description">${escapeHtml7(model.description)}</p>
3900
+ <p class="absolute-voice-provider-capabilities__description">${escapeHtml8(model.description)}</p>
3768
3901
  ${capabilities}
3769
- ${model.error ? `<p class="absolute-voice-provider-capabilities__error">${escapeHtml7(model.error)}</p>` : ""}
3902
+ ${model.error ? `<p class="absolute-voice-provider-capabilities__error">${escapeHtml8(model.error)}</p>` : ""}
3770
3903
  </section>`;
3771
3904
  };
3772
3905
  var getVoiceProviderCapabilitiesCSS = () => `.absolute-voice-provider-capabilities{border:1px solid #bfd7ea;border-radius:20px;background:#f6fbff;color:#08131f;padding:18px;box-shadow:0 18px 40px rgba(14,51,78,.12);font-family:inherit}.absolute-voice-provider-capabilities--error,.absolute-voice-provider-capabilities--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-provider-capabilities__header,.absolute-voice-provider-capabilities__provider header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-provider-capabilities__eyebrow{color:#255f85;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-provider-capabilities__label{font-size:24px;line-height:1}.absolute-voice-provider-capabilities__description,.absolute-voice-provider-capabilities__provider p,.absolute-voice-provider-capabilities__provider dt,.absolute-voice-provider-capabilities__empty{color:#405467}.absolute-voice-provider-capabilities__providers{display:grid;gap:12px;margin-top:14px}.absolute-voice-provider-capabilities__provider{background:#fff;border:1px solid #d7e7f3;border-radius:16px;padding:14px}.absolute-voice-provider-capabilities__provider--selected,.absolute-voice-provider-capabilities__provider--healthy{border-color:#86efac}.absolute-voice-provider-capabilities__provider--degraded,.absolute-voice-provider-capabilities__provider--rate-limited,.absolute-voice-provider-capabilities__provider--suppressed,.absolute-voice-provider-capabilities__provider--unconfigured{border-color:#f2a7a7}.absolute-voice-provider-capabilities__provider p{margin:10px 0}.absolute-voice-provider-capabilities__provider dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-provider-capabilities__provider div{background:#f6fbff;border:1px solid #d7e7f3;border-radius:12px;padding:8px}.absolute-voice-provider-capabilities__provider dt{font-size:12px}.absolute-voice-provider-capabilities__provider dd{font-weight:800;margin:4px 0 0}.absolute-voice-provider-capabilities__empty{margin:14px 0 0}.absolute-voice-provider-capabilities__error{color:#9f1239;font-weight:700}`;
@@ -3809,7 +3942,7 @@ var defineVoiceProviderCapabilitiesElement = (tagName = "absolute-voice-provider
3809
3942
  // src/client/turnQualityWidget.ts
3810
3943
  var DEFAULT_TITLE7 = "Turn Quality";
3811
3944
  var DEFAULT_DESCRIPTION7 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
3812
- var escapeHtml8 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3945
+ var escapeHtml9 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3813
3946
  var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
3814
3947
  var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
3815
3948
  var getTurnDetail = (turn) => {
@@ -3859,25 +3992,25 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
3859
3992
  };
3860
3993
  var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
3861
3994
  const model = createVoiceTurnQualityViewModel(snapshot, options);
3862
- const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${escapeHtml8(turn.status)}">
3995
+ 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--${escapeHtml9(turn.status)}">
3863
3996
  <header>
3864
- <strong>${escapeHtml8(turn.label)}</strong>
3865
- <span>${escapeHtml8(turn.status)}</span>
3997
+ <strong>${escapeHtml9(turn.label)}</strong>
3998
+ <span>${escapeHtml9(turn.status)}</span>
3866
3999
  </header>
3867
- <p>${escapeHtml8(turn.detail)}</p>
4000
+ <p>${escapeHtml9(turn.detail)}</p>
3868
4001
  <dl>${turn.rows.map((row) => `<div>
3869
- <dt>${escapeHtml8(row.label)}</dt>
3870
- <dd>${escapeHtml8(row.value)}</dd>
4002
+ <dt>${escapeHtml9(row.label)}</dt>
4003
+ <dd>${escapeHtml9(row.value)}</dd>
3871
4004
  </div>`).join("")}</dl>
3872
4005
  </article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
3873
- return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml8(model.status)}">
4006
+ return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml9(model.status)}">
3874
4007
  <header class="absolute-voice-turn-quality__header">
3875
- <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml8(model.title)}</span>
3876
- <strong class="absolute-voice-turn-quality__label">${escapeHtml8(model.label)}</strong>
4008
+ <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml9(model.title)}</span>
4009
+ <strong class="absolute-voice-turn-quality__label">${escapeHtml9(model.label)}</strong>
3877
4010
  </header>
3878
- <p class="absolute-voice-turn-quality__description">${escapeHtml8(model.description)}</p>
4011
+ <p class="absolute-voice-turn-quality__description">${escapeHtml9(model.description)}</p>
3879
4012
  ${turns}
3880
- ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml8(model.error)}</p>` : ""}
4013
+ ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml9(model.error)}</p>` : ""}
3881
4014
  </section>`;
3882
4015
  };
3883
4016
  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}`;
@@ -3921,7 +4054,7 @@ var defineVoiceTurnQualityElement = (tagName = "absolute-voice-turn-quality") =>
3921
4054
  var DEFAULT_TITLE8 = "Turn Latency";
3922
4055
  var DEFAULT_DESCRIPTION8 = "Per-turn timing from first transcript to commit and assistant response start.";
3923
4056
  var DEFAULT_PROOF_LABEL = "Run latency proof";
3924
- var escapeHtml9 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
4057
+ var escapeHtml10 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3925
4058
  var formatMs = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
3926
4059
  var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
3927
4060
  const turns = (snapshot.report?.turns ?? []).map((turn) => ({
@@ -3949,25 +4082,25 @@ var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
3949
4082
  };
3950
4083
  var renderVoiceTurnLatencyHTML = (snapshot, options = {}) => {
3951
4084
  const model = createVoiceTurnLatencyViewModel(snapshot, options);
3952
- 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)}">
4085
+ 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)}">
3953
4086
  <header>
3954
- <strong>${escapeHtml9(turn.label)}</strong>
3955
- <span>${escapeHtml9(turn.status)}</span>
4087
+ <strong>${escapeHtml10(turn.label)}</strong>
4088
+ <span>${escapeHtml10(turn.status)}</span>
3956
4089
  </header>
3957
4090
  <dl>${turn.rows.map((row) => `<div>
3958
- <dt>${escapeHtml9(row.label)}</dt>
3959
- <dd>${escapeHtml9(row.value)}</dd>
4091
+ <dt>${escapeHtml10(row.label)}</dt>
4092
+ <dd>${escapeHtml10(row.value)}</dd>
3960
4093
  </div>`).join("")}</dl>
3961
4094
  </article>`).join("")}</div>` : '<p class="absolute-voice-turn-latency__empty">Complete a voice turn to see latency diagnostics.</p>';
3962
- return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml9(model.status)}">
4095
+ return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml10(model.status)}">
3963
4096
  <header class="absolute-voice-turn-latency__header">
3964
- <span class="absolute-voice-turn-latency__eyebrow">${escapeHtml9(model.title)}</span>
3965
- <strong class="absolute-voice-turn-latency__label">${escapeHtml9(model.label)}</strong>
4097
+ <span class="absolute-voice-turn-latency__eyebrow">${escapeHtml10(model.title)}</span>
4098
+ <strong class="absolute-voice-turn-latency__label">${escapeHtml10(model.label)}</strong>
3966
4099
  </header>
3967
- <p class="absolute-voice-turn-latency__description">${escapeHtml9(model.description)}</p>
3968
- ${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${escapeHtml9(model.proofLabel ?? DEFAULT_PROOF_LABEL)}</button>` : ""}
4100
+ <p class="absolute-voice-turn-latency__description">${escapeHtml10(model.description)}</p>
4101
+ ${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${escapeHtml10(model.proofLabel ?? DEFAULT_PROOF_LABEL)}</button>` : ""}
3969
4102
  ${turns}
3970
- ${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml9(model.error)}</p>` : ""}
4103
+ ${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml10(model.error)}</p>` : ""}
3971
4104
  </section>`;
3972
4105
  };
3973
4106
  var mountVoiceTurnLatency = (element, path = "/api/turn-latency", options = {}) => {
@@ -4019,7 +4152,7 @@ var defineVoiceTurnLatencyElement = (tagName = "absolute-voice-turn-latency") =>
4019
4152
  // src/client/traceTimelineWidget.ts
4020
4153
  var DEFAULT_TITLE9 = "Voice Traces";
4021
4154
  var DEFAULT_DESCRIPTION9 = "Latest call timelines with provider latency, fallbacks, handoffs, and errors from your self-hosted trace store.";
4022
- var escapeHtml10 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
4155
+ var escapeHtml11 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
4023
4156
  var formatMs2 = (value) => typeof value === "number" ? `${value}ms` : "n/a";
4024
4157
  var formatProviders = (session) => session.providers.length ? session.providers.map((provider) => provider.provider).join(", ") : "No providers";
4025
4158
  var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
@@ -4045,22 +4178,22 @@ var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
4045
4178
  };
4046
4179
  var renderVoiceTraceTimelineWidgetHTML = (snapshot, options = {}) => {
4047
4180
  const model = createVoiceTraceTimelineViewModel(snapshot, options);
4048
- 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--${escapeHtml10(session.status)}">
4181
+ 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--${escapeHtml11(session.status)}">
4049
4182
  <header>
4050
- <strong>${escapeHtml10(session.sessionId)}</strong>
4051
- <span>${escapeHtml10(session.status)}</span>
4183
+ <strong>${escapeHtml11(session.sessionId)}</strong>
4184
+ <span>${escapeHtml11(session.status)}</span>
4052
4185
  </header>
4053
- <p>${escapeHtml10(session.label)} \xB7 ${escapeHtml10(session.durationLabel)} \xB7 ${escapeHtml10(session.providerLabel)}</p>
4054
- <a href="${escapeHtml10(session.detailHref)}">Open timeline</a>
4186
+ <p>${escapeHtml11(session.label)} \xB7 ${escapeHtml11(session.durationLabel)} \xB7 ${escapeHtml11(session.providerLabel)}</p>
4187
+ <a href="${escapeHtml11(session.detailHref)}">Open timeline</a>
4055
4188
  </article>`).join("")}</div>` : '<p class="absolute-voice-trace-timeline__empty">Run a voice session to see call timelines.</p>';
4056
- return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml10(model.status)}">
4189
+ return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml11(model.status)}">
4057
4190
  <header class="absolute-voice-trace-timeline__header">
4058
- <span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml10(model.title)}</span>
4059
- <strong class="absolute-voice-trace-timeline__label">${escapeHtml10(model.label)}</strong>
4191
+ <span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml11(model.title)}</span>
4192
+ <strong class="absolute-voice-trace-timeline__label">${escapeHtml11(model.label)}</strong>
4060
4193
  </header>
4061
- <p class="absolute-voice-trace-timeline__description">${escapeHtml10(model.description)}</p>
4194
+ <p class="absolute-voice-trace-timeline__description">${escapeHtml11(model.description)}</p>
4062
4195
  ${sessions}
4063
- ${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml10(model.error)}</p>` : ""}
4196
+ ${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml11(model.error)}</p>` : ""}
4064
4197
  </section>`;
4065
4198
  };
4066
4199
  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}`;
@@ -4194,8 +4327,10 @@ export {
4194
4327
  renderVoiceProviderSimulationControlsHTML,
4195
4328
  renderVoiceProviderCapabilitiesHTML,
4196
4329
  renderVoiceOpsStatusHTML,
4330
+ renderVoiceOpsActionHistoryWidgetHTML,
4197
4331
  renderVoiceOpsActionCenterHTML,
4198
4332
  renderVoiceDeliveryRuntimeHTML,
4333
+ recordVoiceOpsActionResult,
4199
4334
  mountVoiceTurnQuality,
4200
4335
  mountVoiceTurnLatency,
4201
4336
  mountVoiceTraceTimeline,
@@ -4204,6 +4339,7 @@ export {
4204
4339
  mountVoiceProviderSimulationControls,
4205
4340
  mountVoiceProviderCapabilities,
4206
4341
  mountVoiceOpsStatus,
4342
+ mountVoiceOpsActionHistory,
4207
4343
  mountVoiceOpsActionCenter,
4208
4344
  mountVoiceDeliveryRuntime,
4209
4345
  getVoiceTurnQualityCSS,
@@ -4213,6 +4349,7 @@ export {
4213
4349
  getVoiceProviderCapabilitiesCSS,
4214
4350
  getVoiceOpsStatusLabel,
4215
4351
  getVoiceOpsStatusCSS,
4352
+ getVoiceOpsActionHistoryCSS,
4216
4353
  getVoiceOpsActionCenterCSS,
4217
4354
  getVoiceDeliveryRuntimeCSS,
4218
4355
  fetchVoiceWorkflowStatus,
@@ -4223,6 +4360,7 @@ export {
4223
4360
  fetchVoiceProviderStatus,
4224
4361
  fetchVoiceProviderCapabilities,
4225
4362
  fetchVoiceOpsStatus,
4363
+ fetchVoiceOpsActionHistory,
4226
4364
  fetchVoiceDeliveryRuntime,
4227
4365
  fetchVoiceCampaignDialerProofStatus,
4228
4366
  defineVoiceTurnQualityElement,
@@ -4254,6 +4392,7 @@ export {
4254
4392
  createVoiceProviderCapabilitiesStore,
4255
4393
  createVoiceOpsStatusViewModel,
4256
4394
  createVoiceOpsStatusStore,
4395
+ createVoiceOpsActionHistoryStore,
4257
4396
  createVoiceOpsActionCenterViewModel,
4258
4397
  createVoiceOpsActionCenterStore,
4259
4398
  createVoiceOpsActionCenterActions,
@@ -9,16 +9,20 @@ export type VoiceOpsActionDescriptor = {
9
9
  };
10
10
  export type VoiceOpsActionCenterClientOptions = {
11
11
  actions?: VoiceOpsActionDescriptor[];
12
+ auditPath?: false | string;
12
13
  fetch?: typeof fetch;
13
14
  intervalMs?: number;
15
+ onActionResult?: (result: VoiceOpsActionRunResult) => Promise<void> | void;
14
16
  };
15
17
  export type VoiceOpsActionRunResult = {
16
18
  actionId: string;
17
19
  body: unknown;
20
+ error?: string;
18
21
  ok: boolean;
19
22
  ranAt: number;
20
23
  status: number;
21
24
  };
25
+ export declare const recordVoiceOpsActionResult: (result: VoiceOpsActionRunResult, options?: VoiceOpsActionCenterClientOptions) => Promise<void>;
22
26
  export type VoiceOpsActionCenterSnapshot = {
23
27
  actions: VoiceOpsActionDescriptor[];
24
28
  error: string | null;
@@ -0,0 +1,19 @@
1
+ import type { VoiceOpsActionHistoryReport } from '../opsActionAuditRoutes';
2
+ export type VoiceOpsActionHistoryClientOptions = {
3
+ fetch?: typeof fetch;
4
+ intervalMs?: number;
5
+ };
6
+ export type VoiceOpsActionHistorySnapshot = {
7
+ error: string | null;
8
+ isLoading: boolean;
9
+ report?: VoiceOpsActionHistoryReport;
10
+ updatedAt?: number;
11
+ };
12
+ export declare const fetchVoiceOpsActionHistory: (path?: string, options?: Pick<VoiceOpsActionHistoryClientOptions, "fetch">) => Promise<VoiceOpsActionHistoryReport>;
13
+ export declare const createVoiceOpsActionHistoryStore: (path?: string, options?: VoiceOpsActionHistoryClientOptions) => {
14
+ close: () => void;
15
+ getServerSnapshot: () => VoiceOpsActionHistorySnapshot;
16
+ getSnapshot: () => VoiceOpsActionHistorySnapshot;
17
+ refresh: () => Promise<VoiceOpsActionHistoryReport | undefined>;
18
+ subscribe: (listener: () => void) => () => void;
19
+ };