@absolutejs/voice 0.0.22-beta.502 → 0.0.22-beta.503
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/dist/client/htmxAttributes.d.ts +28 -0
- package/dist/client/htmxDashboardRenderers.d.ts +2 -10
- package/dist/index.d.ts +6 -0
- package/dist/index.js +447 -113
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -36781,6 +36781,51 @@ var createVoiceRouteAuth = (options) => {
|
|
|
36781
36781
|
}
|
|
36782
36782
|
});
|
|
36783
36783
|
};
|
|
36784
|
+
// src/client/htmxAttributes.ts
|
|
36785
|
+
var escapeAttr = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
36786
|
+
var normalizeHxKey = (key) => key.startsWith("hx-") ? key : `hx-${key}`;
|
|
36787
|
+
var buildVoiceHTMXAttributes = (attrs) => {
|
|
36788
|
+
if (!attrs)
|
|
36789
|
+
return "";
|
|
36790
|
+
const out = [];
|
|
36791
|
+
if (attrs.poll && attrs.refreshUrl) {
|
|
36792
|
+
const intervalSec = Math.max(1, Math.round((attrs.pollIntervalMs ?? 5000) / 1000));
|
|
36793
|
+
out.push(`hx-get="${escapeAttr(attrs.refreshUrl)}"`);
|
|
36794
|
+
out.push(`hx-trigger="every ${intervalSec}s"`);
|
|
36795
|
+
out.push(`hx-swap="${escapeAttr(attrs.swap ?? "outerHTML")}"`);
|
|
36796
|
+
if (attrs.target) {
|
|
36797
|
+
out.push(`hx-target="${escapeAttr(attrs.target)}"`);
|
|
36798
|
+
}
|
|
36799
|
+
} else if (attrs.swap || attrs.target) {
|
|
36800
|
+
if (attrs.swap)
|
|
36801
|
+
out.push(`hx-swap="${escapeAttr(attrs.swap)}"`);
|
|
36802
|
+
if (attrs.target)
|
|
36803
|
+
out.push(`hx-target="${escapeAttr(attrs.target)}"`);
|
|
36804
|
+
}
|
|
36805
|
+
for (const [rawKey, value] of Object.entries(attrs.pushAttributes ?? {})) {
|
|
36806
|
+
const key = normalizeHxKey(rawKey);
|
|
36807
|
+
out.push(`${key}="${escapeAttr(value)}"`);
|
|
36808
|
+
}
|
|
36809
|
+
return out.length === 0 ? "" : ` ${out.join(" ")}`;
|
|
36810
|
+
};
|
|
36811
|
+
var FIRST_TAG_RE = /^(\s*)<([a-zA-Z][\w-]*)((?:\s|>|\/))/;
|
|
36812
|
+
var wrapVoiceHTMLWithHTMXPolling = (html, attrs) => {
|
|
36813
|
+
const attrString = buildVoiceHTMXAttributes(attrs);
|
|
36814
|
+
if (!attrString)
|
|
36815
|
+
return html;
|
|
36816
|
+
const match = FIRST_TAG_RE.exec(html);
|
|
36817
|
+
if (!match)
|
|
36818
|
+
return html;
|
|
36819
|
+
const [matched, leading, tag, terminator] = match;
|
|
36820
|
+
const replaced = `${leading}<${tag}${attrString}${terminator}`;
|
|
36821
|
+
return html.replace(matched, replaced);
|
|
36822
|
+
};
|
|
36823
|
+
var wrapVoiceHTMLInHTMXContainer = (html, attrs) => {
|
|
36824
|
+
const tag = attrs.elementTag ?? "div";
|
|
36825
|
+
const classAttr = attrs.className ? ` class="${escapeAttr(attrs.className)}"` : "";
|
|
36826
|
+
const hx = buildVoiceHTMXAttributes(attrs);
|
|
36827
|
+
return `<${tag}${classAttr}${hx}>${html}</${tag}>`;
|
|
36828
|
+
};
|
|
36784
36829
|
// src/client/costDashboard.ts
|
|
36785
36830
|
var padTwo = (value) => String(value).padStart(2, "0");
|
|
36786
36831
|
var formatBucketKey = (epochMs, bucketBy) => {
|
|
@@ -36860,6 +36905,7 @@ var buildVoiceCostDashboardReport = (options) => {
|
|
|
36860
36905
|
windowStartMs: Number.isFinite(minMs) ? minMs : 0
|
|
36861
36906
|
};
|
|
36862
36907
|
};
|
|
36908
|
+
|
|
36863
36909
|
// src/client/liveCallViewer.ts
|
|
36864
36910
|
var EVENT_BUFFER_LIMIT = 200;
|
|
36865
36911
|
var createLiveCallViewer = (options) => {
|
|
@@ -36967,6 +37013,7 @@ var createLiveCallViewer = (options) => {
|
|
|
36967
37013
|
}
|
|
36968
37014
|
};
|
|
36969
37015
|
};
|
|
37016
|
+
|
|
36970
37017
|
// src/client/replayTimeline.ts
|
|
36971
37018
|
var categorize = (entry) => {
|
|
36972
37019
|
const event = entry.event.toLowerCase();
|
|
@@ -37017,6 +37064,276 @@ var buildReplayTimelineReport = (input) => {
|
|
|
37017
37064
|
}
|
|
37018
37065
|
};
|
|
37019
37066
|
};
|
|
37067
|
+
|
|
37068
|
+
// src/client/htmxDashboardRenderers.ts
|
|
37069
|
+
var escapeHtml50 = (text) => text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37070
|
+
var formatUsd = (value, currency = "USD") => new Intl.NumberFormat("en-US", {
|
|
37071
|
+
currency,
|
|
37072
|
+
maximumFractionDigits: 4,
|
|
37073
|
+
minimumFractionDigits: 2,
|
|
37074
|
+
style: "currency"
|
|
37075
|
+
}).format(value);
|
|
37076
|
+
var formatInteger = (value) => new Intl.NumberFormat("en-US").format(value);
|
|
37077
|
+
var formatRelative = (ms) => {
|
|
37078
|
+
const seconds = Math.max(0, Math.floor(ms / 1000));
|
|
37079
|
+
const minutes = Math.floor(seconds / 60);
|
|
37080
|
+
const remaining = seconds % 60;
|
|
37081
|
+
return `${String(minutes).padStart(2, "0")}:${String(remaining).padStart(2, "0")}`;
|
|
37082
|
+
};
|
|
37083
|
+
var polledWrapperAttributes = (attrs) => buildVoiceHTMXAttributes(attrs);
|
|
37084
|
+
var renderCostRow = (bucket, currency, isTotal) => `<tr data-bucket-key="${escapeHtml50(bucket.bucketKey)}" style="${isTotal ? "border-top:2px solid rgba(255,255,255,0.15);font-weight:600;" : ""}">
|
|
37085
|
+
<td style="padding:8px 12px;">${escapeHtml50(bucket.bucketKey)}</td>
|
|
37086
|
+
<td style="padding:8px 12px;text-align:right;">${formatInteger(bucket.callCount)}</td>
|
|
37087
|
+
<td style="padding:8px 12px;text-align:right;">${formatUsd(bucket.llmUsd, currency)}</td>
|
|
37088
|
+
<td style="padding:8px 12px;text-align:right;">${formatUsd(bucket.ttsUsd, currency)}</td>
|
|
37089
|
+
<td style="padding:8px 12px;text-align:right;">${formatUsd(bucket.sttUsd, currency)}</td>
|
|
37090
|
+
<td style="padding:8px 12px;text-align:right;">${formatUsd(bucket.telephonyUsd, currency)}</td>
|
|
37091
|
+
<td style="padding:8px 12px;text-align:right;">${formatUsd(bucket.totalUsd, currency)}</td>
|
|
37092
|
+
</tr>`;
|
|
37093
|
+
var defaultCostDashboard = ({
|
|
37094
|
+
attributes,
|
|
37095
|
+
currency = "USD",
|
|
37096
|
+
emptyMessage = "No cost events in window.",
|
|
37097
|
+
report,
|
|
37098
|
+
title = "Voice cost dashboard"
|
|
37099
|
+
}) => {
|
|
37100
|
+
const body = report.buckets.map((bucket) => renderCostRow(bucket, currency, false)).join("");
|
|
37101
|
+
const total = renderCostRow(report.grandTotal, currency, true);
|
|
37102
|
+
const inner = report.buckets.length === 0 ? `<p style="font-size:13px;opacity:0.7;">${escapeHtml50(emptyMessage)}</p>` : `<table style="border-collapse:collapse;font-size:13px;width:100%;">
|
|
37103
|
+
<thead><tr style="opacity:0.7;text-align:left;">
|
|
37104
|
+
<th style="padding:8px 12px;">Bucket</th>
|
|
37105
|
+
<th style="padding:8px 12px;text-align:right;">Calls</th>
|
|
37106
|
+
<th style="padding:8px 12px;text-align:right;">LLM</th>
|
|
37107
|
+
<th style="padding:8px 12px;text-align:right;">TTS</th>
|
|
37108
|
+
<th style="padding:8px 12px;text-align:right;">STT</th>
|
|
37109
|
+
<th style="padding:8px 12px;text-align:right;">Tel.</th>
|
|
37110
|
+
<th style="padding:8px 12px;text-align:right;">Total</th>
|
|
37111
|
+
</tr></thead><tbody>${body}${total}</tbody></table>`;
|
|
37112
|
+
return `<section aria-label="voice-cost-dashboard" class="absolute-voice-cost-dashboard"${polledWrapperAttributes(attributes)} style="background:#0f172a;border-radius:16px;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;padding:20px;">
|
|
37113
|
+
<header style="align-items:baseline;display:flex;gap:12px;margin-bottom:12px;">
|
|
37114
|
+
<strong style="font-size:16px;">${escapeHtml50(title)}</strong>
|
|
37115
|
+
<span style="font-size:13px;opacity:0.7;">${report.buckets.length} buckets \xB7 grand total ${formatUsd(report.grandTotal.totalUsd, currency)}</span>
|
|
37116
|
+
</header>
|
|
37117
|
+
${inner}
|
|
37118
|
+
</section>`;
|
|
37119
|
+
};
|
|
37120
|
+
var REPLAY_CATEGORY_COLOR = {
|
|
37121
|
+
agent: "#3b82f6",
|
|
37122
|
+
lifecycle: "#94a3b8",
|
|
37123
|
+
tool: "#f59e0b",
|
|
37124
|
+
user: "#10b981"
|
|
37125
|
+
};
|
|
37126
|
+
var renderReplayEntry = (event, startedAt) => `<li style="align-items:center;border-left:3px solid ${REPLAY_CATEGORY_COLOR[event.category]};display:flex;font-size:13px;gap:12px;padding-left:12px;">
|
|
37127
|
+
<span style="color:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;width:60px;">${formatRelative(event.at - startedAt)}</span>
|
|
37128
|
+
<strong style="font-size:13px;">${escapeHtml50(event.label)}</strong>
|
|
37129
|
+
${event.detail ? `<span style="opacity:0.85;">${escapeHtml50(event.detail)}</span>` : ""}
|
|
37130
|
+
</li>`;
|
|
37131
|
+
var defaultReplayTimeline = ({
|
|
37132
|
+
attributes,
|
|
37133
|
+
emptyMessage = "No timeline events.",
|
|
37134
|
+
report,
|
|
37135
|
+
title
|
|
37136
|
+
}) => {
|
|
37137
|
+
const headline = escapeHtml50(title ?? report.metadata.title ?? "Replay");
|
|
37138
|
+
const items = report.events.map((event) => renderReplayEntry(event, report.startedAt)).join("");
|
|
37139
|
+
const inner = report.events.length === 0 ? `<p style="font-size:13px;opacity:0.7;">${escapeHtml50(emptyMessage)}</p>` : `<ol style="display:flex;flex-direction:column;gap:6px;list-style:none;margin:0;padding:0;">${items}</ol>`;
|
|
37140
|
+
return `<section aria-label="voice-replay-timeline" class="absolute-voice-replay-timeline"${polledWrapperAttributes(attributes)} style="background:#0f172a;border-radius:16px;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;padding:20px;">
|
|
37141
|
+
<header style="align-items:baseline;display:flex;gap:12px;margin-bottom:12px;">
|
|
37142
|
+
<strong style="font-size:16px;">${headline}</strong>
|
|
37143
|
+
<span style="font-size:13px;opacity:0.7;">${report.events.length} events \xB7 ${report.summary.userTurns} user \xB7 ${report.summary.agentTurns} agent \xB7 ${report.summary.toolCalls} tool</span>
|
|
37144
|
+
</header>
|
|
37145
|
+
${inner}
|
|
37146
|
+
</section>`;
|
|
37147
|
+
};
|
|
37148
|
+
var LIVE_CATEGORY_COLOR = {
|
|
37149
|
+
agent_audio: "#3b82f6",
|
|
37150
|
+
agent_text: "#3b82f6",
|
|
37151
|
+
lifecycle: "#94a3b8",
|
|
37152
|
+
tool: "#f59e0b",
|
|
37153
|
+
transcript: "#10b981"
|
|
37154
|
+
};
|
|
37155
|
+
var renderLiveEntry = (event, firstAt) => `<li style="align-items:center;border-left:3px solid ${LIVE_CATEGORY_COLOR[event.kind] ?? "#94a3b8"};display:flex;font-size:13px;gap:12px;padding-left:12px;">
|
|
37156
|
+
<span style="color:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;width:60px;">${formatRelative(event.at - firstAt)}</span>
|
|
37157
|
+
<strong style="font-size:13px;">${escapeHtml50(event.title)}</strong>
|
|
37158
|
+
${event.detail ? `<span style="opacity:0.85;">${escapeHtml50(event.detail)}</span>` : ""}
|
|
37159
|
+
</li>`;
|
|
37160
|
+
var defaultLiveCallViewer = ({
|
|
37161
|
+
attributes,
|
|
37162
|
+
state,
|
|
37163
|
+
title = "Live call"
|
|
37164
|
+
}) => {
|
|
37165
|
+
const firstAt = state.events[0]?.at ?? Date.now();
|
|
37166
|
+
const items = state.events.map((event) => renderLiveEntry(event, firstAt)).join("");
|
|
37167
|
+
return `<section aria-label="voice-live-call-viewer" class="absolute-voice-live-call-viewer" data-agent-state="${escapeHtml50(state.agentState)}"${polledWrapperAttributes(attributes)} style="background:#0f172a;border-radius:16px;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;padding:20px;">
|
|
37168
|
+
<header style="align-items:center;display:flex;gap:12px;margin-bottom:12px;">
|
|
37169
|
+
<strong style="font-size:16px;">${escapeHtml50(title)}</strong>
|
|
37170
|
+
<span style="background:rgba(59,130,246,0.18);border-radius:999px;font-size:11px;padding:3px 10px;text-transform:uppercase;">${escapeHtml50(state.agentState)}</span>
|
|
37171
|
+
<span style="font-size:13px;margin-left:auto;opacity:0.7;">${escapeHtml50(state.sessionId)} \xB7 ${formatRelative(state.callDurationMs)}</span>
|
|
37172
|
+
</header>
|
|
37173
|
+
${state.partialTranscript ? `<p style="background:rgba(16,185,129,0.12);border-radius:12px;font-size:13px;margin:0 0 12px;opacity:0.95;padding:10px 12px;">"${escapeHtml50(state.partialTranscript)}"</p>` : ""}
|
|
37174
|
+
<ol style="display:flex;flex-direction:column;gap:6px;list-style:none;margin:0;max-height:320px;overflow-y:auto;padding:0;">${items}</ol>
|
|
37175
|
+
</section>`;
|
|
37176
|
+
};
|
|
37177
|
+
var resolveVoiceDashboardRenderers = (custom) => ({
|
|
37178
|
+
costDashboard: custom?.costDashboard ?? defaultCostDashboard,
|
|
37179
|
+
liveCallViewer: custom?.liveCallViewer ?? defaultLiveCallViewer,
|
|
37180
|
+
replayTimeline: custom?.replayTimeline ?? defaultReplayTimeline
|
|
37181
|
+
});
|
|
37182
|
+
var renderVoiceCostDashboardHTMX = (input) => (input.custom ?? defaultCostDashboard)(input);
|
|
37183
|
+
var renderVoiceReplayTimelineHTMX = (input) => (input.custom ?? defaultReplayTimeline)(input);
|
|
37184
|
+
var renderVoiceLiveCallViewerHTMX = (input) => (input.custom ?? defaultLiveCallViewer)(input);
|
|
37185
|
+
var renderVoiceCostDashboardFromEvents = (input) => {
|
|
37186
|
+
const report = buildVoiceCostDashboardReport({
|
|
37187
|
+
bucketBy: input.options?.bucketBy,
|
|
37188
|
+
events: input.events,
|
|
37189
|
+
fromMs: input.options?.fromMs,
|
|
37190
|
+
toMs: input.options?.toMs
|
|
37191
|
+
});
|
|
37192
|
+
return renderVoiceCostDashboardHTMX({
|
|
37193
|
+
attributes: input.attributes,
|
|
37194
|
+
currency: input.currency,
|
|
37195
|
+
custom: input.renderer,
|
|
37196
|
+
report,
|
|
37197
|
+
title: input.title
|
|
37198
|
+
});
|
|
37199
|
+
};
|
|
37200
|
+
var renderVoiceReplayTimelineFromArtifact = (input) => {
|
|
37201
|
+
const report = buildReplayTimelineReport({ artifact: input.artifact });
|
|
37202
|
+
return renderVoiceReplayTimelineHTMX({
|
|
37203
|
+
attributes: input.attributes,
|
|
37204
|
+
custom: input.renderer,
|
|
37205
|
+
report,
|
|
37206
|
+
title: input.title
|
|
37207
|
+
});
|
|
37208
|
+
};
|
|
37209
|
+
var renderVoiceLiveCallViewerFromViewer = (input) => renderVoiceLiveCallViewerHTMX({
|
|
37210
|
+
attributes: input.attributes,
|
|
37211
|
+
custom: input.renderer,
|
|
37212
|
+
state: input.viewer.getState(),
|
|
37213
|
+
title: input.title
|
|
37214
|
+
});
|
|
37215
|
+
var renderVoiceLiveCallViewerFromState = (input) => renderVoiceLiveCallViewerHTMX({
|
|
37216
|
+
attributes: input.attributes,
|
|
37217
|
+
custom: input.renderer,
|
|
37218
|
+
state: input.state,
|
|
37219
|
+
title: input.title
|
|
37220
|
+
});
|
|
37221
|
+
var createLiveCallViewerFromOptions = createLiveCallViewer;
|
|
37222
|
+
// src/htmxDashboardRoutes.ts
|
|
37223
|
+
import { Elysia as Elysia54 } from "elysia";
|
|
37224
|
+
var HTML_HEADERS = { "content-type": "text/html; charset=utf-8" };
|
|
37225
|
+
var createVoiceCostDashboardHTMXRoute = (options) => {
|
|
37226
|
+
const renderers = resolveVoiceDashboardRenderers(options.render);
|
|
37227
|
+
const path = options.path ?? "/voice/htmx/cost-dashboard";
|
|
37228
|
+
return new Elysia54({ name: options.name ?? "voice-cost-dashboard-htmx" }).get(path, async () => {
|
|
37229
|
+
const events = await Promise.resolve(options.resolveEvents());
|
|
37230
|
+
const attributes = {
|
|
37231
|
+
poll: typeof options.pollIntervalMs === "number",
|
|
37232
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
37233
|
+
refreshUrl: path
|
|
37234
|
+
};
|
|
37235
|
+
const html = renderVoiceCostDashboardFromEvents({
|
|
37236
|
+
attributes,
|
|
37237
|
+
currency: options.currency,
|
|
37238
|
+
events,
|
|
37239
|
+
options: {
|
|
37240
|
+
bucketBy: options.bucketBy,
|
|
37241
|
+
fromMs: options.fromMs?.(),
|
|
37242
|
+
toMs: options.toMs?.()
|
|
37243
|
+
},
|
|
37244
|
+
renderer: renderers.costDashboard,
|
|
37245
|
+
title: options.title
|
|
37246
|
+
});
|
|
37247
|
+
return new Response(html, { headers: HTML_HEADERS });
|
|
37248
|
+
});
|
|
37249
|
+
};
|
|
37250
|
+
var createVoiceReplayTimelineHTMXRoute = (options) => {
|
|
37251
|
+
const renderers = resolveVoiceDashboardRenderers(options.render);
|
|
37252
|
+
const basePath = options.path ?? "/voice/htmx/replay";
|
|
37253
|
+
return new Elysia54({
|
|
37254
|
+
name: options.name ?? "voice-replay-timeline-htmx"
|
|
37255
|
+
}).get(`${basePath}/:artifactId`, async ({ params, set }) => {
|
|
37256
|
+
const { artifactId } = params;
|
|
37257
|
+
const artifact = await Promise.resolve(options.resolveArtifact(artifactId));
|
|
37258
|
+
if (!artifact) {
|
|
37259
|
+
set.status = 404;
|
|
37260
|
+
return new Response(`<div class="absolute-voice-replay-timeline" data-status="not-found" style="background:#0f172a;color:#f8fafc;padding:20px;border-radius:16px;">Replay artifact not found.</div>`, { headers: HTML_HEADERS, status: 404 });
|
|
37261
|
+
}
|
|
37262
|
+
const html = renderVoiceReplayTimelineFromArtifact({
|
|
37263
|
+
artifact,
|
|
37264
|
+
renderer: renderers.replayTimeline,
|
|
37265
|
+
title: options.title
|
|
37266
|
+
});
|
|
37267
|
+
return new Response(html, { headers: HTML_HEADERS });
|
|
37268
|
+
});
|
|
37269
|
+
};
|
|
37270
|
+
var createVoiceLiveCallViewerHTMXRoute = (options) => {
|
|
37271
|
+
const renderers = resolveVoiceDashboardRenderers(options.render);
|
|
37272
|
+
const basePath = options.path ?? "/voice/htmx/live";
|
|
37273
|
+
return new Elysia54({
|
|
37274
|
+
name: options.name ?? "voice-live-call-viewer-htmx"
|
|
37275
|
+
}).get(`${basePath}/:sessionId`, async ({ params, set }) => {
|
|
37276
|
+
const { sessionId } = params;
|
|
37277
|
+
const viewer = await Promise.resolve(options.resolveViewer(sessionId));
|
|
37278
|
+
if (!viewer) {
|
|
37279
|
+
set.status = 404;
|
|
37280
|
+
return new Response(`<div class="absolute-voice-live-call-viewer" data-status="not-found" style="background:#0f172a;color:#f8fafc;padding:20px;border-radius:16px;">No active call for ${sessionId}.</div>`, { headers: HTML_HEADERS, status: 404 });
|
|
37281
|
+
}
|
|
37282
|
+
const attributes = {
|
|
37283
|
+
poll: typeof options.pollIntervalMs === "number",
|
|
37284
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
37285
|
+
refreshUrl: `${basePath}/${sessionId}`
|
|
37286
|
+
};
|
|
37287
|
+
const html = renderVoiceLiveCallViewerFromViewer({
|
|
37288
|
+
attributes,
|
|
37289
|
+
renderer: renderers.liveCallViewer,
|
|
37290
|
+
title: options.title,
|
|
37291
|
+
viewer
|
|
37292
|
+
});
|
|
37293
|
+
return new Response(html, { headers: HTML_HEADERS });
|
|
37294
|
+
});
|
|
37295
|
+
};
|
|
37296
|
+
var createVoiceHTMXDashboardRoutes = (options) => {
|
|
37297
|
+
let app = new Elysia54({ name: options.name ?? "voice-htmx-dashboards" });
|
|
37298
|
+
if (options.cost) {
|
|
37299
|
+
app = app.use(createVoiceCostDashboardHTMXRoute({
|
|
37300
|
+
...options.cost,
|
|
37301
|
+
render: options.render
|
|
37302
|
+
}));
|
|
37303
|
+
}
|
|
37304
|
+
if (options.replay) {
|
|
37305
|
+
app = app.use(createVoiceReplayTimelineHTMXRoute({
|
|
37306
|
+
...options.replay,
|
|
37307
|
+
render: options.render
|
|
37308
|
+
}));
|
|
37309
|
+
}
|
|
37310
|
+
if (options.liveCall) {
|
|
37311
|
+
app = app.use(createVoiceLiveCallViewerHTMXRoute({
|
|
37312
|
+
...options.liveCall,
|
|
37313
|
+
render: options.render
|
|
37314
|
+
}));
|
|
37315
|
+
}
|
|
37316
|
+
return app;
|
|
37317
|
+
};
|
|
37318
|
+
var createVoiceHTMXDashboardRoutesFromStores = (options) => createVoiceHTMXDashboardRoutes({
|
|
37319
|
+
cost: options.traceStore ? {
|
|
37320
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
37321
|
+
resolveEvents: async () => {
|
|
37322
|
+
const events = await options.traceStore.list({
|
|
37323
|
+
type: "cost.ready"
|
|
37324
|
+
});
|
|
37325
|
+
return events;
|
|
37326
|
+
}
|
|
37327
|
+
} : undefined,
|
|
37328
|
+
liveCall: options.liveViewerByCallId ? {
|
|
37329
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
37330
|
+
resolveViewer: options.liveViewerByCallId
|
|
37331
|
+
} : undefined,
|
|
37332
|
+
render: options.render,
|
|
37333
|
+
replay: options.reviewStore ? {
|
|
37334
|
+
resolveArtifact: async (artifactId) => await options.reviewStore.get(artifactId) ?? undefined
|
|
37335
|
+
} : undefined
|
|
37336
|
+
});
|
|
37020
37337
|
// src/retention.ts
|
|
37021
37338
|
var defaultResolveAt = (event) => {
|
|
37022
37339
|
if (!event || typeof event !== "object")
|
|
@@ -37653,10 +37970,10 @@ var assertVoiceAgentSquadContractEvidence = (reports, input = {}) => {
|
|
|
37653
37970
|
return report;
|
|
37654
37971
|
};
|
|
37655
37972
|
// src/turnLatency.ts
|
|
37656
|
-
import { Elysia as
|
|
37973
|
+
import { Elysia as Elysia55 } from "elysia";
|
|
37657
37974
|
var DEFAULT_WARN_AFTER_MS2 = 1800;
|
|
37658
37975
|
var DEFAULT_FAIL_AFTER_MS2 = 3200;
|
|
37659
|
-
var
|
|
37976
|
+
var escapeHtml51 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37660
37977
|
var firstNumber3 = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
37661
37978
|
var getString19 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
37662
37979
|
var createTraceStageIndex = (events) => {
|
|
@@ -37792,11 +38109,11 @@ await traceStore.append({
|
|
|
37792
38109
|
turnId,
|
|
37793
38110
|
type: 'turn_latency.stage'
|
|
37794
38111
|
});`;
|
|
37795
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
37796
|
-
<header><div><p class="eyebrow">${
|
|
37797
|
-
<dl>${turn.stages.map((stage) => `<div><dt>${
|
|
38112
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml51(turn.status)}">
|
|
38113
|
+
<header><div><p class="eyebrow">${escapeHtml51(turn.sessionId)} \xB7 ${escapeHtml51(turn.turnId)}</p><h2>${escapeHtml51(turn.text || "Empty turn")}</h2></div><strong>${escapeHtml51(turn.status)}</strong></header>
|
|
38114
|
+
<dl>${turn.stages.map((stage) => `<div><dt>${escapeHtml51(stage.label)}</dt><dd>${escapeHtml51(formatMs5(stage.valueMs))}</dd></div>`).join("")}</dl>
|
|
37798
38115
|
</article>`).join("");
|
|
37799
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
38116
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml51(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>${escapeHtml51(title)}</h1><div class="summary"><span class="pill ${escapeHtml51(report.status)}">${escapeHtml51(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">avg ${escapeHtml51(formatMs5(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>${escapeHtml51(snippet)}</code></pre></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
37800
38117
|
};
|
|
37801
38118
|
var createVoiceTurnLatencyJSONHandler = (options) => async () => summarizeVoiceTurnLatency(options);
|
|
37802
38119
|
var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
@@ -37813,7 +38130,7 @@ var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
|
37813
38130
|
var createVoiceTurnLatencyRoutes = (options) => {
|
|
37814
38131
|
const path = options.path ?? "/api/turn-latency";
|
|
37815
38132
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
37816
|
-
const routes = new
|
|
38133
|
+
const routes = new Elysia55({
|
|
37817
38134
|
name: options.name ?? "absolutejs-voice-turn-latency"
|
|
37818
38135
|
}).get(path, createVoiceTurnLatencyJSONHandler(options));
|
|
37819
38136
|
if (htmlPath) {
|
|
@@ -37822,8 +38139,8 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
37822
38139
|
return routes;
|
|
37823
38140
|
};
|
|
37824
38141
|
// src/liveLatency.ts
|
|
37825
|
-
import { Elysia as
|
|
37826
|
-
var
|
|
38142
|
+
import { Elysia as Elysia56 } from "elysia";
|
|
38143
|
+
var escapeHtml52 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37827
38144
|
var percentile6 = (values, percentileValue) => {
|
|
37828
38145
|
if (values.length === 0) {
|
|
37829
38146
|
return;
|
|
@@ -37890,13 +38207,13 @@ await traceStore.append({
|
|
|
37890
38207
|
sessionId,
|
|
37891
38208
|
type: 'client.live_latency'
|
|
37892
38209
|
});`;
|
|
37893
|
-
const rows = report.recent.map((sample) => `<tr><td>${
|
|
37894
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
38210
|
+
const rows = report.recent.map((sample) => `<tr><td>${escapeHtml52(sample.sessionId)}</td><td>${escapeHtml52(formatMs6(sample.latencyMs))}</td><td>${escapeHtml52(sample.status ?? "unknown")}</td><td>${escapeHtml52(new Date(sample.at).toLocaleString())}</td></tr>`).join("");
|
|
38211
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml52(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>${escapeHtml52(title)}</h1><p>Recent real browser speech-to-assistant response measurements from persisted <code>client.live_latency</code> traces.</p><p class="status ${escapeHtml52(report.status)}">Status: ${escapeHtml52(report.status)}</p><section class="metrics"><article><span>p50</span><strong>${escapeHtml52(formatMs6(report.p50LatencyMs))}</strong></article><article><span>p95</span><strong>${escapeHtml52(formatMs6(report.p95LatencyMs))}</strong></article><article><span>Average</span><strong>${escapeHtml52(formatMs6(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>${escapeHtml52(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>`;
|
|
37895
38212
|
};
|
|
37896
38213
|
var createVoiceLiveLatencyRoutes = (options) => {
|
|
37897
38214
|
const path = options.path ?? "/api/live-latency";
|
|
37898
38215
|
const htmlPath = options.htmlPath === undefined ? "/live-latency" : options.htmlPath;
|
|
37899
|
-
const routes = new
|
|
38216
|
+
const routes = new Elysia56({
|
|
37900
38217
|
name: options.name ?? "absolutejs-voice-live-latency"
|
|
37901
38218
|
}).get(path, () => summarizeVoiceLiveLatency(options));
|
|
37902
38219
|
if (htmlPath) {
|
|
@@ -37913,9 +38230,9 @@ var createVoiceLiveLatencyRoutes = (options) => {
|
|
|
37913
38230
|
return routes;
|
|
37914
38231
|
};
|
|
37915
38232
|
// src/turnQuality.ts
|
|
37916
|
-
import { Elysia as
|
|
38233
|
+
import { Elysia as Elysia57 } from "elysia";
|
|
37917
38234
|
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
37918
|
-
var
|
|
38235
|
+
var escapeHtml53 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37919
38236
|
var getTurnLatencyMs = (turn) => {
|
|
37920
38237
|
const firstTranscriptAt = turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs).filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
37921
38238
|
if (firstTranscriptAt === undefined) {
|
|
@@ -37985,24 +38302,24 @@ var summarizeVoiceTurnQuality = async (options) => {
|
|
|
37985
38302
|
};
|
|
37986
38303
|
var renderVoiceTurnQualityHTML = (report, options = {}) => {
|
|
37987
38304
|
const title = options.title ?? "Voice Turn Quality";
|
|
37988
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
38305
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml53(turn.status)}">
|
|
37989
38306
|
<div class="turn-header">
|
|
37990
38307
|
<div>
|
|
37991
|
-
<p class="eyebrow">${
|
|
37992
|
-
<h2>${
|
|
38308
|
+
<p class="eyebrow">${escapeHtml53(turn.sessionId)} \xB7 ${escapeHtml53(turn.turnId)}</p>
|
|
38309
|
+
<h2>${escapeHtml53(turn.text || "Empty turn")}</h2>
|
|
37993
38310
|
</div>
|
|
37994
|
-
<strong>${
|
|
38311
|
+
<strong>${escapeHtml53(turn.status)}</strong>
|
|
37995
38312
|
</div>
|
|
37996
38313
|
<dl>
|
|
37997
|
-
<div><dt>Source</dt><dd>${
|
|
38314
|
+
<div><dt>Source</dt><dd>${escapeHtml53(turn.source ?? "unknown")}</dd></div>
|
|
37998
38315
|
<div><dt>Confidence</dt><dd>${turn.averageConfidence === undefined ? "n/a" : `${Math.round(turn.averageConfidence * 100)}%`}</dd></div>
|
|
37999
|
-
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${
|
|
38000
|
-
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${
|
|
38316
|
+
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${escapeHtml53(turn.fallbackSelectionReason ?? "selected")})` : "no"}</dd></div>
|
|
38317
|
+
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${escapeHtml53(turn.correctionProvider)}` : ""}` : "none"}</dd></div>
|
|
38001
38318
|
<div><dt>Transcripts</dt><dd>${String(turn.selectedTranscriptCount)} selected \xB7 ${String(turn.finalTranscriptCount)} final \xB7 ${String(turn.partialTranscriptCount)} partial</dd></div>
|
|
38002
38319
|
<div><dt>Cost</dt><dd>${turn.costUnits === undefined ? "n/a" : String(turn.costUnits)}</dd></div>
|
|
38003
38320
|
</dl>
|
|
38004
38321
|
</article>`).join("");
|
|
38005
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
38322
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml53(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>${escapeHtml53(title)}</h1><div class="summary"><span class="pill ${escapeHtml53(report.status)}">${escapeHtml53(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>`;
|
|
38006
38323
|
};
|
|
38007
38324
|
var createVoiceTurnQualityJSONHandler = (options) => async () => summarizeVoiceTurnQuality(options);
|
|
38008
38325
|
var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
@@ -38019,7 +38336,7 @@ var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
|
38019
38336
|
var createVoiceTurnQualityRoutes = (options) => {
|
|
38020
38337
|
const path = options.path ?? "/api/turn-quality";
|
|
38021
38338
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
38022
|
-
const routes = new
|
|
38339
|
+
const routes = new Elysia57({
|
|
38023
38340
|
name: options.name ?? "absolutejs-voice-turn-quality"
|
|
38024
38341
|
}).get(path, createVoiceTurnQualityJSONHandler(options));
|
|
38025
38342
|
if (htmlPath) {
|
|
@@ -38028,10 +38345,10 @@ var createVoiceTurnQualityRoutes = (options) => {
|
|
|
38028
38345
|
return routes;
|
|
38029
38346
|
};
|
|
38030
38347
|
// src/phoneAgent.ts
|
|
38031
|
-
import { Elysia as
|
|
38348
|
+
import { Elysia as Elysia59 } from "elysia";
|
|
38032
38349
|
|
|
38033
38350
|
// src/phoneAgentProductionSmoke.ts
|
|
38034
|
-
import { Elysia as
|
|
38351
|
+
import { Elysia as Elysia58 } from "elysia";
|
|
38035
38352
|
var defaultRequirements = [
|
|
38036
38353
|
"media-started",
|
|
38037
38354
|
"transcript",
|
|
@@ -38039,7 +38356,7 @@ var defaultRequirements = [
|
|
|
38039
38356
|
"lifecycle-outcome",
|
|
38040
38357
|
"no-session-error"
|
|
38041
38358
|
];
|
|
38042
|
-
var
|
|
38359
|
+
var escapeHtml54 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
38043
38360
|
var payloadType = (event) => typeof event.payload.type === "string" ? event.payload.type : undefined;
|
|
38044
38361
|
var hasTextPayload = (event) => ["text", "assistantText", "transcript"].some((key) => {
|
|
38045
38362
|
const value = event.payload[key];
|
|
@@ -38148,10 +38465,10 @@ var resolveHandlerOptions = async (options, input) => ({
|
|
|
38148
38465
|
});
|
|
38149
38466
|
var renderVoicePhoneAgentProductionSmokeHTML = (report, options = {}) => {
|
|
38150
38467
|
const title = options.title ?? "AbsoluteJS Voice Phone Smoke Contract";
|
|
38151
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
38152
|
-
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${
|
|
38153
|
-
const requirements = report.required.map((requirement) => `<span class="pill">${
|
|
38154
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
38468
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml54(issue.requirement)}</strong>: ${escapeHtml54(issue.message)}</li>`).join("");
|
|
38469
|
+
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${escapeHtml54(outcome)}</span>`).join("");
|
|
38470
|
+
const requirements = report.required.map((requirement) => `<span class="pill">${escapeHtml54(requirement)}</span>`).join("");
|
|
38471
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml54(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>${escapeHtml54(title)}</h1><p class="status ${report.pass ? "pass" : "fail"}">${report.pass ? "PASS" : "FAIL"}</p><p>Contract <code>${escapeHtml54(report.contractId)}</code>${report.provider ? ` for <code>${escapeHtml54(report.provider)}</code>` : ""}${report.sessionId ? ` on session <code>${escapeHtml54(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>`;
|
|
38155
38472
|
};
|
|
38156
38473
|
var createVoicePhoneAgentProductionSmokeJSONHandler = (options) => async ({
|
|
38157
38474
|
query,
|
|
@@ -38174,7 +38491,7 @@ var createVoicePhoneAgentProductionSmokeHTMLHandler = (options) => async ({
|
|
|
38174
38491
|
var createVoicePhoneAgentProductionSmokeRoutes = (options) => {
|
|
38175
38492
|
const path = options.path ?? "/api/voice/phone/smoke-contract";
|
|
38176
38493
|
const htmlPath = options.htmlPath === undefined ? "/voice/phone/smoke-contract" : options.htmlPath;
|
|
38177
|
-
const routes = new
|
|
38494
|
+
const routes = new Elysia58({
|
|
38178
38495
|
name: options.name ?? "absolutejs-voice-phone-smoke-contract"
|
|
38179
38496
|
}).get(path, createVoicePhoneAgentProductionSmokeJSONHandler(options));
|
|
38180
38497
|
if (htmlPath) {
|
|
@@ -38217,7 +38534,7 @@ var PHONE_AGENT_LIFECYCLE_STAGES = [
|
|
|
38217
38534
|
"completed",
|
|
38218
38535
|
"failed"
|
|
38219
38536
|
];
|
|
38220
|
-
var
|
|
38537
|
+
var escapeHtml55 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
38221
38538
|
var loadRouteJson = async (input) => {
|
|
38222
38539
|
const response = await input.app.handle(new Request(new URL(input.path, input.origin).toString(), {
|
|
38223
38540
|
headers: {
|
|
@@ -38455,10 +38772,10 @@ var renderVoicePhoneAgentSetupHTML = (report) => {
|
|
|
38455
38772
|
const entry = findCarrierMatrixEntry(report.matrix, carrier);
|
|
38456
38773
|
const urls = entry?.setup.urls;
|
|
38457
38774
|
const primaryUrl = carrier.provider === "plivo" ? urls?.twiml : urls?.twiml;
|
|
38458
|
-
return `<tr><td>${
|
|
38775
|
+
return `<tr><td>${escapeHtml55(carrier.name ?? carrier.provider)}</td><td>${escapeHtml55(carrier.provider)}</td><td><code>${escapeHtml55(carrier.setupPath || "disabled")}</code></td><td><code>${escapeHtml55(carrier.smokePath || "disabled")}</code></td><td>${entry ? `<span class="${escapeHtml55(entry.status)}">${escapeHtml55(entry.status.toUpperCase())}</span>` : "unknown"}</td><td>${primaryUrl ? `<code>${escapeHtml55(primaryUrl)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.webhook ? `<code>${escapeHtml55(urls.webhook)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.stream ? `<code>${escapeHtml55(urls.stream)}</code>` : '<span class="muted">missing</span>'}</td></tr>`;
|
|
38459
38776
|
}).join("");
|
|
38460
|
-
const stageList = report.lifecycleStages.map((stage) => `<li><code>${
|
|
38461
|
-
const snippet =
|
|
38777
|
+
const stageList = report.lifecycleStages.map((stage) => `<li><code>${escapeHtml55(stage)}</code></li>`).join("");
|
|
38778
|
+
const snippet = escapeHtml55(`const phoneAgent = createVoicePhoneAgent({
|
|
38462
38779
|
carriers: [
|
|
38463
38780
|
{
|
|
38464
38781
|
provider: 'twilio',
|
|
@@ -38492,11 +38809,11 @@ app.use(
|
|
|
38492
38809
|
);`);
|
|
38493
38810
|
const checklist = report.carriers.map((carrier) => {
|
|
38494
38811
|
const instruction = report.setupInstructions.find((candidate) => candidate.provider === carrier.provider && candidate.carrierName === (carrier.name ?? carrier.provider));
|
|
38495
|
-
const issueList = instruction?.issues.map((issue) => `<li>${
|
|
38496
|
-
const steps = instruction?.steps.map((step) => `<li>${
|
|
38497
|
-
return `<article><h3>${
|
|
38812
|
+
const issueList = instruction?.issues.map((issue) => `<li>${escapeHtml55(issue)}</li>`).join("") ?? "";
|
|
38813
|
+
const steps = instruction?.steps.map((step) => `<li>${escapeHtml55(step)}</li>`).join("") ?? "";
|
|
38814
|
+
return `<article><h3>${escapeHtml55(carrier.name ?? carrier.provider)}</h3><ol>${steps}</ol>${issueList ? `<ul class="issues">${issueList}</ul>` : '<p class="pass">No carrier contract issues.</p>'}</article>`;
|
|
38498
38815
|
}).join("");
|
|
38499
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
38816
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml55(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>${escapeHtml55(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="${escapeHtml55(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>`;
|
|
38500
38817
|
};
|
|
38501
38818
|
var createVoicePhoneAgent = (options) => {
|
|
38502
38819
|
const carrierSummaries = options.carriers.map((carrier) => ({
|
|
@@ -38505,7 +38822,7 @@ var createVoicePhoneAgent = (options) => {
|
|
|
38505
38822
|
setupPath: resolveSetupPath(carrier),
|
|
38506
38823
|
smokePath: resolveSmokePath(carrier)
|
|
38507
38824
|
}));
|
|
38508
|
-
const app = new
|
|
38825
|
+
const app = new Elysia59({
|
|
38509
38826
|
name: options.name ?? "absolutejs-voice-phone-agent"
|
|
38510
38827
|
});
|
|
38511
38828
|
for (const carrier of options.carriers) {
|
|
@@ -40314,8 +40631,8 @@ var createOpenAIVoiceTTS = (options) => {
|
|
|
40314
40631
|
};
|
|
40315
40632
|
};
|
|
40316
40633
|
// src/providerCapabilities.ts
|
|
40317
|
-
import { Elysia as
|
|
40318
|
-
var
|
|
40634
|
+
import { Elysia as Elysia60 } from "elysia";
|
|
40635
|
+
var escapeHtml56 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
40319
40636
|
var fromProviderList = (kind, providers, options) => (providers ?? []).map((provider) => ({
|
|
40320
40637
|
configured: true,
|
|
40321
40638
|
features: options.features?.[provider],
|
|
@@ -40380,27 +40697,27 @@ var summarizeVoiceProviderCapabilities = async (options) => {
|
|
|
40380
40697
|
var renderVoiceProviderCapabilityHTML = (report, options = {}) => {
|
|
40381
40698
|
const title = options.title ?? "Voice Provider Capabilities";
|
|
40382
40699
|
const cards = report.capabilities.map((capability) => {
|
|
40383
|
-
const features = (capability.features ?? []).map((feature) => `<span class="pill">${
|
|
40384
|
-
return `<article class="card ${
|
|
40700
|
+
const features = (capability.features ?? []).map((feature) => `<span class="pill">${escapeHtml56(feature)}</span>`).join("");
|
|
40701
|
+
return `<article class="card ${escapeHtml56(capability.status)}">
|
|
40385
40702
|
<div class="card-header">
|
|
40386
40703
|
<div>
|
|
40387
|
-
<p class="eyebrow">${
|
|
40388
|
-
<h2>${
|
|
40704
|
+
<p class="eyebrow">${escapeHtml56(capability.kind)}</p>
|
|
40705
|
+
<h2>${escapeHtml56(capability.label ?? capability.provider)}</h2>
|
|
40389
40706
|
</div>
|
|
40390
|
-
<strong>${
|
|
40707
|
+
<strong>${escapeHtml56(capability.status)}</strong>
|
|
40391
40708
|
</div>
|
|
40392
|
-
${capability.description ? `<p>${
|
|
40709
|
+
${capability.description ? `<p>${escapeHtml56(capability.description)}</p>` : ""}
|
|
40393
40710
|
<dl>
|
|
40394
40711
|
<div><dt>Configured</dt><dd>${capability.configured ? "yes" : "no"}</dd></div>
|
|
40395
40712
|
<div><dt>Selected</dt><dd>${capability.selected ? "yes" : "no"}</dd></div>
|
|
40396
|
-
<div><dt>Model</dt><dd>${
|
|
40713
|
+
<div><dt>Model</dt><dd>${escapeHtml56(capability.model ?? "default")}</dd></div>
|
|
40397
40714
|
<div><dt>Runs</dt><dd>${String(capability.health?.runCount ?? 0)}</dd></div>
|
|
40398
40715
|
<div><dt>Errors</dt><dd>${String(capability.health?.errorCount ?? 0)}</dd></div>
|
|
40399
40716
|
</dl>
|
|
40400
40717
|
${features ? `<div class="features">${features}</div>` : ""}
|
|
40401
40718
|
</article>`;
|
|
40402
40719
|
}).join("");
|
|
40403
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
40720
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml56(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>${escapeHtml56(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>`;
|
|
40404
40721
|
};
|
|
40405
40722
|
var createVoiceProviderCapabilityJSONHandler = (options) => async () => summarizeVoiceProviderCapabilities(options);
|
|
40406
40723
|
var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
@@ -40417,7 +40734,7 @@ var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
|
40417
40734
|
var createVoiceProviderCapabilityRoutes = (options) => {
|
|
40418
40735
|
const path = options.path ?? "/api/provider-capabilities";
|
|
40419
40736
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
40420
|
-
const routes = new
|
|
40737
|
+
const routes = new Elysia60({
|
|
40421
40738
|
name: options.name ?? "absolutejs-voice-provider-capabilities"
|
|
40422
40739
|
}).get(path, createVoiceProviderCapabilityJSONHandler(options));
|
|
40423
40740
|
if (htmlPath) {
|
|
@@ -40426,7 +40743,7 @@ var createVoiceProviderCapabilityRoutes = (options) => {
|
|
|
40426
40743
|
return routes;
|
|
40427
40744
|
};
|
|
40428
40745
|
// src/providerOrchestration.ts
|
|
40429
|
-
import { Elysia as
|
|
40746
|
+
import { Elysia as Elysia61 } from "elysia";
|
|
40430
40747
|
var defaultRequirement = {
|
|
40431
40748
|
minProviders: 1,
|
|
40432
40749
|
requireBudgetPolicy: false,
|
|
@@ -40439,7 +40756,7 @@ var statusRank7 = {
|
|
|
40439
40756
|
warn: 1,
|
|
40440
40757
|
fail: 2
|
|
40441
40758
|
};
|
|
40442
|
-
var
|
|
40759
|
+
var escapeHtml57 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
40443
40760
|
var isProviderList = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
40444
40761
|
var uniqueSorted8 = (values) => [
|
|
40445
40762
|
...new Set(values.filter((value) => typeof value === "string"))
|
|
@@ -40582,27 +40899,27 @@ var renderVoiceProviderOrchestrationMarkdown = (report) => {
|
|
|
40582
40899
|
};
|
|
40583
40900
|
var renderVoiceProviderOrchestrationHTML = (report, options = {}) => {
|
|
40584
40901
|
const title = options.title ?? "Voice Provider Orchestration";
|
|
40585
|
-
const cards = report.surfaces.map((surface) => `<article class="card ${
|
|
40586
|
-
<div class="card-header"><div><p class="eyebrow">${
|
|
40902
|
+
const cards = report.surfaces.map((surface) => `<article class="card ${escapeHtml57(surface.status)}">
|
|
40903
|
+
<div class="card-header"><div><p class="eyebrow">${escapeHtml57(surface.surface)}</p><h2>${escapeHtml57(surface.strategy ?? "default policy")}</h2></div><strong>${escapeHtml57(surface.status)}</strong></div>
|
|
40587
40904
|
<dl>
|
|
40588
|
-
<div><dt>Providers</dt><dd>${
|
|
40589
|
-
<div><dt>Fallback</dt><dd>${
|
|
40905
|
+
<div><dt>Providers</dt><dd>${escapeHtml57(surface.providers.join(", ") || "none")}</dd></div>
|
|
40906
|
+
<div><dt>Fallback</dt><dd>${escapeHtml57(surface.fallbackProviders.join(" -> ") || "none")}</dd></div>
|
|
40590
40907
|
<div><dt>Circuit breaker</dt><dd>${surface.circuitBreaker ? "yes" : "no"}</dd></div>
|
|
40591
40908
|
<div><dt>Timeout</dt><dd>${surface.timeoutBudget ? `${String(surface.timeoutMs)}ms` : "none"}</dd></div>
|
|
40592
40909
|
<div><dt>Max cost</dt><dd>${surface.budgetPolicy.maxCost ?? "none"}</dd></div>
|
|
40593
40910
|
<div><dt>Max latency</dt><dd>${surface.budgetPolicy.maxLatencyMs ? `${String(surface.budgetPolicy.maxLatencyMs)}ms` : "none"}</dd></div>
|
|
40594
40911
|
<div><dt>Min quality</dt><dd>${surface.budgetPolicy.minQuality ?? "none"}</dd></div>
|
|
40595
|
-
<div><dt>Fallback mode</dt><dd>${
|
|
40912
|
+
<div><dt>Fallback mode</dt><dd>${escapeHtml57(surface.fallbackMode || "default")}</dd></div>
|
|
40596
40913
|
</dl>
|
|
40597
|
-
${surface.issues.length ? `<ul>${surface.issues.map((issue) => `<li><strong>${
|
|
40914
|
+
${surface.issues.length ? `<ul>${surface.issues.map((issue) => `<li><strong>${escapeHtml57(issue.status)}</strong> ${escapeHtml57(issue.message)}</li>`).join("")}</ul>` : "<p>No orchestration issues.</p>"}
|
|
40598
40915
|
</article>`).join("");
|
|
40599
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
40916
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml57(title)}</title><style>body{background:#111827;color:#f9fafb;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.card{background:#172033;border:1px solid #2d3b55;border-radius:22px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(59,130,246,.18),rgba(20,184,166,.12))}.eyebrow{color:#93c5fd;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{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f172a;border:1px solid #334155;border-radius:999px;padding:7px 10px}.grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}.card-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass strong{color:#86efac}.warn strong{color:#fde68a}.fail strong{color:#fca5a5}dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0;overflow-wrap:anywhere}li{margin:.35rem 0}@media(max-width:800px){main{padding:18px}.card-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider Policy Proof</p><h1>${escapeHtml57(title)}</h1><div class="summary"><span class="pill">${escapeHtml57(report.profileId)}</span><span class="pill">${escapeHtml57(report.status)}</span><span class="pill">${String(report.summary.surfaces)} surfaces</span><span class="pill">${String(report.summary.providers)} providers</span><span class="pill">${String(report.issues.length)} issues</span></div></section><section class="grid">${cards || '<article class="card"><p>No provider orchestration surfaces configured.</p></article>'}</section></main></body></html>`;
|
|
40600
40917
|
};
|
|
40601
40918
|
var createVoiceProviderOrchestrationRoutes = (options) => {
|
|
40602
40919
|
const path = options.path ?? "/api/voice/provider-orchestration";
|
|
40603
40920
|
const htmlPath = options.htmlPath === undefined ? "/voice/provider-orchestration" : options.htmlPath;
|
|
40604
40921
|
const markdownPath = options.markdownPath === undefined ? "/voice/provider-orchestration.md" : options.markdownPath;
|
|
40605
|
-
const routes = new
|
|
40922
|
+
const routes = new Elysia61({
|
|
40606
40923
|
name: options.name ?? "absolutejs-voice-provider-orchestration"
|
|
40607
40924
|
}).get(path, () => buildVoiceProviderOrchestrationReport(options));
|
|
40608
40925
|
if (htmlPath) {
|
|
@@ -40775,8 +41092,8 @@ var assertVoiceProviderRoutingContractEvidence = (reports, input = {}) => {
|
|
|
40775
41092
|
return report;
|
|
40776
41093
|
};
|
|
40777
41094
|
// src/voiceMonitoring.ts
|
|
40778
|
-
import { Elysia as
|
|
40779
|
-
var
|
|
41095
|
+
import { Elysia as Elysia62 } from "elysia";
|
|
41096
|
+
var escapeHtml58 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
40780
41097
|
var issueIdForRun = (run) => `voice-monitor:${run.id}:${run.impactedSessions?.[0] ?? "global"}`;
|
|
40781
41098
|
var rollupStatus5 = (runs) => runs.some((run) => run.status === "fail") ? "fail" : runs.some((run) => run.status === "warn") ? "warn" : "pass";
|
|
40782
41099
|
var createVoiceMemoryMonitorIssueStore = (initial = []) => {
|
|
@@ -41029,14 +41346,14 @@ ${rows || "| none | pass | info | | | No monitors configured. |"}
|
|
|
41029
41346
|
};
|
|
41030
41347
|
var renderVoiceMonitorHTML = (report, options = {}) => {
|
|
41031
41348
|
const title = options.title ?? "Voice Monitors";
|
|
41032
|
-
const runs = report.runs.map((run) => `<tr><td>${
|
|
41033
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
41034
|
-
const snippet =
|
|
41349
|
+
const runs = report.runs.map((run) => `<tr><td>${escapeHtml58(run.label)}</td><td class="${escapeHtml58(run.status)}">${escapeHtml58(run.status)}</td><td>${escapeHtml58(run.severity)}</td><td>${escapeHtml58(String(run.value ?? ""))}</td><td>${escapeHtml58(String(run.threshold ?? ""))}</td><td>${escapeHtml58(run.detail ?? "")}</td></tr>`).join("");
|
|
41350
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml58(issue.label)}</strong> <span class="${escapeHtml58(issue.status)}">${escapeHtml58(issue.status)}</span> ${escapeHtml58(issue.detail ?? "")}</li>`).join("");
|
|
41351
|
+
const snippet = escapeHtml58(`app.use(createVoiceMonitorRoutes({
|
|
41035
41352
|
evidence,
|
|
41036
41353
|
issueStore,
|
|
41037
41354
|
monitors: [defineVoiceMonitor(...)]
|
|
41038
41355
|
}));`);
|
|
41039
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
41356
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml58(title)}</title><style>body{background:#10141b;color:#f8f2df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero,.card{background:#171f2b;border:1px solid #2e3a4b;border-radius:24px;margin-bottom:16px;padding:22px}.eyebrow{color:#93c5fd;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);line-height:.92;margin:.2rem 0 1rem}.pill{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;margin-right:8px;padding:8px 12px}.pass{color:#86efac}.warn,.acknowledged{color:#fde68a}.fail,.open{color:#fca5a5}.resolved,.muted{color:#cbd5e1}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2e3a4b;padding:12px;text-align:left;vertical-align:top}pre{background:#0c1118;border:1px solid #2e3a4b;border-radius:16px;color:#dbeafe;overflow:auto;padding:16px}</style></head><body><main><section class="hero"><p class="eyebrow">Code-owned monitoring</p><h1>${escapeHtml58(title)}</h1><p class="pill ${escapeHtml58(report.status)}">Status: ${escapeHtml58(report.status)}</p><p class="pill">Open issues: ${String(report.summary.open)}</p><p class="pill">Critical: ${String(report.summary.criticalOpen)}</p></section><section class="card"><h2>Monitor Runs</h2><table><thead><tr><th>Monitor</th><th>Status</th><th>Severity</th><th>Value</th><th>Threshold</th><th>Detail</th></tr></thead><tbody>${runs}</tbody></table></section><section class="card"><h2>Issues</h2>${issues ? `<ul>${issues}</ul>` : '<p class="pass">No monitor issues.</p>'}</section><section class="card"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceMonitorRoutes(...)</code></h2><pre><code>${snippet}</code></pre></section></main></body></html>`;
|
|
41040
41357
|
};
|
|
41041
41358
|
var actorFromRequest = async (request) => {
|
|
41042
41359
|
if (!request.headers.get("content-type")?.includes("application/json")) {
|
|
@@ -41060,7 +41377,7 @@ var createVoiceMonitorRoutes = (options) => {
|
|
|
41060
41377
|
monitors: options.monitors,
|
|
41061
41378
|
now: options.now
|
|
41062
41379
|
});
|
|
41063
|
-
const routes = new
|
|
41380
|
+
const routes = new Elysia62({
|
|
41064
41381
|
name: options.name ?? "absolutejs-voice-monitoring"
|
|
41065
41382
|
}).get(path, report).get(`${path}.md`, async () => {
|
|
41066
41383
|
return new Response(renderVoiceMonitorMarkdown(await report()), {
|
|
@@ -41107,7 +41424,7 @@ var createVoiceMonitorRoutes = (options) => {
|
|
|
41107
41424
|
};
|
|
41108
41425
|
var createVoiceMonitorRunnerRoutes = (options) => {
|
|
41109
41426
|
const path = options.path ?? "/api/voice/monitor-runner";
|
|
41110
|
-
return new
|
|
41427
|
+
return new Elysia62({
|
|
41111
41428
|
name: options.name ?? "absolutejs-voice-monitor-runner"
|
|
41112
41429
|
}).get(path, () => ({
|
|
41113
41430
|
isRunning: options.runner.isRunning()
|
|
@@ -41495,8 +41812,8 @@ var recommendVoiceReadinessProfile = (options) => {
|
|
|
41495
41812
|
};
|
|
41496
41813
|
};
|
|
41497
41814
|
// src/providerStackRecommendations.ts
|
|
41498
|
-
import { Elysia as
|
|
41499
|
-
var
|
|
41815
|
+
import { Elysia as Elysia63 } from "elysia";
|
|
41816
|
+
var escapeHtml59 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
41500
41817
|
var profileProviderPriorities = {
|
|
41501
41818
|
"meeting-recorder": {
|
|
41502
41819
|
llm: ["openai", "anthropic", "gemini"],
|
|
@@ -41811,17 +42128,17 @@ var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "fu
|
|
|
41811
42128
|
var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
41812
42129
|
const title = options.title ?? "Voice Provider Contract Matrix";
|
|
41813
42130
|
const rows = report.rows.map((row) => {
|
|
41814
|
-
const checks = row.checks.map((check) => `<li class="${
|
|
41815
|
-
return `<article class="row ${
|
|
42131
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml59(check.status)}"><strong>${escapeHtml59(check.label)}</strong><span>${escapeHtml59(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml59(check.remediation.href)}">${escapeHtml59(check.remediation.label)}</a>` : escapeHtml59(check.remediation.label)}: ${escapeHtml59(check.remediation.detail)}</em>` : ""}</li>`).join("");
|
|
42132
|
+
return `<article class="row ${escapeHtml59(row.status)}">
|
|
41816
42133
|
<div>
|
|
41817
|
-
<p class="eyebrow">${
|
|
41818
|
-
<h2>${
|
|
41819
|
-
<p class="status ${
|
|
42134
|
+
<p class="eyebrow">${escapeHtml59(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
|
|
42135
|
+
<h2>${escapeHtml59(row.provider)}</h2>
|
|
42136
|
+
<p class="status ${escapeHtml59(row.status)}">${escapeHtml59(row.status.toUpperCase())}</p>
|
|
41820
42137
|
</div>
|
|
41821
42138
|
<ul>${checks}</ul>
|
|
41822
42139
|
</article>`;
|
|
41823
42140
|
}).join("");
|
|
41824
|
-
const snippet =
|
|
42141
|
+
const snippet = escapeHtml59(`const providerContracts = () =>
|
|
41825
42142
|
createVoiceProviderContractMatrixPreset('phone-agent', {
|
|
41826
42143
|
env: process.env,
|
|
41827
42144
|
providers: {
|
|
@@ -41842,7 +42159,7 @@ createVoiceProductionReadinessRoutes({
|
|
|
41842
42159
|
providerContractMatrix: () =>
|
|
41843
42160
|
buildVoiceProviderContractMatrix(providerContracts())
|
|
41844
42161
|
});`);
|
|
41845
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
42162
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml59(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>${escapeHtml59(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>`;
|
|
41846
42163
|
};
|
|
41847
42164
|
var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
|
|
41848
42165
|
var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
@@ -41857,7 +42174,7 @@ var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
|
41857
42174
|
var createVoiceProviderContractMatrixRoutes = (options) => {
|
|
41858
42175
|
const path = options.path ?? "/api/provider-contracts";
|
|
41859
42176
|
const htmlPath = options.htmlPath ?? "/provider-contracts";
|
|
41860
|
-
const routes = new
|
|
42177
|
+
const routes = new Elysia63({
|
|
41861
42178
|
name: options.name ?? "absolutejs-voice-provider-contract-matrix"
|
|
41862
42179
|
});
|
|
41863
42180
|
const jsonHandler = createVoiceProviderContractMatrixJSONHandler(options.matrix);
|
|
@@ -41975,7 +42292,7 @@ var assertVoiceProviderStackEvidence = (report, input = {}) => {
|
|
|
41975
42292
|
return assertion;
|
|
41976
42293
|
};
|
|
41977
42294
|
// src/opsConsoleRoutes.ts
|
|
41978
|
-
import { Elysia as
|
|
42295
|
+
import { Elysia as Elysia64 } from "elysia";
|
|
41979
42296
|
var DEFAULT_LINKS = [
|
|
41980
42297
|
{
|
|
41981
42298
|
description: "Quality gates for CI, deploy checks, and production readiness.",
|
|
@@ -42010,7 +42327,7 @@ var DEFAULT_LINKS = [
|
|
|
42010
42327
|
label: "Handoffs"
|
|
42011
42328
|
}
|
|
42012
42329
|
];
|
|
42013
|
-
var
|
|
42330
|
+
var escapeHtml60 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
42014
42331
|
var countProviderStatuses = (providers) => {
|
|
42015
42332
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
42016
42333
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -42079,20 +42396,20 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
42079
42396
|
trace
|
|
42080
42397
|
};
|
|
42081
42398
|
};
|
|
42082
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
42399
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml60(input.label)}</span><strong>${escapeHtml60(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml60(input.status)}">${escapeHtml60(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml60(input.href)}">Open</a>` : ""}</article>`;
|
|
42083
42400
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
42084
42401
|
const links = report.links.map((link) => `<article class="surface">
|
|
42085
|
-
<div><h2>${
|
|
42086
|
-
<p><a href="${
|
|
42402
|
+
<div><h2>${escapeHtml60(link.label)}</h2>${link.description ? `<p>${escapeHtml60(link.description)}</p>` : ""}</div>
|
|
42403
|
+
<p><a href="${escapeHtml60(link.href)}">Open ${escapeHtml60(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml60(link.statusHref)}">Status</a>` : ""}</p>
|
|
42087
42404
|
</article>`).join("");
|
|
42088
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
42089
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
42405
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml60(session.sessionId)}</td><td>${escapeHtml60(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml60(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
42406
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml60(event.kind)}</td><td>${escapeHtml60(event.provider ?? "unknown")}</td><td>${escapeHtml60(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml60(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
42090
42407
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
42091
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
42408
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml60(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>${escapeHtml60(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 ${escapeHtml60(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>`;
|
|
42092
42409
|
};
|
|
42093
42410
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
42094
42411
|
const path = options.path ?? "/ops-console";
|
|
42095
|
-
const routes = new
|
|
42412
|
+
const routes = new Elysia64({
|
|
42096
42413
|
name: options.name ?? "absolutejs-voice-ops-console"
|
|
42097
42414
|
});
|
|
42098
42415
|
const getReport = () => buildVoiceOpsConsoleReport(options);
|
|
@@ -42109,7 +42426,7 @@ var createVoiceOpsConsoleRoutes = (options) => {
|
|
|
42109
42426
|
return routes;
|
|
42110
42427
|
};
|
|
42111
42428
|
// src/incidentBundle.ts
|
|
42112
|
-
import { Elysia as
|
|
42429
|
+
import { Elysia as Elysia65 } from "elysia";
|
|
42113
42430
|
var filterIncidentBundleArtifacts = (artifacts, filter = {}) => artifacts.filter((artifact) => {
|
|
42114
42431
|
if (filter.sessionId && artifact.sessionId !== filter.sessionId) {
|
|
42115
42432
|
return false;
|
|
@@ -42326,7 +42643,7 @@ var buildVoiceIncidentBundle = async (options) => {
|
|
|
42326
42643
|
var createVoiceIncidentBundleRoutes = (options) => {
|
|
42327
42644
|
const path = options.path ?? "/api/voice-incidents/:sessionId";
|
|
42328
42645
|
const markdownPath = options.markdownPath === undefined ? "/voice-incidents/:sessionId/markdown" : options.markdownPath;
|
|
42329
|
-
const routes = new
|
|
42646
|
+
const routes = new Elysia65({
|
|
42330
42647
|
name: options.name ?? "absolutejs-voice-incident-bundle"
|
|
42331
42648
|
});
|
|
42332
42649
|
const getSessionId = (params) => params.sessionId ?? "";
|
|
@@ -42527,19 +42844,19 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
42527
42844
|
};
|
|
42528
42845
|
};
|
|
42529
42846
|
// src/opsStatusRoutes.ts
|
|
42530
|
-
import { Elysia as
|
|
42531
|
-
var
|
|
42847
|
+
import { Elysia as Elysia66 } from "elysia";
|
|
42848
|
+
var escapeHtml61 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
42532
42849
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
42533
42850
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
42534
42851
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
42535
42852
|
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;
|
|
42536
|
-
return `<article class="surface ${
|
|
42853
|
+
return `<article class="surface ${escapeHtml61(surface.status)}"><span>${escapeHtml61(surface.status.toUpperCase())}</span><h2>${escapeHtml61(key)}</h2><strong>${escapeHtml61(value)}</strong></article>`;
|
|
42537
42854
|
}).join("");
|
|
42538
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
42855
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml61(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>${escapeHtml61(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml61(report.status)}">Overall: ${escapeHtml61(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>`;
|
|
42539
42856
|
};
|
|
42540
42857
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
42541
42858
|
const path = options.path ?? "/api/voice/ops-status";
|
|
42542
|
-
const routes = new
|
|
42859
|
+
const routes = new Elysia66({
|
|
42543
42860
|
name: options.name ?? "absolutejs-voice-ops-status"
|
|
42544
42861
|
});
|
|
42545
42862
|
routes.get(path, async () => summarizeVoiceOpsStatus(options));
|
|
@@ -42972,8 +43289,8 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
42972
43289
|
};
|
|
42973
43290
|
};
|
|
42974
43291
|
// src/traceDeliveryRoutes.ts
|
|
42975
|
-
import { Elysia as
|
|
42976
|
-
var
|
|
43292
|
+
import { Elysia as Elysia67 } from "elysia";
|
|
43293
|
+
var escapeHtml62 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
42977
43294
|
var getString20 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
42978
43295
|
var getNumber12 = (value) => {
|
|
42979
43296
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -43054,14 +43371,14 @@ var renderSinkResults2 = (delivery) => {
|
|
|
43054
43371
|
if (entries.length === 0) {
|
|
43055
43372
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
43056
43373
|
}
|
|
43057
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
43374
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml62(sinkId)}</strong>: ${escapeHtml62(result.status)}${result.deliveredTo ? ` to ${escapeHtml62(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml62(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
43058
43375
|
};
|
|
43059
|
-
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
43376
|
+
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml62(event.type)} <small>${escapeHtml62(event.id)}</small>${event.sessionId ? ` session=${escapeHtml62(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
|
|
43060
43377
|
var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
|
|
43061
43378
|
const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
|
|
43062
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
43063
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
43064
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
43379
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml62(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
|
|
43380
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml62(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml62(delivery.deliveryStatus)}</span><h2>${escapeHtml62(delivery.id)}</h2><p>${escapeHtml62(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml62(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml62(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
|
|
43381
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml62(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>${escapeHtml62(title)}</h1><p>Checked ${escapeHtml62(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>`;
|
|
43065
43382
|
};
|
|
43066
43383
|
var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
|
|
43067
43384
|
var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -43081,7 +43398,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
43081
43398
|
const path = options.path ?? "/api/voice-trace-deliveries";
|
|
43082
43399
|
const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
|
|
43083
43400
|
const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
|
|
43084
|
-
const routes = new
|
|
43401
|
+
const routes = new Elysia67({
|
|
43085
43402
|
name: options.name ?? "absolutejs-voice-trace-deliveries"
|
|
43086
43403
|
}).get(path, createVoiceTraceDeliveryJSONHandler(options));
|
|
43087
43404
|
if (htmlPath !== false) {
|
|
@@ -43234,7 +43551,7 @@ var createVoiceMemoryStore = () => {
|
|
|
43234
43551
|
return { get, getOrCreate, list, remove, set };
|
|
43235
43552
|
};
|
|
43236
43553
|
// src/opsWebhook.ts
|
|
43237
|
-
import { Elysia as
|
|
43554
|
+
import { Elysia as Elysia68 } from "elysia";
|
|
43238
43555
|
var toHex7 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
43239
43556
|
var signVoiceOpsWebhookBody = async (input) => {
|
|
43240
43557
|
const encoder2 = new TextEncoder;
|
|
@@ -43364,7 +43681,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
|
|
|
43364
43681
|
};
|
|
43365
43682
|
var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
43366
43683
|
const path = options.path ?? "/api/voice-ops/webhook";
|
|
43367
|
-
return new
|
|
43684
|
+
return new Elysia68().post(path, async ({ body, request, set }) => {
|
|
43368
43685
|
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
|
|
43369
43686
|
if (options.signingSecret) {
|
|
43370
43687
|
const verification = await verifyVoiceOpsWebhookSignature({
|
|
@@ -43819,7 +44136,7 @@ var resolveVoiceOpsPreset = (name, overrides = {}) => {
|
|
|
43819
44136
|
};
|
|
43820
44137
|
};
|
|
43821
44138
|
// src/postCallAnalysis.ts
|
|
43822
|
-
import { Elysia as
|
|
44139
|
+
import { Elysia as Elysia69 } from "elysia";
|
|
43823
44140
|
var isStore = (value) => Boolean(value) && typeof value === "object" && value !== null && ("list" in value);
|
|
43824
44141
|
var asArray = async (value) => Array.isArray(value) ? value : isStore(value) ? await value.list() : [];
|
|
43825
44142
|
var getPathValue3 = (source, path) => {
|
|
@@ -43998,7 +44315,7 @@ var resolvePostCallAnalysisReport = async (options, input) => {
|
|
|
43998
44315
|
};
|
|
43999
44316
|
var createVoicePostCallAnalysisRoutes = (options = {}) => {
|
|
44000
44317
|
const path = options.path ?? "/api/voice/post-call-analysis";
|
|
44001
|
-
const routes = new
|
|
44318
|
+
const routes = new Elysia69({
|
|
44002
44319
|
name: options.name ?? "absolutejs-voice-post-call-analysis"
|
|
44003
44320
|
});
|
|
44004
44321
|
routes.get(path, async ({ query }) => {
|
|
@@ -44023,7 +44340,7 @@ var createVoicePostCallAnalysisRoutes = (options = {}) => {
|
|
|
44023
44340
|
return routes;
|
|
44024
44341
|
};
|
|
44025
44342
|
// src/guardrails.ts
|
|
44026
|
-
import { Elysia as
|
|
44343
|
+
import { Elysia as Elysia70 } from "elysia";
|
|
44027
44344
|
var stringifyContent = (value) => typeof value === "string" ? value : JSON.stringify(value) ?? "";
|
|
44028
44345
|
var appliesToStage = (rule, stage) => !rule.stages || rule.stages.length === 0 || rule.stages.includes(stage);
|
|
44029
44346
|
var matchesRule = async (rule, input) => {
|
|
@@ -44325,7 +44642,7 @@ var resolveGuardrailReport = async (options, input) => {
|
|
|
44325
44642
|
};
|
|
44326
44643
|
var createVoiceGuardrailRoutes = (options = {}) => {
|
|
44327
44644
|
const path = options.path ?? "/api/voice/guardrails";
|
|
44328
|
-
const routes = new
|
|
44645
|
+
const routes = new Elysia70({
|
|
44329
44646
|
name: options.name ?? "absolutejs-voice-guardrails"
|
|
44330
44647
|
});
|
|
44331
44648
|
routes.all(path, async ({ request }) => {
|
|
@@ -45104,7 +45421,7 @@ var shapeTelephonyAssistantText = (text, options = {}) => {
|
|
|
45104
45421
|
return ensureTerminalPunctuation(normalizeWhitespace(limitedChars));
|
|
45105
45422
|
};
|
|
45106
45423
|
// src/proofPack.ts
|
|
45107
|
-
import { Elysia as
|
|
45424
|
+
import { Elysia as Elysia71 } from "elysia";
|
|
45108
45425
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
45109
45426
|
import { dirname as dirname3, join as join4 } from "path";
|
|
45110
45427
|
var toGeneratedAt = (value) => value === undefined ? new Date().toISOString() : typeof value === "number" ? new Date(value).toISOString() : value;
|
|
@@ -45690,7 +46007,7 @@ var createVoiceProofPackArtifacts = (input) => [
|
|
|
45690
46007
|
var createVoiceProofPackRoutes = (options) => {
|
|
45691
46008
|
const jsonPath = options.jsonPath ?? "/api/voice/proof-pack";
|
|
45692
46009
|
const markdownPath = options.markdownPath ?? "/voice/proof-pack.md";
|
|
45693
|
-
const app = new
|
|
46010
|
+
const app = new Elysia71({ name: options.name ?? "voice-proof-pack" });
|
|
45694
46011
|
if (jsonPath !== false) {
|
|
45695
46012
|
app.get(jsonPath, async () => new Response(JSON.stringify(await resolveProofPack(options.source), null, 2), {
|
|
45696
46013
|
headers: {
|
|
@@ -46791,7 +47108,7 @@ var buildVoiceMultilingualProofReadinessCheck = (report, options = {}) => {
|
|
|
46791
47108
|
};
|
|
46792
47109
|
};
|
|
46793
47110
|
// src/monitor.ts
|
|
46794
|
-
import { Elysia as
|
|
47111
|
+
import { Elysia as Elysia72 } from "elysia";
|
|
46795
47112
|
var buildAudioFanout = () => {
|
|
46796
47113
|
const handlers = new Set;
|
|
46797
47114
|
return {
|
|
@@ -47080,7 +47397,7 @@ var createVoiceLiveMonitorRoutes = (options) => {
|
|
|
47080
47397
|
transfer: options.controlHandlers?.transfer ?? buildDefaultControlHandler("transfer"),
|
|
47081
47398
|
voicemail: options.controlHandlers?.voicemail ?? buildDefaultControlHandler("voicemail")
|
|
47082
47399
|
};
|
|
47083
|
-
const app = new
|
|
47400
|
+
const app = new Elysia72({ name: "absolutejs-voice-monitor" });
|
|
47084
47401
|
const unsubscribers = new WeakMap;
|
|
47085
47402
|
if (listenPath !== false && listenPath.length > 0) {
|
|
47086
47403
|
app.ws(`/${listenPath.replace(/^\/+/, "")}`, {
|
|
@@ -47253,6 +47570,8 @@ var createVoiceLiveMonitorRoutes = (options) => {
|
|
|
47253
47570
|
export {
|
|
47254
47571
|
writeVoiceProofPack,
|
|
47255
47572
|
writeVoiceMediaPipelineArtifacts,
|
|
47573
|
+
wrapVoiceHTMLWithHTMXPolling,
|
|
47574
|
+
wrapVoiceHTMLInHTMXContainer,
|
|
47256
47575
|
withVoiceOpsTaskId,
|
|
47257
47576
|
withVoiceIntegrationEventId,
|
|
47258
47577
|
voiceTelephonyOutcomeToRouteResult,
|
|
@@ -47352,6 +47671,7 @@ export {
|
|
|
47352
47671
|
resolveVoiceOpsPreset,
|
|
47353
47672
|
resolveVoiceMonitorIssue,
|
|
47354
47673
|
resolveVoiceDiagnosticsTraceFilter,
|
|
47674
|
+
resolveVoiceDashboardRenderers,
|
|
47355
47675
|
resolveVoiceAuditTrailFilter,
|
|
47356
47676
|
resolveVoiceAuditDeliveryFilter,
|
|
47357
47677
|
resolveVoiceAssistantMode,
|
|
@@ -47382,6 +47702,8 @@ export {
|
|
|
47382
47702
|
renderVoiceScenarioFixtureEvalHTML,
|
|
47383
47703
|
renderVoiceScenarioEvalHTML,
|
|
47384
47704
|
renderVoiceResilienceHTML,
|
|
47705
|
+
renderVoiceReplayTimelineHTMX,
|
|
47706
|
+
renderVoiceReplayTimelineFromArtifact,
|
|
47385
47707
|
renderVoiceReconnectContractHTML,
|
|
47386
47708
|
renderVoiceRealtimeProviderContractHTML,
|
|
47387
47709
|
renderVoiceRealtimeChannelMarkdown,
|
|
@@ -47427,6 +47749,9 @@ export {
|
|
|
47427
47749
|
renderVoiceMediaPipelineMarkdown,
|
|
47428
47750
|
renderVoiceMediaPipelineHTML,
|
|
47429
47751
|
renderVoiceLiveLatencyHTML,
|
|
47752
|
+
renderVoiceLiveCallViewerHTMX,
|
|
47753
|
+
renderVoiceLiveCallViewerFromViewer,
|
|
47754
|
+
renderVoiceLiveCallViewerFromState,
|
|
47430
47755
|
renderVoiceLatencySLOMarkdown,
|
|
47431
47756
|
renderVoiceIncidentTimelineMarkdown,
|
|
47432
47757
|
renderVoiceIncidentTimelineHTML,
|
|
@@ -47443,6 +47768,8 @@ export {
|
|
|
47443
47768
|
renderVoiceDeliveryRuntimeHTML,
|
|
47444
47769
|
renderVoiceDataControlMarkdown,
|
|
47445
47770
|
renderVoiceDataControlHTML,
|
|
47771
|
+
renderVoiceCostDashboardHTMX,
|
|
47772
|
+
renderVoiceCostDashboardFromEvents,
|
|
47446
47773
|
renderVoiceCompetitiveCoverageMarkdown,
|
|
47447
47774
|
renderVoiceCompetitiveCoverageHTML,
|
|
47448
47775
|
renderVoiceCampaignsHTML,
|
|
@@ -47676,6 +48003,7 @@ export {
|
|
|
47676
48003
|
createVoiceReviewSavedEvent,
|
|
47677
48004
|
createVoiceRetentionScheduler,
|
|
47678
48005
|
createVoiceResilienceRoutes,
|
|
48006
|
+
createVoiceReplayTimelineHTMXRoute,
|
|
47679
48007
|
createVoiceRedisTelnyxWebhookEventStore,
|
|
47680
48008
|
createVoiceRedisTelephonyWebhookIdempotencyStore,
|
|
47681
48009
|
createVoiceRedisTaskLeaseCoordinator,
|
|
@@ -47800,6 +48128,7 @@ export {
|
|
|
47800
48128
|
createVoiceLiveOpsController,
|
|
47801
48129
|
createVoiceLiveMonitorRoutes,
|
|
47802
48130
|
createVoiceLiveLatencyRoutes,
|
|
48131
|
+
createVoiceLiveCallViewerHTMXRoute,
|
|
47803
48132
|
createVoiceLinearIssueUpdateSink,
|
|
47804
48133
|
createVoiceLinearIssueSyncSinks,
|
|
47805
48134
|
createVoiceLinearIssueSink,
|
|
@@ -47823,6 +48152,8 @@ export {
|
|
|
47823
48152
|
createVoiceHandoffDeliveryWorkerLoop,
|
|
47824
48153
|
createVoiceHandoffDeliveryWorker,
|
|
47825
48154
|
createVoiceHandoffDeliveryRecord,
|
|
48155
|
+
createVoiceHTMXDashboardRoutesFromStores,
|
|
48156
|
+
createVoiceHTMXDashboardRoutes,
|
|
47826
48157
|
createVoiceHMACAuthVerifier,
|
|
47827
48158
|
createVoiceGuardrailRuntime,
|
|
47828
48159
|
createVoiceGuardrailRoutes,
|
|
@@ -47861,6 +48192,7 @@ export {
|
|
|
47861
48192
|
createVoiceDeliveryRuntime,
|
|
47862
48193
|
createVoiceDataControlRoutes,
|
|
47863
48194
|
createVoiceDTMFTool,
|
|
48195
|
+
createVoiceCostDashboardHTMXRoute,
|
|
47864
48196
|
createVoiceCostAccountant,
|
|
47865
48197
|
createVoiceCompetitiveCoverageRoutes,
|
|
47866
48198
|
createVoiceCampaignWorkerLoop,
|
|
@@ -47929,6 +48261,7 @@ export {
|
|
|
47929
48261
|
createMemoryVoiceTelnyxWebhookEventStore,
|
|
47930
48262
|
createMemoryVoiceTelephonyWebhookIdempotencyStore,
|
|
47931
48263
|
createMemoryVoicePlivoWebhookNonceStore,
|
|
48264
|
+
createLiveCallViewerFromOptions,
|
|
47932
48265
|
createLiveCallViewer,
|
|
47933
48266
|
createJSONVoiceAssistantModel,
|
|
47934
48267
|
createInMemoryVoiceCallQuota,
|
|
@@ -48017,6 +48350,7 @@ export {
|
|
|
48017
48350
|
buildVoiceIncidentRecoveryOutcomeReadinessCheck,
|
|
48018
48351
|
buildVoiceIncidentBundle,
|
|
48019
48352
|
buildVoiceIOProviderRouterTraceEvent,
|
|
48353
|
+
buildVoiceHTMXAttributes,
|
|
48020
48354
|
buildVoiceGuardrailReport,
|
|
48021
48355
|
buildVoiceFailureReplay,
|
|
48022
48356
|
buildVoiceDiagnosticsMarkdown,
|