@absolutejs/voice 0.0.22-beta.51 → 0.0.22-beta.511

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 (435) 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 +36 -5
  9. package/dist/angular/index.js +4004 -338
  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-call-player.service.d.ts +19 -0
  13. package/dist/angular/voice-campaign-dialer-proof.service.d.ts +14 -0
  14. package/dist/angular/voice-controller.service.d.ts +3 -1
  15. package/dist/angular/voice-cost-dashboard.service.d.ts +15 -0
  16. package/dist/angular/voice-delivery-runtime.component.d.ts +17 -0
  17. package/dist/angular/voice-delivery-runtime.service.d.ts +16 -0
  18. package/dist/angular/voice-live-agent-console.service.d.ts +16 -0
  19. package/dist/angular/voice-live-call-viewer.service.d.ts +16 -0
  20. package/dist/angular/voice-live-ops.service.d.ts +11 -0
  21. package/dist/angular/voice-ops-action-center.service.d.ts +13 -0
  22. package/dist/angular/voice-ops-status.component.d.ts +15 -0
  23. package/dist/angular/voice-ops-status.service.d.ts +12 -0
  24. package/dist/angular/voice-platform-coverage.service.d.ts +12 -0
  25. package/dist/angular/voice-profile-comparison.service.d.ts +12 -0
  26. package/dist/angular/voice-proof-trends.service.d.ts +12 -0
  27. package/dist/angular/voice-provider-capabilities.service.d.ts +12 -0
  28. package/dist/angular/voice-provider-contracts.service.d.ts +12 -0
  29. package/dist/angular/voice-provider-status.service.d.ts +2 -2
  30. package/dist/angular/voice-readiness-failures.service.d.ts +13 -0
  31. package/dist/angular/voice-reconnect-profile-evidence.service.d.ts +12 -0
  32. package/dist/angular/voice-replay-timeline.service.d.ts +13 -0
  33. package/dist/angular/voice-routing-status.service.d.ts +11 -0
  34. package/dist/angular/voice-session-observability.service.d.ts +12 -0
  35. package/dist/angular/voice-session-snapshot.service.d.ts +13 -0
  36. package/dist/angular/voice-stream.service.d.ts +4 -1
  37. package/dist/angular/voice-trace-timeline.service.d.ts +12 -0
  38. package/dist/angular/voice-turn-latency.service.d.ts +13 -0
  39. package/dist/angular/voice-turn-quality.service.d.ts +12 -0
  40. package/dist/angular/voice-widget.service.d.ts +18 -0
  41. package/dist/angular/voice-workflow-status.service.d.ts +2 -2
  42. package/dist/assistant.d.ts +12 -13
  43. package/dist/assistantExperiment.d.ts +41 -0
  44. package/dist/assistantHealth.d.ts +6 -6
  45. package/dist/assistantMemory.d.ts +3 -3
  46. package/dist/assistantMode.d.ts +22 -0
  47. package/dist/audioConditioning.d.ts +1 -1
  48. package/dist/audit.d.ts +131 -0
  49. package/dist/auditDeliveryRoutes.d.ts +85 -0
  50. package/dist/auditExport.d.ts +34 -0
  51. package/dist/auditRoutes.d.ts +66 -0
  52. package/dist/auditSinks.d.ts +151 -0
  53. package/dist/backchannel.d.ts +18 -0
  54. package/dist/bargeInRoutes.d.ts +56 -0
  55. package/dist/browserCallProfiles.d.ts +120 -0
  56. package/dist/browserMediaRoutes.d.ts +62 -0
  57. package/dist/callDebugger.d.ts +66 -0
  58. package/dist/callDisposition.d.ts +38 -0
  59. package/dist/callQuota.d.ts +54 -0
  60. package/dist/callerMemory.d.ts +37 -0
  61. package/dist/callingWindow.d.ts +26 -0
  62. package/dist/campaign.d.ts +794 -0
  63. package/dist/campaignControls.d.ts +37 -0
  64. package/dist/campaignDialers.d.ts +111 -0
  65. package/dist/campaignTemplate.d.ts +16 -0
  66. package/dist/client/actions.d.ts +95 -1
  67. package/dist/client/agentSquadStatus.d.ts +37 -0
  68. package/dist/client/agentSquadStatusWidget.d.ts +24 -0
  69. package/dist/client/audioPlayer.d.ts +2 -2
  70. package/dist/client/bargeInMonitor.d.ts +7 -0
  71. package/dist/client/browserMedia.d.ts +8 -0
  72. package/dist/client/browserNoiseSuppression.d.ts +42 -0
  73. package/dist/client/browserVoiceSupport.d.ts +22 -0
  74. package/dist/client/callDebugger.d.ts +19 -0
  75. package/dist/client/callDebuggerWidget.d.ts +30 -0
  76. package/dist/client/callPlayer.d.ts +41 -0
  77. package/dist/client/campaignDialerProof.d.ts +23 -0
  78. package/dist/client/connection.d.ts +4 -3
  79. package/dist/client/controller.d.ts +1 -1
  80. package/dist/client/conversationAnalytics.d.ts +29 -0
  81. package/dist/client/costDashboard.d.ts +27 -0
  82. package/dist/client/createVoiceStream.d.ts +1 -1
  83. package/dist/client/deliveryRuntime.d.ts +34 -0
  84. package/dist/client/deliveryRuntimeWidget.d.ts +37 -0
  85. package/dist/client/duplex.d.ts +2 -2
  86. package/dist/client/htmx.d.ts +1 -1
  87. package/dist/client/htmxAttributes.d.ts +28 -0
  88. package/dist/client/htmxBootstrap.js +967 -14
  89. package/dist/client/htmxDashboardRenderers.d.ts +72 -0
  90. package/dist/client/index.d.ts +104 -13
  91. package/dist/client/index.js +10142 -26
  92. package/dist/client/liveAgentConsole.d.ts +28 -0
  93. package/dist/client/liveCallViewer.d.ts +42 -0
  94. package/dist/client/liveOps.d.ts +22 -0
  95. package/dist/client/liveOpsWidget.d.ts +23 -0
  96. package/dist/client/liveTurnLatency.d.ts +41 -0
  97. package/dist/client/microphone.d.ts +4 -4
  98. package/dist/client/opsActionCenter.d.ts +54 -0
  99. package/dist/client/opsActionCenterWidget.d.ts +29 -0
  100. package/dist/client/opsActionHistory.d.ts +19 -0
  101. package/dist/client/opsActionHistoryWidget.d.ts +11 -0
  102. package/dist/client/opsStatus.d.ts +19 -0
  103. package/dist/client/opsStatusWidget.d.ts +40 -0
  104. package/dist/client/platformCoverage.d.ts +19 -0
  105. package/dist/client/platformCoverageWidget.d.ts +37 -0
  106. package/dist/client/profileComparison.d.ts +19 -0
  107. package/dist/client/profileComparisonWidget.d.ts +41 -0
  108. package/dist/client/profileSwitchRecommendation.d.ts +19 -0
  109. package/dist/client/profileSwitchRecommendationWidget.d.ts +12 -0
  110. package/dist/client/proofTrends.d.ts +19 -0
  111. package/dist/client/proofTrendsWidget.d.ts +37 -0
  112. package/dist/client/providerCapabilities.d.ts +19 -0
  113. package/dist/client/providerCapabilitiesWidget.d.ts +32 -0
  114. package/dist/client/providerContracts.d.ts +19 -0
  115. package/dist/client/providerContractsWidget.d.ts +37 -0
  116. package/dist/client/providerSimulationControls.d.ts +33 -0
  117. package/dist/client/providerSimulationControlsWidget.d.ts +20 -0
  118. package/dist/client/providerStatus.d.ts +1 -1
  119. package/dist/client/providerStatusWidget.d.ts +32 -0
  120. package/dist/client/readinessFailures.d.ts +19 -0
  121. package/dist/client/readinessFailuresWidget.d.ts +42 -0
  122. package/dist/client/reconnectProfileEvidence.d.ts +19 -0
  123. package/dist/client/reconnectProfileEvidenceWidget.d.ts +39 -0
  124. package/dist/client/replayTimeline.d.ts +26 -0
  125. package/dist/client/routingStatus.d.ts +19 -0
  126. package/dist/client/routingStatusWidget.d.ts +32 -0
  127. package/dist/client/sessionObservability.d.ts +19 -0
  128. package/dist/client/sessionObservabilityWidget.d.ts +31 -0
  129. package/dist/client/sessionSnapshot.d.ts +21 -0
  130. package/dist/client/sessionSnapshotWidget.d.ts +33 -0
  131. package/dist/client/store.d.ts +1 -1
  132. package/dist/client/traceTimeline.d.ts +19 -0
  133. package/dist/client/traceTimelineWidget.d.ts +36 -0
  134. package/dist/client/turnLatency.d.ts +22 -0
  135. package/dist/client/turnLatencyWidget.d.ts +33 -0
  136. package/dist/client/turnQuality.d.ts +19 -0
  137. package/dist/client/turnQualityWidget.d.ts +32 -0
  138. package/dist/client/voiceWidgetView.d.ts +50 -0
  139. package/dist/client/workflowStatus.d.ts +1 -1
  140. package/dist/competitiveCoverage.d.ts +141 -0
  141. package/dist/correction.d.ts +2 -2
  142. package/dist/costAccounting.d.ts +76 -0
  143. package/dist/costPredictor.d.ts +74 -0
  144. package/dist/dataControl.d.ts +180 -0
  145. package/dist/defineVoiceAssistant.d.ts +68 -0
  146. package/dist/deliveryRuntime.d.ts +158 -0
  147. package/dist/deliverySinkRoutes.d.ts +117 -0
  148. package/dist/demoReadyRoutes.d.ts +98 -0
  149. package/dist/diagnosticsRoutes.d.ts +2 -2
  150. package/dist/dncRegistry.d.ts +38 -0
  151. package/dist/dtmfCollector.d.ts +37 -0
  152. package/dist/evalRoutes.d.ts +10 -4
  153. package/dist/fileStore.d.ts +21 -7
  154. package/dist/generated/htmxBootstrapBundle.d.ts +1 -0
  155. package/dist/guardrails.d.ts +128 -0
  156. package/dist/handoff.d.ts +6 -6
  157. package/dist/handoffHealth.d.ts +5 -5
  158. package/dist/holdAudio.d.ts +23 -0
  159. package/dist/htmx.d.ts +1 -1
  160. package/dist/htmxDashboardRoutes.d.ts +249 -0
  161. package/dist/iceServers.d.ts +34 -0
  162. package/dist/incidentBundle.d.ts +119 -0
  163. package/dist/incidentTimeline.d.ts +260 -0
  164. package/dist/index.d.ts +324 -74
  165. package/dist/index.js +42843 -6521
  166. package/dist/ivrPlan.d.ts +40 -0
  167. package/dist/latencySlo.d.ts +56 -0
  168. package/dist/liveCoach.d.ts +43 -0
  169. package/dist/liveLatency.d.ts +78 -0
  170. package/dist/liveOps.d.ts +190 -0
  171. package/dist/llmJudge.d.ts +45 -0
  172. package/dist/logger.d.ts +1 -1
  173. package/dist/mediaPipelineRoutes.d.ts +171 -0
  174. package/dist/mediaPipelineSurfaces.d.ts +48 -0
  175. package/dist/memoryStore.d.ts +1 -1
  176. package/dist/midCallSummary.d.ts +27 -0
  177. package/dist/modelAdapters.d.ts +59 -7
  178. package/dist/monitor.d.ts +148 -0
  179. package/dist/multilingualProof.d.ts +77 -0
  180. package/dist/oauth2TokenSource.d.ts +21 -0
  181. package/dist/observabilityExport.d.ts +501 -0
  182. package/dist/openaiTTS.d.ts +18 -0
  183. package/dist/operationalStatus.d.ts +87 -0
  184. package/dist/operationsRecord.d.ts +370 -0
  185. package/dist/ops.d.ts +12 -12
  186. package/dist/opsActionAuditRoutes.d.ts +99 -0
  187. package/dist/opsConsoleRoutes.d.ts +8 -5
  188. package/dist/opsPresets.d.ts +2 -2
  189. package/dist/opsRecovery.d.ts +137 -0
  190. package/dist/opsRuntime.d.ts +6 -6
  191. package/dist/opsSinks.d.ts +13 -13
  192. package/dist/opsStatus.d.ts +76 -0
  193. package/dist/opsStatusRoutes.d.ts +33 -0
  194. package/dist/opsWebhook.d.ts +6 -6
  195. package/dist/otelExporter.d.ts +83 -0
  196. package/dist/outcomeContract.d.ts +146 -0
  197. package/dist/outcomeRecipes.d.ts +4 -4
  198. package/dist/phoneAgent.d.ts +139 -0
  199. package/dist/phoneAgentProductionSmoke.d.ts +115 -0
  200. package/dist/phoneProvisioning.d.ts +29 -0
  201. package/dist/platformCoverage.d.ts +91 -0
  202. package/dist/plugin.d.ts +2 -2
  203. package/dist/postCallAnalysis.d.ts +98 -0
  204. package/dist/postCallSurvey.d.ts +41 -0
  205. package/dist/postgresStore.d.ts +17 -6
  206. package/dist/presets.d.ts +3 -3
  207. package/dist/productionReadiness.d.ts +756 -0
  208. package/dist/profileSwitchRecommendation.d.ts +350 -0
  209. package/dist/promptInjectionGuard.d.ts +30 -0
  210. package/dist/proofAssertions.d.ts +32 -0
  211. package/dist/proofPack.d.ts +211 -0
  212. package/dist/proofRunner.d.ts +79 -0
  213. package/dist/proofTrends.d.ts +966 -0
  214. package/dist/providerAdapters.d.ts +16 -5
  215. package/dist/providerCapabilities.d.ts +92 -0
  216. package/dist/providerDecisionTraces.d.ts +130 -0
  217. package/dist/providerHealth.d.ts +3 -3
  218. package/dist/providerOrchestration.d.ts +109 -0
  219. package/dist/providerRouterTraces.d.ts +35 -0
  220. package/dist/providerRoutingContract.d.ts +71 -0
  221. package/dist/providerSlo.d.ts +142 -0
  222. package/dist/providerStackRecommendations.d.ts +187 -0
  223. package/dist/qualityRoutes.d.ts +4 -4
  224. package/dist/queue.d.ts +23 -14
  225. package/dist/ragTool.d.ts +57 -0
  226. package/dist/react/VoiceAgentSquadStatus.d.ts +5 -0
  227. package/dist/react/VoiceCallDebuggerLaunch.d.ts +6 -0
  228. package/dist/react/VoiceCallPlayer.d.ts +11 -0
  229. package/dist/react/VoiceCostDashboard.d.ts +10 -0
  230. package/dist/react/VoiceDeliveryRuntime.d.ts +7 -0
  231. package/dist/react/VoiceLiveAgentConsole.d.ts +11 -0
  232. package/dist/react/VoiceLiveCallViewer.d.ts +9 -0
  233. package/dist/react/VoiceOpsActionCenter.d.ts +5 -0
  234. package/dist/react/VoiceOpsStatus.d.ts +6 -0
  235. package/dist/react/VoicePlatformCoverage.d.ts +6 -0
  236. package/dist/react/VoiceProfileComparison.d.ts +6 -0
  237. package/dist/react/VoiceProfileSwitchRecommendation.d.ts +6 -0
  238. package/dist/react/VoiceProofTrends.d.ts +6 -0
  239. package/dist/react/VoiceProviderCapabilities.d.ts +6 -0
  240. package/dist/react/VoiceProviderContracts.d.ts +6 -0
  241. package/dist/react/VoiceProviderSimulationControls.d.ts +5 -0
  242. package/dist/react/VoiceProviderStatus.d.ts +6 -0
  243. package/dist/react/VoiceReadinessFailures.d.ts +6 -0
  244. package/dist/react/VoiceReconnectProfileEvidence.d.ts +6 -0
  245. package/dist/react/VoiceReplayTimeline.d.ts +6 -0
  246. package/dist/react/VoiceRoutingStatus.d.ts +6 -0
  247. package/dist/react/VoiceSessionObservability.d.ts +6 -0
  248. package/dist/react/VoiceSessionSnapshot.d.ts +6 -0
  249. package/dist/react/VoiceTraceTimeline.d.ts +6 -0
  250. package/dist/react/VoiceTurnLatency.d.ts +6 -0
  251. package/dist/react/VoiceTurnQuality.d.ts +6 -0
  252. package/dist/react/VoiceWidget.d.ts +13 -0
  253. package/dist/react/index.d.ts +59 -5
  254. package/dist/react/index.js +12733 -168
  255. package/dist/react/useVoiceAgentSquadStatus.d.ts +8 -0
  256. package/dist/react/useVoiceCallDebugger.d.ts +8 -0
  257. package/dist/react/useVoiceCampaignDialerProof.d.ts +10 -0
  258. package/dist/react/useVoiceController.d.ts +4 -1
  259. package/dist/react/useVoiceDeliveryRuntime.d.ts +13 -0
  260. package/dist/react/useVoiceLiveOps.d.ts +9 -0
  261. package/dist/react/useVoiceOpsActionCenter.d.ts +11 -0
  262. package/dist/react/useVoiceOpsStatus.d.ts +8 -0
  263. package/dist/react/useVoicePlatformCoverage.d.ts +8 -0
  264. package/dist/react/useVoiceProfileComparison.d.ts +8 -0
  265. package/dist/react/useVoiceProfileSwitchRecommendation.d.ts +8 -0
  266. package/dist/react/useVoiceProofTrends.d.ts +8 -0
  267. package/dist/react/useVoiceProviderCapabilities.d.ts +8 -0
  268. package/dist/react/useVoiceProviderContracts.d.ts +8 -0
  269. package/dist/react/useVoiceProviderSimulationControls.d.ts +10 -0
  270. package/dist/react/useVoiceProviderStatus.d.ts +1 -1
  271. package/dist/react/useVoiceReadinessFailures.d.ts +8 -0
  272. package/dist/react/useVoiceReconnectProfileEvidence.d.ts +8 -0
  273. package/dist/react/useVoiceRoutingStatus.d.ts +8 -0
  274. package/dist/react/useVoiceSessionObservability.d.ts +8 -0
  275. package/dist/react/useVoiceSessionSnapshot.d.ts +9 -0
  276. package/dist/react/useVoiceStream.d.ts +4 -1
  277. package/dist/react/useVoiceTraceTimeline.d.ts +8 -0
  278. package/dist/react/useVoiceTurnLatency.d.ts +9 -0
  279. package/dist/react/useVoiceTurnQuality.d.ts +8 -0
  280. package/dist/react/useVoiceWorkflowStatus.d.ts +1 -1
  281. package/dist/readinessProfiles.d.ts +45 -0
  282. package/dist/realtimeChannel.d.ts +136 -0
  283. package/dist/realtimeProviderContracts.d.ts +133 -0
  284. package/dist/reconnectContract.d.ts +176 -0
  285. package/dist/recordingRedaction.d.ts +18 -0
  286. package/dist/recordingStore.d.ts +60 -0
  287. package/dist/redaction.d.ts +13 -0
  288. package/dist/resilienceRoutes.d.ts +45 -5
  289. package/dist/retention.d.ts +37 -0
  290. package/dist/retryPolicy.d.ts +38 -0
  291. package/dist/routeAuth.d.ts +58 -0
  292. package/dist/routing.d.ts +1 -1
  293. package/dist/runtimeOps.d.ts +3 -3
  294. package/dist/s3Store.d.ts +18 -3
  295. package/dist/semanticTurn.d.ts +27 -0
  296. package/dist/session.d.ts +1 -1
  297. package/dist/sessionObservability.d.ts +145 -0
  298. package/dist/sessionReplay.d.ts +19 -7
  299. package/dist/sessionSnapshot.d.ts +109 -0
  300. package/dist/simulationSuite.d.ts +143 -0
  301. package/dist/sloCalibration.d.ts +185 -0
  302. package/dist/sqliteStore.d.ts +17 -6
  303. package/dist/store.d.ts +1 -1
  304. package/dist/supervisorPermissions.d.ts +33 -0
  305. package/dist/supervisorPresence.d.ts +49 -0
  306. package/dist/svelte/createVoiceAgentSquadStatus.d.ts +9 -0
  307. package/dist/svelte/createVoiceCallDebugger.d.ts +12 -0
  308. package/dist/svelte/createVoiceCallPlayer.d.ts +33 -0
  309. package/dist/svelte/createVoiceCampaignDialerProof.d.ts +9 -0
  310. package/dist/svelte/createVoiceCostDashboard.d.ts +13 -0
  311. package/dist/svelte/createVoiceDeliveryRuntime.d.ts +11 -0
  312. package/dist/svelte/createVoiceLiveAgentConsole.d.ts +23 -0
  313. package/dist/svelte/createVoiceLiveCallViewer.d.ts +26 -0
  314. package/dist/svelte/createVoiceLiveOps.d.ts +13 -0
  315. package/dist/svelte/createVoiceOpsActionCenter.d.ts +10 -0
  316. package/dist/svelte/createVoiceOpsStatus.d.ts +9 -0
  317. package/dist/svelte/createVoicePlatformCoverage.d.ts +7 -0
  318. package/dist/svelte/createVoiceProfileComparison.d.ts +7 -0
  319. package/dist/svelte/createVoiceProofTrends.d.ts +7 -0
  320. package/dist/svelte/createVoiceProviderCapabilities.d.ts +10 -0
  321. package/dist/svelte/createVoiceProviderContracts.d.ts +10 -0
  322. package/dist/svelte/createVoiceProviderSimulationControls.d.ts +11 -0
  323. package/dist/svelte/createVoiceProviderStatus.d.ts +4 -2
  324. package/dist/svelte/createVoiceReadinessFailures.d.ts +7 -0
  325. package/dist/svelte/createVoiceReconnectProfileEvidence.d.ts +7 -0
  326. package/dist/svelte/createVoiceReplayTimeline.d.ts +13 -0
  327. package/dist/svelte/createVoiceRoutingStatus.d.ts +10 -0
  328. package/dist/svelte/createVoiceSessionObservability.d.ts +10 -0
  329. package/dist/svelte/createVoiceSessionSnapshot.d.ts +13 -0
  330. package/dist/svelte/createVoiceStream.d.ts +1 -1
  331. package/dist/svelte/createVoiceTraceTimeline.d.ts +10 -0
  332. package/dist/svelte/createVoiceTurnLatency.d.ts +11 -0
  333. package/dist/svelte/createVoiceTurnQuality.d.ts +10 -0
  334. package/dist/svelte/createVoiceWidget.d.ts +19 -0
  335. package/dist/svelte/createVoiceWorkflowStatus.d.ts +1 -1
  336. package/dist/svelte/index.d.ts +37 -5
  337. package/dist/svelte/index.js +6931 -684
  338. package/dist/telephony/contract.d.ts +61 -0
  339. package/dist/telephony/matrix.d.ts +97 -0
  340. package/dist/telephony/plivo.d.ts +303 -0
  341. package/dist/telephony/response.d.ts +1 -1
  342. package/dist/telephony/security.d.ts +182 -0
  343. package/dist/telephony/telnyx.d.ts +291 -0
  344. package/dist/telephony/twilio.d.ts +147 -13
  345. package/dist/telephonyMediaRoutes.d.ts +72 -0
  346. package/dist/telephonyOutcome.d.ts +273 -0
  347. package/dist/testing/accuracy.d.ts +1 -1
  348. package/dist/testing/benchmark.d.ts +9 -9
  349. package/dist/testing/corrected.d.ts +5 -5
  350. package/dist/testing/duplex.d.ts +3 -3
  351. package/dist/testing/fixtures.d.ts +3 -3
  352. package/dist/testing/index.d.ts +13 -13
  353. package/dist/testing/index.js +5223 -159
  354. package/dist/testing/ioProviderSimulator.d.ts +5 -5
  355. package/dist/testing/providerSimulator.d.ts +5 -5
  356. package/dist/testing/review.d.ts +4 -4
  357. package/dist/testing/sessionBenchmark.d.ts +3 -3
  358. package/dist/testing/stt.d.ts +3 -3
  359. package/dist/testing/telephony.d.ts +25 -0
  360. package/dist/testing/tts.d.ts +1 -1
  361. package/dist/toolContract.d.ts +161 -0
  362. package/dist/toolRuntime.d.ts +50 -0
  363. package/dist/trace.d.ts +46 -7
  364. package/dist/traceDeliveryRoutes.d.ts +86 -0
  365. package/dist/traceTimeline.d.ts +97 -0
  366. package/dist/transcriptAnnotator.d.ts +41 -0
  367. package/dist/turnDetection.d.ts +1 -1
  368. package/dist/turnLatency.d.ts +95 -0
  369. package/dist/turnProfiles.d.ts +2 -2
  370. package/dist/turnQuality.d.ts +94 -0
  371. package/dist/types.d.ts +293 -80
  372. package/dist/vapiAdapter.d.ts +160 -0
  373. package/dist/voiceMonitoring.d.ts +444 -0
  374. package/dist/vue/VoiceCallDebuggerLaunch.d.ts +68 -0
  375. package/dist/vue/VoiceCallPlayer.d.ts +40 -0
  376. package/dist/vue/VoiceCostDashboard.d.ts +57 -0
  377. package/dist/vue/VoiceDeliveryRuntime.d.ts +30 -0
  378. package/dist/vue/VoiceLiveAgentConsole.d.ts +50 -0
  379. package/dist/vue/VoiceLiveCallViewer.d.ts +35 -0
  380. package/dist/vue/VoiceOpsActionCenter.d.ts +13 -0
  381. package/dist/vue/VoiceOpsStatus.d.ts +30 -0
  382. package/dist/vue/VoicePlatformCoverage.d.ts +23 -0
  383. package/dist/vue/VoiceProofTrends.d.ts +21 -0
  384. package/dist/vue/VoiceProviderCapabilities.d.ts +51 -0
  385. package/dist/vue/VoiceProviderContracts.d.ts +21 -0
  386. package/dist/vue/VoiceProviderSimulationControls.d.ts +88 -0
  387. package/dist/vue/VoiceProviderStatus.d.ts +51 -0
  388. package/dist/vue/VoiceReadinessFailures.d.ts +21 -0
  389. package/dist/vue/VoiceReconnectProfileEvidence.d.ts +21 -0
  390. package/dist/vue/VoiceReplayTimeline.d.ts +17 -0
  391. package/dist/vue/VoiceRoutingStatus.d.ts +51 -0
  392. package/dist/vue/VoiceSessionObservability.d.ts +23 -0
  393. package/dist/vue/VoiceSessionSnapshot.d.ts +68 -0
  394. package/dist/vue/VoiceTurnLatency.d.ts +69 -0
  395. package/dist/vue/VoiceTurnQuality.d.ts +51 -0
  396. package/dist/vue/VoiceWidget.d.ts +77 -0
  397. package/dist/vue/index.d.ts +49 -5
  398. package/dist/vue/index.js +11954 -226
  399. package/dist/vue/useVoiceAgentSquadStatus.d.ts +9 -0
  400. package/dist/vue/useVoiceCallDebugger.d.ts +10 -0
  401. package/dist/vue/useVoiceCampaignDialerProof.d.ts +11 -0
  402. package/dist/vue/useVoiceController.d.ts +4 -2
  403. package/dist/vue/useVoiceDeliveryRuntime.d.ts +13 -0
  404. package/dist/vue/useVoiceLiveOps.d.ts +9 -0
  405. package/dist/vue/useVoiceOpsActionCenter.d.ts +11 -0
  406. package/dist/vue/useVoiceOpsStatus.d.ts +9 -0
  407. package/dist/vue/useVoicePlatformCoverage.d.ts +9 -0
  408. package/dist/vue/useVoiceProfileComparison.d.ts +9 -0
  409. package/dist/vue/useVoiceProofTrends.d.ts +9 -0
  410. package/dist/vue/useVoiceProviderCapabilities.d.ts +9 -0
  411. package/dist/vue/useVoiceProviderContracts.d.ts +9 -0
  412. package/dist/vue/useVoiceProviderSimulationControls.d.ts +24 -0
  413. package/dist/vue/useVoiceProviderStatus.d.ts +3 -3
  414. package/dist/vue/useVoiceReadinessFailures.d.ts +959 -0
  415. package/dist/vue/useVoiceReconnectProfileEvidence.d.ts +9 -0
  416. package/dist/vue/useVoiceRoutingStatus.d.ts +8 -0
  417. package/dist/vue/useVoiceSessionObservability.d.ts +9 -0
  418. package/dist/vue/useVoiceSessionSnapshot.d.ts +10 -0
  419. package/dist/vue/useVoiceStream.d.ts +5 -2
  420. package/dist/vue/useVoiceTraceTimeline.d.ts +9 -0
  421. package/dist/vue/useVoiceTurnLatency.d.ts +10 -0
  422. package/dist/vue/useVoiceTurnQuality.d.ts +9 -0
  423. package/dist/vue/useVoiceWorkflowStatus.d.ts +3 -3
  424. package/dist/webhookFanout.d.ts +48 -0
  425. package/dist/webhookVerification.d.ts +27 -0
  426. package/dist/whisperChannel.d.ts +50 -0
  427. package/dist/workflowContract.d.ts +8 -8
  428. package/fixtures/manifest.json +356 -197
  429. package/package.json +265 -256
  430. package/dist/angular/voice-app-kit-status.service.d.ts +0 -12
  431. package/dist/appKit.d.ts +0 -92
  432. package/dist/client/appKitStatus.d.ts +0 -19
  433. package/dist/react/useVoiceAppKitStatus.d.ts +0 -8
  434. package/dist/svelte/createVoiceAppKitStatus.d.ts +0 -8
  435. 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,232 @@ var serverMessageToAction = (message) => {
226
244
  }
