@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 +3 -3
- package/dist/index.js +159 -6
- package/dist/operationsRecord.d.ts +22 -0
- package/dist/telephony/twilio.d.ts +1 -0
- package/dist/telephonyMediaRoutes.d.ts +7 -0
- package/dist/testing/index.js +31 -0
- package/dist/trace.d.ts +1 -1
- package/package.json +1 -1
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, () =>
|
|
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 =
|
|
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
|
|
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 =
|
|
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;
|
package/dist/testing/index.js
CHANGED
|
@@ -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;
|