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

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.
@@ -2062,6 +2062,167 @@ var createVoiceOpsStatusStore = (path = "/api/voice/ops-status", options = {}) =
2062
2062
  }
2063
2063
  };
2064
2064
  };
2065
+ // src/client/opsActionCenter.ts
2066
+ var createVoiceOpsActionCenterActions = (options = {}) => {
2067
+ const deliveryRuntimePath = options.deliveryRuntimePath ?? "/api/voice-delivery-runtime";
2068
+ const actions = [];
2069
+ if (options.includeProductionReadiness !== false) {
2070
+ actions.push({
2071
+ description: "Refresh the production readiness report.",
2072
+ id: "production-readiness",
2073
+ label: "Refresh readiness",
2074
+ method: "GET",
2075
+ path: options.productionReadinessPath ?? "/api/production-readiness"
2076
+ });
2077
+ }
2078
+ if (options.includeDeliveryRuntime !== false) {
2079
+ actions.push({
2080
+ description: "Drain pending and failed audit/trace deliveries.",
2081
+ id: "delivery-runtime.tick",
2082
+ label: "Tick delivery workers",
2083
+ method: "POST",
2084
+ path: `${deliveryRuntimePath.replace(/\/$/, "")}/tick`
2085
+ }, {
2086
+ description: "Move reviewed dead letters back to live delivery queues.",
2087
+ id: "delivery-runtime.requeue-dead-letters",
2088
+ label: "Requeue dead letters",
2089
+ method: "POST",
2090
+ path: `${deliveryRuntimePath.replace(/\/$/, "")}/requeue-dead-letters`
2091
+ });
2092
+ }
2093
+ if (options.includeTurnLatencyProof !== false) {
2094
+ actions.push({
2095
+ description: "Run the synthetic turn latency proof.",
2096
+ id: "turn-latency.proof",
2097
+ label: "Run latency proof",
2098
+ method: "POST",
2099
+ path: options.turnLatencyProofPath ?? "/api/turn-latency/proof"
2100
+ });
2101
+ }
2102
+ if (options.includeProviderSimulation !== false) {
2103
+ const pathPrefix = options.providerSimulationPathPrefix ?? "/api/stt-simulate";
2104
+ for (const provider of options.providers ?? []) {
2105
+ actions.push({
2106
+ description: `Simulate ${provider} provider failure.`,
2107
+ id: `provider.${provider}.failure`,
2108
+ label: `Simulate ${provider} failure`,
2109
+ method: "POST",
2110
+ path: `${pathPrefix}/failure?provider=${encodeURIComponent(provider)}`
2111
+ }, {
2112
+ description: `Mark ${provider} provider recovered.`,
2113
+ id: `provider.${provider}.recovery`,
2114
+ label: `Recover ${provider}`,
2115
+ method: "POST",
2116
+ path: `${pathPrefix}/recovery?provider=${encodeURIComponent(provider)}`
2117
+ });
2118
+ }
2119
+ }
2120
+ return actions;
2121
+ };
2122
+ var runVoiceOpsAction = async (action, options = {}) => {
2123
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2124
+ const response = await fetchImpl(action.path, {
2125
+ method: action.method ?? "POST"
2126
+ });
2127
+ const body = await response.json().catch(() => null);
2128
+ if (!response.ok) {
2129
+ const message = body && typeof body === "object" && "error" in body ? String(body.error) : `Voice ops action "${action.id}" failed: HTTP ${response.status}`;
2130
+ throw new Error(message);
2131
+ }
2132
+ return {
2133
+ actionId: action.id,
2134
+ body,
2135
+ ok: response.ok,
2136
+ ranAt: Date.now(),
2137
+ status: response.status
2138
+ };
2139
+ };
2140
+ var createVoiceOpsActionCenterStore = (options = {}) => {
2141
+ const listeners = new Set;
2142
+ let closed = false;
2143
+ let timer;
2144
+ let snapshot = {
2145
+ actions: options.actions ?? createVoiceOpsActionCenterActions(),
2146
+ error: null,
2147
+ isRunning: false
2148
+ };
2149
+ const emit = () => {
2150
+ for (const listener of listeners) {
2151
+ listener();
2152
+ }
2153
+ };
2154
+ const setActions = (actions) => {
2155
+ snapshot = { ...snapshot, actions, updatedAt: Date.now() };
2156
+ emit();
2157
+ };
2158
+ const run = async (actionId) => {
2159
+ if (closed) {
2160
+ return snapshot.lastResult;
2161
+ }
2162
+ const action = snapshot.actions.find((item) => item.id === actionId);
2163
+ if (!action) {
2164
+ throw new Error(`Voice ops action "${actionId}" is not configured.`);
2165
+ }
2166
+ if (action.disabled) {
2167
+ throw new Error(`Voice ops action "${actionId}" is disabled.`);
2168
+ }
2169
+ snapshot = {
2170
+ ...snapshot,
2171
+ error: null,
2172
+ isRunning: true,
2173
+ runningActionId: action.id
2174
+ };
2175
+ emit();
2176
+ try {
2177
+ const result = await runVoiceOpsAction(action, options);
2178
+ snapshot = {
2179
+ ...snapshot,
2180
+ error: null,
2181
+ isRunning: false,
2182
+ lastResult: result,
2183
+ runningActionId: undefined,
2184
+ updatedAt: Date.now()
2185
+ };
2186
+ emit();
2187
+ return result;
2188
+ } catch (error) {
2189
+ snapshot = {
2190
+ ...snapshot,
2191
+ error: error instanceof Error ? error.message : String(error),
2192
+ isRunning: false,
2193
+ runningActionId: undefined
2194
+ };
2195
+ emit();
2196
+ throw error;
2197
+ }
2198
+ };
2199
+ const close = () => {
2200
+ closed = true;
2201
+ if (timer) {
2202
+ clearInterval(timer);
2203
+ timer = undefined;
2204
+ }
2205
+ listeners.clear();
2206
+ };
2207
+ if (options.intervalMs && options.intervalMs > 0) {
2208
+ timer = setInterval(() => {
2209
+ emit();
2210
+ }, options.intervalMs);
2211
+ }
2212
+ return {
2213
+ close,
2214
+ getServerSnapshot: () => snapshot,
2215
+ getSnapshot: () => snapshot,
2216
+ run,
2217
+ setActions,
2218
+ subscribe: (listener) => {
2219
+ listeners.add(listener);
2220
+ return () => {
2221
+ listeners.delete(listener);
2222
+ };
2223
+ }
2224
+ };
2225
+ };
2065
2226
  // src/client/deliveryRuntime.ts
