@absolutejs/voice 0.0.22-beta.3 → 0.0.22-beta.300

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 (247) hide show
  1. package/README.md +3232 -82
  2. package/dist/agent.d.ts +62 -0
  3. package/dist/agentSquadContract.d.ts +98 -0
  4. package/dist/angular/index.d.ts +17 -0
  5. package/dist/angular/index.js +3598 -1058
  6. package/dist/angular/voice-agent-squad-status.service.d.ts +12 -0
  7. package/dist/angular/voice-campaign-dialer-proof.service.d.ts +14 -0
  8. package/dist/angular/voice-controller.service.d.ts +1 -0
  9. package/dist/angular/voice-delivery-runtime.component.d.ts +17 -0
  10. package/dist/angular/voice-delivery-runtime.service.d.ts +16 -0
  11. package/dist/angular/voice-live-ops.service.d.ts +11 -0
  12. package/dist/angular/voice-ops-action-center.service.d.ts +13 -0
  13. package/dist/angular/voice-ops-status.component.d.ts +15 -0
  14. package/dist/angular/voice-ops-status.service.d.ts +12 -0
  15. package/dist/angular/voice-platform-coverage.service.d.ts +12 -0
  16. package/dist/angular/voice-proof-trends.service.d.ts +12 -0
  17. package/dist/angular/voice-provider-capabilities.service.d.ts +12 -0
  18. package/dist/angular/voice-provider-contracts.service.d.ts +12 -0
  19. package/dist/angular/voice-provider-status.service.d.ts +12 -0
  20. package/dist/angular/voice-readiness-failures.service.d.ts +13 -0
  21. package/dist/angular/voice-routing-status.service.d.ts +11 -0
  22. package/dist/angular/voice-stream.service.d.ts +3 -0
  23. package/dist/angular/voice-trace-timeline.service.d.ts +12 -0
  24. package/dist/angular/voice-turn-latency.service.d.ts +13 -0
  25. package/dist/angular/voice-turn-quality.service.d.ts +12 -0
  26. package/dist/angular/voice-workflow-status.service.d.ts +12 -0
  27. package/dist/assistant.d.ts +20 -0
  28. package/dist/assistantHealth.d.ts +81 -0
  29. package/dist/assistantMemory.d.ts +63 -0
  30. package/dist/audit.d.ts +128 -0
  31. package/dist/auditDeliveryRoutes.d.ts +85 -0
  32. package/dist/auditExport.d.ts +34 -0
  33. package/dist/auditRoutes.d.ts +66 -0
  34. package/dist/auditSinks.d.ts +151 -0
  35. package/dist/bargeInRoutes.d.ts +56 -0
  36. package/dist/campaign.d.ts +768 -0
  37. package/dist/campaignDialers.d.ts +111 -0
  38. package/dist/client/actions.d.ts +105 -0
  39. package/dist/client/agentSquadStatus.d.ts +37 -0
  40. package/dist/client/agentSquadStatusWidget.d.ts +24 -0
  41. package/dist/client/bargeInMonitor.d.ts +7 -0
  42. package/dist/client/campaignDialerProof.d.ts +23 -0
  43. package/dist/client/connection.d.ts +3 -0
  44. package/dist/client/deliveryRuntime.d.ts +34 -0
  45. package/dist/client/deliveryRuntimeWidget.d.ts +37 -0
  46. package/dist/client/duplex.d.ts +1 -1
  47. package/dist/client/htmxBootstrap.js +747 -15
  48. package/dist/client/index.d.ts +72 -0
  49. package/dist/client/index.js +5371 -10
  50. package/dist/client/liveOps.d.ts +22 -0
  51. package/dist/client/liveOpsWidget.d.ts +23 -0
  52. package/dist/client/liveTurnLatency.d.ts +41 -0
  53. package/dist/client/opsActionCenter.d.ts +54 -0
  54. package/dist/client/opsActionCenterWidget.d.ts +29 -0
  55. package/dist/client/opsActionHistory.d.ts +19 -0
  56. package/dist/client/opsActionHistoryWidget.d.ts +11 -0
  57. package/dist/client/opsStatus.d.ts +19 -0
  58. package/dist/client/opsStatusWidget.d.ts +40 -0
  59. package/dist/client/platformCoverage.d.ts +19 -0
  60. package/dist/client/platformCoverageWidget.d.ts +37 -0
  61. package/dist/client/proofTrends.d.ts +19 -0
  62. package/dist/client/proofTrendsWidget.d.ts +37 -0
  63. package/dist/client/providerCapabilities.d.ts +19 -0
  64. package/dist/client/providerCapabilitiesWidget.d.ts +32 -0
  65. package/dist/client/providerContracts.d.ts +19 -0
  66. package/dist/client/providerContractsWidget.d.ts +37 -0
  67. package/dist/client/providerSimulationControls.d.ts +33 -0
  68. package/dist/client/providerSimulationControlsWidget.d.ts +20 -0
  69. package/dist/client/providerStatus.d.ts +19 -0
  70. package/dist/client/providerStatusWidget.d.ts +32 -0
  71. package/dist/client/readinessFailures.d.ts +19 -0
  72. package/dist/client/readinessFailuresWidget.d.ts +42 -0
  73. package/dist/client/routingStatus.d.ts +19 -0
  74. package/dist/client/routingStatusWidget.d.ts +28 -0
  75. package/dist/client/traceTimeline.d.ts +19 -0
  76. package/dist/client/traceTimelineWidget.d.ts +36 -0
  77. package/dist/client/turnLatency.d.ts +22 -0
  78. package/dist/client/turnLatencyWidget.d.ts +33 -0
  79. package/dist/client/turnQuality.d.ts +19 -0
  80. package/dist/client/turnQualityWidget.d.ts +32 -0
  81. package/dist/client/workflowStatus.d.ts +19 -0
  82. package/dist/dataControl.d.ts +180 -0
  83. package/dist/deliveryRuntime.d.ts +158 -0
  84. package/dist/deliverySinkRoutes.d.ts +117 -0
  85. package/dist/demoReadyRoutes.d.ts +98 -0
  86. package/dist/diagnosticsRoutes.d.ts +44 -0
  87. package/dist/evalRoutes.d.ts +219 -0
  88. package/dist/fileStore.d.ts +17 -2
  89. package/dist/guardrails.d.ts +128 -0
  90. package/dist/handoff.d.ts +54 -0
  91. package/dist/handoffHealth.d.ts +94 -0
  92. package/dist/incidentBundle.d.ts +116 -0
  93. package/dist/index.d.ts +151 -10
  94. package/dist/index.js +28252 -3426
  95. package/dist/latencySlo.d.ts +56 -0
  96. package/dist/liveLatency.d.ts +78 -0
  97. package/dist/liveOps.d.ts +190 -0
  98. package/dist/modelAdapters.d.ts +151 -0
  99. package/dist/observabilityExport.d.ts +481 -0
  100. package/dist/openaiRealtime.d.ts +27 -0
  101. package/dist/openaiTTS.d.ts +18 -0
  102. package/dist/operationsRecord.d.ts +254 -0
  103. package/dist/opsActionAuditRoutes.d.ts +99 -0
  104. package/dist/opsConsoleRoutes.d.ts +80 -0
  105. package/dist/opsRecovery.d.ts +137 -0
  106. package/dist/opsStatus.d.ts +76 -0
  107. package/dist/opsStatusRoutes.d.ts +33 -0
  108. package/dist/opsWebhook.d.ts +126 -0
  109. package/dist/outcomeContract.d.ts +146 -0
  110. package/dist/phoneAgent.d.ts +139 -0
  111. package/dist/phoneAgentProductionSmoke.d.ts +115 -0
  112. package/dist/platformCoverage.d.ts +91 -0
  113. package/dist/postCallAnalysis.d.ts +98 -0
  114. package/dist/postgresStore.d.ts +13 -2
  115. package/dist/productionReadiness.d.ts +559 -0
  116. package/dist/proofTrends.d.ts +133 -0
  117. package/dist/providerAdapters.d.ts +48 -0
  118. package/dist/providerCapabilities.d.ts +92 -0
  119. package/dist/providerDecisionTraces.d.ts +130 -0
  120. package/dist/providerHealth.d.ts +79 -0
  121. package/dist/providerOrchestration.d.ts +109 -0
  122. package/dist/providerRoutingContract.d.ts +71 -0
  123. package/dist/providerSlo.d.ts +142 -0
  124. package/dist/providerStackRecommendations.d.ts +187 -0
  125. package/dist/qualityRoutes.d.ts +76 -0
  126. package/dist/queue.d.ts +61 -0
  127. package/dist/react/VoiceAgentSquadStatus.d.ts +5 -0
  128. package/dist/react/VoiceDeliveryRuntime.d.ts +7 -0
  129. package/dist/react/VoiceOpsActionCenter.d.ts +5 -0
  130. package/dist/react/VoiceOpsStatus.d.ts +6 -0
  131. package/dist/react/VoicePlatformCoverage.d.ts +6 -0
  132. package/dist/react/VoiceProofTrends.d.ts +6 -0
  133. package/dist/react/VoiceProviderCapabilities.d.ts +6 -0
  134. package/dist/react/VoiceProviderContracts.d.ts +6 -0
  135. package/dist/react/VoiceProviderSimulationControls.d.ts +5 -0
  136. package/dist/react/VoiceProviderStatus.d.ts +6 -0
  137. package/dist/react/VoiceReadinessFailures.d.ts +6 -0
  138. package/dist/react/VoiceRoutingStatus.d.ts +6 -0
  139. package/dist/react/VoiceTraceTimeline.d.ts +6 -0
  140. package/dist/react/VoiceTurnLatency.d.ts +6 -0
  141. package/dist/react/VoiceTurnQuality.d.ts +6 -0
  142. package/dist/react/index.d.ts +33 -0
  143. package/dist/react/index.js +5188 -14
  144. package/dist/react/useVoiceAgentSquadStatus.d.ts +8 -0
  145. package/dist/react/useVoiceCampaignDialerProof.d.ts +10 -0
  146. package/dist/react/useVoiceController.d.ts +3 -0
  147. package/dist/react/useVoiceDeliveryRuntime.d.ts +13 -0
  148. package/dist/react/useVoiceLiveOps.d.ts +9 -0
  149. package/dist/react/useVoiceOpsActionCenter.d.ts +11 -0
  150. package/dist/react/useVoiceOpsStatus.d.ts +8 -0
  151. package/dist/react/useVoicePlatformCoverage.d.ts +8 -0
  152. package/dist/react/useVoiceProofTrends.d.ts +8 -0
  153. package/dist/react/useVoiceProviderCapabilities.d.ts +8 -0
  154. package/dist/react/useVoiceProviderContracts.d.ts +8 -0
  155. package/dist/react/useVoiceProviderSimulationControls.d.ts +10 -0
  156. package/dist/react/useVoiceProviderStatus.d.ts +8 -0
  157. package/dist/react/useVoiceReadinessFailures.d.ts +8 -0
  158. package/dist/react/useVoiceRoutingStatus.d.ts +8 -0
  159. package/dist/react/useVoiceStream.d.ts +3 -0
  160. package/dist/react/useVoiceTraceTimeline.d.ts +8 -0
  161. package/dist/react/useVoiceTurnLatency.d.ts +9 -0
  162. package/dist/react/useVoiceTurnQuality.d.ts +8 -0
  163. package/dist/react/useVoiceWorkflowStatus.d.ts +8 -0
  164. package/dist/readinessProfiles.d.ts +38 -0
  165. package/dist/reconnectContract.d.ts +88 -0
  166. package/dist/resilienceRoutes.d.ts +143 -0
  167. package/dist/sessionReplay.d.ts +187 -0
  168. package/dist/simulationSuite.d.ts +143 -0
  169. package/dist/sloCalibration.d.ts +185 -0
  170. package/dist/sqliteStore.d.ts +13 -2
  171. package/dist/svelte/createVoiceAgentSquadStatus.d.ts +9 -0
  172. package/dist/svelte/createVoiceCampaignDialerProof.d.ts +9 -0
  173. package/dist/svelte/createVoiceDeliveryRuntime.d.ts +11 -0
  174. package/dist/svelte/createVoiceLiveOps.d.ts +13 -0
  175. package/dist/svelte/createVoiceOpsActionCenter.d.ts +10 -0
  176. package/dist/svelte/createVoiceOpsStatus.d.ts +9 -0
  177. package/dist/svelte/createVoicePlatformCoverage.d.ts +7 -0
  178. package/dist/svelte/createVoiceProofTrends.d.ts +7 -0
  179. package/dist/svelte/createVoiceProviderCapabilities.d.ts +10 -0
  180. package/dist/svelte/createVoiceProviderContracts.d.ts +10 -0
  181. package/dist/svelte/createVoiceProviderSimulationControls.d.ts +11 -0
  182. package/dist/svelte/createVoiceProviderStatus.d.ts +10 -0
  183. package/dist/svelte/createVoiceReadinessFailures.d.ts +7 -0
  184. package/dist/svelte/createVoiceRoutingStatus.d.ts +10 -0
  185. package/dist/svelte/createVoiceTraceTimeline.d.ts +10 -0
  186. package/dist/svelte/createVoiceTurnLatency.d.ts +11 -0
  187. package/dist/svelte/createVoiceTurnQuality.d.ts +10 -0
  188. package/dist/svelte/createVoiceWorkflowStatus.d.ts +8 -0
  189. package/dist/svelte/index.d.ts +18 -0
  190. package/dist/svelte/index.js +5036 -407
  191. package/dist/telephony/contract.d.ts +61 -0
  192. package/dist/telephony/matrix.d.ts +97 -0
  193. package/dist/telephony/plivo.d.ts +303 -0
  194. package/dist/telephony/security.d.ts +182 -0
  195. package/dist/telephony/telnyx.d.ts +291 -0
  196. package/dist/telephony/twilio.d.ts +135 -2
  197. package/dist/telephonyOutcome.d.ts +273 -0
  198. package/dist/testing/index.d.ts +2 -0
  199. package/dist/testing/index.js +3206 -180
  200. package/dist/testing/ioProviderSimulator.d.ts +41 -0
  201. package/dist/testing/providerSimulator.d.ts +44 -0
  202. package/dist/toolContract.d.ts +161 -0
  203. package/dist/toolRuntime.d.ts +50 -0
  204. package/dist/trace.d.ts +19 -1
  205. package/dist/traceDeliveryRoutes.d.ts +86 -0
  206. package/dist/traceTimeline.d.ts +97 -0
  207. package/dist/turnLatency.d.ts +95 -0
  208. package/dist/turnQuality.d.ts +94 -0
  209. package/dist/types.d.ts +180 -4
  210. package/dist/voiceMonitoring.d.ts +444 -0
  211. package/dist/vue/VoiceDeliveryRuntime.d.ts +30 -0
  212. package/dist/vue/VoiceOpsActionCenter.d.ts +13 -0
  213. package/dist/vue/VoiceOpsStatus.d.ts +30 -0
  214. package/dist/vue/VoicePlatformCoverage.d.ts +23 -0
  215. package/dist/vue/VoiceProofTrends.d.ts +21 -0
  216. package/dist/vue/VoiceProviderCapabilities.d.ts +51 -0
  217. package/dist/vue/VoiceProviderContracts.d.ts +21 -0
  218. package/dist/vue/VoiceProviderSimulationControls.d.ts +88 -0
  219. package/dist/vue/VoiceProviderStatus.d.ts +51 -0
  220. package/dist/vue/VoiceReadinessFailures.d.ts +21 -0
  221. package/dist/vue/VoiceRoutingStatus.d.ts +51 -0
  222. package/dist/vue/VoiceTurnLatency.d.ts +69 -0
  223. package/dist/vue/VoiceTurnQuality.d.ts +51 -0
  224. package/dist/vue/index.d.ts +31 -0
  225. package/dist/vue/index.js +4962 -31
  226. package/dist/vue/useVoiceAgentSquadStatus.d.ts +9 -0
  227. package/dist/vue/useVoiceCampaignDialerProof.d.ts +11 -0
  228. package/dist/vue/useVoiceController.d.ts +2 -1
  229. package/dist/vue/useVoiceDeliveryRuntime.d.ts +13 -0
  230. package/dist/vue/useVoiceLiveOps.d.ts +9 -0
  231. package/dist/vue/useVoiceOpsActionCenter.d.ts +11 -0
  232. package/dist/vue/useVoiceOpsStatus.d.ts +9 -0
  233. package/dist/vue/useVoicePlatformCoverage.d.ts +9 -0
  234. package/dist/vue/useVoiceProofTrends.d.ts +9 -0
  235. package/dist/vue/useVoiceProviderCapabilities.d.ts +9 -0
  236. package/dist/vue/useVoiceProviderContracts.d.ts +9 -0
  237. package/dist/vue/useVoiceProviderSimulationControls.d.ts +24 -0
  238. package/dist/vue/useVoiceProviderStatus.d.ts +9 -0
  239. package/dist/vue/useVoiceReadinessFailures.d.ts +775 -0
  240. package/dist/vue/useVoiceRoutingStatus.d.ts +8 -0
  241. package/dist/vue/useVoiceStream.d.ts +4 -1
  242. package/dist/vue/useVoiceTraceTimeline.d.ts +9 -0
  243. package/dist/vue/useVoiceTurnLatency.d.ts +10 -0
  244. package/dist/vue/useVoiceTurnQuality.d.ts +9 -0
  245. package/dist/vue/useVoiceWorkflowStatus.d.ts +9 -0
  246. package/dist/workflowContract.d.ts +91 -0
  247. package/package.json +1 -1
@@ -69,1226 +69,3766 @@ var __decorateElement = (array, flags, name, decorators, target, extra) => {
69
69
  return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
70
70
  };
71
71
 
72
- // src/angular/voice-stream.service.ts
72
+ // src/angular/voice-ops-status.service.ts
73
73
  import { computed, Injectable, signal } from "@angular/core";
74
74
 
