@absolutejs/voice 0.0.22-beta.534 → 0.0.22-beta.535

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.
Files changed (223) hide show
  1. package/dist/angular/index.js +347 -347
  2. package/dist/angular/voice-cost-dashboard.service.d.ts +1 -1
  3. package/dist/angular/voice-replay-timeline.service.d.ts +1 -1
  4. package/dist/client/agentSquadStatusWidget.d.ts +3 -3
  5. package/dist/client/browserVoiceSupport.d.ts +1 -1
  6. package/dist/client/callDebugger.d.ts +2 -2
  7. package/dist/client/callDebuggerWidget.d.ts +3 -3
  8. package/dist/client/campaignDialerProof.d.ts +4 -4
  9. package/dist/client/deliveryRuntime.d.ts +4 -4
  10. package/dist/client/deliveryRuntimeWidget.d.ts +3 -3
  11. package/dist/client/htmxAttributes.d.ts +1 -5
  12. package/dist/client/htmxBootstrap.js +82 -82
  13. package/dist/client/htmxDashboardRenderers.d.ts +17 -17
  14. package/dist/client/index.js +2478 -2484
  15. package/dist/client/liveOps.d.ts +2 -2
  16. package/dist/client/liveOpsWidget.d.ts +3 -3
  17. package/dist/client/opsActionCenter.d.ts +3 -3
  18. package/dist/client/opsActionCenterWidget.d.ts +3 -3
  19. package/dist/client/opsActionHistory.d.ts +2 -2
  20. package/dist/client/opsActionHistoryWidget.d.ts +2 -2
  21. package/dist/client/opsStatus.d.ts +2 -2
  22. package/dist/client/opsStatusWidget.d.ts +4 -4
  23. package/dist/client/platformCoverage.d.ts +2 -2
  24. package/dist/client/platformCoverageWidget.d.ts +3 -3
  25. package/dist/client/profileComparison.d.ts +2 -2
  26. package/dist/client/profileComparisonWidget.d.ts +3 -3
  27. package/dist/client/profileSwitchRecommendation.d.ts +2 -2
  28. package/dist/client/profileSwitchRecommendationWidget.d.ts +3 -3
  29. package/dist/client/proofTrends.d.ts +2 -2
  30. package/dist/client/proofTrendsWidget.d.ts +3 -3
  31. package/dist/client/providerCapabilities.d.ts +2 -2
  32. package/dist/client/providerCapabilitiesWidget.d.ts +3 -3
  33. package/dist/client/providerContracts.d.ts +2 -2
  34. package/dist/client/providerContractsWidget.d.ts +3 -3
  35. package/dist/client/providerSimulationControls.d.ts +1 -1
  36. package/dist/client/providerSimulationControlsWidget.d.ts +4 -4
  37. package/dist/client/providerStatus.d.ts +2 -2
  38. package/dist/client/providerStatusWidget.d.ts +3 -3
  39. package/dist/client/readinessFailures.d.ts +2 -2
  40. package/dist/client/readinessFailuresWidget.d.ts +3 -3
  41. package/dist/client/reconnectProfileEvidence.d.ts +2 -2
  42. package/dist/client/reconnectProfileEvidenceWidget.d.ts +3 -3
  43. package/dist/client/routingStatus.d.ts +2 -2
  44. package/dist/client/routingStatusWidget.d.ts +3 -3
  45. package/dist/client/sessionObservability.d.ts +2 -2
  46. package/dist/client/sessionObservabilityWidget.d.ts +3 -3
  47. package/dist/client/sessionSnapshot.d.ts +2 -2
  48. package/dist/client/sessionSnapshotWidget.d.ts +2 -2
  49. package/dist/client/traceTimeline.d.ts +2 -2
  50. package/dist/client/traceTimelineWidget.d.ts +3 -3
  51. package/dist/client/turnLatency.d.ts +4 -4
  52. package/dist/client/turnLatencyWidget.d.ts +3 -3
  53. package/dist/client/turnQuality.d.ts +2 -2
  54. package/dist/client/turnQualityWidget.d.ts +3 -3
  55. package/dist/client/workflowStatus.d.ts +2 -2
  56. package/dist/core/agent.d.ts +1 -1
  57. package/dist/core/agentSquadContract.d.ts +2 -2
  58. package/dist/core/agentState.d.ts +1 -1
  59. package/dist/core/aiScorecard.d.ts +1 -1
  60. package/dist/core/assistant.d.ts +1 -1
  61. package/dist/core/assistantHealth.d.ts +3 -3
  62. package/dist/core/assistantMemory.d.ts +7 -7
  63. package/dist/core/audioConditioning.d.ts +1 -1
  64. package/dist/core/audit.d.ts +6 -6
  65. package/dist/core/auditDeliveryRoutes.d.ts +7 -7
  66. package/dist/core/auditExport.d.ts +10 -10
  67. package/dist/core/auditRoutes.d.ts +5 -5
  68. package/dist/core/auditSinks.d.ts +7 -7
  69. package/dist/core/bargeInRoutes.d.ts +6 -6
  70. package/dist/core/bookingFlow.d.ts +1 -1
  71. package/dist/core/browserCallProfiles.d.ts +3 -3
  72. package/dist/core/browserMediaRoutes.d.ts +5 -5
  73. package/dist/core/callDebugger.d.ts +1 -1
  74. package/dist/core/callDisposition.d.ts +1 -1
  75. package/dist/core/callScorecard.d.ts +1 -1
  76. package/dist/core/campaign.d.ts +5 -5
  77. package/dist/core/campaignControls.d.ts +1 -1
  78. package/dist/core/campaignDialers.d.ts +4 -4
  79. package/dist/core/campaignTemplate.d.ts +1 -1
  80. package/dist/core/competitiveCoverage.d.ts +2 -2
  81. package/dist/core/conversationSimulator.d.ts +1 -1
  82. package/dist/core/correction.d.ts +1 -1
  83. package/dist/core/dataControl.d.ts +5 -5
  84. package/dist/core/deliveryRuntime.d.ts +7 -7
  85. package/dist/core/deliverySinkRoutes.d.ts +7 -7
  86. package/dist/core/demoReadyRoutes.d.ts +1 -1
  87. package/dist/core/dncRegistry.d.ts +1 -1
  88. package/dist/core/dtmfCollector.d.ts +1 -1
  89. package/dist/core/evalRoutes.d.ts +16 -16
  90. package/dist/core/fileStore.d.ts +18 -18
  91. package/dist/core/guardrails.d.ts +2 -2
  92. package/dist/core/handoff.d.ts +4 -4
  93. package/dist/core/handoffHealth.d.ts +4 -4
  94. package/dist/core/htmx.d.ts +1 -1
  95. package/dist/core/incidentBundle.d.ts +1 -1
  96. package/dist/core/incidentTimeline.d.ts +11 -11
  97. package/dist/core/latencySlo.d.ts +1 -1
  98. package/dist/core/liveCoach.d.ts +1 -1
  99. package/dist/core/liveLatency.d.ts +3 -3
  100. package/dist/core/liveOps.d.ts +6 -6
  101. package/dist/core/mediaPipelineRoutes.d.ts +4 -4
  102. package/dist/core/monitor.d.ts +1 -1
  103. package/dist/core/multilingualProof.d.ts +1 -1
  104. package/dist/core/observabilityExport.d.ts +15 -15
  105. package/dist/core/operationalStatus.d.ts +3 -3
  106. package/dist/core/operationsRecord.d.ts +8 -8
  107. package/dist/core/ops.d.ts +58 -58
  108. package/dist/core/opsActionAuditRoutes.d.ts +10 -10
  109. package/dist/core/opsConsoleRoutes.d.ts +3 -3
  110. package/dist/core/opsRecovery.d.ts +4 -4
  111. package/dist/core/opsSinks.d.ts +6 -6
  112. package/dist/core/opsStatusRoutes.d.ts +3 -3
  113. package/dist/core/opsWebhook.d.ts +9 -9
  114. package/dist/core/otelExporter.d.ts +1 -1
  115. package/dist/core/outcomeContract.d.ts +6 -6
  116. package/dist/core/pathway.d.ts +2 -2
  117. package/dist/core/pathwayRuntime.d.ts +2 -2
  118. package/dist/core/phoneAgent.d.ts +2 -2
  119. package/dist/core/phoneAgentProductionSmoke.d.ts +7 -7
  120. package/dist/core/platformCoverage.d.ts +1 -1
  121. package/dist/core/postCallSurvey.d.ts +1 -1
  122. package/dist/core/postgresStore.d.ts +8 -8
  123. package/dist/core/productionReadiness.d.ts +9 -9
  124. package/dist/core/profileSwitchRecommendation.d.ts +9 -9
  125. package/dist/core/proofAssertions.d.ts +1 -1
  126. package/dist/core/proofPack.d.ts +12 -12
  127. package/dist/core/proofRunner.d.ts +2 -2
  128. package/dist/core/proofTrends.d.ts +26 -26
  129. package/dist/core/providerCapabilities.d.ts +5 -5
  130. package/dist/core/providerDecisionTraces.d.ts +4 -4
  131. package/dist/core/providerHealth.d.ts +3 -3
  132. package/dist/core/providerOrchestration.d.ts +4 -4
  133. package/dist/core/providerRouterTraces.d.ts +2 -2
  134. package/dist/core/providerRoutingContract.d.ts +2 -2
  135. package/dist/core/providerSlo.d.ts +5 -5
  136. package/dist/core/providerStackRecommendations.d.ts +8 -8
  137. package/dist/core/qualityRoutes.d.ts +3 -3
  138. package/dist/core/queue.d.ts +26 -26
  139. package/dist/core/realtimeChannel.d.ts +5 -5
  140. package/dist/core/realtimeProviderContracts.d.ts +3 -3
  141. package/dist/core/reconnectContract.d.ts +16 -16
  142. package/dist/core/recordingStore.d.ts +2 -2
  143. package/dist/core/reminderScheduler.d.ts +1 -1
  144. package/dist/core/resilienceRoutes.d.ts +1 -1
  145. package/dist/core/routing.d.ts +1 -1
  146. package/dist/core/sessionObservability.d.ts +2 -2
  147. package/dist/core/sessionReplay.d.ts +12 -12
  148. package/dist/core/sessionSnapshot.d.ts +1 -1
  149. package/dist/core/simulationSuite.d.ts +4 -4
  150. package/dist/core/sloCalibration.d.ts +12 -12
  151. package/dist/core/sqliteStore.d.ts +8 -8
  152. package/dist/core/telephonyMediaRoutes.d.ts +4 -4
  153. package/dist/core/telephonyOutcome.d.ts +2 -2
  154. package/dist/core/toolContract.d.ts +10 -10
  155. package/dist/core/toolRuntime.d.ts +1 -1
  156. package/dist/core/trace.d.ts +18 -18
  157. package/dist/core/traceDeliveryRoutes.d.ts +7 -7
  158. package/dist/core/traceTimeline.d.ts +3 -3
  159. package/dist/core/turnLatency.d.ts +4 -4
  160. package/dist/core/turnQuality.d.ts +5 -5
  161. package/dist/core/types.d.ts +7 -2
  162. package/dist/core/voiceMonitoring.d.ts +11 -11
  163. package/dist/core/webhookVerification.d.ts +4 -4
  164. package/dist/core/whisperChannel.d.ts +4 -4
  165. package/dist/core/workflowContract.d.ts +13 -13
  166. package/dist/core/zeroDataRetention.d.ts +3 -13
  167. package/dist/drizzle/assistantMemory.d.ts +95 -0
  168. package/dist/drizzle/eval.d.ts +61 -0
  169. package/dist/drizzle/handoff.d.ts +62 -0
  170. package/dist/drizzle/index.d.ts +1029 -0
  171. package/dist/drizzle/index.js +3028 -0
  172. package/dist/drizzle/observabilityExport.d.ts +61 -0
  173. package/dist/drizzle/proofTrends.d.ts +126 -0
  174. package/dist/drizzle/runtimeStorage.d.ts +1311 -0
  175. package/dist/drizzle/shared.d.ts +75 -0
  176. package/dist/embed/index.js +72 -72
  177. package/dist/embed/voice-widget.js +2 -2
  178. package/dist/index.js +7034 -7101
  179. package/dist/react/index.js +2148 -2150
  180. package/dist/svelte/createVoiceAgentSquadStatus.d.ts +2 -2
  181. package/dist/svelte/createVoiceCallDebugger.d.ts +1 -1
  182. package/dist/svelte/createVoiceCallPlayer.d.ts +8 -8
  183. package/dist/svelte/createVoiceCampaignDialerProof.d.ts +2 -2
  184. package/dist/svelte/createVoiceCostDashboard.d.ts +4 -4
  185. package/dist/svelte/createVoiceDeliveryRuntime.d.ts +3 -3
  186. package/dist/svelte/createVoiceLiveAgentConsole.d.ts +4 -4
  187. package/dist/svelte/createVoiceLiveOps.d.ts +4 -4
  188. package/dist/svelte/createVoiceOpsActionCenter.d.ts +2 -2
  189. package/dist/svelte/createVoiceOpsStatus.d.ts +2 -2
  190. package/dist/svelte/createVoiceProviderCapabilities.d.ts +1 -1
  191. package/dist/svelte/createVoiceProviderContracts.d.ts +1 -1
  192. package/dist/svelte/createVoiceProviderSimulationControls.d.ts +1 -1
  193. package/dist/svelte/createVoiceProviderStatus.d.ts +1 -1
  194. package/dist/svelte/createVoiceReplayTimeline.d.ts +1 -1
  195. package/dist/svelte/createVoiceRoutingStatus.d.ts +1 -1
  196. package/dist/svelte/createVoiceSessionObservability.d.ts +1 -1
  197. package/dist/svelte/createVoiceSessionSnapshot.d.ts +1 -1
  198. package/dist/svelte/createVoiceTraceTimeline.d.ts +1 -1
  199. package/dist/svelte/createVoiceTurnLatency.d.ts +2 -2
  200. package/dist/svelte/createVoiceTurnQuality.d.ts +1 -1
  201. package/dist/svelte/createVoiceWidget.d.ts +2 -2
  202. package/dist/svelte/createVoiceWorkflowStatus.d.ts +1 -1
  203. package/dist/svelte/index.js +1518 -1522
  204. package/dist/telephony/matrix.d.ts +3 -3
  205. package/dist/telephony/plivo.d.ts +2 -2
  206. package/dist/telephony/security.d.ts +3 -3
  207. package/dist/telephony/telnyx.d.ts +1 -1
  208. package/dist/telephony/twilio.d.ts +2 -2
  209. package/dist/testing/benchmark.d.ts +6 -6
  210. package/dist/testing/corrected.d.ts +4 -4
  211. package/dist/testing/duplex.d.ts +2 -2
  212. package/dist/testing/fixtures.d.ts +1 -1
  213. package/dist/testing/index.js +1382 -1388
  214. package/dist/testing/review.d.ts +4 -4
  215. package/dist/testing/sessionBenchmark.d.ts +10 -10
  216. package/dist/testing/telephony.d.ts +3 -3
  217. package/dist/testing/tts.d.ts +1 -1
  218. package/dist/vue/VoiceCostDashboard.d.ts +2 -2
  219. package/dist/vue/index.js +2110 -2112
  220. package/dist/vue/useVoiceController.d.ts +5 -5
  221. package/dist/vue/useVoiceDeliveryRuntime.d.ts +1 -1
  222. package/dist/vue/useVoiceStream.d.ts +5 -5
  223. package/package.json +28 -6