2066
2227
  var getDefaultActionPath = (path, action, options) => {
2067
2228
  if (action === "tick") {
@@ -2322,10 +2483,97 @@ var defineVoiceOpsStatusElement = (tagName = "absolute-voice-ops-status") => {
2322
2483
  }
2323
2484
  });
2324
2485
  };
2325
- // src/client/deliveryRuntimeWidget.ts
2326
- var DEFAULT_TITLE2 = "Voice Delivery Runtime";
2327
- var DEFAULT_DESCRIPTION2 = "Audit and trace delivery worker health from your AbsoluteJS voice app.";
2486
+ // src/client/opsActionCenterWidget.ts
2487
+ var DEFAULT_TITLE2 = "Voice Ops Action Center";
2488
+ var DEFAULT_DESCRIPTION2 = "Run production voice proofs and operator actions from one primitive panel.";
2328
2489
  var escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2490
+ var createVoiceOpsActionCenterViewModel = (snapshot, options = {}) => {
2491
+ const status = snapshot.error ? "error" : snapshot.isRunning ? "running" : snapshot.lastResult ? "completed" : "ready";
2492
+ return {
2493
+ actions: snapshot.actions.map((action) => ({
2494
+ description: action.description ?? "",
2495
+ disabled: Boolean(action.disabled || snapshot.isRunning),
2496
+ id: action.id,
2497
+ isRunning: snapshot.runningActionId === action.id,
2498
+ label: action.label
2499
+ })),
2500
+ description: options.description ?? DEFAULT_DESCRIPTION2,
2501
+ error: snapshot.error,
2502
+ isRunning: snapshot.isRunning,
2503
+ label: status === "error" ? "Needs attention" : status === "running" ? "Running" : status === "completed" ? "Action completed" : "Ready",
2504
+ lastResultLabel: snapshot.lastResult ? `${snapshot.lastResult.actionId} returned HTTP ${snapshot.lastResult.status}` : "No action has run yet.",
2505
+ status,
2506
+ title: options.title ?? DEFAULT_TITLE2
2507
+ };
2508
+ };
2509
+ var renderVoiceOpsActionCenterHTML = (snapshot, options = {}) => {
2510
+ const model = createVoiceOpsActionCenterViewModel(snapshot, options);
2511
+ const actions = model.actions.map((action) => `<button type="button" data-absolute-voice-ops-action="${escapeHtml2(action.id)}"${action.disabled ? " disabled" : ""}>
2512
+ ${escapeHtml2(action.isRunning ? "Working..." : action.label)}
2513
+ </button>`).join("");
2514
+ return `<section class="absolute-voice-ops-action-center absolute-voice-ops-action-center--${escapeHtml2(model.status)}">
2515
+ <header class="absolute-voice-ops-action-center__header">
2516
+ <span class="absolute-voice-ops-action-center__eyebrow">${escapeHtml2(model.title)}</span>
2517
+ <strong class="absolute-voice-ops-action-center__label">${escapeHtml2(model.label)}</strong>
2518
+ </header>
2519
+ <p class="absolute-voice-ops-action-center__description">${escapeHtml2(model.description)}</p>
2520
+ <div class="absolute-voice-ops-action-center__actions">${actions}</div>
2521
+ <p class="absolute-voice-ops-action-center__result">${escapeHtml2(model.lastResultLabel)}</p>
2522
+ ${model.error ? `<p class="absolute-voice-ops-action-center__error">${escapeHtml2(model.error)}</p>` : ""}
2523
+ </section>`;
2524
+ };
2525
+ var getVoiceOpsActionCenterCSS = () => `.absolute-voice-ops-action-center{border:1px solid #d5cbb8;border-radius:20px;background:#fffaf1;color:#17130b;padding:18px;box-shadow:0 18px 40px rgba(58,42,16,.12);font-family:inherit}.absolute-voice-ops-action-center--error{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-ops-action-center__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-ops-action-center__eyebrow{color:#725d37;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-ops-action-center__label{font-size:28px;line-height:1}.absolute-voice-ops-action-center__description,.absolute-voice-ops-action-center__result{color:#5b4b2f;margin:12px 0 0}.absolute-voice-ops-action-center__actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}.absolute-voice-ops-action-center__actions button{background:#7c4a03;border:0;border-radius:999px;color:#fff8e8;cursor:pointer;font:inherit;font-weight:800;padding:8px 12px}.absolute-voice-ops-action-center__actions button:disabled{cursor:not-allowed;opacity:.5}.absolute-voice-ops-action-center__error{color:#9f1239;font-weight:700}`;
2526
+ var mountVoiceOpsActionCenter = (element, options = {}) => {
2527
+ const store = createVoiceOpsActionCenterStore(options);
2528
+ const render = () => {
2529
+ element.innerHTML = renderVoiceOpsActionCenterHTML(store.getSnapshot(), options);
2530
+ };
2531
+ const unsubscribe = store.subscribe(render);
2532
+ const handleClick = (event) => {
2533
+ const target = event.target;
2534
+ if (!(target instanceof Element)) {
2535
+ return;
2536
+ }
2537
+ const action = target.closest("[data-absolute-voice-ops-action]");
2538
+ const actionId = action?.getAttribute("data-absolute-voice-ops-action");
2539
+ if (actionId) {
2540
+ store.run(actionId).catch(() => {});
2541
+ }
2542
+ };
2543
+ element.addEventListener?.("click", handleClick);
2544
+ render();
2545
+ return {
2546
+ close: () => {
2547
+ element.removeEventListener?.("click", handleClick);
2548
+ unsubscribe();
2549
+ store.close();
2550
+ },
2551
+ run: store.run
2552
+ };
2553
+ };
2554
+ var defineVoiceOpsActionCenterElement = (tagName = "absolute-voice-ops-action-center", options = {}) => {
2555
+ if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
2556
+ return;
2557
+ }
2558
+ customElements.define(tagName, class AbsoluteVoiceOpsActionCenterElement extends HTMLElement {
2559
+ mounted;
2560
+ connectedCallback() {
2561
+ this.mounted = mountVoiceOpsActionCenter(this, {
2562
+ ...options,
2563
+ description: this.getAttribute("description") ?? options.description,
2564
+ title: this.getAttribute("title") ?? options.title
2565
+ });
2566
+ }
2567
+ disconnectedCallback() {
2568
+ this.mounted?.close();
2569
+ this.mounted = undefined;
2570
+ }
2571
+ });
2572
+ };
2573
+ // src/client/deliveryRuntimeWidget.ts
2574
+ var DEFAULT_TITLE3 = "Voice Delivery Runtime";
2575
+ 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;");
2329
2577
  var createSurface = (id, summary) => {
2330
2578
  if (!summary) {
2331
2579
  return {
@@ -2359,7 +2607,7 @@ var createVoiceDeliveryRuntimeViewModel = (snapshot, options = {}) => {
2359
2607
  ];
2360
2608
  const hasWarnings = surfaces.some((surface) => surface.status === "warn");
2361
2609
  return {
2362
- description: options.description ?? DEFAULT_DESCRIPTION2,
2610
+ description: options.description ?? DEFAULT_DESCRIPTION3,
2363
2611
  error: snapshot.error,
2364
2612
  actionError: snapshot.actionError,
2365
2613
  actionStatus: snapshot.actionStatus,
@@ -2368,32 +2616,32 @@ var createVoiceDeliveryRuntimeViewModel = (snapshot, options = {}) => {
2368
2616
  label: snapshot.error ? "Unavailable" : report ? report.isRunning ? "Running" : "Stopped" : "Checking",
2369
2617
  status: snapshot.error ? "error" : report ? hasWarnings ? "warn" : "pass" : "loading",
2370
2618
  surfaces,
2371
- title: options.title ?? DEFAULT_TITLE2,
2619
+ title: options.title ?? DEFAULT_TITLE3,
2372
2620
  updatedAt: snapshot.updatedAt
2373
2621
  };
2374
2622
  };
2375
2623
  var renderVoiceDeliveryRuntimeHTML = (snapshot, options = {}) => {
2376
2624
  const model = createVoiceDeliveryRuntimeViewModel(snapshot, options);
2377
- const surfaces = model.surfaces.map((surface) => `<li class="absolute-voice-delivery-runtime__surface absolute-voice-delivery-runtime__surface--${escapeHtml2(surface.status)}">
2378
- <span>${escapeHtml2(surface.label)}</span>
2379
- <strong>${escapeHtml2(surface.detail)}</strong>
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>
2380
2628
  <small>${String(surface.failed)} failed &middot; ${String(surface.deadLettered)} dead-lettered</small>
2381
2629
  </li>`).join("");
2382
2630
  const actions = options.includeActions === false ? "" : `<div class="absolute-voice-delivery-runtime__actions">
2383
2631
  <button type="button" data-absolute-voice-delivery-runtime-action="tick">${model.actionStatus === "running" ? "Working..." : "Tick workers"}</button>
2384
2632
  <button type="button" data-absolute-voice-delivery-runtime-action="requeue-dead-letters"${model.surfaces.some((surface) => surface.deadLettered > 0) ? "" : " disabled"}>Requeue dead letters</button>
2385
2633
  </div>`;
2386
- const actionError = model.actionError ? `<p class="absolute-voice-delivery-runtime__error">${escapeHtml2(model.actionError)}</p>` : "";
2387
- return `<section class="absolute-voice-delivery-runtime absolute-voice-delivery-runtime--${escapeHtml2(model.status)}">
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)}">
2388
2636
  <header class="absolute-voice-delivery-runtime__header">
2389
- <span class="absolute-voice-delivery-runtime__eyebrow">${escapeHtml2(model.title)}</span>
2390
- <strong class="absolute-voice-delivery-runtime__label">${escapeHtml2(model.label)}</strong>
2637
+ <span class="absolute-voice-delivery-runtime__eyebrow">${escapeHtml3(model.title)}</span>
2638
+ <strong class="absolute-voice-delivery-runtime__label">${escapeHtml3(model.label)}</strong>
2391
2639
  </header>
2392
- <p class="absolute-voice-delivery-runtime__description">${escapeHtml2(model.description)}</p>
2640
+ <p class="absolute-voice-delivery-runtime__description">${escapeHtml3(model.description)}</p>
2393
2641
  <ul class="absolute-voice-delivery-runtime__surfaces">${surfaces}</ul>
2394
2642
  ${actions}
2395
2643
  ${actionError}
2396
- ${model.error ? `<p class="absolute-voice-delivery-runtime__error">${escapeHtml2(model.error)}</p>` : ""}
2644
+ ${model.error ? `<p class="absolute-voice-delivery-runtime__error">${escapeHtml3(model.error)}</p>` : ""}
2397
2645
  </section>`;
2398
2646
  };
2399
2647
  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}`;
@@ -2529,9 +2777,9 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
2529
2777
  };
2530
2778
  };
2531
2779
  // src/client/routingStatusWidget.ts
2532
- var DEFAULT_TITLE3 = "Voice Routing";
2533
- var DEFAULT_DESCRIPTION3 = "Latest provider routing decision from the self-hosted trace store.";
2534
- var escapeHtml3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2780
+ var DEFAULT_TITLE4 = "Voice Routing";
2781
+ 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;");
2535
2783
  var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
2536
2784
  var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
2537
2785
  const decision = snapshot.decision;
@@ -2555,30 +2803,30 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
2555
2803
  ] : [];
2556
2804
  return {
2557
2805
  decision,
2558
- description: options.description ?? DEFAULT_DESCRIPTION3,
2806
+ description: options.description ?? DEFAULT_DESCRIPTION4,
2559
2807
  error: snapshot.error,
2560
2808
  isLoading: snapshot.isLoading,
2561
2809
  label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
2562
2810
  rows,
2563
2811
  status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
2564
- title: options.title ?? DEFAULT_TITLE3,
2812
+ title: options.title ?? DEFAULT_TITLE4,
2565
2813
  updatedAt: snapshot.updatedAt
2566
2814
  };
2567
2815
  };
2568
2816
  var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
2569
2817
  const model = createVoiceRoutingStatusViewModel(snapshot, options);
2570
2818
  const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
2571
- <span>${escapeHtml3(row.label)}</span>
2572
- <strong>${escapeHtml3(row.value)}</strong>
2819
+ <span>${escapeHtml4(row.label)}</span>
2820
+ <strong>${escapeHtml4(row.value)}</strong>
2573
2821
  </div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
2574
- return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml3(model.status)}">
2822
+ return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml4(model.status)}">
2575
2823
  <header class="absolute-voice-routing-status__header">
2576
- <span class="absolute-voice-routing-status__eyebrow">${escapeHtml3(model.title)}</span>
2577
- <strong class="absolute-voice-routing-status__label">${escapeHtml3(model.label)}</strong>
2824
+ <span class="absolute-voice-routing-status__eyebrow">${escapeHtml4(model.title)}</span>
2825
+ <strong class="absolute-voice-routing-status__label">${escapeHtml4(model.label)}</strong>
2578
2826
  </header>
2579
- <p class="absolute-voice-routing-status__description">${escapeHtml3(model.description)}</p>
2827
+ <p class="absolute-voice-routing-status__description">${escapeHtml4(model.description)}</p>
2580
2828
  ${rows}
2581
- ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml3(model.error)}</p>` : ""}
2829
+ ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml4(model.error)}</p>` : ""}
2582
2830
  </section>`;
