@absolutejs/voice 0.0.22-beta.326 → 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 CHANGED
@@ -16,10 +16,10 @@ export { assertVoiceRealtimeProviderContractEvidence, buildVoiceRealtimeProvider
16
16
  export type { VoiceRealtimeProviderContractAssertionInput, VoiceRealtimeProviderContractAssertionReport, VoiceRealtimeProviderContractCapability, VoiceRealtimeProviderContractCheck, VoiceRealtimeProviderContractDefinition, VoiceRealtimeProviderContractMatrixPresetOptions, VoiceRealtimeProviderContractMatrixInput, VoiceRealtimeProviderContractMatrixReport, VoiceRealtimeProviderContractRoutesOptions, VoiceRealtimeProviderContractRow, VoiceRealtimeProviderPresetProvider, VoiceRealtimeProviderContractStatus } from './realtimeProviderContracts';
17
17
  export { buildVoiceDiagnosticsMarkdown, createVoiceDiagnosticsRoutes, resolveVoiceDiagnosticsTraceFilter } from './diagnosticsRoutes';
18
18
  export { assertVoiceMediaPipelineEvidence, buildVoiceMediaPipelineReport, createVoiceMediaPipelineRoutes, evaluateVoiceMediaPipelineEvidence, renderVoiceMediaPipelineHTML, renderVoiceMediaPipelineMarkdown } from './mediaPipelineRoutes';
19
- export { buildVoiceTelephonyMediaReport, createVoiceTelephonyMediaRoutes, renderVoiceTelephonyMediaHTML } from './telephonyMediaRoutes';
19
+ export { buildVoiceTelephonyMediaReport, createVoiceTelephonyMediaRoutes, getLatestVoiceTelephonyMediaReport, renderVoiceTelephonyMediaHTML } from './telephonyMediaRoutes';
20
20
  export { createVoiceBrowserMediaRoutes, getLatestVoiceBrowserMediaReport, renderVoiceBrowserMediaHTML, summarizeVoiceBrowserMedia } from './browserMediaRoutes';
21
21
  export type { VoiceMediaPipelineAssertionInput, VoiceMediaPipelineAssertionReport, VoiceMediaPipelineReport, VoiceMediaPipelineReportOptions, VoiceMediaPipelineRoutesOptions } from './mediaPipelineRoutes';
22
- export type { VoiceTelephonyMediaCarrierInput, VoiceTelephonyMediaCarrierReport, VoiceTelephonyMediaReport, VoiceTelephonyMediaRoutesOptions, VoiceTelephonyMediaStatus } from './telephonyMediaRoutes';
22
+ export type { VoiceTelephonyMediaCarrierInput, VoiceTelephonyMediaCarrierReport, VoiceTelephonyMediaReport, VoiceTelephonyMediaRoutesOptions, VoiceTelephonyMediaTraceReportOptions, VoiceTelephonyMediaStatus } from './telephonyMediaRoutes';
23
23
  export type { VoiceBrowserMediaReport, VoiceBrowserMediaRoutesOptions, VoiceBrowserMediaSample, VoiceBrowserMediaStatus } from './browserMediaRoutes';
24
24
  export { buildVoiceDemoReadyReport, createVoiceDemoReadyRoutes, renderVoiceDemoReadyHTML } from './demoReadyRoutes';
25
25
  export { buildVoiceDeliverySinkReport, createVoiceDeliverySinkDescriptor, createVoiceDeliverySinkPair, createVoiceDeliverySinkRoutes, createVoiceFileDeliverySink, createVoicePostgresDeliverySink, createVoiceS3DeliverySink, createVoiceSQLiteDeliverySink, createVoiceWebhookDeliverySink, renderVoiceDeliverySinkHTML } from './deliverySinkRoutes';
@@ -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
@@ -12666,6 +12666,32 @@ var buildVoiceTelephonyMediaReport = (input = {}) => {
12666
12666
  status: issues.length === 0 ? "pass" : "fail"
12667
12667
  };
12668
12668
  };