@@ -0,0 +1,3028 @@
1
+ // @bun
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __name = (target, name) => {
6
+ Object.defineProperty(target, "name", {
7
+ value: name,
8
+ enumerable: false,
9
+ configurable: true
10
+ });
11
+ return target;
12
+ };
13
+ var __returnValue = (v) => v;
14
+ function __exportSetter(name, newValue) {
15
+ this[name] = __returnValue.bind(null, newValue);
16
+ }
17
+ var __export = (target, all) => {
18
+ for (var name in all)
19
+ __defProp(target, name, {
20
+ get: all[name],
21
+ enumerable: true,
22
+ configurable: true,
23
+ set: __exportSetter.bind(all, name)
24
+ });
25
+ };
26
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
27
+ var __typeError = (msg) => {
28
+ throw TypeError(msg);
29
+ };
30
+ var __defNormalProp = (obj, key, value) => (key in obj) ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
31
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
32
+ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
33
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
34
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
35
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
36
+ var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
37
+ var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
38
+ var __expectFn = (fn) => fn !== undefined && typeof fn !== "function" ? __typeError("Function expected") : fn;
39
+ var __decoratorContext = (kind, name, done, metadata, fns) => ({
40
+ kind: __decoratorStrings[kind],
41
+ name,
42
+ metadata,
43
+ addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
44
+ });
45
+ var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
46
+ var __runInitializers = (array, flags, self, value) => {
47
+ for (var i = 0, fns = array[flags >> 1], n = fns && fns.length;i < n; i++)
48
+ flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
49
+ return value;
50
+ };
51
+ var __decorateElement = (array, flags, name, decorators, target, extra) => {
52
+ var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
53
+ var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
54
+ var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
55
+ var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
56
+ get [name]() {
57
+ return __privateGet(this, extra);
58
+ },
59
+ set [name](x) {
60
+ __privateSet(this, extra, x);
61
+ }
62
+ }, name));
63
+ k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
64
+ for (var i = decorators.length - 1;i >= 0; i--) {
65
+ ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
66
+ if (k) {
67
+ ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => (name in x) };
68
+ if (k ^ 3)
69
+ access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
70
+ if (k > 2)
71
+ access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
72
+ }
73
+ it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? undefined : { get: desc.get, set: desc.set } : target, ctx);
74
+ done._ = 1;
75
+ if (k ^ 4 || it === undefined)
76
+ __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
77
+ else if (typeof it !== "object" || it === null)
78
+ __typeError("Object expected");
79
+ else
80
+ __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
81
+ }
82
+ return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
83
+ };
84
+ var __require = import.meta.require;
85
+
86
+ // src/drizzle/assistantMemory.ts
87
+ import { and, desc, eq } from "drizzle-orm";
88
+ import { bigint, jsonb, pgTable, primaryKey, text } from "drizzle-orm/pg-core";
89
+
90
+ // src/core/assistantMemory.ts
91
+ var createMemoryId = (input) => `${input.assistantId}:${input.namespace}:${input.key}`;
92
+ var createVoiceAssistantMemoryHandle = async (input) => {
93
+ const namespace = await resolveVoiceAssistantMemoryNamespace({
94
+ assistantId: input.assistantId,
95
+ context: input.context,
96
+ memory: input.memory,
97
+ session: input.session
98
+ });
99
+ const trace = async (event) => {
100
+ await input.trace?.append({
101
+ at: Date.now(),
102
+ payload: {
103
+ assistantId: input.assistantId,
104
+ namespace,
105
+ ...event
106
+ },
107
+ scenarioId: input.session.scenarioId,
108
+ sessionId: input.session.id,
109
+ type: "assistant.memory"
110
+ });
111
+ };
112
+ return {
113
+ namespace,
114
+ delete: async (key) => {
115
+ await input.memory.store.delete({
116
+ assistantId: input.assistantId,
117
+ key,
118
+ namespace
119
+ });
120
+ await trace({
121
+ action: "delete",
122
+ key
123
+ });
124
+ },
125
+ get: async (key) => {
126
+ const record = await input.memory.store.get({
127
+ assistantId: input.assistantId,
128
+ key,
129
+ namespace
130
+ });
131
+ await trace({
132
+ action: "get",
133
+ found: Boolean(record),
134
+ key
135
+ });
136
+ return record?.value;
137
+ },
138
+ list: async () => {
139
+ const records = await input.memory.store.list({
140
+ assistantId: input.assistantId,
141
+ namespace
142
+ });
143
+ await trace({
144
+ action: "list",
145
+ count: records.length
146
+ });
147
+ return records;
148
+ },
149
+ set: async (key, value, metadata) => {
150
+ const record = await input.memory.store.set({
151
+ assistantId: input.assistantId,
152
+ key,
153
+ metadata,
154
+ namespace,
155
+ value
156
+ });
157
+ await trace({
158
+ action: "set",
159
+ key
160
+ });
161
+ return record;
162
+ }
163
+ };
164
+ };
165
+ var createVoiceAssistantMemoryRecord = (input) => {
166
+ const now = Date.now();
167
+ return {
168
+ ...input,
169
+ createdAt: input.createdAt ?? input.updatedAt ?? now,
170
+ updatedAt: input.updatedAt ?? now
171
+ };
172
+ };
173
+ var createVoiceMemoryAssistantMemoryStore = () => {
174
+ const records = new Map;
175
+ return {
176
+ delete: async (input) => {
177
+ records.delete(createMemoryId(input));
178
+ },
179
+ get: async (input) => records.get(createMemoryId(input)),
180
+ list: async (input) => [...records.values()].filter((record) => record.assistantId === input.assistantId && (input.namespace === undefined || record.namespace === input.namespace)).sort((left, right) => right.updatedAt - left.updatedAt),
181
+ set: async (input) => {
182
+ const id = createMemoryId(input);
183
+ const existing = records.get(id);
184
+ const record = createVoiceAssistantMemoryRecord({
185
+ ...input,
186
+ createdAt: input.createdAt ?? existing?.createdAt,
187
+ updatedAt: input.updatedAt
188
+ });
189
+ records.set(id, record);
190
+ return record;
191
+ }
192
+ };
193
+ };
194
+ var resolveVoiceAssistantMemoryNamespace = async (input) => typeof input.memory.namespace === "function" ? await input.memory.namespace(input) : input.memory.namespace;
195
+
196
+ // src/drizzle/assistantMemory.ts
197
+ var voiceAssistantMemoryTable = pgTable("voice_assistant_memory", {
198
+ assistantId: text("assistant_id").notNull(),
199
+ key: text("key").notNull(),
200
+ namespace: text("namespace").notNull(),
201
+ payload: jsonb("payload").notNull(),
202
+ sortAt: bigint("sort_at", { mode: "number" }).notNull()
203
+ }, (table) => [
204
+ primaryKey({
205
+ columns: [table.assistantId, table.namespace, table.key]
206
+ })
207
+ ]);
208
+ var createDrizzleAssistantMemoryStore = (db) => {
209
+ const get = async (input) => {
210
+ const rows = await db.select({ payload: voiceAssistantMemoryTable.payload }).from(voiceAssistantMemoryTable).where(and(eq(voiceAssistantMemoryTable.assistantId, input.assistantId), eq(voiceAssistantMemoryTable.namespace, input.namespace), eq(voiceAssistantMemoryTable.key, input.key))).limit(1);
211
+ return rows[0]?.payload;
212
+ };
213
+ return {
214
+ get,
215
+ delete: async (input) => {
216
+ await db.delete(voiceAssistantMemoryTable).where(and(eq(voiceAssistantMemoryTable.assistantId, input.assistantId), eq(voiceAssistantMemoryTable.namespace, input.namespace), eq(voiceAssistantMemoryTable.key, input.key)));
217
+ },
218
+ list: async (input) => {
219
+ const rows = await db.select({ payload: voiceAssistantMemoryTable.payload }).from(voiceAssistantMemoryTable).where(input.namespace === undefined ? eq(voiceAssistantMemoryTable.assistantId, input.assistantId) : and(eq(voiceAssistantMemoryTable.assistantId, input.assistantId), eq(voiceAssistantMemoryTable.namespace, input.namespace))).orderBy(desc(voiceAssistantMemoryTable.sortAt));
220
+ return rows.map((row) => row.payload);
221
+ },
222
+ set: async (input) => {
223
+ const existing = await get(input);
224
+ const record = createVoiceAssistantMemoryRecord({
225
+ ...input,
226
+ createdAt: input.createdAt ?? existing?.createdAt,
227
+ updatedAt: input.updatedAt
228
+ });
229
+ await db.insert(voiceAssistantMemoryTable).values({
230
+ assistantId: record.assistantId,
231
+ key: record.key,
232
+ namespace: record.namespace,
233
+ payload: record,
234
+ sortAt: record.updatedAt
235
+ }).onConflictDoUpdate({
236
+ set: {
237
+ payload: record,
238
+ sortAt: record.updatedAt
239
+ },
240
+ target: [
241
+ voiceAssistantMemoryTable.assistantId,
242
+ voiceAssistantMemoryTable.namespace,
243
+ voiceAssistantMemoryTable.key
244
+ ]
245
+ });
246
+ return record;
247
+ }
248
+ };
249
+ };
250
+ var createVoiceDrizzleAssistantMemoryStore = (options) => createDrizzleAssistantMemoryStore(options.db);
251
+
252
+ // src/drizzle/shared.ts
253
+ import { desc as desc2, eq as eq2 } from "drizzle-orm";
254
+ import {
255
+ bigint as bigint2,
256
+ jsonb as jsonb2,
257
+ pgTable as pgTable2,
258
+ text as text2
259
+ } from "drizzle-orm/pg-core";
260
+ var voiceDocumentTable = (name) => pgTable2(name, {
261
+ id: text2("id").primaryKey(),
262
+ payload: jsonb2("payload").notNull(),
263
+ sortAt: bigint2("sort_at", { mode: "number" }).notNull()
264
+ });
265
+ var createVoiceDrizzleRecordStore = (input) => {
266
+ const get = async (id) => {
267
+ const rows = await input.db.select({ payload: input.table.payload }).from(input.table).where(eq2(input.table.id, id)).limit(1);
268
+ return rows[0]?.payload;
269
+ };
270
+ const list = async () => {
271
+ const rows = await input.db.select({ payload: input.table.payload }).from(input.table).orderBy(desc2(input.table.sortAt), desc2(input.table.id));
272
+ return rows.map((row) => row.payload);
273
+ };
274
+ const set = async (id, value) => {
275
+ const decorated = input.decorate(id, value);
276
+ await input.db.insert(input.table).values({
277
+ id,
278
+ payload: decorated,
279
+ sortAt: input.getSortAt(decorated)
280
+ }).onConflictDoUpdate({
281
+ set: {
282
+ payload: decorated,
283
+ sortAt: input.getSortAt(decorated)
284
+ },
285
+ target: input.table.id
286
+ });
287
+ };
288
+ const remove = async (id) => {
289
+ await input.db.delete(input.table).where(eq2(input.table.id, id));
290
+ };
291
+ return { get, list, remove, set };
292
+ };
293
+
294
+ // src/drizzle/eval.ts
295
+ var voiceEvalBaselineTable = voiceDocumentTable("voice_eval_baseline");
296
+ var VOICE_EVAL_BASELINE_ID = "baseline";
297
+ var createDrizzleEvalBaselineStore = (db) => {
298
+ const store = createVoiceDrizzleRecordStore({
299
+ db,
300
+ decorate: (_id, value) => value,
301
+ getSortAt: (value) => value.checkedAt,
302
+ table: voiceEvalBaselineTable
303
+ });
304
+ return {
305
+ get: async () => store.get(VOICE_EVAL_BASELINE_ID),
306
+ set: async (report) => {
307
+ await store.set(VOICE_EVAL_BASELINE_ID, report);
308
+ }
309
+ };
310
+ };
311
+ var createVoiceDrizzleEvalBaselineStore = (options) => createDrizzleEvalBaselineStore(options.db);
312
+
313
+ // src/drizzle/handoff.ts
314
+ var voiceHandoffDeliveriesTable = voiceDocumentTable("voice_handoff_deliveries");
315
+ var createDrizzleHandoffDeliveryStore = (db) => createVoiceDrizzleRecordStore({
316
+ db,
317
+ decorate: (_id, value) => value,
318
+ getSortAt: (value) => value.createdAt,
319
+ table: voiceHandoffDeliveriesTable
320
+ });
321
+ var createVoiceDrizzleHandoffDeliveryStore = (options) => createDrizzleHandoffDeliveryStore(options.db);
322
+
323
+ // src/drizzle/observabilityExport.ts
324
+ var voiceObservabilityExportDeliveryReceiptsTable = voiceDocumentTable("voice_observability_export_receipts");
325
+ var createVoiceDrizzleObservabilityExportDeliveryReceiptStore = (options) => createVoiceDrizzleRecordStore({
326
+ db: options.db,
327
+ decorate: (id, value) => ({
328
+ ...value,
329
+ id
330
+ }),
331
+ getSortAt: (value) => value.checkedAt,
332
+ table: voiceObservabilityExportDeliveryReceiptsTable
333
+ });
334
+
335
+ // src/drizzle/proofTrends.ts
336
+ var voiceRealCallProfileEvidenceTable = voiceDocumentTable("voice_real_call_profile_evidence");
337
+ var voiceRealCallProfileRecoveryJobsTable = voiceDocumentTable("voice_real_call_profile_recovery_jobs");
338
+ var parseRealCallProfileEvidenceBoundary = (value) => {
339
+ if (value === undefined) {
340
+ return;
341
+ }
342
+ if (value instanceof Date) {
343
+ return value.getTime();
344
+ }
345
+ if (typeof value === "number") {
346
+ return value;
347
+ }
348
+ return Date.parse(value);
349
+ };
350
+ var readRealCallProfileEvidenceSortTime = (evidence, fallback) => Date.parse(evidence.generatedAt ?? fallback) || Date.parse(fallback);
351
+ var matchesRealCallProfileEvidenceListOptions = (record, input) => {
352
+ const evidenceTime = readRealCallProfileEvidenceSortTime(record, record.createdAt);
353
+ const since = parseRealCallProfileEvidenceBoundary(input.since);
354
+ const until = parseRealCallProfileEvidenceBoundary(input.until);
355
+ return (!input.profileId || record.profileId === input.profileId) && (!input.sessionId || record.sessionId === input.sessionId) && (since === undefined || Number.isNaN(since) || evidenceTime >= since) && (until === undefined || Number.isNaN(until) || evidenceTime <= until);
356
+ };
357
+ var matchesRealCallProfileRecoveryJobListOptions = (job, input) => (!input.actionId || job.actionId === input.actionId) && (!input.status || job.status === input.status);
358
+ var createDrizzleRealCallProfileRecoveryJobStore = (db, options = {}) => {
359
+ const store = createVoiceDrizzleRecordStore({
360
+ db,
361
+ decorate: (_id, value) => value,
362
+ getSortAt: (value) => Date.parse(value.updatedAt) || Date.now(),
363
+ table: voiceRealCallProfileRecoveryJobsTable
364
+ });
365
+ const now = () => (options.now ?? (() => new Date))().toISOString();
366
+ const createId = () => `${options.idPrefix ?? "voice-recovery-job"}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
367
+ return {
368
+ get: store.get,
369
+ create: async (input) => {
370
+ const createdAt = input.createdAt ?? now();
371
+ const job = {
372
+ actionId: input.actionId,
373
+ createdAt,
374
+ id: input.id ?? createId(),
375
+ message: input.message,
376
+ status: input.status ?? "queued",
377
+ updatedAt: createdAt
378
+ };
379
+ await store.set(job.id, job);
380
+ return job;
381
+ },
382
+ list: async (input = {}) => {
383
+ const limit = Number.isFinite(input.limit) && input.limit !== undefined && input.limit > 0 ? Math.floor(input.limit) : 50;
384
+ return (await store.list()).filter((job) => matchesRealCallProfileRecoveryJobListOptions(job, input)).slice(0, limit);
385
+ },
386
+ update: async (id, update) => {
387
+ const existing = await store.get(id);
388
+ if (!existing) {
389
+ return;
390
+ }
391
+ const next = {
392
+ ...existing,
393
+ ...update,
394
+ updatedAt: update.updatedAt ?? now()
395
+ };
396
+ await store.set(id, next);
397
+ return next;
398
+ }
399
+ };
400
+ };
401
+ var createDrizzleRealCallProfileEvidenceStore = (db, options = {}) => {
402
+ const store = createVoiceDrizzleRecordStore({
403
+ db,
404
+ decorate: (_id, value) => value,
405
+ getSortAt: (value) => readRealCallProfileEvidenceSortTime(value, value.createdAt),
406
+ table: voiceRealCallProfileEvidenceTable
407
+ });
408
+ const now = () => (options.now ?? (() => new Date))().toISOString();
409
+ const createId = () => `${options.idPrefix ?? "voice-profile-evidence"}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
410
+ return {
411
+ get: store.get,
412
+ remove: store.remove,
413
+ append: async (input) => {
414
+ const record = {
415
+ ...input,
416
+ createdAt: input.createdAt ?? now(),
417
+ id: input.id ?? createId()
418
+ };
419
+ await store.set(record.id, record);
420
+ return record;
421
+ },
422
+ list: async (input = {}) => {
423
+ const limit = Number.isFinite(input.limit) && input.limit !== undefined && input.limit > 0 ? Math.floor(input.limit) : 500;
424
+ return (await store.list()).filter((record) => matchesRealCallProfileEvidenceListOptions(record, input)).slice(0, limit);
425
+ }
426
+ };
427
+ };
428
+ var createVoiceDrizzleRealCallProfileEvidenceStore = (options) => createDrizzleRealCallProfileEvidenceStore(options.db, {
429
+ idPrefix: options.idPrefix,
430
+ now: options.now
431
+ });
432
+ var createVoiceDrizzleRealCallProfileRecoveryJobStore = (options) => createDrizzleRealCallProfileRecoveryJobStore(options.db, {
433
+ idPrefix: options.idPrefix,
434
+ now: options.now
435
+ });
436
+
437
+ // src/core/audit.ts
438
+ var includes = (filter, value) => {
439
+ if (!filter) {
440
+ return true;
441
+ }
442
+ if (!value) {
443
+ return false;
444
+ }
445
+ return Array.isArray(filter) ? filter.includes(value) : filter === value;
446
+ };
447
+ var createVoiceAuditEvent = (event) => ({
448
+ ...event,
449
+ at: event.at ?? Date.now(),
450
+ id: event.id ?? crypto.randomUUID()
451
+ });
452
+ var createVoiceAuditLogger = (store) => ({
453
+ handoff: (input) => recordVoiceHandoffAuditEvent({ ...input, store }),
454
+ operatorAction: (input) => recordVoiceOperatorAuditEvent({ ...input, store }),
455
+ providerCall: (input) => recordVoiceProviderAuditEvent({ ...input, store }),
456
+ record: (event) => recordVoiceAuditEvent(store, event),
457
+ retention: (input) => recordVoiceRetentionAuditEvent({ ...input, store }),
458
+ toolCall: (input) => recordVoiceToolAuditEvent({ ...input, store })
459
+ });
460
+ var createVoiceMemoryAuditEventStore = () => {
461
+ const events = new Map;
462
+ return {
463
+ append: (event) => {
464
+ const stored = createVoiceAuditEvent(event);
465
+ events.set(stored.id, stored);
466
+ return stored;
467
+ },
468
+ get: (id) => events.get(id),
469
+ list: (filter) => filterVoiceAuditEvents([...events.values()], filter)
470
+ };
471
+ };
472
+ var createVoiceScopedAuditEventStore = (store, scope) => {
473
+ const upstreamFilter = (filter = {}) => {
474
+ const next = { ...filter };
475
+ delete next.limit;
476
+ if (scope.actorId !== undefined) {
477
+ delete next.actorId;
478
+ }
479
+ if (scope.outcome !== undefined) {
480
+ delete next.outcome;
481
+ }
482
+ if (scope.resourceId !== undefined) {
483
+ delete next.resourceId;
484
+ }
485
+ if (scope.resourceType !== undefined) {
486
+ delete next.resourceType;
487
+ }
488
+ if (scope.sessionId !== undefined) {
489
+ delete next.sessionId;
490
+ }
491
+ if (scope.traceId !== undefined) {
492
+ delete next.traceId;
493
+ }
494
+ if (scope.type !== undefined) {
495
+ delete next.type;
496
+ }
497
+ return next;
498
+ };
499
+ const scopedFilter = (filter = {}) => ({
500
+ ...filter,
501
+ ...scope
502
+ });
503
+ return {
504
+ append: (event) => store.append(event),
505
+ get: (id) => store.get(id),
506
+ list: async (filter) => filterVoiceAuditEvents(await store.list(upstreamFilter(filter)), scopedFilter(filter))
507
+ };
508
+ };
509
+ var filterVoiceAuditEvents = (events, filter = {}) => {
510
+ const sorted = events.filter((event) => {
511
+ if (!includes(filter.type, event.type)) {
512
+ return false;
513
+ }
514
+ if (!includes(filter.outcome, event.outcome)) {
515
+ return false;
516
+ }
517
+ if (filter.actorId && event.actor?.id !== filter.actorId) {
518
+ return false;
519
+ }
520
+ if (filter.resourceId && event.resource?.id !== filter.resourceId) {
521
+ return false;
522
+ }
523
+ if (filter.resourceType && event.resource?.type !== filter.resourceType) {
524
+ return false;
525
+ }
526
+ if (filter.sessionId && event.sessionId !== filter.sessionId) {
527
+ return false;
528
+ }
529
+ if (filter.traceId && event.traceId !== filter.traceId) {
530
+ return false;
531
+ }
532
+ if (typeof filter.after === "number" && event.at <= filter.after) {
533
+ return false;
534
+ }
535
+ if (typeof filter.afterOrAt === "number" && event.at < filter.afterOrAt) {
536
+ return false;
537
+ }
538
+ if (typeof filter.before === "number" && event.at >= filter.before) {
539
+ return false;
540
+ }
541
+ if (typeof filter.beforeOrAt === "number" && event.at > filter.beforeOrAt) {
542
+ return false;
543
+ }
544
+ return true;
545
+ }).sort((left, right) => left.at - right.at || left.id.localeCompare(right.id));
546
+ return typeof filter.limit === "number" && filter.limit >= 0 ? sorted.slice(0, filter.limit) : sorted;
547
+ };
548
+ var recordVoiceAuditEvent = (store, event) => store.append(createVoiceAuditEvent(event));
549
+ var recordVoiceHandoffAuditEvent = (input) => recordVoiceAuditEvent(input.store, {
550
+ action: "handoff",
551
+ actor: input.actor,
552
+ metadata: input.metadata,
553
+ outcome: input.outcome,
554
+ payload: {
555
+ fromAgentId: input.fromAgentId,
556
+ reason: input.reason,
557
+ target: input.target,
558
+ toAgentId: input.toAgentId
559
+ },
560
+ resource: {
561
+ id: input.toAgentId ?? input.target,
562
+ type: "handoff"
563
+ },
564
+ sessionId: input.sessionId,
565
+ traceId: input.traceId,
566
+ type: "handoff"
567
+ });
568
+ var recordVoiceOperatorAuditEvent = (input) => recordVoiceAuditEvent(input.store, {
569
+ action: input.action,
570
+ actor: input.actor,
571
+ metadata: input.metadata,
572
+ outcome: input.outcome ?? "success",
573
+ payload: input.payload,
574
+ resource: input.resource,
575
+ sessionId: input.sessionId,
576
+ traceId: input.traceId,
577
+ type: "operator.action"
578
+ });
579
+ var recordVoiceProviderAuditEvent = (input) => recordVoiceAuditEvent(input.store, {
580
+ action: `${input.kind}.provider.call`,
581
+ actor: input.actor,
582
+ metadata: input.metadata,
583
+ outcome: input.outcome,
584
+ payload: {
585
+ cost: input.cost,
586
+ elapsedMs: input.elapsedMs,
587
+ error: input.error,
588
+ kind: input.kind,
589
+ model: input.model,
590
+ provider: input.provider
591
+ },
592
+ resource: {
593
+ id: input.provider,
594
+ type: "provider"
595
+ },
596
+ sessionId: input.sessionId,
597
+ traceId: input.traceId,
598
+ type: "provider.call"
599
+ });
600
+ var recordVoiceRetentionAuditEvent = (input) => recordVoiceAuditEvent(input.store, {
601
+ action: input.dryRun ? "retention.plan" : "retention.apply",
602
+ actor: input.actor ?? {
603
+ id: "voice-retention",
604
+ kind: "system"
605
+ },
606
+ metadata: input.metadata,
607
+ outcome: "success",
608
+ payload: {
609
+ deletedCount: input.report.deletedCount,
610
+ dryRun: input.dryRun,
611
+ scopes: input.report.scopes
612
+ },
613
+ resource: {
614
+ type: "retention-policy"
615
+ },
616
+ type: "retention.policy"
617
+ });
618
+ var recordVoiceToolAuditEvent = (input) => recordVoiceAuditEvent(input.store, {
619
+ action: "tool.call",
620
+ actor: input.actor,
621
+ metadata: input.metadata,
622
+ outcome: input.outcome,
623
+ payload: {
624
+ elapsedMs: input.elapsedMs,
625
+ error: input.error,
626
+ toolCallId: input.toolCallId,
627
+ toolName: input.toolName
628
+ },
629
+ resource: {
630
+ id: input.toolName,
631
+ type: "tool"
632
+ },
633
+ sessionId: input.sessionId,
634
+ traceId: input.traceId,
635
+ type: "tool.call"
636
+ });
637
+
638
+ // src/core/ops.ts
639
+ var createVoiceExternalObjectMap = (input) => {
640
+ const at = input.at ?? Date.now();
641
+ return {
642
+ createdAt: at,
643
+ externalId: input.externalId,
644
+ id: createVoiceExternalObjectMapId(input),
645
+ provider: input.provider,
646
+ sinkId: input.sinkId,
647
+ sourceId: input.sourceId,
648
+ sourceType: input.sourceType,
649
+ updatedAt: at
650
+ };
651
+ };
652
+ var createVoiceExternalObjectMapId = (input) => [
653
+ input.provider,
654
+ input.sinkId ?? "default",
655
+ encodeURIComponent(input.sourceId)
656
+ ].join(":");
657
+ var sleep = async (delayMs) => {
658
+ if (delayMs <= 0) {
659
+ return;
660
+ }
661
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
662
+ };
663
+ var toHex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
664
+ var signVoiceIntegrationWebhookBody = async (input) => {
665
+ const encoder = new TextEncoder;
666
+ const key = await crypto.subtle.importKey("raw", encoder.encode(input.secret), {
667
+ hash: "SHA-256",
668
+ name: "HMAC"
669
+ }, false, ["sign"]);
670
+ const payload = encoder.encode(`${input.timestamp}.${input.body}`);
671
+ const signature = await crypto.subtle.sign("HMAC", key, payload);
672
+ return `sha256=${toHex(new Uint8Array(signature))}`;
673
+ };
674
+ var createVoiceWebhookDeliveryError = (input) => {
675
+ if (input.response) {
676
+ const statusText = input.response.statusText?.trim();
677
+ return `Attempt ${input.attempt} failed with webhook response ${input.response.status}${statusText ? ` ${statusText}` : ""}.`;
678
+ }
679
+ if (input.error instanceof Error) {
680
+ return `Attempt ${input.attempt} failed: ${input.error.message}`;
681
+ }
682
+ return `Attempt ${input.attempt} failed: ${String(input.error)}`;
683
+ };
684
+ var deliverVoiceIntegrationEvent = async (input) => {
685
+ const previousAttempts = input.event.deliveryAttempts ?? 0;
686
+ const matchesConfiguredTypes = input.webhook.eventTypes === undefined ? true : input.webhook.eventTypes.includes(input.event.type);
687
+ if (!matchesConfiguredTypes) {
688
+ return {
689
+ ...input.event,
690
+ deliveryAttempts: 0,
691
+ deliveryError: undefined,
692
+ deliveryStatus: "skipped"
693
+ };
694
+ }
695
+ const fetchImpl = input.webhook.fetch ?? globalThis.fetch;
696
+ if (typeof fetchImpl !== "function") {
697
+ return {
698
+ ...input.event,
699
+ deliveredTo: input.webhook.url,
700
+ deliveryAttempts: 0,
701
+ deliveryError: "Webhook delivery failed: fetch is not available in this runtime.",
702
+ deliveryStatus: "failed"
703
+ };
704
+ }
705
+ const maxRetries = Math.max(0, input.webhook.retries ?? 0);
706
+ const backoffMs = Math.max(0, input.webhook.backoffMs ?? 250);
707
+ const timeoutMs = Math.max(0, input.webhook.timeoutMs ?? 1e4);
708
+ const body = JSON.stringify({
709
+ createdAt: input.event.createdAt,
710
+ id: input.event.id,
711
+ payload: input.event.payload,
712
+ type: input.event.type
713
+ });
714
+ let lastError = "Webhook delivery failed.";
715
+ for (let attempt = 1;attempt <= maxRetries + 1; attempt += 1) {
716
+ let controller;
717
+ let timeout;
718
+ try {
719
+ const headers = {
720
+ "content-type": "application/json",
721
+ ...input.webhook.headers
722
+ };
723
+ if (input.webhook.signingSecret) {
724
+ const timestamp = String(Date.now());
725
+ headers["x-absolutejs-timestamp"] = timestamp;
726
+ headers["x-absolutejs-signature"] = await signVoiceIntegrationWebhookBody({
727
+ body,
728
+ secret: input.webhook.signingSecret,
729
+ timestamp
730
+ });
731
+ }
732
+ controller = timeoutMs > 0 ? new AbortController : undefined;
733
+ const activeController = controller;
734
+ timeout = activeController && timeoutMs > 0 ? setTimeout(() => activeController.abort(), timeoutMs) : undefined;
735
+ const response = await fetchImpl(input.webhook.url, {
736
+ body,
737
+ headers,
738
+ method: "POST",
739
+ signal: controller?.signal
740
+ });
741
+ if (response.ok) {
742
+ if (timeout) {
743
+ clearTimeout(timeout);
744
+ }
745
+ return {
746
+ ...input.event,
747
+ deliveredAt: Date.now(),
748
+ deliveredTo: input.webhook.url,
749
+ deliveryAttempts: previousAttempts + attempt,
750
+ deliveryError: undefined,
751
+ deliveryStatus: "delivered"
752
+ };
753
+ }
754
+ lastError = createVoiceWebhookDeliveryError({
755
+ attempt,
756
+ error: new Error(`HTTP ${response.status}`),
757
+ response
758
+ });
759
+ } catch (error) {
760
+ lastError = createVoiceWebhookDeliveryError({
761
+ attempt,
762
+ error
763
+ });
764
+ } finally {
765
+ if (timeout) {
766
+ clearTimeout(timeout);
767
+ }
768
+ }
769
+ if (attempt <= maxRetries) {
770
+ await sleep(backoffMs * attempt);
771
+ }
772
+ }
773
+ return {
774
+ ...input.event,
775
+ deliveredTo: input.webhook.url,
776
+ deliveryAttempts: previousAttempts + maxRetries + 1,
777
+ deliveryError: lastError,
778
+ deliveryStatus: "failed"
779
+ };
780
+ };
781
+ var ensureTaskHistory = (task, entry) => ({
782
+ ...task,
783
+ history: [
784
+ ...task.history ?? [],
785
+ {
786
+ ...entry,
787
+ at: entry.at ?? Date.now()
788
+ }
789
+ ],
790
+ updatedAt: Date.now()
791
+ });
792
+ var buildVoiceOpsTaskFromReview = (review) => {
793
+ const createdAt = review.generatedAt ?? Date.now();
794
+ const common = {
795
+ createdAt,
796
+ history: [
797
+ {
798
+ actor: "system",
799
+ at: createdAt,
800
+ detail: review.postCall?.summary,
801
+ type: "created"
802
+ }
803
+ ],
804
+ id: `${review.id}:ops`,
805
+ intakeId: review.id,
806
+ outcome: review.summary.outcome,
807
+ recommendedAction: review.postCall?.recommendedAction ?? "Review the voice artifact and decide the next operator action.",
808
+ reviewId: review.id,
809
+ status: "open",
810
+ target: review.postCall?.target,
811
+ updatedAt: createdAt
812
+ };
813
+ switch (review.summary.outcome) {
814
+ case "voicemail":
815
+ return {
816
+ ...common,
817
+ description: review.postCall?.summary ?? "Caller reached voicemail and needs a callback follow-up.",
818
+ kind: "callback",
819
+ title: review.postCall?.target ? `Call back voicemail from ${review.postCall.target}` : "Call back voicemail lead"
820
+ };
821
+ case "no-answer":
822
+ return {
823
+ ...common,
824
+ description: review.postCall?.summary ?? "Live contact was not established and should be retried.",
825
+ kind: "callback",
826
+ title: "Retry no-answer call"
827
+ };
828
+ case "escalated":
829
+ return {
830
+ ...common,
831
+ description: review.postCall?.summary ?? "The automated path escalated this call for human review.",
832
+ kind: "escalation",
833
+ title: "Review escalated call"
834
+ };
835
+ case "transferred":
836
+ return {
837
+ ...common,
838
+ description: review.postCall?.summary ?? "The call was transferred and should be verified downstream.",
839
+ kind: "transfer-check",
840
+ title: review.postCall?.target ? `Verify transfer to ${review.postCall.target}` : "Verify call transfer"
841
+ };
842
+ case "failed":
843
+ return {
844
+ ...common,
845
+ description: review.postCall?.summary ?? "The call failed and needs operator review before retry.",
846
+ kind: "retry-review",
847
+ title: "Inspect failed call before retry"
848
+ };
849
+ default:
850
+ return null;
851
+ }
852
+ };
853
+ var withVoiceIntegrationEventId = (id, event) => ({
854
+ ...event,
855
+ id
856
+ });
857
+ var withVoiceOpsTaskId = (id, task) => ({
858
+ ...task,
859
+ id
860
+ });
861
+ var DEFAULT_VOICE_OPS_TASK_POLICIES = {
862
+ escalated: {
863
+ dueInMs: 10 * 60000,
864
+ name: "escalation-rapid-response",
865
+ priority: "urgent"
866
+ },
867
+ failed: {
868
+ dueInMs: 15 * 60000,
869
+ name: "failed-call-review",
870
+ priority: "high"
871
+ },
872
+ "no-answer": {
873
+ dueInMs: 2 * 60 * 60000,
874
+ name: "no-answer-retry",
875
+ priority: "normal"
876
+ },
877
+ transferred: {
878
+ dueInMs: 20 * 60000,
879
+ name: "transfer-verification",
880
+ priority: "normal"
881
+ },
882
+ voicemail: {
883
+ dueInMs: 30 * 60000,
884
+ name: "voicemail-callback",
885
+ priority: "high"
886
+ }
887
+ };
888
+ var applyVoiceOpsTaskAssignmentRule = (task, rule, input = {}) => {
889
+ const updatedTask = {
890
+ ...task,
891
+ assignee: rule.assign ?? task.assignee,
892
+ priority: rule.priority ?? task.priority,
893
+ queue: rule.queue ?? task.queue,
894
+ recommendedAction: rule.recommendedAction ?? task.recommendedAction,
895
+ title: rule.title ?? task.title
896
+ };
897
+ return ensureTaskHistory(updatedTask, {
898
+ actor: input.actor ?? "system",
899
+ at: input.at,
900
+ detail: input.detail ?? rule.description ?? (rule.name ? `Applied assignment rule ${rule.name}` : "Applied assignment rule"),
901
+ type: "assigned"
902
+ });
903
+ };
904
+ var applyVoiceOpsTaskPolicy = (task, policy, input = {}) => {
905
+ const at = input.at ?? Date.now();
906
+ const updatedTask = {
907
+ ...task,
908
+ assignee: policy.assignee ?? task.assignee,
909
+ dueAt: typeof policy.dueInMs === "number" ? at + Math.max(0, policy.dueInMs) : task.dueAt,
910
+ policyName: policy.name ?? task.policyName,
911
+ priority: policy.priority ?? task.priority,
912
+ queue: policy.queue ?? task.queue,
913
+ recommendedAction: policy.recommendedAction ?? task.recommendedAction,
914
+ target: policy.target ?? task.target,
915
+ title: policy.title ?? task.title
916
+ };
917
+ return ensureTaskHistory(updatedTask, {
918
+ actor: input.actor ?? "system",
919
+ at,
920
+ detail: input.detail ?? (policy.name ? `Applied ops policy ${policy.name}` : "Applied ops task policy"),
921
+ type: "policy-applied"
922
+ });
923
+ };
924
+ var assignVoiceOpsTask = (task, owner, input = {}) => {
925
+ const normalizedOwner = owner.trim() || "ops";
926
+ return ensureTaskHistory({
927
+ ...task,
928
+ assignee: normalizedOwner
929
+ }, {
930
+ actor: input.actor ?? normalizedOwner,
931
+ at: input.at,
932
+ detail: `Assigned to ${normalizedOwner}`,
933
+ type: "assigned"
934
+ });
935
+ };
936
+ var buildVoiceOpsTaskFromSLABreach = (task, policy = {}) => {
937
+ const createdAt = task.slaBreachedAt ?? Date.now();
938
+ const followUp = withVoiceOpsTaskId(`${task.id}:sla`, {
939
+ assignee: policy.assignee ?? task.assignee,
940
+ createdAt,
941
+ description: policy.description ?? `Task ${task.id} breached its SLA and needs operator follow-up.`,
942
+ dueAt: typeof policy.dueInMs === "number" ? createdAt + Math.max(0, policy.dueInMs) : undefined,
943
+ history: [
944
+ {
945
+ actor: "system",
946
+ at: createdAt,
947
+ detail: policy.name ?? (task.policyName ? `Created from SLA breach on policy ${task.policyName}` : "Created from SLA breach"),
948
+ type: "created"
949
+ }
950
+ ],
951
+ kind: task.kind,
952
+ intakeId: task.intakeId,
953
+ outcome: task.outcome,
954
+ priority: policy.priority ?? "urgent",
955
+ policyName: policy.name ?? `${task.policyName ?? task.kind}:sla`,
956
+ queue: policy.queue ?? task.queue,
957
+ recommendedAction: policy.recommendedAction ?? `Review overdue task ${task.id} and decide the next operator action.`,
958
+ reviewId: task.reviewId,
959
+ status: "open",
960
+ target: task.target,
961
+ title: policy.title ?? `SLA follow-up for ${task.title}`,
962
+ updatedAt: createdAt
963
+ });
964
+ return followUp;
965
+ };
966
+ var claimVoiceOpsTask = (task, workerId, input = {
967
+ leaseMs: 30000
968
+ }) => {
969
+ const at = input.at ?? Date.now();
970
+ const leaseMs = Math.max(1, input.leaseMs);
971
+ return ensureTaskHistory({
972
+ ...task,
973
+ claimExpiresAt: at + leaseMs,
974
+ claimedAt: at,
975
+ claimedBy: workerId,
976
+ status: task.status === "done" ? task.status : "in-progress"
977
+ }, {
978
+ actor: input.actor ?? workerId,
979
+ at,
980
+ detail: input.detail ?? `Claimed by ${workerId}`,
981
+ type: "claimed"
982
+ });
983
+ };
984
+ var completeVoiceOpsTask = (task, input = {}) => ensureTaskHistory({
985
+ ...task,
986
+ claimExpiresAt: undefined,
987
+ claimedAt: undefined,
988
+ claimedBy: undefined,
989
+ lastProcessedAt: input.at ?? Date.now(),
990
+ processingError: undefined,
991
+ status: "done"
992
+ }, {
993
+ actor: input.actor ?? task.assignee ?? "ops",
994
+ at: input.at,
995
+ detail: input.detail ?? "Marked done",
996
+ type: "completed"
997
+ });
998
+ var createVoiceCallCompletedEvent = (input) => createVoiceIntegrationEvent("call.completed", {
999
+ call: input.session.call,
1000
+ disposition: input.disposition ?? input.session.call?.disposition,
1001
+ scenarioId: input.session.scenarioId,
1002
+ sessionId: input.session.id,
1003
+ sessionSummary: input.sessionSummary,
1004
+ status: input.session.status,
1005
+ turnCount: input.session.turns.length
1006
+ }, {
1007
+ id: `${input.session.id}:call.completed`
1008
+ });
1009
+ var createVoiceIntegrationEvent = (type, payload, input = {}) => ({
1010
+ createdAt: input.createdAt ?? Date.now(),
1011
+ id: input.id ?? crypto.randomUUID(),
1012
+ payload,
1013
+ type
1014
+ });
1015
+ var createVoiceReviewSavedEvent = (review) => createVoiceIntegrationEvent("review.saved", {
1016
+ elapsedMs: review.summary.elapsedMs,
1017
+ firstTurnLatencyMs: review.summary.firstTurnLatencyMs,
1018
+ outcome: review.summary.outcome,
1019
+ postCall: review.postCall,
1020
+ reviewId: review.id,
1021
+ title: review.title
1022
+ }, {
1023
+ id: `${review.id}:review.saved`
1024
+ });
1025
+ var createVoiceTaskCreatedEvent = (task) => createVoiceIntegrationEvent("task.created", {
1026
+ assignee: task.assignee,
1027
+ dueAt: task.dueAt,
1028
+ kind: task.kind,
1029
+ outcome: task.outcome,
1030
+ priority: task.priority,
1031
+ queue: task.queue,
1032
+ recommendedAction: task.recommendedAction,
1033
+ reviewId: task.reviewId,
1034
+ status: task.status,
1035
+ target: task.target,
1036
+ taskId: task.id,
1037
+ title: task.title
1038
+ }, {
1039
+ id: `${task.id}:task.created:${task.updatedAt}`
1040
+ });
1041
+ var createVoiceTaskSLABreachedEvent = (task) => createVoiceIntegrationEvent("task.sla_breached", {
1042
+ assignee: task.assignee,
1043
+ dueAt: task.dueAt,
1044
+ kind: task.kind,
1045
+ outcome: task.outcome,
1046
+ priority: task.priority,
1047
+ queue: task.queue,
1048
+ recommendedAction: task.recommendedAction,
1049
+ reviewId: task.reviewId,
1050
+ slaBreachedAt: task.slaBreachedAt,
1051
+ status: task.status,
1052
+ target: task.target,
1053
+ taskId: task.id,
1054
+ title: task.title
1055
+ }, {
1056
+ id: `${task.id}:task.sla_breached:${task.slaBreachedAt ?? task.updatedAt}`
1057
+ });
1058
+ var createVoiceTaskUpdatedEvent = (task) => createVoiceIntegrationEvent("task.updated", {
1059
+ assignee: task.assignee,
1060
+ dueAt: task.dueAt,
1061
+ history: task.history,
1062
+ kind: task.kind,
1063
+ outcome: task.outcome,
1064
+ priority: task.priority,
1065
+ queue: task.queue,
1066
+ recommendedAction: task.recommendedAction,
1067
+ reviewId: task.reviewId,
1068
+ slaBreachedAt: task.slaBreachedAt,
1069
+ status: task.status,
1070
+ target: task.target,
1071
+ taskId: task.id,
1072
+ title: task.title,
1073
+ updatedAt: task.updatedAt
1074
+ }, {
1075
+ id: `${task.id}:task.updated:${task.updatedAt}`
1076
+ });
1077
+ var deadLetterVoiceOpsTask = (task, input = {}) => {
1078
+ const at = input.at ?? Date.now();
1079
+ return ensureTaskHistory({
1080
+ ...task,
1081
+ claimExpiresAt: undefined,
1082
+ claimedAt: undefined,
1083
+ claimedBy: undefined,
1084
+ deadLetteredAt: at,
1085
+ lastProcessedAt: at,
1086
+ status: "open"
1087
+ }, {
1088
+ actor: input.actor ?? task.assignee ?? "ops",
1089
+ at,
1090
+ detail: input.detail ?? "Task moved to dead-letter queue",
1091
+ type: "dead-lettered"
1092
+ });
1093
+ };
1094
+ var failVoiceOpsTask = (task, input = {}) => {
1095
+ const at = input.at ?? Date.now();
1096
+ const detail = input.detail ?? input.error ?? "Task processing failed";
1097
+ return ensureTaskHistory({
1098
+ ...task,
1099
+ lastProcessedAt: at,
1100
+ processingAttempts: (task.processingAttempts ?? 0) + 1,
1101
+ processingError: input.error ?? detail,
1102
+ status: task.status === "done" ? task.status : "open"
1103
+ }, {
1104
+ actor: input.actor ?? task.claimedBy ?? task.assignee ?? "ops",
1105
+ at,
1106
+ detail,
1107
+ type: "failed"
1108
+ });
1109
+ };
1110
+ var hasVoiceOpsTaskSLABreach = (task) => typeof task.slaBreachedAt === "number";
1111
+ var heartbeatVoiceOpsTask = (task, workerId, input = {
1112
+ leaseMs: 30000
1113
+ }) => {
1114
+ if (task.claimedBy && task.claimedBy !== workerId) {
1115
+ throw new Error(`Cannot heartbeat task ${task.id}: claimed by ${task.claimedBy}, not ${workerId}.`);
1116
+ }
1117
+ const at = input.at ?? Date.now();
1118
+ const leaseMs = Math.max(1, input.leaseMs);
1119
+ return ensureTaskHistory({
1120
+ ...task,
1121
+ claimExpiresAt: at + leaseMs,
1122
+ claimedAt: task.claimedAt ?? at,
1123
+ claimedBy: workerId
1124
+ }, {
1125
+ actor: input.actor ?? workerId,
1126
+ at,
1127
+ detail: input.detail ?? `Heartbeat from ${workerId}`,
1128
+ type: "heartbeat"
1129
+ });
1130
+ };
1131
+ var isVoiceOpsTaskOverdue = (task, input = {}) => typeof task.dueAt === "number" && task.status !== "done" && task.dueAt <= (input.at ?? Date.now());
1132
+ var listVoiceOpsTasks = (tasks) => [...tasks].sort((left, right) => right.createdAt - left.createdAt);
1133
+ var markVoiceOpsTaskSLABreached = (task, input = {}) => {
1134
+ const at = input.at ?? Date.now();
1135
+ if (hasVoiceOpsTaskSLABreach(task)) {
1136
+ return task;
1137
+ }
1138
+ return ensureTaskHistory({
1139
+ ...task,
1140
+ slaBreachedAt: at
1141
+ }, {
1142
+ actor: input.actor ?? "system",
1143
+ at,
1144
+ detail: input.detail ?? "Task breached its SLA",
1145
+ type: "sla-breached"
1146
+ });
1147
+ };
1148
+ var matchesVoiceOpsTaskAssignmentRule = (task, rule) => {
1149
+ const { when } = rule;
1150
+ if (!when) {
1151
+ return true;
1152
+ }
1153
+ if (when.assignee !== undefined && task.assignee !== when.assignee) {
1154
+ return false;
1155
+ }
1156
+ if (when.kind !== undefined && task.kind !== when.kind) {
1157
+ return false;
1158
+ }
1159
+ if (when.outcome !== undefined && task.outcome !== when.outcome) {
1160
+ return false;
1161
+ }
1162
+ if (when.policyName !== undefined && task.policyName !== when.policyName) {
1163
+ return false;
1164
+ }
1165
+ if (when.priority !== undefined && task.priority !== when.priority) {
1166
+ return false;
1167
+ }
1168
+ if (when.queue !== undefined && task.queue !== when.queue) {
1169
+ return false;
1170
+ }
1171
+ if (when.status !== undefined && task.status !== when.status) {
1172
+ return false;
1173
+ }
1174
+ return true;
1175
+ };
1176
+ var reopenVoiceOpsTask = (task, input = {}) => ensureTaskHistory({
1177
+ ...task,
1178
+ claimExpiresAt: undefined,
1179
+ claimedAt: undefined,
1180
+ claimedBy: undefined,
1181
+ deadLetteredAt: undefined,
1182
+ processingError: undefined,
1183
+ status: "open"
1184
+ }, {
1185
+ actor: input.actor ?? task.assignee ?? "ops",
1186
+ at: input.at,
1187
+ detail: input.detail ?? "Task reopened",
1188
+ type: "reopened"
1189
+ });
1190
+ var requeueVoiceOpsTask = (task, input = {}) => ensureTaskHistory({
1191
+ ...task,
1192
+ claimExpiresAt: undefined,
1193
+ claimedAt: undefined,
1194
+ claimedBy: undefined,
1195
+ processingError: undefined,
1196
+ status: "open"
1197
+ }, {
1198
+ actor: input.actor ?? task.claimedBy ?? task.assignee ?? "ops",
1199
+ at: input.at,
1200
+ detail: input.detail ?? "Task requeued",
1201
+ type: "requeued"
1202
+ });
1203
+ var resolveVoiceOpsTaskAgeBucket = (task, input = {}) => {
1204
+ const at = input.at ?? Date.now();
1205
+ const freshMs = Math.max(0, input.agingMs ?? 30 * 60000);
1206
+ const staleMs = Math.max(freshMs, input.staleMs ?? 4 * 60 * 60000);
1207
+ const dueSoonMs = Math.max(0, input.dueSoonMs ?? 15 * 60000);
1208
+ const ageMs = Math.max(0, at - task.createdAt);
1209
+ if (isVoiceOpsTaskOverdue(task, { at })) {
1210
+ return ageMs >= staleMs ? "stale" : "overdue";
1211
+ }
1212
+ if (typeof task.dueAt === "number" && task.status !== "done" && task.dueAt - at <= dueSoonMs) {
1213
+ return "due-soon";
1214
+ }
1215
+ if (ageMs >= staleMs) {
1216
+ return "stale";
1217
+ }
1218
+ if (ageMs >= freshMs) {
1219
+ return "aging";
1220
+ }
1221
+ return "fresh";
1222
+ };
1223
+ var resolveVoiceOpsTaskAssignment = (input) => input.rules?.find((rule) => matchesVoiceOpsTaskAssignmentRule(input.task, rule));
1224
+ var resolveVoiceOpsTaskPolicy = (input) => {
1225
+ const { disposition } = input;
1226
+ if (!disposition) {
1227
+ return;
1228
+ }
1229
+ const defaultPolicy = DEFAULT_VOICE_OPS_TASK_POLICIES[disposition];
1230
+ const customPolicy = input.policies?.[disposition];
1231
+ if (!defaultPolicy && !customPolicy) {
1232
+ return;
1233
+ }
1234
+ return {
1235
+ ...defaultPolicy,
1236
+ ...customPolicy
1237
+ };
1238
+ };
1239
+ var startVoiceOpsTask = (task, input = {}) => ensureTaskHistory({
1240
+ ...task,
1241
+ status: "in-progress"
1242
+ }, {
1243
+ actor: input.actor ?? task.assignee ?? "ops",
1244
+ at: input.at,
1245
+ detail: input.detail ?? "Work started",
1246
+ type: "started"
1247
+ });
1248
+ var summarizeVoiceOpsTaskAnalytics = (tasks, input = {}) => {
1249
+ const at = input.at ?? Date.now();
1250
+ const agingBuckets = new Map;
1251
+ const assignees = new Map;
1252
+ const workers = new Map;
1253
+ let totalCompleted = 0;
1254
+ let totalOverdue = 0;
1255
+ for (const task of tasks) {
1256
+ const bucket = resolveVoiceOpsTaskAgeBucket(task, { ...input, at });
1257
+ agingBuckets.set(bucket, (agingBuckets.get(bucket) ?? 0) + 1);
1258
+ if (task.assignee) {
1259
+ const assignee = assignees.get(task.assignee) ?? {
1260
+ claimed: 0,
1261
+ completed: 0,
1262
+ completionMsTotal: 0,
1263
+ completionSamples: 0,
1264
+ inProgress: 0,
1265
+ open: 0,
1266
+ overdue: 0,
1267
+ total: 0
1268
+ };
1269
+ assignee.total += 1;
1270
+ if (task.status === "open") {
1271
+ assignee.open += 1;
1272
+ } else if (task.status === "in-progress") {
1273
+ assignee.inProgress += 1;
1274
+ } else if (task.status === "done") {
1275
+ assignee.completed += 1;
1276
+ }
1277
+ if (task.claimedBy && (!task.claimExpiresAt || task.claimExpiresAt > at)) {
1278
+ assignee.claimed += 1;
1279
+ }
1280
+ if (isVoiceOpsTaskOverdue(task, { at })) {
1281
+ assignee.overdue += 1;
1282
+ }
1283
+ const completedAt = task.history.findLast((entry) => entry.type === "completed")?.at;
1284
+ if (task.status === "done" && typeof completedAt === "number") {
1285
+ assignee.completionMsTotal += Math.max(0, completedAt - task.createdAt);
1286
+ assignee.completionSamples += 1;
1287
+ }
1288
+ assignees.set(task.assignee, assignee);
1289
+ }
1290
+ if (isVoiceOpsTaskOverdue(task, { at })) {
1291
+ totalOverdue += 1;
1292
+ }
1293
+ if (task.status === "done") {
1294
+ totalCompleted += 1;
1295
+ }
1296
+ for (const entry of task.history) {
1297
+ if (!["claimed", "heartbeat", "completed", "failed", "requeued"].includes(entry.type)) {
1298
+ continue;
1299
+ }
1300
+ const worker = workers.get(entry.actor) ?? {
1301
+ activeClaims: 0,
1302
+ completed: 0,
1303
+ failed: 0,
1304
+ heartbeats: 0,
1305
+ requeued: 0,
1306
+ totalClaims: 0
1307
+ };
1308
+ if (entry.type === "claimed") {
1309
+ worker.totalClaims += 1;
1310
+ } else if (entry.type === "heartbeat") {
1311
+ worker.heartbeats += 1;
1312
+ } else if (entry.type === "completed") {
1313
+ worker.completed += 1;
1314
+ } else if (entry.type === "failed") {
1315
+ worker.failed += 1;
1316
+ } else if (entry.type === "requeued") {
1317
+ worker.requeued += 1;
1318
+ }
1319
+ workers.set(entry.actor, worker);
1320
+ }
1321
+ if (task.claimedBy && (!task.claimExpiresAt || task.claimExpiresAt > at)) {
1322
+ const worker = workers.get(task.claimedBy) ?? {
1323
+ activeClaims: 0,
1324
+ completed: 0,
1325
+ failed: 0,
1326
+ heartbeats: 0,
1327
+ requeued: 0,
1328
+ totalClaims: 0
1329
+ };
1330
+ worker.activeClaims += 1;
1331
+ workers.set(task.claimedBy, worker);
1332
+ }
1333
+ }
1334
+ return {
1335
+ agingBuckets: [...agingBuckets.entries()].sort((left, right) => right[1] - left[1]),
1336
+ assignees: [...assignees.entries()].map(([assignee, value]) => ({
1337
+ assignee,
1338
+ averageCompletionMs: value.completionSamples > 0 ? value.completionMsTotal / value.completionSamples : undefined,
1339
+ claimed: value.claimed,
1340
+ completed: value.completed,
1341
+ inProgress: value.inProgress,
1342
+ open: value.open,
1343
+ overdue: value.overdue,
1344
+ total: value.total
1345
+ })).sort((left, right) => right.total - left.total),
1346
+ totalCompleted,
1347
+ totalOverdue,
1348
+ totalTasks: tasks.length,
1349
+ workers: [...workers.entries()].map(([workerId, value]) => ({
1350
+ activeClaims: value.activeClaims,
1351
+ completed: value.completed,
1352
+ failed: value.failed,
1353
+ heartbeats: value.heartbeats,
1354
+ requeued: value.requeued,
1355
+ totalClaims: value.totalClaims,
1356
+ workerId
1357
+ })).sort((left, right) => right.totalClaims - left.totalClaims)
1358
+ };
1359
+ };
1360
+ var summarizeVoiceOpsTasks = (tasks) => {
1361
+ const summary = {
1362
+ byClaimedBy: new Map,
1363
+ byKind: new Map,
1364
+ byOutcome: new Map,
1365
+ byPriority: new Map,
1366
+ byQueue: new Map,
1367
+ claimed: 0,
1368
+ done: 0,
1369
+ inProgress: 0,
1370
+ open: 0,
1371
+ overdue: 0,
1372
+ topAssignees: new Map,
1373
+ topQueues: new Map,
1374
+ topTargets: new Map,
1375
+ total: tasks.length
1376
+ };
1377
+ for (const task of tasks) {
1378
+ if (task.status === "open") {
1379
+ summary.open += 1;
1380
+ } else if (task.status === "in-progress") {
1381
+ summary.inProgress += 1;
1382
+ } else if (task.status === "done") {
1383
+ summary.done += 1;
1384
+ }
1385
+ if (task.claimedBy && (!task.claimExpiresAt || task.claimExpiresAt > Date.now())) {
1386
+ summary.claimed += 1;
1387
+ summary.byClaimedBy.set(task.claimedBy, (summary.byClaimedBy.get(task.claimedBy) ?? 0) + 1);
1388
+ }
1389
+ summary.byKind.set(task.kind, (summary.byKind.get(task.kind) ?? 0) + 1);
1390
+ if (task.outcome) {
1391
+ summary.byOutcome.set(task.outcome, (summary.byOutcome.get(task.outcome) ?? 0) + 1);
1392
+ }
1393
+ if (task.target) {
1394
+ summary.topTargets.set(task.target, (summary.topTargets.get(task.target) ?? 0) + 1);
1395
+ }
1396
+ if (task.assignee) {
1397
+ summary.topAssignees.set(task.assignee, (summary.topAssignees.get(task.assignee) ?? 0) + 1);
1398
+ }
1399
+ if (task.priority) {
1400
+ summary.byPriority.set(task.priority, (summary.byPriority.get(task.priority) ?? 0) + 1);
1401
+ }
1402
+ if (task.queue) {
1403
+ summary.byQueue.set(task.queue, (summary.byQueue.get(task.queue) ?? 0) + 1);
1404
+ summary.topQueues.set(task.queue, (summary.topQueues.get(task.queue) ?? 0) + 1);
1405
+ }
1406
+ if (isVoiceOpsTaskOverdue(task)) {
1407
+ summary.overdue += 1;
1408
+ }
1409
+ }
1410
+ return {
1411
+ byClaimedBy: [...summary.byClaimedBy.entries()].sort((left, right) => right[1] - left[1]),
1412
+ byKind: [...summary.byKind.entries()].sort((left, right) => right[1] - left[1]),
1413
+ byOutcome: [...summary.byOutcome.entries()].sort((left, right) => right[1] - left[1]),
1414
+ byPriority: [...summary.byPriority.entries()].sort((left, right) => right[1] - left[1]),
1415
+ byQueue: [...summary.byQueue.entries()].sort((left, right) => right[1] - left[1]),
1416
+ claimed: summary.claimed,
1417
+ done: summary.done,
1418
+ inProgress: summary.inProgress,
1419
+ open: summary.open,
1420
+ overdue: summary.overdue,
1421
+ topAssignees: [...summary.topAssignees.entries()].sort((left, right) => right[1] - left[1]),
1422
+ topQueues: [...summary.topQueues.entries()].sort((left, right) => right[1] - left[1]),
1423
+ topTargets: [...summary.topTargets.entries()].sort((left, right) => right[1] - left[1]),
1424
+ total: summary.total
1425
+ };
1426
+ };
1427
+
1428
+ // src/core/store.ts
1429
+ var createId = () => crypto.randomUUID();
1430
+ var createVoiceSessionRecord = (id, scenarioId) => ({
1431
+ committedTurnIds: [],
1432
+ createdAt: Date.now(),
1433
+ currentTurn: {
1434
+ finalText: "",
1435
+ lastSpeechAt: undefined,
1436
+ lastTranscriptAt: undefined,
1437
+ partialEndedAt: undefined,
1438
+ partialStartedAt: undefined,
1439
+ partialText: "",
1440
+ silenceStartedAt: undefined,
1441
+ transcripts: []
1442
+ },
1443
+ id,
1444
+ scenarioId,
1445
+ reconnect: { attempts: 0 },
1446
+ status: "active",
1447
+ transcripts: [],
1448
+ turns: [],
1449
+ lastCommittedTurn: {
1450
+ committedAt: 0,
1451
+ signature: "",
1452
+ text: "",
1453
+ transcriptIds: []
1454
+ }
1455
+ });
1456
+ var resetVoiceSessionRecord = (id, existing, scenarioId) => ({
1457
+ ...createVoiceSessionRecord(id, scenarioId),
1458
+ metadata: existing?.metadata
1459
+ });
1460
+ var toVoiceSessionSummary = (session) => ({
1461
+ createdAt: session.createdAt,
1462
+ id: session.id,
1463
+ lastActivityAt: session.lastActivityAt,
1464
+ status: session.status,
1465
+ turnCount: session.turns.length
1466
+ });
1467
+
1468
+ // src/internal/html.ts
1469
+ var escapeHtml = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1470
+
1471
+ // src/core/trace.ts
1472
+ var createVoiceTraceEvent = (event) => ({
1473
+ ...event,
1474
+ at: event.at,
1475
+ id: event.id ?? createVoiceTraceEventId({
1476
+ at: event.at,
1477
+ sessionId: event.sessionId,
1478
+ turnId: event.turnId,
1479
+ type: event.type
1480
+ })
1481
+ });
1482
+ var createVoiceTraceEventId = (event) => [
1483
+ event.sessionId,
1484
+ event.turnId ?? "session",
1485
+ event.type,
1486
+ String(event.at ?? Date.now()),
1487
+ crypto.randomUUID()
1488
+ ].map(encodeURIComponent).join(":");
1489
+ var createVoiceTraceSinkDeliveryId = (events) => {
1490
+ const firstEvent = events[0];
1491
+ return [
1492
+ firstEvent?.sessionId ?? "trace",
1493
+ firstEvent?.traceId ?? "sink",
1494
+ String(firstEvent?.at ?? Date.now()),
1495
+ crypto.randomUUID()
1496
+ ].map(encodeURIComponent).join(":");
1497
+ };
1498
+ var createVoiceTraceSinkDeliveryRecord = (input) => {
1499
+ const createdAt = input.createdAt ?? Date.now();
1500
+ return {
1501
+ createdAt,
1502
+ deliveredAt: input.deliveredAt,
1503
+ deliveryAttempts: input.deliveryAttempts,
1504
+ deliveryError: input.deliveryError,
1505
+ deliveryStatus: input.deliveryStatus ?? "pending",
1506
+ events: input.events,
1507
+ id: input.id ?? createVoiceTraceSinkDeliveryId(input.events),
1508
+ sinkDeliveries: input.sinkDeliveries,
1509
+ updatedAt: input.updatedAt ?? createdAt
1510
+ };
1511
+ };
1512
+ var matchesTraceFilter = (event, filter) => {
1513
+ if (filter.sessionId !== undefined && event.sessionId !== filter.sessionId) {
1514
+ return false;
1515
+ }
1516
+ if (filter.turnId !== undefined && event.turnId !== filter.turnId) {
1517
+ return false;
1518
+ }
1519
+ if (filter.scenarioId !== undefined && event.scenarioId !== filter.scenarioId) {
1520
+ return false;
1521
+ }
1522
+ if (filter.traceId !== undefined && event.traceId !== filter.traceId) {
1523
+ return false;
1524
+ }
1525
+ if (filter.type !== undefined) {
1526
+ const types = Array.isArray(filter.type) ? filter.type : [filter.type];
1527
+ if (!types.includes(event.type)) {
1528
+ return false;
1529
+ }
1530
+ }
1531
+ return true;
1532
+ };
1533
+ var createVoiceProofTraceStore = (options = {}) => {
1534
+ const proofStore = options.proofStore ?? createVoiceMemoryTraceEventStore();
1535
+ const scopedProofStore = options.scope ? createVoiceScopedTraceEventStore(proofStore, options.scope) : proofStore;
1536
+ return {
1537
+ append: async (event) => {
1538
+ const stored = await proofStore.append(event);
1539
+ await options.mirrorStore?.append(stored);
1540
+ return stored;
1541
+ },
1542
+ get: async (id) => await proofStore.get(id) ?? await options.mirrorStore?.get(id),
1543
+ list: (filter) => scopedProofStore.list(filter),
1544
+ remove: async (id) => {
1545
+ await Promise.all([
1546
+ proofStore.remove(id),
1547
+ options.mirrorStore?.remove(id) ?? Promise.resolve()
1548
+ ]);
1549
+ }
1550
+ };
1551
+ };
1552
+ var createVoiceScopedTraceEventStore = (store, scope) => {
1553
+ const upstreamFilter = (filter = {}) => {
1554
+ const next = { ...filter };
1555
+ delete next.limit;
1556
+ if (scope.scenarioId !== undefined) {
1557
+ delete next.scenarioId;
1558
+ }
1559
+ if (scope.sessionId !== undefined) {
1560
+ delete next.sessionId;
1561
+ }
1562
+ if (scope.traceId !== undefined) {
1563
+ delete next.traceId;
1564
+ }
1565
+ if (scope.turnId !== undefined) {
1566
+ delete next.turnId;
1567
+ }
1568
+ if (scope.type !== undefined) {
1569
+ delete next.type;
1570
+ }
1571
+ return next;
1572
+ };
1573
+ const scopedFilter = (filter = {}) => ({
1574
+ ...filter,
1575
+ ...scope
1576
+ });
1577
+ return {
1578
+ append: (event) => store.append(event),
1579
+ get: (id) => store.get(id),
1580
+ list: async (filter) => filterVoiceTraceEvents(await store.list(upstreamFilter(filter)), scopedFilter(filter)),
1581
+ remove: (id) => store.remove(id)
1582
+ };
1583
+ };
1584
+ var filterVoiceTraceEvents = (events, filter = {}) => {
1585
+ const sorted = events.filter((event) => matchesTraceFilter(event, filter)).sort((left, right) => left.at - right.at || left.id.localeCompare(right.id));
1586
+ return typeof filter.limit === "number" && filter.limit >= 0 ? sorted.slice(0, filter.limit) : sorted;
1587
+ };
1588
+ var isPruneTimeMatch = (event, options) => {
1589
+ if (typeof options.before === "number" && event.at >= options.before) {
1590
+ return false;
1591
+ }
1592
+ if (typeof options.beforeOrAt === "number" && event.at > options.beforeOrAt) {
1593
+ return false;
1594
+ }
1595
+ return true;
1596
+ };
1597
+ var pruneVoiceTraceEvents = async (options) => {
1598
+ const events = await options.store.list(options.filter);
1599
+ const deleted = selectVoiceTraceEventsForPrune(events, options);
1600
+ if (!options.dryRun) {
1601
+ await Promise.all(deleted.map((event) => options.store.remove(event.id)));
1602
+ }
1603
+ return {
1604
+ deleted,
1605
+ deletedCount: deleted.length,
1606
+ dryRun: Boolean(options.dryRun),
1607
+ scannedCount: events.length
1608
+ };
1609
+ };
1610
+ var selectVoiceTraceEventsForPrune = (events, options = {}) => {
1611
+ let candidates = filterVoiceTraceEvents(events, options.filter).filter((event) => isPruneTimeMatch(event, options));
1612
+ if (typeof options.keepNewest === "number" && options.keepNewest >= 0) {
1613
+ const newestIds = new Set([...candidates].sort((left, right) => right.at - left.at || right.id.localeCompare(left.id)).slice(0, options.keepNewest).map((event) => event.id));
1614
+ candidates = candidates.filter((event) => !newestIds.has(event.id));
1615
+ }
1616
+ return typeof options.limit === "number" && options.limit >= 0 ? candidates.slice(0, options.limit) : candidates;
1617
+ };
1618
+ var sleep2 = async (delayMs) => {
1619
+ if (delayMs <= 0) {
1620
+ return;
1621
+ }
1622
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
1623
+ };
1624
+ var toHex2 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
1625
+ var signVoiceTraceSinkBody = async (input) => {
1626
+ const encoder = new TextEncoder;
1627
+ const key = await crypto.subtle.importKey("raw", encoder.encode(input.secret), {
1628
+ hash: "SHA-256",
1629
+ name: "HMAC"
1630
+ }, false, ["sign"]);
1631
+ const payload = encoder.encode(`${input.timestamp}.${input.body}`);
1632
+ const signature = await crypto.subtle.sign("HMAC", key, payload);
1633
+ return `sha256=${toHex2(new Uint8Array(signature))}`;
1634
+ };
1635
+ var createVoiceTraceSinkDeliveryError = (input) => {
1636
+ if (input.response) {
1637
+ const statusText = input.response.statusText?.trim();
1638
+ return `Attempt ${input.attempt} failed with trace sink response ${input.response.status}${statusText ? ` ${statusText}` : ""}.`;
1639
+ }
1640
+ if (input.error instanceof Error) {
1641
+ return `Attempt ${input.attempt} failed: ${input.error.message}`;
1642
+ }
1643
+ return `Attempt ${input.attempt} failed: ${String(input.error)}`;
1644
+ };
1645
+ var normalizeVoiceTraceS3KeyPrefix = (prefix) => prefix?.trim().replace(/^\/+|\/+$/g, "") ?? "voice/trace-deliveries";
1646
+ var createVoiceTraceS3ObjectKey = (prefix, events) => {
1647
+ const firstEvent = events[0];
1648
+ const safeSessionId = encodeURIComponent(firstEvent?.sessionId ?? "trace");
1649
+ const safeEventId = encodeURIComponent(firstEvent?.id ?? crypto.randomUUID());
1650
+ return `${prefix}/${safeSessionId}/${Date.now()}-${safeEventId}.json`;
1651
+ };
1652
+ var resolveVoiceS3DeliveredTo = (options, key) => {
1653
+ const { bucket } = options;
1654
+ return bucket ? `s3://${bucket}/${key}` : `s3://${key}`;
1655
+ };
1656
+ var aggregateVoiceTraceSinkDeliveryStatus = (deliveries) => {
1657
+ const statuses = Object.values(deliveries).map((delivery) => delivery.status);
1658
+ if (statuses.length === 0 || statuses.every((status) => status === "skipped")) {
1659
+ return "skipped";
1660
+ }
1661
+ if (statuses.some((status) => status === "failed")) {
1662
+ return "failed";
1663
+ }
1664
+ return "delivered";
1665
+ };
1666
+ var createVoiceTraceHTTPSink = (options) => ({
1667
+ eventTypes: options.eventTypes,
1668
+ id: options.id,
1669
+ kind: options.kind ?? "http",
1670
+ deliver: async ({ events }) => {
1671
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1672
+ if (typeof fetchImpl !== "function") {
1673
+ return {
1674
+ attempts: 0,
1675
+ deliveredTo: options.url,
1676
+ error: "Trace sink delivery failed: fetch is not available in this runtime.",
1677
+ eventCount: events.length,
1678
+ status: "failed"
1679
+ };
1680
+ }
1681
+ const maxRetries = Math.max(0, options.retries ?? 0);
1682
+ const backoffMs = Math.max(0, options.backoffMs ?? 250);
1683
+ const timeoutMs = Math.max(0, options.timeoutMs ?? 1e4);
1684
+ const payload = options.body ? await options.body({ events }) : {
1685
+ eventCount: events.length,
1686
+ events,
1687
+ source: "absolutejs-voice"
1688
+ };
1689
+ const body = JSON.stringify(payload);
1690
+ let lastError = "Trace sink delivery failed.";
1691
+ for (let attempt = 1;attempt <= maxRetries + 1; attempt += 1) {
1692
+ let controller;
1693
+ let timeout;
1694
+ try {
1695
+ const headers = {
1696
+ "content-type": "application/json",
1697
+ ...options.headers
1698
+ };
1699
+ if (options.signingSecret) {
1700
+ const timestamp = String(Date.now());
1701
+ headers["x-absolutejs-timestamp"] = timestamp;
1702
+ headers["x-absolutejs-signature"] = await signVoiceTraceSinkBody({
1703
+ body,
1704
+ secret: options.signingSecret,
1705
+ timestamp
1706
+ });
1707
+ }
1708
+ controller = timeoutMs > 0 ? new AbortController : undefined;
1709
+ if (controller && timeoutMs > 0) {
1710
+ timeout = setTimeout(() => controller?.abort(), timeoutMs);
1711
+ }
1712
+ const response = await fetchImpl(options.url, {
1713
+ body,
1714
+ headers,
1715
+ method: options.method ?? "POST",
1716
+ signal: controller?.signal
1717
+ });
1718
+ if (response.ok) {
1719
+ let responseBody;
1720
+ try {
1721
+ responseBody = await response.clone().json();
1722
+ } catch {
1723
+ responseBody = undefined;
1724
+ }
1725
+ return {
1726
+ attempts: attempt,
1727
+ deliveredAt: Date.now(),
1728
+ deliveredTo: options.url,
1729
+ eventCount: events.length,
1730
+ responseBody,
1731
+ status: "delivered"
1732
+ };
1733
+ }
1734
+ lastError = createVoiceTraceSinkDeliveryError({
1735
+ attempt,
1736
+ response
1737
+ });
1738
+ } catch (error) {
1739
+ lastError = createVoiceTraceSinkDeliveryError({
1740
+ attempt,
1741
+ error
1742
+ });
1743
+ } finally {
1744
+ if (timeout) {
1745
+ clearTimeout(timeout);
1746
+ }
1747
+ }
1748
+ if (attempt <= maxRetries) {
1749
+ await sleep2(backoffMs * attempt);
1750
+ }
1751
+ }
1752
+ return {
1753
+ attempts: maxRetries + 1,
1754
+ deliveredTo: options.url,
1755
+ error: lastError,
1756
+ eventCount: events.length,
1757
+ status: "failed"
1758
+ };
1759
+ }
1760
+ });
1761
+ var createVoiceTraceS3Sink = (options) => {
1762
+ const client = options.client ?? new Bun.S3Client(options);
1763
+ const keyPrefix = normalizeVoiceTraceS3KeyPrefix(options.keyPrefix);
1764
+ return {
1765
+ eventTypes: options.eventTypes,
1766
+ id: options.id,
1767
+ kind: options.kind ?? "s3",
1768
+ deliver: async ({ events }) => {
1769
+ const key = createVoiceTraceS3ObjectKey(keyPrefix, events);
1770
+ const payload = options.body ? await options.body({ events, key }) : {
1771
+ eventCount: events.length,
1772
+ events,
1773
+ key,
1774
+ source: "absolutejs-voice"
1775
+ };
1776
+ try {
1777
+ const file = client.file(key, options);
1778
+ await file.write(JSON.stringify(payload), {
1779
+ type: options.contentType ?? "application/json"
1780
+ });
1781
+ return {
1782
+ attempts: 1,
1783
+ deliveredAt: Date.now(),
1784
+ deliveredTo: resolveVoiceS3DeliveredTo(options, key),
1785
+ eventCount: events.length,
1786
+ responseBody: { key },
1787
+ status: "delivered"
1788
+ };
1789
+ } catch (error) {
1790
+ return {
1791
+ attempts: 1,
1792
+ deliveredTo: resolveVoiceS3DeliveredTo(options, key),
1793
+ error: error instanceof Error ? error.message : String(error),
1794
+ eventCount: events.length,
1795
+ status: "failed"
1796
+ };
1797
+ }
1798
+ }
1799
+ };
1800
+ };
1801
+ var createVoiceTraceSinkStore = (options) => {
1802
+ const deliver = async (event) => {
1803
+ const result = await deliverVoiceTraceEventsToSinks({
1804
+ events: [event],
1805
+ redact: options.redact,
1806
+ sinks: options.sinks
1807
+ });
1808
+ await options.onDelivery?.(result);
1809
+ };
1810
+ return {
1811
+ append: async (event) => {
1812
+ const stored = await options.store.append(event);
1813
+ if (options.deliveryQueue) {
1814
+ const delivery2 = createVoiceTraceSinkDeliveryRecord({
1815
+ events: [stored]
1816
+ });
1817
+ await options.deliveryQueue.set(delivery2.id, delivery2);
1818
+ return stored;
1819
+ }
1820
+ const delivery = deliver(stored);
1821
+ if (options.awaitDelivery) {
1822
+ await delivery;
1823
+ } else {
1824
+ delivery.catch((error) => {
1825
+ options.onError?.(error);
1826
+ });
1827
+ }
1828
+ return stored;
1829
+ },
1830
+ get: (id) => options.store.get(id),
1831
+ list: (filter) => options.store.list(filter),
1832
+ remove: (id) => options.store.remove(id)
1833
+ };
1834
+ };
1835
+ var deliverVoiceTraceEventsToSinks = async (input) => {
1836
+ const events = input.redact ? redactVoiceTraceEvents(input.events, input.redact) : input.events;
1837
+ const sinkDeliveries = {};
1838
+ for (const sink of input.sinks) {
1839
+ const sinkEvents = sink.eventTypes?.length ? events.filter((event) => sink.eventTypes?.includes(event.type)) : events;
1840
+ if (sinkEvents.length === 0) {
1841
+ sinkDeliveries[sink.id] = {
1842
+ attempts: 0,
1843
+ eventCount: 0,
1844
+ status: "skipped"
1845
+ };
1846
+ continue;
1847
+ }
1848
+ try {
1849
+ sinkDeliveries[sink.id] = await sink.deliver({
1850
+ events: sinkEvents
1851
+ });
1852
+ } catch (error) {
1853
+ sinkDeliveries[sink.id] = {
1854
+ attempts: 1,
1855
+ error: error instanceof Error ? error.message : String(error),
1856
+ eventCount: sinkEvents.length,
1857
+ status: "failed"
1858
+ };
1859
+ }
1860
+ }
1861
+ return {
1862
+ deliveredAt: Date.now(),
1863
+ eventCount: events.length,
1864
+ sinkDeliveries,
1865
+ status: aggregateVoiceTraceSinkDeliveryStatus(sinkDeliveries)
1866
+ };
1867
+ };
1868
+ var normalizeVoiceProfileTraceTaggerProfile = (profile) => typeof profile === "string" ? { id: profile } : profile?.id ? profile : undefined;
1869
+ var createVoiceMemoryTraceEventStore = () => {
1870
+ const events = new Map;
1871
+ const append = async (event) => {
1872
+ const stored = createVoiceTraceEvent(event);
1873
+ events.set(stored.id, stored);
1874
+ return stored;
1875
+ };
1876
+ const get = async (id) => events.get(id);
1877
+ const list = async (filter) => filterVoiceTraceEvents([...events.values()], filter);
1878
+ const remove = async (id) => {
1879
+ events.delete(id);
1880
+ };
1881
+ return { append, get, list, remove };
1882
+ };
1883
+ var createVoiceMemoryTraceSinkDeliveryStore = () => {
1884
+ const deliveries = new Map;
1885
+ return {
1886
+ get: async (id) => deliveries.get(id),
1887
+ list: async () => [...deliveries.values()].sort((left, right) => left.createdAt - right.createdAt || left.id.localeCompare(right.id)),
1888
+ remove: async (id) => {
1889
+ deliveries.delete(id);
1890
+ },
1891
+ set: async (id, delivery) => {
1892
+ deliveries.set(id, delivery);
1893
+ }
1894
+ };
1895
+ };
1896
+ var createVoiceProfileTraceTagger = (options) => {
1897
+ const profiles = new Map((options.profiles ?? []).map((profile) => [profile.id, profile]));
1898
+ const defaultProfile = normalizeVoiceProfileTraceTaggerProfile(options.defaultProfile);
1899
+ const resolveProfile = async (event) => {
1900
+ const resolved = normalizeVoiceProfileTraceTaggerProfile(await options.resolveProfile?.(event));
1901
+ const profile = resolved ?? defaultProfile;
1902
+ return profile ? profiles.get(profile.id) ?? profile : undefined;
1903
+ };
1904
+ return {
1905
+ append: async (event) => {
1906
+ const profile = await resolveProfile(event);
1907
+ if (!profile) {
1908
+ return options.store.append(event);
1909
+ }
1910
+ const metadata = {
1911
+ ...event.metadata ?? {},
1912
+ benchmarkProfileId: profile.id,
1913
+ profileDescription: event.metadata?.profileDescription ?? profile.description,
1914
+ profileId: profile.id,
1915
+ profileLabel: event.metadata?.profileLabel ?? profile.label
1916
+ };
1917
+ const payload = event.payload && typeof event.payload === "object" ? {
1918
+ ...event.payload,
1919
+ benchmarkProfileId: event.payload.benchmarkProfileId ?? profile.id,
1920
+ profileDescription: event.payload.profileDescription ?? profile.description,
1921
+ profileId: event.payload.profileId ?? profile.id,
1922
+ profileLabel: event.payload.profileLabel ?? profile.label
1923
+ } : event.payload;
1924
+ return options.store.append({
1925
+ ...event,
1926
+ metadata,
1927
+ payload
1928
+ });
1929
+ },
1930
+ get: (id) => options.store.get(id),
1931
+ list: (filter) => options.store.list(filter),
1932
+ remove: (id) => options.store.remove(id)
1933
+ };
1934
+ };
1935
+ var exportVoiceTrace = async (input) => {
1936
+ const events = await input.store.list(input.filter);
1937
+ return {
1938
+ exportedAt: Date.now(),
1939
+ events: input.redact ? redactVoiceTraceEvents(events, input.redact) : events,
1940
+ filter: input.filter,
1941
+ redacted: Boolean(input.redact)
1942
+ };
1943
+ };
1944
+ var toNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
1945
+ var formatTraceValue = (value) => {
1946
+ if (value === undefined || value === null) {
1947
+ return "";
1948
+ }
1949
+ if (typeof value === "string") {
1950
+ return value;
1951
+ }
1952
+ if (typeof value === "number" || typeof value === "boolean") {
1953
+ return String(value);
1954
+ }
1955
+ try {
1956
+ return JSON.stringify(value);
1957
+ } catch {
1958
+ return String(value);
1959
+ }
1960
+ };
1961
+ var DEFAULT_REDACTION_KEYS = [
1962
+ "apiKey",
1963
+ "authorization",
1964
+ "creditCard",
1965
+ "email",
1966
+ "externalId",
1967
+ "password",
1968
+ "phone",
1969
+ "secret",
1970
+ "ssn",
1971
+ "token"
1972
+ ];
1973
+ var DEFAULT_REDACTION_TEXT_KEYS = [
1974
+ "assistantText",
1975
+ "content",
1976
+ "error",
1977
+ "reason",
1978
+ "summary",
1979
+ "text"
1980
+ ];
1981
+ var EMAIL_PATTERN = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
1982
+ var PHONE_PATTERN = /(?<!\d)(?:\+?1[\s.-]?)?(?:\(?\d{3}\)?[\s.-]?)\d{3}[\s.-]?\d{4}(?!\d)/g;
1983
+ var normalizeRedactionKey = (key) => key.trim().toLowerCase().replace(/[^a-z0-9]/g, "");
1984
+ var resolveVoiceTraceRedactionOptions = (options = {}) => ({
1985
+ keys: typeof options === "boolean" ? DEFAULT_REDACTION_KEYS : options.keys ?? DEFAULT_REDACTION_KEYS,
1986
+ redactEmails: typeof options === "boolean" ? true : options.redactEmails ?? true,
1987
+ redactPhoneNumbers: typeof options === "boolean" ? true : options.redactPhoneNumbers ?? true,
1988
+ redactText: typeof options === "boolean" ? true : options.redactText ?? true,
1989
+ replacement: typeof options === "boolean" ? "[redacted]" : options.replacement ?? "[redacted]",
1990
+ textKeys: typeof options === "boolean" ? DEFAULT_REDACTION_TEXT_KEYS : options.textKeys ?? DEFAULT_REDACTION_TEXT_KEYS
1991
+ });
1992
+ var resolveReplacement = (input) => typeof input.options.replacement === "function" ? input.options.replacement({
1993
+ key: input.key,
1994
+ path: input.path,
1995
+ value: input.value
1996
+ }) : input.options.replacement;
1997
+ var redactVoiceTraceText = (value, options = {}, input = {}) => {
1998
+ const resolved = resolveVoiceTraceRedactionOptions(options);
1999
+ let redacted = value;
2000
+ const replacement = resolveReplacement({
2001
+ key: input.key,
2002
+ options: resolved,
2003
+ path: input.path ?? [],
2004
+ value
2005
+ });
2006
+ if (resolved.redactEmails) {
2007
+ redacted = redacted.replace(EMAIL_PATTERN, replacement);
2008
+ }
2009
+ if (resolved.redactPhoneNumbers) {
2010
+ redacted = redacted.replace(PHONE_PATTERN, replacement);
2011
+ }
2012
+ return redacted;
2013
+ };
2014
+ var redactTraceValue = (value, options, path) => {
2015
+ const key = path.at(-1);
2016
+ const normalizedKey = key ? normalizeRedactionKey(key) : undefined;
2017
+ const sensitiveKeys = new Set(options.keys.map(normalizeRedactionKey));
2018
+ const textKeys = new Set(options.textKeys.map(normalizeRedactionKey));
2019
+ if (normalizedKey && sensitiveKeys.has(normalizedKey) && (value === null || ["boolean", "number", "string", "undefined"].includes(typeof value))) {
2020
+ return resolveReplacement({
2021
+ key,
2022
+ options,
2023
+ path,
2024
+ value: String(value ?? "")
2025
+ });
2026
+ }
2027
+ if (typeof value === "string") {
2028
+ const shouldRedactText = options.redactText && (!normalizedKey || textKeys.has(normalizedKey) || path.length === 0);
2029
+ return shouldRedactText ? redactVoiceTraceText(value, options, {
2030
+ key,
2031
+ path
2032
+ }) : value;
2033
+ }
2034
+ if (Array.isArray(value)) {
2035
+ return value.map((item, index) => redactTraceValue(item, options, [...path, String(index)]));
2036
+ }
2037
+ if (typeof value === "object" && value) {
2038
+ return Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [
2039
+ entryKey,
2040
+ redactTraceValue(entryValue, options, [...path, entryKey])
2041
+ ]));
2042
+ }
2043
+ return value;
2044
+ };
2045
+ var evaluateVoiceTrace = (events, options = {}) => {
2046
+ const summary = summarizeVoiceTrace(events);
2047
+ const issues = [];
2048
+ const maxHandoffs = options.maxHandoffs ?? 3;
2049
+ const maxToolErrors = options.maxToolErrors ?? 0;
2050
+ const maxModelCallsPerTurn = options.maxModelCallsPerTurn ?? 6;
2051
+ const turnCountForRatio = Math.max(1, summary.turnCount);
2052
+ if (options.requireCompletedCall !== false && !summary.endedAt) {
2053
+ issues.push({
2054
+ code: "call-not-ended",
2055
+ message: "Trace does not include a call end lifecycle event.",
2056
+ severity: "warning"
2057
+ });
2058
+ }
2059
+ if (summary.failed) {
2060
+ issues.push({
2061
+ code: "session-error",
2062
+ message: "Trace contains a session error or failed call disposition.",
2063
+ severity: "error"
2064
+ });
2065
+ }
2066
+ if (options.requireTranscript !== false && summary.transcriptCount === 0) {
2067
+ issues.push({
2068
+ code: "missing-transcript",
2069
+ message: "Trace does not include any transcript events.",
2070
+ severity: "error"
2071
+ });
2072
+ }
2073
+ if (options.requireTurn !== false && summary.turnCount === 0) {
2074
+ issues.push({
2075
+ code: "missing-turn",
2076
+ message: "Trace does not include any committed turns.",
2077
+ severity: "error"
2078
+ });
2079
+ }
2080
+ if (options.requireAssistantReply !== false && summary.turnCount > 0 && summary.assistantReplyCount === 0) {
2081
+ issues.push({
2082
+ code: "missing-assistant-reply",
2083
+ message: "Trace has committed turns but no assistant replies.",
2084
+ severity: "warning"
2085
+ });
2086
+ }
2087
+ if (summary.toolErrorCount > maxToolErrors) {
2088
+ issues.push({
2089
+ code: "tool-errors",
2090
+ message: `Trace has ${summary.toolErrorCount} tool error(s), above the allowed ${maxToolErrors}.`,
2091
+ severity: "error"
2092
+ });
2093
+ }
2094
+ if (summary.handoffCount > maxHandoffs) {
2095
+ issues.push({
2096
+ code: "too-many-handoffs",
2097
+ message: `Trace has ${summary.handoffCount} handoff(s), above the allowed ${maxHandoffs}.`,
2098
+ severity: "warning"
2099
+ });
2100
+ }
2101
+ if (summary.modelCallCount / turnCountForRatio > maxModelCallsPerTurn) {
2102
+ issues.push({
2103
+ code: "too-many-model-calls",
2104
+ message: `Trace averages more than ${maxModelCallsPerTurn} model calls per committed turn.`,
2105
+ severity: "warning"
2106
+ });
2107
+ }
2108
+ return {
2109
+ issues,
2110
+ pass: !issues.some((issue) => issue.severity === "error"),
2111
+ summary
2112
+ };
2113
+ };
2114
+ var redactVoiceTraceEvent = (event, options = {}) => {
2115
+ const resolved = resolveVoiceTraceRedactionOptions(options);
2116
+ return {
2117
+ ...event,
2118
+ metadata: redactTraceValue(event.metadata, resolved, [
2119
+ "metadata"
2120
+ ]),
2121
+ payload: redactTraceValue(event.payload, resolved, [
2122
+ "payload"
2123
+ ])
2124
+ };
2125
+ };
2126
+ var redactVoiceTraceEvents = (events, options = {}) => events.map((event) => redactVoiceTraceEvent(event, options));
2127
+ var summarizeVoiceTrace = (events) => {
2128
+ const sorted = filterVoiceTraceEvents(events);
2129
+ const firstEvent = sorted[0];
2130
+ const lastEvent = sorted.at(-1);
2131
+ const lifecycleEvents = sorted.filter((event) => event.type === "call.lifecycle");
2132
+ const startEvent = lifecycleEvents.find((event) => event.payload.type === "start");
2133
+ const endEvent = lifecycleEvents.toReversed().find((event) => event.payload.type === "end");
2134
+ const costEvents = sorted.filter((event) => event.type === "turn.cost");
2135
+ const toolEvents = sorted.filter((event) => event.type === "agent.tool");
2136
+ const startedAt = startEvent?.at ?? firstEvent?.at;
2137
+ const endedAt = endEvent?.at ?? lastEvent?.at;
2138
+ const failed = sorted.some((event) => event.type === "session.error") || endEvent?.payload.disposition === "failed";
2139
+ return {
2140
+ assistantReplyCount: sorted.filter((event) => event.type === "turn.assistant").length,
2141
+ callDurationMs: startedAt !== undefined && endedAt !== undefined ? Math.max(0, endedAt - startedAt) : undefined,
2142
+ cost: {
2143
+ estimatedRelativeCostUnits: costEvents.reduce((total, event) => total + toNumber(event.payload.estimatedRelativeCostUnits), 0),
2144
+ totalBillableAudioMs: costEvents.reduce((total, event) => total + toNumber(event.payload.totalBillableAudioMs), 0)
2145
+ },
2146
+ endedAt,
2147
+ errorCount: sorted.filter((event) => event.type === "session.error").length,
2148
+ eventCount: sorted.length,
2149
+ failed,
2150
+ handoffCount: sorted.filter((event) => event.type === "agent.handoff").length,
2151
+ modelCallCount: sorted.filter((event) => event.type === "agent.model").length,
2152
+ sessionId: firstEvent?.sessionId,
2153
+ startedAt,
2154
+ toolCallCount: toolEvents.length,
2155
+ toolErrorCount: toolEvents.filter((event) => event.payload.status === "error").length,
2156
+ traceId: firstEvent?.traceId,
2157
+ transcriptCount: sorted.filter((event) => event.type === "turn.transcript").length,
2158
+ turnCount: sorted.filter((event) => event.type === "turn.committed").length
2159
+ };
2160
+ };
2161
+ var renderTraceEventMarkdown = (event, startedAt) => {
2162
+ const offset = startedAt === undefined ? `${event.at}` : `+${Math.max(0, event.at - startedAt)}ms`;
2163
+ const label = `- ${offset} [${event.type}]`;
2164
+ switch (event.type) {
2165
+ case "turn.transcript":
2166
+ return `${label} ${event.payload.isFinal ? "final" : "partial"} "${formatTraceValue(event.payload.text)}"`;
2167
+ case "turn.committed":
2168
+ return `${label} committed "${formatTraceValue(event.payload.text)}"`;
2169
+ case "turn.assistant":
2170
+ return event.payload.text ? `${label} assistant "${formatTraceValue(event.payload.text)}"` : `${label} ${formatTraceValue(event.payload.status)}`;
2171
+ case "agent.tool":
2172
+ return `${label} ${formatTraceValue(event.payload.toolName)} ${formatTraceValue(event.payload.status)}`;
2173
+ case "agent.context":
2174
+ return `${label} ${formatTraceValue(event.payload.fromAgentId)} -> ${formatTraceValue(event.payload.targetAgentId)} ${formatTraceValue(event.payload.status)}`;
2175
+ case "agent.handoff":
2176
+ return `${label} ${formatTraceValue(event.payload.fromAgentId)} -> ${formatTraceValue(event.payload.targetAgentId)}`;
2177
+ case "session.error":
2178
+ return `${label} ${formatTraceValue(event.payload.error)}`;
2179
+ case "call.lifecycle":
2180
+ return `${label} ${formatTraceValue(event.payload.type)} ${formatTraceValue(event.payload.disposition)}`.trim();
2181
+ default:
2182
+ return `${label} ${formatTraceValue(event.payload)}`;
2183
+ }
2184
+ };
2185
+ var buildVoiceTraceReplay = (events, options = {}) => ({
2186
+ evaluation: evaluateVoiceTrace(options.redact ? redactVoiceTraceEvents(events, options.redact) : events, options.evaluation),
2187
+ html: renderVoiceTraceHTML(events, options),
2188
+ markdown: renderVoiceTraceMarkdown(events, options),
2189
+ summary: summarizeVoiceTrace(options.redact ? redactVoiceTraceEvents(events, options.redact) : events)
2190
+ });
2191
+ var renderVoiceTraceHTML = (events, options = {}) => {
2192
+ const markdown = renderVoiceTraceMarkdown(events, options);
2193
+ const renderEvents = options.redact ? redactVoiceTraceEvents(events, options.redact) : events;
2194
+ const summary = summarizeVoiceTrace(renderEvents);
2195
+ const evaluation = evaluateVoiceTrace(renderEvents, options.evaluation);
2196
+ const eventRows = filterVoiceTraceEvents(renderEvents).map((event) => {
2197
+ const offset = summary.startedAt === undefined ? event.at : Math.max(0, event.at - summary.startedAt);
2198
+ return [
2199
+ "<tr>",
2200
+ `<td>${escapeHtml(String(offset))}</td>`,
2201
+ `<td>${escapeHtml(event.type)}</td>`,
2202
+ `<td>${escapeHtml(event.turnId ?? "")}</td>`,
2203
+ `<td><code>${escapeHtml(JSON.stringify(event.payload))}</code></td>`,
2204
+ "</tr>"
2205
+ ].join("");
2206
+ }).join(`
2207
+ `);
2208
+ return [
2209
+ "<!doctype html>",
2210
+ '<html lang="en">',
2211
+ "<head>",
2212
+ '<meta charset="utf-8" />',
2213
+ '<meta name="viewport" content="width=device-width, initial-scale=1" />',
2214
+ `<title>${escapeHtml(options.title ?? "Voice Trace")}</title>`,
2215
+ "<style>",
2216
+ "body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;line-height:1.45;background:#f8f7f2;color:#181713}",
2217
+ "main{max-width:1100px;margin:auto}",
2218
+ ".summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:.75rem;margin:1rem 0}",
2219
+ ".card{background:white;border:1px solid #ded9cc;border-radius:12px;padding:1rem}",
2220
+ ".pass{color:#126b3a}.fail{color:#9d2222}",
2221
+ "table{border-collapse:collapse;width:100%;background:white;border:1px solid #ded9cc}",
2222
+ "th,td{border-bottom:1px solid #eee8dc;padding:.65rem;text-align:left;vertical-align:top}",
2223
+ "code{white-space:pre-wrap;word-break:break-word}",
2224
+ "pre{background:#181713;color:#f8f7f2;padding:1rem;border-radius:12px;overflow:auto}",
2225
+ "</style>",
2226
+ "</head>",
2227
+ "<body><main>",
2228
+ `<h1>${escapeHtml(options.title ?? `Voice Trace ${summary.sessionId ?? ""}`.trim())}</h1>`,
2229
+ `<p class="${evaluation.pass ? "pass" : "fail"}">QA: ${evaluation.pass ? "pass" : "fail"}</p>`,
2230
+ '<section class="summary">',
2231
+ `<div class="card"><strong>Events</strong><br>${summary.eventCount}</div>`,
2232
+ `<div class="card"><strong>Turns</strong><br>${summary.turnCount}</div>`,
2233
+ `<div class="card"><strong>Transcripts</strong><br>${summary.transcriptCount}</div>`,
2234
+ `<div class="card"><strong>Tool errors</strong><br>${summary.toolErrorCount}</div>`,
2235
+ `<div class="card"><strong>Cost units</strong><br>${summary.cost.estimatedRelativeCostUnits}</div>`,
2236
+ "</section>",
2237
+ "<h2>Timeline</h2>",
2238
+ "<table><thead><tr><th>Offset ms</th><th>Type</th><th>Turn</th><th>Payload</th></tr></thead><tbody>",
2239
+ eventRows,
2240
+ "</tbody></table>",
2241
+ "<h2>Markdown Export</h2>",
2242
+ `<pre>${escapeHtml(markdown)}</pre>`,
2243
+ "</main></body></html>"
2244
+ ].join(`
2245
+ `);
2246
+ };
2247
+ var renderVoiceTraceMarkdown = (events, options = {}) => {
2248
+ const sorted = filterVoiceTraceEvents(options.redact ? redactVoiceTraceEvents(events, options.redact) : events);
2249
+ const summary = summarizeVoiceTrace(sorted);
2250
+ const evaluation = evaluateVoiceTrace(sorted, options.evaluation);
2251
+ const lines = [
2252
+ `# ${options.title ?? `Voice Trace ${summary.sessionId ?? ""}`.trim()}`,
2253
+ "",
2254
+ `Pass: ${evaluation.pass ? "yes" : "no"}`,
2255
+ `Session: ${summary.sessionId ?? "unknown"}`,
2256
+ `Events: ${summary.eventCount}`,
2257
+ `Turns: ${summary.turnCount}`,
2258
+ `Transcripts: ${summary.transcriptCount}`,
2259
+ `Assistant replies: ${summary.assistantReplyCount}`,
2260
+ `Model calls: ${summary.modelCallCount}`,
2261
+ `Tool calls: ${summary.toolCallCount}`,
2262
+ `Handoffs: ${summary.handoffCount}`,
2263
+ `Errors: ${summary.errorCount}`,
2264
+ `Estimated cost units: ${summary.cost.estimatedRelativeCostUnits}`,
2265
+ ""
2266
+ ];
2267
+ if (evaluation.issues.length > 0) {
2268
+ lines.push("## Issues", "");
2269
+ for (const issue of evaluation.issues) {
2270
+ lines.push(`- [${issue.severity}] ${issue.code}: ${issue.message}`);
2271
+ }
2272
+ lines.push("");
2273
+ }
2274
+ lines.push("## Timeline", "");
2275
+ for (const event of sorted) {
2276
+ lines.push(renderTraceEventMarkdown(event, summary.startedAt));
2277
+ }
2278
+ return lines.join(`
2279
+ `);
2280
+ };
2281
+
2282
+ // src/testing/review.ts
2283
+ var roundMetric = (value) => typeof value === "number" ? Math.round(value * 100) / 100 : undefined;
2284
+ var formatMetric = (label, value, unit = "ms") => typeof value === "number" ? `${label}: ${roundMetric(value)}${unit}` : undefined;
2285
+ var findTimelineEvent = (timeline, event, source) => timeline.find((entry) => entry.event === event && (source === undefined || entry.source === source));
2286
+ var formatTimelineText = (entry) => {
2287
+ const parts = [`- ${entry.atMs}ms`, `[${entry.source}]`, entry.event];
2288
+ if (entry.text) {
2289
+ parts.push(`"${entry.text}"`);
2290
+ }
2291
+ if (entry.reason) {
2292
+ parts.push(`reason=${entry.reason}`);
2293
+ }
2294
+ if (typeof entry.bytes === "number") {
2295
+ parts.push(`bytes=${entry.bytes}`);
2296
+ }
2297
+ if (typeof entry.confidence === "number") {
2298
+ parts.push(`confidence=${roundMetric(entry.confidence)}`);
2299
+ }
2300
+ if (entry.name) {
2301
+ parts.push(`name=${entry.name}`);
2302
+ }
2303
+ return parts.join(" ");
2304
+ };
2305
+ var isLowSignalTimelineEvent = (entry) => entry.event === "inbound-media" || entry.event === "inbound-silence-pad" || entry.event === "stt-send" || entry.event === "tts-audio";
2306
+ var summarizeTimelineTraffic = (timeline) => {
2307
+ const summaries = new Map;
2308
+ for (const entry of timeline) {
2309
+ const label = entry.event === "inbound-media" ? "inbound media chunks" : entry.event === "inbound-silence-pad" ? "inbound silence padding" : entry.event === "stt-send" ? "STT audio sends" : entry.event === "tts-audio" ? "post-first TTS audio chunks" : undefined;
2310
+ if (!label) {
2311
+ continue;
2312
+ }
2313
+ const summary = summaries.get(label) ?? {
2314
+ audioMs: 0,
2315
+ bytes: 0,
2316
+ count: 0,
2317
+ label
2318
+ };
2319
+ summary.count += 1;
2320
+ summary.bytes += typeof entry.bytes === "number" ? entry.bytes : 0;
2321
+ summary.audioMs = (summary.audioMs ?? 0) + (typeof entry.chunkDurationMs === "number" ? entry.chunkDurationMs : 0);
2322
+ summaries.set(label, summary);
2323
+ }
2324
+ return [...summaries.values()];
2325
+ };
2326
+ var compactTimeline = (timeline) => {
2327
+ const rows = [];
2328
+ let index = 0;
2329
+ while (index < timeline.length) {
2330
+ const current = timeline[index];
2331
+ if (!current) {
2332
+ break;
2333
+ }
2334
+ const isBurstEvent = isLowSignalTimelineEvent(current) || current.event === "media" && current.source === "twilio";
2335
+ if (!isBurstEvent) {
2336
+ rows.push(formatTimelineText(current));
2337
+ index += 1;
2338
+ continue;
2339
+ }
2340
+ let endIndex = index;
2341
+ let totalBytes = typeof current.bytes === "number" ? current.bytes : 0;
2342
+ let totalChunkDurationMs = typeof current.chunkDurationMs === "number" ? current.chunkDurationMs : 0;
2343
+ while (endIndex + 1 < timeline.length) {
2344
+ const next = timeline[endIndex + 1];
2345
+ if (!next) {
2346
+ break;
2347
+ }
2348
+ if (next.event !== current.event || next.source !== current.source) {
2349
+ break;
2350
+ }
2351
+ totalBytes += typeof next.bytes === "number" ? next.bytes : 0;
2352
+ totalChunkDurationMs += typeof next.chunkDurationMs === "number" ? next.chunkDurationMs : 0;
2353
+ endIndex += 1;
2354
+ }
2355
+ const startAt = current.atMs;
2356
+ const endAt = timeline[endIndex]?.atMs ?? current.atMs;
2357
+ const count = endIndex - index + 1;
2358
+ const parts = [
2359
+ `- ${startAt}-${endAt}ms`,
2360
+ `[${current.source}]`,
2361
+ `${current.event} x${count}`
2362
+ ];
2363
+ if (totalBytes > 0) {
2364
+ parts.push(`bytes=${totalBytes}`);
2365
+ }
2366
+ if (totalChunkDurationMs > 0) {
2367
+ parts.push(`audio=${roundMetric(totalChunkDurationMs)}ms`);
2368
+ }
2369
+ rows.push(parts.join(" "));
2370
+ index = endIndex + 1;
2371
+ }
2372
+ return rows;
2373
+ };
2374
+ var createVoiceCallReviewFromLiveTelephonyReport = (report, options = {}) => {
2375
+ const fixture = report.fixtures?.[0];
2376
+ if (!fixture) {
2377
+ throw new Error("Live telephony review requires at least one fixture result.");
2378
+ }
2379
+ const timeline = [...report.trace ?? []].sort((left, right) => left.atMs - right.atMs);
2380
+ const firstPartial = findTimelineEvent(timeline, "partial", "stt");
2381
+ const commitEvent = findTimelineEvent(timeline, "commit", "turn");
2382
+ const firstTtsAudio = findTimelineEvent(timeline, "tts-first-audio", "benchmark");
2383
+ const firstOutboundMedia = findTimelineEvent(timeline, "media", "twilio");
2384
+ const bargeInEvent = findTimelineEvent(timeline, "barge-in", "benchmark");
2385
+ const clearEvent = findTimelineEvent(timeline, "clear", "twilio");
2386
+ const lastSttText = [...timeline].reverse().find((entry) => entry.source === "stt" && (entry.event === "partial" || entry.event === "final") && typeof entry.text === "string" && entry.text.length > 0)?.text ?? undefined;
2387
+ const latencyBreakdown = [
2388
+ typeof firstPartial?.atMs === "number" ? {
2389
+ label: "start to first partial",
2390
+ valueMs: firstPartial.atMs
2391
+ } : undefined,
2392
+ typeof firstPartial?.atMs === "number" && typeof commitEvent?.atMs === "number" ? {
2393
+ label: "first partial to commit",
2394
+ valueMs: commitEvent.atMs - firstPartial.atMs
2395
+ } : undefined,
2396
+ typeof commitEvent?.atMs === "number" && typeof firstTtsAudio?.atMs === "number" ? {
2397
+ label: "commit to first TTS audio",
2398
+ valueMs: firstTtsAudio.atMs - commitEvent.atMs
2399
+ } : undefined,
2400
+ typeof commitEvent?.atMs === "number" && typeof firstOutboundMedia?.atMs === "number" ? {
2401
+ label: "commit to first outbound media",
2402
+ valueMs: firstOutboundMedia.atMs - commitEvent.atMs
2403
+ } : undefined,
2404
+ typeof bargeInEvent?.atMs === "number" && typeof clearEvent?.atMs === "number" ? {
2405
+ label: "barge-in to clear",
2406
+ valueMs: clearEvent.atMs - bargeInEvent.atMs
2407
+ } : undefined
2408
+ ].filter((value) => value !== undefined && value.valueMs >= 0);
2409
+ const notes = [
2410
+ report.variant?.description,
2411
+ firstPartial?.text ? `First partial: "${firstPartial.text}"` : undefined,
2412
+ lastSttText ? `Last STT text: "${lastSttText}"` : undefined
2413
+ ].filter((value) => typeof value === "string" && value.length > 0);
2414
+ return {
2415
+ config: {
2416
+ preset: options.preset,
2417
+ stt: report.variant ? {
2418
+ description: report.variant.description,
2419
+ id: report.variant.id,
2420
+ model: report.variant.model
2421
+ } : undefined,
2422
+ tts: report.ttsConfig,
2423
+ turnDetection: report.turnDetectionConfig
2424
+ },
2425
+ errors: fixture.errors ?? [],
2426
+ expectedText: fixture.expectedText,
2427
+ fixtureId: fixture.fixtureId,
2428
+ generatedAt: report.generatedAt,
2429
+ latencyBreakdown,
2430
+ notes,
2431
+ path: options.path,
2432
+ summary: {
2433
+ clearLatencyMs: roundMetric(fixture.clearLatencyMs),
2434
+ elapsedMs: roundMetric(fixture.elapsedMs),
2435
+ firstOutboundMediaLatencyMs: roundMetric(fixture.firstOutboundMediaLatencyMs),
2436
+ firstTurnLatencyMs: roundMetric(fixture.firstTurnLatencyMs),
2437
+ markLatencyMs: roundMetric(fixture.markLatencyMs),
2438
+ outboundMediaCount: fixture.outboundMediaCount,
2439
+ pass: fixture.passes,
2440
+ termRecall: roundMetric(fixture.termRecall),
2441
+ turnCount: fixture.turnCount,
2442
+ wordErrorRate: roundMetric(fixture.wordErrorRate)
2443
+ },
2444
+ title: fixture.title ?? "Voice Call Review",
2445
+ timeline,
2446
+ transcript: {
2447
+ actual: fixture.actualText,
2448
+ expected: fixture.expectedText
2449
+ }
2450
+ };
2451
+ };
2452
+ var withVoiceCallReviewId = (id, artifact) => ({
2453
+ ...artifact,
2454
+ id
2455
+ });
2456
+ var toErrorMessage = (error) => {
2457
+ if (typeof error === "string" && error.trim().length > 0) {
2458
+ return error;
2459
+ }
2460
+ if (error instanceof Error && error.message.trim().length > 0) {
2461
+ return error.message;
2462
+ }
2463
+ return "Unknown call error";
2464
+ };
2465
+ var createVoiceCallReviewRecorder = (options = {}) => {
2466
+ const now = options.now ?? (() => Date.now());
2467
+ const startedAt = now();
2468
+ const errors = [];
2469
+ const timeline = [];
2470
+ const committedTurns = [];
2471
+ const committedTurnIds = new Set;
2472
+ const push = (source, event, fields = {}) => {
2473
+ timeline.push({
2474
+ atMs: Math.max(0, now() - startedAt),
2475
+ event,
2476
+ source,
2477
+ ...fields
2478
+ });
2479
+ };
2480
+ return {
2481
+ finalize: () => {
2482
+ const sortedTimeline = [...timeline].sort((left, right) => left.atMs - right.atMs);
2483
+ const firstPartial = findTimelineEvent(sortedTimeline, "partial", "stt");
2484
+ const commitEvent = findTimelineEvent(sortedTimeline, "commit", "turn");
2485
+ const firstTtsAudio = findTimelineEvent(sortedTimeline, "tts-first-audio", "benchmark");
2486
+ const firstOutboundMedia = findTimelineEvent(sortedTimeline, "media", "twilio");
2487
+ const bargeInEvent = findTimelineEvent(sortedTimeline, "barge-in", "benchmark");
2488
+ const clearEvent = findTimelineEvent(sortedTimeline, "clear", "twilio");
2489
+ const markEvent = findTimelineEvent(sortedTimeline, "mark", "twilio");
2490
+ const elapsedMs = sortedTimeline.at(-1)?.atMs ?? 0;
2491
+ const lastSttText = [...sortedTimeline].reverse().find((entry) => entry.source === "stt" && (entry.event === "partial" || entry.event === "final") && typeof entry.text === "string" && entry.text.length > 0)?.text ?? undefined;
2492
+ const latencyBreakdown = [
2493
+ typeof firstPartial?.atMs === "number" ? {
2494
+ label: "start to first partial",
2495
+ valueMs: firstPartial.atMs
2496
+ } : undefined,
2497
+ typeof firstPartial?.atMs === "number" && typeof commitEvent?.atMs === "number" ? {
2498
+ label: "first partial to commit",
2499
+ valueMs: commitEvent.atMs - firstPartial.atMs
2500
+ } : undefined,
2501
+ typeof commitEvent?.atMs === "number" && typeof firstTtsAudio?.atMs === "number" ? {
2502
+ label: "commit to first TTS audio",
2503
+ valueMs: firstTtsAudio.atMs - commitEvent.atMs
2504
+ } : undefined,
2505
+ typeof commitEvent?.atMs === "number" && typeof firstOutboundMedia?.atMs === "number" ? {
2506
+ label: "commit to first outbound media",
2507
+ valueMs: firstOutboundMedia.atMs - commitEvent.atMs
2508
+ } : undefined,
2509
+ typeof bargeInEvent?.atMs === "number" && typeof clearEvent?.atMs === "number" ? {
2510
+ label: "barge-in to clear",
2511
+ valueMs: clearEvent.atMs - bargeInEvent.atMs
2512
+ } : undefined
2513
+ ].filter((value) => value !== undefined && value.valueMs >= 0);
2514
+ return {
2515
+ config: options.config,
2516
+ errors,
2517
+ fixtureId: options.fixtureId,
2518
+ generatedAt: now(),
2519
+ latencyBreakdown,
2520
+ notes: [
2521
+ firstPartial?.text ? `First partial: "${firstPartial.text}"` : undefined,
2522
+ lastSttText ? `Last STT text: "${lastSttText}"` : undefined
2523
+ ].filter((value) => typeof value === "string"),
2524
+ path: options.path,
2525
+ summary: {
2526
+ clearLatencyMs: roundMetric(typeof clearEvent?.atMs === "number" && typeof bargeInEvent?.atMs === "number" ? clearEvent.atMs - bargeInEvent.atMs : undefined),
2527
+ elapsedMs: roundMetric(elapsedMs),
2528
+ firstOutboundMediaLatencyMs: roundMetric(firstOutboundMedia?.atMs),
2529
+ firstTurnLatencyMs: roundMetric(commitEvent?.atMs),
2530
+ markLatencyMs: roundMetric(markEvent?.atMs),
2531
+ outboundMediaCount: sortedTimeline.filter((entry) => entry.source === "twilio" && entry.event === "media").length,
2532
+ pass: errors.length === 0,
2533
+ turnCount: committedTurns.length
2534
+ },
2535
+ title: options.title ?? "Voice Call Review",
2536
+ timeline: sortedTimeline,
2537
+ transcript: {
2538
+ actual: committedTurns.join(" ").trim()
2539
+ }
2540
+ };
2541
+ },
2542
+ recordError: (error) => {
2543
+ const message = toErrorMessage(error);
2544
+ errors.push(message);
2545
+ push("turn", "error", {
2546
+ reason: message
2547
+ });
2548
+ },
2549
+ recordTwilioInbound: (input) => {
2550
+ push("twilio", input.event, {
2551
+ bytes: input.bytes,
2552
+ chunkDurationMs: input.chunkDurationMs,
2553
+ name: input.name,
2554
+ reason: input.reason,
2555
+ text: input.text,
2556
+ track: input.track
2557
+ });
2558
+ },
2559
+ recordTwilioOutbound: (input) => {
2560
+ push("twilio", input.event, {
2561
+ bytes: input.bytes,
2562
+ chunkDurationMs: input.chunkDurationMs,
2563
+ name: input.name,
2564
+ reason: input.reason,
2565
+ text: input.text,
2566
+ track: input.track
2567
+ });
2568
+ },
2569
+ recordVoiceMessage: (message) => {
2570
+ switch (message.type) {
2571
+ case "partial":
2572
+ case "final":
2573
+ push("stt", message.type, {
2574
+ confidence: message.transcript.confidence,
2575
+ text: message.transcript.text
2576
+ });
2577
+ return;
2578
+ case "assistant":
2579
+ push("turn", "assistant", {
2580
+ text: message.text
2581
+ });
2582
+ return;
2583
+ case "audio":
2584
+ push("benchmark", timeline.some((entry) => entry.event === "tts-first-audio") ? "tts-audio" : "tts-first-audio", {
2585
+ bytes: Math.floor(message.chunkBase64.length * 3 / 4)
2586
+ });
2587
+ return;
2588
+ case "turn":
2589
+ if (committedTurnIds.has(message.turn.id)) {
2590
+ return;
2591
+ }
2592
+ committedTurnIds.add(message.turn.id);
2593
+ committedTurns.push(message.turn.text);
2594
+ push("turn", "commit", {
2595
+ confidence: message.turn.quality?.averageConfidence,
2596
+ text: message.turn.text
2597
+ });
2598
+ return;
2599
+ case "error":
2600
+ errors.push(message.message);
2601
+ push("turn", "error", {
2602
+ reason: message.message
2603
+ });
2604
+ return;
2605
+ case "complete":
2606
+ push("turn", "complete", {
2607
+ text: message.sessionId
2608
+ });
2609
+ return;
2610
+ case "session":
2611
+ push("turn", "session", {
2612
+ reason: message.status,
2613
+ text: message.sessionId
2614
+ });
2615
+ return;
2616
+ case "pong":
2617
+ push("benchmark", "pong");
2618
+ }
2619
+ }
2620
+ };
2621
+ };
2622
+ var renderConfigSection = (config) => {
2623
+ if (!config) {
2624
+ return "";
2625
+ }
2626
+ return [
2627
+ "## Config",
2628
+ "",
2629
+ "```json",
2630
+ JSON.stringify(config, null, 2),
2631
+ "```"
2632
+ ].join(`
2633
+ `);
2634
+ };
2635
+ var renderTimeline = (timeline) => {
2636
+ const focusedTimeline = timeline.filter((entry) => !isLowSignalTimelineEvent(entry));
2637
+ if (focusedTimeline.length === 0) {
2638
+ return `## Timeline
2639
+
2640
+ _No timeline events captured._`;
2641
+ }
2642
+ const lines = compactTimeline(focusedTimeline);
2643
+ return ["## Timeline", "", ...lines].join(`
2644
+ `);
2645
+ };
2646
+ var renderTransportSummary = (timeline) => {
2647
+ const summaries = summarizeTimelineTraffic(timeline);
2648
+ if (summaries.length === 0) {
2649
+ return "";
2650
+ }
2651
+ return [
2652
+ "## Transport Summary",
2653
+ "",
2654
+ ...summaries.map((summary) => {
2655
+ const parts = [`- ${summary.label}: ${summary.count}`];
2656
+ if (summary.bytes > 0) {
2657
+ parts.push(`${summary.bytes} bytes`);
2658
+ }
2659
+ if ((summary.audioMs ?? 0) > 0) {
2660
+ parts.push(`${roundMetric(summary.audioMs)}ms audio`);
2661
+ }
2662
+ return parts.join(", ");
2663
+ })
2664
+ ].join(`
2665
+ `);
2666
+ };
2667
+ var renderLatencyBreakdown = (breakdown) => {
2668
+ if (breakdown.length === 0) {
2669
+ return "";
2670
+ }
2671
+ return [
2672
+ "## Latency Breakdown",
2673
+ "",
2674
+ ...breakdown.map((entry) => `- ${entry.label}: ${roundMetric(entry.valueMs)}ms`)
2675
+ ].join(`
2676
+ `);
2677
+ };
2678
+ var renderVoiceCallReviewHTML = (artifact) => {
2679
+ const notes = artifact.notes.map((note) => `<li>${escapeHtml(note)}</li>`).join("");
2680
+ const latency = artifact.latencyBreakdown.map((entry) => `<li><strong>${escapeHtml(entry.label)}:</strong> ${roundMetric(entry.valueMs)}ms</li>`).join("");
2681
+ const transport = summarizeTimelineTraffic(artifact.timeline).map((summary) => {
2682
+ const parts = [`${summary.count}`, "events"];
2683
+ if (summary.bytes > 0) {
2684
+ parts.push(`${summary.bytes} bytes`);
2685
+ }
2686
+ if ((summary.audioMs ?? 0) > 0) {
2687
+ parts.push(`${roundMetric(summary.audioMs)}ms audio`);
2688
+ }
2689
+ return `<li><strong>${escapeHtml(summary.label)}:</strong> ${escapeHtml(parts.join(", "))}</li>`;
2690
+ }).join("");
2691
+ const timeline = compactTimeline(artifact.timeline.filter((entry) => !isLowSignalTimelineEvent(entry))).map((line) => `<li>${escapeHtml(line.replace(/^- /u, ""))}</li>`).join("");
2692
+ return `<!doctype html>
2693
+ <html lang="en">
2694
+ <head>
2695
+ <meta charset="utf-8" />
2696
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
2697
+ <title>${escapeHtml(artifact.title)}</title>
2698
+ <style>
2699
+ :root { color-scheme: dark; }
2700
+ body { font-family: ui-sans-serif, system-ui, sans-serif; margin: 0; padding: 24px; background: #0b0d10; color: #f4f4f5; }
2701
+ main { max-width: 980px; margin: 0 auto; display: grid; gap: 16px; }
2702
+ section { background: #13161b; border: 1px solid #232833; border-radius: 16px; padding: 18px; }
2703
+ h1, h2 { margin: 0 0 12px; }
2704
+ ul { margin: 0; padding-left: 20px; display: grid; gap: 8px; }
2705
+ code, pre { font-family: ui-monospace, SFMono-Regular, monospace; }
2706
+ pre { white-space: pre-wrap; overflow-wrap: anywhere; background: #0f1217; border-radius: 12px; padding: 14px; border: 1px solid #232833; }
2707
+ .grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
2708
+ .metric { display: grid; gap: 4px; }
2709
+ .label { color: #a1a1aa; font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.08em; }
2710
+ .value { font-size: 1.05rem; }
2711
+ </style>
2712
+ </head>
2713
+ <body>
2714
+ <main>
2715
+ <section>
2716
+ <h1>${escapeHtml(artifact.title)}</h1>
2717
+ <div class="grid">
2718
+ <div class="metric"><div class="label">Pass</div><div class="value">${artifact.summary.pass ? "yes" : "no"}</div></div>
2719
+ <div class="metric"><div class="label">First Turn</div><div class="value">${artifact.summary.firstTurnLatencyMs ?? "n/a"}ms</div></div>
2720
+ <div class="metric"><div class="label">First Outbound Media</div><div class="value">${artifact.summary.firstOutboundMediaLatencyMs ?? "n/a"}ms</div></div>
2721
+ <div class="metric"><div class="label">Turn Count</div><div class="value">${artifact.summary.turnCount ?? "n/a"}</div></div>
2722
+ </div>
2723
+ </section>
2724
+ <section>
2725
+ <h2>Transcript</h2>
2726
+ <ul>
2727
+ <li><strong>Expected:</strong> ${escapeHtml(artifact.transcript.expected ?? "n/a")}</li>
2728
+ <li><strong>Actual:</strong> ${escapeHtml(artifact.transcript.actual || "n/a")}</li>
2729
+ </ul>
2730
+ </section>
2731
+ <section>
2732
+ <h2>Notes</h2>
2733
+ <ul>${notes || "<li>No notes.</li>"}</ul>
2734
+ </section>
2735
+ <section>
2736
+ <h2>Latency Breakdown</h2>
2737
+ <ul>${latency || "<li>No latency data.</li>"}</ul>
2738
+ </section>
2739
+ <section>
2740
+ <h2>Transport Summary</h2>
2741
+ <ul>${transport || "<li>No transport data.</li>"}</ul>
2742
+ </section>
2743
+ <section>
2744
+ <h2>Timeline</h2>
2745
+ <ul>${timeline || "<li>No timeline events.</li>"}</ul>
2746
+ </section>
2747
+ <section>
2748
+ <h2>Config</h2>
2749
+ <pre>${escapeHtml(JSON.stringify(artifact.config ?? {}, null, 2))}</pre>
2750
+ </section>
2751
+ </main>
2752
+ </body>
2753
+ </html>`;
2754
+ };
2755
+ var renderVoiceCallReviewMarkdown = (artifact) => {
2756
+ const summaryLines = [
2757
+ `- pass: ${artifact.summary.pass ? "yes" : "no"}`,
2758
+ formatMetric("first turn", artifact.summary.firstTurnLatencyMs),
2759
+ formatMetric("first outbound media", artifact.summary.firstOutboundMediaLatencyMs),
2760
+ formatMetric("mark", artifact.summary.markLatencyMs),
2761
+ formatMetric("clear", artifact.summary.clearLatencyMs),
2762
+ formatMetric("elapsed", artifact.summary.elapsedMs),
2763
+ typeof artifact.summary.wordErrorRate === "number" ? `- word error rate: ${artifact.summary.wordErrorRate}` : undefined,
2764
+ typeof artifact.summary.termRecall === "number" ? `- term recall: ${artifact.summary.termRecall}` : undefined,
2765
+ typeof artifact.summary.turnCount === "number" ? `- turn count: ${artifact.summary.turnCount}` : undefined,
2766
+ typeof artifact.summary.outboundMediaCount === "number" ? `- outbound media count: ${artifact.summary.outboundMediaCount}` : undefined
2767
+ ].filter((value) => typeof value === "string");
2768
+ const notes = artifact.notes.length ? ["## Notes", "", ...artifact.notes.map((note) => `- ${note}`)].join(`
2769
+ `) : "";
2770
+ const errors = artifact.errors.length ? ["## Errors", "", ...artifact.errors.map((error) => `- ${error}`)].join(`
2771
+ `) : "";
2772
+ const latency = renderLatencyBreakdown(artifact.latencyBreakdown);
2773
+ const transportSummary = renderTransportSummary(artifact.timeline);
2774
+ return [
2775
+ `# ${artifact.title}`,
2776
+ "",
2777
+ artifact.path ? `Source: \`${artifact.path}\`` : undefined,
2778
+ artifact.fixtureId ? `Fixture: \`${artifact.fixtureId}\`` : undefined,
2779
+ "",
2780
+ "## Summary",
2781
+ "",
2782
+ ...summaryLines,
2783
+ "",
2784
+ "## Transcript",
2785
+ "",
2786
+ `- expected: ${artifact.transcript.expected ?? "_n/a_"}`,
2787
+ `- actual: ${artifact.transcript.actual}`,
2788
+ "",
2789
+ notes,
2790
+ notes ? "" : undefined,
2791
+ latency,
2792
+ latency ? "" : undefined,
2793
+ transportSummary,
2794
+ transportSummary ? "" : undefined,
2795
+ errors,
2796
+ errors ? "" : undefined,
2797
+ renderConfigSection(artifact.config),
2798
+ renderConfigSection(artifact.config) ? "" : undefined,
2799
+ renderTimeline(artifact.timeline)
2800
+ ].filter((value) => typeof value === "string").join(`
2801
+ `);
2802
+ };
2803
+
2804
+ // src/drizzle/runtimeStorage.ts
2805
+ var voiceAuditDeliveriesTable = voiceDocumentTable("voice_audit_deliveries");
2806
+ var voiceAuditTable = voiceDocumentTable("voice_audit");
2807
+ var voiceCampaignsTable = voiceDocumentTable("voice_campaigns");
2808
+ var voiceEventsTable = voiceDocumentTable("voice_events");
2809
+ var voiceExternalObjectsTable = voiceDocumentTable("voice_external_objects");
2810
+ var voiceReviewsTable = voiceDocumentTable("voice_reviews");
2811
+ var voiceSessionsTable = voiceDocumentTable("voice_sessions");
2812
+ var voiceTasksTable = voiceDocumentTable("voice_tasks");
2813
+ var voiceTelephonyWebhookIdempotencyTable = voiceDocumentTable("voice_telephony_webhook_idempotency");
2814
+ var voiceTraceDeliveriesTable = voiceDocumentTable("voice_trace_deliveries");
2815
+ var voiceTracesTable = voiceDocumentTable("voice_traces");
2816
+ var voiceRuntimeStorageDrizzleSchema = {
2817
+ voiceAudit: voiceAuditTable,
2818
+ voiceAuditDeliveries: voiceAuditDeliveriesTable,
2819
+ voiceCampaigns: voiceCampaignsTable,
2820
+ voiceEvents: voiceEventsTable,
2821
+ voiceExternalObjects: voiceExternalObjectsTable,
2822
+ voiceReviews: voiceReviewsTable,
2823
+ voiceSessions: voiceSessionsTable,
2824
+ voiceTasks: voiceTasksTable,
2825
+ voiceTelephonyWebhookIdempotency: voiceTelephonyWebhookIdempotencyTable,
2826
+ voiceTraceDeliveries: voiceTraceDeliveriesTable,
2827
+ voiceTraces: voiceTracesTable
2828
+ };
2829
+ var createDrizzleSessionStore = (db) => {
2830
+ const store = createVoiceDrizzleRecordStore({
2831
+ db,
2832
+ decorate: (_id, value) => value,
2833
+ getSortAt: (value) => value.lastActivityAt ?? value.createdAt,
2834
+ table: voiceSessionsTable
2835
+ });
2836
+ const getOrCreate = async (id) => {
2837
+ const existing = await store.get(id);
2838
+ if (existing) {
2839
+ return existing;
2840
+ }
2841
+ const session = createVoiceSessionRecord(id);
2842
+ await store.set(id, session);
2843
+ return session;
2844
+ };
2845
+ return {
2846
+ get: store.get,
2847
+ getOrCreate,
2848
+ remove: store.remove,
2849
+ set: store.set,
2850
+ list: async () => (await store.list()).map((session) => toVoiceSessionSummary(session)).sort((first, second) => (second.lastActivityAt ?? second.createdAt) - (first.lastActivityAt ?? first.createdAt))
2851
+ };
2852
+ };
2853
+ var createDrizzleReviewStore = (db) => createVoiceDrizzleRecordStore({
2854
+ db,
2855
+ decorate: (id, value) => withVoiceCallReviewId(id, value),
2856
+ getSortAt: (value) => value.generatedAt ?? 0,
2857
+ table: voiceReviewsTable
2858
+ });
2859
+ var createDrizzleTaskStore = (db) => createVoiceDrizzleRecordStore({
2860
+ db,
2861
+ decorate: (id, value) => withVoiceOpsTaskId(id, value),
2862
+ getSortAt: (value) => value.createdAt,
2863
+ table: voiceTasksTable
2864
+ });
2865
+ var createDrizzleEventStore = (db) => createVoiceDrizzleRecordStore({
2866
+ db,
2867
+ decorate: (id, value) => withVoiceIntegrationEventId(id, value),
2868
+ getSortAt: (value) => value.createdAt,
2869
+ table: voiceEventsTable
2870
+ });
2871
+ var createDrizzleExternalObjectMapStore = (db) => {
2872
+ const store = createVoiceDrizzleRecordStore({
2873
+ db,
2874
+ decorate: (id, value) => ({
2875
+ ...value,
2876
+ id
2877
+ }),
2878
+ getSortAt: (value) => value.updatedAt,
2879
+ table: voiceExternalObjectsTable
2880
+ });
2881
+ const find = async (input) => (await store.list()).find((mapping) => mapping.provider === input.provider && mapping.sourceId === input.sourceId && (input.sinkId === undefined || mapping.sinkId === input.sinkId) && (input.sourceType === undefined || mapping.sourceType === input.sourceType));
2882
+ return {
2883
+ ...store,
2884
+ find
2885
+ };
2886
+ };
2887
+ var createDrizzleTraceEventStore = (db) => {
2888
+ const store = createVoiceDrizzleRecordStore({
2889
+ db,
2890
+ decorate: (_id, value) => value,
2891
+ getSortAt: (value) => value.at,
2892
+ table: voiceTracesTable
2893
+ });
2894
+ const append = async (event) => {
2895
+ const stored = createVoiceTraceEvent(event);
2896
+ await store.set(stored.id, stored);
2897
+ return stored;
2898
+ };
2899
+ return {
2900
+ append,
2901
+ get: store.get,
2902
+ remove: store.remove,
2903
+ list: async (filter) => filterVoiceTraceEvents(await store.list(), filter)
2904
+ };
2905
+ };
2906
+ var createDrizzleTraceSinkDeliveryStore = (db) => createVoiceDrizzleRecordStore({
2907
+ db,
2908
+ decorate: (id, value) => ({
2909
+ ...value,
2910
+ id
2911
+ }),
2912
+ getSortAt: (value) => value.createdAt,
2913
+ table: voiceTraceDeliveriesTable
2914
+ });
2915
+ var createDrizzleAuditEventStore = (db) => {
2916
+ const store = createVoiceDrizzleRecordStore({
2917
+ db,
2918
+ decorate: (_id, value) => value,
2919
+ getSortAt: (value) => value.at,
2920
+ table: voiceAuditTable
2921
+ });
2922
+ const append = async (event) => {
2923
+ const stored = createVoiceAuditEvent(event);
2924
+ await store.set(stored.id, stored);
2925
+ return stored;
2926
+ };
2927
+ return {
2928
+ append,
2929
+ get: store.get,
2930
+ list: async (filter) => filterVoiceAuditEvents(await store.list(), filter)
2931
+ };
2932
+ };
2933
+ var createDrizzleAuditSinkDeliveryStore = (db) => createVoiceDrizzleRecordStore({
2934
+ db,
2935
+ decorate: (id, value) => ({
2936
+ ...value,
2937
+ id
2938
+ }),
2939
+ getSortAt: (value) => value.createdAt,
2940
+ table: voiceAuditDeliveriesTable
2941
+ });
2942
+ var createDrizzleTelephonyWebhookIdempotencyStore = (db) => createVoiceDrizzleRecordStore({
2943
+ db,
2944
+ decorate: (_id, value) => value,
2945
+ getSortAt: (value) => value.updatedAt,
2946
+ table: voiceTelephonyWebhookIdempotencyTable
2947
+ });
2948
+ var createDrizzleCampaignStore = (db) => createVoiceDrizzleRecordStore({
2949
+ db,
2950
+ decorate: (_id, value) => value,
2951
+ getSortAt: (value) => value.campaign.createdAt,
2952
+ table: voiceCampaignsTable
2953
+ });
2954
+ var createVoiceDrizzleAuditEventStore = (options) => createDrizzleAuditEventStore(options.db);
2955
+ var createVoiceDrizzleAuditSinkDeliveryStore = (options) => createDrizzleAuditSinkDeliveryStore(options.db);
2956
+ var createVoiceDrizzleCampaignStore = (options) => createDrizzleCampaignStore(options.db);
2957
+ var createVoiceDrizzleExternalObjectMapStore = (options) => createDrizzleExternalObjectMapStore(options.db);
2958
+ var createVoiceDrizzleIntegrationEventStore = (options) => createDrizzleEventStore(options.db);
2959
+ var createVoiceDrizzleReviewStore = (options) => createDrizzleReviewStore(options.db);
2960
+ var createVoiceDrizzleRuntimeStorage = (options) => ({
2961
+ audit: createDrizzleAuditEventStore(options.db),
2962
+ auditDeliveries: createDrizzleAuditSinkDeliveryStore(options.db),
2963
+ campaigns: createDrizzleCampaignStore(options.db),
2964
+ events: createDrizzleEventStore(options.db),
2965
+ externalObjects: createDrizzleExternalObjectMapStore(options.db),
2966
+ reviews: createDrizzleReviewStore(options.db),
2967
+ session: createDrizzleSessionStore(options.db),
2968
+ tasks: createDrizzleTaskStore(options.db),
2969
+ traceDeliveries: createDrizzleTraceSinkDeliveryStore(options.db),
2970
+ traces: createDrizzleTraceEventStore(options.db)
2971
+ });
2972
+ var createVoiceDrizzleSessionStore = (options) => createDrizzleSessionStore(options.db);
2973
+ var createVoiceDrizzleTaskStore = (options) => createDrizzleTaskStore(options.db);
2974
+ var createVoiceDrizzleTelephonyWebhookIdempotencyStore = (options) => createDrizzleTelephonyWebhookIdempotencyStore(options.db);
2975
+ var createVoiceDrizzleTraceEventStore = (options) => createDrizzleTraceEventStore(options.db);
2976
+ var createVoiceDrizzleTraceSinkDeliveryStore = (options) => createDrizzleTraceSinkDeliveryStore(options.db);
2977
+
2978
+ // src/drizzle/index.ts
2979
+ var voiceDrizzleSchema = {
2980
+ ...voiceRuntimeStorageDrizzleSchema,
2981
+ voiceAssistantMemory: voiceAssistantMemoryTable,
2982
+ voiceEvalBaseline: voiceEvalBaselineTable,
2983
+ voiceHandoffDeliveries: voiceHandoffDeliveriesTable,
2984
+ voiceObservabilityExportReceipts: voiceObservabilityExportDeliveryReceiptsTable,
2985
+ voiceRealCallProfileEvidence: voiceRealCallProfileEvidenceTable,
2986
+ voiceRealCallProfileRecoveryJobs: voiceRealCallProfileRecoveryJobsTable
2987
+ };
2988
+ export {
2989
+ voiceTracesTable,
2990
+ voiceTraceDeliveriesTable,
2991
+ voiceTelephonyWebhookIdempotencyTable,
2992
+ voiceTasksTable,
2993
+ voiceSessionsTable,
2994
+ voiceRuntimeStorageDrizzleSchema,
2995
+ voiceReviewsTable,
2996
+ voiceRealCallProfileRecoveryJobsTable,
2997
+ voiceRealCallProfileEvidenceTable,
2998
+ voiceObservabilityExportDeliveryReceiptsTable,
2999
+ voiceHandoffDeliveriesTable,
3000
+ voiceExternalObjectsTable,
3001
+ voiceEventsTable,
3002
+ voiceEvalBaselineTable,
3003
+ voiceDrizzleSchema,
3004
+ voiceDocumentTable,
3005
+ voiceCampaignsTable,
3006
+ voiceAuditTable,
3007
+ voiceAuditDeliveriesTable,
3008
+ voiceAssistantMemoryTable,
3009
+ createVoiceDrizzleTraceSinkDeliveryStore,
3010
+ createVoiceDrizzleTraceEventStore,
3011
+ createVoiceDrizzleTelephonyWebhookIdempotencyStore,
3012
+ createVoiceDrizzleTaskStore,
3013
+ createVoiceDrizzleSessionStore,
3014
+ createVoiceDrizzleRuntimeStorage,
3015
+ createVoiceDrizzleReviewStore,
3016
+ createVoiceDrizzleRecordStore,
3017
+ createVoiceDrizzleRealCallProfileRecoveryJobStore,
3018
+ createVoiceDrizzleRealCallProfileEvidenceStore,
3019
+ createVoiceDrizzleObservabilityExportDeliveryReceiptStore,
3020
+ createVoiceDrizzleIntegrationEventStore,
3021
+ createVoiceDrizzleHandoffDeliveryStore,
3022
+ createVoiceDrizzleExternalObjectMapStore,
3023
+ createVoiceDrizzleEvalBaselineStore,
3024
+ createVoiceDrizzleCampaignStore,
3025
+ createVoiceDrizzleAuditSinkDeliveryStore,
3026
+ createVoiceDrizzleAuditEventStore,
3027
+ createVoiceDrizzleAssistantMemoryStore
3028
+ };