@absolutejs/voice 0.0.22-beta.198 → 0.0.22-beta.199

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.js CHANGED
@@ -24226,6 +24226,34 @@ var toTool = (event) => ({
24226
24226
  toolName: getString17(event.payload.toolName),
24227
24227
  turnId: event.turnId
24228
24228
  });
24229
+ var toProviderDecision = (event) => {
24230
+ const provider = getString17(event.payload.provider) ?? getString17(event.payload.selectedProvider) ?? getString17(event.payload.fallbackProvider) ?? getString17(event.payload.variantId);
24231
+ const status = getString17(event.payload.providerStatus) ?? getString17(event.payload.status) ?? getString17(event.payload.reason);
24232
+ const error = getString17(event.payload.error);
24233
+ const elapsedMs = getNumber10(event.payload.elapsedMs) ?? getNumber10(event.payload.latencyMs) ?? getNumber10(event.payload.durationMs);
24234
+ if (!provider && !status && !error && elapsedMs === undefined) {
24235
+ return;
24236
+ }
24237
+ return {
24238
+ at: event.at,
24239
+ elapsedMs,
24240
+ error,
24241
+ fallbackProvider: getString17(event.payload.fallbackProvider),
24242
+ provider,
24243
+ reason: getString17(event.payload.reason),
24244
+ selectedProvider: getString17(event.payload.selectedProvider),
24245
+ status,
24246
+ type: event.type,
24247
+ turnId: event.turnId
24248
+ };
24249
+ };
24250
+ var buildTranscript = (replay) => replay.turns.map((turn) => ({
24251
+ assistantReplies: turn.assistantReplies,
24252
+ committedText: turn.committedText,
24253
+ errors: turn.errors.map((error) => getString17(error.error) ?? JSON.stringify(error)).filter((error) => typeof error === "string"),
24254
+ id: turn.id,
24255
+ transcripts: turn.transcripts.map((transcript) => transcript.text).filter((text) => typeof text === "string" && text.length > 0)
24256
+ })).filter((turn) => turn.committedText || turn.assistantReplies.length > 0 || turn.transcripts.length > 0 || turn.errors.length > 0);
24229
24257
  var resolveOutcome4 = (events) => {
24230
24258
  const agentResults = events.filter((event) => event.type === "agent.result");
24231
24259
  return {
@@ -24281,6 +24309,7 @@ var buildVoiceOperationsRecord = async (options) => {
24281
24309
  total: integrationEvents.length
24282
24310
  } : undefined,
24283
24311
  outcome: resolveOutcome4(traceEvents),
24312
+ providerDecisions: traceEvents.map(toProviderDecision).filter((decision) => decision !== undefined),
24284
24313
  providers: timelineSession?.providers ?? [],
24285
24314
  replay,
24286
24315
  reviews: reviews ? {
@@ -24301,13 +24330,46 @@ var buildVoiceOperationsRecord = async (options) => {
24301
24330
  } : undefined,
24302
24331
  timeline: timelineSession?.events ?? [],
24303
24332
  tools: traceEvents.filter((event) => event.type === "agent.tool").map(toTool),
24304
- traceEvents
24333
+ traceEvents,
24334
+ transcript: buildTranscript(replay)
24305
24335
  };
24306
24336
  };
24307
24337
  var escapeHtml41 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
24308
24338
  var formatMs4 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
24339
+ var outcomeLabels = (outcome) => [
24340
+ outcome.complete ? "complete" : undefined,
24341
+ outcome.escalated ? "escalated" : undefined,
24342
+ outcome.transferred ? "transferred" : undefined,
24343
+ outcome.voicemail ? "voicemail" : undefined,
24344
+ outcome.noAnswer ? "no-answer" : undefined
24345
+ ].filter((label) => label !== undefined);
24346
+ var renderIncidentHandoffMarkdown = (record) => {
24347
+ const outcomes = outcomeLabels(record.outcome);
24348
+ const topErrors = record.traceEvents.filter((event) => event.type === "session.error").map((event) => getString17(event.payload.error)).filter((error) => typeof error === "string").slice(0, 3);
24349
+ const openTasks = record.tasks?.tasks.filter((task) => task.status !== "done").map((task) => task.title).slice(0, 3) ?? [];
24350
+ return [
24351
+ `# Voice incident handoff: ${record.sessionId}`,
24352
+ "",
24353
+ `- Status: ${record.status}`,
24354
+ `- Duration: ${formatMs4(record.summary.callDurationMs)}`,
24355
+ `- Turns: ${String(record.summary.turnCount)}`,
24356
+ `- Errors: ${String(record.summary.errorCount)}`,
24357
+ `- Outcome: ${outcomes.join(", ") || "unknown"}`,
24358
+ `- Providers: ${record.providers.map((provider) => provider.provider).join(", ") || "none recorded"}`,
24359
+ `- Open tasks: ${openTasks.join("; ") || "none"}`,
24360
+ `- Top errors: ${topErrors.join("; ") || "none"}`,
24361
+ "",
24362
+ "## Next checks",
24363
+ "- Review provider decisions and fallback status.",
24364
+ "- Review transcript and assistant replies.",
24365
+ "- Review handoffs, tools, audit, tasks, and integration delivery."
24366
+ ].join(`
24367
+ `);
24368
+ };
24309
24369
  var renderVoiceOperationsRecordHTML = (record, options = {}) => {
24310
24370
  const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${escapeHtml41(provider.provider)}</strong><span>${String(provider.eventCount)} events</span><span>${formatMs4(provider.averageElapsedMs)} avg</span><span>${String(provider.errorCount)} errors</span></article>`).join("") : '<p class="muted">No provider events recorded.</p>';
24371
+ const transcript = record.transcript.length ? record.transcript.map((turn) => `<li><strong>${escapeHtml41(turn.id)}</strong>${turn.committedText ? `<p><span class="label">Caller</span>${escapeHtml41(turn.committedText)}</p>` : ""}${turn.assistantReplies.map((reply) => `<p><span class="label">Assistant</span>${escapeHtml41(reply)}</p>`).join("")}${turn.errors.map((error) => `<p class="error"><span class="label">Error</span>${escapeHtml41(error)}</p>`).join("")}</li>`).join("") : "<li>No transcript turns recorded.</li>";
24372
+ const providerDecisions = record.providerDecisions.length ? record.providerDecisions.map((decision) => `<li><strong>${escapeHtml41(decision.provider ?? decision.selectedProvider ?? decision.fallbackProvider ?? "provider")}</strong> <span>${escapeHtml41(decision.status ?? decision.type)}</span> ${formatMs4(decision.elapsedMs)}${decision.fallbackProvider ? `<p>Fallback: ${escapeHtml41(decision.fallbackProvider)}</p>` : ""}${decision.error ? `<p class="error">${escapeHtml41(decision.error)}</p>` : ""}${decision.reason ? `<p>${escapeHtml41(decision.reason)}</p>` : ""}</li>`).join("") : "<li>No provider decisions recorded.</li>";
24311
24373
  const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${escapeHtml41(handoff.fromAgentId ?? "unknown")}</strong> to <strong>${escapeHtml41(handoff.targetAgentId ?? "unknown")}</strong> <span>${escapeHtml41(handoff.status ?? "")}</span><p>${escapeHtml41(handoff.summary ?? handoff.reason ?? "")}</p></li>`).join("") : "<li>No agent handoffs recorded.</li>";
24312
24374
  const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${escapeHtml41(tool.toolName ?? "tool")}</strong> <span>${escapeHtml41(tool.status ?? "")}</span> ${formatMs4(tool.elapsedMs)} ${tool.error ? `<p>${escapeHtml41(tool.error)}</p>` : ""}</li>`).join("") : "<li>No tool calls recorded.</li>";
24313
24375
  const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${escapeHtml41(review.title)}</strong> <span>${escapeHtml41(review.summary.outcome ?? "")}</span><p>${escapeHtml41(review.postCall?.summary ?? review.transcript.actual)}</p></li>`).join("") : "<li>No call reviews recorded.</li>";