2583
2831
  };
2584
2832
  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}`;
@@ -3230,7 +3478,7 @@ var createVoiceProviderSimulationControlsStore = (options) => {
3230
3478
  };
3231
3479
  };
3232
3480
  // src/client/providerSimulationControlsWidget.ts
3233
- var escapeHtml4 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3481
+ var escapeHtml5 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3234
3482
  var formatKind = (kind) => (kind ?? "stt").toUpperCase();
3235
3483
  var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
3236
3484
  const configuredProviders = options.providers.filter((provider) => provider.configured !== false);
@@ -3250,18 +3498,18 @@ var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
3250
3498
  };
3251
3499
  var renderVoiceProviderSimulationControlsHTML = (snapshot, options) => {
3252
3500
  const model = createVoiceProviderSimulationControlsViewModel(snapshot, options);
3253
- const failureButtons = model.failureProviders.map((provider) => `<button type="button" data-voice-provider-fail="${escapeHtml4(provider.provider)}"${!model.canSimulateFailure || snapshot.isRunning ? " disabled" : ""}>Simulate ${escapeHtml4(provider.provider)} ${escapeHtml4(formatKind(options.kind))} failure</button>`).join("");
3254
- const recoveryButtons = model.providers.map((provider) => `<button type="button" data-voice-provider-recover="${escapeHtml4(provider.provider)}"${snapshot.isRunning ? " disabled" : ""}>Mark ${escapeHtml4(provider.provider)} recovered</button>`).join("");
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("");
3255
3503
  return `<section class="absolute-voice-provider-simulation absolute-voice-provider-simulation--${snapshot.error ? "error" : snapshot.isRunning ? "running" : "ready"}">
