@absolutejs/voice 0.0.22-beta.327 → 0.0.22-beta.328
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/index.d.ts +1 -1
- package/dist/index.js +92 -3
- package/dist/operationsRecord.d.ts +22 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -141,7 +141,7 @@ export type { VoiceProductionReadinessAction, VoiceProductionReadinessAuditOptio
|
|
|
141
141
|
export type { VoiceMonitorDefinition, VoiceMonitorEvaluation, VoiceMonitorEvaluationInput, VoiceMonitorIssue, VoiceMonitorIssueStatus, VoiceMonitorIssueStore, VoiceMonitorNotifier, VoiceMonitorNotifierDeliveryInput, VoiceMonitorNotifierDeliveryOptions, VoiceMonitorNotifierDeliveryReceipt, VoiceMonitorNotifierDeliveryReceiptStore, VoiceMonitorNotifierDeliveryReport, VoiceMonitorNotifierDeliveryResult, VoiceMonitorRoutesOptions, VoiceMonitorRun, VoiceMonitorRunOptions, VoiceMonitorRunReport, VoiceMonitorRunner, VoiceMonitorRunnerOptions, VoiceMonitorRunnerRoutesOptions, VoiceMonitorRunnerTickResult, VoiceMonitorSeverity, VoiceMonitorStatus, VoiceMonitorWebhookNotifierOptions } from './voiceMonitoring';
|
|
142
142
|
export type { VoiceReadinessProfileName, VoiceReadinessProfileOptions, VoiceReadinessProfileRecommendation, VoiceReadinessProfileRecommendationScore, VoiceReadinessProfileRoutesOptions } from './readinessProfiles';
|
|
143
143
|
export type { VoiceProviderStackChoice, VoiceProviderStackCapabilities, VoiceProviderStackCapabilityGap, VoiceProviderStackCapabilityGapInput, VoiceProviderStackCapabilityGapReport, VoiceProviderContractCheck, VoiceProviderContractCheckStatus, VoiceProviderContractDefinition, VoiceProviderContractMatrixAssertionInput, VoiceProviderContractMatrixAssertionReport, VoiceProviderContractMatrixHandlerOptions, VoiceProviderContractMatrixHTMLHandlerOptions, VoiceProviderContractMatrixInput, VoiceProviderContractMatrixPresetOptions, VoiceProviderContractMatrixReport, VoiceProviderContractMatrixRoutesOptions, VoiceProviderContractMatrixRow, VoiceProviderStackAssertionInput, VoiceProviderStackAssertionReport, VoiceProviderStackInput, VoiceProviderStackKind, VoiceProviderStackRecommendation } from './providerStackRecommendations';
|
|
144
|
-
export type { VoiceOperationsRecord, VoiceOperationsRecordAgentHandoff, VoiceOperationsRecordAuditSummary, VoiceOperationsRecordGuardrailAssertionInput, VoiceOperationsRecordGuardrailAssertionReport, VoiceOperationsRecordGuardrailDecision, VoiceOperationsRecordGuardrailFinding, VoiceOperationsRecordGuardrailSummary, VoiceOperationsRecordIntegrationEventSummary, VoiceOperationsRecordOptions, VoiceOperationsRecordOutcome, VoiceOperationsRecordProviderDecision, VoiceOperationsRecordProviderDecisionRecoveryStatus, VoiceOperationsRecordProviderDecisionSummary, VoiceOperationsRecordProviderRecoveryAssertionInput, VoiceOperationsRecordProviderRecoveryAssertionReport, VoiceOperationsRecordReviewSummary, VoiceOperationsRecordRoutesOptions, VoiceOperationsRecordStatus, VoiceOperationsRecordTaskSummary, VoiceOperationsRecordTranscriptTurn, VoiceOperationsRecordTool } from './operationsRecord';
|
|
144
|
+
export type { VoiceOperationsRecord, VoiceOperationsRecordAgentHandoff, VoiceOperationsRecordAuditSummary, VoiceOperationsRecordGuardrailAssertionInput, VoiceOperationsRecordGuardrailAssertionReport, VoiceOperationsRecordGuardrailDecision, VoiceOperationsRecordGuardrailFinding, VoiceOperationsRecordGuardrailSummary, VoiceOperationsRecordIntegrationEventSummary, VoiceOperationsRecordOptions, VoiceOperationsRecordOutcome, VoiceOperationsRecordProviderDecision, VoiceOperationsRecordProviderDecisionRecoveryStatus, VoiceOperationsRecordProviderDecisionSummary, VoiceOperationsRecordProviderRecoveryAssertionInput, VoiceOperationsRecordProviderRecoveryAssertionReport, VoiceOperationsRecordReviewSummary, VoiceOperationsRecordRoutesOptions, VoiceOperationsRecordStatus, VoiceOperationsRecordTaskSummary, VoiceOperationsRecordTelephonyMediaEvent, VoiceOperationsRecordTelephonyMediaSummary, VoiceOperationsRecordTranscriptTurn, VoiceOperationsRecordTool } from './operationsRecord';
|
|
145
145
|
export type { VoiceObservabilityExportArtifact, VoiceObservabilityExportArtifactChecksum, VoiceObservabilityExportArtifactFreshness, VoiceObservabilityExportArtifactIndex, VoiceObservabilityExportArtifactIndexItem, VoiceObservabilityExportArtifactKind, VoiceObservabilityExportDeliveryAssertionInput, VoiceObservabilityExportDeliveryAssertionReport, VoiceObservabilityExportDeliverySummary, VoiceObservabilityExportDeliveryDestination, VoiceObservabilityExportDeliveryDestinationResult, VoiceObservabilityExportDeliveryHistory, VoiceObservabilityExportDeliveryOptions, VoiceObservabilityExportDeliveryReceipt, VoiceObservabilityExportDeliveryReceiptStore, VoiceObservabilityExportDeliveryReport, VoiceObservabilityExportEnvelope, VoiceObservabilityExportIssue, VoiceObservabilityExportIssueCode, VoiceObservabilityExportOptions, VoiceObservabilityExportIngestedRecordKind, VoiceObservabilityExportRedactionSummary, VoiceObservabilityExportRecordValidationOptions, VoiceObservabilityExportReplayIssue, VoiceObservabilityExportReplayIssueCode, VoiceObservabilityExportReplayAssertionInput, VoiceObservabilityExportReplayAssertionReport, VoiceObservabilityExportReplayRecords, VoiceObservabilityExportReplayReport, VoiceObservabilityExportReplayRoutesOptions, VoiceObservabilityExportReplaySource, VoiceObservabilityExportReport, VoiceObservabilityExportRoutesOptions, VoiceObservabilityExportSchema, VoiceObservabilityExportStatus, VoiceObservabilityExportValidationIssue, VoiceObservabilityExportValidationResult } from './observabilityExport';
|
|
146
146
|
export type { VoiceOpsRecoveryFailedSession, VoiceOpsRecoveryInterventionSummary, VoiceOpsRecoveryIssue, VoiceOpsRecoveryIssueCode, VoiceOpsRecoveryLinks, VoiceOpsRecoveryProviderSummary, VoiceOpsRecoveryReport, VoiceOpsRecoveryReportOptions, VoiceOpsRecoveryRoutesOptions, VoiceOpsRecoveryStatus } from './opsRecovery';
|
|
147
147
|
export type { StoredVoiceIncidentBundleArtifact, VoiceIncidentBundle, VoiceIncidentBundleArtifactOptions, VoiceIncidentBundleFormat, VoiceIncidentBundleOptions, VoiceIncidentBundleRetentionOptions, VoiceIncidentBundleRetentionReport, VoiceIncidentBundleRoutesOptions, VoiceIncidentBundleStore, VoiceIncidentBundleStoreFilter, VoiceIncidentBundleSummary } from './incidentBundle';
|
package/dist/index.js
CHANGED
|
@@ -27765,6 +27765,7 @@ var createVoiceTraceTimelineRoutes = (options) => {
|
|
|
27765
27765
|
var getString17 = (value) => typeof value === "string" ? value : undefined;
|
|
27766
27766
|
var getNumber10 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
27767
27767
|
var getBoolean3 = (value) => typeof value === "boolean" ? value : undefined;
|
|
27768
|
+
var getRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
27768
27769
|
var countOutcome = (events, outcome) => events.filter((event) => event.outcome === outcome).length;
|
|
27769
27770
|
var matchesSessionScopedId = (id, sessionId) => id === sessionId || id.startsWith(`${sessionId}:`);
|
|
27770
27771
|
var hasPayloadValue = (payload, key, values) => {
|
|
@@ -27823,6 +27824,56 @@ var toGuardrailDecision = (event) => ({
|
|
|
27823
27824
|
toolName: getString17(event.payload.toolName),
|
|
27824
27825
|
turnId: event.turnId
|
|
27825
27826
|
});
|
|
27827
|
+
var decodeBase64Bytes = (value) => {
|
|
27828
|
+
if (typeof value !== "string") {
|
|
27829
|
+
return 0;
|
|
27830
|
+
}
|
|
27831
|
+
try {
|
|
27832
|
+
return Buffer.from(value, "base64").byteLength;
|
|
27833
|
+
} catch {
|
|
27834
|
+
return 0;
|
|
27835
|
+
}
|
|
27836
|
+
};
|
|
27837
|
+
var toTelephonyMediaEvent = (event) => {
|
|
27838
|
+
if (event.type !== "client.telephony_media") {
|
|
27839
|
+
return;
|
|
27840
|
+
}
|
|
27841
|
+
const envelope = getRecord(event.payload.envelope);
|
|
27842
|
+
const start = getRecord(envelope?.start);
|
|
27843
|
+
const media = getRecord(envelope?.media);
|
|
27844
|
+
const stop = getRecord(envelope?.stop);
|
|
27845
|
+
const error = getRecord(envelope?.error);
|
|
27846
|
+
const eventName = getString17(event.payload.event) ?? getString17(envelope?.event) ?? getString17(event.payload.kind) ?? "unknown";
|
|
27847
|
+
const streamId = getString17(event.payload.streamId) ?? getString17(envelope?.streamSid) ?? getString17(start?.streamSid);
|
|
27848
|
+
const callSid = getString17(event.payload.callSid) ?? getString17(start?.callSid) ?? getString17(stop?.callSid);
|
|
27849
|
+
const sequenceNumber = getString17(media?.sequenceNumber) ?? getString17(envelope?.sequenceNumber);
|
|
27850
|
+
const direction = getString17(media?.track) ?? getString17(media?.direction) ?? getString17(event.payload.direction);
|
|
27851
|
+
return {
|
|
27852
|
+
at: event.at,
|
|
27853
|
+
audioBytes: getNumber10(event.payload.audioBytes) ?? getNumber10(media?.audioBytes) ?? decodeBase64Bytes(media?.payload),
|
|
27854
|
+
callSid,
|
|
27855
|
+
carrier: getString17(event.payload.carrier),
|
|
27856
|
+
direction,
|
|
27857
|
+
event: eventName,
|
|
27858
|
+
sequenceNumber,
|
|
27859
|
+
streamId
|
|
27860
|
+
};
|
|
27861
|
+
};
|
|
27862
|
+
var summarizeTelephonyMedia = (events) => {
|
|
27863
|
+
const mediaEvents = events.map(toTelephonyMediaEvent).filter((event) => event !== undefined);
|
|
27864
|
+
const eventNames = mediaEvents.map((event) => event.event.toLowerCase());
|
|
27865
|
+
return {
|
|
27866
|
+
audioBytes: mediaEvents.reduce((total, event) => total + event.audioBytes, 0),
|
|
27867
|
+
carriers: uniqueSorted8(mediaEvents.map((event) => event.carrier)),
|
|
27868
|
+
errors: eventNames.filter((event) => event === "error").length,
|
|
27869
|
+
events: mediaEvents,
|
|
27870
|
+
media: eventNames.filter((event) => event === "media").length,
|
|
27871
|
+
starts: eventNames.filter((event) => event === "start").length,
|
|
27872
|
+
stops: eventNames.filter((event) => event === "stop").length,
|
|
27873
|
+
streamIds: uniqueSorted8(mediaEvents.map((event) => event.streamId)),
|
|
27874
|
+
total: mediaEvents.length
|
|
27875
|
+
};
|
|
27876
|
+
};
|
|
27826
27877
|
var summarizeGuardrails = (events) => {
|
|
27827
27878
|
const decisions = events.filter((event) => event.type === "assistant.guardrail").map(toGuardrailDecision);
|
|
27828
27879
|
const isBlocked = (decision) => decision.allowed === false || decision.status === "fail";
|
|
@@ -27970,6 +28021,7 @@ var buildVoiceOperationsRecord = async (options) => {
|
|
|
27970
28021
|
tasks,
|
|
27971
28022
|
total: tasks.length
|
|
27972
28023
|
} : undefined,
|
|
28024
|
+
telephonyMedia: summarizeTelephonyMedia(traceEvents),
|
|
27973
28025
|
timeline: timelineSession?.events ?? [],
|
|
27974
28026
|
tools: traceEvents.filter((event) => event.type === "agent.tool").map(toTool),
|
|
27975
28027
|
traceEvents,
|
|
@@ -28174,6 +28226,27 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
|
|
|
28174
28226
|
`degraded=${String(providerDecisionSummary.degraded)}`,
|
|
28175
28227
|
`errors=${String(providerDecisionSummary.errors)}`
|
|
28176
28228
|
].join("; ");
|
|
28229
|
+
const telephonyMediaLine = [
|
|
28230
|
+
`events=${String(record.telephonyMedia.total)}`,
|
|
28231
|
+
`starts=${String(record.telephonyMedia.starts)}`,
|
|
28232
|
+
`media=${String(record.telephonyMedia.media)}`,
|
|
28233
|
+
`stops=${String(record.telephonyMedia.stops)}`,
|
|
28234
|
+
`errors=${String(record.telephonyMedia.errors)}`,
|
|
28235
|
+
`audioBytes=${String(record.telephonyMedia.audioBytes)}`,
|
|
28236
|
+
`carriers=${record.telephonyMedia.carriers.join(", ") || "none"}`,
|
|
28237
|
+
`streams=${record.telephonyMedia.streamIds.join(", ") || "none"}`
|
|
28238
|
+
].join("; ");
|
|
28239
|
+
const telephonyMediaLines = record.telephonyMedia.events.length ? record.telephonyMedia.events.slice(0, 12).map((event) => {
|
|
28240
|
+
const parts = [
|
|
28241
|
+
event.carrier ? `carrier=${event.carrier}` : undefined,
|
|
28242
|
+
event.streamId ? `stream=${event.streamId}` : undefined,
|
|
28243
|
+
event.callSid ? `call=${event.callSid}` : undefined,
|
|
28244
|
+
event.direction ? `direction=${event.direction}` : undefined,
|
|
28245
|
+
event.sequenceNumber ? `seq=${event.sequenceNumber}` : undefined,
|
|
28246
|
+
`audioBytes=${String(event.audioBytes)}`
|
|
28247
|
+
].filter((part) => typeof part === "string");
|
|
28248
|
+
return `- ${event.event}: ${parts.join("; ")}`;
|
|
28249
|
+
}) : ["- none recorded"];
|
|
28177
28250
|
return [
|
|
28178
28251
|
`# Voice incident handoff: ${record.sessionId}`,
|
|
28179
28252
|
"",
|
|
@@ -28187,11 +28260,16 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
|
|
|
28187
28260
|
`- Top errors: ${topErrors.join("; ") || "none"}`,
|
|
28188
28261
|
`- Guardrails: ${String(record.guardrails.blocked)} blocked / ${String(record.guardrails.warned)} warned / ${String(record.guardrails.total)} decisions`,
|
|
28189
28262
|
`- Provider recovery: ${providerRecoveryLine}`,
|
|
28263
|
+
`- Telephony media: ${telephonyMediaLine}`,
|
|
28190
28264
|
"",
|
|
28191
28265
|
"## Provider decisions",
|
|
28192
28266
|
"",
|
|
28193
28267
|
...providerDecisionLines,
|
|
28194
28268
|
"",
|
|
28269
|
+
"## Telephony media",
|
|
28270
|
+
"",
|
|
28271
|
+
...telephonyMediaLines,
|
|
28272
|
+
"",
|
|
28195
28273
|
renderVoiceOperationsRecordGuardrailMarkdown(record),
|
|
28196
28274
|
"",
|
|
28197
28275
|
"## Next checks",
|
|
@@ -28234,6 +28312,17 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
28234
28312
|
const findings = decision.findings.map((finding) => finding.label ?? finding.ruleId ?? finding.action).filter((value) => typeof value === "string").join(", ") || "none";
|
|
28235
28313
|
return `<li><strong>assistant.guardrail ${escapeHtml48(decision.stage ?? "unknown")}</strong> <span>${escapeHtml48(decision.status ?? "")}</span><p>Allowed: ${escapeHtml48(String(decision.allowed ?? "unknown"))} \xB7 Proof: ${escapeHtml48(decision.proof ?? "runtime")}${decision.turnId ? ` \xB7 Turn: ${escapeHtml48(decision.turnId)}` : ""}</p><p>${escapeHtml48(findings)}</p></li>`;
|
|
28236
28314
|
}).join("") : "<li>No assistant.guardrail events recorded.</li>";
|
|
28315
|
+
const telephonyMedia = record.telephonyMedia.events.length ? record.telephonyMedia.events.slice(0, 50).map((event) => {
|
|
28316
|
+
const details = [
|
|
28317
|
+
event.carrier ? `Carrier: ${event.carrier}` : undefined,
|
|
28318
|
+
event.streamId ? `Stream: ${event.streamId}` : undefined,
|
|
28319
|
+
event.callSid ? `Call: ${event.callSid}` : undefined,
|
|
28320
|
+
event.direction ? `Direction: ${event.direction}` : undefined,
|
|
28321
|
+
event.sequenceNumber ? `Seq: ${event.sequenceNumber}` : undefined,
|
|
28322
|
+
`Audio bytes: ${String(event.audioBytes)}`
|
|
28323
|
+
].filter((detail) => typeof detail === "string");
|
|
28324
|
+
return `<li><strong>${escapeHtml48(event.event)}</strong> <span>${escapeHtml48(new Date(event.at).toLocaleString())}</span><p>${escapeHtml48(details.join(" \xB7 "))}</p></li>`;
|
|
28325
|
+
}).join("") : "<li>No telephony media trace events recorded.</li>";
|
|
28237
28326
|
const snippet = escapeHtml48(`app.use(
|
|
28238
28327
|
createVoiceOperationsRecordRoutes({
|
|
28239
28328
|
audit: auditStore,
|
|
@@ -28250,7 +28339,7 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
28250
28339
|
);`);
|
|
28251
28340
|
const incidentMarkdown = escapeHtml48(renderVoiceOperationsRecordIncidentMarkdown(record));
|
|
28252
28341
|
const incidentLink = options.incidentHref ? `<a href="${escapeHtml48(options.incidentHref)}">Download incident.md</a>` : "";
|
|
28253
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml48(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted,.label{color:#a9b4bd}.label{display:block;font-size:.72rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.hero-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}.hero-actions a{background:#fbbf24;border-radius:999px;color:#111827;font-weight:900;padding:10px 14px;text-decoration:none}.two-column{display:grid;gap:18px;grid-template-columns:minmax(0,1.15fr) minmax(280px,.85fr)}@media(max-width:860px){main{padding:20px}.two-column{grid-template-columns:1fr}}</style></head><body><main><p class="eyebrow">Call log replacement</p><h1>${escapeHtml48(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml48(record.status)}">${escapeHtml48(record.status)}</p><div class="hero-actions"><a href="#transcript">Transcript</a><a href="#provider-decisions">Provider decisions</a><a href="#guardrails">Guardrails</a><a href="#incident-handoff">Incident handoff</a>${incidentLink}</div><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs5(record.summary.callDurationMs)}</strong></div><div class="card"><span>Provider recovery</span><strong>${escapeHtml48(providerDecisionSummary.recoveryStatus)}</strong><span>${String(providerDecisionSummary.fallbacks)} fallback / ${String(providerDecisionSummary.degraded)} degraded / ${String(providerDecisionSummary.errors)} errors</span></div><div class="card"><span>Guardrails</span><strong>${String(record.guardrails.blocked)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="two-column"><div><h2 id="transcript">Transcript</h2><ul>${transcript}</ul></div><div><h2 id="provider-decisions">Provider Decisions</h2><ul>${providerDecisions}</ul></div></section><section id="guardrails"><h2>Guardrail Evidence</h2><p class="muted">Live <code>assistant.guardrail</code> decisions attached to this session.</p><ul>${guardrails}</ul></section><section id="incident-handoff"><h2>Copyable Incident Handoff</h2><p class="muted">Paste this into Slack, Linear, Zendesk, or an incident review. ${incidentLink}</p><pre><code>${incidentMarkdown}</code></pre></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, guardrails, audit, latency, replay, reviews, tasks, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Provider Summary</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
|
|
28342
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml48(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted,.label{color:#a9b4bd}.label{display:block;font-size:.72rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.hero-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}.hero-actions a{background:#fbbf24;border-radius:999px;color:#111827;font-weight:900;padding:10px 14px;text-decoration:none}.two-column{display:grid;gap:18px;grid-template-columns:minmax(0,1.15fr) minmax(280px,.85fr)}@media(max-width:860px){main{padding:20px}.two-column{grid-template-columns:1fr}}</style></head><body><main><p class="eyebrow">Call log replacement</p><h1>${escapeHtml48(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml48(record.status)}">${escapeHtml48(record.status)}</p><div class="hero-actions"><a href="#transcript">Transcript</a><a href="#provider-decisions">Provider decisions</a><a href="#telephony-media">Telephony media</a><a href="#guardrails">Guardrails</a><a href="#incident-handoff">Incident handoff</a>${incidentLink}</div><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs5(record.summary.callDurationMs)}</strong></div><div class="card"><span>Provider recovery</span><strong>${escapeHtml48(providerDecisionSummary.recoveryStatus)}</strong><span>${String(providerDecisionSummary.fallbacks)} fallback / ${String(providerDecisionSummary.degraded)} degraded / ${String(providerDecisionSummary.errors)} errors</span></div><div class="card"><span>Telephony media</span><strong>${String(record.telephonyMedia.media)}</strong><span>${String(record.telephonyMedia.starts)} start / ${String(record.telephonyMedia.stops)} stop / ${String(record.telephonyMedia.errors)} errors</span></div><div class="card"><span>Guardrails</span><strong>${String(record.guardrails.blocked)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="two-column"><div><h2 id="transcript">Transcript</h2><ul>${transcript}</ul></div><div><h2 id="provider-decisions">Provider Decisions</h2><ul>${providerDecisions}</ul></div></section><section id="telephony-media"><h2>Telephony Media</h2><p class="muted">Live <code>client.telephony_media</code> stream lifecycle evidence attached to this session. Carriers: ${escapeHtml48(record.telephonyMedia.carriers.join(", ") || "none")}. Streams: ${escapeHtml48(record.telephonyMedia.streamIds.join(", ") || "none")}.</p><ul>${telephonyMedia}</ul></section><section id="guardrails"><h2>Guardrail Evidence</h2><p class="muted">Live <code>assistant.guardrail</code> decisions attached to this session.</p><ul>${guardrails}</ul></section><section id="incident-handoff"><h2>Copyable Incident Handoff</h2><p class="muted">Paste this into Slack, Linear, Zendesk, or an incident review. ${incidentLink}</p><pre><code>${incidentMarkdown}</code></pre></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, guardrails, audit, latency, replay, reviews, tasks, media streams, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Provider Summary</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
|
|
28254
28343
|
};
|
|
28255
28344
|
var createVoiceOperationsRecordRoutes = (options) => {
|
|
28256
28345
|
const path = options.path ?? "/api/voice-operations/:sessionId";
|
|
@@ -28323,7 +28412,7 @@ var assertVoiceObservabilityExportSchema = (input) => {
|
|
|
28323
28412
|
};
|
|
28324
28413
|
var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
28325
28414
|
var isStatus2 = (value) => value === "fail" || value === "pass" || value === "warn";
|
|
28326
|
-
var
|
|
28415
|
+
var getRecord2 = (value, key) => isRecord2(value) && isRecord2(value[key]) ? value[key] : undefined;
|
|
28327
28416
|
var getRecordArray = (value, key) => isRecord2(value) && Array.isArray(value[key]) ? value[key] : undefined;
|
|
28328
28417
|
var inferVoiceObservabilityExportRecordKind = (record) => {
|
|
28329
28418
|
if (isRecord2(record.manifest) && isRecord2(record.artifactIndex)) {
|
|
@@ -28347,7 +28436,7 @@ var pushValidationIssue = (issues, issue) => {
|
|
|
28347
28436
|
issues.push(issue);
|
|
28348
28437
|
};
|
|
28349
28438
|
var requireRecordSchema = (issues, record, path) => {
|
|
28350
|
-
const schema =
|
|
28439
|
+
const schema = getRecord2(record, "schema");
|
|
28351
28440
|
if (schema?.id !== voiceObservabilityExportSchemaId || schema?.version !== voiceObservabilityExportSchemaVersion) {
|
|
28352
28441
|
pushValidationIssue(issues, {
|
|
28353
28442
|
code: "voice.observability.export.unsupported_schema",
|
|
@@ -118,6 +118,27 @@ export type VoiceOperationsRecordGuardrailSummary = {
|
|
|
118
118
|
total: number;
|
|
119
119
|
warned: number;
|
|
120
120
|
};
|
|
121
|
+
export type VoiceOperationsRecordTelephonyMediaEvent = {
|
|
122
|
+
at: number;
|
|
123
|
+
audioBytes: number;
|
|
124
|
+
callSid?: string;
|
|
125
|
+
carrier?: string;
|
|
126
|
+
direction?: string;
|
|
127
|
+
event: string;
|
|
128
|
+
sequenceNumber?: string;
|
|
129
|
+
streamId?: string;
|
|
130
|
+
};
|
|
131
|
+
export type VoiceOperationsRecordTelephonyMediaSummary = {
|
|
132
|
+
audioBytes: number;
|
|
133
|
+
carriers: string[];
|
|
134
|
+
errors: number;
|
|
135
|
+
events: VoiceOperationsRecordTelephonyMediaEvent[];
|
|
136
|
+
media: number;
|
|
137
|
+
starts: number;
|
|
138
|
+
stops: number;
|
|
139
|
+
streamIds: string[];
|
|
140
|
+
total: number;
|
|
141
|
+
};
|
|
121
142
|
export type VoiceOperationsRecordGuardrailAssertionInput = {
|
|
122
143
|
minBlocked?: number;
|
|
123
144
|
minDecisions?: number;
|
|
@@ -186,6 +207,7 @@ export type VoiceOperationsRecord = {
|
|
|
186
207
|
status: VoiceOperationsRecordStatus;
|
|
187
208
|
summary: VoiceTraceSummary;
|
|
188
209
|
tasks?: VoiceOperationsRecordTaskSummary;
|
|
210
|
+
telephonyMedia: VoiceOperationsRecordTelephonyMediaSummary;
|
|
189
211
|
timeline: VoiceTraceTimelineEvent[];
|
|
190
212
|
tools: VoiceOperationsRecordTool[];
|
|
191
213
|
traceEvents: StoredVoiceTraceEvent[];
|