@absolutejs/voice 0.0.22-beta.0 → 0.0.22-beta.2

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.
@@ -0,0 +1,276 @@
1
+ import type { RedisClient } from 'bun';
2
+ import type { VoiceIntegrationSink } from './opsSinks';
3
+ import type { VoiceTraceRedactionConfig, VoiceTraceSink, VoiceTraceSinkDeliveryRecord, VoiceTraceSinkDeliveryStore, VoiceTraceSinkDeliveryQueueStatus } from './trace';
4
+ import type { VoiceOpsTaskPriority, StoredVoiceOpsTask, StoredVoiceIntegrationEvent, VoiceIntegrationDeliveryStatus, VoiceIntegrationEventStore, VoiceIntegrationWebhookConfig, VoiceOpsTaskKind, VoiceOpsTaskStatus, VoiceOpsTaskStore } from './ops';
5
+ export type VoiceOpsTaskLease = {
6
+ expiresAt: number;
7
+ taskId: string;
8
+ workerId: string;
9
+ };
10
+ export type VoiceRedisTaskLeaseClient = Pick<RedisClient, 'get' | 'send' | 'set'>;
11
+ export type VoiceRedisTaskLeaseCoordinatorOptions = {
12
+ client?: VoiceRedisTaskLeaseClient;
13
+ keyPrefix?: string;
14
+ url?: string;
15
+ };
16
+ export type VoiceRedisTaskLeaseCoordinator = {
17
+ claim: (input: {
18
+ leaseMs: number;
19
+ taskId: string;
20
+ workerId: string;
21
+ }) => Promise<boolean>;
22
+ get: (taskId: string) => Promise<VoiceOpsTaskLease | null>;
23
+ release: (input: {
24
+ taskId: string;
25
+ workerId: string;
26
+ }) => Promise<boolean>;
27
+ renew: (input: {
28
+ leaseMs: number;
29
+ taskId: string;
30
+ workerId: string;
31
+ }) => Promise<boolean>;
32
+ };
33
+ export type VoiceIdempotencyStore = {
34
+ has: (key: string) => Promise<boolean> | boolean;
35
+ remove: (key: string) => Promise<void> | void;
36
+ set: (key: string, input?: {
37
+ ttlSeconds?: number;
38
+ }) => Promise<void> | void;
39
+ };
40
+ export type VoiceRedisIdempotencyClient = Pick<RedisClient, 'del' | 'exists' | 'set'>;
41
+ export type VoiceRedisIdempotencyStoreOptions = {
42
+ client?: VoiceRedisIdempotencyClient;
43
+ keyPrefix?: string;
44
+ ttlSeconds?: number;
45
+ url?: string;
46
+ };
47
+ export type VoiceWebhookDeliveryWorkerOptions<TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent> = {
48
+ deadLetters?: VoiceIntegrationEventStore<TEvent>;
49
+ events: VoiceIntegrationEventStore<TEvent>;
50
+ idempotency?: VoiceIdempotencyStore;
51
+ idempotencyTtlSeconds?: number;
52
+ leaseMs?: number;
53
+ leases: VoiceRedisTaskLeaseCoordinator;
54
+ maxFailures?: number;
55
+ onDeadLetter?: (event: TEvent) => Promise<void> | void;
56
+ statuses?: VoiceIntegrationDeliveryStatus[];
57
+ webhook: VoiceIntegrationWebhookConfig;
58
+ workerId: string;
59
+ };
60
+ export type VoiceWebhookDeliveryWorkerResult = {
61
+ alreadyProcessed: number;
62
+ attempted: number;
63
+ deadLettered: number;
64
+ delivered: number;
65
+ failed: number;
66
+ skipped: number;
67
+ };
68
+ export type VoiceIntegrationEventQueueSummary = {
69
+ byType: Array<[StoredVoiceIntegrationEvent['type'], number]>;
70
+ deadLettered: number;
71
+ delivered: number;
72
+ failed: number;
73
+ pending: number;
74
+ retryEligible: number;
75
+ skipped: number;
76
+ total: number;
77
+ };
78
+ export type VoiceWebhookDeliveryWorkerLoopOptions<TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent> = {
79
+ onError?: (error: unknown) => Promise<void> | void;
80
+ pollIntervalMs?: number;
81
+ worker: ReturnType<typeof createVoiceWebhookDeliveryWorker<TEvent>>;
82
+ };
83
+ export type VoiceWebhookDeliveryWorkerLoop = {
84
+ isRunning: () => boolean;
85
+ start: () => void;
86
+ stop: () => void;
87
+ tick: () => Promise<VoiceWebhookDeliveryWorkerResult>;
88
+ };
89
+ export type VoiceIntegrationSinkWorkerOptions<TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent> = {
90
+ deadLetters?: VoiceIntegrationEventStore<TEvent>;
91
+ events: VoiceIntegrationEventStore<TEvent>;
92
+ idempotency?: VoiceIdempotencyStore;
93
+ idempotencyTtlSeconds?: number;
94
+ leaseMs?: number;
95
+ leases: VoiceRedisTaskLeaseCoordinator;
96
+ maxFailures?: number;
97
+ onDeadLetter?: (event: TEvent) => Promise<void> | void;
98
+ sinks: VoiceIntegrationSink[];
99
+ workerId: string;
100
+ };
101
+ export type VoiceIntegrationSinkWorkerResult = {
102
+ alreadyProcessed: number;
103
+ attempted: number;
104
+ deadLettered: number;
105
+ delivered: number;
106
+ failed: number;
107
+ skipped: number;
108
+ };
109
+ export type VoiceIntegrationSinkWorkerLoopOptions<TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent> = {
110
+ onError?: (error: unknown) => Promise<void> | void;
111
+ pollIntervalMs?: number;
112
+ worker: ReturnType<typeof createVoiceIntegrationSinkWorker<TEvent>>;
113
+ };
114
+ export type VoiceIntegrationSinkWorkerLoop = {
115
+ isRunning: () => boolean;
116
+ start: () => void;
117
+ stop: () => void;
118
+ tick: () => Promise<VoiceIntegrationSinkWorkerResult>;
119
+ };
120
+ export type VoiceTraceSinkDeliveryWorkerOptions<TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord> = {
121
+ deadLetters?: VoiceTraceSinkDeliveryStore<TDelivery>;
122
+ deliveries: VoiceTraceSinkDeliveryStore<TDelivery>;
123
+ idempotency?: VoiceIdempotencyStore;
124
+ idempotencyTtlSeconds?: number;
125
+ leaseMs?: number;
126
+ leases: VoiceRedisTaskLeaseCoordinator;
127
+ maxFailures?: number;
128
+ onDeadLetter?: (delivery: TDelivery) => Promise<void> | void;
129
+ redact?: VoiceTraceRedactionConfig;
130
+ sinks: VoiceTraceSink[];
131
+ statuses?: VoiceTraceSinkDeliveryQueueStatus[];
132
+ workerId: string;
133
+ };
134
+ export type VoiceTraceSinkDeliveryWorkerResult = {
135
+ alreadyProcessed: number;
136
+ attempted: number;
137
+ deadLettered: number;
138
+ delivered: number;
139
+ failed: number;
140
+ skipped: number;
141
+ };
142
+ export type VoiceTraceSinkDeliveryWorkerLoopOptions<TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord> = {
143
+ onError?: (error: unknown) => Promise<void> | void;
144
+ pollIntervalMs?: number;
145
+ worker: ReturnType<typeof createVoiceTraceSinkDeliveryWorker<TDelivery>>;
146
+ };
147
+ export type VoiceTraceSinkDeliveryWorkerLoop = {
148
+ isRunning: () => boolean;
149
+ start: () => void;
150
+ stop: () => void;
151
+ tick: () => Promise<VoiceTraceSinkDeliveryWorkerResult>;
152
+ };
153
+ export type VoiceTraceSinkDeliveryQueueSummary = {
154
+ deadLettered: number;
155
+ delivered: number;
156
+ failed: number;
157
+ pending: number;
158
+ retryEligible: number;
159
+ skipped: number;
160
+ total: number;
161
+ };
162
+ export type VoiceOpsTaskWorkerOptions<TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask> = {
163
+ leaseMs?: number;
164
+ leases: VoiceRedisTaskLeaseCoordinator;
165
+ tasks: VoiceOpsTaskStore<TTask>;
166
+ workerId: string;
167
+ };
168
+ export type VoiceOpsTaskClaimFilters = {
169
+ assignee?: string;
170
+ excludeTaskIds?: string[];
171
+ kinds?: VoiceOpsTaskKind[];
172
+ };
173
+ export type VoiceOpsTaskWorker<TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask> = {
174
+ assign: (taskId: string, assignee: string, input?: {
175
+ actor?: string;
176
+ at?: number;
177
+ }) => Promise<TTask>;
178
+ claimNext: (filters?: VoiceOpsTaskClaimFilters) => Promise<TTask | null>;
179
+ complete: (taskId: string, input?: {
180
+ actor?: string;
181
+ at?: number;
182
+ detail?: string;
183
+ }) => Promise<TTask>;
184
+ heartbeat: (taskId: string, input?: {
185
+ actor?: string;
186
+ at?: number;
187
+ detail?: string;
188
+ leaseMs?: number;
189
+ }) => Promise<TTask>;
190
+ requeue: (taskId: string, input?: {
191
+ actor?: string;
192
+ at?: number;
193
+ detail?: string;
194
+ }) => Promise<TTask>;
195
+ release: (taskId: string) => Promise<boolean>;
196
+ };
197
+ export type VoiceOpsTaskQueueSummary = {
198
+ byAssignee: Array<[string, number]>;
199
+ byClaimedBy: Array<[string, number]>;
200
+ claimed: number;
201
+ deadLettered: number;
202
+ byKind: Array<[VoiceOpsTaskKind, number]>;
203
+ byPriority: Array<[VoiceOpsTaskPriority, number]>;
204
+ byStatus: Array<[VoiceOpsTaskStatus, number]>;
205
+ failed: number;
206
+ inProgress: number;
207
+ open: number;
208
+ overdue: number;
209
+ retryEligible: number;
210
+ total: number;
211
+ unclaimed: number;
212
+ };
213
+ export type VoiceOpsTaskProcessorWorkerOptions<TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask> = {
214
+ deadLetters?: VoiceOpsTaskStore<TTask>;
215
+ filters?: VoiceOpsTaskClaimFilters;
216
+ leaseMs?: number;
217
+ maxFailures?: number;
218
+ onDeadLetter?: (task: TTask) => Promise<void> | void;
219
+ onError?: (error: unknown, task: TTask) => Promise<void> | void;
220
+ process: (task: TTask) => Promise<void | 'complete' | 'requeue' | {
221
+ action?: 'complete' | 'requeue';
222
+ detail?: string;
223
+ }> | void | 'complete' | 'requeue' | {
224
+ action?: 'complete' | 'requeue';
225
+ detail?: string;
226
+ };
227
+ tasks: VoiceOpsTaskStore<TTask>;
228
+ worker: VoiceOpsTaskWorker<TTask>;
229
+ };
230
+ export type VoiceOpsTaskProcessorWorkerResult = {
231
+ attempted: number;
232
+ completed: number;
233
+ deadLettered: number;
234
+ failed: number;
235
+ idle: number;
236
+ requeued: number;
237
+ };
238
+ export type VoiceOpsTaskProcessorWorkerLoopOptions<TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask> = {
239
+ onError?: (error: unknown) => Promise<void> | void;
240
+ pollIntervalMs?: number;
241
+ worker: ReturnType<typeof createVoiceOpsTaskProcessorWorker<TTask>>;
242
+ };
243
+ export type VoiceOpsTaskProcessorWorkerLoop = {
244
+ isRunning: () => boolean;
245
+ start: () => void;
246
+ stop: () => void;
247
+ tick: () => Promise<VoiceOpsTaskProcessorWorkerResult>;
248
+ };
249
+ export declare const summarizeVoiceIntegrationEvents: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(events: TEvent[], input?: {
250
+ deadLetters?: VoiceIntegrationEventStore<TEvent>;
251
+ }) => Promise<VoiceIntegrationEventQueueSummary> | VoiceIntegrationEventQueueSummary;
252
+ export declare const summarizeVoiceTraceSinkDeliveries: <TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(deliveries: TDelivery[], input?: {
253
+ deadLetters?: VoiceTraceSinkDeliveryStore<TDelivery>;
254
+ }) => Promise<VoiceTraceSinkDeliveryQueueSummary> | VoiceTraceSinkDeliveryQueueSummary;
255
+ export declare const summarizeVoiceOpsTaskQueue: <TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask>(tasks: TTask[], input?: {
256
+ deadLetters?: VoiceOpsTaskStore<TTask>;
257
+ }) => Promise<VoiceOpsTaskQueueSummary> | VoiceOpsTaskQueueSummary;
258
+ export declare const createVoiceRedisTaskLeaseCoordinator: (options?: VoiceRedisTaskLeaseCoordinatorOptions) => VoiceRedisTaskLeaseCoordinator;
259
+ export declare const createVoiceRedisIdempotencyStore: (options?: VoiceRedisIdempotencyStoreOptions) => VoiceIdempotencyStore;
260
+ export declare const createVoiceWebhookDeliveryWorker: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(options: VoiceWebhookDeliveryWorkerOptions<TEvent>) => {
261
+ drain: () => Promise<VoiceWebhookDeliveryWorkerResult>;
262
+ };
263
+ export declare const createVoiceWebhookDeliveryWorkerLoop: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(options: VoiceWebhookDeliveryWorkerLoopOptions<TEvent>) => VoiceWebhookDeliveryWorkerLoop;
264
+ export declare const createVoiceIntegrationSinkWorker: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(options: VoiceIntegrationSinkWorkerOptions<TEvent>) => {
265
+ drain: () => Promise<VoiceIntegrationSinkWorkerResult>;
266
+ };
267
+ export declare const createVoiceIntegrationSinkWorkerLoop: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(options: VoiceIntegrationSinkWorkerLoopOptions<TEvent>) => VoiceIntegrationSinkWorkerLoop;
268
+ export declare const createVoiceTraceSinkDeliveryWorker: <TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(options: VoiceTraceSinkDeliveryWorkerOptions<TDelivery>) => {
269
+ drain: () => Promise<VoiceTraceSinkDeliveryWorkerResult>;
270
+ };
271
+ export declare const createVoiceTraceSinkDeliveryWorkerLoop: <TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(options: VoiceTraceSinkDeliveryWorkerLoopOptions<TDelivery>) => VoiceTraceSinkDeliveryWorkerLoop;
272
+ export declare const createVoiceOpsTaskWorker: <TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask>(options: VoiceOpsTaskWorkerOptions<TTask>) => VoiceOpsTaskWorker<TTask>;
273
+ export declare const createVoiceOpsTaskProcessorWorker: <TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask>(options: VoiceOpsTaskProcessorWorkerOptions<TTask>) => {
274
+ drain: () => Promise<VoiceOpsTaskProcessorWorkerResult>;
275
+ };
276
+ export declare const createVoiceOpsTaskProcessorWorkerLoop: <TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask>(options: VoiceOpsTaskProcessorWorkerLoopOptions<TTask>) => VoiceOpsTaskProcessorWorkerLoop;
@@ -0,0 +1,14 @@
1
+ import type { S3Client, S3Options } from 'bun';
2
+ import type { StoredVoiceCallReviewArtifact, VoiceCallReviewStore } from './testing/review';
3
+ export type VoiceS3ReviewStoreFile = {
4
+ delete: () => Promise<void>;
5
+ exists: () => Promise<boolean>;
6
+ text: () => Promise<string>;
7
+ write: (data: string) => Promise<number>;
8
+ };
9
+ export type VoiceS3ReviewStoreClient = Pick<S3Client, 'file' | 'list'>;
10
+ export type VoiceS3ReviewStoreOptions = S3Options & {
11
+ client?: VoiceS3ReviewStoreClient;
12
+ keyPrefix?: string;
13
+ };
14
+ export declare const createVoiceS3ReviewStore: <TArtifact extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact>(options: VoiceS3ReviewStoreOptions) => VoiceCallReviewStore<TArtifact>;
@@ -0,0 +1,26 @@
1
+ import { type StoredVoiceTraceEvent, type VoiceTraceSinkDeliveryRecord, type VoiceTraceSinkDeliveryStore, type VoiceTraceEventStore } from './trace';
2
+ import type { StoredVoiceIntegrationEvent, StoredVoiceExternalObjectMap, StoredVoiceOpsTask, VoiceExternalObjectMapStore, VoiceIntegrationEventStore, VoiceOpsTaskStore } from './ops';
3
+ import type { StoredVoiceCallReviewArtifact, VoiceCallReviewStore } from './testing/review';
4
+ import type { VoiceSessionRecord, VoiceSessionStore } from './types';
5
+ export type VoiceSQLiteStoreOptions = {
6
+ path: string;
7
+ tableName?: string;
8
+ tablePrefix?: string;
9
+ };
10
+ export type VoiceSQLiteRuntimeStorage<TSession extends VoiceSessionRecord = VoiceSessionRecord, TReview extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact, TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask, TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent, TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap, TTrace extends StoredVoiceTraceEvent = StoredVoiceTraceEvent, TTraceDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord> = {
11
+ events: VoiceIntegrationEventStore<TEvent>;
12
+ externalObjects: VoiceExternalObjectMapStore<TMapping>;
13
+ reviews: VoiceCallReviewStore<TReview>;
14
+ session: VoiceSessionStore<TSession>;
15
+ tasks: VoiceOpsTaskStore<TTask>;
16
+ traceDeliveries: VoiceTraceSinkDeliveryStore<TTraceDelivery>;
17
+ traces: VoiceTraceEventStore<TTrace>;
18
+ };
19
+ export declare const createVoiceSQLiteSessionStore: <TSession extends VoiceSessionRecord = VoiceSessionRecord>(options: VoiceSQLiteStoreOptions) => VoiceSessionStore<TSession>;
20
+ export declare const createVoiceSQLiteReviewStore: <TArtifact extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact>(options: VoiceSQLiteStoreOptions) => VoiceCallReviewStore<TArtifact>;
21
+ export declare const createVoiceSQLiteTaskStore: <TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask>(options: VoiceSQLiteStoreOptions) => VoiceOpsTaskStore<TTask>;
22
+ export declare const createVoiceSQLiteIntegrationEventStore: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(options: VoiceSQLiteStoreOptions) => VoiceIntegrationEventStore<TEvent>;
23
+ export declare const createVoiceSQLiteExternalObjectMapStore: <TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap>(options: VoiceSQLiteStoreOptions) => VoiceExternalObjectMapStore<TMapping>;
24
+ export declare const createVoiceSQLiteTraceEventStore: <TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent>(options: VoiceSQLiteStoreOptions) => VoiceTraceEventStore<TEvent>;
25
+ export declare const createVoiceSQLiteTraceSinkDeliveryStore: <TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(options: VoiceSQLiteStoreOptions) => VoiceTraceSinkDeliveryStore<TDelivery>;
26
+ export declare const createVoiceSQLiteRuntimeStorage: <TSession extends VoiceSessionRecord = VoiceSessionRecord, TReview extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact, TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask, TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent, TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap, TTrace extends StoredVoiceTraceEvent = StoredVoiceTraceEvent, TTraceDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(options: VoiceSQLiteStoreOptions) => VoiceSQLiteRuntimeStorage<TSession, TReview, TTask, TEvent, TMapping, TTrace, TTraceDelivery>;
@@ -3747,6 +3747,17 @@ var createVoiceSession = (options) => {
3747
3747
  settleMs: options.sttFallback.settleMs ?? DEFAULT_FALLBACK_SETTLE_MS,
3748
3748
  trigger: options.sttFallback.trigger ?? "empty-or-low-confidence"
3749
3749
  } : undefined;
