@absolutejs/voice 0.0.22-beta.194 → 0.0.22-beta.196
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 +41 -0
- package/dist/dataControl.d.ts +92 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +797 -280
- package/dist/opsRecovery.d.ts +136 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12712,6 +12712,20 @@ var createVoiceDeliveryRuntimeRoutes = (options) => {
|
|
|
12712
12712
|
return routes;
|
|
12713
12713
|
};
|
|
12714
12714
|
// src/dataControl.ts
|
|
12715
|
+
import { Elysia as Elysia15 } from "elysia";
|
|
12716
|
+
var voiceComplianceRedactionDefaults = {
|
|
12717
|
+
keys: [
|
|
12718
|
+
"apiKey",
|
|
12719
|
+
"authorization",
|
|
12720
|
+
"email",
|
|
12721
|
+
"password",
|
|
12722
|
+
"phone",
|
|
12723
|
+
"secret",
|
|
12724
|
+
"token"
|
|
12725
|
+
],
|
|
12726
|
+
redactEmails: true,
|
|
12727
|
+
redactPhoneNumbers: true
|
|
12728
|
+
};
|
|
12715
12729
|
var allRetentionScopes = [
|
|
12716
12730
|
"auditDeliveries",
|
|
12717
12731
|
"campaigns",
|
|
@@ -12925,17 +12939,269 @@ var applyVoiceDataRetentionPolicy = async (options) => {
|
|
|
12925
12939
|
return report;
|
|
12926
12940
|
};
|
|
12927
12941
|
var buildVoiceDataRetentionPlan = (options) => applyVoiceDataRetentionPolicy({ ...options, dryRun: true });
|
|
12942
|
+
var getBooleanQuery = (value) => value === true || value === "true" || value === "1";
|
|
12943
|
+
var getNumberQuery = (value) => {
|
|
12944
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : undefined;
|
|
12945
|
+
return typeof parsed === "number" && Number.isFinite(parsed) ? parsed : undefined;
|
|
12946
|
+
};
|
|
12947
|
+
var parseRetentionScopes = (value) => {
|
|
12948
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
12949
|
+
return;
|
|
12950
|
+
}
|
|
12951
|
+
const allowed = new Set(allRetentionScopes);
|
|
12952
|
+
return value.split(",").map((entry) => entry.trim()).filter((entry) => allowed.has(entry));
|
|
12953
|
+
};
|
|
12954
|
+
var escapeHtml17 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12955
|
+
var buildStorageSurfaces = (options) => [
|
|
12956
|
+
{
|
|
12957
|
+
configured: Boolean(options.session ?? options.sessions),
|
|
12958
|
+
control: "session",
|
|
12959
|
+
name: "Sessions",
|
|
12960
|
+
selfHosted: true
|
|
12961
|
+
},
|
|
12962
|
+
{
|
|
12963
|
+
configured: Boolean(options.traces),
|
|
12964
|
+
control: "audit",
|
|
12965
|
+
name: "Trace events",
|
|
12966
|
+
selfHosted: true
|
|
12967
|
+
},
|
|
12968
|
+
{
|
|
12969
|
+
configured: Boolean(options.audit),
|
|
12970
|
+
control: "audit",
|
|
12971
|
+
name: "Audit events",
|
|
12972
|
+
selfHosted: true
|
|
12973
|
+
},
|
|
12974
|
+
{
|
|
12975
|
+
configured: Boolean(options.reviews),
|
|
12976
|
+
control: "artifact",
|
|
12977
|
+
name: "Call reviews",
|
|
12978
|
+
selfHosted: true
|
|
12979
|
+
},
|
|
12980
|
+
{
|
|
12981
|
+
configured: Boolean(options.tasks),
|
|
12982
|
+
control: "workflow",
|
|
12983
|
+
name: "Ops tasks",
|
|
12984
|
+
selfHosted: true
|
|
12985
|
+
},
|
|
12986
|
+
{
|
|
12987
|
+
configured: Boolean(options.events),
|
|
12988
|
+
control: "workflow",
|
|
12989
|
+
name: "Integration events",
|
|
12990
|
+
selfHosted: true
|
|
12991
|
+
},
|
|
12992
|
+
{
|
|
12993
|
+
configured: Boolean(options.campaigns),
|
|
12994
|
+
control: "workflow",
|
|
12995
|
+
name: "Campaign records",
|
|
12996
|
+
selfHosted: true
|
|
12997
|
+
},
|
|
12998
|
+
{
|
|
12999
|
+
configured: Boolean(options.auditDeliveries ?? options.traceDeliveries),
|
|
13000
|
+
control: "queue",
|
|
13001
|
+
name: "Audit/trace delivery queues",
|
|
13002
|
+
selfHosted: true
|
|
13003
|
+
},
|
|
13004
|
+
{
|
|
13005
|
+
configured: Boolean(options.incidentBundles),
|
|
13006
|
+
control: "artifact",
|
|
13007
|
+
name: "Incident bundles",
|
|
13008
|
+
selfHosted: true
|
|
13009
|
+
}
|
|
13010
|
+
];
|
|
13011
|
+
var defaultProviderKeys = [
|
|
13012
|
+
{
|
|
13013
|
+
env: "OPENAI_API_KEY",
|
|
13014
|
+
name: "OpenAI",
|
|
13015
|
+
recommendation: "Keep provider keys server-side, scoped per environment, and never expose them to browser voice clients.",
|
|
13016
|
+
required: false
|
|
13017
|
+
},
|
|
13018
|
+
{
|
|
13019
|
+
env: "ANTHROPIC_API_KEY",
|
|
13020
|
+
name: "Anthropic",
|
|
13021
|
+
recommendation: "Use least-privilege project keys and route all requests through your AbsoluteJS server.",
|
|
13022
|
+
required: false
|
|
13023
|
+
},
|
|
13024
|
+
{
|
|
13025
|
+
env: "GEMINI_API_KEY",
|
|
13026
|
+
name: "Gemini",
|
|
13027
|
+
recommendation: "Use server-owned credentials and redact prompts/tool payloads before support export.",
|
|
13028
|
+
required: false
|
|
13029
|
+
},
|
|
13030
|
+
{
|
|
13031
|
+
env: "DEEPGRAM_API_KEY",
|
|
13032
|
+
name: "Deepgram",
|
|
13033
|
+
recommendation: "Keep STT credentials server-side and pair transcript exports with PII redaction.",
|
|
13034
|
+
required: false
|
|
13035
|
+
}
|
|
13036
|
+
];
|
|
13037
|
+
var resolveDataControlRedaction = (redact) => redact === false ? undefined : redact === true || redact === undefined ? voiceComplianceRedactionDefaults : redact;
|
|
13038
|
+
var createVoiceZeroRetentionPolicy = (options) => ({
|
|
13039
|
+
...options,
|
|
13040
|
+
beforeOrAt: options.beforeOrAt ?? Date.now(),
|
|
13041
|
+
dryRun: options.dryRun ?? true,
|
|
13042
|
+
scopes: options.scopes ?? allRetentionScopes
|
|
13043
|
+
});
|
|
13044
|
+
var buildVoiceDataControlReport = async (options) => {
|
|
13045
|
+
const redaction = resolveDataControlRedaction(options.redact);
|
|
13046
|
+
const retentionPlan = await buildVoiceDataRetentionPlan({
|
|
13047
|
+
...options,
|
|
13048
|
+
...options.retention ?? {},
|
|
13049
|
+
auditDeliveries: options.auditDeliveries,
|
|
13050
|
+
traceDeliveries: options.traceDeliveries
|
|
13051
|
+
});
|
|
13052
|
+
const auditExport = options.audit ? await exportVoiceAuditTrail({
|
|
13053
|
+
filter: options.auditFilter,
|
|
13054
|
+
redact: redaction,
|
|
13055
|
+
store: options.audit
|
|
13056
|
+
}) : undefined;
|
|
13057
|
+
return {
|
|
13058
|
+
auditExport,
|
|
13059
|
+
checkedAt: Date.now(),
|
|
13060
|
+
redaction: {
|
|
13061
|
+
defaults: voiceComplianceRedactionDefaults,
|
|
13062
|
+
enabled: Boolean(redaction)
|
|
13063
|
+
},
|
|
13064
|
+
retentionPlan,
|
|
13065
|
+
storage: buildStorageSurfaces(options),
|
|
13066
|
+
providerKeys: options.providerKeys ?? defaultProviderKeys,
|
|
13067
|
+
zeroRetentionAvailable: true
|
|
13068
|
+
};
|
|
13069
|
+
};
|
|
13070
|
+
var renderDataRetentionReportRows = (report) => report.scopes.map((scope) => `<tr><td>${escapeHtml17(scope.scope)}</td><td>${scope.scannedCount}</td><td>${scope.deletedCount}</td><td>${escapeHtml17(scope.skippedReason ?? "")}</td><td><code>${escapeHtml17(scope.deletedIds.join(", "))}</code></td></tr>`).join("");
|
|
13071
|
+
var renderVoiceDataControlHTML = (report, options = {}) => {
|
|
13072
|
+
const title = options.title ?? "Voice Data Control";
|
|
13073
|
+
const storageRows = report.storage.map((surface) => `<tr><td>${escapeHtml17(surface.name)}</td><td>${surface.configured ? "Configured" : "Missing"}</td><td>${escapeHtml17(surface.control)}</td><td>${surface.selfHosted ? "Yes" : "No"}</td></tr>`).join("");
|
|
13074
|
+
const keyRows = report.providerKeys.map((key) => `<tr><td>${escapeHtml17(key.name)}</td><td><code>${escapeHtml17(key.env ?? "n/a")}</code></td><td>${key.required ? "Required" : "Optional"}</td><td>${escapeHtml17(key.recommendation)}</td></tr>`).join("");
|
|
13075
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml17(title)}</title><style>body{background:#f8f7f2;color:#181713;font-family:ui-sans-serif,system-ui,sans-serif;line-height:1.45;margin:2rem}main{max-width:1120px;margin:auto}.summary{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:1rem 0}.card,table{background:white;border:1px solid #ddd6c8;border-radius:14px}.card{padding:1rem}table{border-collapse:collapse;width:100%;overflow:hidden}td,th{border-bottom:1px solid #eee8dc;padding:.7rem;text-align:left;vertical-align:top}code{white-space:pre-wrap;word-break:break-word}a{color:#9a3412}</style></head><body><main><h1>${escapeHtml17(title)}</h1><p>Self-hosted data-control proof for retention, redaction, audit export, deletion planning, customer-owned storage, and provider key handling.</p><section class="summary"><div class="card"><strong>Redaction</strong><br>${report.redaction.enabled ? "enabled" : "disabled"}</div><div class="card"><strong>Retention dry-run deletes</strong><br>${report.retentionPlan.deletedCount}</div><div class="card"><strong>Audit export events</strong><br>${report.auditExport?.events.length ?? 0}</div><div class="card"><strong>Zero retention recipe</strong><br>${report.zeroRetentionAvailable ? "available" : "missing"}</div></section><h2>Customer-Owned Storage</h2><table><thead><tr><th>Surface</th><th>Status</th><th>Control</th><th>Self-hosted</th></tr></thead><tbody>${storageRows}</tbody></table><h2>Retention Plan</h2><table><thead><tr><th>Scope</th><th>Scanned</th><th>Would delete</th><th>Skipped</th><th>Ids</th></tr></thead><tbody>${renderDataRetentionReportRows(report.retentionPlan)}</tbody></table><h2>Provider Keys</h2><table><thead><tr><th>Provider</th><th>Env</th><th>Required</th><th>Recommendation</th></tr></thead><tbody>${keyRows}</tbody></table><p><a href="./data-control/audit.md">Redacted audit Markdown</a> \xB7 <a href="./data-control/audit.html">Redacted audit HTML</a></p></main></body></html>`;
|
|
13076
|
+
};
|
|
13077
|
+
var renderVoiceDataControlMarkdown = (report, options = {}) => [
|
|
13078
|
+
`# ${options.title ?? "Voice Data Control"}`,
|
|
13079
|
+
"",
|
|
13080
|
+
`Checked: ${new Date(report.checkedAt).toISOString()}`,
|
|
13081
|
+
`Redaction: ${report.redaction.enabled ? "enabled" : "disabled"}`,
|
|
13082
|
+
`Retention dry-run deletes: ${report.retentionPlan.deletedCount}`,
|
|
13083
|
+
`Audit export events: ${report.auditExport?.events.length ?? 0}`,
|
|
13084
|
+
"",
|
|
13085
|
+
"## Customer-Owned Storage",
|
|
13086
|
+
"",
|
|
13087
|
+
...report.storage.map((surface) => `- ${surface.name}: ${surface.configured ? "configured" : "missing"} (${surface.control})`),
|
|
13088
|
+
"",
|
|
13089
|
+
"## Retention Plan",
|
|
13090
|
+
"",
|
|
13091
|
+
...report.retentionPlan.scopes.map((scope) => `- ${scope.scope}: scanned ${scope.scannedCount}, would delete ${scope.deletedCount}${scope.skippedReason ? `, skipped=${scope.skippedReason}` : ""}`),
|
|
13092
|
+
"",
|
|
13093
|
+
"## Provider Keys",
|
|
13094
|
+
"",
|
|
13095
|
+
...report.providerKeys.map((key) => `- ${key.name}${key.env ? ` (${key.env})` : ""}: ${key.recommendation}`)
|
|
13096
|
+
].join(`
|
|
13097
|
+
`);
|
|
13098
|
+
var buildRetentionPolicyFromQuery = (query, options) => ({
|
|
13099
|
+
...options,
|
|
13100
|
+
before: getNumberQuery(query.before),
|
|
13101
|
+
beforeOrAt: getNumberQuery(query.beforeOrAt),
|
|
13102
|
+
dryRun: !getBooleanQuery(query.apply),
|
|
13103
|
+
limit: getNumberQuery(query.limit),
|
|
13104
|
+
scopes: parseRetentionScopes(query.scopes),
|
|
13105
|
+
traceFilter: typeof query.sessionId === "string" && query.sessionId.trim() ? { sessionId: query.sessionId } : undefined
|
|
13106
|
+
});
|
|
13107
|
+
var parseRetentionPolicyBody = (body, options, dryRun) => {
|
|
13108
|
+
const input = body && typeof body === "object" ? body : {};
|
|
13109
|
+
return {
|
|
13110
|
+
...options,
|
|
13111
|
+
before: getNumberQuery(input.before),
|
|
13112
|
+
beforeOrAt: getNumberQuery(input.beforeOrAt),
|
|
13113
|
+
dryRun,
|
|
13114
|
+
limit: getNumberQuery(input.limit),
|
|
13115
|
+
scopes: parseRetentionScopes(input.scopes),
|
|
13116
|
+
traceFilter: typeof input.sessionId === "string" && input.sessionId.trim() ? { sessionId: input.sessionId } : undefined
|
|
13117
|
+
};
|
|
13118
|
+
};
|
|
13119
|
+
var createVoiceDataControlRoutes = (options) => {
|
|
13120
|
+
const path = options.path ?? "/data-control";
|
|
13121
|
+
const title = options.title ?? "AbsoluteJS Voice Data Control";
|
|
13122
|
+
const routes = new Elysia15({
|
|
13123
|
+
name: options.name ?? "absolutejs-voice-data-control"
|
|
13124
|
+
});
|
|
13125
|
+
routes.get(path, async ({ query }) => {
|
|
13126
|
+
const report = await buildVoiceDataControlReport({
|
|
13127
|
+
...options,
|
|
13128
|
+
retention: buildRetentionPolicyFromQuery(query, options)
|
|
13129
|
+
});
|
|
13130
|
+
return new Response(renderVoiceDataControlHTML(report, { title }), {
|
|
13131
|
+
headers: {
|
|
13132
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
13133
|
+
...options.headers
|
|
13134
|
+
}
|
|
13135
|
+
});
|
|
13136
|
+
});
|
|
13137
|
+
routes.get(`${path}.json`, async ({ query }) => buildVoiceDataControlReport({
|
|
13138
|
+
...options,
|
|
13139
|
+
retention: buildRetentionPolicyFromQuery(query, options)
|
|
13140
|
+
}));
|
|
13141
|
+
routes.get(`${path}.md`, async ({ query }) => {
|
|
13142
|
+
const report = await buildVoiceDataControlReport({
|
|
13143
|
+
...options,
|
|
13144
|
+
retention: buildRetentionPolicyFromQuery(query, options)
|
|
13145
|
+
});
|
|
13146
|
+
return new Response(renderVoiceDataControlMarkdown(report, { title }), {
|
|
13147
|
+
headers: {
|
|
13148
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
13149
|
+
...options.headers
|
|
13150
|
+
}
|
|
13151
|
+
});
|
|
13152
|
+
});
|
|
13153
|
+
routes.post(`${path}/retention/plan`, async ({ body }) => buildVoiceDataRetentionPlan(parseRetentionPolicyBody(body, options, true)));
|
|
13154
|
+
routes.post(`${path}/retention/apply`, async ({ body, set }) => {
|
|
13155
|
+
const input = body && typeof body === "object" ? body : {};
|
|
13156
|
+
if (input.confirm !== "apply-retention-policy") {
|
|
13157
|
+
set.status = 400;
|
|
13158
|
+
return {
|
|
13159
|
+
error: 'Refusing to apply retention without confirm="apply-retention-policy". Use /retention/plan first.'
|
|
13160
|
+
};
|
|
13161
|
+
}
|
|
13162
|
+
return applyVoiceDataRetentionPolicy(parseRetentionPolicyBody(body, options, false));
|
|
13163
|
+
});
|
|
13164
|
+
routes.get(`${path}/audit.json`, async () => options.audit ? exportVoiceAuditTrail({
|
|
13165
|
+
redact: resolveDataControlRedaction(options.redact),
|
|
13166
|
+
store: options.audit
|
|
13167
|
+
}) : { events: [], exportedAt: Date.now(), redacted: false });
|
|
13168
|
+
routes.get(`${path}/audit.md`, async () => {
|
|
13169
|
+
const events = options.audit ? await options.audit.list() : [];
|
|
13170
|
+
return new Response(renderVoiceAuditMarkdown(events, {
|
|
13171
|
+
redact: resolveDataControlRedaction(options.redact),
|
|
13172
|
+
title: `${title} Audit Export`
|
|
13173
|
+
}), {
|
|
13174
|
+
headers: {
|
|
13175
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
13176
|
+
...options.headers
|
|
13177
|
+
}
|
|
13178
|
+
});
|
|
13179
|
+
});
|
|
13180
|
+
routes.get(`${path}/audit.html`, async () => {
|
|
13181
|
+
const events = options.audit ? await options.audit.list() : [];
|
|
13182
|
+
return new Response(renderVoiceAuditHTML(events, {
|
|
13183
|
+
redact: resolveDataControlRedaction(options.redact),
|
|
13184
|
+
title: `${title} Audit Export`
|
|
13185
|
+
}), {
|
|
13186
|
+
headers: {
|
|
13187
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
13188
|
+
...options.headers
|
|
13189
|
+
}
|
|
13190
|
+
});
|
|
13191
|
+
});
|
|
13192
|
+
return routes;
|
|
13193
|
+
};
|
|
12928
13194
|
// src/evalRoutes.ts
|
|
12929
|
-
import { Elysia as
|
|
13195
|
+
import { Elysia as Elysia18 } from "elysia";
|
|
12930
13196
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
12931
13197
|
import { dirname as dirname2 } from "path";
|
|
12932
13198
|
|
|
12933
13199
|
// src/qualityRoutes.ts
|
|
12934
|
-
import { Elysia as
|
|
13200
|
+
import { Elysia as Elysia17 } from "elysia";
|
|
12935
13201
|
|
|
12936
13202
|
// src/handoffHealth.ts
|
|
12937
|
-
import { Elysia as
|
|
12938
|
-
var
|
|
13203
|
+
import { Elysia as Elysia16 } from "elysia";
|
|
13204
|
+
var escapeHtml18 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12939
13205
|
var getString6 = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
12940
13206
|
var isStatus = (value) => value === "delivered" || value === "failed" || value === "skipped";
|
|
12941
13207
|
var increment3 = (record, key) => {
|
|
@@ -13053,10 +13319,10 @@ var renderActionSummary = (summary) => {
|
|
|
13053
13319
|
return [
|
|
13054
13320
|
'<section class="voice-handoff-health-columns">',
|
|
13055
13321
|
"<article><h3>Actions</h3>",
|
|
13056
|
-
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${
|
|
13322
|
+
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${escapeHtml18(action)}: ${String(count)}</li>`).join("")}</ul>`,
|
|
13057
13323
|
"</article>",
|
|
13058
13324
|
"<article><h3>Adapters</h3>",
|
|
13059
|
-
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${
|
|
13325
|
+
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${escapeHtml18(adapterId)}: ${String(counts.delivered)} delivered / ${String(counts.failed)} failed / ${String(counts.skipped)} skipped</li>`).join("")}</ul>`,
|
|
13060
13326
|
"</article>",
|
|
13061
13327
|
"</section>"
|
|
13062
13328
|
].join("");
|
|
@@ -13070,22 +13336,22 @@ var renderVoiceHandoffHealthHTML = (summary) => [
|
|
|
13070
13336
|
summary.events.length === 0 ? '<p class="voice-handoff-health-empty">No handoffs found.</p>' : [
|
|
13071
13337
|
'<div class="voice-handoff-health-events">',
|
|
13072
13338
|
...summary.events.map((event) => [
|
|
13073
|
-
`<article class="${
|
|
13339
|
+
`<article class="${escapeHtml18(event.status)}">`,
|
|
13074
13340
|
'<div class="voice-handoff-health-event-header">',
|
|
13075
|
-
`<strong>${
|
|
13076
|
-
`<span>${
|
|
13341
|
+
`<strong>${escapeHtml18(event.action ?? "handoff")}</strong>`,
|
|
13342
|
+
`<span>${escapeHtml18(event.status)}</span>`,
|
|
13077
13343
|
"</div>",
|
|
13078
|
-
`<p><small>${
|
|
13079
|
-
event.target ? `<p>Target: ${
|
|
13080
|
-
event.reason ? `<p>Reason: ${
|
|
13344
|
+
`<p><small>${escapeHtml18(event.sessionId)}</small></p>`,
|
|
13345
|
+
event.target ? `<p>Target: ${escapeHtml18(event.target)}</p>` : "",
|
|
13346
|
+
event.reason ? `<p>Reason: ${escapeHtml18(event.reason)}</p>` : "",
|
|
13081
13347
|
event.deliveries.length ? `<ul>${event.deliveries.map((delivery) => [
|
|
13082
13348
|
"<li>",
|
|
13083
|
-
`${
|
|
13084
|
-
delivery.deliveredTo ? ` to ${
|
|
13085
|
-
delivery.error ? ` (${
|
|
13349
|
+
`${escapeHtml18(delivery.adapterId)}: ${escapeHtml18(delivery.status)}`,
|
|
13350
|
+
delivery.deliveredTo ? ` to ${escapeHtml18(delivery.deliveredTo)}` : "",
|
|
13351
|
+
delivery.error ? ` (${escapeHtml18(delivery.error)})` : "",
|
|
13086
13352
|
"</li>"
|
|
13087
13353
|
].join("")).join("")}</ul>` : "",
|
|
13088
|
-
event.replayHref ? `<p><a href="${
|
|
13354
|
+
event.replayHref ? `<p><a href="${escapeHtml18(event.replayHref)}">Open replay</a></p>` : "",
|
|
13089
13355
|
"</article>"
|
|
13090
13356
|
].join("")),
|
|
13091
13357
|
"</div>"
|
|
@@ -13117,7 +13383,7 @@ var createVoiceHandoffHealthHTMLHandler = (options = {}) => async ({ query }) =>
|
|
|
13117
13383
|
var createVoiceHandoffHealthRoutes = (options = {}) => {
|
|
13118
13384
|
const path = options.path ?? "/api/voice-handoffs";
|
|
13119
13385
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
13120
|
-
const routes = new
|
|
13386
|
+
const routes = new Elysia16({
|
|
13121
13387
|
name: options.name ?? "absolutejs-voice-handoff-health"
|
|
13122
13388
|
}).get(path, createVoiceHandoffHealthJSONHandler(options));
|
|
13123
13389
|
if (htmlPath) {
|
|
@@ -13238,17 +13504,17 @@ var evaluateVoiceQuality = async (input) => {
|
|
|
13238
13504
|
thresholds
|
|
13239
13505
|
};
|
|
13240
13506
|
};
|
|
13241
|
-
var
|
|
13507
|
+
var escapeHtml19 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13242
13508
|
var formatMetricValue = (metric) => metric.unit === "rate" ? `${(metric.actual * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.actual)}ms` : String(metric.actual);
|
|
13243
13509
|
var formatThreshold = (metric) => metric.unit === "rate" ? `${(metric.threshold * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.threshold)}ms` : String(metric.threshold);
|
|
13244
13510
|
var renderVoiceQualityHTML = (report, options = {}) => {
|
|
13245
|
-
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${
|
|
13246
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
13511
|
+
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${escapeHtml19(metric.label)}</td><td>${escapeHtml19(formatMetricValue(metric))}</td><td>${escapeHtml19(formatThreshold(metric))}</td><td>${metric.pass ? "pass" : "fail"}</td><td><code>${escapeHtml19(key)}</code></td></tr>`).join("");
|
|
13512
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml19(link.href)}">${escapeHtml19(link.label)}</a>`).join("")}</nav>` : "";
|
|
13247
13513
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>AbsoluteJS Voice Quality</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1100px;margin:auto}nav{display:flex;flex-wrap:wrap;gap:.5rem;margin:0 0 1.25rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;padding:.35rem .75rem;font-weight:800}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}table{border-collapse:collapse;width:100%;background:white;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}.pass td{border-left:4px solid #16a34a}.fail td{border-left:4px solid #dc2626}code{background:#f3f4f6;padding:.15rem .3rem;border-radius:.3rem}</style></head><body><main>${links}<h1>Voice quality gates</h1><p class="status ${report.status}">${report.status}</p><p>${report.eventCount} event(s) checked.</p><table><thead><tr><th>Metric</th><th>Actual</th><th>Threshold</th><th>Status</th><th>Key</th></tr></thead><tbody>${rows}</tbody></table></main></body></html>`;
|
|
13248
13514
|
};
|
|
13249
13515
|
var createVoiceQualityRoutes = (options) => {
|
|
13250
13516
|
const path = options.path ?? "/quality";
|
|
13251
|
-
const routes = new
|
|
13517
|
+
const routes = new Elysia17({
|
|
13252
13518
|
name: options.name ?? "absolutejs-voice-quality"
|
|
13253
13519
|
});
|
|
13254
13520
|
const getReport = () => evaluateVoiceQuality({
|
|
@@ -13277,7 +13543,7 @@ var createVoiceQualityRoutes = (options) => {
|
|
|
13277
13543
|
};
|
|
13278
13544
|
|
|
13279
13545
|
// src/evalRoutes.ts
|
|
13280
|
-
var
|
|
13546
|
+
var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13281
13547
|
var rate2 = (count, total) => count / Math.max(1, total);
|
|
13282
13548
|
var normalizeSearchText = (value) => value.trim().toLowerCase();
|
|
13283
13549
|
var getString8 = (value) => typeof value === "string" ? value : undefined;
|
|
@@ -13585,7 +13851,7 @@ var createVoiceFileScenarioFixtureStore = (filePath) => ({
|
|
|
13585
13851
|
var formatTime = (value) => value === undefined ? "unknown" : new Date(value).toLocaleString();
|
|
13586
13852
|
var formatPercent = (value) => `${(value * 100).toFixed(2)}%`;
|
|
13587
13853
|
var renderVoiceEvalPrimitiveCopy = () => {
|
|
13588
|
-
const snippet =
|
|
13854
|
+
const snippet = escapeHtml20(`app.use(
|
|
13589
13855
|
createVoiceEvalRoutes({
|
|
13590
13856
|
path: '/evals',
|
|
13591
13857
|
store: traceStore,
|
|
@@ -13606,44 +13872,44 @@ var renderVoiceEvalPrimitiveCopy = () => {
|
|
|
13606
13872
|
};
|
|
13607
13873
|
var renderVoiceEvalHTML = (report, options = {}) => {
|
|
13608
13874
|
const title = options.title ?? "AbsoluteJS Voice Evals";
|
|
13609
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
13610
|
-
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${
|
|
13875
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml20(link.href)}">${escapeHtml20(link.label)}</a>`).join("")}</nav>` : "";
|
|
13876
|
+
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${escapeHtml20(bucket.key)}</td><td>${bucket.total}</td><td>${bucket.passed}</td><td>${bucket.failed}</td></tr>`).join("") : '<tr><td colspan="4">No eval buckets yet.</td></tr>';
|
|
13611
13877
|
const sessions = report.sessions.length ? report.sessions.map((session) => {
|
|
13612
13878
|
const failedMetrics = Object.entries(session.quality.metrics).filter(([, metric]) => !metric.pass).map(([, metric]) => metric.label).join(", ");
|
|
13613
|
-
return `<tr class="${session.status}"><td>${
|
|
13879
|
+
return `<tr class="${session.status}"><td>${escapeHtml20(session.sessionId)}</td><td>${escapeHtml20(session.status)}</td><td>${session.eventCount}</td><td>${session.summary.turnCount}</td><td>${session.summary.errorCount}</td><td>${escapeHtml20(formatTime(session.endedAt))}</td><td>${escapeHtml20(failedMetrics || "none")}</td></tr>`;
|
|
13614
13880
|
}).join("") : '<tr><td colspan="7">No sessions found.</td></tr>';
|
|
13615
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13881
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml20(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.pass{color:#166534}.fail{color:#991b1b}.status.pass{background:#dcfce7}.status.fail{background:#fee2e2}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,.primitive{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.primitive{background:#fffdf7;border-color:#d6c7a3;margin:1rem 0}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.card strong{display:block;font-size:2rem}table{border-collapse:collapse;background:white;width:100%;margin:1rem 0 2rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml20(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${renderVoiceEvalPrimitiveCopy()}<h2>Trend</h2><table><thead><tr><th>Day</th><th>Total</th><th>Passed</th><th>Failed</th></tr></thead><tbody>${trend}</tbody></table><h2>Session Eval Results</h2><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Last event</th><th>Failed metrics</th></tr></thead><tbody>${sessions}</tbody></table></main></body></html>`;
|
|
13616
13882
|
};
|
|
13617
13883
|
var renderVoiceEvalBaselineHTML = (comparison, options = {}) => {
|
|
13618
13884
|
const title = options.title ?? "AbsoluteJS Voice Eval Baseline";
|
|
13619
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
13620
|
-
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${
|
|
13621
|
-
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${
|
|
13622
|
-
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${
|
|
13623
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13885
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml20(link.href)}">${escapeHtml20(link.label)}</a>`).join("")}</nav>` : "";
|
|
13886
|
+
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${escapeHtml20(reason)}</li>`).join("") : "<li>No baseline regressions detected.</li>";
|
|
13887
|
+
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${escapeHtml20(id)}</li>`).join("") : "<li>none</li>";
|
|
13888
|
+
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${escapeHtml20(id)}</li>`).join("") : "<li>none</li>";
|
|
13889
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml20(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1000px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.pass{background:#dcfce7;color:#166534}.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:1rem 0}.card{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.card strong{display:block;font-size:2rem}section{background:white;border:1px solid #e7e5e4;border-radius:1rem;margin:1rem 0;padding:1rem}</style></head><body><main>${links}<h1>${escapeHtml20(title)}</h1><p class="status ${comparison.status}">${comparison.status}</p><div class="grid"><article class="card"><span>Baseline pass rate</span><strong>${escapeHtml20(formatPercent(comparison.baseline.passRate))}</strong></article><article class="card"><span>Current pass rate</span><strong>${escapeHtml20(formatPercent(comparison.current.passRate))}</strong></article><article class="card"><span>Failed delta</span><strong>${comparison.deltas.failed}</strong></article><article class="card"><span>Pass rate delta</span><strong>${escapeHtml20(formatPercent(comparison.deltas.passRate))}</strong></article></div><section><h2>Regression Reasons</h2><ul>${reasons}</ul></section><section><h2>New Failed Sessions</h2><ul>${newFailures}</ul></section><section><h2>Recovered Sessions</h2><ul>${recovered}</ul></section></main></body></html>`;
|
|
13624
13890
|
};
|
|
13625
13891
|
var renderVoiceScenarioEvalHTML = (report, options = {}) => {
|
|
13626
13892
|
const title = options.title ?? "AbsoluteJS Voice Scenario Evals";
|
|
13627
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
13893
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml20(link.href)}">${escapeHtml20(link.label)}</a>`).join("")}</nav>` : "";
|
|
13628
13894
|
const scenarios = report.scenarios.length ? report.scenarios.map((scenario) => {
|
|
13629
|
-
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${
|
|
13630
|
-
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => `<tr class="${session.status}"><td>${
|
|
13631
|
-
return `<section class="scenario ${scenario.status}"><h2>${
|
|
13895
|
+
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${escapeHtml20(issue)}</li>`).join("")}</ul>` : "";
|
|
13896
|
+
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => `<tr class="${session.status}"><td>${escapeHtml20(session.sessionId)}</td><td>${escapeHtml20(session.status)}</td><td>${session.eventCount}</td><td>${escapeHtml20(session.issues.join(", ") || "none")}</td></tr>`).join("") : '<tr><td colspan="4">No matching sessions.</td></tr>';
|
|
13897
|
+
return `<section class="scenario ${scenario.status}"><h2>${escapeHtml20(scenario.label)}</h2>${scenario.description ? `<p>${escapeHtml20(scenario.description)}</p>` : ""}<p class="status ${scenario.status}">${scenario.status}</p><p>${scenario.passed} passed, ${scenario.failed} failed, ${scenario.matchedSessions} matched.</p>${scenarioIssues}<table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Issues</th></tr></thead><tbody>${sessions}</tbody></table></section>`;
|
|
13632
13898
|
}).join("") : "<section><p>No scenarios configured.</p></section>";
|
|
13633
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13899
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml20(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,section{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.primitive{background:#fffdf7;border-color:#d6c7a3}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.card strong{display:block;font-size:2rem}section{margin:1rem 0}table{border-collapse:collapse;width:100%;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml20(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${renderVoiceEvalPrimitiveCopy()}${scenarios}</main></body></html>`;
|
|
13634
13900
|
};
|
|
13635
13901
|
var renderVoiceScenarioFixtureEvalHTML = (report, options = {}) => {
|
|
13636
13902
|
const title = options.title ?? "AbsoluteJS Voice Fixture Evals";
|
|
13637
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
13903
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml20(link.href)}">${escapeHtml20(link.label)}</a>`).join("")}</nav>` : "";
|
|
13638
13904
|
const fixtures = report.fixtures.length ? report.fixtures.map((fixture) => {
|
|
13639
|
-
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${
|
|
13640
|
-
return `<section class="${fixture.status}"><h2>${
|
|
13905
|
+
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${escapeHtml20(scenario.label)}</td><td>${escapeHtml20(scenario.status)}</td><td>${scenario.matchedSessions}</td><td>${escapeHtml20([...scenario.issues, ...scenario.sessions.flatMap((session) => session.issues)].join(", ") || "none")}</td></tr>`).join("");
|
|
13906
|
+
return `<section class="${fixture.status}"><h2>${escapeHtml20(fixture.label)}</h2>${fixture.description ? `<p>${escapeHtml20(fixture.description)}</p>` : ""}<p class="status ${fixture.status}">${fixture.status}</p><table><thead><tr><th>Scenario</th><th>Status</th><th>Sessions</th><th>Issues</th></tr></thead><tbody>${scenarios}</tbody></table></section>`;
|
|
13641
13907
|
}).join("") : "<section><p>No scenario fixtures configured.</p></section>";
|
|
13642
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13908
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml20(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,section{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.primitive{background:#fffdf7;border-color:#d6c7a3}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.card strong{display:block;font-size:2rem}section{margin:1rem 0}table{border-collapse:collapse;width:100%;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml20(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${renderVoiceEvalPrimitiveCopy()}${fixtures}</main></body></html>`;
|
|
13643
13909
|
};
|
|
13644
13910
|
var createVoiceEvalRoutes = (options) => {
|
|
13645
13911
|
const path = options.path ?? "/evals";
|
|
13646
|
-
const routes = new
|
|
13912
|
+
const routes = new Elysia18({
|
|
13647
13913
|
name: options.name ?? "absolutejs-voice-evals"
|
|
13648
13914
|
});
|
|
13649
13915
|
const getReport = () => runVoiceSessionEvals({
|
|
@@ -13777,11 +14043,11 @@ var createVoiceEvalRoutes = (options) => {
|
|
|
13777
14043
|
return routes;
|
|
13778
14044
|
};
|
|
13779
14045
|
// src/simulationSuite.ts
|
|
13780
|
-
import { Elysia as
|
|
14046
|
+
import { Elysia as Elysia21 } from "elysia";
|
|
13781
14047
|
|
|
13782
14048
|
// src/outcomeContract.ts
|
|
13783
|
-
import { Elysia as
|
|
13784
|
-
var
|
|
14049
|
+
import { Elysia as Elysia19 } from "elysia";
|
|
14050
|
+
var escapeHtml21 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13785
14051
|
var getPayloadString = (event, key) => typeof event.payload[key] === "string" ? event.payload[key] : undefined;
|
|
13786
14052
|
var toList = async (input) => Array.isArray(input) ? input : await input?.list() ?? [];
|
|
13787
14053
|
var hydrateSessions = async (input) => {
|
|
@@ -13889,9 +14155,9 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
13889
14155
|
const contracts = report.contracts.map((contract) => `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
13890
14156
|
<div class="contract-header">
|
|
13891
14157
|
<div>
|
|
13892
|
-
<p class="eyebrow">${
|
|
13893
|
-
<h2>${
|
|
13894
|
-
${contract.description ? `<p>${
|
|
14158
|
+
<p class="eyebrow">${escapeHtml21(contract.contractId)}</p>
|
|
14159
|
+
<h2>${escapeHtml21(contract.label ?? contract.contractId)}</h2>
|
|
14160
|
+
${contract.description ? `<p>${escapeHtml21(contract.description)}</p>` : ""}
|
|
13895
14161
|
</div>
|
|
13896
14162
|
<strong>${contract.pass ? "pass" : "fail"}</strong>
|
|
13897
14163
|
</div>
|
|
@@ -13902,9 +14168,9 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
13902
14168
|
<span>handoffs ${String(contract.matched.handoffs)}</span>
|
|
13903
14169
|
<span>events ${String(contract.matched.integrationEvents)}</span>
|
|
13904
14170
|
</div>
|
|
13905
|
-
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${
|
|
14171
|
+
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${escapeHtml21(issue.message)}</li>`).join("")}</ul>` : ""}
|
|
13906
14172
|
</section>`).join("");
|
|
13907
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14173
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml21(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(14,165,233,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary,.grid{display:flex;flex-wrap:wrap;gap:10px}.pill,.grid span{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}li{margin:8px 0}@media(max-width:800px){main{padding:18px}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Business Outcome Verification</p><h1>${escapeHtml21(title)}</h1><div class="summary"><span class="pill ${report.status}">${report.status}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section>${contracts || '<section class="contract"><p>No outcome contracts configured.</p></section>'}</main></body></html>`;
|
|
13908
14174
|
};
|
|
13909
14175
|
var createVoiceOutcomeContractJSONHandler = (options) => async () => runVoiceOutcomeContractSuite(options);
|
|
13910
14176
|
var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
@@ -13920,7 +14186,7 @@ var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
|
13920
14186
|
var createVoiceOutcomeContractRoutes = (options) => {
|
|
13921
14187
|
const path = options.path ?? "/api/outcome-contracts";
|
|
13922
14188
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
13923
|
-
const routes = new
|
|
14189
|
+
const routes = new Elysia19({
|
|
13924
14190
|
name: options.name ?? "absolutejs-voice-outcome-contracts"
|
|
13925
14191
|
}).get(path, createVoiceOutcomeContractJSONHandler(options));
|
|
13926
14192
|
if (htmlPath) {
|
|
@@ -13930,7 +14196,7 @@ var createVoiceOutcomeContractRoutes = (options) => {
|
|
|
13930
14196
|
};
|
|
13931
14197
|
|
|
13932
14198
|
// src/toolContract.ts
|
|
13933
|
-
import { Elysia as
|
|
14199
|
+
import { Elysia as Elysia20 } from "elysia";
|
|
13934
14200
|
|
|
13935
14201
|
// src/toolRuntime.ts
|
|
13936
14202
|
var toErrorMessage4 = (error) => error instanceof Error ? error.message : String(error);
|
|
@@ -14139,7 +14405,7 @@ var createDefaultTurn = (caseId) => ({
|
|
|
14139
14405
|
});
|
|
14140
14406
|
var defaultApi = {};
|
|
14141
14407
|
var sameJSON = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
14142
|
-
var
|
|
14408
|
+
var escapeHtml22 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14143
14409
|
var evaluateExpectation = (input) => {
|
|
14144
14410
|
const issues = [];
|
|
14145
14411
|
const expect = input.expect;
|
|
@@ -14303,7 +14569,7 @@ var runVoiceToolContractSuite = async (options) => {
|
|
|
14303
14569
|
};
|
|
14304
14570
|
var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
14305
14571
|
const title = options.title ?? "Voice Tool Contracts";
|
|
14306
|
-
const snippet =
|
|
14572
|
+
const snippet = escapeHtml22(`app.use(
|
|
14307
14573
|
createVoiceToolContractRoutes({
|
|
14308
14574
|
htmlPath: '/tool-contracts',
|
|
14309
14575
|
path: '/api/tool-contracts',
|
|
@@ -14329,19 +14595,19 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
14329
14595
|
);`);
|
|
14330
14596
|
const contracts = report.contracts.map((contract) => {
|
|
14331
14597
|
const cases = contract.cases.map((testCase) => `<tr>
|
|
14332
|
-
<td>${
|
|
14598
|
+
<td>${escapeHtml22(testCase.label ?? testCase.caseId)}</td>
|
|
14333
14599
|
<td class="${testCase.pass ? "pass" : "fail"}">${testCase.pass ? "pass" : "fail"}</td>
|
|
14334
|
-
<td>${
|
|
14600
|
+
<td>${escapeHtml22(testCase.status)}</td>
|
|
14335
14601
|
<td>${String(testCase.attempts)}</td>
|
|
14336
14602
|
<td>${String(testCase.elapsedMs)}ms</td>
|
|
14337
14603
|
<td>${testCase.timedOut ? "yes" : "no"}</td>
|
|
14338
|
-
<td>${
|
|
14604
|
+
<td>${escapeHtml22(testCase.issues.map((issue) => issue.message).join(" ") || testCase.error || "")}</td>
|
|
14339
14605
|
</tr>`).join("");
|
|
14340
14606
|
return `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
14341
14607
|
<div class="contract-header">
|
|
14342
14608
|
<div>
|
|
14343
|
-
<p class="eyebrow">${
|
|
14344
|
-
<h2>${
|
|
14609
|
+
<p class="eyebrow">${escapeHtml22(contract.toolName)}</p>
|
|
14610
|
+
<h2>${escapeHtml22(contract.label ?? contract.contractId)}</h2>
|
|
14345
14611
|
</div>
|
|
14346
14612
|
<strong class="${contract.pass ? "pass" : "fail"}">${contract.pass ? "Passing" : "Failing"}</strong>
|
|
14347
14613
|
</div>
|
|
@@ -14351,7 +14617,7 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
14351
14617
|
</table>
|
|
14352
14618
|
</section>`;
|
|
14353
14619
|
}).join("");
|
|
14354
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14620
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml22(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.primitive,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(245,158,11,.12))}.primitive{background:#151b20;border-color:#5a4421}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}h2{margin:.2rem 0 1rem}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}.primitive p{color:#d8dee6;line-height:1.55}.primitive pre{background:#0f1217;border:1px solid #2a323a;border-radius:16px;color:#fef3c7;overflow:auto;padding:14px}.primitive code{color:#fef3c7}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left;vertical-align:top}th{color:#a8b0b8;font-size:.82rem}@media(max-width:800px){main{padding:18px}table{display:block;overflow:auto}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Tool Reliability</p><h1>${escapeHtml22(title)}</h1><div class="summary"><span class="pill ${report.status === "pass" ? "pass" : "fail"}">${escapeHtml22(report.status)}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceToolContractRoutes(...)</code> certifies tool behavior</h2><p>Define deterministic tool cases for retries, idempotency, timeouts, result shape, and error handling so assistant tools fail in pre-production instead of live calls.</p><pre><code>${snippet}</code></pre></section>${contracts || '<section class="contract"><p>No tool contracts configured.</p></section>'}</main></body></html>`;
|
|
14355
14621
|
};
|
|
14356
14622
|
var createVoiceToolContractJSONHandler = (options) => () => runVoiceToolContractSuite(options);
|
|
14357
14623
|
var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
@@ -14368,7 +14634,7 @@ var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
|
14368
14634
|
var createVoiceToolContractRoutes = (options) => {
|
|
14369
14635
|
const path = options.path ?? "/api/tool-contracts";
|
|
14370
14636
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
14371
|
-
const routes = new
|
|
14637
|
+
const routes = new Elysia20({
|
|
14372
14638
|
name: options.name ?? "absolutejs-voice-tool-contracts"
|
|
14373
14639
|
}).get(path, createVoiceToolContractJSONHandler(options));
|
|
14374
14640
|
if (htmlPath) {
|
|
@@ -14378,7 +14644,7 @@ var createVoiceToolContractRoutes = (options) => {
|
|
|
14378
14644
|
};
|
|
14379
14645
|
|
|
14380
14646
|
// src/simulationSuite.ts
|
|
14381
|
-
var
|
|
14647
|
+
var escapeHtml23 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14382
14648
|
var summarizeSection = (report) => ({
|
|
14383
14649
|
failed: report.failed,
|
|
14384
14650
|
passed: report.passed,
|
|
@@ -14514,15 +14780,15 @@ var renderSection = (label, summary) => {
|
|
|
14514
14780
|
if (!summary) {
|
|
14515
14781
|
return "";
|
|
14516
14782
|
}
|
|
14517
|
-
return `<article class="${
|
|
14783
|
+
return `<article class="${escapeHtml23(summary.status)}"><span>${escapeHtml23(label)}</span><strong>${escapeHtml23(summary.status)}</strong><p>${summary.passed}/${summary.total} passed, ${summary.failed} failed.</p></article>`;
|
|
14518
14784
|
};
|
|
14519
14785
|
var renderAction = (action) => {
|
|
14520
|
-
const content = `<strong>${
|
|
14521
|
-
return action.href ? `<a class="action" href="${
|
|
14786
|
+
const content = `<strong>${escapeHtml23(action.label)}</strong><p>${escapeHtml23(action.description)}</p><span>${escapeHtml23(action.section)} / ${escapeHtml23(action.severity)}</span>`;
|
|
14787
|
+
return action.href ? `<a class="action" href="${escapeHtml23(action.href)}">${content}</a>` : `<article class="action">${content}</article>`;
|
|
14522
14788
|
};
|
|
14523
14789
|
var renderVoiceSimulationSuiteHTML = (report, options = {}) => {
|
|
14524
14790
|
const title = options.title ?? "Voice Simulation Suite";
|
|
14525
|
-
const snippet =
|
|
14791
|
+
const snippet = escapeHtml23(`app.use(
|
|
14526
14792
|
createVoiceSimulationSuiteRoutes({
|
|
14527
14793
|
htmlPath: '/voice/simulations',
|
|
14528
14794
|
path: '/api/voice/simulations',
|
|
@@ -14555,12 +14821,12 @@ app.use(
|
|
|
14555
14821
|
store: traceStore
|
|
14556
14822
|
})
|
|
14557
14823
|
);`);
|
|
14558
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14824
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml23(title)}</title><style>body{background:#10151c;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}.hero,.primitive{background:linear-gradient(135deg,rgba(34,197,94,.18),rgba(59,130,246,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.primitive{background:#151d27;border-color:#355078}.eyebrow{color:#93c5fd;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.badge{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.grid,.actions{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin:18px 0}.grid article,.action{background:#151d27;border:1px solid #283544;border-radius:18px;color:inherit;padding:16px;text-decoration:none}.grid span,.action span{color:#aab5c0}.grid strong{display:block;font-size:2rem;margin:.25rem 0;text-transform:uppercase}.action strong{display:block;color:#f8f3e7;margin-bottom:.35rem}.action p,.primitive p{color:#d8dee6;line-height:1.55;margin:.3rem 0 .6rem}pre{background:#151d27;border:1px solid #283544;border-radius:18px;overflow:auto;padding:16px}.primitive pre{background:#0b1118;color:#dbeafe}.primitive code{color:#bfdbfe}</style></head><body><main><section class="hero"><p class="eyebrow">Pre-production proof</p><h1>${escapeHtml23(title)}</h1><p>One report for session quality, scenario evals, fixture simulations, tool contracts, and outcome contracts.</p><p class="badge ${escapeHtml23(report.status)}">Status: ${escapeHtml23(report.status)}</p><section class="grid">${renderSection("Sessions", report.summary.sessions)}${renderSection("Scenarios", report.summary.scenarios)}${renderSection("Fixtures", report.summary.fixtures)}${renderSection("Tools", report.summary.tools)}${renderSection("Outcomes", report.summary.outcomes)}</section></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceSimulationSuiteRoutes(...)</code> builds this pre-production proof surface</h2><p>Run session quality checks, scenario evals, fixture-backed simulations, tool contracts, and outcome contracts from one route group before live traffic sees a regression.</p><pre><code>${snippet}</code></pre></section><h2>Actions</h2><section class="actions">${report.actions.length > 0 ? report.actions.map(renderAction).join("") : '<article class="action"><strong>No action required</strong><p>All enabled simulation sections are passing.</p></article>'}</section><pre>${escapeHtml23(JSON.stringify({ summary: report.summary, actions: report.actions }, null, 2))}</pre></main></body></html>`;
|
|
14559
14825
|
};
|
|
14560
14826
|
var createVoiceSimulationSuiteRoutes = (options) => {
|
|
14561
14827
|
const path = options.path ?? "/api/voice/simulations";
|
|
14562
14828
|
const htmlPath = options.htmlPath === undefined ? "/voice/simulations" : options.htmlPath;
|
|
14563
|
-
const app = new
|
|
14829
|
+
const app = new Elysia21({
|
|
14564
14830
|
name: options.name ?? "absolutejs-voice-simulation-suite"
|
|
14565
14831
|
}).get(path, () => runVoiceSimulationSuite(options));
|
|
14566
14832
|
if (htmlPath) {
|
|
@@ -14872,9 +15138,9 @@ var createVoiceWorkflowContractHandler = (input) => {
|
|
|
14872
15138
|
};
|
|
14873
15139
|
};
|
|
14874
15140
|
// src/sessionReplay.ts
|
|
14875
|
-
import { Elysia as
|
|
15141
|
+
import { Elysia as Elysia22 } from "elysia";
|
|
14876
15142
|
var getString9 = (value) => typeof value === "string" ? value : undefined;
|
|
14877
|
-
var
|
|
15143
|
+
var escapeHtml24 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14878
15144
|
var increment4 = (record, key) => {
|
|
14879
15145
|
record[key] = (record[key] ?? 0) + 1;
|
|
14880
15146
|
};
|
|
@@ -15054,10 +15320,10 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
15054
15320
|
var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="voice-sessions-empty">No voice sessions found.</p>' : [
|
|
15055
15321
|
'<div class="voice-sessions-list">',
|
|
15056
15322
|
...sessions.map((session) => [
|
|
15057
|
-
`<article class="voice-session-card ${
|
|
15323
|
+
`<article class="voice-session-card ${escapeHtml24(session.status)}">`,
|
|
15058
15324
|
'<div class="voice-session-card-header">',
|
|
15059
|
-
`<strong>${
|
|
15060
|
-
`<span>${
|
|
15325
|
+
`<strong>${escapeHtml24(session.sessionId)}</strong>`,
|
|
15326
|
+
`<span>${escapeHtml24(session.status)}</span>`,
|
|
15061
15327
|
"</div>",
|
|
15062
15328
|
"<dl>",
|
|
15063
15329
|
`<div><dt>Events</dt><dd>${String(session.eventCount)}</dd></div>`,
|
|
@@ -15065,9 +15331,9 @@ var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="v
|
|
|
15065
15331
|
`<div><dt>Transcripts</dt><dd>${String(session.transcriptCount)}</dd></div>`,
|
|
15066
15332
|
`<div><dt>Errors</dt><dd>${String(session.errorCount)}</dd></div>`,
|
|
15067
15333
|
"</dl>",
|
|
15068
|
-
session.latestOutcome ? `<p>Outcome: ${
|
|
15069
|
-
session.providers.length ? `<p>Providers: ${session.providers.map(
|
|
15070
|
-
session.replayHref ? `<p><a href="${
|
|
15334
|
+
session.latestOutcome ? `<p>Outcome: ${escapeHtml24(session.latestOutcome)}</p>` : "",
|
|
15335
|
+
session.providers.length ? `<p>Providers: ${session.providers.map(escapeHtml24).join(", ")}</p>` : "",
|
|
15336
|
+
session.replayHref ? `<p><a href="${escapeHtml24(session.replayHref)}">Open replay</a></p>` : "",
|
|
15071
15337
|
"</article>"
|
|
15072
15338
|
].join("")),
|
|
15073
15339
|
"</div>"
|
|
@@ -15098,7 +15364,7 @@ var createVoiceSessionsHTMLHandler = (options = {}) => async ({ query }) => {
|
|
|
15098
15364
|
var createVoiceSessionListRoutes = (options = {}) => {
|
|
15099
15365
|
const path = options.path ?? "/api/voice-sessions";
|
|
15100
15366
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
15101
|
-
const routes = new
|
|
15367
|
+
const routes = new Elysia22({
|
|
15102
15368
|
name: options.name ?? "absolutejs-voice-session-list"
|
|
15103
15369
|
}).get(path, createVoiceSessionsJSONHandler(options));
|
|
15104
15370
|
if (htmlPath) {
|
|
@@ -15126,7 +15392,7 @@ var createVoiceSessionReplayHTMLHandler = (options) => async ({ params }) => {
|
|
|
15126
15392
|
var createVoiceSessionReplayRoutes = (options) => {
|
|
15127
15393
|
const path = options.path ?? "/api/voice-sessions/:sessionId/replay";
|
|
15128
15394
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
15129
|
-
const routes = new
|
|
15395
|
+
const routes = new Elysia22({
|
|
15130
15396
|
name: options.name ?? "absolutejs-voice-session-replay"
|
|
15131
15397
|
}).get(path, createVoiceSessionReplayJSONHandler(options));
|
|
15132
15398
|
if (htmlPath) {
|
|
@@ -15355,10 +15621,10 @@ var assertVoiceAgentSquadContract = async (options) => {
|
|
|
15355
15621
|
return report;
|
|
15356
15622
|
};
|
|
15357
15623
|
// src/turnLatency.ts
|
|
15358
|
-
import { Elysia as
|
|
15624
|
+
import { Elysia as Elysia23 } from "elysia";
|
|
15359
15625
|
var DEFAULT_WARN_AFTER_MS = 1800;
|
|
15360
15626
|
var DEFAULT_FAIL_AFTER_MS = 3200;
|
|
15361
|
-
var
|
|
15627
|
+
var escapeHtml25 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15362
15628
|
var firstNumber = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
15363
15629
|
var getString10 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
15364
15630
|
var createTraceStageIndex = (events) => {
|
|
@@ -15490,11 +15756,11 @@ await traceStore.append({
|
|
|
15490
15756
|
turnId,
|
|
15491
15757
|
type: 'turn_latency.stage'
|
|
15492
15758
|
});`;
|
|
15493
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
15494
|
-
<header><div><p class="eyebrow">${
|
|
15495
|
-
<dl>${turn.stages.map((stage) => `<div><dt>${
|
|
15759
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml25(turn.status)}">
|
|
15760
|
+
<header><div><p class="eyebrow">${escapeHtml25(turn.sessionId)} \xB7 ${escapeHtml25(turn.turnId)}</p><h2>${escapeHtml25(turn.text || "Empty turn")}</h2></div><strong>${escapeHtml25(turn.status)}</strong></header>
|
|
15761
|
+
<dl>${turn.stages.map((stage) => `<div><dt>${escapeHtml25(stage.label)}</dt><dd>${escapeHtml25(formatMs(stage.valueMs))}</dd></div>`).join("")}</dl>
|
|
15496
15762
|
</article>`).join("");
|
|
15497
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
15763
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml25(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.turn,.primitive{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(94,234,212,.16),rgba(251,191,36,.1))}.eyebrow{color:#5eead4;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.primitive p{color:#cbd5e1}.primitive pre{background:#0a0d10;border:1px solid #2a323a;border-radius:16px;color:#d9fff7;overflow:auto;padding:16px}.turn header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.warn,.empty{color:#fde68a}.fail{color:#fca5a5}.turn.fail{border-color:rgba(248,113,113,.45)}dl{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{font-weight:900;margin:0}@media(max-width:800px){main{padding:18px}.turn header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">End-to-end responsiveness</p><h1>${escapeHtml25(title)}</h1><div class="summary"><span class="pill ${escapeHtml25(report.status)}">${escapeHtml25(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">avg ${escapeHtml25(formatMs(report.averageTotalMs))}</span><span class="pill">${String(report.warnings)} warnings</span><span class="pill">${String(report.failed)} failed</span></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceTurnLatencyRoutes(...)</code> exposes the full turn waterfall</h2><p>Attach stage traces for speech detection, commit, model response, TTS send, and first audio so teams can prove where latency actually comes from.</p><pre><code>${escapeHtml25(snippet)}</code></pre></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
15498
15764
|
};
|
|
15499
15765
|
var createVoiceTurnLatencyJSONHandler = (options) => async () => summarizeVoiceTurnLatency(options);
|
|
15500
15766
|
var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
@@ -15511,7 +15777,7 @@ var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
|
15511
15777
|
var createVoiceTurnLatencyRoutes = (options) => {
|
|
15512
15778
|
const path = options.path ?? "/api/turn-latency";
|
|
15513
15779
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
15514
|
-
const routes = new
|
|
15780
|
+
const routes = new Elysia23({
|
|
15515
15781
|
name: options.name ?? "absolutejs-voice-turn-latency"
|
|
15516
15782
|
}).get(path, createVoiceTurnLatencyJSONHandler(options));
|
|
15517
15783
|
if (htmlPath) {
|
|
@@ -15520,8 +15786,8 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
15520
15786
|
return routes;
|
|
15521
15787
|
};
|
|
15522
15788
|
// src/liveLatency.ts
|
|
15523
|
-
import { Elysia as
|
|
15524
|
-
var
|
|
15789
|
+
import { Elysia as Elysia24 } from "elysia";
|
|
15790
|
+
var escapeHtml26 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15525
15791
|
var percentile = (values, percentileValue) => {
|
|
15526
15792
|
if (values.length === 0) {
|
|
15527
15793
|
return;
|
|
@@ -15588,13 +15854,13 @@ await traceStore.append({
|
|
|
15588
15854
|
sessionId,
|
|
15589
15855
|
type: 'client.live_latency'
|
|
15590
15856
|
});`;
|
|
15591
|
-
const rows = report.recent.map((sample) => `<tr><td>${
|
|
15592
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
15857
|
+
const rows = report.recent.map((sample) => `<tr><td>${escapeHtml26(sample.sessionId)}</td><td>${escapeHtml26(formatMs2(sample.latencyMs))}</td><td>${escapeHtml26(sample.status ?? "unknown")}</td><td>${escapeHtml26(new Date(sample.at).toLocaleString())}</td></tr>`).join("");
|
|
15858
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml26(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{background:linear-gradient(135deg,rgba(94,234,212,.16),rgba(245,158,11,.1));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;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{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.warn,.empty{color:#fbbf24}.fail{color:#fca5a5}.metrics{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:18px 0}.metrics article,table,.primitive{background:#141922;border:1px solid #26313d;border-radius:18px}.metrics article,.primitive{padding:16px}.metrics span{color:#a8b0b8}.metrics strong{display:block;font-size:2rem;margin-top:.25rem}.primitive{margin:0 0 18px}.primitive h2{margin:.2rem 0 .5rem}.primitive p{color:#cbd5e1}.primitive pre{background:#080b10;border:1px solid #26313d;border-radius:16px;color:#d9fff7;overflow:auto;padding:16px}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #26313d;padding:12px;text-align:left}@media(max-width:760px){main{padding:20px}}</style></head><body><main><section class="hero"><p class="eyebrow">Browser proof</p><h1>${escapeHtml26(title)}</h1><p>Recent real browser speech-to-assistant response measurements from persisted <code>client.live_latency</code> traces.</p><p class="status ${escapeHtml26(report.status)}">Status: ${escapeHtml26(report.status)}</p><section class="metrics"><article><span>p50</span><strong>${escapeHtml26(formatMs2(report.p50LatencyMs))}</strong></article><article><span>p95</span><strong>${escapeHtml26(formatMs2(report.p95LatencyMs))}</strong></article><article><span>Average</span><strong>${escapeHtml26(formatMs2(report.averageLatencyMs))}</strong></article><article><span>Samples</span><strong>${String(report.total)}</strong></article></section></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceLiveLatencyRoutes(...)</code> turns real browser timing into a release gate</h2><p>Persist live timing samples into the trace store so readiness, simulations, and trace timelines all point at the same self-hosted proof.</p><pre><code>${escapeHtml26(snippet)}</code></pre></section><table><thead><tr><th>Session</th><th>Latency</th><th>Status</th><th>Measured</th></tr></thead><tbody>${rows || '<tr><td colspan="4">No live latency samples yet.</td></tr>'}</tbody></table></main></body></html>`;
|
|
15593
15859
|
};
|
|
15594
15860
|
var createVoiceLiveLatencyRoutes = (options) => {
|
|
15595
15861
|
const path = options.path ?? "/api/live-latency";
|
|
15596
15862
|
const htmlPath = options.htmlPath === undefined ? "/live-latency" : options.htmlPath;
|
|
15597
|
-
const routes = new
|
|
15863
|
+
const routes = new Elysia24({
|
|
15598
15864
|
name: options.name ?? "absolutejs-voice-live-latency"
|
|
15599
15865
|
}).get(path, () => summarizeVoiceLiveLatency(options));
|
|
15600
15866
|
if (htmlPath) {
|
|
@@ -15913,9 +16179,9 @@ None.
|
|
|
15913
16179
|
`}`;
|
|
15914
16180
|
};
|
|
15915
16181
|
// src/turnQuality.ts
|
|
15916
|
-
import { Elysia as
|
|
16182
|
+
import { Elysia as Elysia25 } from "elysia";
|
|
15917
16183
|
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
15918
|
-
var
|
|
16184
|
+
var escapeHtml27 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15919
16185
|
var getTurnLatencyMs = (turn) => {
|
|
15920
16186
|
const firstTranscriptAt = turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs).filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
15921
16187
|
if (firstTranscriptAt === undefined) {
|
|
@@ -15986,24 +16252,24 @@ var summarizeVoiceTurnQuality = async (options) => {
|
|
|
15986
16252
|
};
|
|
15987
16253
|
var renderVoiceTurnQualityHTML = (report, options = {}) => {
|
|
15988
16254
|
const title = options.title ?? "Voice Turn Quality";
|
|
15989
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
16255
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml27(turn.status)}">
|
|
15990
16256
|
<div class="turn-header">
|
|
15991
16257
|
<div>
|
|
15992
|
-
<p class="eyebrow">${
|
|
15993
|
-
<h2>${
|
|
16258
|
+
<p class="eyebrow">${escapeHtml27(turn.sessionId)} \xB7 ${escapeHtml27(turn.turnId)}</p>
|
|
16259
|
+
<h2>${escapeHtml27(turn.text || "Empty turn")}</h2>
|
|
15994
16260
|
</div>
|
|
15995
|
-
<strong>${
|
|
16261
|
+
<strong>${escapeHtml27(turn.status)}</strong>
|
|
15996
16262
|
</div>
|
|
15997
16263
|
<dl>
|
|
15998
|
-
<div><dt>Source</dt><dd>${
|
|
16264
|
+
<div><dt>Source</dt><dd>${escapeHtml27(turn.source ?? "unknown")}</dd></div>
|
|
15999
16265
|
<div><dt>Confidence</dt><dd>${turn.averageConfidence === undefined ? "n/a" : `${Math.round(turn.averageConfidence * 100)}%`}</dd></div>
|
|
16000
|
-
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${
|
|
16001
|
-
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${
|
|
16266
|
+
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${escapeHtml27(turn.fallbackSelectionReason ?? "selected")})` : "no"}</dd></div>
|
|
16267
|
+
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${escapeHtml27(turn.correctionProvider)}` : ""}` : "none"}</dd></div>
|
|
16002
16268
|
<div><dt>Transcripts</dt><dd>${String(turn.selectedTranscriptCount)} selected \xB7 ${String(turn.finalTranscriptCount)} final \xB7 ${String(turn.partialTranscriptCount)} partial</dd></div>
|
|
16003
16269
|
<div><dt>Cost</dt><dd>${turn.costUnits === undefined ? "n/a" : String(turn.costUnits)}</dd></div>
|
|
16004
16270
|
</dl>
|
|
16005
16271
|
</article>`).join("");
|
|
16006
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
16272
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml27(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.turn{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(251,191,36,.16),rgba(34,197,94,.1))}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.turn-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.warn,.unknown{color:#fde68a}.fail{color:#fca5a5}.turn.fail{border-color:rgba(248,113,113,.45)}dl{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0}@media(max-width:800px){main{padding:18px}.turn-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Realtime STT Debugging</p><h1>${escapeHtml27(title)}</h1><div class="summary"><span class="pill ${escapeHtml27(report.status)}">${escapeHtml27(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">${String(report.warnings)} warnings</span><span class="pill">${String(report.failed)} failed</span><span class="pill">${String(report.sessions)} sessions</span></div></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
16007
16273
|
};
|
|
16008
16274
|
var createVoiceTurnQualityJSONHandler = (options) => async () => summarizeVoiceTurnQuality(options);
|
|
16009
16275
|
var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
@@ -16020,7 +16286,7 @@ var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
|
16020
16286
|
var createVoiceTurnQualityRoutes = (options) => {
|
|
16021
16287
|
const path = options.path ?? "/api/turn-quality";
|
|
16022
16288
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
16023
|
-
const routes = new
|
|
16289
|
+
const routes = new Elysia25({
|
|
16024
16290
|
name: options.name ?? "absolutejs-voice-turn-quality"
|
|
16025
16291
|
}).get(path, createVoiceTurnQualityJSONHandler(options));
|
|
16026
16292
|
if (htmlPath) {
|
|
@@ -16029,7 +16295,7 @@ var createVoiceTurnQualityRoutes = (options) => {
|
|
|
16029
16295
|
return routes;
|
|
16030
16296
|
};
|
|
16031
16297
|
// src/telephonyOutcome.ts
|
|
16032
|
-
import { Elysia as
|
|
16298
|
+
import { Elysia as Elysia26 } from "elysia";
|
|
16033
16299
|
var DEFAULT_COMPLETED_STATUSES = [
|
|
16034
16300
|
"answered",
|
|
16035
16301
|
"completed",
|
|
@@ -16679,7 +16945,7 @@ var createVoiceTelephonyWebhookHandler = (options = {}) => async (input) => {
|
|
|
16679
16945
|
var createVoiceTelephonyWebhookRoutes = (options = {}) => {
|
|
16680
16946
|
const path = options.path ?? "/api/voice/telephony/webhook";
|
|
16681
16947
|
const handler = createVoiceTelephonyWebhookHandler(options);
|
|
16682
|
-
return new
|
|
16948
|
+
return new Elysia26({
|
|
16683
16949
|
name: options.name ?? "absolutejs-voice-telephony-webhooks"
|
|
16684
16950
|
}).post(path, async ({ query, request }) => {
|
|
16685
16951
|
try {
|
|
@@ -16700,11 +16966,11 @@ var createVoiceTelephonyWebhookRoutes = (options = {}) => {
|
|
|
16700
16966
|
});
|
|
16701
16967
|
};
|
|
16702
16968
|
// src/phoneAgent.ts
|
|
16703
|
-
import { Elysia as
|
|
16969
|
+
import { Elysia as Elysia32 } from "elysia";
|
|
16704
16970
|
|
|
16705
16971
|
// src/telephony/plivo.ts
|
|
16706
16972
|
import { Buffer as Buffer5 } from "buffer";
|
|
16707
|
-
import { Elysia as
|
|
16973
|
+
import { Elysia as Elysia28 } from "elysia";
|
|
16708
16974
|
|
|
16709
16975
|
// src/telephony/contract.ts
|
|
16710
16976
|
var DEFAULT_REQUIREMENTS = [
|
|
@@ -16788,7 +17054,7 @@ var evaluateVoiceTelephonyContract = (input) => {
|
|
|
16788
17054
|
|
|
16789
17055
|
// src/telephony/twilio.ts
|
|
16790
17056
|
import { Buffer as Buffer4 } from "buffer";
|
|
16791
|
-
import { Elysia as
|
|
17057
|
+
import { Elysia as Elysia27 } from "elysia";
|
|
16792
17058
|
var TWILIO_MULAW_SAMPLE_RATE = 8000;
|
|
16793
17059
|
var VOICE_PCM_SAMPLE_RATE = 16000;
|
|
16794
17060
|
var escapeXml2 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
@@ -16818,7 +17084,7 @@ var resolveTwilioStreamParameters = async (parameters, input) => {
|
|
|
16818
17084
|
return parameters;
|
|
16819
17085
|
};
|
|
16820
17086
|
var joinUrlPath2 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
16821
|
-
var
|
|
17087
|
+
var escapeHtml28 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16822
17088
|
var getWebhookVerificationUrl = (webhook, input) => {
|
|
16823
17089
|
if (!webhook?.verificationUrl) {
|
|
16824
17090
|
return;
|
|
@@ -16861,23 +17127,23 @@ var buildTwilioVoiceSetupStatus = async (options, input) => {
|
|
|
16861
17127
|
};
|
|
16862
17128
|
var renderTwilioVoiceSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16863
17129
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio setup</p>
|
|
16864
|
-
<h1>${
|
|
17130
|
+
<h1>${escapeHtml28(title)}</h1>
|
|
16865
17131
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
16866
17132
|
<section>
|
|
16867
17133
|
<h2>URLs</h2>
|
|
16868
17134
|
<ul>
|
|
16869
|
-
<li><strong>TwiML:</strong> <code>${
|
|
16870
|
-
<li><strong>Media stream:</strong> <code>${
|
|
16871
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
17135
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml28(status.urls.twiml)}</code></li>
|
|
17136
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml28(status.urls.stream)}</code></li>
|
|
17137
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml28(status.urls.webhook)}</code></li>
|
|
16872
17138
|
</ul>
|
|
16873
17139
|
</section>
|
|
16874
17140
|
<section>
|
|
16875
17141
|
<h2>Signing</h2>
|
|
16876
17142
|
<p>Mode: <code>${status.signing.mode}</code></p>
|
|
16877
|
-
${status.signing.verificationUrl ? `<p>Verification URL: <code>${
|
|
17143
|
+
${status.signing.verificationUrl ? `<p>Verification URL: <code>${escapeHtml28(status.signing.verificationUrl)}</code></p>` : ""}
|
|
16878
17144
|
</section>
|
|
16879
|
-
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
16880
|
-
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
17145
|
+
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml28(name)}</code></li>`).join("")}</ul></section>` : ""}
|
|
17146
|
+
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml28(warning)}</li>`).join("")}</ul></section>` : ""}
|
|
16881
17147
|
</main>`;
|
|
16882
17148
|
var extractTwilioStreamUrl = (twiml) => twiml.match(/<Stream\b[^>]*\surl="([^"]+)"/i)?.[1]?.replaceAll("&", "&");
|
|
16883
17149
|
var createSmokeCheck = (name, status, message, details) => ({
|
|
@@ -16888,20 +17154,20 @@ var createSmokeCheck = (name, status, message, details) => ({
|
|
|
16888
17154
|
});
|
|
16889
17155
|
var renderTwilioVoiceSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16890
17156
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio smoke test</p>
|
|
16891
|
-
<h1>${
|
|
17157
|
+
<h1>${escapeHtml28(title)}</h1>
|
|
16892
17158
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
16893
17159
|
<section>
|
|
16894
17160
|
<h2>Checks</h2>
|
|
16895
17161
|
<ul>
|
|
16896
|
-
${report.checks.map((check) => `<li><strong>${
|
|
17162
|
+
${report.checks.map((check) => `<li><strong>${escapeHtml28(check.name)}</strong>: ${escapeHtml28(check.status)}${check.message ? ` - ${escapeHtml28(check.message)}` : ""}</li>`).join("")}
|
|
16897
17163
|
</ul>
|
|
16898
17164
|
</section>
|
|
16899
17165
|
<section>
|
|
16900
17166
|
<h2>Observed URLs</h2>
|
|
16901
17167
|
<ul>
|
|
16902
|
-
<li><strong>TwiML:</strong> <code>${
|
|
16903
|
-
<li><strong>Stream:</strong> <code>${
|
|
16904
|
-
<li><strong>Webhook:</strong> <code>${
|
|
17168
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml28(report.setup.urls.twiml)}</code></li>
|
|
17169
|
+
<li><strong>Stream:</strong> <code>${escapeHtml28(report.twiml?.streamUrl ?? report.setup.urls.stream)}</code></li>
|
|
17170
|
+
<li><strong>Webhook:</strong> <code>${escapeHtml28(report.setup.urls.webhook)}</code></li>
|
|
16905
17171
|
</ul>
|
|
16906
17172
|
</section>
|
|
16907
17173
|
</main>`;
|
|
@@ -17361,7 +17627,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
17361
17627
|
const smokePath = options.smoke?.path === false ? false : options.smoke?.path ?? "/api/voice/twilio/smoke";
|
|
17362
17628
|
const bridges = new WeakMap;
|
|
17363
17629
|
const webhookPolicy = options.webhook?.policy ?? options.outcomePolicy ?? createVoiceTelephonyOutcomePolicy();
|
|
17364
|
-
const app = new
|
|
17630
|
+
const app = new Elysia27({
|
|
17365
17631
|
name: options.name ?? "absolutejs-voice-twilio"
|
|
17366
17632
|
}).get(twimlPath, async ({ query, request }) => {
|
|
17367
17633
|
const streamUrl = await resolveTwilioStreamUrl(options, {
|
|
@@ -17498,7 +17764,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
17498
17764
|
|
|
17499
17765
|
// src/telephony/plivo.ts
|
|
17500
17766
|
var escapeXml3 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17501
|
-
var
|
|
17767
|
+
var escapeHtml29 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17502
17768
|
var joinUrlPath3 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
17503
17769
|
var resolveRequestOrigin2 = (request) => {
|
|
17504
17770
|
const url = new URL(request.url);
|
|
@@ -17749,21 +18015,21 @@ var buildPlivoVoiceSetupStatus = async (options, input) => {
|
|
|
17749
18015
|
};
|
|
17750
18016
|
var renderPlivoSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
17751
18017
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo setup</p>
|
|
17752
|
-
<h1>${
|
|
18018
|
+
<h1>${escapeHtml29(title)}</h1>
|
|
17753
18019
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
17754
18020
|
<ul>
|
|
17755
|
-
<li><strong>Answer XML:</strong> <code>${
|
|
17756
|
-
<li><strong>Audio stream:</strong> <code>${
|
|
17757
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
18021
|
+
<li><strong>Answer XML:</strong> <code>${escapeHtml29(status.urls.answer)}</code></li>
|
|
18022
|
+
<li><strong>Audio stream:</strong> <code>${escapeHtml29(status.urls.stream)}</code></li>
|
|
18023
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml29(status.urls.webhook)}</code></li>
|
|
17758
18024
|
</ul>
|
|
17759
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
17760
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
18025
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml29(name)}</code></li>`).join("")}</ul>` : ""}
|
|
18026
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml29(warning)}</li>`).join("")}</ul>` : ""}
|
|
17761
18027
|
</main>`;
|
|
17762
18028
|
var renderPlivoSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
17763
18029
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo smoke test</p>
|
|
17764
|
-
<h1>${
|
|
18030
|
+
<h1>${escapeHtml29(title)}</h1>
|
|
17765
18031
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
17766
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
18032
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml29(check.name)}</strong>: ${escapeHtml29(check.status)}${check.message ? ` - ${escapeHtml29(check.message)}` : ""}</li>`).join("")}</ul>
|
|
17767
18033
|
</main>`;
|
|
17768
18034
|
var runPlivoSmokeTest = async (input) => {
|
|
17769
18035
|
const setup = await buildPlivoVoiceSetupStatus(input.options, input);
|
|
@@ -17858,7 +18124,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
17858
18124
|
request: input.request
|
|
17859
18125
|
}) : verificationUrl ?? input.request.url
|
|
17860
18126
|
}) : undefined);
|
|
17861
|
-
const app = new
|
|
18127
|
+
const app = new Elysia28({
|
|
17862
18128
|
name: options.name ?? "absolutejs-voice-plivo"
|
|
17863
18129
|
}).get(answerPath, async ({ query, request }) => {
|
|
17864
18130
|
const streamUrl = await resolvePlivoStreamUrl(options, {
|
|
@@ -17969,9 +18235,9 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
17969
18235
|
|
|
17970
18236
|
// src/telephony/telnyx.ts
|
|
17971
18237
|
import { Buffer as Buffer6 } from "buffer";
|
|
17972
|
-
import { Elysia as
|
|
18238
|
+
import { Elysia as Elysia29 } from "elysia";
|
|
17973
18239
|
var escapeXml4 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17974
|
-
var
|
|
18240
|
+
var escapeHtml30 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17975
18241
|
var joinUrlPath4 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
17976
18242
|
var resolveRequestOrigin3 = (request) => {
|
|
17977
18243
|
const url = new URL(request.url);
|
|
@@ -18172,21 +18438,21 @@ var buildTelnyxVoiceSetupStatus = async (options, input) => {
|
|
|
18172
18438
|
};
|
|
18173
18439
|
var renderTelnyxSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
18174
18440
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx setup</p>
|
|
18175
|
-
<h1>${
|
|
18441
|
+
<h1>${escapeHtml30(title)}</h1>
|
|
18176
18442
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
18177
18443
|
<ul>
|
|
18178
|
-
<li><strong>TeXML:</strong> <code>${
|
|
18179
|
-
<li><strong>Media stream:</strong> <code>${
|
|
18180
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
18444
|
+
<li><strong>TeXML:</strong> <code>${escapeHtml30(status.urls.texml)}</code></li>
|
|
18445
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml30(status.urls.stream)}</code></li>
|
|
18446
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml30(status.urls.webhook)}</code></li>
|
|
18181
18447
|
</ul>
|
|
18182
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
18183
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
18448
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml30(name)}</code></li>`).join("")}</ul>` : ""}
|
|
18449
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml30(warning)}</li>`).join("")}</ul>` : ""}
|
|
18184
18450
|
</main>`;
|
|
18185
18451
|
var renderTelnyxSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
18186
18452
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx smoke test</p>
|
|
18187
|
-
<h1>${
|
|
18453
|
+
<h1>${escapeHtml30(title)}</h1>
|
|
18188
18454
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
18189
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
18455
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml30(check.name)}</strong>: ${escapeHtml30(check.status)}${check.message ? ` - ${escapeHtml30(check.message)}` : ""}</li>`).join("")}</ul>
|
|
18190
18456
|
</main>`;
|
|
18191
18457
|
var runTelnyxSmokeTest = async (input) => {
|
|
18192
18458
|
const setup = await buildTelnyxVoiceSetupStatus(input.options, input);
|
|
@@ -18280,7 +18546,7 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
18280
18546
|
publicKey: options.webhook?.publicKey,
|
|
18281
18547
|
toleranceSeconds: options.webhook?.toleranceSeconds
|
|
18282
18548
|
}) : undefined);
|
|
18283
|
-
const app = new
|
|
18549
|
+
const app = new Elysia29({
|
|
18284
18550
|
name: options.name ?? "absolutejs-voice-telnyx"
|
|
18285
18551
|
}).get(texmlPath, async ({ query, request }) => {
|
|
18286
18552
|
const streamUrl = await resolveTelnyxStreamUrl(options, {
|
|
@@ -18390,8 +18656,8 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
18390
18656
|
};
|
|
18391
18657
|
|
|
18392
18658
|
// src/telephony/matrix.ts
|
|
18393
|
-
import { Elysia as
|
|
18394
|
-
var
|
|
18659
|
+
import { Elysia as Elysia30 } from "elysia";
|
|
18660
|
+
var escapeHtml31 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
18395
18661
|
var labelForProvider = (provider) => provider.split("-").map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
|
|
18396
18662
|
var resolveEntryStatus = (contract, setup, smoke) => {
|
|
18397
18663
|
if (!contract.pass || !setup.ready || smoke?.pass === false) {
|
|
@@ -18452,13 +18718,13 @@ var badgeStyles = {
|
|
|
18452
18718
|
};
|
|
18453
18719
|
var renderVoiceTelephonyCarrierMatrixHTML = (matrix, options = {}) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 1040px; margin: 40px auto; padding: 0 20px; color: #172033;">
|
|
18454
18720
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Carrier matrix</p>
|
|
18455
|
-
<h1 style="font-size: 34px; margin: 0 0 8px;">${
|
|
18721
|
+
<h1 style="font-size: 34px; margin: 0 0 8px;">${escapeHtml31(options.title ?? "AbsoluteJS Voice Carrier Matrix")}</h1>
|
|
18456
18722
|
<p style="color:#52606d; margin: 0 0 24px;">${matrix.summary.ready}/${matrix.summary.providers} ready, ${matrix.summary.contractsPassing}/${matrix.summary.providers} contract passing, ${matrix.summary.smokePassing}/${matrix.summary.providers} smoke passing.</p>
|
|
18457
18723
|
<section style="display:grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px;">
|
|
18458
18724
|
${matrix.entries.map((entry) => `<article style="border:1px solid #d9e2ec; border-radius:18px; padding:18px; background:#fff; box-shadow:0 18px 48px rgba(15,23,42,.08);">
|
|
18459
18725
|
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center;">
|
|
18460
|
-
<h2 style="margin:0; font-size:20px;">${
|
|
18461
|
-
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${
|
|
18726
|
+
<h2 style="margin:0; font-size:20px;">${escapeHtml31(entry.name)}</h2>
|
|
18727
|
+
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${escapeHtml31(entry.status.toUpperCase())}</span>
|
|
18462
18728
|
</div>
|
|
18463
18729
|
<dl style="display:grid; grid-template-columns: 1fr 1fr; gap:8px 12px; margin:16px 0;">
|
|
18464
18730
|
<dt style="color:#64748b;">Setup</dt><dd style="margin:0; font-weight:700;">${entry.ready ? "Ready" : "Needs attention"}</dd>
|
|
@@ -18466,15 +18732,15 @@ ${matrix.entries.map((entry) => `<article style="border:1px solid #d9e2ec; borde
|
|
|
18466
18732
|
<dt style="color:#64748b;">Smoke</dt><dd style="margin:0; font-weight:700;">${entry.smoke ? entry.smoke.pass ? "Pass" : "Fail" : "Missing"}</dd>
|
|
18467
18733
|
<dt style="color:#64748b;">Contract</dt><dd style="margin:0; font-weight:700;">${entry.contract.pass ? "Pass" : "Fail"}</dd>
|
|
18468
18734
|
</dl>
|
|
18469
|
-
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${
|
|
18470
|
-
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${
|
|
18471
|
-
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${
|
|
18735
|
+
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${escapeHtml31(entry.setup.urls.stream || "missing")}</code></p>
|
|
18736
|
+
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${escapeHtml31(entry.setup.urls.webhook || "missing")}</code></p>
|
|
18737
|
+
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${escapeHtml31(issue.severity)}: ${escapeHtml31(issue.message)}</li>`).join("")}</ul>` : '<p style="margin:12px 0 0; color:#166534;">No contract issues.</p>'}
|
|
18472
18738
|
</article>`).join("")}
|
|
18473
18739
|
</section>
|
|
18474
18740
|
</main>`;
|
|
18475
18741
|
var createVoiceTelephonyCarrierMatrixRoutes = (options) => {
|
|
18476
18742
|
const path = options.path ?? "/api/voice/telephony/carriers";
|
|
18477
|
-
return new
|
|
18743
|
+
return new Elysia30({
|
|
18478
18744
|
name: options.name ?? "absolutejs-voice-telephony-carrier-matrix"
|
|
18479
18745
|
}).get(path, async ({ query, request }) => {
|
|
18480
18746
|
const providers = await options.load({ query, request });
|
|
@@ -18496,7 +18762,7 @@ var createVoiceTelephonyCarrierMatrixRoutes = (options) => {
|
|
|
18496
18762
|
};
|
|
18497
18763
|
|
|
18498
18764
|
// src/phoneAgentProductionSmoke.ts
|
|
18499
|
-
import { Elysia as
|
|
18765
|
+
import { Elysia as Elysia31 } from "elysia";
|
|
18500
18766
|
var defaultRequirements = [
|
|
18501
18767
|
"media-started",
|
|
18502
18768
|
"transcript",
|
|
@@ -18504,7 +18770,7 @@ var defaultRequirements = [
|
|
|
18504
18770
|
"lifecycle-outcome",
|
|
18505
18771
|
"no-session-error"
|
|
18506
18772
|
];
|
|
18507
|
-
var
|
|
18773
|
+
var escapeHtml32 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
18508
18774
|
var payloadType = (event) => typeof event.payload.type === "string" ? event.payload.type : undefined;
|
|
18509
18775
|
var hasTextPayload = (event) => ["text", "assistantText", "transcript"].some((key) => {
|
|
18510
18776
|
const value = event.payload[key];
|
|
@@ -18613,10 +18879,10 @@ var resolveHandlerOptions = async (options, input) => ({
|
|
|
18613
18879
|
});
|
|
18614
18880
|
var renderVoicePhoneAgentProductionSmokeHTML = (report, options = {}) => {
|
|
18615
18881
|
const title = options.title ?? "AbsoluteJS Voice Phone Smoke Contract";
|
|
18616
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
18617
|
-
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${
|
|
18618
|
-
const requirements = report.required.map((requirement) => `<span class="pill">${
|
|
18619
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
18882
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml32(issue.requirement)}</strong>: ${escapeHtml32(issue.message)}</li>`).join("");
|
|
18883
|
+
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${escapeHtml32(outcome)}</span>`).join("");
|
|
18884
|
+
const requirements = report.required.map((requirement) => `<span class="pill">${escapeHtml32(requirement)}</span>`).join("");
|
|
18885
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml32(title)}</title><style>body{background:#0e141b;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1050px;padding:32px}.hero,.panel{background:#151d26;border:1px solid #283544;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.metric{background:#0f151d;border:1px solid #283544;border-radius:16px;padding:14px}.metric strong{display:block;font-size:1.8rem}.pill{background:#0f151d;border:1px solid #3f3f46;border-radius:999px;display:inline-flex;margin:4px;padding:7px 10px}.issues{color:#fca5a5}code{color:#fde68a}@media(max-width:720px){main{padding:18px}}</style></head><body><main><section class="hero"><p class="eyebrow">Phone agent production smoke</p><h1>${escapeHtml32(title)}</h1><p class="status ${report.pass ? "pass" : "fail"}">${report.pass ? "PASS" : "FAIL"}</p><p>Contract <code>${escapeHtml32(report.contractId)}</code>${report.provider ? ` for <code>${escapeHtml32(report.provider)}</code>` : ""}${report.sessionId ? ` on session <code>${escapeHtml32(report.sessionId)}</code>` : ""}.</p></section><section class="panel"><h2>Observed Trace Evidence</h2><div class="grid"><div class="metric"><span>Media starts</span><strong>${String(report.observed.mediaStarts)}</strong></div><div class="metric"><span>Transcripts</span><strong>${String(report.observed.transcripts)}</strong></div><div class="metric"><span>Assistant responses</span><strong>${String(report.observed.assistantResponses)}</strong></div><div class="metric"><span>Session errors</span><strong>${String(report.observed.sessionErrors)}</strong></div></div><p>${outcomes || '<span class="pill">No lifecycle outcome</span>'}</p></section><section class="panel"><h2>Requirements</h2><p>${requirements}</p>${issues ? `<ul class="issues">${issues}</ul>` : '<p class="pass">All required phone-agent smoke evidence is present.</p>'}</section></main></body></html>`;
|
|
18620
18886
|
};
|
|
18621
18887
|
var createVoicePhoneAgentProductionSmokeJSONHandler = (options) => async ({
|
|
18622
18888
|
query,
|
|
@@ -18639,7 +18905,7 @@ var createVoicePhoneAgentProductionSmokeHTMLHandler = (options) => async ({
|
|
|
18639
18905
|
var createVoicePhoneAgentProductionSmokeRoutes = (options) => {
|
|
18640
18906
|
const path = options.path ?? "/api/voice/phone/smoke-contract";
|
|
18641
18907
|
const htmlPath = options.htmlPath === undefined ? "/voice/phone/smoke-contract" : options.htmlPath;
|
|
18642
|
-
const routes = new
|
|
18908
|
+
const routes = new Elysia31({
|
|
18643
18909
|
name: options.name ?? "absolutejs-voice-phone-smoke-contract"
|
|
18644
18910
|
}).get(path, createVoicePhoneAgentProductionSmokeJSONHandler(options));
|
|
18645
18911
|
if (htmlPath) {
|
|
@@ -18682,7 +18948,7 @@ var PHONE_AGENT_LIFECYCLE_STAGES = [
|
|
|
18682
18948
|
"completed",
|
|
18683
18949
|
"failed"
|
|
18684
18950
|
];
|
|
18685
|
-
var
|
|
18951
|
+
var escapeHtml33 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
18686
18952
|
var loadRouteJson = async (input) => {
|
|
18687
18953
|
const response = await input.app.handle(new Request(new URL(input.path, input.origin).toString(), {
|
|
18688
18954
|
headers: {
|
|
@@ -18742,10 +19008,10 @@ var renderVoicePhoneAgentSetupHTML = (report) => {
|
|
|
18742
19008
|
const entry = report.matrix?.entries.find((candidate) => candidate.provider === carrier.provider && (candidate.name === carrier.name || candidate.name === (carrier.name ?? carrier.provider)));
|
|
18743
19009
|
const urls = entry?.setup.urls;
|
|
18744
19010
|
const primaryUrl = carrier.provider === "plivo" ? urls?.twiml : urls?.twiml;
|
|
18745
|
-
return `<tr><td>${
|
|
19011
|
+
return `<tr><td>${escapeHtml33(carrier.name ?? carrier.provider)}</td><td>${escapeHtml33(carrier.provider)}</td><td><code>${escapeHtml33(carrier.setupPath || "disabled")}</code></td><td><code>${escapeHtml33(carrier.smokePath || "disabled")}</code></td><td>${entry ? `<span class="${escapeHtml33(entry.status)}">${escapeHtml33(entry.status.toUpperCase())}</span>` : "unknown"}</td><td>${primaryUrl ? `<code>${escapeHtml33(primaryUrl)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.webhook ? `<code>${escapeHtml33(urls.webhook)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.stream ? `<code>${escapeHtml33(urls.stream)}</code>` : '<span class="muted">missing</span>'}</td></tr>`;
|
|
18746
19012
|
}).join("");
|
|
18747
|
-
const stageList = report.lifecycleStages.map((stage) => `<li><code>${
|
|
18748
|
-
const snippet =
|
|
19013
|
+
const stageList = report.lifecycleStages.map((stage) => `<li><code>${escapeHtml33(stage)}</code></li>`).join("");
|
|
19014
|
+
const snippet = escapeHtml33(`const phoneAgent = createVoicePhoneAgent({
|
|
18749
19015
|
carriers: [
|
|
18750
19016
|
{
|
|
18751
19017
|
provider: 'twilio',
|
|
@@ -18782,10 +19048,10 @@ app.use(
|
|
|
18782
19048
|
const urls = entry?.setup.urls;
|
|
18783
19049
|
const answerLabel = carrier.provider === "telnyx" ? "TeXML URL" : carrier.provider === "plivo" ? "Answer URL" : "TwiML URL";
|
|
18784
19050
|
const answerUrl = urls?.twiml;
|
|
18785
|
-
const issueList = entry?.issues.map((issue) => `<li>${
|
|
18786
|
-
return `<article><h3>${
|
|
19051
|
+
const issueList = entry?.issues.map((issue) => `<li>${escapeHtml33(issue.severity)}: ${escapeHtml33(issue.message)}</li>`).join("") ?? "";
|
|
19052
|
+
return `<article><h3>${escapeHtml33(carrier.name ?? carrier.provider)}</h3><ol><li>Set ${escapeHtml33(answerLabel)} to <code>${escapeHtml33(answerUrl ?? "missing")}</code>.</li><li>Set status webhook to <code>${escapeHtml33(urls?.webhook ?? "missing")}</code>.</li><li>Allow media stream URL <code>${escapeHtml33(urls?.stream ?? "missing")}</code>.</li><li>Open setup: ${carrier.setupPath ? `<a href="${escapeHtml33(carrier.setupPath)}?format=html">${escapeHtml33(carrier.setupPath)}</a>` : '<span class="muted">disabled</span>'}.</li><li>Run smoke: ${carrier.smokePath ? `<a href="${escapeHtml33(carrier.smokePath)}?format=html">${escapeHtml33(carrier.smokePath)}</a>` : '<span class="muted">disabled</span>'}.</li>${report.productionSmokePath ? `<li>Certify production smoke traces: <a href="${escapeHtml33(report.productionSmokePath.replace("/api/", "/"))}?sessionId=">${escapeHtml33(report.productionSmokePath)}</a>.</li>` : ""}</ol>${issueList ? `<ul class="issues">${issueList}</ul>` : '<p class="pass">No carrier contract issues.</p>'}</article>`;
|
|
18787
19053
|
}).join("");
|
|
18788
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
19054
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml33(report.title)}</title><style>body{background:#10151c;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.primitive{background:linear-gradient(135deg,rgba(20,184,166,.2),rgba(245,158,11,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.primitive{background:#151d27;border-color:#365a60}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.badge{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.warn{color:#fde68a}.muted{color:#aab5c0}table{background:#151d27;border:1px solid #283544;border-collapse:collapse;border-radius:18px;display:block;overflow:auto;width:100%}td,th{border-bottom:1px solid #283544;padding:12px;text-align:left;vertical-align:top}code{color:#fde68a;overflow-wrap:anywhere}.primitive p{color:#cbd5de;line-height:1.55}.primitive pre{background:#0b1118;border:1px solid #283544;border-radius:18px;color:#fef3c7;overflow:auto;padding:16px}.checklist{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));margin:18px 0}.checklist article{background:#151d27;border:1px solid #283544;border-radius:18px;padding:18px}.checklist ol{padding-left:20px}.issues{color:#fca5a5}.stages{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));padding-left:18px}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Phone agent setup</p><h1>${escapeHtml33(report.title)}</h1><p>One self-hosted entrypoint for carrier routes, setup reports, smoke checks, and normalized call lifecycle stages.</p><p class="badge ${report.ready ? "pass" : "fail"}">Ready: ${String(report.ready)}</p>${report.matrixPath ? `<p><a href="${escapeHtml33(report.matrixPath)}?format=html">Open carrier matrix</a></p>` : ""}</section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoicePhoneAgent(...)</code> builds this carrier control plane</h2><p>Mount carrier routes once, expose setup and smoke proof, then feed the same carrier matrix and phone-agent smoke reports into production readiness so carrier regressions block deploys.</p><pre><code>${snippet}</code></pre></section><h2>Carrier Setup Checklist</h2><section class="checklist">${checklist}</section><h2>Carrier URLs</h2><table><thead><tr><th>Name</th><th>Provider</th><th>Setup</th><th>Smoke</th><th>Status</th><th>Answer/TwiML/TeXML</th><th>Webhook</th><th>Stream</th></tr></thead><tbody>${carrierRows}</tbody></table><h2>Lifecycle Schema</h2><ul class="stages">${stageList}</ul></main></body></html>`;
|
|
18789
19055
|
};
|
|
18790
19056
|
var createVoicePhoneAgent = (options) => {
|
|
18791
19057
|
const carrierSummaries = options.carriers.map((carrier) => ({
|
|
@@ -18794,7 +19060,7 @@ var createVoicePhoneAgent = (options) => {
|
|
|
18794
19060
|
setupPath: resolveSetupPath(carrier),
|
|
18795
19061
|
smokePath: resolveSmokePath(carrier)
|
|
18796
19062
|
}));
|
|
18797
|
-
const app = new
|
|
19063
|
+
const app = new Elysia32({
|
|
18798
19064
|
name: options.name ?? "absolutejs-voice-phone-agent"
|
|
18799
19065
|
});
|
|
18800
19066
|
for (const carrier of options.carriers) {
|
|
@@ -20848,8 +21114,8 @@ var createOpenAIVoiceTTS = (options) => {
|
|
|
20848
21114
|
};
|
|
20849
21115
|
};
|
|
20850
21116
|
// src/providerCapabilities.ts
|
|
20851
|
-
import { Elysia as
|
|
20852
|
-
var
|
|
21117
|
+
import { Elysia as Elysia33 } from "elysia";
|
|
21118
|
+
var escapeHtml34 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
20853
21119
|
var fromProviderList = (kind, providers, options) => (providers ?? []).map((provider) => ({
|
|
20854
21120
|
configured: true,
|
|
20855
21121
|
features: options.features?.[provider],
|
|
@@ -20912,27 +21178,27 @@ var summarizeVoiceProviderCapabilities = async (options) => {
|
|
|
20912
21178
|
var renderVoiceProviderCapabilityHTML = (report, options = {}) => {
|
|
20913
21179
|
const title = options.title ?? "Voice Provider Capabilities";
|
|
20914
21180
|
const cards = report.capabilities.map((capability) => {
|
|
20915
|
-
const features = (capability.features ?? []).map((feature) => `<span class="pill">${
|
|
20916
|
-
return `<article class="card ${
|
|
21181
|
+
const features = (capability.features ?? []).map((feature) => `<span class="pill">${escapeHtml34(feature)}</span>`).join("");
|
|
21182
|
+
return `<article class="card ${escapeHtml34(capability.status)}">
|
|
20917
21183
|
<div class="card-header">
|
|
20918
21184
|
<div>
|
|
20919
|
-
<p class="eyebrow">${
|
|
20920
|
-
<h2>${
|
|
21185
|
+
<p class="eyebrow">${escapeHtml34(capability.kind)}</p>
|
|
21186
|
+
<h2>${escapeHtml34(capability.label ?? capability.provider)}</h2>
|
|
20921
21187
|
</div>
|
|
20922
|
-
<strong>${
|
|
21188
|
+
<strong>${escapeHtml34(capability.status)}</strong>
|
|
20923
21189
|
</div>
|
|
20924
|
-
${capability.description ? `<p>${
|
|
21190
|
+
${capability.description ? `<p>${escapeHtml34(capability.description)}</p>` : ""}
|
|
20925
21191
|
<dl>
|
|
20926
21192
|
<div><dt>Configured</dt><dd>${capability.configured ? "yes" : "no"}</dd></div>
|
|
20927
21193
|
<div><dt>Selected</dt><dd>${capability.selected ? "yes" : "no"}</dd></div>
|
|
20928
|
-
<div><dt>Model</dt><dd>${
|
|
21194
|
+
<div><dt>Model</dt><dd>${escapeHtml34(capability.model ?? "default")}</dd></div>
|
|
20929
21195
|
<div><dt>Runs</dt><dd>${String(capability.health?.runCount ?? 0)}</dd></div>
|
|
20930
21196
|
<div><dt>Errors</dt><dd>${String(capability.health?.errorCount ?? 0)}</dd></div>
|
|
20931
21197
|
</dl>
|
|
20932
21198
|
${features ? `<div class="features">${features}</div>` : ""}
|
|
20933
21199
|
</article>`;
|
|
20934
21200
|
}).join("");
|
|
20935
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21201
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml34(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.card{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(14,165,233,.16),rgba(34,197,94,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary,.features{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.card-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.selected,.healthy{color:#86efac}.unconfigured,.degraded,.rate-limited,.suppressed{color:#fca5a5}.idle,.recoverable{color:#fde68a}dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0}@media(max-width:800px){main{padding:18px}.card-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider Discovery</p><h1>${escapeHtml34(title)}</h1><div class="summary"><span class="pill">${String(report.configured)} configured</span><span class="pill">${String(report.selected)} selected</span><span class="pill">${String(report.unconfigured)} missing</span><span class="pill">${String(report.total)} total</span></div></section><section class="grid">${cards || '<article class="card"><p>No provider capabilities configured.</p></article>'}</section></main></body></html>`;
|
|
20936
21202
|
};
|
|
20937
21203
|
var createVoiceProviderCapabilityJSONHandler = (options) => async () => summarizeVoiceProviderCapabilities(options);
|
|
20938
21204
|
var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
@@ -20949,7 +21215,7 @@ var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
|
20949
21215
|
var createVoiceProviderCapabilityRoutes = (options) => {
|
|
20950
21216
|
const path = options.path ?? "/api/provider-capabilities";
|
|
20951
21217
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
20952
|
-
const routes = new
|
|
21218
|
+
const routes = new Elysia33({
|
|
20953
21219
|
name: options.name ?? "absolutejs-voice-provider-capabilities"
|
|
20954
21220
|
}).get(path, createVoiceProviderCapabilityJSONHandler(options));
|
|
20955
21221
|
if (htmlPath) {
|
|
@@ -20958,8 +21224,8 @@ var createVoiceProviderCapabilityRoutes = (options) => {
|
|
|
20958
21224
|
return routes;
|
|
20959
21225
|
};
|
|
20960
21226
|
// src/resilienceRoutes.ts
|
|
20961
|
-
import { Elysia as
|
|
20962
|
-
var
|
|
21227
|
+
import { Elysia as Elysia34 } from "elysia";
|
|
21228
|
+
var escapeHtml35 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
20963
21229
|
var getString12 = (value) => typeof value === "string" ? value : undefined;
|
|
20964
21230
|
var getNumber7 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
20965
21231
|
var getBoolean2 = (value) => value === true;
|
|
@@ -21106,13 +21372,13 @@ var summarizeRoutingEvents = (events) => {
|
|
|
21106
21372
|
};
|
|
21107
21373
|
var renderProviderCards = (title, providers) => {
|
|
21108
21374
|
if (providers.length === 0) {
|
|
21109
|
-
return `<p class="muted">No ${
|
|
21375
|
+
return `<p class="muted">No ${escapeHtml35(title)} provider health yet.</p>`;
|
|
21110
21376
|
}
|
|
21111
21377
|
return `<div class="provider-grid">${providers.map((provider) => `
|
|
21112
|
-
<article class="card provider ${
|
|
21378
|
+
<article class="card provider ${escapeHtml35(provider.status)}">
|
|
21113
21379
|
<div class="card-header">
|
|
21114
|
-
<strong>${
|
|
21115
|
-
<span>${
|
|
21380
|
+
<strong>${escapeHtml35(provider.provider)}</strong>
|
|
21381
|
+
<span>${escapeHtml35(provider.status)}${provider.recommended ? " \xB7 recommended" : ""}</span>
|
|
21116
21382
|
</div>
|
|
21117
21383
|
<dl>
|
|
21118
21384
|
<div><dt>Runs</dt><dd>${provider.runCount}</dd></div>
|
|
@@ -21121,7 +21387,7 @@ var renderProviderCards = (title, providers) => {
|
|
|
21121
21387
|
<div><dt>Timeouts</dt><dd>${provider.timeoutCount}</dd></div>
|
|
21122
21388
|
<div><dt>Fallbacks</dt><dd>${provider.fallbackCount}</dd></div>
|
|
21123
21389
|
</dl>
|
|
21124
|
-
${provider.lastError ? `<p class="muted">${
|
|
21390
|
+
${provider.lastError ? `<p class="muted">${escapeHtml35(provider.lastError)}</p>` : ""}
|
|
21125
21391
|
</article>
|
|
21126
21392
|
`).join("")}</div>`;
|
|
21127
21393
|
};
|
|
@@ -21130,24 +21396,24 @@ var renderTimeline2 = (events) => {
|
|
|
21130
21396
|
return '<p class="muted">No provider routing events yet. Run the app or simulate provider failover.</p>';
|
|
21131
21397
|
}
|
|
21132
21398
|
return `<div class="timeline">${events.slice(0, 40).map((event) => `
|
|
21133
|
-
<article class="card event ${
|
|
21399
|
+
<article class="card event ${escapeHtml35(event.status ?? "unknown")}">
|
|
21134
21400
|
<div class="card-header">
|
|
21135
|
-
<strong>${
|
|
21401
|
+
<strong>${escapeHtml35(event.kind.toUpperCase())} ${escapeHtml35(event.operation ?? "generate")}</strong>
|
|
21136
21402
|
<span>${new Date(event.at).toLocaleString()}</span>
|
|
21137
21403
|
</div>
|
|
21138
21404
|
<p>
|
|
21139
|
-
<span class="pill">${
|
|
21140
|
-
<span class="pill">provider: ${
|
|
21141
|
-
${event.fallbackProvider ? `<span class="pill">fallback: ${
|
|
21405
|
+
<span class="pill">${escapeHtml35(event.status ?? "unknown")}</span>
|
|
21406
|
+
<span class="pill">provider: ${escapeHtml35(event.provider ?? "unknown")}</span>
|
|
21407
|
+
${event.fallbackProvider ? `<span class="pill">fallback: ${escapeHtml35(event.fallbackProvider)}</span>` : ""}
|
|
21142
21408
|
${event.timedOut ? '<span class="pill danger">timed out</span>' : ""}
|
|
21143
21409
|
</p>
|
|
21144
21410
|
<dl>
|
|
21145
21411
|
<div><dt>Attempt</dt><dd>${event.attempt ?? 0}</dd></div>
|
|
21146
21412
|
<div><dt>Elapsed</dt><dd>${event.elapsedMs ?? 0}ms</dd></div>
|
|
21147
21413
|
<div><dt>Budget</dt><dd>${event.latencyBudgetMs ?? 0}ms</dd></div>
|
|
21148
|
-
<div><dt>Session</dt><dd>${
|
|
21414
|
+
<div><dt>Session</dt><dd>${escapeHtml35(event.sessionId)}</dd></div>
|
|
21149
21415
|
</dl>
|
|
21150
|
-
${event.error ? `<p class="muted">${
|
|
21416
|
+
${event.error ? `<p class="muted">${escapeHtml35(event.error)}</p>` : ""}
|
|
21151
21417
|
</article>
|
|
21152
21418
|
`).join("")}</div>`;
|
|
21153
21419
|
};
|
|
@@ -21157,9 +21423,9 @@ var renderSessionKind = (kind, summary) => {
|
|
|
21157
21423
|
const status = latest?.status ?? "idle";
|
|
21158
21424
|
const fallback = latest?.fallbackProvider && latest.fallbackProvider !== provider ? ` -> ${latest.fallbackProvider}` : "";
|
|
21159
21425
|
return `<div>
|
|
21160
|
-
<dt>${
|
|
21161
|
-
<dd>${
|
|
21162
|
-
<small>${
|
|
21426
|
+
<dt>${escapeHtml35(kind.toUpperCase())}</dt>
|
|
21427
|
+
<dd>${escapeHtml35(provider)}${escapeHtml35(fallback)}</dd>
|
|
21428
|
+
<small>${escapeHtml35(status)} \xB7 ${summary.runCount} event${summary.runCount === 1 ? "" : "s"} \xB7 ${summary.errorCount} error${summary.errorCount === 1 ? "" : "s"} \xB7 ${summary.fallbackCount} fallback${summary.fallbackCount === 1 ? "" : "s"}</small>
|
|
21163
21429
|
</div>`;
|
|
21164
21430
|
};
|
|
21165
21431
|
var renderSessionSummaries = (sessions) => {
|
|
@@ -21167,10 +21433,10 @@ var renderSessionSummaries = (sessions) => {
|
|
|
21167
21433
|
return '<p class="muted">No call-level routing summaries yet. Run a voice session or provider simulation.</p>';
|
|
21168
21434
|
}
|
|
21169
21435
|
return `<div class="session-grid">${sessions.slice(0, 12).map((session) => `
|
|
21170
|
-
<article class="card session ${
|
|
21436
|
+
<article class="card session ${escapeHtml35(session.status)}">
|
|
21171
21437
|
<div class="card-header">
|
|
21172
|
-
<strong>${
|
|
21173
|
-
<span>${
|
|
21438
|
+
<strong>${escapeHtml35(session.sessionId)}</strong>
|
|
21439
|
+
<span>${escapeHtml35(session.status)}</span>
|
|
21174
21440
|
</div>
|
|
21175
21441
|
<p>
|
|
21176
21442
|
<span class="pill">${session.eventCount} routing events</span>
|
|
@@ -21197,21 +21463,21 @@ var renderSimulationControls = (kind, simulation) => {
|
|
|
21197
21463
|
const pathPrefix = simulation.pathPrefix ?? `/api/${kind}-simulate`;
|
|
21198
21464
|
const failureProviders = simulation.failureProviders ?? configuredProviders.map(({ provider }) => provider);
|
|
21199
21465
|
const canFail = (provider) => configuredProviders.some((entry) => entry.provider === provider) && (!simulation.fallbackRequiredProvider || configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider));
|
|
21200
|
-
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${
|
|
21201
|
-
<p class="muted">${
|
|
21466
|
+
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${escapeHtml35(pathPrefix)}">
|
|
21467
|
+
<p class="muted">${escapeHtml35(simulation.failureMessage ?? `Simulate ${kind.toUpperCase()} provider failure without changing provider credentials.`)}</p>
|
|
21202
21468
|
<div class="simulate-actions">
|
|
21203
|
-
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${
|
|
21204
|
-
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${
|
|
21469
|
+
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${escapeHtml35(provider)}"${canFail(provider) ? "" : " disabled"}>Simulate ${escapeHtml35(provider)} ${kind.toUpperCase()} failure</button>`).join("")}
|
|
21470
|
+
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${escapeHtml35(provider.provider)}">Mark ${escapeHtml35(provider.provider)} recovered</button>`).join("")}
|
|
21205
21471
|
</div>
|
|
21206
|
-
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${
|
|
21472
|
+
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${escapeHtml35(simulation.fallbackRequiredMessage ?? `Configure ${simulation.fallbackRequiredProvider} to enable fallback simulation.`)}</p>` : ""}
|
|
21207
21473
|
<pre class="simulate-output" hidden></pre>
|
|
21208
21474
|
</div>`;
|
|
21209
21475
|
};
|
|
21210
21476
|
var renderVoiceResilienceHTML = (input) => {
|
|
21211
21477
|
const summary = summarizeRoutingEvents(input.routingEvents);
|
|
21212
|
-
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${
|
|
21213
|
-
const links = input.links?.length ? input.links.map((link) => `<a href="${
|
|
21214
|
-
const snippet =
|
|
21478
|
+
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${escapeHtml35(kind)}: ${String(count)}</span>`).join("");
|
|
21479
|
+
const links = input.links?.length ? input.links.map((link) => `<a href="${escapeHtml35(link.href)}">${escapeHtml35(link.label)}</a>`).join(" \xB7 ") : "";
|
|
21480
|
+
const snippet = escapeHtml35(`const sttSimulator = createVoiceIOProviderFailureSimulator({
|
|
21215
21481
|
kind: 'stt',
|
|
21216
21482
|
providers: ['deepgram', 'assemblyai'],
|
|
21217
21483
|
fallback: ['deepgram', 'assemblyai'],
|
|
@@ -21249,7 +21515,7 @@ app.use(
|
|
|
21249
21515
|
<head>
|
|
21250
21516
|
<meta charset="utf-8" />
|
|
21251
21517
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
21252
|
-
<title>${
|
|
21518
|
+
<title>${escapeHtml35(input.title ?? "AbsoluteJS Voice Resilience")}</title>
|
|
21253
21519
|
<style>
|
|
21254
21520
|
:root { color-scheme: dark; }
|
|
21255
21521
|
body { background: radial-gradient(circle at top left, #172554, #09090b 36%, #050505); color: #f4f4f5; font-family: ui-sans-serif, system-ui, sans-serif; margin: 0; padding: 24px; }
|
|
@@ -21401,7 +21667,7 @@ var registerSimulationRoutes = (routes, simulation, defaultPathPrefix) => {
|
|
|
21401
21667
|
};
|
|
21402
21668
|
var createVoiceResilienceRoutes = (options) => {
|
|
21403
21669
|
const path = options.path ?? "/resilience";
|
|
21404
|
-
const routes = new
|
|
21670
|
+
const routes = new Elysia34({
|
|
21405
21671
|
name: options.name ?? "absolutejs-voice-resilience"
|
|
21406
21672
|
}).get(path, async () => {
|
|
21407
21673
|
const events = await options.store.list();
|
|
@@ -21479,8 +21745,8 @@ var assertVoiceProviderRoutingContract = async (options) => {
|
|
|
21479
21745
|
return report;
|
|
21480
21746
|
};
|
|
21481
21747
|
// src/productionReadiness.ts
|
|
21482
|
-
import { Elysia as
|
|
21483
|
-
var
|
|
21748
|
+
import { Elysia as Elysia35 } from "elysia";
|
|
21749
|
+
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21484
21750
|
var rollupStatus2 = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
21485
21751
|
var readinessGateCodes = {
|
|
21486
21752
|
"Agent squad contracts": "voice.readiness.agent_squad_contracts",
|
|
@@ -22431,22 +22697,22 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22431
22697
|
var buildVoiceProductionReadinessGate = async (options, input = {}) => summarizeVoiceProductionReadinessGate(await buildVoiceProductionReadinessReport(options, input), options.gate || undefined);
|
|
22432
22698
|
var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
22433
22699
|
const title = options.title ?? "AbsoluteJS Voice Production Readiness";
|
|
22434
|
-
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${
|
|
22700
|
+
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${escapeHtml36(report.profile.name)}</h2><p>${escapeHtml36(report.profile.description)}</p><p>${escapeHtml36(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="${escapeHtml36(surface.href)}">${escapeHtml36(surface.label)}</a>` : escapeHtml36(surface.label)}</strong></article>`).join("")}</div></section>` : "";
|
|
22435
22701
|
const checks = report.checks.map((check, index) => {
|
|
22436
|
-
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${
|
|
22437
|
-
return `<article class="check ${
|
|
22702
|
+
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml36(action.href)}">${escapeHtml36(action.label)}</button>` : `<a href="${escapeHtml36(action.href)}">${escapeHtml36(action.label)}</a>`).join("");
|
|
22703
|
+
return `<article class="check ${escapeHtml36(check.status)}">
|
|
22438
22704
|
<div>
|
|
22439
|
-
<span>${
|
|
22440
|
-
<h2>${
|
|
22441
|
-
${check.detail ? `<p>${
|
|
22442
|
-
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${
|
|
22705
|
+
<span>${escapeHtml36(check.status.toUpperCase())}</span>
|
|
22706
|
+
<h2>${escapeHtml36(check.label)}</h2>
|
|
22707
|
+
${check.detail ? `<p>${escapeHtml36(check.detail)}</p>` : ""}
|
|
22708
|
+
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml36(check.proofSource.href)}">${escapeHtml36(check.proofSource.sourceLabel)}</a>` : escapeHtml36(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml36(check.proofSource.detail)}` : ""}</p>` : ""}
|
|
22443
22709
|
${actions ? `<p class="actions">${actions}</p>` : ""}
|
|
22444
22710
|
</div>
|
|
22445
|
-
<strong>${
|
|
22446
|
-
${check.href ? `<a href="${
|
|
22711
|
+
<strong>${escapeHtml36(String(check.value ?? check.status))}</strong>
|
|
22712
|
+
${check.href ? `<a href="${escapeHtml36(check.href)}">Open surface</a>` : ""}
|
|
22447
22713
|
</article>`;
|
|
22448
22714
|
}).join("");
|
|
22449
|
-
const snippet =
|
|
22715
|
+
const snippet = escapeHtml36(`createVoiceProductionReadinessRoutes({
|
|
22450
22716
|
htmlPath: '/production-readiness',
|
|
22451
22717
|
path: '/api/production-readiness',
|
|
22452
22718
|
gatePath: '/api/production-readiness/gate',
|
|
@@ -22462,13 +22728,13 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
|
22462
22728
|
providerRoutingContracts: loadProviderRoutingContracts,
|
|
22463
22729
|
store: traceStore
|
|
22464
22730
|
});`);
|
|
22465
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
22731
|
+
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{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>${escapeHtml36(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 ${escapeHtml36(report.status)}">Overall: ${escapeHtml36(report.status.toUpperCase())}</p><p>Checked ${escapeHtml36(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>`;
|
|
22466
22732
|
};
|
|
22467
22733
|
var createVoiceProductionReadinessRoutes = (options) => {
|
|
22468
22734
|
const path = options.path ?? "/api/production-readiness";
|
|
22469
22735
|
const gatePath = options.gatePath === undefined ? "/api/production-readiness/gate" : options.gatePath;
|
|
22470
22736
|
const htmlPath = options.htmlPath ?? "/production-readiness";
|
|
22471
|
-
const routes = new
|
|
22737
|
+
const routes = new Elysia35({
|
|
22472
22738
|
name: options.name ?? "absolutejs-voice-production-readiness"
|
|
22473
22739
|
});
|
|
22474
22740
|
routes.get(path, async ({ query, request }) => buildVoiceProductionReadinessReport(options, { query, request }));
|
|
@@ -22832,8 +23098,8 @@ var recommendVoiceReadinessProfile = (options) => {
|
|
|
22832
23098
|
};
|
|
22833
23099
|
};
|
|
22834
23100
|
// src/providerStackRecommendations.ts
|
|
22835
|
-
import { Elysia as
|
|
22836
|
-
var
|
|
23101
|
+
import { Elysia as Elysia36 } from "elysia";
|
|
23102
|
+
var escapeHtml37 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
22837
23103
|
var profileProviderPriorities = {
|
|
22838
23104
|
"meeting-recorder": {
|
|
22839
23105
|
llm: ["openai", "anthropic", "gemini"],
|
|
@@ -23076,17 +23342,17 @@ var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "fu
|
|
|
23076
23342
|
var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
23077
23343
|
const title = options.title ?? "Voice Provider Contract Matrix";
|
|
23078
23344
|
const rows = report.rows.map((row) => {
|
|
23079
|
-
const checks = row.checks.map((check) => `<li class="${
|
|
23080
|
-
return `<article class="row ${
|
|
23345
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml37(check.status)}"><strong>${escapeHtml37(check.label)}</strong><span>${escapeHtml37(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml37(check.remediation.href)}">${escapeHtml37(check.remediation.label)}</a>` : escapeHtml37(check.remediation.label)}: ${escapeHtml37(check.remediation.detail)}</em>` : ""}</li>`).join("");
|
|
23346
|
+
return `<article class="row ${escapeHtml37(row.status)}">
|
|
23081
23347
|
<div>
|
|
23082
|
-
<p class="eyebrow">${
|
|
23083
|
-
<h2>${
|
|
23084
|
-
<p class="status ${
|
|
23348
|
+
<p class="eyebrow">${escapeHtml37(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
|
|
23349
|
+
<h2>${escapeHtml37(row.provider)}</h2>
|
|
23350
|
+
<p class="status ${escapeHtml37(row.status)}">${escapeHtml37(row.status.toUpperCase())}</p>
|
|
23085
23351
|
</div>
|
|
23086
23352
|
<ul>${checks}</ul>
|
|
23087
23353
|
</article>`;
|
|
23088
23354
|
}).join("");
|
|
23089
|
-
const snippet =
|
|
23355
|
+
const snippet = escapeHtml37(`const providerContracts = () =>
|
|
23090
23356
|
createVoiceProviderContractMatrixPreset('phone-agent', {
|
|
23091
23357
|
env: process.env,
|
|
23092
23358
|
providers: {
|
|
@@ -23107,7 +23373,7 @@ createVoiceProductionReadinessRoutes({
|
|
|
23107
23373
|
providerContractMatrix: () =>
|
|
23108
23374
|
buildVoiceProviderContractMatrix(providerContracts())
|
|
23109
23375
|
});`);
|
|
23110
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23376
|
+
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:#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>${escapeHtml37(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>`;
|
|
23111
23377
|
};
|
|
23112
23378
|
var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
|
|
23113
23379
|
var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
@@ -23122,7 +23388,7 @@ var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
|
23122
23388
|
var createVoiceProviderContractMatrixRoutes = (options) => {
|
|
23123
23389
|
const path = options.path ?? "/api/provider-contracts";
|
|
23124
23390
|
const htmlPath = options.htmlPath ?? "/provider-contracts";
|
|
23125
|
-
const routes = new
|
|
23391
|
+
const routes = new Elysia36({
|
|
23126
23392
|
name: options.name ?? "absolutejs-voice-provider-contract-matrix"
|
|
23127
23393
|
});
|
|
23128
23394
|
const jsonHandler = createVoiceProviderContractMatrixJSONHandler(options.matrix);
|
|
@@ -23181,7 +23447,7 @@ var evaluateVoiceProviderStackGaps = (input) => {
|
|
|
23181
23447
|
};
|
|
23182
23448
|
};
|
|
23183
23449
|
// src/opsConsoleRoutes.ts
|
|
23184
|
-
import { Elysia as
|
|
23450
|
+
import { Elysia as Elysia37 } from "elysia";
|
|
23185
23451
|
var DEFAULT_LINKS = [
|
|
23186
23452
|
{
|
|
23187
23453
|
description: "Quality gates for CI, deploy checks, and production readiness.",
|
|
@@ -23216,7 +23482,7 @@ var DEFAULT_LINKS = [
|
|
|
23216
23482
|
label: "Handoffs"
|
|
23217
23483
|
}
|
|
23218
23484
|
];
|
|
23219
|
-
var
|
|
23485
|
+
var escapeHtml38 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23220
23486
|
var countProviderStatuses = (providers) => {
|
|
23221
23487
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
23222
23488
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -23285,20 +23551,20 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
23285
23551
|
trace
|
|
23286
23552
|
};
|
|
23287
23553
|
};
|
|
23288
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
23554
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml38(input.label)}</span><strong>${escapeHtml38(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml38(input.status)}">${escapeHtml38(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml38(input.href)}">Open</a>` : ""}</article>`;
|
|
23289
23555
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
23290
23556
|
const links = report.links.map((link) => `<article class="surface">
|
|
23291
|
-
<div><h2>${
|
|
23292
|
-
<p><a href="${
|
|
23557
|
+
<div><h2>${escapeHtml38(link.label)}</h2>${link.description ? `<p>${escapeHtml38(link.description)}</p>` : ""}</div>
|
|
23558
|
+
<p><a href="${escapeHtml38(link.href)}">Open ${escapeHtml38(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml38(link.statusHref)}">Status</a>` : ""}</p>
|
|
23293
23559
|
</article>`).join("");
|
|
23294
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
23295
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
23560
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml38(session.sessionId)}</td><td>${escapeHtml38(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml38(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
23561
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml38(event.kind)}</td><td>${escapeHtml38(event.provider ?? "unknown")}</td><td>${escapeHtml38(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml38(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
23296
23562
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
23297
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23563
|
+
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{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>${escapeHtml38(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 ${escapeHtml38(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>`;
|
|
23298
23564
|
};
|
|
23299
23565
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
23300
23566
|
const path = options.path ?? "/ops-console";
|
|
23301
|
-
const routes = new
|
|
23567
|
+
const routes = new Elysia37({
|
|
23302
23568
|
name: options.name ?? "absolutejs-voice-ops-console"
|
|
23303
23569
|
});
|
|
23304
23570
|
const getReport = () => buildVoiceOpsConsoleReport(options);
|
|
@@ -23315,11 +23581,11 @@ var createVoiceOpsConsoleRoutes = (options) => {
|
|
|
23315
23581
|
return routes;
|
|
23316
23582
|
};
|
|
23317
23583
|
// src/operationsRecord.ts
|
|
23318
|
-
import { Elysia as
|
|
23584
|
+
import { Elysia as Elysia39 } from "elysia";
|
|
23319
23585
|
|
|
23320
23586
|
// src/traceTimeline.ts
|
|
23321
|
-
import { Elysia as
|
|
23322
|
-
var
|
|
23587
|
+
import { Elysia as Elysia38 } from "elysia";
|
|
23588
|
+
var escapeHtml39 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23323
23589
|
var getString14 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
23324
23590
|
var getNumber9 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
23325
23591
|
var firstString3 = (payload, keys) => {
|
|
@@ -23487,16 +23753,16 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
23487
23753
|
};
|
|
23488
23754
|
};
|
|
23489
23755
|
var formatMs3 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
23490
|
-
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>${
|
|
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>${escapeHtml39(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>`;
|
|
23491
23757
|
var renderVoiceTraceTimelineSessionHTML = (session, options = {}) => {
|
|
23492
|
-
const events = session.events.map((event) => `<tr class="${
|
|
23493
|
-
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${
|
|
23494
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23758
|
+
const events = session.events.map((event) => `<tr class="${escapeHtml39(event.status ?? "")}"><td>+${String(event.offsetMs)}ms</td><td>${escapeHtml39(event.type)}</td><td>${escapeHtml39(event.label)}</td><td>${escapeHtml39(event.provider ?? "")}</td><td>${escapeHtml39(event.status ?? "")}</td><td>${formatMs3(event.elapsedMs)}</td></tr>`).join("");
|
|
23759
|
+
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${escapeHtml39(issue.severity)}">${escapeHtml39(issue.code)}: ${escapeHtml39(issue.message)}</li>`).join("") : "<li>none</li>";
|
|
23760
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml39(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>${escapeHtml39(session.sessionId)}</h1><p class="status ${escapeHtml39(session.status)}">${escapeHtml39(session.status)}</p></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>`;
|
|
23495
23761
|
};
|
|
23496
|
-
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${
|
|
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="${escapeHtml39(session.status)}"><td><a href="/traces/${encodeURIComponent(session.sessionId)}">${escapeHtml39(session.sessionId)}</a></td><td>${escapeHtml39(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) => escapeHtml39(provider.provider)).join(", ")}</td></tr>`).join("");
|
|
23497
23763
|
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}}";
|
|
23498
23764
|
var renderVoiceTraceTimelineHTML = (report, options = {}) => {
|
|
23499
|
-
const snippet =
|
|
23765
|
+
const snippet = escapeHtml39(`const traceStore = createVoiceTraceSinkStore({
|
|
23500
23766
|
store: runtimeStorage.traces,
|
|
23501
23767
|
sinks: [
|
|
23502
23768
|
createVoiceTraceHTTPSink({
|
|
@@ -23522,13 +23788,13 @@ app.use(
|
|
|
23522
23788
|
traceDeliveries: runtimeStorage.traceDeliveries
|
|
23523
23789
|
})
|
|
23524
23790
|
);`);
|
|
23525
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23791
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml39(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>${escapeHtml39(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>`;
|
|
23526
23792
|
};
|
|
23527
23793
|
var createVoiceTraceTimelineRoutes = (options) => {
|
|
23528
23794
|
const path = options.path ?? "/api/voice-traces";
|
|
23529
23795
|
const htmlPath = options.htmlPath ?? "/traces";
|
|
23530
23796
|
const title = options.title ?? "AbsoluteJS Voice Trace Timelines";
|
|
23531
|
-
const routes = new
|
|
23797
|
+
const routes = new Elysia38({
|
|
23532
23798
|
name: options.name ?? "absolutejs-voice-trace-timelines"
|
|
23533
23799
|
});
|
|
23534
23800
|
const buildReport = async () => summarizeVoiceTraceTimeline(await options.store.list(), {
|
|
@@ -23682,16 +23948,16 @@ var buildVoiceOperationsRecord = async (options) => {
|
|
|
23682
23948
|
traceEvents
|
|
23683
23949
|
};
|
|
23684
23950
|
};
|
|
23685
|
-
var
|
|
23951
|
+
var escapeHtml40 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23686
23952
|
var formatMs4 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
23687
23953
|
var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
23688
|
-
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${
|
|
23689
|
-
const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${
|
|
23690
|
-
const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${
|
|
23691
|
-
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${
|
|
23692
|
-
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${
|
|
23693
|
-
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${
|
|
23694
|
-
const snippet =
|
|
23954
|
+
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${escapeHtml40(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>';
|
|
23955
|
+
const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${escapeHtml40(handoff.fromAgentId ?? "unknown")}</strong> to <strong>${escapeHtml40(handoff.targetAgentId ?? "unknown")}</strong> <span>${escapeHtml40(handoff.status ?? "")}</span><p>${escapeHtml40(handoff.summary ?? handoff.reason ?? "")}</p></li>`).join("") : "<li>No agent handoffs recorded.</li>";
|
|
23956
|
+
const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${escapeHtml40(tool.toolName ?? "tool")}</strong> <span>${escapeHtml40(tool.status ?? "")}</span> ${formatMs4(tool.elapsedMs)} ${tool.error ? `<p>${escapeHtml40(tool.error)}</p>` : ""}</li>`).join("") : "<li>No tool calls recorded.</li>";
|
|
23957
|
+
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${escapeHtml40(review.title)}</strong> <span>${escapeHtml40(review.summary.outcome ?? "")}</span><p>${escapeHtml40(review.postCall?.summary ?? review.transcript.actual)}</p></li>`).join("") : "<li>No call reviews recorded.</li>";
|
|
23958
|
+
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${escapeHtml40(task.title)}</strong> <span>${escapeHtml40(task.status)}</span><p>${escapeHtml40(task.recommendedAction)}</p></li>`).join("") : "<li>No ops tasks recorded.</li>";
|
|
23959
|
+
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${escapeHtml40(event.type)}</strong> <span>${escapeHtml40(event.deliveryStatus ?? "local")}</span><p>${escapeHtml40(event.deliveryError ?? event.deliveredTo ?? "")}</p></li>`).join("") : "<li>No integration events recorded.</li>";
|
|
23960
|
+
const snippet = escapeHtml40(`app.use(
|
|
23695
23961
|
createVoiceOperationsRecordRoutes({
|
|
23696
23962
|
audit: auditStore,
|
|
23697
23963
|
integrationEvents: opsEvents,
|
|
@@ -23705,12 +23971,12 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
23705
23971
|
tasks: opsTasks
|
|
23706
23972
|
})
|
|
23707
23973
|
);`);
|
|
23708
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23974
|
+
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 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>${escapeHtml40(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml40(record.status)}">${escapeHtml40(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>`;
|
|
23709
23975
|
};
|
|
23710
23976
|
var createVoiceOperationsRecordRoutes = (options) => {
|
|
23711
23977
|
const path = options.path ?? "/api/voice-operations/:sessionId";
|
|
23712
23978
|
const htmlPath = options.htmlPath === undefined ? "/voice-operations/:sessionId" : options.htmlPath;
|
|
23713
|
-
const routes = new
|
|
23979
|
+
const routes = new Elysia39({
|
|
23714
23980
|
name: options.name ?? "absolutejs-voice-operations-record"
|
|
23715
23981
|
});
|
|
23716
23982
|
const buildRecord = (sessionId) => buildVoiceOperationsRecord({
|
|
@@ -23742,8 +24008,248 @@ var createVoiceOperationsRecordRoutes = (options) => {
|
|
|
23742
24008
|
}
|
|
23743
24009
|
return routes;
|
|
23744
24010
|
};
|
|
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
|
+
};
|
|
23745
24251
|
// src/incidentBundle.ts
|
|
23746
|
-
import { Elysia as
|
|
24252
|
+
import { Elysia as Elysia41 } from "elysia";
|
|
23747
24253
|
var filterIncidentBundleArtifacts = (artifacts, filter = {}) => artifacts.filter((artifact) => {
|
|
23748
24254
|
if (filter.sessionId && artifact.sessionId !== filter.sessionId) {
|
|
23749
24255
|
return false;
|
|
@@ -23942,7 +24448,7 @@ var buildVoiceIncidentBundle = async (options) => {
|
|
|
23942
24448
|
var createVoiceIncidentBundleRoutes = (options) => {
|
|
23943
24449
|
const path = options.path ?? "/api/voice-incidents/:sessionId";
|
|
23944
24450
|
const markdownPath = options.markdownPath === undefined ? "/voice-incidents/:sessionId/markdown" : options.markdownPath;
|
|
23945
|
-
const routes = new
|
|
24451
|
+
const routes = new Elysia41({
|
|
23946
24452
|
name: options.name ?? "absolutejs-voice-incident-bundle"
|
|
23947
24453
|
});
|
|
23948
24454
|
const getSessionId = (params) => params.sessionId ?? "";
|
|
@@ -24143,19 +24649,19 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
24143
24649
|
};
|
|
24144
24650
|
};
|
|
24145
24651
|
// src/opsStatusRoutes.ts
|
|
24146
|
-
import { Elysia as
|
|
24147
|
-
var
|
|
24652
|
+
import { Elysia as Elysia42 } from "elysia";
|
|
24653
|
+
var escapeHtml42 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
24148
24654
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
24149
24655
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
24150
24656
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
24151
24657
|
const value = "recovered" in surface ? surface.total === 0 ? "0 events" : `${surface.recovered}/${surface.total}` : ("auditTotal" in surface) ? `${surface.auditTotal + surface.traceTotal} deliveries` : ("total" in surface) ? `${Math.max(surface.total - ("failed" in surface ? surface.failed : ("degraded" in surface) ? surface.degraded : 0), 0)}/${surface.total}` : surface.status;
|
|
24152
|
-
return `<article class="surface ${
|
|
24658
|
+
return `<article class="surface ${escapeHtml42(surface.status)}"><span>${escapeHtml42(surface.status.toUpperCase())}</span><h2>${escapeHtml42(key)}</h2><strong>${escapeHtml42(value)}</strong></article>`;
|
|
24153
24659
|
}).join("");
|
|
24154
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
24660
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml42(title)}</title><style>body{background:#0d141b;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.2),rgba(245,158,11,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;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{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.surfaces{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.surface{background:#151d26;border:1px solid #283544;border-radius:20px;padding:18px}.surface span{color:#aab5c0;font-size:.78rem;font-weight:900;letter-spacing:.08em}.surface strong{font-size:1.5rem}.pass{border-color:rgba(34,197,94,.55)}.fail{border-color:rgba(239,68,68,.75)}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Ops status</p><h1>${escapeHtml42(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml42(report.status)}">Overall: ${escapeHtml42(report.status.toUpperCase())}</p><p>${report.passed}/${report.total} checks passing</p></section><section class="surfaces">${surfaces || '<article class="surface pass"><span>PASS</span><h2>No checks configured</h2><strong>0/0</strong></article>'}</section></main></body></html>`;
|
|
24155
24661
|
};
|
|
24156
24662
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
24157
24663
|
const path = options.path ?? "/api/voice/ops-status";
|
|
24158
|
-
const routes = new
|
|
24664
|
+
const routes = new Elysia42({
|
|
24159
24665
|
name: options.name ?? "absolutejs-voice-ops-status"
|
|
24160
24666
|
});
|
|
24161
24667
|
routes.get(path, async () => summarizeVoiceOpsStatus(options));
|
|
@@ -24588,9 +25094,9 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
24588
25094
|
};
|
|
24589
25095
|
};
|
|
24590
25096
|
// src/traceDeliveryRoutes.ts
|
|
24591
|
-
import { Elysia as
|
|
24592
|
-
var
|
|
24593
|
-
var
|
|
25097
|
+
import { Elysia as Elysia43 } from "elysia";
|
|
25098
|
+
var escapeHtml43 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
25099
|
+
var getString17 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
24594
25100
|
var getNumber11 = (value) => {
|
|
24595
25101
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
24596
25102
|
return value;
|
|
@@ -24602,13 +25108,13 @@ var getNumber11 = (value) => {
|
|
|
24602
25108
|
return;
|
|
24603
25109
|
};
|
|
24604
25110
|
var parseStatus2 = (value) => {
|
|
24605
|
-
const text =
|
|
25111
|
+
const text = getString17(value);
|
|
24606
25112
|
return text === "pending" || text === "delivered" || text === "failed" || text === "skipped" || text === "all" ? text : undefined;
|
|
24607
25113
|
};
|
|
24608
25114
|
var resolveVoiceTraceDeliveryFilter = (query = {}, base = {}) => ({
|
|
24609
25115
|
...base,
|
|
24610
25116
|
limit: getNumber11(query.limit) ?? base.limit,
|
|
24611
|
-
q:
|
|
25117
|
+
q: getString17(query.q) ?? base.q,
|
|
24612
25118
|
status: parseStatus2(query.status) ?? base.status
|
|
24613
25119
|
});
|
|
24614
25120
|
var deliverySearchText2 = (delivery) => [
|
|
@@ -24670,14 +25176,14 @@ var renderSinkResults2 = (delivery) => {
|
|
|
24670
25176
|
if (entries.length === 0) {
|
|
24671
25177
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
24672
25178
|
}
|
|
24673
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
25179
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml43(sinkId)}</strong>: ${escapeHtml43(result.status)}${result.deliveredTo ? ` to ${escapeHtml43(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml43(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
24674
25180
|
};
|
|
24675
|
-
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
25181
|
+
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml43(event.type)} <small>${escapeHtml43(event.id)}</small>${event.sessionId ? ` session=${escapeHtml43(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
|
|
24676
25182
|
var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
|
|
24677
25183
|
const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
|
|
24678
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
24679
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
24680
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
25184
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml43(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
|
|
25185
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml43(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml43(delivery.deliveryStatus)}</span><h2>${escapeHtml43(delivery.id)}</h2><p>${escapeHtml43(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml43(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml43(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
|
|
25186
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml43(title)}</title><style>body{background:#0f1318;color:#f4efe1;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(14,165,233,.14));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#86efac;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.grid{display:grid;gap:12px;grid-template-columns:repeat(4,1fr);margin-bottom:16px}.grid article,.delivery{background:#151b22;border:1px solid #26313d;border-radius:22px;padding:18px}.grid span,.delivery span{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.grid strong{display:block;font-size:2rem}.deliveries{display:grid;gap:14px}.delivery.failed{border-color:rgba(239,68,68,.75)}.delivery.pending{border-color:rgba(245,158,11,.7)}.delivery.delivered{border-color:rgba(34,197,94,.55)}.delivery.skipped{border-color:rgba(148,163,184,.6)}.head{align-items:start;display:flex;gap:14px;justify-content:space-between}.delivery h2{font-size:1.05rem;margin:.3rem 0;overflow-wrap:anywhere}.delivery h3{margin:1rem 0 .3rem}.delivery p,.delivery li{color:#c8d0d8}.error{color:#fecaca!important}button{background:#86efac;border:0;border-radius:999px;color:#07111f;cursor:pointer;font-weight:900;margin-top:12px;padding:10px 14px}@media(max-width:760px){main{padding:20px}.grid{grid-template-columns:1fr 1fr}.head{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Trace export health</p><h1>${escapeHtml43(title)}</h1><p>Checked ${escapeHtml43(new Date(report.checkedAt).toLocaleString())}. Showing ${String(report.deliveries.length)} delivery item(s).</p>${drainAction}</section>${renderMetricGrid3(report)}<section class="deliveries">${rows || "<p>No trace deliveries match this filter.</p>"}</section></main></body></html>`;
|
|
24681
25187
|
};
|
|
24682
25188
|
var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
|
|
24683
25189
|
var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -24697,7 +25203,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
24697
25203
|
const path = options.path ?? "/api/voice-trace-deliveries";
|
|
24698
25204
|
const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
|
|
24699
25205
|
const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
|
|
24700
|
-
const routes = new
|
|
25206
|
+
const routes = new Elysia43({
|
|
24701
25207
|
name: options.name ?? "absolutejs-voice-trace-deliveries"
|
|
24702
25208
|
}).get(path, createVoiceTraceDeliveryJSONHandler(options));
|
|
24703
25209
|
if (htmlPath !== false) {
|
|
@@ -25321,7 +25827,7 @@ var createVoiceMemoryStore = () => {
|
|
|
25321
25827
|
return { get, getOrCreate, list, remove, set };
|
|
25322
25828
|
};
|
|
25323
25829
|
// src/opsWebhook.ts
|
|
25324
|
-
import { Elysia as
|
|
25830
|
+
import { Elysia as Elysia44 } from "elysia";
|
|
25325
25831
|
var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
25326
25832
|
var signVoiceOpsWebhookBody = async (input) => {
|
|
25327
25833
|
const encoder = new TextEncoder;
|
|
@@ -25451,7 +25957,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
|
|
|
25451
25957
|
};
|
|
25452
25958
|
var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
25453
25959
|
const path = options.path ?? "/api/voice-ops/webhook";
|
|
25454
|
-
return new
|
|
25960
|
+
return new Elysia44().post(path, async ({ body, request, set }) => {
|
|
25455
25961
|
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
|
|
25456
25962
|
if (options.signingSecret) {
|
|
25457
25963
|
const verification = await verifyVoiceOpsWebhookSignature({
|
|
@@ -26295,6 +26801,7 @@ export {
|
|
|
26295
26801
|
withVoiceOpsTaskId,
|
|
26296
26802
|
withVoiceIntegrationEventId,
|
|
26297
26803
|
voiceTelephonyOutcomeToRouteResult,
|
|
26804
|
+
voiceComplianceRedactionDefaults,
|
|
26298
26805
|
voice,
|
|
26299
26806
|
verifyVoiceTwilioWebhookSignature,
|
|
26300
26807
|
verifyVoiceTelnyxWebhookSignature,
|
|
@@ -26393,6 +26900,8 @@ export {
|
|
|
26393
26900
|
renderVoicePhoneAgentProductionSmokeHTML,
|
|
26394
26901
|
renderVoiceOutcomeContractHTML,
|
|
26395
26902
|
renderVoiceOpsStatusHTML,
|
|
26903
|
+
renderVoiceOpsRecoveryMarkdown,
|
|
26904
|
+
renderVoiceOpsRecoveryHTML,
|
|
26396
26905
|
renderVoiceOpsConsoleHTML,
|
|
26397
26906
|
renderVoiceOpsActionHistoryHTML,
|
|
26398
26907
|
renderVoiceOperationsRecordHTML,
|
|
@@ -26404,6 +26913,8 @@ export {
|
|
|
26404
26913
|
renderVoiceDemoReadyHTML,
|
|
26405
26914
|
renderVoiceDeliverySinkHTML,
|
|
26406
26915
|
renderVoiceDeliveryRuntimeHTML,
|
|
26916
|
+
renderVoiceDataControlMarkdown,
|
|
26917
|
+
renderVoiceDataControlHTML,
|
|
26407
26918
|
renderVoiceCampaignsHTML,
|
|
26408
26919
|
renderVoiceCampaignObservabilityHTML,
|
|
26409
26920
|
renderVoiceCallReviewMarkdown,
|
|
@@ -26461,6 +26972,7 @@ export {
|
|
|
26461
26972
|
deliverVoiceAuditEventsToSinks,
|
|
26462
26973
|
decodeTwilioMulawBase64,
|
|
26463
26974
|
deadLetterVoiceOpsTask,
|
|
26975
|
+
createVoiceZeroRetentionPolicy,
|
|
26464
26976
|
createVoiceZendeskTicketUpdateSink,
|
|
26465
26977
|
createVoiceZendeskTicketSyncSinks,
|
|
26466
26978
|
createVoiceZendeskTicketSink,
|
|
@@ -26586,6 +27098,7 @@ export {
|
|
|
26586
27098
|
createVoiceOpsTaskProcessorWorker,
|
|
26587
27099
|
createVoiceOpsStatusRoutes,
|
|
26588
27100
|
createVoiceOpsRuntime,
|
|
27101
|
+
createVoiceOpsRecoveryRoutes,
|
|
26589
27102
|
createVoiceOpsConsoleRoutes,
|
|
26590
27103
|
createVoiceOpsActionAuditRoutes,
|
|
26591
27104
|
createVoiceOperationsRecordRoutes,
|
|
@@ -26648,6 +27161,7 @@ export {
|
|
|
26648
27161
|
createVoiceDeliveryRuntimeRoutes,
|
|
26649
27162
|
createVoiceDeliveryRuntimePresetConfig,
|
|
26650
27163
|
createVoiceDeliveryRuntime,
|
|
27164
|
+
createVoiceDataControlRoutes,
|
|
26651
27165
|
createVoiceCampaignWorkerLoop,
|
|
26652
27166
|
createVoiceCampaignWorker,
|
|
26653
27167
|
createVoiceCampaignTelephonyOutcomeHandler,
|
|
@@ -26718,6 +27232,8 @@ export {
|
|
|
26718
27232
|
buildVoiceProductionReadinessGate,
|
|
26719
27233
|
buildVoiceOpsTaskFromSLABreach,
|
|
26720
27234
|
buildVoiceOpsTaskFromReview,
|
|
27235
|
+
buildVoiceOpsRecoveryReport,
|
|
27236
|
+
buildVoiceOpsRecoveryReadinessCheck,
|
|
26721
27237
|
buildVoiceOpsConsoleReport,
|
|
26722
27238
|
buildVoiceOpsActionHistoryReport,
|
|
26723
27239
|
buildVoiceOperationsRecord,
|
|
@@ -26729,6 +27245,7 @@ export {
|
|
|
26729
27245
|
buildVoiceDeliverySinkReport,
|
|
26730
27246
|
buildVoiceDeliveryRuntimeReport,
|
|
26731
27247
|
buildVoiceDataRetentionPlan,
|
|
27248
|
+
buildVoiceDataControlReport,
|
|
26732
27249
|
buildVoiceCampaignObservabilityReport,
|
|
26733
27250
|
buildVoiceAuditTrailReport,
|
|
26734
27251
|
buildVoiceAuditExport,
|