3256
3504
  <header class="absolute-voice-provider-simulation__header">
3257
- <span class="absolute-voice-provider-simulation__eyebrow">${escapeHtml4(model.title)}</span>
3258
- <strong class="absolute-voice-provider-simulation__label">${escapeHtml4(model.label)}</strong>
3505
+ <span class="absolute-voice-provider-simulation__eyebrow">${escapeHtml5(model.title)}</span>
3506
+ <strong class="absolute-voice-provider-simulation__label">${escapeHtml5(model.label)}</strong>
3259
3507
  </header>
3260
- <p class="absolute-voice-provider-simulation__description">${escapeHtml4(model.description)}</p>
3261
- ${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${escapeHtml4(options.fallbackRequiredMessage ?? "Configure fallback providers before simulating failure.")}</p>`}
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>`}
3262
3510
  <div class="absolute-voice-provider-simulation__actions">${failureButtons}${recoveryButtons}</div>
3263
- ${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${escapeHtml4(snapshot.error)}</p>` : ""}
3264
- ${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${escapeHtml4(model.resultText)}</pre>` : ""}
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>` : ""}
3265
3513
  </section>`;
3266
3514
  };
3267
3515
  var bindVoiceProviderSimulationControls = (element, store) => {
@@ -3326,9 +3574,9 @@ var defineVoiceProviderSimulationControlsElement = (tagName = "absolute-voice-pr
3326
3574
  });
