@absolutejs/voice 0.0.22-beta.5 → 0.0.22-beta.500

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 (385) hide show
  1. package/README.md +4556 -603
  2. package/dist/agent.d.ts +79 -5
  3. package/dist/agentSquadContract.d.ts +98 -0
  4. package/dist/agentState.d.ts +12 -0
  5. package/dist/agentTools.d.ts +133 -0
  6. package/dist/aiVoiceModel.d.ts +15 -0
  7. package/dist/amdDetector.d.ts +25 -0
  8. package/dist/angular/index.d.ts +26 -2
  9. package/dist/angular/index.js +3703 -246
  10. package/dist/angular/voice-agent-squad-status.service.d.ts +12 -0
  11. package/dist/angular/voice-call-debugger.service.d.ts +12 -0
  12. package/dist/angular/voice-campaign-dialer-proof.service.d.ts +14 -0
  13. package/dist/angular/voice-controller.service.d.ts +3 -1
  14. package/dist/angular/voice-delivery-runtime.component.d.ts +17 -0
  15. package/dist/angular/voice-delivery-runtime.service.d.ts +16 -0
  16. package/dist/angular/voice-live-ops.service.d.ts +11 -0
  17. package/dist/angular/voice-ops-action-center.service.d.ts +13 -0
  18. package/dist/angular/voice-ops-status.component.d.ts +15 -0
  19. package/dist/angular/voice-ops-status.service.d.ts +12 -0
  20. package/dist/angular/voice-platform-coverage.service.d.ts +12 -0
  21. package/dist/angular/voice-profile-comparison.service.d.ts +12 -0
  22. package/dist/angular/voice-proof-trends.service.d.ts +12 -0
  23. package/dist/angular/voice-provider-capabilities.service.d.ts +12 -0
  24. package/dist/angular/voice-provider-contracts.service.d.ts +12 -0
  25. package/dist/angular/voice-provider-status.service.d.ts +12 -0
  26. package/dist/angular/voice-readiness-failures.service.d.ts +13 -0
  27. package/dist/angular/voice-reconnect-profile-evidence.service.d.ts +12 -0
  28. package/dist/angular/voice-routing-status.service.d.ts +11 -0
  29. package/dist/angular/voice-session-observability.service.d.ts +12 -0
  30. package/dist/angular/voice-session-snapshot.service.d.ts +13 -0
  31. package/dist/angular/voice-stream.service.d.ts +6 -1
  32. package/dist/angular/voice-trace-timeline.service.d.ts +12 -0
  33. package/dist/angular/voice-turn-latency.service.d.ts +13 -0
  34. package/dist/angular/voice-turn-quality.service.d.ts +12 -0
  35. package/dist/angular/voice-widget.service.d.ts +18 -0
  36. package/dist/angular/voice-workflow-status.service.d.ts +12 -0
  37. package/dist/assistant.d.ts +12 -13
  38. package/dist/assistantHealth.d.ts +81 -0
  39. package/dist/assistantMemory.d.ts +3 -3
  40. package/dist/assistantMode.d.ts +22 -0
  41. package/dist/audioConditioning.d.ts +1 -1
  42. package/dist/audit.d.ts +131 -0
  43. package/dist/auditDeliveryRoutes.d.ts +85 -0
  44. package/dist/auditExport.d.ts +34 -0
  45. package/dist/auditRoutes.d.ts +66 -0
  46. package/dist/auditSinks.d.ts +151 -0
  47. package/dist/backchannel.d.ts +18 -0
  48. package/dist/bargeInRoutes.d.ts +56 -0
  49. package/dist/browserCallProfiles.d.ts +120 -0
  50. package/dist/browserMediaRoutes.d.ts +62 -0
  51. package/dist/callDebugger.d.ts +66 -0
  52. package/dist/callQuota.d.ts +54 -0
  53. package/dist/callerMemory.d.ts +37 -0
  54. package/dist/campaign.d.ts +794 -0
  55. package/dist/campaignControls.d.ts +37 -0
  56. package/dist/campaignDialers.d.ts +111 -0
  57. package/dist/client/actions.d.ts +117 -1
  58. package/dist/client/agentSquadStatus.d.ts +37 -0
  59. package/dist/client/agentSquadStatusWidget.d.ts +24 -0
  60. package/dist/client/audioPlayer.d.ts +2 -2
  61. package/dist/client/bargeInMonitor.d.ts +7 -0
  62. package/dist/client/browserMedia.d.ts +8 -0
  63. package/dist/client/callDebugger.d.ts +19 -0
  64. package/dist/client/callDebuggerWidget.d.ts +30 -0
  65. package/dist/client/campaignDialerProof.d.ts +23 -0
  66. package/dist/client/connection.d.ts +5 -1
  67. package/dist/client/controller.d.ts +1 -1
  68. package/dist/client/costDashboard.d.ts +27 -0
  69. package/dist/client/createVoiceStream.d.ts +1 -1
  70. package/dist/client/deliveryRuntime.d.ts +34 -0
  71. package/dist/client/deliveryRuntimeWidget.d.ts +37 -0
  72. package/dist/client/duplex.d.ts +2 -2
  73. package/dist/client/htmx.d.ts +1 -1
  74. package/dist/client/htmxBootstrap.js +1011 -16
  75. package/dist/client/index.d.ts +104 -7
  76. package/dist/client/index.js +10409 -10
  77. package/dist/client/liveCallViewer.d.ts +42 -0
  78. package/dist/client/liveOps.d.ts +22 -0
  79. package/dist/client/liveOpsWidget.d.ts +23 -0
  80. package/dist/client/liveTurnLatency.d.ts +41 -0
  81. package/dist/client/microphone.d.ts +4 -4
  82. package/dist/client/opsActionCenter.d.ts +54 -0
  83. package/dist/client/opsActionCenterWidget.d.ts +29 -0
  84. package/dist/client/opsActionHistory.d.ts +19 -0
  85. package/dist/client/opsActionHistoryWidget.d.ts +11 -0
  86. package/dist/client/opsStatus.d.ts +19 -0
  87. package/dist/client/opsStatusWidget.d.ts +40 -0
  88. package/dist/client/platformCoverage.d.ts +19 -0
  89. package/dist/client/platformCoverageWidget.d.ts +37 -0
  90. package/dist/client/profileComparison.d.ts +19 -0
  91. package/dist/client/profileComparisonWidget.d.ts +41 -0
  92. package/dist/client/profileSwitchRecommendation.d.ts +19 -0
  93. package/dist/client/profileSwitchRecommendationWidget.d.ts +12 -0
  94. package/dist/client/proofTrends.d.ts +19 -0
  95. package/dist/client/proofTrendsWidget.d.ts +37 -0
  96. package/dist/client/providerCapabilities.d.ts +19 -0
  97. package/dist/client/providerCapabilitiesWidget.d.ts +32 -0
  98. package/dist/client/providerContracts.d.ts +19 -0
  99. package/dist/client/providerContractsWidget.d.ts +37 -0
  100. package/dist/client/providerSimulationControls.d.ts +33 -0
  101. package/dist/client/providerSimulationControlsWidget.d.ts +20 -0
  102. package/dist/client/providerStatus.d.ts +19 -0
  103. package/dist/client/providerStatusWidget.d.ts +32 -0
  104. package/dist/client/readinessFailures.d.ts +19 -0
  105. package/dist/client/readinessFailuresWidget.d.ts +42 -0
  106. package/dist/client/reconnectProfileEvidence.d.ts +19 -0
  107. package/dist/client/reconnectProfileEvidenceWidget.d.ts +39 -0
  108. package/dist/client/replayTimeline.d.ts +26 -0
  109. package/dist/client/routingStatus.d.ts +19 -0
  110. package/dist/client/routingStatusWidget.d.ts +32 -0
  111. package/dist/client/sessionObservability.d.ts +19 -0
  112. package/dist/client/sessionObservabilityWidget.d.ts +31 -0
  113. package/dist/client/sessionSnapshot.d.ts +21 -0
  114. package/dist/client/sessionSnapshotWidget.d.ts +33 -0
  115. package/dist/client/store.d.ts +1 -1
  116. package/dist/client/traceTimeline.d.ts +19 -0
  117. package/dist/client/traceTimelineWidget.d.ts +36 -0
  118. package/dist/client/turnLatency.d.ts +22 -0
  119. package/dist/client/turnLatencyWidget.d.ts +33 -0
  120. package/dist/client/turnQuality.d.ts +19 -0
  121. package/dist/client/turnQualityWidget.d.ts +32 -0
  122. package/dist/client/voiceWidgetView.d.ts +50 -0
  123. package/dist/client/workflowStatus.d.ts +19 -0
  124. package/dist/competitiveCoverage.d.ts +141 -0
  125. package/dist/correction.d.ts +2 -2
  126. package/dist/costAccounting.d.ts +76 -0
  127. package/dist/dataControl.d.ts +180 -0
  128. package/dist/defineVoiceAssistant.d.ts +68 -0
  129. package/dist/deliveryRuntime.d.ts +158 -0
  130. package/dist/deliverySinkRoutes.d.ts +117 -0
  131. package/dist/demoReadyRoutes.d.ts +98 -0
  132. package/dist/diagnosticsRoutes.d.ts +44 -0
  133. package/dist/evalRoutes.d.ts +219 -0
  134. package/dist/fileStore.d.ts +21 -7
  135. package/dist/generated/htmxBootstrapBundle.d.ts +1 -0
  136. package/dist/guardrails.d.ts +128 -0
  137. package/dist/handoff.d.ts +54 -0
  138. package/dist/handoffHealth.d.ts +94 -0
  139. package/dist/htmx.d.ts +1 -1
  140. package/dist/incidentBundle.d.ts +119 -0
  141. package/dist/incidentTimeline.d.ts +260 -0
  142. package/dist/index.d.ts +278 -46
  143. package/dist/index.js +42417 -4242
  144. package/dist/ivrPlan.d.ts +40 -0
  145. package/dist/latencySlo.d.ts +56 -0
  146. package/dist/liveLatency.d.ts +78 -0
  147. package/dist/liveOps.d.ts +190 -0
  148. package/dist/llmJudge.d.ts +45 -0
  149. package/dist/logger.d.ts +1 -1
  150. package/dist/mediaPipelineRoutes.d.ts +171 -0
  151. package/dist/mediaPipelineSurfaces.d.ts +48 -0
  152. package/dist/memoryStore.d.ts +1 -1
  153. package/dist/midCallSummary.d.ts +27 -0
  154. package/dist/modelAdapters.d.ts +135 -2
  155. package/dist/monitor.d.ts +148 -0
  156. package/dist/multilingualProof.d.ts +77 -0
  157. package/dist/oauth2TokenSource.d.ts +21 -0
  158. package/dist/observabilityExport.d.ts +501 -0
  159. package/dist/openaiTTS.d.ts +18 -0
  160. package/dist/operationalStatus.d.ts +87 -0
  161. package/dist/operationsRecord.d.ts +370 -0
  162. package/dist/ops.d.ts +12 -12
  163. package/dist/opsActionAuditRoutes.d.ts +99 -0
  164. package/dist/opsConsoleRoutes.d.ts +80 -0
  165. package/dist/opsPresets.d.ts +2 -2
  166. package/dist/opsRecovery.d.ts +137 -0
  167. package/dist/opsRuntime.d.ts +6 -6
  168. package/dist/opsSinks.d.ts +13 -13
  169. package/dist/opsStatus.d.ts +76 -0
  170. package/dist/opsStatusRoutes.d.ts +33 -0
  171. package/dist/opsWebhook.d.ts +126 -0
  172. package/dist/otelExporter.d.ts +83 -0
  173. package/dist/outcomeContract.d.ts +146 -0
  174. package/dist/outcomeRecipes.d.ts +4 -4
  175. package/dist/phoneAgent.d.ts +139 -0
  176. package/dist/phoneAgentProductionSmoke.d.ts +115 -0
  177. package/dist/platformCoverage.d.ts +91 -0
  178. package/dist/plugin.d.ts +2 -2
  179. package/dist/postCallAnalysis.d.ts +98 -0
  180. package/dist/postgresStore.d.ts +17 -6
  181. package/dist/presets.d.ts +3 -3
  182. package/dist/productionReadiness.d.ts +756 -0
  183. package/dist/profileSwitchRecommendation.d.ts +350 -0
  184. package/dist/proofAssertions.d.ts +32 -0
  185. package/dist/proofPack.d.ts +211 -0
  186. package/dist/proofRunner.d.ts +79 -0
  187. package/dist/proofTrends.d.ts +966 -0
  188. package/dist/providerAdapters.d.ts +48 -0
  189. package/dist/providerCapabilities.d.ts +92 -0
  190. package/dist/providerDecisionTraces.d.ts +130 -0
  191. package/dist/providerHealth.d.ts +79 -0
  192. package/dist/providerOrchestration.d.ts +109 -0
  193. package/dist/providerRouterTraces.d.ts +35 -0
  194. package/dist/providerRoutingContract.d.ts +71 -0
  195. package/dist/providerSlo.d.ts +142 -0
  196. package/dist/providerStackRecommendations.d.ts +187 -0
  197. package/dist/qualityRoutes.d.ts +76 -0
  198. package/dist/queue.d.ts +72 -11
  199. package/dist/ragTool.d.ts +57 -0
  200. package/dist/react/VoiceAgentSquadStatus.d.ts +5 -0
  201. package/dist/react/VoiceCallDebuggerLaunch.d.ts +6 -0
  202. package/dist/react/VoiceCostDashboard.d.ts +10 -0
  203. package/dist/react/VoiceDeliveryRuntime.d.ts +7 -0
  204. package/dist/react/VoiceLiveCallViewer.d.ts +9 -0
  205. package/dist/react/VoiceOpsActionCenter.d.ts +5 -0
  206. package/dist/react/VoiceOpsStatus.d.ts +6 -0
  207. package/dist/react/VoicePlatformCoverage.d.ts +6 -0
  208. package/dist/react/VoiceProfileComparison.d.ts +6 -0
  209. package/dist/react/VoiceProfileSwitchRecommendation.d.ts +6 -0
  210. package/dist/react/VoiceProofTrends.d.ts +6 -0
  211. package/dist/react/VoiceProviderCapabilities.d.ts +6 -0
  212. package/dist/react/VoiceProviderContracts.d.ts +6 -0
  213. package/dist/react/VoiceProviderSimulationControls.d.ts +5 -0
  214. package/dist/react/VoiceProviderStatus.d.ts +6 -0
  215. package/dist/react/VoiceReadinessFailures.d.ts +6 -0
  216. package/dist/react/VoiceReconnectProfileEvidence.d.ts +6 -0
  217. package/dist/react/VoiceReplayTimeline.d.ts +6 -0
  218. package/dist/react/VoiceRoutingStatus.d.ts +6 -0
  219. package/dist/react/VoiceSessionObservability.d.ts +6 -0
  220. package/dist/react/VoiceSessionSnapshot.d.ts +6 -0
  221. package/dist/react/VoiceTraceTimeline.d.ts +6 -0
  222. package/dist/react/VoiceTurnLatency.d.ts +6 -0
  223. package/dist/react/VoiceTurnQuality.d.ts +6 -0
  224. package/dist/react/VoiceWidget.d.ts +13 -0
  225. package/dist/react/index.d.ts +55 -2
  226. package/dist/react/index.js +12290 -15
  227. package/dist/react/useVoiceAgentSquadStatus.d.ts +8 -0
  228. package/dist/react/useVoiceCallDebugger.d.ts +8 -0
  229. package/dist/react/useVoiceCampaignDialerProof.d.ts +10 -0
  230. package/dist/react/useVoiceController.d.ts +6 -1
  231. package/dist/react/useVoiceDeliveryRuntime.d.ts +13 -0
  232. package/dist/react/useVoiceLiveOps.d.ts +9 -0
  233. package/dist/react/useVoiceOpsActionCenter.d.ts +11 -0
  234. package/dist/react/useVoiceOpsStatus.d.ts +8 -0
  235. package/dist/react/useVoicePlatformCoverage.d.ts +8 -0
  236. package/dist/react/useVoiceProfileComparison.d.ts +8 -0
  237. package/dist/react/useVoiceProfileSwitchRecommendation.d.ts +8 -0
  238. package/dist/react/useVoiceProofTrends.d.ts +8 -0
  239. package/dist/react/useVoiceProviderCapabilities.d.ts +8 -0
  240. package/dist/react/useVoiceProviderContracts.d.ts +8 -0
  241. package/dist/react/useVoiceProviderSimulationControls.d.ts +10 -0
  242. package/dist/react/useVoiceProviderStatus.d.ts +8 -0
  243. package/dist/react/useVoiceReadinessFailures.d.ts +8 -0
  244. package/dist/react/useVoiceReconnectProfileEvidence.d.ts +8 -0
  245. package/dist/react/useVoiceRoutingStatus.d.ts +8 -0
  246. package/dist/react/useVoiceSessionObservability.d.ts +8 -0
  247. package/dist/react/useVoiceSessionSnapshot.d.ts +9 -0
  248. package/dist/react/useVoiceStream.d.ts +6 -1
  249. package/dist/react/useVoiceTraceTimeline.d.ts +8 -0
  250. package/dist/react/useVoiceTurnLatency.d.ts +9 -0
  251. package/dist/react/useVoiceTurnQuality.d.ts +8 -0
  252. package/dist/react/useVoiceWorkflowStatus.d.ts +8 -0
  253. package/dist/readinessProfiles.d.ts +45 -0
  254. package/dist/realtimeChannel.d.ts +136 -0
  255. package/dist/realtimeProviderContracts.d.ts +133 -0
  256. package/dist/reconnectContract.d.ts +176 -0
  257. package/dist/recordingRedaction.d.ts +18 -0
  258. package/dist/recordingStore.d.ts +60 -0
  259. package/dist/redaction.d.ts +13 -0
  260. package/dist/resilienceRoutes.d.ts +146 -0
  261. package/dist/retention.d.ts +37 -0
  262. package/dist/routeAuth.d.ts +58 -0
  263. package/dist/routing.d.ts +1 -1
  264. package/dist/runtimeOps.d.ts +3 -3
  265. package/dist/s3Store.d.ts +18 -3
  266. package/dist/semanticTurn.d.ts +27 -0
  267. package/dist/session.d.ts +1 -1
  268. package/dist/sessionObservability.d.ts +145 -0
  269. package/dist/sessionReplay.d.ts +187 -0
  270. package/dist/sessionSnapshot.d.ts +109 -0
  271. package/dist/simulationSuite.d.ts +143 -0
  272. package/dist/sloCalibration.d.ts +185 -0
  273. package/dist/sqliteStore.d.ts +17 -6
  274. package/dist/store.d.ts +1 -1
  275. package/dist/svelte/createVoiceAgentSquadStatus.d.ts +9 -0
  276. package/dist/svelte/createVoiceCallDebugger.d.ts +12 -0
  277. package/dist/svelte/createVoiceCampaignDialerProof.d.ts +9 -0
  278. package/dist/svelte/createVoiceDeliveryRuntime.d.ts +11 -0
  279. package/dist/svelte/createVoiceLiveOps.d.ts +13 -0
  280. package/dist/svelte/createVoiceOpsActionCenter.d.ts +10 -0
  281. package/dist/svelte/createVoiceOpsStatus.d.ts +9 -0
  282. package/dist/svelte/createVoicePlatformCoverage.d.ts +7 -0
  283. package/dist/svelte/createVoiceProfileComparison.d.ts +7 -0
  284. package/dist/svelte/createVoiceProofTrends.d.ts +7 -0
  285. package/dist/svelte/createVoiceProviderCapabilities.d.ts +10 -0
  286. package/dist/svelte/createVoiceProviderContracts.d.ts +10 -0
  287. package/dist/svelte/createVoiceProviderSimulationControls.d.ts +11 -0
  288. package/dist/svelte/createVoiceProviderStatus.d.ts +10 -0
  289. package/dist/svelte/createVoiceReadinessFailures.d.ts +7 -0
  290. package/dist/svelte/createVoiceReconnectProfileEvidence.d.ts +7 -0
  291. package/dist/svelte/createVoiceRoutingStatus.d.ts +10 -0
  292. package/dist/svelte/createVoiceSessionObservability.d.ts +10 -0
  293. package/dist/svelte/createVoiceSessionSnapshot.d.ts +13 -0
  294. package/dist/svelte/createVoiceStream.d.ts +1 -1
  295. package/dist/svelte/createVoiceTraceTimeline.d.ts +10 -0
  296. package/dist/svelte/createVoiceTurnLatency.d.ts +11 -0
  297. package/dist/svelte/createVoiceTurnQuality.d.ts +10 -0
  298. package/dist/svelte/createVoiceWidget.d.ts +19 -0
  299. package/dist/svelte/createVoiceWorkflowStatus.d.ts +8 -0
  300. package/dist/svelte/index.d.ts +27 -2
  301. package/dist/svelte/index.js +6010 -150
  302. package/dist/telephony/contract.d.ts +61 -0
  303. package/dist/telephony/matrix.d.ts +97 -0
  304. package/dist/telephony/plivo.d.ts +303 -0
  305. package/dist/telephony/response.d.ts +1 -1
  306. package/dist/telephony/security.d.ts +182 -0
  307. package/dist/telephony/telnyx.d.ts +291 -0
  308. package/dist/telephony/twilio.d.ts +147 -13
  309. package/dist/telephonyMediaRoutes.d.ts +72 -0
  310. package/dist/telephonyOutcome.d.ts +273 -0
  311. package/dist/testing/accuracy.d.ts +1 -1
  312. package/dist/testing/benchmark.d.ts +9 -9
  313. package/dist/testing/corrected.d.ts +5 -5
  314. package/dist/testing/duplex.d.ts +3 -3
  315. package/dist/testing/fixtures.d.ts +3 -3
  316. package/dist/testing/index.d.ts +13 -11
  317. package/dist/testing/index.js +9117 -2592
  318. package/dist/testing/ioProviderSimulator.d.ts +41 -0
  319. package/dist/testing/providerSimulator.d.ts +44 -0
  320. package/dist/testing/review.d.ts +4 -4
  321. package/dist/testing/sessionBenchmark.d.ts +3 -3
  322. package/dist/testing/stt.d.ts +3 -3
  323. package/dist/testing/telephony.d.ts +25 -0
  324. package/dist/testing/tts.d.ts +1 -1
  325. package/dist/toolContract.d.ts +161 -0
  326. package/dist/toolRuntime.d.ts +50 -0
  327. package/dist/trace.d.ts +46 -7
  328. package/dist/traceDeliveryRoutes.d.ts +86 -0
  329. package/dist/traceTimeline.d.ts +97 -0
  330. package/dist/turnDetection.d.ts +1 -1
  331. package/dist/turnLatency.d.ts +95 -0
  332. package/dist/turnProfiles.d.ts +2 -2
  333. package/dist/turnQuality.d.ts +94 -0
  334. package/dist/types.d.ts +366 -71
  335. package/dist/vapiAdapter.d.ts +160 -0
  336. package/dist/voiceMonitoring.d.ts +444 -0
  337. package/dist/vue/VoiceCallDebuggerLaunch.d.ts +68 -0
  338. package/dist/vue/VoiceDeliveryRuntime.d.ts +30 -0
  339. package/dist/vue/VoiceOpsActionCenter.d.ts +13 -0
  340. package/dist/vue/VoiceOpsStatus.d.ts +30 -0
  341. package/dist/vue/VoicePlatformCoverage.d.ts +23 -0
  342. package/dist/vue/VoiceProofTrends.d.ts +21 -0
  343. package/dist/vue/VoiceProviderCapabilities.d.ts +51 -0
  344. package/dist/vue/VoiceProviderContracts.d.ts +21 -0
  345. package/dist/vue/VoiceProviderSimulationControls.d.ts +88 -0
  346. package/dist/vue/VoiceProviderStatus.d.ts +51 -0
  347. package/dist/vue/VoiceReadinessFailures.d.ts +21 -0
  348. package/dist/vue/VoiceReconnectProfileEvidence.d.ts +21 -0
  349. package/dist/vue/VoiceRoutingStatus.d.ts +51 -0
  350. package/dist/vue/VoiceSessionObservability.d.ts +23 -0
  351. package/dist/vue/VoiceSessionSnapshot.d.ts +68 -0
  352. package/dist/vue/VoiceTurnLatency.d.ts +69 -0
  353. package/dist/vue/VoiceTurnQuality.d.ts +51 -0
  354. package/dist/vue/VoiceWidget.d.ts +77 -0
  355. package/dist/vue/index.d.ts +44 -2
  356. package/dist/vue/index.js +11027 -31
  357. package/dist/vue/useVoiceAgentSquadStatus.d.ts +9 -0
  358. package/dist/vue/useVoiceCallDebugger.d.ts +10 -0
  359. package/dist/vue/useVoiceCampaignDialerProof.d.ts +11 -0
  360. package/dist/vue/useVoiceController.d.ts +4 -2
  361. package/dist/vue/useVoiceDeliveryRuntime.d.ts +13 -0
  362. package/dist/vue/useVoiceLiveOps.d.ts +9 -0
  363. package/dist/vue/useVoiceOpsActionCenter.d.ts +11 -0
  364. package/dist/vue/useVoiceOpsStatus.d.ts +9 -0
  365. package/dist/vue/useVoicePlatformCoverage.d.ts +9 -0
  366. package/dist/vue/useVoiceProfileComparison.d.ts +9 -0
  367. package/dist/vue/useVoiceProofTrends.d.ts +9 -0
  368. package/dist/vue/useVoiceProviderCapabilities.d.ts +9 -0
  369. package/dist/vue/useVoiceProviderContracts.d.ts +9 -0
  370. package/dist/vue/useVoiceProviderSimulationControls.d.ts +24 -0
  371. package/dist/vue/useVoiceProviderStatus.d.ts +9 -0
  372. package/dist/vue/useVoiceReadinessFailures.d.ts +959 -0
  373. package/dist/vue/useVoiceReconnectProfileEvidence.d.ts +9 -0
  374. package/dist/vue/useVoiceRoutingStatus.d.ts +8 -0
  375. package/dist/vue/useVoiceSessionObservability.d.ts +9 -0
  376. package/dist/vue/useVoiceSessionSnapshot.d.ts +10 -0
  377. package/dist/vue/useVoiceStream.d.ts +7 -2
  378. package/dist/vue/useVoiceTraceTimeline.d.ts +9 -0
  379. package/dist/vue/useVoiceTurnLatency.d.ts +10 -0
  380. package/dist/vue/useVoiceTurnQuality.d.ts +9 -0
  381. package/dist/vue/useVoiceWorkflowStatus.d.ts +9 -0
  382. package/dist/webhookVerification.d.ts +27 -0
  383. package/dist/workflowContract.d.ts +91 -0
  384. package/fixtures/manifest.json +356 -197
  385. package/package.json +265 -256