227
245
  };
228
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
+
229
473
  // src/client/connection.ts
230
474
  var WS_OPEN = 1;
231
475
  var WS_CLOSED = 3;
@@ -245,6 +489,7 @@ var NOOP_CONNECTION = {
245
489
  getSessionId: () => "",
246
490
  send: noop,
247
491
  sendAudio: noop,
492
+ simulateDisconnect: noop,
248
493
  start: () => {},
249
494
  subscribe: noopUnsubscribe
250
495
  };
@@ -269,10 +514,12 @@ var isVoiceServerMessage = (value) => {
269
514
  case "assistant":
270
515
  case "call_lifecycle":
271
516
  case "complete":
517
+ case "connection":
272
518
  case "error":
273
519
  case "final":
274
520
  case "partial":
275
521
  case "pong":
522
+ case "replay":
276
523
  case "session":
277
524
  case "turn":
278
525
  return true;
@@ -309,6 +556,9 @@ var createVoiceConnection = (path, options = {}) => {
309
556
  sessionId: options.sessionId ?? createSessionId(),
310
557
  ws: null
311
558
  };
559
+ const emitConnection = (reconnect) => {
560
+ listeners.forEach((listener) => listener(reconnect));
561
+ };
312
562
  const clearTimers = () => {
313
563
  if (state.pingInterval) {
314
564
  clearInterval(state.pingInterval);
@@ -331,9 +581,28 @@ var createVoiceConnection = (path, options = {}) => {
331
581
  }
332
582
  };
333
583
  const scheduleReconnect = () => {
584
+ const nextAttemptAt = Date.now() + RECONNECT_DELAY_MS;
334
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
+ });
335
596
  state.reconnectTimeout = setTimeout(() => {
336
597
  if (state.reconnectAttempts > maxReconnectAttempts) {
598
+ emitConnection({
599
+ reconnect: {
600
+ attempts: state.reconnectAttempts,
601
+ maxAttempts: maxReconnectAttempts,
602
+ status: "exhausted"
603
+ },
604
+ type: "connection"
605
+ });
337
606
  return;
338
607
  }
339
608
  connect();
@@ -343,9 +612,21 @@ var createVoiceConnection = (path, options = {}) => {
343
612
  const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
344
613
  ws.binaryType = "arraybuffer";
345
614
  ws.onopen = () => {
615
+ const wasReconnecting = state.reconnectAttempts > 0;
346
616
  state.isConnected = true;
347
- state.reconnectAttempts = 0;
348
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
+ }
349
630
  listeners.forEach((listener) => listener({
350
631
  scenarioId: state.scenarioId ?? undefined,
351
632
  sessionId: state.sessionId,
@@ -375,6 +656,16 @@ var createVoiceConnection = (path, options = {}) => {
375
656
  const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && state.reconnectAttempts < maxReconnectAttempts;
376
657
  if (reconnectable) {
377
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
+ });
378
669
  }
379
670
  };
380
671
  state.ws = ws;
@@ -423,6 +714,11 @@ var createVoiceConnection = (path, options = {}) => {
423
714
  state.isConnected = false;
424
715
  listeners.clear();
425
716
  };
717
+ const simulateDisconnect = () => {
718
+ if (state.ws?.readyState === WS_OPEN) {
719
+ state.ws.close(4000, "absolutejs-voice-reconnect-proof");
720
+ }
721
+ };
426
722
  const subscribe = (callback) => {
427
723
  listeners.add(callback);
428
724
  return () => {
@@ -439,20 +735,28 @@ var createVoiceConnection = (path, options = {}) => {
439
735
  getSessionId: () => state.sessionId,
440
736
  send,
441
737
  sendAudio,
738
+ simulateDisconnect,
442
739
  start,
443
740
  subscribe
444
741
  };
445
742
  };
446
743
 
447
744
  // src/client/store.ts
745
+ var createInitialReconnectState = () => ({
746
+ attempts: 0,
747
+ maxAttempts: 0,
748
+ status: "idle"
749
+ });
448
750
  var createInitialState = () => ({
449
751
  assistantAudio: [],
450
752
  assistantTexts: [],
451
753
  call: null,
452
754
  error: null,
453
755
  isConnected: false,
756
+ sessionMetadata: null,
454
757
  scenarioId: null,
455
758
  partial: "",
759
+ reconnect: createInitialReconnectState(),
456
760
  sessionId: null,
457
761
  status: "idle",
458
762
  turns: []
@@ -509,7 +813,19 @@ var createVoiceStreamStore = () => {
509
813
  case "connected":
510
814
  state = {
511
815
  ...state,
512
- 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
513
829
  };
514
830
  break;
515
831
  case "disconnected":
@@ -537,6 +853,27 @@ var createVoiceStreamStore = () => {
537
853
  partial: action.transcript.text
538
854
  };
539
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;
540
877
  case "session":
541
878
  state = {
542
879
  ...state,
@@ -544,6 +881,7 @@ var createVoiceStreamStore = () => {
544
881
  scenarioId: action.scenarioId ?? state.scenarioId,
545
882
  isConnected: action.status === "active",
546
883
  sessionId: action.sessionId,
884
+ sessionMetadata: action.sessionMetadata ?? state.sessionMetadata,
547
885
  status: action.status
548
886
  };
549
887
  break;
@@ -574,20 +912,50 @@ var createVoiceStreamStore = () => {
574
912
  var createVoiceStream = (path, options = {}) => {
575
913
  const connection = createVoiceConnection(path, options);
576
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;
577
920
  const subscribers = new Set;
578
921
  const start = (input) => Promise.resolve().then(() => {
579
922
  if (!input?.sessionId && !input?.scenarioId) {
580
923
  return;
581
924
  }
582
925
  connection.start(input);
926
+ browserMediaReporter?.start();
583
927
  });
584
928
  const notify = () => {
585
929
  subscribers.forEach((subscriber) => subscriber());
586
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
+ };
587
952
  const unsubscribeConnection = connection.subscribe((message) => {
588
953
  const action = serverMessageToAction(message);
589
954
  if (action) {
590
955
  store.dispatch(action);
956
+ if (message.type === "connection") {
957
+ reportReconnect();
958
+ }
591
959
  notify();
592
960
  }
593
961
  });
@@ -597,6 +965,7 @@ var createVoiceStream = (path, options = {}) => {
597
965
  },
598
966
  close() {
599
967
  unsubscribeConnection();
968
+ browserMediaReporter?.close();
600
969
  connection.close();
601
970
  store.dispatch({ type: "disconnected" });
602
971
  notify();
@@ -619,10 +988,16 @@ var createVoiceStream = (path, options = {}) => {
619
988
  get scenarioId() {
620
989
  return store.getSnapshot().scenarioId;
621
990
  },
991
+ get sessionMetadata() {
992
+ return store.getSnapshot().sessionMetadata;
993
+ },
622
994
  start,
623
995
  get partial() {
624
996
  return store.getSnapshot().partial;
625
997
  },
998
+ get reconnect() {
999
+ return store.getSnapshot().reconnect;
1000
+ },
626
1001
  get sessionId() {
627
1002
  return connection.getSessionId();
628
1003
  },
@@ -644,6 +1019,9 @@ var createVoiceStream = (path, options = {}) => {
644
1019
  sendAudio(audio) {
645
1020
  connection.sendAudio(audio);
646
1021
  },
1022
+ simulateDisconnect() {
1023
+ connection.simulateDisconnect();
1024
+ },
647
1025
  subscribe(subscriber) {
648
1026
  subscribers.add(subscriber);
649
1027
  return () => {
@@ -941,8 +1319,10 @@ var createInitialState2 = (stream) => ({
941
1319
  isConnected: stream.isConnected,
942
1320
  isRecording: false,
943
1321
  partial: stream.partial,
1322
+ reconnect: stream.reconnect,
944
1323
  recordingError: null,
945
1324
  sessionId: stream.sessionId,
1325
+ sessionMetadata: stream.sessionMetadata,
946
1326
  scenarioId: stream.scenarioId,
947
1327
  status: stream.status,
948
1328
  turns: [...stream.turns]
@@ -970,7 +1350,9 @@ var createVoiceController = (path, options = {}) => {
970
1350
  error: stream.error,
971
1351
  isConnected: stream.isConnected,
972
1352
  partial: stream.partial,
1353
+ reconnect: stream.reconnect,
973
1354
  sessionId: stream.sessionId,
1355
+ sessionMetadata: stream.sessionMetadata,
974
1356
  scenarioId: stream.scenarioId,
975
1357
  status: stream.status,
976
1358
  turns: [...stream.turns]
@@ -994,7 +1376,13 @@ var createVoiceController = (path, options = {}) => {
994
1376
  capture = createMicrophoneCapture({
995
1377
  channelCount: options.capture?.channelCount ?? preset.capture.channelCount,
996
1378
  onLevel: options.capture?.onLevel,
997
- 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
+ },
998
1386
  sampleRateHz: options.capture?.sampleRateHz ?? preset.capture.sampleRateHz
999
1387
  });
1000
1388
  return capture;
@@ -1064,10 +1452,17 @@ var createVoiceController = (path, options = {}) => {
1064
1452
  get recordingError() {
1065
1453
  return state.recordingError;
1066
1454
  },
1455
+ get reconnect() {
1456
+ return state.reconnect;
1457
+ },
1067
1458
  sendAudio: (audio) => stream.sendAudio(audio),
1459
+ simulateDisconnect: () => stream.simulateDisconnect(),
1068
1460
  get sessionId() {
1069
1461
  return state.sessionId;
1070
1462
  },
1463
+ get sessionMetadata() {
1464
+ return state.sessionMetadata;
1465
+ },
1071
1466
  get scenarioId() {
1072
1467
  return state.scenarioId;
1073
1468
  },
@@ -1104,6 +1499,475 @@ var createVoiceController = (path, options = {}) => {
1104
1499
  };
1105
1500
  };
1106
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);
1967
+ }
1968
+ };
1969
+ };
1970
+
1107
1971
  // src/client/htmxBootstrap.ts
1108
1972
  var VOICE_WAVE_POINTS = 48;
1109
1973
  var VOICE_WAVE_WIDTH = 320;
@@ -1126,7 +1990,7 @@ var DEFAULT_GUIDED_PROMPTS = [
1126
1990
  "Now describe what you are trying to do or test.",
1127
1991
  "Finish with any detail that feels blocked, risky, or unclear."
1128
1992
  ];
1129
- 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));
1130
1994
  var escapeHtml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1131
1995
  var readErrorField = (value, key) => {
1132
1996
  const candidate = value[key];
@@ -1160,6 +2024,17 @@ var formatErrorMessage = (error) => {
1160
2024
  }
1161
2025
  return "Unexpected error";
1162
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
+ };
1163
2038
  var createInitialVoiceWaveLevels = (count = VOICE_WAVE_POINTS) => Array.from({ length: count }, () => 0);
1164
2039
  var pushVoiceWaveLevel = (levels, nextLevel, count = VOICE_WAVE_POINTS) => {
1165
2040
  const next = levels.slice(-(count - 1));
@@ -1216,6 +2091,20 @@ var parsePromptList = (value) => {
1216
2091
  } catch {}
1217
2092
  return DEFAULT_GUIDED_PROMPTS;
1218
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
+ };
1219
2108
  var requireElement = (root, selector, ctor, name) => {
1220
2109
  const value = selector ? document.querySelector(selector) : null;
1221
2110
  if (value instanceof ctor) {
@@ -1266,11 +2155,20 @@ var initVoiceHTMXRoot = (root) => {
1266
2155
  const guidedPrompts = parsePromptList(root.dataset.voiceGuidedPrompts);
1267
2156
  const guidedLabel = root.dataset.voiceGuidedLabel ?? DEFAULT_GUIDED_LABEL;
1268
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;
1269
2166
  const syncElement = requireElement(document, root.dataset.voiceSync, HTMLElement, "voice-htmx-sync");
1270
2167
  const connectionMetric = requireElement(root, root.dataset.voiceConnection, HTMLElement, "metric-connection");
1271
2168
  const errorStatus = requireElement(root, root.dataset.voiceError, HTMLElement, "status-error");
1272
2169
  const microphoneStatus = requireElement(root, root.dataset.voiceMicrophone, HTMLElement, "status-mic");
1273
2170
  const promptStatus = requireElement(root, root.dataset.voicePrompt, HTMLElement, "status-prompt");
2171
+ const reconnectStatus = resolveElement2(root, root.dataset.voiceReconnect, HTMLElement);
1274
2172
  const chatList = requireElement(root, root.dataset.voiceChat, HTMLElement, "chat-list");
1275
2173
  const startGuidedButton = requireElement(root, root.dataset.voiceStartGuided, HTMLButtonElement, "start-guided");
1276
2174
  const startGeneralButton = requireElement(root, root.dataset.voiceStartGeneral, HTMLButtonElement, "start-general");
@@ -1279,35 +2177,70 @@ var initVoiceHTMXRoot = (root) => {
1279
2177
  const voiceMonitorCopy = requireElement(root, root.dataset.voiceMonitorCopy, HTMLElement, "voice-monitor-copy");
1280
2178
  const voiceWaveGlow = requireElement(root, root.dataset.voiceWaveGlow, SVGPathElement, "voice-wave-glow");
1281
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;
1282
2190
  const guidedVoice = createVoiceController(guidedPath, {
1283
2191
  capture: {
2192
+ onAudio: (audio, sendAudio) => {
2193
+ if (guidedBargeInBinding) {
2194
+ guidedBargeInBinding.sendAudio(audio);
2195
+ return;
2196
+ }
2197
+ sendAudio(audio);
2198
+ },
1284
2199
  onLevel: (level) => {
2200
+ guidedBargeInBinding?.handleLevel(level);
1285
2201
  waveLevels = pushVoiceWaveLevel(waveLevels, level);
1286
2202
  renderWave();
1287
2203
  }
1288
2204
  },
2205
+ connection: {
2206
+ reconnectReportPath
2207
+ },
1289
2208
  preset: "guided-intake"
1290
2209
  });
1291
2210
  const generalVoice = createVoiceController(generalPath, {
1292
2211
  capture: {
2212
+ onAudio: (audio, sendAudio) => {
2213
+ if (generalBargeInBinding) {
2214
+ generalBargeInBinding.sendAudio(audio);
2215
+ return;
2216
+ }
2217
+ sendAudio(audio);
2218
+ },
1293
2219
  onLevel: (level) => {
2220
+ generalBargeInBinding?.handleLevel(level);
1294
2221
  waveLevels = pushVoiceWaveLevel(waveLevels, level);
1295
2222
  renderWave();
1296
2223
  }
1297
2224
  },
2225
+ connection: {
2226
+ reconnectReportPath
2227
+ },
1298
2228
  preset: "dictation"
1299
2229
  });
1300
2230
  const stopGuidedBinding = guidedVoice.bindHTMX({ element: syncElement });
1301
2231
  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();
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
+ });
1310
2242
  const currentVoice = () => activeMode === "general" ? generalVoice : guidedVoice;
2243
+ const currentAudioPlayer = () => activeMode === "general" ? generalAudioPlayer : guidedAudioPlayer;
1311
2244
  const renderWave = () => {
1312
2245
  const path = createVoiceWavePath(waveLevels);
1313
2246
  voiceWaveGlow.setAttribute("d", path);
@@ -1322,6 +2255,9 @@ var initVoiceHTMXRoot = (root) => {
1322
2255
  const status = voice.status;
1323
2256
  connectionMetric.textContent = voice.isConnected ? "Connected" : "Waiting";
1324
2257
  errorStatus.textContent = micError || voice.error || "None";
2258
+ if (reconnectStatus) {
2259
+ reconnectStatus.textContent = formatReconnectState(voice.reconnect);
2260
+ }
1325
2261
  microphoneStatus.textContent = isCapturing ? DEFAULT_MIC_LIVE : DEFAULT_MIC_IDLE;
1326
2262
  promptStatus.textContent = resolvePromptMessage({
1327
2263
  guidedPrompts,
@@ -1385,8 +2321,18 @@ var initVoiceHTMXRoot = (root) => {
1385
2321
  render();
1386
2322
  }
1387
2323
  };
1388
- guidedVoice.subscribe(render);
1389
- 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
+ });
1390
2336
  startGuidedButton.addEventListener("click", () => {
1391
2337
  startMode("guided");
1392
2338
  });
@@ -1396,9 +2342,16 @@ var initVoiceHTMXRoot = (root) => {
1396
2342
  stopButton.addEventListener("click", () => {
1397
2343
  stopMic();
1398
2344
  });
2345
+ root.addEventListener("absolute-voice-simulate-disconnect", () => {
2346
+ currentVoice().simulateDisconnect();
2347
+ });
1399
2348
  window.addEventListener("beforeunload", () => {
1400
2349
  guidedVoice.stopRecording();
1401
2350
  generalVoice.stopRecording();
2351
+ guidedBargeInBinding?.close();
2352
+ generalBargeInBinding?.close();
2353
+ guidedAudioPlayer.close();
2354
+ generalAudioPlayer.close();
1402
2355
  stopGuidedBinding();
1403
2356
  stopGeneralBinding();
1404
2357
  guidedVoice.close();