3327
3575
  };
3328
3576
  // src/client/providerStatusWidget.ts
3329
- var DEFAULT_TITLE4 = "Voice Providers";
3330
- var DEFAULT_DESCRIPTION4 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
3331
- var escapeHtml5 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3577
+ var DEFAULT_TITLE5 = "Voice Providers";
3578
+ 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;");
3332
3580
  var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
3333
3581
  var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
3334
3582
  var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
@@ -3372,37 +3620,37 @@ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
3372
3620
  const warningCount = providers.filter((provider) => isWarningStatus(provider.status)).length;
3373
3621
  const healthyCount = providers.filter((provider) => provider.status === "healthy").length;
3374
3622
  return {
3375
- description: options.description ?? DEFAULT_DESCRIPTION4,
3623
+ description: options.description ?? DEFAULT_DESCRIPTION5,
3376
3624
  error: snapshot.error,
3377
3625
  isLoading: snapshot.isLoading,
3378
3626
  label: snapshot.error ? "Unavailable" : providers.length ? warningCount > 0 ? `${warningCount} needs attention` : `${healthyCount} healthy` : snapshot.isLoading ? "Checking" : "No provider traffic",
3379
3627
  providers,
3380
3628
  status: snapshot.error ? "error" : providers.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
3381
- title: options.title ?? DEFAULT_TITLE4,
3629
+ title: options.title ?? DEFAULT_TITLE5,
3382
3630
  updatedAt: snapshot.updatedAt
3383
3631
  };
3384
3632
  };
3385
3633
  var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
3386
3634
  const model = createVoiceProviderStatusViewModel(snapshot, options);
3387
- 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--${escapeHtml5(provider.status)}">
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)}">
3388
3636
  <header>
3389
- <strong>${escapeHtml5(provider.label)}</strong>
3390
- <span>${escapeHtml5(formatStatus(provider.status))}</span>
3637
+ <strong>${escapeHtml6(provider.label)}</strong>
3638
+ <span>${escapeHtml6(formatStatus(provider.status))}</span>
3391
3639
  </header>
3392
- <p>${escapeHtml5(provider.detail)}</p>
3640
+ <p>${escapeHtml6(provider.detail)}</p>
3393
3641
  <dl>${provider.rows.map((row) => `<div>
3394
- <dt>${escapeHtml5(row.label)}</dt>
3395
- <dd>${escapeHtml5(row.value)}</dd>
3642
+ <dt>${escapeHtml6(row.label)}</dt>
3643
+ <dd>${escapeHtml6(row.value)}</dd>
3396
3644
  </div>`).join("")}</dl>
3397
3645
  </article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
3398
- return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml5(model.status)}">
3646
+ return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml6(model.status)}">
3399
3647
  <header class="absolute-voice-provider-status__header">
3400
- <span class="absolute-voice-provider-status__eyebrow">${escapeHtml5(model.title)}</span>
3401
- <strong class="absolute-voice-provider-status__label">${escapeHtml5(model.label)}</strong>
3648
+ <span class="absolute-voice-provider-status__eyebrow">${escapeHtml6(model.title)}</span>
3649
+ <strong class="absolute-voice-provider-status__label">${escapeHtml6(model.label)}</strong>
3402
3650
  </header>
3403
- <p class="absolute-voice-provider-status__description">${escapeHtml5(model.description)}</p>
3651
+ <p class="absolute-voice-provider-status__description">${escapeHtml6(model.description)}</p>
3404
3652
  ${providers}
3405
- ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml5(model.error)}</p>` : ""}
3653
+ ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml6(model.error)}</p>` : ""}
3406
3654
  </section>`;
3407
3655
  };
3408
3656
  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}`;
@@ -3443,9 +3691,9 @@ var defineVoiceProviderStatusElement = (tagName = "absolute-voice-provider-statu
3443
3691
  });
3444
3692
  };
3445
3693
  // src/client/providerCapabilitiesWidget.ts
3446
- var DEFAULT_TITLE5 = "Provider Capabilities";
3447
- var DEFAULT_DESCRIPTION5 = "Configured, selected, and healthy voice providers for this deployment.";
3448
- var escapeHtml6 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3694
+ var DEFAULT_TITLE6 = "Provider Capabilities";
3695
+ 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;");
3449
3697
  var formatProvider2 = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
3450
3698
  var formatKind2 = (kind) => kind.toUpperCase();
3451
3699
  var formatStatus2 = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
@@ -3489,36 +3737,36 @@ var createVoiceProviderCapabilitiesViewModel = (snapshot, options = {}) => {
3489
3737
  const selectedCount = snapshot.report?.selected ?? capabilities.filter((capability) => capability.selected).length;
3490
3738
  return {
3491
3739
  capabilities,
3492
- description: options.description ?? DEFAULT_DESCRIPTION5,
3740
+ description: options.description ?? DEFAULT_DESCRIPTION6,
3493
3741
  error: snapshot.error,
3494
3742
  isLoading: snapshot.isLoading,
3495
3743
  label: snapshot.error ? "Unavailable" : capabilities.length ? warningCount > 0 ? `${warningCount} needs attention` : `${selectedCount} selected` : snapshot.isLoading ? "Checking" : "No capabilities",
3496
3744
  status: snapshot.error ? "error" : capabilities.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
3497
- title: options.title ?? DEFAULT_TITLE5,
3745
+ title: options.title ?? DEFAULT_TITLE6,
3498
3746
  updatedAt: snapshot.updatedAt
3499
3747
  };
3500
3748
  };
3501
3749
  var renderVoiceProviderCapabilitiesHTML = (snapshot, options = {}) => {
3502
3750
  const model = createVoiceProviderCapabilitiesViewModel(snapshot, options);
3503
- 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--${escapeHtml6(capability.status)}">
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)}">
3504
3752
  <header>