75
- // src/client/actions.ts
76
- var normalizeErrorMessage = (value) => {
77
- if (typeof value === "string" && value.trim()) {
78
- return value;
79
- }
80
- if (value instanceof Error && value.message.trim()) {
81
- return value.message;
75
+ // src/client/opsStatus.ts
76
+ var fetchVoiceOpsStatus = async (path = "/api/voice/ops-status", options = {}) => {
77
+ const fetchImpl = options.fetch ?? globalThis.fetch;
78
+ const response = await fetchImpl(path);
79
+ if (!response.ok) {
80
+ throw new Error(`Voice ops status failed: HTTP ${response.status}`);
82
81
  }
83
- if (value && typeof value === "object") {
84
- const record = value;
85
- for (const key of ["message", "reason", "description"]) {
86
- const candidate = record[key];
87
- if (typeof candidate === "string" && candidate.trim()) {
88
- return candidate;
89
- }
90
- }
91
- if ("error" in record) {
92
- return normalizeErrorMessage(record.error);
82
+ return await response.json();
83
+ };
84
+ var createVoiceOpsStatusStore = (path = "/api/voice/ops-status", options = {}) => {
85
+ const listeners = new Set;
86
+ let closed = false;
87
+ let timer;
88
+ let snapshot = {
89
+ error: null,
90
+ isLoading: false
91
+ };
92
+ const emit = () => {
93
+ for (const listener of listeners) {
94
+ listener();
93
95
  }
94
- if ("cause" in record) {
95
- return normalizeErrorMessage(record.cause);
96
+ };
97
+ const refresh = async () => {
98
+ if (closed) {
99
+ return snapshot.report;
96
100
  }
101
+ snapshot = {
102
+ ...snapshot,
103
+ error: null,
104
+ isLoading: true
105
+ };
106
+ emit();
97
107
  try {
98
- return JSON.stringify(value);
99
- } catch {}
100
- }
101
- return "Unexpected error";
102
- };
103
- var serverMessageToAction = (message) => {
104
- switch (message.type) {
105
- case "audio":
106
- return {
107
- chunk: Uint8Array.from(atob(message.chunkBase64), (char) => char.charCodeAt(0)),
108
- format: message.format,
109
- receivedAt: message.receivedAt,
110
- turnId: message.turnId,
111
- type: "audio"
112
- };
113
- case "assistant":
114
- return {
115
- text: message.text,
116
- type: "assistant"
117
- };
118
- case "complete":
119
- return {
120
- sessionId: message.sessionId,
121
- type: "complete"
122
- };
123
- case "error":
124
- return {
125
- message: normalizeErrorMessage(message.message),
126
- type: "error"
127
- };
128
- case "final":
129
- return {
130
- transcript: message.transcript,
131
- type: "final"
132
- };
133
- case "partial":
134
- return {
135
- transcript: message.transcript,
136
- type: "partial"
108
+ const report = await fetchVoiceOpsStatus(path, options);
109
+ snapshot = {
110
+ error: null,
111
+ isLoading: false,
112
+ report,
113
+ updatedAt: Date.now()
137
114
  };
138
- case "session":
139
- return {
140
- sessionId: message.sessionId,
141
- scenarioId: message.scenarioId,
142
- status: message.status,
143
- type: "session"
144
- };
145
- case "turn":
146
- return {
147
- turn: message.turn,
148
- type: "turn"
115
+ emit();
116
+ return report;
117
+ } catch (error) {
118
+ snapshot = {
119
+ ...snapshot,
120
+ error: error instanceof Error ? error.message : String(error),
121
+ isLoading: false
149
122
  };
150
- default:
151
- return null;
123
+ emit();
124
+ throw error;
125
+ }
126
+ };
127
+ const close = () => {
128
+ closed = true;
129
+ if (timer) {
130
+ clearInterval(timer);
131
+ timer = undefined;
132
+ }
133
+ listeners.clear();
134
+ };
135
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
136
+ timer = setInterval(() => {
137
+ refresh().catch(() => {});
138
+ }, options.intervalMs);
152
139
  }
140
+ return {
141
+ close,
142
+ getServerSnapshot: () => snapshot,
143
+ getSnapshot: () => snapshot,
144
+ refresh,
145
+ subscribe: (listener) => {
146
+ listeners.add(listener);
147
+ return () => {
148
+ listeners.delete(listener);
149
+ };
150
+ }
151
+ };
153
152
  };
154
153
 
155
- // src/client/connection.ts
156
- var WS_OPEN = 1;
157
- var WS_CLOSED = 3;
158
- var WS_NORMAL_CLOSURE = 1000;
159
- var DEFAULT_MAX_RECONNECT_ATTEMPTS = 10;
160
- var DEFAULT_PING_INTERVAL = 30000;
161
- var RECONNECT_DELAY_MS = 500;
162
- var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
163
- var noop = () => {};
164
- var noopUnsubscribe = () => noop;
165
- var NOOP_CONNECTION = {
166
- start: () => {},
167
- close: noop,
168
- endTurn: noop,
169
- getReadyState: () => WS_CLOSED,
170
- getScenarioId: () => "",
171
- getSessionId: () => "",
172
- send: noop,
173
- sendAudio: noop,
174
- subscribe: noopUnsubscribe
175
- };
176
- var createSessionId = () => crypto.randomUUID();
177
- var buildWsUrl = (path, sessionId, scenarioId) => {
178
- const { hostname, port, protocol } = window.location;
179
- const wsProtocol = protocol === "https:" ? "wss:" : "ws:";
180
- const portSuffix = port ? `:${port}` : "";
181
- const url = new URL(`${wsProtocol}//${hostname}${portSuffix}${path}`);
182
- url.searchParams.set("sessionId", sessionId);
183
- if (scenarioId) {
184
- url.searchParams.set(DEFAULT_SCENARIO_QUERY_PARAM, scenarioId);
154
+ // src/angular/voice-ops-status.service.ts
155
+ var _dec = [
156
+ Injectable({ providedIn: "root" })
157
+ ];
158
+ var _init = __decoratorStart(undefined);
159
+
160
+ class VoiceOpsStatusService {
161
+ connect(path = "/api/voice/ops-status", options = {}) {
162
+ const store = createVoiceOpsStatusStore(path, options);
163
+ const errorSignal = signal(null);
164
+ const isLoadingSignal = signal(false);
165
+ const reportSignal = signal(undefined);
166
+ const updatedAtSignal = signal(undefined);
167
+ const sync = () => {
168
+ const snapshot = store.getSnapshot();
169
+ errorSignal.set(snapshot.error);
170
+ isLoadingSignal.set(snapshot.isLoading);
171
+ reportSignal.set(snapshot.report);
172
+ updatedAtSignal.set(snapshot.updatedAt);
173
+ };
174
+ const unsubscribe = store.subscribe(sync);
175
+ sync();
176
+ if (typeof window !== "undefined") {
177
+ store.refresh().catch(() => {});
178
+ }
179
+ return {
180
+ close: () => {
181
+ unsubscribe();
182
+ store.close();
183
+ },
184
+ error: computed(() => errorSignal()),
185
+ isLoading: computed(() => isLoadingSignal()),
186
+ refresh: store.refresh,
187
+ report: computed(() => reportSignal()),
188
+ updatedAt: computed(() => updatedAtSignal())
189
+ };
185
190
  }
186
- return url.toString();
187
- };
188
- var isVoiceServerMessage = (value) => {
189
- if (!value || typeof value !== "object" || !("type" in value)) {
190
- return false;
191
+ }
192
+ VoiceOpsStatusService = __decorateElement(_init, 0, "VoiceOpsStatusService", _dec, VoiceOpsStatusService);
193
+ __runInitializers(_init, 1, VoiceOpsStatusService);
194
+ __decoratorMetadata(_init, VoiceOpsStatusService);
195
+ let _VoiceOpsStatusService = VoiceOpsStatusService;
196
+ // src/angular/voice-platform-coverage.service.ts
197
+ import { computed as computed2, Injectable as Injectable2, signal as signal2 } from "@angular/core";
198
+
199
+ // src/client/platformCoverage.ts
200
+ var fetchVoicePlatformCoverage = async (path = "/api/voice/platform-coverage", options = {}) => {
201
+ const fetchImpl = options.fetch ?? globalThis.fetch;
202
+ const response = await fetchImpl(path);
203
+ if (!response.ok) {
204
+ throw new Error(`Voice platform coverage failed: HTTP ${response.status}`);
191
205
  }
192
- switch (value.type) {
193
- case "audio":
194
- case "assistant":
195
- case "complete":
196
- case "error":
197
- case "final":
198
- case "partial":
199
- case "pong":
200
- case "session":
201
- case "turn":
202
- return true;
203
- default:
204
- return false;
206
+ return await response.json();
207
+ };
208
+ var createVoicePlatformCoverageStore = (path = "/api/voice/platform-coverage", options = {}) => {
209
+ const listeners = new Set;
210
+ let closed = false;
211
+ let timer;
212
+ let snapshot = {
213
+ error: null,
214
+ isLoading: false
215
+ };
216
+ const emit = () => {
217
+ for (const listener of listeners) {
218
+ listener();
219
+ }
220
+ };
221
+ const refresh = async () => {
222
+ if (closed) {
223
+ return snapshot.report;
224
+ }
225
+ snapshot = {
226
+ ...snapshot,
227
+ error: null,
228
+ isLoading: true
229
+ };
230
+ emit();
231
+ try {
232
+ const report = await fetchVoicePlatformCoverage(path, options);
233
+ snapshot = {
234
+ error: null,
235
+ isLoading: false,
236
+ report,
237
+ updatedAt: Date.now()
238
+ };
239
+ emit();
240
+ return report;
241
+ } catch (error) {
242
+ snapshot = {
243
+ ...snapshot,
244
+ error: error instanceof Error ? error.message : String(error),
245
+ isLoading: false
246
+ };
247
+ emit();
248
+ throw error;
249
+ }
250
+ };
251
+ const close = () => {
252
+ closed = true;
253
+ if (timer) {
254
+ clearInterval(timer);
255
+ timer = undefined;
256
+ }
257
+ listeners.clear();
258
+ };
259
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
260
+ timer = setInterval(() => {
261
+ refresh().catch(() => {});
262
+ }, options.intervalMs);
205
263
  }
264
+ return {
265
+ close,
266
+ getServerSnapshot: () => snapshot,
267
+ getSnapshot: () => snapshot,
268
+ refresh,
269
+ subscribe: (listener) => {
270
+ listeners.add(listener);
271
+ return () => {
272
+ listeners.delete(listener);
273
+ };
274
+ }
275
+ };
206
276
  };
207
- var parseServerMessage = (event) => {
208
- if (typeof event.data !== "string") {
209
- return null;
277
+
278
+ // src/angular/voice-platform-coverage.service.ts
279
+ var _dec = [
280
+ Injectable2({ providedIn: "root" })
281
+ ];
282
+ var _init = __decoratorStart(undefined);
283
+
284
+ class VoicePlatformCoverageService {
285
+ connect(path = "/api/voice/platform-coverage", options = {}) {
286
+ const store = createVoicePlatformCoverageStore(path, options);
287
+ const errorSignal = signal2(null);
288
+ const isLoadingSignal = signal2(false);
289
+ const reportSignal = signal2(undefined);
290
+ const updatedAtSignal = signal2(undefined);
291
+ const sync = () => {
292
+ const snapshot = store.getSnapshot();
293
+ errorSignal.set(snapshot.error);
294
+ isLoadingSignal.set(snapshot.isLoading);
295
+ reportSignal.set(snapshot.report);
296
+ updatedAtSignal.set(snapshot.updatedAt);
297
+ };
298
+ const unsubscribe = store.subscribe(sync);
299
+ sync();
300
+ if (typeof window !== "undefined") {
301
+ store.refresh().catch(() => {});
302
+ }
303
+ return {
304
+ close: () => {
305
+ unsubscribe();
306
+ store.close();
307
+ },
308
+ error: computed2(() => errorSignal()),
309
+ isLoading: computed2(() => isLoadingSignal()),
310
+ refresh: store.refresh,
311
+ report: computed2(() => reportSignal()),
312
+ updatedAt: computed2(() => updatedAtSignal())
313
+ };
210
314
  }
211
- try {
212
- const parsed = JSON.parse(event.data);
213
- return isVoiceServerMessage(parsed) ? parsed : null;
214
- } catch {
215
- return null;
315
+ }
316
+ VoicePlatformCoverageService = __decorateElement(_init, 0, "VoicePlatformCoverageService", _dec, VoicePlatformCoverageService);
317
+ __runInitializers(_init, 1, VoicePlatformCoverageService);
318
+ __decoratorMetadata(_init, VoicePlatformCoverageService);
319
+ let _VoicePlatformCoverageService = VoicePlatformCoverageService;
320
+ // src/angular/voice-proof-trends.service.ts
321
+ import { computed as computed3, Injectable as Injectable3, signal as signal3 } from "@angular/core";
322
+
323
+ // src/client/proofTrends.ts
324
+ var fetchVoiceProofTrends = async (path = "/api/voice/proof-trends", options = {}) => {
325
+ const fetchImpl = options.fetch ?? globalThis.fetch;
326
+ const response = await fetchImpl(path);
327
+ if (!response.ok) {
328
+ throw new Error(`Voice proof trends failed: HTTP ${response.status}`);
216
329
  }
330
+ return await response.json();
217
331
  };
218
- var createVoiceConnection = (path, options = {}) => {
219
- if (typeof window === "undefined") {
220
- return NOOP_CONNECTION;
221
- }
332
+ var createVoiceProofTrendsStore = (path = "/api/voice/proof-trends", options = {}) => {
222
333
  const listeners = new Set;
223
- const shouldReconnect = options.reconnect !== false;
224
- const maxReconnectAttempts = options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;
225
- const pingInterval = options.pingInterval ?? DEFAULT_PING_INTERVAL;
226
- const state = {
227
- isConnected: false,
228
- pendingMessages: [],
229
- scenarioId: options.scenarioId ?? null,
230
- pingInterval: null,
231
- reconnectAttempts: 0,
232
- reconnectTimeout: null,
233
- sessionId: options.sessionId ?? createSessionId(),
234
- ws: null
334
+ let closed = false;
335
+ let timer;
336
+ let snapshot = {
337
+ error: null,
338
+ isLoading: false
235
339
  };
236
- const clearTimers = () => {
237
- if (state.pingInterval) {
238
- clearInterval(state.pingInterval);
239
- state.pingInterval = null;
340
+ const emit = () => {
341
+ for (const listener of listeners) {
342
+ listener();
240
343
  }
241
- if (state.reconnectTimeout) {
242
- clearTimeout(state.reconnectTimeout);
243
- state.reconnectTimeout = null;
344
+ };
345
+ const refresh = async () => {
346
+ if (closed) {
347
+ return snapshot.report;
348
+ }
349
+ snapshot = {
350
+ ...snapshot,
351
+ error: null,
352
+ isLoading: true
353
+ };
354
+ emit();
355
+ try {
356
+ const report = await fetchVoiceProofTrends(path, options);
357
+ snapshot = {
358
+ error: null,
359
+ isLoading: false,
360
+ report,
361
+ updatedAt: Date.now()
362
+ };
363
+ emit();
364
+ return report;
365
+ } catch (error) {
366
+ snapshot = {
367
+ ...snapshot,
368
+ error: error instanceof Error ? error.message : String(error),
369
+ isLoading: false
370
+ };
371
+ emit();
372
+ throw error;
244
373
  }
245
374
  };
246
- const flushPendingMessages = () => {
247
- if (state.ws?.readyState !== WS_OPEN) {
248
- return;
375
+ const close = () => {
376
+ closed = true;
377
+ if (timer) {
378
+ clearInterval(timer);
379
+ timer = undefined;
249
380
  }
250
- while (state.pendingMessages.length > 0) {
251
- const next = state.pendingMessages.shift();
252
- if (next !== undefined) {
253
- state.ws.send(next);
254
- }
381
+ listeners.clear();
382
+ };
383
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
384
+ timer = setInterval(() => {
385
+ refresh().catch(() => {});
386
+ }, options.intervalMs);
387
+ }
388
+ return {
389
+ close,
390
+ getServerSnapshot: () => snapshot,
391
+ getSnapshot: () => snapshot,
392
+ refresh,
393
+ subscribe: (listener) => {
394
+ listeners.add(listener);
395
+ return () => {
396
+ listeners.delete(listener);
397
+ };
255
398
  }
256
399
  };
257
- const scheduleReconnect = () => {
258
- state.reconnectAttempts += 1;
259
- state.reconnectTimeout = setTimeout(() => {
260
- if (state.reconnectAttempts > maxReconnectAttempts) {
261
- return;
262
- }
263
- connect();
264
- }, RECONNECT_DELAY_MS);
265
- };
266
- const connect = () => {
267
- const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
268
- ws.binaryType = "arraybuffer";
269
- ws.onopen = () => {
270
- state.isConnected = true;
271
- state.reconnectAttempts = 0;
272
- flushPendingMessages();
273
- listeners.forEach((listener) => listener({
274
- scenarioId: state.scenarioId ?? undefined,
275
- sessionId: state.sessionId,
276
- status: "active",
277
- type: "session"
278
- }));
279
- state.pingInterval = setInterval(() => {
280
- if (ws.readyState === WS_OPEN) {
281
- ws.send(JSON.stringify({ type: "ping" }));
282
- }
283
- }, pingInterval);
284
- };
285
- ws.onmessage = (event) => {
286
- const parsed = parseServerMessage(event);
287
- if (!parsed) {
288
- return;
289
- }
290
- if (parsed.type === "session") {
291
- state.sessionId = parsed.sessionId;
292
- state.scenarioId = parsed.scenarioId ?? state.scenarioId;
293
- }
294
- listeners.forEach((listener) => listener(parsed));
400
+ };
401
+
402
+ // src/angular/voice-proof-trends.service.ts
403
+ var _dec = [
404
+ Injectable3({ providedIn: "root" })
405
+ ];
406
+ var _init = __decoratorStart(undefined);
407
+
408
+ class VoiceProofTrendsService {
409
+ connect(path = "/api/voice/proof-trends", options = {}) {
410
+ const store = createVoiceProofTrendsStore(path, options);
411
+ const errorSignal = signal3(null);
412
+ const isLoadingSignal = signal3(false);
413
+ const reportSignal = signal3(undefined);
414
+ const updatedAtSignal = signal3(undefined);
415
+ const sync = () => {
416
+ const snapshot = store.getSnapshot();
417
+ errorSignal.set(snapshot.error);
418
+ isLoadingSignal.set(snapshot.isLoading);
419
+ reportSignal.set(snapshot.report);
420
+ updatedAtSignal.set(snapshot.updatedAt);
295
421
  };
296
- ws.onclose = (event) => {
297
- state.isConnected = false;
298
- clearTimers();
299
- const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && state.reconnectAttempts < maxReconnectAttempts;
300
- if (reconnectable) {
301
- scheduleReconnect();
302
- }
422
+ const unsubscribe = store.subscribe(sync);
423
+ sync();
424
+ if (typeof window !== "undefined") {
425
+ store.refresh().catch(() => {});
426
+ }
427
+ return {
428
+ close: () => {
429
+ unsubscribe();
430
+ store.close();
431
+ },
432
+ error: computed3(() => errorSignal()),
433
+ isLoading: computed3(() => isLoadingSignal()),
434
+ refresh: store.refresh,
435
+ report: computed3(() => reportSignal()),
436
+ updatedAt: computed3(() => updatedAtSignal())
303
437
  };
304
- state.ws = ws;
438
+ }
439
+ }
440
+ VoiceProofTrendsService = __decorateElement(_init, 0, "VoiceProofTrendsService", _dec, VoiceProofTrendsService);
441
+ __runInitializers(_init, 1, VoiceProofTrendsService);
442
+ __decoratorMetadata(_init, VoiceProofTrendsService);
443
+ let _VoiceProofTrendsService = VoiceProofTrendsService;
444
+ // src/angular/voice-readiness-failures.service.ts
445
+ import { computed as computed4, Injectable as Injectable4, signal as signal4 } from "@angular/core";
446
+
447
+ // src/client/readinessFailures.ts
448
+ var fetchVoiceReadinessFailures = async (path = "/api/production-readiness", options = {}) => {
449
+ const fetchImpl = options.fetch ?? globalThis.fetch;
450
+ const response = await fetchImpl(path);
451
+ if (!response.ok) {
452
+ throw new Error(`Voice readiness failed: HTTP ${response.status}`);
453
+ }
454
+ return await response.json();
455
+ };
456
+ var createVoiceReadinessFailuresStore = (path = "/api/production-readiness", options = {}) => {
457
+ const listeners = new Set;
458
+ let closed = false;
459
+ let timer;
460
+ let snapshot = {
461
+ error: null,
462
+ isLoading: false
305
463
  };
306
- const sendSerialized = (value) => {
307
- if (state.ws?.readyState === WS_OPEN) {
308
- state.ws.send(value);
309
- return;
464
+ const emit = () => {
465
+ for (const listener of listeners) {
466
+ listener();
310
467
  }
311
- state.pendingMessages.push(value);
312
468
  };
313
- const send = (message) => {
314
- sendSerialized(JSON.stringify(message));
469
+ const refresh = async () => {
470
+ if (closed) {
471
+ return snapshot.report;
472
+ }
473
+ snapshot = { ...snapshot, error: null, isLoading: true };
474
+ emit();
475
+ try {
476
+ const report = await fetchVoiceReadinessFailures(path, options);
477
+ snapshot = {
478
+ error: null,
479
+ isLoading: false,
480
+ report,
481
+ updatedAt: Date.now()
482
+ };
483
+ emit();
484
+ return report;
485
+ } catch (error) {
486
+ snapshot = {
487
+ ...snapshot,
488
+ error: error instanceof Error ? error.message : String(error),
489
+ isLoading: false
490
+ };
491
+ emit();
492
+ throw error;
493
+ }
315
494
  };
316
- const start = (input = {}) => {
317
- if (input.sessionId) {
318
- state.sessionId = input.sessionId;
495
+ const close = () => {
496
+ closed = true;
497
+ if (timer) {
498
+ clearInterval(timer);
499
+ timer = undefined;
319
500
  }
320
- if (input.scenarioId) {
321
- state.scenarioId = input.scenarioId;
501
+ listeners.clear();
502
+ };
503
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
504
+ timer = setInterval(() => {
505
+ refresh().catch(() => {});
506
+ }, options.intervalMs);
507
+ }
508
+ return {
509
+ close,
510
+ getServerSnapshot: () => snapshot,
511
+ getSnapshot: () => snapshot,
512
+ refresh,
513
+ subscribe: (listener) => {
514
+ listeners.add(listener);
515
+ return () => {
516
+ listeners.delete(listener);
517
+ };
322
518
  }
323
- send({
324
- type: "start",
325
- sessionId: state.sessionId,
326
- scenarioId: state.scenarioId ?? undefined
519
+ };
520
+ };
521
+
522
+ // src/angular/voice-readiness-failures.service.ts
523
+ var _dec = [
524
+ Injectable4({ providedIn: "root" })
525
+ ];
526
+ var _init = __decoratorStart(undefined);
527
+
528
+ class VoiceReadinessFailuresService {
529
+ connect(path = "/api/production-readiness", options = {}) {
530
+ const store = createVoiceReadinessFailuresStore(path, options);
531
+ const errorSignal = signal4(null);
532
+ const isLoadingSignal = signal4(false);
533
+ const reportSignal = signal4(undefined);
534
+ const updatedAtSignal = signal4(undefined);
535
+ const sync = () => {
536
+ const snapshot = store.getSnapshot();
537
+ errorSignal.set(snapshot.error);
538
+ isLoadingSignal.set(snapshot.isLoading);
539
+ reportSignal.set(snapshot.report);
540
+ updatedAtSignal.set(snapshot.updatedAt);
541
+ };
542
+ const unsubscribe = store.subscribe(sync);
543
+ sync();
544
+ if (typeof window !== "undefined") {
545
+ store.refresh().catch(() => {});
546
+ }
547
+ return {
548
+ close: () => {
549
+ unsubscribe();
550
+ store.close();
551
+ },
552
+ error: computed4(() => errorSignal()),
553
+ explanations: computed4(() => reportSignal()?.checks.filter((check) => check.status !== "pass" && !!check.gateExplanation) ?? []),
554
+ isLoading: computed4(() => isLoadingSignal()),
555
+ refresh: store.refresh,
556
+ report: computed4(() => reportSignal()),
557
+ updatedAt: computed4(() => updatedAtSignal())
558
+ };
559
+ }
560
+ }
561
+ VoiceReadinessFailuresService = __decorateElement(_init, 0, "VoiceReadinessFailuresService", _dec, VoiceReadinessFailuresService);
562
+ __runInitializers(_init, 1, VoiceReadinessFailuresService);
563
+ __decoratorMetadata(_init, VoiceReadinessFailuresService);
564
+ let _VoiceReadinessFailuresService = VoiceReadinessFailuresService;
565
+ // src/angular/voice-ops-action-center.service.ts
566
+ import { computed as computed5, Injectable as Injectable5, signal as signal5 } from "@angular/core";
567
+
568
+ // src/client/opsActionCenter.ts
569
+ var recordVoiceOpsActionResult = async (result, options = {}) => {
570
+ if (options.auditPath === false) {
571
+ return;
572
+ }
573
+ const path = options.auditPath ?? "/api/voice/ops-actions/audit";
574
+ const fetchImpl = options.fetch ?? globalThis.fetch;
575
+ const response = await fetchImpl(path, {
576
+ body: JSON.stringify(result),
577
+ headers: {
578
+ "Content-Type": "application/json"
579
+ },
580
+ method: "POST"
581
+ });
582
+ if (!response.ok) {
583
+ throw new Error(`Voice ops action audit failed: HTTP ${response.status}`);
584
+ }
585
+ };
586
+ var createVoiceOpsActionCenterActions = (options = {}) => {
587
+ const deliveryRuntimePath = options.deliveryRuntimePath ?? "/api/voice-delivery-runtime";
588
+ const actions = [];
589
+ if (options.includeProductionReadiness !== false) {
590
+ actions.push({
591
+ description: "Refresh the production readiness report.",
592
+ id: "production-readiness",
593
+ label: "Refresh readiness",
594
+ method: "GET",
595
+ path: options.productionReadinessPath ?? "/api/production-readiness"
327
596
  });
597
+ }
598
+ if (options.includeDeliveryRuntime !== false) {
599
+ actions.push({
600
+ description: "Drain pending and failed audit/trace deliveries.",
601
+ id: "delivery-runtime.tick",
602
+ label: "Tick delivery workers",
603
+ method: "POST",
604
+ path: `${deliveryRuntimePath.replace(/\/$/, "")}/tick`
605
+ }, {
606
+ description: "Move reviewed dead letters back to live delivery queues.",
607
+ id: "delivery-runtime.requeue-dead-letters",
608
+ label: "Requeue dead letters",
609
+ method: "POST",
610
+ path: `${deliveryRuntimePath.replace(/\/$/, "")}/requeue-dead-letters`
611
+ });
612
+ }
613
+ if (options.includeTurnLatencyProof !== false) {
614
+ actions.push({
615
+ description: "Run the synthetic turn latency proof.",
616
+ id: "turn-latency.proof",
617
+ label: "Run latency proof",
618
+ method: "POST",
619
+ path: options.turnLatencyProofPath ?? "/api/turn-latency/proof"
620
+ });
621
+ }
622
+ if (options.includeProviderSimulation !== false) {
623
+ const pathPrefix = options.providerSimulationPathPrefix ?? "/api/stt-simulate";
624
+ for (const provider of options.providers ?? []) {
625
+ actions.push({
626
+ description: `Simulate ${provider} provider failure.`,
627
+ id: `provider.${provider}.failure`,
628
+ label: `Simulate ${provider} failure`,
629
+ method: "POST",
630
+ path: `${pathPrefix}/failure?provider=${encodeURIComponent(provider)}`
631
+ }, {
632
+ description: `Mark ${provider} provider recovered.`,
633
+ id: `provider.${provider}.recovery`,
634
+ label: `Recover ${provider}`,
635
+ method: "POST",
636
+ path: `${pathPrefix}/recovery?provider=${encodeURIComponent(provider)}`
637
+ });
638
+ }
639
+ }
640
+ return actions;
641
+ };
642
+ var runVoiceOpsAction = async (action, options = {}) => {
643
+ const fetchImpl = options.fetch ?? globalThis.fetch;
644
+ const response = await fetchImpl(action.path, {
645
+ method: action.method ?? "POST"
646
+ });
647
+ const body = await response.json().catch(() => null);
648
+ if (!response.ok) {
649
+ const message = body && typeof body === "object" && "error" in body ? String(body.error) : `Voice ops action "${action.id}" failed: HTTP ${response.status}`;
650
+ throw new Error(message);
651
+ }
652
+ return {
653
+ actionId: action.id,
654
+ body,
655
+ ok: response.ok,
656
+ ranAt: Date.now(),
657
+ status: response.status
328
658
  };
329
- const sendAudio = (audio) => {
330
- sendSerialized(audio);
659
+ };
660
+ var createVoiceOpsActionCenterStore = (options = {}) => {
661
+ const listeners = new Set;
662
+ let closed = false;
663
+ let timer;
664
+ let snapshot = {
665
+ actions: options.actions ?? createVoiceOpsActionCenterActions(),
666
+ error: null,
667
+ isRunning: false
331
668
  };
332
- const endTurn = () => {
333
- send({ type: "end_turn" });
669
+ const emit = () => {
670
+ for (const listener of listeners) {
671
+ listener();
672
+ }
673
+ };
674
+ const setActions = (actions) => {
675
+ snapshot = { ...snapshot, actions, updatedAt: Date.now() };
676
+ emit();
677
+ };
678
+ const run = async (actionId) => {
679
+ if (closed) {
680
+ return snapshot.lastResult;
681
+ }
682
+ const action = snapshot.actions.find((item) => item.id === actionId);
683
+ if (!action) {
684
+ throw new Error(`Voice ops action "${actionId}" is not configured.`);
685
+ }
686
+ if (action.disabled) {
687
+ throw new Error(`Voice ops action "${actionId}" is disabled.`);
688
+ }
689
+ snapshot = {
690
+ ...snapshot,
691
+ error: null,
692
+ isRunning: true,
693
+ runningActionId: action.id
694
+ };
695
+ emit();
696
+ try {
697
+ const result = await runVoiceOpsAction(action, options);
698
+ await options.onActionResult?.(result);
699
+ await recordVoiceOpsActionResult(result, options);
700
+ snapshot = {
701
+ ...snapshot,
702
+ error: null,
703
+ isRunning: false,
704
+ lastResult: result,
705
+ runningActionId: undefined,
706
+ updatedAt: Date.now()
707
+ };
708
+ emit();
709
+ return result;
710
+ } catch (error) {
711
+ const result = {
712
+ actionId: action.id,
713
+ body: null,
714
+ error: error instanceof Error ? error.message : String(error),
715
+ ok: false,
716
+ ranAt: Date.now(),
717
+ status: 0
718
+ };
719
+ await options.onActionResult?.(result);
720
+ await recordVoiceOpsActionResult(result, options).catch(() => {});
721
+ snapshot = {
722
+ ...snapshot,
723
+ error: error instanceof Error ? error.message : String(error),
724
+ isRunning: false,
725
+ runningActionId: undefined
726
+ };
727
+ emit();
728
+ throw error;
729
+ }
334
730
  };
335
731
  const close = () => {
336
- clearTimers();
337
- if (state.ws) {
338
- state.ws.close(WS_NORMAL_CLOSURE);
339
- state.ws = null;
732
+ closed = true;
733
+ if (timer) {
734
+ clearInterval(timer);
735
+ timer = undefined;
340
736
  }
341
- state.isConnected = false;
342
737
  listeners.clear();
343
738
  };
344
- const subscribe = (callback) => {
345
- listeners.add(callback);
346
- return () => {
347
- listeners.delete(callback);
739
+ if (options.intervalMs && options.intervalMs > 0) {
740
+ timer = setInterval(() => {
741
+ emit();
742
+ }, options.intervalMs);
743
+ }
744
+ return {
745
+ close,
746
+ getServerSnapshot: () => snapshot,
747
+ getSnapshot: () => snapshot,
748
+ run,
749
+ setActions,
750
+ subscribe: (listener) => {
751
+ listeners.add(listener);
752
+ return () => {
753
+ listeners.delete(listener);
754
+ };
755
+ }
756
+ };
757
+ };
758
+
759
+ // src/angular/voice-ops-action-center.service.ts
760
+ var _dec = [
761
+ Injectable5({ providedIn: "root" })
762
+ ];
763
+ var _init = __decoratorStart(undefined);
764
+
765
+ class VoiceOpsActionCenterService {
766
+ connect(options = {}) {
767
+ const store = createVoiceOpsActionCenterStore(options);
768
+ const actionsSignal = signal5([]);
769
+ const errorSignal = signal5(null);
770
+ const isRunningSignal = signal5(false);
771
+ const lastResultSignal = signal5(undefined);
772
+ const runningActionIdSignal = signal5(undefined);
773
+ const sync = () => {
774
+ const snapshot = store.getSnapshot();
775
+ actionsSignal.set(snapshot.actions);
776
+ errorSignal.set(snapshot.error);
777
+ isRunningSignal.set(snapshot.isRunning);
778
+ lastResultSignal.set(snapshot.lastResult);
779
+ runningActionIdSignal.set(snapshot.runningActionId);
780
+ };
781
+ const unsubscribe = store.subscribe(sync);
782
+ sync();
783
+ return {
784
+ actions: computed5(() => actionsSignal()),
785
+ close: () => {
786
+ unsubscribe();
787
+ store.close();
788
+ },
789
+ error: computed5(() => errorSignal()),
790
+ isRunning: computed5(() => isRunningSignal()),
791
+ lastResult: computed5(() => lastResultSignal()),
792
+ run: store.run,
793
+ runningActionId: computed5(() => runningActionIdSignal()),
794
+ setActions: store.setActions
348
795
  };
796
+ }
797
+ }
798
+ VoiceOpsActionCenterService = __decorateElement(_init, 0, "VoiceOpsActionCenterService", _dec, VoiceOpsActionCenterService);
799
+ __runInitializers(_init, 1, VoiceOpsActionCenterService);
800
+ __decoratorMetadata(_init, VoiceOpsActionCenterService);
801
+ let _VoiceOpsActionCenterService = VoiceOpsActionCenterService;
802
+ // src/angular/voice-live-ops.service.ts
803
+ import { computed as computed6, Injectable as Injectable6, signal as signal6 } from "@angular/core";
804
+
805
+ // src/client/liveOps.ts
806
+ var postVoiceLiveOpsAction = async (input, options = {}) => {
807
+ if (!input.sessionId) {
808
+ throw new Error("Start a voice session before running live ops actions.");
809
+ }
810
+ const fetchImpl = options.fetch ?? globalThis.fetch;
811
+ const response = await fetchImpl(options.actionPath ?? "/api/voice/live-ops/action", {
812
+ body: JSON.stringify(input),
813
+ headers: {
814
+ "Content-Type": "application/json"
815
+ },
816
+ method: "POST"
817
+ });
818
+ const payload = await response.json().catch(() => null);
819
+ if (!response.ok || !payload?.ok) {
820
+ const message = payload && typeof payload === "object" && "error" in payload ? String(payload.error) : `Voice live ops action failed: HTTP ${response.status}`;
821
+ throw new Error(message);
822
+ }
823
+ return payload;
824
+ };
825
+ var createVoiceLiveOpsStore = (options = {}) => {
826
+ const listeners = new Set;
827
+ let closed = false;
828
+ let snapshot = {
829
+ error: null,
830
+ isRunning: false
831
+ };
832
+ const emit = () => {
833
+ for (const listener of listeners) {
834
+ listener();
835
+ }
836
+ };
837
+ const run = async (input) => {
838
+ if (closed) {
839
+ return snapshot.lastResult;
840
+ }
841
+ snapshot = {
842
+ ...snapshot,
843
+ error: null,
844
+ isRunning: true,
845
+ runningAction: input.action
846
+ };
847
+ emit();
848
+ try {
849
+ const result = await postVoiceLiveOpsAction(input, options);
850
+ await options.onControl?.(result);
851
+ snapshot = {
852
+ ...snapshot,
853
+ error: null,
854
+ isRunning: false,
855
+ lastResult: result,
856
+ runningAction: undefined,
857
+ updatedAt: Date.now()
858
+ };
859
+ emit();
860
+ return result;
861
+ } catch (error) {
862
+ snapshot = {
863
+ ...snapshot,
864
+ error: error instanceof Error ? error.message : String(error),
865
+ isRunning: false,
866
+ runningAction: undefined,
867
+ updatedAt: Date.now()
868
+ };
869
+ emit();
870
+ throw error;
871
+ }
872
+ };
873
+ const close = () => {
874
+ closed = true;
875
+ listeners.clear();
349
876
  };
350
- connect();
351
877
  return {
352
- start,
353
878
  close,
354
- endTurn,
355
- getReadyState: () => state.ws?.readyState ?? WS_CLOSED,
356
- getScenarioId: () => state.scenarioId ?? "",
357
- getSessionId: () => state.sessionId,
358
- send,
359
- sendAudio,
360
- subscribe
879
+ getServerSnapshot: () => snapshot,
880
+ getSnapshot: () => snapshot,
881
+ run,
882
+ subscribe: (listener) => {
883
+ listeners.add(listener);
884
+ return () => {
885
+ listeners.delete(listener);
886
+ };
887
+ }
361
888
  };
362
889
  };
363
890
 
364
- // src/client/store.ts
365
- var createInitialState = () => ({
366
- assistantAudio: [],
367
- assistantTexts: [],
368
- error: null,
369
- isConnected: false,
370
- scenarioId: null,
371
- partial: "",
372
- sessionId: null,
373
- status: "idle",
374
- turns: []
375
- });
376
- var createVoiceStreamStore = () => {
377
- let state = createInitialState();
378
- const subscribers = new Set;
379
- const notify = () => {
380
- subscribers.forEach((subscriber) => subscriber());
891
+ // src/angular/voice-live-ops.service.ts
892
+ var _dec = [
893
+ Injectable6({ providedIn: "root" })
894
+ ];
895
+ var _init = __decoratorStart(undefined);
896
+
897
+ class VoiceLiveOpsService {
898
+ connect(options = {}) {
899
+ const store = createVoiceLiveOpsStore(options);
900
+ const errorSignal = signal6(null);
901
+ const isRunningSignal = signal6(false);
902
+ const lastResultSignal = signal6(undefined);
903
+ const runningActionSignal = signal6(undefined);
904
+ const sync = () => {
905
+ const snapshot = store.getSnapshot();
906
+ errorSignal.set(snapshot.error);
907
+ isRunningSignal.set(snapshot.isRunning);
908
+ lastResultSignal.set(snapshot.lastResult);
909
+ runningActionSignal.set(snapshot.runningAction);
910
+ };
911
+ const unsubscribe = store.subscribe(sync);
912
+ sync();
913
+ return {
914
+ close: () => {
915
+ unsubscribe();
916
+ store.close();
917
+ },
918
+ error: computed6(() => errorSignal()),
919
+ isRunning: computed6(() => isRunningSignal()),
920
+ lastResult: computed6(() => lastResultSignal()),
921
+ run: store.run,
922
+ runningAction: computed6(() => runningActionSignal())
923
+ };
924
+ }
925
+ }
926
+ VoiceLiveOpsService = __decorateElement(_init, 0, "VoiceLiveOpsService", _dec, VoiceLiveOpsService);
927
+ __runInitializers(_init, 1, VoiceLiveOpsService);
928
+ __decoratorMetadata(_init, VoiceLiveOpsService);
929
+ let _VoiceLiveOpsService = VoiceLiveOpsService;
930
+ // src/angular/voice-delivery-runtime.service.ts
931
+ import { computed as computed7, Injectable as Injectable7, signal as signal7 } from "@angular/core";
932
+
933
+ // src/client/deliveryRuntime.ts
934
+ var getDefaultActionPath = (path, action, options) => {
935
+ if (action === "tick") {
936
+ return options.tickPath ?? `${path.replace(/\/$/, "")}/tick`;
937
+ }
938
+ return options.requeueDeadLettersPath ?? `${path.replace(/\/$/, "")}/requeue-dead-letters`;
939
+ };
940
+ var fetchVoiceDeliveryRuntime = async (path = "/api/voice-delivery-runtime", options = {}) => {
941
+ const fetchImpl = options.fetch ?? globalThis.fetch;
942
+ const response = await fetchImpl(path);
943
+ if (!response.ok) {
944
+ throw new Error(`Voice delivery runtime failed: HTTP ${response.status}`);
945
+ }
946
+ return await response.json();
947
+ };
948
+ var runVoiceDeliveryRuntimeAction = async (action, path = "/api/voice-delivery-runtime", options = {}) => {
949
+ const fetchImpl = options.fetch ?? globalThis.fetch;
950
+ const response = await fetchImpl(getDefaultActionPath(path, action, options), {
951
+ method: "POST"
952
+ });
953
+ if (!response.ok) {
954
+ throw new Error(`Voice delivery runtime ${action} failed: HTTP ${response.status}`);
955
+ }
956
+ const body = await response.json();
957
+ return {
958
+ action,
959
+ result: body.result,
960
+ summary: body.summary,
961
+ updatedAt: Date.now()
381
962
  };
382
- const dispatch = (action) => {
383
- switch (action.type) {
384
- case "audio":
385
- state = {
386
- ...state,
387
- assistantAudio: [
388
- ...state.assistantAudio,
389
- {
390
- chunk: action.chunk,
391
- format: action.format,
392
- receivedAt: action.receivedAt,
393
- turnId: action.turnId
394
- }
395
- ]
396
- };
397
- break;
398
- case "assistant":
399
- state = {
400
- ...state,
401
- assistantTexts: [...state.assistantTexts, action.text]
402
- };
403
- break;
404
- case "complete":
405
- state = {
406
- ...state,
407
- sessionId: action.sessionId,
408
- status: "completed"
409
- };
410
- break;
411
- case "connected":
412
- state = {
413
- ...state,
414
- isConnected: true
415
- };
416
- break;
417
- case "disconnected":
418
- state = {
419
- ...state,
420
- isConnected: false
421
- };
422
- break;
423
- case "error":
424
- state = {
425
- ...state,
426
- error: action.message
427
- };
428
- break;
429
- case "final":
430
- state = {
431
- ...state,
432
- partial: action.transcript.text,
433
- turns: state.turns.map((turn) => turn)
434
- };
435
- break;
436
- case "partial":
437
- state = {
438
- ...state,
439
- partial: action.transcript.text
440
- };
441
- break;
442
- case "session":
443
- state = {
444
- ...state,
445
- error: null,
446
- scenarioId: action.scenarioId ?? state.scenarioId,
447
- isConnected: action.status === "active",
448
- sessionId: action.sessionId,
449
- status: action.status
450
- };
451
- break;
452
- case "turn":
453
- state = {
454
- ...state,
455
- partial: "",
456
- turns: [...state.turns, action.turn]
457
- };
458
- break;
963
+ };
964
+ var createVoiceDeliveryRuntimeStore = (path = "/api/voice-delivery-runtime", options = {}) => {
965
+ const listeners = new Set;
966
+ let closed = false;
967
+ let timer;
968
+ let snapshot = {
969
+ actionError: null,
970
+ actionStatus: "idle",
971
+ error: null,
972
+ isLoading: false
973
+ };
974
+ const emit = () => {
975
+ for (const listener of listeners) {
976
+ listener();
459
977
  }
460
- notify();
461
978
  };
462
- return {
463
- dispatch,
464
- getServerSnapshot: () => state,
465
- getSnapshot: () => state,
466
- subscribe: (subscriber) => {
467
- subscribers.add(subscriber);
468
- return () => {
469
- subscribers.delete(subscriber);
979
+ const refresh = async () => {
980
+ if (closed) {
981
+ return snapshot.report;
982
+ }
983
+ snapshot = {
984
+ ...snapshot,
985
+ error: null,
986
+ isLoading: true
987
+ };
988
+ emit();
989
+ try {
990
+ const report = await fetchVoiceDeliveryRuntime(path, options);
991
+ snapshot = {
992
+ ...snapshot,
993
+ error: null,
994
+ isLoading: false,
995
+ report,
996
+ updatedAt: Date.now()
997
+ };
998
+ emit();
999
+ return report;
1000
+ } catch (error) {
1001
+ snapshot = {
1002
+ ...snapshot,
1003
+ error: error instanceof Error ? error.message : String(error),
1004
+ isLoading: false
470
1005
  };
1006
+ emit();
1007
+ throw error;
471
1008
  }
472
1009
  };
473
- };
474
-
475
- // src/client/createVoiceStream.ts
476
- var createVoiceStream = (path, options = {}) => {
477
- const connection = createVoiceConnection(path, options);
478
- const store = createVoiceStreamStore();
479
- const subscribers = new Set;
480
- const start = (input) => Promise.resolve().then(() => {
481
- if (!input?.sessionId && !input?.scenarioId) {
482
- return;
1010
+ const runAction = async (action) => {
1011
+ if (closed) {
1012
+ return snapshot.lastAction;
1013
+ }
1014
+ snapshot = {
1015
+ ...snapshot,
1016
+ actionError: null,
1017
+ actionStatus: "running"
1018
+ };
1019
+ emit();
1020
+ try {
1021
+ const result = await runVoiceDeliveryRuntimeAction(action, path, options);
1022
+ snapshot = {
1023
+ ...snapshot,
1024
+ actionError: null,
1025
+ actionStatus: "completed",
1026
+ lastAction: result
1027
+ };
1028
+ emit();
1029
+ await refresh();
1030
+ return result;
1031
+ } catch (error) {
1032
+ snapshot = {
1033
+ ...snapshot,
1034
+ actionError: error instanceof Error ? error.message : String(error),
1035
+ actionStatus: "failed"
1036
+ };
1037
+ emit();
1038
+ throw error;
483
1039
  }
484
- connection.start(input);
485
- });
486
- const notify = () => {
487
- subscribers.forEach((subscriber) => subscriber());
488
1040
  };
489
- const unsubscribeConnection = connection.subscribe((message) => {
490
- const action = serverMessageToAction(message);
491
- if (action) {
492
- store.dispatch(action);
493
- notify();
1041
+ const close = () => {
1042
+ closed = true;
1043
+ if (timer) {
1044
+ clearInterval(timer);
1045
+ timer = undefined;
494
1046
  }
495
- });
1047
+ listeners.clear();
1048
+ };
1049
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
1050
+ timer = setInterval(() => {
1051
+ refresh().catch(() => {});
1052
+ }, options.intervalMs);
1053
+ }
496
1054
  return {
497
- close() {
498
- unsubscribeConnection();
499
- connection.close();
500
- store.dispatch({ type: "disconnected" });
501
- notify();
502
- },
503
- endTurn() {
504
- connection.endTurn();
505
- },
506
- get error() {
507
- return store.getSnapshot().error;
508
- },
509
- getServerSnapshot() {
510
- return store.getServerSnapshot();
511
- },
512
- getSnapshot() {
513
- return store.getSnapshot();
514
- },
515
- get isConnected() {
516
- return store.getSnapshot().isConnected;
517
- },
518
- get scenarioId() {
519
- return store.getSnapshot().scenarioId;
520
- },
521
- start,
522
- get partial() {
523
- return store.getSnapshot().partial;
524
- },
525
- get sessionId() {
526
- return connection.getSessionId();
527
- },
528
- get status() {
529
- return store.getSnapshot().status;
530
- },
531
- get turns() {
532
- return store.getSnapshot().turns;
533
- },
534
- get assistantTexts() {
535
- return store.getSnapshot().assistantTexts;
536
- },
537
- get assistantAudio() {
538
- return store.getSnapshot().assistantAudio;
539
- },
540
- sendAudio(audio) {
541
- connection.sendAudio(audio);
542
- },
543
- subscribe(subscriber) {
544
- subscribers.add(subscriber);
1055
+ close,
1056
+ getServerSnapshot: () => snapshot,
1057
+ getSnapshot: () => snapshot,
1058
+ requeueDeadLetters: () => runAction("requeue-dead-letters"),
1059
+ refresh,
1060
+ tick: () => runAction("tick"),
1061
+ subscribe: (listener) => {
1062
+ listeners.add(listener);
545
1063
  return () => {
546
- subscribers.delete(subscriber);
1064
+ listeners.delete(listener);
547
1065
  };
548
1066
  }
549
1067
  };
550
1068
  };
551
1069
 
552
- // src/angular/voice-stream.service.ts
1070
+ // src/angular/voice-delivery-runtime.service.ts
553
1071
  var _dec = [
554
- Injectable({ providedIn: "root" })
1072
+ Injectable7({ providedIn: "root" })
555
1073
  ];
556
1074
  var _init = __decoratorStart(undefined);
557
1075
 
558
- class VoiceStreamService {
559
- connect(path, options = {}) {
560
- const stream = createVoiceStream(path, options);
561
- const assistantAudioSignal = signal([]);
562
- const assistantTextsSignal = signal([]);
563
- const errorSignal = signal(null);
564
- const isConnectedSignal = signal(false);
565
- const partialSignal = signal("");
566
- const sessionIdSignal = signal(stream.sessionId);
567
- const statusSignal = signal(stream.status);
568
- const turnsSignal = signal([]);
1076
+ class VoiceDeliveryRuntimeService {
1077
+ connect(path = "/api/voice-delivery-runtime", options = {}) {
1078
+ const store = createVoiceDeliveryRuntimeStore(path, options);
1079
+ const actionErrorSignal = signal7(null);
1080
+ const actionStatusSignal = signal7("idle");
1081
+ const errorSignal = signal7(null);
1082
+ const isLoadingSignal = signal7(false);
1083
+ const reportSignal = signal7(undefined);
1084
+ const updatedAtSignal = signal7(undefined);
569
1085
  const sync = () => {
570
- assistantAudioSignal.set([...stream.assistantAudio]);
571
- assistantTextsSignal.set([...stream.assistantTexts]);
572
- errorSignal.set(stream.error);
573
- isConnectedSignal.set(stream.isConnected);
574
- partialSignal.set(stream.partial);
575
- sessionIdSignal.set(stream.sessionId);
576
- statusSignal.set(stream.status);
577
- turnsSignal.set([...stream.turns]);
1086
+ const snapshot = store.getSnapshot();
1087
+ actionErrorSignal.set(snapshot.actionError);
1088
+ actionStatusSignal.set(snapshot.actionStatus);
1089
+ errorSignal.set(snapshot.error);
1090
+ isLoadingSignal.set(snapshot.isLoading);
1091
+ reportSignal.set(snapshot.report);
1092
+ updatedAtSignal.set(snapshot.updatedAt);
578
1093
  };
579
- const unsubscribe = stream.subscribe(sync);
1094
+ const unsubscribe = store.subscribe(sync);
580
1095
  sync();
1096
+ if (typeof window !== "undefined") {
1097
+ store.refresh().catch(() => {});
1098
+ }
581
1099
  return {
582
- assistantAudio: computed(() => assistantAudioSignal()),
583
- assistantTexts: computed(() => assistantTextsSignal()),
584
1100
  close: () => {
585
1101
  unsubscribe();
586
- stream.close();
1102
+ store.close();
587
1103
  },
588
- endTurn: () => stream.endTurn(),
589
- error: computed(() => errorSignal()),
590
- isConnected: computed(() => isConnectedSignal()),
591
- partial: computed(() => partialSignal()),
592
- sendAudio: (audio) => stream.sendAudio(audio),
593
- sessionId: computed(() => sessionIdSignal()),
594
- status: computed(() => statusSignal()),
595
- turns: computed(() => turnsSignal())
1104
+ error: computed7(() => errorSignal()),
1105
+ actionError: computed7(() => actionErrorSignal()),
1106
+ actionStatus: computed7(() => actionStatusSignal()),
1107
+ isLoading: computed7(() => isLoadingSignal()),
1108
+ requeueDeadLetters: store.requeueDeadLetters,
1109
+ refresh: store.refresh,
1110
+ report: computed7(() => reportSignal()),
1111
+ tick: store.tick,
1112
+ updatedAt: computed7(() => updatedAtSignal())
596
1113
  };
597
1114
  }
598
1115
  }
599
- VoiceStreamService = __decorateElement(_init, 0, "VoiceStreamService", _dec, VoiceStreamService);
600
- __runInitializers(_init, 1, VoiceStreamService);
601
- __decoratorMetadata(_init, VoiceStreamService);
602
- let _VoiceStreamService = VoiceStreamService;
603
- // src/angular/voice-controller.service.ts
604
- import { computed as computed2, Injectable as Injectable2, signal as signal2 } from "@angular/core";
1116
+ VoiceDeliveryRuntimeService = __decorateElement(_init, 0, "VoiceDeliveryRuntimeService", _dec, VoiceDeliveryRuntimeService);
1117
+ __runInitializers(_init, 1, VoiceDeliveryRuntimeService);
1118
+ __decoratorMetadata(_init, VoiceDeliveryRuntimeService);
1119
+ let _VoiceDeliveryRuntimeService = VoiceDeliveryRuntimeService;
1120
+ // src/angular/voice-campaign-dialer-proof.service.ts
1121
+ import { computed as computed8, Injectable as Injectable8, signal as signal8 } from "@angular/core";
605
1122
 
606
- // src/client/htmx.ts
607
- var DEFAULT_EVENT_NAME = "voice-refresh";
608
- var DEFAULT_QUERY_PARAM = "sessionId";
609
- var resolveElement = (input) => {
610
- if (typeof input !== "string") {
611
- return input;
1123
+ // src/client/campaignDialerProof.ts
1124
+ var fetchVoiceCampaignDialerProofStatus = async (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
1125
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1126
+ const response = await fetchImpl(path);
1127
+ if (!response.ok) {
1128
+ throw new Error(`Voice campaign dialer proof status failed: HTTP ${response.status}`);
612
1129
  }
613
- return document.querySelector(input);
1130
+ return await response.json();
614
1131
  };
615
- var buildRoute = (element, route, queryParam, sessionId) => {
616
- const baseRoute = route ?? element.getAttribute("hx-get") ?? "";
617
- if (!baseRoute) {
618
- return "";
619
- }
620
- const url = new URL(baseRoute, window.location.origin);
621
- if (sessionId) {
622
- url.searchParams.set(queryParam, sessionId);
623
- } else {
624
- url.searchParams.delete(queryParam);
1132
+ var runVoiceCampaignDialerProofAction = async (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
1133
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1134
+ const response = await fetchImpl(path, { method: "POST" });
1135
+ if (!response.ok) {
1136
+ throw new Error(`Voice campaign dialer proof failed: HTTP ${response.status}`);
625
1137
  }
626
- return `${url.pathname}${url.search}${url.hash}`;
1138
+ return await response.json();
627
1139
  };
628
- var bindVoiceHTMX = (stream, options) => {
629
- if (typeof window === "undefined" || typeof document === "undefined") {
630
- return () => {};
631
- }
632
- const element = resolveElement(options.element);
633
- if (!element) {
634
- return () => {};
635
- }
636
- const eventName = options.eventName ?? DEFAULT_EVENT_NAME;
637
- const queryParam = options.sessionQueryParam ?? DEFAULT_QUERY_PARAM;
638
- const sync = () => {
639
- const htmxWindow = window;
640
- const nextRoute = buildRoute(element, options.route, queryParam, stream.sessionId);
641
- if (nextRoute) {
642
- element.setAttribute("hx-get", nextRoute);
1140
+ var createVoiceCampaignDialerProofStore = (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
1141
+ const listeners = new Set;
1142
+ let closed = false;
1143
+ let timer;
1144
+ let snapshot = {
1145
+ error: null,
1146
+ isLoading: false
1147
+ };
1148
+ const emit = () => {
1149
+ for (const listener of listeners) {
1150
+ listener();
643
1151
  }
644
- htmxWindow.htmx?.process?.(element);
645
- htmxWindow.htmx?.trigger?.(element, eventName);
646
1152
  };
647
- const unsubscribe = stream.subscribe(sync);
648
- sync();
649
- return () => {
650
- unsubscribe();
1153
+ const refresh = async () => {
1154
+ if (closed) {
1155
+ return snapshot.status;
1156
+ }
1157
+ snapshot = { ...snapshot, error: null, isLoading: true };
1158
+ emit();
1159
+ try {
1160
+ const status = await fetchVoiceCampaignDialerProofStatus(path, options);
1161
+ snapshot = {
1162
+ ...snapshot,
1163
+ error: null,
1164
+ isLoading: false,
1165
+ status,
1166
+ updatedAt: Date.now()
1167
+ };
1168
+ emit();
1169
+ return status;
1170
+ } catch (error) {
1171
+ snapshot = {
1172
+ ...snapshot,
1173
+ error: error instanceof Error ? error.message : String(error),
1174
+ isLoading: false
1175
+ };
1176
+ emit();
1177
+ throw error;
1178
+ }
651
1179
  };
652
- };
653
-
654
- // src/client/microphone.ts
655
- var clampSample = (value) => Math.max(-1, Math.min(1, value));
656
- var floatTo16BitPCM = (input) => {
657
- const output = new Int16Array(input.length);
658
- for (let index = 0;index < input.length; index += 1) {
659
- const sample = clampSample(input[index] ?? 0);
660
- output[index] = sample < 0 ? sample * 32768 : sample * 32767;
661
- }
662
- return new Uint8Array(output.buffer);
663
- };
664
- var getPcmLevel = (audio) => {
665
- const bytes = audio instanceof Uint8Array ? audio : new Uint8Array(audio);
666
- if (bytes.byteLength < 2) {
667
- return 0;
668
- }
669
- const samples = new Int16Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 2));
670
- if (samples.length === 0) {
671
- return 0;
672
- }
673
- let sumSquares = 0;
674
- for (const sample of samples) {
675
- const normalized = sample / 32768;
676
- sumSquares += normalized * normalized;
677
- }
678
- return Math.min(1, Math.max(0, Math.sqrt(sumSquares / samples.length) * 5.5));
679
- };
680
- var downsampleBuffer = (input, sourceRate, targetRate) => {
681
- if (sourceRate === targetRate) {
682
- return input;
683
- }
684
- const ratio = sourceRate / targetRate;
685
- const length = Math.round(input.length / ratio);
686
- const output = new Float32Array(length);
687
- let offsetResult = 0;
688
- let offsetBuffer = 0;
689
- while (offsetResult < output.length) {
690
- const nextOffsetBuffer = Math.round((offsetResult + 1) * ratio);
691
- let accum = 0;
692
- let count = 0;
693
- for (let index = offsetBuffer;index < nextOffsetBuffer && index < input.length; index += 1) {
694
- accum += input[index] ?? 0;
695
- count += 1;
696
- }
697
- output[offsetResult] = count > 0 ? accum / count : 0;
698
- offsetResult += 1;
699
- offsetBuffer = nextOffsetBuffer;
700
- }
701
- return output;
702
- };
703
- var createMicrophoneCapture = (options) => {
704
- let audioContext = null;
705
- let sourceNode = null;
706
- let processorNode = null;
707
- let mediaStream = null;
708
- const start = async () => {
709
- if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) {
710
- throw new Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");
1180
+ const runProof = async () => {
1181
+ const runPath = options.runPath ?? snapshot.status?.runPath ?? path;
1182
+ snapshot = { ...snapshot, error: null, isLoading: true };
1183
+ emit();
1184
+ try {
1185
+ const report = await runVoiceCampaignDialerProofAction(runPath, options);
1186
+ snapshot = {
1187
+ ...snapshot,
1188
+ error: null,
1189
+ isLoading: false,
1190
+ report,
1191
+ status: {
1192
+ generatedAt: Date.now(),
1193
+ mode: report.mode,
1194
+ ok: report.ok,
1195
+ providers: report.providers.map((provider) => provider.provider),
1196
+ runPath,
1197
+ safe: true
1198
+ },
1199
+ updatedAt: Date.now()
1200
+ };
1201
+ emit();
1202
+ return report;
1203
+ } catch (error) {
1204
+ snapshot = {
1205
+ ...snapshot,
1206
+ error: error instanceof Error ? error.message : String(error),
1207
+ isLoading: false
1208
+ };
1209
+ emit();
1210
+ throw error;
711
1211
  }
712
- const AudioContextCtor = (typeof window !== "undefined" ? window.AudioContext ?? window.webkitAudioContext : undefined) ?? AudioContext;
713
- if (!AudioContextCtor) {
714
- throw new Error("Browser microphone capture requires AudioContext support.");
1212
+ };
1213
+ const close = () => {
1214
+ closed = true;
1215
+ if (timer) {
1216
+ clearInterval(timer);
1217
+ timer = undefined;
715
1218
  }
716
- mediaStream = await navigator.mediaDevices.getUserMedia({
717
- audio: {
718
- channelCount: options.channelCount ?? 1
719
- }
720
- });
721
- audioContext = new AudioContextCtor;
722
- sourceNode = audioContext.createMediaStreamSource(mediaStream);
723
- processorNode = audioContext.createScriptProcessor(4096, 1, 1);
724
- processorNode.onaudioprocess = (event) => {
725
- const channel = event.inputBuffer.getChannelData(0);
726
- const downsampled = downsampleBuffer(channel, audioContext?.sampleRate ?? 48000, options.sampleRateHz ?? 16000);
727
- const pcm = floatTo16BitPCM(downsampled);
728
- options.onLevel?.(getPcmLevel(pcm));
729
- options.onAudio(pcm);
730
- };
731
- sourceNode.connect(processorNode);
732
- processorNode.connect(audioContext.destination);
1219
+ listeners.clear();
733
1220
  };
734
- const stop = () => {
735
- processorNode?.disconnect();
736
- sourceNode?.disconnect();
737
- mediaStream?.getTracks().forEach((track) => track.stop());
738
- audioContext?.close();
739
- options.onLevel?.(0);
740
- audioContext = null;
741
- mediaStream = null;
742
- processorNode = null;
743
- sourceNode = null;
1221
+ if (options.intervalMs && options.intervalMs > 0) {
1222
+ timer = setInterval(() => {
1223
+ refresh().catch(() => {});
1224
+ }, options.intervalMs);
1225
+ }
1226
+ return {
1227
+ close,
1228
+ getServerSnapshot: () => snapshot,
1229
+ getSnapshot: () => snapshot,
1230
+ refresh,
1231
+ runProof,
1232
+ subscribe: (listener) => {
1233
+ listeners.add(listener);
1234
+ return () => {
1235
+ listeners.delete(listener);
1236
+ };
1237
+ }
744
1238
  };
745
- return { start, stop };
746
1239
  };
747
1240
 
748
- // src/audioConditioning.ts
749
- var DEFAULT_TARGET_LEVEL = 0.08;
750
- var DEFAULT_MAX_GAIN = 3;
751
- var DEFAULT_NOISE_GATE_THRESHOLD = 0.006;
752
- var DEFAULT_NOISE_GATE_ATTENUATION = 0.15;
753
- var toInt16Array = (audio) => {
754
- if (audio instanceof ArrayBuffer) {
755
- return new Int16Array(audio, 0, Math.floor(audio.byteLength / 2));
1241
+ // src/angular/voice-campaign-dialer-proof.service.ts
1242
+ var _dec = [
1243
+ Injectable8({ providedIn: "root" })
1244
+ ];
1245
+ var _init = __decoratorStart(undefined);
1246
+
1247
+ class VoiceCampaignDialerProofService {
1248
+ connect(path = "/api/voice/campaigns/dialer-proof", options = {}) {
1249
+ const store = createVoiceCampaignDialerProofStore(path, options);
1250
+ const errorSignal = signal8(null);
1251
+ const isLoadingSignal = signal8(false);
1252
+ const reportSignal = signal8(undefined);
1253
+ const statusSignal = signal8(undefined);
1254
+ const updatedAtSignal = signal8(undefined);
1255
+ const sync = () => {
1256
+ const snapshot = store.getSnapshot();
1257
+ errorSignal.set(snapshot.error);
1258
+ isLoadingSignal.set(snapshot.isLoading);
1259
+ reportSignal.set(snapshot.report);
1260
+ statusSignal.set(snapshot.status);
1261
+ updatedAtSignal.set(snapshot.updatedAt);
1262
+ };
1263
+ const unsubscribe = store.subscribe(sync);
1264
+ sync();
1265
+ store.refresh().catch(() => {});
1266
+ return {
1267
+ close: () => {
1268
+ unsubscribe();
1269
+ store.close();
1270
+ },
1271
+ error: computed8(() => errorSignal()),
1272
+ isLoading: computed8(() => isLoadingSignal()),
1273
+ refresh: store.refresh,
1274
+ report: computed8(() => reportSignal()),
1275
+ runProof: store.runProof,
1276
+ status: computed8(() => statusSignal()),
1277
+ updatedAt: computed8(() => updatedAtSignal())
1278
+ };
756
1279
  }
757
- return new Int16Array(audio.buffer, audio.byteOffset, Math.floor(audio.byteLength / 2));
758
- };
759
- var computeRms = (samples) => {
760
- if (samples.length === 0) {
761
- return 0;
1280
+ }
1281
+ VoiceCampaignDialerProofService = __decorateElement(_init, 0, "VoiceCampaignDialerProofService", _dec, VoiceCampaignDialerProofService);
1282
+ __runInitializers(_init, 1, VoiceCampaignDialerProofService);
1283
+ __decoratorMetadata(_init, VoiceCampaignDialerProofService);
1284
+ let _VoiceCampaignDialerProofService = VoiceCampaignDialerProofService;
1285
+ // src/angular/voice-stream.service.ts
1286
+ import { computed as computed9, Injectable as Injectable9, signal as signal9 } from "@angular/core";
1287
+
1288
+ // src/client/actions.ts
1289
+ var normalizeErrorMessage = (value) => {
1290
+ if (typeof value === "string" && value.trim()) {
1291
+ return value;
762
1292
  }
763
- let sumSquares = 0;
764
- for (const sample of samples) {
765
- const normalized = sample / 32768;
766
- sumSquares += normalized * normalized;
1293
+ if (value instanceof Error && value.message.trim()) {
1294
+ return value.message;
767
1295
  }
768
- return Math.sqrt(sumSquares / samples.length);
769
- };
770
- var resolveAudioConditioningConfig = (config) => {
771
- if (!config || config.enabled === false) {
772
- return;
1296
+ if (value && typeof value === "object") {
1297
+ const record = value;
1298
+ for (const key of ["message", "reason", "description"]) {
1299
+ const candidate = record[key];
1300
+ if (typeof candidate === "string" && candidate.trim()) {
1301
+ return candidate;
1302
+ }
1303
+ }
1304
+ if ("error" in record) {
1305
+ return normalizeErrorMessage(record.error);
1306
+ }
1307
+ if ("cause" in record) {
1308
+ return normalizeErrorMessage(record.cause);
1309
+ }
1310
+ try {
1311
+ return JSON.stringify(value);
1312
+ } catch {}
773
1313
  }
774
- return {
775
- enabled: true,
776
- maxGain: config.maxGain ?? DEFAULT_MAX_GAIN,
777
- noiseGateAttenuation: config.noiseGateAttenuation ?? DEFAULT_NOISE_GATE_ATTENUATION,
778
- noiseGateThreshold: config.noiseGateThreshold ?? DEFAULT_NOISE_GATE_THRESHOLD,
779
- targetLevel: config.targetLevel ?? DEFAULT_TARGET_LEVEL
780
- };
1314
+ return "Unexpected error";
781
1315
  };
782
- var conditionAudioChunk = (audio, config) => {
783
- if (!config) {
784
- return audio;
785
- }
786
- const source = toInt16Array(audio);
787
- if (source.length === 0) {
788
- return audio;
789
- }
790
- const rms = computeRms(source);
791
- const output = new Int16Array(source.length);
792
- const gateFactor = rms < config.noiseGateThreshold ? config.noiseGateAttenuation : 1;
793
- const baseLevel = Math.max(rms * gateFactor, 0.000001);
794
- const gain = Math.min(config.maxGain, config.targetLevel / baseLevel);
795
- const appliedGain = Math.max(0.25, gain) * gateFactor;
796
- for (let index = 0;index < source.length; index += 1) {
797
- const next = Math.round(source[index] * appliedGain);
798
- output[index] = Math.max(-32768, Math.min(32767, next));
1316
+ var serverMessageToAction = (message) => {
1317
+ switch (message.type) {
1318
+ case "audio":
1319
+ return {
1320
+ chunk: Uint8Array.from(atob(message.chunkBase64), (char) => char.charCodeAt(0)),
1321
+ format: message.format,
1322
+ receivedAt: message.receivedAt,
1323
+ turnId: message.turnId,
1324
+ type: "audio"
1325
+ };
1326
+ case "assistant":
1327
+ return {
1328
+ text: message.text,
1329
+ type: "assistant"
1330
+ };
1331
+ case "complete":
1332
+ return {
1333
+ sessionId: message.sessionId,
1334
+ type: "complete"
1335
+ };
1336
+ case "connection":
1337
+ return {
1338
+ reconnect: message.reconnect,
1339
+ type: "connection"
1340
+ };
1341
+ case "call_lifecycle":
1342
+ return {
1343
+ event: message.event,
1344
+ sessionId: message.sessionId,
1345
+ type: "call_lifecycle"
1346
+ };
1347
+ case "error":
1348
+ return {
1349
+ message: normalizeErrorMessage(message.message),
1350
+ type: "error"
1351
+ };
1352
+ case "final":
1353
+ return {
1354
+ transcript: message.transcript,
1355
+ type: "final"
1356
+ };
1357
+ case "partial":
1358
+ return {
1359
+ transcript: message.transcript,
1360
+ type: "partial"
1361
+ };
1362
+ case "replay":
1363
+ return {
1364
+ assistantTexts: message.assistantTexts,
1365
+ call: message.call,
1366
+ partial: message.partial,
1367
+ scenarioId: message.scenarioId,
1368
+ sessionId: message.sessionId,
1369
+ status: message.status,
1370
+ turns: message.turns,
1371
+ type: "replay"
1372
+ };
1373
+ case "session":
1374
+ return {
1375
+ sessionId: message.sessionId,
1376
+ scenarioId: message.scenarioId,
1377
+ status: message.status,
1378
+ type: "session"
1379
+ };
1380
+ case "turn":
1381
+ return {
1382
+ turn: message.turn,
1383
+ type: "turn"
1384
+ };
1385
+ default:
1386
+ return null;
799
1387
  }
800
- return new Uint8Array(output.buffer);
801
1388
  };
802
1389
 
803
- // src/turnProfiles.ts
804
- var TURN_PROFILE_DEFAULTS = {
805
- balanced: {
806
- qualityProfile: "general",
807
- silenceMs: 1400,
808
- speechThreshold: 0.012,
809
- transcriptStabilityMs: 1000
810
- },
811
- fast: {
812
- qualityProfile: "general",
813
- silenceMs: 700,
814
- speechThreshold: 0.015,
815
- transcriptStabilityMs: 450
816
- },
817
- "long-form": {
818
- qualityProfile: "general",
1390
+ // src/client/connection.ts
1391
+ var WS_OPEN = 1;
1392
+ var WS_CLOSED = 3;
1393
+ var WS_NORMAL_CLOSURE = 1000;
1394
+ var DEFAULT_MAX_RECONNECT_ATTEMPTS = 10;
1395
+ var DEFAULT_PING_INTERVAL = 30000;
1396
+ var RECONNECT_DELAY_MS = 500;
1397
+ var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
1398
+ var noop = () => {};
1399
+ var noopUnsubscribe = () => noop;
1400
+ var NOOP_CONNECTION = {
1401
+ callControl: noop,
1402
+ close: noop,
1403
+ endTurn: noop,
1404
+ getReadyState: () => WS_CLOSED,
1405
+ getScenarioId: () => "",
1406
+ getSessionId: () => "",
1407
+ send: noop,
1408
+ sendAudio: noop,
1409
+ start: () => {},
1410
+ subscribe: noopUnsubscribe
1411
+ };
1412
+ var createSessionId = () => crypto.randomUUID();
1413
+ var buildWsUrl = (path, sessionId, scenarioId) => {
1414
+ const { hostname, port, protocol } = window.location;
1415
+ const wsProtocol = protocol === "https:" ? "wss:" : "ws:";
1416
+ const portSuffix = port ? `:${port}` : "";
1417
+ const url = new URL(`${wsProtocol}//${hostname}${portSuffix}${path}`);
1418
+ url.searchParams.set("sessionId", sessionId);
1419
+ if (scenarioId) {
1420
+ url.searchParams.set(DEFAULT_SCENARIO_QUERY_PARAM, scenarioId);
1421
+ }
1422
+ return url.toString();
1423
+ };
1424
+ var isVoiceServerMessage = (value) => {
1425
+ if (!value || typeof value !== "object" || !("type" in value)) {
1426
+ return false;
1427
+ }
1428
+ switch (value.type) {
1429
+ case "audio":
1430
+ case "assistant":
1431
+ case "call_lifecycle":
1432
+ case "complete":
1433
+ case "connection":
1434
+ case "error":
1435
+ case "final":
1436
+ case "partial":
1437
+ case "pong":
1438
+ case "replay":
1439
+ case "session":
1440
+ case "turn":
1441
+ return true;
1442
+ default:
1443
+ return false;
1444
+ }
1445
+ };
1446
+ var parseServerMessage = (event) => {
1447
+ if (typeof event.data !== "string") {
1448
+ return null;
1449
+ }
1450
+ try {
1451
+ const parsed = JSON.parse(event.data);
1452
+ return isVoiceServerMessage(parsed) ? parsed : null;
1453
+ } catch {
1454
+ return null;
1455
+ }
1456
+ };
1457
+ var createVoiceConnection = (path, options = {}) => {
1458
+ if (typeof window === "undefined") {
1459
+ return NOOP_CONNECTION;
1460
+ }
1461
+ const listeners = new Set;
1462
+ const shouldReconnect = options.reconnect !== false;
1463
+ const maxReconnectAttempts = options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;
1464
+ const pingInterval = options.pingInterval ?? DEFAULT_PING_INTERVAL;
1465
+ const state = {
1466
+ isConnected: false,
1467
+ pendingMessages: [],
1468
+ scenarioId: options.scenarioId ?? null,
1469
+ pingInterval: null,
1470
+ reconnectAttempts: 0,
1471
+ reconnectTimeout: null,
1472
+ sessionId: options.sessionId ?? createSessionId(),
1473
+ ws: null
1474
+ };
1475
+ const emitConnection = (reconnect) => {
1476
+ listeners.forEach((listener) => listener(reconnect));
1477
+ };
1478
+ const clearTimers = () => {
1479
+ if (state.pingInterval) {
1480
+ clearInterval(state.pingInterval);
1481
+ state.pingInterval = null;
1482
+ }
1483
+ if (state.reconnectTimeout) {
1484
+ clearTimeout(state.reconnectTimeout);
1485
+ state.reconnectTimeout = null;
1486
+ }
1487
+ };
1488
+ const flushPendingMessages = () => {
1489
+ if (state.ws?.readyState !== WS_OPEN) {
1490
+ return;
1491
+ }
1492
+ while (state.pendingMessages.length > 0) {
1493
+ const next = state.pendingMessages.shift();
1494
+ if (next !== undefined) {
1495
+ state.ws.send(next);
1496
+ }
1497
+ }
1498
+ };
1499
+ const scheduleReconnect = () => {
1500
+ const nextAttemptAt = Date.now() + RECONNECT_DELAY_MS;
1501
+ state.reconnectAttempts += 1;
1502
+ emitConnection({
1503
+ reconnect: {
1504
+ attempts: state.reconnectAttempts,
1505
+ lastDisconnectAt: Date.now(),
1506
+ maxAttempts: maxReconnectAttempts,
1507
+ nextAttemptAt,
1508
+ status: "reconnecting"
1509
+ },
1510
+ type: "connection"
1511
+ });
1512
+ state.reconnectTimeout = setTimeout(() => {
1513
+ if (state.reconnectAttempts > maxReconnectAttempts) {
1514
+ emitConnection({
1515
+ reconnect: {
1516
+ attempts: state.reconnectAttempts,
1517
+ maxAttempts: maxReconnectAttempts,
1518
+ status: "exhausted"
1519
+ },
1520
+ type: "connection"
1521
+ });
1522
+ return;
1523
+ }
1524
+ connect();
1525
+ }, RECONNECT_DELAY_MS);
1526
+ };
1527
+ const connect = () => {
1528
+ const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
1529
+ ws.binaryType = "arraybuffer";
1530
+ ws.onopen = () => {
1531
+ const wasReconnecting = state.reconnectAttempts > 0;
1532
+ state.isConnected = true;
1533
+ flushPendingMessages();
1534
+ if (wasReconnecting) {
1535
+ emitConnection({
1536
+ reconnect: {
1537
+ attempts: state.reconnectAttempts,
1538
+ lastResumedAt: Date.now(),
1539
+ maxAttempts: maxReconnectAttempts,
1540
+ status: "resumed"
1541
+ },
1542
+ type: "connection"
1543
+ });
1544
+ state.reconnectAttempts = 0;
1545
+ }
1546
+ listeners.forEach((listener) => listener({
1547
+ scenarioId: state.scenarioId ?? undefined,
1548
+ sessionId: state.sessionId,
1549
+ status: "active",
1550
+ type: "session"
1551
+ }));
1552
+ state.pingInterval = setInterval(() => {
1553
+ if (ws.readyState === WS_OPEN) {
1554
+ ws.send(JSON.stringify({ type: "ping" }));
1555
+ }
1556
+ }, pingInterval);
1557
+ };
1558
+ ws.onmessage = (event) => {
1559
+ const parsed = parseServerMessage(event);
1560
+ if (!parsed) {
1561
+ return;
1562
+ }
1563
+ if (parsed.type === "session") {
1564
+ state.sessionId = parsed.sessionId;
1565
+ state.scenarioId = parsed.scenarioId ?? state.scenarioId;
1566
+ }
1567
+ listeners.forEach((listener) => listener(parsed));
1568
+ };
1569
+ ws.onclose = (event) => {
1570
+ state.isConnected = false;
1571
+ clearTimers();
1572
+ const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && state.reconnectAttempts < maxReconnectAttempts;
1573
+ if (reconnectable) {
1574
+ scheduleReconnect();
1575
+ } else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
1576
+ emitConnection({
1577
+ reconnect: {
1578
+ attempts: state.reconnectAttempts,
1579
+ lastDisconnectAt: Date.now(),
1580
+ maxAttempts: maxReconnectAttempts,
1581
+ status: "exhausted"
1582
+ },
1583
+ type: "connection"
1584
+ });
1585
+ }
1586
+ };
1587
+ state.ws = ws;
1588
+ };
1589
+ const sendSerialized = (value) => {
1590
+ if (state.ws?.readyState === WS_OPEN) {
1591
+ state.ws.send(value);
1592
+ return;
1593
+ }
1594
+ state.pendingMessages.push(value);
1595
+ };
1596
+ const send = (message) => {
1597
+ sendSerialized(JSON.stringify(message));
1598
+ };
1599
+ const start = (input = {}) => {
1600
+ if (input.sessionId) {
1601
+ state.sessionId = input.sessionId;
1602
+ }
1603
+ if (input.scenarioId) {
1604
+ state.scenarioId = input.scenarioId;
1605
+ }
1606
+ send({
1607
+ type: "start",
1608
+ sessionId: state.sessionId,
1609
+ scenarioId: state.scenarioId ?? undefined
1610
+ });
1611
+ };
1612
+ const sendAudio = (audio) => {
1613
+ sendSerialized(audio);
1614
+ };
1615
+ const endTurn = () => {
1616
+ send({ type: "end_turn" });
1617
+ };
1618
+ const callControl = (message) => {
1619
+ send({
1620
+ ...message,
1621
+ type: "call_control"
1622
+ });
1623
+ };
1624
+ const close = () => {
1625
+ clearTimers();
1626
+ if (state.ws) {
1627
+ state.ws.close(WS_NORMAL_CLOSURE);
1628
+ state.ws = null;
1629
+ }
1630
+ state.isConnected = false;
1631
+ listeners.clear();
1632
+ };
1633
+ const subscribe = (callback) => {
1634
+ listeners.add(callback);
1635
+ return () => {
1636
+ listeners.delete(callback);
1637
+ };
1638
+ };
1639
+ connect();
1640
+ return {
1641
+ callControl,
1642
+ close,
1643
+ endTurn,
1644
+ getReadyState: () => state.ws?.readyState ?? WS_CLOSED,
1645
+ getScenarioId: () => state.scenarioId ?? "",
1646
+ getSessionId: () => state.sessionId,
1647
+ send,
1648
+ sendAudio,
1649
+ start,
1650
+ subscribe
1651
+ };
1652
+ };
1653
+
1654
+ // src/client/store.ts
1655
+ var createInitialReconnectState = () => ({
1656
+ attempts: 0,
1657
+ maxAttempts: 0,
1658
+ status: "idle"
1659
+ });
1660
+ var createInitialState = () => ({
1661
+ assistantAudio: [],
1662
+ assistantTexts: [],
1663
+ call: null,
1664
+ error: null,
1665
+ isConnected: false,
1666
+ scenarioId: null,
1667
+ partial: "",
1668
+ reconnect: createInitialReconnectState(),
1669
+ sessionId: null,
1670
+ status: "idle",
1671
+ turns: []
1672
+ });
1673
+ var createVoiceStreamStore = () => {
1674
+ let state = createInitialState();
1675
+ const subscribers = new Set;
1676
+ const notify = () => {
1677
+ subscribers.forEach((subscriber) => subscriber());
1678
+ };
1679
+ const dispatch = (action) => {
1680
+ switch (action.type) {
1681
+ case "audio":
1682
+ state = {
1683
+ ...state,
1684
+ assistantAudio: [
1685
+ ...state.assistantAudio,
1686
+ {
1687
+ chunk: action.chunk,
1688
+ format: action.format,
1689
+ receivedAt: action.receivedAt,
1690
+ turnId: action.turnId
1691
+ }
1692
+ ]
1693
+ };
1694
+ break;
1695
+ case "assistant":
1696
+ state = {
1697
+ ...state,
1698
+ assistantTexts: [...state.assistantTexts, action.text]
1699
+ };
1700
+ break;
1701
+ case "complete":
1702
+ state = {
1703
+ ...state,
1704
+ sessionId: action.sessionId,
1705
+ status: "completed"
1706
+ };
1707
+ break;
1708
+ case "call_lifecycle":
1709
+ state = {
1710
+ ...state,
1711
+ call: {
1712
+ ...state.call,
1713
+ disposition: action.event.type === "end" ? action.event.disposition : state.call?.disposition,
1714
+ endedAt: action.event.type === "end" ? action.event.at : state.call?.endedAt,
1715
+ events: [...state.call?.events ?? [], action.event],
1716
+ lastEventAt: action.event.at,
1717
+ startedAt: state.call?.startedAt ?? action.event.at
1718
+ },
1719
+ sessionId: action.sessionId
1720
+ };
1721
+ break;
1722
+ case "connected":
1723
+ state = {
1724
+ ...state,
1725
+ isConnected: true,
1726
+ reconnect: state.reconnect.status === "reconnecting" ? {
1727
+ ...state.reconnect,
1728
+ lastResumedAt: Date.now(),
1729
+ nextAttemptAt: undefined,
1730
+ status: "resumed"
1731
+ } : state.reconnect
1732
+ };
1733
+ break;
1734
+ case "connection":
1735
+ state = {
1736
+ ...state,
1737
+ reconnect: action.reconnect
1738
+ };
1739
+ break;
1740
+ case "disconnected":
1741
+ state = {
1742
+ ...state,
1743
+ isConnected: false
1744
+ };
1745
+ break;
1746
+ case "error":
1747
+ state = {
1748
+ ...state,
1749
+ error: action.message
1750
+ };
1751
+ break;
1752
+ case "final":
1753
+ state = {
1754
+ ...state,
1755
+ partial: action.transcript.text,
1756
+ turns: state.turns.map((turn) => turn)
1757
+ };
1758
+ break;
1759
+ case "partial":
1760
+ state = {
1761
+ ...state,
1762
+ partial: action.transcript.text
1763
+ };
1764
+ break;
1765
+ case "replay":
1766
+ state = {
1767
+ ...state,
1768
+ assistantTexts: [...action.assistantTexts],
1769
+ call: action.call ?? null,
1770
+ error: null,
1771
+ isConnected: action.status === "active",
1772
+ partial: action.partial,
1773
+ reconnect: state.reconnect.status === "reconnecting" ? {
1774
+ ...state.reconnect,
1775
+ lastResumedAt: Date.now(),
1776
+ nextAttemptAt: undefined,
1777
+ status: "resumed"
1778
+ } : state.reconnect,
1779
+ scenarioId: action.scenarioId ?? state.scenarioId,
1780
+ sessionId: action.sessionId,
1781
+ status: action.status,
1782
+ turns: [...action.turns]
1783
+ };
1784
+ break;
1785
+ case "session":
1786
+ state = {
1787
+ ...state,
1788
+ error: null,
1789
+ scenarioId: action.scenarioId ?? state.scenarioId,
1790
+ isConnected: action.status === "active",
1791
+ sessionId: action.sessionId,
1792
+ status: action.status
1793
+ };
1794
+ break;
1795
+ case "turn":
1796
+ state = {
1797
+ ...state,
1798
+ partial: "",
1799
+ turns: [...state.turns, action.turn]
1800
+ };
1801
+ break;
1802
+ }
1803
+ notify();
1804
+ };
1805
+ return {
1806
+ dispatch,
1807
+ getServerSnapshot: () => state,
1808
+ getSnapshot: () => state,
1809
+ subscribe: (subscriber) => {
1810
+ subscribers.add(subscriber);
1811
+ return () => {
1812
+ subscribers.delete(subscriber);
1813
+ };
1814
+ }
1815
+ };
1816
+ };
1817
+
1818
+ // src/client/createVoiceStream.ts
1819
+ var createVoiceStream = (path, options = {}) => {
1820
+ const connection = createVoiceConnection(path, options);
1821
+ const store = createVoiceStreamStore();
1822
+ const subscribers = new Set;
1823
+ const start = (input) => Promise.resolve().then(() => {
1824
+ if (!input?.sessionId && !input?.scenarioId) {
1825
+ return;
1826
+ }
1827
+ connection.start(input);
1828
+ });
1829
+ const notify = () => {
1830
+ subscribers.forEach((subscriber) => subscriber());
1831
+ };
1832
+ const reportReconnect = () => {
1833
+ if (!options.reconnectReportPath || typeof fetch === "undefined") {
1834
+ return;
1835
+ }
1836
+ const snapshot = store.getSnapshot();
1837
+ const body = JSON.stringify({
1838
+ at: Date.now(),
1839
+ reconnect: snapshot.reconnect,
1840
+ scenarioId: snapshot.scenarioId,
1841
+ sessionId: connection.getSessionId(),
1842
+ turnIds: snapshot.turns.map((turn) => turn.id)
1843
+ });
1844
+ fetch(options.reconnectReportPath, {
1845
+ body,
1846
+ headers: {
1847
+ "Content-Type": "application/json"
1848
+ },
1849
+ keepalive: true,
1850
+ method: "POST"
1851
+ }).catch(() => {});
1852
+ };
1853
+ const unsubscribeConnection = connection.subscribe((message) => {
1854
+ const action = serverMessageToAction(message);
1855
+ if (action) {
1856
+ store.dispatch(action);
1857
+ if (message.type === "connection") {
1858
+ reportReconnect();
1859
+ }
1860
+ notify();
1861
+ }
1862
+ });
1863
+ return {
1864
+ callControl(message) {
1865
+ connection.callControl(message);
1866
+ },
1867
+ close() {
1868
+ unsubscribeConnection();
1869
+ connection.close();
1870
+ store.dispatch({ type: "disconnected" });
1871
+ notify();
1872
+ },
1873
+ endTurn() {
1874
+ connection.endTurn();
1875
+ },
1876
+ get error() {
1877
+ return store.getSnapshot().error;
1878
+ },
1879
+ getServerSnapshot() {
1880
+ return store.getServerSnapshot();
1881
+ },
1882
+ getSnapshot() {
1883
+ return store.getSnapshot();
1884
+ },
1885
+ get isConnected() {
1886
+ return store.getSnapshot().isConnected;
1887
+ },
1888
+ get scenarioId() {
1889
+ return store.getSnapshot().scenarioId;
1890
+ },
1891
+ start,
1892
+ get partial() {
1893
+ return store.getSnapshot().partial;
1894
+ },
1895
+ get reconnect() {
1896
+ return store.getSnapshot().reconnect;
1897
+ },
1898
+ get sessionId() {
1899
+ return connection.getSessionId();
1900
+ },
1901
+ get status() {
1902
+ return store.getSnapshot().status;
1903
+ },
1904
+ get turns() {
1905
+ return store.getSnapshot().turns;
1906
+ },
1907
+ get assistantTexts() {
1908
+ return store.getSnapshot().assistantTexts;
1909
+ },
1910
+ get assistantAudio() {
1911
+ return store.getSnapshot().assistantAudio;
1912
+ },
1913
+ get call() {
1914
+ return store.getSnapshot().call;
1915
+ },
1916
+ sendAudio(audio) {
1917
+ connection.sendAudio(audio);
1918
+ },
1919
+ subscribe(subscriber) {
1920
+ subscribers.add(subscriber);
1921
+ return () => {
1922
+ subscribers.delete(subscriber);
1923
+ };
1924
+ }
1925
+ };
1926
+ };
1927
+
1928
+ // src/angular/voice-stream.service.ts
1929
+ var _dec = [
1930
+ Injectable9({ providedIn: "root" })
1931
+ ];
1932
+ var _init = __decoratorStart(undefined);
1933
+
1934
+ class VoiceStreamService {
1935
+ connect(path, options = {}) {
1936
+ const stream = createVoiceStream(path, options);
1937
+ const assistantAudioSignal = signal9([]);
1938
+ const assistantTextsSignal = signal9([]);
1939
+ const callSignal = signal9(null);
1940
+ const errorSignal = signal9(null);
1941
+ const isConnectedSignal = signal9(false);
1942
+ const partialSignal = signal9("");
1943
+ const reconnectSignal = signal9(stream.reconnect);
1944
+ const sessionIdSignal = signal9(stream.sessionId);
1945
+ const statusSignal = signal9(stream.status);
1946
+ const turnsSignal = signal9([]);
1947
+ const sync = () => {
1948
+ assistantAudioSignal.set([...stream.assistantAudio]);
1949
+ assistantTextsSignal.set([...stream.assistantTexts]);
1950
+ callSignal.set(stream.call);
1951
+ errorSignal.set(stream.error);
1952
+ isConnectedSignal.set(stream.isConnected);
1953
+ partialSignal.set(stream.partial);
1954
+ reconnectSignal.set(stream.reconnect);
1955
+ sessionIdSignal.set(stream.sessionId);
1956
+ statusSignal.set(stream.status);
1957
+ turnsSignal.set([...stream.turns]);
1958
+ };
1959
+ const unsubscribe = stream.subscribe(sync);
1960
+ sync();
1961
+ return {
1962
+ assistantAudio: computed9(() => assistantAudioSignal()),
1963
+ assistantTexts: computed9(() => assistantTextsSignal()),
1964
+ call: computed9(() => callSignal()),
1965
+ callControl: (message) => stream.callControl(message),
1966
+ close: () => {
1967
+ unsubscribe();
1968
+ stream.close();
1969
+ },
1970
+ endTurn: () => stream.endTurn(),
1971
+ error: computed9(() => errorSignal()),
1972
+ isConnected: computed9(() => isConnectedSignal()),
1973
+ partial: computed9(() => partialSignal()),
1974
+ reconnect: computed9(() => reconnectSignal()),
1975
+ sendAudio: (audio) => stream.sendAudio(audio),
1976
+ sessionId: computed9(() => sessionIdSignal()),
1977
+ status: computed9(() => statusSignal()),
1978
+ turns: computed9(() => turnsSignal())
1979
+ };
1980
+ }
1981
+ }
1982
+ VoiceStreamService = __decorateElement(_init, 0, "VoiceStreamService", _dec, VoiceStreamService);
1983
+ __runInitializers(_init, 1, VoiceStreamService);
1984
+ __decoratorMetadata(_init, VoiceStreamService);
1985
+ let _VoiceStreamService = VoiceStreamService;
1986
+ // src/angular/voice-controller.service.ts
1987
+ import { computed as computed10, Injectable as Injectable10, signal as signal10 } from "@angular/core";
1988
+
1989
+ // src/client/htmx.ts
1990
+ var DEFAULT_EVENT_NAME = "voice-refresh";
1991
+ var DEFAULT_QUERY_PARAM = "sessionId";
1992
+ var resolveElement = (input) => {
1993
+ if (typeof input !== "string") {
1994
+ return input;
1995
+ }
1996
+ return document.querySelector(input);
1997
+ };
1998
+ var buildRoute = (element, route, queryParam, sessionId) => {
1999
+ const baseRoute = route ?? element.getAttribute("hx-get") ?? "";
2000
+ if (!baseRoute) {
2001
+ return "";
2002
+ }
2003
+ const url = new URL(baseRoute, window.location.origin);
2004
+ if (sessionId) {
2005
+ url.searchParams.set(queryParam, sessionId);
2006
+ } else {
2007
+ url.searchParams.delete(queryParam);
2008
+ }
2009
+ return `${url.pathname}${url.search}${url.hash}`;
2010
+ };
2011
+ var bindVoiceHTMX = (stream, options) => {
2012
+ if (typeof window === "undefined" || typeof document === "undefined") {
2013
+ return () => {};
2014
+ }
2015
+ const element = resolveElement(options.element);
2016
+ if (!element) {
2017
+ return () => {};
2018
+ }
2019
+ const eventName = options.eventName ?? DEFAULT_EVENT_NAME;
2020
+ const queryParam = options.sessionQueryParam ?? DEFAULT_QUERY_PARAM;
2021
+ const sync = () => {
2022
+ const htmxWindow = window;
2023
+ const nextRoute = buildRoute(element, options.route, queryParam, stream.sessionId);
2024
+ if (nextRoute) {
2025
+ element.setAttribute("hx-get", nextRoute);
2026
+ }
2027
+ htmxWindow.htmx?.process?.(element);
2028
+ htmxWindow.htmx?.trigger?.(element, eventName);
2029
+ };
2030
+ const unsubscribe = stream.subscribe(sync);
2031
+ sync();
2032
+ return () => {
2033
+ unsubscribe();
2034
+ };
2035
+ };
2036
+
2037
+ // src/client/microphone.ts
2038
+ var clampSample = (value) => Math.max(-1, Math.min(1, value));
2039
+ var floatTo16BitPCM = (input) => {
2040
+ const output = new Int16Array(input.length);
2041
+ for (let index = 0;index < input.length; index += 1) {
2042
+ const sample = clampSample(input[index] ?? 0);
2043
+ output[index] = sample < 0 ? sample * 32768 : sample * 32767;
2044
+ }
2045
+ return new Uint8Array(output.buffer);
2046
+ };
2047
+ var getPcmLevel = (audio) => {
2048
+ const bytes = audio instanceof Uint8Array ? audio : new Uint8Array(audio);
2049
+ if (bytes.byteLength < 2) {
2050
+ return 0;
2051
+ }
2052
+ const samples = new Int16Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 2));
2053
+ if (samples.length === 0) {
2054
+ return 0;
2055
+ }
2056
+ let sumSquares = 0;
2057
+ for (const sample of samples) {
2058
+ const normalized = sample / 32768;
2059
+ sumSquares += normalized * normalized;
2060
+ }
2061
+ return Math.min(1, Math.max(0, Math.sqrt(sumSquares / samples.length) * 5.5));
2062
+ };
2063
+ var downsampleBuffer = (input, sourceRate, targetRate) => {
2064
+ if (sourceRate === targetRate) {
2065
+ return input;
2066
+ }
2067
+ const ratio = sourceRate / targetRate;
2068
+ const length = Math.round(input.length / ratio);
2069
+ const output = new Float32Array(length);
2070
+ let offsetResult = 0;
2071
+ let offsetBuffer = 0;
2072
+ while (offsetResult < output.length) {
2073
+ const nextOffsetBuffer = Math.round((offsetResult + 1) * ratio);
2074
+ let accum = 0;
2075
+ let count = 0;
2076
+ for (let index = offsetBuffer;index < nextOffsetBuffer && index < input.length; index += 1) {
2077
+ accum += input[index] ?? 0;
2078
+ count += 1;
2079
+ }
2080
+ output[offsetResult] = count > 0 ? accum / count : 0;
2081
+ offsetResult += 1;
2082
+ offsetBuffer = nextOffsetBuffer;
2083
+ }
2084
+ return output;
2085
+ };
2086
+ var createMicrophoneCapture = (options) => {
2087
+ let audioContext = null;
2088
+ let sourceNode = null;
2089
+ let processorNode = null;
2090
+ let mediaStream = null;
2091
+ const start = async () => {
2092
+ if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) {
2093
+ throw new Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");
2094
+ }
2095
+ const AudioContextCtor = (typeof window !== "undefined" ? window.AudioContext ?? window.webkitAudioContext : undefined) ?? AudioContext;
2096
+ if (!AudioContextCtor) {
2097
+ throw new Error("Browser microphone capture requires AudioContext support.");
2098
+ }
2099
+ mediaStream = await navigator.mediaDevices.getUserMedia({
2100
+ audio: {
2101
+ channelCount: options.channelCount ?? 1
2102
+ }
2103
+ });
2104
+ audioContext = new AudioContextCtor;
2105
+ sourceNode = audioContext.createMediaStreamSource(mediaStream);
2106
+ processorNode = audioContext.createScriptProcessor(4096, 1, 1);
2107
+ processorNode.onaudioprocess = (event) => {
2108
+ const channel = event.inputBuffer.getChannelData(0);
2109
+ const downsampled = downsampleBuffer(channel, audioContext?.sampleRate ?? 48000, options.sampleRateHz ?? 16000);
2110
+ const pcm = floatTo16BitPCM(downsampled);
2111
+ options.onLevel?.(getPcmLevel(pcm));
2112
+ options.onAudio(pcm);
2113
+ };
2114
+ sourceNode.connect(processorNode);
2115
+ processorNode.connect(audioContext.destination);
2116
+ };
2117
+ const stop = () => {
2118
+ processorNode?.disconnect();
2119
+ sourceNode?.disconnect();
2120
+ mediaStream?.getTracks().forEach((track) => track.stop());
2121
+ audioContext?.close();
2122
+ options.onLevel?.(0);
2123
+ audioContext = null;
2124
+ mediaStream = null;
2125
+ processorNode = null;
2126
+ sourceNode = null;
2127
+ };
2128
+ return { start, stop };
2129
+ };
2130
+
2131
+ // src/audioConditioning.ts
2132
+ var DEFAULT_TARGET_LEVEL = 0.08;
2133
+ var DEFAULT_MAX_GAIN = 3;
2134
+ var DEFAULT_NOISE_GATE_THRESHOLD = 0.006;
2135
+ var DEFAULT_NOISE_GATE_ATTENUATION = 0.15;
2136
+ var toInt16Array = (audio) => {
2137
+ if (audio instanceof ArrayBuffer) {
2138
+ return new Int16Array(audio, 0, Math.floor(audio.byteLength / 2));
2139
+ }
2140
+ return new Int16Array(audio.buffer, audio.byteOffset, Math.floor(audio.byteLength / 2));
2141
+ };
2142
+ var computeRms = (samples) => {
2143
+ if (samples.length === 0) {
2144
+ return 0;
2145
+ }
2146
+ let sumSquares = 0;
2147
+ for (const sample of samples) {
2148
+ const normalized = sample / 32768;
2149
+ sumSquares += normalized * normalized;
2150
+ }
2151
+ return Math.sqrt(sumSquares / samples.length);
2152
+ };
2153
+ var resolveAudioConditioningConfig = (config) => {
2154
+ if (!config || config.enabled === false) {
2155
+ return;
2156
+ }
2157
+ return {
2158
+ enabled: true,
2159
+ maxGain: config.maxGain ?? DEFAULT_MAX_GAIN,
2160
+ noiseGateAttenuation: config.noiseGateAttenuation ?? DEFAULT_NOISE_GATE_ATTENUATION,
2161
+ noiseGateThreshold: config.noiseGateThreshold ?? DEFAULT_NOISE_GATE_THRESHOLD,
2162
+ targetLevel: config.targetLevel ?? DEFAULT_TARGET_LEVEL
2163
+ };
2164
+ };
2165
+ var conditionAudioChunk = (audio, config) => {
2166
+ if (!config) {
2167
+ return audio;
2168
+ }
2169
+ const source = toInt16Array(audio);
2170
+ if (source.length === 0) {
2171
+ return audio;
2172
+ }
2173
+ const rms = computeRms(source);
2174
+ const output = new Int16Array(source.length);
2175
+ const gateFactor = rms < config.noiseGateThreshold ? config.noiseGateAttenuation : 1;
2176
+ const baseLevel = Math.max(rms * gateFactor, 0.000001);
2177
+ const gain = Math.min(config.maxGain, config.targetLevel / baseLevel);
2178
+ const appliedGain = Math.max(0.25, gain) * gateFactor;
2179
+ for (let index = 0;index < source.length; index += 1) {
2180
+ const next = Math.round(source[index] * appliedGain);
2181
+ output[index] = Math.max(-32768, Math.min(32767, next));
2182
+ }
2183
+ return new Uint8Array(output.buffer);
2184
+ };
2185
+
2186
+ // src/turnProfiles.ts
2187
+ var TURN_PROFILE_DEFAULTS = {
2188
+ balanced: {
2189
+ qualityProfile: "general",
2190
+ silenceMs: 1400,
2191
+ speechThreshold: 0.012,
2192
+ transcriptStabilityMs: 1000
2193
+ },
2194
+ fast: {
2195
+ qualityProfile: "general",
2196
+ silenceMs: 700,
2197
+ speechThreshold: 0.015,
2198
+ transcriptStabilityMs: 450
2199
+ },
2200
+ "long-form": {
2201
+ qualityProfile: "general",
819
2202
  silenceMs: 2200,
820
2203
  speechThreshold: 0.01,
821
2204
  transcriptStabilityMs: 1500
822
2205
  }
823
2206
  };
824
- var QUALITY_PROFILE_DEFAULTS = {
825
- general: {},
826
- "accent-heavy": {
827
- silenceMs: 1200,
828
- speechThreshold: 0.01,
829
- transcriptStabilityMs: 1200
830
- },
831
- "noisy-room": {
832
- silenceMs: 2000,
833
- speechThreshold: 0.02,
834
- transcriptStabilityMs: 1600
835
- },
836
- "short-command": {
837
- silenceMs: 500,
838
- speechThreshold: 0.016,
839
- transcriptStabilityMs: 420
2207
+ var QUALITY_PROFILE_DEFAULTS = {
2208
+ general: {},
2209
+ "accent-heavy": {
2210
+ silenceMs: 1200,
2211
+ speechThreshold: 0.01,
2212
+ transcriptStabilityMs: 1200
2213
+ },
2214
+ "noisy-room": {
2215
+ silenceMs: 2000,
2216
+ speechThreshold: 0.02,
2217
+ transcriptStabilityMs: 1600
2218
+ },
2219
+ "short-command": {
2220
+ silenceMs: 500,
2221
+ speechThreshold: 0.016,
2222
+ transcriptStabilityMs: 420
2223
+ }
2224
+ };
2225
+ var DEFAULT_TURN_PROFILE = "fast";
2226
+ var DEFAULT_QUALITY_PROFILE = "general";
2227
+ var resolveTurnDetectionConfig = (config) => {
2228
+ const profile = config?.profile ?? DEFAULT_TURN_PROFILE;
2229
+ const qualityProfile = config?.qualityProfile ?? DEFAULT_QUALITY_PROFILE;
2230
+ const preset = TURN_PROFILE_DEFAULTS[profile];
2231
+ const quality = QUALITY_PROFILE_DEFAULTS[qualityProfile];
2232
+ return {
2233
+ profile,
2234
+ qualityProfile,
2235
+ silenceMs: config?.silenceMs ?? quality.silenceMs ?? preset.silenceMs,
2236
+ speechThreshold: config?.speechThreshold ?? quality.speechThreshold ?? preset.speechThreshold,
2237
+ transcriptStabilityMs: config?.transcriptStabilityMs ?? quality.transcriptStabilityMs ?? preset.transcriptStabilityMs
2238
+ };
2239
+ };
2240
+
2241
+ // src/presets.ts
2242
+ var PRESET_INPUTS = {
2243
+ chat: {
2244
+ audioConditioning: {
2245
+ enabled: true,
2246
+ maxGain: 2.5,
2247
+ noiseGateAttenuation: 0,
2248
+ noiseGateThreshold: 0.004,
2249
+ targetLevel: 0.08
2250
+ },
2251
+ capture: {
2252
+ channelCount: 1,
2253
+ sampleRateHz: 16000
2254
+ },
2255
+ connection: {
2256
+ maxReconnectAttempts: 10,
2257
+ pingInterval: 30000,
2258
+ reconnect: true
2259
+ },
2260
+ sttLifecycle: "continuous",
2261
+ turnDetection: {
2262
+ qualityProfile: "short-command",
2263
+ profile: "balanced"
2264
+ }
2265
+ },
2266
+ default: {
2267
+ capture: {
2268
+ channelCount: 1,
2269
+ sampleRateHz: 16000
2270
+ },
2271
+ connection: {
2272
+ maxReconnectAttempts: 10,
2273
+ pingInterval: 30000,
2274
+ reconnect: true
2275
+ },
2276
+ sttLifecycle: "continuous",
2277
+ turnDetection: {
2278
+ qualityProfile: "general",
2279
+ profile: "fast"
2280
+ }
2281
+ },
2282
+ dictation: {
2283
+ audioConditioning: {
2284
+ enabled: true,
2285
+ maxGain: 2.25,
2286
+ noiseGateAttenuation: 0.05,
2287
+ noiseGateThreshold: 0.003,
2288
+ targetLevel: 0.08
2289
+ },
2290
+ capture: {
2291
+ channelCount: 1,
2292
+ sampleRateHz: 16000
2293
+ },
2294
+ connection: {
2295
+ maxReconnectAttempts: 12,
2296
+ pingInterval: 30000,
2297
+ reconnect: true
2298
+ },
2299
+ sttLifecycle: "continuous",
2300
+ turnDetection: {
2301
+ qualityProfile: "accent-heavy",
2302
+ profile: "long-form"
2303
+ }
2304
+ },
2305
+ "guided-intake": {
2306
+ audioConditioning: {
2307
+ enabled: true,
2308
+ maxGain: 2.5,
2309
+ noiseGateAttenuation: 0,
2310
+ noiseGateThreshold: 0.004,
2311
+ targetLevel: 0.08
2312
+ },
2313
+ capture: {
2314
+ channelCount: 1,
2315
+ sampleRateHz: 16000
2316
+ },
2317
+ connection: {
2318
+ maxReconnectAttempts: 12,
2319
+ pingInterval: 30000,
2320
+ reconnect: true
2321
+ },
2322
+ sttLifecycle: "turn-scoped",
2323
+ turnDetection: {
2324
+ qualityProfile: "accent-heavy",
2325
+ profile: "long-form"
2326
+ }
2327
+ },
2328
+ "noisy-room": {
2329
+ audioConditioning: {
2330
+ enabled: true,
2331
+ maxGain: 3,
2332
+ noiseGateAttenuation: 0.12,
2333
+ noiseGateThreshold: 0.006,
2334
+ targetLevel: 0.085
2335
+ },
2336
+ capture: {
2337
+ channelCount: 1,
2338
+ sampleRateHz: 16000
2339
+ },
2340
+ connection: {
2341
+ maxReconnectAttempts: 14,
2342
+ pingInterval: 45000,
2343
+ reconnect: true
2344
+ },
2345
+ sttLifecycle: "continuous",
2346
+ turnDetection: {
2347
+ qualityProfile: "noisy-room",
2348
+ profile: "long-form",
2349
+ silenceMs: 2100,
2350
+ speechThreshold: 0.02,
2351
+ transcriptStabilityMs: 1650
2352
+ }
2353
+ },
2354
+ "pstn-balanced": {
2355
+ audioConditioning: {
2356
+ enabled: true,
2357
+ maxGain: 2.8,
2358
+ noiseGateAttenuation: 0.07,
2359
+ noiseGateThreshold: 0.005,
2360
+ targetLevel: 0.08
2361
+ },
2362
+ capture: {
2363
+ channelCount: 1,
2364
+ sampleRateHz: 16000
2365
+ },
2366
+ connection: {
2367
+ maxReconnectAttempts: 14,
2368
+ pingInterval: 45000,
2369
+ reconnect: true
2370
+ },
2371
+ sttLifecycle: "continuous",
2372
+ turnDetection: {
2373
+ qualityProfile: "noisy-room",
2374
+ profile: "long-form",
2375
+ silenceMs: 660,
2376
+ speechThreshold: 0.012,
2377
+ transcriptStabilityMs: 300
2378
+ }
2379
+ },
2380
+ "pstn-fast": {
2381
+ audioConditioning: {
2382
+ enabled: true,
2383
+ maxGain: 2.75,
2384
+ noiseGateAttenuation: 0.06,
2385
+ noiseGateThreshold: 0.005,
2386
+ targetLevel: 0.08
2387
+ },
2388
+ capture: {
2389
+ channelCount: 1,
2390
+ sampleRateHz: 16000
2391
+ },
2392
+ connection: {
2393
+ maxReconnectAttempts: 14,
2394
+ pingInterval: 45000,
2395
+ reconnect: true
2396
+ },
2397
+ sttLifecycle: "continuous",
2398
+ turnDetection: {
2399
+ qualityProfile: "noisy-room",
2400
+ profile: "long-form",
2401
+ silenceMs: 620,
2402
+ speechThreshold: 0.012,
2403
+ transcriptStabilityMs: 280
2404
+ }
2405
+ },
2406
+ reliability: {
2407
+ audioConditioning: {
2408
+ enabled: true,
2409
+ maxGain: 2.9,
2410
+ noiseGateAttenuation: 0.08,
2411
+ noiseGateThreshold: 0.005,
2412
+ targetLevel: 0.08
2413
+ },
2414
+ capture: {
2415
+ channelCount: 1,
2416
+ sampleRateHz: 16000
2417
+ },
2418
+ connection: {
2419
+ maxReconnectAttempts: 14,
2420
+ pingInterval: 45000,
2421
+ reconnect: true
2422
+ },
2423
+ sttLifecycle: "continuous",
2424
+ turnDetection: {
2425
+ qualityProfile: "noisy-room",
2426
+ profile: "long-form"
2427
+ }
2428
+ }
2429
+ };
2430
+ var resolveVoiceRuntimePreset = (name = "default") => {
2431
+ const preset = PRESET_INPUTS[name];
2432
+ return {
2433
+ audioConditioning: resolveAudioConditioningConfig(preset.audioConditioning),
2434
+ capture: {
2435
+ channelCount: preset.capture?.channelCount ?? 1,
2436
+ sampleRateHz: preset.capture?.sampleRateHz ?? 16000
2437
+ },
2438
+ connection: {
2439
+ ...preset.connection
2440
+ },
2441
+ name,
2442
+ sttLifecycle: preset.sttLifecycle ?? "continuous",
2443
+ turnDetection: resolveTurnDetectionConfig(preset.turnDetection)
2444
+ };
2445
+ };
2446
+
2447
+ // src/client/controller.ts
2448
+ var createInitialState2 = (stream) => ({
2449
+ assistantAudio: [...stream.assistantAudio],
2450
+ assistantTexts: [...stream.assistantTexts],
2451
+ call: stream.call,
2452
+ error: stream.error,
2453
+ isConnected: stream.isConnected,
2454
+ isRecording: false,
2455
+ partial: stream.partial,
2456
+ reconnect: stream.reconnect,
2457
+ recordingError: null,
2458
+ sessionId: stream.sessionId,
2459
+ scenarioId: stream.scenarioId,
2460
+ status: stream.status,
2461
+ turns: [...stream.turns]
2462
+ });
2463
+ var createVoiceController = (path, options = {}) => {
2464
+ const preset = resolveVoiceRuntimePreset(options.preset);
2465
+ const stream = createVoiceStream(path, {
2466
+ ...preset.connection,
2467
+ ...options.connection
2468
+ });
2469
+ let capture = null;
2470
+ let state = createInitialState2(stream);
2471
+ const subscribers = new Set;
2472
+ const notify = () => {
2473
+ for (const subscriber of subscribers) {
2474
+ subscriber();
2475
+ }
2476
+ };
2477
+ const sync = () => {
2478
+ state = {
2479
+ ...state,
2480
+ assistantAudio: [...stream.assistantAudio],
2481
+ assistantTexts: [...stream.assistantTexts],
2482
+ call: stream.call,
2483
+ error: stream.error,
2484
+ isConnected: stream.isConnected,
2485
+ partial: stream.partial,
2486
+ reconnect: stream.reconnect,
2487
+ sessionId: stream.sessionId,
2488
+ scenarioId: stream.scenarioId,
2489
+ status: stream.status,
2490
+ turns: [...stream.turns]
2491
+ };
2492
+ if (options.autoStopOnComplete !== false && state.status === "completed" && state.isRecording) {
2493
+ capture?.stop();
2494
+ capture = null;
2495
+ state = {
2496
+ ...state,
2497
+ isRecording: false
2498
+ };
2499
+ }
2500
+ notify();
2501
+ };
2502
+ const unsubscribeStream = stream.subscribe(sync);
2503
+ sync();
2504
+ const ensureCapture = () => {
2505
+ if (capture) {
2506
+ return capture;
2507
+ }
2508
+ capture = createMicrophoneCapture({
2509
+ channelCount: options.capture?.channelCount ?? preset.capture.channelCount,
2510
+ onLevel: options.capture?.onLevel,
2511
+ onAudio: (audio) => {
2512
+ if (options.capture?.onAudio) {
2513
+ options.capture.onAudio(audio, stream.sendAudio);
2514
+ return;
2515
+ }
2516
+ stream.sendAudio(audio);
2517
+ },
2518
+ sampleRateHz: options.capture?.sampleRateHz ?? preset.capture.sampleRateHz
2519
+ });
2520
+ return capture;
2521
+ };
2522
+ const stopRecording = () => {
2523
+ capture?.stop();
2524
+ capture = null;
2525
+ state = {
2526
+ ...state,
2527
+ isRecording: false
2528
+ };
2529
+ notify();
2530
+ };
2531
+ const startRecording = async () => {
2532
+ if (state.isRecording) {
2533
+ return;
2534
+ }
2535
+ try {
2536
+ state = {
2537
+ ...state,
2538
+ recordingError: null
2539
+ };
2540
+ notify();
2541
+ await ensureCapture().start();
2542
+ state = {
2543
+ ...state,
2544
+ isRecording: true
2545
+ };
2546
+ notify();
2547
+ } catch (error) {
2548
+ capture = null;
2549
+ state = {
2550
+ ...state,
2551
+ isRecording: false,
2552
+ recordingError: error instanceof Error ? error.message : String(error)
2553
+ };
2554
+ notify();
2555
+ throw error;
2556
+ }
2557
+ };
2558
+ const close = () => {
2559
+ unsubscribeStream();
2560
+ stopRecording();
2561
+ stream.close();
2562
+ };
2563
+ return {
2564
+ bindHTMX(bindingOptions) {
2565
+ return bindVoiceHTMX(stream, bindingOptions);
2566
+ },
2567
+ callControl: (message) => stream.callControl(message),
2568
+ close,
2569
+ endTurn: () => stream.endTurn(),
2570
+ get error() {
2571
+ return state.error;
2572
+ },
2573
+ getServerSnapshot: () => state,
2574
+ getSnapshot: () => state,
2575
+ get isConnected() {
2576
+ return state.isConnected;
2577
+ },
2578
+ get isRecording() {
2579
+ return state.isRecording;
2580
+ },
2581
+ get partial() {
2582
+ return state.partial;
2583
+ },
2584
+ get recordingError() {
2585
+ return state.recordingError;
2586
+ },
2587
+ get reconnect() {
2588
+ return state.reconnect;
2589
+ },
2590
+ sendAudio: (audio) => stream.sendAudio(audio),
2591
+ get sessionId() {
2592
+ return state.sessionId;
2593
+ },
2594
+ get scenarioId() {
2595
+ return state.scenarioId;
2596
+ },
2597
+ startRecording,
2598
+ get status() {
2599
+ return state.status;
2600
+ },
2601
+ stopRecording,
2602
+ subscribe: (subscriber) => {
2603
+ subscribers.add(subscriber);
2604
+ return () => {
2605
+ subscribers.delete(subscriber);
2606
+ };
2607
+ },
2608
+ toggleRecording: async () => {
2609
+ if (state.isRecording) {
2610
+ stopRecording();
2611
+ return;
2612
+ }
2613
+ await startRecording();
2614
+ },
2615
+ get turns() {
2616
+ return state.turns;
2617
+ },
2618
+ get assistantTexts() {
2619
+ return state.assistantTexts;
2620
+ },
2621
+ get assistantAudio() {
2622
+ return state.assistantAudio;
2623
+ },
2624
+ get call() {
2625
+ return state.call;
2626
+ }
2627
+ };
2628
+ };
2629
+
2630
+ // src/angular/voice-controller.service.ts
2631
+ var _dec = [
2632
+ Injectable10({ providedIn: "root" })
2633
+ ];
2634
+ var _init = __decoratorStart(undefined);
2635
+
2636
+ class VoiceControllerService {
2637
+ connect(path, options = {}) {
2638
+ const controller = createVoiceController(path, options);
2639
+ const assistantAudioSignal = signal10([]);
2640
+ const assistantTextsSignal = signal10([]);
2641
+ const errorSignal = signal10(null);
2642
+ const isConnectedSignal = signal10(false);
2643
+ const isRecordingSignal = signal10(false);
2644
+ const partialSignal = signal10("");
2645
+ const reconnectSignal = signal10(controller.reconnect);
2646
+ const recordingErrorSignal = signal10(null);
2647
+ const sessionIdSignal = signal10(controller.sessionId);
2648
+ const statusSignal = signal10(controller.status);
2649
+ const turnsSignal = signal10([]);
2650
+ const sync = () => {
2651
+ assistantAudioSignal.set([...controller.assistantAudio]);
2652
+ assistantTextsSignal.set([...controller.assistantTexts]);
2653
+ errorSignal.set(controller.error);
2654
+ isConnectedSignal.set(controller.isConnected);
2655
+ isRecordingSignal.set(controller.isRecording);
2656
+ partialSignal.set(controller.partial);
2657
+ reconnectSignal.set(controller.reconnect);
2658
+ recordingErrorSignal.set(controller.recordingError);
2659
+ sessionIdSignal.set(controller.sessionId);
2660
+ statusSignal.set(controller.status);
2661
+ turnsSignal.set([...controller.turns]);
2662
+ };
2663
+ const unsubscribe = controller.subscribe(sync);
2664
+ sync();
2665
+ return {
2666
+ assistantAudio: computed10(() => assistantAudioSignal()),
2667
+ assistantTexts: computed10(() => assistantTextsSignal()),
2668
+ bindHTMX: controller.bindHTMX,
2669
+ close: () => {
2670
+ unsubscribe();
2671
+ controller.close();
2672
+ },
2673
+ endTurn: () => controller.endTurn(),
2674
+ error: computed10(() => errorSignal()),
2675
+ isConnected: computed10(() => isConnectedSignal()),
2676
+ isRecording: computed10(() => isRecordingSignal()),
2677
+ partial: computed10(() => partialSignal()),
2678
+ reconnect: computed10(() => reconnectSignal()),
2679
+ recordingError: computed10(() => recordingErrorSignal()),
2680
+ sendAudio: (audio) => controller.sendAudio(audio),
2681
+ sessionId: computed10(() => sessionIdSignal()),
2682
+ startRecording: () => controller.startRecording(),
2683
+ status: computed10(() => statusSignal()),
2684
+ stopRecording: () => controller.stopRecording(),
2685
+ toggleRecording: () => controller.toggleRecording(),
2686
+ turns: computed10(() => turnsSignal())
2687
+ };
2688
+ }
2689
+ }
2690
+ VoiceControllerService = __decorateElement(_init, 0, "VoiceControllerService", _dec, VoiceControllerService);
2691
+ __runInitializers(_init, 1, VoiceControllerService);
2692
+ __decoratorMetadata(_init, VoiceControllerService);
2693
+ let _VoiceControllerService = VoiceControllerService;
2694
+ // src/angular/voice-provider-capabilities.service.ts
2695
+ import { computed as computed11, Injectable as Injectable11, signal as signal11 } from "@angular/core";
2696
+
2697
+ // src/client/providerCapabilities.ts
2698
+ var fetchVoiceProviderCapabilities = async (path = "/api/provider-capabilities", options = {}) => {
2699
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2700
+ const response = await fetchImpl(path);
2701
+ if (!response.ok) {
2702
+ throw new Error(`Voice provider capabilities failed: HTTP ${response.status}`);
2703
+ }
2704
+ return await response.json();
2705
+ };
2706
+ var createVoiceProviderCapabilitiesStore = (path = "/api/provider-capabilities", options = {}) => {
2707
+ const listeners = new Set;
2708
+ let closed = false;
2709
+ let timer;
2710
+ let snapshot = {
2711
+ error: null,
2712
+ isLoading: false
2713
+ };
2714
+ const emit = () => {
2715
+ for (const listener of listeners) {
2716
+ listener();
2717
+ }
2718
+ };
2719
+ const refresh = async () => {
2720
+ if (closed) {
2721
+ return snapshot.report;
2722
+ }
2723
+ snapshot = {
2724
+ ...snapshot,
2725
+ error: null,
2726
+ isLoading: true
2727
+ };
2728
+ emit();
2729
+ try {
2730
+ const report = await fetchVoiceProviderCapabilities(path, options);
2731
+ snapshot = {
2732
+ error: null,
2733
+ isLoading: false,
2734
+ report,
2735
+ updatedAt: Date.now()
2736
+ };
2737
+ emit();
2738
+ return report;
2739
+ } catch (error) {
2740
+ snapshot = {
2741
+ ...snapshot,
2742
+ error: error instanceof Error ? error.message : String(error),
2743
+ isLoading: false
2744
+ };
2745
+ emit();
2746
+ throw error;
2747
+ }
2748
+ };
2749
+ const close = () => {
2750
+ closed = true;
2751
+ if (timer) {
2752
+ clearInterval(timer);
2753
+ timer = undefined;
2754
+ }
2755
+ listeners.clear();
2756
+ };
2757
+ if (options.intervalMs && options.intervalMs > 0) {
2758
+ timer = setInterval(() => {
2759
+ refresh().catch(() => {});
2760
+ }, options.intervalMs);
2761
+ }
2762
+ return {
2763
+ close,
2764
+ getServerSnapshot: () => snapshot,
2765
+ getSnapshot: () => snapshot,
2766
+ refresh,
2767
+ subscribe: (listener) => {
2768
+ listeners.add(listener);
2769
+ return () => {
2770
+ listeners.delete(listener);
2771
+ };
2772
+ }
2773
+ };
2774
+ };
2775
+
2776
+ // src/angular/voice-provider-capabilities.service.ts
2777
+ var _dec = [
2778
+ Injectable11({ providedIn: "root" })
2779
+ ];
2780
+ var _init = __decoratorStart(undefined);
2781
+
2782
+ class VoiceProviderCapabilitiesService {
2783
+ connect(path = "/api/provider-capabilities", options = {}) {
2784
+ const store = createVoiceProviderCapabilitiesStore(path, options);
2785
+ const errorSignal = signal11(null);
2786
+ const isLoadingSignal = signal11(false);
2787
+ const reportSignal = signal11(undefined);
2788
+ const updatedAtSignal = signal11(undefined);
2789
+ const sync = () => {
2790
+ const snapshot = store.getSnapshot();
2791
+ errorSignal.set(snapshot.error);
2792
+ isLoadingSignal.set(snapshot.isLoading);
2793
+ reportSignal.set(snapshot.report);
2794
+ updatedAtSignal.set(snapshot.updatedAt);
2795
+ };
2796
+ const unsubscribe = store.subscribe(sync);
2797
+ sync();
2798
+ store.refresh().catch(() => {});
2799
+ return {
2800
+ close: () => {
2801
+ unsubscribe();
2802
+ store.close();
2803
+ },
2804
+ error: computed11(() => errorSignal()),
2805
+ isLoading: computed11(() => isLoadingSignal()),
2806
+ refresh: store.refresh,
2807
+ report: computed11(() => reportSignal()),
2808
+ updatedAt: computed11(() => updatedAtSignal())
2809
+ };
2810
+ }
2811
+ }
2812
+ VoiceProviderCapabilitiesService = __decorateElement(_init, 0, "VoiceProviderCapabilitiesService", _dec, VoiceProviderCapabilitiesService);
2813
+ __runInitializers(_init, 1, VoiceProviderCapabilitiesService);
2814
+ __decoratorMetadata(_init, VoiceProviderCapabilitiesService);
2815
+ let _VoiceProviderCapabilitiesService = VoiceProviderCapabilitiesService;
2816
+ // src/angular/voice-provider-contracts.service.ts
2817
+ import { computed as computed12, Injectable as Injectable12, signal as signal12 } from "@angular/core";
2818
+
2819
+ // src/client/providerContracts.ts
2820
+ var fetchVoiceProviderContracts = async (path = "/api/provider-contracts", options = {}) => {
2821
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2822
+ const response = await fetchImpl(path);
2823
+ if (!response.ok) {
2824
+ throw new Error(`Voice provider contracts failed: HTTP ${response.status}`);
2825
+ }
2826
+ return await response.json();
2827
+ };
2828
+ var createVoiceProviderContractsStore = (path = "/api/provider-contracts", options = {}) => {
2829
+ const listeners = new Set;
2830
+ let closed = false;
2831
+ let timer;
2832
+ let snapshot = {
2833
+ error: null,
2834
+ isLoading: false
2835
+ };
2836
+ const emit = () => {
2837
+ for (const listener of listeners) {
2838
+ listener();
2839
+ }
2840
+ };
2841
+ const refresh = async () => {
2842
+ if (closed) {
2843
+ return snapshot.report;
2844
+ }
2845
+ snapshot = { ...snapshot, error: null, isLoading: true };
2846
+ emit();
2847
+ try {
2848
+ const report = await fetchVoiceProviderContracts(path, options);
2849
+ snapshot = {
2850
+ error: null,
2851
+ isLoading: false,
2852
+ report,
2853
+ updatedAt: Date.now()
2854
+ };
2855
+ emit();
2856
+ return report;
2857
+ } catch (error) {
2858
+ snapshot = {
2859
+ ...snapshot,
2860
+ error: error instanceof Error ? error.message : String(error),
2861
+ isLoading: false
2862
+ };
2863
+ emit();
2864
+ throw error;
2865
+ }
2866
+ };
2867
+ const close = () => {
2868
+ closed = true;
2869
+ if (timer) {
2870
+ clearInterval(timer);
2871
+ timer = undefined;
2872
+ }
2873
+ listeners.clear();
2874
+ };
2875
+ if (options.intervalMs && options.intervalMs > 0) {
2876
+ timer = setInterval(() => {
2877
+ refresh().catch(() => {});
2878
+ }, options.intervalMs);
2879
+ }
2880
+ return {
2881
+ close,
2882
+ getServerSnapshot: () => snapshot,
2883
+ getSnapshot: () => snapshot,
2884
+ refresh,
2885
+ subscribe: (listener) => {
2886
+ listeners.add(listener);
2887
+ return () => {
2888
+ listeners.delete(listener);
2889
+ };
2890
+ }
2891
+ };
2892
+ };
2893
+
2894
+ // src/angular/voice-provider-contracts.service.ts
2895
+ var _dec = [
2896
+ Injectable12({ providedIn: "root" })
2897
+ ];
2898
+ var _init = __decoratorStart(undefined);
2899
+
2900
+ class VoiceProviderContractsService {
2901
+ connect(path = "/api/provider-contracts", options = {}) {
2902
+ const store = createVoiceProviderContractsStore(path, options);
2903
+ const errorSignal = signal12(null);
2904
+ const isLoadingSignal = signal12(false);
2905
+ const reportSignal = signal12(undefined);
2906
+ const updatedAtSignal = signal12(undefined);
2907
+ const sync = () => {
2908
+ const snapshot = store.getSnapshot();
2909
+ errorSignal.set(snapshot.error);
2910
+ isLoadingSignal.set(snapshot.isLoading);
2911
+ reportSignal.set(snapshot.report);
2912
+ updatedAtSignal.set(snapshot.updatedAt);
2913
+ };
2914
+ const unsubscribe = store.subscribe(sync);
2915
+ sync();
2916
+ store.refresh().catch(() => {});
2917
+ return {
2918
+ close: () => {
2919
+ unsubscribe();
2920
+ store.close();
2921
+ },
2922
+ error: computed12(() => errorSignal()),
2923
+ isLoading: computed12(() => isLoadingSignal()),
2924
+ refresh: store.refresh,
2925
+ report: computed12(() => reportSignal()),
2926
+ updatedAt: computed12(() => updatedAtSignal())
2927
+ };
2928
+ }
2929
+ }
2930
+ VoiceProviderContractsService = __decorateElement(_init, 0, "VoiceProviderContractsService", _dec, VoiceProviderContractsService);
2931
+ __runInitializers(_init, 1, VoiceProviderContractsService);
2932
+ __decoratorMetadata(_init, VoiceProviderContractsService);
2933
+ let _VoiceProviderContractsService = VoiceProviderContractsService;
2934
+ // src/angular/voice-provider-status.service.ts
2935
+ import { computed as computed13, Injectable as Injectable13, signal as signal13 } from "@angular/core";
2936
+
2937
+ // src/client/providerStatus.ts
2938
+ var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
2939
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2940
+ const response = await fetchImpl(path);
2941
+ if (!response.ok) {
2942
+ throw new Error(`Voice provider status failed: HTTP ${response.status}`);
2943
+ }
2944
+ return await response.json();
2945
+ };
2946
+ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
2947
+ const listeners = new Set;
2948
+ let closed = false;
2949
+ let timer;
2950
+ let snapshot = {
2951
+ error: null,
2952
+ isLoading: false,
2953
+ providers: []
2954
+ };
2955
+ const emit = () => {
2956
+ for (const listener of listeners) {
2957
+ listener();
2958
+ }
2959
+ };
2960
+ const refresh = async () => {
2961
+ if (closed) {
2962
+ return snapshot.providers;
2963
+ }
2964
+ snapshot = {
2965
+ ...snapshot,
2966
+ error: null,
2967
+ isLoading: true
2968
+ };
2969
+ emit();
2970
+ try {
2971
+ const providers = await fetchVoiceProviderStatus(path, options);
2972
+ snapshot = {
2973
+ error: null,
2974
+ isLoading: false,
2975
+ providers,
2976
+ updatedAt: Date.now()
2977
+ };
2978
+ emit();
2979
+ return providers;
2980
+ } catch (error) {
2981
+ snapshot = {
2982
+ ...snapshot,
2983
+ error: error instanceof Error ? error.message : String(error),
2984
+ isLoading: false
2985
+ };
2986
+ emit();
2987
+ throw error;
2988
+ }
2989
+ };
2990
+ const close = () => {
2991
+ closed = true;
2992
+ if (timer) {
2993
+ clearInterval(timer);
2994
+ timer = undefined;
2995
+ }
2996
+ listeners.clear();
2997
+ };
2998
+ if (options.intervalMs && options.intervalMs > 0) {
2999
+ timer = setInterval(() => {
3000
+ refresh().catch(() => {});
3001
+ }, options.intervalMs);
3002
+ }
3003
+ return {
3004
+ close,
3005
+ getServerSnapshot: () => snapshot,
3006
+ getSnapshot: () => snapshot,
3007
+ refresh,
3008
+ subscribe: (listener) => {
3009
+ listeners.add(listener);
3010
+ return () => {
3011
+ listeners.delete(listener);
3012
+ };
3013
+ }
3014
+ };
3015
+ };
3016
+
3017
+ // src/angular/voice-provider-status.service.ts
3018
+ var _dec = [
3019
+ Injectable13({ providedIn: "root" })
3020
+ ];
3021
+ var _init = __decoratorStart(undefined);
3022
+
3023
+ class VoiceProviderStatusService {
3024
+ connect(path = "/api/provider-status", options = {}) {
3025
+ const store = createVoiceProviderStatusStore(path, options);
3026
+ const errorSignal = signal13(null);
3027
+ const isLoadingSignal = signal13(false);
3028
+ const providersSignal = signal13([]);
3029
+ const updatedAtSignal = signal13(undefined);
3030
+ const sync = () => {
3031
+ const snapshot = store.getSnapshot();
3032
+ errorSignal.set(snapshot.error);
3033
+ isLoadingSignal.set(snapshot.isLoading);
3034
+ providersSignal.set([...snapshot.providers]);
3035
+ updatedAtSignal.set(snapshot.updatedAt);
3036
+ };
3037
+ const unsubscribe = store.subscribe(sync);
3038
+ sync();
3039
+ store.refresh().catch(() => {});
3040
+ return {
3041
+ close: () => {
3042
+ unsubscribe();
3043
+ store.close();
3044
+ },
3045
+ error: computed13(() => errorSignal()),
3046
+ isLoading: computed13(() => isLoadingSignal()),
3047
+ providers: computed13(() => providersSignal()),
3048
+ refresh: store.refresh,
3049
+ updatedAt: computed13(() => updatedAtSignal())
3050
+ };
3051
+ }
3052
+ }
3053
+ VoiceProviderStatusService = __decorateElement(_init, 0, "VoiceProviderStatusService", _dec, VoiceProviderStatusService);
3054
+ __runInitializers(_init, 1, VoiceProviderStatusService);
3055
+ __decoratorMetadata(_init, VoiceProviderStatusService);
3056
+ let _VoiceProviderStatusService = VoiceProviderStatusService;
3057
+ // src/angular/voice-routing-status.service.ts
3058
+ import { Injectable as Injectable14, signal as signal14 } from "@angular/core";
3059
+
3060
+ // src/client/routingStatus.ts
3061
+ var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
3062
+ const fetchImpl = options.fetch ?? globalThis.fetch;
3063
+ const response = await fetchImpl(path);
3064
+ if (!response.ok) {
3065
+ throw new Error(`Voice routing status failed: HTTP ${response.status}`);
3066
+ }
3067
+ return await response.json();
3068
+ };
3069
+ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {}) => {
3070
+ const listeners = new Set;
3071
+ let closed = false;
3072
+ let timer;
3073
+ let snapshot = {
3074
+ decision: null,
3075
+ error: null,
3076
+ isLoading: false
3077
+ };
3078
+ const emit = () => {
3079
+ for (const listener of listeners) {
3080
+ listener();
3081
+ }
3082
+ };
3083
+ const refresh = async () => {
3084
+ if (closed) {
3085
+ return snapshot.decision;
3086
+ }
3087
+ snapshot = {
3088
+ ...snapshot,
3089
+ error: null,
3090
+ isLoading: true
3091
+ };
3092
+ emit();
3093
+ try {
3094
+ const decision = await fetchVoiceRoutingStatus(path, options);
3095
+ snapshot = {
3096
+ decision,
3097
+ error: null,
3098
+ isLoading: false,
3099
+ updatedAt: Date.now()
3100
+ };
3101
+ emit();
3102
+ return decision;
3103
+ } catch (error) {
3104
+ snapshot = {
3105
+ ...snapshot,
3106
+ error: error instanceof Error ? error.message : String(error),
3107
+ isLoading: false
3108
+ };
3109
+ emit();
3110
+ throw error;
3111
+ }
3112
+ };
3113
+ const close = () => {
3114
+ closed = true;
3115
+ if (timer) {
3116
+ clearInterval(timer);
3117
+ timer = undefined;
3118
+ }
3119
+ listeners.clear();
3120
+ };
3121
+ if (options.intervalMs && options.intervalMs > 0) {
3122
+ timer = setInterval(() => {
3123
+ refresh().catch(() => {});
3124
+ }, options.intervalMs);
3125
+ }
3126
+ return {
3127
+ close,
3128
+ getServerSnapshot: () => snapshot,
3129
+ getSnapshot: () => snapshot,
3130
+ refresh,
3131
+ subscribe: (listener) => {
3132
+ listeners.add(listener);
3133
+ return () => {
3134
+ listeners.delete(listener);
3135
+ };
3136
+ }
3137
+ };
3138
+ };
3139
+
3140
+ // src/angular/voice-routing-status.service.ts
3141
+ var _dec = [
3142
+ Injectable14({ providedIn: "root" })
3143
+ ];
3144
+ var _init = __decoratorStart(undefined);
3145
+
3146
+ class VoiceRoutingStatusService {
3147
+ connect(path = "/api/routing/latest", options = {}) {
3148
+ const store = createVoiceRoutingStatusStore(path, options);
3149
+ const decisionSignal = signal14(null);
3150
+ const errorSignal = signal14(null);
3151
+ const isLoadingSignal = signal14(false);
3152
+ const updatedAtSignal = signal14(undefined);
3153
+ const sync = () => {
3154
+ const snapshot = store.getSnapshot();
3155
+ decisionSignal.set(snapshot.decision);
3156
+ errorSignal.set(snapshot.error);
3157
+ isLoadingSignal.set(snapshot.isLoading);
3158
+ updatedAtSignal.set(snapshot.updatedAt);
3159
+ };
3160
+ const unsubscribe = store.subscribe(sync);
3161
+ sync();
3162
+ store.refresh().catch(() => {});
3163
+ return {
3164
+ close: () => {
3165
+ unsubscribe();
3166
+ store.close();
3167
+ },
3168
+ decision: decisionSignal.asReadonly(),
3169
+ error: errorSignal.asReadonly(),
3170
+ isLoading: isLoadingSignal.asReadonly(),
3171
+ refresh: store.refresh,
3172
+ updatedAt: updatedAtSignal.asReadonly()
3173
+ };
3174
+ }
3175
+ }
3176
+ VoiceRoutingStatusService = __decorateElement(_init, 0, "VoiceRoutingStatusService", _dec, VoiceRoutingStatusService);
3177
+ __runInitializers(_init, 1, VoiceRoutingStatusService);
3178
+ __decoratorMetadata(_init, VoiceRoutingStatusService);
3179
+ let _VoiceRoutingStatusService = VoiceRoutingStatusService;
3180
+ // src/angular/voice-trace-timeline.service.ts
3181
+ import { computed as computed14, Injectable as Injectable15, signal as signal15 } from "@angular/core";
3182
+
3183
+ // src/client/traceTimeline.ts
3184
+ var fetchVoiceTraceTimeline = async (path = "/api/voice-traces", options = {}) => {
3185
+ const fetchImpl = options.fetch ?? globalThis.fetch;
3186
+ const response = await fetchImpl(path);
3187
+ if (!response.ok) {
3188
+ throw new Error(`Voice trace timeline failed: HTTP ${response.status}`);
3189
+ }
3190
+ return await response.json();
3191
+ };
3192
+ var createVoiceTraceTimelineStore = (path = "/api/voice-traces", options = {}) => {
3193
+ const listeners = new Set;
3194
+ let closed = false;
3195
+ let timer;
3196
+ let snapshot = {
3197
+ error: null,
3198
+ isLoading: false,
3199
+ report: null
3200
+ };
3201
+ const emit = () => {
3202
+ for (const listener of listeners) {
3203
+ listener();
3204
+ }
3205
+ };
3206
+ const refresh = async () => {
3207
+ if (closed) {
3208
+ return snapshot.report;
3209
+ }
3210
+ snapshot = {
3211
+ ...snapshot,
3212
+ error: null,
3213
+ isLoading: true
3214
+ };
3215
+ emit();
3216
+ try {
3217
+ const report = await fetchVoiceTraceTimeline(path, options);
3218
+ snapshot = {
3219
+ error: null,
3220
+ isLoading: false,
3221
+ report,
3222
+ updatedAt: Date.now()
3223
+ };
3224
+ emit();
3225
+ return report;
3226
+ } catch (error) {
3227
+ snapshot = {
3228
+ ...snapshot,
3229
+ error: error instanceof Error ? error.message : String(error),
3230
+ isLoading: false
3231
+ };
3232
+ emit();
3233
+ throw error;
3234
+ }
3235
+ };
3236
+ const close = () => {
3237
+ closed = true;
3238
+ if (timer) {
3239
+ clearInterval(timer);
3240
+ timer = undefined;
3241
+ }
3242
+ listeners.clear();
3243
+ };
3244
+ if (options.intervalMs && options.intervalMs > 0) {
3245
+ timer = setInterval(() => {
3246
+ refresh().catch(() => {});
3247
+ }, options.intervalMs);
3248
+ }
3249
+ return {
3250
+ close,
3251
+ getServerSnapshot: () => snapshot,
3252
+ getSnapshot: () => snapshot,
3253
+ refresh,
3254
+ subscribe: (listener) => {
3255
+ listeners.add(listener);
3256
+ return () => {
3257
+ listeners.delete(listener);
3258
+ };
3259
+ }
3260
+ };
3261
+ };
3262
+
3263
+ // src/angular/voice-trace-timeline.service.ts
3264
+ var _dec = [
3265
+ Injectable15({ providedIn: "root" })
3266
+ ];
3267
+ var _init = __decoratorStart(undefined);
3268
+
3269
+ class VoiceTraceTimelineService {
3270
+ connect(path = "/api/voice-traces", options = {}) {
3271
+ const store = createVoiceTraceTimelineStore(path, options);
3272
+ const errorSignal = signal15(null);
3273
+ const isLoadingSignal = signal15(false);
3274
+ const reportSignal = signal15(null);
3275
+ const updatedAtSignal = signal15(undefined);
3276
+ const sync = () => {
3277
+ const snapshot = store.getSnapshot();
3278
+ errorSignal.set(snapshot.error);
3279
+ isLoadingSignal.set(snapshot.isLoading);
3280
+ reportSignal.set(snapshot.report);
3281
+ updatedAtSignal.set(snapshot.updatedAt);
3282
+ };
3283
+ const unsubscribe = store.subscribe(sync);
3284
+ sync();
3285
+ store.refresh().catch(() => {});
3286
+ return {
3287
+ close: () => {
3288
+ unsubscribe();
3289
+ store.close();
3290
+ },
3291
+ error: computed14(() => errorSignal()),
3292
+ isLoading: computed14(() => isLoadingSignal()),
3293
+ refresh: store.refresh,
3294
+ report: computed14(() => reportSignal()),
3295
+ updatedAt: computed14(() => updatedAtSignal())
3296
+ };
3297
+ }
3298
+ }
3299
+ VoiceTraceTimelineService = __decorateElement(_init, 0, "VoiceTraceTimelineService", _dec, VoiceTraceTimelineService);
3300
+ __runInitializers(_init, 1, VoiceTraceTimelineService);
3301
+ __decoratorMetadata(_init, VoiceTraceTimelineService);
3302
+ let _VoiceTraceTimelineService = VoiceTraceTimelineService;
3303
+ // src/angular/voice-agent-squad-status.service.ts
3304
+ import { computed as computed15, Injectable as Injectable16, signal as signal16 } from "@angular/core";
3305
+
3306
+ // src/client/agentSquadStatus.ts
3307
+ var getString = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
3308
+ var getPayloadString = (event, key) => getString(event.payload?.[key]);
3309
+ var eventStatus = (event) => {
3310
+ const status = getPayloadString(event, "status");
3311
+ if (status === "blocked")
3312
+ return "blocked";
3313
+ if (status === "unknown-target")
3314
+ return "unknown-target";
3315
+ if (status === "allowed")
3316
+ return "handoff";
3317
+ return event.type === "agent.result" ? "active" : "handoff";
3318
+ };
3319
+ var deriveSessionSpecialist = (session) => {
3320
+ const events = [...session.events].sort((left, right) => left.at - right.at);
3321
+ const agentEvents = events.filter((event) => event.type === "agent.handoff" || event.type === "agent.context" || event.type === "agent.result" || event.type === "agent.model");
3322
+ const latest = agentEvents.at(-1);
3323
+ if (!latest) {
3324
+ return {
3325
+ lastEventAt: session.lastEventAt,
3326
+ sessionId: session.sessionId,
3327
+ status: "idle"
3328
+ };
840
3329
  }
3330
+ const handoffEvents = events.filter((event) => event.type === "agent.handoff");
3331
+ const lastHandoff = handoffEvents.at(-1);
3332
+ const latestAgentId = getPayloadString(latest, "agentId");
3333
+ const handoffStatus = lastHandoff ? eventStatus(lastHandoff) : undefined;
3334
+ const currentTarget = handoffStatus === "blocked" || handoffStatus === "unknown-target" ? getPayloadString(lastHandoff, "fromAgentId") ?? latestAgentId : getPayloadString(lastHandoff ?? latest, "targetAgentId") ?? latestAgentId;
3335
+ return {
3336
+ fromAgentId: getPayloadString(lastHandoff ?? latest, "fromAgentId"),
3337
+ lastEventAt: latest.at,
3338
+ reason: getPayloadString(lastHandoff ?? latest, "reason") ?? getPayloadString(latest, "handoffTarget"),
3339
+ sessionId: session.sessionId,
3340
+ status: lastHandoff ? eventStatus(lastHandoff) : "active",
3341
+ summary: getPayloadString(lastHandoff ?? latest, "summary"),
3342
+ targetAgentId: currentTarget,
3343
+ turnId: latest.turnId
3344
+ };
841
3345
  };
842
- var DEFAULT_TURN_PROFILE = "fast";
843
- var DEFAULT_QUALITY_PROFILE = "general";
844
- var resolveTurnDetectionConfig = (config) => {
845
- const profile = config?.profile ?? DEFAULT_TURN_PROFILE;
846
- const qualityProfile = config?.qualityProfile ?? DEFAULT_QUALITY_PROFILE;
847
- const preset = TURN_PROFILE_DEFAULTS[profile];
848
- const quality = QUALITY_PROFILE_DEFAULTS[qualityProfile];
3346
+ var buildVoiceAgentSquadStatusReport = (timeline, options = {}) => {
3347
+ const sessions = (timeline?.sessions ?? []).filter((session) => !options.sessionId || session.sessionId === options.sessionId).map(deriveSessionSpecialist).sort((left, right) => (right.lastEventAt ?? 0) - (left.lastEventAt ?? 0));
3348
+ const active = sessions.filter((session) => session.status !== "idle");
849
3349
  return {
850
- profile,
851
- qualityProfile,
852
- silenceMs: config?.silenceMs ?? quality.silenceMs ?? preset.silenceMs,
853
- speechThreshold: config?.speechThreshold ?? quality.speechThreshold ?? preset.speechThreshold,
854
- transcriptStabilityMs: config?.transcriptStabilityMs ?? quality.transcriptStabilityMs ?? preset.transcriptStabilityMs
3350
+ active,
3351
+ checkedAt: timeline?.checkedAt,
3352
+ current: active[0] ?? sessions[0],
3353
+ sessionCount: sessions.length,
3354
+ sessions
3355
+ };
3356
+ };
3357
+ var createVoiceAgentSquadStatusStore = (path = "/api/voice-traces", options = {}) => {
3358
+ const timelineStore = createVoiceTraceTimelineStore(path, options);
3359
+ const getReport = () => buildVoiceAgentSquadStatusReport(timelineStore.getSnapshot().report, {
3360
+ sessionId: options.sessionId
3361
+ });
3362
+ const getSnapshot = () => {
3363
+ const snapshot = timelineStore.getSnapshot();
3364
+ return {
3365
+ error: snapshot.error,
3366
+ isLoading: snapshot.isLoading,
3367
+ report: getReport(),
3368
+ updatedAt: snapshot.updatedAt
3369
+ };
3370
+ };
3371
+ return {
3372
+ close: timelineStore.close,
3373
+ getServerSnapshot: getSnapshot,
3374
+ getSnapshot,
3375
+ refresh: timelineStore.refresh,
3376
+ subscribe: timelineStore.subscribe
855
3377
  };
856
3378
  };
857
3379
 
858
- // src/presets.ts
859
- var PRESET_INPUTS = {
860
- chat: {
861
- audioConditioning: {
862
- enabled: true,
863
- maxGain: 2.5,
864
- noiseGateAttenuation: 0,
865
- noiseGateThreshold: 0.004,
866
- targetLevel: 0.08
867
- },
868
- capture: {
869
- channelCount: 1,
870
- sampleRateHz: 16000
871
- },
872
- connection: {
873
- maxReconnectAttempts: 10,
874
- pingInterval: 30000,
875
- reconnect: true
876
- },
877
- sttLifecycle: "continuous",
878
- turnDetection: {
879
- qualityProfile: "short-command",
880
- profile: "balanced"
881
- }
882
- },
883
- default: {
884
- capture: {
885
- channelCount: 1,
886
- sampleRateHz: 16000
887
- },
888
- connection: {
889
- maxReconnectAttempts: 10,
890
- pingInterval: 30000,
891
- reconnect: true
892
- },
893
- sttLifecycle: "continuous",
894
- turnDetection: {
895
- qualityProfile: "general",
896
- profile: "fast"
897
- }
898
- },
899
- dictation: {
900
- audioConditioning: {
901
- enabled: true,
902
- maxGain: 2.25,
903
- noiseGateAttenuation: 0.05,
904
- noiseGateThreshold: 0.003,
905
- targetLevel: 0.08
906
- },
907
- capture: {
908
- channelCount: 1,
909
- sampleRateHz: 16000
910
- },
911
- connection: {
912
- maxReconnectAttempts: 12,
913
- pingInterval: 30000,
914
- reconnect: true
915
- },
916
- sttLifecycle: "continuous",
917
- turnDetection: {
918
- qualityProfile: "accent-heavy",
919
- profile: "long-form"
920
- }
921
- },
922
- "guided-intake": {
923
- audioConditioning: {
924
- enabled: true,
925
- maxGain: 2.5,
926
- noiseGateAttenuation: 0,
927
- noiseGateThreshold: 0.004,
928
- targetLevel: 0.08
929
- },
930
- capture: {
931
- channelCount: 1,
932
- sampleRateHz: 16000
933
- },
934
- connection: {
935
- maxReconnectAttempts: 12,
936
- pingInterval: 30000,
937
- reconnect: true
938
- },
939
- sttLifecycle: "turn-scoped",
940
- turnDetection: {
941
- qualityProfile: "accent-heavy",
942
- profile: "long-form"
943
- }
944
- },
945
- "noisy-room": {
946
- audioConditioning: {
947
- enabled: true,
948
- maxGain: 3,
949
- noiseGateAttenuation: 0.12,
950
- noiseGateThreshold: 0.006,
951
- targetLevel: 0.085
952
- },
953
- capture: {
954
- channelCount: 1,
955
- sampleRateHz: 16000
956
- },
957
- connection: {
958
- maxReconnectAttempts: 14,
959
- pingInterval: 45000,
960
- reconnect: true
961
- },
962
- sttLifecycle: "continuous",
963
- turnDetection: {
964
- qualityProfile: "noisy-room",
965
- profile: "long-form",
966
- silenceMs: 2100,
967
- speechThreshold: 0.02,
968
- transcriptStabilityMs: 1650
969
- }
970
- },
971
- "pstn-balanced": {
972
- audioConditioning: {
973
- enabled: true,
974
- maxGain: 2.8,
975
- noiseGateAttenuation: 0.07,
976
- noiseGateThreshold: 0.005,
977
- targetLevel: 0.08
978
- },
979
- capture: {
980
- channelCount: 1,
981
- sampleRateHz: 16000
982
- },
983
- connection: {
984
- maxReconnectAttempts: 14,
985
- pingInterval: 45000,
986
- reconnect: true
987
- },
988
- sttLifecycle: "continuous",
989
- turnDetection: {
990
- qualityProfile: "noisy-room",
991
- profile: "long-form",
992
- silenceMs: 660,
993
- speechThreshold: 0.012,
994
- transcriptStabilityMs: 300
3380
+ // src/angular/voice-agent-squad-status.service.ts
3381
+ var _dec = [
3382
+ Injectable16({ providedIn: "root" })
3383
+ ];
3384
+ var _init = __decoratorStart(undefined);
3385
+
3386
+ class VoiceAgentSquadStatusService {
3387
+ connect(path = "/api/voice-traces", options = {}) {
3388
+ const store = createVoiceAgentSquadStatusStore(path, options);
3389
+ const errorSignal = signal16(null);
3390
+ const isLoadingSignal = signal16(false);
3391
+ const reportSignal = signal16(undefined);
3392
+ const updatedAtSignal = signal16(undefined);
3393
+ const sync = () => {
3394
+ const snapshot = store.getSnapshot();
3395
+ errorSignal.set(snapshot.error);
3396
+ isLoadingSignal.set(snapshot.isLoading);
3397
+ reportSignal.set(snapshot.report);
3398
+ updatedAtSignal.set(snapshot.updatedAt);
3399
+ };
3400
+ const unsubscribe = store.subscribe(sync);
3401
+ sync();
3402
+ store.refresh().catch(() => {});
3403
+ return {
3404
+ close: () => {
3405
+ unsubscribe();
3406
+ store.close();
3407
+ },
3408
+ current: computed15(() => reportSignal()?.current),
3409
+ error: computed15(() => errorSignal()),
3410
+ isLoading: computed15(() => isLoadingSignal()),
3411
+ refresh: store.refresh,
3412
+ report: computed15(() => reportSignal()),
3413
+ updatedAt: computed15(() => updatedAtSignal())
3414
+ };
3415
+ }
3416
+ }
3417
+ VoiceAgentSquadStatusService = __decorateElement(_init, 0, "VoiceAgentSquadStatusService", _dec, VoiceAgentSquadStatusService);
3418
+ __runInitializers(_init, 1, VoiceAgentSquadStatusService);
3419
+ __decoratorMetadata(_init, VoiceAgentSquadStatusService);
3420
+ let _VoiceAgentSquadStatusService = VoiceAgentSquadStatusService;
3421
+ // src/angular/voice-turn-latency.service.ts
3422
+ import { computed as computed16, Injectable as Injectable17, signal as signal17 } from "@angular/core";
3423
+
3424
+ // src/client/turnLatency.ts
3425
+ var fetchVoiceTurnLatency = async (path = "/api/turn-latency", options = {}) => {
3426
+ const fetchImpl = options.fetch ?? globalThis.fetch;
3427
+ const response = await fetchImpl(path);
3428
+ if (!response.ok) {
3429
+ throw new Error(`Voice turn latency failed: HTTP ${response.status}`);
3430
+ }
3431
+ return await response.json();
3432
+ };
3433
+ var runVoiceTurnLatencyProof = async (path, options = {}) => {
3434
+ const fetchImpl = options.fetch ?? globalThis.fetch;
3435
+ const response = await fetchImpl(path, { method: "POST" });
3436
+ if (!response.ok) {
3437
+ throw new Error(`Voice turn latency proof failed: HTTP ${response.status}`);
3438
+ }
3439
+ return response.json();
3440
+ };
3441
+ var createVoiceTurnLatencyStore = (path = "/api/turn-latency", options = {}) => {
3442
+ const listeners = new Set;
3443
+ let closed = false;
3444
+ let timer;
3445
+ let snapshot = {
3446
+ error: null,
3447
+ isLoading: false
3448
+ };
3449
+ const emit = () => {
3450
+ for (const listener of listeners) {
3451
+ listener();
995
3452
  }
996
- },
997
- "pstn-fast": {
998
- audioConditioning: {
999
- enabled: true,
1000
- maxGain: 2.75,
1001
- noiseGateAttenuation: 0.06,
1002
- noiseGateThreshold: 0.005,
1003
- targetLevel: 0.08
1004
- },
1005
- capture: {
1006
- channelCount: 1,
1007
- sampleRateHz: 16000
1008
- },
1009
- connection: {
1010
- maxReconnectAttempts: 14,
1011
- pingInterval: 45000,
1012
- reconnect: true
1013
- },
1014
- sttLifecycle: "continuous",
1015
- turnDetection: {
1016
- qualityProfile: "noisy-room",
1017
- profile: "long-form",
1018
- silenceMs: 620,
1019
- speechThreshold: 0.012,
1020
- transcriptStabilityMs: 280
3453
+ };
3454
+ const refresh = async () => {
3455
+ if (closed) {
3456
+ return snapshot.report;
1021
3457
  }
1022
- },
1023
- reliability: {
1024
- audioConditioning: {
1025
- enabled: true,
1026
- maxGain: 2.9,
1027
- noiseGateAttenuation: 0.08,
1028
- noiseGateThreshold: 0.005,
1029
- targetLevel: 0.08
1030
- },
1031
- capture: {
1032
- channelCount: 1,
1033
- sampleRateHz: 16000
1034
- },
1035
- connection: {
1036
- maxReconnectAttempts: 14,
1037
- pingInterval: 45000,
1038
- reconnect: true
1039
- },
1040
- sttLifecycle: "continuous",
1041
- turnDetection: {
1042
- qualityProfile: "noisy-room",
1043
- profile: "long-form"
3458
+ snapshot = { ...snapshot, error: null, isLoading: true };
3459
+ emit();
3460
+ try {
3461
+ const report = await fetchVoiceTurnLatency(path, options);
3462
+ snapshot = {
3463
+ error: null,
3464
+ isLoading: false,
3465
+ report,
3466
+ updatedAt: Date.now()
3467
+ };
3468
+ emit();
3469
+ return report;
3470
+ } catch (error) {
3471
+ snapshot = {
3472
+ ...snapshot,
3473
+ error: error instanceof Error ? error.message : String(error),
3474
+ isLoading: false
3475
+ };
3476
+ emit();
3477
+ throw error;
3478
+ }
3479
+ };
3480
+ const runProof = async () => {
3481
+ if (!options.proofPath) {
3482
+ throw new Error("Voice turn latency proof path is not configured.");
3483
+ }
3484
+ snapshot = { ...snapshot, error: null, isLoading: true };
3485
+ emit();
3486
+ try {
3487
+ await runVoiceTurnLatencyProof(options.proofPath, options);
3488
+ return await refresh();
3489
+ } catch (error) {
3490
+ snapshot = {
3491
+ ...snapshot,
3492
+ error: error instanceof Error ? error.message : String(error),
3493
+ isLoading: false
3494
+ };
3495
+ emit();
3496
+ throw error;
1044
3497
  }
3498
+ };
3499
+ const close = () => {
3500
+ closed = true;
3501
+ if (timer) {
3502
+ clearInterval(timer);
3503
+ timer = undefined;
3504
+ }
3505
+ listeners.clear();
3506
+ };
3507
+ if (options.intervalMs && options.intervalMs > 0) {
3508
+ timer = setInterval(() => {
3509
+ refresh().catch(() => {});
3510
+ }, options.intervalMs);
1045
3511
  }
1046
- };
1047
- var resolveVoiceRuntimePreset = (name = "default") => {
1048
- const preset = PRESET_INPUTS[name];
1049
3512
  return {
1050
- audioConditioning: resolveAudioConditioningConfig(preset.audioConditioning),
1051
- capture: {
1052
- channelCount: preset.capture?.channelCount ?? 1,
1053
- sampleRateHz: preset.capture?.sampleRateHz ?? 16000
1054
- },
1055
- connection: {
1056
- ...preset.connection
1057
- },
1058
- name,
1059
- sttLifecycle: preset.sttLifecycle ?? "continuous",
1060
- turnDetection: resolveTurnDetectionConfig(preset.turnDetection)
3513
+ close,
3514
+ getServerSnapshot: () => snapshot,
3515
+ getSnapshot: () => snapshot,
3516
+ refresh,
3517
+ runProof,
3518
+ subscribe: (listener) => {
3519
+ listeners.add(listener);
3520
+ return () => {
3521
+ listeners.delete(listener);
3522
+ };
3523
+ }
1061
3524
  };
1062
3525
  };
1063
3526
 
1064
- // src/client/controller.ts
1065
- var createInitialState2 = (stream) => ({
1066
- assistantAudio: [...stream.assistantAudio],
1067
- assistantTexts: [...stream.assistantTexts],
1068
- error: stream.error,
1069
- isConnected: stream.isConnected,
1070
- isRecording: false,
1071
- partial: stream.partial,
1072
- recordingError: null,
1073
- sessionId: stream.sessionId,
1074
- scenarioId: stream.scenarioId,
1075
- status: stream.status,
1076
- turns: [...stream.turns]
1077
- });
1078
- var createVoiceController = (path, options = {}) => {
1079
- const preset = resolveVoiceRuntimePreset(options.preset);
1080
- const stream = createVoiceStream(path, {
1081
- ...preset.connection,
1082
- ...options.connection
1083
- });
1084
- let capture = null;
1085
- let state = createInitialState2(stream);
1086
- const subscribers = new Set;
1087
- const notify = () => {
1088
- for (const subscriber of subscribers) {
1089
- subscriber();
3527
+ // src/angular/voice-turn-latency.service.ts
3528
+ var _dec = [
3529
+ Injectable17({ providedIn: "root" })
3530
+ ];
3531
+ var _init = __decoratorStart(undefined);
3532
+
3533
+ class VoiceTurnLatencyService {
3534
+ connect(path = "/api/turn-latency", options = {}) {
3535
+ const store = createVoiceTurnLatencyStore(path, options);
3536
+ const errorSignal = signal17(null);
3537
+ const isLoadingSignal = signal17(false);
3538
+ const reportSignal = signal17(undefined);
3539
+ const updatedAtSignal = signal17(undefined);
3540
+ const sync = () => {
3541
+ const snapshot = store.getSnapshot();
3542
+ errorSignal.set(snapshot.error);
3543
+ isLoadingSignal.set(snapshot.isLoading);
3544
+ reportSignal.set(snapshot.report);
3545
+ updatedAtSignal.set(snapshot.updatedAt);
3546
+ };
3547
+ const unsubscribe = store.subscribe(sync);
3548
+ sync();
3549
+ store.refresh().catch(() => {});
3550
+ return {
3551
+ close: () => {
3552
+ unsubscribe();
3553
+ store.close();
3554
+ },
3555
+ error: computed16(() => errorSignal()),
3556
+ isLoading: computed16(() => isLoadingSignal()),
3557
+ refresh: store.refresh,
3558
+ report: computed16(() => reportSignal()),
3559
+ runProof: store.runProof,
3560
+ updatedAt: computed16(() => updatedAtSignal())
3561
+ };
3562
+ }
3563
+ }
3564
+ VoiceTurnLatencyService = __decorateElement(_init, 0, "VoiceTurnLatencyService", _dec, VoiceTurnLatencyService);
3565
+ __runInitializers(_init, 1, VoiceTurnLatencyService);
3566
+ __decoratorMetadata(_init, VoiceTurnLatencyService);
3567
+ let _VoiceTurnLatencyService = VoiceTurnLatencyService;
3568
+ // src/angular/voice-turn-quality.service.ts
3569
+ import { computed as computed17, Injectable as Injectable18, signal as signal18 } from "@angular/core";
3570
+
3571
+ // src/client/turnQuality.ts
3572
+ var fetchVoiceTurnQuality = async (path = "/api/turn-quality", options = {}) => {
3573
+ const fetchImpl = options.fetch ?? globalThis.fetch;
3574
+ const response = await fetchImpl(path);
3575
+ if (!response.ok) {
3576
+ throw new Error(`Voice turn quality failed: HTTP ${response.status}`);
3577
+ }
3578
+ return await response.json();
3579
+ };
3580
+ var createVoiceTurnQualityStore = (path = "/api/turn-quality", options = {}) => {
3581
+ const listeners = new Set;
3582
+ let closed = false;
3583
+ let timer;
3584
+ let snapshot = {
3585
+ error: null,
3586
+ isLoading: false
3587
+ };
3588
+ const emit = () => {
3589
+ for (const listener of listeners) {
3590
+ listener();
1090
3591
  }
1091
3592
  };
1092
- const sync = () => {
1093
- state = {
1094
- ...state,
1095
- assistantAudio: [...stream.assistantAudio],
1096
- assistantTexts: [...stream.assistantTexts],
1097
- error: stream.error,
1098
- isConnected: stream.isConnected,
1099
- partial: stream.partial,
1100
- sessionId: stream.sessionId,
1101
- scenarioId: stream.scenarioId,
1102
- status: stream.status,
1103
- turns: [...stream.turns]
3593
+ const refresh = async () => {
3594
+ if (closed) {
3595
+ return snapshot.report;
3596
+ }
3597
+ snapshot = {
3598
+ ...snapshot,
3599
+ error: null,
3600
+ isLoading: true
1104
3601
  };
1105
- if (options.autoStopOnComplete !== false && state.status === "completed" && state.isRecording) {
1106
- capture?.stop();
1107
- capture = null;
1108
- state = {
1109
- ...state,
1110
- isRecording: false
3602
+ emit();
3603
+ try {
3604
+ const report = await fetchVoiceTurnQuality(path, options);
3605
+ snapshot = {
3606
+ error: null,
3607
+ isLoading: false,
3608
+ report,
3609
+ updatedAt: Date.now()
1111
3610
  };
3611
+ emit();
3612
+ return report;
3613
+ } catch (error) {
3614
+ snapshot = {
3615
+ ...snapshot,
3616
+ error: error instanceof Error ? error.message : String(error),
3617
+ isLoading: false
3618
+ };
3619
+ emit();
3620
+ throw error;
1112
3621
  }
1113
- notify();
1114
3622
  };
1115
- const unsubscribeStream = stream.subscribe(sync);
1116
- sync();
1117
- const ensureCapture = () => {
1118
- if (capture) {
1119
- return capture;
3623
+ const close = () => {
3624
+ closed = true;
3625
+ if (timer) {
3626
+ clearInterval(timer);
3627
+ timer = undefined;
1120
3628
  }
1121
- capture = createMicrophoneCapture({
1122
- channelCount: options.capture?.channelCount ?? preset.capture.channelCount,
1123
- onLevel: options.capture?.onLevel,
1124
- onAudio: (audio) => stream.sendAudio(audio),
1125
- sampleRateHz: options.capture?.sampleRateHz ?? preset.capture.sampleRateHz
1126
- });
1127
- return capture;
3629
+ listeners.clear();
1128
3630
  };
1129
- const stopRecording = () => {
1130
- capture?.stop();
1131
- capture = null;
1132
- state = {
1133
- ...state,
1134
- isRecording: false
3631
+ if (options.intervalMs && options.intervalMs > 0) {
3632
+ timer = setInterval(() => {
3633
+ refresh().catch(() => {});
3634
+ }, options.intervalMs);
3635
+ }
3636
+ return {
3637
+ close,
3638
+ getServerSnapshot: () => snapshot,
3639
+ getSnapshot: () => snapshot,
3640
+ refresh,
3641
+ subscribe: (listener) => {
3642
+ listeners.add(listener);
3643
+ return () => {
3644
+ listeners.delete(listener);
3645
+ };
3646
+ }
3647
+ };
3648
+ };
3649
+
3650
+ // src/angular/voice-turn-quality.service.ts
3651
+ var _dec = [
3652
+ Injectable18({ providedIn: "root" })
3653
+ ];
3654
+ var _init = __decoratorStart(undefined);
3655
+
3656
+ class VoiceTurnQualityService {
3657
+ connect(path = "/api/turn-quality", options = {}) {
3658
+ const store = createVoiceTurnQualityStore(path, options);
3659
+ const errorSignal = signal18(null);
3660
+ const isLoadingSignal = signal18(false);
3661
+ const reportSignal = signal18(undefined);
3662
+ const updatedAtSignal = signal18(undefined);
3663
+ const sync = () => {
3664
+ const snapshot = store.getSnapshot();
3665
+ errorSignal.set(snapshot.error);
3666
+ isLoadingSignal.set(snapshot.isLoading);
3667
+ reportSignal.set(snapshot.report);
3668
+ updatedAtSignal.set(snapshot.updatedAt);
1135
3669
  };
1136
- notify();
3670
+ const unsubscribe = store.subscribe(sync);
3671
+ sync();
3672
+ store.refresh().catch(() => {});
3673
+ return {
3674
+ close: () => {
3675
+ unsubscribe();
3676
+ store.close();
3677
+ },
3678
+ error: computed17(() => errorSignal()),
3679
+ isLoading: computed17(() => isLoadingSignal()),
3680
+ refresh: store.refresh,
3681
+ report: computed17(() => reportSignal()),
3682
+ updatedAt: computed17(() => updatedAtSignal())
3683
+ };
3684
+ }
3685
+ }
3686
+ VoiceTurnQualityService = __decorateElement(_init, 0, "VoiceTurnQualityService", _dec, VoiceTurnQualityService);
3687
+ __runInitializers(_init, 1, VoiceTurnQualityService);
3688
+ __decoratorMetadata(_init, VoiceTurnQualityService);
3689
+ let _VoiceTurnQualityService = VoiceTurnQualityService;
3690
+ // src/angular/voice-workflow-status.service.ts
3691
+ import { computed as computed18, Injectable as Injectable19, signal as signal19 } from "@angular/core";
3692
+
3693
+ // src/client/workflowStatus.ts
3694
+ var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
3695
+ const fetchImpl = options.fetch ?? globalThis.fetch;
3696
+ const response = await fetchImpl(path);
3697
+ if (!response.ok) {
3698
+ throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
3699
+ }
3700
+ return await response.json();
3701
+ };
3702
+ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
3703
+ const listeners = new Set;
3704
+ let closed = false;
3705
+ let timer;
3706
+ let snapshot = {
3707
+ error: null,
3708
+ isLoading: false
1137
3709
  };
1138
- const startRecording = async () => {
1139
- if (state.isRecording) {
1140
- return;
3710
+ const emit = () => {
3711
+ for (const listener of listeners) {
3712
+ listener();
3713
+ }
3714
+ };
3715
+ const refresh = async () => {
3716
+ if (closed) {
3717
+ return snapshot.report;
1141
3718
  }
3719
+ snapshot = {
3720
+ ...snapshot,
3721
+ error: null,
3722
+ isLoading: true
3723
+ };
3724
+ emit();
1142
3725
  try {
1143
- state = {
1144
- ...state,
1145
- recordingError: null
1146
- };
1147
- notify();
1148
- await ensureCapture().start();
1149
- state = {
1150
- ...state,
1151
- isRecording: true
3726
+ const report = await fetchVoiceWorkflowStatus(path, options);
3727
+ snapshot = {
3728
+ error: null,
3729
+ isLoading: false,
3730
+ report,
3731
+ updatedAt: Date.now()
1152
3732
  };
1153
- notify();
3733
+ emit();
3734
+ return report;
1154
3735
  } catch (error) {
1155
- capture = null;
1156
- state = {
1157
- ...state,
1158
- isRecording: false,
1159
- recordingError: error instanceof Error ? error.message : String(error)
3736
+ snapshot = {
3737
+ ...snapshot,
3738
+ error: error instanceof Error ? error.message : String(error),
3739
+ isLoading: false
1160
3740
  };
1161
- notify();
3741
+ emit();
1162
3742
  throw error;
1163
3743
  }
1164
3744
  };
1165
3745
  const close = () => {
1166
- unsubscribeStream();
1167
- stopRecording();
1168
- stream.close();
3746
+ closed = true;
3747
+ if (timer) {
3748
+ clearInterval(timer);
3749
+ timer = undefined;
3750
+ }
3751
+ listeners.clear();
1169
3752
  };
3753
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
3754
+ timer = setInterval(() => {
3755
+ refresh().catch(() => {});
3756
+ }, options.intervalMs);
3757
+ }
1170
3758
  return {
1171
- bindHTMX(bindingOptions) {
1172
- return bindVoiceHTMX(stream, bindingOptions);
1173
- },
1174
3759
  close,
1175
- endTurn: () => stream.endTurn(),
1176
- get error() {
1177
- return state.error;
1178
- },
1179
- getServerSnapshot: () => state,
1180
- getSnapshot: () => state,
1181
- get isConnected() {
1182
- return state.isConnected;
1183
- },
1184
- get isRecording() {
1185
- return state.isRecording;
1186
- },
1187
- get partial() {
1188
- return state.partial;
1189
- },
1190
- get recordingError() {
1191
- return state.recordingError;
1192
- },
1193
- sendAudio: (audio) => stream.sendAudio(audio),
1194
- get sessionId() {
1195
- return state.sessionId;
1196
- },
1197
- get scenarioId() {
1198
- return state.scenarioId;
1199
- },
1200
- startRecording,
1201
- get status() {
1202
- return state.status;
1203
- },
1204
- stopRecording,
1205
- subscribe: (subscriber) => {
1206
- subscribers.add(subscriber);
3760
+ getServerSnapshot: () => snapshot,
3761
+ getSnapshot: () => snapshot,
3762
+ refresh,
3763
+ subscribe: (listener) => {
3764
+ listeners.add(listener);
1207
3765
  return () => {
1208
- subscribers.delete(subscriber);
3766
+ listeners.delete(listener);
1209
3767
  };
1210
- },
1211
- toggleRecording: async () => {
1212
- if (state.isRecording) {
1213
- stopRecording();
1214
- return;
1215
- }
1216
- await startRecording();
1217
- },
1218
- get turns() {
1219
- return state.turns;
1220
- },
1221
- get assistantTexts() {
1222
- return state.assistantTexts;
1223
- },
1224
- get assistantAudio() {
1225
- return state.assistantAudio;
1226
3768
  }
1227
3769
  };
1228
3770
  };
1229
3771
 
1230
- // src/angular/voice-controller.service.ts
3772
+ // src/angular/voice-workflow-status.service.ts
1231
3773
  var _dec = [
1232
- Injectable2({ providedIn: "root" })
3774
+ Injectable19({ providedIn: "root" })
1233
3775
  ];
1234
3776
  var _init = __decoratorStart(undefined);
1235
3777
 
1236
- class VoiceControllerService {
1237
- connect(path, options = {}) {
1238
- const controller = createVoiceController(path, options);
1239
- const assistantAudioSignal = signal2([]);
1240
- const assistantTextsSignal = signal2([]);
1241
- const errorSignal = signal2(null);
1242
- const isConnectedSignal = signal2(false);
1243
- const isRecordingSignal = signal2(false);
1244
- const partialSignal = signal2("");
1245
- const recordingErrorSignal = signal2(null);
1246
- const sessionIdSignal = signal2(controller.sessionId);
1247
- const statusSignal = signal2(controller.status);
1248
- const turnsSignal = signal2([]);
3778
+ class VoiceWorkflowStatusService {
3779
+ connect(path = "/evals/scenarios/json", options = {}) {
3780
+ const store = createVoiceWorkflowStatusStore(path, options);
3781
+ const errorSignal = signal19(null);
3782
+ const isLoadingSignal = signal19(false);
3783
+ const reportSignal = signal19(undefined);
3784
+ const updatedAtSignal = signal19(undefined);
1249
3785
  const sync = () => {
1250
- assistantAudioSignal.set([...controller.assistantAudio]);
1251
- assistantTextsSignal.set([...controller.assistantTexts]);
1252
- errorSignal.set(controller.error);
1253
- isConnectedSignal.set(controller.isConnected);
1254
- isRecordingSignal.set(controller.isRecording);
1255
- partialSignal.set(controller.partial);
1256
- recordingErrorSignal.set(controller.recordingError);
1257
- sessionIdSignal.set(controller.sessionId);
1258
- statusSignal.set(controller.status);
1259
- turnsSignal.set([...controller.turns]);
3786
+ const snapshot = store.getSnapshot();
3787
+ errorSignal.set(snapshot.error);
3788
+ isLoadingSignal.set(snapshot.isLoading);
3789
+ reportSignal.set(snapshot.report);
3790
+ updatedAtSignal.set(snapshot.updatedAt);
1260
3791
  };
1261
- const unsubscribe = controller.subscribe(sync);
3792
+ const unsubscribe = store.subscribe(sync);
1262
3793
  sync();
3794
+ if (typeof window !== "undefined") {
3795
+ store.refresh().catch(() => {});
3796
+ }
1263
3797
  return {
1264
- assistantAudio: computed2(() => assistantAudioSignal()),
1265
- assistantTexts: computed2(() => assistantTextsSignal()),
1266
- bindHTMX: controller.bindHTMX,
1267
3798
  close: () => {
1268
3799
  unsubscribe();
1269
- controller.close();
3800
+ store.close();
1270
3801
  },
1271
- endTurn: () => controller.endTurn(),
1272
- error: computed2(() => errorSignal()),
1273
- isConnected: computed2(() => isConnectedSignal()),
1274
- isRecording: computed2(() => isRecordingSignal()),
1275
- partial: computed2(() => partialSignal()),
1276
- recordingError: computed2(() => recordingErrorSignal()),
1277
- sendAudio: (audio) => controller.sendAudio(audio),
1278
- sessionId: computed2(() => sessionIdSignal()),
1279
- startRecording: () => controller.startRecording(),
1280
- status: computed2(() => statusSignal()),
1281
- stopRecording: () => controller.stopRecording(),
1282
- toggleRecording: () => controller.toggleRecording(),
1283
- turns: computed2(() => turnsSignal())
3802
+ error: computed18(() => errorSignal()),
3803
+ isLoading: computed18(() => isLoadingSignal()),
3804
+ refresh: store.refresh,
3805
+ report: computed18(() => reportSignal()),
3806
+ updatedAt: computed18(() => updatedAtSignal())
1284
3807
  };
1285
3808
  }
1286
3809
  }
1287
- VoiceControllerService = __decorateElement(_init, 0, "VoiceControllerService", _dec, VoiceControllerService);
1288
- __runInitializers(_init, 1, VoiceControllerService);
1289
- __decoratorMetadata(_init, VoiceControllerService);
1290
- let _VoiceControllerService = VoiceControllerService;
3810
+ VoiceWorkflowStatusService = __decorateElement(_init, 0, "VoiceWorkflowStatusService", _dec, VoiceWorkflowStatusService);
3811
+ __runInitializers(_init, 1, VoiceWorkflowStatusService);
3812
+ __decoratorMetadata(_init, VoiceWorkflowStatusService);
3813
+ let _VoiceWorkflowStatusService = VoiceWorkflowStatusService;
1291
3814
  export {
3815
+ VoiceWorkflowStatusService,
3816
+ VoiceTurnQualityService,
3817
+ VoiceTurnLatencyService,
3818
+ VoiceTraceTimelineService,
1292
3819
  VoiceStreamService,
1293
- VoiceControllerService
3820
+ VoiceRoutingStatusService,
3821
+ VoiceReadinessFailuresService,
3822
+ VoiceProviderStatusService,
3823
+ VoiceProviderContractsService,
3824
+ VoiceProviderCapabilitiesService,
3825
+ VoiceProofTrendsService,
3826
+ VoicePlatformCoverageService,
3827
+ VoiceOpsStatusService,
3828
+ VoiceOpsActionCenterService,
3829
+ VoiceLiveOpsService,
3830
+ VoiceDeliveryRuntimeService,
3831
+ VoiceControllerService,
3832
+ VoiceCampaignDialerProofService,
3833
+ VoiceAgentSquadStatusService
1294
3834
  };