@absolutejs/voice 0.0.22-beta.153 → 0.0.22-beta.155
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -0
- package/dist/angular/index.d.ts +1 -0
- package/dist/angular/index.js +378 -140
- package/dist/angular/voice-ops-action-center.service.d.ts +13 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +400 -114
- package/dist/client/opsActionCenter.d.ts +54 -0
- package/dist/client/opsActionCenterWidget.d.ts +29 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +144 -53
- package/dist/opsActionAuditRoutes.d.ts +79 -0
- package/dist/react/VoiceOpsActionCenter.d.ts +5 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +640 -289
- package/dist/react/useVoiceOpsActionCenter.d.ts +11 -0
- package/dist/svelte/createVoiceOpsActionCenter.d.ts +10 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +407 -114
- package/dist/trace.d.ts +1 -1
- package/dist/vue/VoiceOpsActionCenter.d.ts +13 -0
- package/dist/vue/index.d.ts +2 -0
- package/dist/vue/index.js +668 -298
- package/dist/vue/useVoiceOpsActionCenter.d.ts +11 -0
- package/package.json +1 -1
package/dist/svelte/index.js
CHANGED
|
@@ -468,6 +468,298 @@ var createVoiceDeliveryRuntime = (path = "/api/voice-delivery-runtime", options
|
|
|
468
468
|
tick: store.tick
|
|
469
469
|
};
|
|
470
470
|
};
|
|
471
|
+
// src/client/opsActionCenter.ts
|
|
472
|
+
var recordVoiceOpsActionResult = async (result, options = {}) => {
|
|
473
|
+
if (options.auditPath === false) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const path = options.auditPath ?? "/api/voice/ops-actions/audit";
|
|
477
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
478
|
+
const response = await fetchImpl(path, {
|
|
479
|
+
body: JSON.stringify(result),
|
|
480
|
+
headers: {
|
|
481
|
+
"Content-Type": "application/json"
|
|
482
|
+
},
|
|
483
|
+
method: "POST"
|
|
484
|
+
});
|
|
485
|
+
if (!response.ok) {
|
|
486
|
+
throw new Error(`Voice ops action audit failed: HTTP ${response.status}`);
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
var createVoiceOpsActionCenterActions = (options = {}) => {
|
|
490
|
+
const deliveryRuntimePath = options.deliveryRuntimePath ?? "/api/voice-delivery-runtime";
|
|
491
|
+
const actions = [];
|
|
492
|
+
if (options.includeProductionReadiness !== false) {
|
|
493
|
+
actions.push({
|
|
494
|
+
description: "Refresh the production readiness report.",
|
|
495
|
+
id: "production-readiness",
|
|
496
|
+
label: "Refresh readiness",
|
|
497
|
+
method: "GET",
|
|
498
|
+
path: options.productionReadinessPath ?? "/api/production-readiness"
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
if (options.includeDeliveryRuntime !== false) {
|
|
502
|
+
actions.push({
|
|
503
|
+
description: "Drain pending and failed audit/trace deliveries.",
|
|
504
|
+
id: "delivery-runtime.tick",
|
|
505
|
+
label: "Tick delivery workers",
|
|
506
|
+
method: "POST",
|
|
507
|
+
path: `${deliveryRuntimePath.replace(/\/$/, "")}/tick`
|
|
508
|
+
}, {
|
|
509
|
+
description: "Move reviewed dead letters back to live delivery queues.",
|
|
510
|
+
id: "delivery-runtime.requeue-dead-letters",
|
|
511
|
+
label: "Requeue dead letters",
|
|
512
|
+
method: "POST",
|
|
513
|
+
path: `${deliveryRuntimePath.replace(/\/$/, "")}/requeue-dead-letters`
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
if (options.includeTurnLatencyProof !== false) {
|
|
517
|
+
actions.push({
|
|
518
|
+
description: "Run the synthetic turn latency proof.",
|
|
519
|
+
id: "turn-latency.proof",
|
|
520
|
+
label: "Run latency proof",
|
|
521
|
+
method: "POST",
|
|
522
|
+
path: options.turnLatencyProofPath ?? "/api/turn-latency/proof"
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
if (options.includeProviderSimulation !== false) {
|
|
526
|
+
const pathPrefix = options.providerSimulationPathPrefix ?? "/api/stt-simulate";
|
|
527
|
+
for (const provider of options.providers ?? []) {
|
|
528
|
+
actions.push({
|
|
529
|
+
description: `Simulate ${provider} provider failure.`,
|
|
530
|
+
id: `provider.${provider}.failure`,
|
|
531
|
+
label: `Simulate ${provider} failure`,
|
|
532
|
+
method: "POST",
|
|
533
|
+
path: `${pathPrefix}/failure?provider=${encodeURIComponent(provider)}`
|
|
534
|
+
}, {
|
|
535
|
+
description: `Mark ${provider} provider recovered.`,
|
|
536
|
+
id: `provider.${provider}.recovery`,
|
|
537
|
+
label: `Recover ${provider}`,
|
|
538
|
+
method: "POST",
|
|
539
|
+
path: `${pathPrefix}/recovery?provider=${encodeURIComponent(provider)}`
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return actions;
|
|
544
|
+
};
|
|
545
|
+
var runVoiceOpsAction = async (action, options = {}) => {
|
|
546
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
547
|
+
const response = await fetchImpl(action.path, {
|
|
548
|
+
method: action.method ?? "POST"
|
|
549
|
+
});
|
|
550
|
+
const body = await response.json().catch(() => null);
|
|
551
|
+
if (!response.ok) {
|
|
552
|
+
const message = body && typeof body === "object" && "error" in body ? String(body.error) : `Voice ops action "${action.id}" failed: HTTP ${response.status}`;
|
|
553
|
+
throw new Error(message);
|
|
554
|
+
}
|
|
555
|
+
return {
|
|
556
|
+
actionId: action.id,
|
|
557
|
+
body,
|
|
558
|
+
ok: response.ok,
|
|
559
|
+
ranAt: Date.now(),
|
|
560
|
+
status: response.status
|
|
561
|
+
};
|
|
562
|
+
};
|
|
563
|
+
var createVoiceOpsActionCenterStore = (options = {}) => {
|
|
564
|
+
const listeners = new Set;
|
|
565
|
+
let closed = false;
|
|
566
|
+
let timer;
|
|
567
|
+
let snapshot = {
|
|
568
|
+
actions: options.actions ?? createVoiceOpsActionCenterActions(),
|
|
569
|
+
error: null,
|
|
570
|
+
isRunning: false
|
|
571
|
+
};
|
|
572
|
+
const emit = () => {
|
|
573
|
+
for (const listener of listeners) {
|
|
574
|
+
listener();
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
const setActions = (actions) => {
|
|
578
|
+
snapshot = { ...snapshot, actions, updatedAt: Date.now() };
|
|
579
|
+
emit();
|
|
580
|
+
};
|
|
581
|
+
const run = async (actionId) => {
|
|
582
|
+
if (closed) {
|
|
583
|
+
return snapshot.lastResult;
|
|
584
|
+
}
|
|
585
|
+
const action = snapshot.actions.find((item) => item.id === actionId);
|
|
586
|
+
if (!action) {
|
|
587
|
+
throw new Error(`Voice ops action "${actionId}" is not configured.`);
|
|
588
|
+
}
|
|
589
|
+
if (action.disabled) {
|
|
590
|
+
throw new Error(`Voice ops action "${actionId}" is disabled.`);
|
|
591
|
+
}
|
|
592
|
+
snapshot = {
|
|
593
|
+
...snapshot,
|
|
594
|
+
error: null,
|
|
595
|
+
isRunning: true,
|
|
596
|
+
runningActionId: action.id
|
|
597
|
+
};
|
|
598
|
+
emit();
|
|
599
|
+
try {
|
|
600
|
+
const result = await runVoiceOpsAction(action, options);
|
|
601
|
+
await options.onActionResult?.(result);
|
|
602
|
+
await recordVoiceOpsActionResult(result, options);
|
|
603
|
+
snapshot = {
|
|
604
|
+
...snapshot,
|
|
605
|
+
error: null,
|
|
606
|
+
isRunning: false,
|
|
607
|
+
lastResult: result,
|
|
608
|
+
runningActionId: undefined,
|
|
609
|
+
updatedAt: Date.now()
|
|
610
|
+
};
|
|
611
|
+
emit();
|
|
612
|
+
return result;
|
|
613
|
+
} catch (error) {
|
|
614
|
+
const result = {
|
|
615
|
+
actionId: action.id,
|
|
616
|
+
body: null,
|
|
617
|
+
error: error instanceof Error ? error.message : String(error),
|
|
618
|
+
ok: false,
|
|
619
|
+
ranAt: Date.now(),
|
|
620
|
+
status: 0
|
|
621
|
+
};
|
|
622
|
+
await options.onActionResult?.(result);
|
|
623
|
+
await recordVoiceOpsActionResult(result, options).catch(() => {});
|
|
624
|
+
snapshot = {
|
|
625
|
+
...snapshot,
|
|
626
|
+
error: error instanceof Error ? error.message : String(error),
|
|
627
|
+
isRunning: false,
|
|
628
|
+
runningActionId: undefined
|
|
629
|
+
};
|
|
630
|
+
emit();
|
|
631
|
+
throw error;
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
const close = () => {
|
|
635
|
+
closed = true;
|
|
636
|
+
if (timer) {
|
|
637
|
+
clearInterval(timer);
|
|
638
|
+
timer = undefined;
|
|
639
|
+
}
|
|
640
|
+
listeners.clear();
|
|
641
|
+
};
|
|
642
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
643
|
+
timer = setInterval(() => {
|
|
644
|
+
emit();
|
|
645
|
+
}, options.intervalMs);
|
|
646
|
+
}
|
|
647
|
+
return {
|
|
648
|
+
close,
|
|
649
|
+
getServerSnapshot: () => snapshot,
|
|
650
|
+
getSnapshot: () => snapshot,
|
|
651
|
+
run,
|
|
652
|
+
setActions,
|
|
653
|
+
subscribe: (listener) => {
|
|
654
|
+
listeners.add(listener);
|
|
655
|
+
return () => {
|
|
656
|
+
listeners.delete(listener);
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
// src/client/opsActionCenterWidget.ts
|
|
663
|
+
var DEFAULT_TITLE2 = "Voice Ops Action Center";
|
|
664
|
+
var DEFAULT_DESCRIPTION2 = "Run production voice proofs and operator actions from one primitive panel.";
|
|
665
|
+
var escapeHtml2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
666
|
+
var createVoiceOpsActionCenterViewModel = (snapshot, options = {}) => {
|
|
667
|
+
const status = snapshot.error ? "error" : snapshot.isRunning ? "running" : snapshot.lastResult ? "completed" : "ready";
|
|
668
|
+
return {
|
|
669
|
+
actions: snapshot.actions.map((action) => ({
|
|
670
|
+
description: action.description ?? "",
|
|
671
|
+
disabled: Boolean(action.disabled || snapshot.isRunning),
|
|
672
|
+
id: action.id,
|
|
673
|
+
isRunning: snapshot.runningActionId === action.id,
|
|
674
|
+
label: action.label
|
|
675
|
+
})),
|
|
676
|
+
description: options.description ?? DEFAULT_DESCRIPTION2,
|
|
677
|
+
error: snapshot.error,
|
|
678
|
+
isRunning: snapshot.isRunning,
|
|
679
|
+
label: status === "error" ? "Needs attention" : status === "running" ? "Running" : status === "completed" ? "Action completed" : "Ready",
|
|
680
|
+
lastResultLabel: snapshot.lastResult ? `${snapshot.lastResult.actionId} returned HTTP ${snapshot.lastResult.status}` : "No action has run yet.",
|
|
681
|
+
status,
|
|
682
|
+
title: options.title ?? DEFAULT_TITLE2
|
|
683
|
+
};
|
|
684
|
+
};
|
|
685
|
+
var renderVoiceOpsActionCenterHTML = (snapshot, options = {}) => {
|
|
686
|
+
const model = createVoiceOpsActionCenterViewModel(snapshot, options);
|
|
687
|
+
const actions = model.actions.map((action) => `<button type="button" data-absolute-voice-ops-action="${escapeHtml2(action.id)}"${action.disabled ? " disabled" : ""}>
|
|
688
|
+
${escapeHtml2(action.isRunning ? "Working..." : action.label)}
|
|
689
|
+
</button>`).join("");
|
|
690
|
+
return `<section class="absolute-voice-ops-action-center absolute-voice-ops-action-center--${escapeHtml2(model.status)}">
|
|
691
|
+
<header class="absolute-voice-ops-action-center__header">
|
|
692
|
+
<span class="absolute-voice-ops-action-center__eyebrow">${escapeHtml2(model.title)}</span>
|
|
693
|
+
<strong class="absolute-voice-ops-action-center__label">${escapeHtml2(model.label)}</strong>
|
|
694
|
+
</header>
|
|
695
|
+
<p class="absolute-voice-ops-action-center__description">${escapeHtml2(model.description)}</p>
|
|
696
|
+
<div class="absolute-voice-ops-action-center__actions">${actions}</div>
|
|
697
|
+
<p class="absolute-voice-ops-action-center__result">${escapeHtml2(model.lastResultLabel)}</p>
|
|
698
|
+
${model.error ? `<p class="absolute-voice-ops-action-center__error">${escapeHtml2(model.error)}</p>` : ""}
|
|
699
|
+
</section>`;
|
|
700
|
+
};
|
|
701
|
+
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}`;
|
|
702
|
+
var mountVoiceOpsActionCenter = (element, options = {}) => {
|
|
703
|
+
const store = createVoiceOpsActionCenterStore(options);
|
|
704
|
+
const render = () => {
|
|
705
|
+
element.innerHTML = renderVoiceOpsActionCenterHTML(store.getSnapshot(), options);
|
|
706
|
+
};
|
|
707
|
+
const unsubscribe = store.subscribe(render);
|
|
708
|
+
const handleClick = (event) => {
|
|
709
|
+
const target = event.target;
|
|
710
|
+
if (!(target instanceof Element)) {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
const action = target.closest("[data-absolute-voice-ops-action]");
|
|
714
|
+
const actionId = action?.getAttribute("data-absolute-voice-ops-action");
|
|
715
|
+
if (actionId) {
|
|
716
|
+
store.run(actionId).catch(() => {});
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
element.addEventListener?.("click", handleClick);
|
|
720
|
+
render();
|
|
721
|
+
return {
|
|
722
|
+
close: () => {
|
|
723
|
+
element.removeEventListener?.("click", handleClick);
|
|
724
|
+
unsubscribe();
|
|
725
|
+
store.close();
|
|
726
|
+
},
|
|
727
|
+
run: store.run
|
|
728
|
+
};
|
|
729
|
+
};
|
|
730
|
+
var defineVoiceOpsActionCenterElement = (tagName = "absolute-voice-ops-action-center", options = {}) => {
|
|
731
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
customElements.define(tagName, class AbsoluteVoiceOpsActionCenterElement extends HTMLElement {
|
|
735
|
+
mounted;
|
|
736
|
+
connectedCallback() {
|
|
737
|
+
this.mounted = mountVoiceOpsActionCenter(this, {
|
|
738
|
+
...options,
|
|
739
|
+
description: this.getAttribute("description") ?? options.description,
|
|
740
|
+
title: this.getAttribute("title") ?? options.title
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
disconnectedCallback() {
|
|
744
|
+
this.mounted?.close();
|
|
745
|
+
this.mounted = undefined;
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
// src/svelte/createVoiceOpsActionCenter.ts
|
|
751
|
+
var createVoiceOpsActionCenter = (options = {}) => {
|
|
752
|
+
const store = createVoiceOpsActionCenterStore(options);
|
|
753
|
+
return {
|
|
754
|
+
close: store.close,
|
|
755
|
+
getHTML: () => renderVoiceOpsActionCenterHTML(store.getSnapshot(), options),
|
|
756
|
+
getSnapshot: store.getSnapshot,
|
|
757
|
+
getViewModel: () => createVoiceOpsActionCenterViewModel(store.getSnapshot(), options),
|
|
758
|
+
run: store.run,
|
|
759
|
+
setActions: store.setActions,
|
|
760
|
+
subscribe: store.subscribe
|
|
761
|
+
};
|
|
762
|
+
};
|
|
471
763
|
// src/client/opsStatus.ts
|
|
472
764
|
var fetchVoiceOpsStatus = async (path = "/api/voice/ops-status", options = {}) => {
|
|
473
765
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -548,8 +840,8 @@ var createVoiceOpsStatusStore = (path = "/api/voice/ops-status", options = {}) =
|
|
|
548
840
|
};
|
|
549
841
|
|
|
550
842
|
// src/client/opsStatusWidget.ts
|
|
551
|
-
var
|
|
552
|
-
var
|
|
843
|
+
var DEFAULT_TITLE3 = "Voice Ops Status";
|
|
844
|
+
var DEFAULT_DESCRIPTION3 = "Certified workflow, provider, and handoff readiness from your AbsoluteJS voice app.";
|
|
553
845
|
var SURFACE_LABELS = {
|
|
554
846
|
handoffs: "Handoffs",
|
|
555
847
|
providers: "Providers",
|
|
@@ -557,7 +849,7 @@ var SURFACE_LABELS = {
|
|
|
557
849
|
sessions: "Sessions",
|
|
558
850
|
workflows: "Workflows"
|
|
559
851
|
};
|
|
560
|
-
var
|
|
852
|
+
var escapeHtml3 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
561
853
|
var readNumber = (value, key) => value && typeof value === "object" && (key in value) ? Number(value[key] ?? 0) : 0;
|
|
562
854
|
var surfaceDetail = (surface) => {
|
|
563
855
|
const total = readNumber(surface, "total");
|
|
@@ -597,7 +889,7 @@ var createVoiceOpsStatusViewModel = (snapshot, options = {}) => {
|
|
|
597
889
|
};
|
|
598
890
|
});
|
|
599
891
|
return {
|
|
600
|
-
description: options.description ??
|
|
892
|
+
description: options.description ?? DEFAULT_DESCRIPTION3,
|
|
601
893
|
error: snapshot.error,
|
|
602
894
|
isLoading: snapshot.isLoading,
|
|
603
895
|
label: getVoiceOpsStatusLabel(report, snapshot.error),
|
|
@@ -605,31 +897,31 @@ var createVoiceOpsStatusViewModel = (snapshot, options = {}) => {
|
|
|
605
897
|
passed: report?.passed ?? 0,
|
|
606
898
|
status: snapshot.error ? "error" : report ? report.status : snapshot.isLoading ? "loading" : "loading",
|
|
607
899
|
surfaces,
|
|
608
|
-
title: options.title ??
|
|
900
|
+
title: options.title ?? DEFAULT_TITLE3,
|
|
609
901
|
total: report?.total ?? 0,
|
|
610
902
|
updatedAt: snapshot.updatedAt
|
|
611
903
|
};
|
|
612
904
|
};
|
|
613
905
|
var renderVoiceOpsStatusHTML = (snapshot, options = {}) => {
|
|
614
906
|
const model = createVoiceOpsStatusViewModel(snapshot, options);
|
|
615
|
-
const surfaces = model.surfaces.length ? model.surfaces.map((surface) => `<li class="absolute-voice-ops-status__surface absolute-voice-ops-status__surface--${
|
|
616
|
-
<span>${
|
|
617
|
-
<strong>${
|
|
907
|
+
const surfaces = model.surfaces.length ? model.surfaces.map((surface) => `<li class="absolute-voice-ops-status__surface absolute-voice-ops-status__surface--${escapeHtml3(surface.status)}">
|
|
908
|
+
<span>${escapeHtml3(surface.label)}</span>
|
|
909
|
+
<strong>${escapeHtml3(surface.detail)}</strong>
|
|
618
910
|
</li>`).join("") : '<li class="absolute-voice-ops-status__surface"><span>Status</span><strong>Waiting for first check</strong></li>';
|
|
619
|
-
const links = model.links.length ? `<nav class="absolute-voice-ops-status__links">${model.links.slice(0, 4).map((link) => `<a href="${
|
|
620
|
-
return `<section class="absolute-voice-ops-status absolute-voice-ops-status--${
|
|
911
|
+
const links = model.links.length ? `<nav class="absolute-voice-ops-status__links">${model.links.slice(0, 4).map((link) => `<a href="${escapeHtml3(link.href)}">${escapeHtml3(link.label)}</a>`).join("")}</nav>` : "";
|
|
912
|
+
return `<section class="absolute-voice-ops-status absolute-voice-ops-status--${escapeHtml3(model.status)}">
|
|
621
913
|
<header class="absolute-voice-ops-status__header">
|
|
622
|
-
<span class="absolute-voice-ops-status__eyebrow">${
|
|
623
|
-
<strong class="absolute-voice-ops-status__label">${
|
|
914
|
+
<span class="absolute-voice-ops-status__eyebrow">${escapeHtml3(model.title)}</span>
|
|
915
|
+
<strong class="absolute-voice-ops-status__label">${escapeHtml3(model.label)}</strong>
|
|
624
916
|
</header>
|
|
625
|
-
<p class="absolute-voice-ops-status__description">${
|
|
917
|
+
<p class="absolute-voice-ops-status__description">${escapeHtml3(model.description)}</p>
|
|
626
918
|
<div class="absolute-voice-ops-status__summary">
|
|
627
919
|
<span>${model.passed} passing</span>
|
|
628
920
|
<span>${Math.max(model.total - model.passed, 0)} failing</span>
|
|
629
921
|
<span>${model.total} checks</span>
|
|
630
922
|
</div>
|
|
631
923
|
<ul class="absolute-voice-ops-status__surfaces">${surfaces}</ul>
|
|
632
|
-
${model.error ? `<p class="absolute-voice-ops-status__error">${
|
|
924
|
+
${model.error ? `<p class="absolute-voice-ops-status__error">${escapeHtml3(model.error)}</p>` : ""}
|
|
633
925
|
${links}
|
|
634
926
|
</section>`;
|
|
635
927
|
};
|
|
@@ -764,7 +1056,7 @@ var createVoiceProviderSimulationControlsStore = (options) => {
|
|
|
764
1056
|
};
|
|
765
1057
|
|
|
766
1058
|
// src/client/providerSimulationControlsWidget.ts
|
|
767
|
-
var
|
|
1059
|
+
var escapeHtml4 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
768
1060
|
var formatKind = (kind) => (kind ?? "stt").toUpperCase();
|
|
769
1061
|
var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
|
|
770
1062
|
const configuredProviders = options.providers.filter((provider) => provider.configured !== false);
|
|
@@ -784,18 +1076,18 @@ var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
|
|
|
784
1076
|
};
|
|
785
1077
|
var renderVoiceProviderSimulationControlsHTML = (snapshot, options) => {
|
|
786
1078
|
const model = createVoiceProviderSimulationControlsViewModel(snapshot, options);
|
|
787
|
-
const failureButtons = model.failureProviders.map((provider) => `<button type="button" data-voice-provider-fail="${
|
|
788
|
-
const recoveryButtons = model.providers.map((provider) => `<button type="button" data-voice-provider-recover="${
|
|
1079
|
+
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("");
|
|
1080
|
+
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("");
|
|
789
1081
|
return `<section class="absolute-voice-provider-simulation absolute-voice-provider-simulation--${snapshot.error ? "error" : snapshot.isRunning ? "running" : "ready"}">
|
|
790
1082
|
<header class="absolute-voice-provider-simulation__header">
|
|
791
|
-
<span class="absolute-voice-provider-simulation__eyebrow">${
|
|
792
|
-
<strong class="absolute-voice-provider-simulation__label">${
|
|
1083
|
+
<span class="absolute-voice-provider-simulation__eyebrow">${escapeHtml4(model.title)}</span>
|
|
1084
|
+
<strong class="absolute-voice-provider-simulation__label">${escapeHtml4(model.label)}</strong>
|
|
793
1085
|
</header>
|
|
794
|
-
<p class="absolute-voice-provider-simulation__description">${
|
|
795
|
-
${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${
|
|
1086
|
+
<p class="absolute-voice-provider-simulation__description">${escapeHtml4(model.description)}</p>
|
|
1087
|
+
${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${escapeHtml4(options.fallbackRequiredMessage ?? "Configure fallback providers before simulating failure.")}</p>`}
|
|
796
1088
|
<div class="absolute-voice-provider-simulation__actions">${failureButtons}${recoveryButtons}</div>
|
|
797
|
-
${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${
|
|
798
|
-
${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${
|
|
1089
|
+
${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${escapeHtml4(snapshot.error)}</p>` : ""}
|
|
1090
|
+
${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${escapeHtml4(model.resultText)}</pre>` : ""}
|
|
799
1091
|
</section>`;
|
|
800
1092
|
};
|
|
801
1093
|
var bindVoiceProviderSimulationControls = (element, store) => {
|
|
@@ -950,9 +1242,9 @@ var createVoiceProviderCapabilitiesStore = (path = "/api/provider-capabilities",
|
|
|
950
1242
|
};
|
|
951
1243
|
|
|
952
1244
|
// src/client/providerCapabilitiesWidget.ts
|
|
953
|
-
var
|
|
954
|
-
var
|
|
955
|
-
var
|
|
1245
|
+
var DEFAULT_TITLE4 = "Provider Capabilities";
|
|
1246
|
+
var DEFAULT_DESCRIPTION4 = "Configured, selected, and healthy voice providers for this deployment.";
|
|
1247
|
+
var escapeHtml5 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
956
1248
|
var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
|
|
957
1249
|
var formatKind2 = (kind) => kind.toUpperCase();
|
|
958
1250
|
var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
@@ -996,36 +1288,36 @@ var createVoiceProviderCapabilitiesViewModel = (snapshot, options = {}) => {
|
|
|
996
1288
|
const selectedCount = snapshot.report?.selected ?? capabilities.filter((capability) => capability.selected).length;
|
|
997
1289
|
return {
|
|
998
1290
|
capabilities,
|
|
999
|
-
description: options.description ??
|
|
1291
|
+
description: options.description ?? DEFAULT_DESCRIPTION4,
|
|
1000
1292
|
error: snapshot.error,
|
|
1001
1293
|
isLoading: snapshot.isLoading,
|
|
1002
1294
|
label: snapshot.error ? "Unavailable" : capabilities.length ? warningCount > 0 ? `${warningCount} needs attention` : `${selectedCount} selected` : snapshot.isLoading ? "Checking" : "No capabilities",
|
|
1003
1295
|
status: snapshot.error ? "error" : capabilities.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
1004
|
-
title: options.title ??
|
|
1296
|
+
title: options.title ?? DEFAULT_TITLE4,
|
|
1005
1297
|
updatedAt: snapshot.updatedAt
|
|
1006
1298
|
};
|
|
1007
1299
|
};
|
|
1008
1300
|
var renderVoiceProviderCapabilitiesHTML = (snapshot, options = {}) => {
|
|
1009
1301
|
const model = createVoiceProviderCapabilitiesViewModel(snapshot, options);
|
|
1010
|
-
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--${
|
|
1302
|
+
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--${escapeHtml5(capability.status)}">
|
|
1011
1303
|
<header>
|
|
1012
|
-
<strong>${
|
|
1013
|
-
<span>${
|
|
1304
|
+
<strong>${escapeHtml5(capability.label)}</strong>
|
|
1305
|
+
<span>${escapeHtml5(formatStatus(capability.status))}</span>
|
|
1014
1306
|
</header>
|
|
1015
|
-
<p>${
|
|
1307
|
+
<p>${escapeHtml5(capability.detail)}</p>
|
|
1016
1308
|
<dl>${capability.rows.map((row) => `<div>
|
|
1017
|
-
<dt>${
|
|
1018
|
-
<dd>${
|
|
1309
|
+
<dt>${escapeHtml5(row.label)}</dt>
|
|
1310
|
+
<dd>${escapeHtml5(row.value)}</dd>
|
|
1019
1311
|
</div>`).join("")}</dl>
|
|
1020
1312
|
</article>`).join("")}</div>` : '<p class="absolute-voice-provider-capabilities__empty">Configure provider capabilities to see deployment coverage.</p>';
|
|
1021
|
-
return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${
|
|
1313
|
+
return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${escapeHtml5(model.status)}">
|
|
1022
1314
|
<header class="absolute-voice-provider-capabilities__header">
|
|
1023
|
-
<span class="absolute-voice-provider-capabilities__eyebrow">${
|
|
1024
|
-
<strong class="absolute-voice-provider-capabilities__label">${
|
|
1315
|
+
<span class="absolute-voice-provider-capabilities__eyebrow">${escapeHtml5(model.title)}</span>
|
|
1316
|
+
<strong class="absolute-voice-provider-capabilities__label">${escapeHtml5(model.label)}</strong>
|
|
1025
1317
|
</header>
|
|
1026
|
-
<p class="absolute-voice-provider-capabilities__description">${
|
|
1318
|
+
<p class="absolute-voice-provider-capabilities__description">${escapeHtml5(model.description)}</p>
|
|
1027
1319
|
${capabilities}
|
|
1028
|
-
${model.error ? `<p class="absolute-voice-provider-capabilities__error">${
|
|
1320
|
+
${model.error ? `<p class="absolute-voice-provider-capabilities__error">${escapeHtml5(model.error)}</p>` : ""}
|
|
1029
1321
|
</section>`;
|
|
1030
1322
|
};
|
|
1031
1323
|
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}`;
|
|
@@ -1798,9 +2090,9 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
|
|
|
1798
2090
|
};
|
|
1799
2091
|
|
|
1800
2092
|
// src/client/providerStatusWidget.ts
|
|
1801
|
-
var
|
|
1802
|
-
var
|
|
1803
|
-
var
|
|
2093
|
+
var DEFAULT_TITLE5 = "Voice Providers";
|
|
2094
|
+
var DEFAULT_DESCRIPTION5 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
|
|
2095
|
+
var escapeHtml6 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1804
2096
|
var formatProvider2 = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
|
|
1805
2097
|
var formatStatus2 = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
1806
2098
|
var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
|
|
@@ -1844,37 +2136,37 @@ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
|
|
|
1844
2136
|
const warningCount = providers.filter((provider) => isWarningStatus2(provider.status)).length;
|
|
1845
2137
|
const healthyCount = providers.filter((provider) => provider.status === "healthy").length;
|
|
1846
2138
|
return {
|
|
1847
|
-
description: options.description ??
|
|
2139
|
+
description: options.description ?? DEFAULT_DESCRIPTION5,
|
|
1848
2140
|
error: snapshot.error,
|
|
1849
2141
|
isLoading: snapshot.isLoading,
|
|
1850
2142
|
label: snapshot.error ? "Unavailable" : providers.length ? warningCount > 0 ? `${warningCount} needs attention` : `${healthyCount} healthy` : snapshot.isLoading ? "Checking" : "No provider traffic",
|
|
1851
2143
|
providers,
|
|
1852
2144
|
status: snapshot.error ? "error" : providers.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
1853
|
-
title: options.title ??
|
|
2145
|
+
title: options.title ?? DEFAULT_TITLE5,
|
|
1854
2146
|
updatedAt: snapshot.updatedAt
|
|
1855
2147
|
};
|
|
1856
2148
|
};
|
|
1857
2149
|
var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
|
|
1858
2150
|
const model = createVoiceProviderStatusViewModel(snapshot, options);
|
|
1859
|
-
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--${
|
|
2151
|
+
const providers = model.providers.length ? `<div class="absolute-voice-provider-status__providers">${model.providers.map((provider) => `<article class="absolute-voice-provider-status__provider absolute-voice-provider-status__provider--${escapeHtml6(provider.status)}">
|
|
1860
2152
|
<header>
|
|
1861
|
-
<strong>${
|
|
1862
|
-
<span>${
|
|
2153
|
+
<strong>${escapeHtml6(provider.label)}</strong>
|
|
2154
|
+
<span>${escapeHtml6(formatStatus2(provider.status))}</span>
|
|
1863
2155
|
</header>
|
|
1864
|
-
<p>${
|
|
2156
|
+
<p>${escapeHtml6(provider.detail)}</p>
|
|
1865
2157
|
<dl>${provider.rows.map((row) => `<div>
|
|
1866
|
-
<dt>${
|
|
1867
|
-
<dd>${
|
|
2158
|
+
<dt>${escapeHtml6(row.label)}</dt>
|
|
2159
|
+
<dd>${escapeHtml6(row.value)}</dd>
|
|
1868
2160
|
</div>`).join("")}</dl>
|
|
1869
2161
|
</article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
|
|
1870
|
-
return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${
|
|
2162
|
+
return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml6(model.status)}">
|
|
1871
2163
|
<header class="absolute-voice-provider-status__header">
|
|
1872
|
-
<span class="absolute-voice-provider-status__eyebrow">${
|
|
1873
|
-
<strong class="absolute-voice-provider-status__label">${
|
|
2164
|
+
<span class="absolute-voice-provider-status__eyebrow">${escapeHtml6(model.title)}</span>
|
|
2165
|
+
<strong class="absolute-voice-provider-status__label">${escapeHtml6(model.label)}</strong>
|
|
1874
2166
|
</header>
|
|
1875
|
-
<p class="absolute-voice-provider-status__description">${
|
|
2167
|
+
<p class="absolute-voice-provider-status__description">${escapeHtml6(model.description)}</p>
|
|
1876
2168
|
${providers}
|
|
1877
|
-
${model.error ? `<p class="absolute-voice-provider-status__error">${
|
|
2169
|
+
${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml6(model.error)}</p>` : ""}
|
|
1878
2170
|
</section>`;
|
|
1879
2171
|
};
|
|
1880
2172
|
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}`;
|
|
@@ -2005,9 +2297,9 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
|
|
|
2005
2297
|
};
|
|
2006
2298
|
|
|
2007
2299
|
// src/client/routingStatusWidget.ts
|
|
2008
|
-
var
|
|
2009
|
-
var
|
|
2010
|
-
var
|
|
2300
|
+
var DEFAULT_TITLE6 = "Voice Routing";
|
|
2301
|
+
var DEFAULT_DESCRIPTION6 = "Latest provider routing decision from the self-hosted trace store.";
|
|
2302
|
+
var escapeHtml7 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2011
2303
|
var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
|
|
2012
2304
|
var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
2013
2305
|
const decision = snapshot.decision;
|
|
@@ -2031,30 +2323,30 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
|
2031
2323
|
] : [];
|
|
2032
2324
|
return {
|
|
2033
2325
|
decision,
|
|
2034
|
-
description: options.description ??
|
|
2326
|
+
description: options.description ?? DEFAULT_DESCRIPTION6,
|
|
2035
2327
|
error: snapshot.error,
|
|
2036
2328
|
isLoading: snapshot.isLoading,
|
|
2037
2329
|
label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
|
|
2038
2330
|
rows,
|
|
2039
2331
|
status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2040
|
-
title: options.title ??
|
|
2332
|
+
title: options.title ?? DEFAULT_TITLE6,
|
|
2041
2333
|
updatedAt: snapshot.updatedAt
|
|
2042
2334
|
};
|
|
2043
2335
|
};
|
|
2044
2336
|
var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
2045
2337
|
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
2046
2338
|
const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
|
|
2047
|
-
<span>${
|
|
2048
|
-
<strong>${
|
|
2339
|
+
<span>${escapeHtml7(row.label)}</span>
|
|
2340
|
+
<strong>${escapeHtml7(row.value)}</strong>
|
|
2049
2341
|
</div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
|
|
2050
|
-
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${
|
|
2342
|
+
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml7(model.status)}">
|
|
2051
2343
|
<header class="absolute-voice-routing-status__header">
|
|
2052
|
-
<span class="absolute-voice-routing-status__eyebrow">${
|
|
2053
|
-
<strong class="absolute-voice-routing-status__label">${
|
|
2344
|
+
<span class="absolute-voice-routing-status__eyebrow">${escapeHtml7(model.title)}</span>
|
|
2345
|
+
<strong class="absolute-voice-routing-status__label">${escapeHtml7(model.label)}</strong>
|
|
2054
2346
|
</header>
|
|
2055
|
-
<p class="absolute-voice-routing-status__description">${
|
|
2347
|
+
<p class="absolute-voice-routing-status__description">${escapeHtml7(model.description)}</p>
|
|
2056
2348
|
${rows}
|
|
2057
|
-
${model.error ? `<p class="absolute-voice-routing-status__error">${
|
|
2349
|
+
${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml7(model.error)}</p>` : ""}
|
|
2058
2350
|
</section>`;
|
|
2059
2351
|
};
|
|
2060
2352
|
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}`;
|
|
@@ -2185,9 +2477,9 @@ var createVoiceTraceTimelineStore = (path = "/api/voice-traces", options = {}) =
|
|
|
2185
2477
|
};
|
|
2186
2478
|
|
|
2187
2479
|
// src/client/traceTimelineWidget.ts
|
|
2188
|
-
var
|
|
2189
|
-
var
|
|
2190
|
-
var
|
|
2480
|
+
var DEFAULT_TITLE7 = "Voice Traces";
|
|
2481
|
+
var DEFAULT_DESCRIPTION7 = "Latest call timelines with provider latency, fallbacks, handoffs, and errors from your self-hosted trace store.";
|
|
2482
|
+
var escapeHtml8 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2191
2483
|
var formatMs = (value) => typeof value === "number" ? `${value}ms` : "n/a";
|
|
2192
2484
|
var formatProviders = (session) => session.providers.length ? session.providers.map((provider) => provider.provider).join(", ") : "No providers";
|
|
2193
2485
|
var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
|
|
@@ -2201,34 +2493,34 @@ var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
|
|
|
2201
2493
|
const failed = sessions.filter((session) => session.status === "failed").length;
|
|
2202
2494
|
const warnings = sessions.filter((session) => session.status === "warning").length;
|
|
2203
2495
|
return {
|
|
2204
|
-
description: options.description ??
|
|
2496
|
+
description: options.description ?? DEFAULT_DESCRIPTION7,
|
|
2205
2497
|
error: snapshot.error,
|
|
2206
2498
|
isLoading: snapshot.isLoading,
|
|
2207
2499
|
label: snapshot.error ? "Unavailable" : failed > 0 ? `${failed} failed` : warnings > 0 ? `${warnings} warning` : sessions.length ? `${sessions.length} recent` : snapshot.isLoading ? "Checking" : "No traces yet",
|
|
2208
2500
|
sessions,
|
|
2209
2501
|
status: snapshot.error ? "error" : failed > 0 ? "failed" : warnings > 0 ? "warning" : sessions.length ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2210
|
-
title: options.title ??
|
|
2502
|
+
title: options.title ?? DEFAULT_TITLE7,
|
|
2211
2503
|
updatedAt: snapshot.updatedAt
|
|
2212
2504
|
};
|
|
2213
2505
|
};
|
|
2214
2506
|
var renderVoiceTraceTimelineWidgetHTML = (snapshot, options = {}) => {
|
|
2215
2507
|
const model = createVoiceTraceTimelineViewModel(snapshot, options);
|
|
2216
|
-
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--${
|
|
2508
|
+
const sessions = model.sessions.length ? `<div class="absolute-voice-trace-timeline__sessions">${model.sessions.map((session) => `<article class="absolute-voice-trace-timeline__session absolute-voice-trace-timeline__session--${escapeHtml8(session.status)}">
|
|
2217
2509
|
<header>
|
|
2218
|
-
<strong>${
|
|
2219
|
-
<span>${
|
|
2510
|
+
<strong>${escapeHtml8(session.sessionId)}</strong>
|
|
2511
|
+
<span>${escapeHtml8(session.status)}</span>
|
|
2220
2512
|
</header>
|
|
2221
|
-
<p>${
|
|
2222
|
-
<a href="${
|
|
2513
|
+
<p>${escapeHtml8(session.label)} \xB7 ${escapeHtml8(session.durationLabel)} \xB7 ${escapeHtml8(session.providerLabel)}</p>
|
|
2514
|
+
<a href="${escapeHtml8(session.detailHref)}">Open timeline</a>
|
|
2223
2515
|
</article>`).join("")}</div>` : '<p class="absolute-voice-trace-timeline__empty">Run a voice session to see call timelines.</p>';
|
|
2224
|
-
return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${
|
|
2516
|
+
return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml8(model.status)}">
|
|
2225
2517
|
<header class="absolute-voice-trace-timeline__header">
|
|
2226
|
-
<span class="absolute-voice-trace-timeline__eyebrow">${
|
|
2227
|
-
<strong class="absolute-voice-trace-timeline__label">${
|
|
2518
|
+
<span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml8(model.title)}</span>
|
|
2519
|
+
<strong class="absolute-voice-trace-timeline__label">${escapeHtml8(model.label)}</strong>
|
|
2228
2520
|
</header>
|
|
2229
|
-
<p class="absolute-voice-trace-timeline__description">${
|
|
2521
|
+
<p class="absolute-voice-trace-timeline__description">${escapeHtml8(model.description)}</p>
|
|
2230
2522
|
${sessions}
|
|
2231
|
-
${model.error ? `<p class="absolute-voice-trace-timeline__error">${
|
|
2523
|
+
${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml8(model.error)}</p>` : ""}
|
|
2232
2524
|
</section>`;
|
|
2233
2525
|
};
|
|
2234
2526
|
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}`;
|
|
@@ -2385,10 +2677,10 @@ var createVoiceTurnLatencyStore = (path = "/api/turn-latency", options = {}) =>
|
|
|
2385
2677
|
};
|
|
2386
2678
|
|
|
2387
2679
|
// src/client/turnLatencyWidget.ts
|
|
2388
|
-
var
|
|
2389
|
-
var
|
|
2680
|
+
var DEFAULT_TITLE8 = "Turn Latency";
|
|
2681
|
+
var DEFAULT_DESCRIPTION8 = "Per-turn timing from first transcript to commit and assistant response start.";
|
|
2390
2682
|
var DEFAULT_PROOF_LABEL = "Run latency proof";
|
|
2391
|
-
var
|
|
2683
|
+
var escapeHtml9 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2392
2684
|
var formatMs2 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
2393
2685
|
var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
|
|
2394
2686
|
const turns = (snapshot.report?.turns ?? []).map((turn) => ({
|
|
@@ -2402,39 +2694,39 @@ var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
|
|
|
2402
2694
|
const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
|
|
2403
2695
|
const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
|
|
2404
2696
|
return {
|
|
2405
|
-
description: options.description ??
|
|
2697
|
+
description: options.description ?? DEFAULT_DESCRIPTION8,
|
|
2406
2698
|
error: snapshot.error,
|
|
2407
2699
|
isLoading: snapshot.isLoading,
|
|
2408
2700
|
label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} slow` : warningCount > 0 ? `${warningCount} warnings` : `avg ${formatMs2(snapshot.report?.averageTotalMs)}` : snapshot.isLoading ? "Checking" : "No turns",
|
|
2409
2701
|
proofLabel: options.proofPath ? options.proofLabel ?? DEFAULT_PROOF_LABEL : undefined,
|
|
2410
2702
|
showProofAction: Boolean(options.proofPath),
|
|
2411
2703
|
status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2412
|
-
title: options.title ??
|
|
2704
|
+
title: options.title ?? DEFAULT_TITLE8,
|
|
2413
2705
|
turns,
|
|
2414
2706
|
updatedAt: snapshot.updatedAt
|
|
2415
2707
|
};
|
|
2416
2708
|
};
|
|
2417
2709
|
var renderVoiceTurnLatencyHTML = (snapshot, options = {}) => {
|
|
2418
2710
|
const model = createVoiceTurnLatencyViewModel(snapshot, options);
|
|
2419
|
-
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--${
|
|
2711
|
+
const turns = model.turns.length ? `<div class="absolute-voice-turn-latency__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-latency__turn absolute-voice-turn-latency__turn--${escapeHtml9(turn.status)}">
|
|
2420
2712
|
<header>
|
|
2421
|
-
<strong>${
|
|
2422
|
-
<span>${
|
|
2713
|
+
<strong>${escapeHtml9(turn.label)}</strong>
|
|
2714
|
+
<span>${escapeHtml9(turn.status)}</span>
|
|
2423
2715
|
</header>
|
|
2424
2716
|
<dl>${turn.rows.map((row) => `<div>
|
|
2425
|
-
<dt>${
|
|
2426
|
-
<dd>${
|
|
2717
|
+
<dt>${escapeHtml9(row.label)}</dt>
|
|
2718
|
+
<dd>${escapeHtml9(row.value)}</dd>
|
|
2427
2719
|
</div>`).join("")}</dl>
|
|
2428
2720
|
</article>`).join("")}</div>` : '<p class="absolute-voice-turn-latency__empty">Complete a voice turn to see latency diagnostics.</p>';
|
|
2429
|
-
return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${
|
|
2721
|
+
return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml9(model.status)}">
|
|
2430
2722
|
<header class="absolute-voice-turn-latency__header">
|
|
2431
|
-
<span class="absolute-voice-turn-latency__eyebrow">${
|
|
2432
|
-
<strong class="absolute-voice-turn-latency__label">${
|
|
2723
|
+
<span class="absolute-voice-turn-latency__eyebrow">${escapeHtml9(model.title)}</span>
|
|
2724
|
+
<strong class="absolute-voice-turn-latency__label">${escapeHtml9(model.label)}</strong>
|
|
2433
2725
|
</header>
|
|
2434
|
-
<p class="absolute-voice-turn-latency__description">${
|
|
2435
|
-
${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${
|
|
2726
|
+
<p class="absolute-voice-turn-latency__description">${escapeHtml9(model.description)}</p>
|
|
2727
|
+
${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${escapeHtml9(model.proofLabel ?? DEFAULT_PROOF_LABEL)}</button>` : ""}
|
|
2436
2728
|
${turns}
|
|
2437
|
-
${model.error ? `<p class="absolute-voice-turn-latency__error">${
|
|
2729
|
+
${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml9(model.error)}</p>` : ""}
|
|
2438
2730
|
</section>`;
|
|
2439
2731
|
};
|
|
2440
2732
|
var mountVoiceTurnLatency = (element, path = "/api/turn-latency", options = {}) => {
|
|
@@ -2573,9 +2865,9 @@ var createVoiceTurnQualityStore = (path = "/api/turn-quality", options = {}) =>
|
|
|
2573
2865
|
};
|
|
2574
2866
|
|
|
2575
2867
|
// src/client/turnQualityWidget.ts
|
|
2576
|
-
var
|
|
2577
|
-
var
|
|
2578
|
-
var
|
|
2868
|
+
var DEFAULT_TITLE9 = "Turn Quality";
|
|
2869
|
+
var DEFAULT_DESCRIPTION9 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
|
|
2870
|
+
var escapeHtml10 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2579
2871
|
var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
|
|
2580
2872
|
var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
|
|
2581
2873
|
var getTurnDetail = (turn) => {
|
|
@@ -2613,37 +2905,37 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
|
|
|
2613
2905
|
const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
|
|
2614
2906
|
const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
|
|
2615
2907
|
return {
|
|
2616
|
-
description: options.description ??
|
|
2908
|
+
description: options.description ?? DEFAULT_DESCRIPTION9,
|
|
2617
2909
|
error: snapshot.error,
|
|
2618
2910
|
isLoading: snapshot.isLoading,
|
|
2619
2911
|
label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} failed` : warningCount > 0 ? `${warningCount} warnings` : `${turns.length} healthy` : snapshot.isLoading ? "Checking" : "No turns",
|
|
2620
2912
|
status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2621
|
-
title: options.title ??
|
|
2913
|
+
title: options.title ?? DEFAULT_TITLE9,
|
|
2622
2914
|
turns,
|
|
2623
2915
|
updatedAt: snapshot.updatedAt
|
|
2624
2916
|
};
|
|
2625
2917
|
};
|
|
2626
2918
|
var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
|
|
2627
2919
|
const model = createVoiceTurnQualityViewModel(snapshot, options);
|
|
2628
|
-
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--${
|
|
2920
|
+
const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${escapeHtml10(turn.status)}">
|
|
2629
2921
|
<header>
|
|
2630
|
-
<strong>${
|
|
2631
|
-
<span>${
|
|
2922
|
+
<strong>${escapeHtml10(turn.label)}</strong>
|
|
2923
|
+
<span>${escapeHtml10(turn.status)}</span>
|
|
2632
2924
|
</header>
|
|
2633
|
-
<p>${
|
|
2925
|
+
<p>${escapeHtml10(turn.detail)}</p>
|
|
2634
2926
|
<dl>${turn.rows.map((row) => `<div>
|
|
2635
|
-
<dt>${
|
|
2636
|
-
<dd>${
|
|
2927
|
+
<dt>${escapeHtml10(row.label)}</dt>
|
|
2928
|
+
<dd>${escapeHtml10(row.value)}</dd>
|
|
2637
2929
|
</div>`).join("")}</dl>
|
|
2638
2930
|
</article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
|
|
2639
|
-
return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${
|
|
2931
|
+
return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml10(model.status)}">
|
|
2640
2932
|
<header class="absolute-voice-turn-quality__header">
|
|
2641
|
-
<span class="absolute-voice-turn-quality__eyebrow">${
|
|
2642
|
-
<strong class="absolute-voice-turn-quality__label">${
|
|
2933
|
+
<span class="absolute-voice-turn-quality__eyebrow">${escapeHtml10(model.title)}</span>
|
|
2934
|
+
<strong class="absolute-voice-turn-quality__label">${escapeHtml10(model.label)}</strong>
|
|
2643
2935
|
</header>
|
|
2644
|
-
<p class="absolute-voice-turn-quality__description">${
|
|
2936
|
+
<p class="absolute-voice-turn-quality__description">${escapeHtml10(model.description)}</p>
|
|
2645
2937
|
${turns}
|
|
2646
|
-
${model.error ? `<p class="absolute-voice-turn-quality__error">${
|
|
2938
|
+
${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml10(model.error)}</p>` : ""}
|
|
2647
2939
|
</section>`;
|
|
2648
2940
|
};
|
|
2649
2941
|
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}`;
|
|
@@ -3425,6 +3717,7 @@ export {
|
|
|
3425
3717
|
createVoiceProviderSimulationControls,
|
|
3426
3718
|
createVoiceProviderCapabilities,
|
|
3427
3719
|
createVoiceOpsStatus,
|
|
3720
|
+
createVoiceOpsActionCenter,
|
|
3428
3721
|
createVoiceDeliveryRuntime,
|
|
3429
3722
|
createVoiceController,
|
|
3430
3723
|
createVoiceCampaignDialerProof
|