@absolutejs/voice 0.0.22-beta.196 → 0.0.22-beta.198
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 +60 -1
- package/dist/campaign.d.ts +7 -3
- package/dist/index.js +641 -525
- package/dist/opsRecovery.d.ts +1 -0
- package/dist/productionReadiness.d.ts +12 -0
- package/dist/sessionReplay.d.ts +2 -0
- package/dist/traceTimeline.d.ts +3 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6662,11 +6662,37 @@ recipient-no-consent,Barbara,+15550001004,no,delta`,
|
|
|
6662
6662
|
};
|
|
6663
6663
|
};
|
|
6664
6664
|
var escapeHtml3 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
6665
|
+
var getString = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
6666
|
+
var campaignAttemptSessionId = (attempt) => getString(attempt.metadata?.sessionId) ?? getString(attempt.metadata?.voiceSessionId) ?? getString(attempt.metadata?.callSessionId);
|
|
6667
|
+
var resolveCampaignOperationsRecordHref = (value, input) => {
|
|
6668
|
+
if (value === false || !input.sessionId) {
|
|
6669
|
+
return;
|
|
6670
|
+
}
|
|
6671
|
+
if (typeof value === "function") {
|
|
6672
|
+
return value(input);
|
|
6673
|
+
}
|
|
6674
|
+
if (typeof value === "string") {
|
|
6675
|
+
const encoded = encodeURIComponent(input.sessionId);
|
|
6676
|
+
return value.includes(":sessionId") ? value.replace(":sessionId", encoded) : `${value.replace(/\/$/, "")}/${encoded}`;
|
|
6677
|
+
}
|
|
6678
|
+
return;
|
|
6679
|
+
};
|
|
6665
6680
|
var renderVoiceCampaignsHTML = (records, options = {}) => {
|
|
6666
6681
|
const title = options.title ?? "Voice Campaigns";
|
|
6667
6682
|
const rows = records.map((record) => `<tr><td>${escapeHtml3(record.campaign.name)}</td><td>${escapeHtml3(record.campaign.status)}</td><td>${String(record.recipients.length)}</td><td>${String(record.attempts.length)}</td><td>${new Date(record.campaign.updatedAt).toLocaleString()}</td></tr>`).join("");
|
|
6668
6683
|
const summary = summarizeVoiceCampaigns(records);
|
|
6669
|
-
|
|
6684
|
+
const attemptRows = records.flatMap((record) => record.attempts.slice(-8).reverse().map((attempt) => {
|
|
6685
|
+
const recipient = record.recipients.find((item) => item.id === attempt.recipientId);
|
|
6686
|
+
const sessionId = campaignAttemptSessionId(attempt);
|
|
6687
|
+
const href = resolveCampaignOperationsRecordHref(options.operationsRecordHref, {
|
|
6688
|
+
attempt,
|
|
6689
|
+
campaign: record.campaign,
|
|
6690
|
+
recipient,
|
|
6691
|
+
sessionId
|
|
6692
|
+
});
|
|
6693
|
+
return `<tr><td>${escapeHtml3(record.campaign.name)}</td><td>${escapeHtml3(attempt.status)}</td><td>${escapeHtml3(recipient?.phone ?? attempt.recipientId)}</td><td>${escapeHtml3(sessionId ?? "")}</td><td>${href ? `<a href="${escapeHtml3(href)}">Open operations record</a>` : ""}</td></tr>`;
|
|
6694
|
+
})).slice(0, 20).join("");
|
|
6695
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml3(title)}</title><style>body{background:#111827;color:#f9fafb;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}.hero{background:linear-gradient(135deg,rgba(251,146,60,.18),rgba(45,212,191,.12));border:1px solid #334155;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#fdba74;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:18px 0}.grid article,table{background:#172033;border:1px solid #334155;border-radius:18px}.grid article{padding:16px}.grid span{color:#aab5c0}.grid strong{display:block;font-size:2rem;margin:.25rem 0}table{border-collapse:collapse;margin-top:18px;overflow:hidden;width:100%}td,th{border-bottom:1px solid #334155;padding:12px;text-align:left}a{color:#fdba74}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted outbound</p><h1>${escapeHtml3(title)}</h1><p>Campaign orchestration, recipients, attempts, retries, and outcomes without a hosted dialer dashboard.</p><section class="grid"><article><span>Campaigns</span><strong>${String(summary.campaigns.total)}</strong></article><article><span>Recipients</span><strong>${String(summary.recipients.total)}</strong></article><article><span>Attempts</span><strong>${String(summary.attempts.total)}</strong></article><article><span>Running</span><strong>${String(summary.campaigns.running)}</strong></article></section></section><table><thead><tr><th>Name</th><th>Status</th><th>Recipients</th><th>Attempts</th><th>Updated</th></tr></thead><tbody>${rows || '<tr><td colspan="5">No campaigns yet.</td></tr>'}</tbody></table><h2>Recent attempts</h2><table><thead><tr><th>Campaign</th><th>Status</th><th>Recipient</th><th>Session</th><th>Debug</th></tr></thead><tbody>${attemptRows || '<tr><td colspan="5">No attempts yet.</td></tr>'}</tbody></table></main></body></html>`;
|
|
6670
6696
|
};
|
|
6671
6697
|
var renderVoiceCampaignObservabilityHTML = (report, options = {}) => {
|
|
6672
6698
|
const title = options.title ?? "Voice Campaign Observability";
|
|
@@ -8634,7 +8660,7 @@ import { Elysia as Elysia4 } from "elysia";
|
|
|
8634
8660
|
|
|
8635
8661
|
// src/providerHealth.ts
|
|
8636
8662
|
import { Elysia as Elysia3 } from "elysia";
|
|
8637
|
-
var
|
|
8663
|
+
var getString2 = (value) => typeof value === "string" ? value : undefined;
|
|
8638
8664
|
var getNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
8639
8665
|
var isProviderStatus = (value) => value === "success" || value === "fallback" || value === "error";
|
|
8640
8666
|
var summarizeVoiceProviderHealth = async (input) => {
|
|
@@ -8740,7 +8766,7 @@ var summarizeVoiceProviderHealth = async (input) => {
|
|
|
8740
8766
|
if (event.payload.timedOut === true) {
|
|
8741
8767
|
entry.timeoutCount += 1;
|
|
8742
8768
|
}
|
|
8743
|
-
entry.lastError =
|
|
8769
|
+
entry.lastError = getString2(event.payload.error);
|
|
8744
8770
|
entry.lastErrorAt = event.at;
|
|
8745
8771
|
entry.rateLimited ||= event.payload.rateLimited === true;
|
|
8746
8772
|
}
|
|
@@ -8836,16 +8862,16 @@ var renderCountMap = (values) => {
|
|
|
8836
8862
|
"</div>"
|
|
8837
8863
|
].join("");
|
|
8838
8864
|
};
|
|
8839
|
-
var
|
|
8865
|
+
var getString3 = (value) => typeof value === "string" ? value : undefined;
|
|
8840
8866
|
var getRecentFailures = (events, maxFailures, replayHref) => events.filter((event) => event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.error === "string") || event.type === "assistant.guardrail" && event.payload.action === "blocked").toReversed().slice(0, maxFailures).map((event) => {
|
|
8841
8867
|
const failure = {
|
|
8842
8868
|
at: event.at,
|
|
8843
|
-
assistantId:
|
|
8844
|
-
error:
|
|
8845
|
-
provider:
|
|
8869
|
+
assistantId: getString3(event.payload.assistantId),
|
|
8870
|
+
error: getString3(event.payload.error),
|
|
8871
|
+
provider: getString3(event.payload.provider),
|
|
8846
8872
|
rateLimited: event.payload.rateLimited === true ? true : undefined,
|
|
8847
8873
|
sessionId: event.sessionId,
|
|
8848
|
-
status:
|
|
8874
|
+
status: getString3(event.payload.providerStatus),
|
|
8849
8875
|
turnId: event.turnId,
|
|
8850
8876
|
type: event.type
|
|
8851
8877
|
};
|
|
@@ -9783,7 +9809,7 @@ var buildVoiceAuditExport = (events, options = {}) => {
|
|
|
9783
9809
|
|
|
9784
9810
|
// src/auditRoutes.ts
|
|
9785
9811
|
var escapeHtml8 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
9786
|
-
var
|
|
9812
|
+
var getString4 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
9787
9813
|
var getNumber2 = (value) => {
|
|
9788
9814
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
9789
9815
|
return value;
|
|
@@ -9795,7 +9821,7 @@ var getNumber2 = (value) => {
|
|
|
9795
9821
|
return;
|
|
9796
9822
|
};
|
|
9797
9823
|
var parseType = (value) => {
|
|
9798
|
-
const text =
|
|
9824
|
+
const text = getString4(value);
|
|
9799
9825
|
return text && [
|
|
9800
9826
|
"handoff",
|
|
9801
9827
|
"operator.action",
|
|
@@ -9805,11 +9831,11 @@ var parseType = (value) => {
|
|
|
9805
9831
|
].includes(text) ? text : undefined;
|
|
9806
9832
|
};
|
|
9807
9833
|
var parseOutcome = (value) => {
|
|
9808
|
-
const text =
|
|
9834
|
+
const text = getString4(value);
|
|
9809
9835
|
return text && ["error", "skipped", "success"].includes(text) ? text : undefined;
|
|
9810
9836
|
};
|
|
9811
9837
|
var parseBoolean = (value, fallback = false) => {
|
|
9812
|
-
const text =
|
|
9838
|
+
const text = getString4(value)?.toLowerCase();
|
|
9813
9839
|
if (["1", "true", "yes"].includes(text ?? "")) {
|
|
9814
9840
|
return true;
|
|
9815
9841
|
}
|
|
@@ -9819,7 +9845,7 @@ var parseBoolean = (value, fallback = false) => {
|
|
|
9819
9845
|
return fallback;
|
|
9820
9846
|
};
|
|
9821
9847
|
var parseExportFormat = (value) => {
|
|
9822
|
-
const text =
|
|
9848
|
+
const text = getString4(value)?.toLowerCase();
|
|
9823
9849
|
return text === "markdown" || text === "md" ? "markdown" : text === "html" ? "html" : "json";
|
|
9824
9850
|
};
|
|
9825
9851
|
var increment2 = (counts, key) => {
|
|
@@ -9831,17 +9857,17 @@ var increment2 = (counts, key) => {
|
|
|
9831
9857
|
var sortedCounts = (counts) => [...counts.entries()].sort(([leftKey, leftCount], [rightKey, rightCount]) => rightCount - leftCount || leftKey.localeCompare(rightKey));
|
|
9832
9858
|
var resolveVoiceAuditTrailFilter = (query = {}, base = {}) => ({
|
|
9833
9859
|
...base,
|
|
9834
|
-
actorId:
|
|
9860
|
+
actorId: getString4(query.actorId) ?? base.actorId,
|
|
9835
9861
|
after: getNumber2(query.after) ?? base.after,
|
|
9836
9862
|
afterOrAt: getNumber2(query.afterOrAt) ?? base.afterOrAt,
|
|
9837
9863
|
before: getNumber2(query.before) ?? base.before,
|
|
9838
9864
|
beforeOrAt: getNumber2(query.beforeOrAt) ?? base.beforeOrAt,
|
|
9839
9865
|
limit: getNumber2(query.limit) ?? base.limit,
|
|
9840
9866
|
outcome: parseOutcome(query.outcome) ?? base.outcome,
|
|
9841
|
-
resourceId:
|
|
9842
|
-
resourceType:
|
|
9843
|
-
sessionId:
|
|
9844
|
-
traceId:
|
|
9867
|
+
resourceId: getString4(query.resourceId) ?? base.resourceId,
|
|
9868
|
+
resourceType: getString4(query.resourceType) ?? base.resourceType,
|
|
9869
|
+
sessionId: getString4(query.sessionId) ?? base.sessionId,
|
|
9870
|
+
traceId: getString4(query.traceId) ?? base.traceId,
|
|
9845
9871
|
type: parseType(query.type) ?? base.type
|
|
9846
9872
|
});
|
|
9847
9873
|
var summarizeVoiceAuditTrail = (events) => {
|
|
@@ -10416,7 +10442,7 @@ var createVoiceAuditSinkDeliveryWorkerLoop = (options) => {
|
|
|
10416
10442
|
// src/auditDeliveryRoutes.ts
|
|
10417
10443
|
import { Elysia as Elysia6 } from "elysia";
|
|
10418
10444
|
var escapeHtml9 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
10419
|
-
var
|
|
10445
|
+
var getString5 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
10420
10446
|
var getNumber3 = (value) => {
|
|
10421
10447
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
10422
10448
|
return value;
|
|
@@ -10428,13 +10454,13 @@ var getNumber3 = (value) => {
|
|
|
10428
10454
|
return;
|
|
10429
10455
|
};
|
|
10430
10456
|
var parseStatus = (value) => {
|
|
10431
|
-
const text =
|
|
10457
|
+
const text = getString5(value);
|
|
10432
10458
|
return text === "pending" || text === "delivered" || text === "failed" || text === "skipped" || text === "all" ? text : undefined;
|
|
10433
10459
|
};
|
|
10434
10460
|
var resolveVoiceAuditDeliveryFilter = (query = {}, base = {}) => ({
|
|
10435
10461
|
...base,
|
|
10436
10462
|
limit: getNumber3(query.limit) ?? base.limit,
|
|
10437
|
-
q:
|
|
10463
|
+
q: getString5(query.q) ?? base.q,
|
|
10438
10464
|
status: parseStatus(query.status) ?? base.status
|
|
10439
10465
|
});
|
|
10440
10466
|
var deliverySearchText = (delivery) => [
|
|
@@ -10779,7 +10805,7 @@ var createVoiceReconnectContractRoutes = (options) => {
|
|
|
10779
10805
|
// src/diagnosticsRoutes.ts
|
|
10780
10806
|
import { Elysia as Elysia9 } from "elysia";
|
|
10781
10807
|
var escapeHtml12 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
10782
|
-
var
|
|
10808
|
+
var getString6 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
10783
10809
|
var getNumber4 = (value) => {
|
|
10784
10810
|
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : undefined;
|
|
10785
10811
|
return typeof parsed === "number" && Number.isFinite(parsed) ? parsed : undefined;
|
|
@@ -10794,15 +10820,15 @@ var parseTraceTypeFilter = (value) => {
|
|
|
10794
10820
|
};
|
|
10795
10821
|
var resolveVoiceDiagnosticsTraceFilter = (query) => ({
|
|
10796
10822
|
limit: getNumber4(query.limit),
|
|
10797
|
-
scenarioId:
|
|
10798
|
-
sessionId:
|
|
10799
|
-
traceId:
|
|
10800
|
-
turnId:
|
|
10823
|
+
scenarioId: getString6(query.scenarioId),
|
|
10824
|
+
sessionId: getString6(query.sessionId),
|
|
10825
|
+
traceId: getString6(query.traceId),
|
|
10826
|
+
turnId: getString6(query.turnId),
|
|
10801
10827
|
type: parseTraceTypeFilter(query.type)
|
|
10802
10828
|
});
|
|
10803
10829
|
var filterByDiagnosticsQuery = (events, query) => {
|
|
10804
|
-
const provider =
|
|
10805
|
-
const status =
|
|
10830
|
+
const provider = getString6(query.provider);
|
|
10831
|
+
const status = getString6(query.status);
|
|
10806
10832
|
const since = getNumber4(query.since);
|
|
10807
10833
|
const until = getNumber4(query.until);
|
|
10808
10834
|
return filterVoiceTraceEvents(events, resolveVoiceDiagnosticsTraceFilter(query)).filter((event) => (!provider || event.payload.provider === provider) && (!status || event.payload.providerStatus === status || event.payload.status === status) && (since === undefined || event.at >= since) && (until === undefined || event.at <= until));
|
|
@@ -13202,7 +13228,7 @@ import { Elysia as Elysia17 } from "elysia";
|
|
|
13202
13228
|
// src/handoffHealth.ts
|
|
13203
13229
|
import { Elysia as Elysia16 } from "elysia";
|
|
13204
13230
|
var escapeHtml18 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13205
|
-
var
|
|
13231
|
+
var getString7 = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
13206
13232
|
var isStatus = (value) => value === "delivered" || value === "failed" || value === "skipped";
|
|
13207
13233
|
var increment3 = (record, key) => {
|
|
13208
13234
|
record[key] = (record[key] ?? 0) + 1;
|
|
@@ -13210,11 +13236,11 @@ var increment3 = (record, key) => {
|
|
|
13210
13236
|
var normalizeDelivery = (adapterId, value) => {
|
|
13211
13237
|
const record = value && typeof value === "object" ? value : {};
|
|
13212
13238
|
return {
|
|
13213
|
-
adapterId:
|
|
13214
|
-
adapterKind:
|
|
13239
|
+
adapterId: getString7(record.adapterId) ?? adapterId,
|
|
13240
|
+
adapterKind: getString7(record.adapterKind),
|
|
13215
13241
|
deliveredAt: typeof record.deliveredAt === "number" ? record.deliveredAt : undefined,
|
|
13216
|
-
deliveredTo:
|
|
13217
|
-
error:
|
|
13242
|
+
deliveredTo: getString7(record.deliveredTo),
|
|
13243
|
+
error: getString7(record.error),
|
|
13218
13244
|
status: isStatus(record.status) ? record.status : "failed"
|
|
13219
13245
|
};
|
|
13220
13246
|
};
|
|
@@ -13248,13 +13274,13 @@ var summarizeVoiceHandoffHealth = async (options = {}) => {
|
|
|
13248
13274
|
const status = isStatus(event.payload.status) ? event.payload.status : "failed";
|
|
13249
13275
|
const deliveries = normalizeDeliveries(event.payload);
|
|
13250
13276
|
const item = {
|
|
13251
|
-
action:
|
|
13277
|
+
action: getString7(event.payload.action),
|
|
13252
13278
|
at: event.at,
|
|
13253
13279
|
deliveries,
|
|
13254
|
-
reason:
|
|
13280
|
+
reason: getString7(event.payload.reason),
|
|
13255
13281
|
sessionId: event.sessionId,
|
|
13256
13282
|
status,
|
|
13257
|
-
target:
|
|
13283
|
+
target: getString7(event.payload.target)
|
|
13258
13284
|
};
|
|
13259
13285
|
return {
|
|
13260
13286
|
...item,
|
|
@@ -13403,7 +13429,7 @@ var DEFAULT_THRESHOLDS = {
|
|
|
13403
13429
|
maxProviderFallbackRate: 0.25,
|
|
13404
13430
|
maxProviderTimeoutRate: 0.03
|
|
13405
13431
|
};
|
|
13406
|
-
var
|
|
13432
|
+
var getString8 = (value) => typeof value === "string" ? value : undefined;
|
|
13407
13433
|
var getNumber5 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
13408
13434
|
var rate = (count, total) => count / Math.max(1, total);
|
|
13409
13435
|
var roundMetric2 = (value) => Math.round(value * 1e4) / 1e4;
|
|
@@ -13422,11 +13448,11 @@ var evaluateVoiceQuality = async (input) => {
|
|
|
13422
13448
|
const assistantReplies = events.filter((event) => event.type === "turn.assistant");
|
|
13423
13449
|
const sessionIdsWithAssistantReply = new Set(assistantReplies.map((event) => event.sessionId));
|
|
13424
13450
|
const sessionsWithTurns = new Set(committedTurns.map((event) => event.sessionId));
|
|
13425
|
-
const emptyTurns = committedTurns.filter((event) => !
|
|
13451
|
+
const emptyTurns = committedTurns.filter((event) => !getString8(event.payload.text)?.trim());
|
|
13426
13452
|
const turnTextsBySession = new Map;
|
|
13427
13453
|
let duplicateTurns = 0;
|
|
13428
13454
|
for (const turn of committedTurns) {
|
|
13429
|
-
const normalized =
|
|
13455
|
+
const normalized = getString8(turn.payload.text)?.trim().toLowerCase();
|
|
13430
13456
|
if (!normalized) {
|
|
13431
13457
|
continue;
|
|
13432
13458
|
}
|
|
@@ -13546,7 +13572,7 @@ var createVoiceQualityRoutes = (options) => {
|
|
|
13546
13572
|
var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13547
13573
|
var rate2 = (count, total) => count / Math.max(1, total);
|
|
13548
13574
|
var normalizeSearchText = (value) => value.trim().toLowerCase();
|
|
13549
|
-
var
|
|
13575
|
+
var getString9 = (value) => typeof value === "string" ? value : undefined;
|
|
13550
13576
|
var getObject = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
13551
13577
|
var getPathValue = (value, path) => {
|
|
13552
13578
|
let current = value;
|
|
@@ -13633,16 +13659,16 @@ var runVoiceSessionEvals = async (options = {}) => {
|
|
|
13633
13659
|
trend: buildTrend(limitedSessions)
|
|
13634
13660
|
};
|
|
13635
13661
|
};
|
|
13636
|
-
var getSessionText = (events, type) => events.filter((event) => event.type === type).map((event) =>
|
|
13662
|
+
var getSessionText = (events, type) => events.filter((event) => event.type === type).map((event) => getString9(event.payload.text)).filter((text) => Boolean(text?.trim())).join(`
|
|
13637
13663
|
`);
|
|
13638
13664
|
var countProviderErrors = (events) => events.filter((event) => event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.provider === "string")).length;
|
|
13639
13665
|
var evaluateScenarioSession = (scenario, sessionId, events) => {
|
|
13640
13666
|
const issues = [];
|
|
13641
13667
|
const committedText = getSessionText(events, "turn.committed");
|
|
13642
13668
|
const assistantText = getSessionText(events, "turn.assistant");
|
|
13643
|
-
const lifecycleTypes = events.filter((event) => event.type === "call.lifecycle").map((event) =>
|
|
13644
|
-
const dispositions = events.filter((event) => event.type === "call.lifecycle").map((event) =>
|
|
13645
|
-
const handoffActions = events.filter((event) => event.type === "call.handoff").map((event) =>
|
|
13669
|
+
const lifecycleTypes = events.filter((event) => event.type === "call.lifecycle").map((event) => getString9(event.payload.type)).filter((type) => Boolean(type));
|
|
13670
|
+
const dispositions = events.filter((event) => event.type === "call.lifecycle").map((event) => getString9(event.payload.disposition)).filter((disposition) => Boolean(disposition));
|
|
13671
|
+
const handoffActions = events.filter((event) => event.type === "call.handoff").map((event) => getString9(event.payload.action)).filter((action) => Boolean(action));
|
|
13646
13672
|
const turnCount = events.filter((event) => event.type === "turn.committed").length;
|
|
13647
13673
|
const sessionErrorCount = events.filter((event) => event.type === "session.error").length;
|
|
13648
13674
|
const providerErrorCount = countProviderErrors(events);
|
|
@@ -13691,12 +13717,12 @@ var evaluateScenarioSession = (scenario, sessionId, events) => {
|
|
|
13691
13717
|
}
|
|
13692
13718
|
}
|
|
13693
13719
|
for (const contractId of scenario.requiredWorkflowContracts ?? []) {
|
|
13694
|
-
const matching = workflowContractEvents.filter((event) =>
|
|
13720
|
+
const matching = workflowContractEvents.filter((event) => getString9(event.payload.contractId) === contractId);
|
|
13695
13721
|
if (matching.length === 0) {
|
|
13696
13722
|
issues.push(`Missing workflow contract: ${contractId}`);
|
|
13697
13723
|
continue;
|
|
13698
13724
|
}
|
|
13699
|
-
if (matching.some((event) =>
|
|
13725
|
+
if (matching.some((event) => getString9(event.payload.status) !== "pass")) {
|
|
13700
13726
|
issues.push(`Workflow contract failed: ${contractId}`);
|
|
13701
13727
|
}
|
|
13702
13728
|
}
|
|
@@ -15139,11 +15165,24 @@ var createVoiceWorkflowContractHandler = (input) => {
|
|
|
15139
15165
|
};
|
|
15140
15166
|
// src/sessionReplay.ts
|
|
15141
15167
|
import { Elysia as Elysia22 } from "elysia";
|
|
15142
|
-
var
|
|
15168
|
+
var getString10 = (value) => typeof value === "string" ? value : undefined;
|
|
15143
15169
|
var escapeHtml24 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15144
15170
|
var increment4 = (record, key) => {
|
|
15145
15171
|
record[key] = (record[key] ?? 0) + 1;
|
|
15146
15172
|
};
|
|
15173
|
+
var resolveSessionHref = (value, session) => {
|
|
15174
|
+
if (value === false) {
|
|
15175
|
+
return;
|
|
15176
|
+
}
|
|
15177
|
+
if (typeof value === "function") {
|
|
15178
|
+
return value(session);
|
|
15179
|
+
}
|
|
15180
|
+
if (typeof value === "string") {
|
|
15181
|
+
const encoded = encodeURIComponent(session.sessionId);
|
|
15182
|
+
return value.includes(":sessionId") ? value.replace(":sessionId", encoded) : `${value.replace(/\/$/, "")}/${encoded}`;
|
|
15183
|
+
}
|
|
15184
|
+
return;
|
|
15185
|
+
};
|
|
15147
15186
|
var isProviderErrorEvent = (event) => event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.error === "string");
|
|
15148
15187
|
var isRecoveredProviderFallbackEvent = (event) => event.type === "session.error" && (event.payload.providerStatus === "fallback" || event.payload.status === "fallback") && event.payload.recovered === true;
|
|
15149
15188
|
var turnRecoveryKey = (event) => event.turnId ?? `session:${event.sessionId}`;
|
|
@@ -15189,14 +15228,14 @@ var buildReplayTurns = (events) => {
|
|
|
15189
15228
|
case "turn.transcript":
|
|
15190
15229
|
turn.transcripts.push({
|
|
15191
15230
|
isFinal: event.payload.isFinal === true,
|
|
15192
|
-
text:
|
|
15231
|
+
text: getString10(event.payload.text)
|
|
15193
15232
|
});
|
|
15194
15233
|
break;
|
|
15195
15234
|
case "turn.committed":
|
|
15196
|
-
turn.committedText =
|
|
15235
|
+
turn.committedText = getString10(event.payload.text);
|
|
15197
15236
|
break;
|
|
15198
15237
|
case "turn.assistant": {
|
|
15199
|
-
const text =
|
|
15238
|
+
const text = getString10(event.payload.text);
|
|
15200
15239
|
if (text) {
|
|
15201
15240
|
turn.assistantReplies.push(text);
|
|
15202
15241
|
}
|
|
@@ -15266,7 +15305,7 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
15266
15305
|
let errorCount = 0;
|
|
15267
15306
|
const recoveredTurns = new Set(sorted.filter(isRecoveredProviderFallbackEvent).map((event) => turnRecoveryKey(event)));
|
|
15268
15307
|
for (const event of sorted) {
|
|
15269
|
-
const provider =
|
|
15308
|
+
const provider = getString10(event.payload.provider);
|
|
15270
15309
|
if (provider) {
|
|
15271
15310
|
providers.add(provider);
|
|
15272
15311
|
}
|
|
@@ -15274,7 +15313,7 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
15274
15313
|
errorCount += 1;
|
|
15275
15314
|
increment4(providerErrors, provider ?? "unknown");
|
|
15276
15315
|
}
|
|
15277
|
-
const outcome =
|
|
15316
|
+
const outcome = getString10(event.payload.outcome);
|
|
15278
15317
|
if (outcome) {
|
|
15279
15318
|
latestOutcome = outcome;
|
|
15280
15319
|
}
|
|
@@ -15295,6 +15334,7 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
15295
15334
|
const replayHref = options.replayHref === false ? "" : typeof options.replayHref === "function" ? options.replayHref(item) : `${options.replayHref ?? "/api/voice-sessions"}/${encodeURIComponent(sessionId)}/replay/htmx`;
|
|
15296
15335
|
return {
|
|
15297
15336
|
...item,
|
|
15337
|
+
operationsRecordHref: resolveSessionHref(options.operationsRecordHref, item),
|
|
15298
15338
|
replayHref
|
|
15299
15339
|
};
|
|
15300
15340
|
});
|
|
@@ -15333,7 +15373,7 @@ var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="v
|
|
|
15333
15373
|
"</dl>",
|
|
15334
15374
|
session.latestOutcome ? `<p>Outcome: ${escapeHtml24(session.latestOutcome)}</p>` : "",
|
|
15335
15375
|
session.providers.length ? `<p>Providers: ${session.providers.map(escapeHtml24).join(", ")}</p>` : "",
|
|
15336
|
-
session.replayHref ? `<p
|
|
15376
|
+
session.replayHref ? `<p>${session.operationsRecordHref ? `<a href="${escapeHtml24(session.operationsRecordHref)}">Open operations record</a> \xB7 ` : ""}<a href="${escapeHtml24(session.replayHref)}">Open replay</a></p>` : "",
|
|
15337
15377
|
"</article>"
|
|
15338
15378
|
].join("")),
|
|
15339
15379
|
"</div>"
|
|
@@ -15626,14 +15666,14 @@ var DEFAULT_WARN_AFTER_MS = 1800;
|
|
|
15626
15666
|
var DEFAULT_FAIL_AFTER_MS = 3200;
|
|
15627
15667
|
var escapeHtml25 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15628
15668
|
var firstNumber = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
15629
|
-
var
|
|
15669
|
+
var getString11 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
15630
15670
|
var createTraceStageIndex = (events) => {
|
|
15631
15671
|
const index = new Map;
|
|
15632
15672
|
for (const event of events) {
|
|
15633
15673
|
if (event.type !== "turn_latency.stage" || !event.turnId) {
|
|
15634
15674
|
continue;
|
|
15635
15675
|
}
|
|
15636
|
-
const stage =
|
|
15676
|
+
const stage = getString11(event.payload.stage);
|
|
15637
15677
|
if (!stage) {
|
|
15638
15678
|
continue;
|
|
15639
15679
|
}
|
|
@@ -15900,7 +15940,7 @@ var TRACE_TYPES = [
|
|
|
15900
15940
|
"turn_latency.stage"
|
|
15901
15941
|
];
|
|
15902
15942
|
var getNumber6 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
15903
|
-
var
|
|
15943
|
+
var getString12 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
15904
15944
|
var percentile2 = (values, percentileValue) => {
|
|
15905
15945
|
if (values.length === 0) {
|
|
15906
15946
|
return;
|
|
@@ -15937,7 +15977,7 @@ var providerStageForEvent = (event) => {
|
|
|
15937
15977
|
if (event.type === "turn.transcript") {
|
|
15938
15978
|
return "provider_stt";
|
|
15939
15979
|
}
|
|
15940
|
-
const kind =
|
|
15980
|
+
const kind = getString12(event.payload.providerKind) ?? getString12(event.payload.kind) ?? getString12(event.payload.lane);
|
|
15941
15981
|
if (kind === "llm" || kind === "model") {
|
|
15942
15982
|
return "provider_llm";
|
|
15943
15983
|
}
|
|
@@ -15956,7 +15996,7 @@ var collectTraceStageMeasurements = (events, options) => {
|
|
|
15956
15996
|
if (event.type !== "turn_latency.stage" || !event.turnId) {
|
|
15957
15997
|
continue;
|
|
15958
15998
|
}
|
|
15959
|
-
const stage =
|
|
15999
|
+
const stage = getString12(event.payload.stage);
|
|
15960
16000
|
if (!stage) {
|
|
15961
16001
|
continue;
|
|
15962
16002
|
}
|
|
@@ -16085,7 +16125,7 @@ var collectDirectMeasurements = (events, options) => {
|
|
|
16085
16125
|
at: event.at,
|
|
16086
16126
|
budget: resolveBudget(providerStage, options),
|
|
16087
16127
|
latencyMs: eventElapsedMs(event),
|
|
16088
|
-
provider:
|
|
16128
|
+
provider: getString12(event.payload.provider),
|
|
16089
16129
|
sessionId: event.sessionId,
|
|
16090
16130
|
stage: providerStage,
|
|
16091
16131
|
turnId: event.turnId
|
|
@@ -21226,7 +21266,7 @@ var createVoiceProviderCapabilityRoutes = (options) => {
|
|
|
21226
21266
|
// src/resilienceRoutes.ts
|
|
21227
21267
|
import { Elysia as Elysia34 } from "elysia";
|
|
21228
21268
|
var escapeHtml35 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21229
|
-
var
|
|
21269
|
+
var getString13 = (value) => typeof value === "string" ? value : undefined;
|
|
21230
21270
|
var getNumber7 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
21231
21271
|
var getBoolean2 = (value) => value === true;
|
|
21232
21272
|
var isProviderStatus2 = (value) => value === "error" || value === "fallback" || value === "success";
|
|
@@ -21236,24 +21276,24 @@ var listVoiceRoutingEvents = (events) => {
|
|
|
21236
21276
|
if (event.type !== "session.error") {
|
|
21237
21277
|
continue;
|
|
21238
21278
|
}
|
|
21239
|
-
const provider =
|
|
21279
|
+
const provider = getString13(event.payload.provider);
|
|
21240
21280
|
const providerStatus = isProviderStatus2(event.payload.providerStatus) ? event.payload.providerStatus : undefined;
|
|
21241
21281
|
if (!provider || !providerStatus) {
|
|
21242
21282
|
continue;
|
|
21243
21283
|
}
|
|
21244
|
-
const kind =
|
|
21284
|
+
const kind = getString13(event.payload.kind);
|
|
21245
21285
|
routingEvents.push({
|
|
21246
21286
|
at: event.at,
|
|
21247
21287
|
attempt: getNumber7(event.payload.attempt),
|
|
21248
21288
|
elapsedMs: getNumber7(event.payload.elapsedMs),
|
|
21249
|
-
error:
|
|
21250
|
-
fallbackProvider:
|
|
21289
|
+
error: getString13(event.payload.error),
|
|
21290
|
+
fallbackProvider: getString13(event.payload.fallbackProvider),
|
|
21251
21291
|
kind: kind === "stt" || kind === "tts" ? kind : "llm",
|
|
21252
21292
|
latencyBudgetMs: getNumber7(event.payload.latencyBudgetMs),
|
|
21253
|
-
operation:
|
|
21293
|
+
operation: getString13(event.payload.operation),
|
|
21254
21294
|
provider,
|
|
21255
|
-
routing:
|
|
21256
|
-
selectedProvider:
|
|
21295
|
+
routing: getString13(event.payload.routing),
|
|
21296
|
+
selectedProvider: getString13(event.payload.selectedProvider),
|
|
21257
21297
|
sessionId: event.sessionId,
|
|
21258
21298
|
status: providerStatus,
|
|
21259
21299
|
suppressionRemainingMs: getNumber7(event.payload.suppressionRemainingMs),
|
|
@@ -21745,164 +21785,424 @@ var assertVoiceProviderRoutingContract = async (options) => {
|
|
|
21745
21785
|
return report;
|
|
21746
21786
|
};
|
|
21747
21787
|
// src/productionReadiness.ts
|
|
21788
|
+
import { Elysia as Elysia36 } from "elysia";
|
|
21789
|
+
|
|
21790
|
+
// src/opsRecovery.ts
|
|
21748
21791
|
import { Elysia as Elysia35 } from "elysia";
|
|
21749
21792
|
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21750
|
-
var
|
|
21751
|
-
var
|
|
21752
|
-
|
|
21753
|
-
|
|
21754
|
-
"Audit sink delivery": "voice.readiness.audit_sink_delivery",
|
|
21755
|
-
"Barge-in interruption proof": "voice.readiness.barge_in_interruption",
|
|
21756
|
-
"Campaign readiness proof": "voice.readiness.campaign_readiness",
|
|
21757
|
-
"Carrier readiness": "voice.readiness.carrier_readiness",
|
|
21758
|
-
"Delivery runtime": "voice.readiness.delivery_runtime",
|
|
21759
|
-
"Handoff delivery": "voice.readiness.handoff_delivery",
|
|
21760
|
-
"Live latency proof": "voice.readiness.live_latency",
|
|
21761
|
-
"Operations records": "voice.readiness.operations_records",
|
|
21762
|
-
"Operator action history": "voice.readiness.operator_action_history",
|
|
21763
|
-
"Phone agent production smoke": "voice.readiness.phone_agent_smoke",
|
|
21764
|
-
"Provider contract matrix": "voice.readiness.provider_contract_matrix",
|
|
21765
|
-
"Provider fallback recovery": "voice.readiness.provider_fallback_recovery",
|
|
21766
|
-
"Provider health": "voice.readiness.provider_health",
|
|
21767
|
-
"Provider routing contracts": "voice.readiness.provider_routing_contracts",
|
|
21768
|
-
"Provider stack capabilities": "voice.readiness.provider_stack_capabilities",
|
|
21769
|
-
"Quality gates": "voice.readiness.quality_gates",
|
|
21770
|
-
"Reconnect recovery contracts": "voice.readiness.reconnect_contracts",
|
|
21771
|
-
"Routing evidence": "voice.readiness.routing_evidence",
|
|
21772
|
-
"Session health": "voice.readiness.session_health",
|
|
21773
|
-
"Trace sink delivery": "voice.readiness.trace_sink_delivery"
|
|
21774
|
-
};
|
|
21775
|
-
var readinessGateCodeForCheck = (check) => readinessGateCodes[check.label] ?? `voice.readiness.${check.label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
21776
|
-
var normalizeReadinessLabel = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
21777
|
-
var issueMatchesProfileSurface = (issue, surface) => normalizeReadinessLabel(issue.label) === normalizeReadinessLabel(surface.label) || issue.code.includes(surface.key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_"));
|
|
21778
|
-
var summarizeProfileSurfaceStatus = (issues) => issues.some((issue) => issue.status === "fail") ? "fail" : issues.some((issue) => issue.status === "warn") ? "warn" : "pass";
|
|
21779
|
-
var summarizeVoiceProductionReadinessGateProfile = (report, issues) => {
|
|
21780
|
-
if (!report.profile) {
|
|
21781
|
-
return;
|
|
21782
|
-
}
|
|
21783
|
-
return {
|
|
21784
|
-
description: report.profile.description,
|
|
21785
|
-
name: report.profile.name,
|
|
21786
|
-
purpose: report.profile.purpose,
|
|
21787
|
-
surfaces: report.profile.surfaces.map((surface) => {
|
|
21788
|
-
const surfaceIssues = issues.filter((issue) => issueMatchesProfileSurface(issue, surface));
|
|
21789
|
-
return {
|
|
21790
|
-
...surface,
|
|
21791
|
-
issues: surfaceIssues,
|
|
21792
|
-
status: summarizeProfileSurfaceStatus(surfaceIssues)
|
|
21793
|
-
};
|
|
21794
|
-
})
|
|
21795
|
-
};
|
|
21796
|
-
};
|
|
21797
|
-
var summarizeVoiceProductionReadinessGate = (report, options = {}) => {
|
|
21798
|
-
const issues = report.checks.filter((check) => check.status !== "pass").map((check) => ({
|
|
21799
|
-
code: readinessGateCodeForCheck(check),
|
|
21800
|
-
detail: check.detail,
|
|
21801
|
-
href: check.href,
|
|
21802
|
-
label: check.label,
|
|
21803
|
-
status: check.status,
|
|
21804
|
-
value: check.value
|
|
21805
|
-
}));
|
|
21806
|
-
const failures = issues.filter((issue) => issue.status === "fail");
|
|
21807
|
-
const warnings = issues.filter((issue) => issue.status === "warn");
|
|
21808
|
-
const ok = failures.length === 0 && (options.failOnWarnings ? warnings.length === 0 : true);
|
|
21809
|
-
return {
|
|
21810
|
-
checkedAt: report.checkedAt,
|
|
21811
|
-
failures,
|
|
21812
|
-
ok,
|
|
21813
|
-
profile: summarizeVoiceProductionReadinessGateProfile(report, issues),
|
|
21814
|
-
status: ok ? report.status : "fail",
|
|
21815
|
-
warnings
|
|
21816
|
-
};
|
|
21817
|
-
};
|
|
21818
|
-
var carrierStatus = (matrix) => matrix.summary.failing > 0 ? "fail" : matrix.summary.warnings > 0 || matrix.summary.ready < matrix.summary.providers ? "warn" : "pass";
|
|
21819
|
-
var resolveCarriers = async (options, input) => {
|
|
21820
|
-
if (options.carriers === false || options.carriers === undefined) {
|
|
21821
|
-
return;
|
|
21822
|
-
}
|
|
21823
|
-
const providers = typeof options.carriers === "function" ? await options.carriers(input) : options.carriers;
|
|
21824
|
-
return createVoiceTelephonyCarrierMatrix({
|
|
21825
|
-
providers: [...providers]
|
|
21826
|
-
});
|
|
21827
|
-
};
|
|
21828
|
-
var resolveAgentSquadContracts = async (options, input) => {
|
|
21829
|
-
if (options.agentSquadContracts === false || options.agentSquadContracts === undefined) {
|
|
21830
|
-
return;
|
|
21831
|
-
}
|
|
21832
|
-
return typeof options.agentSquadContracts === "function" ? await options.agentSquadContracts(input) : options.agentSquadContracts;
|
|
21833
|
-
};
|
|
21834
|
-
var resolveProviderRoutingContracts = async (options, input) => {
|
|
21835
|
-
if (options.providerRoutingContracts === false || options.providerRoutingContracts === undefined) {
|
|
21836
|
-
return;
|
|
21837
|
-
}
|
|
21838
|
-
return typeof options.providerRoutingContracts === "function" ? await options.providerRoutingContracts(input) : options.providerRoutingContracts;
|
|
21839
|
-
};
|
|
21840
|
-
var resolveProviderStack = async (options, input) => {
|
|
21841
|
-
if (options.providerStack === false || options.providerStack === undefined) {
|
|
21842
|
-
return;
|
|
21843
|
-
}
|
|
21844
|
-
return typeof options.providerStack === "function" ? await options.providerStack(input) : options.providerStack;
|
|
21845
|
-
};
|
|
21846
|
-
var resolveProviderContractMatrix = async (options, input) => {
|
|
21847
|
-
if (options.providerContractMatrix === false || options.providerContractMatrix === undefined) {
|
|
21848
|
-
return;
|
|
21793
|
+
var getString14 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
21794
|
+
var hrefForSession = (value, sessionId) => {
|
|
21795
|
+
if (typeof value === "function") {
|
|
21796
|
+
return value(sessionId);
|
|
21849
21797
|
}
|
|
21850
|
-
|
|
21851
|
-
};
|
|
21852
|
-
var resolvePhoneAgentSmokes = async (options, input) => {
|
|
21853
|
-
if (options.phoneAgentSmokes === false || options.phoneAgentSmokes === undefined) {
|
|
21798
|
+
if (typeof value !== "string") {
|
|
21854
21799
|
return;
|
|
21855
21800
|
}
|
|
21856
|
-
|
|
21857
|
-
|
|
21858
|
-
|
|
21859
|
-
if (options.reconnectContracts === false || options.reconnectContracts === undefined) {
|
|
21860
|
-
return;
|
|
21801
|
+
const encoded = encodeURIComponent(sessionId);
|
|
21802
|
+
if (value.includes(":sessionId")) {
|
|
21803
|
+
return value.replace(":sessionId", encoded);
|
|
21861
21804
|
}
|
|
21862
|
-
return
|
|
21805
|
+
return value;
|
|
21863
21806
|
};
|
|
21864
|
-
var
|
|
21865
|
-
|
|
21866
|
-
|
|
21807
|
+
var operationsRecordHrefForSession = (links, sessionId) => hrefForSession(links?.operationsRecords, sessionId);
|
|
21808
|
+
var rollupStatus2 = (issues) => issues.some((issue) => issue.severity === "fail") ? "fail" : issues.some((issue) => issue.severity === "warn") ? "warn" : "pass";
|
|
21809
|
+
var providerUnresolved = (provider) => provider.status === "degraded" || provider.status === "rate-limited" || provider.status === "suppressed";
|
|
21810
|
+
var collectFailedSessions = (events, limit, links) => events.filter((event) => {
|
|
21811
|
+
if (event.type !== "session.error") {
|
|
21812
|
+
return false;
|
|
21867
21813
|
}
|
|
21868
|
-
|
|
21814
|
+
const providerStatus = event.payload.providerStatus;
|
|
21815
|
+
return providerStatus !== "success" && providerStatus !== "fallback";
|
|
21816
|
+
}).sort((left, right) => right.at - left.at).slice(0, limit).map((event) => ({
|
|
21817
|
+
at: event.at,
|
|
21818
|
+
error: getString14(event.payload.error),
|
|
21819
|
+
operationsRecordHref: operationsRecordHrefForSession(links, event.sessionId),
|
|
21820
|
+
provider: getString14(event.payload.provider),
|
|
21821
|
+
sessionId: event.sessionId,
|
|
21822
|
+
traceId: event.traceId
|
|
21823
|
+
}));
|
|
21824
|
+
var collectInterventions = (events, limit) => {
|
|
21825
|
+
const interventionEvents = events.filter((event) => event.type === "operator.action").sort((left, right) => right.at - left.at);
|
|
21826
|
+
return {
|
|
21827
|
+
events: interventionEvents.slice(0, limit).map((event) => ({
|
|
21828
|
+
action: getString14(event.payload.action),
|
|
21829
|
+
at: event.at,
|
|
21830
|
+
operatorId: getString14(event.payload.operatorId) ?? getString14(event.payload.actorId),
|
|
21831
|
+
sessionId: event.sessionId,
|
|
21832
|
+
traceId: event.traceId
|
|
21833
|
+
})),
|
|
21834
|
+
total: interventionEvents.length
|
|
21835
|
+
};
|
|
21869
21836
|
};
|
|
21870
|
-
var
|
|
21871
|
-
if (
|
|
21837
|
+
var addDeliveryIssues = (issues, input) => {
|
|
21838
|
+
if (!input.summary) {
|
|
21872
21839
|
return;
|
|
21873
21840
|
}
|
|
21874
|
-
|
|
21875
|
-
|
|
21876
|
-
|
|
21877
|
-
|
|
21878
|
-
|
|
21841
|
+
const failed = input.summary.failed + input.summary.deadLettered;
|
|
21842
|
+
if (failed > 0) {
|
|
21843
|
+
issues.push({
|
|
21844
|
+
code: input.failedCode,
|
|
21845
|
+
detail: `${failed} failed or dead-lettered delivery record(s).`,
|
|
21846
|
+
href: input.href,
|
|
21847
|
+
label: input.failedLabel,
|
|
21848
|
+
severity: "fail",
|
|
21849
|
+
value: failed
|
|
21850
|
+
});
|
|
21879
21851
|
}
|
|
21880
|
-
|
|
21881
|
-
|
|
21882
|
-
|
|
21883
|
-
|
|
21884
|
-
|
|
21885
|
-
|
|
21852
|
+
const pending = input.summary.pending + input.summary.retryEligible;
|
|
21853
|
+
if (pending > 0) {
|
|
21854
|
+
issues.push({
|
|
21855
|
+
code: input.pendingCode,
|
|
21856
|
+
detail: `${pending} pending or retry-eligible delivery record(s).`,
|
|
21857
|
+
href: input.href,
|
|
21858
|
+
label: input.pendingLabel,
|
|
21859
|
+
severity: "warn",
|
|
21860
|
+
value: pending
|
|
21861
|
+
});
|
|
21886
21862
|
}
|
|
21887
|
-
const runtime = typeof options.deliveryRuntime === "function" ? await options.deliveryRuntime(input) : options.deliveryRuntime;
|
|
21888
|
-
return isVoiceDeliveryRuntime(runtime) ? await runtime.summarize() : runtime;
|
|
21889
21863
|
};
|
|
21890
|
-
var
|
|
21891
|
-
|
|
21892
|
-
|
|
21893
|
-
|
|
21894
|
-
|
|
21895
|
-
|
|
21896
|
-
|
|
21897
|
-
|
|
21898
|
-
|
|
21899
|
-
}
|
|
21900
|
-
{
|
|
21901
|
-
|
|
21902
|
-
|
|
21864
|
+
var buildVoiceOpsRecoveryReport = async (options = {}) => {
|
|
21865
|
+
const limit = options.limit ?? 50;
|
|
21866
|
+
const events = options.events ?? await options.traces?.list({ limit: Math.max(limit, 500) }) ?? [];
|
|
21867
|
+
const providers = await summarizeVoiceProviderHealth({
|
|
21868
|
+
events,
|
|
21869
|
+
providers: options.providers
|
|
21870
|
+
});
|
|
21871
|
+
const auditDeliveries = options.auditDeliveries ? await summarizeVoiceAuditSinkDeliveries(await options.auditDeliveries.list(), {
|
|
21872
|
+
deadLetters: options.auditDeliveryDeadLetters
|
|
21873
|
+
}) : undefined;
|
|
21874
|
+
const traceDeliveries = options.traceDeliveries ? await summarizeVoiceTraceSinkDeliveries(await options.traceDeliveries.list(), {
|
|
21875
|
+
deadLetters: options.traceDeliveryDeadLetters
|
|
21876
|
+
}) : undefined;
|
|
21877
|
+
const handoffDeliveries = options.handoffDeliveries ? await summarizeVoiceHandoffDeliveries(await options.handoffDeliveries.list(), {
|
|
21878
|
+
deadLetters: options.handoffDeliveryDeadLetters
|
|
21879
|
+
}) : undefined;
|
|
21880
|
+
const latency = options.latency === false ? undefined : await buildVoiceLatencySLOGate({
|
|
21881
|
+
events,
|
|
21882
|
+
...options.latency ?? {}
|
|
21883
|
+
});
|
|
21884
|
+
const failedSessions = collectFailedSessions(events, limit, options.links);
|
|
21885
|
+
const interventions = collectInterventions(events, limit);
|
|
21886
|
+
const issues = [];
|
|
21887
|
+
const unresolvedProviders = providers.filter(providerUnresolved);
|
|
21888
|
+
for (const provider of unresolvedProviders) {
|
|
21889
|
+
const failedSession = failedSessions.find((session) => session.provider === provider.provider);
|
|
21890
|
+
issues.push({
|
|
21891
|
+
code: "voice.ops_recovery.provider_unresolved_failure",
|
|
21892
|
+
detail: provider.lastError ?? `${provider.provider} status is ${provider.status}.`,
|
|
21893
|
+
href: failedSession?.operationsRecordHref ?? options.links?.providers,
|
|
21894
|
+
label: `Provider ${provider.provider} needs recovery`,
|
|
21895
|
+
severity: "fail",
|
|
21896
|
+
value: provider.status
|
|
21897
|
+
});
|
|
21903
21898
|
}
|
|
21904
|
-
|
|
21905
|
-
|
|
21899
|
+
addDeliveryIssues(issues, {
|
|
21900
|
+
failedCode: "voice.ops_recovery.audit_delivery_failed",
|
|
21901
|
+
failedLabel: "Audit delivery failures",
|
|
21902
|
+
href: options.links?.auditDeliveries,
|
|
21903
|
+
pendingCode: "voice.ops_recovery.audit_delivery_pending",
|
|
21904
|
+
pendingLabel: "Audit delivery backlog",
|
|
21905
|
+
summary: auditDeliveries
|
|
21906
|
+
});
|
|
21907
|
+
addDeliveryIssues(issues, {
|
|
21908
|
+
failedCode: "voice.ops_recovery.trace_delivery_failed",
|
|
21909
|
+
failedLabel: "Trace delivery failures",
|
|
21910
|
+
href: options.links?.traceDeliveries,
|
|
21911
|
+
pendingCode: "voice.ops_recovery.trace_delivery_pending",
|
|
21912
|
+
pendingLabel: "Trace delivery backlog",
|
|
21913
|
+
summary: traceDeliveries
|
|
21914
|
+
});
|
|
21915
|
+
addDeliveryIssues(issues, {
|
|
21916
|
+
failedCode: "voice.ops_recovery.handoff_failed",
|
|
21917
|
+
failedLabel: "Handoff delivery failures",
|
|
21918
|
+
href: options.links?.handoffs,
|
|
21919
|
+
pendingCode: "voice.ops_recovery.handoff_pending",
|
|
21920
|
+
pendingLabel: "Handoff delivery backlog",
|
|
21921
|
+
summary: handoffDeliveries
|
|
21922
|
+
});
|
|
21923
|
+
if (latency?.failed) {
|
|
21924
|
+
const failedMeasurement = latency.measurements.find((measurement) => measurement.status === "fail");
|
|
21925
|
+
issues.push({
|
|
21926
|
+
code: "voice.ops_recovery.latency_slo_failed",
|
|
21927
|
+
detail: `${latency.failed} latency SLO measurement(s) failed.`,
|
|
21928
|
+
href: failedMeasurement ? operationsRecordHrefForSession(options.links, failedMeasurement.sessionId) ?? hrefForSession(options.links?.traces, failedMeasurement.sessionId) : undefined,
|
|
21929
|
+
label: "Latency SLO failures",
|
|
21930
|
+
severity: "fail",
|
|
21931
|
+
value: latency.failed
|
|
21932
|
+
});
|
|
21933
|
+
} else if (latency?.warnings) {
|
|
21934
|
+
issues.push({
|
|
21935
|
+
code: "voice.ops_recovery.latency_slo_warn",
|
|
21936
|
+
detail: `${latency.warnings} latency SLO measurement(s) are warning.`,
|
|
21937
|
+
label: "Latency SLO warnings",
|
|
21938
|
+
severity: "warn",
|
|
21939
|
+
value: latency.warnings
|
|
21940
|
+
});
|
|
21941
|
+
}
|
|
21942
|
+
return {
|
|
21943
|
+
auditDeliveries,
|
|
21944
|
+
checkedAt: Date.now(),
|
|
21945
|
+
failedSessions,
|
|
21946
|
+
handoffDeliveries,
|
|
21947
|
+
interventions,
|
|
21948
|
+
issues,
|
|
21949
|
+
latency,
|
|
21950
|
+
providers: {
|
|
21951
|
+
healthy: providers.filter((provider) => provider.status === "healthy").length,
|
|
21952
|
+
providers,
|
|
21953
|
+
recoveredFallbacks: providers.reduce((total, provider) => total + provider.fallbackCount, 0),
|
|
21954
|
+
unresolvedFailures: unresolvedProviders.length
|
|
21955
|
+
},
|
|
21956
|
+
status: rollupStatus2(issues),
|
|
21957
|
+
traceDeliveries
|
|
21958
|
+
};
|
|
21959
|
+
};
|
|
21960
|
+
var buildVoiceOpsRecoveryReadinessCheck = (report, options = {}) => ({
|
|
21961
|
+
detail: report.status === "pass" ? `${report.providers.recoveredFallbacks} recovered fallback(s), ${report.interventions.total} operator intervention(s), and no unresolved recovery issues.` : `${report.issues.length} recovery issue(s) require attention.`,
|
|
21962
|
+
href: options.href,
|
|
21963
|
+
label: options.label ?? "Ops recovery",
|
|
21964
|
+
status: report.status,
|
|
21965
|
+
value: report.issues.length
|
|
21966
|
+
});
|
|
21967
|
+
var renderVoiceOpsRecoveryMarkdown = (report, options = {}) => {
|
|
21968
|
+
const title = options.title ?? "Voice Ops Recovery";
|
|
21969
|
+
const issueRows = report.issues.map((issue) => `| ${issue.severity} | ${issue.code} | ${issue.href ? `[${issue.label}](${issue.href})` : issue.label} | ${issue.value ?? ""} | ${issue.detail ?? ""} |`).join(`
|
|
21970
|
+
`);
|
|
21971
|
+
const providers = report.providers.providers.map((provider) => `| ${provider.provider} | ${provider.status} | ${provider.runCount} | ${provider.errorCount} | ${provider.fallbackCount} | ${provider.lastError ?? ""} |`).join(`
|
|
21972
|
+
`);
|
|
21973
|
+
const failedSessions = report.failedSessions.map((session) => `- ${session.operationsRecordHref ? `[${session.sessionId}](${session.operationsRecordHref})` : session.sessionId}${session.provider ? ` via ${session.provider}` : ""}${session.error ? `: ${session.error}` : ""}`).join(`
|
|
21974
|
+
`);
|
|
21975
|
+
return `# ${title}
|
|
21976
|
+
|
|
21977
|
+
Status: ${report.status}
|
|
21978
|
+
|
|
21979
|
+
Checked at: ${new Date(report.checkedAt).toISOString()}
|
|
21980
|
+
|
|
21981
|
+
Recovered fallbacks: ${report.providers.recoveredFallbacks}
|
|
21982
|
+
Unresolved provider failures: ${report.providers.unresolvedFailures}
|
|
21983
|
+
Operator interventions: ${report.interventions.total}
|
|
21984
|
+
|
|
21985
|
+
## Issues
|
|
21986
|
+
|
|
21987
|
+
| Severity | Code | Label | Value | Detail |
|
|
21988
|
+
| --- | --- | --- | ---: | --- |
|
|
21989
|
+
${issueRows || "| pass | none | No recovery issues | 0 | |"}
|
|
21990
|
+
|
|
21991
|
+
## Providers
|
|
21992
|
+
|
|
21993
|
+
| Provider | Status | Runs | Errors | Fallbacks | Last error |
|
|
21994
|
+
| --- | --- | ---: | ---: | ---: | --- |
|
|
21995
|
+
${providers || "| none | idle | 0 | 0 | 0 | |"}
|
|
21996
|
+
|
|
21997
|
+
## Failed Sessions
|
|
21998
|
+
|
|
21999
|
+
${failedSessions || "None."}
|
|
22000
|
+
|
|
22001
|
+
## Latency
|
|
22002
|
+
|
|
22003
|
+
${report.latency ? renderVoiceLatencySLOMarkdown(report.latency, { title: "Latency SLO" }) : "Latency SLO disabled."}
|
|
22004
|
+
`;
|
|
22005
|
+
};
|
|
22006
|
+
var renderDeliverySummary = (label, summary) => summary ? `<article><span>${escapeHtml36(label)}</span><strong>${String(summary.failed + summary.deadLettered)} failed</strong><small>${String(summary.pending)} pending \xB7 ${String(summary.retryEligible)} retry eligible \xB7 ${String(summary.total)} total</small></article>` : `<article><span>${escapeHtml36(label)}</span><strong>not configured</strong></article>`;
|
|
22007
|
+
var renderVoiceOpsRecoveryHTML = (report, options = {}) => {
|
|
22008
|
+
const title = options.title ?? "Voice Ops Recovery";
|
|
22009
|
+
const issues = report.issues.map((issue) => `<tr><td>${escapeHtml36(issue.severity)}</td><td><code>${escapeHtml36(issue.code)}</code></td><td>${issue.href ? `<a href="${escapeHtml36(issue.href)}">${escapeHtml36(issue.label)}</a>` : escapeHtml36(issue.label)}</td><td>${escapeHtml36(String(issue.value ?? ""))}</td><td>${escapeHtml36(issue.detail ?? "")}</td></tr>`).join("");
|
|
22010
|
+
const providers = report.providers.providers.map((provider) => `<tr><td>${escapeHtml36(provider.provider)}</td><td>${escapeHtml36(provider.status)}</td><td>${String(provider.runCount)}</td><td>${String(provider.errorCount)}</td><td>${String(provider.fallbackCount)}</td><td>${escapeHtml36(provider.lastError ?? "")}</td></tr>`).join("");
|
|
22011
|
+
const failedSessions = report.failedSessions.map((session) => `<li>${session.operationsRecordHref ? `<a href="${escapeHtml36(session.operationsRecordHref)}">${escapeHtml36(session.sessionId)}</a>` : escapeHtml36(session.sessionId)}${session.provider ? ` via ${escapeHtml36(session.provider)}` : ""}${session.error ? `: ${escapeHtml36(session.error)}` : ""}</li>`).join("");
|
|
22012
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml36(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#f8fafc;color:#172033;margin:2rem;line-height:1.45}main{max-width:1180px;margin:auto}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:.75rem;margin:1rem 0}article{background:white;border:1px solid #dbe3ef;border-radius:14px;padding:1rem;box-shadow:0 10px 28px rgba(15,23,42,.05)}article span{display:block;color:#64748b;font-size:.85rem}article strong{display:block;font-size:1.5rem;margin:.2rem 0}article small{color:#64748b}table{border-collapse:collapse;width:100%;background:white;border:1px solid #dbe3ef;border-radius:14px;overflow:hidden}th,td{border-bottom:1px solid #e2e8f0;padding:.7rem;text-align:left;vertical-align:top}code{font-size:.85em}.status{display:inline-flex;border-radius:999px;padding:.35rem .7rem;background:${report.status === "fail" ? "#fee2e2" : report.status === "warn" ? "#fef3c7" : "#dcfce7"};color:${report.status === "fail" ? "#991b1b" : report.status === "warn" ? "#92400e" : "#166534"};font-weight:700}</style></head><body><main><h1>${escapeHtml36(title)}</h1><p><span class="status">${escapeHtml36(report.status)}</span> Checked ${escapeHtml36(new Date(report.checkedAt).toLocaleString())}</p><section class="grid"><article><span>Recovered fallbacks</span><strong>${String(report.providers.recoveredFallbacks)}</strong></article><article><span>Unresolved providers</span><strong>${String(report.providers.unresolvedFailures)}</strong></article><article><span>Operator interventions</span><strong>${String(report.interventions.total)}</strong></article><article><span>Latency status</span><strong>${escapeHtml36(report.latency?.status ?? "disabled")}</strong></article>${renderDeliverySummary("Audit delivery", report.auditDeliveries)}${renderDeliverySummary("Trace delivery", report.traceDeliveries)}${renderDeliverySummary("Handoff delivery", report.handoffDeliveries)}</section><h2>Issues</h2><table><thead><tr><th>Severity</th><th>Code</th><th>Label</th><th>Value</th><th>Detail</th></tr></thead><tbody>${issues || '<tr><td colspan="5">No recovery issues.</td></tr>'}</tbody></table><h2>Providers</h2><table><thead><tr><th>Provider</th><th>Status</th><th>Runs</th><th>Errors</th><th>Fallbacks</th><th>Last error</th></tr></thead><tbody>${providers || '<tr><td colspan="6">No provider activity.</td></tr>'}</tbody></table><h2>Failed Sessions</h2><ul>${failedSessions || "<li>None.</li>"}</ul></main></body></html>`;
|
|
22013
|
+
};
|
|
22014
|
+
var createVoiceOpsRecoveryRoutes = (options = {}) => {
|
|
22015
|
+
const path = options.path ?? "/api/voice/ops-recovery";
|
|
22016
|
+
const htmlPath = options.htmlPath === undefined ? "/ops-recovery" : options.htmlPath;
|
|
22017
|
+
const markdownPath = options.markdownPath === undefined ? `${path}.md` : options.markdownPath;
|
|
22018
|
+
const routes = new Elysia35({
|
|
22019
|
+
name: options.name ?? "absolutejs-voice-ops-recovery"
|
|
22020
|
+
}).get(path, async () => buildVoiceOpsRecoveryReport(options));
|
|
22021
|
+
if (htmlPath) {
|
|
22022
|
+
routes.get(htmlPath, async () => {
|
|
22023
|
+
const report = await buildVoiceOpsRecoveryReport(options);
|
|
22024
|
+
const render = options.render ?? renderVoiceOpsRecoveryHTML;
|
|
22025
|
+
return new Response(await render(report), {
|
|
22026
|
+
headers: {
|
|
22027
|
+
"content-type": "text/html; charset=utf-8",
|
|
22028
|
+
...options.headers
|
|
22029
|
+
}
|
|
22030
|
+
});
|
|
22031
|
+
});
|
|
22032
|
+
}
|
|
22033
|
+
if (markdownPath) {
|
|
22034
|
+
routes.get(markdownPath, async () => {
|
|
22035
|
+
const report = await buildVoiceOpsRecoveryReport(options);
|
|
22036
|
+
return new Response(renderVoiceOpsRecoveryMarkdown(report, { title: options.title }), {
|
|
22037
|
+
headers: {
|
|
22038
|
+
"content-type": "text/markdown; charset=utf-8",
|
|
22039
|
+
...options.headers
|
|
22040
|
+
}
|
|
22041
|
+
});
|
|
22042
|
+
});
|
|
22043
|
+
}
|
|
22044
|
+
return routes;
|
|
22045
|
+
};
|
|
22046
|
+
|
|
22047
|
+
// src/productionReadiness.ts
|
|
22048
|
+
var escapeHtml37 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
22049
|
+
var rollupStatus3 = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
22050
|
+
var readinessGateCodes = {
|
|
22051
|
+
"Agent squad contracts": "voice.readiness.agent_squad_contracts",
|
|
22052
|
+
"Audit evidence": "voice.readiness.audit_evidence",
|
|
22053
|
+
"Audit sink delivery": "voice.readiness.audit_sink_delivery",
|
|
22054
|
+
"Barge-in interruption proof": "voice.readiness.barge_in_interruption",
|
|
22055
|
+
"Campaign readiness proof": "voice.readiness.campaign_readiness",
|
|
22056
|
+
"Carrier readiness": "voice.readiness.carrier_readiness",
|
|
22057
|
+
"Delivery runtime": "voice.readiness.delivery_runtime",
|
|
22058
|
+
"Handoff delivery": "voice.readiness.handoff_delivery",
|
|
22059
|
+
"Live latency proof": "voice.readiness.live_latency",
|
|
22060
|
+
"Operations records": "voice.readiness.operations_records",
|
|
22061
|
+
"Operator action history": "voice.readiness.operator_action_history",
|
|
22062
|
+
"Ops recovery": "voice.readiness.ops_recovery",
|
|
22063
|
+
"Phone agent production smoke": "voice.readiness.phone_agent_smoke",
|
|
22064
|
+
"Provider contract matrix": "voice.readiness.provider_contract_matrix",
|
|
22065
|
+
"Provider fallback recovery": "voice.readiness.provider_fallback_recovery",
|
|
22066
|
+
"Provider health": "voice.readiness.provider_health",
|
|
22067
|
+
"Provider routing contracts": "voice.readiness.provider_routing_contracts",
|
|
22068
|
+
"Provider stack capabilities": "voice.readiness.provider_stack_capabilities",
|
|
22069
|
+
"Quality gates": "voice.readiness.quality_gates",
|
|
22070
|
+
"Reconnect recovery contracts": "voice.readiness.reconnect_contracts",
|
|
22071
|
+
"Routing evidence": "voice.readiness.routing_evidence",
|
|
22072
|
+
"Session health": "voice.readiness.session_health",
|
|
22073
|
+
"Trace sink delivery": "voice.readiness.trace_sink_delivery"
|
|
22074
|
+
};
|
|
22075
|
+
var readinessGateCodeForCheck = (check) => readinessGateCodes[check.label] ?? `voice.readiness.${check.label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
22076
|
+
var normalizeReadinessLabel = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
22077
|
+
var issueMatchesProfileSurface = (issue, surface) => normalizeReadinessLabel(issue.label) === normalizeReadinessLabel(surface.label) || issue.code.includes(surface.key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_"));
|
|
22078
|
+
var summarizeProfileSurfaceStatus = (issues) => issues.some((issue) => issue.status === "fail") ? "fail" : issues.some((issue) => issue.status === "warn") ? "warn" : "pass";
|
|
22079
|
+
var summarizeVoiceProductionReadinessGateProfile = (report, issues) => {
|
|
22080
|
+
if (!report.profile) {
|
|
22081
|
+
return;
|
|
22082
|
+
}
|
|
22083
|
+
return {
|
|
22084
|
+
description: report.profile.description,
|
|
22085
|
+
name: report.profile.name,
|
|
22086
|
+
purpose: report.profile.purpose,
|
|
22087
|
+
surfaces: report.profile.surfaces.map((surface) => {
|
|
22088
|
+
const surfaceIssues = issues.filter((issue) => issueMatchesProfileSurface(issue, surface));
|
|
22089
|
+
return {
|
|
22090
|
+
...surface,
|
|
22091
|
+
issues: surfaceIssues,
|
|
22092
|
+
status: summarizeProfileSurfaceStatus(surfaceIssues)
|
|
22093
|
+
};
|
|
22094
|
+
})
|
|
22095
|
+
};
|
|
22096
|
+
};
|
|
22097
|
+
var summarizeVoiceProductionReadinessGate = (report, options = {}) => {
|
|
22098
|
+
const issues = report.checks.filter((check) => check.status !== "pass").map((check) => ({
|
|
22099
|
+
code: readinessGateCodeForCheck(check),
|
|
22100
|
+
detail: check.detail,
|
|
22101
|
+
href: check.href,
|
|
22102
|
+
label: check.label,
|
|
22103
|
+
status: check.status,
|
|
22104
|
+
value: check.value
|
|
22105
|
+
}));
|
|
22106
|
+
const failures = issues.filter((issue) => issue.status === "fail");
|
|
22107
|
+
const warnings = issues.filter((issue) => issue.status === "warn");
|
|
22108
|
+
const ok = failures.length === 0 && (options.failOnWarnings ? warnings.length === 0 : true);
|
|
22109
|
+
return {
|
|
22110
|
+
checkedAt: report.checkedAt,
|
|
22111
|
+
failures,
|
|
22112
|
+
ok,
|
|
22113
|
+
profile: summarizeVoiceProductionReadinessGateProfile(report, issues),
|
|
22114
|
+
status: ok ? report.status : "fail",
|
|
22115
|
+
warnings
|
|
22116
|
+
};
|
|
22117
|
+
};
|
|
22118
|
+
var carrierStatus = (matrix) => matrix.summary.failing > 0 ? "fail" : matrix.summary.warnings > 0 || matrix.summary.ready < matrix.summary.providers ? "warn" : "pass";
|
|
22119
|
+
var resolveCarriers = async (options, input) => {
|
|
22120
|
+
if (options.carriers === false || options.carriers === undefined) {
|
|
22121
|
+
return;
|
|
22122
|
+
}
|
|
22123
|
+
const providers = typeof options.carriers === "function" ? await options.carriers(input) : options.carriers;
|
|
22124
|
+
return createVoiceTelephonyCarrierMatrix({
|
|
22125
|
+
providers: [...providers]
|
|
22126
|
+
});
|
|
22127
|
+
};
|
|
22128
|
+
var resolveAgentSquadContracts = async (options, input) => {
|
|
22129
|
+
if (options.agentSquadContracts === false || options.agentSquadContracts === undefined) {
|
|
22130
|
+
return;
|
|
22131
|
+
}
|
|
22132
|
+
return typeof options.agentSquadContracts === "function" ? await options.agentSquadContracts(input) : options.agentSquadContracts;
|
|
22133
|
+
};
|
|
22134
|
+
var resolveProviderRoutingContracts = async (options, input) => {
|
|
22135
|
+
if (options.providerRoutingContracts === false || options.providerRoutingContracts === undefined) {
|
|
22136
|
+
return;
|
|
22137
|
+
}
|
|
22138
|
+
return typeof options.providerRoutingContracts === "function" ? await options.providerRoutingContracts(input) : options.providerRoutingContracts;
|
|
22139
|
+
};
|
|
22140
|
+
var resolveProviderStack = async (options, input) => {
|
|
22141
|
+
if (options.providerStack === false || options.providerStack === undefined) {
|
|
22142
|
+
return;
|
|
22143
|
+
}
|
|
22144
|
+
return typeof options.providerStack === "function" ? await options.providerStack(input) : options.providerStack;
|
|
22145
|
+
};
|
|
22146
|
+
var resolveProviderContractMatrix = async (options, input) => {
|
|
22147
|
+
if (options.providerContractMatrix === false || options.providerContractMatrix === undefined) {
|
|
22148
|
+
return;
|
|
22149
|
+
}
|
|
22150
|
+
return typeof options.providerContractMatrix === "function" ? await options.providerContractMatrix(input) : options.providerContractMatrix;
|
|
22151
|
+
};
|
|
22152
|
+
var resolvePhoneAgentSmokes = async (options, input) => {
|
|
22153
|
+
if (options.phoneAgentSmokes === false || options.phoneAgentSmokes === undefined) {
|
|
22154
|
+
return;
|
|
22155
|
+
}
|
|
22156
|
+
return typeof options.phoneAgentSmokes === "function" ? await options.phoneAgentSmokes(input) : options.phoneAgentSmokes;
|
|
22157
|
+
};
|
|
22158
|
+
var resolveReconnectContracts = async (options, input) => {
|
|
22159
|
+
if (options.reconnectContracts === false || options.reconnectContracts === undefined) {
|
|
22160
|
+
return;
|
|
22161
|
+
}
|
|
22162
|
+
return typeof options.reconnectContracts === "function" ? await options.reconnectContracts(input) : options.reconnectContracts;
|
|
22163
|
+
};
|
|
22164
|
+
var resolveBargeInReports = async (options, input) => {
|
|
22165
|
+
if (options.bargeInReports === false || options.bargeInReports === undefined) {
|
|
22166
|
+
return;
|
|
22167
|
+
}
|
|
22168
|
+
return typeof options.bargeInReports === "function" ? await options.bargeInReports(input) : options.bargeInReports;
|
|
22169
|
+
};
|
|
22170
|
+
var resolveCampaignReadiness = async (options, input) => {
|
|
22171
|
+
if (options.campaignReadiness === false || options.campaignReadiness === undefined) {
|
|
22172
|
+
return;
|
|
22173
|
+
}
|
|
22174
|
+
return typeof options.campaignReadiness === "function" ? await options.campaignReadiness(input) : options.campaignReadiness;
|
|
22175
|
+
};
|
|
22176
|
+
var resolveProofSources = async (options, input) => {
|
|
22177
|
+
if (options.proofSources === false || options.proofSources === undefined) {
|
|
22178
|
+
return;
|
|
22179
|
+
}
|
|
22180
|
+
return typeof options.proofSources === "function" ? await options.proofSources(input) : options.proofSources;
|
|
22181
|
+
};
|
|
22182
|
+
var isVoiceDeliveryRuntime = (value) => typeof value.summarize === "function";
|
|
22183
|
+
var resolveDeliveryRuntime = async (options, input) => {
|
|
22184
|
+
if (options.deliveryRuntime === false || options.deliveryRuntime === undefined) {
|
|
22185
|
+
return;
|
|
22186
|
+
}
|
|
22187
|
+
const runtime = typeof options.deliveryRuntime === "function" ? await options.deliveryRuntime(input) : options.deliveryRuntime;
|
|
22188
|
+
return isVoiceDeliveryRuntime(runtime) ? await runtime.summarize() : runtime;
|
|
22189
|
+
};
|
|
22190
|
+
var defaultAuditRequirements = [
|
|
22191
|
+
{
|
|
22192
|
+
label: "Provider-call audit",
|
|
22193
|
+
type: "provider.call"
|
|
22194
|
+
},
|
|
22195
|
+
{
|
|
22196
|
+
label: "Retention audit",
|
|
22197
|
+
maxAgeMs: 7 * 24 * 60 * 60 * 1000,
|
|
22198
|
+
type: "retention.policy"
|
|
22199
|
+
},
|
|
22200
|
+
{
|
|
22201
|
+
label: "Operator-action audit",
|
|
22202
|
+
type: "operator.action"
|
|
22203
|
+
}
|
|
22204
|
+
];
|
|
22205
|
+
var resolveAuditRequirement = (requirement) => typeof requirement === "string" ? {
|
|
21906
22206
|
type: requirement
|
|
21907
22207
|
} : requirement;
|
|
21908
22208
|
var summarizeAuditEvidence = async (options) => {
|
|
@@ -22004,6 +22304,15 @@ var summarizeOpsActionHistory = async (options) => {
|
|
|
22004
22304
|
warnWhenEmpty: opsActionHistory.warnWhenEmpty
|
|
22005
22305
|
};
|
|
22006
22306
|
};
|
|
22307
|
+
var resolveOpsRecovery = async (options, input) => {
|
|
22308
|
+
if (!options.opsRecovery) {
|
|
22309
|
+
return;
|
|
22310
|
+
}
|
|
22311
|
+
if (typeof options.opsRecovery === "function") {
|
|
22312
|
+
return options.opsRecovery(input);
|
|
22313
|
+
}
|
|
22314
|
+
return options.opsRecovery;
|
|
22315
|
+
};
|
|
22007
22316
|
var summarizeTraceDeliveries = async (options) => {
|
|
22008
22317
|
if (!options.traceDeliveries) {
|
|
22009
22318
|
return;
|
|
@@ -22093,7 +22402,7 @@ var summarizeLiveLatency = (events, options) => {
|
|
|
22093
22402
|
warnings
|
|
22094
22403
|
};
|
|
22095
22404
|
};
|
|
22096
|
-
var
|
|
22405
|
+
var getString15 = (value) => typeof value === "string" ? value : undefined;
|
|
22097
22406
|
var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
22098
22407
|
var voiceOperationsRecordHref = (base, sessionId) => {
|
|
22099
22408
|
const encoded = encodeURIComponent(sessionId);
|
|
@@ -22105,7 +22414,7 @@ var voiceOperationsRecordHref = (base, sessionId) => {
|
|
|
22105
22414
|
var buildOperationsRecordLinks = (input) => {
|
|
22106
22415
|
const failedSessionSet = new Set(input.failedSessionIds);
|
|
22107
22416
|
const providerErrors = input.events.filter((event) => event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.error === "string")).map((event) => ({
|
|
22108
|
-
detail:
|
|
22417
|
+
detail: getString15(event.payload.error),
|
|
22109
22418
|
href: voiceOperationsRecordHref(input.base, event.sessionId),
|
|
22110
22419
|
label: "Open provider error operations record",
|
|
22111
22420
|
sessionId: event.sessionId,
|
|
@@ -22160,6 +22469,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22160
22469
|
reconnectContracts,
|
|
22161
22470
|
bargeInReports,
|
|
22162
22471
|
campaignReadiness,
|
|
22472
|
+
opsRecovery,
|
|
22163
22473
|
proofSources
|
|
22164
22474
|
] = await Promise.all([
|
|
22165
22475
|
evaluateVoiceQuality({ events }),
|
|
@@ -22193,6 +22503,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22193
22503
|
resolveReconnectContracts(options, { query, request }),
|
|
22194
22504
|
resolveBargeInReports(options, { query, request }),
|
|
22195
22505
|
resolveCampaignReadiness(options, { query, request }),
|
|
22506
|
+
resolveOpsRecovery(options, { query, request }),
|
|
22196
22507
|
resolveProofSources(options, { query, request })
|
|
22197
22508
|
]);
|
|
22198
22509
|
const deliveryRuntime = summarizeDeliveryRuntime(deliveryRuntimeSummary);
|
|
@@ -22256,6 +22567,27 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22256
22567
|
}
|
|
22257
22568
|
]
|
|
22258
22569
|
},
|
|
22570
|
+
...opsRecovery ? [
|
|
22571
|
+
{
|
|
22572
|
+
...buildVoiceOpsRecoveryReadinessCheck(opsRecovery, {
|
|
22573
|
+
href: options.links?.opsRecovery ?? "/ops-recovery"
|
|
22574
|
+
}),
|
|
22575
|
+
actions: opsRecovery.status === "pass" ? [] : [
|
|
22576
|
+
...opsRecovery.issues[0]?.href ? [
|
|
22577
|
+
{
|
|
22578
|
+
description: "Open the exact impacted operations record for the first recovery issue.",
|
|
22579
|
+
href: opsRecovery.issues[0].href,
|
|
22580
|
+
label: "Open impacted operations record"
|
|
22581
|
+
}
|
|
22582
|
+
] : [],
|
|
22583
|
+
{
|
|
22584
|
+
description: "Open the unified recovery report for provider fallback, delivery, handoff, live-ops, and SLO issues.",
|
|
22585
|
+
href: options.links?.opsRecovery ?? "/ops-recovery",
|
|
22586
|
+
label: "Open ops recovery"
|
|
22587
|
+
}
|
|
22588
|
+
]
|
|
22589
|
+
}
|
|
22590
|
+
] : [],
|
|
22259
22591
|
{
|
|
22260
22592
|
detail: failedSessions === 0 ? sessions.length > 0 ? "Recent sessions have no recorded provider/session failures." : "No sessions have been recorded yet; run a smoke or live session for proof." : `${failedSessions} recent session(s) have failures.`,
|
|
22261
22593
|
href: firstOperationsRecordHref(operationsRecords.failedSessions) ?? options.links?.sessions ?? "/sessions",
|
|
@@ -22641,6 +22973,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22641
22973
|
liveLatency: "/traces",
|
|
22642
22974
|
operationsRecords: "/voice-operations",
|
|
22643
22975
|
opsActions: "/voice/ops-actions",
|
|
22976
|
+
opsRecovery: "/ops-recovery",
|
|
22644
22977
|
phoneAgentSmoke: "/sessions",
|
|
22645
22978
|
providerContracts: "/provider-contracts",
|
|
22646
22979
|
providerRoutingContracts: "/resilience",
|
|
@@ -22654,7 +22987,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22654
22987
|
profile: options.profile || undefined,
|
|
22655
22988
|
operationsRecords,
|
|
22656
22989
|
proofSources,
|
|
22657
|
-
status:
|
|
22990
|
+
status: rollupStatus3(checks),
|
|
22658
22991
|
summary: {
|
|
22659
22992
|
agentSquadContracts: agentSquadContractSummary,
|
|
22660
22993
|
audit,
|
|
@@ -22669,6 +23002,12 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22669
23002
|
},
|
|
22670
23003
|
liveLatency,
|
|
22671
23004
|
opsActionHistory,
|
|
23005
|
+
opsRecovery: opsRecovery ? {
|
|
23006
|
+
issues: opsRecovery.issues.length,
|
|
23007
|
+
recoveredFallbacks: opsRecovery.providers.recoveredFallbacks,
|
|
23008
|
+
status: opsRecovery.status,
|
|
23009
|
+
unresolvedProviderFailures: opsRecovery.providers.unresolvedFailures
|
|
23010
|
+
} : undefined,
|
|
22672
23011
|
providers: {
|
|
22673
23012
|
degraded: degradedProviders,
|
|
22674
23013
|
total: providers.length
|
|
@@ -22697,22 +23036,22 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22697
23036
|
var buildVoiceProductionReadinessGate = async (options, input = {}) => summarizeVoiceProductionReadinessGate(await buildVoiceProductionReadinessReport(options, input), options.gate || undefined);
|
|
22698
23037
|
var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
22699
23038
|
const title = options.title ?? "AbsoluteJS Voice Production Readiness";
|
|
22700
|
-
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${
|
|
23039
|
+
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${escapeHtml37(report.profile.name)}</h2><p>${escapeHtml37(report.profile.description)}</p><p>${escapeHtml37(report.profile.purpose)}</p><div class="profile-surfaces">${report.profile.surfaces.map((surface) => `<article class="${surface.configured ? "pass" : "warn"}"><span>${surface.configured ? "CONFIGURED" : "EXPECTED"}</span><strong>${surface.href ? `<a href="${escapeHtml37(surface.href)}">${escapeHtml37(surface.label)}</a>` : escapeHtml37(surface.label)}</strong></article>`).join("")}</div></section>` : "";
|
|
22701
23040
|
const checks = report.checks.map((check, index) => {
|
|
22702
|
-
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${
|
|
22703
|
-
return `<article class="check ${
|
|
23041
|
+
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml37(action.href)}">${escapeHtml37(action.label)}</button>` : `<a href="${escapeHtml37(action.href)}">${escapeHtml37(action.label)}</a>`).join("");
|
|
23042
|
+
return `<article class="check ${escapeHtml37(check.status)}">
|
|
22704
23043
|
<div>
|
|
22705
|
-
<span>${
|
|
22706
|
-
<h2>${
|
|
22707
|
-
${check.detail ? `<p>${
|
|
22708
|
-
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${
|
|
23044
|
+
<span>${escapeHtml37(check.status.toUpperCase())}</span>
|
|
23045
|
+
<h2>${escapeHtml37(check.label)}</h2>
|
|
23046
|
+
${check.detail ? `<p>${escapeHtml37(check.detail)}</p>` : ""}
|
|
23047
|
+
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml37(check.proofSource.href)}">${escapeHtml37(check.proofSource.sourceLabel)}</a>` : escapeHtml37(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml37(check.proofSource.detail)}` : ""}</p>` : ""}
|
|
22709
23048
|
${actions ? `<p class="actions">${actions}</p>` : ""}
|
|
22710
23049
|
</div>
|
|
22711
|
-
<strong>${
|
|
22712
|
-
${check.href ? `<a href="${
|
|
23050
|
+
<strong>${escapeHtml37(String(check.value ?? check.status))}</strong>
|
|
23051
|
+
${check.href ? `<a href="${escapeHtml37(check.href)}">Open surface</a>` : ""}
|
|
22713
23052
|
</article>`;
|
|
22714
23053
|
}).join("");
|
|
22715
|
-
const snippet =
|
|
23054
|
+
const snippet = escapeHtml37(`createVoiceProductionReadinessRoutes({
|
|
22716
23055
|
htmlPath: '/production-readiness',
|
|
22717
23056
|
path: '/api/production-readiness',
|
|
22718
23057
|
gatePath: '/api/production-readiness/gate',
|
|
@@ -22728,13 +23067,13 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
|
22728
23067
|
providerRoutingContracts: loadProviderRoutingContracts,
|
|
22729
23068
|
store: traceStore
|
|
22730
23069
|
});`);
|
|
22731
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23070
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml37(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero,.primitive,.profile{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.primitive,.profile{background:#111722}.primitive{border-color:#3a3f2d}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{display:inline-flex;border:1px solid #3f3f46;border-radius:999px;padding:8px 12px}.primitive code{color:#fde68a}.primitive p{color:#c8ccd3;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#0b0f16;border:1px solid #2c3440;border-radius:18px;color:#fef3c7;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.check.pass,.profile-surfaces .pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.warn,.profile-surfaces .warn{border-color:rgba(245,158,11,.65)}.status.fail,.check.fail{border-color:rgba(239,68,68,.75)}.checks{display:grid;gap:14px}.check{align-items:center;background:#141922;border:1px solid #26313d;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.check span,.profile-surfaces span{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p,.profile p{color:#b9c0c8;margin:.2rem 0 0}.check .proof-source{color:#f9d77e;font-weight:800}.check strong{font-size:1.5rem}.profile-surfaces{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin-top:16px}.profile-surfaces article{background:#141922;border:1px solid #26313d;border-radius:16px;padding:14px}.profile-surfaces strong{display:block;margin-top:6px}.actions{display:flex;flex-wrap:wrap;gap:10px}.check a,a{color:#fbbf24}button{background:#fbbf24;border:0;border-radius:999px;color:#111827;cursor:pointer;font-weight:800;padding:9px 12px}button:disabled{cursor:wait;opacity:.65}@media(max-width:760px){main{padding:20px}.check{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted readiness</p><h1>${escapeHtml37(title)}</h1><p>One deployable pass/fail report for quality gates, provider failover, session health, handoffs, routing evidence, and optional carrier readiness.</p><p class="status ${escapeHtml37(report.status)}">Overall: ${escapeHtml37(report.status.toUpperCase())}</p><p>Checked ${escapeHtml37(new Date(report.checkedAt).toLocaleString())}</p></section>${profile}<section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProductionReadinessRoutes(...)</code> builds this deploy gate</h2><p>Mount one package primitive to expose JSON readiness, HTML readiness, and a machine-readable gate route. Feed it the proof stores and contract reports your app already owns.</p><pre><code>${snippet}</code></pre></section><section class="checks">${checks}</section></main><script>document.querySelectorAll("[data-readiness-action]").forEach((button)=>{button.addEventListener("click",async()=>{const url=button.getAttribute("data-action-url");if(!url)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(url,{method:"POST"});button.textContent=response.ok?"Done. Reloading...":"Failed";if(response.ok)setTimeout(()=>location.reload(),500)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1500)}})});</script></body></html>`;
|
|
22732
23071
|
};
|
|
22733
23072
|
var createVoiceProductionReadinessRoutes = (options) => {
|
|
22734
23073
|
const path = options.path ?? "/api/production-readiness";
|
|
22735
23074
|
const gatePath = options.gatePath === undefined ? "/api/production-readiness/gate" : options.gatePath;
|
|
22736
23075
|
const htmlPath = options.htmlPath ?? "/production-readiness";
|
|
22737
|
-
const routes = new
|
|
23076
|
+
const routes = new Elysia36({
|
|
22738
23077
|
name: options.name ?? "absolutejs-voice-production-readiness"
|
|
22739
23078
|
});
|
|
22740
23079
|
routes.get(path, async ({ query, request }) => buildVoiceProductionReadinessReport(options, { query, request }));
|
|
@@ -23098,8 +23437,8 @@ var recommendVoiceReadinessProfile = (options) => {
|
|
|
23098
23437
|
};
|
|
23099
23438
|
};
|
|
23100
23439
|
// src/providerStackRecommendations.ts
|
|
23101
|
-
import { Elysia as
|
|
23102
|
-
var
|
|
23440
|
+
import { Elysia as Elysia37 } from "elysia";
|
|
23441
|
+
var escapeHtml38 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23103
23442
|
var profileProviderPriorities = {
|
|
23104
23443
|
"meeting-recorder": {
|
|
23105
23444
|
llm: ["openai", "anthropic", "gemini"],
|
|
@@ -23342,17 +23681,17 @@ var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "fu
|
|
|
23342
23681
|
var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
23343
23682
|
const title = options.title ?? "Voice Provider Contract Matrix";
|
|
23344
23683
|
const rows = report.rows.map((row) => {
|
|
23345
|
-
const checks = row.checks.map((check) => `<li class="${
|
|
23346
|
-
return `<article class="row ${
|
|
23684
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml38(check.status)}"><strong>${escapeHtml38(check.label)}</strong><span>${escapeHtml38(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml38(check.remediation.href)}">${escapeHtml38(check.remediation.label)}</a>` : escapeHtml38(check.remediation.label)}: ${escapeHtml38(check.remediation.detail)}</em>` : ""}</li>`).join("");
|
|
23685
|
+
return `<article class="row ${escapeHtml38(row.status)}">
|
|
23347
23686
|
<div>
|
|
23348
|
-
<p class="eyebrow">${
|
|
23349
|
-
<h2>${
|
|
23350
|
-
<p class="status ${
|
|
23687
|
+
<p class="eyebrow">${escapeHtml38(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
|
|
23688
|
+
<h2>${escapeHtml38(row.provider)}</h2>
|
|
23689
|
+
<p class="status ${escapeHtml38(row.status)}">${escapeHtml38(row.status.toUpperCase())}</p>
|
|
23351
23690
|
</div>
|
|
23352
23691
|
<ul>${checks}</ul>
|
|
23353
23692
|
</article>`;
|
|
23354
23693
|
}).join("");
|
|
23355
|
-
const snippet =
|
|
23694
|
+
const snippet = escapeHtml38(`const providerContracts = () =>
|
|
23356
23695
|
createVoiceProviderContractMatrixPreset('phone-agent', {
|
|
23357
23696
|
env: process.env,
|
|
23358
23697
|
providers: {
|
|
@@ -23373,7 +23712,7 @@ createVoiceProductionReadinessRoutes({
|
|
|
23373
23712
|
providerContractMatrix: () =>
|
|
23374
23713
|
buildVoiceProviderContractMatrix(providerContracts())
|
|
23375
23714
|
});`);
|
|
23376
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23715
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml38(title)}</title><style>body{background:#0f1412;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.primitive,.row{background:#17201b;border:1px solid #2d3b32;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(125,211,252,.12))}.primitive{background:#111814;border-color:#41604a}.eyebrow{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill,.status{border:1px solid #3f4f45;border-radius:999px;display:inline-flex;padding:8px 12px}.primitive code{color:#bbf7d0}.primitive p{color:#c8d8ca;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#08110d;border:1px solid #294132;border-radius:18px;color:#d9f99d;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.row.pass,.pass{border-color:rgba(34,197,94,.65)}.status.warn,.row.warn,.warn{border-color:rgba(245,158,11,.7)}.status.fail,.row.fail,.fail{border-color:rgba(239,68,68,.75)}.row{display:grid;gap:20px;grid-template-columns:minmax(180px,.45fr) 1fr}.row ul{display:grid;gap:10px;list-style:none;margin:0;padding:0}.row li{background:#111814;border:1px solid #2d3b32;border-radius:16px;display:grid;gap:4px;padding:12px}.row li span{color:#b8c2ba}.row li em{color:#f9d77e;font-style:normal}.row li a{color:#86efac}@media(max-width:760px){main{padding:18px}.row{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider contracts</p><h1>${escapeHtml38(title)}</h1><p>Self-hosted provider proof for configured state, required env, latency budgets, fallback, streaming, and declared capabilities.</p><div class="summary"><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.warned)} warning</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} total</span></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProviderContractMatrixPreset(...)</code> builds this matrix</h2><p>Give AbsoluteJS your configured LLM, STT, and TTS providers once. It turns them into deploy-checkable proof for env, fallback, streaming, latency budgets, selected providers, and profile-required capabilities without a hosted dashboard.</p><pre><code>${snippet}</code></pre></section>${rows || '<article class="row"><p>No provider contracts configured.</p></article>'}</main></body></html>`;
|
|
23377
23716
|
};
|
|
23378
23717
|
var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
|
|
23379
23718
|
var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
@@ -23388,7 +23727,7 @@ var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
|
23388
23727
|
var createVoiceProviderContractMatrixRoutes = (options) => {
|
|
23389
23728
|
const path = options.path ?? "/api/provider-contracts";
|
|
23390
23729
|
const htmlPath = options.htmlPath ?? "/provider-contracts";
|
|
23391
|
-
const routes = new
|
|
23730
|
+
const routes = new Elysia37({
|
|
23392
23731
|
name: options.name ?? "absolutejs-voice-provider-contract-matrix"
|
|
23393
23732
|
});
|
|
23394
23733
|
const jsonHandler = createVoiceProviderContractMatrixJSONHandler(options.matrix);
|
|
@@ -23447,7 +23786,7 @@ var evaluateVoiceProviderStackGaps = (input) => {
|
|
|
23447
23786
|
};
|
|
23448
23787
|
};
|
|
23449
23788
|
// src/opsConsoleRoutes.ts
|
|
23450
|
-
import { Elysia as
|
|
23789
|
+
import { Elysia as Elysia38 } from "elysia";
|
|
23451
23790
|
var DEFAULT_LINKS = [
|
|
23452
23791
|
{
|
|
23453
23792
|
description: "Quality gates for CI, deploy checks, and production readiness.",
|
|
@@ -23482,7 +23821,7 @@ var DEFAULT_LINKS = [
|
|
|
23482
23821
|
label: "Handoffs"
|
|
23483
23822
|
}
|
|
23484
23823
|
];
|
|
23485
|
-
var
|
|
23824
|
+
var escapeHtml39 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23486
23825
|
var countProviderStatuses = (providers) => {
|
|
23487
23826
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
23488
23827
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -23551,20 +23890,20 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
23551
23890
|
trace
|
|
23552
23891
|
};
|
|
23553
23892
|
};
|
|
23554
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
23893
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml39(input.label)}</span><strong>${escapeHtml39(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml39(input.status)}">${escapeHtml39(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml39(input.href)}">Open</a>` : ""}</article>`;
|
|
23555
23894
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
23556
23895
|
const links = report.links.map((link) => `<article class="surface">
|
|
23557
|
-
<div><h2>${
|
|
23558
|
-
<p><a href="${
|
|
23896
|
+
<div><h2>${escapeHtml39(link.label)}</h2>${link.description ? `<p>${escapeHtml39(link.description)}</p>` : ""}</div>
|
|
23897
|
+
<p><a href="${escapeHtml39(link.href)}">Open ${escapeHtml39(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml39(link.statusHref)}">Status</a>` : ""}</p>
|
|
23559
23898
|
</article>`).join("");
|
|
23560
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
23561
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
23899
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml39(session.sessionId)}</td><td>${escapeHtml39(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml39(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
23900
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml39(event.kind)}</td><td>${escapeHtml39(event.provider ?? "unknown")}</td><td>${escapeHtml39(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml39(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
23562
23901
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
23563
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23902
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml39(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#101316;color:#f6f2e8;margin:0}main{max-width:1180px;margin:auto;padding:32px}a{color:#fbbf24}header{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin-bottom:24px}.eyebrow{color:#fbbf24;font-weight:800;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.5rem);line-height:.95;margin:.2rem 0 1rem}.muted{color:#a8b0b8}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.metric,.surface{background:#181d22;border:1px solid #2a323a;border-radius:20px;padding:18px}.metric strong{display:block;font-size:2.2rem;margin:.25rem 0}.pass,.healthy{color:#86efac}.fail,.failed,.degraded{color:#fca5a5}.surfaces{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:24px 0}table{width:100%;border-collapse:collapse;background:#181d22;border-radius:16px;overflow:hidden;margin:12px 0 28px}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left}section{margin-top:30px}@media(max-width:700px){main{padding:20px}header{display:block}}</style></head><body><main><header><div><p class="eyebrow">Self-hosted voice operations</p><h1>${escapeHtml39(title)}</h1><p class="muted">One deployable control plane for quality gates, failover, traces, sessions, handoffs, and provider health.</p></div><p class="muted">Checked ${escapeHtml39(new Date(report.checkedAt).toLocaleString())}</p></header><div class="grid">${renderMetricCard({ label: "Quality", value: report.quality.status, status: report.quality.status, href: "/quality" })}${renderMetricCard({ label: "Events", value: report.eventCount, href: "/diagnostics" })}${renderMetricCard({ label: "Sessions", value: report.sessions.total, status: report.sessions.failed > 0 ? "failed" : "healthy", href: "/sessions" })}${renderMetricCard({ label: "Handoffs failed", value: report.handoffs.failed, status: report.handoffs.failed > 0 ? "failed" : "healthy", href: "/handoffs" })}${renderMetricCard({ label: "Providers degraded", value: report.providers.degraded, status: report.providers.degraded > 0 ? "degraded" : "healthy", href: "/resilience" })}</div><section><h2>Operational Surfaces</h2><div class="surfaces">${links}</div></section><section><h2>Recent Sessions</h2><table><thead><tr><th>Session</th><th>Status</th><th>Turns</th><th>Errors</th><th>Replay</th></tr></thead><tbody>${sessions}</tbody></table></section><section><h2>Recent Provider Routing</h2><table><thead><tr><th>Kind</th><th>Provider</th><th>Status</th><th>Elapsed</th><th>Session</th></tr></thead><tbody>${routing}</tbody></table></section></main></body></html>`;
|
|
23564
23903
|
};
|
|
23565
23904
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
23566
23905
|
const path = options.path ?? "/ops-console";
|
|
23567
|
-
const routes = new
|
|
23906
|
+
const routes = new Elysia38({
|
|
23568
23907
|
name: options.name ?? "absolutejs-voice-ops-console"
|
|
23569
23908
|
});
|
|
23570
23909
|
const getReport = () => buildVoiceOpsConsoleReport(options);
|
|
@@ -23581,16 +23920,16 @@ var createVoiceOpsConsoleRoutes = (options) => {
|
|
|
23581
23920
|
return routes;
|
|
23582
23921
|
};
|
|
23583
23922
|
// src/operationsRecord.ts
|
|
23584
|
-
import { Elysia as
|
|
23923
|
+
import { Elysia as Elysia40 } from "elysia";
|
|
23585
23924
|
|
|
23586
23925
|
// src/traceTimeline.ts
|
|
23587
|
-
import { Elysia as
|
|
23588
|
-
var
|
|
23589
|
-
var
|
|
23926
|
+
import { Elysia as Elysia39 } from "elysia";
|
|
23927
|
+
var escapeHtml40 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23928
|
+
var getString16 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
23590
23929
|
var getNumber9 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
23591
23930
|
var firstString3 = (payload, keys) => {
|
|
23592
23931
|
for (const key of keys) {
|
|
23593
|
-
const value =
|
|
23932
|
+
const value = getString16(payload[key]);
|
|
23594
23933
|
if (value) {
|
|
23595
23934
|
return value;
|
|
23596
23935
|
}
|
|
@@ -23620,6 +23959,19 @@ var eventStatus = (event) => firstString3(event.payload, [
|
|
|
23620
23959
|
"reason"
|
|
23621
23960
|
]);
|
|
23622
23961
|
var eventElapsedMs2 = (event) => firstNumber3(event.payload, ["elapsedMs", "latencyMs", "durationMs"]);
|
|
23962
|
+
var resolveSessionHref2 = (value, sessionId) => {
|
|
23963
|
+
if (value === false) {
|
|
23964
|
+
return;
|
|
23965
|
+
}
|
|
23966
|
+
if (typeof value === "function") {
|
|
23967
|
+
return value(sessionId);
|
|
23968
|
+
}
|
|
23969
|
+
if (typeof value === "string") {
|
|
23970
|
+
const encoded = encodeURIComponent(sessionId);
|
|
23971
|
+
return value.includes(":sessionId") ? value.replace(":sessionId", encoded) : `${value.replace(/\/$/, "")}/${encoded}`;
|
|
23972
|
+
}
|
|
23973
|
+
return;
|
|
23974
|
+
};
|
|
23623
23975
|
var timelineLabel = (event) => {
|
|
23624
23976
|
switch (event.type) {
|
|
23625
23977
|
case "call.lifecycle":
|
|
@@ -23627,15 +23979,15 @@ var timelineLabel = (event) => {
|
|
|
23627
23979
|
case "turn.transcript":
|
|
23628
23980
|
return event.payload.isFinal === true ? "Final transcript" : "Partial transcript";
|
|
23629
23981
|
case "turn.committed":
|
|
23630
|
-
return `Committed turn${
|
|
23982
|
+
return `Committed turn${getString16(event.payload.reason) ? ` (${getString16(event.payload.reason)})` : ""}`;
|
|
23631
23983
|
case "turn.assistant":
|
|
23632
23984
|
return "Assistant reply";
|
|
23633
23985
|
case "agent.model":
|
|
23634
23986
|
return `Model call${eventProvider(event) ? ` via ${eventProvider(event)}` : ""}`;
|
|
23635
23987
|
case "agent.tool":
|
|
23636
|
-
return `Tool ${
|
|
23988
|
+
return `Tool ${getString16(event.payload.toolName) ?? "call"}`;
|
|
23637
23989
|
case "agent.handoff":
|
|
23638
|
-
return `Agent handoff${
|
|
23990
|
+
return `Agent handoff${getString16(event.payload.targetAgentId) ? ` to ${getString16(event.payload.targetAgentId)}` : ""}`;
|
|
23639
23991
|
case "assistant.run":
|
|
23640
23992
|
return `Assistant run${eventProvider(event) ? ` via ${eventProvider(event)}` : ""}`;
|
|
23641
23993
|
case "assistant.guardrail":
|
|
@@ -23645,11 +23997,11 @@ var timelineLabel = (event) => {
|
|
|
23645
23997
|
case "client.live_latency":
|
|
23646
23998
|
return `Live latency${eventElapsedMs2(event) !== undefined ? ` ${eventElapsedMs2(event)}ms` : ""}`;
|
|
23647
23999
|
case "session.error":
|
|
23648
|
-
return `Error${
|
|
24000
|
+
return `Error${getString16(event.payload.error) ? `: ${getString16(event.payload.error)}` : ""}`;
|
|
23649
24001
|
case "turn.cost":
|
|
23650
24002
|
return "Cost telemetry";
|
|
23651
24003
|
case "turn_latency.stage":
|
|
23652
|
-
return `Latency ${
|
|
24004
|
+
return `Latency ${getString16(event.payload.stage) ?? "stage"}`;
|
|
23653
24005
|
case "workflow.contract":
|
|
23654
24006
|
return `Workflow contract ${eventStatus(event) ?? ""}`.trim();
|
|
23655
24007
|
default:
|
|
@@ -23737,6 +24089,7 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
23737
24089
|
type: event.type
|
|
23738
24090
|
})),
|
|
23739
24091
|
lastEventAt: sorted.at(-1)?.at,
|
|
24092
|
+
operationsRecordHref: resolveSessionHref2(options.operationsRecordHref, sessionId),
|
|
23740
24093
|
providers: summarizeProviders(sorted),
|
|
23741
24094
|
sessionId,
|
|
23742
24095
|
startedAt: summary.startedAt,
|
|
@@ -23753,16 +24106,17 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
23753
24106
|
};
|
|
23754
24107
|
};
|
|
23755
24108
|
var formatMs3 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
23756
|
-
var renderProviderCards2 = (session) => session.providers.length === 0 ? '<p class="muted">No provider events recorded for this session.</p>' : `<div class="providers">${session.providers.map((provider) => `<article><strong>${
|
|
24109
|
+
var renderProviderCards2 = (session) => session.providers.length === 0 ? '<p class="muted">No provider events recorded for this session.</p>' : `<div class="providers">${session.providers.map((provider) => `<article><strong>${escapeHtml40(provider.provider)}</strong><dl><div><dt>Events</dt><dd>${String(provider.eventCount)}</dd></div><div><dt>Avg</dt><dd>${formatMs3(provider.averageElapsedMs)}</dd></div><div><dt>Max</dt><dd>${formatMs3(provider.maxElapsedMs)}</dd></div><div><dt>Errors</dt><dd>${String(provider.errorCount)}</dd></div><div><dt>Fallbacks</dt><dd>${String(provider.fallbackCount)}</dd></div><div><dt>Timeouts</dt><dd>${String(provider.timeoutCount)}</dd></div></dl></article>`).join("")}</div>`;
|
|
23757
24110
|
var renderVoiceTraceTimelineSessionHTML = (session, options = {}) => {
|
|
23758
|
-
const events = session.events.map((event) => `<tr class="${
|
|
23759
|
-
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${
|
|
23760
|
-
|
|
24111
|
+
const events = session.events.map((event) => `<tr class="${escapeHtml40(event.status ?? "")}"><td>+${String(event.offsetMs)}ms</td><td>${escapeHtml40(event.type)}</td><td>${escapeHtml40(event.label)}</td><td>${escapeHtml40(event.provider ?? "")}</td><td>${escapeHtml40(event.status ?? "")}</td><td>${formatMs3(event.elapsedMs)}</td></tr>`).join("");
|
|
24112
|
+
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${escapeHtml40(issue.severity)}">${escapeHtml40(issue.code)}: ${escapeHtml40(issue.message)}</li>`).join("") : "<li>none</li>";
|
|
24113
|
+
const supportLinks = session.operationsRecordHref ? `<p><a href="${escapeHtml40(session.operationsRecordHref)}">Open operations record</a></p>` : "";
|
|
24114
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml40(options.title ?? "Voice Trace Timeline")}</title><style>${timelineCSS}</style></head><body><main><a href="/traces">Back to traces</a><header><p class="eyebrow">Call timeline</p><h1>${escapeHtml40(session.sessionId)}</h1><p class="status ${escapeHtml40(session.status)}">${escapeHtml40(session.status)}</p>${supportLinks}</header><section class="metrics"><article><span>Events</span><strong>${String(session.summary.eventCount)}</strong></article><article><span>Turns</span><strong>${String(session.summary.turnCount)}</strong></article><article><span>Errors</span><strong>${String(session.summary.errorCount)}</strong></article><article><span>Duration</span><strong>${formatMs3(session.summary.callDurationMs)}</strong></article></section><section><h2>Providers</h2>${renderProviderCards2(session)}</section><section><h2>Issues</h2><ul>${issues}</ul></section><section><h2>Timeline</h2><table><thead><tr><th>Offset</th><th>Type</th><th>Event</th><th>Provider</th><th>Status</th><th>Latency</th></tr></thead><tbody>${events}</tbody></table></section></main></body></html>`;
|
|
23761
24115
|
};
|
|
23762
|
-
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${
|
|
24116
|
+
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${escapeHtml40(session.status)}"><td>${session.operationsRecordHref ? `<a href="${escapeHtml40(session.operationsRecordHref)}">${escapeHtml40(session.sessionId)}</a>` : `<a href="/traces/${encodeURIComponent(session.sessionId)}">${escapeHtml40(session.sessionId)}</a>`}</td><td>${escapeHtml40(session.status)}</td><td>${String(session.summary.eventCount)}</td><td>${String(session.summary.turnCount)}</td><td>${String(session.summary.errorCount)}</td><td>${formatMs3(session.summary.callDurationMs)}</td><td>${session.providers.map((provider) => escapeHtml40(provider.provider)).join(", ")}</td></tr>`).join("");
|
|
23763
24117
|
var timelineCSS = "body{background:#0f1318;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}a{color:#fbbf24}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.5rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.metrics,.providers{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin:20px 0}.metrics article,.providers article{background:#181f27;border:1px solid #2b3642;border-radius:20px;padding:16px}.metrics span,dt,.muted{color:#a8b0b8}.metrics strong{display:block;font-size:2rem}dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:12px 0 0}dd{font-weight:800;margin:4px 0 0}table{background:#181f27;border-collapse:collapse;border-radius:18px;overflow:hidden;width:100%}td,th{border-bottom:1px solid #2b3642;padding:12px;text-align:left}section{margin-top:28px}@media(max-width:760px){main{padding:20px}table{font-size:.9rem}}";
|
|
23764
24118
|
var renderVoiceTraceTimelineHTML = (report, options = {}) => {
|
|
23765
|
-
const snippet =
|
|
24119
|
+
const snippet = escapeHtml40(`const traceStore = createVoiceTraceSinkStore({
|
|
23766
24120
|
store: runtimeStorage.traces,
|
|
23767
24121
|
sinks: [
|
|
23768
24122
|
createVoiceTraceHTTPSink({
|
|
@@ -23788,24 +24142,26 @@ app.use(
|
|
|
23788
24142
|
traceDeliveries: runtimeStorage.traceDeliveries
|
|
23789
24143
|
})
|
|
23790
24144
|
);`);
|
|
23791
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
24145
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml40(options.title ?? "Voice Trace Timelines")}</title><style>${timelineCSS}.primitive{background:#181f27;border:1px solid #334155;border-radius:20px;margin:20px 0;padding:18px}.primitive p{line-height:1.55}.primitive pre{background:#0b1118;border:1px solid #2b3642;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.primitive code{color:#bfdbfe}</style></head><body><main><header><p class="eyebrow">Self-hosted voice debugging</p><h1>${escapeHtml40(options.title ?? "Voice Trace Timelines")}</h1><p class="muted">Per-call event timelines with provider latency, fallback, timeout, handoff, and error context.</p></header><section class="metrics"><article><span>Sessions</span><strong>${String(report.total)}</strong></article><article><span>Failed</span><strong>${String(report.failed)}</strong></article><article><span>Warnings</span><strong>${String(report.warnings)}</strong></article></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceTraceTimelineRoutes(...)</code> makes traces the proof backbone</h2><p class="muted">Mount trace timelines from the same trace store used by readiness, simulations, provider recovery, delivery sinks, and phone-agent smoke proof.</p><pre><code>${snippet}</code></pre></section><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Duration</th><th>Providers</th></tr></thead><tbody>${renderSessionRows(report)}</tbody></table></main></body></html>`;
|
|
23792
24146
|
};
|
|
23793
24147
|
var createVoiceTraceTimelineRoutes = (options) => {
|
|
23794
24148
|
const path = options.path ?? "/api/voice-traces";
|
|
23795
24149
|
const htmlPath = options.htmlPath ?? "/traces";
|
|
23796
24150
|
const title = options.title ?? "AbsoluteJS Voice Trace Timelines";
|
|
23797
|
-
const routes = new
|
|
24151
|
+
const routes = new Elysia39({
|
|
23798
24152
|
name: options.name ?? "absolutejs-voice-trace-timelines"
|
|
23799
24153
|
});
|
|
23800
24154
|
const buildReport = async () => summarizeVoiceTraceTimeline(await options.store.list(), {
|
|
23801
24155
|
evaluation: options.evaluation,
|
|
23802
24156
|
limit: options.limit,
|
|
24157
|
+
operationsRecordHref: options.operationsRecordHref,
|
|
23803
24158
|
redact: options.redact
|
|
23804
24159
|
});
|
|
23805
24160
|
const findSession = async (sessionId) => {
|
|
23806
24161
|
const report = summarizeVoiceTraceTimeline(await options.store.list({ sessionId }), {
|
|
23807
24162
|
evaluation: options.evaluation,
|
|
23808
24163
|
limit: 1,
|
|
24164
|
+
operationsRecordHref: options.operationsRecordHref,
|
|
23809
24165
|
redact: options.redact
|
|
23810
24166
|
});
|
|
23811
24167
|
return report.sessions[0];
|
|
@@ -23842,7 +24198,7 @@ var createVoiceTraceTimelineRoutes = (options) => {
|
|
|
23842
24198
|
};
|
|
23843
24199
|
|
|
23844
24200
|
// src/operationsRecord.ts
|
|
23845
|
-
var
|
|
24201
|
+
var getString17 = (value) => typeof value === "string" ? value : undefined;
|
|
23846
24202
|
var getNumber10 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
23847
24203
|
var countOutcome = (events, outcome) => events.filter((event) => event.outcome === outcome).length;
|
|
23848
24204
|
var matchesSessionScopedId = (id, sessionId) => id === sessionId || id.startsWith(`${sessionId}:`);
|
|
@@ -23853,21 +24209,21 @@ var hasPayloadValue = (payload, key, values) => {
|
|
|
23853
24209
|
var countIntegrationDeliveryStatus = (events, status) => events.filter((event) => event.deliveryStatus === status).length;
|
|
23854
24210
|
var toHandoff = (event) => ({
|
|
23855
24211
|
at: event.at,
|
|
23856
|
-
fromAgentId:
|
|
24212
|
+
fromAgentId: getString17(event.payload.fromAgentId),
|
|
23857
24213
|
metadata: event.payload.metadata && typeof event.payload.metadata === "object" && !Array.isArray(event.payload.metadata) ? event.payload.metadata : undefined,
|
|
23858
|
-
reason:
|
|
23859
|
-
status:
|
|
23860
|
-
summary:
|
|
23861
|
-
targetAgentId:
|
|
24214
|
+
reason: getString17(event.payload.reason),
|
|
24215
|
+
status: getString17(event.payload.status),
|
|
24216
|
+
summary: getString17(event.payload.summary),
|
|
24217
|
+
targetAgentId: getString17(event.payload.targetAgentId),
|
|
23862
24218
|
turnId: event.turnId
|
|
23863
24219
|
});
|
|
23864
24220
|
var toTool = (event) => ({
|
|
23865
24221
|
at: event.at,
|
|
23866
24222
|
elapsedMs: getNumber10(event.payload.elapsedMs),
|
|
23867
|
-
error:
|
|
23868
|
-
status:
|
|
23869
|
-
toolCallId:
|
|
23870
|
-
toolName:
|
|
24223
|
+
error: getString17(event.payload.error),
|
|
24224
|
+
status: getString17(event.payload.status),
|
|
24225
|
+
toolCallId: getString17(event.payload.toolCallId),
|
|
24226
|
+
toolName: getString17(event.payload.toolName),
|
|
23871
24227
|
turnId: event.turnId
|
|
23872
24228
|
});
|
|
23873
24229
|
var resolveOutcome4 = (events) => {
|
|
@@ -23948,16 +24304,16 @@ var buildVoiceOperationsRecord = async (options) => {
|
|
|
23948
24304
|
traceEvents
|
|
23949
24305
|
};
|
|
23950
24306
|
};
|
|
23951
|
-
var
|
|
24307
|
+
var escapeHtml41 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23952
24308
|
var formatMs4 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
23953
24309
|
var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
23954
|
-
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${
|
|
23955
|
-
const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${
|
|
23956
|
-
const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${
|
|
23957
|
-
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${
|
|
23958
|
-
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${
|
|
23959
|
-
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${
|
|
23960
|
-
const snippet =
|
|
24310
|
+
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${escapeHtml41(provider.provider)}</strong><span>${String(provider.eventCount)} events</span><span>${formatMs4(provider.averageElapsedMs)} avg</span><span>${String(provider.errorCount)} errors</span></article>`).join("") : '<p class="muted">No provider events recorded.</p>';
|
|
24311
|
+
const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${escapeHtml41(handoff.fromAgentId ?? "unknown")}</strong> to <strong>${escapeHtml41(handoff.targetAgentId ?? "unknown")}</strong> <span>${escapeHtml41(handoff.status ?? "")}</span><p>${escapeHtml41(handoff.summary ?? handoff.reason ?? "")}</p></li>`).join("") : "<li>No agent handoffs recorded.</li>";
|
|
24312
|
+
const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${escapeHtml41(tool.toolName ?? "tool")}</strong> <span>${escapeHtml41(tool.status ?? "")}</span> ${formatMs4(tool.elapsedMs)} ${tool.error ? `<p>${escapeHtml41(tool.error)}</p>` : ""}</li>`).join("") : "<li>No tool calls recorded.</li>";
|
|
24313
|
+
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${escapeHtml41(review.title)}</strong> <span>${escapeHtml41(review.summary.outcome ?? "")}</span><p>${escapeHtml41(review.postCall?.summary ?? review.transcript.actual)}</p></li>`).join("") : "<li>No call reviews recorded.</li>";
|
|
24314
|
+
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${escapeHtml41(task.title)}</strong> <span>${escapeHtml41(task.status)}</span><p>${escapeHtml41(task.recommendedAction)}</p></li>`).join("") : "<li>No ops tasks recorded.</li>";
|
|
24315
|
+
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${escapeHtml41(event.type)}</strong> <span>${escapeHtml41(event.deliveryStatus ?? "local")}</span><p>${escapeHtml41(event.deliveryError ?? event.deliveredTo ?? "")}</p></li>`).join("") : "<li>No integration events recorded.</li>";
|
|
24316
|
+
const snippet = escapeHtml41(`app.use(
|
|
23961
24317
|
createVoiceOperationsRecordRoutes({
|
|
23962
24318
|
audit: auditStore,
|
|
23963
24319
|
integrationEvents: opsEvents,
|
|
@@ -23971,12 +24327,12 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
23971
24327
|
tasks: opsTasks
|
|
23972
24328
|
})
|
|
23973
24329
|
);`);
|
|
23974
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
24330
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted{color:#a9b4bd}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}</style></head><body><main><p class="eyebrow">Portable production proof</p><h1>${escapeHtml41(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml41(record.status)}">${escapeHtml41(record.status)}</p><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs4(record.summary.callDurationMs)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, audit, latency, replay, reviews, tasks, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Providers</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
|
|
23975
24331
|
};
|
|
23976
24332
|
var createVoiceOperationsRecordRoutes = (options) => {
|
|
23977
24333
|
const path = options.path ?? "/api/voice-operations/:sessionId";
|
|
23978
24334
|
const htmlPath = options.htmlPath === undefined ? "/voice-operations/:sessionId" : options.htmlPath;
|
|
23979
|
-
const routes = new
|
|
24335
|
+
const routes = new Elysia40({
|
|
23980
24336
|
name: options.name ?? "absolutejs-voice-operations-record"
|
|
23981
24337
|
});
|
|
23982
24338
|
const buildRecord = (sessionId) => buildVoiceOperationsRecord({
|
|
@@ -24008,246 +24364,6 @@ var createVoiceOperationsRecordRoutes = (options) => {
|
|
|
24008
24364
|
}
|
|
24009
24365
|
return routes;
|
|
24010
24366
|
};
|
|
24011
|
-
// src/opsRecovery.ts
|
|
24012
|
-
import { Elysia as Elysia40 } from "elysia";
|
|
24013
|
-
var escapeHtml41 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
24014
|
-
var getString16 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
24015
|
-
var hrefForSession = (value, sessionId) => typeof value === "function" ? value(sessionId) : value;
|
|
24016
|
-
var rollupStatus3 = (issues) => issues.some((issue) => issue.severity === "fail") ? "fail" : issues.some((issue) => issue.severity === "warn") ? "warn" : "pass";
|
|
24017
|
-
var providerUnresolved = (provider) => provider.status === "degraded" || provider.status === "rate-limited" || provider.status === "suppressed";
|
|
24018
|
-
var collectFailedSessions = (events, limit) => events.filter((event) => {
|
|
24019
|
-
if (event.type !== "session.error") {
|
|
24020
|
-
return false;
|
|
24021
|
-
}
|
|
24022
|
-
const providerStatus = event.payload.providerStatus;
|
|
24023
|
-
return providerStatus !== "success" && providerStatus !== "fallback";
|
|
24024
|
-
}).sort((left, right) => right.at - left.at).slice(0, limit).map((event) => ({
|
|
24025
|
-
at: event.at,
|
|
24026
|
-
error: getString16(event.payload.error),
|
|
24027
|
-
provider: getString16(event.payload.provider),
|
|
24028
|
-
sessionId: event.sessionId,
|
|
24029
|
-
traceId: event.traceId
|
|
24030
|
-
}));
|
|
24031
|
-
var collectInterventions = (events, limit) => {
|
|
24032
|
-
const interventionEvents = events.filter((event) => event.type === "operator.action").sort((left, right) => right.at - left.at);
|
|
24033
|
-
return {
|
|
24034
|
-
events: interventionEvents.slice(0, limit).map((event) => ({
|
|
24035
|
-
action: getString16(event.payload.action),
|
|
24036
|
-
at: event.at,
|
|
24037
|
-
operatorId: getString16(event.payload.operatorId) ?? getString16(event.payload.actorId),
|
|
24038
|
-
sessionId: event.sessionId,
|
|
24039
|
-
traceId: event.traceId
|
|
24040
|
-
})),
|
|
24041
|
-
total: interventionEvents.length
|
|
24042
|
-
};
|
|
24043
|
-
};
|
|
24044
|
-
var addDeliveryIssues = (issues, input) => {
|
|
24045
|
-
if (!input.summary) {
|
|
24046
|
-
return;
|
|
24047
|
-
}
|
|
24048
|
-
const failed = input.summary.failed + input.summary.deadLettered;
|
|
24049
|
-
if (failed > 0) {
|
|
24050
|
-
issues.push({
|
|
24051
|
-
code: input.failedCode,
|
|
24052
|
-
detail: `${failed} failed or dead-lettered delivery record(s).`,
|
|
24053
|
-
href: input.href,
|
|
24054
|
-
label: input.failedLabel,
|
|
24055
|
-
severity: "fail",
|
|
24056
|
-
value: failed
|
|
24057
|
-
});
|
|
24058
|
-
}
|
|
24059
|
-
const pending = input.summary.pending + input.summary.retryEligible;
|
|
24060
|
-
if (pending > 0) {
|
|
24061
|
-
issues.push({
|
|
24062
|
-
code: input.pendingCode,
|
|
24063
|
-
detail: `${pending} pending or retry-eligible delivery record(s).`,
|
|
24064
|
-
href: input.href,
|
|
24065
|
-
label: input.pendingLabel,
|
|
24066
|
-
severity: "warn",
|
|
24067
|
-
value: pending
|
|
24068
|
-
});
|
|
24069
|
-
}
|
|
24070
|
-
};
|
|
24071
|
-
var buildVoiceOpsRecoveryReport = async (options = {}) => {
|
|
24072
|
-
const limit = options.limit ?? 50;
|
|
24073
|
-
const events = options.events ?? await options.traces?.list({ limit: Math.max(limit, 500) }) ?? [];
|
|
24074
|
-
const providers = await summarizeVoiceProviderHealth({
|
|
24075
|
-
events,
|
|
24076
|
-
providers: options.providers
|
|
24077
|
-
});
|
|
24078
|
-
const auditDeliveries = options.auditDeliveries ? await summarizeVoiceAuditSinkDeliveries(await options.auditDeliveries.list(), {
|
|
24079
|
-
deadLetters: options.auditDeliveryDeadLetters
|
|
24080
|
-
}) : undefined;
|
|
24081
|
-
const traceDeliveries = options.traceDeliveries ? await summarizeVoiceTraceSinkDeliveries(await options.traceDeliveries.list(), {
|
|
24082
|
-
deadLetters: options.traceDeliveryDeadLetters
|
|
24083
|
-
}) : undefined;
|
|
24084
|
-
const handoffDeliveries = options.handoffDeliveries ? await summarizeVoiceHandoffDeliveries(await options.handoffDeliveries.list(), {
|
|
24085
|
-
deadLetters: options.handoffDeliveryDeadLetters
|
|
24086
|
-
}) : undefined;
|
|
24087
|
-
const latency = options.latency === false ? undefined : await buildVoiceLatencySLOGate({
|
|
24088
|
-
events,
|
|
24089
|
-
...options.latency ?? {}
|
|
24090
|
-
});
|
|
24091
|
-
const failedSessions = collectFailedSessions(events, limit);
|
|
24092
|
-
const interventions = collectInterventions(events, limit);
|
|
24093
|
-
const issues = [];
|
|
24094
|
-
const unresolvedProviders = providers.filter(providerUnresolved);
|
|
24095
|
-
for (const provider of unresolvedProviders) {
|
|
24096
|
-
issues.push({
|
|
24097
|
-
code: "voice.ops_recovery.provider_unresolved_failure",
|
|
24098
|
-
detail: provider.lastError ?? `${provider.provider} status is ${provider.status}.`,
|
|
24099
|
-
href: options.links?.providers,
|
|
24100
|
-
label: `Provider ${provider.provider} needs recovery`,
|
|
24101
|
-
severity: "fail",
|
|
24102
|
-
value: provider.status
|
|
24103
|
-
});
|
|
24104
|
-
}
|
|
24105
|
-
addDeliveryIssues(issues, {
|
|
24106
|
-
failedCode: "voice.ops_recovery.audit_delivery_failed",
|
|
24107
|
-
failedLabel: "Audit delivery failures",
|
|
24108
|
-
href: options.links?.auditDeliveries,
|
|
24109
|
-
pendingCode: "voice.ops_recovery.audit_delivery_pending",
|
|
24110
|
-
pendingLabel: "Audit delivery backlog",
|
|
24111
|
-
summary: auditDeliveries
|
|
24112
|
-
});
|
|
24113
|
-
addDeliveryIssues(issues, {
|
|
24114
|
-
failedCode: "voice.ops_recovery.trace_delivery_failed",
|
|
24115
|
-
failedLabel: "Trace delivery failures",
|
|
24116
|
-
href: options.links?.traceDeliveries,
|
|
24117
|
-
pendingCode: "voice.ops_recovery.trace_delivery_pending",
|
|
24118
|
-
pendingLabel: "Trace delivery backlog",
|
|
24119
|
-
summary: traceDeliveries
|
|
24120
|
-
});
|
|
24121
|
-
addDeliveryIssues(issues, {
|
|
24122
|
-
failedCode: "voice.ops_recovery.handoff_failed",
|
|
24123
|
-
failedLabel: "Handoff delivery failures",
|
|
24124
|
-
href: options.links?.handoffs,
|
|
24125
|
-
pendingCode: "voice.ops_recovery.handoff_pending",
|
|
24126
|
-
pendingLabel: "Handoff delivery backlog",
|
|
24127
|
-
summary: handoffDeliveries
|
|
24128
|
-
});
|
|
24129
|
-
if (latency?.failed) {
|
|
24130
|
-
issues.push({
|
|
24131
|
-
code: "voice.ops_recovery.latency_slo_failed",
|
|
24132
|
-
detail: `${latency.failed} latency SLO measurement(s) failed.`,
|
|
24133
|
-
href: options.links?.traces ? hrefForSession(options.links.traces, latency.measurements[0]?.sessionId ?? "") : undefined,
|
|
24134
|
-
label: "Latency SLO failures",
|
|
24135
|
-
severity: "fail",
|
|
24136
|
-
value: latency.failed
|
|
24137
|
-
});
|
|
24138
|
-
} else if (latency?.warnings) {
|
|
24139
|
-
issues.push({
|
|
24140
|
-
code: "voice.ops_recovery.latency_slo_warn",
|
|
24141
|
-
detail: `${latency.warnings} latency SLO measurement(s) are warning.`,
|
|
24142
|
-
label: "Latency SLO warnings",
|
|
24143
|
-
severity: "warn",
|
|
24144
|
-
value: latency.warnings
|
|
24145
|
-
});
|
|
24146
|
-
}
|
|
24147
|
-
return {
|
|
24148
|
-
auditDeliveries,
|
|
24149
|
-
checkedAt: Date.now(),
|
|
24150
|
-
failedSessions,
|
|
24151
|
-
handoffDeliveries,
|
|
24152
|
-
interventions,
|
|
24153
|
-
issues,
|
|
24154
|
-
latency,
|
|
24155
|
-
providers: {
|
|
24156
|
-
healthy: providers.filter((provider) => provider.status === "healthy").length,
|
|
24157
|
-
providers,
|
|
24158
|
-
recoveredFallbacks: providers.reduce((total, provider) => total + provider.fallbackCount, 0),
|
|
24159
|
-
unresolvedFailures: unresolvedProviders.length
|
|
24160
|
-
},
|
|
24161
|
-
status: rollupStatus3(issues),
|
|
24162
|
-
traceDeliveries
|
|
24163
|
-
};
|
|
24164
|
-
};
|
|
24165
|
-
var buildVoiceOpsRecoveryReadinessCheck = (report, options = {}) => ({
|
|
24166
|
-
detail: report.status === "pass" ? `${report.providers.recoveredFallbacks} recovered fallback(s), ${report.interventions.total} operator intervention(s), and no unresolved recovery issues.` : `${report.issues.length} recovery issue(s) require attention.`,
|
|
24167
|
-
href: options.href,
|
|
24168
|
-
label: options.label ?? "Ops recovery",
|
|
24169
|
-
status: report.status,
|
|
24170
|
-
value: report.issues.length
|
|
24171
|
-
});
|
|
24172
|
-
var renderVoiceOpsRecoveryMarkdown = (report, options = {}) => {
|
|
24173
|
-
const title = options.title ?? "Voice Ops Recovery";
|
|
24174
|
-
const issueRows = report.issues.map((issue) => `| ${issue.severity} | ${issue.code} | ${issue.label} | ${issue.value ?? ""} | ${issue.detail ?? ""} |`).join(`
|
|
24175
|
-
`);
|
|
24176
|
-
const providers = report.providers.providers.map((provider) => `| ${provider.provider} | ${provider.status} | ${provider.runCount} | ${provider.errorCount} | ${provider.fallbackCount} | ${provider.lastError ?? ""} |`).join(`
|
|
24177
|
-
`);
|
|
24178
|
-
const failedSessions = report.failedSessions.map((session) => `- ${session.sessionId}${session.provider ? ` via ${session.provider}` : ""}${session.error ? `: ${session.error}` : ""}`).join(`
|
|
24179
|
-
`);
|
|
24180
|
-
return `# ${title}
|
|
24181
|
-
|
|
24182
|
-
Status: ${report.status}
|
|
24183
|
-
|
|
24184
|
-
Checked at: ${new Date(report.checkedAt).toISOString()}
|
|
24185
|
-
|
|
24186
|
-
Recovered fallbacks: ${report.providers.recoveredFallbacks}
|
|
24187
|
-
Unresolved provider failures: ${report.providers.unresolvedFailures}
|
|
24188
|
-
Operator interventions: ${report.interventions.total}
|
|
24189
|
-
|
|
24190
|
-
## Issues
|
|
24191
|
-
|
|
24192
|
-
| Severity | Code | Label | Value | Detail |
|
|
24193
|
-
| --- | --- | --- | ---: | --- |
|
|
24194
|
-
${issueRows || "| pass | none | No recovery issues | 0 | |"}
|
|
24195
|
-
|
|
24196
|
-
## Providers
|
|
24197
|
-
|
|
24198
|
-
| Provider | Status | Runs | Errors | Fallbacks | Last error |
|
|
24199
|
-
| --- | --- | ---: | ---: | ---: | --- |
|
|
24200
|
-
${providers || "| none | idle | 0 | 0 | 0 | |"}
|
|
24201
|
-
|
|
24202
|
-
## Failed Sessions
|
|
24203
|
-
|
|
24204
|
-
${failedSessions || "None."}
|
|
24205
|
-
|
|
24206
|
-
## Latency
|
|
24207
|
-
|
|
24208
|
-
${report.latency ? renderVoiceLatencySLOMarkdown(report.latency, { title: "Latency SLO" }) : "Latency SLO disabled."}
|
|
24209
|
-
`;
|
|
24210
|
-
};
|
|
24211
|
-
var renderDeliverySummary = (label, summary) => summary ? `<article><span>${escapeHtml41(label)}</span><strong>${String(summary.failed + summary.deadLettered)} failed</strong><small>${String(summary.pending)} pending \xB7 ${String(summary.retryEligible)} retry eligible \xB7 ${String(summary.total)} total</small></article>` : `<article><span>${escapeHtml41(label)}</span><strong>not configured</strong></article>`;
|
|
24212
|
-
var renderVoiceOpsRecoveryHTML = (report, options = {}) => {
|
|
24213
|
-
const title = options.title ?? "Voice Ops Recovery";
|
|
24214
|
-
const issues = report.issues.map((issue) => `<tr><td>${escapeHtml41(issue.severity)}</td><td><code>${escapeHtml41(issue.code)}</code></td><td>${escapeHtml41(issue.label)}</td><td>${escapeHtml41(String(issue.value ?? ""))}</td><td>${escapeHtml41(issue.detail ?? "")}</td></tr>`).join("");
|
|
24215
|
-
const providers = report.providers.providers.map((provider) => `<tr><td>${escapeHtml41(provider.provider)}</td><td>${escapeHtml41(provider.status)}</td><td>${String(provider.runCount)}</td><td>${String(provider.errorCount)}</td><td>${String(provider.fallbackCount)}</td><td>${escapeHtml41(provider.lastError ?? "")}</td></tr>`).join("");
|
|
24216
|
-
const failedSessions = report.failedSessions.map((session) => `<li>${escapeHtml41(session.sessionId)}${session.provider ? ` via ${escapeHtml41(session.provider)}` : ""}${session.error ? `: ${escapeHtml41(session.error)}` : ""}</li>`).join("");
|
|
24217
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#f8fafc;color:#172033;margin:2rem;line-height:1.45}main{max-width:1180px;margin:auto}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:.75rem;margin:1rem 0}article{background:white;border:1px solid #dbe3ef;border-radius:14px;padding:1rem;box-shadow:0 10px 28px rgba(15,23,42,.05)}article span{display:block;color:#64748b;font-size:.85rem}article strong{display:block;font-size:1.5rem;margin:.2rem 0}article small{color:#64748b}table{border-collapse:collapse;width:100%;background:white;border:1px solid #dbe3ef;border-radius:14px;overflow:hidden}th,td{border-bottom:1px solid #e2e8f0;padding:.7rem;text-align:left;vertical-align:top}code{font-size:.85em}.status{display:inline-flex;border-radius:999px;padding:.35rem .7rem;background:${report.status === "fail" ? "#fee2e2" : report.status === "warn" ? "#fef3c7" : "#dcfce7"};color:${report.status === "fail" ? "#991b1b" : report.status === "warn" ? "#92400e" : "#166534"};font-weight:700}</style></head><body><main><h1>${escapeHtml41(title)}</h1><p><span class="status">${escapeHtml41(report.status)}</span> Checked ${escapeHtml41(new Date(report.checkedAt).toLocaleString())}</p><section class="grid"><article><span>Recovered fallbacks</span><strong>${String(report.providers.recoveredFallbacks)}</strong></article><article><span>Unresolved providers</span><strong>${String(report.providers.unresolvedFailures)}</strong></article><article><span>Operator interventions</span><strong>${String(report.interventions.total)}</strong></article><article><span>Latency status</span><strong>${escapeHtml41(report.latency?.status ?? "disabled")}</strong></article>${renderDeliverySummary("Audit delivery", report.auditDeliveries)}${renderDeliverySummary("Trace delivery", report.traceDeliveries)}${renderDeliverySummary("Handoff delivery", report.handoffDeliveries)}</section><h2>Issues</h2><table><thead><tr><th>Severity</th><th>Code</th><th>Label</th><th>Value</th><th>Detail</th></tr></thead><tbody>${issues || '<tr><td colspan="5">No recovery issues.</td></tr>'}</tbody></table><h2>Providers</h2><table><thead><tr><th>Provider</th><th>Status</th><th>Runs</th><th>Errors</th><th>Fallbacks</th><th>Last error</th></tr></thead><tbody>${providers || '<tr><td colspan="6">No provider activity.</td></tr>'}</tbody></table><h2>Failed Sessions</h2><ul>${failedSessions || "<li>None.</li>"}</ul></main></body></html>`;
|
|
24218
|
-
};
|
|
24219
|
-
var createVoiceOpsRecoveryRoutes = (options = {}) => {
|
|
24220
|
-
const path = options.path ?? "/api/voice/ops-recovery";
|
|
24221
|
-
const htmlPath = options.htmlPath === undefined ? "/ops-recovery" : options.htmlPath;
|
|
24222
|
-
const markdownPath = options.markdownPath === undefined ? `${path}.md` : options.markdownPath;
|
|
24223
|
-
const routes = new Elysia40({
|
|
24224
|
-
name: options.name ?? "absolutejs-voice-ops-recovery"
|
|
24225
|
-
}).get(path, async () => buildVoiceOpsRecoveryReport(options));
|
|
24226
|
-
if (htmlPath) {
|
|
24227
|
-
routes.get(htmlPath, async () => {
|
|
24228
|
-
const report = await buildVoiceOpsRecoveryReport(options);
|
|
24229
|
-
const render = options.render ?? renderVoiceOpsRecoveryHTML;
|
|
24230
|
-
return new Response(await render(report), {
|
|
24231
|
-
headers: {
|
|
24232
|
-
"content-type": "text/html; charset=utf-8",
|
|
24233
|
-
...options.headers
|
|
24234
|
-
}
|
|
24235
|
-
});
|
|
24236
|
-
});
|
|
24237
|
-
}
|
|
24238
|
-
if (markdownPath) {
|
|
24239
|
-
routes.get(markdownPath, async () => {
|
|
24240
|
-
const report = await buildVoiceOpsRecoveryReport(options);
|
|
24241
|
-
return new Response(renderVoiceOpsRecoveryMarkdown(report, { title: options.title }), {
|
|
24242
|
-
headers: {
|
|
24243
|
-
"content-type": "text/markdown; charset=utf-8",
|
|
24244
|
-
...options.headers
|
|
24245
|
-
}
|
|
24246
|
-
});
|
|
24247
|
-
});
|
|
24248
|
-
}
|
|
24249
|
-
return routes;
|
|
24250
|
-
};
|
|
24251
24367
|
// src/incidentBundle.ts
|
|
24252
24368
|
import { Elysia as Elysia41 } from "elysia";
|
|
24253
24369
|
var filterIncidentBundleArtifacts = (artifacts, filter = {}) => artifacts.filter((artifact) => {
|
|
@@ -25096,7 +25212,7 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
25096
25212
|
// src/traceDeliveryRoutes.ts
|
|
25097
25213
|
import { Elysia as Elysia43 } from "elysia";
|
|
25098
25214
|
var escapeHtml43 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
25099
|
-
var
|
|
25215
|
+
var getString18 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
25100
25216
|
var getNumber11 = (value) => {
|
|
25101
25217
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
25102
25218
|
return value;
|
|
@@ -25108,13 +25224,13 @@ var getNumber11 = (value) => {
|
|
|
25108
25224
|
return;
|
|
25109
25225
|
};
|
|
25110
25226
|
var parseStatus2 = (value) => {
|
|
25111
|
-
const text =
|
|
25227
|
+
const text = getString18(value);
|
|
25112
25228
|
return text === "pending" || text === "delivered" || text === "failed" || text === "skipped" || text === "all" ? text : undefined;
|
|
25113
25229
|
};
|
|
25114
25230
|
var resolveVoiceTraceDeliveryFilter = (query = {}, base = {}) => ({
|
|
25115
25231
|
...base,
|
|
25116
25232
|
limit: getNumber11(query.limit) ?? base.limit,
|
|
25117
|
-
q:
|
|
25233
|
+
q: getString18(query.q) ?? base.q,
|
|
25118
25234
|
status: parseStatus2(query.status) ?? base.status
|
|
25119
25235
|
});
|
|
25120
25236
|
var deliverySearchText2 = (delivery) => [
|