@absolutejs/voice 0.0.22-beta.52 → 0.0.22-beta.520

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