@@ -24327,7 +24389,8 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
24327
24389
  tasks: opsTasks
24328
24390
  })
24329
24391
  );`);
24330
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted{color:#a9b4bd}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}</style></head><body><main><p class="eyebrow">Portable production proof</p><h1>${escapeHtml41(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml41(record.status)}">${escapeHtml41(record.status)}</p><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs4(record.summary.callDurationMs)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, audit, latency, replay, reviews, tasks, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Providers</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
24392
+ const incidentMarkdown = escapeHtml41(renderIncidentHandoffMarkdown(record));
24393
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted,.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>${escapeHtml41(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml41(record.status)}">${escapeHtml41(record.status)}</p><div class="hero-actions"><a href="#transcript">Transcript</a><a href="#provider-decisions">Provider decisions</a><a href="#incident-handoff">Incident handoff</a></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>${formatMs4(record.summary.callDurationMs)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="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="incident-handoff"><h2>Copyable Incident Handoff</h2><p class="muted">Paste this into Slack, Linear, Zendesk, or an incident review.</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, 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>`;
24331
24394
  };
24332
24395
  var createVoiceOperationsRecordRoutes = (options) => {
24333
24396
  const path = options.path ?? "/api/voice-operations/:sessionId";
@@ -33,6 +33,25 @@ export type VoiceOperationsRecordTool = {
33
33
  toolName?: string;
34
34
  turnId?: string;
35
35
  };
36
+ export type VoiceOperationsRecordTranscriptTurn = {
37
+ assistantReplies: string[];
38
+ committedText?: string;
39
+ errors: string[];
40
+ id: string;
41
+ transcripts: string[];
42
+ };
43
+ export type VoiceOperationsRecordProviderDecision = {
44
+ at: number;
45
+ elapsedMs?: number;
46
+ error?: string;
47
+ fallbackProvider?: string;
48
+ provider?: string;
49
+ reason?: string;
50
+ selectedProvider?: string;
51
+ status?: string;
52
+ type: StoredVoiceTraceEvent['type'];
53
+ turnId?: string;
54
+ };
36
55
  export type VoiceOperationsRecordAuditSummary = {
37
56
  error: number;
38
57
  events: StoredVoiceAuditEvent[];
@@ -68,6 +87,7 @@ export type VoiceOperationsRecord = {
68
87
  handoffs: VoiceOperationsRecordAgentHandoff[];
69
88
  integrationEvents?: VoiceOperationsRecordIntegrationEventSummary;
70
89
  outcome: VoiceOperationsRecordOutcome;
90
+ providerDecisions: VoiceOperationsRecordProviderDecision[];
71
91
  providers: VoiceTraceTimelineProviderSummary[];
72
92
  replay: VoiceSessionReplay;
73
93
  reviews?: VoiceOperationsRecordReviewSummary;
@@ -78,6 +98,7 @@ export type VoiceOperationsRecord = {
78
98
  timeline: VoiceTraceTimelineEvent[];
79
99
  tools: VoiceOperationsRecordTool[];
80
100
  traceEvents: StoredVoiceTraceEvent[];
101
+ transcript: VoiceOperationsRecordTranscriptTurn[];
81
102
  };
82
103
  export type VoiceOperationsRecordOptions = {
83
104
  audit?: VoiceAuditEventStore;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.198",
3
+ "version": "0.0.22-beta.199",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",