3505
- <strong>${escapeHtml6(capability.label)}</strong>
3506
- <span>${escapeHtml6(formatStatus2(capability.status))}</span>
3753
+ <strong>${escapeHtml7(capability.label)}</strong>
3754
+ <span>${escapeHtml7(formatStatus2(capability.status))}</span>
3507
3755
  </header>
3508
- <p>${escapeHtml6(capability.detail)}</p>
3756
+ <p>${escapeHtml7(capability.detail)}</p>
3509
3757
  <dl>${capability.rows.map((row) => `<div>
3510
- <dt>${escapeHtml6(row.label)}</dt>
3511
- <dd>${escapeHtml6(row.value)}</dd>
3758
+ <dt>${escapeHtml7(row.label)}</dt>
3759
+ <dd>${escapeHtml7(row.value)}</dd>
3512
3760
  </div>`).join("")}</dl>
3513
3761
  </article>`).join("")}</div>` : '<p class="absolute-voice-provider-capabilities__empty">Configure provider capabilities to see deployment coverage.</p>';
3514
- return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${escapeHtml6(model.status)}">
3762
+ return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${escapeHtml7(model.status)}">
3515
3763
  <header class="absolute-voice-provider-capabilities__header">
3516
- <span class="absolute-voice-provider-capabilities__eyebrow">${escapeHtml6(model.title)}</span>
3517
- <strong class="absolute-voice-provider-capabilities__label">${escapeHtml6(model.label)}</strong>
3764
+ <span class="absolute-voice-provider-capabilities__eyebrow">${escapeHtml7(model.title)}</span>
3765
+ <strong class="absolute-voice-provider-capabilities__label">${escapeHtml7(model.label)}</strong>
3518
3766
  </header>
3519
- <p class="absolute-voice-provider-capabilities__description">${escapeHtml6(model.description)}</p>
3767
+ <p class="absolute-voice-provider-capabilities__description">${escapeHtml7(model.description)}</p>
3520
3768
  ${capabilities}
3521
- ${model.error ? `<p class="absolute-voice-provider-capabilities__error">${escapeHtml6(model.error)}</p>` : ""}
3769
+ ${model.error ? `<p class="absolute-voice-provider-capabilities__error">${escapeHtml7(model.error)}</p>` : ""}
3522
3770
  </section>`;
3523
3771
  };
3524
3772
  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}`;
@@ -3559,9 +3807,9 @@ var defineVoiceProviderCapabilitiesElement = (tagName = "absolute-voice-provider
3559
3807
  });
3560
3808
  };
3561
3809
  // src/client/turnQualityWidget.ts
3562
- var DEFAULT_TITLE6 = "Turn Quality";
3563
- var DEFAULT_DESCRIPTION6 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
3564
- var escapeHtml7 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3810
+ var DEFAULT_TITLE7 = "Turn Quality";
3811
+ 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;");
3565
3813
  var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
3566
3814
  var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
3567
3815
  var getTurnDetail = (turn) => {
@@ -3599,37 +3847,37 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
3599
3847
  const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
3600
3848
  const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
3601
3849
  return {
3602
- description: options.description ?? DEFAULT_DESCRIPTION6,
3850
+ description: options.description ?? DEFAULT_DESCRIPTION7,
3603
3851
  error: snapshot.error,
3604
3852
  isLoading: snapshot.isLoading,
3605
3853
  label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} failed` : warningCount > 0 ? `${warningCount} warnings` : `${turns.length} healthy` : snapshot.isLoading ? "Checking" : "No turns",
3606
3854
  status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
3607
- title: options.title ?? DEFAULT_TITLE6,
3855
+ title: options.title ?? DEFAULT_TITLE7,
3608
3856
  turns,
3609
3857
  updatedAt: snapshot.updatedAt
3610
3858
  };
3611
3859
  };
3612
3860
  var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
3613
3861
  const model = createVoiceTurnQualityViewModel(snapshot, options);
3614
- const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${escapeHtml7(turn.status)}">
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)}">
3615
3863
  <header>
3616
- <strong>${escapeHtml7(turn.label)}</strong>
3617
- <span>${escapeHtml7(turn.status)}</span>
3864
+ <strong>${escapeHtml8(turn.label)}</strong>
3865
+ <span>${escapeHtml8(turn.status)}</span>
3618
3866
  </header>
3619
- <p>${escapeHtml7(turn.detail)}</p>
3867
+ <p>${escapeHtml8(turn.detail)}</p>
3620
3868
  <dl>${turn.rows.map((row) => `<div>
3621
- <dt>${escapeHtml7(row.label)}</dt>
3622
- <dd>${escapeHtml7(row.value)}</dd>
3869
+ <dt>${escapeHtml8(row.label)}</dt>
3870
+ <dd>${escapeHtml8(row.value)}</dd>
3623
3871
  </div>`).join("")}</dl>
3624
3872
  </article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
3625
- return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml7(model.status)}">
3873
+ return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml8(model.status)}">
3626
3874
  <header class="absolute-voice-turn-quality__header">
3627
- <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml7(model.title)}</span>
3628
- <strong class="absolute-voice-turn-quality__label">${escapeHtml7(model.label)}</strong>
3875
+ <span class="absolute-voice-turn-quality__eyebrow">${escapeHtml8(model.title)}</span>
3876
+ <strong class="absolute-voice-turn-quality__label">${escapeHtml8(model.label)}</strong>
3629
3877
  </header>
3630
- <p class="absolute-voice-turn-quality__description">${escapeHtml7(model.description)}</p>
3878
+ <p class="absolute-voice-turn-quality__description">${escapeHtml8(model.description)}</p>
3631
3879
  ${turns}
3632
- ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml7(model.error)}</p>` : ""}
3880
+ ${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml8(model.error)}</p>` : ""}
3633
3881
  </section>`;
3634
3882
  };