12669
+ var getLatestVoiceTelephonyMediaReport = async (options) => {
12670
+ const events = (await options.store.list({ type: "client.telephony_media" })).filter((event) => {
12671
+ const carrier = event.payload.carrier;
12672
+ return typeof carrier === "string" && (!options.carriers || options.carriers.includes(carrier)) && event.payload.envelope && typeof event.payload.envelope === "object";
12673
+ }).sort((left, right) => left.at - right.at);
12674
+ if (events.length === 0) {
12675
+ return;
12676
+ }
12677
+ const byCarrier = new Map;
12678
+ for (const event of events) {
12679
+ const carrier = event.payload.carrier;
12680
+ const envelopes = byCarrier.get(carrier) ?? [];
12681
+ envelopes.push(event.payload.envelope);
12682
+ byCarrier.set(carrier, envelopes);
12683
+ }
12684
+ return buildVoiceTelephonyMediaReport({
12685
+ carriers: [...byCarrier.entries()].map(([carrier, lifecycleEnvelopes]) => ({
12686
+ carrier,
12687
+ envelope: lifecycleEnvelopes.find((envelope) => {
12688
+ const event = envelope.event;
12689
+ return typeof event === "string" && event.toLowerCase() === "media";
12690
+ }),
12691
+ lifecycleEnvelopes
12692
+ }))
12693
+ });
12694
+ };
12669
12695
  var renderVoiceTelephonyMediaHTML = (report, options = {}) => {
12670
12696
  const title = options.title ?? "Voice Telephony Media Proof";
12671
12697
  const rows = report.carriers.map((carrier) => `<tr><td>${escapeHtml16(carrier.carrier)}</td><td>${escapeHtml16(carrier.status)}</td><td>${String(carrier.audioBytes)}</td><td>${String(carrier.lifecycle.mediaEvents)}</td><td>${escapeHtml16(carrier.lifecycle.started ? "yes" : "no")}</td><td>${escapeHtml16(carrier.lifecycle.stopped ? "yes" : "no")}</td><td>${escapeHtml16(carrier.frame?.kind ?? "missing")}</td><td>${escapeHtml16(carrier.frame?.format?.encoding ?? "missing")}</td><td>${escapeHtml16(carrier.issues.join(" ") || "none")}</td></tr>`).join("");
@@ -12677,10 +12703,14 @@ var createVoiceTelephonyMediaRoutes = (options = {}) => {
12677
12703
  const routes = new Elysia13({
12678
12704
  name: options.name ?? "absolutejs-voice-telephony-media"
12679
12705
  });
12680
- routes.get(path, () => buildVoiceTelephonyMediaReport({ carriers: options.carriers }));
12706
+ routes.get(path, async () => options.store ? await getLatestVoiceTelephonyMediaReport({
12707
+ store: options.store
12708
+ }) ?? buildVoiceTelephonyMediaReport({ carriers: options.carriers }) : buildVoiceTelephonyMediaReport({ carriers: options.carriers }));
12681
12709
  if (htmlPath) {
12682
- routes.get(htmlPath, () => {
12683
- const report = buildVoiceTelephonyMediaReport({
12710
+ routes.get(htmlPath, async () => {
12711
+ const report = options.store ? await getLatestVoiceTelephonyMediaReport({
12712
+ store: options.store
12713
+ }) ?? buildVoiceTelephonyMediaReport({ carriers: options.carriers }) : buildVoiceTelephonyMediaReport({
12684
12714
  carriers: options.carriers
12685
12715
  });
12686
12716
  return new Response(renderVoiceTelephonyMediaHTML(report, options), {
@@ -21764,6 +21794,25 @@ var createTwilioMediaStreamBridge = (socket, options) => {
21764
21794
  };
21765
21795
  let sessionHandle = null;
21766
21796
  let reviewArtifactDelivered = false;
21797
+ const telephonyMediaCarrier = options.telephonyMediaCarrier ?? "twilio";
21798
+ const appendTelephonyMediaTrace = async (message, override) => {
21799
+ const trace = options.trace;
21800
+ const sessionId = override?.sessionId ?? bridgeState.sessionId ?? (message.event === "start" ? message.start.customParameters?.sessionId : undefined) ?? (message.event === "start" ? message.start.streamSid : "telephony-media");
21801
+ const streamSid = override?.streamSid ?? (message.event === "start" ? message.start.streamSid : ("streamSid" in message) ? message.streamSid : undefined);
21802
+ await trace?.append({
21803
+ at: Date.now(),
21804
+ payload: {
21805
+ callSid: message.event === "start" ? message.start.callSid : message.event === "stop" ? message.stop?.callSid : bridgeState.callSid ?? undefined,
21806
+ carrier: telephonyMediaCarrier,
21807
+ envelope: message,
21808
+ event: message.event,
21809
+ streamId: streamSid
21810
+ },
21811
+ scenarioId: bridgeState.scenarioId ?? undefined,
21812
+ sessionId,
21813
+ type: "client.telephony_media"
21814
+ });
21815
+ };
21767
21816
  const resolveLexicon2 = async () => {
21768
21817
  if (typeof options.lexicon === "function") {
21769
21818
  return normalizeLexicon2(await options.lexicon({
@@ -21864,6 +21913,10 @@ var createTwilioMediaStreamBridge = (socket, options) => {
21864
21913
  reason: message.start.callSid,
21865
21914
  text: bridgeState.sessionId ?? undefined
21866
21915
  });
21916
+ await appendTelephonyMediaTrace(message, {
21917
+ sessionId: bridgeState.sessionId ?? undefined,
21918
+ streamSid: bridgeState.streamSid ?? undefined
21919
+ });
21867
21920
  await ensureSession();
21868
21921
  return;
21869
21922
  }
@@ -21884,6 +21937,10 @@ var createTwilioMediaStreamBridge = (socket, options) => {
21884
21937
  })));
21885
21938
  }
21886
21939
  bridgeState.hasOutboundAudioSinceLastInbound = false;
21940
+ await appendTelephonyMediaTrace(message, {
21941
+ sessionId: bridgeState.sessionId ?? undefined,
21942
+ streamSid: bridgeState.streamSid ?? undefined
21943
+ });
21887
21944
  await activeSession.receiveAudio(transcodeTwilioInboundPayloadToPCM16(message.media.payload));
21888
21945
  return;
21889
21946
  }
@@ -21898,6 +21955,10 @@ var createTwilioMediaStreamBridge = (socket, options) => {
21898
21955
  event: "stop",
21899
21956
  reason: message.stop?.callSid
21900
21957
  });
21958
+ await appendTelephonyMediaTrace(message, {
21959
+ sessionId: bridgeState.sessionId ?? undefined,
21960
+ streamSid: bridgeState.streamSid ?? undefined
21961
+ });
21901
21962
  await sessionHandle?.close("twilio-stop");
21902
21963
  return;
21903
21964
  }
@@ -22198,6 +22259,7 @@ var createPlivoTwilioSocketAdapter = (socket) => ({
22198
22259
  var createPlivoMediaStreamBridge = (socket, options) => {
22199
22260
  const bridge = createTwilioMediaStreamBridge(createPlivoTwilioSocketAdapter(socket), {
22200
22261
  ...options,
22262
+ telephonyMediaCarrier: "plivo",
22201
22263
  onVoiceMessage: options.onVoiceMessage ? (input) => options.onVoiceMessage?.({
22202
22264
  callId: input.callSid,
22203
22265
  message: input.message,
@@ -22825,6 +22887,7 @@ var createTelnyxTwilioSocketAdapter = (socket) => ({
22825
22887
  var createTelnyxMediaStreamBridge = (socket, options) => {
22826
22888
  const bridge = createTwilioMediaStreamBridge(createTelnyxTwilioSocketAdapter(socket), {
22827
22889
  ...options,
22890
+ telephonyMediaCarrier: "telnyx",
22828
22891
  onVoiceMessage: options.onVoiceMessage ? (input) => options.onVoiceMessage?.({
22829
22892
  callControlId: input.callSid,
22830
22893
  message: input.message,
@@ -27702,6 +27765,7 @@ var createVoiceTraceTimelineRoutes = (options) => {
27702
27765
  var getString17 = (value) => typeof value === "string" ? value : undefined;
27703
27766
  var getNumber10 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
27704
27767
  var getBoolean3 = (value) => typeof value === "boolean" ? value : undefined;
27768
+ var getRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
27705
27769
  var countOutcome = (events, outcome) => events.filter((event) => event.outcome === outcome).length;
27706
27770
  var matchesSessionScopedId = (id, sessionId) => id === sessionId || id.startsWith(`${sessionId}:`);
27707
27771
  var hasPayloadValue = (payload, key, values) => {
@@ -27760,6 +27824,56 @@ var toGuardrailDecision = (event) => ({
27760
27824
  toolName: getString17(event.payload.toolName),
27761
27825
  turnId: event.turnId
27762
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
+ };
27763
27877
  var summarizeGuardrails = (events) => {
27764
27878
  const decisions = events.filter((event) => event.type === "assistant.guardrail").map(toGuardrailDecision);
27765
27879
  const isBlocked = (decision) => decision.allowed === false || decision.status === "fail";
@@ -27907,6 +28021,7 @@ var buildVoiceOperationsRecord = async (options) => {
27907
28021
  tasks,
27908
28022
  total: tasks.length
27909
28023
  } : undefined,
28024
+ telephonyMedia: summarizeTelephonyMedia(traceEvents),
27910
28025
  timeline: timelineSession?.events ?? [],
27911
28026
  tools: traceEvents.filter((event) => event.type === "agent.tool").map(toTool),
27912
28027
  traceEvents,
@@ -28111,6 +28226,27 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
28111
28226
  `degraded=${String(providerDecisionSummary.degraded)}`,
28112
28227
  `errors=${String(providerDecisionSummary.errors)}`
28113
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"];
28114
28250
  return [
28115
28251
  `# Voice incident handoff: ${record.sessionId}`,
28116
28252
  "",
@@ -28124,11 +28260,16 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
28124
28260
  `- Top errors: ${topErrors.join("; ") || "none"}`,
28125
28261
  `- Guardrails: ${String(record.guardrails.blocked)} blocked / ${String(record.guardrails.warned)} warned / ${String(record.guardrails.total)} decisions`,
28126
28262
  `- Provider recovery: ${providerRecoveryLine}`,
28263
+ `- Telephony media: ${telephonyMediaLine}`,
28127
28264
  "",
28128
28265
  "## Provider decisions",
28129
28266
  "",
28130
28267
  ...providerDecisionLines,
28131
28268
  "",
28269
+ "## Telephony media",
28270
+ "",
28271
+ ...telephonyMediaLines,
28272
+ "",
28132
28273
  renderVoiceOperationsRecordGuardrailMarkdown(record),
28133
28274
  "",
28134
28275
  "## Next checks",
@@ -28171,6 +28312,17 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
28171
28312
  const findings = decision.findings.map((finding) => finding.label ?? finding.ruleId ?? finding.action).filter((value) => typeof value === "string").join(", ") || "none";
28172
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>`;
28173
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>";
28174
28326
  const snippet = escapeHtml48(`app.use(
28175
28327
  createVoiceOperationsRecordRoutes({
28176
28328
  audit: auditStore,
@@ -28187,7 +28339,7 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
28187
28339
  );`);
28188
28340
  const incidentMarkdown = escapeHtml48(renderVoiceOperationsRecordIncidentMarkdown(record));
28189
28341
  const incidentLink = options.incidentHref ? `<a href="${escapeHtml48(options.incidentHref)}">Download incident.md</a>` : "";
28190
- 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>`;
28191
28343
  };
28192
28344
  var createVoiceOperationsRecordRoutes = (options) => {
28193
28345
  const path = options.path ?? "/api/voice-operations/:sessionId";
@@ -28260,7 +28412,7 @@ var assertVoiceObservabilityExportSchema = (input) => {
28260
28412
  };
28261
28413
  var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
28262
28414
  var isStatus2 = (value) => value === "fail" || value === "pass" || value === "warn";
28263
- var getRecord = (value, key) => isRecord2(value) && isRecord2(value[key]) ? value[key] : undefined;
28415
+ var getRecord2 = (value, key) => isRecord2(value) && isRecord2(value[key]) ? value[key] : undefined;
28264
28416
  var getRecordArray = (value, key) => isRecord2(value) && Array.isArray(value[key]) ? value[key] : undefined;
28265
28417
  var inferVoiceObservabilityExportRecordKind = (record) => {
28266
28418
  if (isRecord2(record.manifest) && isRecord2(record.artifactIndex)) {
@@ -28284,7 +28436,7 @@ var pushValidationIssue = (issues, issue) => {
28284
28436
  issues.push(issue);
28285
28437
  };
28286
28438
  var requireRecordSchema = (issues, record, path) => {
28287
- const schema = getRecord(record, "schema");
28439
+ const schema = getRecord2(record, "schema");
28288
28440
  if (schema?.id !== voiceObservabilityExportSchemaId || schema?.version !== voiceObservabilityExportSchemaVersion) {
28289
28441
  pushValidationIssue(issues, {
28290
28442
  code: "voice.observability.export.unsupported_schema",
@@ -35542,6 +35694,7 @@ export {
35542
35694
  hasVoiceOpsTaskSLABreach,
35543
35695
  getVoiceLiveOpsControlStatus,
35544
35696
  getVoiceCampaignDialerProofStatus,
35697
+ getLatestVoiceTelephonyMediaReport,
35545
35698
  getLatestVoiceBrowserMediaReport,
35546
35699
  formatVoiceProofTrendAge,
35547
35700
  filterVoiceTraceEvents,
@@ -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[];
@@ -98,6 +98,7 @@ export type TwilioMediaStreamBridgeOptions<TContext = unknown, TSession extends
98
98
  scenarioId?: string;
99
99
  sessionId?: string;
100
100
  stt: STTAdapter;
101
+ telephonyMediaCarrier?: 'plivo' | 'telnyx' | 'twilio';
101
102
  };
102
103
  export type TwilioMediaStreamBridge = {
103
104
  close: (reason?: string) => Promise<void>;
@@ -1,5 +1,6 @@
1
1
  import { Elysia } from 'elysia';
2
2
  import type { MediaFrame, MediaTelephonyCarrier, MediaTelephonyEnvelope, MediaTelephonyStreamLifecycleReport } from '@absolutejs/media';
3
+ import type { VoiceTraceEventStore } from './trace';
3
4
  export type VoiceTelephonyMediaStatus = 'fail' | 'pass';
4
5
  export type VoiceTelephonyMediaCarrierInput = {
5
6
  carrier: MediaTelephonyCarrier;
@@ -27,11 +28,17 @@ export type VoiceTelephonyMediaRoutesOptions = {
27
28
  htmlPath?: false | string;
28
29
  name?: string;
29
30
  path?: string;
31
+ store?: VoiceTraceEventStore;
30
32
  title?: string;
31
33
  };
34
+ export type VoiceTelephonyMediaTraceReportOptions = {
35
+ carriers?: readonly MediaTelephonyCarrier[];
36
+ store: VoiceTraceEventStore;
37
+ };
32
38
  export declare const buildVoiceTelephonyMediaReport: (input?: {
33
39
  carriers?: readonly VoiceTelephonyMediaCarrierInput[];
34
40
  }) => VoiceTelephonyMediaReport;
41
+ export declare const getLatestVoiceTelephonyMediaReport: (options: VoiceTelephonyMediaTraceReportOptions) => Promise<VoiceTelephonyMediaReport | undefined>;
35
42
  export declare const renderVoiceTelephonyMediaHTML: (report: VoiceTelephonyMediaReport, options?: {
36
43
  title?: string;
37
44
  }) => string;
@@ -10202,6 +10202,25 @@ var createTwilioMediaStreamBridge = (socket, options) => {
10202
10202
  };
10203
10203
  let sessionHandle = null;
10204
10204
  let reviewArtifactDelivered = false;
10205
+ const telephonyMediaCarrier = options.telephonyMediaCarrier ?? "twilio";
10206
+ const appendTelephonyMediaTrace = async (message, override) => {
10207
+ const trace = options.trace;
10208
+ const sessionId = override?.sessionId ?? bridgeState.sessionId ?? (message.event === "start" ? message.start.customParameters?.sessionId : undefined) ?? (message.event === "start" ? message.start.streamSid : "telephony-media");
10209
+ const streamSid = override?.streamSid ?? (message.event === "start" ? message.start.streamSid : ("streamSid" in message) ? message.streamSid : undefined);
10210
+ await trace?.append({
10211
+ at: Date.now(),
10212
+ payload: {
10213
+ callSid: message.event === "start" ? message.start.callSid : message.event === "stop" ? message.stop?.callSid : bridgeState.callSid ?? undefined,
10214
+ carrier: telephonyMediaCarrier,
10215
+ envelope: message,
10216
+ event: message.event,
10217
+ streamId: streamSid
10218
+ },
10219
+ scenarioId: bridgeState.scenarioId ?? undefined,
10220
+ sessionId,
10221
+ type: "client.telephony_media"
10222
+ });
10223
+ };
10205
10224
  const resolveLexicon = async () => {
10206
10225
  if (typeof options.lexicon === "function") {
10207
10226
  return normalizeLexicon(await options.lexicon({
@@ -10302,6 +10321,10 @@ var createTwilioMediaStreamBridge = (socket, options) => {
10302
10321
  reason: message.start.callSid,
10303
10322
  text: bridgeState.sessionId ?? undefined
10304
10323
  });
10324
+ await appendTelephonyMediaTrace(message, {
10325
+ sessionId: bridgeState.sessionId ?? undefined,
10326
+ streamSid: bridgeState.streamSid ?? undefined
10327
+ });
10305
10328
  await ensureSession();
10306
10329
  return;
10307
10330
  }
@@ -10322,6 +10345,10 @@ var createTwilioMediaStreamBridge = (socket, options) => {
10322
10345
  })));
10323
10346
  }
10324
10347
  bridgeState.hasOutboundAudioSinceLastInbound = false;
10348
+ await appendTelephonyMediaTrace(message, {
10349
+ sessionId: bridgeState.sessionId ?? undefined,
10350
+ streamSid: bridgeState.streamSid ?? undefined
10351
+ });
10325
10352
  await activeSession.receiveAudio(transcodeTwilioInboundPayloadToPCM16(message.media.payload));
10326
10353
  return;
10327
10354
  }
@@ -10336,6 +10363,10 @@ var createTwilioMediaStreamBridge = (socket, options) => {
10336
10363
  event: "stop",
10337
10364
  reason: message.stop?.callSid
10338
10365
  });
10366
+ await appendTelephonyMediaTrace(message, {
10367
+ sessionId: bridgeState.sessionId ?? undefined,
10368
+ streamSid: bridgeState.streamSid ?? undefined
10369
+ });
10339
10370
  await sessionHandle?.close("twilio-stop");
10340
10371
  return;
10341
10372
  }
package/dist/trace.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { S3Client, S3Options } from 'bun';
2
- export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.context' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.handoff' | 'call.lifecycle' | 'client.barge_in' | 'client.browser_media' | 'client.live_latency' | 'client.reconnect' | 'operator.action' | 'provider.decision' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn_latency.stage' | 'turn.transcript' | 'workflow.contract';
2
+ export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.context' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.handoff' | 'call.lifecycle' | 'client.barge_in' | 'client.browser_media' | 'client.live_latency' | 'client.reconnect' | 'client.telephony_media' | 'operator.action' | 'provider.decision' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn_latency.stage' | 'turn.transcript' | 'workflow.contract';
3
3
  export type VoiceTraceEvent<TPayload extends Record<string, unknown> = Record<string, unknown>> = {
4
4
  at: number;
5
5
  id?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.326",
3
+ "version": "0.0.22-beta.328",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",