@absolutejs/voice 0.0.22-beta.461 → 0.0.22-beta.464
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.
- package/dist/angular/index.js +265 -0
- package/dist/client/htmxBootstrap.js +330 -7
- package/dist/client/index.js +322 -0
- package/dist/generated/htmxBootstrapBundle.d.ts +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.js +1231 -497
- package/dist/mediaPipelineRoutes.d.ts +55 -1
- package/dist/mediaPipelineSurfaces.d.ts +48 -0
- package/dist/productionReadiness.d.ts +17 -0
- package/dist/proofTrends.d.ts +25 -0
- package/dist/react/index.js +322 -0
- package/dist/sessionObservability.d.ts +41 -0
- package/dist/svelte/index.js +265 -0
- package/dist/testing/index.js +265 -0
- package/dist/vue/index.js +322 -0
- package/dist/vue/useVoiceReadinessFailures.d.ts +16 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5186,7 +5186,7 @@ var createVoiceSession = (options) => {
|
|
|
5186
5186
|
};
|
|
5187
5187
|
|
|
5188
5188
|
// src/generated/htmxBootstrapBundle.ts
|
|
5189
|
-
var HTMX_BOOTSTRAP_BUNDLE = 'var Ue=(e)=>{if(typeof e!=="string")return e;return document.querySelector(e)},Ne=(e,n,t,o)=>{let r=n??e.getAttribute("hx-get")??"";if(!r)return"";let i=new URL(r,window.location.origin);if(o)i.searchParams.set(t,o);else i.searchParams.delete(t);return`${i.pathname}${i.search}${i.hash}`},de=(e,n)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let t=Ue(n.element);if(!t)return()=>{};let o=n.eventName??"voice-refresh",r=n.sessionQueryParam??"sessionId",i=()=>{let l=window,g=Ne(t,n.route,r,e.sessionId);if(g)t.setAttribute("hx-get",g);l.htmx?.process?.(t),l.htmx?.trigger?.(t,o)},c=e.subscribe(i);return i(),()=>{c()}};var He=(e)=>Math.max(-1,Math.min(1,e)),Ge=(e)=>{let n=new Int16Array(e.length);for(let t=0;t<e.length;t+=1){let o=He(e[t]??0);n[t]=o<0?o*32768:o*32767}return new Uint8Array(n.buffer)},Be=(e)=>{let n=e instanceof Uint8Array?e:new Uint8Array(e);if(n.byteLength<2)return 0;let t=new Int16Array(n.buffer,n.byteOffset,Math.floor(n.byteLength/2));if(t.length===0)return 0;let o=0;for(let r of t){let i=r/32768;o+=i*i}return Math.min(1,Math.max(0,Math.sqrt(o/t.length)*5.5))},We=(e,n,t)=>{if(n===t)return e;let o=n/t,r=Math.round(e.length/o),i=new Float32Array(r),c=0,l=0;while(c<i.length){let g=Math.round((c+1)*o),y=0,d=0;for(let h=l;h<g&&h<e.length;h+=1)y+=e[h]??0,d+=1;i[c]=d>0?y/d:0,c+=1,l=g}return i},ge=(e)=>{let n=null,t=null,o=null,r=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let l=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!l)throw Error("Browser microphone capture requires AudioContext support.");r=await navigator.mediaDevices.getUserMedia({audio:{channelCount:e.channelCount??1}}),n=new l,t=n.createMediaStreamSource(r),o=n.createScriptProcessor(4096,1,1),o.onaudioprocess=(g)=>{let y=g.inputBuffer.getChannelData(0),d=We(y,n?.sampleRate??48000,e.sampleRateHz??16000),h=Ge(d);e.onLevel?.(Be(h)),e.onAudio(h)},t.connect(o),o.connect(n.destination)},stop:()=>{o?.disconnect(),t?.disconnect(),r?.getTracks().forEach((l)=>l.stop()),n?.close(),e.onLevel?.(0),n=null,r=null,o=null,t=null}}};var ee=(e)=>{if(typeof e==="string"&&e.trim())return e;if(e instanceof Error&&e.message.trim())return e.message;if(e&&typeof e==="object"){let n=e;for(let t of["message","reason","description"]){let o=n[t];if(typeof o==="string"&&o.trim())return o}if("error"in n)return ee(n.error);if("cause"in n)return ee(n.cause);try{return JSON.stringify(e)}catch{}}return"Unexpected error"},Ae=(e)=>{switch(e.type){case"audio":return{chunk:Uint8Array.from(atob(e.chunkBase64),(n)=>n.charCodeAt(0)),format:e.format,receivedAt:e.receivedAt,turnId:e.turnId,type:"audio"};case"assistant":return{text:e.text,type:"assistant"};case"complete":return{sessionId:e.sessionId,type:"complete"};case"connection":return{reconnect:e.reconnect,type:"connection"};case"call_lifecycle":return{event:e.event,sessionId:e.sessionId,type:"call_lifecycle"};case"error":return{message:ee(e.message),type:"error"};case"final":return{transcript:e.transcript,type:"final"};case"partial":return{transcript:e.transcript,type:"partial"};case"replay":return{assistantTexts:e.assistantTexts,call:e.call,partial:e.partial,scenarioId:e.scenarioId,sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,status:e.status,turns:e.turns,type:"replay"};case"session":return{sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,scenarioId:e.scenarioId,status:e.status,type:"session"};case"turn":return{turn:e.turn,type:"turn"};default:return null}};var H=(e,n,t,o)=>{e.push({code:t,message:o,severity:n})};var $e=(e)=>e.length===0?void 0:e.reduce((n,t)=>n+t,0)/e.length,K=(e)=>e.length===0?void 0:Math.max(...e);var b=(e,n)=>{let t=e[n];return typeof t==="number"&&Number.isFinite(t)?t:void 0},Z=(e,n)=>{let t=e[n];return typeof t==="boolean"?t:void 0},D=(e,n)=>{let t=e[n];return typeof t==="string"?t:void 0},ne=(e)=>String(e.id??D(e,"ssrc")??b(e,"ssrc")??D(e,"trackIdentifier")??D(e,"mid")??"unknown"),he=(e)=>e===void 0?void 0:e*1000;var ke=(e)=>{let n={};for(let[t,o]of Object.entries(e))if(o===null||typeof o==="boolean"||typeof o==="number"||typeof o==="string")n[t]=o;return n};var ye=(e={})=>{let n=e.stats??[],t=[],o=n.filter((s)=>s.type==="inbound-rtp"&&D(s,"kind")!=="video"),r=n.filter((s)=>s.type==="outbound-rtp"&&D(s,"kind")!=="video"),i=n.filter((s)=>s.type==="candidate-pair"),c=n.filter((s)=>(s.type==="track"||s.type==="media-source")&&D(s,"kind")==="audio"),l=i.filter((s)=>Z(s,"selected")===!0||Z(s,"nominated")===!0||D(s,"state")==="succeeded").length,g=c.filter((s)=>D(s,"readyState")!=="ended"&&D(s,"trackState")!=="ended"&&Z(s,"ended")!==!0).length,y=c.filter((s)=>D(s,"readyState")==="ended"||D(s,"trackState")==="ended"||Z(s,"ended")===!0).length,d=o.reduce((s,S)=>s+(b(S,"packetsReceived")??0),0),h=r.reduce((s,S)=>s+(b(S,"packetsSent")??0),0),a=[...o,...r].reduce((s,S)=>s+Math.max(0,b(S,"packetsLost")??0),0),M=d+a,C=M===0?0:a/M,I=o.reduce((s,S)=>s+(b(S,"bytesReceived")??0),0),w=r.reduce((s,S)=>s+(b(S,"bytesSent")??0),0),R=K(i.map((s)=>he(b(s,"currentRoundTripTime")??b(s,"roundTripTime"))).filter((s)=>s!==void 0)),u=K([...o,...r].map((s)=>he(b(s,"jitter"))).filter((s)=>s!==void 0)),O=K(o.map((s)=>{let S=b(s,"jitterBufferDelay"),L=b(s,"jitterBufferEmittedCount");return S!==void 0&&L!==void 0&&L>0?S/L*1000:void 0}).filter((s)=>s!==void 0)),U=c.map((s)=>b(s,"audioLevel")).filter((s)=>s!==void 0);if(e.requireConnectedCandidatePair&&i.length>0&&l===0)H(t,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(e.requireLiveAudioTrack&&g===0)H(t,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(e.maxPacketLossRatio!==void 0&&C>e.maxPacketLossRatio)H(t,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(C)} above ${String(e.maxPacketLossRatio)}.`);if(e.maxRoundTripTimeMs!==void 0&&R!==void 0&&R>e.maxRoundTripTimeMs)H(t,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(R)}ms above ${String(e.maxRoundTripTimeMs)}ms.`);if(e.maxJitterMs!==void 0&&u!==void 0&&u>e.maxJitterMs)H(t,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(u)}ms above ${String(e.maxJitterMs)}ms.`);return{activeCandidatePairs:l,audioLevelAverage:$e(U),bytesReceived:I,bytesSent:w,checkedAt:Date.now(),endedAudioTracks:y,inboundPackets:d,issues:t,jitterBufferDelayMs:O,jitterMs:u,liveAudioTracks:g,outboundPackets:h,packetLossRatio:C,packetsLost:a,roundTripTimeMs:R,status:t.some((s)=>s.severity==="error")?"fail":t.length>0?"warn":"pass",totalStats:n.length}},Ce=async(e)=>{return[...(await e.peerConnection.getStats(e.selector??null)).values()].map(ke)};var fe=(e={})=>{let n=e.stats??[],t=e.previousStats??[],o=[],r=new Map(t.map((a)=>[ne(a),a])),c=n.filter((a)=>(a.type==="inbound-rtp"||a.type==="outbound-rtp")&&D(a,"kind")!=="video"&&D(a,"mediaType")!=="video").map((a)=>{let M=a.type==="outbound-rtp"?"outbound":"inbound",C=M==="outbound"?"packetsSent":"packetsReceived",I=M==="outbound"?"bytesSent":"bytesReceived",w=r.get(ne(a)),R=b(a,C),u=w?b(w,C):void 0,O=b(a,I),U=w?b(w,I):void 0,s=a.timestamp!==void 0&&w?.timestamp!==void 0?a.timestamp-w.timestamp:void 0;return{bytesDelta:O!==void 0&&U!==void 0?O-U:void 0,currentPackets:R,direction:M,id:ne(a),packetDelta:R!==void 0&&u!==void 0?R-u:void 0,previousPackets:u,timeDeltaMs:s}}),l=c.filter((a)=>a.direction==="inbound"),g=c.filter((a)=>a.direction==="outbound"),y=K(c.map((a)=>a.timeDeltaMs).filter((a)=>a!==void 0)),d=l.filter((a)=>e.maxInboundPacketStallMs!==void 0&&a.timeDeltaMs!==void 0&&a.timeDeltaMs>=e.maxInboundPacketStallMs&&a.packetDelta!==void 0&&a.packetDelta<=0).length,h=g.filter((a)=>e.maxOutboundPacketStallMs!==void 0&&a.timeDeltaMs!==void 0&&a.timeDeltaMs>=e.maxOutboundPacketStallMs&&a.packetDelta!==void 0&&a.packetDelta<=0).length;if(e.requireInboundAudio&&l.length===0)H(o,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(e.requireOutboundAudio&&g.length===0)H(o,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(e.maxGapMs!==void 0&&y!==void 0&&y>e.maxGapMs)H(o,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(y)}ms above ${String(e.maxGapMs)}ms.`);if(d>0)H(o,"error","media.webrtc_inbound_stalled",`${String(d)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)H(o,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:l.length,issues:o,maxObservedGapMs:y,outboundAudioStreams:g.length,stalledInboundStreams:d,stalledOutboundStreams:h,status:o.some((a)=>a.severity==="error")?"fail":o.length>0?"warn":"pass",streams:c,totalStats:n.length}};var qe="/api/voice/browser-media",Xe=5000,ze=async(e)=>e.peerConnection??await e.getPeerConnection?.()??null,Ye=async(e,n)=>{let t=n.fetch??globalThis.fetch;if(!t)return;await t(n.path??qe,{body:JSON.stringify(e),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},Te=(e)=>{let n=null,t=[],o=async()=>{let c=await ze(e);if(!c)return;let l=await Ce({peerConnection:c}),g=ye({...e,stats:l}),y=e.continuity===!1?void 0:fe({...e.continuity,previousStats:t,stats:l}),d={at:Date.now(),continuity:y,report:g,scenarioId:e.getScenarioId?.()??null,sessionId:e.getSessionId?.()??null};return t=l,e.onReport?.(d),await Ye(d,e),d},r=()=>{o().catch((c)=>{e.onError?.(c)})},i=()=>{if(n)clearInterval(n),n=null};return{close:i,reportOnce:o,start:()=>{if(n)return;r(),n=setInterval(r,e.intervalMs??Xe)},stop:i}};var B=()=>{},Je=()=>B,Qe={callControl:B,close:B,endTurn:B,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",send:B,sendAudio:B,simulateDisconnect:B,start:()=>{},subscribe:Je},Ze=()=>crypto.randomUUID(),Ke=(e,n,t)=>{let{hostname:o,port:r,protocol:i}=window.location,c=i==="https:"?"wss:":"ws:",l=r?`:${r}`:"",g=new URL(`${c}//${o}${l}${e}`);if(g.searchParams.set("sessionId",n),t)g.searchParams.set("scenarioId",t);return g.toString()},me=(e)=>{if(!e||typeof e!=="object"||!("type"in e))return!1;switch(e.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},je=(e)=>{if(typeof e.data!=="string")return null;try{let n=JSON.parse(e.data);return me(n)?n:null}catch{return null}},Se=(e,n={})=>{if(typeof window>"u")return Qe;let t=new Set,o=n.reconnect!==!1,r=n.maxReconnectAttempts??10,i=n.pingInterval??30000,c={isConnected:!1,pendingMessages:[],scenarioId:n.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:n.sessionId??Ze(),ws:null},l=(s)=>{t.forEach((S)=>S(s))},g=()=>{if(c.pingInterval)clearInterval(c.pingInterval),c.pingInterval=null;if(c.reconnectTimeout)clearTimeout(c.reconnectTimeout),c.reconnectTimeout=null},y=()=>{if(c.ws?.readyState!==1)return;while(c.pendingMessages.length>0){let s=c.pendingMessages.shift();if(s!==void 0)c.ws.send(s)}},d=()=>{let s=Date.now()+500;c.reconnectAttempts+=1,l({reconnect:{attempts:c.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:r,nextAttemptAt:s,status:"reconnecting"},type:"connection"}),c.reconnectTimeout=setTimeout(()=>{if(c.reconnectAttempts>r){l({reconnect:{attempts:c.reconnectAttempts,maxAttempts:r,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let s=new WebSocket(Ke(e,c.sessionId,c.scenarioId));s.binaryType="arraybuffer",s.onopen=()=>{let S=c.reconnectAttempts>0;if(c.isConnected=!0,y(),S)l({reconnect:{attempts:c.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:r,status:"resumed"},type:"connection"}),c.reconnectAttempts=0;t.forEach((L)=>L({scenarioId:c.scenarioId??void 0,sessionId:c.sessionId,status:"active",type:"session"})),c.pingInterval=setInterval(()=>{if(s.readyState===1)s.send(JSON.stringify({type:"ping"}))},i)},s.onmessage=(S)=>{let L=je(S);if(!L)return;if(L.type==="session")c.sessionId=L.sessionId,c.scenarioId=L.scenarioId??c.scenarioId;t.forEach((X)=>X(L))},s.onclose=(S)=>{if(c.isConnected=!1,g(),o&&S.code!==1000&&c.reconnectAttempts<r)d();else if(o&&S.code!==1000)l({reconnect:{attempts:c.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:r,status:"exhausted"},type:"connection"})},c.ws=s},a=(s)=>{if(c.ws?.readyState===1){c.ws.send(s);return}c.pendingMessages.push(s)},M=(s)=>{a(JSON.stringify(s))},C=(s={})=>{if(s.sessionId)c.sessionId=s.sessionId;if(s.scenarioId)c.scenarioId=s.scenarioId;M({type:"start",sessionId:c.sessionId,scenarioId:c.scenarioId??void 0})},I=(s)=>{a(s)},w=()=>{M({type:"end_turn"})},R=(s)=>{M({...s,type:"call_control"})},u=()=>{if(g(),c.ws)c.ws.close(1000),c.ws=null;c.isConnected=!1,t.clear()},O=()=>{if(c.ws?.readyState===1)c.ws.close(4000,"absolutejs-voice-reconnect-proof")},U=(s)=>{return t.add(s),()=>{t.delete(s)}};return h(),{callControl:R,close:u,endTurn:w,getReadyState:()=>c.ws?.readyState??3,getScenarioId:()=>c.scenarioId??"",getSessionId:()=>c.sessionId,send:M,sendAudio:I,simulateDisconnect:O,start:C,subscribe:U}};var ve=()=>({attempts:0,maxAttempts:0,status:"idle"}),Fe=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:"",reconnect:ve(),sessionId:null,status:"idle",turns:[]}),Me=()=>{let e=Fe(),n=new Set,t=()=>{n.forEach((r)=>r())};return{dispatch:(r)=>{switch(r.type){case"audio":e={...e,assistantAudio:[...e.assistantAudio,{chunk:r.chunk,format:r.format,receivedAt:r.receivedAt,turnId:r.turnId}]};break;case"assistant":e={...e,assistantTexts:[...e.assistantTexts,r.text]};break;case"complete":e={...e,sessionId:r.sessionId,status:"completed"};break;case"call_lifecycle":e={...e,call:{...e.call,disposition:r.event.type==="end"?r.event.disposition:e.call?.disposition,endedAt:r.event.type==="end"?r.event.at:e.call?.endedAt,events:[...e.call?.events??[],r.event],lastEventAt:r.event.at,startedAt:e.call?.startedAt??r.event.at},sessionId:r.sessionId};break;case"connected":e={...e,isConnected:!0,reconnect:e.reconnect.status==="reconnecting"?{...e.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:e.reconnect};break;case"connection":e={...e,reconnect:r.reconnect};break;case"disconnected":e={...e,isConnected:!1};break;case"error":e={...e,error:r.message};break;case"final":e={...e,partial:r.transcript.text,turns:e.turns.map((i)=>i)};break;case"partial":e={...e,partial:r.transcript.text};break;case"replay":e={...e,assistantTexts:[...r.assistantTexts],call:r.call??null,error:null,isConnected:r.status==="active",partial:r.partial,reconnect:e.reconnect.status==="reconnecting"?{...e.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:e.reconnect,scenarioId:r.scenarioId??e.scenarioId,sessionId:r.sessionId,sessionMetadata:r.sessionMetadata??e.sessionMetadata,status:r.status,turns:[...r.turns]};break;case"session":e={...e,error:null,scenarioId:r.scenarioId??e.scenarioId,isConnected:r.status==="active",sessionId:r.sessionId,sessionMetadata:r.sessionMetadata??e.sessionMetadata,status:r.status};break;case"turn":e={...e,partial:"",turns:[...e.turns,r.turn]};break}t()},getServerSnapshot:()=>e,getSnapshot:()=>e,subscribe:(r)=>{return n.add(r),()=>{n.delete(r)}}}};var Ie=(e,n={})=>{let t=Se(e,n),o=Me(),r=n.browserMedia&&typeof window<"u"?Te({...n.browserMedia,getScenarioId:()=>n.browserMedia?n.browserMedia.getScenarioId?.()??t.getScenarioId():t.getScenarioId(),getSessionId:()=>n.browserMedia?n.browserMedia.getSessionId?.()??t.getSessionId():t.getSessionId()}):null,i=new Set,c=(d)=>Promise.resolve().then(()=>{if(!d?.sessionId&&!d?.scenarioId)return;t.start(d),r?.start()}),l=()=>{i.forEach((d)=>d())},g=()=>{if(!n.reconnectReportPath||typeof fetch>"u")return;let d=o.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:d.reconnect,scenarioId:d.scenarioId,sessionId:t.getSessionId(),turnIds:d.turns.map((a)=>a.id)});fetch(n.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},y=t.subscribe((d)=>{let h=Ae(d);if(h){if(o.dispatch(h),d.type==="connection")g();l()}});return{callControl(d){t.callControl(d)},close(){y(),r?.close(),t.close(),o.dispatch({type:"disconnected"}),l()},endTurn(){t.endTurn()},get error(){return o.getSnapshot().error},getServerSnapshot(){return o.getServerSnapshot()},getSnapshot(){return o.getSnapshot()},get isConnected(){return o.getSnapshot().isConnected},get scenarioId(){return o.getSnapshot().scenarioId},get sessionMetadata(){return o.getSnapshot().sessionMetadata},start:c,get partial(){return o.getSnapshot().partial},get reconnect(){return o.getSnapshot().reconnect},get sessionId(){return t.getSessionId()},get status(){return o.getSnapshot().status},get turns(){return o.getSnapshot().turns},get assistantTexts(){return o.getSnapshot().assistantTexts},get assistantAudio(){return o.getSnapshot().assistantAudio},get call(){return o.getSnapshot().call},sendAudio(d){t.sendAudio(d)},simulateDisconnect(){t.simulateDisconnect()},subscribe(d){return i.add(d),()=>{i.delete(d)}}}};var Ve=(e)=>{if(!e||e.enabled===!1)return;return{enabled:!0,maxGain:e.maxGain??3,noiseGateAttenuation:e.noiseGateAttenuation??0.15,noiseGateThreshold:e.noiseGateThreshold??0.006,targetLevel:e.targetLevel??0.08}};var pe={balanced:{qualityProfile:"general",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},en={general:{},"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var we=(e)=>{let n=e?.profile??"fast",t=e?.qualityProfile??"general",o=pe[n],r=en[t];return{profile:n,qualityProfile:t,silenceMs:e?.silenceMs??r.silenceMs??o.silenceMs,speechThreshold:e?.speechThreshold??r.speechThreshold??o.speechThreshold,transcriptStabilityMs:e?.transcriptStabilityMs??r.transcriptStabilityMs??o.transcriptStabilityMs}};var nn={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"short-command",profile:"balanced"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"general",profile:"fast"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"accent-heavy",profile:"long-form"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{qualityProfile:"accent-heavy",profile:"long-form"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"noisy-room",profile:"long-form",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"noisy-room",profile:"long-form",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"noisy-room",profile:"long-form",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"noisy-room",profile:"long-form"}}},Le=(e="default")=>{let n=nn[e];return{audioConditioning:Ve(n.audioConditioning),capture:{channelCount:n.capture?.channelCount??1,sampleRateHz:n.capture?.sampleRateHz??16000},connection:{...n.connection},name:e,sttLifecycle:n.sttLifecycle??"continuous",turnDetection:we(n.turnDetection)}};var on=(e)=>({assistantAudio:[...e.assistantAudio],assistantTexts:[...e.assistantTexts],call:e.call,error:e.error,isConnected:e.isConnected,isRecording:!1,partial:e.partial,reconnect:e.reconnect,recordingError:null,sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,scenarioId:e.scenarioId,status:e.status,turns:[...e.turns]}),m=(e,n={})=>{let t=Le(n.preset),o=Ie(e,{...t.connection,...n.connection}),r=null,i=on(o),c=new Set,l=()=>{for(let C of c)C()},g=()=>{if(i={...i,assistantAudio:[...o.assistantAudio],assistantTexts:[...o.assistantTexts],call:o.call,error:o.error,isConnected:o.isConnected,partial:o.partial,reconnect:o.reconnect,sessionId:o.sessionId,sessionMetadata:o.sessionMetadata,scenarioId:o.scenarioId,status:o.status,turns:[...o.turns]},n.autoStopOnComplete!==!1&&i.status==="completed"&&i.isRecording)r?.stop(),r=null,i={...i,isRecording:!1};l()},y=o.subscribe(g);g();let d=()=>{if(r)return r;return r=ge({channelCount:n.capture?.channelCount??t.capture.channelCount,onLevel:n.capture?.onLevel,onAudio:(C)=>{if(n.capture?.onAudio){n.capture.onAudio(C,o.sendAudio);return}o.sendAudio(C)},sampleRateHz:n.capture?.sampleRateHz??t.capture.sampleRateHz}),r},h=()=>{r?.stop(),r=null,i={...i,isRecording:!1},l()},a=async()=>{if(i.isRecording)return;try{i={...i,recordingError:null},l(),await d().start(),i={...i,isRecording:!0},l()}catch(C){throw r=null,i={...i,isRecording:!1,recordingError:C instanceof Error?C.message:String(C)},l(),C}};return{bindHTMX(C){return de(o,C)},callControl:(C)=>o.callControl(C),close:()=>{y(),h(),o.close()},endTurn:()=>o.endTurn(),get error(){return i.error},getServerSnapshot:()=>i,getSnapshot:()=>i,get isConnected(){return i.isConnected},get isRecording(){return i.isRecording},get partial(){return i.partial},get recordingError(){return i.recordingError},get reconnect(){return i.reconnect},sendAudio:(C)=>o.sendAudio(C),simulateDisconnect:()=>o.simulateDisconnect(),get sessionId(){return i.sessionId},get sessionMetadata(){return i.sessionMetadata},get scenarioId(){return i.scenarioId},startRecording:a,get status(){return i.status},stopRecording:h,subscribe:(C)=>{return c.add(C),()=>{c.delete(C)}},toggleRecording:async()=>{if(i.isRecording){h();return}await a()},get turns(){return i.turns},get assistantTexts(){return i.assistantTexts},get assistantAudio(){return i.assistantAudio},get call(){return i.call}}};var tn=()=>({activeSourceCount:0,error:null,isActive:!1,isPlaying:!1,lastInterruptLatencyMs:void 0,lastPlaybackStopLatencyMs:void 0,processedChunkCount:0,queuedChunkCount:0}),cn=()=>{if(typeof window>"u")return typeof AudioContext>"u"?void 0:AudioContext;return window.AudioContext??window.webkitAudioContext},rn=(e,n)=>{let t=n.format;if(t.container!=="raw"||t.encoding!=="pcm_s16le")throw Error(`Unsupported assistant audio format: ${t.container}/${t.encoding}`);let o=n.chunk,r=Math.max(1,t.channels),i=Math.floor(o.byteLength/2),c=Math.max(1,Math.floor(i/r)),l=e.createBuffer(r,c,t.sampleRateHz),g=new DataView(o.buffer,o.byteOffset,o.byteLength);for(let y=0;y<r;y+=1){let d=l.getChannelData(y);for(let h=0;h<c;h+=1){let M=(h*r+y)*2;if(M+1>=o.byteLength){d[h]=0;continue}d[h]=g.getInt16(M,!0)/32768}}return l},j=(e,n={})=>{let t=new Set,o=new Set,r=(n.lookaheadMs??15)/1000,i=tn(),c=null,l=null,g=0,y=Promise.resolve(),d=null,h=null,a=null,M=null,C=()=>{for(let A of t)A()},I=(A)=>{i={...i,...A},C()},w=()=>{if(i.error!==null)I({error:null})},R=()=>{if(M!==null)clearTimeout(M),M=null},u=(A)=>{R(),d=null,I({activeSourceCount:o.size,isPlaying:!1,lastInterruptLatencyMs:A,lastPlaybackStopLatencyMs:i.lastPlaybackStopLatencyMs??A}),a?.(),a=null,h=null},O=(A)=>{if(!A)return 0;return Math.max(0,((A.baseLatency??0)+(A.outputLatency??0))*1000)},U=(A)=>{if(!l)return;let f=1;if(l.gain.setValueAtTime){l.gain.setValueAtTime(f,A?.currentTime??0);return}l.gain.value=f},s=(A)=>{if(!l)return;let f=0;if(l.gain.setValueAtTime){l.gain.setValueAtTime(f,A?.currentTime??0);return}l.gain.value=f},S=()=>{if(d===null||o.size>0)return;u(Date.now()-d)},L=async()=>{if(c)return c;if(n.createAudioContext)c=n.createAudioContext();else{let A=cn();if(!A)throw Error("Assistant audio playback requires AudioContext support.");c=new A}if(c.createGain)l=c.createGain(),l.connect?.(c.destination);return g=c.currentTime,c},X=async(A)=>{let f=await L(),E=rn(f,A),V=f.createBufferSource();V.buffer=E,V.connect(l??f.destination),V.onended=()=>{o.delete(V),V.disconnect?.(),I({activeSourceCount:o.size,isPlaying:o.size>0&&i.isActive}),S()};let Y=Math.max(f.currentTime+r,g);g=Y+E.duration,o.add(V),I({activeSourceCount:o.size,isPlaying:!0}),V.start(Y)},_=(A)=>{for(let f of[...o])f.stop?.();if(g=c?c.currentTime:0,A?.forceClear){for(let f of o)f.disconnect?.();o.clear(),S()}},W=async()=>{if(!i.isActive)return;let A=e.assistantAudio.slice(i.processedChunkCount);if(A.length===0)return;try{w();for(let f of A)await X(f);I({processedChunkCount:e.assistantAudio.length,queuedChunkCount:i.queuedChunkCount+A.length})}catch(f){I({error:f instanceof Error?f.message:String(f)})}},P=()=>{return y=y.then(()=>W(),()=>W()),y},$=e.subscribe(()=>{if(n.autoStart&&!i.isActive&&e.assistantAudio.length>0){N.start();return}if(i.isActive)P()}),N={close:async()=>{if($(),_({forceClear:!0}),R(),a?.(),a=null,h=null,d=null,c&&c.state!=="closed")await c.close();c=null,l?.disconnect?.(),l=null,g=0,I({activeSourceCount:0,isActive:!1,isPlaying:!1})},get activeSourceCount(){return i.activeSourceCount},get error(){return i.error},getSnapshot:()=>i,get isActive(){return i.isActive},get isPlaying(){return i.isPlaying},interrupt:async()=>{let A=Date.now(),f=await L();d=A,s(f);let E=Date.now()-A+O(f);if(I({isActive:!1,isPlaying:o.size>0,lastPlaybackStopLatencyMs:E}),o.size===0){u(E);return}if(!h)h=new Promise((V)=>{a=V});R(),M=setTimeout(()=>{for(let V of o)V.disconnect?.();o.clear(),u(Date.now()-A)},250),_(),await h},get lastInterruptLatencyMs(){return i.lastInterruptLatencyMs},get lastPlaybackStopLatencyMs(){return i.lastPlaybackStopLatencyMs},pause:async()=>{if(!c){I({activeSourceCount:0,isActive:!1,isPlaying:!1});return}await c.suspend(),I({activeSourceCount:o.size,isActive:!1,isPlaying:!1})},get processedChunkCount(){return i.processedChunkCount},get queuedChunkCount(){return i.queuedChunkCount},start:async()=>{try{w();let A=await L();if(U(A),A.state==="suspended")await A.resume();I({activeSourceCount:o.size,isActive:!0,isPlaying:A.state==="running"}),await P()}catch(A){throw I({error:A instanceof Error?A.message:String(A),isActive:!1,isPlaying:!1}),A}},subscribe:(A)=>{return t.add(A),()=>{t.delete(A)}}};return N};var sn=()=>`barge-in:${Date.now()}:${crypto.randomUUID?.()??Math.random().toString(36).slice(2)}`,ln=(e,n)=>{let t=e.filter((c)=>c.status==="stopped"),o=t.map((c)=>c.latencyMs).filter((c)=>typeof c==="number"),r=t.filter((c)=>typeof c.latencyMs==="number"&&c.latencyMs>n).length,i=t.length-r;return{averageLatencyMs:o.length>0?Math.round(o.reduce((c,l)=>c+l,0)/o.length):void 0,events:[...e],failed:r,lastEvent:e.at(-1),passed:i,status:e.length===0?"empty":r>0?"fail":t.length===0?"warn":"pass",thresholdMs:n,total:t.length}},ue=(e={})=>{let n=new Set,t=e.thresholdMs??250,o=e.fetch??globalThis.fetch,r=[],i=()=>{for(let g of n)g()},c=(g)=>{if(!e.path||typeof o!=="function")return;o(e.path,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},method:"POST"}).catch(()=>{})},l=(g,y)=>{let d={at:Date.now(),id:sn(),latencyMs:y.latencyMs,playbackStopLatencyMs:y.playbackStopLatencyMs,reason:y.reason,sessionId:y.sessionId,status:g,thresholdMs:t};return r.push(d),c(d),i(),d};return{getSnapshot:()=>ln(r,t),recordRequested:(g)=>l("requested",g),recordSkipped:(g)=>l("skipped",g),recordStopped:(g)=>l("stopped",g),subscribe:(g)=>{return n.add(g),()=>{n.delete(g)}}}};var an=0.08,dn=(e,n={})=>(n.enabled??!0)&&e>=(n.interruptThreshold??an),oe=(e,n,t={})=>{let o=e.partial,r=(c)=>{if(!n.isPlaying||t.enabled===!1){t.monitor?.recordSkipped({reason:c,sessionId:e.sessionId});return}t.monitor?.recordRequested({reason:c,sessionId:e.sessionId}),n.interrupt().then(()=>{t.monitor?.recordStopped({latencyMs:n.lastInterruptLatencyMs,playbackStopLatencyMs:n.lastPlaybackStopLatencyMs,reason:c,sessionId:e.sessionId})})},i=e.subscribe(()=>{if(t.interruptOnPartial===!1){o=e.partial;return}if(!o&&e.partial)r("partial-transcript");o=e.partial});return{close:()=>{i()},handleLevel:(c)=>{if(dn(c,t))r("input-level")},sendAudio:(c)=>{r("manual-audio"),e.sendAudio(c)}}};var se=48,gn=320,An=88,hn="Guided test",yn="General recording",Cn="Pick a scenario to begin the demo.",fn="I can walk you through a short guided voice test.",Tn="I can capture one freeform recording and confirm that it landed.",Sn="Choose a scenario to begin. Guided test asks follow-up prompts. General recording just captures what you say.",Mn="Click Start general recording to capture one freeform answer.",_e="Speak freely. When you pause, the recording will be captured.",re="Recording saved. Start again if you want another capture.",Ee="Guided test complete. Review the saved summary below.",Pe="All prompts are covered. You can stop the microphone or keep speaking for extra detail.",In="Ready. Start guided test or general recording to begin.",Vn="Live. Answer the prompt, then click Stop microphone when finished.",Re=["Start with a quick introduction about who you are.","Now describe what you are trying to do or test.","Finish with any detail that feels blocked, risky, or unclear."],De=(e,n,t)=>Math.min(t,Math.max(n,e)),q=(e)=>e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll(\'"\',""").replaceAll("\'","'"),te=(e,n)=>{let t=e[n];if(typeof t==="string"&&t.trim())return t;return null},ie=(e)=>{if(typeof e==="string"&&e.trim())return e;if(e instanceof Error&&e.message.trim())return e.message;if(e&&typeof e==="object"){let n=e,t=te(n,"message")??te(n,"reason")??te(n,"description");if(t)return t;if("error"in n)return ie(n.error);if("cause"in n)return ie(n.cause);try{return JSON.stringify(e)}catch{}}return"Unexpected error"},wn=(e)=>{let n=[e.status];if(e.attempts>0||e.maxAttempts>0)n.push(`${e.attempts}/${e.maxAttempts} attempts`);if(e.nextAttemptAt){let t=Math.max(0,e.nextAttemptAt-Date.now());n.push(`retry in ${Math.ceil(t/100)/10}s`)}return n.join(" \xB7 ")},v=(e=se)=>Array.from({length:e},()=>0),be=(e,n,t=se)=>{let o=e.slice(-(t-1));o.push(De(n,0,1));while(o.length<t)o.unshift(0);return o},Ln=(e,n=gn,t=An)=>{let o=e.length>1?e:v(se),r=n/(o.length-1),i=t/2,c=t*0.34;if(Math.max(...o,0)<=0.015)return`M 0 ${i} L ${n} ${i}`;let g=o.map((d,h)=>{let a=h*0.76,M=Math.sin(a)*0.78+Math.sin(a*0.41)*0.22,C=d*c,I=r*h,w=De(i+M*C,8,t-8);return{x:I,y:w}});if(g.length===0)return`M 0 ${i} L ${n} ${i}`;let y=`M ${g[0]?.x??0} ${g[0]?.y??i}`;for(let d=1;d<g.length;d+=1){let h=g[d-1],a=g[d];if(!h||!a)continue;let M=(h.x+a.x)/2;y+=` Q ${M} ${h.y} ${a.x} ${a.y}`}return y},un=(e)=>{if(!e)return Re;try{let n=JSON.parse(e);if(Array.isArray(n)){let t=n.filter((o)=>typeof o==="string").map((o)=>o.trim()).filter(Boolean);if(t.length>0)return t}}catch{}return Re},ce=(e)=>{if(!e)return;let n=Number(e);return Number.isFinite(n)?n:void 0},Rn=(e,n,t)=>{if(!n)return null;let o=document.querySelector(n);return o instanceof t?o:null},x=(e,n,t,o)=>{let r=n?document.querySelector(n):null;if(r instanceof t)return r;let i=e.querySelector(`#${o}`);if(i instanceof t)return i;throw Error(`Voice HTMX bootstrap could not find the required element "${o}".`)},bn=(e)=>{if(!e.mode)return Cn;if(!e.hasStarted)return e.mode==="guided"?fn:Tn;if(e.status==="completed")return e.mode==="guided"?Ee:re;if(e.mode==="general")return _e;return e.guidedPrompts[e.turnCount]??Pe},_n=(e)=>{if(!e.mode)return Sn;if(e.status==="completed")return e.mode==="guided"?Ee:re;if(!e.hasStarted)return e.mode==="guided"?`Click Start guided test to begin. First prompt: ${e.guidedPrompts[0]??"Answer the first prompt."}`:Mn;if(e.mode==="general")return e.turnCount===0?_e:re;return e.guidedPrompts[e.turnCount]??Pe},En=(e)=>{let n=e.dataset.voiceGuidedPath,t=e.dataset.voiceGeneralPath;if(!n||!t)throw Error("Voice HTMX bootstrap requires data-voice-guided-path and data-voice-general-path.");let o=un(e.dataset.voiceGuidedPrompts),r=e.dataset.voiceGuidedLabel??hn,i=e.dataset.voiceGeneralLabel??yn,c=e.dataset.voiceReconnectReportPath,l=e.dataset.voiceBargeInPath,g=l?ue({path:l,thresholdMs:ce(e.dataset.voiceBargeInThresholdMs)}):null,y=ce(e.dataset.voiceBargeInRecentWindowMs)??4000,d=ce(e.dataset.voiceBargeInSpeechThreshold)??0.04,h=x(document,e.dataset.voiceSync,HTMLElement,"voice-htmx-sync"),a=x(e,e.dataset.voiceConnection,HTMLElement,"metric-connection"),M=x(e,e.dataset.voiceError,HTMLElement,"status-error"),C=x(e,e.dataset.voiceMicrophone,HTMLElement,"status-mic"),I=x(e,e.dataset.voicePrompt,HTMLElement,"status-prompt"),w=Rn(e,e.dataset.voiceReconnect,HTMLElement),R=x(e,e.dataset.voiceChat,HTMLElement,"chat-list"),u=x(e,e.dataset.voiceStartGuided,HTMLButtonElement,"start-guided"),O=x(e,e.dataset.voiceStartGeneral,HTMLButtonElement,"start-general"),U=x(e,e.dataset.voiceStop,HTMLButtonElement,"stop-mic"),s=x(e,e.dataset.voiceMonitor,HTMLElement,"voice-monitor"),S=x(e,e.dataset.voiceMonitorCopy,HTMLElement,"voice-monitor-copy"),L=x(e,e.dataset.voiceWaveGlow,SVGPathElement,"voice-wave-glow"),X=x(e,e.dataset.voiceWavePath,SVGPathElement,"voice-wave-path"),_=null,W={general:!1,guided:!1},P=!1,$=null,N=v(),A=null,f=null,E=m(n,{capture:{onAudio:(T,G)=>{if(A){A.sendAudio(T);return}G(T)},onLevel:(T)=>{A?.handleLevel(T),N=be(N,T),F()}},connection:{reconnectReportPath:c},preset:"guided-intake"}),V=m(t,{capture:{onAudio:(T,G)=>{if(f){f.sendAudio(T);return}G(T)},onLevel:(T)=>{f?.handleLevel(T),N=be(N,T),F()}},connection:{reconnectReportPath:c},preset:"dictation"}),Y=E.bindHTMX({element:h}),xe=V.bindHTMX({element:h}),J=j(E),Q=j(V);A=oe(E,J,{interruptThreshold:d,monitor:g??void 0}),f=oe(V,Q,{interruptThreshold:d,monitor:g??void 0});let z=()=>_==="general"?V:E,Dn=()=>_==="general"?Q:J,F=()=>{let T=Ln(N);L.setAttribute("d",T),X.setAttribute("d",T),S.innerHTML=`<span class="voice-live-dot"></span>${P?"Microphone live":"Microphone idle"}`,S.classList.toggle("is-live",P),s.classList.toggle("is-live",P)},k=()=>{let T=z(),G=(_?W[_]:!1)||T.turns.length>0,ae=T.status;if(a.textContent=T.isConnected?"Connected":"Waiting",M.textContent=$||T.error||"None",w)w.textContent=wn(T.reconnect);C.textContent=P?Vn:In,I.textContent=_n({guidedPrompts:o,hasStarted:G,mode:_,status:ae,turnCount:T.turns.length}),u.hidden=P,O.hidden=P,U.hidden=!P,R.innerHTML=`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${q(_==="general"?i:_==="guided"?r:"Voice demo")}</div>\n <p class="voice-turn-text">${q(bn({generalLabel:i,guidedLabel:r,guidedPrompts:o,hasStarted:G,mode:_,status:ae,turnCount:T.turns.length}))}</p>\n</article>${T.turns.map((p)=>`<div class="voice-chat-stack">\n <article class="voice-chat-message user">\n <div class="voice-chat-role">You</div>\n <p class="voice-turn-text">${q(p.text)}</p>\n </article>\n ${p.assistantText?`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${q(_==="general"?i:_==="guided"?r:"Guide")}</div>\n <p class="voice-turn-text">${q(p.assistantText)}</p>\n </article>`:""}\n</div>`).join("")}${T.partial?`<article class="voice-chat-message user pending">\n <div class="voice-chat-role">Speaking</div>\n <p class="voice-turn-text">${q(T.partial)}</p>\n</article>`:""}`,F()},Oe=()=>{z().stopRecording(),P=!1,$=null,N=v(),k()},le=async(T)=>{_=T,W={...W,[T]:!0};try{await z().startRecording(),$=null,P=!0,k()}catch(G){z().stopRecording(),P=!1,N=v(),$=ie(G),k()}};E.subscribe(()=>{if(E.assistantAudio.length>0)J.start().catch(()=>{});k()}),V.subscribe(()=>{if(V.assistantAudio.length>0)Q.start().catch(()=>{});k()}),u.addEventListener("click",()=>{le("guided")}),O.addEventListener("click",()=>{le("general")}),U.addEventListener("click",()=>{Oe()}),e.addEventListener("absolute-voice-simulate-disconnect",()=>{z().simulateDisconnect()}),window.addEventListener("beforeunload",()=>{E.stopRecording(),V.stopRecording(),A?.close(),f?.close(),J.close(),Q.close(),Y(),xe(),E.close(),V.close()}),k()},Pn=()=>{if(typeof window>"u"||typeof document>"u")return;let e=Array.from(document.querySelectorAll("[data-voice-htmx]"));for(let n of e)if(n instanceof HTMLElement)En(n)};Pn();export{Pn as initVoiceHTMX};\n';
|
|
5189
|
+
var HTMX_BOOTSTRAP_BUNDLE = 'var We=(e)=>{if(typeof e!=="string")return e;return document.querySelector(e)},$e=(e,n,c,o)=>{let s=n??e.getAttribute("hx-get")??"";if(!s)return"";let t=new URL(s,window.location.origin);if(o)t.searchParams.set(c,o);else t.searchParams.delete(c);return`${t.pathname}${t.search}${t.hash}`},fe=(e,n)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let c=We(n.element);if(!c)return()=>{};let o=n.eventName??"voice-refresh",s=n.sessionQueryParam??"sessionId",t=()=>{let r=window,d=$e(c,n.route,s,e.sessionId);if(d)c.setAttribute("hx-get",d);r.htmx?.process?.(c),r.htmx?.trigger?.(c,o)},i=e.subscribe(t);return t(),()=>{i()}};var ke=(e)=>Math.max(-1,Math.min(1,e)),qe=(e)=>{let n=new Int16Array(e.length);for(let c=0;c<e.length;c+=1){let o=ke(e[c]??0);n[c]=o<0?o*32768:o*32767}return new Uint8Array(n.buffer)},Xe=(e)=>{let n=e instanceof Uint8Array?e:new Uint8Array(e);if(n.byteLength<2)return 0;let c=new Int16Array(n.buffer,n.byteOffset,Math.floor(n.byteLength/2));if(c.length===0)return 0;let o=0;for(let s of c){let t=s/32768;o+=t*t}return Math.min(1,Math.max(0,Math.sqrt(o/c.length)*5.5))},ze=(e,n,c)=>{if(n===c)return e;let o=n/c,s=Math.round(e.length/o),t=new Float32Array(s),i=0,r=0;while(i<t.length){let d=Math.round((i+1)*o),f=0,a=0;for(let A=r;A<d&&A<e.length;A+=1)f+=e[A]??0,a+=1;t[i]=a>0?f/a:0,i+=1,r=d}return t},Ae=(e)=>{let n=null,c=null,o=null,s=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let r=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!r)throw Error("Browser microphone capture requires AudioContext support.");s=await navigator.mediaDevices.getUserMedia({audio:{channelCount:e.channelCount??1}}),n=new r,c=n.createMediaStreamSource(s),o=n.createScriptProcessor(4096,1,1),o.onaudioprocess=(d)=>{let f=d.inputBuffer.getChannelData(0),a=ze(f,n?.sampleRate??48000,e.sampleRateHz??16000),A=qe(a);e.onLevel?.(Xe(A)),e.onAudio(A)},c.connect(o),o.connect(n.destination)},stop:()=>{o?.disconnect(),c?.disconnect(),s?.getTracks().forEach((r)=>r.stop()),n?.close(),e.onLevel?.(0),n=null,s=null,o=null,c=null}}};var ne=(e)=>{if(typeof e==="string"&&e.trim())return e;if(e instanceof Error&&e.message.trim())return e.message;if(e&&typeof e==="object"){let n=e;for(let c of["message","reason","description"]){let o=n[c];if(typeof o==="string"&&o.trim())return o}if("error"in n)return ne(n.error);if("cause"in n)return ne(n.cause);try{return JSON.stringify(e)}catch{}}return"Unexpected error"},he=(e)=>{switch(e.type){case"audio":return{chunk:Uint8Array.from(atob(e.chunkBase64),(n)=>n.charCodeAt(0)),format:e.format,receivedAt:e.receivedAt,turnId:e.turnId,type:"audio"};case"assistant":return{text:e.text,type:"assistant"};case"complete":return{sessionId:e.sessionId,type:"complete"};case"connection":return{reconnect:e.reconnect,type:"connection"};case"call_lifecycle":return{event:e.event,sessionId:e.sessionId,type:"call_lifecycle"};case"error":return{message:ne(e.message),type:"error"};case"final":return{transcript:e.transcript,type:"final"};case"partial":return{transcript:e.transcript,type:"partial"};case"replay":return{assistantTexts:e.assistantTexts,call:e.call,partial:e.partial,scenarioId:e.scenarioId,sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,status:e.status,turns:e.turns,type:"replay"};case"session":return{sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,scenarioId:e.scenarioId,status:e.status,type:"session"};case"turn":return{turn:e.turn,type:"turn"};default:return null}};var{mkdir:pn,writeFile:ec}=(()=>({}));function H(e){if(typeof e!=="string")throw TypeError("Path must be a string. Received "+JSON.stringify(e))}function Ce(e,n){var c="",o=0,s=-1,t=0,i;for(var r=0;r<=e.length;++r){if(r<e.length)i=e.charCodeAt(r);else if(i===47)break;else i=47;if(i===47){if(s===r-1||t===1);else if(s!==r-1&&t===2){if(c.length<2||o!==2||c.charCodeAt(c.length-1)!==46||c.charCodeAt(c.length-2)!==46){if(c.length>2){var d=c.lastIndexOf("/");if(d!==c.length-1){if(d===-1)c="",o=0;else c=c.slice(0,d),o=c.length-1-c.lastIndexOf("/");s=r,t=0;continue}}else if(c.length===2||c.length===1){c="",o=0,s=r,t=0;continue}}if(n){if(c.length>0)c+="/..";else c="..";o=2}}else{if(c.length>0)c+="/"+e.slice(s+1,r);else c=e.slice(s+1,r);o=r-s-1}s=r,t=0}else if(i===46&&t!==-1)++t;else t=-1}return c}function Ye(e,n){var c=n.dir||n.root,o=n.base||(n.name||"")+(n.ext||"");if(!c)return o;if(c===n.root)return c+o;return c+e+o}function ce(){var e="",n=!1,c;for(var o=arguments.length-1;o>=-1&&!n;o--){var s;if(o>=0)s=arguments[o];else{if(c===void 0)c=process.cwd();s=c}if(H(s),s.length===0)continue;e=s+"/"+e,n=s.charCodeAt(0)===47}if(e=Ce(e,!n),n)if(e.length>0)return"/"+e;else return"/";else if(e.length>0)return e;else return"."}function ye(e){if(H(e),e.length===0)return".";var n=e.charCodeAt(0)===47,c=e.charCodeAt(e.length-1)===47;if(e=Ce(e,!n),e.length===0&&!n)e=".";if(e.length>0&&c)e+="/";if(n)return"/"+e;return e}function Je(e){return H(e),e.length>0&&e.charCodeAt(0)===47}function Te(){if(arguments.length===0)return".";var e;for(var n=0;n<arguments.length;++n){var c=arguments[n];if(H(c),c.length>0)if(e===void 0)e=c;else e+="/"+c}if(e===void 0)return".";return ye(e)}function Qe(e,n){if(H(e),H(n),e===n)return"";if(e=ce(e),n=ce(n),e===n)return"";var c=1;for(;c<e.length;++c)if(e.charCodeAt(c)!==47)break;var o=e.length,s=o-c,t=1;for(;t<n.length;++t)if(n.charCodeAt(t)!==47)break;var i=n.length,r=i-t,d=s<r?s:r,f=-1,a=0;for(;a<=d;++a){if(a===d){if(r>d){if(n.charCodeAt(t+a)===47)return n.slice(t+a+1);else if(a===0)return n.slice(t+a)}else if(s>d){if(e.charCodeAt(c+a)===47)f=a;else if(a===0)f=0}break}var A=e.charCodeAt(c+a),g=n.charCodeAt(t+a);if(A!==g)break;else if(A===47)f=a}var y="";for(a=c+f+1;a<=o;++a)if(a===o||e.charCodeAt(a)===47)if(y.length===0)y+="..";else y+="/..";if(y.length>0)return y+n.slice(t+f);else{if(t+=f,n.charCodeAt(t)===47)++t;return n.slice(t)}}function Ze(e){return e}function Ke(e){if(H(e),e.length===0)return".";var n=e.charCodeAt(0),c=n===47,o=-1,s=!0;for(var t=e.length-1;t>=1;--t)if(n=e.charCodeAt(t),n===47){if(!s){o=t;break}}else s=!1;if(o===-1)return c?"/":".";if(c&&o===1)return"//";return e.slice(0,o)}function me(e,n){if(n!==void 0&&typeof n!=="string")throw TypeError(\'"ext" argument must be a string\');H(e);var c=0,o=-1,s=!0,t;if(n!==void 0&&n.length>0&&n.length<=e.length){if(n.length===e.length&&n===e)return"";var i=n.length-1,r=-1;for(t=e.length-1;t>=0;--t){var d=e.charCodeAt(t);if(d===47){if(!s){c=t+1;break}}else{if(r===-1)s=!1,r=t+1;if(i>=0)if(d===n.charCodeAt(i)){if(--i===-1)o=t}else i=-1,o=r}}if(c===o)o=r;else if(o===-1)o=e.length;return e.slice(c,o)}else{for(t=e.length-1;t>=0;--t)if(e.charCodeAt(t)===47){if(!s){c=t+1;break}}else if(o===-1)s=!1,o=t+1;if(o===-1)return"";return e.slice(c,o)}}function je(e){H(e);var n=-1,c=0,o=-1,s=!0,t=0;for(var i=e.length-1;i>=0;--i){var r=e.charCodeAt(i);if(r===47){if(!s){c=i+1;break}continue}if(o===-1)s=!1,o=i+1;if(r===46){if(n===-1)n=i;else if(t!==1)t=1}else if(n!==-1)t=-1}if(n===-1||o===-1||t===0||t===1&&n===o-1&&n===c+1)return"";return e.slice(n,o)}function Fe(e){if(e===null||typeof e!=="object")throw TypeError(\'The "pathObject" argument must be of type Object. Received type \'+typeof e);return Ye("/",e)}function ve(e){H(e);var n={root:"",dir:"",base:"",ext:"",name:""};if(e.length===0)return n;var c=e.charCodeAt(0),o=c===47,s;if(o)n.root="/",s=1;else s=0;var t=-1,i=0,r=-1,d=!0,f=e.length-1,a=0;for(;f>=s;--f){if(c=e.charCodeAt(f),c===47){if(!d){i=f+1;break}continue}if(r===-1)d=!1,r=f+1;if(c===46){if(t===-1)t=f;else if(a!==1)a=1}else if(t!==-1)a=-1}if(t===-1||r===-1||a===0||a===1&&t===r-1&&t===i+1){if(r!==-1)if(i===0&&o)n.base=n.name=e.slice(1,r);else n.base=n.name=e.slice(i,r)}else{if(i===0&&o)n.name=e.slice(1,t),n.base=e.slice(1,r);else n.name=e.slice(i,t),n.base=e.slice(i,r);n.ext=e.slice(t,r)}if(i>0)n.dir=e.slice(0,i-1);else if(o)n.dir="/";return n}var pe="/",en=":",jn=((e)=>(e.posix=e,e))({resolve:ce,normalize:ye,isAbsolute:Je,join:Te,relative:Qe,_makeLong:Ze,dirname:Ke,basename:me,extname:je,format:Fe,parse:ve,sep:pe,delimiter:en,win32:null,posix:null});var G=(e,n,c,o)=>{e.push({code:c,message:o,severity:n})};var nn=(e)=>e.length===0?void 0:e.reduce((n,c)=>n+c,0)/e.length,m=(e)=>e.length===0?void 0:Math.max(...e);var u=(e,n)=>{let c=e[n];return typeof c==="number"&&Number.isFinite(c)?c:void 0},K=(e,n)=>{let c=e[n];return typeof c==="boolean"?c:void 0},D=(e,n)=>{let c=e[n];return typeof c==="string"?c:void 0},oe=(e)=>String(e.id??D(e,"ssrc")??u(e,"ssrc")??D(e,"trackIdentifier")??D(e,"mid")??"unknown"),Se=(e)=>e===void 0?void 0:e*1000;var cn=(e)=>{let n={};for(let[c,o]of Object.entries(e))if(o===null||typeof o==="boolean"||typeof o==="number"||typeof o==="string")n[c]=o;return n};var Me=(e={})=>{let n=e.stats??[],c=[],o=n.filter((l)=>l.type==="inbound-rtp"&&D(l,"kind")!=="video"),s=n.filter((l)=>l.type==="outbound-rtp"&&D(l,"kind")!=="video"),t=n.filter((l)=>l.type==="candidate-pair"),i=n.filter((l)=>(l.type==="track"||l.type==="media-source")&&D(l,"kind")==="audio"),r=t.filter((l)=>K(l,"selected")===!0||K(l,"nominated")===!0||D(l,"state")==="succeeded").length,d=i.filter((l)=>D(l,"readyState")!=="ended"&&D(l,"trackState")!=="ended"&&K(l,"ended")!==!0).length,f=i.filter((l)=>D(l,"readyState")==="ended"||D(l,"trackState")==="ended"||K(l,"ended")===!0).length,a=o.reduce((l,M)=>l+(u(M,"packetsReceived")??0),0),A=s.reduce((l,M)=>l+(u(M,"packetsSent")??0),0),g=[...o,...s].reduce((l,M)=>l+Math.max(0,u(M,"packetsLost")??0),0),y=a+g,C=y===0?0:g/y,I=o.reduce((l,M)=>l+(u(M,"bytesReceived")??0),0),L=s.reduce((l,M)=>l+(u(M,"bytesSent")??0),0),R=m(t.map((l)=>Se(u(l,"currentRoundTripTime")??u(l,"roundTripTime"))).filter((l)=>l!==void 0)),w=m([...o,...s].map((l)=>Se(u(l,"jitter"))).filter((l)=>l!==void 0)),x=m(o.map((l)=>{let M=u(l,"jitterBufferDelay"),b=u(l,"jitterBufferEmittedCount");return M!==void 0&&b!==void 0&&b>0?M/b*1000:void 0}).filter((l)=>l!==void 0)),U=i.map((l)=>u(l,"audioLevel")).filter((l)=>l!==void 0);if(e.requireConnectedCandidatePair&&t.length>0&&r===0)G(c,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(e.requireLiveAudioTrack&&d===0)G(c,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(e.maxPacketLossRatio!==void 0&&C>e.maxPacketLossRatio)G(c,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(C)} above ${String(e.maxPacketLossRatio)}.`);if(e.maxRoundTripTimeMs!==void 0&&R!==void 0&&R>e.maxRoundTripTimeMs)G(c,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(R)}ms above ${String(e.maxRoundTripTimeMs)}ms.`);if(e.maxJitterMs!==void 0&&w!==void 0&&w>e.maxJitterMs)G(c,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(w)}ms above ${String(e.maxJitterMs)}ms.`);return{activeCandidatePairs:r,audioLevelAverage:nn(U),bytesReceived:I,bytesSent:L,checkedAt:Date.now(),endedAudioTracks:f,inboundPackets:a,issues:c,jitterBufferDelayMs:x,jitterMs:w,liveAudioTracks:d,outboundPackets:A,packetLossRatio:C,packetsLost:g,roundTripTimeMs:R,status:c.some((l)=>l.severity==="error")?"fail":c.length>0?"warn":"pass",totalStats:n.length}},Ie=async(e)=>{return[...(await e.peerConnection.getStats(e.selector??null)).values()].map(cn)};var Ve=(e={})=>{let n=e.stats??[],c=e.previousStats??[],o=[],s=new Map(c.map((g)=>[oe(g),g])),i=n.filter((g)=>(g.type==="inbound-rtp"||g.type==="outbound-rtp")&&D(g,"kind")!=="video"&&D(g,"mediaType")!=="video").map((g)=>{let y=g.type==="outbound-rtp"?"outbound":"inbound",C=y==="outbound"?"packetsSent":"packetsReceived",I=y==="outbound"?"bytesSent":"bytesReceived",L=s.get(oe(g)),R=u(g,C),w=L?u(L,C):void 0,x=u(g,I),U=L?u(L,I):void 0,l=g.timestamp!==void 0&&L?.timestamp!==void 0?g.timestamp-L.timestamp:void 0;return{bytesDelta:x!==void 0&&U!==void 0?x-U:void 0,currentPackets:R,direction:y,id:oe(g),packetDelta:R!==void 0&&w!==void 0?R-w:void 0,previousPackets:w,timeDeltaMs:l}}),r=i.filter((g)=>g.direction==="inbound"),d=i.filter((g)=>g.direction==="outbound"),f=m(i.map((g)=>g.timeDeltaMs).filter((g)=>g!==void 0)),a=r.filter((g)=>e.maxInboundPacketStallMs!==void 0&&g.timeDeltaMs!==void 0&&g.timeDeltaMs>=e.maxInboundPacketStallMs&&g.packetDelta!==void 0&&g.packetDelta<=0).length,A=d.filter((g)=>e.maxOutboundPacketStallMs!==void 0&&g.timeDeltaMs!==void 0&&g.timeDeltaMs>=e.maxOutboundPacketStallMs&&g.packetDelta!==void 0&&g.packetDelta<=0).length;if(e.requireInboundAudio&&r.length===0)G(o,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(e.requireOutboundAudio&&d.length===0)G(o,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(e.maxGapMs!==void 0&&f!==void 0&&f>e.maxGapMs)G(o,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(f)}ms above ${String(e.maxGapMs)}ms.`);if(a>0)G(o,"error","media.webrtc_inbound_stalled",`${String(a)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(A>0)G(o,"error","media.webrtc_outbound_stalled",`${String(A)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:r.length,issues:o,maxObservedGapMs:f,outboundAudioStreams:d.length,stalledInboundStreams:a,stalledOutboundStreams:A,status:o.some((g)=>g.severity==="error")?"fail":o.length>0?"warn":"pass",streams:i,totalStats:n.length}};var on="/api/voice/browser-media",tn=5000,sn=async(e)=>e.peerConnection??await e.getPeerConnection?.()??null,rn=async(e,n)=>{let c=n.fetch??globalThis.fetch;if(!c)return;await c(n.path??on,{body:JSON.stringify(e),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},Le=(e)=>{let n=null,c=[],o=async()=>{let i=await sn(e);if(!i)return;let r=await Ie({peerConnection:i}),d=Me({...e,stats:r}),f=e.continuity===!1?void 0:Ve({...e.continuity,previousStats:c,stats:r}),a={at:Date.now(),continuity:f,report:d,scenarioId:e.getScenarioId?.()??null,sessionId:e.getSessionId?.()??null};return c=r,e.onReport?.(a),await rn(a,e),a},s=()=>{o().catch((i)=>{e.onError?.(i)})},t=()=>{if(n)clearInterval(n),n=null};return{close:t,reportOnce:o,start:()=>{if(n)return;s(),n=setInterval(s,e.intervalMs??tn)},stop:t}};var W=()=>{},ln=()=>W,an={callControl:W,close:W,endTurn:W,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",send:W,sendAudio:W,simulateDisconnect:W,start:()=>{},subscribe:ln},dn=()=>crypto.randomUUID(),gn=(e,n,c)=>{let{hostname:o,port:s,protocol:t}=window.location,i=t==="https:"?"wss:":"ws:",r=s?`:${s}`:"",d=new URL(`${i}//${o}${r}${e}`);if(d.searchParams.set("sessionId",n),c)d.searchParams.set("scenarioId",c);return d.toString()},fn=(e)=>{if(!e||typeof e!=="object"||!("type"in e))return!1;switch(e.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},An=(e)=>{if(typeof e.data!=="string")return null;try{let n=JSON.parse(e.data);return fn(n)?n:null}catch{return null}},be=(e,n={})=>{if(typeof window>"u")return an;let c=new Set,o=n.reconnect!==!1,s=n.maxReconnectAttempts??10,t=n.pingInterval??30000,i={isConnected:!1,pendingMessages:[],scenarioId:n.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:n.sessionId??dn(),ws:null},r=(l)=>{c.forEach((M)=>M(l))},d=()=>{if(i.pingInterval)clearInterval(i.pingInterval),i.pingInterval=null;if(i.reconnectTimeout)clearTimeout(i.reconnectTimeout),i.reconnectTimeout=null},f=()=>{if(i.ws?.readyState!==1)return;while(i.pendingMessages.length>0){let l=i.pendingMessages.shift();if(l!==void 0)i.ws.send(l)}},a=()=>{let l=Date.now()+500;i.reconnectAttempts+=1,r({reconnect:{attempts:i.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:s,nextAttemptAt:l,status:"reconnecting"},type:"connection"}),i.reconnectTimeout=setTimeout(()=>{if(i.reconnectAttempts>s){r({reconnect:{attempts:i.reconnectAttempts,maxAttempts:s,status:"exhausted"},type:"connection"});return}A()},500)},A=()=>{let l=new WebSocket(gn(e,i.sessionId,i.scenarioId));l.binaryType="arraybuffer",l.onopen=()=>{let M=i.reconnectAttempts>0;if(i.isConnected=!0,f(),M)r({reconnect:{attempts:i.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:s,status:"resumed"},type:"connection"}),i.reconnectAttempts=0;c.forEach((b)=>b({scenarioId:i.scenarioId??void 0,sessionId:i.sessionId,status:"active",type:"session"})),i.pingInterval=setInterval(()=>{if(l.readyState===1)l.send(JSON.stringify({type:"ping"}))},t)},l.onmessage=(M)=>{let b=An(M);if(!b)return;if(b.type==="session")i.sessionId=b.sessionId,i.scenarioId=b.scenarioId??i.scenarioId;c.forEach((z)=>z(b))},l.onclose=(M)=>{if(i.isConnected=!1,d(),o&&M.code!==1000&&i.reconnectAttempts<s)a();else if(o&&M.code!==1000)r({reconnect:{attempts:i.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:s,status:"exhausted"},type:"connection"})},i.ws=l},g=(l)=>{if(i.ws?.readyState===1){i.ws.send(l);return}i.pendingMessages.push(l)},y=(l)=>{g(JSON.stringify(l))},C=(l={})=>{if(l.sessionId)i.sessionId=l.sessionId;if(l.scenarioId)i.scenarioId=l.scenarioId;y({type:"start",sessionId:i.sessionId,scenarioId:i.scenarioId??void 0})},I=(l)=>{g(l)},L=()=>{y({type:"end_turn"})},R=(l)=>{y({...l,type:"call_control"})},w=()=>{if(d(),i.ws)i.ws.close(1000),i.ws=null;i.isConnected=!1,c.clear()},x=()=>{if(i.ws?.readyState===1)i.ws.close(4000,"absolutejs-voice-reconnect-proof")},U=(l)=>{return c.add(l),()=>{c.delete(l)}};return A(),{callControl:R,close:w,endTurn:L,getReadyState:()=>i.ws?.readyState??3,getScenarioId:()=>i.scenarioId??"",getSessionId:()=>i.sessionId,send:y,sendAudio:I,simulateDisconnect:x,start:C,subscribe:U}};var hn=()=>({attempts:0,maxAttempts:0,status:"idle"}),Cn=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:"",reconnect:hn(),sessionId:null,status:"idle",turns:[]}),we=()=>{let e=Cn(),n=new Set,c=()=>{n.forEach((s)=>s())};return{dispatch:(s)=>{switch(s.type){case"audio":e={...e,assistantAudio:[...e.assistantAudio,{chunk:s.chunk,format:s.format,receivedAt:s.receivedAt,turnId:s.turnId}]};break;case"assistant":e={...e,assistantTexts:[...e.assistantTexts,s.text]};break;case"complete":e={...e,sessionId:s.sessionId,status:"completed"};break;case"call_lifecycle":e={...e,call:{...e.call,disposition:s.event.type==="end"?s.event.disposition:e.call?.disposition,endedAt:s.event.type==="end"?s.event.at:e.call?.endedAt,events:[...e.call?.events??[],s.event],lastEventAt:s.event.at,startedAt:e.call?.startedAt??s.event.at},sessionId:s.sessionId};break;case"connected":e={...e,isConnected:!0,reconnect:e.reconnect.status==="reconnecting"?{...e.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:e.reconnect};break;case"connection":e={...e,reconnect:s.reconnect};break;case"disconnected":e={...e,isConnected:!1};break;case"error":e={...e,error:s.message};break;case"final":e={...e,partial:s.transcript.text,turns:e.turns.map((t)=>t)};break;case"partial":e={...e,partial:s.transcript.text};break;case"replay":e={...e,assistantTexts:[...s.assistantTexts],call:s.call??null,error:null,isConnected:s.status==="active",partial:s.partial,reconnect:e.reconnect.status==="reconnecting"?{...e.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:e.reconnect,scenarioId:s.scenarioId??e.scenarioId,sessionId:s.sessionId,sessionMetadata:s.sessionMetadata??e.sessionMetadata,status:s.status,turns:[...s.turns]};break;case"session":e={...e,error:null,scenarioId:s.scenarioId??e.scenarioId,isConnected:s.status==="active",sessionId:s.sessionId,sessionMetadata:s.sessionMetadata??e.sessionMetadata,status:s.status};break;case"turn":e={...e,partial:"",turns:[...e.turns,s.turn]};break}c()},getServerSnapshot:()=>e,getSnapshot:()=>e,subscribe:(s)=>{return n.add(s),()=>{n.delete(s)}}}};var Re=(e,n={})=>{let c=be(e,n),o=we(),s=n.browserMedia&&typeof window<"u"?Le({...n.browserMedia,getScenarioId:()=>n.browserMedia?n.browserMedia.getScenarioId?.()??c.getScenarioId():c.getScenarioId(),getSessionId:()=>n.browserMedia?n.browserMedia.getSessionId?.()??c.getSessionId():c.getSessionId()}):null,t=new Set,i=(a)=>Promise.resolve().then(()=>{if(!a?.sessionId&&!a?.scenarioId)return;c.start(a),s?.start()}),r=()=>{t.forEach((a)=>a())},d=()=>{if(!n.reconnectReportPath||typeof fetch>"u")return;let a=o.getSnapshot(),A=JSON.stringify({at:Date.now(),reconnect:a.reconnect,scenarioId:a.scenarioId,sessionId:c.getSessionId(),turnIds:a.turns.map((g)=>g.id)});fetch(n.reconnectReportPath,{body:A,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},f=c.subscribe((a)=>{let A=he(a);if(A){if(o.dispatch(A),a.type==="connection")d();r()}});return{callControl(a){c.callControl(a)},close(){f(),s?.close(),c.close(),o.dispatch({type:"disconnected"}),r()},endTurn(){c.endTurn()},get error(){return o.getSnapshot().error},getServerSnapshot(){return o.getServerSnapshot()},getSnapshot(){return o.getSnapshot()},get isConnected(){return o.getSnapshot().isConnected},get scenarioId(){return o.getSnapshot().scenarioId},get sessionMetadata(){return o.getSnapshot().sessionMetadata},start:i,get partial(){return o.getSnapshot().partial},get reconnect(){return o.getSnapshot().reconnect},get sessionId(){return c.getSessionId()},get status(){return o.getSnapshot().status},get turns(){return o.getSnapshot().turns},get assistantTexts(){return o.getSnapshot().assistantTexts},get assistantAudio(){return o.getSnapshot().assistantAudio},get call(){return o.getSnapshot().call},sendAudio(a){c.sendAudio(a)},simulateDisconnect(){c.simulateDisconnect()},subscribe(a){return t.add(a),()=>{t.delete(a)}}}};var ue=(e)=>{if(!e||e.enabled===!1)return;return{enabled:!0,maxGain:e.maxGain??3,noiseGateAttenuation:e.noiseGateAttenuation??0.15,noiseGateThreshold:e.noiseGateThreshold??0.006,targetLevel:e.targetLevel??0.08}};var yn={balanced:{qualityProfile:"general",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},Tn={general:{},"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var _e=(e)=>{let n=e?.profile??"fast",c=e?.qualityProfile??"general",o=yn[n],s=Tn[c];return{profile:n,qualityProfile:c,silenceMs:e?.silenceMs??s.silenceMs??o.silenceMs,speechThreshold:e?.speechThreshold??s.speechThreshold??o.speechThreshold,transcriptStabilityMs:e?.transcriptStabilityMs??s.transcriptStabilityMs??o.transcriptStabilityMs}};var Sn={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"short-command",profile:"balanced"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"general",profile:"fast"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"accent-heavy",profile:"long-form"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{qualityProfile:"accent-heavy",profile:"long-form"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"noisy-room",profile:"long-form",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"noisy-room",profile:"long-form",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"noisy-room",profile:"long-form",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"noisy-room",profile:"long-form"}}},Ee=(e="default")=>{let n=Sn[e];return{audioConditioning:ue(n.audioConditioning),capture:{channelCount:n.capture?.channelCount??1,sampleRateHz:n.capture?.sampleRateHz??16000},connection:{...n.connection},name:e,sttLifecycle:n.sttLifecycle??"continuous",turnDetection:_e(n.turnDetection)}};var Mn=(e)=>({assistantAudio:[...e.assistantAudio],assistantTexts:[...e.assistantTexts],call:e.call,error:e.error,isConnected:e.isConnected,isRecording:!1,partial:e.partial,reconnect:e.reconnect,recordingError:null,sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,scenarioId:e.scenarioId,status:e.status,turns:[...e.turns]}),j=(e,n={})=>{let c=Ee(n.preset),o=Re(e,{...c.connection,...n.connection}),s=null,t=Mn(o),i=new Set,r=()=>{for(let C of i)C()},d=()=>{if(t={...t,assistantAudio:[...o.assistantAudio],assistantTexts:[...o.assistantTexts],call:o.call,error:o.error,isConnected:o.isConnected,partial:o.partial,reconnect:o.reconnect,sessionId:o.sessionId,sessionMetadata:o.sessionMetadata,scenarioId:o.scenarioId,status:o.status,turns:[...o.turns]},n.autoStopOnComplete!==!1&&t.status==="completed"&&t.isRecording)s?.stop(),s=null,t={...t,isRecording:!1};r()},f=o.subscribe(d);d();let a=()=>{if(s)return s;return s=Ae({channelCount:n.capture?.channelCount??c.capture.channelCount,onLevel:n.capture?.onLevel,onAudio:(C)=>{if(n.capture?.onAudio){n.capture.onAudio(C,o.sendAudio);return}o.sendAudio(C)},sampleRateHz:n.capture?.sampleRateHz??c.capture.sampleRateHz}),s},A=()=>{s?.stop(),s=null,t={...t,isRecording:!1},r()},g=async()=>{if(t.isRecording)return;try{t={...t,recordingError:null},r(),await a().start(),t={...t,isRecording:!0},r()}catch(C){throw s=null,t={...t,isRecording:!1,recordingError:C instanceof Error?C.message:String(C)},r(),C}};return{bindHTMX(C){return fe(o,C)},callControl:(C)=>o.callControl(C),close:()=>{f(),A(),o.close()},endTurn:()=>o.endTurn(),get error(){return t.error},getServerSnapshot:()=>t,getSnapshot:()=>t,get isConnected(){return t.isConnected},get isRecording(){return t.isRecording},get partial(){return t.partial},get recordingError(){return t.recordingError},get reconnect(){return t.reconnect},sendAudio:(C)=>o.sendAudio(C),simulateDisconnect:()=>o.simulateDisconnect(),get sessionId(){return t.sessionId},get sessionMetadata(){return t.sessionMetadata},get scenarioId(){return t.scenarioId},startRecording:g,get status(){return t.status},stopRecording:A,subscribe:(C)=>{return i.add(C),()=>{i.delete(C)}},toggleRecording:async()=>{if(t.isRecording){A();return}await g()},get turns(){return t.turns},get assistantTexts(){return t.assistantTexts},get assistantAudio(){return t.assistantAudio},get call(){return t.call}}};var In=()=>({activeSourceCount:0,error:null,isActive:!1,isPlaying:!1,lastInterruptLatencyMs:void 0,lastPlaybackStopLatencyMs:void 0,processedChunkCount:0,queuedChunkCount:0}),Vn=()=>{if(typeof window>"u")return typeof AudioContext>"u"?void 0:AudioContext;return window.AudioContext??window.webkitAudioContext},Ln=(e,n)=>{let c=n.format;if(c.container!=="raw"||c.encoding!=="pcm_s16le")throw Error(`Unsupported assistant audio format: ${c.container}/${c.encoding}`);let o=n.chunk,s=Math.max(1,c.channels),t=Math.floor(o.byteLength/2),i=Math.max(1,Math.floor(t/s)),r=e.createBuffer(s,i,c.sampleRateHz),d=new DataView(o.buffer,o.byteOffset,o.byteLength);for(let f=0;f<s;f+=1){let a=r.getChannelData(f);for(let A=0;A<i;A+=1){let y=(A*s+f)*2;if(y+1>=o.byteLength){a[A]=0;continue}a[A]=d.getInt16(y,!0)/32768}}return r},F=(e,n={})=>{let c=new Set,o=new Set,s=(n.lookaheadMs??15)/1000,t=In(),i=null,r=null,d=0,f=Promise.resolve(),a=null,A=null,g=null,y=null,C=()=>{for(let h of c)h()},I=(h)=>{t={...t,...h},C()},L=()=>{if(t.error!==null)I({error:null})},R=()=>{if(y!==null)clearTimeout(y),y=null},w=(h)=>{R(),a=null,I({activeSourceCount:o.size,isPlaying:!1,lastInterruptLatencyMs:h,lastPlaybackStopLatencyMs:t.lastPlaybackStopLatencyMs??h}),g?.(),g=null,A=null},x=(h)=>{if(!h)return 0;return Math.max(0,((h.baseLatency??0)+(h.outputLatency??0))*1000)},U=(h)=>{if(!r)return;let T=1;if(r.gain.setValueAtTime){r.gain.setValueAtTime(T,h?.currentTime??0);return}r.gain.value=T},l=(h)=>{if(!r)return;let T=0;if(r.gain.setValueAtTime){r.gain.setValueAtTime(T,h?.currentTime??0);return}r.gain.value=T},M=()=>{if(a===null||o.size>0)return;w(Date.now()-a)},b=async()=>{if(i)return i;if(n.createAudioContext)i=n.createAudioContext();else{let h=Vn();if(!h)throw Error("Assistant audio playback requires AudioContext support.");i=new h}if(i.createGain)r=i.createGain(),r.connect?.(i.destination);return d=i.currentTime,i},z=async(h)=>{let T=await b(),E=Ln(T,h),V=T.createBufferSource();V.buffer=E,V.connect(r??T.destination),V.onended=()=>{o.delete(V),V.disconnect?.(),I({activeSourceCount:o.size,isPlaying:o.size>0&&t.isActive}),M()};let J=Math.max(T.currentTime+s,d);d=J+E.duration,o.add(V),I({activeSourceCount:o.size,isPlaying:!0}),V.start(J)},_=(h)=>{for(let T of[...o])T.stop?.();if(d=i?i.currentTime:0,h?.forceClear){for(let T of o)T.disconnect?.();o.clear(),M()}},$=async()=>{if(!t.isActive)return;let h=e.assistantAudio.slice(t.processedChunkCount);if(h.length===0)return;try{L();for(let T of h)await z(T);I({processedChunkCount:e.assistantAudio.length,queuedChunkCount:t.queuedChunkCount+h.length})}catch(T){I({error:T instanceof Error?T.message:String(T)})}},P=()=>{return f=f.then(()=>$(),()=>$()),f},k=e.subscribe(()=>{if(n.autoStart&&!t.isActive&&e.assistantAudio.length>0){N.start();return}if(t.isActive)P()}),N={close:async()=>{if(k(),_({forceClear:!0}),R(),g?.(),g=null,A=null,a=null,i&&i.state!=="closed")await i.close();i=null,r?.disconnect?.(),r=null,d=0,I({activeSourceCount:0,isActive:!1,isPlaying:!1})},get activeSourceCount(){return t.activeSourceCount},get error(){return t.error},getSnapshot:()=>t,get isActive(){return t.isActive},get isPlaying(){return t.isPlaying},interrupt:async()=>{let h=Date.now(),T=await b();a=h,l(T);let E=Date.now()-h+x(T);if(I({isActive:!1,isPlaying:o.size>0,lastPlaybackStopLatencyMs:E}),o.size===0){w(E);return}if(!A)A=new Promise((V)=>{g=V});R(),y=setTimeout(()=>{for(let V of o)V.disconnect?.();o.clear(),w(Date.now()-h)},250),_(),await A},get lastInterruptLatencyMs(){return t.lastInterruptLatencyMs},get lastPlaybackStopLatencyMs(){return t.lastPlaybackStopLatencyMs},pause:async()=>{if(!i){I({activeSourceCount:0,isActive:!1,isPlaying:!1});return}await i.suspend(),I({activeSourceCount:o.size,isActive:!1,isPlaying:!1})},get processedChunkCount(){return t.processedChunkCount},get queuedChunkCount(){return t.queuedChunkCount},start:async()=>{try{L();let h=await b();if(U(h),h.state==="suspended")await h.resume();I({activeSourceCount:o.size,isActive:!0,isPlaying:h.state==="running"}),await P()}catch(h){throw I({error:h instanceof Error?h.message:String(h),isActive:!1,isPlaying:!1}),h}},subscribe:(h)=>{return c.add(h),()=>{c.delete(h)}}};return N};var bn=()=>`barge-in:${Date.now()}:${crypto.randomUUID?.()??Math.random().toString(36).slice(2)}`,wn=(e,n)=>{let c=e.filter((i)=>i.status==="stopped"),o=c.map((i)=>i.latencyMs).filter((i)=>typeof i==="number"),s=c.filter((i)=>typeof i.latencyMs==="number"&&i.latencyMs>n).length,t=c.length-s;return{averageLatencyMs:o.length>0?Math.round(o.reduce((i,r)=>i+r,0)/o.length):void 0,events:[...e],failed:s,lastEvent:e.at(-1),passed:t,status:e.length===0?"empty":s>0?"fail":c.length===0?"warn":"pass",thresholdMs:n,total:c.length}},Pe=(e={})=>{let n=new Set,c=e.thresholdMs??250,o=e.fetch??globalThis.fetch,s=[],t=()=>{for(let d of n)d()},i=(d)=>{if(!e.path||typeof o!=="function")return;o(e.path,{body:JSON.stringify(d),headers:{"Content-Type":"application/json"},method:"POST"}).catch(()=>{})},r=(d,f)=>{let a={at:Date.now(),id:bn(),latencyMs:f.latencyMs,playbackStopLatencyMs:f.playbackStopLatencyMs,reason:f.reason,sessionId:f.sessionId,status:d,thresholdMs:c};return s.push(a),i(a),t(),a};return{getSnapshot:()=>wn(s,c),recordRequested:(d)=>r("requested",d),recordSkipped:(d)=>r("skipped",d),recordStopped:(d)=>r("stopped",d),subscribe:(d)=>{return n.add(d),()=>{n.delete(d)}}}};var Rn=0.08,un=(e,n={})=>(n.enabled??!0)&&e>=(n.interruptThreshold??Rn),te=(e,n,c={})=>{let o=e.partial,s=(i)=>{if(!n.isPlaying||c.enabled===!1){c.monitor?.recordSkipped({reason:i,sessionId:e.sessionId});return}c.monitor?.recordRequested({reason:i,sessionId:e.sessionId}),n.interrupt().then(()=>{c.monitor?.recordStopped({latencyMs:n.lastInterruptLatencyMs,playbackStopLatencyMs:n.lastPlaybackStopLatencyMs,reason:i,sessionId:e.sessionId})})},t=e.subscribe(()=>{if(c.interruptOnPartial===!1){o=e.partial;return}if(!o&&e.partial)s("partial-transcript");o=e.partial});return{close:()=>{t()},handleLevel:(i)=>{if(un(i,c))s("input-level")},sendAudio:(i)=>{s("manual-audio"),e.sendAudio(i)}}};var ae=48,_n=320,En=88,Pn="Guided test",Dn="General recording",On="Pick a scenario to begin the demo.",xn="I can walk you through a short guided voice test.",Un="I can capture one freeform recording and confirm that it landed.",Nn="Choose a scenario to begin. Guided test asks follow-up prompts. General recording just captures what you say.",Hn="Click Start general recording to capture one freeform answer.",xe="Speak freely. When you pause, the recording will be captured.",re="Recording saved. Start again if you want another capture.",Ue="Guided test complete. Review the saved summary below.",Ne="All prompts are covered. You can stop the microphone or keep speaking for extra detail.",Gn="Ready. Start guided test or general recording to begin.",Bn="Live. Answer the prompt, then click Stop microphone when finished.",De=["Start with a quick introduction about who you are.","Now describe what you are trying to do or test.","Finish with any detail that feels blocked, risky, or unclear."],He=(e,n,c)=>Math.min(c,Math.max(n,e)),X=(e)=>e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll(\'"\',""").replaceAll("\'","'"),ie=(e,n)=>{let c=e[n];if(typeof c==="string"&&c.trim())return c;return null},le=(e)=>{if(typeof e==="string"&&e.trim())return e;if(e instanceof Error&&e.message.trim())return e.message;if(e&&typeof e==="object"){let n=e,c=ie(n,"message")??ie(n,"reason")??ie(n,"description");if(c)return c;if("error"in n)return le(n.error);if("cause"in n)return le(n.cause);try{return JSON.stringify(e)}catch{}}return"Unexpected error"},Wn=(e)=>{let n=[e.status];if(e.attempts>0||e.maxAttempts>0)n.push(`${e.attempts}/${e.maxAttempts} attempts`);if(e.nextAttemptAt){let c=Math.max(0,e.nextAttemptAt-Date.now());n.push(`retry in ${Math.ceil(c/100)/10}s`)}return n.join(" \xB7 ")},v=(e=ae)=>Array.from({length:e},()=>0),Oe=(e,n,c=ae)=>{let o=e.slice(-(c-1));o.push(He(n,0,1));while(o.length<c)o.unshift(0);return o},$n=(e,n=_n,c=En)=>{let o=e.length>1?e:v(ae),s=n/(o.length-1),t=c/2,i=c*0.34;if(Math.max(...o,0)<=0.015)return`M 0 ${t} L ${n} ${t}`;let d=o.map((a,A)=>{let g=A*0.76,y=Math.sin(g)*0.78+Math.sin(g*0.41)*0.22,C=a*i,I=s*A,L=He(t+y*C,8,c-8);return{x:I,y:L}});if(d.length===0)return`M 0 ${t} L ${n} ${t}`;let f=`M ${d[0]?.x??0} ${d[0]?.y??t}`;for(let a=1;a<d.length;a+=1){let A=d[a-1],g=d[a];if(!A||!g)continue;let y=(A.x+g.x)/2;f+=` Q ${y} ${A.y} ${g.x} ${g.y}`}return f},kn=(e)=>{if(!e)return De;try{let n=JSON.parse(e);if(Array.isArray(n)){let c=n.filter((o)=>typeof o==="string").map((o)=>o.trim()).filter(Boolean);if(c.length>0)return c}}catch{}return De},se=(e)=>{if(!e)return;let n=Number(e);return Number.isFinite(n)?n:void 0},qn=(e,n,c)=>{if(!n)return null;let o=document.querySelector(n);return o instanceof c?o:null},O=(e,n,c,o)=>{let s=n?document.querySelector(n):null;if(s instanceof c)return s;let t=e.querySelector(`#${o}`);if(t instanceof c)return t;throw Error(`Voice HTMX bootstrap could not find the required element "${o}".`)},Xn=(e)=>{if(!e.mode)return On;if(!e.hasStarted)return e.mode==="guided"?xn:Un;if(e.status==="completed")return e.mode==="guided"?Ue:re;if(e.mode==="general")return xe;return e.guidedPrompts[e.turnCount]??Ne},zn=(e)=>{if(!e.mode)return Nn;if(e.status==="completed")return e.mode==="guided"?Ue:re;if(!e.hasStarted)return e.mode==="guided"?`Click Start guided test to begin. First prompt: ${e.guidedPrompts[0]??"Answer the first prompt."}`:Hn;if(e.mode==="general")return e.turnCount===0?xe:re;return e.guidedPrompts[e.turnCount]??Ne},Yn=(e)=>{let n=e.dataset.voiceGuidedPath,c=e.dataset.voiceGeneralPath;if(!n||!c)throw Error("Voice HTMX bootstrap requires data-voice-guided-path and data-voice-general-path.");let o=kn(e.dataset.voiceGuidedPrompts),s=e.dataset.voiceGuidedLabel??Pn,t=e.dataset.voiceGeneralLabel??Dn,i=e.dataset.voiceReconnectReportPath,r=e.dataset.voiceBargeInPath,d=r?Pe({path:r,thresholdMs:se(e.dataset.voiceBargeInThresholdMs)}):null,f=se(e.dataset.voiceBargeInRecentWindowMs)??4000,a=se(e.dataset.voiceBargeInSpeechThreshold)??0.04,A=O(document,e.dataset.voiceSync,HTMLElement,"voice-htmx-sync"),g=O(e,e.dataset.voiceConnection,HTMLElement,"metric-connection"),y=O(e,e.dataset.voiceError,HTMLElement,"status-error"),C=O(e,e.dataset.voiceMicrophone,HTMLElement,"status-mic"),I=O(e,e.dataset.voicePrompt,HTMLElement,"status-prompt"),L=qn(e,e.dataset.voiceReconnect,HTMLElement),R=O(e,e.dataset.voiceChat,HTMLElement,"chat-list"),w=O(e,e.dataset.voiceStartGuided,HTMLButtonElement,"start-guided"),x=O(e,e.dataset.voiceStartGeneral,HTMLButtonElement,"start-general"),U=O(e,e.dataset.voiceStop,HTMLButtonElement,"stop-mic"),l=O(e,e.dataset.voiceMonitor,HTMLElement,"voice-monitor"),M=O(e,e.dataset.voiceMonitorCopy,HTMLElement,"voice-monitor-copy"),b=O(e,e.dataset.voiceWaveGlow,SVGPathElement,"voice-wave-glow"),z=O(e,e.dataset.voiceWavePath,SVGPathElement,"voice-wave-path"),_=null,$={general:!1,guided:!1},P=!1,k=null,N=v(),h=null,T=null,E=j(n,{capture:{onAudio:(S,B)=>{if(h){h.sendAudio(S);return}B(S)},onLevel:(S)=>{h?.handleLevel(S),N=Oe(N,S),p()}},connection:{reconnectReportPath:i},preset:"guided-intake"}),V=j(c,{capture:{onAudio:(S,B)=>{if(T){T.sendAudio(S);return}B(S)},onLevel:(S)=>{T?.handleLevel(S),N=Oe(N,S),p()}},connection:{reconnectReportPath:i},preset:"dictation"}),J=E.bindHTMX({element:A}),Ge=V.bindHTMX({element:A}),Q=F(E),Z=F(V);h=te(E,Q,{interruptThreshold:a,monitor:d??void 0}),T=te(V,Z,{interruptThreshold:a,monitor:d??void 0});let Y=()=>_==="general"?V:E,Qn=()=>_==="general"?Z:Q,p=()=>{let S=$n(N);b.setAttribute("d",S),z.setAttribute("d",S),M.innerHTML=`<span class="voice-live-dot"></span>${P?"Microphone live":"Microphone idle"}`,M.classList.toggle("is-live",P),l.classList.toggle("is-live",P)},q=()=>{let S=Y(),B=(_?$[_]:!1)||S.turns.length>0,ge=S.status;if(g.textContent=S.isConnected?"Connected":"Waiting",y.textContent=k||S.error||"None",L)L.textContent=Wn(S.reconnect);C.textContent=P?Bn:Gn,I.textContent=zn({guidedPrompts:o,hasStarted:B,mode:_,status:ge,turnCount:S.turns.length}),w.hidden=P,x.hidden=P,U.hidden=!P,R.innerHTML=`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${X(_==="general"?t:_==="guided"?s:"Voice demo")}</div>\n <p class="voice-turn-text">${X(Xn({generalLabel:t,guidedLabel:s,guidedPrompts:o,hasStarted:B,mode:_,status:ge,turnCount:S.turns.length}))}</p>\n</article>${S.turns.map((ee)=>`<div class="voice-chat-stack">\n <article class="voice-chat-message user">\n <div class="voice-chat-role">You</div>\n <p class="voice-turn-text">${X(ee.text)}</p>\n </article>\n ${ee.assistantText?`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${X(_==="general"?t:_==="guided"?s:"Guide")}</div>\n <p class="voice-turn-text">${X(ee.assistantText)}</p>\n </article>`:""}\n</div>`).join("")}${S.partial?`<article class="voice-chat-message user pending">\n <div class="voice-chat-role">Speaking</div>\n <p class="voice-turn-text">${X(S.partial)}</p>\n</article>`:""}`,p()},Be=()=>{Y().stopRecording(),P=!1,k=null,N=v(),q()},de=async(S)=>{_=S,$={...$,[S]:!0};try{await Y().startRecording(),k=null,P=!0,q()}catch(B){Y().stopRecording(),P=!1,N=v(),k=le(B),q()}};E.subscribe(()=>{if(E.assistantAudio.length>0)Q.start().catch(()=>{});q()}),V.subscribe(()=>{if(V.assistantAudio.length>0)Z.start().catch(()=>{});q()}),w.addEventListener("click",()=>{de("guided")}),x.addEventListener("click",()=>{de("general")}),U.addEventListener("click",()=>{Be()}),e.addEventListener("absolute-voice-simulate-disconnect",()=>{Y().simulateDisconnect()}),window.addEventListener("beforeunload",()=>{E.stopRecording(),V.stopRecording(),h?.close(),T?.close(),Q.close(),Z.close(),J(),Ge(),E.close(),V.close()}),q()},Jn=()=>{if(typeof window>"u"||typeof document>"u")return;let e=Array.from(document.querySelectorAll("[data-voice-htmx]"));for(let n of e)if(n instanceof HTMLElement)Yn(n)};Jn();export{Jn as initVoiceHTMX};\n';
|
|
5190
5190
|
|
|
5191
5191
|
// src/profileSwitchRecommendation.ts
|
|
5192
5192
|
import { Elysia } from "elysia";
|
|
@@ -12557,6 +12557,8 @@ var createVoiceDiagnosticsRoutes = (options) => {
|
|
|
12557
12557
|
import { Elysia as Elysia13 } from "elysia";
|
|
12558
12558
|
|
|
12559
12559
|
// node_modules/@absolutejs/media/dist/index.js
|
|
12560
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
12561
|
+
import { join } from "path";
|
|
12560
12562
|
var formatLabel2 = (format) => `${format.container}/${format.encoding}/${String(format.sampleRateHz)}hz/${String(format.channels)}ch`;
|
|
12561
12563
|
var formatMatches2 = (actual, expected) => actual.container === expected.container && actual.encoding === expected.encoding && actual.sampleRateHz === expected.sampleRateHz && actual.channels === expected.channels;
|
|
12562
12564
|
var pushIssue = (issues, severity, code, message) => {
|
|
@@ -13217,6 +13219,269 @@ var buildMediaPipelineCalibrationReport = (input = {}) => {
|
|
|
13217
13219
|
turnCommitFrames: turnCommitFrames.length
|
|
13218
13220
|
};
|
|
13219
13221
|
};
|
|
13222
|
+
var DEFAULT_METADATA_DENY = [
|
|
13223
|
+
"audioPayload",
|
|
13224
|
+
"auth",
|
|
13225
|
+
"authorization",
|
|
13226
|
+
"cookie",
|
|
13227
|
+
"email",
|
|
13228
|
+
"phone",
|
|
13229
|
+
"phoneNumber",
|
|
13230
|
+
"rawPayload",
|
|
13231
|
+
"secret",
|
|
13232
|
+
"token",
|
|
13233
|
+
"transcript",
|
|
13234
|
+
"utterance"
|
|
13235
|
+
];
|
|
13236
|
+
var DEFAULT_TRUNCATE = 8;
|
|
13237
|
+
var issueCodes = (issues) => Array.from(new Set(issues.map((issue) => issue.code)));
|
|
13238
|
+
var lastEventKind = (events) => events[events.length - 1]?.kind;
|
|
13239
|
+
var formatOptionalMs = (value) => value === undefined ? "n/a" : `${String(Math.round(value))}ms`;
|
|
13240
|
+
var formatRatio = (value) => `${(value * 100).toFixed(1)}%`;
|
|
13241
|
+
var escapeMarkdownCell = (value) => value.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
13242
|
+
var renderIssuesTable = (issues) => {
|
|
13243
|
+
if (issues.length === 0) {
|
|
13244
|
+
return `- No issues.
|
|
13245
|
+
`;
|
|
13246
|
+
}
|
|
13247
|
+
const rows = issues.map((issue) => `| ${issue.severity} | ${escapeMarkdownCell(issue.code)} | ${escapeMarkdownCell(issue.message)} |`).join(`
|
|
13248
|
+
`);
|
|
13249
|
+
return `| Severity | Code | Message |
|
|
13250
|
+
| --- | --- | --- |
|
|
13251
|
+
${rows}
|
|
13252
|
+
`;
|
|
13253
|
+
};
|
|
13254
|
+
var summarizeMediaQualityReport = (report) => ({
|
|
13255
|
+
backpressureEvents: report.backpressureEvents,
|
|
13256
|
+
description: `${report.totalFrames} frame(s), ${report.gapCount} gap(s), speech ${formatRatio(report.speechRatio)}, status ${report.status}.`,
|
|
13257
|
+
driftMs: report.timestampDriftMs,
|
|
13258
|
+
frameCount: report.totalFrames,
|
|
13259
|
+
gapCount: report.gapCount,
|
|
13260
|
+
issueCodes: issueCodes(report.issues),
|
|
13261
|
+
issueCount: report.issues.length,
|
|
13262
|
+
jitterMs: report.jitterMs,
|
|
13263
|
+
silenceRatio: report.silenceRatio,
|
|
13264
|
+
speechRatio: report.speechRatio,
|
|
13265
|
+
status: report.status
|
|
13266
|
+
});
|
|
13267
|
+
var summarizeMediaTransportReport = (report) => ({
|
|
13268
|
+
backpressureEvents: report.backpressureEvents,
|
|
13269
|
+
description: `${report.name}: ${report.state}, in ${report.inputFrames}, out ${report.outputFrames}, backpressure ${report.backpressureEvents}.`,
|
|
13270
|
+
errors: report.events.filter((event) => event.kind === "error").length,
|
|
13271
|
+
inputFrames: report.inputFrames,
|
|
13272
|
+
lastEventKind: lastEventKind(report.events),
|
|
13273
|
+
name: report.name,
|
|
13274
|
+
outputFrames: report.outputFrames,
|
|
13275
|
+
state: report.state,
|
|
13276
|
+
status: report.status
|
|
13277
|
+
});
|
|
13278
|
+
var summarizeMediaProcessorGraphReport = (report) => {
|
|
13279
|
+
const errorIssueCodes = Array.from(new Set(report.errors.map((event) => event.kind)));
|
|
13280
|
+
return {
|
|
13281
|
+
backpressureEvents: report.backpressure.events.length,
|
|
13282
|
+
description: `${report.name}: ${report.state}, ${report.nodes.length} node(s), in ${report.inputFrames}, out ${report.emittedFrames}, dropped ${report.droppedFrames}.`,
|
|
13283
|
+
droppedFrames: report.droppedFrames,
|
|
13284
|
+
edgeCount: report.edges.length,
|
|
13285
|
+
edgeEventCount: report.edgeEvents.length,
|
|
13286
|
+
emittedFrames: report.emittedFrames,
|
|
13287
|
+
errorCount: report.errors.length,
|
|
13288
|
+
inputFrames: report.inputFrames,
|
|
13289
|
+
issueCodes: errorIssueCodes,
|
|
13290
|
+
lifecycleEventCount: report.lifecycleEvents.length,
|
|
13291
|
+
name: report.name,
|
|
13292
|
+
nodeCount: report.nodes.length,
|
|
13293
|
+
state: report.state,
|
|
13294
|
+
status: report.status,
|
|
13295
|
+
timingMaxMs: report.timing.maxNodeMs
|
|
13296
|
+
};
|
|
13297
|
+
};
|
|
13298
|
+
var renderMediaQualityMarkdown = (report, options = {}) => {
|
|
13299
|
+
const title = options.title ?? "Media Quality Report";
|
|
13300
|
+
const lines = [
|
|
13301
|
+
`# ${title}`,
|
|
13302
|
+
"",
|
|
13303
|
+
`Status: **${report.status}**`,
|
|
13304
|
+
"",
|
|
13305
|
+
"| Metric | Value |",
|
|
13306
|
+
"| --- | ---: |",
|
|
13307
|
+
`| Total frames | ${report.totalFrames} |`,
|
|
13308
|
+
`| Input audio | ${report.inputAudioFrames} |`,
|
|
13309
|
+
`| Assistant audio | ${report.assistantAudioFrames} |`,
|
|
13310
|
+
`| Gaps | ${report.gapCount} |`,
|
|
13311
|
+
`| Jitter | ${formatOptionalMs(report.jitterMs)} |`,
|
|
13312
|
+
`| Timestamp drift | ${formatOptionalMs(report.timestampDriftMs)} |`,
|
|
13313
|
+
`| Speech ratio | ${formatRatio(report.speechRatio)} |`,
|
|
13314
|
+
`| Silence ratio | ${formatRatio(report.silenceRatio)} |`,
|
|
13315
|
+
`| Backpressure events | ${report.backpressureEvents} |`,
|
|
13316
|
+
"",
|
|
13317
|
+
"## Issues",
|
|
13318
|
+
"",
|
|
13319
|
+
renderIssuesTable(report.issues).trimEnd()
|
|
13320
|
+
];
|
|
13321
|
+
return `${lines.join(`
|
|
13322
|
+
`)}
|
|
13323
|
+
`;
|
|
13324
|
+
};
|
|
13325
|
+
var renderMediaTransportMarkdown = (report, options = {}) => {
|
|
13326
|
+
const title = options.title ?? `Media Transport: ${report.name}`;
|
|
13327
|
+
const limit = options.redact?.truncateArraysAt ?? DEFAULT_TRUNCATE;
|
|
13328
|
+
const events = report.events.slice(-limit);
|
|
13329
|
+
const eventRows = events.length === 0 ? "- No transport events recorded." : ["| At | Kind | State | Buffered | Error |", "| --- | --- | --- | ---: | --- |"].concat(events.map((event) => `| ${event.at} | ${event.kind} | ${event.state} | ${event.bufferedFrames ?? ""} | ${escapeMarkdownCell(event.error ?? "")} |`)).join(`
|
|
13330
|
+
`);
|
|
13331
|
+
const lines = [
|
|
13332
|
+
`# ${title}`,
|
|
13333
|
+
"",
|
|
13334
|
+
`Status: **${report.status}** \xB7 State: **${report.state}**`,
|
|
13335
|
+
"",
|
|
13336
|
+
"| Metric | Value |",
|
|
13337
|
+
"| --- | ---: |",
|
|
13338
|
+
`| Input frames | ${report.inputFrames} |`,
|
|
13339
|
+
`| Output frames | ${report.outputFrames} |`,
|
|
13340
|
+
`| Backpressure events | ${report.backpressureEvents} |`,
|
|
13341
|
+
`| Connected | ${report.connected ? "yes" : "no"} |`,
|
|
13342
|
+
`| Closed | ${report.closed ? "yes" : "no"} |`,
|
|
13343
|
+
`| Failed | ${report.failed ? "yes" : "no"} |`,
|
|
13344
|
+
"",
|
|
13345
|
+
`## Last ${events.length} event(s)`,
|
|
13346
|
+
"",
|
|
13347
|
+
eventRows
|
|
13348
|
+
];
|
|
13349
|
+
return `${lines.join(`
|
|
13350
|
+
`)}
|
|
13351
|
+
`;
|
|
13352
|
+
};
|
|
13353
|
+
var renderMediaProcessorGraphMarkdown = (report, options = {}) => {
|
|
13354
|
+
const title = options.title ?? `Media Processor Graph: ${report.name}`;
|
|
13355
|
+
const limit = options.redact?.truncateArraysAt ?? DEFAULT_TRUNCATE;
|
|
13356
|
+
const nodeRows = report.nodes.map((node) => `| ${escapeMarkdownCell(node.name)} | ${node.kind} | ${node.status} | ${node.inputFrames} | ${node.emittedFrames} | ${node.droppedFrames} | ${node.errors.length} |`).join(`
|
|
13357
|
+
`);
|
|
13358
|
+
const edgeRows = report.edges.slice(0, limit).map((edge) => `| ${escapeMarkdownCell(edge.from)} | ${escapeMarkdownCell(edge.to)} | ${edge.status} | ${edge.emittedFrames} |`).join(`
|
|
13359
|
+
`);
|
|
13360
|
+
const issues = report.errors.map((event) => ({
|
|
13361
|
+
code: event.kind,
|
|
13362
|
+
message: event.error ?? `Processor graph ${event.kind} (state ${event.state}).`,
|
|
13363
|
+
severity: "error"
|
|
13364
|
+
}));
|
|
13365
|
+
const lines = [
|
|
13366
|
+
`# ${title}`,
|
|
13367
|
+
"",
|
|
13368
|
+
`Status: **${report.status}** \xB7 State: **${report.state}**`,
|
|
13369
|
+
"",
|
|
13370
|
+
"| Metric | Value |",
|
|
13371
|
+
"| --- | ---: |",
|
|
13372
|
+
`| Nodes | ${report.nodes.length} |`,
|
|
13373
|
+
`| Input frames | ${report.inputFrames} |`,
|
|
13374
|
+
`| Emitted frames | ${report.emittedFrames} |`,
|
|
13375
|
+
`| Dropped frames | ${report.droppedFrames} |`,
|
|
13376
|
+
`| Lifecycle events | ${report.lifecycleEvents.length} |`,
|
|
13377
|
+
`| Edge events | ${report.edgeEvents.length} |`,
|
|
13378
|
+
`| Backpressure events | ${report.backpressure.events.length} |`,
|
|
13379
|
+
`| Timing max | ${formatOptionalMs(report.timing.maxNodeMs)} |`,
|
|
13380
|
+
`| Timing average | ${formatOptionalMs(report.timing.averageNodeMs)} |`,
|
|
13381
|
+
"",
|
|
13382
|
+
"## Nodes",
|
|
13383
|
+
"",
|
|
13384
|
+
nodeRows ? `| Node | Kind | Status | In | Out | Dropped | Errors |
|
|
13385
|
+
| --- | --- | --- | ---: | ---: | ---: | ---: |
|
|
13386
|
+
${nodeRows}` : "- No nodes.",
|
|
13387
|
+
"",
|
|
13388
|
+
`## Edges (showing up to ${limit})`,
|
|
13389
|
+
"",
|
|
13390
|
+
edgeRows ? `| From | To | Status | Frames |
|
|
13391
|
+
| --- | --- | --- | ---: |
|
|
13392
|
+
${edgeRows}` : "- No edges.",
|
|
13393
|
+
"",
|
|
13394
|
+
"## Errors",
|
|
13395
|
+
"",
|
|
13396
|
+
renderIssuesTable(issues).trimEnd()
|
|
13397
|
+
];
|
|
13398
|
+
return `${lines.join(`
|
|
13399
|
+
`)}
|
|
13400
|
+
`;
|
|
13401
|
+
};
|
|
13402
|
+
var truncateArrays = (value, limit, seen) => {
|
|
13403
|
+
if (Array.isArray(value)) {
|
|
13404
|
+
const head = value.slice(0, limit).map((entry) => truncateArrays(entry, limit, seen));
|
|
13405
|
+
if (value.length > limit) {
|
|
13406
|
+
return [...head, { truncated: value.length - limit }];
|
|
13407
|
+
}
|
|
13408
|
+
return head;
|
|
13409
|
+
}
|
|
13410
|
+
if (value && typeof value === "object") {
|
|
13411
|
+
if (seen.has(value))
|
|
13412
|
+
return value;
|
|
13413
|
+
seen.add(value);
|
|
13414
|
+
const next = {};
|
|
13415
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
13416
|
+
next[key] = truncateArrays(entry, limit, seen);
|
|
13417
|
+
}
|
|
13418
|
+
return next;
|
|
13419
|
+
}
|
|
13420
|
+
return value;
|
|
13421
|
+
};
|
|
13422
|
+
var applyRedaction = (value, options, seen) => {
|
|
13423
|
+
const mode = options.mode ?? "omit";
|
|
13424
|
+
const maskValue = options.maskValue ?? "[redacted]";
|
|
13425
|
+
const allow = new Set(options.metadataAllow ?? []);
|
|
13426
|
+
const deny = new Set(options.metadataDeny ?? DEFAULT_METADATA_DENY);
|
|
13427
|
+
const walk = (input) => {
|
|
13428
|
+
if (Array.isArray(input)) {
|
|
13429
|
+
return input.map((entry) => walk(entry));
|
|
13430
|
+
}
|
|
13431
|
+
if (input && typeof input === "object") {
|
|
13432
|
+
if (seen.has(input))
|
|
13433
|
+
return input;
|
|
13434
|
+
seen.add(input);
|
|
13435
|
+
const next = {};
|
|
13436
|
+
for (const [key, entry] of Object.entries(input)) {
|
|
13437
|
+
if (allow.has(key)) {
|
|
13438
|
+
next[key] = entry;
|
|
13439
|
+
continue;
|
|
13440
|
+
}
|
|
13441
|
+
if (deny.has(key)) {
|
|
13442
|
+
if (mode === "mask")
|
|
13443
|
+
next[key] = maskValue;
|
|
13444
|
+
continue;
|
|
13445
|
+
}
|
|
13446
|
+
next[key] = walk(entry);
|
|
13447
|
+
}
|
|
13448
|
+
return next;
|
|
13449
|
+
}
|
|
13450
|
+
return input;
|
|
13451
|
+
};
|
|
13452
|
+
return walk(value);
|
|
13453
|
+
};
|
|
13454
|
+
var redactMediaReport = (report, options = {}) => {
|
|
13455
|
+
const limit = options.truncateArraysAt ?? DEFAULT_TRUNCATE;
|
|
13456
|
+
const truncated = truncateArrays(report, limit, new WeakSet);
|
|
13457
|
+
return applyRedaction(truncated, options, new WeakSet);
|
|
13458
|
+
};
|
|
13459
|
+
var buildArtifactPair = (report, summary, markdown, options) => {
|
|
13460
|
+
const jsonValue = options.redact ? redactMediaReport(report, options.redact) : report;
|
|
13461
|
+
return {
|
|
13462
|
+
json: JSON.stringify(jsonValue, null, 2),
|
|
13463
|
+
jsonValue,
|
|
13464
|
+
markdown,
|
|
13465
|
+
summary
|
|
13466
|
+
};
|
|
13467
|
+
};
|
|
13468
|
+
var buildMediaQualityArtifact = (report, options = {}) => buildArtifactPair(report, summarizeMediaQualityReport(report), renderMediaQualityMarkdown(report, options), options);
|
|
13469
|
+
var buildMediaTransportArtifact = (report, options = {}) => buildArtifactPair(report, summarizeMediaTransportReport(report), renderMediaTransportMarkdown(report, options), options);
|
|
13470
|
+
var buildMediaProcessorGraphArtifact = (report, options = {}) => buildArtifactPair(report, summarizeMediaProcessorGraphReport(report), renderMediaProcessorGraphMarkdown(report, options), options);
|
|
13471
|
+
var writeMediaArtifact = async (input) => {
|
|
13472
|
+
await mkdir(input.dir, { recursive: true });
|
|
13473
|
+
const jsonPath = join(input.dir, `${input.slug}.json`);
|
|
13474
|
+
const markdownPath = join(input.dir, `${input.slug}.md`);
|
|
13475
|
+
await Promise.all([
|
|
13476
|
+
writeFile(jsonPath, input.json, "utf8"),
|
|
13477
|
+
writeFile(markdownPath, input.markdown, "utf8")
|
|
13478
|
+
]);
|
|
13479
|
+
return {
|
|
13480
|
+
jsonPath,
|
|
13481
|
+
markdownPath,
|
|
13482
|
+
summary: input.summary
|
|
13483
|
+
};
|
|
13484
|
+
};
|
|
13220
13485
|
|
|
13221
13486
|
// src/mediaPipelineRoutes.ts
|
|
13222
13487
|
var escapeHtml16 = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -13460,6 +13725,70 @@ var createVoiceMediaPipelineRoutes = (options = {}) => {
|
|
|
13460
13725
|
}
|
|
13461
13726
|
return app;
|
|
13462
13727
|
};
|
|
13728
|
+
var PROOF_SUMMARY_ISSUE_LIMIT = 8;
|
|
13729
|
+
var collectIssueCodes = (issues) => Array.from(new Set(issues.map((issue) => issue.code)));
|
|
13730
|
+
var summarizeVoiceMediaPipelineReport = (report, options = {}) => {
|
|
13731
|
+
const calibration = report.calibration;
|
|
13732
|
+
const quality = summarizeMediaQualityReport(report.quality);
|
|
13733
|
+
const processorGraph = report.processorGraph ? summarizeMediaProcessorGraphReport(report.processorGraph) : undefined;
|
|
13734
|
+
const transport = report.transport ? summarizeMediaTransportReport(report.transport) : undefined;
|
|
13735
|
+
const interruptionIssueCodes = collectIssueCodes(report.interruption.issues);
|
|
13736
|
+
const issueCodes2 = Array.from(new Set([
|
|
13737
|
+
...collectIssueCodes(calibration.issues),
|
|
13738
|
+
...quality.issueCodes,
|
|
13739
|
+
...interruptionIssueCodes,
|
|
13740
|
+
...processorGraph?.issueCodes ?? []
|
|
13741
|
+
]));
|
|
13742
|
+
const issues = [
|
|
13743
|
+
...calibration.issues,
|
|
13744
|
+
...report.quality.issues,
|
|
13745
|
+
...report.interruption.issues
|
|
13746
|
+
].slice(0, PROOF_SUMMARY_ISSUE_LIMIT);
|
|
13747
|
+
return {
|
|
13748
|
+
artifacts: options.artifacts,
|
|
13749
|
+
calibration: {
|
|
13750
|
+
assistantAudioFrames: calibration.assistantAudioFrames,
|
|
13751
|
+
backpressureFrames: calibration.backpressureFrames,
|
|
13752
|
+
firstAudioLatencyMs: calibration.firstAudioLatencyMs,
|
|
13753
|
+
inputAudioFrames: calibration.inputAudioFrames,
|
|
13754
|
+
interruptionFrames: calibration.interruptionFrames,
|
|
13755
|
+
issueCodes: collectIssueCodes(calibration.issues),
|
|
13756
|
+
jitterMs: calibration.jitterMs,
|
|
13757
|
+
resamplingRequired: calibration.resamplingRequired,
|
|
13758
|
+
status: calibration.status,
|
|
13759
|
+
surface: calibration.surface,
|
|
13760
|
+
traceLinkedFrames: calibration.traceLinkedFrames,
|
|
13761
|
+
turnCommitFrames: calibration.turnCommitFrames
|
|
13762
|
+
},
|
|
13763
|
+
checkedAt: report.checkedAt,
|
|
13764
|
+
frames: report.frames,
|
|
13765
|
+
interruption: {
|
|
13766
|
+
interruptionFrames: report.interruption.interruptionFrames,
|
|
13767
|
+
issueCodes: interruptionIssueCodes,
|
|
13768
|
+
latenciesMs: report.interruption.latenciesMs,
|
|
13769
|
+
status: report.interruption.status
|
|
13770
|
+
},
|
|
13771
|
+
issueCodes: issueCodes2,
|
|
13772
|
+
issues,
|
|
13773
|
+
ok: report.ok,
|
|
13774
|
+
processorGraph,
|
|
13775
|
+
quality,
|
|
13776
|
+
resampling: report.resampling ? {
|
|
13777
|
+
ratio: report.resampling.ratio,
|
|
13778
|
+
required: report.resampling.required,
|
|
13779
|
+
status: report.resampling.status
|
|
13780
|
+
} : undefined,
|
|
13781
|
+
sessionIds: report.sessionIds,
|
|
13782
|
+
status: report.status,
|
|
13783
|
+
surface: report.surface,
|
|
13784
|
+
transport,
|
|
13785
|
+
vad: {
|
|
13786
|
+
inputAudioFrames: report.vad.inputAudioFrames,
|
|
13787
|
+
segmentCount: report.vad.segments.length,
|
|
13788
|
+
status: report.vad.status
|
|
13789
|
+
}
|
|
13790
|
+
};
|
|
13791
|
+
};
|
|
13463
13792
|
// src/telephonyMediaRoutes.ts
|
|
13464
13793
|
import { Elysia as Elysia14 } from "elysia";
|
|
13465
13794
|
var demoPayload = Buffer.from(new Uint8Array([1, 2, 3, 4])).toString("base64");
|
|
@@ -13964,6 +14293,213 @@ var createVoiceBrowserCallProfileRoutes = (options = {}) => {
|
|
|
13964
14293
|
}
|
|
13965
14294
|
return routes;
|
|
13966
14295
|
};
|
|
14296
|
+
// src/mediaPipelineSurfaces.ts
|
|
14297
|
+
var calibrationIssues = (report) => report.calibration.issues.map((issue) => ({
|
|
14298
|
+
code: issue.code,
|
|
14299
|
+
message: issue.message,
|
|
14300
|
+
severity: issue.severity,
|
|
14301
|
+
source: "calibration",
|
|
14302
|
+
surface: report.surface
|
|
14303
|
+
}));
|
|
14304
|
+
var qualityIssues = (report) => report.quality.issues.map((issue) => ({
|
|
14305
|
+
code: issue.code,
|
|
14306
|
+
message: issue.message,
|
|
14307
|
+
severity: issue.severity,
|
|
14308
|
+
source: "quality",
|
|
14309
|
+
surface: report.surface
|
|
14310
|
+
}));
|
|
14311
|
+
var interruptionIssues = (report) => report.interruption.issues.map((issue) => ({
|
|
14312
|
+
code: issue.code,
|
|
14313
|
+
message: issue.message,
|
|
14314
|
+
severity: issue.severity,
|
|
14315
|
+
source: "interruption",
|
|
14316
|
+
surface: report.surface
|
|
14317
|
+
}));
|
|
14318
|
+
var transportIssues = (report) => {
|
|
14319
|
+
if (!report.transport)
|
|
14320
|
+
return [];
|
|
14321
|
+
const entries = [];
|
|
14322
|
+
if (report.transport.failed) {
|
|
14323
|
+
entries.push({
|
|
14324
|
+
code: "media.transport_failed",
|
|
14325
|
+
message: `Media transport ${report.transport.name} entered the failed state.`,
|
|
14326
|
+
severity: "error",
|
|
14327
|
+
source: "transport",
|
|
14328
|
+
surface: report.surface
|
|
14329
|
+
});
|
|
14330
|
+
}
|
|
14331
|
+
if (report.transport.backpressureEvents > 0) {
|
|
14332
|
+
entries.push({
|
|
14333
|
+
code: "media.transport_backpressure",
|
|
14334
|
+
message: `Media transport ${report.transport.name} reported ${String(report.transport.backpressureEvents)} backpressure event(s).`,
|
|
14335
|
+
severity: "warning",
|
|
14336
|
+
source: "transport",
|
|
14337
|
+
surface: report.surface
|
|
14338
|
+
});
|
|
14339
|
+
}
|
|
14340
|
+
const errorEvents = report.transport.events.filter((event) => event.kind === "error");
|
|
14341
|
+
for (const event of errorEvents) {
|
|
14342
|
+
entries.push({
|
|
14343
|
+
code: "media.transport_error",
|
|
14344
|
+
message: event.error ?? "Media transport error event.",
|
|
14345
|
+
severity: "error",
|
|
14346
|
+
source: "transport",
|
|
14347
|
+
surface: report.surface
|
|
14348
|
+
});
|
|
14349
|
+
}
|
|
14350
|
+
return entries;
|
|
14351
|
+
};
|
|
14352
|
+
var processorGraphIssues = (report) => {
|
|
14353
|
+
if (!report.processorGraph)
|
|
14354
|
+
return [];
|
|
14355
|
+
return report.processorGraph.errors.map((event) => ({
|
|
14356
|
+
code: `media.graph_${event.kind}`,
|
|
14357
|
+
message: event.error ?? `Processor graph reported ${event.kind} (state ${event.state}).`,
|
|
14358
|
+
severity: "error",
|
|
14359
|
+
source: "processor-graph",
|
|
14360
|
+
surface: report.surface
|
|
14361
|
+
}));
|
|
14362
|
+
};
|
|
14363
|
+
var extractVoiceMediaPipelineIssueEntries = (report) => [
|
|
14364
|
+
...calibrationIssues(report),
|
|
14365
|
+
...qualityIssues(report),
|
|
14366
|
+
...interruptionIssues(report),
|
|
14367
|
+
...transportIssues(report),
|
|
14368
|
+
...processorGraphIssues(report)
|
|
14369
|
+
];
|
|
14370
|
+
var toReadinessStatus = (status) => status === "fail" ? "fail" : status === "warn" ? "warn" : "pass";
|
|
14371
|
+
var readinessStatusFromIssues = (issues) => {
|
|
14372
|
+
if (issues.some((issue) => issue.severity === "error"))
|
|
14373
|
+
return "fail";
|
|
14374
|
+
if (issues.length > 0)
|
|
14375
|
+
return "warn";
|
|
14376
|
+
return "pass";
|
|
14377
|
+
};
|
|
14378
|
+
var buildVoiceMediaPipelineReadinessChecks = (report, options = {}) => {
|
|
14379
|
+
const baseHref = options.baseHref ?? "/voice/media-pipeline";
|
|
14380
|
+
const label = options.label ?? "Media pipeline";
|
|
14381
|
+
const checks = [
|
|
14382
|
+
{
|
|
14383
|
+
detail: `Status ${report.status}, surface ${report.surface}, ${String(report.frames)} frame(s).`,
|
|
14384
|
+
href: baseHref,
|
|
14385
|
+
label: `${label}: overall`,
|
|
14386
|
+
status: toReadinessStatus(report.status)
|
|
14387
|
+
},
|
|
14388
|
+
{
|
|
14389
|
+
detail: `${String(report.quality.issues.length)} quality issue(s); gaps ${String(report.quality.gapCount)}, jitter ${String(report.quality.jitterMs ?? "n/a")}ms, speech ratio ${(report.quality.speechRatio * 100).toFixed(1)}%.`,
|
|
14390
|
+
href: baseHref,
|
|
14391
|
+
label: `${label}: media quality`,
|
|
14392
|
+
status: toReadinessStatus(report.quality.status),
|
|
14393
|
+
value: report.quality.gapCount
|
|
14394
|
+
}
|
|
14395
|
+
];
|
|
14396
|
+
if (report.transport) {
|
|
14397
|
+
checks.push({
|
|
14398
|
+
detail: `${report.transport.state}, in ${String(report.transport.inputFrames)}, out ${String(report.transport.outputFrames)}, backpressure ${String(report.transport.backpressureEvents)}.`,
|
|
14399
|
+
href: baseHref,
|
|
14400
|
+
label: `${label}: transport`,
|
|
14401
|
+
status: toReadinessStatus(report.transport.status),
|
|
14402
|
+
value: report.transport.state
|
|
14403
|
+
});
|
|
14404
|
+
}
|
|
14405
|
+
if (report.processorGraph) {
|
|
14406
|
+
checks.push({
|
|
14407
|
+
detail: `${report.processorGraph.state}, nodes ${String(report.processorGraph.nodes.length)}, errors ${String(report.processorGraph.errors.length)}, dropped ${String(report.processorGraph.droppedFrames)}.`,
|
|
14408
|
+
href: baseHref,
|
|
14409
|
+
label: `${label}: processor graph`,
|
|
14410
|
+
status: toReadinessStatus(report.processorGraph.status),
|
|
14411
|
+
value: report.processorGraph.state
|
|
14412
|
+
});
|
|
14413
|
+
}
|
|
14414
|
+
checks.push({
|
|
14415
|
+
detail: `${String(report.interruption.interruptionFrames)} interruption frame(s); ${String(report.interruption.issues.length)} issue(s).`,
|
|
14416
|
+
href: baseHref,
|
|
14417
|
+
label: `${label}: interruption`,
|
|
14418
|
+
status: readinessStatusFromIssues(report.interruption.issues),
|
|
14419
|
+
value: report.interruption.interruptionFrames
|
|
14420
|
+
});
|
|
14421
|
+
return checks;
|
|
14422
|
+
};
|
|
14423
|
+
var severityToIncident = (severity) => severity === "error" ? "critical" : "warn";
|
|
14424
|
+
var sourceSuffix = {
|
|
14425
|
+
calibration: "calibration",
|
|
14426
|
+
interruption: "interruption",
|
|
14427
|
+
"processor-graph": "graph",
|
|
14428
|
+
quality: "quality",
|
|
14429
|
+
transport: "transport"
|
|
14430
|
+
};
|
|
14431
|
+
var buildVoiceMediaPipelineIncidentEvents = (report, options = {}) => {
|
|
14432
|
+
const baseHref = options.baseHref ?? "/voice/media-pipeline";
|
|
14433
|
+
const category = options.category ?? "monitor";
|
|
14434
|
+
const source = options.source ?? "media-pipeline";
|
|
14435
|
+
const now = options.now ?? (() => Date.now());
|
|
14436
|
+
const at = now();
|
|
14437
|
+
const entries = extractVoiceMediaPipelineIssueEntries(report);
|
|
14438
|
+
return entries.map((entry, index) => ({
|
|
14439
|
+
at,
|
|
14440
|
+
category,
|
|
14441
|
+
detail: entry.message,
|
|
14442
|
+
href: baseHref,
|
|
14443
|
+
id: `media-pipeline:${sourceSuffix[entry.source]}:${entry.code}:${String(index)}`,
|
|
14444
|
+
label: `Media ${sourceSuffix[entry.source]} ${entry.severity}: ${entry.code}`,
|
|
14445
|
+
severity: severityToIncident(entry.severity),
|
|
14446
|
+
source,
|
|
14447
|
+
value: entry.code
|
|
14448
|
+
}));
|
|
14449
|
+
};
|
|
14450
|
+
var buildHref = (base, jsonPath, fallbackSlug) => {
|
|
14451
|
+
if (!base)
|
|
14452
|
+
return;
|
|
14453
|
+
const filename = jsonPath.split("/").pop() ?? `${fallbackSlug}.json`;
|
|
14454
|
+
return `${base.replace(/\/$/, "")}/${filename}`;
|
|
14455
|
+
};
|
|
14456
|
+
var recordFromWrite = (kind, write, hrefBase, slug) => ({
|
|
14457
|
+
href: buildHref(hrefBase, write.jsonPath, slug),
|
|
14458
|
+
jsonPath: write.jsonPath,
|
|
14459
|
+
kind,
|
|
14460
|
+
markdownPath: write.markdownPath,
|
|
14461
|
+
summary: write.summary
|
|
14462
|
+
});
|
|
14463
|
+
var writeVoiceMediaPipelineArtifacts = async (options) => {
|
|
14464
|
+
const slugPrefix = options.slugPrefix ?? "media";
|
|
14465
|
+
const qualitySlug = `${slugPrefix}-quality`;
|
|
14466
|
+
const transportSlug = `${slugPrefix}-transport`;
|
|
14467
|
+
const graphSlug = `${slugPrefix}-processor-graph`;
|
|
14468
|
+
const artifacts = [];
|
|
14469
|
+
const hrefs = {};
|
|
14470
|
+
const qualityArtifact = buildMediaQualityArtifact(options.report.quality);
|
|
14471
|
+
const qualityWrite = await writeMediaArtifact({
|
|
14472
|
+
dir: options.dir,
|
|
14473
|
+
slug: qualitySlug,
|
|
14474
|
+
...qualityArtifact
|
|
14475
|
+
});
|
|
14476
|
+
const qualityRecord = recordFromWrite("quality", qualityWrite, options.hrefBase, qualitySlug);
|
|
14477
|
+
artifacts.push(qualityRecord);
|
|
14478
|
+
hrefs.quality = qualityRecord.href;
|
|
14479
|
+
if (options.report.transport) {
|
|
14480
|
+
const transportArtifact = buildMediaTransportArtifact(options.report.transport);
|
|
14481
|
+
const transportWrite = await writeMediaArtifact({
|
|
14482
|
+
dir: options.dir,
|
|
14483
|
+
slug: transportSlug,
|
|
14484
|
+
...transportArtifact
|
|
14485
|
+
});
|
|
14486
|
+
const transportRecord = recordFromWrite("transport", transportWrite, options.hrefBase, transportSlug);
|
|
14487
|
+
artifacts.push(transportRecord);
|
|
14488
|
+
hrefs.transport = transportRecord.href;
|
|
14489
|
+
}
|
|
14490
|
+
if (options.report.processorGraph) {
|
|
14491
|
+
const graphArtifact = buildMediaProcessorGraphArtifact(options.report.processorGraph);
|
|
14492
|
+
const graphWrite = await writeMediaArtifact({
|
|
14493
|
+
dir: options.dir,
|
|
14494
|
+
slug: graphSlug,
|
|
14495
|
+
...graphArtifact
|
|
14496
|
+
});
|
|
14497
|
+
const graphRecord = recordFromWrite("processor-graph", graphWrite, options.hrefBase, graphSlug);
|
|
14498
|
+
artifacts.push(graphRecord);
|
|
14499
|
+
hrefs.processorGraph = graphRecord.href;
|
|
14500
|
+
}
|
|
14501
|
+
return { artifacts, hrefs };
|
|
14502
|
+
};
|
|
13967
14503
|
// src/demoReadyRoutes.ts
|
|
13968
14504
|
import { Elysia as Elysia17 } from "elysia";
|
|
13969
14505
|
var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -16864,7 +17400,38 @@ var resolveRealCallEvidenceRuntimeReconnectReports = async (reports, options) =>
|
|
|
16864
17400
|
}
|
|
16865
17401
|
return Array.isArray(resolved) ? resolved : [resolved];
|
|
16866
17402
|
};
|
|
17403
|
+
var resolveRealCallEvidenceRuntimeSurfaceEvidence = async (source, options) => {
|
|
17404
|
+
const resolved = typeof source === "function" ? await source(options) : source;
|
|
17405
|
+
if (!resolved) {
|
|
17406
|
+
return [];
|
|
17407
|
+
}
|
|
17408
|
+
return Array.isArray(resolved) ? [...resolved] : [resolved];
|
|
17409
|
+
};
|
|
17410
|
+
var createRealCallEvidenceRuntimeSessionId = (prefix, generatedAt) => `${prefix}-${Date.parse(generatedAt) || Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
17411
|
+
var buildVoiceRealCallProfileEvidenceFromRuntimeSurface = (input, options) => {
|
|
17412
|
+
const evidence = Array.isArray(input) ? input : [input];
|
|
17413
|
+
return evidence.map((item) => {
|
|
17414
|
+
const generatedAt = item.generatedAt ?? (options.now ?? (() => new Date))().toISOString();
|
|
17415
|
+
return {
|
|
17416
|
+
...item,
|
|
17417
|
+
generatedAt,
|
|
17418
|
+
profileId: item.profileId ?? options.defaultProfileId,
|
|
17419
|
+
providers: item.providers ? [...item.providers] : undefined,
|
|
17420
|
+
sessionId: item.sessionId ?? createRealCallEvidenceRuntimeSessionId(options.defaultSessionPrefix, generatedAt),
|
|
17421
|
+
surfaces: [
|
|
17422
|
+
...new Set([...item.surfaces ?? [], ...options.defaultSurfaces])
|
|
17423
|
+
].sort()
|
|
17424
|
+
};
|
|
17425
|
+
});
|
|
17426
|
+
};
|
|
17427
|
+
var buildVoiceRealCallProfileEvidenceFromRuntimeProviderRoles = (input, options = {}) => buildVoiceRealCallProfileEvidenceFromRuntimeSurface(input, {
|
|
17428
|
+
defaultProfileId: options.defaultProfileId ?? "provider-role-evidence",
|
|
17429
|
+
defaultSessionPrefix: options.defaultSessionPrefix ?? "provider-role-evidence",
|
|
17430
|
+
defaultSurfaces: ["provider-path"],
|
|
17431
|
+
now: options.now
|
|
17432
|
+
});
|
|
16867
17433
|
var mergeRealCallEvidenceRuntimeOptions = (base, override = {}) => ({
|
|
17434
|
+
browserEvidence: override.browserEvidence ?? base.browserEvidence,
|
|
16868
17435
|
dedupe: override.dedupe ?? base.dedupe,
|
|
16869
17436
|
evidenceStore: override.evidenceStore ?? base.evidenceStore,
|
|
16870
17437
|
existingEvidenceLimit: override.existingEvidenceLimit ?? base.existingEvidenceLimit,
|
|
@@ -16873,6 +17440,8 @@ var mergeRealCallEvidenceRuntimeOptions = (base, override = {}) => ({
|
|
|
16873
17440
|
...override.history ?? {}
|
|
16874
17441
|
},
|
|
16875
17442
|
now: override.now ?? base.now,
|
|
17443
|
+
phoneEvidence: override.phoneEvidence ?? base.phoneEvidence,
|
|
17444
|
+
providerRoleEvidence: override.providerRoleEvidence ?? base.providerRoleEvidence,
|
|
16876
17445
|
reconnectEvidence: {
|
|
16877
17446
|
...base.reconnectEvidence ?? {},
|
|
16878
17447
|
...override.reconnectEvidence ?? {}
|
|
@@ -16921,6 +17490,30 @@ var buildRealCallEvidenceRuntimeReport = async (options, input = {}) => {
|
|
|
16921
17490
|
};
|
|
16922
17491
|
var collectVoiceRealCallEvidenceRuntimeEvidence = async (options) => {
|
|
16923
17492
|
const evidence = [];
|
|
17493
|
+
const browserEvidence = await resolveRealCallEvidenceRuntimeSurfaceEvidence(options.browserEvidence, options);
|
|
17494
|
+
if (browserEvidence.length > 0) {
|
|
17495
|
+
evidence.push(...buildVoiceRealCallProfileEvidenceFromRuntimeSurface(browserEvidence, {
|
|
17496
|
+
defaultProfileId: "browser-call",
|
|
17497
|
+
defaultSessionPrefix: "browser-call",
|
|
17498
|
+
defaultSurfaces: ["browser"],
|
|
17499
|
+
now: options.now
|
|
17500
|
+
}));
|
|
17501
|
+
}
|
|
17502
|
+
const phoneEvidence = await resolveRealCallEvidenceRuntimeSurfaceEvidence(options.phoneEvidence, options);
|
|
17503
|
+
if (phoneEvidence.length > 0) {
|
|
17504
|
+
evidence.push(...buildVoiceRealCallProfileEvidenceFromRuntimeSurface(phoneEvidence, {
|
|
17505
|
+
defaultProfileId: "phone-agent",
|
|
17506
|
+
defaultSessionPrefix: "phone-agent",
|
|
17507
|
+
defaultSurfaces: ["phone", "telephony"],
|
|
17508
|
+
now: options.now
|
|
17509
|
+
}));
|
|
17510
|
+
}
|
|
17511
|
+
const providerRoleEvidence = await resolveRealCallEvidenceRuntimeSurfaceEvidence(options.providerRoleEvidence, options);
|
|
17512
|
+
if (providerRoleEvidence.length > 0) {
|
|
17513
|
+
evidence.push(...buildVoiceRealCallProfileEvidenceFromRuntimeProviderRoles(providerRoleEvidence, {
|
|
17514
|
+
now: options.now
|
|
17515
|
+
}));
|
|
17516
|
+
}
|
|
16924
17517
|
if (options.traceStore) {
|
|
16925
17518
|
evidence.push(...buildVoiceRealCallProfileEvidenceFromTraceEvents(await options.traceStore.list({
|
|
16926
17519
|
limit: options.traceEvidence?.limit ?? 5000
|
|
@@ -21756,8 +22349,8 @@ var createVoiceLiveOpsRoutes = (options = {}) => {
|
|
|
21756
22349
|
};
|
|
21757
22350
|
// src/deliveryRuntime.ts
|
|
21758
22351
|
import { Elysia as Elysia32 } from "elysia";
|
|
21759
|
-
import { mkdir } from "fs/promises";
|
|
21760
|
-
import { dirname, join } from "path";
|
|
22352
|
+
import { mkdir as mkdir2 } from "fs/promises";
|
|
22353
|
+
import { dirname, join as join2 } from "path";
|
|
21761
22354
|
var escapeHtml32 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21762
22355
|
var renderSummaryCard = (label, summary) => {
|
|
21763
22356
|
if (!summary) {
|
|
@@ -21782,8 +22375,8 @@ var createDeliveryRuntimeFileSink = (input) => ({
|
|
|
21782
22375
|
deliver: async ({ events }) => {
|
|
21783
22376
|
const firstEvent = events[0];
|
|
21784
22377
|
const fileName = `${Date.now()}-${encodeURIComponent(firstEvent?.id ?? crypto.randomUUID())}.json`;
|
|
21785
|
-
const path =
|
|
21786
|
-
await
|
|
22378
|
+
const path = join2(input.directory, input.kind, fileName);
|
|
22379
|
+
await mkdir2(dirname(path), { recursive: true });
|
|
21787
22380
|
await Bun.write(path, JSON.stringify({
|
|
21788
22381
|
eventCount: events.length,
|
|
21789
22382
|
events,
|
|
@@ -22045,10 +22638,10 @@ var createVoiceDeliveryRuntimeRoutes = (options) => {
|
|
|
22045
22638
|
return routes;
|
|
22046
22639
|
};
|
|
22047
22640
|
// src/operationalStatus.ts
|
|
22048
|
-
import { Elysia as
|
|
22641
|
+
import { Elysia as Elysia47 } from "elysia";
|
|
22049
22642
|
|
|
22050
22643
|
// src/productionReadiness.ts
|
|
22051
|
-
import { Elysia as
|
|
22644
|
+
import { Elysia as Elysia46 } from "elysia";
|
|
22052
22645
|
|
|
22053
22646
|
// src/handoffHealth.ts
|
|
22054
22647
|
import { Elysia as Elysia33 } from "elysia";
|
|
@@ -26620,8 +27213,336 @@ var createVoiceProviderSloRoutes = (options) => {
|
|
|
26620
27213
|
return app;
|
|
26621
27214
|
};
|
|
26622
27215
|
|
|
26623
|
-
// src/
|
|
27216
|
+
// src/sessionObservability.ts
|
|
26624
27217
|
import { Elysia as Elysia42 } from "elysia";
|
|
27218
|
+
var escapeHtml40 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
27219
|
+
var formatMs4 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
27220
|
+
var resolveHref = (href, sessionId) => {
|
|
27221
|
+
if (href === false) {
|
|
27222
|
+
return;
|
|
27223
|
+
}
|
|
27224
|
+
if (typeof href === "function") {
|
|
27225
|
+
return href(sessionId);
|
|
27226
|
+
}
|
|
27227
|
+
if (typeof href === "string") {
|
|
27228
|
+
return href.includes(":sessionId") ? href.replaceAll(":sessionId", encodeURIComponent(sessionId)) : `${href.replace(/\/$/, "")}/${encodeURIComponent(sessionId)}`;
|
|
27229
|
+
}
|
|
27230
|
+
return;
|
|
27231
|
+
};
|
|
27232
|
+
var buildLinks = (options) => {
|
|
27233
|
+
const links = [];
|
|
27234
|
+
const add = (rel, label, href) => {
|
|
27235
|
+
if (href) {
|
|
27236
|
+
links.push({ href, label, rel });
|
|
27237
|
+
}
|
|
27238
|
+
};
|
|
27239
|
+
add("operations-record", "Open operations record", resolveHref(options.operationsRecordHref, options.sessionId));
|
|
27240
|
+
add("trace-timeline", "Open trace timeline", resolveHref(options.traceTimelineHref, options.sessionId));
|
|
27241
|
+
add("call-debugger", "Open call debugger", resolveHref(options.callDebuggerHref, options.sessionId));
|
|
27242
|
+
add("incident-markdown", "Download incident Markdown", resolveHref(options.incidentMarkdownHref, options.sessionId));
|
|
27243
|
+
return [...links, ...options.customLinks ?? []];
|
|
27244
|
+
};
|
|
27245
|
+
var buildTurnWaterfalls = (record) => {
|
|
27246
|
+
const byTurn = new Map;
|
|
27247
|
+
const getTurn = (turnId) => {
|
|
27248
|
+
const existing = byTurn.get(turnId);
|
|
27249
|
+
if (existing) {
|
|
27250
|
+
return existing;
|
|
27251
|
+
}
|
|
27252
|
+
const turn = {
|
|
27253
|
+
assistantReplies: 0,
|
|
27254
|
+
errors: 0,
|
|
27255
|
+
providerDecisions: 0,
|
|
27256
|
+
stages: [],
|
|
27257
|
+
toolCalls: 0,
|
|
27258
|
+
transcripts: 0
|
|
27259
|
+
};
|
|
27260
|
+
byTurn.set(turnId, turn);
|
|
27261
|
+
return turn;
|
|
27262
|
+
};
|
|
27263
|
+
for (const event of record.timeline) {
|
|
27264
|
+
if (!event.turnId) {
|
|
27265
|
+
continue;
|
|
27266
|
+
}
|
|
27267
|
+
const turn = getTurn(event.turnId);
|
|
27268
|
+
turn.stages.push({
|
|
27269
|
+
at: event.at,
|
|
27270
|
+
elapsedMs: event.elapsedMs,
|
|
27271
|
+
label: event.label,
|
|
27272
|
+
offsetMs: event.offsetMs,
|
|
27273
|
+
provider: event.provider,
|
|
27274
|
+
status: event.status,
|
|
27275
|
+
type: event.type
|
|
27276
|
+
});
|
|
27277
|
+
if (event.type === "turn.transcript") {
|
|
27278
|
+
turn.transcripts += 1;
|
|
27279
|
+
}
|
|
27280
|
+
if (event.type === "turn.assistant") {
|
|
27281
|
+
turn.assistantReplies += 1;
|
|
27282
|
+
}
|
|
27283
|
+
if (event.type === "agent.tool") {
|
|
27284
|
+
turn.toolCalls += 1;
|
|
27285
|
+
}
|
|
27286
|
+
if (event.type === "provider.decision") {
|
|
27287
|
+
turn.providerDecisions += 1;
|
|
27288
|
+
}
|
|
27289
|
+
if (event.type === "session.error" || event.status === "error") {
|
|
27290
|
+
turn.errors += 1;
|
|
27291
|
+
}
|
|
27292
|
+
}
|
|
27293
|
+
for (const transcript of record.transcript) {
|
|
27294
|
+
const turn = getTurn(transcript.id);
|
|
27295
|
+
turn.assistantReplies = Math.max(turn.assistantReplies, transcript.assistantReplies.length);
|
|
27296
|
+
turn.errors += transcript.errors.length;
|
|
27297
|
+
turn.transcripts = Math.max(turn.transcripts, transcript.transcripts.length);
|
|
27298
|
+
}
|
|
27299
|
+
return [...byTurn.entries()].map(([turnId, turn]) => {
|
|
27300
|
+
const startedAt = turn.stages[0]?.at;
|
|
27301
|
+
const endedAt = turn.stages.at(-1)?.at;
|
|
27302
|
+
return {
|
|
27303
|
+
assistantReplies: turn.assistantReplies,
|
|
27304
|
+
durationMs: startedAt !== undefined && endedAt !== undefined ? Math.max(0, endedAt - startedAt) : undefined,
|
|
27305
|
+
endedAt,
|
|
27306
|
+
errors: turn.errors,
|
|
27307
|
+
providerDecisions: turn.providerDecisions,
|
|
27308
|
+
stages: turn.stages,
|
|
27309
|
+
startedAt,
|
|
27310
|
+
toolCalls: turn.toolCalls,
|
|
27311
|
+
transcripts: turn.transcripts,
|
|
27312
|
+
turnId
|
|
27313
|
+
};
|
|
27314
|
+
}).sort((left, right) => (left.startedAt ?? 0) - (right.startedAt ?? 0));
|
|
27315
|
+
};
|
|
27316
|
+
var buildVoiceSessionObservabilityReport = async (options) => {
|
|
27317
|
+
const record = await buildVoiceOperationsRecord({
|
|
27318
|
+
audit: options.audit,
|
|
27319
|
+
evaluation: options.evaluation,
|
|
27320
|
+
events: options.events,
|
|
27321
|
+
integrationEvents: options.integrationEvents,
|
|
27322
|
+
redact: options.redact,
|
|
27323
|
+
reviews: options.reviews,
|
|
27324
|
+
sessionId: options.sessionId,
|
|
27325
|
+
store: options.store,
|
|
27326
|
+
tasks: options.tasks
|
|
27327
|
+
});
|
|
27328
|
+
const failureReplay = buildVoiceFailureReplay(record, {
|
|
27329
|
+
operationsRecordHref: resolveHref(options.operationsRecordHref, options.sessionId)
|
|
27330
|
+
});
|
|
27331
|
+
const incidentMarkdown = renderVoiceOperationsRecordIncidentMarkdown(record);
|
|
27332
|
+
const statuses = [
|
|
27333
|
+
record.status,
|
|
27334
|
+
failureReplay.status === "failed" ? "failed" : failureReplay.status === "degraded" ? "warning" : "healthy"
|
|
27335
|
+
];
|
|
27336
|
+
const status = statuses.includes("failed") ? "failed" : statuses.includes("warning") ? "warning" : "healthy";
|
|
27337
|
+
return {
|
|
27338
|
+
checkedAt: Date.now(),
|
|
27339
|
+
failureReplay,
|
|
27340
|
+
incidentMarkdown,
|
|
27341
|
+
links: buildLinks(options),
|
|
27342
|
+
record,
|
|
27343
|
+
sessionId: options.sessionId,
|
|
27344
|
+
status,
|
|
27345
|
+
summary: {
|
|
27346
|
+
durationMs: record.summary.callDurationMs,
|
|
27347
|
+
errors: record.summary.errorCount,
|
|
27348
|
+
events: record.summary.eventCount,
|
|
27349
|
+
fallbacks: record.providerDecisionSummary.fallbacks,
|
|
27350
|
+
guardrailBlocks: record.guardrails.blocked,
|
|
27351
|
+
handoffs: record.handoffs.length,
|
|
27352
|
+
providerRecoveryStatus: record.providerDecisionSummary.recoveryStatus,
|
|
27353
|
+
providers: record.providerDecisionSummary.providers,
|
|
27354
|
+
telephonyMediaEvents: record.telephonyMedia.total,
|
|
27355
|
+
toolCalls: record.tools.length,
|
|
27356
|
+
turns: record.summary.turnCount
|
|
27357
|
+
},
|
|
27358
|
+
turns: buildTurnWaterfalls(record)
|
|
27359
|
+
};
|
|
27360
|
+
};
|
|
27361
|
+
var renderLinks = (links) => links.length === 0 ? "" : `<div class="actions">${links.map((link) => `<a href="${escapeHtml40(link.href)}">${escapeHtml40(link.label)}</a>`).join("")}</div>`;
|
|
27362
|
+
var renderTurns = (turns) => turns.length === 0 ? '<p class="muted">No turn-level events recorded yet.</p>' : turns.map((turn) => `<article class="turn"><header><strong>${escapeHtml40(turn.turnId)}</strong><span>${formatMs4(turn.durationMs)}</span></header><dl><div><dt>Transcripts</dt><dd>${String(turn.transcripts)}</dd></div><div><dt>Assistant</dt><dd>${String(turn.assistantReplies)}</dd></div><div><dt>Tools</dt><dd>${String(turn.toolCalls)}</dd></div><div><dt>Providers</dt><dd>${String(turn.providerDecisions)}</dd></div><div><dt>Errors</dt><dd>${String(turn.errors)}</dd></div></dl><table><thead><tr><th>Offset</th><th>Type</th><th>Stage</th><th>Provider</th><th>Status</th><th>Latency</th></tr></thead><tbody>${turn.stages.map((stage) => `<tr><td>+${String(stage.offsetMs)}ms</td><td>${escapeHtml40(stage.type)}</td><td>${escapeHtml40(stage.label)}</td><td>${escapeHtml40(stage.provider ?? "")}</td><td>${escapeHtml40(stage.status ?? "")}</td><td>${formatMs4(stage.elapsedMs)}</td></tr>`).join("")}</tbody></table></article>`).join("");
|
|
27363
|
+
var renderVoiceSessionObservabilityMarkdown = (report) => `# Voice session observability: ${report.sessionId}
|
|
27364
|
+
|
|
27365
|
+
Status: ${report.status}
|
|
27366
|
+
|
|
27367
|
+
- Events: ${report.summary.events}
|
|
27368
|
+
- Turns: ${report.summary.turns}
|
|
27369
|
+
- Errors: ${report.summary.errors}
|
|
27370
|
+
- Duration: ${formatMs4(report.summary.durationMs)}
|
|
27371
|
+
- Providers: ${report.summary.providers.join(", ") || "none"}
|
|
27372
|
+
- Provider recovery: ${report.summary.providerRecoveryStatus}
|
|
27373
|
+
- Fallbacks: ${report.summary.fallbacks}
|
|
27374
|
+
- Tool calls: ${report.summary.toolCalls}
|
|
27375
|
+
- Handoffs: ${report.summary.handoffs}
|
|
27376
|
+
- Guardrail blocks: ${report.summary.guardrailBlocks}
|
|
27377
|
+
- Telephony media events: ${report.summary.telephonyMediaEvents}
|
|
27378
|
+
|
|
27379
|
+
## Links
|
|
27380
|
+
|
|
27381
|
+
${report.links.length ? report.links.map((link) => `- [${link.label}](${link.href})`).join(`
|
|
27382
|
+
`) : "- none"}
|
|
27383
|
+
|
|
27384
|
+
## Turn Waterfalls
|
|
27385
|
+
|
|
27386
|
+
${report.turns.length ? report.turns.map((turn) => `### ${turn.turnId}
|
|
27387
|
+
|
|
27388
|
+
- Duration: ${formatMs4(turn.durationMs)}
|
|
27389
|
+
- Transcripts: ${turn.transcripts}
|
|
27390
|
+
- Assistant replies: ${turn.assistantReplies}
|
|
27391
|
+
- Tool calls: ${turn.toolCalls}
|
|
27392
|
+
- Provider decisions: ${turn.providerDecisions}
|
|
27393
|
+
- Errors: ${turn.errors}
|
|
27394
|
+
|
|
27395
|
+
${turn.stages.map((stage) => `- +${stage.offsetMs}ms ${stage.type}: ${stage.label}${stage.provider ? ` (${stage.provider})` : ""}${stage.status ? ` [${stage.status}]` : ""}`).join(`
|
|
27396
|
+
`)}`).join(`
|
|
27397
|
+
|
|
27398
|
+
`) : "No turn-level events recorded."}
|
|
27399
|
+
|
|
27400
|
+
## Incident Handoff
|
|
27401
|
+
|
|
27402
|
+
${report.incidentMarkdown}`;
|
|
27403
|
+
var renderVoiceSessionObservabilityHTML = (report, options = {}) => `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml40(options.title ?? "Voice Session Observability")}</title><style>body{background:#0d1412;color:#f7f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #425046;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.actions{display:flex;flex-wrap:wrap;gap:10px;margin:18px 0}.actions a{background:#fbbf24;border-radius:999px;color:#111827;font-weight:900;padding:10px 14px;text-decoration:none}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin:22px 0}.card,.turn,.incident{background:#17201c;border:1px solid #2e3c35;border-radius:20px;padding:16px}.card span,.muted,dt{color:#a8b4ad}.card strong{display:block;font-size:2rem}section{margin-top:30px}.turn{margin:16px 0}.turn header{align-items:center;display:flex;justify-content:space-between;gap:14px}dl{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));margin:14px 0}dd{font-weight:900;margin:3px 0 0}table{border-collapse:collapse;margin-top:14px;width:100%}td,th{border-top:1px solid #2e3c35;padding:10px;text-align:left}pre{background:#08100d;border:1px solid #2e3c35;border-radius:16px;color:#d9f99d;overflow:auto;padding:14px}@media(max-width:760px){main{padding:20px}table{font-size:.9rem}}</style></head><body><main><header><p class="eyebrow">Session observability</p><h1>${escapeHtml40(report.sessionId)}</h1><p class="status ${escapeHtml40(report.status)}">${escapeHtml40(report.status)}</p>${renderLinks(report.links)}<p class="muted">One support/debug report across trace timeline, operations record, provider recovery, turn waterfalls, guardrails, tools, handoffs, failure replay, and incident handoff.</p></header><section class="grid"><article class="card"><span>Events</span><strong>${String(report.summary.events)}</strong></article><article class="card"><span>Turns</span><strong>${String(report.summary.turns)}</strong></article><article class="card"><span>Errors</span><strong>${String(report.summary.errors)}</strong></article><article class="card"><span>Duration</span><strong>${formatMs4(report.summary.durationMs)}</strong></article><article class="card"><span>Fallbacks</span><strong>${String(report.summary.fallbacks)}</strong></article><article class="card"><span>Tools</span><strong>${String(report.summary.toolCalls)}</strong></article><article class="card"><span>Handoffs</span><strong>${String(report.summary.handoffs)}</strong></article><article class="card"><span>Guardrails blocked</span><strong>${String(report.summary.guardrailBlocks)}</strong></article><article class="card"><span>Telephony media</span><strong>${String(report.summary.telephonyMediaEvents)}</strong></article></section><section><h2>Turn Waterfalls</h2>${renderTurns(report.turns)}</section><section class="incident"><h2>Incident Handoff</h2><pre><code>${escapeHtml40(report.incidentMarkdown)}</code></pre></section></main></body></html>`;
|
|
27404
|
+
var routeSessionId = (params) => typeof params.sessionId === "string" ? params.sessionId : "";
|
|
27405
|
+
var linkByRel = (links) => {
|
|
27406
|
+
const map = new Map;
|
|
27407
|
+
for (const link of links) {
|
|
27408
|
+
map.set(link.rel, true);
|
|
27409
|
+
}
|
|
27410
|
+
return (rel) => map.get(rel) ?? false;
|
|
27411
|
+
};
|
|
27412
|
+
var evaluateVoiceSessionObservabilityEvidence = (report, input = {}) => {
|
|
27413
|
+
const issues = [];
|
|
27414
|
+
const has = linkByRel(report.links);
|
|
27415
|
+
const status = report.status === "healthy" ? "pass" : report.status === "warning" ? "warn" : "fail";
|
|
27416
|
+
const turnsWithWaterfalls = report.turns.filter((turn) => turn.stages.length > 0).length;
|
|
27417
|
+
const stages = report.turns.reduce((total, turn) => total + turn.stages.length, 0);
|
|
27418
|
+
const providerDecisions = report.turns.reduce((total, turn) => total + turn.providerDecisions, 0);
|
|
27419
|
+
const toolCalls = report.turns.reduce((total, turn) => total + turn.toolCalls, 0);
|
|
27420
|
+
if (input.requireHealthy && report.status !== "healthy") {
|
|
27421
|
+
issues.push("Expected a healthy session observability report.");
|
|
27422
|
+
}
|
|
27423
|
+
if (input.minTurns !== undefined && report.summary.turns < input.minTurns) {
|
|
27424
|
+
issues.push(`Expected at least ${String(input.minTurns)} turn(s), found ${String(report.summary.turns)}.`);
|
|
27425
|
+
}
|
|
27426
|
+
if (input.minTurnWaterfalls !== undefined && turnsWithWaterfalls < input.minTurnWaterfalls) {
|
|
27427
|
+
issues.push(`Expected at least ${String(input.minTurnWaterfalls)} turn(s) with stage data, found ${String(turnsWithWaterfalls)}.`);
|
|
27428
|
+
}
|
|
27429
|
+
if (input.minStageCount !== undefined && stages < input.minStageCount) {
|
|
27430
|
+
issues.push(`Expected at least ${String(input.minStageCount)} turn stages, found ${String(stages)}.`);
|
|
27431
|
+
}
|
|
27432
|
+
if (input.minProviderDecisions !== undefined && providerDecisions < input.minProviderDecisions) {
|
|
27433
|
+
issues.push(`Expected at least ${String(input.minProviderDecisions)} provider decision stage(s), found ${String(providerDecisions)}.`);
|
|
27434
|
+
}
|
|
27435
|
+
if (input.minToolCalls !== undefined && toolCalls < input.minToolCalls) {
|
|
27436
|
+
issues.push(`Expected at least ${String(input.minToolCalls)} tool call stage(s), found ${String(toolCalls)}.`);
|
|
27437
|
+
}
|
|
27438
|
+
if (input.maxErrors !== undefined && report.summary.errors > input.maxErrors) {
|
|
27439
|
+
issues.push(`Expected at most ${String(input.maxErrors)} error(s), found ${String(report.summary.errors)}.`);
|
|
27440
|
+
}
|
|
27441
|
+
if (input.maxFallbacks !== undefined && report.summary.fallbacks > input.maxFallbacks) {
|
|
27442
|
+
issues.push(`Expected at most ${String(input.maxFallbacks)} fallback(s), found ${String(report.summary.fallbacks)}.`);
|
|
27443
|
+
}
|
|
27444
|
+
if (input.requireOperationsRecordLink && !has("operations-record")) {
|
|
27445
|
+
issues.push("Expected operations-record link in session observability evidence.");
|
|
27446
|
+
}
|
|
27447
|
+
if (input.requireTraceTimelineLink && !has("trace-timeline")) {
|
|
27448
|
+
issues.push("Expected trace-timeline link in session observability evidence.");
|
|
27449
|
+
}
|
|
27450
|
+
if (input.requireCallDebuggerLink && !has("call-debugger")) {
|
|
27451
|
+
issues.push("Expected call-debugger link in session observability evidence.");
|
|
27452
|
+
}
|
|
27453
|
+
if (input.requireIncidentMarkdownLink && !has("incident-markdown")) {
|
|
27454
|
+
issues.push("Expected incident-markdown link in session observability evidence.");
|
|
27455
|
+
}
|
|
27456
|
+
if (input.requireIncidentMarkdown && (!report.incidentMarkdown || report.incidentMarkdown.trim().length < 24 || !report.incidentMarkdown.toLowerCase().includes("incident handoff"))) {
|
|
27457
|
+
issues.push("Expected readable incident markdown payload.");
|
|
27458
|
+
}
|
|
27459
|
+
if (input.requireProviderRecoveryStatus !== undefined && (Array.isArray(input.requireProviderRecoveryStatus) ? input.requireProviderRecoveryStatus : [input.requireProviderRecoveryStatus]).indexOf(report.summary.providerRecoveryStatus) === -1) {
|
|
27460
|
+
issues.push(`Expected provider recovery status ${String(input.requireProviderRecoveryStatus)}, found ${report.summary.providerRecoveryStatus}.`);
|
|
27461
|
+
}
|
|
27462
|
+
return {
|
|
27463
|
+
checkedAt: Date.now(),
|
|
27464
|
+
issues,
|
|
27465
|
+
ok: issues.length === 0,
|
|
27466
|
+
status: issues.length > 0 ? status === "fail" ? "fail" : "warn" : status,
|
|
27467
|
+
summary: {
|
|
27468
|
+
incidentMarkdownLength: report.incidentMarkdown.length,
|
|
27469
|
+
providerDecisions,
|
|
27470
|
+
providerRecoveryStatus: report.summary.providerRecoveryStatus,
|
|
27471
|
+
reportStatus: report.status,
|
|
27472
|
+
stages,
|
|
27473
|
+
turns: report.turns.length,
|
|
27474
|
+
turnsWithWaterfalls,
|
|
27475
|
+
toolCalls,
|
|
27476
|
+
links: {
|
|
27477
|
+
callDebugger: has("call-debugger"),
|
|
27478
|
+
incidentMarkdown: has("incident-markdown"),
|
|
27479
|
+
operationsRecord: has("operations-record"),
|
|
27480
|
+
traceTimeline: has("trace-timeline")
|
|
27481
|
+
}
|
|
27482
|
+
}
|
|
27483
|
+
};
|
|
27484
|
+
};
|
|
27485
|
+
var assertVoiceSessionObservabilityEvidence = (report, input = {}) => {
|
|
27486
|
+
const assertion = evaluateVoiceSessionObservabilityEvidence(report, input);
|
|
27487
|
+
if (!assertion.ok) {
|
|
27488
|
+
throw new Error(`Voice session observability evidence assertion failed: ${assertion.issues.join(" ")}`);
|
|
27489
|
+
}
|
|
27490
|
+
return assertion;
|
|
27491
|
+
};
|
|
27492
|
+
var createVoiceSessionObservabilityRoutes = (options) => {
|
|
27493
|
+
const path = options.path ?? "/api/voice/session-observability/:sessionId";
|
|
27494
|
+
const htmlPath = options.htmlPath ?? "/voice/session-observability/:sessionId";
|
|
27495
|
+
const incidentPath = options.incidentPath ?? "/api/voice/session-observability/:sessionId/incident.md";
|
|
27496
|
+
const title = options.title ?? "AbsoluteJS Voice Session Observability";
|
|
27497
|
+
const routes = new Elysia42({
|
|
27498
|
+
name: options.name ?? "absolutejs-voice-session-observability"
|
|
27499
|
+
});
|
|
27500
|
+
const build = (sessionId) => buildVoiceSessionObservabilityReport({
|
|
27501
|
+
audit: options.audit,
|
|
27502
|
+
callDebuggerHref: options.callDebuggerHref,
|
|
27503
|
+
customLinks: options.customLinks,
|
|
27504
|
+
evaluation: options.evaluation,
|
|
27505
|
+
events: options.events,
|
|
27506
|
+
incidentMarkdownHref: options.incidentMarkdownHref ?? (incidentPath === false ? false : incidentPath),
|
|
27507
|
+
integrationEvents: options.integrationEvents,
|
|
27508
|
+
operationsRecordHref: options.operationsRecordHref,
|
|
27509
|
+
redact: options.redact,
|
|
27510
|
+
reviews: options.reviews,
|
|
27511
|
+
sessionId,
|
|
27512
|
+
store: options.store,
|
|
27513
|
+
tasks: options.tasks,
|
|
27514
|
+
traceTimelineHref: options.traceTimelineHref
|
|
27515
|
+
});
|
|
27516
|
+
routes.get(path, async ({ params }) => Response.json(await build(routeSessionId(params))));
|
|
27517
|
+
if (htmlPath !== false) {
|
|
27518
|
+
routes.get(htmlPath, async ({ params }) => {
|
|
27519
|
+
const report = await build(routeSessionId(params));
|
|
27520
|
+
const body = await (options.render ?? ((input) => renderVoiceSessionObservabilityHTML(input, { title })))(report);
|
|
27521
|
+
return new Response(body, {
|
|
27522
|
+
headers: {
|
|
27523
|
+
"content-type": "text/html; charset=utf-8",
|
|
27524
|
+
...options.headers
|
|
27525
|
+
}
|
|
27526
|
+
});
|
|
27527
|
+
});
|
|
27528
|
+
}
|
|
27529
|
+
if (incidentPath !== false) {
|
|
27530
|
+
routes.get(incidentPath, async ({ params }) => {
|
|
27531
|
+
const report = await build(routeSessionId(params));
|
|
27532
|
+
const body = await (options.renderIncidentMarkdown ?? renderVoiceSessionObservabilityMarkdown)(report);
|
|
27533
|
+
return new Response(body, {
|
|
27534
|
+
headers: {
|
|
27535
|
+
"content-type": "text/markdown; charset=utf-8",
|
|
27536
|
+
...options.headers
|
|
27537
|
+
}
|
|
27538
|
+
});
|
|
27539
|
+
});
|
|
27540
|
+
}
|
|
27541
|
+
return routes;
|
|
27542
|
+
};
|
|
27543
|
+
|
|
27544
|
+
// src/opsRecovery.ts
|
|
27545
|
+
import { Elysia as Elysia43 } from "elysia";
|
|
26625
27546
|
|
|
26626
27547
|
// src/latencySlo.ts
|
|
26627
27548
|
var DEFAULT_WARN_AFTER_MS = 1800;
|
|
@@ -26927,7 +27848,7 @@ None.
|
|
|
26927
27848
|
};
|
|
26928
27849
|
|
|
26929
27850
|
// src/opsRecovery.ts
|
|
26930
|
-
var
|
|
27851
|
+
var escapeHtml41 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
26931
27852
|
var getString15 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
26932
27853
|
var hrefForSession = (value, sessionId) => {
|
|
26933
27854
|
if (typeof value === "function") {
|
|
@@ -27141,19 +28062,19 @@ ${failedSessions || "None."}
|
|
|
27141
28062
|
${report.latency ? renderVoiceLatencySLOMarkdown(report.latency, { title: "Latency SLO" }) : "Latency SLO disabled."}
|
|
27142
28063
|
`;
|
|
27143
28064
|
};
|
|
27144
|
-
var renderDeliverySummary = (label, summary) => summary ? `<article><span>${
|
|
28065
|
+
var renderDeliverySummary = (label, summary) => summary ? `<article><span>${escapeHtml41(label)}</span><strong>${String(summary.failed + summary.deadLettered)} failed</strong><small>${String(summary.pending)} pending \xB7 ${String(summary.retryEligible)} retry eligible \xB7 ${String(summary.total)} total</small></article>` : `<article><span>${escapeHtml41(label)}</span><strong>not configured</strong></article>`;
|
|
27145
28066
|
var renderVoiceOpsRecoveryHTML = (report, options = {}) => {
|
|
27146
28067
|
const title = options.title ?? "Voice Ops Recovery";
|
|
27147
|
-
const issues = report.issues.map((issue) => `<tr><td>${
|
|
27148
|
-
const providers = report.providers.providers.map((provider) => `<tr><td>${
|
|
27149
|
-
const failedSessions = report.failedSessions.map((session) => `<li>${session.operationsRecordHref ? `<a href="${
|
|
27150
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
28068
|
+
const issues = report.issues.map((issue) => `<tr><td>${escapeHtml41(issue.severity)}</td><td><code>${escapeHtml41(issue.code)}</code></td><td>${issue.href ? `<a href="${escapeHtml41(issue.href)}">${escapeHtml41(issue.label)}</a>` : escapeHtml41(issue.label)}</td><td>${escapeHtml41(String(issue.value ?? ""))}</td><td>${escapeHtml41(issue.detail ?? "")}</td></tr>`).join("");
|
|
28069
|
+
const providers = report.providers.providers.map((provider) => `<tr><td>${escapeHtml41(provider.provider)}</td><td>${escapeHtml41(provider.status)}</td><td>${String(provider.runCount)}</td><td>${String(provider.errorCount)}</td><td>${String(provider.fallbackCount)}</td><td>${escapeHtml41(provider.lastError ?? "")}</td></tr>`).join("");
|
|
28070
|
+
const failedSessions = report.failedSessions.map((session) => `<li>${session.operationsRecordHref ? `<a href="${escapeHtml41(session.operationsRecordHref)}">${escapeHtml41(session.sessionId)}</a>` : escapeHtml41(session.sessionId)}${session.provider ? ` via ${escapeHtml41(session.provider)}` : ""}${session.error ? `: ${escapeHtml41(session.error)}` : ""}</li>`).join("");
|
|
28071
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#f8fafc;color:#172033;margin:2rem;line-height:1.45}main{max-width:1180px;margin:auto}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:.75rem;margin:1rem 0}article{background:white;border:1px solid #dbe3ef;border-radius:14px;padding:1rem;box-shadow:0 10px 28px rgba(15,23,42,.05)}article span{display:block;color:#64748b;font-size:.85rem}article strong{display:block;font-size:1.5rem;margin:.2rem 0}article small{color:#64748b}table{border-collapse:collapse;width:100%;background:white;border:1px solid #dbe3ef;border-radius:14px;overflow:hidden}th,td{border-bottom:1px solid #e2e8f0;padding:.7rem;text-align:left;vertical-align:top}code{font-size:.85em}.status{display:inline-flex;border-radius:999px;padding:.35rem .7rem;background:${report.status === "fail" ? "#fee2e2" : report.status === "warn" ? "#fef3c7" : "#dcfce7"};color:${report.status === "fail" ? "#991b1b" : report.status === "warn" ? "#92400e" : "#166534"};font-weight:700}</style></head><body><main><h1>${escapeHtml41(title)}</h1><p><span class="status">${escapeHtml41(report.status)}</span> Checked ${escapeHtml41(new Date(report.checkedAt).toLocaleString())}</p><section class="grid"><article><span>Recovered fallbacks</span><strong>${String(report.providers.recoveredFallbacks)}</strong></article><article><span>Unresolved providers</span><strong>${String(report.providers.unresolvedFailures)}</strong></article><article><span>Operator interventions</span><strong>${String(report.interventions.total)}</strong></article><article><span>Latency status</span><strong>${escapeHtml41(report.latency?.status ?? "disabled")}</strong></article>${renderDeliverySummary("Audit delivery", report.auditDeliveries)}${renderDeliverySummary("Trace delivery", report.traceDeliveries)}${renderDeliverySummary("Handoff delivery", report.handoffDeliveries)}</section><h2>Issues</h2><table><thead><tr><th>Severity</th><th>Code</th><th>Label</th><th>Value</th><th>Detail</th></tr></thead><tbody>${issues || '<tr><td colspan="5">No recovery issues.</td></tr>'}</tbody></table><h2>Providers</h2><table><thead><tr><th>Provider</th><th>Status</th><th>Runs</th><th>Errors</th><th>Fallbacks</th><th>Last error</th></tr></thead><tbody>${providers || '<tr><td colspan="6">No provider activity.</td></tr>'}</tbody></table><h2>Failed Sessions</h2><ul>${failedSessions || "<li>None.</li>"}</ul></main></body></html>`;
|
|
27151
28072
|
};
|
|
27152
28073
|
var createVoiceOpsRecoveryRoutes = (options = {}) => {
|
|
27153
28074
|
const path = options.path ?? "/api/voice/ops-recovery";
|
|
27154
28075
|
const htmlPath = options.htmlPath === undefined ? "/ops-recovery" : options.htmlPath;
|
|
27155
28076
|
const markdownPath = options.markdownPath === undefined ? `${path}.md` : options.markdownPath;
|
|
27156
|
-
const routes = new
|
|
28077
|
+
const routes = new Elysia43({
|
|
27157
28078
|
name: options.name ?? "absolutejs-voice-ops-recovery"
|
|
27158
28079
|
}).get(path, async () => buildVoiceOpsRecoveryReport(options));
|
|
27159
28080
|
if (htmlPath) {
|
|
@@ -27183,8 +28104,8 @@ var createVoiceOpsRecoveryRoutes = (options = {}) => {
|
|
|
27183
28104
|
};
|
|
27184
28105
|
|
|
27185
28106
|
// src/incidentTimeline.ts
|
|
27186
|
-
import { Elysia as
|
|
27187
|
-
var
|
|
28107
|
+
import { Elysia as Elysia44 } from "elysia";
|
|
28108
|
+
var escapeHtml42 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
27188
28109
|
var resolveValue = async (value) => typeof value === "function" ? await value() : value;
|
|
27189
28110
|
var linkForSession = (link, sessionId) => {
|
|
27190
28111
|
if (!link || !sessionId) {
|
|
@@ -27305,8 +28226,8 @@ var buildVoiceIncidentRecoveryOutcomeReport = async (options) => {
|
|
|
27305
28226
|
};
|
|
27306
28227
|
var renderVoiceIncidentRecoveryOutcomeHTML = (report, options = {}) => {
|
|
27307
28228
|
const title = options.title ?? "AbsoluteJS Voice Incident Recovery Outcomes";
|
|
27308
|
-
const rows = report.entries.map((entry) => `<article class="${
|
|
27309
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
28229
|
+
const rows = report.entries.map((entry) => `<article class="${escapeHtml42(entry.outcome)}"><span>${escapeHtml42(entry.outcome.toUpperCase())}</span><h2>${escapeHtml42(entry.actionId)}</h2><p>${escapeHtml42(new Date(entry.at).toLocaleString())}</p><strong>${escapeHtml42(entry.beforeStatus ?? "unknown")} -> ${escapeHtml42(entry.afterStatus ?? "unknown")}</strong>${entry.detail ? `<p>${escapeHtml42(entry.detail)}</p>` : ""}</article>`).join("");
|
|
28230
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml42(title)}</title><style>body{background:#10120d;color:#fbf4df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,article{background:#181711;border:1px solid #39301d;border-radius:24px;padding:20px}.hero{margin-bottom:16px}h1{font-size:clamp(2rem,6vw,4.5rem);line-height:.95}.summary{display:flex;flex-wrap:wrap;gap:10px}.summary span{border:1px solid #4a3f23;border-radius:999px;padding:8px 12px}section{display:grid;gap:12px}article.improved{border-color:rgba(34,197,94,.65)}article.failed,article.regressed{border-color:rgba(239,68,68,.8)}article.unchanged{border-color:rgba(245,158,11,.7)}article span{color:#fcd34d;font-weight:900;letter-spacing:.08em}article strong{display:block;font-size:1.4rem;margin:.5rem 0}p{color:#cfc5a8}</style></head><body><main><section class="hero"><span>Recovery proof</span><h1>${escapeHtml42(title)}</h1><div class="summary"><span>${String(report.improved)} improved</span><span>${String(report.unchanged)} unchanged</span><span>${String(report.regressed)} regressed</span><span>${String(report.failed)} failed</span><span>${String(report.total)} total</span></div></section><section>${rows || "<p>No incident recovery actions have been recorded.</p>"}</section></main></body></html>`;
|
|
27310
28231
|
};
|
|
27311
28232
|
var buildVoiceIncidentRecoveryOutcomeReadinessCheck = (report, options = {}) => {
|
|
27312
28233
|
const failOnFailed = options.failOnFailed ?? true;
|
|
@@ -27503,8 +28424,8 @@ ${rows || "| n/a | 0 | 0 | 0 | 0 | 0 | n/a | n/a |"}
|
|
|
27503
28424
|
};
|
|
27504
28425
|
var renderVoiceIncidentRecoveryTrendHTML = (report, options = {}) => {
|
|
27505
28426
|
const title = options.title ?? "AbsoluteJS Voice Incident Recovery Trend";
|
|
27506
|
-
const rows = report.cycles.map((cycle) => `<tr><td>${
|
|
27507
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
28427
|
+
const rows = report.cycles.map((cycle) => `<tr><td>${escapeHtml42(new Date(cycle.checkedAt).toLocaleString())}</td><td>${String(cycle.total)}</td><td>${String(cycle.improved)}</td><td>${String(cycle.unchanged)}</td><td>${String(cycle.regressed)}</td><td>${String(cycle.failed)}</td><td>${escapeHtml42(percent(cycle.improvementRate))}</td><td>${escapeHtml42(percent(cycle.regressionRate))}</td></tr>`).join("");
|
|
28428
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml42(title)}</title><style>body{background:#10120d;color:#fbf4df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}.hero,table{background:#181711;border:1px solid #39301d;border-radius:24px}.hero{margin-bottom:16px;padding:24px}h1{font-size:clamp(2rem,6vw,4.5rem);line-height:.95}.summary{display:flex;flex-wrap:wrap;gap:10px}.summary span{border:1px solid #4a3f23;border-radius:999px;padding:8px 12px}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #39301d;padding:12px;text-align:left}.pass{color:#86efac}.warn,.empty{color:#fcd34d}.fail{color:#fca5a5}p{color:#cfc5a8}</style></head><body><main><section class="hero"><span>Recovery trend</span><h1>${escapeHtml42(title)}</h1><p class="${escapeHtml42(report.status)}">Status: ${escapeHtml42(report.status)}</p><div class="summary"><span>${String(report.summary.cycles)} cycles</span><span>${String(report.summary.total)} actions</span><span>${escapeHtml42(percent(report.summary.improvementRate))} improved</span><span>${escapeHtml42(percent(report.summary.regressionRate))} regressed</span><span>${escapeHtml42(percent(report.trend.improvementRateDelta))} improvement delta</span></div></section><table><thead><tr><th>Checked at</th><th>Total</th><th>Improved</th><th>Unchanged</th><th>Regressed</th><th>Failed</th><th>Improve %</th><th>Regress %</th></tr></thead><tbody>${rows || '<tr><td colspan="8">No recovery outcome history has been recorded.</td></tr>'}</tbody></table></main></body></html>`;
|
|
27508
28429
|
};
|
|
27509
28430
|
var pushOperationalStatusEvents = (events, report, links) => {
|
|
27510
28431
|
if (!report) {
|
|
@@ -27730,22 +28651,22 @@ ${report.actions.map((action) => `- ${action.method ?? "GET"} ${action.id}: ${ac
|
|
|
27730
28651
|
var renderVoiceIncidentTimelineHTML = (report, options = {}) => {
|
|
27731
28652
|
const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
|
|
27732
28653
|
const actionPath = options.actionPath ?? "/api/voice/incident-timeline/actions";
|
|
27733
|
-
const events = report.events.map((event) => `<article class="${
|
|
27734
|
-
<span>${
|
|
27735
|
-
<h2>${
|
|
27736
|
-
<p>${
|
|
27737
|
-
${event.value === undefined ? "" : `<strong>${
|
|
27738
|
-
${event.detail ? `<p>${
|
|
27739
|
-
<div>${event.href ? `<a href="${
|
|
28654
|
+
const events = report.events.map((event) => `<article class="${escapeHtml42(event.severity)}">
|
|
28655
|
+
<span>${escapeHtml42(event.severity.toUpperCase())} / ${escapeHtml42(event.category)}</span>
|
|
28656
|
+
<h2>${escapeHtml42(event.label)}</h2>
|
|
28657
|
+
<p>${escapeHtml42(new Date(event.at).toLocaleString())}${event.sessionId ? ` \xB7 session ${escapeHtml42(event.sessionId)}` : ""}</p>
|
|
28658
|
+
${event.value === undefined ? "" : `<strong>${escapeHtml42(String(event.value))}</strong>`}
|
|
28659
|
+
${event.detail ? `<p>${escapeHtml42(event.detail)}</p>` : ""}
|
|
28660
|
+
<div>${event.href ? `<a href="${escapeHtml42(event.href)}">Open source</a>` : ""}${event.action?.href ? `<a href="${escapeHtml42(event.action.href)}">${escapeHtml42(event.action.label)}</a>` : ""}</div>
|
|
27740
28661
|
</article>`).join("");
|
|
27741
28662
|
const actions = report.actions.map((action) => {
|
|
27742
|
-
const label =
|
|
27743
|
-
const detail = action.detail ? `<p>${
|
|
27744
|
-
const href = action.href ? `<a href="${
|
|
27745
|
-
const control = action.method === "POST" ? `<button type="button" data-voice-incident-action="${
|
|
27746
|
-
return `<article class="action"><span>${
|
|
28663
|
+
const label = escapeHtml42(action.label);
|
|
28664
|
+
const detail = action.detail ? `<p>${escapeHtml42(action.detail)}</p>` : "";
|
|
28665
|
+
const href = action.href ? `<a href="${escapeHtml42(action.href)}">Open target</a>` : "";
|
|
28666
|
+
const control = action.method === "POST" ? `<button type="button" data-voice-incident-action="${escapeHtml42(action.id)}" ${action.disabled ? "disabled" : ""}>${label}</button>` : href;
|
|
28667
|
+
return `<article class="action"><span>${escapeHtml42(action.method ?? "GET")}</span><h2>${label}</h2>${detail}<div>${control}${href && action.method === "POST" ? href : ""}</div></article>`;
|
|
27747
28668
|
}).join("");
|
|
27748
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
28669
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml42(title)}</title><style>body{background:#11110d;color:#faf4df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero{background:linear-gradient(135deg,rgba(248,113,113,.2),rgba(245,158,11,.13),rgba(34,197,94,.12));border:1px solid #39301d;border-radius:30px;margin-bottom:18px;padding:28px}.eyebrow{color:#fcd34d;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #575030;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.status.pass{border-color:rgba(34,197,94,.65)}.status.warn{border-color:rgba(245,158,11,.75)}.status.fail{border-color:rgba(239,68,68,.85)}.grid{display:grid;gap:14px}.actions{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:0 0 18px}.summary{display:flex;flex-wrap:wrap;gap:10px}.summary span{background:#181711;border:1px solid #39301d;border-radius:999px;padding:8px 12px}article{background:#181711;border:1px solid #39301d;border-radius:22px;padding:18px}article.critical{border-color:rgba(239,68,68,.85)}article.warn{border-color:rgba(245,158,11,.75)}article.info{border-color:rgba(34,197,94,.55)}article.action{border-color:#5b4a22}article span{color:#fcd34d;font-size:.78rem;font-weight:900;letter-spacing:.08em}article h2{margin:.35rem 0}.muted,article p{color:#cfc5a8}article strong{display:block;font-size:1.3rem;margin:.5rem 0}a{color:#fde68a;margin-right:12px}button{background:#fcd34d;border:0;border-radius:999px;color:#171307;cursor:pointer;font-weight:900;padding:10px 14px}button:disabled{cursor:not-allowed;opacity:.55}</style></head><body><main><section class="hero"><p class="eyebrow">Operational triage</p><h1>${escapeHtml42(title)}</h1><p class="status ${escapeHtml42(report.status)}">Overall: ${escapeHtml42(report.status.toUpperCase())}</p><p class="muted">Generated ${escapeHtml42(new Date(report.generatedAt).toLocaleString())}</p><div class="summary"><span>${String(report.summary.critical)} critical</span><span>${String(report.summary.warn)} warn</span><span>${String(report.summary.info)} info</span><span>${String(report.summary.total)} total</span></div></section><h2>Recovery actions</h2><section class="actions">${actions || '<article class="action"><span>NONE</span><h2>No recovery actions</h2><p>No executable actions are available for this report.</p></article>'}</section><h2>Timeline</h2><section class="grid">${events || '<article class="info"><span>INFO</span><h2>No incident events</h2><p>No non-pass operational events were found in this window.</p></article>'}</section></main><script>const voiceIncidentActionPath=${JSON.stringify(actionPath)};document.querySelectorAll("[data-voice-incident-action]").forEach((button)=>{button.addEventListener("click",async()=>{const id=button.getAttribute("data-voice-incident-action");if(!id)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(voiceIncidentActionPath+"/"+encodeURIComponent(id),{method:"POST"});button.textContent=response.ok?"Done":"Failed";if(response.ok)setTimeout(()=>location.reload(),700)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1600)}})});</script></body></html>`;
|
|
27749
28670
|
};
|
|
27750
28671
|
var createVoiceIncidentTimelineRoutes = (options) => {
|
|
27751
28672
|
const path = options.path ?? "/api/voice/incident-timeline";
|
|
@@ -27765,7 +28686,7 @@ var createVoiceIncidentTimelineRoutes = (options) => {
|
|
|
27765
28686
|
})
|
|
27766
28687
|
]);
|
|
27767
28688
|
};
|
|
27768
|
-
const routes = new
|
|
28689
|
+
const routes = new Elysia44({
|
|
27769
28690
|
name: options.name ?? "absolutejs-voice-incident-timeline"
|
|
27770
28691
|
}).get(path, async () => {
|
|
27771
28692
|
const report = await buildVoiceIncidentTimelineReport(options);
|
|
@@ -27957,11 +28878,11 @@ var createVoiceIncidentTimelineRoutes = (options) => {
|
|
|
27957
28878
|
};
|
|
27958
28879
|
|
|
27959
28880
|
// src/observabilityExport.ts
|
|
27960
|
-
import { Elysia as
|
|
28881
|
+
import { Elysia as Elysia45 } from "elysia";
|
|
27961
28882
|
import { Database as Database4 } from "bun:sqlite";
|
|
27962
28883
|
import { createHash } from "crypto";
|
|
27963
|
-
import { mkdir as
|
|
27964
|
-
import { join as
|
|
28884
|
+
import { mkdir as mkdir3, readFile, stat, unlink } from "fs/promises";
|
|
28885
|
+
import { join as join3 } from "path";
|
|
27965
28886
|
var voiceObservabilityExportSchemaVersion = "1.0.0";
|
|
27966
28887
|
var voiceObservabilityExportSchemaId = "com.absolutejs.voice.observability-export";
|
|
27967
28888
|
var createVoiceObservabilityExportSchema = () => ({
|
|
@@ -28536,15 +29457,15 @@ var loadVoiceObservabilityExportReplaySource = async (source) => {
|
|
|
28536
29457
|
return source;
|
|
28537
29458
|
}
|
|
28538
29459
|
if (source.kind === "file") {
|
|
28539
|
-
const root =
|
|
28540
|
-
const receiptPath = source.receiptDirectory ?
|
|
29460
|
+
const root = join3(source.directory, source.runId);
|
|
29461
|
+
const receiptPath = source.receiptDirectory ? join3(source.receiptDirectory, `${encodeURIComponent(deliveryReceiptId(source.runId))}.json`) : undefined;
|
|
28541
29462
|
const deliveryReceipt = receiptPath ? await Bun.file(receiptPath).text().then(JSON.parse).catch(() => {
|
|
28542
29463
|
return;
|
|
28543
29464
|
}) : undefined;
|
|
28544
29465
|
return {
|
|
28545
|
-
artifactIndex: JSON.parse(await Bun.file(
|
|
29466
|
+
artifactIndex: JSON.parse(await Bun.file(join3(root, "artifact-index.json")).text()),
|
|
28546
29467
|
deliveryReceipt,
|
|
28547
|
-
manifest: JSON.parse(await Bun.file(
|
|
29468
|
+
manifest: JSON.parse(await Bun.file(join3(root, "manifest.json")).text())
|
|
28548
29469
|
};
|
|
28549
29470
|
}
|
|
28550
29471
|
if (source.kind === "s3") {
|
|
@@ -28622,7 +29543,7 @@ var createVoiceObservabilityExportReplayRoutes = (options) => {
|
|
|
28622
29543
|
...options.headers ?? {}
|
|
28623
29544
|
};
|
|
28624
29545
|
const buildReport = () => resolveVoiceObservabilityExportReplayReport(options.source);
|
|
28625
|
-
const app = new
|
|
29546
|
+
const app = new Elysia45({
|
|
28626
29547
|
name: options.name ?? "absolute-voice-observability-export-replay"
|
|
28627
29548
|
});
|
|
28628
29549
|
app.get(path, async () => Response.json(await buildReport(), { headers }));
|
|
@@ -28754,7 +29675,7 @@ var createVoiceMemoryObservabilityExportDeliveryReceiptStore = () => {
|
|
|
28754
29675
|
};
|
|
28755
29676
|
};
|
|
28756
29677
|
var createVoiceFileObservabilityExportDeliveryReceiptStore = (options) => {
|
|
28757
|
-
const receiptPath = (id) =>
|
|
29678
|
+
const receiptPath = (id) => join3(options.directory, `${encodeURIComponent(id)}.json`);
|
|
28758
29679
|
return {
|
|
28759
29680
|
get: async (id) => {
|
|
28760
29681
|
const file = Bun.file(receiptPath(id));
|
|
@@ -28764,10 +29685,10 @@ var createVoiceFileObservabilityExportDeliveryReceiptStore = (options) => {
|
|
|
28764
29685
|
return JSON.parse(await file.text());
|
|
28765
29686
|
},
|
|
28766
29687
|
list: async () => {
|
|
28767
|
-
await
|
|
29688
|
+
await mkdir3(options.directory, { recursive: true });
|
|
28768
29689
|
const receipts = [];
|
|
28769
29690
|
for (const entry of await Array.fromAsync(new Bun.Glob("*.json").scan(options.directory))) {
|
|
28770
|
-
const file = Bun.file(
|
|
29691
|
+
const file = Bun.file(join3(options.directory, entry));
|
|
28771
29692
|
receipts.push(JSON.parse(await file.text()));
|
|
28772
29693
|
}
|
|
28773
29694
|
return receipts.sort((left, right) => right.checkedAt - left.checkedAt);
|
|
@@ -28778,7 +29699,7 @@ var createVoiceFileObservabilityExportDeliveryReceiptStore = (options) => {
|
|
|
28778
29699
|
});
|
|
28779
29700
|
},
|
|
28780
29701
|
set: async (id, receipt) => {
|
|
28781
|
-
await
|
|
29702
|
+
await mkdir3(options.directory, { recursive: true });
|
|
28782
29703
|
await Bun.write(receiptPath(id), `${JSON.stringify(receipt, null, 2)}
|
|
28783
29704
|
`);
|
|
28784
29705
|
}
|
|
@@ -29284,16 +30205,16 @@ var deliverVoiceObservabilityExport = async (options) => {
|
|
|
29284
30205
|
const label = destination.label ?? (destination.kind === "file" ? "File observability export" : destination.kind === "s3" ? "S3 observability export" : destination.kind === "sqlite" ? "SQLite observability export" : destination.kind === "postgres" ? "Postgres observability export" : "Webhook observability export");
|
|
29285
30206
|
try {
|
|
29286
30207
|
if (destination.kind === "file") {
|
|
29287
|
-
const target =
|
|
29288
|
-
await
|
|
29289
|
-
await Bun.write(
|
|
29290
|
-
await Bun.write(
|
|
30208
|
+
const target = join3(destination.directory, runId);
|
|
30209
|
+
await mkdir3(join3(target, "artifacts"), { recursive: true });
|
|
30210
|
+
await Bun.write(join3(target, "manifest.json"), manifest);
|
|
30211
|
+
await Bun.write(join3(target, "artifact-index.json"), index);
|
|
29291
30212
|
if (destination.includeArtifacts !== false) {
|
|
29292
30213
|
for (const artifact of options.report.artifacts) {
|
|
29293
30214
|
if (!artifact.path) {
|
|
29294
30215
|
continue;
|
|
29295
30216
|
}
|
|
29296
|
-
await Bun.write(
|
|
30217
|
+
await Bun.write(join3(target, "artifacts", safeArtifactFileName(artifact)), await readFile(stripArtifactPathAnchor(artifact.path)));
|
|
29297
30218
|
}
|
|
29298
30219
|
}
|
|
29299
30220
|
return {
|
|
@@ -29488,7 +30409,7 @@ var createVoiceObservabilityExportRoutes = (options = {}) => {
|
|
|
29488
30409
|
artifactDownload: options.links?.artifactDownload ?? (artifactDownloadPath ? (artifact) => `${artifactDownloadPath}/${encodeURIComponent(artifact.id)}` : undefined)
|
|
29489
30410
|
}
|
|
29490
30411
|
});
|
|
29491
|
-
const app = new
|
|
30412
|
+
const app = new Elysia45({
|
|
29492
30413
|
name: options.name ?? "absolute-voice-observability-export"
|
|
29493
30414
|
});
|
|
29494
30415
|
app.get(path, async () => Response.json(await buildReport(), { headers }));
|
|
@@ -29595,7 +30516,7 @@ var buildVoiceReadinessRecoveryActions = (input, options = {}) => {
|
|
|
29595
30516
|
sourceChecks: sourceChecks.length
|
|
29596
30517
|
};
|
|
29597
30518
|
};
|
|
29598
|
-
var
|
|
30519
|
+
var escapeHtml43 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
29599
30520
|
var formatVoiceProofFreshnessDuration = (valueMs) => {
|
|
29600
30521
|
if (valueMs < 1000) {
|
|
29601
30522
|
return `${Math.max(0, Math.round(valueMs))}ms`;
|
|
@@ -29795,6 +30716,7 @@ var readinessGateCodes = {
|
|
|
29795
30716
|
"Reconnect recovery contracts": "voice.readiness.reconnect_contracts",
|
|
29796
30717
|
"Routing evidence": "voice.readiness.routing_evidence",
|
|
29797
30718
|
"Session health": "voice.readiness.session_health",
|
|
30719
|
+
"Session observability evidence": "voice.readiness.session_observability_evidence",
|
|
29798
30720
|
"Trace sink delivery": "voice.readiness.trace_sink_delivery"
|
|
29799
30721
|
};
|
|
29800
30722
|
var readinessGateCodeForCheck = (check) => readinessGateCodes[check.label] ?? `voice.readiness.${check.label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
@@ -29960,6 +30882,18 @@ var resolveMonitoringNotifierDelivery = async (options, input) => {
|
|
|
29960
30882
|
}
|
|
29961
30883
|
return typeof options.monitoringNotifierDelivery === "function" ? await options.monitoringNotifierDelivery(input) : options.monitoringNotifierDelivery;
|
|
29962
30884
|
};
|
|
30885
|
+
var resolveSessionObservability = async (options, input) => {
|
|
30886
|
+
if (options.sessionObservability === false || options.sessionObservability === undefined) {
|
|
30887
|
+
return;
|
|
30888
|
+
}
|
|
30889
|
+
return typeof options.sessionObservability === "function" ? await options.sessionObservability(input) : options.sessionObservability;
|
|
30890
|
+
};
|
|
30891
|
+
var resolveSessionObservabilityEvidence = async (options, input) => {
|
|
30892
|
+
if (options.sessionObservabilityEvidence === false || options.sessionObservabilityEvidence === undefined) {
|
|
30893
|
+
return;
|
|
30894
|
+
}
|
|
30895
|
+
return typeof options.sessionObservabilityEvidence === "function" ? await options.sessionObservabilityEvidence(input) : options.sessionObservabilityEvidence;
|
|
30896
|
+
};
|
|
29963
30897
|
var resolveMediaPipeline = async (options, input) => {
|
|
29964
30898
|
if (options.mediaPipeline === false || options.mediaPipeline === undefined) {
|
|
29965
30899
|
return;
|
|
@@ -30481,6 +31415,8 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
30481
31415
|
mediaPipeline,
|
|
30482
31416
|
browserMedia,
|
|
30483
31417
|
telephonyMedia,
|
|
31418
|
+
sessionObservability,
|
|
31419
|
+
sessionObservabilityEvidence,
|
|
30484
31420
|
telephonyWebhookSecurity,
|
|
30485
31421
|
reconnectContracts,
|
|
30486
31422
|
bargeInReports,
|
|
@@ -30530,6 +31466,8 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
30530
31466
|
time("mediaPipeline", () => resolveMediaPipeline(options, { query, request })),
|
|
30531
31467
|
time("browserMedia", () => resolveBrowserMedia(options, { query, request })),
|
|
30532
31468
|
time("telephonyMedia", () => resolveTelephonyMedia(options, { query, request })),
|
|
31469
|
+
time("sessionObservability", () => resolveSessionObservability(options, { query, request })),
|
|
31470
|
+
time("sessionObservabilityEvidence", () => resolveSessionObservabilityEvidence(options, { query, request })),
|
|
30533
31471
|
time("telephonyWebhookSecurity", () => resolveTelephonyWebhookSecurity(options, { query, request })),
|
|
30534
31472
|
time("reconnectContracts", () => resolveReconnectContracts(options, { query, request })),
|
|
30535
31473
|
time("bargeInReports", () => resolveBargeInReports(options, { query, request })),
|
|
@@ -30573,6 +31511,15 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
30573
31511
|
warnWhenEmpty: options.incidentRecoveryTrendSLO?.warnWhenEmpty,
|
|
30574
31512
|
mode: options.incidentRecoveryTrendSLO?.mode
|
|
30575
31513
|
}) : undefined;
|
|
31514
|
+
const sessionObservabilityEvidenceSummary = sessionObservability ? evaluateVoiceSessionObservabilityEvidence(sessionObservability, sessionObservabilityEvidence ?? {}) : undefined;
|
|
31515
|
+
const sessionObservabilitySummary = sessionObservability && sessionObservabilityEvidenceSummary ? {
|
|
31516
|
+
failed: sessionObservabilityEvidenceSummary.status === "fail" ? 1 : 0,
|
|
31517
|
+
passed: sessionObservabilityEvidenceSummary.status === "pass" ? 1 : 0,
|
|
31518
|
+
status: sessionObservabilityEvidenceSummary.status,
|
|
31519
|
+
total: 1,
|
|
31520
|
+
warnings: sessionObservabilityEvidenceSummary.status === "warn" ? 1 : 0
|
|
31521
|
+
} : undefined;
|
|
31522
|
+
const proofSource = (...keys) => keys.map((key) => proofSources?.[key]).find((source) => source !== undefined);
|
|
30576
31523
|
const checks = [
|
|
30577
31524
|
{
|
|
30578
31525
|
detail: quality.status === "pass" ? "Quality gates are passing." : "Quality gates need attention.",
|
|
@@ -30693,6 +31640,23 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
30693
31640
|
}
|
|
30694
31641
|
] : []
|
|
30695
31642
|
},
|
|
31643
|
+
...sessionObservability && sessionObservabilitySummary ? [
|
|
31644
|
+
{
|
|
31645
|
+
detail: sessionObservabilitySummary.status === "pass" ? `Session observability is healthy with ${sessionObservabilityEvidenceSummary?.summary.turnsWithWaterfalls ?? 0} turn(s) containing waterfall stages and ${sessionObservabilityEvidenceSummary?.summary.providerDecisions ?? 0} provider decision stage(s).` : `${sessionObservabilityEvidenceSummary?.issues.join("; ") ?? "Session observability has unresolved issues."}`,
|
|
31646
|
+
href: options.links?.sessionObservability ?? "/voice/session-observability",
|
|
31647
|
+
label: "Session observability evidence",
|
|
31648
|
+
proofSource: proofSource("sessionObservability", "sessionObservability"),
|
|
31649
|
+
status: sessionObservabilitySummary.status,
|
|
31650
|
+
value: `${sessionObservabilitySummary.passed}/${sessionObservabilitySummary.total}`,
|
|
31651
|
+
actions: sessionObservabilitySummary.status === "pass" ? [] : [
|
|
31652
|
+
{
|
|
31653
|
+
description: "Open session observability report and address missing turns, provider decisions, tool calls, links, and incident markdown coverage.",
|
|
31654
|
+
href: options.links?.sessionObservability ?? "/voice/session-observability",
|
|
31655
|
+
label: "Open session observability"
|
|
31656
|
+
}
|
|
31657
|
+
]
|
|
31658
|
+
}
|
|
31659
|
+
] : [],
|
|
30696
31660
|
{
|
|
30697
31661
|
detail: routingEvents.length > 0 ? `${routingSessions.length} session(s) have provider routing evidence.` : "No provider routing traces are recorded yet.",
|
|
30698
31662
|
href: options.links?.resilience ?? "/resilience",
|
|
@@ -30709,7 +31673,6 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
30709
31673
|
}
|
|
30710
31674
|
];
|
|
30711
31675
|
checks.push(...additionalChecks);
|
|
30712
|
-
const proofSource = (...keys) => keys.map((key) => proofSources?.[key]).find((source) => source !== undefined);
|
|
30713
31676
|
const calibratedThresholdActions = () => options.links?.sloReadinessThresholds ? [
|
|
30714
31677
|
{
|
|
30715
31678
|
description: "Open the calibrated thresholds currently driving this readiness gate.",
|
|
@@ -31556,6 +32519,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
31556
32519
|
profileSwitchLiveDecisions: "/voice/profile-switch-live-decisions",
|
|
31557
32520
|
profileSwitchPolicy: "/voice/profile-switch-policy",
|
|
31558
32521
|
profileSwitchReadiness: "/voice/profile-switch-readiness",
|
|
32522
|
+
sessionObservability: "/voice/session-observability",
|
|
31559
32523
|
telephonyMedia: "/voice/telephony-media",
|
|
31560
32524
|
telephonyWebhookSecurity: "/api/voice/telephony/webhook-security",
|
|
31561
32525
|
providerContracts: "/provider-contracts",
|
|
@@ -31626,6 +32590,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
31626
32590
|
providerRoutingContracts: providerRoutingContractSummary,
|
|
31627
32591
|
providerSlo: providerSloSummary,
|
|
31628
32592
|
reconnectContracts: reconnectContractSummary,
|
|
32593
|
+
sessionObservability: sessionObservabilitySummary,
|
|
31629
32594
|
quality: {
|
|
31630
32595
|
status: quality.status
|
|
31631
32596
|
},
|
|
@@ -31644,25 +32609,25 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
31644
32609
|
var buildVoiceProductionReadinessGate = async (options, input = {}) => summarizeVoiceProductionReadinessGate(await buildVoiceProductionReadinessReport(options, input), options.gate || undefined);
|
|
31645
32610
|
var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
31646
32611
|
const title = options.title ?? "AbsoluteJS Voice Production Readiness";
|
|
31647
|
-
const thresholdLink = report.links.sloReadinessThresholds ? `<p><a href="${
|
|
31648
|
-
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${
|
|
32612
|
+
const thresholdLink = report.links.sloReadinessThresholds ? `<p><a href="${escapeHtml43(report.links.sloReadinessThresholds)}">Open Calibration -> Active Readiness Gate</a> to inspect the thresholds currently driving calibrated provider, latency, interruption, reconnect, and monitoring gates.</p>` : "";
|
|
32613
|
+
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${escapeHtml43(report.profile.name)}</h2><p>${escapeHtml43(report.profile.description)}</p><p>${escapeHtml43(report.profile.purpose)}</p><div class="profile-surfaces">${report.profile.surfaces.map((surface) => `<article class="${surface.configured ? "pass" : "warn"}"><span>${surface.configured ? "CONFIGURED" : "EXPECTED"}</span><strong>${surface.href ? `<a href="${escapeHtml43(surface.href)}">${escapeHtml43(surface.label)}</a>` : escapeHtml43(surface.label)}</strong></article>`).join("")}</div></section>` : "";
|
|
31649
32614
|
const checks = report.checks.map((check, index) => {
|
|
31650
|
-
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${
|
|
31651
|
-
const explanation = check.gateExplanation ? `<p class="gate-explanation">Why this gate is ${
|
|
31652
|
-
return `<article class="check ${
|
|
32615
|
+
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml43(action.href)}">${escapeHtml43(action.label)}</button>` : `<a href="${escapeHtml43(action.href)}">${escapeHtml43(action.label)}</a>`).join("");
|
|
32616
|
+
const explanation = check.gateExplanation ? `<p class="gate-explanation">Why this gate is ${escapeHtml43(check.status)}: observed ${escapeHtml43(String(check.gateExplanation.observed ?? "n/a"))}${check.gateExplanation.unit ? ` ${escapeHtml43(check.gateExplanation.unit)}` : ""}; threshold ${escapeHtml43(String(check.gateExplanation.threshold ?? "n/a"))}${check.gateExplanation.unit ? ` ${escapeHtml43(check.gateExplanation.unit)}` : ""}. ${escapeHtml43(check.gateExplanation.remediation)} ${check.gateExplanation.sourceHref ? `<a href="${escapeHtml43(check.gateExplanation.sourceHref)}">Open threshold source</a>` : ""}</p>` : "";
|
|
32617
|
+
return `<article class="check ${escapeHtml43(check.status)}">
|
|
31653
32618
|
<div>
|
|
31654
|
-
<span>${
|
|
31655
|
-
<h2>${
|
|
31656
|
-
${check.detail ? `<p>${
|
|
32619
|
+
<span>${escapeHtml43(check.status.toUpperCase())}</span>
|
|
32620
|
+
<h2>${escapeHtml43(check.label)}</h2>
|
|
32621
|
+
${check.detail ? `<p>${escapeHtml43(check.detail)}</p>` : ""}
|
|
31657
32622
|
${explanation}
|
|
31658
|
-
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${
|
|
32623
|
+
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml43(check.proofSource.href)}">${escapeHtml43(check.proofSource.sourceLabel)}</a>` : escapeHtml43(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml43(check.proofSource.detail)}` : ""}</p>` : ""}
|
|
31659
32624
|
${actions ? `<p class="actions">${actions}</p>` : ""}
|
|
31660
32625
|
</div>
|
|
31661
|
-
<strong>${
|
|
31662
|
-
${check.href ? `<a href="${
|
|
32626
|
+
<strong>${escapeHtml43(String(check.value ?? check.status))}</strong>
|
|
32627
|
+
${check.href ? `<a href="${escapeHtml43(check.href)}">Open surface</a>` : ""}
|
|
31663
32628
|
</article>`;
|
|
31664
32629
|
}).join("");
|
|
31665
|
-
const snippet =
|
|
32630
|
+
const snippet = escapeHtml43(`createVoiceProductionReadinessRoutes({
|
|
31666
32631
|
htmlPath: '/production-readiness',
|
|
31667
32632
|
path: '/api/production-readiness',
|
|
31668
32633
|
gatePath: '/api/production-readiness/gate',
|
|
@@ -31678,13 +32643,13 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
|
31678
32643
|
providerRoutingContracts: loadProviderRoutingContracts,
|
|
31679
32644
|
store: traceStore
|
|
31680
32645
|
});`);
|
|
31681
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
32646
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml43(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero,.primitive,.profile{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.primitive,.profile{background:#111722}.primitive{border-color:#3a3f2d}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{display:inline-flex;border:1px solid #3f3f46;border-radius:999px;padding:8px 12px}.primitive code{color:#fde68a}.primitive p{color:#c8ccd3;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#0b0f16;border:1px solid #2c3440;border-radius:18px;color:#fef3c7;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.check.pass,.profile-surfaces .pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.warn,.profile-surfaces .warn{border-color:rgba(245,158,11,.65)}.status.fail,.check.fail{border-color:rgba(239,68,68,.75)}.checks{display:grid;gap:14px}.check{align-items:center;background:#141922;border:1px solid #26313d;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.check span,.profile-surfaces span{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p,.profile p{color:#b9c0c8;margin:.2rem 0 0}.check .proof-source{color:#f9d77e;font-weight:800}.check .gate-explanation{background:#0b0f16;border:1px solid #2c3440;border-radius:14px;color:#fef3c7;margin-top:10px;padding:10px}.check strong{font-size:1.5rem}.profile-surfaces{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin-top:16px}.profile-surfaces article{background:#141922;border:1px solid #26313d;border-radius:16px;padding:14px}.profile-surfaces strong{display:block;margin-top:6px}.actions{display:flex;flex-wrap:wrap;gap:10px}.check a,a{color:#fbbf24}button{background:#fbbf24;border:0;border-radius:999px;color:#111827;cursor:pointer;font-weight:800;padding:9px 12px}button:disabled{cursor:wait;opacity:.65}@media(max-width:760px){main{padding:20px}.check{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted readiness</p><h1>${escapeHtml43(title)}</h1><p>One deployable pass/fail report for quality gates, provider failover, session health, handoffs, routing evidence, and optional carrier readiness.</p><p class="status ${escapeHtml43(report.status)}">Overall: ${escapeHtml43(report.status.toUpperCase())}</p><p>Checked ${escapeHtml43(new Date(report.checkedAt).toLocaleString())}</p>${thresholdLink}</section>${profile}<section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProductionReadinessRoutes(...)</code> builds this deploy gate</h2><p>Mount one package primitive to expose JSON readiness, HTML readiness, and a machine-readable gate route. Feed it the proof stores and contract reports your app already owns.</p><pre><code>${snippet}</code></pre></section><section class="checks">${checks}</section></main><script>document.querySelectorAll("[data-readiness-action]").forEach((button)=>{button.addEventListener("click",async()=>{const url=button.getAttribute("data-action-url");if(!url)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(url,{method:"POST"});button.textContent=response.ok?"Done. Reloading...":"Failed";if(response.ok)setTimeout(()=>location.reload(),500)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1500)}})});</script></body></html>`;
|
|
31682
32647
|
};
|
|
31683
32648
|
var createVoiceProductionReadinessRoutes = (options) => {
|
|
31684
32649
|
const path = options.path ?? "/api/production-readiness";
|
|
31685
32650
|
const gatePath = options.gatePath === undefined ? "/api/production-readiness/gate" : options.gatePath;
|
|
31686
32651
|
const htmlPath = options.htmlPath ?? "/production-readiness";
|
|
31687
|
-
const routes = new
|
|
32652
|
+
const routes = new Elysia46({
|
|
31688
32653
|
name: options.name ?? "absolutejs-voice-production-readiness"
|
|
31689
32654
|
});
|
|
31690
32655
|
let cachedReport;
|
|
@@ -31756,7 +32721,7 @@ var createVoiceProductionReadinessRoutes = (options) => {
|
|
|
31756
32721
|
};
|
|
31757
32722
|
|
|
31758
32723
|
// src/operationalStatus.ts
|
|
31759
|
-
var
|
|
32724
|
+
var escapeHtml44 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
31760
32725
|
var resolveValue2 = async (value) => typeof value === "function" ? await value() : value;
|
|
31761
32726
|
var isDeliveryRuntime = (value) => Boolean(value && typeof value === "object" && "isRunning" in value && "summarize" in value);
|
|
31762
32727
|
var worstStatus3 = (statuses) => statuses.includes("fail") ? "fail" : statuses.includes("warn") ? "warn" : "pass";
|
|
@@ -31826,19 +32791,19 @@ var buildVoiceOperationalStatusReport = async (options) => {
|
|
|
31826
32791
|
};
|
|
31827
32792
|
var renderVoiceOperationalStatusHTML = (report, options = {}) => {
|
|
31828
32793
|
const title = options.title ?? "AbsoluteJS Voice Operational Status";
|
|
31829
|
-
const checks = report.checks.map((check) => `<article class="${
|
|
31830
|
-
<span>${
|
|
31831
|
-
<h2>${
|
|
31832
|
-
<strong>${
|
|
31833
|
-
${check.detail ? `<p>${
|
|
31834
|
-
${check.href ? `<a href="${
|
|
32794
|
+
const checks = report.checks.map((check) => `<article class="${escapeHtml44(check.status)}">
|
|
32795
|
+
<span>${escapeHtml44(check.status.toUpperCase())}</span>
|
|
32796
|
+
<h2>${escapeHtml44(check.label)}</h2>
|
|
32797
|
+
<strong>${escapeHtml44(String(check.value ?? check.status))}</strong>
|
|
32798
|
+
${check.detail ? `<p>${escapeHtml44(check.detail)}</p>` : ""}
|
|
32799
|
+
${check.href ? `<a href="${escapeHtml44(check.href)}">Open surface</a>` : ""}
|
|
31835
32800
|
</article>`).join("");
|
|
31836
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
32801
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml44(title)}</title><style>body{background:#10130f;color:#f8f3df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1040px;padding:32px}.hero{background:linear-gradient(135deg,rgba(132,204,22,.18),rgba(14,165,233,.13));border:1px solid #2c3a28;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#bef264;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}article{background:#171d15;border:1px solid #2c3a28;border-radius:22px;padding:18px}article.pass{border-color:rgba(34,197,94,.65)}article.warn{border-color:rgba(245,158,11,.75)}article.fail{border-color:rgba(239,68,68,.85)}article span{color:#bef264;font-size:.78rem;font-weight:900;letter-spacing:.08em}article.warn span{color:#fbbf24}article.fail span{color:#fca5a5}article strong{display:block;font-size:1.6rem;margin:.4rem 0}article p{color:#c5ceb9}a{color:#bef264}</style></head><body><main><section class="hero"><p class="eyebrow">Operational status</p><h1>${escapeHtml44(title)}</h1><p class="status ${escapeHtml44(report.status)}">Overall: ${escapeHtml44(report.status.toUpperCase())}</p><p>${String(report.summary.pass)}/${String(report.summary.total)} checks passing. Checked ${escapeHtml44(new Date(report.checkedAt).toLocaleString())}.</p></section><section class="grid">${checks || '<article class="pass"><span>PASS</span><h2>No operational checks configured</h2><strong>0/0</strong></article>'}</section></main></body></html>`;
|
|
31837
32802
|
};
|
|
31838
32803
|
var createVoiceOperationalStatusRoutes = (options) => {
|
|
31839
32804
|
const path = options.path ?? "/api/voice/operational-status";
|
|
31840
32805
|
const htmlPath = options.htmlPath === undefined ? "/voice/operational-status" : options.htmlPath;
|
|
31841
|
-
const routes = new
|
|
32806
|
+
const routes = new Elysia47({
|
|
31842
32807
|
name: options.name ?? "absolutejs-voice-operational-status"
|
|
31843
32808
|
}).get(path, async () => {
|
|
31844
32809
|
const report = await buildVoiceOperationalStatusReport(options);
|
|
@@ -31865,7 +32830,7 @@ var createVoiceOperationalStatusRoutes = (options) => {
|
|
|
31865
32830
|
return routes;
|
|
31866
32831
|
};
|
|
31867
32832
|
// src/dataControl.ts
|
|
31868
|
-
import { Elysia as
|
|
32833
|
+
import { Elysia as Elysia48 } from "elysia";
|
|
31869
32834
|
var voiceComplianceRedactionDefaults = {
|
|
31870
32835
|
keys: [
|
|
31871
32836
|
"apiKey",
|
|
@@ -32104,7 +33069,7 @@ var parseRetentionScopes = (value) => {
|
|
|
32104
33069
|
const allowed = new Set(allRetentionScopes);
|
|
32105
33070
|
return value.split(",").map((entry) => entry.trim()).filter((entry) => allowed.has(entry));
|
|
32106
33071
|
};
|
|
32107
|
-
var
|
|
33072
|
+
var escapeHtml45 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
32108
33073
|
var buildStorageSurfaces = (options) => [
|
|
32109
33074
|
{
|
|
32110
33075
|
configured: Boolean(options.session ?? options.sessions),
|
|
@@ -32341,12 +33306,12 @@ var buildVoiceDataControlReport = async (options) => {
|
|
|
32341
33306
|
zeroRetentionAvailable: true
|
|
32342
33307
|
};
|
|
32343
33308
|
};
|
|
32344
|
-
var renderDataRetentionReportRows = (report) => report.scopes.map((scope) => `<tr><td>${
|
|
33309
|
+
var renderDataRetentionReportRows = (report) => report.scopes.map((scope) => `<tr><td>${escapeHtml45(scope.scope)}</td><td>${scope.scannedCount}</td><td>${scope.deletedCount}</td><td>${escapeHtml45(scope.skippedReason ?? "")}</td><td><code>${escapeHtml45(scope.deletedIds.join(", "))}</code></td></tr>`).join("");
|
|
32345
33310
|
var renderVoiceDataControlHTML = (report, options = {}) => {
|
|
32346
33311
|
const title = options.title ?? "Voice Data Control";
|
|
32347
|
-
const storageRows = report.storage.map((surface) => `<tr><td>${
|
|
32348
|
-
const keyRows = report.providerKeys.map((key) => `<tr><td>${
|
|
32349
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${
|
|
33312
|
+
const storageRows = report.storage.map((surface) => `<tr><td>${escapeHtml45(surface.name)}</td><td>${surface.configured ? "Configured" : "Missing"}</td><td>${escapeHtml45(surface.control)}</td><td>${surface.selfHosted ? "Yes" : "No"}</td></tr>`).join("");
|
|
33313
|
+
const keyRows = report.providerKeys.map((key) => `<tr><td>${escapeHtml45(key.name)}</td><td><code>${escapeHtml45(key.env ?? "n/a")}</code></td><td>${key.required ? "Required" : "Optional"}</td><td>${escapeHtml45(key.recommendation)}</td></tr>`).join("");
|
|
33314
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml45(title)}</title><style>body{background:#f8f7f2;color:#181713;font-family:ui-sans-serif,system-ui,sans-serif;line-height:1.45;margin:2rem}main{max-width:1120px;margin:auto}.summary{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:1rem 0}.card,table{background:white;border:1px solid #ddd6c8;border-radius:14px}.card{padding:1rem}table{border-collapse:collapse;width:100%;overflow:hidden}td,th{border-bottom:1px solid #eee8dc;padding:.7rem;text-align:left;vertical-align:top}code{white-space:pre-wrap;word-break:break-word}a{color:#9a3412}</style></head><body><main><h1>${escapeHtml45(title)}</h1><p>Self-hosted data-control proof for retention, redaction, audit export, deletion planning, customer-owned storage, and provider key handling.</p><section class="summary"><div class="card"><strong>Redaction</strong><br>${report.redaction.enabled ? "enabled" : "disabled"}</div><div class="card"><strong>Retention dry-run deletes</strong><br>${report.retentionPlan.deletedCount}</div><div class="card"><strong>Audit export events</strong><br>${report.auditExport?.events.length ?? 0}</div><div class="card"><strong>Zero retention recipe</strong><br>${report.zeroRetentionAvailable ? "available" : "missing"}</div></section><h2>Customer-Owned Storage</h2><table><thead><tr><th>Surface</th><th>Status</th><th>Control</th><th>Self-hosted</th></tr></thead><tbody>${storageRows}</tbody></table><h2>Retention Plan</h2><table><thead><tr><th>Scope</th><th>Scanned</th><th>Would delete</th><th>Skipped</th><th>Ids</th></tr></thead><tbody>${renderDataRetentionReportRows(report.retentionPlan)}</tbody></table><h2>Provider Keys</h2><table><thead><tr><th>Provider</th><th>Env</th><th>Required</th><th>Recommendation</th></tr></thead><tbody>${keyRows}</tbody></table><p><a href="./data-control/audit.md">Redacted audit Markdown</a> \xB7 <a href="./data-control/audit.html">Redacted audit HTML</a></p></main></body></html>`;
|
|
32350
33315
|
};
|
|
32351
33316
|
var renderVoiceDataControlMarkdown = (report, options = {}) => [
|
|
32352
33317
|
`# ${options.title ?? "Voice Data Control"}`,
|
|
@@ -32404,7 +33369,7 @@ var parseRetentionPolicyBody = (body, options, dryRun) => {
|
|
|
32404
33369
|
var createVoiceDataControlRoutes = (options) => {
|
|
32405
33370
|
const path = options.path ?? "/data-control";
|
|
32406
33371
|
const title = options.title ?? "AbsoluteJS Voice Data Control";
|
|
32407
|
-
const routes = new
|
|
33372
|
+
const routes = new Elysia48({
|
|
32408
33373
|
name: options.name ?? "absolutejs-voice-data-control"
|
|
32409
33374
|
});
|
|
32410
33375
|
routes.get(path, async ({ query }) => {
|
|
@@ -32480,10 +33445,10 @@ var createVoiceDataControlRoutes = (options) => {
|
|
|
32480
33445
|
return routes;
|
|
32481
33446
|
};
|
|
32482
33447
|
// src/evalRoutes.ts
|
|
32483
|
-
import { Elysia as
|
|
32484
|
-
import { mkdir as
|
|
33448
|
+
import { Elysia as Elysia49 } from "elysia";
|
|
33449
|
+
import { mkdir as mkdir4 } from "fs/promises";
|
|
32485
33450
|
import { dirname as dirname2 } from "path";
|
|
32486
|
-
var
|
|
33451
|
+
var escapeHtml46 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
32487
33452
|
var rate4 = (count, total) => count / Math.max(1, total);
|
|
32488
33453
|
var normalizeSearchText = (value) => value.trim().toLowerCase();
|
|
32489
33454
|
var getString18 = (value) => typeof value === "string" ? value : undefined;
|
|
@@ -32793,7 +33758,7 @@ var createVoiceFileEvalBaselineStore = (filePath) => ({
|
|
|
32793
33758
|
return text.trim() ? JSON.parse(text) : undefined;
|
|
32794
33759
|
},
|
|
32795
33760
|
set: async (report) => {
|
|
32796
|
-
await
|
|
33761
|
+
await mkdir4(dirname2(filePath), { recursive: true });
|
|
32797
33762
|
await Bun.write(filePath, JSON.stringify(report, null, 2));
|
|
32798
33763
|
}
|
|
32799
33764
|
});
|
|
@@ -32814,7 +33779,7 @@ var createVoiceFileScenarioFixtureStore = (filePath) => ({
|
|
|
32814
33779
|
var formatTime = (value) => value === undefined ? "unknown" : new Date(value).toLocaleString();
|
|
32815
33780
|
var formatPercent = (value) => `${(value * 100).toFixed(2)}%`;
|
|
32816
33781
|
var renderVoiceEvalPrimitiveCopy = () => {
|
|
32817
|
-
const snippet =
|
|
33782
|
+
const snippet = escapeHtml46(`app.use(
|
|
32818
33783
|
createVoiceEvalRoutes({
|
|
32819
33784
|
path: '/evals',
|
|
32820
33785
|
store: traceStore,
|
|
@@ -32835,48 +33800,48 @@ var renderVoiceEvalPrimitiveCopy = () => {
|
|
|
32835
33800
|
};
|
|
32836
33801
|
var renderVoiceEvalHTML = (report, options = {}) => {
|
|
32837
33802
|
const title = options.title ?? "AbsoluteJS Voice Evals";
|
|
32838
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
32839
|
-
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${
|
|
33803
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml46(link.href)}">${escapeHtml46(link.label)}</a>`).join("")}</nav>` : "";
|
|
33804
|
+
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${escapeHtml46(bucket.key)}</td><td>${bucket.total}</td><td>${bucket.passed}</td><td>${bucket.failed}</td></tr>`).join("") : '<tr><td colspan="4">No eval buckets yet.</td></tr>';
|
|
32840
33805
|
const sessions = report.sessions.length ? report.sessions.map((session) => {
|
|
32841
33806
|
const failedMetrics = Object.entries(session.quality.metrics).filter(([, metric]) => !metric.pass).map(([, metric]) => metric.label).join(", ");
|
|
32842
|
-
const sessionLabel = session.operationsRecordHref ? `<a href="${
|
|
32843
|
-
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${
|
|
33807
|
+
const sessionLabel = session.operationsRecordHref ? `<a href="${escapeHtml46(session.operationsRecordHref)}">${escapeHtml46(session.sessionId)}</a>` : escapeHtml46(session.sessionId);
|
|
33808
|
+
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${escapeHtml46(session.status)}</td><td>${session.eventCount}</td><td>${session.summary.turnCount}</td><td>${session.summary.errorCount}</td><td>${escapeHtml46(formatTime(session.endedAt))}</td><td>${escapeHtml46(failedMetrics || "none")}</td></tr>`;
|
|
32844
33809
|
}).join("") : '<tr><td colspan="7">No sessions found.</td></tr>';
|
|
32845
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
33810
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml46(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.pass{color:#166534}.fail{color:#991b1b}.status.pass{background:#dcfce7}.status.fail{background:#fee2e2}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,.primitive{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.primitive{background:#fffdf7;border-color:#d6c7a3;margin:1rem 0}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.card strong{display:block;font-size:2rem}table{border-collapse:collapse;background:white;width:100%;margin:1rem 0 2rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml46(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${renderVoiceEvalPrimitiveCopy()}<h2>Trend</h2><table><thead><tr><th>Day</th><th>Total</th><th>Passed</th><th>Failed</th></tr></thead><tbody>${trend}</tbody></table><h2>Session Eval Results</h2><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Last event</th><th>Failed metrics</th></tr></thead><tbody>${sessions}</tbody></table></main></body></html>`;
|
|
32846
33811
|
};
|
|
32847
33812
|
var renderVoiceEvalBaselineHTML = (comparison, options = {}) => {
|
|
32848
33813
|
const title = options.title ?? "AbsoluteJS Voice Eval Baseline";
|
|
32849
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
32850
|
-
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${
|
|
32851
|
-
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${
|
|
32852
|
-
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${
|
|
32853
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
33814
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml46(link.href)}">${escapeHtml46(link.label)}</a>`).join("")}</nav>` : "";
|
|
33815
|
+
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${escapeHtml46(reason)}</li>`).join("") : "<li>No baseline regressions detected.</li>";
|
|
33816
|
+
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${escapeHtml46(id)}</li>`).join("") : "<li>none</li>";
|
|
33817
|
+
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${escapeHtml46(id)}</li>`).join("") : "<li>none</li>";
|
|
33818
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml46(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1000px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.pass{background:#dcfce7;color:#166534}.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:1rem 0}.card{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.card strong{display:block;font-size:2rem}section{background:white;border:1px solid #e7e5e4;border-radius:1rem;margin:1rem 0;padding:1rem}</style></head><body><main>${links}<h1>${escapeHtml46(title)}</h1><p class="status ${comparison.status}">${comparison.status}</p><div class="grid"><article class="card"><span>Baseline pass rate</span><strong>${escapeHtml46(formatPercent(comparison.baseline.passRate))}</strong></article><article class="card"><span>Current pass rate</span><strong>${escapeHtml46(formatPercent(comparison.current.passRate))}</strong></article><article class="card"><span>Failed delta</span><strong>${comparison.deltas.failed}</strong></article><article class="card"><span>Pass rate delta</span><strong>${escapeHtml46(formatPercent(comparison.deltas.passRate))}</strong></article></div><section><h2>Regression Reasons</h2><ul>${reasons}</ul></section><section><h2>New Failed Sessions</h2><ul>${newFailures}</ul></section><section><h2>Recovered Sessions</h2><ul>${recovered}</ul></section></main></body></html>`;
|
|
32854
33819
|
};
|
|
32855
33820
|
var renderVoiceScenarioEvalHTML = (report, options = {}) => {
|
|
32856
33821
|
const title = options.title ?? "AbsoluteJS Voice Scenario Evals";
|
|
32857
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
33822
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml46(link.href)}">${escapeHtml46(link.label)}</a>`).join("")}</nav>` : "";
|
|
32858
33823
|
const scenarios = report.scenarios.length ? report.scenarios.map((scenario) => {
|
|
32859
|
-
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${
|
|
33824
|
+
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${escapeHtml46(issue)}</li>`).join("")}</ul>` : "";
|
|
32860
33825
|
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => {
|
|
32861
|
-
const sessionLabel = session.operationsRecordHref ? `<a href="${
|
|
32862
|
-
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${
|
|
33826
|
+
const sessionLabel = session.operationsRecordHref ? `<a href="${escapeHtml46(session.operationsRecordHref)}">${escapeHtml46(session.sessionId)}</a>` : escapeHtml46(session.sessionId);
|
|
33827
|
+
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${escapeHtml46(session.status)}</td><td>${session.eventCount}</td><td>${escapeHtml46(session.issues.join(", ") || "none")}</td></tr>`;
|
|
32863
33828
|
}).join("") : '<tr><td colspan="4">No matching sessions.</td></tr>';
|
|
32864
|
-
return `<section class="scenario ${scenario.status}"><h2>${
|
|
33829
|
+
return `<section class="scenario ${scenario.status}"><h2>${escapeHtml46(scenario.label)}</h2>${scenario.description ? `<p>${escapeHtml46(scenario.description)}</p>` : ""}<p class="status ${scenario.status}">${scenario.status}</p><p>${scenario.passed} passed, ${scenario.failed} failed, ${scenario.matchedSessions} matched.</p>${scenarioIssues}<table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Issues</th></tr></thead><tbody>${sessions}</tbody></table></section>`;
|
|
32865
33830
|
}).join("") : "<section><p>No scenarios configured.</p></section>";
|
|
32866
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
33831
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml46(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,section{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.primitive{background:#fffdf7;border-color:#d6c7a3}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.card strong{display:block;font-size:2rem}section{margin:1rem 0}table{border-collapse:collapse;width:100%;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml46(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${renderVoiceEvalPrimitiveCopy()}${scenarios}</main></body></html>`;
|
|
32867
33832
|
};
|
|
32868
33833
|
var renderVoiceScenarioFixtureEvalHTML = (report, options = {}) => {
|
|
32869
33834
|
const title = options.title ?? "AbsoluteJS Voice Fixture Evals";
|
|
32870
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
33835
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml46(link.href)}">${escapeHtml46(link.label)}</a>`).join("")}</nav>` : "";
|
|
32871
33836
|
const fixtures = report.fixtures.length ? report.fixtures.map((fixture) => {
|
|
32872
|
-
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${
|
|
32873
|
-
return `<section class="${fixture.status}"><h2>${
|
|
33837
|
+
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${escapeHtml46(scenario.label)}</td><td>${escapeHtml46(scenario.status)}</td><td>${scenario.matchedSessions}</td><td>${escapeHtml46([...scenario.issues, ...scenario.sessions.flatMap((session) => session.issues)].join(", ") || "none")}</td></tr>`).join("");
|
|
33838
|
+
return `<section class="${fixture.status}"><h2>${escapeHtml46(fixture.label)}</h2>${fixture.description ? `<p>${escapeHtml46(fixture.description)}</p>` : ""}<p class="status ${fixture.status}">${fixture.status}</p><table><thead><tr><th>Scenario</th><th>Status</th><th>Sessions</th><th>Issues</th></tr></thead><tbody>${scenarios}</tbody></table></section>`;
|
|
32874
33839
|
}).join("") : "<section><p>No scenario fixtures configured.</p></section>";
|
|
32875
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
33840
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml46(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,section{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.primitive{background:#fffdf7;border-color:#d6c7a3}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.card strong{display:block;font-size:2rem}section{margin:1rem 0}table{border-collapse:collapse;width:100%;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml46(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${renderVoiceEvalPrimitiveCopy()}${fixtures}</main></body></html>`;
|
|
32876
33841
|
};
|
|
32877
33842
|
var createVoiceEvalRoutes = (options) => {
|
|
32878
33843
|
const path = options.path ?? "/evals";
|
|
32879
|
-
const routes = new
|
|
33844
|
+
const routes = new Elysia49({
|
|
32880
33845
|
name: options.name ?? "absolutejs-voice-evals"
|
|
32881
33846
|
});
|
|
32882
33847
|
const getReport = () => runVoiceSessionEvals({
|
|
@@ -33013,11 +33978,11 @@ var createVoiceEvalRoutes = (options) => {
|
|
|
33013
33978
|
return routes;
|
|
33014
33979
|
};
|
|
33015
33980
|
// src/simulationSuite.ts
|
|
33016
|
-
import { Elysia as
|
|
33981
|
+
import { Elysia as Elysia52 } from "elysia";
|
|
33017
33982
|
|
|
33018
33983
|
// src/outcomeContract.ts
|
|
33019
|
-
import { Elysia as
|
|
33020
|
-
var
|
|
33984
|
+
import { Elysia as Elysia50 } from "elysia";
|
|
33985
|
+
var escapeHtml47 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
33021
33986
|
var resolveSessionHref4 = (value, sessionId) => {
|
|
33022
33987
|
if (value === false) {
|
|
33023
33988
|
return;
|
|
@@ -33228,13 +34193,13 @@ var assertVoiceOutcomeContractEvidence = (report, input = {}) => {
|
|
|
33228
34193
|
var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
33229
34194
|
const title = options.title ?? "Voice Outcome Contracts";
|
|
33230
34195
|
const contracts = report.contracts.map((contract) => {
|
|
33231
|
-
const sessionLinks = contract.operationsRecordHrefs.length ? `<p>${contract.operationsRecordHrefs.map((href, index) => `<a href="${
|
|
34196
|
+
const sessionLinks = contract.operationsRecordHrefs.length ? `<p>${contract.operationsRecordHrefs.map((href, index) => `<a href="${escapeHtml47(href)}">${escapeHtml47(contract.sessionIds[index] ?? href)}</a>`).join(" \xB7 ")}</p>` : "";
|
|
33232
34197
|
return `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
33233
34198
|
<div class="contract-header">
|
|
33234
34199
|
<div>
|
|
33235
|
-
<p class="eyebrow">${
|
|
33236
|
-
<h2>${
|
|
33237
|
-
${contract.description ? `<p>${
|
|
34200
|
+
<p class="eyebrow">${escapeHtml47(contract.contractId)}</p>
|
|
34201
|
+
<h2>${escapeHtml47(contract.label ?? contract.contractId)}</h2>
|
|
34202
|
+
${contract.description ? `<p>${escapeHtml47(contract.description)}</p>` : ""}
|
|
33238
34203
|
${sessionLinks}
|
|
33239
34204
|
</div>
|
|
33240
34205
|
<strong>${contract.pass ? "pass" : "fail"}</strong>
|
|
@@ -33246,10 +34211,10 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
33246
34211
|
<span>handoffs ${String(contract.matched.handoffs)}</span>
|
|
33247
34212
|
<span>events ${String(contract.matched.integrationEvents)}</span>
|
|
33248
34213
|
</div>
|
|
33249
|
-
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${
|
|
34214
|
+
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${escapeHtml47(issue.message)}</li>`).join("")}</ul>` : ""}
|
|
33250
34215
|
</section>`;
|
|
33251
34216
|
}).join("");
|
|
33252
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
34217
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml47(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(14,165,233,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary,.grid{display:flex;flex-wrap:wrap;gap:10px}.pill,.grid span{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}li{margin:8px 0}@media(max-width:800px){main{padding:18px}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Business Outcome Verification</p><h1>${escapeHtml47(title)}</h1><div class="summary"><span class="pill ${report.status}">${report.status}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section>${contracts || '<section class="contract"><p>No outcome contracts configured.</p></section>'}</main></body></html>`;
|
|
33253
34218
|
};
|
|
33254
34219
|
var createVoiceOutcomeContractJSONHandler = (options) => async () => runVoiceOutcomeContractSuite(options);
|
|
33255
34220
|
var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
@@ -33265,7 +34230,7 @@ var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
|
33265
34230
|
var createVoiceOutcomeContractRoutes = (options) => {
|
|
33266
34231
|
const path = options.path ?? "/api/outcome-contracts";
|
|
33267
34232
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
33268
|
-
const routes = new
|
|
34233
|
+
const routes = new Elysia50({
|
|
33269
34234
|
name: options.name ?? "absolutejs-voice-outcome-contracts"
|
|
33270
34235
|
}).get(path, createVoiceOutcomeContractJSONHandler(options));
|
|
33271
34236
|
if (htmlPath) {
|
|
@@ -33275,7 +34240,7 @@ var createVoiceOutcomeContractRoutes = (options) => {
|
|
|
33275
34240
|
};
|
|
33276
34241
|
|
|
33277
34242
|
// src/toolContract.ts
|
|
33278
|
-
import { Elysia as
|
|
34243
|
+
import { Elysia as Elysia51 } from "elysia";
|
|
33279
34244
|
|
|
33280
34245
|
// src/toolRuntime.ts
|
|
33281
34246
|
var toErrorMessage4 = (error) => error instanceof Error ? error.message : String(error);
|
|
@@ -33484,7 +34449,7 @@ var createDefaultTurn = (caseId) => ({
|
|
|
33484
34449
|
});
|
|
33485
34450
|
var defaultApi = {};
|
|
33486
34451
|
var sameJSON = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
33487
|
-
var
|
|
34452
|
+
var escapeHtml48 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
33488
34453
|
var resolveSessionHref5 = (value, sessionId) => {
|
|
33489
34454
|
if (value === false) {
|
|
33490
34455
|
return;
|
|
@@ -33735,7 +34700,7 @@ var assertVoiceToolContractEvidence = (report, input = {}) => {
|
|
|
33735
34700
|
};
|
|
33736
34701
|
var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
33737
34702
|
const title = options.title ?? "Voice Tool Contracts";
|
|
33738
|
-
const snippet =
|
|
34703
|
+
const snippet = escapeHtml48(`app.use(
|
|
33739
34704
|
createVoiceToolContractRoutes({
|
|
33740
34705
|
htmlPath: '/tool-contracts',
|
|
33741
34706
|
path: '/api/tool-contracts',
|
|
@@ -33761,20 +34726,20 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
33761
34726
|
);`);
|
|
33762
34727
|
const contracts = report.contracts.map((contract) => {
|
|
33763
34728
|
const cases = contract.cases.map((testCase) => `<tr>
|
|
33764
|
-
<td>${testCase.operationsRecordHref ? `<a href="${
|
|
34729
|
+
<td>${testCase.operationsRecordHref ? `<a href="${escapeHtml48(testCase.operationsRecordHref)}">${escapeHtml48(testCase.label ?? testCase.caseId)}</a>` : escapeHtml48(testCase.label ?? testCase.caseId)}</td>
|
|
33765
34730
|
<td class="${testCase.pass ? "pass" : "fail"}">${testCase.pass ? "pass" : "fail"}</td>
|
|
33766
|
-
<td>${
|
|
33767
|
-
<td>${
|
|
34731
|
+
<td>${escapeHtml48(testCase.status)}</td>
|
|
34732
|
+
<td>${escapeHtml48(testCase.sessionId)}</td>
|
|
33768
34733
|
<td>${String(testCase.attempts)}</td>
|
|
33769
34734
|
<td>${String(testCase.elapsedMs)}ms</td>
|
|
33770
34735
|
<td>${testCase.timedOut ? "yes" : "no"}</td>
|
|
33771
|
-
<td>${
|
|
34736
|
+
<td>${escapeHtml48(testCase.issues.map((issue) => issue.message).join(" ") || testCase.error || "")}</td>
|
|
33772
34737
|
</tr>`).join("");
|
|
33773
34738
|
return `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
33774
34739
|
<div class="contract-header">
|
|
33775
34740
|
<div>
|
|
33776
|
-
<p class="eyebrow">${
|
|
33777
|
-
<h2>${
|
|
34741
|
+
<p class="eyebrow">${escapeHtml48(contract.toolName)}</p>
|
|
34742
|
+
<h2>${escapeHtml48(contract.label ?? contract.contractId)}</h2>
|
|
33778
34743
|
</div>
|
|
33779
34744
|
<strong class="${contract.pass ? "pass" : "fail"}">${contract.pass ? "Passing" : "Failing"}</strong>
|
|
33780
34745
|
</div>
|
|
@@ -33784,7 +34749,7 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
33784
34749
|
</table>
|
|
33785
34750
|
</section>`;
|
|
33786
34751
|
}).join("");
|
|
33787
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
34752
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml48(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.primitive,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(245,158,11,.12))}.primitive{background:#151b20;border-color:#5a4421}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}h2{margin:.2rem 0 1rem}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}.primitive p{color:#d8dee6;line-height:1.55}.primitive pre{background:#0f1217;border:1px solid #2a323a;border-radius:16px;color:#fef3c7;overflow:auto;padding:14px}.primitive code{color:#fef3c7}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left;vertical-align:top}th{color:#a8b0b8;font-size:.82rem}@media(max-width:800px){main{padding:18px}table{display:block;overflow:auto}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Tool Reliability</p><h1>${escapeHtml48(title)}</h1><div class="summary"><span class="pill ${report.status === "pass" ? "pass" : "fail"}">${escapeHtml48(report.status)}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceToolContractRoutes(...)</code> certifies tool behavior</h2><p>Define deterministic tool cases for retries, idempotency, timeouts, result shape, and error handling so assistant tools fail in pre-production instead of live calls.</p><pre><code>${snippet}</code></pre></section>${contracts || '<section class="contract"><p>No tool contracts configured.</p></section>'}</main></body></html>`;
|
|
33788
34753
|
};
|
|
33789
34754
|
var createVoiceToolContractJSONHandler = (options) => () => runVoiceToolContractSuite(options);
|
|
33790
34755
|
var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
@@ -33801,7 +34766,7 @@ var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
|
33801
34766
|
var createVoiceToolContractRoutes = (options) => {
|
|
33802
34767
|
const path = options.path ?? "/api/tool-contracts";
|
|
33803
34768
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
33804
|
-
const routes = new
|
|
34769
|
+
const routes = new Elysia51({
|
|
33805
34770
|
name: options.name ?? "absolutejs-voice-tool-contracts"
|
|
33806
34771
|
}).get(path, createVoiceToolContractJSONHandler(options));
|
|
33807
34772
|
if (htmlPath) {
|
|
@@ -33811,7 +34776,7 @@ var createVoiceToolContractRoutes = (options) => {
|
|
|
33811
34776
|
};
|
|
33812
34777
|
|
|
33813
34778
|
// src/simulationSuite.ts
|
|
33814
|
-
var
|
|
34779
|
+
var escapeHtml49 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
33815
34780
|
var summarizeSection = (report) => ({
|
|
33816
34781
|
failed: report.failed,
|
|
33817
34782
|
passed: report.passed,
|
|
@@ -34007,15 +34972,15 @@ var renderSection = (label, summary) => {
|
|
|
34007
34972
|
if (!summary) {
|
|
34008
34973
|
return "";
|
|
34009
34974
|
}
|
|
34010
|
-
return `<article class="${
|
|
34975
|
+
return `<article class="${escapeHtml49(summary.status)}"><span>${escapeHtml49(label)}</span><strong>${escapeHtml49(summary.status)}</strong><p>${summary.passed}/${summary.total} passed, ${summary.failed} failed.</p></article>`;
|
|
34011
34976
|
};
|
|
34012
34977
|
var renderAction = (action) => {
|
|
34013
|
-
const content = `<strong>${
|
|
34014
|
-
return action.href ? `<a class="action" href="${
|
|
34978
|
+
const content = `<strong>${escapeHtml49(action.label)}</strong><p>${escapeHtml49(action.description)}</p><span>${escapeHtml49(action.section)} / ${escapeHtml49(action.severity)}</span>`;
|
|
34979
|
+
return action.href ? `<a class="action" href="${escapeHtml49(action.href)}">${content}</a>` : `<article class="action">${content}</article>`;
|
|
34015
34980
|
};
|
|
34016
34981
|
var renderVoiceSimulationSuiteHTML = (report, options = {}) => {
|
|
34017
34982
|
const title = options.title ?? "Voice Simulation Suite";
|
|
34018
|
-
const snippet =
|
|
34983
|
+
const snippet = escapeHtml49(`app.use(
|
|
34019
34984
|
createVoiceSimulationSuiteRoutes({
|
|
34020
34985
|
htmlPath: '/voice/simulations',
|
|
34021
34986
|
path: '/api/voice/simulations',
|
|
@@ -34048,12 +35013,12 @@ app.use(
|
|
|
34048
35013
|
store: traceStore
|
|
34049
35014
|
})
|
|
34050
35015
|
);`);
|
|
34051
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
35016
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml49(title)}</title><style>body{background:#10151c;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}.hero,.primitive{background:linear-gradient(135deg,rgba(34,197,94,.18),rgba(59,130,246,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.primitive{background:#151d27;border-color:#355078}.eyebrow{color:#93c5fd;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.badge{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.grid,.actions{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin:18px 0}.grid article,.action{background:#151d27;border:1px solid #283544;border-radius:18px;color:inherit;padding:16px;text-decoration:none}.grid span,.action span{color:#aab5c0}.grid strong{display:block;font-size:2rem;margin:.25rem 0;text-transform:uppercase}.action strong{display:block;color:#f8f3e7;margin-bottom:.35rem}.action p,.primitive p{color:#d8dee6;line-height:1.55;margin:.3rem 0 .6rem}pre{background:#151d27;border:1px solid #283544;border-radius:18px;overflow:auto;padding:16px}.primitive pre{background:#0b1118;color:#dbeafe}.primitive code{color:#bfdbfe}</style></head><body><main><section class="hero"><p class="eyebrow">Pre-production proof</p><h1>${escapeHtml49(title)}</h1><p>One report for session quality, scenario evals, fixture simulations, tool contracts, and outcome contracts.</p><p class="badge ${escapeHtml49(report.status)}">Status: ${escapeHtml49(report.status)}</p><section class="grid">${renderSection("Sessions", report.summary.sessions)}${renderSection("Scenarios", report.summary.scenarios)}${renderSection("Fixtures", report.summary.fixtures)}${renderSection("Tools", report.summary.tools)}${renderSection("Outcomes", report.summary.outcomes)}</section></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceSimulationSuiteRoutes(...)</code> builds this pre-production proof surface</h2><p>Run session quality checks, scenario evals, fixture-backed simulations, tool contracts, and outcome contracts from one route group before live traffic sees a regression.</p><pre><code>${snippet}</code></pre></section><h2>Actions</h2><section class="actions">${report.actions.length > 0 ? report.actions.map(renderAction).join("") : '<article class="action"><strong>No action required</strong><p>All enabled simulation sections are passing.</p></article>'}</section><pre>${escapeHtml49(JSON.stringify({ summary: report.summary, actions: report.actions }, null, 2))}</pre></main></body></html>`;
|
|
34052
35017
|
};
|
|
34053
35018
|
var createVoiceSimulationSuiteRoutes = (options) => {
|
|
34054
35019
|
const path = options.path ?? "/api/voice/simulations";
|
|
34055
35020
|
const htmlPath = options.htmlPath === undefined ? "/voice/simulations" : options.htmlPath;
|
|
34056
|
-
const app = new
|
|
35021
|
+
const app = new Elysia52({
|
|
34057
35022
|
name: options.name ?? "absolutejs-voice-simulation-suite"
|
|
34058
35023
|
}).get(path, () => runVoiceSimulationSuite(options));
|
|
34059
35024
|
if (htmlPath) {
|
|
@@ -34680,10 +35645,10 @@ var assertVoiceAgentSquadContractEvidence = (reports, input = {}) => {
|
|
|
34680
35645
|
return report;
|
|
34681
35646
|
};
|
|
34682
35647
|
// src/turnLatency.ts
|
|
34683
|
-
import { Elysia as
|
|
35648
|
+
import { Elysia as Elysia53 } from "elysia";
|
|
34684
35649
|
var DEFAULT_WARN_AFTER_MS2 = 1800;
|
|
34685
35650
|
var DEFAULT_FAIL_AFTER_MS2 = 3200;
|
|
34686
|
-
var
|
|
35651
|
+
var escapeHtml50 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
34687
35652
|
var firstNumber4 = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
34688
35653
|
var getString19 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
34689
35654
|
var createTraceStageIndex = (events) => {
|
|
@@ -34798,7 +35763,7 @@ var summarizeVoiceTurnLatency = async (options) => {
|
|
|
34798
35763
|
warnings
|
|
34799
35764
|
};
|
|
34800
35765
|
};
|
|
34801
|
-
var
|
|
35766
|
+
var formatMs5 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
34802
35767
|
var renderVoiceTurnLatencyHTML = (report, options = {}) => {
|
|
34803
35768
|
const title = options.title ?? "Voice Turn Latency";
|
|
34804
35769
|
const snippet = `app.use(
|
|
@@ -34819,11 +35784,11 @@ await traceStore.append({
|
|
|
34819
35784
|
turnId,
|
|
34820
35785
|
type: 'turn_latency.stage'
|
|
34821
35786
|
});`;
|
|
34822
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
34823
|
-
<header><div><p class="eyebrow">${
|
|
34824
|
-
<dl>${turn.stages.map((stage) => `<div><dt>${
|
|
35787
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml50(turn.status)}">
|
|
35788
|
+
<header><div><p class="eyebrow">${escapeHtml50(turn.sessionId)} \xB7 ${escapeHtml50(turn.turnId)}</p><h2>${escapeHtml50(turn.text || "Empty turn")}</h2></div><strong>${escapeHtml50(turn.status)}</strong></header>
|
|
35789
|
+
<dl>${turn.stages.map((stage) => `<div><dt>${escapeHtml50(stage.label)}</dt><dd>${escapeHtml50(formatMs5(stage.valueMs))}</dd></div>`).join("")}</dl>
|
|
34825
35790
|
</article>`).join("");
|
|
34826
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
35791
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml50(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.turn,.primitive{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(94,234,212,.16),rgba(251,191,36,.1))}.eyebrow{color:#5eead4;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.primitive p{color:#cbd5e1}.primitive pre{background:#0a0d10;border:1px solid #2a323a;border-radius:16px;color:#d9fff7;overflow:auto;padding:16px}.turn header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.warn,.empty{color:#fde68a}.fail{color:#fca5a5}.turn.fail{border-color:rgba(248,113,113,.45)}dl{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{font-weight:900;margin:0}@media(max-width:800px){main{padding:18px}.turn header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">End-to-end responsiveness</p><h1>${escapeHtml50(title)}</h1><div class="summary"><span class="pill ${escapeHtml50(report.status)}">${escapeHtml50(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">avg ${escapeHtml50(formatMs5(report.averageTotalMs))}</span><span class="pill">${String(report.warnings)} warnings</span><span class="pill">${String(report.failed)} failed</span></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceTurnLatencyRoutes(...)</code> exposes the full turn waterfall</h2><p>Attach stage traces for speech detection, commit, model response, TTS send, and first audio so teams can prove where latency actually comes from.</p><pre><code>${escapeHtml50(snippet)}</code></pre></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
34827
35792
|
};
|
|
34828
35793
|
var createVoiceTurnLatencyJSONHandler = (options) => async () => summarizeVoiceTurnLatency(options);
|
|
34829
35794
|
var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
@@ -34840,7 +35805,7 @@ var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
|
34840
35805
|
var createVoiceTurnLatencyRoutes = (options) => {
|
|
34841
35806
|
const path = options.path ?? "/api/turn-latency";
|
|
34842
35807
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
34843
|
-
const routes = new
|
|
35808
|
+
const routes = new Elysia53({
|
|
34844
35809
|
name: options.name ?? "absolutejs-voice-turn-latency"
|
|
34845
35810
|
}).get(path, createVoiceTurnLatencyJSONHandler(options));
|
|
34846
35811
|
if (htmlPath) {
|
|
@@ -34849,8 +35814,8 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
34849
35814
|
return routes;
|
|
34850
35815
|
};
|
|
34851
35816
|
// src/liveLatency.ts
|
|
34852
|
-
import { Elysia as
|
|
34853
|
-
var
|
|
35817
|
+
import { Elysia as Elysia54 } from "elysia";
|
|
35818
|
+
var escapeHtml51 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
34854
35819
|
var percentile6 = (values, percentileValue) => {
|
|
34855
35820
|
if (values.length === 0) {
|
|
34856
35821
|
return;
|
|
@@ -34895,7 +35860,7 @@ var summarizeVoiceLiveLatency = async (options) => {
|
|
|
34895
35860
|
warnings
|
|
34896
35861
|
};
|
|
34897
35862
|
};
|
|
34898
|
-
var
|
|
35863
|
+
var formatMs6 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
34899
35864
|
var renderVoiceLiveLatencyHTML = (report, options = {}) => {
|
|
34900
35865
|
const title = options.title ?? "Voice Live Latency";
|
|
34901
35866
|
const snippet = `app.use(
|
|
@@ -34917,13 +35882,13 @@ await traceStore.append({
|
|
|
34917
35882
|
sessionId,
|
|
34918
35883
|
type: 'client.live_latency'
|
|
34919
35884
|
});`;
|
|
34920
|
-
const rows = report.recent.map((sample) => `<tr><td>${
|
|
34921
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
35885
|
+
const rows = report.recent.map((sample) => `<tr><td>${escapeHtml51(sample.sessionId)}</td><td>${escapeHtml51(formatMs6(sample.latencyMs))}</td><td>${escapeHtml51(sample.status ?? "unknown")}</td><td>${escapeHtml51(new Date(sample.at).toLocaleString())}</td></tr>`).join("");
|
|
35886
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml51(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero{background:linear-gradient(135deg,rgba(94,234,212,.16),rgba(245,158,11,.1));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.warn,.empty{color:#fbbf24}.fail{color:#fca5a5}.metrics{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:18px 0}.metrics article,table,.primitive{background:#141922;border:1px solid #26313d;border-radius:18px}.metrics article,.primitive{padding:16px}.metrics span{color:#a8b0b8}.metrics strong{display:block;font-size:2rem;margin-top:.25rem}.primitive{margin:0 0 18px}.primitive h2{margin:.2rem 0 .5rem}.primitive p{color:#cbd5e1}.primitive pre{background:#080b10;border:1px solid #26313d;border-radius:16px;color:#d9fff7;overflow:auto;padding:16px}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #26313d;padding:12px;text-align:left}@media(max-width:760px){main{padding:20px}}</style></head><body><main><section class="hero"><p class="eyebrow">Browser proof</p><h1>${escapeHtml51(title)}</h1><p>Recent real browser speech-to-assistant response measurements from persisted <code>client.live_latency</code> traces.</p><p class="status ${escapeHtml51(report.status)}">Status: ${escapeHtml51(report.status)}</p><section class="metrics"><article><span>p50</span><strong>${escapeHtml51(formatMs6(report.p50LatencyMs))}</strong></article><article><span>p95</span><strong>${escapeHtml51(formatMs6(report.p95LatencyMs))}</strong></article><article><span>Average</span><strong>${escapeHtml51(formatMs6(report.averageLatencyMs))}</strong></article><article><span>Samples</span><strong>${String(report.total)}</strong></article></section></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceLiveLatencyRoutes(...)</code> turns real browser timing into a release gate</h2><p>Persist live timing samples into the trace store so readiness, simulations, and trace timelines all point at the same self-hosted proof.</p><pre><code>${escapeHtml51(snippet)}</code></pre></section><table><thead><tr><th>Session</th><th>Latency</th><th>Status</th><th>Measured</th></tr></thead><tbody>${rows || '<tr><td colspan="4">No live latency samples yet.</td></tr>'}</tbody></table></main></body></html>`;
|
|
34922
35887
|
};
|
|
34923
35888
|
var createVoiceLiveLatencyRoutes = (options) => {
|
|
34924
35889
|
const path = options.path ?? "/api/live-latency";
|
|
34925
35890
|
const htmlPath = options.htmlPath === undefined ? "/live-latency" : options.htmlPath;
|
|
34926
|
-
const routes = new
|
|
35891
|
+
const routes = new Elysia54({
|
|
34927
35892
|
name: options.name ?? "absolutejs-voice-live-latency"
|
|
34928
35893
|
}).get(path, () => summarizeVoiceLiveLatency(options));
|
|
34929
35894
|
if (htmlPath) {
|
|
@@ -34940,9 +35905,9 @@ var createVoiceLiveLatencyRoutes = (options) => {
|
|
|
34940
35905
|
return routes;
|
|
34941
35906
|
};
|
|
34942
35907
|
// src/turnQuality.ts
|
|
34943
|
-
import { Elysia as
|
|
35908
|
+
import { Elysia as Elysia55 } from "elysia";
|
|
34944
35909
|
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
34945
|
-
var
|
|
35910
|
+
var escapeHtml52 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
34946
35911
|
var getTurnLatencyMs = (turn) => {
|
|
34947
35912
|
const firstTranscriptAt = turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs).filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
34948
35913
|
if (firstTranscriptAt === undefined) {
|
|
@@ -35012,24 +35977,24 @@ var summarizeVoiceTurnQuality = async (options) => {
|
|
|
35012
35977
|
};
|
|
35013
35978
|
var renderVoiceTurnQualityHTML = (report, options = {}) => {
|
|
35014
35979
|
const title = options.title ?? "Voice Turn Quality";
|
|
35015
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
35980
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml52(turn.status)}">
|
|
35016
35981
|
<div class="turn-header">
|
|
35017
35982
|
<div>
|
|
35018
|
-
<p class="eyebrow">${
|
|
35019
|
-
<h2>${
|
|
35983
|
+
<p class="eyebrow">${escapeHtml52(turn.sessionId)} \xB7 ${escapeHtml52(turn.turnId)}</p>
|
|
35984
|
+
<h2>${escapeHtml52(turn.text || "Empty turn")}</h2>
|
|
35020
35985
|
</div>
|
|
35021
|
-
<strong>${
|
|
35986
|
+
<strong>${escapeHtml52(turn.status)}</strong>
|
|
35022
35987
|
</div>
|
|
35023
35988
|
<dl>
|
|
35024
|
-
<div><dt>Source</dt><dd>${
|
|
35989
|
+
<div><dt>Source</dt><dd>${escapeHtml52(turn.source ?? "unknown")}</dd></div>
|
|
35025
35990
|
<div><dt>Confidence</dt><dd>${turn.averageConfidence === undefined ? "n/a" : `${Math.round(turn.averageConfidence * 100)}%`}</dd></div>
|
|
35026
|
-
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${
|
|
35027
|
-
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${
|
|
35991
|
+
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${escapeHtml52(turn.fallbackSelectionReason ?? "selected")})` : "no"}</dd></div>
|
|
35992
|
+
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${escapeHtml52(turn.correctionProvider)}` : ""}` : "none"}</dd></div>
|
|
35028
35993
|
<div><dt>Transcripts</dt><dd>${String(turn.selectedTranscriptCount)} selected \xB7 ${String(turn.finalTranscriptCount)} final \xB7 ${String(turn.partialTranscriptCount)} partial</dd></div>
|
|
35029
35994
|
<div><dt>Cost</dt><dd>${turn.costUnits === undefined ? "n/a" : String(turn.costUnits)}</dd></div>
|
|
35030
35995
|
</dl>
|
|
35031
35996
|
</article>`).join("");
|
|
35032
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
35997
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml52(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.turn{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(251,191,36,.16),rgba(34,197,94,.1))}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.turn-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.warn,.unknown{color:#fde68a}.fail{color:#fca5a5}.turn.fail{border-color:rgba(248,113,113,.45)}dl{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0}@media(max-width:800px){main{padding:18px}.turn-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Realtime STT Debugging</p><h1>${escapeHtml52(title)}</h1><div class="summary"><span class="pill ${escapeHtml52(report.status)}">${escapeHtml52(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">${String(report.warnings)} warnings</span><span class="pill">${String(report.failed)} failed</span><span class="pill">${String(report.sessions)} sessions</span></div></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
35033
35998
|
};
|
|
35034
35999
|
var createVoiceTurnQualityJSONHandler = (options) => async () => summarizeVoiceTurnQuality(options);
|
|
35035
36000
|
var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
@@ -35046,7 +36011,7 @@ var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
|
35046
36011
|
var createVoiceTurnQualityRoutes = (options) => {
|
|
35047
36012
|
const path = options.path ?? "/api/turn-quality";
|
|
35048
36013
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
35049
|
-
const routes = new
|
|
36014
|
+
const routes = new Elysia55({
|
|
35050
36015
|
name: options.name ?? "absolutejs-voice-turn-quality"
|
|
35051
36016
|
}).get(path, createVoiceTurnQualityJSONHandler(options));
|
|
35052
36017
|
if (htmlPath) {
|
|
@@ -35055,10 +36020,10 @@ var createVoiceTurnQualityRoutes = (options) => {
|
|
|
35055
36020
|
return routes;
|
|
35056
36021
|
};
|
|
35057
36022
|
// src/phoneAgent.ts
|
|
35058
|
-
import { Elysia as
|
|
36023
|
+
import { Elysia as Elysia57 } from "elysia";
|
|
35059
36024
|
|
|
35060
36025
|
// src/phoneAgentProductionSmoke.ts
|
|
35061
|
-
import { Elysia as
|
|
36026
|
+
import { Elysia as Elysia56 } from "elysia";
|
|
35062
36027
|
var defaultRequirements = [
|
|
35063
36028
|
"media-started",
|
|
35064
36029
|
"transcript",
|
|
@@ -35066,7 +36031,7 @@ var defaultRequirements = [
|
|
|
35066
36031
|
"lifecycle-outcome",
|
|
35067
36032
|
"no-session-error"
|
|
35068
36033
|
];
|
|
35069
|
-
var
|
|
36034
|
+
var escapeHtml53 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
35070
36035
|
var payloadType = (event) => typeof event.payload.type === "string" ? event.payload.type : undefined;
|
|
35071
36036
|
var hasTextPayload = (event) => ["text", "assistantText", "transcript"].some((key) => {
|
|
35072
36037
|
const value = event.payload[key];
|
|
@@ -35175,10 +36140,10 @@ var resolveHandlerOptions = async (options, input) => ({
|
|
|
35175
36140
|
});
|
|
35176
36141
|
var renderVoicePhoneAgentProductionSmokeHTML = (report, options = {}) => {
|
|
35177
36142
|
const title = options.title ?? "AbsoluteJS Voice Phone Smoke Contract";
|
|
35178
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
35179
|
-
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${
|
|
35180
|
-
const requirements = report.required.map((requirement) => `<span class="pill">${
|
|
35181
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
36143
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml53(issue.requirement)}</strong>: ${escapeHtml53(issue.message)}</li>`).join("");
|
|
36144
|
+
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${escapeHtml53(outcome)}</span>`).join("");
|
|
36145
|
+
const requirements = report.required.map((requirement) => `<span class="pill">${escapeHtml53(requirement)}</span>`).join("");
|
|
36146
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml53(title)}</title><style>body{background:#0e141b;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1050px;padding:32px}.hero,.panel{background:#151d26;border:1px solid #283544;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.metric{background:#0f151d;border:1px solid #283544;border-radius:16px;padding:14px}.metric strong{display:block;font-size:1.8rem}.pill{background:#0f151d;border:1px solid #3f3f46;border-radius:999px;display:inline-flex;margin:4px;padding:7px 10px}.issues{color:#fca5a5}code{color:#fde68a}@media(max-width:720px){main{padding:18px}}</style></head><body><main><section class="hero"><p class="eyebrow">Phone agent production smoke</p><h1>${escapeHtml53(title)}</h1><p class="status ${report.pass ? "pass" : "fail"}">${report.pass ? "PASS" : "FAIL"}</p><p>Contract <code>${escapeHtml53(report.contractId)}</code>${report.provider ? ` for <code>${escapeHtml53(report.provider)}</code>` : ""}${report.sessionId ? ` on session <code>${escapeHtml53(report.sessionId)}</code>` : ""}.</p></section><section class="panel"><h2>Observed Trace Evidence</h2><div class="grid"><div class="metric"><span>Media starts</span><strong>${String(report.observed.mediaStarts)}</strong></div><div class="metric"><span>Transcripts</span><strong>${String(report.observed.transcripts)}</strong></div><div class="metric"><span>Assistant responses</span><strong>${String(report.observed.assistantResponses)}</strong></div><div class="metric"><span>Session errors</span><strong>${String(report.observed.sessionErrors)}</strong></div></div><p>${outcomes || '<span class="pill">No lifecycle outcome</span>'}</p></section><section class="panel"><h2>Requirements</h2><p>${requirements}</p>${issues ? `<ul class="issues">${issues}</ul>` : '<p class="pass">All required phone-agent smoke evidence is present.</p>'}</section></main></body></html>`;
|
|
35182
36147
|
};
|
|
35183
36148
|
var createVoicePhoneAgentProductionSmokeJSONHandler = (options) => async ({
|
|
35184
36149
|
query,
|
|
@@ -35201,7 +36166,7 @@ var createVoicePhoneAgentProductionSmokeHTMLHandler = (options) => async ({
|
|
|
35201
36166
|
var createVoicePhoneAgentProductionSmokeRoutes = (options) => {
|
|
35202
36167
|
const path = options.path ?? "/api/voice/phone/smoke-contract";
|
|
35203
36168
|
const htmlPath = options.htmlPath === undefined ? "/voice/phone/smoke-contract" : options.htmlPath;
|
|
35204
|
-
const routes = new
|
|
36169
|
+
const routes = new Elysia56({
|
|
35205
36170
|
name: options.name ?? "absolutejs-voice-phone-smoke-contract"
|
|
35206
36171
|
}).get(path, createVoicePhoneAgentProductionSmokeJSONHandler(options));
|
|
35207
36172
|
if (htmlPath) {
|
|
@@ -35244,7 +36209,7 @@ var PHONE_AGENT_LIFECYCLE_STAGES = [
|
|
|
35244
36209
|
"completed",
|
|
35245
36210
|
"failed"
|
|
35246
36211
|
];
|
|
35247
|
-
var
|
|
36212
|
+
var escapeHtml54 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
35248
36213
|
var loadRouteJson = async (input) => {
|
|
35249
36214
|
const response = await input.app.handle(new Request(new URL(input.path, input.origin).toString(), {
|
|
35250
36215
|
headers: {
|
|
@@ -35482,10 +36447,10 @@ var renderVoicePhoneAgentSetupHTML = (report) => {
|
|
|
35482
36447
|
const entry = findCarrierMatrixEntry(report.matrix, carrier);
|
|
35483
36448
|
const urls = entry?.setup.urls;
|
|
35484
36449
|
const primaryUrl = carrier.provider === "plivo" ? urls?.twiml : urls?.twiml;
|
|
35485
|
-
return `<tr><td>${
|
|
36450
|
+
return `<tr><td>${escapeHtml54(carrier.name ?? carrier.provider)}</td><td>${escapeHtml54(carrier.provider)}</td><td><code>${escapeHtml54(carrier.setupPath || "disabled")}</code></td><td><code>${escapeHtml54(carrier.smokePath || "disabled")}</code></td><td>${entry ? `<span class="${escapeHtml54(entry.status)}">${escapeHtml54(entry.status.toUpperCase())}</span>` : "unknown"}</td><td>${primaryUrl ? `<code>${escapeHtml54(primaryUrl)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.webhook ? `<code>${escapeHtml54(urls.webhook)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.stream ? `<code>${escapeHtml54(urls.stream)}</code>` : '<span class="muted">missing</span>'}</td></tr>`;
|
|
35486
36451
|
}).join("");
|
|
35487
|
-
const stageList = report.lifecycleStages.map((stage) => `<li><code>${
|
|
35488
|
-
const snippet =
|
|
36452
|
+
const stageList = report.lifecycleStages.map((stage) => `<li><code>${escapeHtml54(stage)}</code></li>`).join("");
|
|
36453
|
+
const snippet = escapeHtml54(`const phoneAgent = createVoicePhoneAgent({
|
|
35489
36454
|
carriers: [
|
|
35490
36455
|
{
|
|
35491
36456
|
provider: 'twilio',
|
|
@@ -35519,11 +36484,11 @@ app.use(
|
|
|
35519
36484
|
);`);
|
|
35520
36485
|
const checklist = report.carriers.map((carrier) => {
|
|
35521
36486
|
const instruction = report.setupInstructions.find((candidate) => candidate.provider === carrier.provider && candidate.carrierName === (carrier.name ?? carrier.provider));
|
|
35522
|
-
const issueList = instruction?.issues.map((issue) => `<li>${
|
|
35523
|
-
const steps = instruction?.steps.map((step) => `<li>${
|
|
35524
|
-
return `<article><h3>${
|
|
36487
|
+
const issueList = instruction?.issues.map((issue) => `<li>${escapeHtml54(issue)}</li>`).join("") ?? "";
|
|
36488
|
+
const steps = instruction?.steps.map((step) => `<li>${escapeHtml54(step)}</li>`).join("") ?? "";
|
|
36489
|
+
return `<article><h3>${escapeHtml54(carrier.name ?? carrier.provider)}</h3><ol>${steps}</ol>${issueList ? `<ul class="issues">${issueList}</ul>` : '<p class="pass">No carrier contract issues.</p>'}</article>`;
|
|
35525
36490
|
}).join("");
|
|
35526
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
36491
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml54(report.title)}</title><style>body{background:#10151c;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.primitive{background:linear-gradient(135deg,rgba(20,184,166,.2),rgba(245,158,11,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.primitive{background:#151d27;border-color:#365a60}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.badge{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.warn{color:#fde68a}.muted{color:#aab5c0}table{background:#151d27;border:1px solid #283544;border-collapse:collapse;border-radius:18px;display:block;overflow:auto;width:100%}td,th{border-bottom:1px solid #283544;padding:12px;text-align:left;vertical-align:top}code{color:#fde68a;overflow-wrap:anywhere}.primitive p{color:#cbd5de;line-height:1.55}.primitive pre{background:#0b1118;border:1px solid #283544;border-radius:18px;color:#fef3c7;overflow:auto;padding:16px}.checklist{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));margin:18px 0}.checklist article{background:#151d27;border:1px solid #283544;border-radius:18px;padding:18px}.checklist ol{padding-left:20px}.issues{color:#fca5a5}.stages{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));padding-left:18px}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Phone agent setup</p><h1>${escapeHtml54(report.title)}</h1><p>One self-hosted entrypoint for carrier routes, setup reports, smoke checks, and normalized call lifecycle stages.</p><p class="badge ${report.ready ? "pass" : "fail"}">Ready: ${String(report.ready)}</p>${report.matrixPath ? `<p><a href="${escapeHtml54(report.matrixPath)}?format=html">Open carrier matrix</a></p>` : ""}</section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoicePhoneAgent(...)</code> builds this carrier control plane</h2><p>Mount carrier routes once, expose setup and smoke proof, then feed the same carrier matrix and phone-agent smoke reports into production readiness so carrier regressions block deploys.</p><pre><code>${snippet}</code></pre></section><h2>Carrier Setup Checklist</h2><section class="checklist">${checklist}</section><h2>Carrier URLs</h2><table><thead><tr><th>Name</th><th>Provider</th><th>Setup</th><th>Smoke</th><th>Status</th><th>Answer/TwiML/TeXML</th><th>Webhook</th><th>Stream</th></tr></thead><tbody>${carrierRows}</tbody></table><h2>Lifecycle Schema</h2><ul class="stages">${stageList}</ul></main></body></html>`;
|
|
35527
36492
|
};
|
|
35528
36493
|
var createVoicePhoneAgent = (options) => {
|
|
35529
36494
|
const carrierSummaries = options.carriers.map((carrier) => ({
|
|
@@ -35532,7 +36497,7 @@ var createVoicePhoneAgent = (options) => {
|
|
|
35532
36497
|
setupPath: resolveSetupPath(carrier),
|
|
35533
36498
|
smokePath: resolveSmokePath(carrier)
|
|
35534
36499
|
}));
|
|
35535
|
-
const app = new
|
|
36500
|
+
const app = new Elysia57({
|
|
35536
36501
|
name: options.name ?? "absolutejs-voice-phone-agent"
|
|
35537
36502
|
});
|
|
35538
36503
|
for (const carrier of options.carriers) {
|
|
@@ -35624,21 +36589,21 @@ var createVoicePhoneAgent = (options) => {
|
|
|
35624
36589
|
};
|
|
35625
36590
|
// src/fileStore.ts
|
|
35626
36591
|
import {
|
|
35627
|
-
mkdir as
|
|
36592
|
+
mkdir as mkdir5,
|
|
35628
36593
|
readFile as readFile2,
|
|
35629
36594
|
readdir,
|
|
35630
36595
|
rename,
|
|
35631
36596
|
rm,
|
|
35632
36597
|
stat as stat2,
|
|
35633
|
-
writeFile
|
|
36598
|
+
writeFile as writeFile2
|
|
35634
36599
|
} from "fs/promises";
|
|
35635
|
-
import { join as
|
|
36600
|
+
import { join as join4 } from "path";
|
|
35636
36601
|
var listJsonFiles = async (directory) => {
|
|
35637
36602
|
try {
|
|
35638
36603
|
const entries = await readdir(directory, {
|
|
35639
36604
|
withFileTypes: true
|
|
35640
36605
|
});
|
|
35641
|
-
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) =>
|
|
36606
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => join4(directory, entry.name));
|
|
35642
36607
|
} catch (error) {
|
|
35643
36608
|
if (error.code === "ENOENT") {
|
|
35644
36609
|
return [];
|
|
@@ -35656,7 +36621,7 @@ var listRecentJsonFiles = async (directory, limit) => {
|
|
|
35656
36621
|
}
|
|
35657
36622
|
return (await rebuildRecentJsonFileIndex(directory)).slice(0, limit).map((entry) => entry.path);
|
|
35658
36623
|
};
|
|
35659
|
-
var recentJsonFileIndexPath = (directory) =>
|
|
36624
|
+
var recentJsonFileIndexPath = (directory) => join4(directory, ".recent-index");
|
|
35660
36625
|
var sortRecentJsonFileIndexEntries = (entries) => entries.sort((left, right) => right.updatedAt - left.updatedAt);
|
|
35661
36626
|
var readRecentJsonFileIndex = async (directory) => {
|
|
35662
36627
|
try {
|
|
@@ -35670,12 +36635,12 @@ var readRecentJsonFileIndex = async (directory) => {
|
|
|
35670
36635
|
}
|
|
35671
36636
|
};
|
|
35672
36637
|
var writeRecentJsonFileIndex = async (directory, files) => {
|
|
35673
|
-
await
|
|
36638
|
+
await mkdir5(directory, {
|
|
35674
36639
|
recursive: true
|
|
35675
36640
|
});
|
|
35676
36641
|
const path = recentJsonFileIndexPath(directory);
|
|
35677
36642
|
const tempPath = `${path}.${crypto.randomUUID()}.tmp`;
|
|
35678
|
-
await
|
|
36643
|
+
await writeFile2(tempPath, JSON.stringify({
|
|
35679
36644
|
files: sortRecentJsonFileIndexEntries(files).slice(0, 5000),
|
|
35680
36645
|
version: 1
|
|
35681
36646
|
}));
|
|
@@ -35751,15 +36716,15 @@ var omitReadWindow = (filter) => {
|
|
|
35751
36716
|
return next;
|
|
35752
36717
|
};
|
|
35753
36718
|
var encodeStoreId = (id) => `${encodeURIComponent(id)}.json`;
|
|
35754
|
-
var resolveFilePath = (directory, id) =>
|
|
36719
|
+
var resolveFilePath = (directory, id) => join4(directory, encodeStoreId(id));
|
|
35755
36720
|
var createMemoryStoreId = (input) => `${input.assistantId}:${input.namespace}:${input.key}`;
|
|
35756
36721
|
var readJsonFile = async (path) => JSON.parse(await readFile2(path, "utf8"));
|
|
35757
36722
|
var writeJsonFile = async (path, value, options) => {
|
|
35758
|
-
await
|
|
36723
|
+
await mkdir5(options.directory, {
|
|
35759
36724
|
recursive: true
|
|
35760
36725
|
});
|
|
35761
36726
|
const tempPath = `${path}.${crypto.randomUUID()}.tmp`;
|
|
35762
|
-
await
|
|
36727
|
+
await writeFile2(tempPath, JSON.stringify(value, null, options.pretty === false ? undefined : 2));
|
|
35763
36728
|
await rename(tempPath, path);
|
|
35764
36729
|
};
|
|
35765
36730
|
var createVoiceFileSessionStore = (options) => {
|
|
@@ -36133,51 +37098,51 @@ var createVoiceFileIncidentBundleStore = (options) => {
|
|
|
36133
37098
|
var createVoiceFileRuntimeStorage = (options) => ({
|
|
36134
37099
|
audit: createVoiceFileAuditEventStore({
|
|
36135
37100
|
...options,
|
|
36136
|
-
directory:
|
|
37101
|
+
directory: join4(options.directory, "audit")
|
|
36137
37102
|
}),
|
|
36138
37103
|
auditDeliveries: createVoiceFileAuditSinkDeliveryStore({
|
|
36139
37104
|
...options,
|
|
36140
|
-
directory:
|
|
37105
|
+
directory: join4(options.directory, "audit-deliveries")
|
|
36141
37106
|
}),
|
|
36142
37107
|
campaigns: createVoiceFileCampaignStore({
|
|
36143
37108
|
...options,
|
|
36144
|
-
directory:
|
|
37109
|
+
directory: join4(options.directory, "campaigns")
|
|
36145
37110
|
}),
|
|
36146
37111
|
events: createVoiceFileIntegrationEventStore({
|
|
36147
37112
|
...options,
|
|
36148
|
-
directory:
|
|
37113
|
+
directory: join4(options.directory, "events")
|
|
36149
37114
|
}),
|
|
36150
37115
|
externalObjects: createVoiceFileExternalObjectMapStore({
|
|
36151
37116
|
...options,
|
|
36152
|
-
directory:
|
|
37117
|
+
directory: join4(options.directory, "external-objects")
|
|
36153
37118
|
}),
|
|
36154
37119
|
incidentBundles: createVoiceFileIncidentBundleStore({
|
|
36155
37120
|
...options,
|
|
36156
|
-
directory:
|
|
37121
|
+
directory: join4(options.directory, "incident-bundles")
|
|
36157
37122
|
}),
|
|
36158
37123
|
memories: createVoiceFileAssistantMemoryStore({
|
|
36159
37124
|
...options,
|
|
36160
|
-
directory:
|
|
37125
|
+
directory: join4(options.directory, "memories")
|
|
36161
37126
|
}),
|
|
36162
37127
|
reviews: createVoiceFileReviewStore({
|
|
36163
37128
|
...options,
|
|
36164
|
-
directory:
|
|
37129
|
+
directory: join4(options.directory, "reviews")
|
|
36165
37130
|
}),
|
|
36166
37131
|
session: createVoiceFileSessionStore({
|
|
36167
37132
|
...options,
|
|
36168
|
-
directory:
|
|
37133
|
+
directory: join4(options.directory, "sessions")
|
|
36169
37134
|
}),
|
|
36170
37135
|
tasks: createVoiceFileTaskStore({
|
|
36171
37136
|
...options,
|
|
36172
|
-
directory:
|
|
37137
|
+
directory: join4(options.directory, "tasks")
|
|
36173
37138
|
}),
|
|
36174
37139
|
traceDeliveries: createVoiceFileTraceSinkDeliveryStore({
|
|
36175
37140
|
...options,
|
|
36176
|
-
directory:
|
|
37141
|
+
directory: join4(options.directory, "trace-deliveries")
|
|
36177
37142
|
}),
|
|
36178
37143
|
traces: createVoiceFileTraceEventStore({
|
|
36179
37144
|
...options,
|
|
36180
|
-
directory:
|
|
37145
|
+
directory: join4(options.directory, "traces")
|
|
36181
37146
|
})
|
|
36182
37147
|
});
|
|
36183
37148
|
var createStoredVoiceCallReviewArtifact = (id, artifact) => withVoiceCallReviewId(id, artifact);
|
|
@@ -37281,8 +38246,8 @@ var createOpenAIVoiceTTS = (options) => {
|
|
|
37281
38246
|
};
|
|
37282
38247
|
};
|
|
37283
38248
|
// src/providerCapabilities.ts
|
|
37284
|
-
import { Elysia as
|
|
37285
|
-
var
|
|
38249
|
+
import { Elysia as Elysia58 } from "elysia";
|
|
38250
|
+
var escapeHtml55 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37286
38251
|
var fromProviderList = (kind, providers, options) => (providers ?? []).map((provider) => ({
|
|
37287
38252
|
configured: true,
|
|
37288
38253
|
features: options.features?.[provider],
|
|
@@ -37347,27 +38312,27 @@ var summarizeVoiceProviderCapabilities = async (options) => {
|
|
|
37347
38312
|
var renderVoiceProviderCapabilityHTML = (report, options = {}) => {
|
|
37348
38313
|
const title = options.title ?? "Voice Provider Capabilities";
|
|
37349
38314
|
const cards = report.capabilities.map((capability) => {
|
|
37350
|
-
const features = (capability.features ?? []).map((feature) => `<span class="pill">${
|
|
37351
|
-
return `<article class="card ${
|
|
38315
|
+
const features = (capability.features ?? []).map((feature) => `<span class="pill">${escapeHtml55(feature)}</span>`).join("");
|
|
38316
|
+
return `<article class="card ${escapeHtml55(capability.status)}">
|
|
37352
38317
|
<div class="card-header">
|
|
37353
38318
|
<div>
|
|
37354
|
-
<p class="eyebrow">${
|
|
37355
|
-
<h2>${
|
|
38319
|
+
<p class="eyebrow">${escapeHtml55(capability.kind)}</p>
|
|
38320
|
+
<h2>${escapeHtml55(capability.label ?? capability.provider)}</h2>
|
|
37356
38321
|
</div>
|
|
37357
|
-
<strong>${
|
|
38322
|
+
<strong>${escapeHtml55(capability.status)}</strong>
|
|
37358
38323
|
</div>
|
|
37359
|
-
${capability.description ? `<p>${
|
|
38324
|
+
${capability.description ? `<p>${escapeHtml55(capability.description)}</p>` : ""}
|
|
37360
38325
|
<dl>
|
|
37361
38326
|
<div><dt>Configured</dt><dd>${capability.configured ? "yes" : "no"}</dd></div>
|
|
37362
38327
|
<div><dt>Selected</dt><dd>${capability.selected ? "yes" : "no"}</dd></div>
|
|
37363
|
-
<div><dt>Model</dt><dd>${
|
|
38328
|
+
<div><dt>Model</dt><dd>${escapeHtml55(capability.model ?? "default")}</dd></div>
|
|
37364
38329
|
<div><dt>Runs</dt><dd>${String(capability.health?.runCount ?? 0)}</dd></div>
|
|
37365
38330
|
<div><dt>Errors</dt><dd>${String(capability.health?.errorCount ?? 0)}</dd></div>
|
|
37366
38331
|
</dl>
|
|
37367
38332
|
${features ? `<div class="features">${features}</div>` : ""}
|
|
37368
38333
|
</article>`;
|
|
37369
38334
|
}).join("");
|
|
37370
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
38335
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml55(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.card{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(14,165,233,.16),rgba(34,197,94,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary,.features{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.card-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.selected,.healthy{color:#86efac}.unconfigured,.degraded,.rate-limited,.suppressed{color:#fca5a5}.idle,.recoverable{color:#fde68a}dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0}@media(max-width:800px){main{padding:18px}.card-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider Discovery</p><h1>${escapeHtml55(title)}</h1><div class="summary"><span class="pill">${String(report.configured)} configured</span><span class="pill">${String(report.selected)} selected</span><span class="pill">${String(report.unconfigured)} missing</span><span class="pill">${String(report.total)} total</span></div></section><section class="grid">${cards || '<article class="card"><p>No provider capabilities configured.</p></article>'}</section></main></body></html>`;
|
|
37371
38336
|
};
|
|
37372
38337
|
var createVoiceProviderCapabilityJSONHandler = (options) => async () => summarizeVoiceProviderCapabilities(options);
|
|
37373
38338
|
var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
@@ -37384,7 +38349,7 @@ var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
|
37384
38349
|
var createVoiceProviderCapabilityRoutes = (options) => {
|
|
37385
38350
|
const path = options.path ?? "/api/provider-capabilities";
|
|
37386
38351
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
37387
|
-
const routes = new
|
|
38352
|
+
const routes = new Elysia58({
|
|
37388
38353
|
name: options.name ?? "absolutejs-voice-provider-capabilities"
|
|
37389
38354
|
}).get(path, createVoiceProviderCapabilityJSONHandler(options));
|
|
37390
38355
|
if (htmlPath) {
|
|
@@ -37393,7 +38358,7 @@ var createVoiceProviderCapabilityRoutes = (options) => {
|
|
|
37393
38358
|
return routes;
|
|
37394
38359
|
};
|
|
37395
38360
|
// src/providerOrchestration.ts
|
|
37396
|
-
import { Elysia as
|
|
38361
|
+
import { Elysia as Elysia59 } from "elysia";
|
|
37397
38362
|
var defaultRequirement = {
|
|
37398
38363
|
minProviders: 1,
|
|
37399
38364
|
requireBudgetPolicy: false,
|
|
@@ -37406,7 +38371,7 @@ var statusRank7 = {
|
|
|
37406
38371
|
warn: 1,
|
|
37407
38372
|
fail: 2
|
|
37408
38373
|
};
|
|
37409
|
-
var
|
|
38374
|
+
var escapeHtml56 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37410
38375
|
var isProviderList = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
37411
38376
|
var uniqueSorted8 = (values) => [
|
|
37412
38377
|
...new Set(values.filter((value) => typeof value === "string"))
|
|
@@ -37549,27 +38514,27 @@ var renderVoiceProviderOrchestrationMarkdown = (report) => {
|
|
|
37549
38514
|
};
|
|
37550
38515
|
var renderVoiceProviderOrchestrationHTML = (report, options = {}) => {
|
|
37551
38516
|
const title = options.title ?? "Voice Provider Orchestration";
|
|
37552
|
-
const cards = report.surfaces.map((surface) => `<article class="card ${
|
|
37553
|
-
<div class="card-header"><div><p class="eyebrow">${
|
|
38517
|
+
const cards = report.surfaces.map((surface) => `<article class="card ${escapeHtml56(surface.status)}">
|
|
38518
|
+
<div class="card-header"><div><p class="eyebrow">${escapeHtml56(surface.surface)}</p><h2>${escapeHtml56(surface.strategy ?? "default policy")}</h2></div><strong>${escapeHtml56(surface.status)}</strong></div>
|
|
37554
38519
|
<dl>
|
|
37555
|
-
<div><dt>Providers</dt><dd>${
|
|
37556
|
-
<div><dt>Fallback</dt><dd>${
|
|
38520
|
+
<div><dt>Providers</dt><dd>${escapeHtml56(surface.providers.join(", ") || "none")}</dd></div>
|
|
38521
|
+
<div><dt>Fallback</dt><dd>${escapeHtml56(surface.fallbackProviders.join(" -> ") || "none")}</dd></div>
|
|
37557
38522
|
<div><dt>Circuit breaker</dt><dd>${surface.circuitBreaker ? "yes" : "no"}</dd></div>
|
|
37558
38523
|
<div><dt>Timeout</dt><dd>${surface.timeoutBudget ? `${String(surface.timeoutMs)}ms` : "none"}</dd></div>
|
|
37559
38524
|
<div><dt>Max cost</dt><dd>${surface.budgetPolicy.maxCost ?? "none"}</dd></div>
|
|
37560
38525
|
<div><dt>Max latency</dt><dd>${surface.budgetPolicy.maxLatencyMs ? `${String(surface.budgetPolicy.maxLatencyMs)}ms` : "none"}</dd></div>
|
|
37561
38526
|
<div><dt>Min quality</dt><dd>${surface.budgetPolicy.minQuality ?? "none"}</dd></div>
|
|
37562
|
-
<div><dt>Fallback mode</dt><dd>${
|
|
38527
|
+
<div><dt>Fallback mode</dt><dd>${escapeHtml56(surface.fallbackMode || "default")}</dd></div>
|
|
37563
38528
|
</dl>
|
|
37564
|
-
${surface.issues.length ? `<ul>${surface.issues.map((issue) => `<li><strong>${
|
|
38529
|
+
${surface.issues.length ? `<ul>${surface.issues.map((issue) => `<li><strong>${escapeHtml56(issue.status)}</strong> ${escapeHtml56(issue.message)}</li>`).join("")}</ul>` : "<p>No orchestration issues.</p>"}
|
|
37565
38530
|
</article>`).join("");
|
|
37566
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
38531
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml56(title)}</title><style>body{background:#111827;color:#f9fafb;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.card{background:#172033;border:1px solid #2d3b55;border-radius:22px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(59,130,246,.18),rgba(20,184,166,.12))}.eyebrow{color:#93c5fd;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f172a;border:1px solid #334155;border-radius:999px;padding:7px 10px}.grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}.card-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass strong{color:#86efac}.warn strong{color:#fde68a}.fail strong{color:#fca5a5}dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0;overflow-wrap:anywhere}li{margin:.35rem 0}@media(max-width:800px){main{padding:18px}.card-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider Policy Proof</p><h1>${escapeHtml56(title)}</h1><div class="summary"><span class="pill">${escapeHtml56(report.profileId)}</span><span class="pill">${escapeHtml56(report.status)}</span><span class="pill">${String(report.summary.surfaces)} surfaces</span><span class="pill">${String(report.summary.providers)} providers</span><span class="pill">${String(report.issues.length)} issues</span></div></section><section class="grid">${cards || '<article class="card"><p>No provider orchestration surfaces configured.</p></article>'}</section></main></body></html>`;
|
|
37567
38532
|
};
|
|
37568
38533
|
var createVoiceProviderOrchestrationRoutes = (options) => {
|
|
37569
38534
|
const path = options.path ?? "/api/voice/provider-orchestration";
|
|
37570
38535
|
const htmlPath = options.htmlPath === undefined ? "/voice/provider-orchestration" : options.htmlPath;
|
|
37571
38536
|
const markdownPath = options.markdownPath === undefined ? "/voice/provider-orchestration.md" : options.markdownPath;
|
|
37572
|
-
const routes = new
|
|
38537
|
+
const routes = new Elysia59({
|
|
37573
38538
|
name: options.name ?? "absolutejs-voice-provider-orchestration"
|
|
37574
38539
|
}).get(path, () => buildVoiceProviderOrchestrationReport(options));
|
|
37575
38540
|
if (htmlPath) {
|
|
@@ -37742,8 +38707,8 @@ var assertVoiceProviderRoutingContractEvidence = (reports, input = {}) => {
|
|
|
37742
38707
|
return report;
|
|
37743
38708
|
};
|
|
37744
38709
|
// src/voiceMonitoring.ts
|
|
37745
|
-
import { Elysia as
|
|
37746
|
-
var
|
|
38710
|
+
import { Elysia as Elysia60 } from "elysia";
|
|
38711
|
+
var escapeHtml57 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37747
38712
|
var issueIdForRun = (run) => `voice-monitor:${run.id}:${run.impactedSessions?.[0] ?? "global"}`;
|
|
37748
38713
|
var rollupStatus5 = (runs) => runs.some((run) => run.status === "fail") ? "fail" : runs.some((run) => run.status === "warn") ? "warn" : "pass";
|
|
37749
38714
|
var createVoiceMemoryMonitorIssueStore = (initial = []) => {
|
|
@@ -37996,14 +38961,14 @@ ${rows || "| none | pass | info | | | No monitors configured. |"}
|
|
|
37996
38961
|
};
|
|
37997
38962
|
var renderVoiceMonitorHTML = (report, options = {}) => {
|
|
37998
38963
|
const title = options.title ?? "Voice Monitors";
|
|
37999
|
-
const runs = report.runs.map((run) => `<tr><td>${
|
|
38000
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
38001
|
-
const snippet =
|
|
38964
|
+
const runs = report.runs.map((run) => `<tr><td>${escapeHtml57(run.label)}</td><td class="${escapeHtml57(run.status)}">${escapeHtml57(run.status)}</td><td>${escapeHtml57(run.severity)}</td><td>${escapeHtml57(String(run.value ?? ""))}</td><td>${escapeHtml57(String(run.threshold ?? ""))}</td><td>${escapeHtml57(run.detail ?? "")}</td></tr>`).join("");
|
|
38965
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml57(issue.label)}</strong> <span class="${escapeHtml57(issue.status)}">${escapeHtml57(issue.status)}</span> ${escapeHtml57(issue.detail ?? "")}</li>`).join("");
|
|
38966
|
+
const snippet = escapeHtml57(`app.use(createVoiceMonitorRoutes({
|
|
38002
38967
|
evidence,
|
|
38003
38968
|
issueStore,
|
|
38004
38969
|
monitors: [defineVoiceMonitor(...)]
|
|
38005
38970
|
}));`);
|
|
38006
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
38971
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml57(title)}</title><style>body{background:#10141b;color:#f8f2df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero,.card{background:#171f2b;border:1px solid #2e3a4b;border-radius:24px;margin-bottom:16px;padding:22px}.eyebrow{color:#93c5fd;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);line-height:.92;margin:.2rem 0 1rem}.pill{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;margin-right:8px;padding:8px 12px}.pass{color:#86efac}.warn,.acknowledged{color:#fde68a}.fail,.open{color:#fca5a5}.resolved,.muted{color:#cbd5e1}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2e3a4b;padding:12px;text-align:left;vertical-align:top}pre{background:#0c1118;border:1px solid #2e3a4b;border-radius:16px;color:#dbeafe;overflow:auto;padding:16px}</style></head><body><main><section class="hero"><p class="eyebrow">Code-owned monitoring</p><h1>${escapeHtml57(title)}</h1><p class="pill ${escapeHtml57(report.status)}">Status: ${escapeHtml57(report.status)}</p><p class="pill">Open issues: ${String(report.summary.open)}</p><p class="pill">Critical: ${String(report.summary.criticalOpen)}</p></section><section class="card"><h2>Monitor Runs</h2><table><thead><tr><th>Monitor</th><th>Status</th><th>Severity</th><th>Value</th><th>Threshold</th><th>Detail</th></tr></thead><tbody>${runs}</tbody></table></section><section class="card"><h2>Issues</h2>${issues ? `<ul>${issues}</ul>` : '<p class="pass">No monitor issues.</p>'}</section><section class="card"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceMonitorRoutes(...)</code></h2><pre><code>${snippet}</code></pre></section></main></body></html>`;
|
|
38007
38972
|
};
|
|
38008
38973
|
var actorFromRequest = async (request) => {
|
|
38009
38974
|
if (!request.headers.get("content-type")?.includes("application/json")) {
|
|
@@ -38027,7 +38992,7 @@ var createVoiceMonitorRoutes = (options) => {
|
|
|
38027
38992
|
monitors: options.monitors,
|
|
38028
38993
|
now: options.now
|
|
38029
38994
|
});
|
|
38030
|
-
const routes = new
|
|
38995
|
+
const routes = new Elysia60({
|
|
38031
38996
|
name: options.name ?? "absolutejs-voice-monitoring"
|
|
38032
38997
|
}).get(path, report).get(`${path}.md`, async () => {
|
|
38033
38998
|
return new Response(renderVoiceMonitorMarkdown(await report()), {
|
|
@@ -38074,7 +39039,7 @@ var createVoiceMonitorRoutes = (options) => {
|
|
|
38074
39039
|
};
|
|
38075
39040
|
var createVoiceMonitorRunnerRoutes = (options) => {
|
|
38076
39041
|
const path = options.path ?? "/api/voice/monitor-runner";
|
|
38077
|
-
return new
|
|
39042
|
+
return new Elysia60({
|
|
38078
39043
|
name: options.name ?? "absolutejs-voice-monitor-runner"
|
|
38079
39044
|
}).get(path, () => ({
|
|
38080
39045
|
isRunning: options.runner.isRunning()
|
|
@@ -38462,8 +39427,8 @@ var recommendVoiceReadinessProfile = (options) => {
|
|
|
38462
39427
|
};
|
|
38463
39428
|
};
|
|
38464
39429
|
// src/providerStackRecommendations.ts
|
|
38465
|
-
import { Elysia as
|
|
38466
|
-
var
|
|
39430
|
+
import { Elysia as Elysia61 } from "elysia";
|
|
39431
|
+
var escapeHtml58 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
38467
39432
|
var profileProviderPriorities = {
|
|
38468
39433
|
"meeting-recorder": {
|
|
38469
39434
|
llm: ["openai", "anthropic", "gemini"],
|
|
@@ -38778,17 +39743,17 @@ var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "fu
|
|
|
38778
39743
|
var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
38779
39744
|
const title = options.title ?? "Voice Provider Contract Matrix";
|
|
38780
39745
|
const rows = report.rows.map((row) => {
|
|
38781
|
-
const checks = row.checks.map((check) => `<li class="${
|
|
38782
|
-
return `<article class="row ${
|
|
39746
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml58(check.status)}"><strong>${escapeHtml58(check.label)}</strong><span>${escapeHtml58(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml58(check.remediation.href)}">${escapeHtml58(check.remediation.label)}</a>` : escapeHtml58(check.remediation.label)}: ${escapeHtml58(check.remediation.detail)}</em>` : ""}</li>`).join("");
|
|
39747
|
+
return `<article class="row ${escapeHtml58(row.status)}">
|
|
38783
39748
|
<div>
|
|
38784
|
-
<p class="eyebrow">${
|
|
38785
|
-
<h2>${
|
|
38786
|
-
<p class="status ${
|
|
39749
|
+
<p class="eyebrow">${escapeHtml58(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
|
|
39750
|
+
<h2>${escapeHtml58(row.provider)}</h2>
|
|
39751
|
+
<p class="status ${escapeHtml58(row.status)}">${escapeHtml58(row.status.toUpperCase())}</p>
|
|
38787
39752
|
</div>
|
|
38788
39753
|
<ul>${checks}</ul>
|
|
38789
39754
|
</article>`;
|
|
38790
39755
|
}).join("");
|
|
38791
|
-
const snippet =
|
|
39756
|
+
const snippet = escapeHtml58(`const providerContracts = () =>
|
|
38792
39757
|
createVoiceProviderContractMatrixPreset('phone-agent', {
|
|
38793
39758
|
env: process.env,
|
|
38794
39759
|
providers: {
|
|
@@ -38809,7 +39774,7 @@ createVoiceProductionReadinessRoutes({
|
|
|
38809
39774
|
providerContractMatrix: () =>
|
|
38810
39775
|
buildVoiceProviderContractMatrix(providerContracts())
|
|
38811
39776
|
});`);
|
|
38812
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
39777
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml58(title)}</title><style>body{background:#0f1412;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.primitive,.row{background:#17201b;border:1px solid #2d3b32;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(125,211,252,.12))}.primitive{background:#111814;border-color:#41604a}.eyebrow{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill,.status{border:1px solid #3f4f45;border-radius:999px;display:inline-flex;padding:8px 12px}.primitive code{color:#bbf7d0}.primitive p{color:#c8d8ca;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#08110d;border:1px solid #294132;border-radius:18px;color:#d9f99d;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.row.pass,.pass{border-color:rgba(34,197,94,.65)}.status.warn,.row.warn,.warn{border-color:rgba(245,158,11,.7)}.status.fail,.row.fail,.fail{border-color:rgba(239,68,68,.75)}.row{display:grid;gap:20px;grid-template-columns:minmax(180px,.45fr) 1fr}.row ul{display:grid;gap:10px;list-style:none;margin:0;padding:0}.row li{background:#111814;border:1px solid #2d3b32;border-radius:16px;display:grid;gap:4px;padding:12px}.row li span{color:#b8c2ba}.row li em{color:#f9d77e;font-style:normal}.row li a{color:#86efac}@media(max-width:760px){main{padding:18px}.row{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider contracts</p><h1>${escapeHtml58(title)}</h1><p>Self-hosted provider proof for configured state, required env, latency budgets, fallback, streaming, and declared capabilities.</p><div class="summary"><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.warned)} warning</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} total</span></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProviderContractMatrixPreset(...)</code> builds this matrix</h2><p>Give AbsoluteJS your configured LLM, STT, and TTS providers once. It turns them into deploy-checkable proof for env, fallback, streaming, latency budgets, selected providers, and profile-required capabilities without a hosted dashboard.</p><pre><code>${snippet}</code></pre></section>${rows || '<article class="row"><p>No provider contracts configured.</p></article>'}</main></body></html>`;
|
|
38813
39778
|
};
|
|
38814
39779
|
var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
|
|
38815
39780
|
var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
@@ -38824,7 +39789,7 @@ var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
|
38824
39789
|
var createVoiceProviderContractMatrixRoutes = (options) => {
|
|
38825
39790
|
const path = options.path ?? "/api/provider-contracts";
|
|
38826
39791
|
const htmlPath = options.htmlPath ?? "/provider-contracts";
|
|
38827
|
-
const routes = new
|
|
39792
|
+
const routes = new Elysia61({
|
|
38828
39793
|
name: options.name ?? "absolutejs-voice-provider-contract-matrix"
|
|
38829
39794
|
});
|
|
38830
39795
|
const jsonHandler = createVoiceProviderContractMatrixJSONHandler(options.matrix);
|
|
@@ -38942,7 +39907,7 @@ var assertVoiceProviderStackEvidence = (report, input = {}) => {
|
|
|
38942
39907
|
return assertion;
|
|
38943
39908
|
};
|
|
38944
39909
|
// src/opsConsoleRoutes.ts
|
|
38945
|
-
import { Elysia as
|
|
39910
|
+
import { Elysia as Elysia62 } from "elysia";
|
|
38946
39911
|
var DEFAULT_LINKS = [
|
|
38947
39912
|
{
|
|
38948
39913
|
description: "Quality gates for CI, deploy checks, and production readiness.",
|
|
@@ -38977,7 +39942,7 @@ var DEFAULT_LINKS = [
|
|
|
38977
39942
|
label: "Handoffs"
|
|
38978
39943
|
}
|
|
38979
39944
|
];
|
|
38980
|
-
var
|
|
39945
|
+
var escapeHtml59 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
38981
39946
|
var countProviderStatuses = (providers) => {
|
|
38982
39947
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
38983
39948
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -39046,20 +40011,20 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
39046
40011
|
trace
|
|
39047
40012
|
};
|
|
39048
40013
|
};
|
|
39049
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
40014
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml59(input.label)}</span><strong>${escapeHtml59(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml59(input.status)}">${escapeHtml59(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml59(input.href)}">Open</a>` : ""}</article>`;
|
|
39050
40015
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
39051
40016
|
const links = report.links.map((link) => `<article class="surface">
|
|
39052
|
-
<div><h2>${
|
|
39053
|
-
<p><a href="${
|
|
40017
|
+
<div><h2>${escapeHtml59(link.label)}</h2>${link.description ? `<p>${escapeHtml59(link.description)}</p>` : ""}</div>
|
|
40018
|
+
<p><a href="${escapeHtml59(link.href)}">Open ${escapeHtml59(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml59(link.statusHref)}">Status</a>` : ""}</p>
|
|
39054
40019
|
</article>`).join("");
|
|
39055
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
39056
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
40020
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml59(session.sessionId)}</td><td>${escapeHtml59(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml59(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
40021
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml59(event.kind)}</td><td>${escapeHtml59(event.provider ?? "unknown")}</td><td>${escapeHtml59(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml59(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
39057
40022
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
39058
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
40023
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml59(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#101316;color:#f6f2e8;margin:0}main{max-width:1180px;margin:auto;padding:32px}a{color:#fbbf24}header{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin-bottom:24px}.eyebrow{color:#fbbf24;font-weight:800;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.5rem);line-height:.95;margin:.2rem 0 1rem}.muted{color:#a8b0b8}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.metric,.surface{background:#181d22;border:1px solid #2a323a;border-radius:20px;padding:18px}.metric strong{display:block;font-size:2.2rem;margin:.25rem 0}.pass,.healthy{color:#86efac}.fail,.failed,.degraded{color:#fca5a5}.surfaces{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:24px 0}table{width:100%;border-collapse:collapse;background:#181d22;border-radius:16px;overflow:hidden;margin:12px 0 28px}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left}section{margin-top:30px}@media(max-width:700px){main{padding:20px}header{display:block}}</style></head><body><main><header><div><p class="eyebrow">Self-hosted voice operations</p><h1>${escapeHtml59(title)}</h1><p class="muted">One deployable control plane for quality gates, failover, traces, sessions, handoffs, and provider health.</p></div><p class="muted">Checked ${escapeHtml59(new Date(report.checkedAt).toLocaleString())}</p></header><div class="grid">${renderMetricCard({ label: "Quality", value: report.quality.status, status: report.quality.status, href: "/quality" })}${renderMetricCard({ label: "Events", value: report.eventCount, href: "/diagnostics" })}${renderMetricCard({ label: "Sessions", value: report.sessions.total, status: report.sessions.failed > 0 ? "failed" : "healthy", href: "/sessions" })}${renderMetricCard({ label: "Handoffs failed", value: report.handoffs.failed, status: report.handoffs.failed > 0 ? "failed" : "healthy", href: "/handoffs" })}${renderMetricCard({ label: "Providers degraded", value: report.providers.degraded, status: report.providers.degraded > 0 ? "degraded" : "healthy", href: "/resilience" })}</div><section><h2>Operational Surfaces</h2><div class="surfaces">${links}</div></section><section><h2>Recent Sessions</h2><table><thead><tr><th>Session</th><th>Status</th><th>Turns</th><th>Errors</th><th>Replay</th></tr></thead><tbody>${sessions}</tbody></table></section><section><h2>Recent Provider Routing</h2><table><thead><tr><th>Kind</th><th>Provider</th><th>Status</th><th>Elapsed</th><th>Session</th></tr></thead><tbody>${routing}</tbody></table></section></main></body></html>`;
|
|
39059
40024
|
};
|
|
39060
40025
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
39061
40026
|
const path = options.path ?? "/ops-console";
|
|
39062
|
-
const routes = new
|
|
40027
|
+
const routes = new Elysia62({
|
|
39063
40028
|
name: options.name ?? "absolutejs-voice-ops-console"
|
|
39064
40029
|
});
|
|
39065
40030
|
const getReport = () => buildVoiceOpsConsoleReport(options);
|
|
@@ -39075,246 +40040,6 @@ var createVoiceOpsConsoleRoutes = (options) => {
|
|
|
39075
40040
|
routes.get(`${path}/json`, async () => getReport());
|
|
39076
40041
|
return routes;
|
|
39077
40042
|
};
|
|
39078
|
-
// src/sessionObservability.ts
|
|
39079
|
-
import { Elysia as Elysia62 } from "elysia";
|
|
39080
|
-
var escapeHtml59 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
39081
|
-
var formatMs6 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
39082
|
-
var resolveHref = (href, sessionId) => {
|
|
39083
|
-
if (href === false) {
|
|
39084
|
-
return;
|
|
39085
|
-
}
|
|
39086
|
-
if (typeof href === "function") {
|
|
39087
|
-
return href(sessionId);
|
|
39088
|
-
}
|
|
39089
|
-
if (typeof href === "string") {
|
|
39090
|
-
return href.includes(":sessionId") ? href.replaceAll(":sessionId", encodeURIComponent(sessionId)) : `${href.replace(/\/$/, "")}/${encodeURIComponent(sessionId)}`;
|
|
39091
|
-
}
|
|
39092
|
-
return;
|
|
39093
|
-
};
|
|
39094
|
-
var buildLinks = (options) => {
|
|
39095
|
-
const links = [];
|
|
39096
|
-
const add = (rel, label, href) => {
|
|
39097
|
-
if (href) {
|
|
39098
|
-
links.push({ href, label, rel });
|
|
39099
|
-
}
|
|
39100
|
-
};
|
|
39101
|
-
add("operations-record", "Open operations record", resolveHref(options.operationsRecordHref, options.sessionId));
|
|
39102
|
-
add("trace-timeline", "Open trace timeline", resolveHref(options.traceTimelineHref, options.sessionId));
|
|
39103
|
-
add("call-debugger", "Open call debugger", resolveHref(options.callDebuggerHref, options.sessionId));
|
|
39104
|
-
add("incident-markdown", "Download incident Markdown", resolveHref(options.incidentMarkdownHref, options.sessionId));
|
|
39105
|
-
return [...links, ...options.customLinks ?? []];
|
|
39106
|
-
};
|
|
39107
|
-
var buildTurnWaterfalls = (record) => {
|
|
39108
|
-
const byTurn = new Map;
|
|
39109
|
-
const getTurn = (turnId) => {
|
|
39110
|
-
const existing = byTurn.get(turnId);
|
|
39111
|
-
if (existing) {
|
|
39112
|
-
return existing;
|
|
39113
|
-
}
|
|
39114
|
-
const turn = {
|
|
39115
|
-
assistantReplies: 0,
|
|
39116
|
-
errors: 0,
|
|
39117
|
-
providerDecisions: 0,
|
|
39118
|
-
stages: [],
|
|
39119
|
-
toolCalls: 0,
|
|
39120
|
-
transcripts: 0
|
|
39121
|
-
};
|
|
39122
|
-
byTurn.set(turnId, turn);
|
|
39123
|
-
return turn;
|
|
39124
|
-
};
|
|
39125
|
-
for (const event of record.timeline) {
|
|
39126
|
-
if (!event.turnId) {
|
|
39127
|
-
continue;
|
|
39128
|
-
}
|
|
39129
|
-
const turn = getTurn(event.turnId);
|
|
39130
|
-
turn.stages.push({
|
|
39131
|
-
at: event.at,
|
|
39132
|
-
elapsedMs: event.elapsedMs,
|
|
39133
|
-
label: event.label,
|
|
39134
|
-
offsetMs: event.offsetMs,
|
|
39135
|
-
provider: event.provider,
|
|
39136
|
-
status: event.status,
|
|
39137
|
-
type: event.type
|
|
39138
|
-
});
|
|
39139
|
-
if (event.type === "turn.transcript") {
|
|
39140
|
-
turn.transcripts += 1;
|
|
39141
|
-
}
|
|
39142
|
-
if (event.type === "turn.assistant") {
|
|
39143
|
-
turn.assistantReplies += 1;
|
|
39144
|
-
}
|
|
39145
|
-
if (event.type === "agent.tool") {
|
|
39146
|
-
turn.toolCalls += 1;
|
|
39147
|
-
}
|
|
39148
|
-
if (event.type === "provider.decision") {
|
|
39149
|
-
turn.providerDecisions += 1;
|
|
39150
|
-
}
|
|
39151
|
-
if (event.type === "session.error" || event.status === "error") {
|
|
39152
|
-
turn.errors += 1;
|
|
39153
|
-
}
|
|
39154
|
-
}
|
|
39155
|
-
for (const transcript of record.transcript) {
|
|
39156
|
-
const turn = getTurn(transcript.id);
|
|
39157
|
-
turn.assistantReplies = Math.max(turn.assistantReplies, transcript.assistantReplies.length);
|
|
39158
|
-
turn.errors += transcript.errors.length;
|
|
39159
|
-
turn.transcripts = Math.max(turn.transcripts, transcript.transcripts.length);
|
|
39160
|
-
}
|
|
39161
|
-
return [...byTurn.entries()].map(([turnId, turn]) => {
|
|
39162
|
-
const startedAt = turn.stages[0]?.at;
|
|
39163
|
-
const endedAt = turn.stages.at(-1)?.at;
|
|
39164
|
-
return {
|
|
39165
|
-
assistantReplies: turn.assistantReplies,
|
|
39166
|
-
durationMs: startedAt !== undefined && endedAt !== undefined ? Math.max(0, endedAt - startedAt) : undefined,
|
|
39167
|
-
endedAt,
|
|
39168
|
-
errors: turn.errors,
|
|
39169
|
-
providerDecisions: turn.providerDecisions,
|
|
39170
|
-
stages: turn.stages,
|
|
39171
|
-
startedAt,
|
|
39172
|
-
toolCalls: turn.toolCalls,
|
|
39173
|
-
transcripts: turn.transcripts,
|
|
39174
|
-
turnId
|
|
39175
|
-
};
|
|
39176
|
-
}).sort((left, right) => (left.startedAt ?? 0) - (right.startedAt ?? 0));
|
|
39177
|
-
};
|
|
39178
|
-
var buildVoiceSessionObservabilityReport = async (options) => {
|
|
39179
|
-
const record = await buildVoiceOperationsRecord({
|
|
39180
|
-
audit: options.audit,
|
|
39181
|
-
evaluation: options.evaluation,
|
|
39182
|
-
events: options.events,
|
|
39183
|
-
integrationEvents: options.integrationEvents,
|
|
39184
|
-
redact: options.redact,
|
|
39185
|
-
reviews: options.reviews,
|
|
39186
|
-
sessionId: options.sessionId,
|
|
39187
|
-
store: options.store,
|
|
39188
|
-
tasks: options.tasks
|
|
39189
|
-
});
|
|
39190
|
-
const failureReplay = buildVoiceFailureReplay(record, {
|
|
39191
|
-
operationsRecordHref: resolveHref(options.operationsRecordHref, options.sessionId)
|
|
39192
|
-
});
|
|
39193
|
-
const incidentMarkdown = renderVoiceOperationsRecordIncidentMarkdown(record);
|
|
39194
|
-
const statuses = [
|
|
39195
|
-
record.status,
|
|
39196
|
-
failureReplay.status === "failed" ? "failed" : failureReplay.status === "degraded" ? "warning" : "healthy"
|
|
39197
|
-
];
|
|
39198
|
-
const status = statuses.includes("failed") ? "failed" : statuses.includes("warning") ? "warning" : "healthy";
|
|
39199
|
-
return {
|
|
39200
|
-
checkedAt: Date.now(),
|
|
39201
|
-
failureReplay,
|
|
39202
|
-
incidentMarkdown,
|
|
39203
|
-
links: buildLinks(options),
|
|
39204
|
-
record,
|
|
39205
|
-
sessionId: options.sessionId,
|
|
39206
|
-
status,
|
|
39207
|
-
summary: {
|
|
39208
|
-
durationMs: record.summary.callDurationMs,
|
|
39209
|
-
errors: record.summary.errorCount,
|
|
39210
|
-
events: record.summary.eventCount,
|
|
39211
|
-
fallbacks: record.providerDecisionSummary.fallbacks,
|
|
39212
|
-
guardrailBlocks: record.guardrails.blocked,
|
|
39213
|
-
handoffs: record.handoffs.length,
|
|
39214
|
-
providerRecoveryStatus: record.providerDecisionSummary.recoveryStatus,
|
|
39215
|
-
providers: record.providerDecisionSummary.providers,
|
|
39216
|
-
telephonyMediaEvents: record.telephonyMedia.total,
|
|
39217
|
-
toolCalls: record.tools.length,
|
|
39218
|
-
turns: record.summary.turnCount
|
|
39219
|
-
},
|
|
39220
|
-
turns: buildTurnWaterfalls(record)
|
|
39221
|
-
};
|
|
39222
|
-
};
|
|
39223
|
-
var renderLinks = (links) => links.length === 0 ? "" : `<div class="actions">${links.map((link) => `<a href="${escapeHtml59(link.href)}">${escapeHtml59(link.label)}</a>`).join("")}</div>`;
|
|
39224
|
-
var renderTurns = (turns) => turns.length === 0 ? '<p class="muted">No turn-level events recorded yet.</p>' : turns.map((turn) => `<article class="turn"><header><strong>${escapeHtml59(turn.turnId)}</strong><span>${formatMs6(turn.durationMs)}</span></header><dl><div><dt>Transcripts</dt><dd>${String(turn.transcripts)}</dd></div><div><dt>Assistant</dt><dd>${String(turn.assistantReplies)}</dd></div><div><dt>Tools</dt><dd>${String(turn.toolCalls)}</dd></div><div><dt>Providers</dt><dd>${String(turn.providerDecisions)}</dd></div><div><dt>Errors</dt><dd>${String(turn.errors)}</dd></div></dl><table><thead><tr><th>Offset</th><th>Type</th><th>Stage</th><th>Provider</th><th>Status</th><th>Latency</th></tr></thead><tbody>${turn.stages.map((stage) => `<tr><td>+${String(stage.offsetMs)}ms</td><td>${escapeHtml59(stage.type)}</td><td>${escapeHtml59(stage.label)}</td><td>${escapeHtml59(stage.provider ?? "")}</td><td>${escapeHtml59(stage.status ?? "")}</td><td>${formatMs6(stage.elapsedMs)}</td></tr>`).join("")}</tbody></table></article>`).join("");
|
|
39225
|
-
var renderVoiceSessionObservabilityMarkdown = (report) => `# Voice session observability: ${report.sessionId}
|
|
39226
|
-
|
|
39227
|
-
Status: ${report.status}
|
|
39228
|
-
|
|
39229
|
-
- Events: ${report.summary.events}
|
|
39230
|
-
- Turns: ${report.summary.turns}
|
|
39231
|
-
- Errors: ${report.summary.errors}
|
|
39232
|
-
- Duration: ${formatMs6(report.summary.durationMs)}
|
|
39233
|
-
- Providers: ${report.summary.providers.join(", ") || "none"}
|
|
39234
|
-
- Provider recovery: ${report.summary.providerRecoveryStatus}
|
|
39235
|
-
- Fallbacks: ${report.summary.fallbacks}
|
|
39236
|
-
- Tool calls: ${report.summary.toolCalls}
|
|
39237
|
-
- Handoffs: ${report.summary.handoffs}
|
|
39238
|
-
- Guardrail blocks: ${report.summary.guardrailBlocks}
|
|
39239
|
-
- Telephony media events: ${report.summary.telephonyMediaEvents}
|
|
39240
|
-
|
|
39241
|
-
## Links
|
|
39242
|
-
|
|
39243
|
-
${report.links.length ? report.links.map((link) => `- [${link.label}](${link.href})`).join(`
|
|
39244
|
-
`) : "- none"}
|
|
39245
|
-
|
|
39246
|
-
## Turn Waterfalls
|
|
39247
|
-
|
|
39248
|
-
${report.turns.length ? report.turns.map((turn) => `### ${turn.turnId}
|
|
39249
|
-
|
|
39250
|
-
- Duration: ${formatMs6(turn.durationMs)}
|
|
39251
|
-
- Transcripts: ${turn.transcripts}
|
|
39252
|
-
- Assistant replies: ${turn.assistantReplies}
|
|
39253
|
-
- Tool calls: ${turn.toolCalls}
|
|
39254
|
-
- Provider decisions: ${turn.providerDecisions}
|
|
39255
|
-
- Errors: ${turn.errors}
|
|
39256
|
-
|
|
39257
|
-
${turn.stages.map((stage) => `- +${stage.offsetMs}ms ${stage.type}: ${stage.label}${stage.provider ? ` (${stage.provider})` : ""}${stage.status ? ` [${stage.status}]` : ""}`).join(`
|
|
39258
|
-
`)}`).join(`
|
|
39259
|
-
|
|
39260
|
-
`) : "No turn-level events recorded."}
|
|
39261
|
-
|
|
39262
|
-
## Incident Handoff
|
|
39263
|
-
|
|
39264
|
-
${report.incidentMarkdown}`;
|
|
39265
|
-
var renderVoiceSessionObservabilityHTML = (report, options = {}) => `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml59(options.title ?? "Voice Session Observability")}</title><style>body{background:#0d1412;color:#f7f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #425046;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.actions{display:flex;flex-wrap:wrap;gap:10px;margin:18px 0}.actions a{background:#fbbf24;border-radius:999px;color:#111827;font-weight:900;padding:10px 14px;text-decoration:none}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin:22px 0}.card,.turn,.incident{background:#17201c;border:1px solid #2e3c35;border-radius:20px;padding:16px}.card span,.muted,dt{color:#a8b4ad}.card strong{display:block;font-size:2rem}section{margin-top:30px}.turn{margin:16px 0}.turn header{align-items:center;display:flex;justify-content:space-between;gap:14px}dl{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));margin:14px 0}dd{font-weight:900;margin:3px 0 0}table{border-collapse:collapse;margin-top:14px;width:100%}td,th{border-top:1px solid #2e3c35;padding:10px;text-align:left}pre{background:#08100d;border:1px solid #2e3c35;border-radius:16px;color:#d9f99d;overflow:auto;padding:14px}@media(max-width:760px){main{padding:20px}table{font-size:.9rem}}</style></head><body><main><header><p class="eyebrow">Session observability</p><h1>${escapeHtml59(report.sessionId)}</h1><p class="status ${escapeHtml59(report.status)}">${escapeHtml59(report.status)}</p>${renderLinks(report.links)}<p class="muted">One support/debug report across trace timeline, operations record, provider recovery, turn waterfalls, guardrails, tools, handoffs, failure replay, and incident handoff.</p></header><section class="grid"><article class="card"><span>Events</span><strong>${String(report.summary.events)}</strong></article><article class="card"><span>Turns</span><strong>${String(report.summary.turns)}</strong></article><article class="card"><span>Errors</span><strong>${String(report.summary.errors)}</strong></article><article class="card"><span>Duration</span><strong>${formatMs6(report.summary.durationMs)}</strong></article><article class="card"><span>Fallbacks</span><strong>${String(report.summary.fallbacks)}</strong></article><article class="card"><span>Tools</span><strong>${String(report.summary.toolCalls)}</strong></article><article class="card"><span>Handoffs</span><strong>${String(report.summary.handoffs)}</strong></article><article class="card"><span>Guardrails blocked</span><strong>${String(report.summary.guardrailBlocks)}</strong></article><article class="card"><span>Telephony media</span><strong>${String(report.summary.telephonyMediaEvents)}</strong></article></section><section><h2>Turn Waterfalls</h2>${renderTurns(report.turns)}</section><section class="incident"><h2>Incident Handoff</h2><pre><code>${escapeHtml59(report.incidentMarkdown)}</code></pre></section></main></body></html>`;
|
|
39266
|
-
var routeSessionId = (params) => typeof params.sessionId === "string" ? params.sessionId : "";
|
|
39267
|
-
var createVoiceSessionObservabilityRoutes = (options) => {
|
|
39268
|
-
const path = options.path ?? "/api/voice/session-observability/:sessionId";
|
|
39269
|
-
const htmlPath = options.htmlPath ?? "/voice/session-observability/:sessionId";
|
|
39270
|
-
const incidentPath = options.incidentPath ?? "/api/voice/session-observability/:sessionId/incident.md";
|
|
39271
|
-
const title = options.title ?? "AbsoluteJS Voice Session Observability";
|
|
39272
|
-
const routes = new Elysia62({
|
|
39273
|
-
name: options.name ?? "absolutejs-voice-session-observability"
|
|
39274
|
-
});
|
|
39275
|
-
const build = (sessionId) => buildVoiceSessionObservabilityReport({
|
|
39276
|
-
audit: options.audit,
|
|
39277
|
-
callDebuggerHref: options.callDebuggerHref,
|
|
39278
|
-
customLinks: options.customLinks,
|
|
39279
|
-
evaluation: options.evaluation,
|
|
39280
|
-
events: options.events,
|
|
39281
|
-
incidentMarkdownHref: options.incidentMarkdownHref ?? (incidentPath === false ? false : incidentPath),
|
|
39282
|
-
integrationEvents: options.integrationEvents,
|
|
39283
|
-
operationsRecordHref: options.operationsRecordHref,
|
|
39284
|
-
redact: options.redact,
|
|
39285
|
-
reviews: options.reviews,
|
|
39286
|
-
sessionId,
|
|
39287
|
-
store: options.store,
|
|
39288
|
-
tasks: options.tasks,
|
|
39289
|
-
traceTimelineHref: options.traceTimelineHref
|
|
39290
|
-
});
|
|
39291
|
-
routes.get(path, async ({ params }) => Response.json(await build(routeSessionId(params))));
|
|
39292
|
-
if (htmlPath !== false) {
|
|
39293
|
-
routes.get(htmlPath, async ({ params }) => {
|
|
39294
|
-
const report = await build(routeSessionId(params));
|
|
39295
|
-
const body = await (options.render ?? ((input) => renderVoiceSessionObservabilityHTML(input, { title })))(report);
|
|
39296
|
-
return new Response(body, {
|
|
39297
|
-
headers: {
|
|
39298
|
-
"content-type": "text/html; charset=utf-8",
|
|
39299
|
-
...options.headers
|
|
39300
|
-
}
|
|
39301
|
-
});
|
|
39302
|
-
});
|
|
39303
|
-
}
|
|
39304
|
-
if (incidentPath !== false) {
|
|
39305
|
-
routes.get(incidentPath, async ({ params }) => {
|
|
39306
|
-
const report = await build(routeSessionId(params));
|
|
39307
|
-
const body = await (options.renderIncidentMarkdown ?? renderVoiceSessionObservabilityMarkdown)(report);
|
|
39308
|
-
return new Response(body, {
|
|
39309
|
-
headers: {
|
|
39310
|
-
"content-type": "text/markdown; charset=utf-8",
|
|
39311
|
-
...options.headers
|
|
39312
|
-
}
|
|
39313
|
-
});
|
|
39314
|
-
});
|
|
39315
|
-
}
|
|
39316
|
-
return routes;
|
|
39317
|
-
};
|
|
39318
40043
|
// src/incidentBundle.ts
|
|
39319
40044
|
import { Elysia as Elysia63 } from "elysia";
|
|
39320
40045
|
var filterIncidentBundleArtifacts = (artifacts, filter = {}) => artifacts.filter((artifact) => {
|
|
@@ -42256,8 +42981,8 @@ var shapeTelephonyAssistantText = (text, options = {}) => {
|
|
|
42256
42981
|
};
|
|
42257
42982
|
// src/proofPack.ts
|
|
42258
42983
|
import { Elysia as Elysia69 } from "elysia";
|
|
42259
|
-
import { mkdir as
|
|
42260
|
-
import { dirname as dirname3, join as
|
|
42984
|
+
import { mkdir as mkdir6 } from "fs/promises";
|
|
42985
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
42261
42986
|
var toGeneratedAt = (value) => value === undefined ? new Date().toISOString() : typeof value === "number" ? new Date(value).toISOString() : value;
|
|
42262
42987
|
var getProofPackMetadata = (proofPack) => {
|
|
42263
42988
|
const built = buildVoiceProofPack(proofPack);
|
|
@@ -42786,10 +43511,10 @@ var writeVoiceProofPack = async (input, options = { outputDir: ".voice-runtime/p
|
|
|
42786
43511
|
...input,
|
|
42787
43512
|
outputDir: options.outputDir
|
|
42788
43513
|
});
|
|
42789
|
-
const jsonPath =
|
|
42790
|
-
const markdownPath =
|
|
42791
|
-
await
|
|
42792
|
-
await
|
|
43514
|
+
const jsonPath = join5(options.outputDir, options.jsonFileName ?? "latest.json");
|
|
43515
|
+
const markdownPath = join5(options.outputDir, options.markdownFileName ?? "latest.md");
|
|
43516
|
+
await mkdir6(dirname3(jsonPath), { recursive: true });
|
|
43517
|
+
await mkdir6(dirname3(markdownPath), { recursive: true });
|
|
42793
43518
|
await Promise.all([
|
|
42794
43519
|
Bun.write(jsonPath, JSON.stringify(proofPack, null, 2)),
|
|
42795
43520
|
Bun.write(markdownPath, renderVoiceProofPackMarkdown(proofPack))
|
|
@@ -42862,6 +43587,7 @@ var createVoiceProofPackRoutes = (options) => {
|
|
|
42862
43587
|
};
|
|
42863
43588
|
export {
|
|
42864
43589
|
writeVoiceProofPack,
|
|
43590
|
+
writeVoiceMediaPipelineArtifacts,
|
|
42865
43591
|
withVoiceOpsTaskId,
|
|
42866
43592
|
withVoiceIntegrationEventId,
|
|
42867
43593
|
voiceTelephonyOutcomeToRouteResult,
|
|
@@ -42899,6 +43625,7 @@ export {
|
|
|
42899
43625
|
summarizeVoiceOpsTaskQueue,
|
|
42900
43626
|
summarizeVoiceOpsTaskAnalytics,
|
|
42901
43627
|
summarizeVoiceOpsStatus,
|
|
43628
|
+
summarizeVoiceMediaPipelineReport,
|
|
42902
43629
|
summarizeVoiceLiveLatency,
|
|
42903
43630
|
summarizeVoiceIntegrationEvents,
|
|
42904
43631
|
summarizeVoiceHandoffHealth,
|
|
@@ -43105,6 +43832,7 @@ export {
|
|
|
43105
43832
|
filterVoiceAuditEvents,
|
|
43106
43833
|
fetchVoiceProofTarget,
|
|
43107
43834
|
failVoiceOpsTask,
|
|
43835
|
+
extractVoiceMediaPipelineIssueEntries,
|
|
43108
43836
|
exportVoiceTrace,
|
|
43109
43837
|
exportVoiceAuditTrail,
|
|
43110
43838
|
evaluateVoiceTrace,
|
|
@@ -43113,6 +43841,7 @@ export {
|
|
|
43113
43841
|
evaluateVoiceTelephonyWebhookNormalizationEvidence,
|
|
43114
43842
|
evaluateVoiceTelephonyContract,
|
|
43115
43843
|
evaluateVoiceSimulationSuiteEvidence,
|
|
43844
|
+
evaluateVoiceSessionObservabilityEvidence,
|
|
43116
43845
|
evaluateVoiceRealtimeProviderContractEvidence,
|
|
43117
43846
|
evaluateVoiceRealtimeChannelEvidence,
|
|
43118
43847
|
evaluateVoiceQuality,
|
|
@@ -43507,6 +44236,8 @@ export {
|
|
|
43507
44236
|
buildVoiceRealCallProfileHistoryReportFromStore,
|
|
43508
44237
|
buildVoiceRealCallProfileHistoryReport,
|
|
43509
44238
|
buildVoiceRealCallProfileEvidenceFromTraceEvents,
|
|
44239
|
+
buildVoiceRealCallProfileEvidenceFromRuntimeSurface,
|
|
44240
|
+
buildVoiceRealCallProfileEvidenceFromRuntimeProviderRoles,
|
|
43510
44241
|
buildVoiceRealCallProfileEvidenceFromReconnectProofReports,
|
|
43511
44242
|
buildVoiceRealCallProfileDefaults,
|
|
43512
44243
|
buildVoiceRealCallEvidenceRuntimeWorkerReadinessCheck,
|
|
@@ -43544,6 +44275,8 @@ export {
|
|
|
43544
44275
|
buildVoiceObservabilityArtifactIndex,
|
|
43545
44276
|
buildVoiceMonitorRunReport,
|
|
43546
44277
|
buildVoiceMediaPipelineReport,
|
|
44278
|
+
buildVoiceMediaPipelineReadinessChecks,
|
|
44279
|
+
buildVoiceMediaPipelineIncidentEvents,
|
|
43547
44280
|
buildVoiceLiveOpsControlState,
|
|
43548
44281
|
buildVoiceLatencySLOGate,
|
|
43549
44282
|
buildVoiceIncidentTimelineReport,
|
|
@@ -43575,6 +44308,7 @@ export {
|
|
|
43575
44308
|
assertVoiceTelephonyWebhookNormalizationEvidence,
|
|
43576
44309
|
assertVoiceSloCalibration,
|
|
43577
44310
|
assertVoiceSimulationSuiteEvidence,
|
|
44311
|
+
assertVoiceSessionObservabilityEvidence,
|
|
43578
44312
|
assertVoiceRealtimeProviderContractEvidence,
|
|
43579
44313
|
assertVoiceRealtimeChannelEvidence,
|
|
43580
44314
|
assertVoiceProviderStackEvidence,
|