@absolutejs/voice 0.0.22-beta.546 → 0.0.22-beta.548

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.
@@ -745,7 +745,9 @@ export type VoiceSurfaceConfig<O> = false | O | (Record<string, never> extends O
745
745
  export type VoicePluginConfig<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
746
746
  costTelemetry?: VoiceCostTelemetryConfig<TContext, TSession, TResult>;
747
747
  path: string;
748
- greeting?: string | (() => string | Promise<string>);
748
+ greeting?: string | ((input: {
749
+ session: TSession;
750
+ }) => string | Promise<string>);
749
751
  languageStrategy?: VoiceLanguageStrategy;
750
752
  lexicon?: VoiceLexiconEntry[] | VoiceLexiconResolver<TContext>;
751
753
  phraseHints?: VoicePhraseHint[] | VoicePhraseHintResolver<TContext>;
@@ -859,7 +861,9 @@ export type CreateVoiceSessionOptions<TContext = unknown, TSession extends Voice
859
861
  id: string;
860
862
  context: TContext;
861
863
  socket: VoiceSocket;
862
- greeting?: string | (() => string | Promise<string>);
864
+ greeting?: string | ((input: {
865
+ session: TSession;
866
+ }) => string | Promise<string>);
863
867
  stt?: STTAdapter;
864
868
  realtime?: RealtimeAdapter;
865
869
  realtimeInputFormat?: AudioFormat;
package/dist/index.js CHANGED
@@ -5721,7 +5721,7 @@ var createVoiceSession = (options) => {
5721
5721
  kickCallSilenceWatchdog();
5722
5722
  startAmdEvaluationTimer();
5723
5723
  if (shouldFireOnSession && options.greeting && session.turns.length === 0) {
5724
- const greetingText = typeof options.greeting === "function" ? await options.greeting() : options.greeting;
5724
+ const greetingText = typeof options.greeting === "function" ? await options.greeting({ session }) : options.greeting;
5725
5725
  const greetingTurnId = createId();
5726
5726
  await send({
5727
5727
  text: greetingText,
@@ -24302,8 +24302,23 @@ var createTwilioSocketAdapter = (socket, getState) => ({
24302
24302
  if (!state.streamSid) {
24303
24303
  return;
24304
24304
  }
24305
+ const clearMessage = { event: "clear", streamSid: state.streamSid };
24305
24306
  state.reviewRecorder?.recordTwilioOutbound({ event: "clear" });
24306
- await Promise.resolve(socket.send(JSON.stringify({ event: "clear", streamSid: state.streamSid })));
24307
+ await state.trace?.append({
24308
+ at: Date.now(),
24309
+ payload: {
24310
+ callSid: state.callSid ?? undefined,
24311
+ carrier: state.carrier,
24312
+ direction: "outbound",
24313
+ envelope: clearMessage,
24314
+ event: "clear",
24315
+ streamId: state.streamSid
24316
+ },
24317
+ scenarioId: state.scenarioId ?? undefined,
24318
+ sessionId: state.sessionId ?? state.streamSid,
24319
+ type: "client.telephony_media"
24320
+ });
24321
+ await Promise.resolve(socket.send(JSON.stringify(clearMessage)));
24307
24322
  },
24308
24323
  close: async (code, reason) => {
24309
24324
  await Promise.resolve(socket.close(code, reason));
@@ -24506,7 +24521,9 @@ var createTwilioMediaStreamBridge = (socket, options) => {
24506
24521
  sessionHandle = createVoiceSession({
24507
24522
  audioConditioning,
24508
24523
  context: options.context,
24524
+ costAccountant: options.costAccountant,
24509
24525
  costTelemetry: options.costTelemetry,
24526
+ costTelephony: options.costTelephony,
24510
24527
  greeting: options.greeting,
24511
24528
  id: bridgeState.sessionId,
24512
24529
  languageStrategy: options.languageStrategy,
@@ -24514,6 +24531,7 @@ var createTwilioMediaStreamBridge = (socket, options) => {
24514
24531
  logger,
24515
24532
  phraseHints,
24516
24533
  reconnect,
24534
+ recording: options.recording,
24517
24535
  route,
24518
24536
  scenarioId: bridgeState.scenarioId ?? undefined,
24519
24537
  socket: voiceSocket,
@@ -24521,6 +24539,7 @@ var createTwilioMediaStreamBridge = (socket, options) => {
24521
24539
  stt: options.stt,
24522
24540
  sttFallback: resolveSTTFallbackConfig(options.sttFallback),
24523
24541
  sttLifecycle: options.sttLifecycle ?? runtimePreset.sttLifecycle,
24542
+ trace: options.trace,
24524
24543
  tts: options.tts,
24525
24544
  turnDetection
24526
24545
  });
@@ -107,6 +107,14 @@ export type TwilioMediaStreamBridgeOptions<TContext = unknown, TSession extends
107
107
  sessionId?: string;
108
108
  stt: STTAdapter;
109
109
  telephonyMediaCarrier?: "plivo" | "telnyx" | "twilio";
110
+ /** Capture call audio (per-channel) to a recording store. */
111
+ recording?: import("../core/types").VoiceSessionRecordingConfig;
112
+ /** Per-call cost accounting (STT/LLM/TTS) — emits turn.cost/cost.ready traces. */
113
+ costAccountant?: import("../core/costAccounting").VoiceCostAccountant;
114
+ /** Telephony cost provider for the accountant (e.g. "twilio"). */
115
+ costTelephony?: {
116
+ provider?: string;
117
+ };
110
118
  };
111
119
  export type TwilioMediaStreamBridge = {
112
120
  close: (reason?: string) => Promise<void>;
@@ -7538,7 +7538,7 @@ var createVoiceSession = (options) => {
7538
7538
  kickCallSilenceWatchdog();
7539
7539
  startAmdEvaluationTimer();
7540
7540
  if (shouldFireOnSession && options.greeting && session.turns.length === 0) {
7541
- const greetingText = typeof options.greeting === "function" ? await options.greeting() : options.greeting;
7541
+ const greetingText = typeof options.greeting === "function" ? await options.greeting({ session }) : options.greeting;
7542
7542
  const greetingTurnId = createId();
7543
7543
  await send({
7544
7544
  text: greetingText,
@@ -12838,8 +12838,23 @@ var createTwilioSocketAdapter = (socket, getState) => ({
12838
12838
  if (!state.streamSid) {
12839
12839
  return;
12840
12840
  }
12841
+ const clearMessage = { event: "clear", streamSid: state.streamSid };
12841
12842
  state.reviewRecorder?.recordTwilioOutbound({ event: "clear" });
12842
- await Promise.resolve(socket.send(JSON.stringify({ event: "clear", streamSid: state.streamSid })));
12843
+ await state.trace?.append({
12844
+ at: Date.now(),
12845
+ payload: {
12846
+ callSid: state.callSid ?? undefined,
12847
+ carrier: state.carrier,
12848
+ direction: "outbound",
12849
+ envelope: clearMessage,
12850
+ event: "clear",
12851
+ streamId: state.streamSid
12852
+ },
12853
+ scenarioId: state.scenarioId ?? undefined,
12854
+ sessionId: state.sessionId ?? state.streamSid,
12855
+ type: "client.telephony_media"
12856
+ });
12857
+ await Promise.resolve(socket.send(JSON.stringify(clearMessage)));
12843
12858
  },
12844
12859
  close: async (code, reason) => {
12845
12860
  await Promise.resolve(socket.close(code, reason));
@@ -13042,7 +13057,9 @@ var createTwilioMediaStreamBridge = (socket, options) => {
13042
13057
  sessionHandle = createVoiceSession({
13043
13058
  audioConditioning,
13044
13059
  context: options.context,
13060
+ costAccountant: options.costAccountant,
13045
13061
  costTelemetry: options.costTelemetry,
13062
+ costTelephony: options.costTelephony,
13046
13063
  greeting: options.greeting,
13047
13064
  id: bridgeState.sessionId,
13048
13065
  languageStrategy: options.languageStrategy,
@@ -13050,6 +13067,7 @@ var createTwilioMediaStreamBridge = (socket, options) => {
13050
13067
  logger,
13051
13068
  phraseHints,
13052
13069
  reconnect,
13070
+ recording: options.recording,
13053
13071
  route,
13054
13072
  scenarioId: bridgeState.scenarioId ?? undefined,
13055
13073
  socket: voiceSocket,
@@ -13057,6 +13075,7 @@ var createTwilioMediaStreamBridge = (socket, options) => {
13057
13075
  stt: options.stt,
13058
13076
  sttFallback: resolveSTTFallbackConfig(options.sttFallback),
13059
13077
  sttLifecycle: options.sttLifecycle ?? runtimePreset.sttLifecycle,
13078
+ trace: options.trace,
13060
13079
  tts: options.tts,
13061
13080
  turnDetection
13062
13081
  });
@@ -13361,7 +13380,7 @@ var createFakeSTTAdapter = (inputSpy, sttDelayMs) => ({
13361
13380
  final: new Set,
13362
13381
  partial: new Set
13363
13382
  };
13364
- let delivered = false;
13383
+ let sendCount = 0;
13365
13384
  return {
13366
13385
  close: async () => {
13367
13386
  for (const handler of listeners.close) {
@@ -13376,31 +13395,44 @@ var createFakeSTTAdapter = (inputSpy, sttDelayMs) => ({
13376
13395
  },
13377
13396
  send: async (audio) => {
13378
13397
  inputSpy.push(toUint8Array2(audio));
13379
- if (delivered) {
13398
+ sendCount += 1;
13399
+ if (sendCount === 1) {
13400
+ if (sttDelayMs > 0) {
13401
+ await Bun.sleep(sttDelayMs);
13402
+ }
13403
+ const receivedAt = Date.now();
13404
+ for (const handler of listeners.final) {
13405
+ handler({
13406
+ receivedAt,
13407
+ transcript: {
13408
+ id: "telephony-benchmark-final",
13409
+ isFinal: true,
13410
+ text: "hello from twilio"
13411
+ },
13412
+ type: "final"
13413
+ });
13414
+ }
13415
+ for (const handler of listeners.endOfTurn) {
13416
+ handler({
13417
+ reason: "vendor",
13418
+ receivedAt,
13419
+ type: "endOfTurn"
13420
+ });
13421
+ }
13380
13422
  return;
13381
13423
  }
13382
- delivered = true;
13383
- if (sttDelayMs > 0) {
13384
- await Bun.sleep(sttDelayMs);
13385
- }
13386
- const receivedAt = Date.now();
13387
- for (const handler of listeners.final) {
13388
- handler({
13389
- receivedAt,
13390
- transcript: {
13391
- id: "telephony-benchmark-final",
13392
- isFinal: true,
13393
- text: "hello from twilio"
13394
- },
13395
- type: "final"
13396
- });
13397
- }
13398
- for (const handler of listeners.endOfTurn) {
13399
- handler({
13400
- reason: "vendor",
13401
- receivedAt,
13402
- type: "endOfTurn"
13403
- });
13424
+ if (sendCount === 2) {
13425
+ for (const handler of listeners.partial) {
13426
+ handler({
13427
+ receivedAt: Date.now(),
13428
+ transcript: {
13429
+ id: "telephony-benchmark-partial",
13430
+ isFinal: false,
13431
+ text: "actually wait"
13432
+ },
13433
+ type: "partial"
13434
+ });
13435
+ }
13404
13436
  }
13405
13437
  }
13406
13438
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.546",
3
+ "version": "0.0.22-beta.548",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",