@absolutejs/voice 0.0.22-beta.322 → 0.0.22-beta.323

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.
@@ -1414,6 +1414,63 @@ var stringStat = (stat, key) => {
1414
1414
  };
1415
1415
  var statKey = (stat) => String(stat.id ?? stringStat(stat, "ssrc") ?? numericStat(stat, "ssrc") ?? stringStat(stat, "trackIdentifier") ?? stringStat(stat, "mid") ?? "unknown");
1416
1416
  var secondsToMs = (value) => value === undefined ? undefined : value * 1000;
1417
+ var DEFAULT_TELEPHONY_FORMAT = {
1418
+ channels: 1,
1419
+ container: "raw",
1420
+ encoding: "mulaw",
1421
+ sampleRateHz: 8000
1422
+ };
1423
+ var bytesToBase64 = (audio) => {
1424
+ const bytes = audio instanceof ArrayBuffer ? new Uint8Array(audio) : new Uint8Array(audio.buffer, audio.byteOffset, audio.byteLength);
1425
+ return Buffer.from(bytes).toString("base64");
1426
+ };
1427
+ var base64ToBytes = (value) => new Uint8Array(Buffer.from(value, "base64"));
1428
+ var unknownRecord = (value) => value && typeof value === "object" ? value : {};
1429
+ var firstString = (records, keys) => {
1430
+ for (const record of records) {
1431
+ for (const key of keys) {
1432
+ const value = record[key];
1433
+ if (typeof value === "string" && value.length > 0) {
1434
+ return value;
1435
+ }
1436
+ if (typeof value === "number" && Number.isFinite(value)) {
1437
+ return String(value);
1438
+ }
1439
+ }
1440
+ }
1441
+ return;
1442
+ };
1443
+ var firstNumber = (records, keys) => {
1444
+ for (const record of records) {
1445
+ for (const key of keys) {
1446
+ const value = record[key];
1447
+ if (typeof value === "number" && Number.isFinite(value)) {
1448
+ return value;
1449
+ }
1450
+ if (typeof value === "string") {
1451
+ const parsed = Number(value);
1452
+ if (Number.isFinite(parsed)) {
1453
+ return parsed;
1454
+ }
1455
+ }
1456
+ }
1457
+ }
1458
+ return;
1459
+ };
1460
+ var telephonyDirection = (track) => {
1461
+ const normalized = track?.toLowerCase();
1462
+ if (!normalized) {
1463
+ return "unknown";
1464
+ }
1465
+ if (normalized.includes("inbound") || normalized.includes("caller") || normalized.includes("in")) {
1466
+ return "inbound";
1467
+ }
1468
+ if (normalized.includes("outbound") || normalized.includes("assistant") || normalized.includes("out")) {
1469
+ return "outbound";
1470
+ }
1471
+ return "unknown";
1472
+ };
1473
+ var telephonyFrameKind = (direction) => direction === "outbound" ? "assistant-audio" : "input-audio";
1417
1474
  var normalizeWebRTCStat = (stat) => {
1418
1475
  const sample = {};
1419
1476
  for (const [key, value] of Object.entries(stat)) {
@@ -1423,6 +1480,113 @@ var normalizeWebRTCStat = (stat) => {
1423
1480
  }
1424
1481
  return sample;
1425
1482
  };
1483
+ var parseTelephonyMediaFrame = (input) => {
1484
+ const envelope = input.envelope;
1485
+ const media = unknownRecord(envelope.media);
1486
+ const payload = firstString([media, envelope], ["payload", "audio", "data"]) ?? firstString([unknownRecord(envelope.message)], ["payload"]);
1487
+ if (!payload) {
1488
+ return;
1489
+ }
1490
+ const carrier = input.carrier ?? firstString([envelope], ["provider"]) ?? "telephony";
1491
+ const streamId = firstString([media, envelope], ["streamSid", "stream_id", "streamId", "streamId", "callSid", "call_id"]);
1492
+ const sequenceNumber = firstString([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
1493
+ const track = firstString([media, envelope], ["track", "direction"]);
1494
+ const direction = telephonyDirection(track);
1495
+ const timestamp = firstNumber([media, envelope], ["timestamp", "time", "startedAt"]);
1496
+ return {
1497
+ at: timestamp,
1498
+ audio: base64ToBytes(payload),
1499
+ format: input.format ?? DEFAULT_TELEPHONY_FORMAT,
1500
+ id: [
1501
+ carrier,
1502
+ streamId ?? input.sessionId ?? "stream",
1503
+ sequenceNumber ?? timestamp ?? Date.now()
1504
+ ].join(":"),
1505
+ kind: telephonyFrameKind(direction),
1506
+ metadata: {
1507
+ carrier,
1508
+ direction,
1509
+ event: firstString([envelope], ["event", "type"]),
1510
+ sequenceNumber,
1511
+ streamId,
1512
+ track
1513
+ },
1514
+ sessionId: input.sessionId ?? streamId,
1515
+ source: "telephony"
1516
+ };
1517
+ };
1518
+ var serializeTelephonyMediaFrame = (input) => {
1519
+ const carrier = input.carrier ?? input.frame.metadata?.carrier ?? "telephony";
1520
+ const streamId = input.streamId ?? (typeof input.frame.metadata?.streamId === "string" ? input.frame.metadata.streamId : input.frame.sessionId);
1521
+ const sequenceNumber = input.sequenceNumber ?? (typeof input.frame.metadata?.sequenceNumber === "string" || typeof input.frame.metadata?.sequenceNumber === "number" ? input.frame.metadata.sequenceNumber : undefined);
1522
+ const direction = input.frame.kind === "assistant-audio" ? "outbound" : "inbound";
1523
+ const payload = input.frame.audio ? bytesToBase64(input.frame.audio) : "";
1524
+ if (carrier === "twilio") {
1525
+ return {
1526
+ event: "media",
1527
+ sequenceNumber,
1528
+ streamSid: streamId,
1529
+ media: {
1530
+ payload,
1531
+ timestamp: input.frame.at,
1532
+ track: direction
1533
+ }
1534
+ };
1535
+ }
1536
+ if (carrier === "telnyx") {
1537
+ return {
1538
+ event: "media",
1539
+ stream_id: streamId,
1540
+ sequence_number: sequenceNumber,
1541
+ media: {
1542
+ payload,
1543
+ timestamp: input.frame.at,
1544
+ track: direction
1545
+ }
1546
+ };
1547
+ }
1548
+ if (carrier === "plivo") {
1549
+ return {
1550
+ event: "media",
1551
+ streamId,
1552
+ sequenceNumber,
1553
+ media: {
1554
+ payload,
1555
+ timestamp: input.frame.at,
1556
+ track: direction
1557
+ }
1558
+ };
1559
+ }
1560
+ return {
1561
+ event: "media",
1562
+ provider: carrier,
1563
+ sequenceNumber,
1564
+ streamId,
1565
+ media: {
1566
+ payload,
1567
+ timestamp: input.frame.at,
1568
+ track: direction
1569
+ }
1570
+ };
1571
+ };
1572
+ var createTelephonyMediaSerializer = (input) => {
1573
+ const format = input.format ?? DEFAULT_TELEPHONY_FORMAT;
1574
+ return {
1575
+ carrier: input.carrier,
1576
+ format,
1577
+ parse: (envelope) => parseTelephonyMediaFrame({
1578
+ carrier: input.carrier,
1579
+ envelope,
1580
+ format,
1581
+ sessionId: input.sessionId ?? input.streamId
1582
+ }),
1583
+ serialize: (frame) => serializeTelephonyMediaFrame({
1584
+ carrier: input.carrier,
1585
+ frame,
1586
+ streamId: input.streamId
1587
+ })
1588
+ };
1589
+ };
1426
1590
  var buildMediaResamplingPlan = (input) => {
1427
1591
  const required = !formatMatches(input.inputFormat, input.outputFormat);
1428
1592
  return {
@@ -807,6 +807,63 @@ var stringStat = (stat, key) => {
807
807
  };
808
808
  var statKey = (stat) => String(stat.id ?? stringStat(stat, "ssrc") ?? numericStat(stat, "ssrc") ?? stringStat(stat, "trackIdentifier") ?? stringStat(stat, "mid") ?? "unknown");
809
809
  var secondsToMs = (value) => value === undefined ? undefined : value * 1000;
810
+ var DEFAULT_TELEPHONY_FORMAT = {
811
+ channels: 1,
812
+ container: "raw",
813
+ encoding: "mulaw",
814
+ sampleRateHz: 8000
815
+ };
816
+ var bytesToBase64 = (audio) => {
817
+ const bytes = audio instanceof ArrayBuffer ? new Uint8Array(audio) : new Uint8Array(audio.buffer, audio.byteOffset, audio.byteLength);
818
+ return Buffer.from(bytes).toString("base64");
819
+ };
820
+ var base64ToBytes = (value) => new Uint8Array(Buffer.from(value, "base64"));
821
+ var unknownRecord = (value) => value && typeof value === "object" ? value : {};
822
+ var firstString = (records, keys) => {
823
+ for (const record of records) {
824
+ for (const key of keys) {
825
+ const value = record[key];
826
+ if (typeof value === "string" && value.length > 0) {
827
+ return value;
828
+ }
829
+ if (typeof value === "number" && Number.isFinite(value)) {
830
+ return String(value);
831
+ }
832
+ }
833
+ }
834
+ return;
835
+ };
836
+ var firstNumber = (records, keys) => {
837
+ for (const record of records) {
838
+ for (const key of keys) {
839
+ const value = record[key];
840
+ if (typeof value === "number" && Number.isFinite(value)) {
841
+ return value;
842
+ }
843
+ if (typeof value === "string") {
844
+ const parsed = Number(value);
845
+ if (Number.isFinite(parsed)) {
846
+ return parsed;
847
+ }
848
+ }
849
+ }
850
+ }
851
+ return;
852
+ };
853
+ var telephonyDirection = (track) => {
854
+ const normalized = track?.toLowerCase();
855
+ if (!normalized) {
856
+ return "unknown";
857
+ }
858
+ if (normalized.includes("inbound") || normalized.includes("caller") || normalized.includes("in")) {
859
+ return "inbound";
860
+ }
861
+ if (normalized.includes("outbound") || normalized.includes("assistant") || normalized.includes("out")) {
862
+ return "outbound";
863
+ }
864
+ return "unknown";
865
+ };
866
+ var telephonyFrameKind = (direction) => direction === "outbound" ? "assistant-audio" : "input-audio";
810
867
  var normalizeWebRTCStat = (stat) => {
811
868
  const sample = {};
812
869
  for (const [key, value] of Object.entries(stat)) {
@@ -816,6 +873,113 @@ var normalizeWebRTCStat = (stat) => {
816
873
  }
817
874
  return sample;
818
875
  };
876
+ var parseTelephonyMediaFrame = (input) => {
877
+ const envelope = input.envelope;
878
+ const media = unknownRecord(envelope.media);
879
+ const payload = firstString([media, envelope], ["payload", "audio", "data"]) ?? firstString([unknownRecord(envelope.message)], ["payload"]);
880
+ if (!payload) {
881
+ return;
882
+ }
883
+ const carrier = input.carrier ?? firstString([envelope], ["provider"]) ?? "telephony";
884
+ const streamId = firstString([media, envelope], ["streamSid", "stream_id", "streamId", "streamId", "callSid", "call_id"]);
885
+ const sequenceNumber = firstString([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
886
+ const track = firstString([media, envelope], ["track", "direction"]);
887
+ const direction = telephonyDirection(track);
888
+ const timestamp = firstNumber([media, envelope], ["timestamp", "time", "startedAt"]);
889
+ return {
890
+ at: timestamp,
891
+ audio: base64ToBytes(payload),
892
+ format: input.format ?? DEFAULT_TELEPHONY_FORMAT,
893
+ id: [
894
+ carrier,
895
+ streamId ?? input.sessionId ?? "stream",
896
+ sequenceNumber ?? timestamp ?? Date.now()
897
+ ].join(":"),
898
+ kind: telephonyFrameKind(direction),
899
+ metadata: {
900
+ carrier,
901
+ direction,
902
+ event: firstString([envelope], ["event", "type"]),
903
+ sequenceNumber,
904
+ streamId,
905
+ track
906
+ },
907
+ sessionId: input.sessionId ?? streamId,
908
+ source: "telephony"
909
+ };
910
+ };
911
+ var serializeTelephonyMediaFrame = (input) => {
912
+ const carrier = input.carrier ?? input.frame.metadata?.carrier ?? "telephony";
913
+ const streamId = input.streamId ?? (typeof input.frame.metadata?.streamId === "string" ? input.frame.metadata.streamId : input.frame.sessionId);
914
+ const sequenceNumber = input.sequenceNumber ?? (typeof input.frame.metadata?.sequenceNumber === "string" || typeof input.frame.metadata?.sequenceNumber === "number" ? input.frame.metadata.sequenceNumber : undefined);
915
+ const direction = input.frame.kind === "assistant-audio" ? "outbound" : "inbound";
916
+ const payload = input.frame.audio ? bytesToBase64(input.frame.audio) : "";
917
+ if (carrier === "twilio") {
918
+ return {
919
+ event: "media",
920
+ sequenceNumber,
921
+ streamSid: streamId,
922
+ media: {
923
+ payload,
924
+ timestamp: input.frame.at,
925
+ track: direction
926
+ }
927
+ };
928
+ }
929
+ if (carrier === "telnyx") {
930
+ return {
931
+ event: "media",
932
+ stream_id: streamId,
933
+ sequence_number: sequenceNumber,
934
+ media: {
935
+ payload,
936
+ timestamp: input.frame.at,
937
+ track: direction
938
+ }
939
+ };
940
+ }
941
+ if (carrier === "plivo") {
942
+ return {
943
+ event: "media",
944
+ streamId,
945
+ sequenceNumber,
946
+ media: {
947
+ payload,
948
+ timestamp: input.frame.at,
949
+ track: direction
950
+ }
951
+ };
952
+ }
953
+ return {
954
+ event: "media",
955
+ provider: carrier,
956
+ sequenceNumber,
957
+ streamId,
958
+ media: {
959
+ payload,
960
+ timestamp: input.frame.at,
961
+ track: direction
962
+ }
963
+ };
964
+ };
965
+ var createTelephonyMediaSerializer = (input) => {
966
+ const format = input.format ?? DEFAULT_TELEPHONY_FORMAT;
967
+ return {
968
+ carrier: input.carrier,
969
+ format,
970
+ parse: (envelope) => parseTelephonyMediaFrame({
971
+ carrier: input.carrier,
972
+ envelope,
973
+ format,
974
+ sessionId: input.sessionId ?? input.streamId
975
+ }),
976
+ serialize: (frame) => serializeTelephonyMediaFrame({
977
+ carrier: input.carrier,
978
+ frame,
979
+ streamId: input.streamId
980
+ })
981
+ };
982
+ };
819
983
  var buildMediaResamplingPlan = (input) => {
820
984
  const required = !formatMatches(input.inputFormat, input.outputFormat);
821
985
  return {
package/dist/index.d.ts CHANGED
@@ -16,8 +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
20
  export { createVoiceBrowserMediaRoutes, getLatestVoiceBrowserMediaReport, renderVoiceBrowserMediaHTML, summarizeVoiceBrowserMedia } from './browserMediaRoutes';
20
21
  export type { VoiceMediaPipelineAssertionInput, VoiceMediaPipelineAssertionReport, VoiceMediaPipelineReport, VoiceMediaPipelineReportOptions, VoiceMediaPipelineRoutesOptions } from './mediaPipelineRoutes';
22
+ export type { VoiceTelephonyMediaCarrierInput, VoiceTelephonyMediaCarrierReport, VoiceTelephonyMediaReport, VoiceTelephonyMediaRoutesOptions, VoiceTelephonyMediaStatus } from './telephonyMediaRoutes';
21
23
  export type { VoiceBrowserMediaReport, VoiceBrowserMediaRoutesOptions, VoiceBrowserMediaSample, VoiceBrowserMediaStatus } from './browserMediaRoutes';
22
24
  export { buildVoiceDemoReadyReport, createVoiceDemoReadyRoutes, renderVoiceDemoReadyHTML } from './demoReadyRoutes';
23
25
  export { buildVoiceDeliverySinkReport, createVoiceDeliverySinkDescriptor, createVoiceDeliverySinkPair, createVoiceDeliverySinkRoutes, createVoiceFileDeliverySink, createVoicePostgresDeliverySink, createVoiceS3DeliverySink, createVoiceSQLiteDeliverySink, createVoiceWebhookDeliverySink, renderVoiceDeliverySinkHTML } from './deliverySinkRoutes';