@@ -188,6 +188,17 @@ var serverMessageToAction = (message) => {
188
188
  sessionId: message.sessionId,
189
189
  type: "complete"
190
190
  };
191
+ case "connection":
192
+ return {
193
+ reconnect: message.reconnect,
194
+ type: "connection"
195
+ };
196
+ case "call_lifecycle":
197
+ return {
198
+ event: message.event,
199
+ sessionId: message.sessionId,
200
+ type: "call_lifecycle"
201
+ };
191
202
  case "error":
192
203
  return {
193
204
  message: normalizeErrorMessage(message.message),
@@ -203,9 +214,22 @@ var serverMessageToAction = (message) => {
203
214
  transcript: message.transcript,
204
215
  type: "partial"
205
216
  };
217
+ case "replay":
218
+ return {
219
+ assistantTexts: message.assistantTexts,
220
+ call: message.call,
221
+ partial: message.partial,
222
+ scenarioId: message.scenarioId,
223
+ sessionId: message.sessionId,
224
+ sessionMetadata: message.sessionMetadata,
225
+ status: message.status,
226
+ turns: message.turns,
227
+ type: "replay"
228
+ };
206
229
  case "session":
207
230
  return {
208
231
  sessionId: message.sessionId,
232
+ sessionMetadata: message.sessionMetadata,
209
233
  scenarioId: message.scenarioId,
210
234
  status: message.status,
211
235
  type: "session"
@@ -220,6 +244,232 @@ var serverMessageToAction = (message) => {
220
244
  }
221
245
  };
222
246
 
247
+ // node_modules/@absolutejs/media/dist/index.js
248
+ var pushIssue = (issues, severity, code, message) => {
249
+ issues.push({ code, message, severity });
250
+ };
251
+ var average = (values) => values.length === 0 ? undefined : values.reduce((total, value) => total + value, 0) / values.length;
252
+ var max = (values) => values.length === 0 ? undefined : Math.max(...values);
253
+ var numericStat = (stat, key) => {
254
+ const value = stat[key];
255
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
256
+ };
257
+ var booleanStat = (stat, key) => {
258
+ const value = stat[key];
259
+ return typeof value === "boolean" ? value : undefined;
260
+ };
261
+ var stringStat = (stat, key) => {
262
+ const value = stat[key];
263
+ return typeof value === "string" ? value : undefined;
264
+ };
265
+ var statKey = (stat) => String(stat.id ?? stringStat(stat, "ssrc") ?? numericStat(stat, "ssrc") ?? stringStat(stat, "trackIdentifier") ?? stringStat(stat, "mid") ?? "unknown");
266
+ var secondsToMs = (value) => value === undefined ? undefined : value * 1000;
267
+ var normalizeWebRTCStat = (stat) => {
268
+ const sample = {};
269
+ for (const [key, value] of Object.entries(stat)) {
270
+ if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
271
+ sample[key] = value;
272
+ }
273
+ }
274
+ return sample;
275
+ };
276
+ var buildMediaWebRTCStatsReport = (input = {}) => {
277
+ const stats = input.stats ?? [];
278
+ const issues = [];
279
+ const inbound = stats.filter((stat) => stat.type === "inbound-rtp" && stringStat(stat, "kind") !== "video");
280
+ const outbound = stats.filter((stat) => stat.type === "outbound-rtp" && stringStat(stat, "kind") !== "video");
281
+ const candidatePairs = stats.filter((stat) => stat.type === "candidate-pair");
282
+ const audioTracks = stats.filter((stat) => (stat.type === "track" || stat.type === "media-source") && stringStat(stat, "kind") === "audio");
283
+ const activeCandidatePairs = candidatePairs.filter((stat) => booleanStat(stat, "selected") === true || booleanStat(stat, "nominated") === true || stringStat(stat, "state") === "succeeded").length;
284
+ const liveAudioTracks = audioTracks.filter((stat) => stringStat(stat, "readyState") !== "ended" && stringStat(stat, "trackState") !== "ended" && booleanStat(stat, "ended") !== true).length;
285
+ const endedAudioTracks = audioTracks.filter((stat) => stringStat(stat, "readyState") === "ended" || stringStat(stat, "trackState") === "ended" || booleanStat(stat, "ended") === true).length;
286
+ const inboundPackets = inbound.reduce((total, stat) => total + (numericStat(stat, "packetsReceived") ?? 0), 0);
287
+ const outboundPackets = outbound.reduce((total, stat) => total + (numericStat(stat, "packetsSent") ?? 0), 0);
288
+ const packetsLost = [...inbound, ...outbound].reduce((total, stat) => total + Math.max(0, numericStat(stat, "packetsLost") ?? 0), 0);
289
+ const packetLossDenominator = inboundPackets + packetsLost;
290
+ const packetLossRatio = packetLossDenominator === 0 ? 0 : packetsLost / packetLossDenominator;
291
+ const bytesReceived = inbound.reduce((total, stat) => total + (numericStat(stat, "bytesReceived") ?? 0), 0);
292
+ const bytesSent = outbound.reduce((total, stat) => total + (numericStat(stat, "bytesSent") ?? 0), 0);
293
+ const roundTripTimeMs = max(candidatePairs.map((stat) => secondsToMs(numericStat(stat, "currentRoundTripTime") ?? numericStat(stat, "roundTripTime"))).filter((value) => value !== undefined));
294
+ const jitterMs = max([...inbound, ...outbound].map((stat) => secondsToMs(numericStat(stat, "jitter"))).filter((value) => value !== undefined));
295
+ const jitterBufferDelayMs = max(inbound.map((stat) => {
296
+ const delay = numericStat(stat, "jitterBufferDelay");
297
+ const emitted = numericStat(stat, "jitterBufferEmittedCount");
298
+ return delay !== undefined && emitted !== undefined && emitted > 0 ? delay / emitted * 1000 : undefined;
299
+ }).filter((value) => value !== undefined));
300
+ const audioLevels = audioTracks.map((stat) => numericStat(stat, "audioLevel")).filter((value) => value !== undefined);
301
+ if (input.requireConnectedCandidatePair && candidatePairs.length > 0 && activeCandidatePairs === 0) {
302
+ pushIssue(issues, "error", "media.webrtc_candidate_pair_missing", "No active WebRTC candidate pair was observed.");
303
+ }
304
+ if (input.requireLiveAudioTrack && liveAudioTracks === 0) {
305
+ pushIssue(issues, "error", "media.webrtc_audio_track_missing", "No live WebRTC audio track was observed.");
306
+ }
307
+ if (input.maxPacketLossRatio !== undefined && packetLossRatio > input.maxPacketLossRatio) {
308
+ pushIssue(issues, "warning", "media.webrtc_packet_loss", `Observed WebRTC packet loss ratio ${String(packetLossRatio)} above ${String(input.maxPacketLossRatio)}.`);
309
+ }
310
+ if (input.maxRoundTripTimeMs !== undefined && roundTripTimeMs !== undefined && roundTripTimeMs > input.maxRoundTripTimeMs) {
311
+ pushIssue(issues, "warning", "media.webrtc_round_trip_time", `Observed WebRTC RTT ${String(roundTripTimeMs)}ms above ${String(input.maxRoundTripTimeMs)}ms.`);
312
+ }
313
+ if (input.maxJitterMs !== undefined && jitterMs !== undefined && jitterMs > input.maxJitterMs) {
314
+ pushIssue(issues, "warning", "media.webrtc_jitter", `Observed WebRTC jitter ${String(jitterMs)}ms above ${String(input.maxJitterMs)}ms.`);
315
+ }
316
+ return {
317
+ activeCandidatePairs,
318
+ audioLevelAverage: average(audioLevels),
319
+ bytesReceived,
320
+ bytesSent,
321
+ checkedAt: Date.now(),
322
+ endedAudioTracks,
323
+ inboundPackets,
324
+ issues,
325
+ jitterBufferDelayMs,
326
+ jitterMs,
327
+ liveAudioTracks,
328
+ outboundPackets,
329
+ packetLossRatio,
330
+ packetsLost,
331
+ roundTripTimeMs,
332
+ status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
333
+ totalStats: stats.length
334
+ };
335
+ };
336
+ var collectMediaWebRTCStats = async (input) => {
337
+ const report = await input.peerConnection.getStats(input.selector ?? null);
338
+ return [...report.values()].map(normalizeWebRTCStat);
339
+ };
340
+ var buildMediaWebRTCStreamContinuityReport = (input = {}) => {
341
+ const stats = input.stats ?? [];
342
+ const previousStats = input.previousStats ?? [];
343
+ const issues = [];
344
+ const previousByKey = new Map(previousStats.map((stat) => [statKey(stat), stat]));
345
+ const audioRtp = stats.filter((stat) => (stat.type === "inbound-rtp" || stat.type === "outbound-rtp") && stringStat(stat, "kind") !== "video" && stringStat(stat, "mediaType") !== "video");
346
+ const streams = audioRtp.map((stat) => {
347
+ const direction = stat.type === "outbound-rtp" ? "outbound" : "inbound";
348
+ const packetsKey = direction === "outbound" ? "packetsSent" : "packetsReceived";
349
+ const bytesKey = direction === "outbound" ? "bytesSent" : "bytesReceived";
350
+ const previous = previousByKey.get(statKey(stat));
351
+ const currentPackets = numericStat(stat, packetsKey);
352
+ const previousPackets = previous ? numericStat(previous, packetsKey) : undefined;
353
+ const currentBytes = numericStat(stat, bytesKey);
354
+ const previousBytes = previous ? numericStat(previous, bytesKey) : undefined;
355
+ const timeDeltaMs = stat.timestamp !== undefined && previous?.timestamp !== undefined ? stat.timestamp - previous.timestamp : undefined;
356
+ return {
357
+ bytesDelta: currentBytes !== undefined && previousBytes !== undefined ? currentBytes - previousBytes : undefined,
358
+ currentPackets,
359
+ direction,
360
+ id: statKey(stat),
361
+ packetDelta: currentPackets !== undefined && previousPackets !== undefined ? currentPackets - previousPackets : undefined,
362
+ previousPackets,
363
+ timeDeltaMs
364
+ };
365
+ });
366
+ const inbound = streams.filter((stream) => stream.direction === "inbound");
367
+ const outbound = streams.filter((stream) => stream.direction === "outbound");
368
+ const maxObservedGapMs = max(streams.map((stream) => stream.timeDeltaMs).filter((value) => value !== undefined));
369
+ const stalledInboundStreams = inbound.filter((stream) => input.maxInboundPacketStallMs !== undefined && stream.timeDeltaMs !== undefined && stream.timeDeltaMs >= input.maxInboundPacketStallMs && stream.packetDelta !== undefined && stream.packetDelta <= 0).length;
370
+ const stalledOutboundStreams = outbound.filter((stream) => input.maxOutboundPacketStallMs !== undefined && stream.timeDeltaMs !== undefined && stream.timeDeltaMs >= input.maxOutboundPacketStallMs && stream.packetDelta !== undefined && stream.packetDelta <= 0).length;
371
+ if (input.requireInboundAudio && inbound.length === 0) {
372
+ pushIssue(issues, "error", "media.webrtc_inbound_audio_missing", "No inbound WebRTC audio RTP stream was observed.");
373
+ }
374
+ if (input.requireOutboundAudio && outbound.length === 0) {
375
+ pushIssue(issues, "error", "media.webrtc_outbound_audio_missing", "No outbound WebRTC audio RTP stream was observed.");
376
+ }
377
+ if (input.maxGapMs !== undefined && maxObservedGapMs !== undefined && maxObservedGapMs > input.maxGapMs) {
378
+ pushIssue(issues, "warning", "media.webrtc_stream_gap", `Observed WebRTC stream sample gap ${String(maxObservedGapMs)}ms above ${String(input.maxGapMs)}ms.`);
379
+ }
380
+ if (stalledInboundStreams > 0) {
381
+ pushIssue(issues, "error", "media.webrtc_inbound_stalled", `${String(stalledInboundStreams)} inbound WebRTC audio stream(s) stopped receiving packets.`);
382
+ }
383
+ if (stalledOutboundStreams > 0) {
384
+ pushIssue(issues, "error", "media.webrtc_outbound_stalled", `${String(stalledOutboundStreams)} outbound WebRTC audio stream(s) stopped sending packets.`);
385
+ }
386
+ return {
387
+ checkedAt: Date.now(),
388
+ inboundAudioStreams: inbound.length,
389
+ issues,
390
+ maxObservedGapMs,
391
+ outboundAudioStreams: outbound.length,
392
+ stalledInboundStreams,
393
+ stalledOutboundStreams,
394
+ status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
395
+ streams,
396
+ totalStats: stats.length
397
+ };
398
+ };
399
+
400
+ // src/client/browserMedia.ts
401
+ var DEFAULT_BROWSER_MEDIA_PATH = "/api/voice/browser-media";
402
+ var DEFAULT_BROWSER_MEDIA_INTERVAL_MS = 5000;
403
+ var resolvePeerConnection = async (options) => options.peerConnection ?? await options.getPeerConnection?.() ?? null;
404
+ var postBrowserMediaReport = async (payload, options) => {
405
+ const requestFetch = options.fetch ?? globalThis.fetch;
406
+ if (!requestFetch) {
407
+ return;
408
+ }
409
+ await requestFetch(options.path ?? DEFAULT_BROWSER_MEDIA_PATH, {
410
+ body: JSON.stringify(payload),
411
+ headers: {
412
+ "Content-Type": "application/json"
413
+ },
414
+ keepalive: true,
415
+ method: "POST"
416
+ });
417
+ };
418
+ var createVoiceBrowserMediaReporter = (options) => {
419
+ let interval = null;
420
+ let previousStats = [];
421
+ const reportOnce = async () => {
422
+ const peerConnection = await resolvePeerConnection(options);
423
+ if (!peerConnection) {
424
+ return;
425
+ }
426
+ const stats = await collectMediaWebRTCStats({ peerConnection });
427
+ const report = buildMediaWebRTCStatsReport({
428
+ ...options,
429
+ stats
430
+ });
431
+ const continuity = options.continuity === false ? undefined : buildMediaWebRTCStreamContinuityReport({
432
+ ...options.continuity,
433
+ previousStats,
434
+ stats
435
+ });
436
+ const payload = {
437
+ at: Date.now(),
438
+ continuity,
439
+ report,
440
+ scenarioId: options.getScenarioId?.() ?? null,
441
+ sessionId: options.getSessionId?.() ?? null
442
+ };
443
+ previousStats = stats;
444
+ options.onReport?.(payload);
445
+ await postBrowserMediaReport(payload, options);
446
+ return payload;
447
+ };
448
+ const run = () => {
449
+ reportOnce().catch((error) => {
450
+ options.onError?.(error);
451
+ });
452
+ };
453
+ const stop = () => {
454
+ if (interval) {
455
+ clearInterval(interval);
456
+ interval = null;
457
+ }
458
+ };
459
+ return {
460
+ close: stop,
461
+ reportOnce,
462
+ start: () => {
463
+ if (interval) {
464
+ return;
465
+ }
466
+ run();
467
+ interval = setInterval(run, options.intervalMs ?? DEFAULT_BROWSER_MEDIA_INTERVAL_MS);
468
+ },
469
+ stop
470
+ };
471
+ };
472
+
223
473
  // src/client/connection.ts