3635
3883
  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}`;
@@ -3670,10 +3918,10 @@ var defineVoiceTurnQualityElement = (tagName = "absolute-voice-turn-quality") =>
3670
3918
  });
3671
3919
  };
3672
3920
  // src/client/turnLatencyWidget.ts
3673
- var DEFAULT_TITLE7 = "Turn Latency";
3674
- var DEFAULT_DESCRIPTION7 = "Per-turn timing from first transcript to commit and assistant response start.";
3921
+ var DEFAULT_TITLE8 = "Turn Latency";
3922
+ var DEFAULT_DESCRIPTION8 = "Per-turn timing from first transcript to commit and assistant response start.";
3675
3923
  var DEFAULT_PROOF_LABEL = "Run latency proof";
3676
- var escapeHtml8 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3924
+ var escapeHtml9 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3677
3925
  var formatMs = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
3678
3926
  var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
3679
3927
  const turns = (snapshot.report?.turns ?? []).map((turn) => ({
@@ -3687,39 +3935,39 @@ var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
3687
3935
  const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
3688
3936
  const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
3689
3937
  return {
3690
- description: options.description ?? DEFAULT_DESCRIPTION7,
3938
+ description: options.description ?? DEFAULT_DESCRIPTION8,
3691
3939
  error: snapshot.error,
3692
3940
  isLoading: snapshot.isLoading,
3693
3941
  label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} slow` : warningCount > 0 ? `${warningCount} warnings` : `avg ${formatMs(snapshot.report?.averageTotalMs)}` : snapshot.isLoading ? "Checking" : "No turns",
3694
3942
  proofLabel: options.proofPath ? options.proofLabel ?? DEFAULT_PROOF_LABEL : undefined,
3695
3943
  showProofAction: Boolean(options.proofPath),
3696
3944
  status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
3697
- title: options.title ?? DEFAULT_TITLE7,
3945
+ title: options.title ?? DEFAULT_TITLE8,
3698
3946
  turns,
3699
3947
  updatedAt: snapshot.updatedAt
3700
3948
  };
3701
3949
  };
3702
3950
  var renderVoiceTurnLatencyHTML = (snapshot, options = {}) => {
3703
3951
  const model = createVoiceTurnLatencyViewModel(snapshot, options);
3704
- 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--${escapeHtml8(turn.status)}">
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)}">
3705
3953
  <header>
3706
- <strong>${escapeHtml8(turn.label)}</strong>
3707
- <span>${escapeHtml8(turn.status)}</span>
3954
+ <strong>${escapeHtml9(turn.label)}</strong>
3955
+ <span>${escapeHtml9(turn.status)}</span>
3708
3956
  </header>
3709
3957
  <dl>${turn.rows.map((row) => `<div>
3710
- <dt>${escapeHtml8(row.label)}</dt>
3711
- <dd>${escapeHtml8(row.value)}</dd>
3958
+ <dt>${escapeHtml9(row.label)}</dt>
3959
+ <dd>${escapeHtml9(row.value)}</dd>
3712
3960
  </div>`).join("")}</dl>
3713
3961
  </article>`).join("")}</div>` : '<p class="absolute-voice-turn-latency__empty">Complete a voice turn to see latency diagnostics.</p>';
3714
- return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml8(model.status)}">
3962
+ return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml9(model.status)}">
3715
3963
  <header class="absolute-voice-turn-latency__header">
3716
- <span class="absolute-voice-turn-latency__eyebrow">${escapeHtml8(model.title)}</span>
3717
- <strong class="absolute-voice-turn-latency__label">${escapeHtml8(model.label)}</strong>
3964
+ <span class="absolute-voice-turn-latency__eyebrow">${escapeHtml9(model.title)}</span>
3965
+ <strong class="absolute-voice-turn-latency__label">${escapeHtml9(model.label)}</strong>
3718
3966
  </header>
3719
- <p class="absolute-voice-turn-latency__description">${escapeHtml8(model.description)}</p>
3720
- ${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${escapeHtml8(model.proofLabel ?? DEFAULT_PROOF_LABEL)}</button>` : ""}
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>` : ""}
3721
3969
  ${turns}