3750
+ const appendTrace = async (input) => {
3751
+ await options.trace?.append({
3752
+ at: Date.now(),
3753
+ metadata: input.metadata,
3754
+ payload: input.payload,
3755
+ scenarioId: input.session?.scenarioId ?? options.scenarioId,
3756
+ sessionId: options.id,
3757
+ turnId: input.turnId,
3758
+ type: input.type
3759
+ });
3760
+ };
3750
3761
  const phraseHints = options.phraseHints ?? [];
3751
3762
  const lexicon = options.lexicon ?? [];
3752
3763
  let socket = options.socket;
@@ -3927,6 +3938,23 @@ var createVoiceSession = (options) => {
3927
3938
  return;
3928
3939
  }
3929
3940
  const resolvedError = toError(error);
3941
+ await appendTrace({
3942
+ payload: {
3943
+ error: resolvedError.message,
3944
+ recoverable: false
3945
+ },
3946
+ session,
3947
+ type: "session.error"
3948
+ });
3949
+ await appendTrace({
3950
+ payload: {
3951
+ disposition: "failed",
3952
+ reason: resolvedError.message,
3953
+ type: "end"
3954
+ },
3955
+ session,
3956
+ type: "call.lifecycle"
3957
+ });
3930
3958
  await send({
3931
3959
  message: resolvedError.message,
3932
3960
  recoverable: false,
@@ -3984,6 +4012,16 @@ var createVoiceSession = (options) => {
3984
4012
  if (!didComplete) {
3985
4013
  return;
3986
4014
  }
4015
+ await appendTrace({
4016
+ payload: {
4017
+ disposition,
4018
+ reason: input.reason,
4019
+ target: input.target,
4020
+ type: "end"
4021
+ },
4022
+ session,
4023
+ type: "call.lifecycle"
4024
+ });
3987
4025
  await send({
3988
4026
  sessionId: options.id,
3989
4027
  type: "complete"
@@ -4045,7 +4083,7 @@ var createVoiceSession = (options) => {
4045
4083
  });
4046
4084
  };
4047
4085
  const transferInternal = async (input) => {
4048
- await writeSession((currentSession) => {
4086
+ const session = await writeSession((currentSession) => {
4049
4087
  pushCallLifecycleEvent(currentSession, {
4050
4088
  metadata: input.metadata,
4051
4089
  reason: input.reason,
@@ -4053,6 +4091,16 @@ var createVoiceSession = (options) => {
4053
4091
  type: "transfer"
4054
4092
  });
4055
4093
  });
4094
+ await appendTrace({
4095
+ metadata: input.metadata,
4096
+ payload: {
4097
+ reason: input.reason,
4098
+ target: input.target,
4099
+ type: "transfer"
4100
+ },
4101
+ session,
4102
+ type: "call.lifecycle"
4103
+ });
4056
4104
  await completeInternal(input.result, {
4057
4105
  disposition: "transferred",
4058
4106
  invokeOnComplete: false,
@@ -4062,13 +4110,22 @@ var createVoiceSession = (options) => {
4062
4110
  });
4063
4111
  };
4064
4112
  const escalateInternal = async (input) => {
4065
- await writeSession((currentSession) => {
4113
+ const session = await writeSession((currentSession) => {
4066
4114
  pushCallLifecycleEvent(currentSession, {
4067
4115
  metadata: input.metadata,
4068
4116
  reason: input.reason,
4069
4117
  type: "escalation"
4070
4118
  });
4071
4119
  });
4120
+ await appendTrace({
4121
+ metadata: input.metadata,
4122
+ payload: {
4123
+ reason: input.reason,
4124
+ type: "escalation"
4125
+ },
4126
+ session,
4127
+ type: "call.lifecycle"
4128
+ });
4072
4129
  await completeInternal(input.result, {
4073
4130
  disposition: "escalated",
4074
4131
  invokeOnComplete: false,
@@ -4077,12 +4134,20 @@ var createVoiceSession = (options) => {
4077
4134
  });
4078
4135
  };
4079
4136
  const markNoAnswerInternal = async (input) => {
4080
- await writeSession((currentSession) => {
4137
+ const session = await writeSession((currentSession) => {
4081
4138
  pushCallLifecycleEvent(currentSession, {
4082
4139
  metadata: input?.metadata,
4083
4140
  type: "no-answer"
4084
4141
  });
4085
4142
  });
4143
+ await appendTrace({
4144
+ metadata: input?.metadata,
4145
+ payload: {
4146
+ type: "no-answer"
4147
+ },
4148
+ session,
4149
+ type: "call.lifecycle"
4150
+ });
4086
4151
  await completeInternal(input?.result, {
4087
4152
  disposition: "no-answer",
4088
4153
  invokeOnComplete: false,
@@ -4090,12 +4155,20 @@ var createVoiceSession = (options) => {
4090
4155
  });
4091
4156
  };
4092
4157
  const markVoicemailInternal = async (input) => {
4093
- await writeSession((currentSession) => {
4158
+ const session = await writeSession((currentSession) => {
4094
4159
  pushCallLifecycleEvent(currentSession, {
4095
4160
  metadata: input?.metadata,
4096
4161
  type: "voicemail"
4097
4162
  });
4098
4163
  });
4164
+ await appendTrace({
4165
+ metadata: input?.metadata,
4166
+ payload: {
4167
+ type: "voicemail"
4168
+ },
4169
+ session,
4170
+ type: "call.lifecycle"
4171
+ });
4099
4172
  await completeInternal(input?.result, {
4100
4173
  disposition: "voicemail",
4101
4174
  invokeOnComplete: false,
@@ -4103,6 +4176,14 @@ var createVoiceSession = (options) => {
4103
4176
  });
4104
4177
  };
4105
4178
  const handleError = async (event) => {
4179
+ await appendTrace({
4180
+ payload: {
4181
+ code: event.code,
4182
+ error: event.error.message,
4183
+ recoverable: event.recoverable
4184
+ },
4185
+ type: "session.error"
4186
+ });
4106
4187
  await send({
4107
4188
  message: event.error.message,
4108
4189
  recoverable: event.recoverable,
@@ -4391,6 +4472,20 @@ var createVoiceSession = (options) => {
4391
4472
  transcript,
4392
4473
  type: "partial"
4393
4474
  });
4475
+ await appendTrace({
4476
+ payload: {
4477
+ confidence: transcript.confidence,
4478
+ isFinal: false,
4479
+ language: transcript.language,
4480
+ receivedAt: Date.now(),
4481
+ speaker: transcript.speaker,
4482
+ text: transcript.text,
4483
+ transcriptId: transcript.id,
4484
+ vendor: transcript.vendor
4485
+ },
4486
+ session,
4487
+ type: "turn.transcript"
4488
+ });
4394
4489
  };
4395
4490
  const handleFinal = async (transcript) => {
4396
4491
  const session = await writeSession((session2) => {
@@ -4420,6 +4515,20 @@ var createVoiceSession = (options) => {
4420
4515
  transcript,
4421
4516
  type: "final"
4422
4517
  });
4518
+ await appendTrace({
4519
+ payload: {
4520
+ confidence: transcript.confidence,
4521
+ isFinal: true,
4522
+ language: transcript.language,
4523
+ receivedAt: Date.now(),
4524
+ speaker: transcript.speaker,
4525
+ text: transcript.text,
4526
+ transcriptId: transcript.id,
4527
+ vendor: transcript.vendor
4528
+ },
4529
+ session,
4530
+ type: "turn.transcript"
4531
+ });
4423
4532
  };
4424
4533
  const resumePendingTurnCommit = (session) => {
4425
4534
  const pendingText = buildTurnText(session.currentTurn.transcripts, session.currentTurn.partialText, {
@@ -4576,11 +4685,30 @@ var createVoiceSession = (options) => {
4576
4685
  turnId: turn.id,
4577
4686
  type: "assistant"
4578
4687
  });
4688
+ await appendTrace({
4689
+ payload: {
4690
+ text: output.assistantText,
4691
+ ttsConfigured: Boolean(options.tts)
4692
+ },
4693
+ session,
4694
+ turnId: turn.id,
4695
+ type: "turn.assistant"
4696
+ });
4579
4697
  try {
4580
4698
  const activeTTSSession = await ensureTTSSession();
4581
4699
  if (activeTTSSession) {
4700
+ const ttsStartedAt = Date.now();
4582
4701
  activeTTSTurnId = turn.id;
4583
4702
  await activeTTSSession.send(output.assistantText);
4703
+ await appendTrace({
4704
+ payload: {
4705
+ elapsedMs: Date.now() - ttsStartedAt,
4706
+ status: "sent"
4707
+ },
4708
+ session,
4709
+ turnId: turn.id,
4710
+ type: "turn.assistant"
4711
+ });
4584
4712
  }
4585
4713
  } catch (error) {
4586
4714
  logger.warn("voice tts send failed", {
@@ -4588,6 +4716,15 @@ var createVoiceSession = (options) => {
4588
4716
  sessionId: options.id,
4589
4717
  turnId: turn.id
4590
4718
  });
4719
+ await appendTrace({
4720
+ payload: {
4721
+ error: toError(error).message,
4722
+ status: "tts-send-failed"
4723
+ },
4724
+ session,
4725
+ turnId: turn.id,
4726
+ type: "session.error"
4727
+ });
4591
4728
  }
4592
4729
  }
4593
4730
  if (output?.result !== undefined) {
@@ -4737,6 +4874,28 @@ var createVoiceSession = (options) => {
4737
4874
  session: updatedSession,
4738
4875
  turn
4739
4876
  });
4877
+ await appendTrace({
4878
+ payload: {
4879
+ correctionChanged: correctionDiagnostics?.changed,
4880
+ correctionProvider: correctionDiagnostics?.provider,
4881
+ fallbackUsed,
4882
+ reason,
4883
+ source,
4884
+ text: turn.text,
4885
+ transcriptCount: turn.transcripts.length
4886
+ },
4887
+ session: updatedSession,
4888
+ turnId: turn.id,
4889
+ type: "turn.committed"
4890
+ });
4891
+ await appendTrace({
4892
+ payload: {
4893
+ ...costEstimate
4894
+ },
4895
+ session: updatedSession,
4896
+ turnId: turn.id,
4897
+ type: "turn.cost"
4898
+ });
4740
4899
  await send({
4741
4900
  turn,
4742
4901
  type: "turn"
@@ -4788,6 +4947,15 @@ var createVoiceSession = (options) => {
4788
4947
  });
4789
4948
  }
4790
4949
  await options.store.set(options.id, session);
4950
+ if (shouldFireOnSession) {
4951
+ await appendTrace({
4952
+ payload: {
4953
+ type: "start"
4954
+ },
4955
+ session,
4956
+ type: "call.lifecycle"
4957
+ });
4958
+ }
4791
4959
  await send({
4792
4960
  sessionId: options.id,
4793
4961
  status: session.status,
@@ -4894,6 +5062,15 @@ var createVoiceSession = (options) => {
4894
5062
  await closeAdapter(reason);
4895
5063
  await Promise.resolve(socket.close(1000, reason));
4896
5064
  if (session.call?.endedAt && session.call.disposition === "closed") {
5065
+ await appendTrace({
5066
+ payload: {
5067
+ disposition: "closed",
5068
+ reason,
5069
+ type: "end"
5070
+ },
5071
+ session,
5072
+ type: "call.lifecycle"
5073
+ });
4897
5074
  await options.route.onCallEnd?.({
4898
5075
  api,
4899
5076
  context: options.context,