224
474
  var WS_OPEN = 1;
225
475
  var WS_CLOSED = 3;
@@ -231,7 +481,7 @@ var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
231
481
  var noop = () => {};
232
482
  var noopUnsubscribe = () => noop;
233
483
  var NOOP_CONNECTION = {
234
- start: () => {},
484
+ callControl: noop,
235
485
  close: noop,
236
486
  endTurn: noop,
237
487
  getReadyState: () => WS_CLOSED,
@@ -239,6 +489,8 @@ var NOOP_CONNECTION = {
239
489
  getSessionId: () => "",
240
490
  send: noop,
241
491
  sendAudio: noop,
492
+ simulateDisconnect: noop,
493
+ start: () => {},
242
494
  subscribe: noopUnsubscribe
243
495
  };
244
496
  var createSessionId = () => crypto.randomUUID();
@@ -260,11 +512,14 @@ var isVoiceServerMessage = (value) => {
260
512
  switch (value.type) {
261
513
  case "audio":
262
514
  case "assistant":
515
+ case "call_lifecycle":
263
516
  case "complete":
517
+ case "connection":
264
518
  case "error":
265
519
  case "final":
266
520
  case "partial":
267
521
  case "pong":
522
+ case "replay":
268
523
  case "session":
269
524
  case "turn":
270
525
  return true;
@@ -301,6 +556,9 @@ var createVoiceConnection = (path, options = {}) => {
301
556
  sessionId: options.sessionId ?? createSessionId(),
302
557
  ws: null
303
558
  };
559
+ const emitConnection = (reconnect) => {
560
+ listeners.forEach((listener) => listener(reconnect));
561
+ };
304
562
  const clearTimers = () => {
305
563
  if (state.pingInterval) {
306
564
  clearInterval(state.pingInterval);
@@ -323,9 +581,28 @@ var createVoiceConnection = (path, options = {}) => {
323
581
  }
324
582
  };
325
583
  const scheduleReconnect = () => {
584
+ const nextAttemptAt = Date.now() + RECONNECT_DELAY_MS;
326
585
  state.reconnectAttempts += 1;
586
+ emitConnection({
587
+ reconnect: {
588
+ attempts: state.reconnectAttempts,
589
+ lastDisconnectAt: Date.now(),
590
+ maxAttempts: maxReconnectAttempts,
591
+ nextAttemptAt,
592
+ status: "reconnecting"
593
+ },
594
+ type: "connection"
595
+ });
327
596
  state.reconnectTimeout = setTimeout(() => {
328
597
  if (state.reconnectAttempts > maxReconnectAttempts) {
598
+ emitConnection({
599
+ reconnect: {
600
+ attempts: state.reconnectAttempts,
601
+ maxAttempts: maxReconnectAttempts,
602
+ status: "exhausted"
603
+ },
604
+ type: "connection"
605
+ });
329
606
  return;
330
607
  }
331
608
  connect();
@@ -335,9 +612,21 @@ var createVoiceConnection = (path, options = {}) => {
335
612
  const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
336
613
  ws.binaryType = "arraybuffer";
337
614
  ws.onopen = () => {
615
+ const wasReconnecting = state.reconnectAttempts > 0;
338
616
  state.isConnected = true;
339
- state.reconnectAttempts = 0;
340
617
  flushPendingMessages();
618
+ if (wasReconnecting) {
619
+ emitConnection({
620
+ reconnect: {
621
+ attempts: state.reconnectAttempts,
622
+ lastResumedAt: Date.now(),
623
+ maxAttempts: maxReconnectAttempts,
624
+ status: "resumed"
625
+ },
626
+ type: "connection"
627
+ });
628
+ state.reconnectAttempts = 0;
629
+ }
341
630
  listeners.forEach((listener) => listener({
342
631
  scenarioId: state.scenarioId ?? undefined,
343
632
  sessionId: state.sessionId,
@@ -367,6 +656,16 @@ var createVoiceConnection = (path, options = {}) => {
367
656
  const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && state.reconnectAttempts < maxReconnectAttempts;
368
657
  if (reconnectable) {
369
658
  scheduleReconnect();
659
+ } else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
660
+ emitConnection({
661
+ reconnect: {
662
+ attempts: state.reconnectAttempts,
663
+ lastDisconnectAt: Date.now(),
664
+ maxAttempts: maxReconnectAttempts,
665
+ status: "exhausted"
666
+ },
667
+ type: "connection"
668
+ });
370
669
  }
371
670
  };
372
671
  state.ws = ws;
@@ -400,6 +699,12 @@ var createVoiceConnection = (path, options = {}) => {
400
699
  const endTurn = () => {
401
700
  send({ type: "end_turn" });
402
701
  };
702
+ const callControl = (message) => {
703
+ send({
704
+ ...message,
705
+ type: "call_control"
706
+ });
707
+ };
403
708
  const close = () => {
404
709
  clearTimers();
405
710
  if (state.ws) {
@@ -409,6 +714,11 @@ var createVoiceConnection = (path, options = {}) => {
409
714
  state.isConnected = false;
410
715
  listeners.clear();
411
716
  };
717
+ const simulateDisconnect = () => {
718
+ if (state.ws?.readyState === WS_OPEN) {
719
+ state.ws.close(4000, "absolutejs-voice-reconnect-proof");
720
+ }
721
+ };
412
722
  const subscribe = (callback) => {
413
723
  listeners.add(callback);
414
724
  return () => {
@@ -417,7 +727,7 @@ var createVoiceConnection = (path, options = {}) => {
417
727
  };
418
728
  connect();
419
729
  return {
420
- start,
730
+ callControl,
421
731
  close,
422
732
  endTurn,
423
733
  getReadyState: () => state.ws?.readyState ?? WS_CLOSED,
@@ -425,18 +735,28 @@ var createVoiceConnection = (path, options = {}) => {
425
735
  getSessionId: () => state.sessionId,
426
736
  send,
427
737
  sendAudio,
738
+ simulateDisconnect,
739
+ start,
428
740
  subscribe
429
741
  };
430
742
  };
431
743
 
432
744
  // src/client/store.ts
745
+ var createInitialReconnectState = () => ({
746
+ attempts: 0,
747
+ maxAttempts: 0,
748
+ status: "idle"
749
+ });
433
750
  var createInitialState = () => ({
434
751
  assistantAudio: [],
435
752
  assistantTexts: [],
753
+ call: null,
436
754
  error: null,
437
755
  isConnected: false,
756
+ sessionMetadata: null,
438
757
  scenarioId: null,
439
758
  partial: "",
759
+ reconnect: createInitialReconnectState(),
440
760
  sessionId: null,
441
761
  status: "idle",
442
762
  turns: []
@@ -476,10 +796,36 @@ var createVoiceStreamStore = () => {
476
796
  status: "completed"
477
797
  };
478
798
  break;
799
+ case "call_lifecycle":
800
+ state = {
801
+ ...state,
802
+ call: {
803
+ ...state.call,
804
+ disposition: action.event.type === "end" ? action.event.disposition : state.call?.disposition,
805
+ endedAt: action.event.type === "end" ? action.event.at : state.call?.endedAt,
806
+ events: [...state.call?.events ?? [], action.event],
807
+ lastEventAt: action.event.at,
808
+ startedAt: state.call?.startedAt ?? action.event.at
809
+ },
810
+ sessionId: action.sessionId
811
+ };
812
+ break;
479
813
  case "connected":
480
814
  state = {
481
815
  ...state,
482
- isConnected: true
816
+ isConnected: true,
817
+ reconnect: state.reconnect.status === "reconnecting" ? {
818
+ ...state.reconnect,
819
+ lastResumedAt: Date.now(),
820
+ nextAttemptAt: undefined,
821
+ status: "resumed"
822
+ } : state.reconnect
823
+ };
824
+ break;
825
+ case "connection":
826
+ state = {
827
+ ...state,
828
+ reconnect: action.reconnect
483
829
  };
484
830
  break;
485
831
  case "disconnected":
@@ -507,6 +853,27 @@ var createVoiceStreamStore = () => {
507
853
  partial: action.transcript.text
508
854
  };
509
855
  break;
856
+ case "replay":
857
+ state = {
858
+ ...state,
859
+ assistantTexts: [...action.assistantTexts],
860
+ call: action.call ?? null,
861
+ error: null,
862
+ isConnected: action.status === "active",
863
+ partial: action.partial,
864
+ reconnect: state.reconnect.status === "reconnecting" ? {
865
+ ...state.reconnect,
866
+ lastResumedAt: Date.now(),
867
+ nextAttemptAt: undefined,
868
+ status: "resumed"
869
+ } : state.reconnect,
870
+ scenarioId: action.scenarioId ?? state.scenarioId,
871
+ sessionId: action.sessionId,
872
+ sessionMetadata: action.sessionMetadata ?? state.sessionMetadata,
873
+ status: action.status,
874
+ turns: [...action.turns]
875
+ };
876
+ break;
510
877
  case "session":
511
878
  state = {
512
879
  ...state,
@@ -514,6 +881,7 @@ var createVoiceStreamStore = () => {
514
881
  scenarioId: action.scenarioId ?? state.scenarioId,
515
882
  isConnected: action.status === "active",
516
883
  sessionId: action.sessionId,
884
+ sessionMetadata: action.sessionMetadata ?? state.sessionMetadata,
517
885
  status: action.status
518
886
  };
519
887
  break;
@@ -544,26 +912,60 @@ var createVoiceStreamStore = () => {
544
912
  var createVoiceStream = (path, options = {}) => {
545
913
  const connection = createVoiceConnection(path, options);
546
914
  const store = createVoiceStreamStore();
915
+ const browserMediaReporter = options.browserMedia && typeof window !== "undefined" ? createVoiceBrowserMediaReporter({
916
+ ...options.browserMedia,
917
+ getScenarioId: () => options.browserMedia ? options.browserMedia.getScenarioId?.() ?? connection.getScenarioId() : connection.getScenarioId(),
918
+ getSessionId: () => options.browserMedia ? options.browserMedia.getSessionId?.() ?? connection.getSessionId() : connection.getSessionId()
919
+ }) : null;
547
920
  const subscribers = new Set;
548
921
  const start = (input) => Promise.resolve().then(() => {
549
922
  if (!input?.sessionId && !input?.scenarioId) {
550
923
  return;
551
924
  }
552
925
  connection.start(input);
926
+ browserMediaReporter?.start();
553
927
  });
554
928
  const notify = () => {
555
929
  subscribers.forEach((subscriber) => subscriber());
556
930
  };
931
+ const reportReconnect = () => {
932
+ if (!options.reconnectReportPath || typeof fetch === "undefined") {
933
+ return;
934
+ }
935
+ const snapshot = store.getSnapshot();
936
+ const body = JSON.stringify({
937
+ at: Date.now(),
938
+ reconnect: snapshot.reconnect,
939
+ scenarioId: snapshot.scenarioId,
940
+ sessionId: connection.getSessionId(),
941
+ turnIds: snapshot.turns.map((turn) => turn.id)
942
+ });
943
+ fetch(options.reconnectReportPath, {
944
+ body,
945
+ headers: {
946
+ "Content-Type": "application/json"
947
+ },
948
+ keepalive: true,
949
+ method: "POST"
950
+ }).catch(() => {});
951
+ };
557
952
  const unsubscribeConnection = connection.subscribe((message) => {
558
953
  const action = serverMessageToAction(message);
559
954
  if (action) {
560
955
  store.dispatch(action);
956
+ if (message.type === "connection") {
957
+ reportReconnect();
958
+ }
561
959
  notify();
562
960
  }
563
961
  });
564
962
  return {
963
+ callControl(message) {
964
+ connection.callControl(message);
965
+ },
565
966
  close() {
566
967
  unsubscribeConnection();
968
+ browserMediaReporter?.close();
567
969
  connection.close();
568
970
  store.dispatch({ type: "disconnected" });
569
971
  notify();
@@ -586,10 +988,16 @@ var createVoiceStream = (path, options = {}) => {
586
988
  get scenarioId() {
587
989
  return store.getSnapshot().scenarioId;
588
990
  },
991
+ get sessionMetadata() {
992
+ return store.getSnapshot().sessionMetadata;
993
+ },
589
994
  start,
590
995
  get partial() {
591
996
  return store.getSnapshot().partial;
592
997
  },
998
+ get reconnect() {
999
+ return store.getSnapshot().reconnect;
1000
+ },
593
1001
  get sessionId() {
594
1002
  return connection.getSessionId();
595
1003
  },
@@ -605,9 +1013,15 @@ var createVoiceStream = (path, options = {}) => {
605
1013
  get assistantAudio() {
606
1014
  return store.getSnapshot().assistantAudio;
607
1015
  },
1016
+ get call() {
1017
+ return store.getSnapshot().call;
1018
+ },
608
1019
  sendAudio(audio) {
609
1020
  connection.sendAudio(audio);
610
1021
  },
1022
+ simulateDisconnect() {
1023
+ connection.simulateDisconnect();
1024
+ },
611
1025
  subscribe(subscriber) {
612
1026
  subscribers.add(subscriber);
613
1027
  return () => {
@@ -900,12 +1314,15 @@ var resolveVoiceRuntimePreset = (name = "default") => {
900
1314
  var createInitialState2 = (stream) => ({
901
1315
  assistantAudio: [...stream.assistantAudio],
902
1316
  assistantTexts: [...stream.assistantTexts],
1317
+ call: stream.call,
903
1318
  error: stream.error,
904
1319
  isConnected: stream.isConnected,
905
1320
  isRecording: false,
906
1321
  partial: stream.partial,
1322
+ reconnect: stream.reconnect,
907
1323
  recordingError: null,
908
1324
  sessionId: stream.sessionId,
1325
+ sessionMetadata: stream.sessionMetadata,
909
1326
  scenarioId: stream.scenarioId,
910
1327
  status: stream.status,
911
1328
  turns: [...stream.turns]
@@ -929,10 +1346,13 @@ var createVoiceController = (path, options = {}) => {
929
1346
  ...state,
930
1347
  assistantAudio: [...stream.assistantAudio],
931
1348
  assistantTexts: [...stream.assistantTexts],
1349
+ call: stream.call,
932
1350
  error: stream.error,
933
1351
  isConnected: stream.isConnected,
934
1352
  partial: stream.partial,
1353
+ reconnect: stream.reconnect,
935
1354
  sessionId: stream.sessionId,
1355
+ sessionMetadata: stream.sessionMetadata,
936
1356
  scenarioId: stream.scenarioId,
937
1357
  status: stream.status,
938
1358
  turns: [...stream.turns]
@@ -956,7 +1376,13 @@ var createVoiceController = (path, options = {}) => {
956
1376
  capture = createMicrophoneCapture({
957
1377
  channelCount: options.capture?.channelCount ?? preset.capture.channelCount,
958
1378
  onLevel: options.capture?.onLevel,
959
- onAudio: (audio) => stream.sendAudio(audio),
1379
+ onAudio: (audio) => {
1380
+ if (options.capture?.onAudio) {
1381
+ options.capture.onAudio(audio, stream.sendAudio);
1382
+ return;
1383
+ }
1384
+ stream.sendAudio(audio);
1385
+ },
960
1386
  sampleRateHz: options.capture?.sampleRateHz ?? preset.capture.sampleRateHz
961
1387
  });
962
1388
  return capture;
@@ -1006,6 +1432,7 @@ var createVoiceController = (path, options = {}) => {
1006
1432
  bindHTMX(bindingOptions) {
1007
1433
  return bindVoiceHTMX(stream, bindingOptions);
1008
1434
  },
1435
+ callControl: (message) => stream.callControl(message),
1009
1436
  close,
1010
1437
  endTurn: () => stream.endTurn(),
1011
1438
  get error() {
@@ -1025,10 +1452,17 @@ var createVoiceController = (path, options = {}) => {
1025
1452
  get recordingError() {
1026
1453
  return state.recordingError;
1027
1454
  },
1455
+ get reconnect() {
1456
+ return state.reconnect;
1457
+ },
1028
1458
  sendAudio: (audio) => stream.sendAudio(audio),
1459
+ simulateDisconnect: () => stream.simulateDisconnect(),
1029
1460
  get sessionId() {
1030
1461
  return state.sessionId;
1031
1462
  },
1463
+ get sessionMetadata() {
1464
+ return state.sessionMetadata;
1465
+ },
1032
1466
  get scenarioId() {
1033
1467
  return state.scenarioId;
1034
1468
  },
@@ -1058,6 +1492,478 @@ var createVoiceController = (path, options = {}) => {
1058
1492
  },
1059
1493
  get assistantAudio() {
1060
1494
  return state.assistantAudio;
1495
+ },
1496
+ get call() {
1497
+ return state.call;
1498
+ }
1499
+ };
1500
+ };
1501
+
1502
+ // src/client/audioPlayer.ts
1503
+ var DEFAULT_LOOKAHEAD_MS = 15;
1504
+ var createInitialState3 = () => ({
1505
+ activeSourceCount: 0,
1506
+ error: null,
1507
+ isActive: false,
1508
+ isPlaying: false,
1509
+ lastInterruptLatencyMs: undefined,
1510
+ lastPlaybackStopLatencyMs: undefined,
1511
+ processedChunkCount: 0,
1512
+ queuedChunkCount: 0
1513
+ });
1514
+ var getAudioContextCtor = () => {
1515
+ if (typeof window === "undefined") {
1516
+ return typeof AudioContext === "undefined" ? undefined : AudioContext;
1517
+ }
1518
+ return window.AudioContext ?? window.webkitAudioContext;
1519
+ };
1520
+ var decodePCM16LEChunk = (audioContext, chunk) => {
1521
+ const format = chunk.format;
1522
+ if (format.container !== "raw" || format.encoding !== "pcm_s16le") {
1523
+ throw new Error(`Unsupported assistant audio format: ${format.container}/${format.encoding}`);
1524
+ }
1525
+ const bytes = chunk.chunk;
1526
+ const channels = Math.max(1, format.channels);
1527
+ const sampleCount = Math.floor(bytes.byteLength / 2);
1528
+ const frameCount = Math.max(1, Math.floor(sampleCount / channels));
1529
+ const audioBuffer = audioContext.createBuffer(channels, frameCount, format.sampleRateHz);
1530
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1531
+ for (let channelIndex = 0;channelIndex < channels; channelIndex += 1) {
1532
+ const channelData = audioBuffer.getChannelData(channelIndex);
1533
+ for (let frameIndex = 0;frameIndex < frameCount; frameIndex += 1) {
1534
+ const sampleIndex = frameIndex * channels + channelIndex;
1535
+ const sampleOffset = sampleIndex * 2;
1536
+ if (sampleOffset + 1 >= bytes.byteLength) {
1537
+ channelData[frameIndex] = 0;
1538
+ continue;
1539
+ }
1540
+ channelData[frameIndex] = view.getInt16(sampleOffset, true) / 32768;
1541
+ }
1542
+ }
1543
+ return audioBuffer;
1544
+ };
1545
+ var createVoiceAudioPlayer = (source, options = {}) => {
1546
+ const subscribers = new Set;
1547
+ const sourceNodes = new Set;
1548
+ const lookaheadSeconds = (options.lookaheadMs ?? DEFAULT_LOOKAHEAD_MS) / 1000;
1549
+ let state = createInitialState3();
1550
+ let audioContext = null;
1551
+ let outputNode = null;
1552
+ let queueEndTime = 0;
1553
+ let syncPromise = Promise.resolve();
1554
+ let interruptStartedAt = null;
1555
+ let interruptPromise = null;
1556
+ let resolveInterruptPromise = null;
1557
+ let interruptFallbackTimer = null;
1558
+ const notify = () => {
1559
+ for (const subscriber of subscribers) {
1560
+ subscriber();
1561
+ }
1562
+ };
1563
+ const setState = (next) => {
1564
+ state = {
1565
+ ...state,
1566
+ ...next
1567
+ };
1568
+ notify();
1569
+ };
1570
+ const clearError = () => {
1571
+ if (state.error !== null) {
1572
+ setState({ error: null });
1573
+ }
1574
+ };
1575
+ const clearInterruptTimer = () => {
1576
+ if (interruptFallbackTimer !== null) {
1577
+ clearTimeout(interruptFallbackTimer);
1578
+ interruptFallbackTimer = null;
1579
+ }
1580
+ };
1581
+ const resolveInterrupt = (latencyMs) => {
1582
+ clearInterruptTimer();
1583
+ interruptStartedAt = null;
1584
+ setState({
1585
+ activeSourceCount: sourceNodes.size,
1586
+ isPlaying: false,
1587
+ lastInterruptLatencyMs: latencyMs,
1588
+ lastPlaybackStopLatencyMs: state.lastPlaybackStopLatencyMs ?? latencyMs
1589
+ });
1590
+ resolveInterruptPromise?.();
1591
+ resolveInterruptPromise = null;
1592
+ interruptPromise = null;
1593
+ };
1594
+ const estimateOutputStopLatencyMs = (context) => {
1595
+ if (!context) {
1596
+ return 0;
1597
+ }
1598
+ return Math.max(0, ((context.baseLatency ?? 0) + (context.outputLatency ?? 0)) * 1000);
1599
+ };
1600
+ const restoreOutputGain = (context) => {
1601
+ if (!outputNode) {
1602
+ return;
1603
+ }
1604
+ const gainValue = 1;
1605
+ if (outputNode.gain.setValueAtTime) {
1606
+ outputNode.gain.setValueAtTime(gainValue, context?.currentTime ?? 0);
1607
+ return;
1608
+ }
1609
+ outputNode.gain.value = gainValue;
1610
+ };
1611
+ const muteOutputGain = (context) => {
1612
+ if (!outputNode) {
1613
+ return;
1614
+ }
1615
+ const gainValue = 0;
1616
+ if (outputNode.gain.setValueAtTime) {
1617
+ outputNode.gain.setValueAtTime(gainValue, context?.currentTime ?? 0);
1618
+ return;
1619
+ }
1620
+ outputNode.gain.value = gainValue;
1621
+ };
1622
+ const maybeResolveInterrupt = () => {
1623
+ if (interruptStartedAt === null || sourceNodes.size > 0) {
1624
+ return;
1625
+ }
1626
+ resolveInterrupt(Date.now() - interruptStartedAt);
1627
+ };
1628
+ const ensureAudioContext = async () => {
1629
+ if (audioContext) {
1630
+ return audioContext;
1631
+ }
1632
+ if (options.createAudioContext) {
1633
+ audioContext = options.createAudioContext();
1634
+ } else {
1635
+ const AudioContextCtor = getAudioContextCtor();
1636
+ if (!AudioContextCtor) {
1637
+ throw new Error("Assistant audio playback requires AudioContext support.");
1638
+ }
1639
+ audioContext = new AudioContextCtor;
1640
+ }
1641
+ if (audioContext.createGain) {
1642
+ outputNode = audioContext.createGain();
1643
+ outputNode.connect?.(audioContext.destination);
1644
+ }
1645
+ queueEndTime = audioContext.currentTime;
1646
+ return audioContext;
1647
+ };
1648
+ const scheduleChunk = async (chunk) => {
1649
+ const context = await ensureAudioContext();
1650
+ const buffer = decodePCM16LEChunk(context, chunk);
1651
+ const node = context.createBufferSource();
1652
+ node.buffer = buffer;
1653
+ node.connect(outputNode ?? context.destination);
1654
+ node.onended = () => {
1655
+ sourceNodes.delete(node);
1656
+ node.disconnect?.();
1657
+ setState({
1658
+ activeSourceCount: sourceNodes.size,
1659
+ isPlaying: sourceNodes.size > 0 && state.isActive
1660
+ });
1661
+ maybeResolveInterrupt();
1662
+ };
1663
+ const startAt = Math.max(context.currentTime + lookaheadSeconds, queueEndTime);
1664
+ queueEndTime = startAt + buffer.duration;
1665
+ sourceNodes.add(node);
1666
+ setState({
1667
+ activeSourceCount: sourceNodes.size,
1668
+ isPlaying: true
1669
+ });
1670
+ node.start(startAt);
1671
+ };
1672
+ const stopQueuedPlayback = (options2) => {
1673
+ for (const node of [...sourceNodes]) {
1674
+ node.stop?.();
1675
+ }
1676
+ queueEndTime = audioContext ? audioContext.currentTime : 0;
1677
+ if (options2?.forceClear) {
1678
+ for (const node of sourceNodes) {
1679
+ node.disconnect?.();
1680
+ }
1681
+ sourceNodes.clear();
1682
+ maybeResolveInterrupt();
1683
+ }
1684
+ };
1685
+ const sync = async () => {
1686
+ if (!state.isActive) {
1687
+ return;
1688
+ }
1689
+ const nextChunks = source.assistantAudio.slice(state.processedChunkCount);
1690
+ if (nextChunks.length === 0) {
1691
+ return;
1692
+ }
1693
+ try {
1694
+ clearError();
1695
+ for (const chunk of nextChunks) {
1696
+ await scheduleChunk(chunk);
1697
+ }
1698
+ setState({
1699
+ processedChunkCount: source.assistantAudio.length,
1700
+ queuedChunkCount: state.queuedChunkCount + nextChunks.length
1701
+ });
1702
+ } catch (error) {
1703
+ setState({
1704
+ error: error instanceof Error ? error.message : String(error)
1705
+ });
1706
+ }
1707
+ };
1708
+ const queueSync = () => {
1709
+ syncPromise = syncPromise.then(() => sync(), () => sync());
1710
+ return syncPromise;
1711
+ };
1712
+ const unsubscribeSource = source.subscribe(() => {
1713
+ if (options.autoStart && !state.isActive && source.assistantAudio.length > 0) {
1714
+ player.start();
1715
+ return;
1716
+ }
1717
+ if (state.isActive) {
1718
+ queueSync();
1719
+ }
1720
+ });
1721
+ const player = {
1722
+ close: async () => {
1723
+ unsubscribeSource();
1724
+ stopQueuedPlayback({ forceClear: true });
1725
+ clearInterruptTimer();
1726
+ resolveInterruptPromise?.();
1727
+ resolveInterruptPromise = null;
1728
+ interruptPromise = null;
1729
+ interruptStartedAt = null;
1730
+ if (audioContext && audioContext.state !== "closed") {
1731
+ await audioContext.close();
1732
+ }
1733
+ audioContext = null;
1734
+ outputNode?.disconnect?.();
1735
+ outputNode = null;
1736
+ queueEndTime = 0;
1737
+ setState({
1738
+ activeSourceCount: 0,
1739
+ isActive: false,
1740
+ isPlaying: false
1741
+ });
1742
+ },
1743
+ get activeSourceCount() {
1744
+ return state.activeSourceCount;
1745
+ },
1746
+ get error() {
1747
+ return state.error;
1748
+ },
1749
+ getSnapshot: () => state,
1750
+ get isActive() {
1751
+ return state.isActive;
1752
+ },
1753
+ get isPlaying() {
1754
+ return state.isPlaying;
1755
+ },
1756
+ interrupt: async () => {
1757
+ const startedAt = Date.now();
1758
+ const context = await ensureAudioContext();
1759
+ interruptStartedAt = startedAt;
1760
+ muteOutputGain(context);
1761
+ const playbackStopLatencyMs = Date.now() - startedAt + estimateOutputStopLatencyMs(context);
1762
+ setState({
1763
+ isActive: false,
1764
+ isPlaying: sourceNodes.size > 0,
1765
+ lastPlaybackStopLatencyMs: playbackStopLatencyMs
1766
+ });
1767
+ if (sourceNodes.size === 0) {
1768
+ resolveInterrupt(playbackStopLatencyMs);
1769
+ return;
1770
+ }
1771
+ if (!interruptPromise) {
1772
+ interruptPromise = new Promise((resolve) => {
1773
+ resolveInterruptPromise = resolve;
1774
+ });
1775
+ }
1776
+ clearInterruptTimer();
1777
+ interruptFallbackTimer = setTimeout(() => {
1778
+ for (const node of sourceNodes) {
1779
+ node.disconnect?.();
1780
+ }
1781
+ sourceNodes.clear();
1782
+ resolveInterrupt(Date.now() - startedAt);
1783
+ }, 250);
1784
+ stopQueuedPlayback();
1785
+ await interruptPromise;
1786
+ },
1787
+ get lastInterruptLatencyMs() {
1788
+ return state.lastInterruptLatencyMs;
1789
+ },
1790
+ get lastPlaybackStopLatencyMs() {
1791
+ return state.lastPlaybackStopLatencyMs;
1792
+ },
1793
+ pause: async () => {
1794
+ if (!audioContext) {
1795
+ setState({
1796
+ activeSourceCount: 0,
1797
+ isActive: false,
1798
+ isPlaying: false
1799
+ });
1800
+ return;
1801
+ }
1802
+ await audioContext.suspend();
1803
+ setState({
1804
+ activeSourceCount: sourceNodes.size,
1805
+ isActive: false,
1806
+ isPlaying: false
1807
+ });
1808
+ },
1809
+ get processedChunkCount() {
1810
+ return state.processedChunkCount;
1811
+ },
1812
+ get queuedChunkCount() {
1813
+ return state.queuedChunkCount;
1814
+ },
1815
+ start: async () => {
1816
+ try {
1817
+ clearError();
1818
+ const context = await ensureAudioContext();
1819
+ restoreOutputGain(context);
1820
+ if (context.state === "suspended") {
1821
+ await context.resume();
1822
+ }
1823
+ setState({
1824
+ activeSourceCount: sourceNodes.size,
1825
+ isActive: true,
1826
+ isPlaying: context.state === "running"
1827
+ });
1828
+ await queueSync();
1829
+ } catch (error) {
1830
+ setState({
1831
+ error: error instanceof Error ? error.message : String(error),
1832
+ isActive: false,
1833
+ isPlaying: false
1834
+ });
1835
+ throw error;
1836
+ }
1837
+ },
1838
+ subscribe: (subscriber) => {
1839
+ subscribers.add(subscriber);
1840
+ return () => {
1841
+ subscribers.delete(subscriber);
1842
+ };
1843
+ }
1844
+ };
1845
+ return player;
1846
+ };
1847
+
1848
+ // src/client/bargeInMonitor.ts
1849
+ var DEFAULT_THRESHOLD_MS = 250;
1850
+ var createEventId = () => `barge-in:${Date.now()}:${crypto.randomUUID?.() ?? Math.random().toString(36).slice(2)}`;
1851
+ var summarize = (events, thresholdMs) => {
1852
+ const stopped = events.filter((event) => event.status === "stopped");
1853
+ const latencies = stopped.map((event) => event.latencyMs).filter((value) => typeof value === "number");
1854
+ const failed = stopped.filter((event) => typeof event.latencyMs === "number" && event.latencyMs > thresholdMs).length;
1855
+ const passed = stopped.length - failed;
1856
+ return {
1857
+ averageLatencyMs: latencies.length > 0 ? Math.round(latencies.reduce((total, value) => total + value, 0) / latencies.length) : undefined,
1858
+ events: [...events],
1859
+ failed,
1860
+ lastEvent: events.at(-1),
1861
+ passed,
1862
+ status: events.length === 0 ? "empty" : failed > 0 ? "fail" : stopped.length === 0 ? "warn" : "pass",
1863
+ thresholdMs,
1864
+ total: stopped.length
1865
+ };
1866
+ };
1867
+ var createVoiceBargeInMonitor = (options = {}) => {
1868
+ const listeners = new Set;
1869
+ const thresholdMs = options.thresholdMs ?? DEFAULT_THRESHOLD_MS;
1870
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1871
+ const events = [];
1872
+ const emit = () => {
1873
+ for (const listener of listeners) {
1874
+ listener();
1875
+ }
1876
+ };
1877
+ const postEvent = (event) => {
1878
+ if (!options.path || typeof fetchImpl !== "function") {
1879
+ return;
1880
+ }
1881
+ fetchImpl(options.path, {
1882
+ body: JSON.stringify(event),
1883
+ headers: {
1884
+ "Content-Type": "application/json"
1885
+ },
1886
+ method: "POST"
1887
+ }).catch(() => {});
1888
+ };
1889
+ const record = (status, input) => {
1890
+ const event = {
1891
+ at: Date.now(),
1892
+ id: createEventId(),
1893
+ latencyMs: input.latencyMs,
1894
+ playbackStopLatencyMs: input.playbackStopLatencyMs,
1895
+ reason: input.reason,
1896
+ sessionId: input.sessionId,
1897
+ status,
1898
+ thresholdMs
1899
+ };
1900
+ events.push(event);
1901
+ postEvent(event);
1902
+ emit();
1903
+ return event;
1904
+ };
1905
+ return {
1906
+ getSnapshot: () => summarize(events, thresholdMs),
1907
+ recordRequested: (input) => record("requested", input),
1908
+ recordSkipped: (input) => record("skipped", input),
1909
+ recordStopped: (input) => record("stopped", input),
1910
+ subscribe: (subscriber) => {
1911
+ listeners.add(subscriber);
1912
+ return () => {
1913
+ listeners.delete(subscriber);
1914
+ };
1915
+ }
1916
+ };
1917
+ };
1918
+
1919
+ // src/client/duplex.ts
1920
+ var DEFAULT_INTERRUPT_THRESHOLD = 0.08;
1921
+ var shouldInterruptForLevel = (level, options = {}) => (options.enabled ?? true) && level >= (options.interruptThreshold ?? DEFAULT_INTERRUPT_THRESHOLD);
1922
+ var bindVoiceBargeIn = (controller, player, options = {}) => {
1923
+ let lastPartial = controller.partial;
1924
+ const interruptIfPlaying = (reason) => {
1925
+ if (!player.isPlaying || options.enabled === false) {
1926
+ options.monitor?.recordSkipped({
1927
+ reason,
1928
+ sessionId: controller.sessionId
1929
+ });
1930
+ return;
1931
+ }
1932
+ options.monitor?.recordRequested({
1933
+ reason,
1934
+ sessionId: controller.sessionId
1935
+ });
1936
+ player.interrupt().then(() => {
1937
+ options.monitor?.recordStopped({
1938
+ latencyMs: player.lastInterruptLatencyMs,
1939
+ playbackStopLatencyMs: player.lastPlaybackStopLatencyMs,
1940
+ reason,
1941
+ sessionId: controller.sessionId
1942
+ });
1943
+ });
1944
+ };
1945
+ const unsubscribe = controller.subscribe(() => {
1946
+ if (options.interruptOnPartial === false) {
1947
+ lastPartial = controller.partial;
1948
+ return;
1949
+ }
1950
+ if (!lastPartial && controller.partial) {
1951
+ interruptIfPlaying("partial-transcript");
1952
+ }
1953
+ lastPartial = controller.partial;
1954
+ });
1955
+ return {
1956
+ close: () => {
1957
+ unsubscribe();
1958
+ },
1959
+ handleLevel: (level) => {
1960
+ if (shouldInterruptForLevel(level, options)) {
1961
+ interruptIfPlaying("input-level");
1962
+ }
1963
+ },
1964
+ sendAudio: (audio) => {
1965
+ interruptIfPlaying("manual-audio");
1966
+ controller.sendAudio(audio);
1061
1967
  }
1062
1968
  };
1063
1969
  };
@@ -1084,7 +1990,7 @@ var DEFAULT_GUIDED_PROMPTS = [
1084
1990
  "Now describe what you are trying to do or test.",
1085
1991
  "Finish with any detail that feels blocked, risky, or unclear."
1086
1992
  ];
1087
- var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
1993
+ var clamp = (value, min, max2) => Math.min(max2, Math.max(min, value));
1088
1994
  var escapeHtml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1089
1995
  var readErrorField = (value, key) => {
1090
1996
  const candidate = value[key];
@@ -1118,6 +2024,17 @@ var formatErrorMessage = (error) => {
1118
2024
  }
1119
2025
  return "Unexpected error";
1120
2026
  };
2027
+ var formatReconnectState = (reconnect) => {
2028
+ const pieces = [reconnect.status];
2029
+ if (reconnect.attempts > 0 || reconnect.maxAttempts > 0) {
2030
+ pieces.push(`${reconnect.attempts}/${reconnect.maxAttempts} attempts`);
2031
+ }
2032
+ if (reconnect.nextAttemptAt) {
2033
+ const waitMs = Math.max(0, reconnect.nextAttemptAt - Date.now());
2034
+ pieces.push(`retry in ${Math.ceil(waitMs / 100) / 10}s`);
2035
+ }
2036
+ return pieces.join(" · ");
2037
+ };
1121
2038
  var createInitialVoiceWaveLevels = (count = VOICE_WAVE_POINTS) => Array.from({ length: count }, () => 0);
1122
2039
  var pushVoiceWaveLevel = (levels, nextLevel, count = VOICE_WAVE_POINTS) => {
1123
2040
  const next = levels.slice(-(count - 1));
@@ -1174,6 +2091,20 @@ var parsePromptList = (value) => {
1174
2091
  } catch {}
1175
2092
  return DEFAULT_GUIDED_PROMPTS;
1176
2093
  };
2094
+ var parseOptionalNumber = (value) => {
2095
+ if (!value) {
2096
+ return;
2097
+ }
2098
+ const parsed = Number(value);
2099
+ return Number.isFinite(parsed) ? parsed : undefined;
2100
+ };
2101
+ var resolveElement2 = (root, selector, ctor) => {
2102
+ if (!selector) {
2103
+ return null;
2104
+ }
2105
+ const value = document.querySelector(selector);
2106
+ return value instanceof ctor ? value : null;
2107
+ };
1177
2108
  var requireElement = (root, selector, ctor, name) => {
1178
2109
  const value = selector ? document.querySelector(selector) : null;
1179
2110
  if (value instanceof ctor) {
@@ -1224,11 +2155,20 @@ var initVoiceHTMXRoot = (root) => {
1224
2155
  const guidedPrompts = parsePromptList(root.dataset.voiceGuidedPrompts);
1225
2156
  const guidedLabel = root.dataset.voiceGuidedLabel ?? DEFAULT_GUIDED_LABEL;
1226
2157
  const generalLabel = root.dataset.voiceGeneralLabel ?? DEFAULT_GENERAL_LABEL;
2158
+ const reconnectReportPath = root.dataset.voiceReconnectReportPath;
2159
+ const bargeInPath = root.dataset.voiceBargeInPath;
2160
+ const bargeInMonitor = bargeInPath ? createVoiceBargeInMonitor({
2161
+ path: bargeInPath,
2162
+ thresholdMs: parseOptionalNumber(root.dataset.voiceBargeInThresholdMs)
2163
+ }) : null;
2164
+ const bargeInRecentWindowMs = parseOptionalNumber(root.dataset.voiceBargeInRecentWindowMs) ?? 4000;
2165
+ const bargeInSpeechThreshold = parseOptionalNumber(root.dataset.voiceBargeInSpeechThreshold) ?? 0.04;
1227
2166
  const syncElement = requireElement(document, root.dataset.voiceSync, HTMLElement, "voice-htmx-sync");
1228
2167
  const connectionMetric = requireElement(root, root.dataset.voiceConnection, HTMLElement, "metric-connection");
1229
2168
  const errorStatus = requireElement(root, root.dataset.voiceError, HTMLElement, "status-error");
1230
2169
  const microphoneStatus = requireElement(root, root.dataset.voiceMicrophone, HTMLElement, "status-mic");
1231
2170
  const promptStatus = requireElement(root, root.dataset.voicePrompt, HTMLElement, "status-prompt");
2171
+ const reconnectStatus = resolveElement2(root, root.dataset.voiceReconnect, HTMLElement);
1232
2172
  const chatList = requireElement(root, root.dataset.voiceChat, HTMLElement, "chat-list");
1233
2173
  const startGuidedButton = requireElement(root, root.dataset.voiceStartGuided, HTMLButtonElement, "start-guided");
1234
2174
  const startGeneralButton = requireElement(root, root.dataset.voiceStartGeneral, HTMLButtonElement, "start-general");
@@ -1237,35 +2177,70 @@ var initVoiceHTMXRoot = (root) => {
1237
2177
  const voiceMonitorCopy = requireElement(root, root.dataset.voiceMonitorCopy, HTMLElement, "voice-monitor-copy");
1238
2178
  const voiceWaveGlow = requireElement(root, root.dataset.voiceWaveGlow, SVGPathElement, "voice-wave-glow");
1239
2179
  const voiceWavePath = requireElement(root, root.dataset.voiceWavePath, SVGPathElement, "voice-wave-path");
2180
+ let activeMode = null;
2181
+ let hasStartedModes = {
2182
+ general: false,
2183
+ guided: false
2184
+ };
2185
+ let isCapturing = false;
2186
+ let micError = null;
2187
+ let waveLevels = createInitialVoiceWaveLevels();
2188
+ let guidedBargeInBinding = null;
2189
+ let generalBargeInBinding = null;
1240
2190
  const guidedVoice = createVoiceController(guidedPath, {
1241
2191
  capture: {
2192
+ onAudio: (audio, sendAudio) => {
2193
+ if (guidedBargeInBinding) {
2194
+ guidedBargeInBinding.sendAudio(audio);
2195
+ return;
2196
+ }
2197
+ sendAudio(audio);
2198
+ },
1242
2199
  onLevel: (level) => {
2200
+ guidedBargeInBinding?.handleLevel(level);
1243
2201
  waveLevels = pushVoiceWaveLevel(waveLevels, level);
1244
2202
  renderWave();
1245
2203
  }
1246
2204
  },
2205
+ connection: {
2206
+ reconnectReportPath
2207
+ },
1247
2208
  preset: "guided-intake"
1248
2209
  });
1249
2210
  const generalVoice = createVoiceController(generalPath, {
1250
2211
  capture: {
2212
+ onAudio: (audio, sendAudio) => {
2213
+ if (generalBargeInBinding) {
2214
+ generalBargeInBinding.sendAudio(audio);
2215
+ return;
2216
+ }
2217
+ sendAudio(audio);
2218
+ },
1251
2219
  onLevel: (level) => {
2220
+ generalBargeInBinding?.handleLevel(level);
1252
2221
  waveLevels = pushVoiceWaveLevel(waveLevels, level);
1253
2222
  renderWave();
1254
2223
  }
1255
2224
  },
2225
+ connection: {
2226
+ reconnectReportPath
2227
+ },
1256
2228
  preset: "dictation"
1257
2229
  });
1258
2230
  const stopGuidedBinding = guidedVoice.bindHTMX({ element: syncElement });
1259
2231
  const stopGeneralBinding = generalVoice.bindHTMX({ element: syncElement });
1260
- let activeMode = null;
1261
- let hasStartedModes = {
1262
- general: false,
1263
- guided: false
1264
- };
1265
- let isCapturing = false;
1266
- let micError = null;
1267
- let waveLevels = createInitialVoiceWaveLevels();
2232
+ const guidedAudioPlayer = createVoiceAudioPlayer(guidedVoice);
2233
+ const generalAudioPlayer = createVoiceAudioPlayer(generalVoice);
2234
+ guidedBargeInBinding = bindVoiceBargeIn(guidedVoice, guidedAudioPlayer, {
2235
+ interruptThreshold: bargeInSpeechThreshold,
2236
+ monitor: bargeInMonitor ?? undefined
2237
+ });
2238
+ generalBargeInBinding = bindVoiceBargeIn(generalVoice, generalAudioPlayer, {
2239
+ interruptThreshold: bargeInSpeechThreshold,
2240
+ monitor: bargeInMonitor ?? undefined
2241
+ });
1268
2242
  const currentVoice = () => activeMode === "general" ? generalVoice : guidedVoice;
2243
+ const currentAudioPlayer = () => activeMode === "general" ? generalAudioPlayer : guidedAudioPlayer;
1269
2244
  const renderWave = () => {
1270
2245
  const path = createVoiceWavePath(waveLevels);
1271
2246
  voiceWaveGlow.setAttribute("d", path);
@@ -1280,6 +2255,9 @@ var initVoiceHTMXRoot = (root) => {
1280
2255
  const status = voice.status;
1281
2256
  connectionMetric.textContent = voice.isConnected ? "Connected" : "Waiting";
1282
2257
  errorStatus.textContent = micError || voice.error || "None";
2258
+ if (reconnectStatus) {
2259
+ reconnectStatus.textContent = formatReconnectState(voice.reconnect);
2260
+ }
1283
2261
  microphoneStatus.textContent = isCapturing ? DEFAULT_MIC_LIVE : DEFAULT_MIC_IDLE;
1284
2262
  promptStatus.textContent = resolvePromptMessage({
1285
2263
  guidedPrompts,
@@ -1343,8 +2321,18 @@ var initVoiceHTMXRoot = (root) => {
1343
2321
  render();
1344
2322
  }
1345
2323
  };
1346
- guidedVoice.subscribe(render);
1347
- generalVoice.subscribe(render);
2324
+ guidedVoice.subscribe(() => {
2325
+ if (guidedVoice.assistantAudio.length > 0) {
2326
+ guidedAudioPlayer.start().catch(() => {});
2327
+ }
2328
+ render();
2329
+ });
2330
+ generalVoice.subscribe(() => {
2331
+ if (generalVoice.assistantAudio.length > 0) {
2332
+ generalAudioPlayer.start().catch(() => {});
2333
+ }
2334
+ render();
2335
+ });
1348
2336
  startGuidedButton.addEventListener("click", () => {
1349
2337
  startMode("guided");
1350
2338
  });
@@ -1354,9 +2342,16 @@ var initVoiceHTMXRoot = (root) => {
1354
2342
  stopButton.addEventListener("click", () => {
1355
2343
  stopMic();
1356
2344
  });
2345
+ root.addEventListener("absolute-voice-simulate-disconnect", () => {
2346
+ currentVoice().simulateDisconnect();
2347
+ });
1357
2348
  window.addEventListener("beforeunload", () => {
1358
2349
  guidedVoice.stopRecording();
1359
2350
  generalVoice.stopRecording();
2351
+ guidedBargeInBinding?.close();
2352
+ generalBargeInBinding?.close();
2353
+ guidedAudioPlayer.close();
2354
+ generalAudioPlayer.close();
1360
2355
  stopGuidedBinding();
1361
2356
  stopGeneralBinding();
1362
2357
  guidedVoice.close();