3722
- ${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml8(model.error)}</p>` : ""}
3970
+ ${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml9(model.error)}</p>` : ""}
3723
3971
  </section>`;
3724
3972
  };
3725
3973
  var mountVoiceTurnLatency = (element, path = "/api/turn-latency", options = {}) => {
@@ -3769,9 +4017,9 @@ var defineVoiceTurnLatencyElement = (tagName = "absolute-voice-turn-latency") =>
3769
4017
  });
3770
4018
  };
3771
4019
  // src/client/traceTimelineWidget.ts
3772
- var DEFAULT_TITLE8 = "Voice Traces";
3773
- var DEFAULT_DESCRIPTION8 = "Latest call timelines with provider latency, fallbacks, handoffs, and errors from your self-hosted trace store.";
3774
- var escapeHtml9 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
4020
+ var DEFAULT_TITLE9 = "Voice Traces";
4021
+ 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;");
3775
4023
  var formatMs2 = (value) => typeof value === "number" ? `${value}ms` : "n/a";
3776
4024
  var formatProviders = (session) => session.providers.length ? session.providers.map((provider) => provider.provider).join(", ") : "No providers";
3777
4025
  var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
@@ -3785,34 +4033,34 @@ var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
3785
4033
  const failed = sessions.filter((session) => session.status === "failed").length;
3786
4034
  const warnings = sessions.filter((session) => session.status === "warning").length;
3787
4035
  return {
3788
- description: options.description ?? DEFAULT_DESCRIPTION8,
4036
+ description: options.description ?? DEFAULT_DESCRIPTION9,
3789
4037
  error: snapshot.error,
3790
4038
  isLoading: snapshot.isLoading,
3791
4039
  label: snapshot.error ? "Unavailable" : failed > 0 ? `${failed} failed` : warnings > 0 ? `${warnings} warning` : sessions.length ? `${sessions.length} recent` : snapshot.isLoading ? "Checking" : "No traces yet",
3792
4040
  sessions,
3793
4041
  status: snapshot.error ? "error" : failed > 0 ? "failed" : warnings > 0 ? "warning" : sessions.length ? "ready" : snapshot.isLoading ? "loading" : "empty",
3794
- title: options.title ?? DEFAULT_TITLE8,
4042
+ title: options.title ?? DEFAULT_TITLE9,
3795
4043
  updatedAt: snapshot.updatedAt
3796
4044
  };
3797
4045
  };
3798
4046
  var renderVoiceTraceTimelineWidgetHTML = (snapshot, options = {}) => {
3799
4047
  const model = createVoiceTraceTimelineViewModel(snapshot, options);
3800
- 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)}">
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)}">
3801
4049
  <header>
3802
- <strong>${escapeHtml9(session.sessionId)}</strong>
3803
- <span>${escapeHtml9(session.status)}</span>
4050
+ <strong>${escapeHtml10(session.sessionId)}</strong>
4051
+ <span>${escapeHtml10(session.status)}</span>
3804
4052
  </header>
3805
- <p>${escapeHtml9(session.label)} \xB7 ${escapeHtml9(session.durationLabel)} \xB7 ${escapeHtml9(session.providerLabel)}</p>
3806
- <a href="${escapeHtml9(session.detailHref)}">Open timeline</a>
4053
+ <p>${escapeHtml10(session.label)} \xB7 ${escapeHtml10(session.durationLabel)} \xB7 ${escapeHtml10(session.providerLabel)}</p>
4054
+ <a href="${escapeHtml10(session.detailHref)}">Open timeline</a>
3807
4055
  </article>`).join("")}</div>` : '<p class="absolute-voice-trace-timeline__empty">Run a voice session to see call timelines.</p>';
3808
- return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml9(model.status)}">
4056
+ return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml10(model.status)}">
3809
4057
  <header class="absolute-voice-trace-timeline__header">
3810
- <span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml9(model.title)}</span>
3811
- <strong class="absolute-voice-trace-timeline__label">${escapeHtml9(model.label)}</strong>
4058
+ <span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml10(model.title)}</span>
4059
+ <strong class="absolute-voice-trace-timeline__label">${escapeHtml10(model.label)}</strong>
3812
4060
  </header>
3813
- <p class="absolute-voice-trace-timeline__description">${escapeHtml9(model.description)}</p>
4061
+ <p class="absolute-voice-trace-timeline__description">${escapeHtml10(model.description)}</p>
3814
4062
  ${sessions}
3815
- ${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml9(model.error)}</p>` : ""}
4063
+ ${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml10(model.error)}</p>` : ""}
3816
4064
  </section>`;
3817
4065
  };
3818
4066
  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}`;
@@ -3935,6 +4183,7 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
3935
4183
  };
3936
4184
  export {
3937
4185
  runVoiceTurnLatencyProof,
4186
+ runVoiceOpsAction,
3938
4187
  runVoiceDeliveryRuntimeAction,
3939
4188
  runVoiceCampaignDialerProofAction,
3940
4189
  renderVoiceTurnQualityHTML,
@@ -3945,6 +4194,7 @@ export {
3945
4194
  renderVoiceProviderSimulationControlsHTML,
3946
4195
  renderVoiceProviderCapabilitiesHTML,
3947
4196
  renderVoiceOpsStatusHTML,
4197
+ renderVoiceOpsActionCenterHTML,
3948
4198
  renderVoiceDeliveryRuntimeHTML,
3949
4199
  mountVoiceTurnQuality,
3950
4200
  mountVoiceTurnLatency,
@@ -3954,6 +4204,7 @@ export {
3954
4204
  mountVoiceProviderSimulationControls,
3955
4205
  mountVoiceProviderCapabilities,
3956
4206
  mountVoiceOpsStatus,
4207
+ mountVoiceOpsActionCenter,
3957
4208
  mountVoiceDeliveryRuntime,
3958
4209
  getVoiceTurnQualityCSS,
3959
4210
  getVoiceTraceTimelineCSS,
@@ -3962,6 +4213,7 @@ export {
3962
4213
  getVoiceProviderCapabilitiesCSS,
3963
4214
  getVoiceOpsStatusLabel,
3964
4215
  getVoiceOpsStatusCSS,
4216
+ getVoiceOpsActionCenterCSS,
3965
4217
  getVoiceDeliveryRuntimeCSS,
3966
4218
  fetchVoiceWorkflowStatus,
3967
4219
  fetchVoiceTurnQuality,
@@ -3981,6 +4233,7 @@ export {
3981
4233
  defineVoiceProviderSimulationControlsElement,
3982
4234
  defineVoiceProviderCapabilitiesElement,
3983
4235
  defineVoiceOpsStatusElement,
4236
+ defineVoiceOpsActionCenterElement,
3984
4237
  defineVoiceDeliveryRuntimeElement,
3985
4238
  decodeVoiceAudioChunk,
3986
4239
  createVoiceWorkflowStatusStore,
@@ -4001,6 +4254,9 @@ export {
4001
4254
  createVoiceProviderCapabilitiesStore,
4002
4255
  createVoiceOpsStatusViewModel,
4003
4256
  createVoiceOpsStatusStore,
4257
+ createVoiceOpsActionCenterViewModel,
4258
+ createVoiceOpsActionCenterStore,
4259
+ createVoiceOpsActionCenterActions,
4004
4260
  createVoiceLiveTurnLatencyMonitor,
4005
4261
  createVoiceDuplexController,
4006
4262
  createVoiceDeliveryRuntimeViewModel,