@absolutejs/voice 0.0.22-beta.152 → 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.
- package/README.md +39 -0
- package/dist/angular/index.d.ts +1 -0
- package/dist/angular/index.js +471 -137
- package/dist/angular/voice-delivery-runtime.component.d.ts +3 -0
- package/dist/angular/voice-delivery-runtime.service.d.ts +4 -0
- package/dist/angular/voice-ops-action-center.service.d.ts +13 -0
- package/dist/client/deliveryRuntime.d.ts +15 -0
- package/dist/client/deliveryRuntimeWidget.d.ts +3 -0
- package/dist/client/index.d.ts +6 -2
- package/dist/client/index.js +454 -114
- package/dist/client/opsActionCenter.d.ts +50 -0
- package/dist/client/opsActionCenterWidget.d.ts +29 -0
- package/dist/deliveryRuntime.d.ts +13 -1
- package/dist/index.js +44 -1
- package/dist/react/VoiceDeliveryRuntime.d.ts +2 -1
- package/dist/react/VoiceOpsActionCenter.d.ts +5 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +720 -286
- package/dist/react/useVoiceDeliveryRuntime.d.ts +5 -0
- package/dist/react/useVoiceOpsActionCenter.d.ts +11 -0
- package/dist/svelte/createVoiceDeliveryRuntime.d.ts +2 -0
- package/dist/svelte/createVoiceOpsActionCenter.d.ts +10 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +464 -115
- package/dist/vue/VoiceDeliveryRuntime.d.ts +9 -0
- package/dist/vue/VoiceOpsActionCenter.d.ts +13 -0
- package/dist/vue/index.d.ts +2 -0
- package/dist/vue/index.js +748 -292
- package/dist/vue/useVoiceDeliveryRuntime.d.ts +4 -0
- package/dist/vue/useVoiceOpsActionCenter.d.ts +11 -0
- package/package.json +1 -1
package/dist/svelte/index.js
CHANGED
|
@@ -190,6 +190,12 @@ var createVoiceCampaignDialerProofStore = (path = "/api/voice/campaigns/dialer-p
|
|
|
190
190
|
// src/svelte/createVoiceCampaignDialerProof.ts
|
|
191
191
|
var createVoiceCampaignDialerProof = (path = "/api/voice/campaigns/dialer-proof", options = {}) => createVoiceCampaignDialerProofStore(path, options);
|
|
192
192
|
// src/client/deliveryRuntime.ts
|
|
193
|
+
var getDefaultActionPath = (path, action, options) => {
|
|
194
|
+
if (action === "tick") {
|
|
195
|
+
return options.tickPath ?? `${path.replace(/\/$/, "")}/tick`;
|
|
196
|
+
}
|
|
197
|
+
return options.requeueDeadLettersPath ?? `${path.replace(/\/$/, "")}/requeue-dead-letters`;
|
|
198
|
+
};
|
|
193
199
|
var fetchVoiceDeliveryRuntime = async (path = "/api/voice-delivery-runtime", options = {}) => {
|
|
194
200
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
195
201
|
const response = await fetchImpl(path);
|
|
@@ -198,11 +204,29 @@ var fetchVoiceDeliveryRuntime = async (path = "/api/voice-delivery-runtime", opt
|
|
|
198
204
|
}
|
|
199
205
|
return await response.json();
|
|
200
206
|
};
|
|
207
|
+
var runVoiceDeliveryRuntimeAction = async (action, path = "/api/voice-delivery-runtime", options = {}) => {
|
|
208
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
209
|
+
const response = await fetchImpl(getDefaultActionPath(path, action, options), {
|
|
210
|
+
method: "POST"
|
|
211
|
+
});
|
|
212
|
+
if (!response.ok) {
|
|
213
|
+
throw new Error(`Voice delivery runtime ${action} failed: HTTP ${response.status}`);
|
|
214
|
+
}
|
|
215
|
+
const body = await response.json();
|
|
216
|
+
return {
|
|
217
|
+
action,
|
|
218
|
+
result: body.result,
|
|
219
|
+
summary: body.summary,
|
|
220
|
+
updatedAt: Date.now()
|
|
221
|
+
};
|
|
222
|
+
};
|
|
201
223
|
var createVoiceDeliveryRuntimeStore = (path = "/api/voice-delivery-runtime", options = {}) => {
|
|
202
224
|
const listeners = new Set;
|
|
203
225
|
let closed = false;
|
|
204
226
|
let timer;
|
|
205
227
|
let snapshot = {
|
|
228
|
+
actionError: null,
|
|
229
|
+
actionStatus: "idle",
|
|
206
230
|
error: null,
|
|
207
231
|
isLoading: false
|
|
208
232
|
};
|
|
@@ -224,6 +248,7 @@ var createVoiceDeliveryRuntimeStore = (path = "/api/voice-delivery-runtime", opt
|
|
|
224
248
|
try {
|
|
225
249
|
const report = await fetchVoiceDeliveryRuntime(path, options);
|
|
226
250
|
snapshot = {
|
|
251
|
+
...snapshot,
|
|
227
252
|
error: null,
|
|
228
253
|
isLoading: false,
|
|
229
254
|
report,
|
|
@@ -241,6 +266,37 @@ var createVoiceDeliveryRuntimeStore = (path = "/api/voice-delivery-runtime", opt
|
|
|
241
266
|
throw error;
|
|
242
267
|
}
|
|
243
268
|
};
|
|
269
|
+
const runAction = async (action) => {
|
|
270
|
+
if (closed) {
|
|
271
|
+
return snapshot.lastAction;
|
|
272
|
+
}
|
|
273
|
+
snapshot = {
|
|
274
|
+
...snapshot,
|
|
275
|
+
actionError: null,
|
|
276
|
+
actionStatus: "running"
|
|
277
|
+
};
|
|
278
|
+
emit();
|
|
279
|
+
try {
|
|
280
|
+
const result = await runVoiceDeliveryRuntimeAction(action, path, options);
|
|
281
|
+
snapshot = {
|
|
282
|
+
...snapshot,
|
|
283
|
+
actionError: null,
|
|
284
|
+
actionStatus: "completed",
|
|
285
|
+
lastAction: result
|
|
286
|
+
};
|
|
287
|
+
emit();
|
|
288
|
+
await refresh();
|
|
289
|
+
return result;
|
|
290
|
+
} catch (error) {
|
|
291
|
+
snapshot = {
|
|
292
|
+
...snapshot,
|
|
293
|
+
actionError: error instanceof Error ? error.message : String(error),
|
|
294
|
+
actionStatus: "failed"
|
|
295
|
+
};
|
|
296
|
+
emit();
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
244
300
|
const close = () => {
|
|
245
301
|
closed = true;
|
|
246
302
|
if (timer) {
|
|
@@ -258,7 +314,9 @@ var createVoiceDeliveryRuntimeStore = (path = "/api/voice-delivery-runtime", opt
|
|
|
258
314
|
close,
|
|
259
315
|
getServerSnapshot: () => snapshot,
|
|
260
316
|
getSnapshot: () => snapshot,
|
|
317
|
+
requeueDeadLetters: () => runAction("requeue-dead-letters"),
|
|
261
318
|
refresh,
|
|
319
|
+
tick: () => runAction("tick"),
|
|
262
320
|
subscribe: (listener) => {
|
|
263
321
|
listeners.add(listener);
|
|
264
322
|
return () => {
|
|
@@ -307,6 +365,8 @@ var createVoiceDeliveryRuntimeViewModel = (snapshot, options = {}) => {
|
|
|
307
365
|
return {
|
|
308
366
|
description: options.description ?? DEFAULT_DESCRIPTION,
|
|
309
367
|
error: snapshot.error,
|
|
368
|
+
actionError: snapshot.actionError,
|
|
369
|
+
actionStatus: snapshot.actionStatus,
|
|
310
370
|
isLoading: snapshot.isLoading,
|
|
311
371
|
isRunning: Boolean(report?.isRunning),
|
|
312
372
|
label: snapshot.error ? "Unavailable" : report ? report.isRunning ? "Running" : "Stopped" : "Checking",
|
|
@@ -323,6 +383,11 @@ var renderVoiceDeliveryRuntimeHTML = (snapshot, options = {}) => {
|
|
|
323
383
|
<strong>${escapeHtml(surface.detail)}</strong>
|
|
324
384
|
<small>${String(surface.failed)} failed · ${String(surface.deadLettered)} dead-lettered</small>
|
|
325
385
|
</li>`).join("");
|
|
386
|
+
const actions = options.includeActions === false ? "" : `<div class="absolute-voice-delivery-runtime__actions">
|
|
387
|
+
<button type="button" data-absolute-voice-delivery-runtime-action="tick">${model.actionStatus === "running" ? "Working..." : "Tick workers"}</button>
|
|
388
|
+
<button type="button" data-absolute-voice-delivery-runtime-action="requeue-dead-letters"${model.surfaces.some((surface) => surface.deadLettered > 0) ? "" : " disabled"}>Requeue dead letters</button>
|
|
389
|
+
</div>`;
|
|
390
|
+
const actionError = model.actionError ? `<p class="absolute-voice-delivery-runtime__error">${escapeHtml(model.actionError)}</p>` : "";
|
|
326
391
|
return `<section class="absolute-voice-delivery-runtime absolute-voice-delivery-runtime--${escapeHtml(model.status)}">
|
|
327
392
|
<header class="absolute-voice-delivery-runtime__header">
|
|
328
393
|
<span class="absolute-voice-delivery-runtime__eyebrow">${escapeHtml(model.title)}</span>
|
|
@@ -330,20 +395,38 @@ var renderVoiceDeliveryRuntimeHTML = (snapshot, options = {}) => {
|
|
|
330
395
|
</header>
|
|
331
396
|
<p class="absolute-voice-delivery-runtime__description">${escapeHtml(model.description)}</p>
|
|
332
397
|
<ul class="absolute-voice-delivery-runtime__surfaces">${surfaces}</ul>
|
|
398
|
+
${actions}
|
|
399
|
+
${actionError}
|
|
333
400
|
${model.error ? `<p class="absolute-voice-delivery-runtime__error">${escapeHtml(model.error)}</p>` : ""}
|
|
334
401
|
</section>`;
|
|
335
402
|
};
|
|
336
|
-
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__error{color:#9f1239;font-weight:700}`;
|
|
403
|
+
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}`;
|
|
337
404
|
var mountVoiceDeliveryRuntime = (element, path = "/api/voice-delivery-runtime", options = {}) => {
|
|
338
405
|
const store = createVoiceDeliveryRuntimeStore(path, options);
|
|
339
406
|
const render = () => {
|
|
340
407
|
element.innerHTML = renderVoiceDeliveryRuntimeHTML(store.getSnapshot(), options);
|
|
341
408
|
};
|
|
342
409
|
const unsubscribe = store.subscribe(render);
|
|
410
|
+
const handleClick = (event) => {
|
|
411
|
+
const target = event.target;
|
|
412
|
+
if (!(target instanceof Element)) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const action = target.closest("[data-absolute-voice-delivery-runtime-action]");
|
|
416
|
+
const actionName = action?.getAttribute("data-absolute-voice-delivery-runtime-action");
|
|
417
|
+
if (actionName === "tick") {
|
|
418
|
+
store.tick().catch(() => {});
|
|
419
|
+
}
|
|
420
|
+
if (actionName === "requeue-dead-letters") {
|
|
421
|
+
store.requeueDeadLetters().catch(() => {});
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
element.addEventListener?.("click", handleClick);
|
|
343
425
|
render();
|
|
344
426
|
store.refresh().catch(() => {});
|
|
345
427
|
return {
|
|
346
428
|
close: () => {
|
|
429
|
+
element.removeEventListener?.("click", handleClick);
|
|
347
430
|
unsubscribe();
|
|
348
431
|
store.close();
|
|
349
432
|
},
|
|
@@ -379,7 +462,272 @@ var createVoiceDeliveryRuntime = (path = "/api/voice-delivery-runtime", options
|
|
|
379
462
|
getHTML: () => renderVoiceDeliveryRuntimeHTML(store.getSnapshot(), options),
|
|
380
463
|
getSnapshot: store.getSnapshot,
|
|
381
464
|
getViewModel: () => createVoiceDeliveryRuntimeViewModel(store.getSnapshot(), options),
|
|
465
|
+
requeueDeadLetters: store.requeueDeadLetters,
|
|
382
466
|
refresh: store.refresh,
|
|
467
|
+
subscribe: store.subscribe,
|
|
468
|
+
tick: store.tick
|
|
469
|
+
};
|
|
470
|
+
};
|
|
471
|
+
// src/client/opsActionCenter.ts
|
|
472
|
+
var createVoiceOpsActionCenterActions = (options = {}) => {
|
|
473
|
+
const deliveryRuntimePath = options.deliveryRuntimePath ?? "/api/voice-delivery-runtime";
|
|
474
|
+
const actions = [];
|
|
475
|
+
if (options.includeProductionReadiness !== false) {
|
|
476
|
+
actions.push({
|
|
477
|
+
description: "Refresh the production readiness report.",
|
|
478
|
+
id: "production-readiness",
|
|
479
|
+
label: "Refresh readiness",
|
|
480
|
+
method: "GET",
|
|
481
|
+
path: options.productionReadinessPath ?? "/api/production-readiness"
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
if (options.includeDeliveryRuntime !== false) {
|
|
485
|
+
actions.push({
|
|
486
|
+
description: "Drain pending and failed audit/trace deliveries.",
|
|
487
|
+
id: "delivery-runtime.tick",
|
|
488
|
+
label: "Tick delivery workers",
|
|
489
|
+
method: "POST",
|
|
490
|
+
path: `${deliveryRuntimePath.replace(/\/$/, "")}/tick`
|
|
491
|
+
}, {
|
|
492
|
+
description: "Move reviewed dead letters back to live delivery queues.",
|
|
493
|
+
id: "delivery-runtime.requeue-dead-letters",
|
|
494
|
+
label: "Requeue dead letters",
|
|
495
|
+
method: "POST",
|
|
496
|
+
path: `${deliveryRuntimePath.replace(/\/$/, "")}/requeue-dead-letters`
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
if (options.includeTurnLatencyProof !== false) {
|
|
500
|
+
actions.push({
|
|
501
|
+
description: "Run the synthetic turn latency proof.",
|
|
502
|
+
id: "turn-latency.proof",
|
|
503
|
+
label: "Run latency proof",
|
|
504
|
+
method: "POST",
|
|
505
|
+
path: options.turnLatencyProofPath ?? "/api/turn-latency/proof"
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
if (options.includeProviderSimulation !== false) {
|
|
509
|
+
const pathPrefix = options.providerSimulationPathPrefix ?? "/api/stt-simulate";
|
|
510
|
+
for (const provider of options.providers ?? []) {
|
|
511
|
+
actions.push({
|
|
512
|
+
description: `Simulate ${provider} provider failure.`,
|
|
513
|
+
id: `provider.${provider}.failure`,
|
|
514
|
+
label: `Simulate ${provider} failure`,
|
|
515
|
+
method: "POST",
|
|
516
|
+
path: `${pathPrefix}/failure?provider=${encodeURIComponent(provider)}`
|
|
517
|
+
}, {
|
|
518
|
+
description: `Mark ${provider} provider recovered.`,
|
|
519
|
+
id: `provider.${provider}.recovery`,
|
|
520
|
+
label: `Recover ${provider}`,
|
|
521
|
+
method: "POST",
|
|
522
|
+
path: `${pathPrefix}/recovery?provider=${encodeURIComponent(provider)}`
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return actions;
|
|
527
|
+
};
|
|
528
|
+
var runVoiceOpsAction = async (action, options = {}) => {
|
|
529
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
530
|
+
const response = await fetchImpl(action.path, {
|
|
531
|
+
method: action.method ?? "POST"
|
|
532
|
+
});
|
|
533
|
+
const body = await response.json().catch(() => null);
|
|
534
|
+
if (!response.ok) {
|
|
535
|
+
const message = body && typeof body === "object" && "error" in body ? String(body.error) : `Voice ops action "${action.id}" failed: HTTP ${response.status}`;
|
|
536
|
+
throw new Error(message);
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
actionId: action.id,
|
|
540
|
+
body,
|
|
541
|
+
ok: response.ok,
|
|
542
|
+
ranAt: Date.now(),
|
|
543
|
+
status: response.status
|
|
544
|
+
};
|
|
545
|
+
};
|
|
546
|
+
var createVoiceOpsActionCenterStore = (options = {}) => {
|
|
547
|
+
const listeners = new Set;
|
|
548
|
+
let closed = false;
|
|
549
|
+
let timer;
|
|
550
|
+
let snapshot = {
|
|
551
|
+
actions: options.actions ?? createVoiceOpsActionCenterActions(),
|
|
552
|
+
error: null,
|
|
553
|
+
isRunning: false
|
|
554
|
+
};
|
|
555
|
+
const emit = () => {
|
|
556
|
+
for (const listener of listeners) {
|
|
557
|
+
listener();
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
const setActions = (actions) => {
|
|
561
|
+
snapshot = { ...snapshot, actions, updatedAt: Date.now() };
|
|
562
|
+
emit();
|
|
563
|
+
};
|
|
564
|
+
const run = async (actionId) => {
|
|
565
|
+
if (closed) {
|
|
566
|
+
return snapshot.lastResult;
|
|
567
|
+
}
|
|
568
|
+
const action = snapshot.actions.find((item) => item.id === actionId);
|
|
569
|
+
if (!action) {
|
|
570
|
+
throw new Error(`Voice ops action "${actionId}" is not configured.`);
|
|
571
|
+
}
|
|
572
|
+
if (action.disabled) {
|
|
573
|
+
throw new Error(`Voice ops action "${actionId}" is disabled.`);
|
|
574
|
+
}
|
|
575
|
+
snapshot = {
|
|
576
|
+
...snapshot,
|
|
577
|
+
error: null,
|
|
578
|
+
isRunning: true,
|
|
579
|
+
runningActionId: action.id
|
|
580
|
+
};
|
|
581
|
+
emit();
|
|
582
|
+
try {
|
|
583
|
+
const result = await runVoiceOpsAction(action, options);
|
|
584
|
+
snapshot = {
|
|
585
|
+
...snapshot,
|
|
586
|
+
error: null,
|
|
587
|
+
isRunning: false,
|
|
588
|
+
lastResult: result,
|
|
589
|
+
runningActionId: undefined,
|
|
590
|
+
updatedAt: Date.now()
|
|
591
|
+
};
|
|
592
|
+
emit();
|
|
593
|
+
return result;
|
|
594
|
+
} catch (error) {
|
|
595
|
+
snapshot = {
|
|
596
|
+
...snapshot,
|
|
597
|
+
error: error instanceof Error ? error.message : String(error),
|
|
598
|
+
isRunning: false,
|
|
599
|
+
runningActionId: undefined
|
|
600
|
+
};
|
|
601
|
+
emit();
|
|
602
|
+
throw error;
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
const close = () => {
|
|
606
|
+
closed = true;
|
|
607
|
+
if (timer) {
|
|
608
|
+
clearInterval(timer);
|
|
609
|
+
timer = undefined;
|
|
610
|
+
}
|
|
611
|
+
listeners.clear();
|
|
612
|
+
};
|
|
613
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
614
|
+
timer = setInterval(() => {
|
|
615
|
+
emit();
|
|
616
|
+
}, options.intervalMs);
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
close,
|
|
620
|
+
getServerSnapshot: () => snapshot,
|
|
621
|
+
getSnapshot: () => snapshot,
|
|
622
|
+
run,
|
|
623
|
+
setActions,
|
|
624
|
+
subscribe: (listener) => {
|
|
625
|
+
listeners.add(listener);
|
|
626
|
+
return () => {
|
|
627
|
+
listeners.delete(listener);
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
// src/client/opsActionCenterWidget.ts
|
|
634
|
+
var DEFAULT_TITLE2 = "Voice Ops Action Center";
|
|
635
|
+
var DEFAULT_DESCRIPTION2 = "Run production voice proofs and operator actions from one primitive panel.";
|
|
636
|
+
var escapeHtml2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
637
|
+
var createVoiceOpsActionCenterViewModel = (snapshot, options = {}) => {
|
|
638
|
+
const status = snapshot.error ? "error" : snapshot.isRunning ? "running" : snapshot.lastResult ? "completed" : "ready";
|
|
639
|
+
return {
|
|
640
|
+
actions: snapshot.actions.map((action) => ({
|
|
641
|
+
description: action.description ?? "",
|
|
642
|
+
disabled: Boolean(action.disabled || snapshot.isRunning),
|
|
643
|
+
id: action.id,
|
|
644
|
+
isRunning: snapshot.runningActionId === action.id,
|
|
645
|
+
label: action.label
|
|
646
|
+
})),
|
|
647
|
+
description: options.description ?? DEFAULT_DESCRIPTION2,
|
|
648
|
+
error: snapshot.error,
|
|
649
|
+
isRunning: snapshot.isRunning,
|
|
650
|
+
label: status === "error" ? "Needs attention" : status === "running" ? "Running" : status === "completed" ? "Action completed" : "Ready",
|
|
651
|
+
lastResultLabel: snapshot.lastResult ? `${snapshot.lastResult.actionId} returned HTTP ${snapshot.lastResult.status}` : "No action has run yet.",
|
|
652
|
+
status,
|
|
653
|
+
title: options.title ?? DEFAULT_TITLE2
|
|
654
|
+
};
|
|
655
|
+
};
|
|
656
|
+
var renderVoiceOpsActionCenterHTML = (snapshot, options = {}) => {
|
|
657
|
+
const model = createVoiceOpsActionCenterViewModel(snapshot, options);
|
|
658
|
+
const actions = model.actions.map((action) => `<button type="button" data-absolute-voice-ops-action="${escapeHtml2(action.id)}"${action.disabled ? " disabled" : ""}>
|
|
659
|
+
${escapeHtml2(action.isRunning ? "Working..." : action.label)}
|
|
660
|
+
</button>`).join("");
|
|
661
|
+
return `<section class="absolute-voice-ops-action-center absolute-voice-ops-action-center--${escapeHtml2(model.status)}">
|
|
662
|
+
<header class="absolute-voice-ops-action-center__header">
|
|
663
|
+
<span class="absolute-voice-ops-action-center__eyebrow">${escapeHtml2(model.title)}</span>
|
|
664
|
+
<strong class="absolute-voice-ops-action-center__label">${escapeHtml2(model.label)}</strong>
|
|
665
|
+
</header>
|
|
666
|
+
<p class="absolute-voice-ops-action-center__description">${escapeHtml2(model.description)}</p>
|
|
667
|
+
<div class="absolute-voice-ops-action-center__actions">${actions}</div>
|
|
668
|
+
<p class="absolute-voice-ops-action-center__result">${escapeHtml2(model.lastResultLabel)}</p>
|
|
669
|
+
${model.error ? `<p class="absolute-voice-ops-action-center__error">${escapeHtml2(model.error)}</p>` : ""}
|
|
670
|
+
</section>`;
|
|
671
|
+
};
|
|
672
|
+
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}`;
|
|
673
|
+
var mountVoiceOpsActionCenter = (element, options = {}) => {
|
|
674
|
+
const store = createVoiceOpsActionCenterStore(options);
|
|
675
|
+
const render = () => {
|
|
676
|
+
element.innerHTML = renderVoiceOpsActionCenterHTML(store.getSnapshot(), options);
|
|
677
|
+
};
|
|
678
|
+
const unsubscribe = store.subscribe(render);
|
|
679
|
+
const handleClick = (event) => {
|
|
680
|
+
const target = event.target;
|
|
681
|
+
if (!(target instanceof Element)) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
const action = target.closest("[data-absolute-voice-ops-action]");
|
|
685
|
+
const actionId = action?.getAttribute("data-absolute-voice-ops-action");
|
|
686
|
+
if (actionId) {
|
|
687
|
+
store.run(actionId).catch(() => {});
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
element.addEventListener?.("click", handleClick);
|
|
691
|
+
render();
|
|
692
|
+
return {
|
|
693
|
+
close: () => {
|
|
694
|
+
element.removeEventListener?.("click", handleClick);
|
|
695
|
+
unsubscribe();
|
|
696
|
+
store.close();
|
|
697
|
+
},
|
|
698
|
+
run: store.run
|
|
699
|
+
};
|
|
700
|
+
};
|
|
701
|
+
var defineVoiceOpsActionCenterElement = (tagName = "absolute-voice-ops-action-center", options = {}) => {
|
|
702
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
customElements.define(tagName, class AbsoluteVoiceOpsActionCenterElement extends HTMLElement {
|
|
706
|
+
mounted;
|
|
707
|
+
connectedCallback() {
|
|
708
|
+
this.mounted = mountVoiceOpsActionCenter(this, {
|
|
709
|
+
...options,
|
|
710
|
+
description: this.getAttribute("description") ?? options.description,
|
|
711
|
+
title: this.getAttribute("title") ?? options.title
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
disconnectedCallback() {
|
|
715
|
+
this.mounted?.close();
|
|
716
|
+
this.mounted = undefined;
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
// src/svelte/createVoiceOpsActionCenter.ts
|
|
722
|
+
var createVoiceOpsActionCenter = (options = {}) => {
|
|
723
|
+
const store = createVoiceOpsActionCenterStore(options);
|
|
724
|
+
return {
|
|
725
|
+
close: store.close,
|
|
726
|
+
getHTML: () => renderVoiceOpsActionCenterHTML(store.getSnapshot(), options),
|
|
727
|
+
getSnapshot: store.getSnapshot,
|
|
728
|
+
getViewModel: () => createVoiceOpsActionCenterViewModel(store.getSnapshot(), options),
|
|
729
|
+
run: store.run,
|
|
730
|
+
setActions: store.setActions,
|
|
383
731
|
subscribe: store.subscribe
|
|
384
732
|
};
|
|
385
733
|
};
|
|
@@ -463,8 +811,8 @@ var createVoiceOpsStatusStore = (path = "/api/voice/ops-status", options = {}) =
|
|
|
463
811
|
};
|
|
464
812
|
|
|
465
813
|
// src/client/opsStatusWidget.ts
|
|
466
|
-
var
|
|
467
|
-
var
|
|
814
|
+
var DEFAULT_TITLE3 = "Voice Ops Status";
|
|
815
|
+
var DEFAULT_DESCRIPTION3 = "Certified workflow, provider, and handoff readiness from your AbsoluteJS voice app.";
|
|
468
816
|
var SURFACE_LABELS = {
|
|
469
817
|
handoffs: "Handoffs",
|
|
470
818
|
providers: "Providers",
|
|
@@ -472,7 +820,7 @@ var SURFACE_LABELS = {
|
|
|
472
820
|
sessions: "Sessions",
|
|
473
821
|
workflows: "Workflows"
|
|
474
822
|
};
|
|
475
|
-
var
|
|
823
|
+
var escapeHtml3 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
476
824
|
var readNumber = (value, key) => value && typeof value === "object" && (key in value) ? Number(value[key] ?? 0) : 0;
|
|
477
825
|
var surfaceDetail = (surface) => {
|
|
478
826
|
const total = readNumber(surface, "total");
|
|
@@ -512,7 +860,7 @@ var createVoiceOpsStatusViewModel = (snapshot, options = {}) => {
|
|
|
512
860
|
};
|
|
513
861
|
});
|
|
514
862
|
return {
|
|
515
|
-
description: options.description ??
|
|
863
|
+
description: options.description ?? DEFAULT_DESCRIPTION3,
|
|
516
864
|
error: snapshot.error,
|
|
517
865
|
isLoading: snapshot.isLoading,
|
|
518
866
|
label: getVoiceOpsStatusLabel(report, snapshot.error),
|
|
@@ -520,31 +868,31 @@ var createVoiceOpsStatusViewModel = (snapshot, options = {}) => {
|
|
|
520
868
|
passed: report?.passed ?? 0,
|
|
521
869
|
status: snapshot.error ? "error" : report ? report.status : snapshot.isLoading ? "loading" : "loading",
|
|
522
870
|
surfaces,
|
|
523
|
-
title: options.title ??
|
|
871
|
+
title: options.title ?? DEFAULT_TITLE3,
|
|
524
872
|
total: report?.total ?? 0,
|
|
525
873
|
updatedAt: snapshot.updatedAt
|
|
526
874
|
};
|
|
527
875
|
};
|
|
528
876
|
var renderVoiceOpsStatusHTML = (snapshot, options = {}) => {
|
|
529
877
|
const model = createVoiceOpsStatusViewModel(snapshot, options);
|
|
530
|
-
const surfaces = model.surfaces.length ? model.surfaces.map((surface) => `<li class="absolute-voice-ops-status__surface absolute-voice-ops-status__surface--${
|
|
531
|
-
<span>${
|
|
532
|
-
<strong>${
|
|
878
|
+
const surfaces = model.surfaces.length ? model.surfaces.map((surface) => `<li class="absolute-voice-ops-status__surface absolute-voice-ops-status__surface--${escapeHtml3(surface.status)}">
|
|
879
|
+
<span>${escapeHtml3(surface.label)}</span>
|
|
880
|
+
<strong>${escapeHtml3(surface.detail)}</strong>
|
|
533
881
|
</li>`).join("") : '<li class="absolute-voice-ops-status__surface"><span>Status</span><strong>Waiting for first check</strong></li>';
|
|
534
|
-
const links = model.links.length ? `<nav class="absolute-voice-ops-status__links">${model.links.slice(0, 4).map((link) => `<a href="${
|
|
535
|
-
return `<section class="absolute-voice-ops-status absolute-voice-ops-status--${
|
|
882
|
+
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>` : "";
|
|
883
|
+
return `<section class="absolute-voice-ops-status absolute-voice-ops-status--${escapeHtml3(model.status)}">
|
|
536
884
|
<header class="absolute-voice-ops-status__header">
|
|
537
|
-
<span class="absolute-voice-ops-status__eyebrow">${
|
|
538
|
-
<strong class="absolute-voice-ops-status__label">${
|
|
885
|
+
<span class="absolute-voice-ops-status__eyebrow">${escapeHtml3(model.title)}</span>
|
|
886
|
+
<strong class="absolute-voice-ops-status__label">${escapeHtml3(model.label)}</strong>
|
|
539
887
|
</header>
|
|
540
|
-
<p class="absolute-voice-ops-status__description">${
|
|
888
|
+
<p class="absolute-voice-ops-status__description">${escapeHtml3(model.description)}</p>
|
|
541
889
|
<div class="absolute-voice-ops-status__summary">
|
|
542
890
|
<span>${model.passed} passing</span>
|
|
543
891
|
<span>${Math.max(model.total - model.passed, 0)} failing</span>
|
|
544
892
|
<span>${model.total} checks</span>
|
|
545
893
|
</div>
|
|
546
894
|
<ul class="absolute-voice-ops-status__surfaces">${surfaces}</ul>
|
|
547
|
-
${model.error ? `<p class="absolute-voice-ops-status__error">${
|
|
895
|
+
${model.error ? `<p class="absolute-voice-ops-status__error">${escapeHtml3(model.error)}</p>` : ""}
|
|
548
896
|
${links}
|
|
549
897
|
</section>`;
|
|
550
898
|
};
|
|
@@ -679,7 +1027,7 @@ var createVoiceProviderSimulationControlsStore = (options) => {
|
|
|
679
1027
|
};
|
|
680
1028
|
|
|
681
1029
|
// src/client/providerSimulationControlsWidget.ts
|
|
682
|
-
var
|
|
1030
|
+
var escapeHtml4 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
683
1031
|
var formatKind = (kind) => (kind ?? "stt").toUpperCase();
|
|
684
1032
|
var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
|
|
685
1033
|
const configuredProviders = options.providers.filter((provider) => provider.configured !== false);
|
|
@@ -699,18 +1047,18 @@ var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
|
|
|
699
1047
|
};
|
|
700
1048
|
var renderVoiceProviderSimulationControlsHTML = (snapshot, options) => {
|
|
701
1049
|
const model = createVoiceProviderSimulationControlsViewModel(snapshot, options);
|
|
702
|
-
const failureButtons = model.failureProviders.map((provider) => `<button type="button" data-voice-provider-fail="${
|
|
703
|
-
const recoveryButtons = model.providers.map((provider) => `<button type="button" data-voice-provider-recover="${
|
|
1050
|
+
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("");
|
|
1051
|
+
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("");
|
|
704
1052
|
return `<section class="absolute-voice-provider-simulation absolute-voice-provider-simulation--${snapshot.error ? "error" : snapshot.isRunning ? "running" : "ready"}">
|
|
705
1053
|
<header class="absolute-voice-provider-simulation__header">
|
|
706
|
-
<span class="absolute-voice-provider-simulation__eyebrow">${
|
|
707
|
-
<strong class="absolute-voice-provider-simulation__label">${
|
|
1054
|
+
<span class="absolute-voice-provider-simulation__eyebrow">${escapeHtml4(model.title)}</span>
|
|
1055
|
+
<strong class="absolute-voice-provider-simulation__label">${escapeHtml4(model.label)}</strong>
|
|
708
1056
|
</header>
|
|
709
|
-
<p class="absolute-voice-provider-simulation__description">${
|
|
710
|
-
${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${
|
|
1057
|
+
<p class="absolute-voice-provider-simulation__description">${escapeHtml4(model.description)}</p>
|
|
1058
|
+
${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${escapeHtml4(options.fallbackRequiredMessage ?? "Configure fallback providers before simulating failure.")}</p>`}
|
|
711
1059
|
<div class="absolute-voice-provider-simulation__actions">${failureButtons}${recoveryButtons}</div>
|
|
712
|
-
${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${
|
|
713
|
-
${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${
|
|
1060
|
+
${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${escapeHtml4(snapshot.error)}</p>` : ""}
|
|
1061
|
+
${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${escapeHtml4(model.resultText)}</pre>` : ""}
|
|
714
1062
|
</section>`;
|
|
715
1063
|
};
|
|
716
1064
|
var bindVoiceProviderSimulationControls = (element, store) => {
|
|
@@ -865,9 +1213,9 @@ var createVoiceProviderCapabilitiesStore = (path = "/api/provider-capabilities",
|
|
|
865
1213
|
};
|
|
866
1214
|
|
|
867
1215
|
// src/client/providerCapabilitiesWidget.ts
|
|
868
|
-
var
|
|
869
|
-
var
|
|
870
|
-
var
|
|
1216
|
+
var DEFAULT_TITLE4 = "Provider Capabilities";
|
|
1217
|
+
var DEFAULT_DESCRIPTION4 = "Configured, selected, and healthy voice providers for this deployment.";
|
|
1218
|
+
var escapeHtml5 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
871
1219
|
var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
|
|
872
1220
|
var formatKind2 = (kind) => kind.toUpperCase();
|
|
873
1221
|
var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
@@ -911,36 +1259,36 @@ var createVoiceProviderCapabilitiesViewModel = (snapshot, options = {}) => {
|
|
|
911
1259
|
const selectedCount = snapshot.report?.selected ?? capabilities.filter((capability) => capability.selected).length;
|
|
912
1260
|
return {
|
|
913
1261
|
capabilities,
|
|
914
|
-
description: options.description ??
|
|
1262
|
+
description: options.description ?? DEFAULT_DESCRIPTION4,
|
|
915
1263
|
error: snapshot.error,
|
|
916
1264
|
isLoading: snapshot.isLoading,
|
|
917
1265
|
label: snapshot.error ? "Unavailable" : capabilities.length ? warningCount > 0 ? `${warningCount} needs attention` : `${selectedCount} selected` : snapshot.isLoading ? "Checking" : "No capabilities",
|
|
918
1266
|
status: snapshot.error ? "error" : capabilities.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
919
|
-
title: options.title ??
|
|
1267
|
+
title: options.title ?? DEFAULT_TITLE4,
|
|
920
1268
|
updatedAt: snapshot.updatedAt
|
|
921
1269
|
};
|
|
922
1270
|
};
|
|
923
1271
|
var renderVoiceProviderCapabilitiesHTML = (snapshot, options = {}) => {
|
|
924
1272
|
const model = createVoiceProviderCapabilitiesViewModel(snapshot, options);
|
|
925
|
-
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--${
|
|
1273
|
+
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)}">
|
|
926
1274
|
<header>
|
|
927
|
-
<strong>${
|
|
928
|
-
<span>${
|
|
1275
|
+
<strong>${escapeHtml5(capability.label)}</strong>
|
|
1276
|
+
<span>${escapeHtml5(formatStatus(capability.status))}</span>
|
|
929
1277
|
</header>
|
|
930
|
-
<p>${
|
|
1278
|
+
<p>${escapeHtml5(capability.detail)}</p>
|
|
931
1279
|
<dl>${capability.rows.map((row) => `<div>
|
|
932
|
-
<dt>${
|
|
933
|
-
<dd>${
|
|
1280
|
+
<dt>${escapeHtml5(row.label)}</dt>
|
|
1281
|
+
<dd>${escapeHtml5(row.value)}</dd>
|
|
934
1282
|
</div>`).join("")}</dl>
|
|
935
1283
|
</article>`).join("")}</div>` : '<p class="absolute-voice-provider-capabilities__empty">Configure provider capabilities to see deployment coverage.</p>';
|
|
936
|
-
return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${
|
|
1284
|
+
return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${escapeHtml5(model.status)}">
|
|
937
1285
|
<header class="absolute-voice-provider-capabilities__header">
|
|
938
|
-
<span class="absolute-voice-provider-capabilities__eyebrow">${
|
|
939
|
-
<strong class="absolute-voice-provider-capabilities__label">${
|
|
1286
|
+
<span class="absolute-voice-provider-capabilities__eyebrow">${escapeHtml5(model.title)}</span>
|
|
1287
|
+
<strong class="absolute-voice-provider-capabilities__label">${escapeHtml5(model.label)}</strong>
|
|
940
1288
|
</header>
|
|
941
|
-
<p class="absolute-voice-provider-capabilities__description">${
|
|
1289
|
+
<p class="absolute-voice-provider-capabilities__description">${escapeHtml5(model.description)}</p>
|
|
942
1290
|
${capabilities}
|
|
943
|
-
${model.error ? `<p class="absolute-voice-provider-capabilities__error">${
|
|
1291
|
+
${model.error ? `<p class="absolute-voice-provider-capabilities__error">${escapeHtml5(model.error)}</p>` : ""}
|
|
944
1292
|
</section>`;
|
|
945
1293
|
};
|
|
946
1294
|
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}`;
|
|
@@ -1713,9 +2061,9 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
|
|
|
1713
2061
|
};
|
|
1714
2062
|
|
|
1715
2063
|
// src/client/providerStatusWidget.ts
|
|
1716
|
-
var
|
|
1717
|
-
var
|
|
1718
|
-
var
|
|
2064
|
+
var DEFAULT_TITLE5 = "Voice Providers";
|
|
2065
|
+
var DEFAULT_DESCRIPTION5 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
|
|
2066
|
+
var escapeHtml6 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1719
2067
|
var formatProvider2 = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
|
|
1720
2068
|
var formatStatus2 = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
1721
2069
|
var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
|
|
@@ -1759,37 +2107,37 @@ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
|
|
|
1759
2107
|
const warningCount = providers.filter((provider) => isWarningStatus2(provider.status)).length;
|
|
1760
2108
|
const healthyCount = providers.filter((provider) => provider.status === "healthy").length;
|
|
1761
2109
|
return {
|
|
1762
|
-
description: options.description ??
|
|
2110
|
+
description: options.description ?? DEFAULT_DESCRIPTION5,
|
|
1763
2111
|
error: snapshot.error,
|
|
1764
2112
|
isLoading: snapshot.isLoading,
|
|
1765
2113
|
label: snapshot.error ? "Unavailable" : providers.length ? warningCount > 0 ? `${warningCount} needs attention` : `${healthyCount} healthy` : snapshot.isLoading ? "Checking" : "No provider traffic",
|
|
1766
2114
|
providers,
|
|
1767
2115
|
status: snapshot.error ? "error" : providers.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
1768
|
-
title: options.title ??
|
|
2116
|
+
title: options.title ?? DEFAULT_TITLE5,
|
|
1769
2117
|
updatedAt: snapshot.updatedAt
|
|
1770
2118
|
};
|
|
1771
2119
|
};
|
|
1772
2120
|
var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
|
|
1773
2121
|
const model = createVoiceProviderStatusViewModel(snapshot, options);
|
|
1774
|
-
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--${
|
|
2122
|
+
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)}">
|
|
1775
2123
|
<header>
|
|
1776
|
-
<strong>${
|
|
1777
|
-
<span>${
|
|
2124
|
+
<strong>${escapeHtml6(provider.label)}</strong>
|
|
2125
|
+
<span>${escapeHtml6(formatStatus2(provider.status))}</span>
|
|
1778
2126
|
</header>
|
|
1779
|
-
<p>${
|
|
2127
|
+
<p>${escapeHtml6(provider.detail)}</p>
|
|
1780
2128
|
<dl>${provider.rows.map((row) => `<div>
|
|
1781
|
-
<dt>${
|
|
1782
|
-
<dd>${
|
|
2129
|
+
<dt>${escapeHtml6(row.label)}</dt>
|
|
2130
|
+
<dd>${escapeHtml6(row.value)}</dd>
|
|
1783
2131
|
</div>`).join("")}</dl>
|
|
1784
2132
|
</article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
|
|
1785
|
-
return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${
|
|
2133
|
+
return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml6(model.status)}">
|
|
1786
2134
|
<header class="absolute-voice-provider-status__header">
|
|
1787
|
-
<span class="absolute-voice-provider-status__eyebrow">${
|
|
1788
|
-
<strong class="absolute-voice-provider-status__label">${
|
|
2135
|
+
<span class="absolute-voice-provider-status__eyebrow">${escapeHtml6(model.title)}</span>
|
|
2136
|
+
<strong class="absolute-voice-provider-status__label">${escapeHtml6(model.label)}</strong>
|
|
1789
2137
|
</header>
|
|
1790
|
-
<p class="absolute-voice-provider-status__description">${
|
|
2138
|
+
<p class="absolute-voice-provider-status__description">${escapeHtml6(model.description)}</p>
|
|
1791
2139
|
${providers}
|
|
1792
|
-
${model.error ? `<p class="absolute-voice-provider-status__error">${
|
|
2140
|
+
${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml6(model.error)}</p>` : ""}
|
|
1793
2141
|
</section>`;
|
|
1794
2142
|
};
|
|
1795
2143
|
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}`;
|
|
@@ -1920,9 +2268,9 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
|
|
|
1920
2268
|
};
|
|
1921
2269
|
|
|
1922
2270
|
// src/client/routingStatusWidget.ts
|
|
1923
|
-
var
|
|
1924
|
-
var
|
|
1925
|
-
var
|
|
2271
|
+
var DEFAULT_TITLE6 = "Voice Routing";
|
|
2272
|
+
var DEFAULT_DESCRIPTION6 = "Latest provider routing decision from the self-hosted trace store.";
|
|
2273
|
+
var escapeHtml7 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1926
2274
|
var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
|
|
1927
2275
|
var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
1928
2276
|
const decision = snapshot.decision;
|
|
@@ -1946,30 +2294,30 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
|
1946
2294
|
] : [];
|
|
1947
2295
|
return {
|
|
1948
2296
|
decision,
|
|
1949
|
-
description: options.description ??
|
|
2297
|
+
description: options.description ?? DEFAULT_DESCRIPTION6,
|
|
1950
2298
|
error: snapshot.error,
|
|
1951
2299
|
isLoading: snapshot.isLoading,
|
|
1952
2300
|
label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
|
|
1953
2301
|
rows,
|
|
1954
2302
|
status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
1955
|
-
title: options.title ??
|
|
2303
|
+
title: options.title ?? DEFAULT_TITLE6,
|
|
1956
2304
|
updatedAt: snapshot.updatedAt
|
|
1957
2305
|
};
|
|
1958
2306
|
};
|
|
1959
2307
|
var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
1960
2308
|
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
1961
2309
|
const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
|
|
1962
|
-
<span>${
|
|
1963
|
-
<strong>${
|
|
2310
|
+
<span>${escapeHtml7(row.label)}</span>
|
|
2311
|
+
<strong>${escapeHtml7(row.value)}</strong>
|
|
1964
2312
|
</div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
|
|
1965
|
-
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${
|
|
2313
|
+
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml7(model.status)}">
|
|
1966
2314
|
<header class="absolute-voice-routing-status__header">
|
|
1967
|
-
<span class="absolute-voice-routing-status__eyebrow">${
|
|
1968
|
-
<strong class="absolute-voice-routing-status__label">${
|
|
2315
|
+
<span class="absolute-voice-routing-status__eyebrow">${escapeHtml7(model.title)}</span>
|
|
2316
|
+
<strong class="absolute-voice-routing-status__label">${escapeHtml7(model.label)}</strong>
|
|
1969
2317
|
</header>
|
|
1970
|
-
<p class="absolute-voice-routing-status__description">${
|
|
2318
|
+
<p class="absolute-voice-routing-status__description">${escapeHtml7(model.description)}</p>
|
|
1971
2319
|
${rows}
|
|
1972
|
-
${model.error ? `<p class="absolute-voice-routing-status__error">${
|
|
2320
|
+
${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml7(model.error)}</p>` : ""}
|
|
1973
2321
|
</section>`;
|
|
1974
2322
|
};
|
|
1975
2323
|
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}`;
|
|
@@ -2100,9 +2448,9 @@ var createVoiceTraceTimelineStore = (path = "/api/voice-traces", options = {}) =
|
|
|
2100
2448
|
};
|
|
2101
2449
|
|
|
2102
2450
|
// src/client/traceTimelineWidget.ts
|
|
2103
|
-
var
|
|
2104
|
-
var
|
|
2105
|
-
var
|
|
2451
|
+
var DEFAULT_TITLE7 = "Voice Traces";
|
|
2452
|
+
var DEFAULT_DESCRIPTION7 = "Latest call timelines with provider latency, fallbacks, handoffs, and errors from your self-hosted trace store.";
|
|
2453
|
+
var escapeHtml8 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2106
2454
|
var formatMs = (value) => typeof value === "number" ? `${value}ms` : "n/a";
|
|
2107
2455
|
var formatProviders = (session) => session.providers.length ? session.providers.map((provider) => provider.provider).join(", ") : "No providers";
|
|
2108
2456
|
var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
|
|
@@ -2116,34 +2464,34 @@ var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
|
|
|
2116
2464
|
const failed = sessions.filter((session) => session.status === "failed").length;
|
|
2117
2465
|
const warnings = sessions.filter((session) => session.status === "warning").length;
|
|
2118
2466
|
return {
|
|
2119
|
-
description: options.description ??
|
|
2467
|
+
description: options.description ?? DEFAULT_DESCRIPTION7,
|
|
2120
2468
|
error: snapshot.error,
|
|
2121
2469
|
isLoading: snapshot.isLoading,
|
|
2122
2470
|
label: snapshot.error ? "Unavailable" : failed > 0 ? `${failed} failed` : warnings > 0 ? `${warnings} warning` : sessions.length ? `${sessions.length} recent` : snapshot.isLoading ? "Checking" : "No traces yet",
|
|
2123
2471
|
sessions,
|
|
2124
2472
|
status: snapshot.error ? "error" : failed > 0 ? "failed" : warnings > 0 ? "warning" : sessions.length ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2125
|
-
title: options.title ??
|
|
2473
|
+
title: options.title ?? DEFAULT_TITLE7,
|
|
2126
2474
|
updatedAt: snapshot.updatedAt
|
|
2127
2475
|
};
|
|
2128
2476
|
};
|
|
2129
2477
|
var renderVoiceTraceTimelineWidgetHTML = (snapshot, options = {}) => {
|
|
2130
2478
|
const model = createVoiceTraceTimelineViewModel(snapshot, options);
|
|
2131
|
-
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--${
|
|
2479
|
+
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)}">
|
|
2132
2480
|
<header>
|
|
2133
|
-
<strong>${
|
|
2134
|
-
<span>${
|
|
2481
|
+
<strong>${escapeHtml8(session.sessionId)}</strong>
|
|
2482
|
+
<span>${escapeHtml8(session.status)}</span>
|
|
2135
2483
|
</header>
|
|
2136
|
-
<p>${
|
|
2137
|
-
<a href="${
|
|
2484
|
+
<p>${escapeHtml8(session.label)} \xB7 ${escapeHtml8(session.durationLabel)} \xB7 ${escapeHtml8(session.providerLabel)}</p>
|
|
2485
|
+
<a href="${escapeHtml8(session.detailHref)}">Open timeline</a>
|
|
2138
2486
|
</article>`).join("")}</div>` : '<p class="absolute-voice-trace-timeline__empty">Run a voice session to see call timelines.</p>';
|
|
2139
|
-
return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${
|
|
2487
|
+
return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml8(model.status)}">
|
|
2140
2488
|
<header class="absolute-voice-trace-timeline__header">
|
|
2141
|
-
<span class="absolute-voice-trace-timeline__eyebrow">${
|
|
2142
|
-
<strong class="absolute-voice-trace-timeline__label">${
|
|
2489
|
+
<span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml8(model.title)}</span>
|
|
2490
|
+
<strong class="absolute-voice-trace-timeline__label">${escapeHtml8(model.label)}</strong>
|
|
2143
2491
|
</header>
|
|
2144
|
-
<p class="absolute-voice-trace-timeline__description">${
|
|
2492
|
+
<p class="absolute-voice-trace-timeline__description">${escapeHtml8(model.description)}</p>
|
|
2145
2493
|
${sessions}
|
|
2146
|
-
${model.error ? `<p class="absolute-voice-trace-timeline__error">${
|
|
2494
|
+
${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml8(model.error)}</p>` : ""}
|
|
2147
2495
|
</section>`;
|
|
2148
2496
|
};
|
|
2149
2497
|
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}`;
|
|
@@ -2300,10 +2648,10 @@ var createVoiceTurnLatencyStore = (path = "/api/turn-latency", options = {}) =>
|
|
|
2300
2648
|
};
|
|
2301
2649
|
|
|
2302
2650
|
// src/client/turnLatencyWidget.ts
|
|
2303
|
-
var
|
|
2304
|
-
var
|
|
2651
|
+
var DEFAULT_TITLE8 = "Turn Latency";
|
|
2652
|
+
var DEFAULT_DESCRIPTION8 = "Per-turn timing from first transcript to commit and assistant response start.";
|
|
2305
2653
|
var DEFAULT_PROOF_LABEL = "Run latency proof";
|
|
2306
|
-
var
|
|
2654
|
+
var escapeHtml9 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2307
2655
|
var formatMs2 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
2308
2656
|
var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
|
|
2309
2657
|
const turns = (snapshot.report?.turns ?? []).map((turn) => ({
|
|
@@ -2317,39 +2665,39 @@ var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
|
|
|
2317
2665
|
const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
|
|
2318
2666
|
const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
|
|
2319
2667
|
return {
|
|
2320
|
-
description: options.description ??
|
|
2668
|
+
description: options.description ?? DEFAULT_DESCRIPTION8,
|
|
2321
2669
|
error: snapshot.error,
|
|
2322
2670
|
isLoading: snapshot.isLoading,
|
|
2323
2671
|
label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} slow` : warningCount > 0 ? `${warningCount} warnings` : `avg ${formatMs2(snapshot.report?.averageTotalMs)}` : snapshot.isLoading ? "Checking" : "No turns",
|
|
2324
2672
|
proofLabel: options.proofPath ? options.proofLabel ?? DEFAULT_PROOF_LABEL : undefined,
|
|
2325
2673
|
showProofAction: Boolean(options.proofPath),
|
|
2326
2674
|
status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2327
|
-
title: options.title ??
|
|
2675
|
+
title: options.title ?? DEFAULT_TITLE8,
|
|
2328
2676
|
turns,
|
|
2329
2677
|
updatedAt: snapshot.updatedAt
|
|
2330
2678
|
};
|
|
2331
2679
|
};
|
|
2332
2680
|
var renderVoiceTurnLatencyHTML = (snapshot, options = {}) => {
|
|
2333
2681
|
const model = createVoiceTurnLatencyViewModel(snapshot, options);
|
|
2334
|
-
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--${
|
|
2682
|
+
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)}">
|
|
2335
2683
|
<header>
|
|
2336
|
-
<strong>${
|
|
2337
|
-
<span>${
|
|
2684
|
+
<strong>${escapeHtml9(turn.label)}</strong>
|
|
2685
|
+
<span>${escapeHtml9(turn.status)}</span>
|
|
2338
2686
|
</header>
|
|
2339
2687
|
<dl>${turn.rows.map((row) => `<div>
|
|
2340
|
-
<dt>${
|
|
2341
|
-
<dd>${
|
|
2688
|
+
<dt>${escapeHtml9(row.label)}</dt>
|
|
2689
|
+
<dd>${escapeHtml9(row.value)}</dd>
|
|
2342
2690
|
</div>`).join("")}</dl>
|
|
2343
2691
|
</article>`).join("")}</div>` : '<p class="absolute-voice-turn-latency__empty">Complete a voice turn to see latency diagnostics.</p>';
|
|
2344
|
-
return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${
|
|
2692
|
+
return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml9(model.status)}">
|
|
2345
2693
|
<header class="absolute-voice-turn-latency__header">
|
|
2346
|
-
<span class="absolute-voice-turn-latency__eyebrow">${
|
|
2347
|
-
<strong class="absolute-voice-turn-latency__label">${
|
|
2694
|
+
<span class="absolute-voice-turn-latency__eyebrow">${escapeHtml9(model.title)}</span>
|
|
2695
|
+
<strong class="absolute-voice-turn-latency__label">${escapeHtml9(model.label)}</strong>
|
|
2348
2696
|
</header>
|
|
2349
|
-
<p class="absolute-voice-turn-latency__description">${
|
|
2350
|
-
${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${
|
|
2697
|
+
<p class="absolute-voice-turn-latency__description">${escapeHtml9(model.description)}</p>
|
|
2698
|
+
${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${escapeHtml9(model.proofLabel ?? DEFAULT_PROOF_LABEL)}</button>` : ""}
|
|
2351
2699
|
${turns}
|
|
2352
|
-
${model.error ? `<p class="absolute-voice-turn-latency__error">${
|
|
2700
|
+
${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml9(model.error)}</p>` : ""}
|
|
2353
2701
|
</section>`;
|
|
2354
2702
|
};
|
|
2355
2703
|
var mountVoiceTurnLatency = (element, path = "/api/turn-latency", options = {}) => {
|
|
@@ -2488,9 +2836,9 @@ var createVoiceTurnQualityStore = (path = "/api/turn-quality", options = {}) =>
|
|
|
2488
2836
|
};
|
|
2489
2837
|
|
|
2490
2838
|
// src/client/turnQualityWidget.ts
|
|
2491
|
-
var
|
|
2492
|
-
var
|
|
2493
|
-
var
|
|
2839
|
+
var DEFAULT_TITLE9 = "Turn Quality";
|
|
2840
|
+
var DEFAULT_DESCRIPTION9 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
|
|
2841
|
+
var escapeHtml10 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2494
2842
|
var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
|
|
2495
2843
|
var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
|
|
2496
2844
|
var getTurnDetail = (turn) => {
|
|
@@ -2528,37 +2876,37 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
|
|
|
2528
2876
|
const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
|
|
2529
2877
|
const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
|
|
2530
2878
|
return {
|
|
2531
|
-
description: options.description ??
|
|
2879
|
+
description: options.description ?? DEFAULT_DESCRIPTION9,
|
|
2532
2880
|
error: snapshot.error,
|
|
2533
2881
|
isLoading: snapshot.isLoading,
|
|
2534
2882
|
label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} failed` : warningCount > 0 ? `${warningCount} warnings` : `${turns.length} healthy` : snapshot.isLoading ? "Checking" : "No turns",
|
|
2535
2883
|
status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2536
|
-
title: options.title ??
|
|
2884
|
+
title: options.title ?? DEFAULT_TITLE9,
|
|
2537
2885
|
turns,
|
|
2538
2886
|
updatedAt: snapshot.updatedAt
|
|
2539
2887
|
};
|
|
2540
2888
|
};
|
|
2541
2889
|
var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
|
|
2542
2890
|
const model = createVoiceTurnQualityViewModel(snapshot, options);
|
|
2543
|
-
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--${
|
|
2891
|
+
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)}">
|
|
2544
2892
|
<header>
|
|
2545
|
-
<strong>${
|
|
2546
|
-
<span>${
|
|
2893
|
+
<strong>${escapeHtml10(turn.label)}</strong>
|
|
2894
|
+
<span>${escapeHtml10(turn.status)}</span>
|
|
2547
2895
|
</header>
|
|
2548
|
-
<p>${
|
|
2896
|
+
<p>${escapeHtml10(turn.detail)}</p>
|
|
2549
2897
|
<dl>${turn.rows.map((row) => `<div>
|
|
2550
|
-
<dt>${
|
|
2551
|
-
<dd>${
|
|
2898
|
+
<dt>${escapeHtml10(row.label)}</dt>
|
|
2899
|
+
<dd>${escapeHtml10(row.value)}</dd>
|
|
2552
2900
|
</div>`).join("")}</dl>
|
|
2553
2901
|
</article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
|
|
2554
|
-
return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${
|
|
2902
|
+
return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml10(model.status)}">
|
|
2555
2903
|
<header class="absolute-voice-turn-quality__header">
|
|
2556
|
-
<span class="absolute-voice-turn-quality__eyebrow">${
|
|
2557
|
-
<strong class="absolute-voice-turn-quality__label">${
|
|
2904
|
+
<span class="absolute-voice-turn-quality__eyebrow">${escapeHtml10(model.title)}</span>
|
|
2905
|
+
<strong class="absolute-voice-turn-quality__label">${escapeHtml10(model.label)}</strong>
|
|
2558
2906
|
</header>
|
|
2559
|
-
<p class="absolute-voice-turn-quality__description">${
|
|
2907
|
+
<p class="absolute-voice-turn-quality__description">${escapeHtml10(model.description)}</p>
|
|
2560
2908
|
${turns}
|
|
2561
|
-
${model.error ? `<p class="absolute-voice-turn-quality__error">${
|
|
2909
|
+
${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml10(model.error)}</p>` : ""}
|
|
2562
2910
|
</section>`;
|
|
2563
2911
|
};
|
|
2564
2912
|
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}`;
|
|
@@ -3340,6 +3688,7 @@ export {
|
|
|
3340
3688
|
createVoiceProviderSimulationControls,
|
|
3341
3689
|
createVoiceProviderCapabilities,
|
|
3342
3690
|
createVoiceOpsStatus,
|
|
3691
|
+
createVoiceOpsActionCenter,
|
|
3343
3692
|
createVoiceDeliveryRuntime,
|
|
3344
3693
|
createVoiceController,
|
|
3345
3694
|
createVoiceCampaignDialerProof
|