@absolutejs/voice 0.0.22-beta.328 → 0.0.22-beta.329

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21722,12 +21722,35 @@ var createTwilioSocketAdapter = (socket, getState) => ({
21722
21722
  }
21723
21723
  if (message.type === "audio") {
21724
21724
  const payload = transcodePCMToTwilioOutboundPayload(Uint8Array.from(Buffer4.from(message.chunkBase64, "base64")), message.format);
21725
+ const outboundMessage = {
21726
+ event: "media",
21727
+ media: {
21728
+ payload,
21729
+ track: "outbound"
21730
+ },
21731
+ streamSid: state.streamSid
21732
+ };
21725
21733
  state.hasOutboundAudioSinceLastInbound = true;
21726
21734
  state.reviewRecorder?.recordTwilioOutbound({
21727
21735
  bytes: payload.length,
21728
21736
  event: "media",
21729
21737
  track: "outbound"
21730
21738
  });
21739
+ await state.trace?.append({
21740
+ at: Date.now(),
21741
+ payload: {
21742
+ audioBytes: Buffer4.from(payload, "base64").byteLength,
21743
+ callSid: state.callSid ?? undefined,
21744
+ carrier: state.carrier,
21745
+ direction: "outbound",
21746
+ envelope: outboundMessage,
21747
+ event: "media",
21748
+ streamId: state.streamSid
21749
+ },
21750
+ scenarioId: state.scenarioId ?? undefined,
21751
+ sessionId: state.sessionId ?? state.streamSid,
21752
+ type: "client.telephony_media"
21753
+ });
21731
21754
  await Promise.resolve(socket.send(JSON.stringify({
21732
21755
  event: "media",
21733
21756
  media: {
@@ -21738,17 +21761,32 @@ var createTwilioSocketAdapter = (socket, getState) => ({
21738
21761
  return;
21739
21762
  }
21740
21763
  if (message.type === "assistant" && message.turnId) {
21741
- state.reviewRecorder?.recordTwilioOutbound({
21742
- event: "mark",
21743
- name: `assistant:${message.turnId}`
21744
- });
21745
- await Promise.resolve(socket.send(JSON.stringify({
21764
+ const outboundMessage = {
21746
21765
  event: "mark",
21747
21766
  mark: {
21748
21767
  name: `assistant:${message.turnId}`
21749
21768
  },
21750
21769
  streamSid: state.streamSid
21751
- })));
21770
+ };
21771
+ state.reviewRecorder?.recordTwilioOutbound({
21772
+ event: "mark",
21773
+ name: `assistant:${message.turnId}`
21774
+ });
21775
+ await state.trace?.append({
21776
+ at: Date.now(),
21777
+ payload: {
21778
+ callSid: state.callSid ?? undefined,
21779
+ carrier: state.carrier,
21780
+ direction: "outbound",
21781
+ envelope: outboundMessage,
21782
+ event: "mark",
21783
+ streamId: state.streamSid
21784
+ },
21785
+ scenarioId: state.scenarioId ?? undefined,
21786
+ sessionId: state.sessionId ?? state.streamSid,
21787
+ type: "client.telephony_media"
21788
+ });
21789
+ await Promise.resolve(socket.send(JSON.stringify(outboundMessage)));
21752
21790
  }
21753
21791
  }
21754
21792
  });
@@ -21771,6 +21809,7 @@ var createTwilioMediaStreamBridge = (socket, options) => {
21771
21809
  };
21772
21810
  const bridgeState = {
21773
21811
  callSid: null,
21812
+ carrier: options.telephonyMediaCarrier ?? "twilio",
21774
21813
  hasOutboundAudioSinceLastInbound: false,
21775
21814
  onVoiceMessage: options.onVoiceMessage,
21776
21815
  reviewRecorder: options.review ? createVoiceCallReviewRecorder({
@@ -21790,11 +21829,12 @@ var createTwilioMediaStreamBridge = (socket, options) => {
21790
21829
  }) : undefined,
21791
21830
  scenarioId: options.scenarioId ?? null,
21792
21831
  sessionId: options.sessionId ?? null,
21793
- streamSid: null
21832
+ streamSid: null,
21833
+ trace: options.trace
21794
21834
  };
21795
21835
  let sessionHandle = null;
21796
21836
  let reviewArtifactDelivered = false;
21797
- const telephonyMediaCarrier = options.telephonyMediaCarrier ?? "twilio";
21837
+ const telephonyMediaCarrier = bridgeState.carrier;
21798
21838
  const appendTelephonyMediaTrace = async (message, override) => {
21799
21839
  const trace = options.trace;
21800
21840
  const sessionId = override?.sessionId ?? bridgeState.sessionId ?? (message.event === "start" ? message.start.customParameters?.sessionId : undefined) ?? (message.event === "start" ? message.start.streamSid : "telephony-media");
@@ -21928,13 +21968,28 @@ var createTwilioMediaStreamBridge = (socket, options) => {
21928
21968
  track: message.media.track
21929
21969
  });
21930
21970
  if (options.clearOnInboundMedia !== false && bridgeState.hasOutboundAudioSinceLastInbound && bridgeState.streamSid) {
21971
+ const outboundMessage = {
21972
+ event: "clear",
21973
+ streamSid: bridgeState.streamSid
21974
+ };
21931
21975
  bridgeState.reviewRecorder?.recordTwilioOutbound({
21932
21976
  event: "clear"
21933
21977
  });
21934
- await Promise.resolve(socket.send(JSON.stringify({
21935
- event: "clear",
21936
- streamSid: bridgeState.streamSid
21937
- })));
21978
+ await options.trace?.append({
21979
+ at: Date.now(),
21980
+ payload: {
21981
+ callSid: bridgeState.callSid ?? undefined,
21982
+ carrier: telephonyMediaCarrier,
21983
+ direction: "outbound",
21984
+ envelope: outboundMessage,
21985
+ event: "clear",
21986
+ streamId: bridgeState.streamSid
21987
+ },
21988
+ scenarioId: bridgeState.scenarioId ?? undefined,
21989
+ sessionId: bridgeState.sessionId ?? bridgeState.streamSid,
21990
+ type: "client.telephony_media"
21991
+ });
21992
+ await Promise.resolve(socket.send(JSON.stringify(outboundMessage)));
21938
21993
  }
21939
21994
  bridgeState.hasOutboundAudioSinceLastInbound = false;
21940
21995
  await appendTelephonyMediaTrace(message, {
@@ -27865,9 +27920,13 @@ var summarizeTelephonyMedia = (events) => {
27865
27920
  return {
27866
27921
  audioBytes: mediaEvents.reduce((total, event) => total + event.audioBytes, 0),
27867
27922
  carriers: uniqueSorted8(mediaEvents.map((event) => event.carrier)),
27923
+ clears: eventNames.filter((event) => event === "clear").length,
27868
27924
  errors: eventNames.filter((event) => event === "error").length,
27869
27925
  events: mediaEvents,
27926
+ inbound: mediaEvents.filter((event) => event.direction === "inbound").length,
27927
+ marks: eventNames.filter((event) => event === "mark").length,
27870
27928
  media: eventNames.filter((event) => event === "media").length,
27929
+ outbound: mediaEvents.filter((event) => event.direction === "outbound").length,
27871
27930
  starts: eventNames.filter((event) => event === "start").length,
27872
27931
  stops: eventNames.filter((event) => event === "stop").length,
27873
27932
  streamIds: uniqueSorted8(mediaEvents.map((event) => event.streamId)),
@@ -28230,6 +28289,10 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
28230
28289
  `events=${String(record.telephonyMedia.total)}`,
28231
28290
  `starts=${String(record.telephonyMedia.starts)}`,
28232
28291
  `media=${String(record.telephonyMedia.media)}`,
28292
+ `inbound=${String(record.telephonyMedia.inbound)}`,
28293
+ `outbound=${String(record.telephonyMedia.outbound)}`,
28294
+ `marks=${String(record.telephonyMedia.marks)}`,
28295
+ `clears=${String(record.telephonyMedia.clears)}`,
28233
28296
  `stops=${String(record.telephonyMedia.stops)}`,
28234
28297
  `errors=${String(record.telephonyMedia.errors)}`,
28235
28298
  `audioBytes=${String(record.telephonyMedia.audioBytes)}`,
@@ -28339,7 +28402,7 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
28339
28402
  );`);
28340
28403
  const incidentMarkdown = escapeHtml48(renderVoiceOperationsRecordIncidentMarkdown(record));
28341
28404
  const incidentLink = options.incidentHref ? `<a href="${escapeHtml48(options.incidentHref)}">Download incident.md</a>` : "";
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>`;
28405
+ 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.inbound)} inbound / ${String(record.telephonyMedia.outbound)} outbound / ${String(record.telephonyMedia.clears)} clears</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")}. Inbound: ${String(record.telephonyMedia.inbound)}. Outbound: ${String(record.telephonyMedia.outbound)}. Marks: ${String(record.telephonyMedia.marks)}. Clears: ${String(record.telephonyMedia.clears)}.</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>`;
28343
28406
  };
28344
28407
  var createVoiceOperationsRecordRoutes = (options) => {
28345
28408
  const path = options.path ?? "/api/voice-operations/:sessionId";
@@ -131,9 +131,13 @@ export type VoiceOperationsRecordTelephonyMediaEvent = {
131
131
  export type VoiceOperationsRecordTelephonyMediaSummary = {
132
132
  audioBytes: number;
133
133
  carriers: string[];
134
+ clears: number;
134
135
  errors: number;
135
136
  events: VoiceOperationsRecordTelephonyMediaEvent[];
137
+ inbound: number;
138
+ marks: number;
136
139
  media: number;
140
+ outbound: number;
137
141
  starts: number;
138
142
  stops: number;
139
143
  streamIds: string[];
@@ -10130,12 +10130,35 @@ var createTwilioSocketAdapter = (socket, getState) => ({
10130
10130
  }
10131
10131
  if (message.type === "audio") {
10132
10132
  const payload = transcodePCMToTwilioOutboundPayload(Uint8Array.from(Buffer3.from(message.chunkBase64, "base64")), message.format);
10133
+ const outboundMessage = {
10134
+ event: "media",
10135
+ media: {
10136
+ payload,
10137
+ track: "outbound"
10138
+ },
10139
+ streamSid: state.streamSid
10140
+ };
10133
10141
  state.hasOutboundAudioSinceLastInbound = true;
10134
10142
  state.reviewRecorder?.recordTwilioOutbound({
10135
10143
  bytes: payload.length,
10136
10144
  event: "media",
10137
10145
  track: "outbound"
10138
10146
  });
10147
+ await state.trace?.append({
10148
+ at: Date.now(),
10149
+ payload: {
10150
+ audioBytes: Buffer3.from(payload, "base64").byteLength,
10151
+ callSid: state.callSid ?? undefined,
10152
+ carrier: state.carrier,
10153
+ direction: "outbound",
10154
+ envelope: outboundMessage,
10155
+ event: "media",
10156
+ streamId: state.streamSid
10157
+ },
10158
+ scenarioId: state.scenarioId ?? undefined,
10159
+ sessionId: state.sessionId ?? state.streamSid,
10160
+ type: "client.telephony_media"
10161
+ });
10139
10162
  await Promise.resolve(socket.send(JSON.stringify({
10140
10163
  event: "media",
10141
10164
  media: {
@@ -10146,17 +10169,32 @@ var createTwilioSocketAdapter = (socket, getState) => ({
10146
10169
  return;
10147
10170
  }
10148
10171
  if (message.type === "assistant" && message.turnId) {
10149
- state.reviewRecorder?.recordTwilioOutbound({
10150
- event: "mark",
10151
- name: `assistant:${message.turnId}`
10152
- });
10153
- await Promise.resolve(socket.send(JSON.stringify({
10172
+ const outboundMessage = {
10154
10173
  event: "mark",
10155
10174
  mark: {
10156
10175
  name: `assistant:${message.turnId}`
10157
10176
  },
10158
10177
  streamSid: state.streamSid
10159
- })));
10178
+ };
10179
+ state.reviewRecorder?.recordTwilioOutbound({
10180
+ event: "mark",
10181
+ name: `assistant:${message.turnId}`
10182
+ });
10183
+ await state.trace?.append({
10184
+ at: Date.now(),
10185
+ payload: {
10186
+ callSid: state.callSid ?? undefined,
10187
+ carrier: state.carrier,
10188
+ direction: "outbound",
10189
+ envelope: outboundMessage,
10190
+ event: "mark",
10191
+ streamId: state.streamSid
10192
+ },
10193
+ scenarioId: state.scenarioId ?? undefined,
10194
+ sessionId: state.sessionId ?? state.streamSid,
10195
+ type: "client.telephony_media"
10196
+ });
10197
+ await Promise.resolve(socket.send(JSON.stringify(outboundMessage)));
10160
10198
  }
10161
10199
  }
10162
10200
  });
@@ -10179,6 +10217,7 @@ var createTwilioMediaStreamBridge = (socket, options) => {
10179
10217
  };
10180
10218
  const bridgeState = {
10181
10219
  callSid: null,
10220
+ carrier: options.telephonyMediaCarrier ?? "twilio",
10182
10221
  hasOutboundAudioSinceLastInbound: false,
10183
10222
  onVoiceMessage: options.onVoiceMessage,
10184
10223
  reviewRecorder: options.review ? createVoiceCallReviewRecorder({
@@ -10198,11 +10237,12 @@ var createTwilioMediaStreamBridge = (socket, options) => {
10198
10237
  }) : undefined,
10199
10238
  scenarioId: options.scenarioId ?? null,
10200
10239
  sessionId: options.sessionId ?? null,
10201
- streamSid: null
10240
+ streamSid: null,
10241
+ trace: options.trace
10202
10242
  };
10203
10243
  let sessionHandle = null;
10204
10244
  let reviewArtifactDelivered = false;
10205
- const telephonyMediaCarrier = options.telephonyMediaCarrier ?? "twilio";
10245
+ const telephonyMediaCarrier = bridgeState.carrier;
10206
10246
  const appendTelephonyMediaTrace = async (message, override) => {
10207
10247
  const trace = options.trace;
10208
10248
  const sessionId = override?.sessionId ?? bridgeState.sessionId ?? (message.event === "start" ? message.start.customParameters?.sessionId : undefined) ?? (message.event === "start" ? message.start.streamSid : "telephony-media");
@@ -10336,13 +10376,28 @@ var createTwilioMediaStreamBridge = (socket, options) => {
10336
10376
  track: message.media.track
10337
10377
  });
10338
10378
  if (options.clearOnInboundMedia !== false && bridgeState.hasOutboundAudioSinceLastInbound && bridgeState.streamSid) {
10379
+ const outboundMessage = {
10380
+ event: "clear",
10381
+ streamSid: bridgeState.streamSid
10382
+ };
10339
10383
  bridgeState.reviewRecorder?.recordTwilioOutbound({
10340
10384
  event: "clear"
10341
10385
  });
10342
- await Promise.resolve(socket.send(JSON.stringify({
10343
- event: "clear",
10344
- streamSid: bridgeState.streamSid
10345
- })));
10386
+ await options.trace?.append({
10387
+ at: Date.now(),
10388
+ payload: {
10389
+ callSid: bridgeState.callSid ?? undefined,
10390
+ carrier: telephonyMediaCarrier,
10391
+ direction: "outbound",
10392
+ envelope: outboundMessage,
10393
+ event: "clear",
10394
+ streamId: bridgeState.streamSid
10395
+ },
10396
+ scenarioId: bridgeState.scenarioId ?? undefined,
10397
+ sessionId: bridgeState.sessionId ?? bridgeState.streamSid,
10398
+ type: "client.telephony_media"
10399
+ });
10400
+ await Promise.resolve(socket.send(JSON.stringify(outboundMessage)));
10346
10401
  }
10347
10402
  bridgeState.hasOutboundAudioSinceLastInbound = false;
10348
10403
  await appendTelephonyMediaTrace(message, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.328",
3
+ "version": "0.0.22-beta.329",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",